Working on texture test

This commit is contained in:
Brad Davis 2018-05-09 09:52:00 -07:00
parent 1d403b4d67
commit 7fbad47351
7 changed files with 214 additions and 49 deletions

View file

@ -0,0 +1,3 @@
set(TARGET_NAME test-utils)
setup_hifi_library(Network Gui)

View file

@ -0,0 +1,21 @@
#include "FileDownloader.h"
#include <QtCore/QCoreApplication>
#include <QtNetwork/QNetworkReply>
FileDownloader::FileDownloader(QUrl url, const Handler& handler, QObject* parent) : QObject(parent), _handler(handler) {
connect(&_accessManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(fileDownloaded(QNetworkReply*)));
_accessManager.get(QNetworkRequest(url));
}
void FileDownloader::waitForDownload() {
while (!_complete) {
QCoreApplication::processEvents();
}
}
void FileDownloader::fileDownloaded(QNetworkReply* pReply) {
_handler(pReply->readAll());
pReply->deleteLater();
_complete = true;
}

View file

@ -0,0 +1,23 @@
#pragma once
#include <QtCore/QObject>
#include <QtNetwork/QNetworkAccessManager>
class FileDownloader : public QObject {
Q_OBJECT
public:
using Handler = std::function<void(const QByteArray& data)>;
FileDownloader(QUrl url, const Handler& handler, QObject* parent = 0);
void waitForDownload();
private slots:
void fileDownloaded(QNetworkReply* pReply);
private:
QNetworkAccessManager _accessManager;
Handler _handler;
bool _complete { false };
};

View file

@ -13,8 +13,9 @@
#define hifi_QTestExtensions_hpp
#include <QtTest/QtTest>
#include <QtCore/QFileInfo>
#include <functional>
#include <NumericalConstants.h>
#include "GLMTestUtils.h"
// Implements several extensions to QtTest.
@ -302,3 +303,43 @@ inline auto errorTest (float actual, float expected, float acceptableRelativeErr
QCOMPARE_WITH_LAMBDA(actual, expected, errorTest(actual, expected, relativeError))
inline QString getTestResource(const QString& relativePath) {
static QDir dir;
static std::once_flag once;
std::call_once(once, []{
QFileInfo fileInfo(__FILE__);
auto parentDir = fileInfo.absoluteDir();
auto rootDir = parentDir.absoluteFilePath("..");
dir = QDir::cleanPath(rootDir);
});
return QDir::cleanPath(dir.absoluteFilePath(relativePath));
}
inline bool afterUsecs(uint64_t& startUsecs, uint64_t maxIntervalUecs) {
auto now = usecTimestampNow();
auto interval = now - startUsecs;
if (interval > maxIntervalUecs) {
startUsecs = now;
return true;
}
return false;
}
inline bool afterSecs(uint64_t& startUsecs, uint64_t maxIntervalSecs) {
return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND);
}
template <typename F>
void reportEvery(uint64_t& lastReportUsecs, uint64_t secs, F lamdba) {
if (afterSecs(lastReportUsecs, secs)) {
lamdba();
}
}
inline void failAfter(uint64_t startUsecs, uint64_t secs, const char* message) {
if (afterSecs(startUsecs, secs)) {
QFAIL(message);
}
}

View file

@ -1,9 +1,17 @@
# Declare dependencies
macro (setup_testcase_dependencies)
# link in the shared libraries
link_hifi_libraries(shared ktx gpu gl ${PLATFORM_GL_BACKEND})
link_hifi_libraries(shared test-utils ktx gpu gl ${PLATFORM_GL_BACKEND})
package_libraries_for_deployment()
target_opengl()
target_zlib()
find_package(QuaZip REQUIRED)
target_include_directories(${TARGET_NAME} SYSTEM PUBLIC ${QUAZIP_INCLUDE_DIRS})
target_link_libraries(${TARGET_NAME} ${QUAZIP_LIBRARIES})
if (WIN32)
add_paths_to_fixup_libs(${QUAZIP_DLL_PATH})
add_dependency_external_projects(wasapi)
endif ()
endmacro ()
setup_hifi_testcase()

View file

@ -8,16 +8,27 @@
#include "TextureTest.h"
#include <QtCore/QTemporaryFile>
#include <gpu/Forward.h>
#include <gl/Config.h>
#include <gl/GLHelpers.h>
#include <gpu/gl/GLBackend.h>
#include <NumericalConstants.h>
#include <test-utils/FileDownloader.h>
#include <quazip5/quazip.h>
#include <quazip5/JlCompress.h>
#include "../../QTestExtensions.h"
#pragma optimize("", off)
QTEST_MAIN(TextureTest)
#pragma optimize("", off)
#define LOAD_TEXTURE_COUNT 40
static const QDir TEST_DIR("D:/ktx_texture_test");
static const QString TEST_DATA("https://hifi-public.s3.amazonaws.com/austin/test_data/test_ktx.zip");
std::string vertexShaderSource = R"SHADER(
#line 14
@ -54,7 +65,10 @@ void main() {
)SHADER";
#define USE_SERVER_DATA 1
void TextureTest::initTestCase() {
_resourcesPath = getTestResource("interface/resources");
getDefaultOpenGLSurfaceFormat();
_canvas.create();
if (!_canvas.makeCurrent()) {
@ -63,7 +77,32 @@ void TextureTest::initTestCase() {
gl::initModuleGl();
gpu::Context::init<gpu::gl::GLBackend>();
_gpuContext = std::make_shared<gpu::Context>();
gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(4096));
#if USE_SERVER_DATA
if (!_testDataDir.isValid()) {
qFatal("Unable to create temp directory");
}
QString path = _testDataDir.path();
FileDownloader(TEST_DATA,
[&](const QByteArray& data) {
QTemporaryFile zipFile;
if (zipFile.open()) {
zipFile.write(data);
zipFile.close();
}
if (!_testDataDir.isValid()) {
qFatal("Unable to create temp dir");
}
auto files = JlCompress::extractDir(zipFile.fileName(), _testDataDir.path());
for (const auto& file : files) {
qDebug() << file;
}
})
.waitForDownload();
_resourcesPath = _testDataDir.path();
#else
_resourcesPath = "D:/test_ktx";
#endif
_canvas.makeCurrent();
{
@ -80,19 +119,21 @@ void TextureTest::initTestCase() {
_pipeline = gpu::Pipeline::create(program, state);
}
{ _framebuffer.reset(gpu::Framebuffer::create("cached", gpu::Element::COLOR_SRGBA_32, _size.x, _size.y)); }
_framebuffer.reset(gpu::Framebuffer::create("cached", gpu::Element::COLOR_SRGBA_32, _size.x, _size.y));
// Find the test textures
{
auto entryList = TEST_DIR.entryList({ "*.ktx" }, QDir::Filter::Files);
QDir resourcesDir(_resourcesPath);
auto entryList = resourcesDir.entryList({ "*.ktx" }, QDir::Filter::Files);
_textureFiles.reserve(entryList.size());
for (auto entry : entryList) {
auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
auto textureFile = resourcesDir.absoluteFilePath(entry).toStdString();
_textureFiles.push_back(textureFile);
}
}
// Load the test textures
{
std::shuffle(_textureFiles.begin(), _textureFiles.end(), std::default_random_engine());
size_t newTextureCount = std::min<size_t>(_textureFiles.size(), LOAD_TEXTURE_COUNT);
for (size_t i = 0; i < newTextureCount; ++i) {
const auto& textureFile = _textureFiles[i];
@ -103,6 +144,9 @@ void TextureTest::initTestCase() {
}
void TextureTest::cleanupTestCase() {
_framebuffer.reset();
_pipeline.reset();
_gpuContext->recycle();
_gpuContext.reset();
}
@ -133,35 +177,8 @@ void TextureTest::renderFrame(const std::function<void(gpu::Batch&)>& renderLamb
endFrame();
}
inline bool afterUsecs(uint64_t& startUsecs, uint64_t maxIntervalUecs) {
auto now = usecTimestampNow();
auto interval = now - startUsecs;
if (interval > maxIntervalUecs) {
startUsecs = now;
return true;
}
return false;
}
inline bool afterSecs(uint64_t& startUsecs, uint64_t maxIntervalSecs) {
return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND);
}
template <typename F>
void reportEvery(uint64_t& lastReportUsecs, uint64_t secs, F lamdba) {
if (afterSecs(lastReportUsecs, secs)) {
lamdba();
}
}
inline void failAfter(uint64_t startUsecs, uint64_t secs, const char* message) {
if (afterSecs(startUsecs, secs)) {
qFatal(message);
}
}
void TextureTest::testTextureLoading() {
QVERIFY(_textures.size() > 0);
auto renderTexturesLamdba = [this](gpu::Batch& batch) {
batch.setPipeline(_pipeline);
for (const auto& texture : _textures) {
@ -170,39 +187,87 @@ void TextureTest::testTextureLoading() {
}
};
size_t totalSize = 0;
size_t expectedAllocation = 0;
for (const auto& texture : _textures) {
totalSize += texture->evalTotalSize();
expectedAllocation += texture->evalTotalSize();
}
QVERIFY(_textures.size() > 0);
auto reportLambda = [=] {
qDebug() << "Expected" << totalSize;
qDebug() << "Allowed " << gpu::Texture::getAllowedGPUMemoryUsage();
qDebug() << "Allowed " << gpu::Texture::getAllowedGPUMemoryUsage();
qDebug() << "Allocated " << gpu::Context::getTextureResourceGPUMemSize();
qDebug() << "Populated " << gpu::Context::getTextureResourcePopulatedGPUMemSize();
qDebug() << "Pending " << gpu::Context::getTexturePendingGPUTransferMemSize();
};
auto allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
auto populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
// Cycle frames we're fully allocated
// We need to use the texture rendering lambda
auto lastReport = usecTimestampNow();
auto start = usecTimestampNow();
while (totalSize != gpu::Context::getTextureResourceGPUMemSize()) {
while (expectedAllocation != allocatedMemory) {
reportEvery(lastReport, 4, reportLambda);
failAfter(start, 10, "Failed to allocate texture memory after 10 seconds");
renderFrame(renderTexturesLamdba);
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
}
QCOMPARE(allocatedMemory, expectedAllocation);
// Restart the timer
start = usecTimestampNow();
auto allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
auto populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
while (allocatedMemory != populatedMemory && 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) {
// Cycle frames we're fully populated
while (allocatedMemory != populatedMemory || 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) {
reportEvery(lastReport, 4, reportLambda);
failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
renderFrame();
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
}
QCOMPARE(allocatedMemory, totalSize);
QCOMPARE(populatedMemory, totalSize);
}
reportLambda();
QCOMPARE(populatedMemory, allocatedMemory);
// FIXME workaround a race condition in the difference between populated size and the actual _populatedMip value in the texture
for (size_t i = 0; i < _textures.size(); ++i) {
renderFrame();
}
// Test on-demand deallocation of memory
auto maxMemory = allocatedMemory / 2;
gpu::Texture::setAllowedGPUMemoryUsage(maxMemory);
// Restart the timer
start = usecTimestampNow();
// Cycle frames until the allocated memory is below the max memory
while (allocatedMemory > maxMemory || allocatedMemory != populatedMemory) {
reportEvery(lastReport, 4, reportLambda);
failAfter(start, 10, "Failed to deallocate texture memory after 10 seconds");
renderFrame(renderTexturesLamdba);
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
}
reportLambda();
// Verify that the allocation is now below the target
QVERIFY(allocatedMemory <= maxMemory);
// Verify that populated memory is the same as allocated memory
QCOMPARE(populatedMemory, allocatedMemory);
// Restart the timer
start = usecTimestampNow();
// Reset the max memory to automatic
gpu::Texture::setAllowedGPUMemoryUsage(0);
// Cycle frames we're fully populated
while (allocatedMemory != expectedAllocation || allocatedMemory != populatedMemory) {
reportEvery(lastReport, 4, reportLambda);
failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
renderFrame();
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
}
reportLambda();
QCOMPARE(allocatedMemory, expectedAllocation);
QCOMPARE(populatedMemory, allocatedMemory);
}

View file

@ -9,6 +9,8 @@
#pragma once
#include <QtTest/QtTest>
#include <QtCore/QTemporaryDir>
#include <gpu/Forward.h>
#include <gl/OffscreenGLCanvas.h>
@ -18,7 +20,7 @@ class TextureTest : public QObject {
private:
void beginFrame();
void endFrame();
void renderFrame(const std::function<void(gpu::Batch&)>& = [](gpu::Batch&){});
void renderFrame(const std::function<void(gpu::Batch&)>& = [](gpu::Batch&) {});
private slots:
void initTestCase();
@ -26,6 +28,8 @@ private slots:
void testTextureLoading();
private:
QString _resourcesPath;
QTemporaryDir _testDataDir;
OffscreenGLCanvas _canvas;
gpu::ContextPointer _gpuContext;
gpu::PipelinePointer _pipeline;