mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 17:01:18 +02:00
Working on texture test
This commit is contained in:
parent
1d403b4d67
commit
7fbad47351
7 changed files with 214 additions and 49 deletions
3
libraries/test-utils/CMakeLists.txt
Normal file
3
libraries/test-utils/CMakeLists.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
set(TARGET_NAME test-utils)
|
||||||
|
setup_hifi_library(Network Gui)
|
||||||
|
|
21
libraries/test-utils/src/test-utils/FileDownloader.cpp
Normal file
21
libraries/test-utils/src/test-utils/FileDownloader.cpp
Normal 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;
|
||||||
|
}
|
23
libraries/test-utils/src/test-utils/FileDownloader.h
Normal file
23
libraries/test-utils/src/test-utils/FileDownloader.h
Normal 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 };
|
||||||
|
};
|
|
@ -13,8 +13,9 @@
|
||||||
#define hifi_QTestExtensions_hpp
|
#define hifi_QTestExtensions_hpp
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <NumericalConstants.h>
|
||||||
#include "GLMTestUtils.h"
|
#include "GLMTestUtils.h"
|
||||||
|
|
||||||
// Implements several extensions to QtTest.
|
// 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))
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
# Declare dependencies
|
# Declare dependencies
|
||||||
macro (setup_testcase_dependencies)
|
macro (setup_testcase_dependencies)
|
||||||
# link in the shared libraries
|
# 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()
|
package_libraries_for_deployment()
|
||||||
target_opengl()
|
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 ()
|
endmacro ()
|
||||||
|
|
||||||
setup_hifi_testcase()
|
setup_hifi_testcase()
|
||||||
|
|
|
@ -8,16 +8,27 @@
|
||||||
|
|
||||||
#include "TextureTest.h"
|
#include "TextureTest.h"
|
||||||
|
|
||||||
|
#include <QtCore/QTemporaryFile>
|
||||||
|
|
||||||
#include <gpu/Forward.h>
|
#include <gpu/Forward.h>
|
||||||
#include <gl/Config.h>
|
#include <gl/Config.h>
|
||||||
#include <gl/GLHelpers.h>
|
#include <gl/GLHelpers.h>
|
||||||
#include <gpu/gl/GLBackend.h>
|
#include <gpu/gl/GLBackend.h>
|
||||||
#include <NumericalConstants.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)
|
QTEST_MAIN(TextureTest)
|
||||||
#pragma optimize("", off)
|
|
||||||
#define LOAD_TEXTURE_COUNT 40
|
#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(
|
std::string vertexShaderSource = R"SHADER(
|
||||||
#line 14
|
#line 14
|
||||||
|
@ -54,7 +65,10 @@ void main() {
|
||||||
|
|
||||||
)SHADER";
|
)SHADER";
|
||||||
|
|
||||||
|
#define USE_SERVER_DATA 1
|
||||||
|
|
||||||
void TextureTest::initTestCase() {
|
void TextureTest::initTestCase() {
|
||||||
|
_resourcesPath = getTestResource("interface/resources");
|
||||||
getDefaultOpenGLSurfaceFormat();
|
getDefaultOpenGLSurfaceFormat();
|
||||||
_canvas.create();
|
_canvas.create();
|
||||||
if (!_canvas.makeCurrent()) {
|
if (!_canvas.makeCurrent()) {
|
||||||
|
@ -63,7 +77,32 @@ void TextureTest::initTestCase() {
|
||||||
gl::initModuleGl();
|
gl::initModuleGl();
|
||||||
gpu::Context::init<gpu::gl::GLBackend>();
|
gpu::Context::init<gpu::gl::GLBackend>();
|
||||||
_gpuContext = std::make_shared<gpu::Context>();
|
_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();
|
_canvas.makeCurrent();
|
||||||
{
|
{
|
||||||
|
@ -80,19 +119,21 @@ void TextureTest::initTestCase() {
|
||||||
_pipeline = gpu::Pipeline::create(program, state);
|
_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());
|
_textureFiles.reserve(entryList.size());
|
||||||
for (auto entry : entryList) {
|
for (auto entry : entryList) {
|
||||||
auto textureFile = TEST_DIR.absoluteFilePath(entry).toStdString();
|
auto textureFile = resourcesDir.absoluteFilePath(entry).toStdString();
|
||||||
_textureFiles.push_back(textureFile);
|
_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);
|
size_t newTextureCount = std::min<size_t>(_textureFiles.size(), LOAD_TEXTURE_COUNT);
|
||||||
for (size_t i = 0; i < newTextureCount; ++i) {
|
for (size_t i = 0; i < newTextureCount; ++i) {
|
||||||
const auto& textureFile = _textureFiles[i];
|
const auto& textureFile = _textureFiles[i];
|
||||||
|
@ -103,6 +144,9 @@ void TextureTest::initTestCase() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureTest::cleanupTestCase() {
|
void TextureTest::cleanupTestCase() {
|
||||||
|
_framebuffer.reset();
|
||||||
|
_pipeline.reset();
|
||||||
|
_gpuContext->recycle();
|
||||||
_gpuContext.reset();
|
_gpuContext.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,35 +177,8 @@ void TextureTest::renderFrame(const std::function<void(gpu::Batch&)>& renderLamb
|
||||||
endFrame();
|
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() {
|
void TextureTest::testTextureLoading() {
|
||||||
|
QVERIFY(_textures.size() > 0);
|
||||||
auto renderTexturesLamdba = [this](gpu::Batch& batch) {
|
auto renderTexturesLamdba = [this](gpu::Batch& batch) {
|
||||||
batch.setPipeline(_pipeline);
|
batch.setPipeline(_pipeline);
|
||||||
for (const auto& texture : _textures) {
|
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) {
|
for (const auto& texture : _textures) {
|
||||||
totalSize += texture->evalTotalSize();
|
expectedAllocation += texture->evalTotalSize();
|
||||||
}
|
}
|
||||||
|
QVERIFY(_textures.size() > 0);
|
||||||
|
|
||||||
auto reportLambda = [=] {
|
auto reportLambda = [=] {
|
||||||
qDebug() << "Expected" << totalSize;
|
|
||||||
qDebug() << "Allowed " << gpu::Texture::getAllowedGPUMemoryUsage();
|
qDebug() << "Allowed " << gpu::Texture::getAllowedGPUMemoryUsage();
|
||||||
qDebug() << "Allocated " << gpu::Context::getTextureResourceGPUMemSize();
|
qDebug() << "Allocated " << gpu::Context::getTextureResourceGPUMemSize();
|
||||||
qDebug() << "Populated " << gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
qDebug() << "Populated " << gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
||||||
qDebug() << "Pending " << gpu::Context::getTexturePendingGPUTransferMemSize();
|
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 lastReport = usecTimestampNow();
|
||||||
auto start = usecTimestampNow();
|
auto start = usecTimestampNow();
|
||||||
while (totalSize != gpu::Context::getTextureResourceGPUMemSize()) {
|
while (expectedAllocation != allocatedMemory) {
|
||||||
reportEvery(lastReport, 4, reportLambda);
|
reportEvery(lastReport, 4, reportLambda);
|
||||||
failAfter(start, 10, "Failed to allocate texture memory after 10 seconds");
|
failAfter(start, 10, "Failed to allocate texture memory after 10 seconds");
|
||||||
renderFrame(renderTexturesLamdba);
|
renderFrame(renderTexturesLamdba);
|
||||||
|
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
|
||||||
|
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
||||||
}
|
}
|
||||||
|
QCOMPARE(allocatedMemory, expectedAllocation);
|
||||||
|
|
||||||
// Restart the timer
|
// Restart the timer
|
||||||
start = usecTimestampNow();
|
start = usecTimestampNow();
|
||||||
auto allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
|
// Cycle frames we're fully populated
|
||||||
auto populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
while (allocatedMemory != populatedMemory || 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) {
|
||||||
while (allocatedMemory != populatedMemory && 0 != gpu::Context::getTexturePendingGPUTransferMemSize()) {
|
|
||||||
reportEvery(lastReport, 4, reportLambda);
|
reportEvery(lastReport, 4, reportLambda);
|
||||||
failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
|
failAfter(start, 10, "Failed to populate texture memory after 10 seconds");
|
||||||
renderFrame();
|
renderFrame();
|
||||||
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
|
allocatedMemory = gpu::Context::getTextureResourceGPUMemSize();
|
||||||
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
populatedMemory = gpu::Context::getTextureResourcePopulatedGPUMemSize();
|
||||||
}
|
}
|
||||||
QCOMPARE(allocatedMemory, totalSize);
|
reportLambda();
|
||||||
QCOMPARE(populatedMemory, totalSize);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
#include <QtCore/QTemporaryDir>
|
||||||
|
|
||||||
#include <gpu/Forward.h>
|
#include <gpu/Forward.h>
|
||||||
#include <gl/OffscreenGLCanvas.h>
|
#include <gl/OffscreenGLCanvas.h>
|
||||||
|
|
||||||
|
@ -26,6 +28,8 @@ private slots:
|
||||||
void testTextureLoading();
|
void testTextureLoading();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
QString _resourcesPath;
|
||||||
|
QTemporaryDir _testDataDir;
|
||||||
OffscreenGLCanvas _canvas;
|
OffscreenGLCanvas _canvas;
|
||||||
gpu::ContextPointer _gpuContext;
|
gpu::ContextPointer _gpuContext;
|
||||||
gpu::PipelinePointer _pipeline;
|
gpu::PipelinePointer _pipeline;
|
||||||
|
|
Loading…
Reference in a new issue