From 4bb8d4e78e6a67676e52c73f395beacac8e58534 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 24 Oct 2016 14:10:01 -0700 Subject: [PATCH 1/5] Force all loaded textures to sparse compatible sizes --- libraries/model/src/model/TextureMap.cpp | 43 ++++++++++++++++++-- tests/gpu-test/src/main.cpp | 50 +++++++++++++++++++++++- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 30f176b5a6..b9f08a8303 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -22,11 +22,48 @@ using namespace gpu; // FIXME: Declare this to enable compression //#define COMPRESS_TEXTURES +#define SPARSE_PAGE_DIMENSION 128 + +static const uvec2 SPARSE_PAGE_SIZE(SPARSE_PAGE_DIMENSION); bool DEV_DECIMATE_TEXTURES = false; -QImage processSourceImage(const QImage& srcImage) { - if (DEV_DECIMATE_TEXTURES) { - return srcImage.scaled(srcImage.size() * 0.5f); + +bool needsSparseRectification(const uvec2& size) { + // Don't attempt to rectify small textures (textures less than the sparse page size in any dimension) + if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) { + return false; } + + // Don't rectify textures that are already an exact multiple of sparse page size + if (uvec2(0) == (size % SPARSE_PAGE_SIZE)) { + return false; + } + + // Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify! + return true; +} + +uvec2 rectifyToSparseSize(const uvec2& size) { + uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, uvec2(0), uvec2(1))); + uvec2 result = pages * SPARSE_PAGE_SIZE; + return result; +} + +QImage processSourceImage(const QImage& srcImage) { + const uvec2 srcImageSize = toGlm(srcImage.size()); + uvec2 targetSize = srcImageSize; + + if (needsSparseRectification(srcImageSize)) { + targetSize = rectifyToSparseSize(srcImageSize); + } + + if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, uvec2(2)))) { + targetSize /= 2; + } + + if (targetSize != srcImageSize) { + return srcImage.scaled(fromGlm(targetSize)); + } + return srcImage; } diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 008f363e53..9608576a6f 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -164,11 +164,59 @@ class MyTestWindow : public TestWindow { } }; +extern bool needsSparseRectification(const uvec2& size); +extern uvec2 rectifyToSparseSize(const uvec2& size); -int main(int argc, char** argv) { +void testSparseRectify() { + std::vector> NEEDS_SPARSE_TESTS {{ + // Already sparse + { {1024, 1024 }, false }, + { { 128, 128 }, false }, + // Too small in one dimension + { { 127, 127 }, false }, + { { 1, 1 }, false }, + { { 1000, 1 }, false }, + { { 1024, 1 }, false }, + { { 100, 100 }, false }, + // needs rectification + { { 1000, 1000 }, true }, + { { 1024, 1000 }, true }, + } }; + + for (const auto& test : NEEDS_SPARSE_TESTS) { + const auto& size = test.first; + const auto& expected = test.second; + auto result = needsSparseRectification(size); + Q_ASSERT(expected == result); + result = needsSparseRectification(uvec2(size.y, size.x)); + Q_ASSERT(expected == result); + } + + std::vector> SPARSE_SIZE_TESTS { { + // needs rectification + { { 1000, 1000 }, { 1024, 1024 } }, + { { 1024, 1000 }, { 1024, 1024 } }, + } }; + + for (const auto& test : SPARSE_SIZE_TESTS) { + const auto& size = test.first; + const auto& expected = test.second; + auto result = rectifyToSparseSize(size); + Q_ASSERT(expected == result); + result = rectifyToSparseSize(uvec2(size.y, size.x)); + Q_ASSERT(expected == uvec2(result.y, result.x)); + } +} + +int main(int argc, char** argv) { + testSparseRectify(); + + // FIXME this test appears to be broken +#if 0 QGuiApplication app(argc, argv); MyTestWindow window; app.exec(); +#endif return 0; } From f26dc4dcb9d4faf494bd51b987dd845bf081040d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 24 Oct 2016 16:46:42 -0700 Subject: [PATCH 2/5] Don't rectify skybox textures --- libraries/model/src/model/TextureMap.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index b9f08a8303..52fe24e167 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -48,11 +48,11 @@ uvec2 rectifyToSparseSize(const uvec2& size) { return result; } -QImage processSourceImage(const QImage& srcImage) { +QImage processSourceImage(const QImage& srcImage, bool cubemap) { const uvec2 srcImageSize = toGlm(srcImage.size()); uvec2 targetSize = srcImageSize; - if (needsSparseRectification(srcImageSize)) { + if (!cubemap && needsSparseRectification(srcImageSize)) { targetSize = rectifyToSparseSize(srcImageSize); } @@ -97,7 +97,7 @@ void TextureMap::setLightmapOffsetScale(float offset, float scale) { } const QImage TextureUsage::process2DImageColor(const QImage& srcImage, bool& validAlpha, bool& alphaAsMask) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); validAlpha = false; alphaAsMask = true; const uint8 OPAQUE_ALPHA = 255; @@ -265,7 +265,7 @@ gpu::Texture* TextureUsage::createLightmapTextureFromImage(const QImage& srcImag gpu::Texture* TextureUsage::createNormalTextureFromNormalImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); @@ -299,7 +299,7 @@ double mapComponent(double sobelValue) { } gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); @@ -371,7 +371,7 @@ gpu::Texture* TextureUsage::createNormalTextureFromBumpImage(const QImage& srcIm } gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); @@ -405,7 +405,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromImage(const QImage& srcIma } gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); @@ -443,7 +443,7 @@ gpu::Texture* TextureUsage::createRoughnessTextureFromGlossImage(const QImage& s } gpu::Texture* TextureUsage::createMetallicTextureFromImage(const QImage& srcImage, const std::string& srcImageName) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, false); if (!image.hasAlphaChannel()) { if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); @@ -736,7 +736,7 @@ const int CubeLayout::NUM_CUBEMAP_LAYOUTS = sizeof(CubeLayout::CUBEMAP_LAYOUTS) gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcImage, const std::string& srcImageName, bool isLinear, bool doCompress, bool generateMips, bool generateIrradiance) { gpu::Texture* theTexture = nullptr; if ((srcImage.width() > 0) && (srcImage.height() > 0)) { - QImage image = processSourceImage(srcImage); + QImage image = processSourceImage(srcImage, true); if (image.format() != QImage::Format_RGB888) { image = image.convertToFormat(QImage::Format_RGB888); } From 78d55011a45812ea429a88b5b1284f8ceabadf99 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 24 Oct 2016 16:58:37 -0700 Subject: [PATCH 3/5] Limit sparse textures to 1 GB for now --- libraries/gpu-gl/src/gpu/gl/GLTexture.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 9a19d6f309..61a76c2d0b 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -102,12 +102,17 @@ float GLTexture::getMemoryPressure() { // If no memory limit has been set, use a percentage of the total dedicated memory if (!availableTextureMemory) { +#if 0 auto totalMemory = getDedicatedMemory(); if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; } else { availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; } +#else + // Hardcode texture limit for sparse textures at 1 GB for now + availableTextureMemory = GPU_MEMORY_RESERVE_BYTES; +#endif } // Return the consumed texture memory divided by the available texture memory. From a9bf2a5cd368225cade72d22f4a7100072287856 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 24 Oct 2016 16:59:31 -0700 Subject: [PATCH 4/5] Limit max texture size, don't force cubemaps to sparse page size --- libraries/model/src/model/TextureMap.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 52fe24e167..4a6bbf6f9a 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -21,10 +21,8 @@ using namespace gpu; // FIXME: Declare this to enable compression //#define COMPRESS_TEXTURES - -#define SPARSE_PAGE_DIMENSION 128 - -static const uvec2 SPARSE_PAGE_SIZE(SPARSE_PAGE_DIMENSION); +static const uvec2 SPARSE_PAGE_SIZE(128); +static const uvec2 MAX_TEXTURE_SIZE(4096); bool DEV_DECIMATE_TEXTURES = false; bool needsSparseRectification(const uvec2& size) { @@ -52,8 +50,12 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { const uvec2 srcImageSize = toGlm(srcImage.size()); uvec2 targetSize = srcImageSize; - if (!cubemap && needsSparseRectification(srcImageSize)) { - targetSize = rectifyToSparseSize(srcImageSize); + while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) { + targetSize /= 2; + } + + if (!cubemap && needsSparseRectification(targetSize)) { + targetSize = rectifyToSparseSize(targetSize); } if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, uvec2(2)))) { @@ -746,7 +748,8 @@ gpu::Texture* TextureUsage::processCubeTextureColorFromImage(const QImage& srcIm defineColorTexelFormats(formatGPU, formatMip, image, isLinear, doCompress); // Find the layout of the cubemap in the 2D image - int foundLayout = CubeLayout::findLayout(image.width(), image.height()); + // Use the original image size since processSourceImage may have altered the size / aspect ratio + int foundLayout = CubeLayout::findLayout(srcImage.width(), srcImage.height()); std::vector faces; // If found, go extract the faces as separate images From ce627eeea1506d449f45742faa12c578eccb3c6b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 24 Oct 2016 17:11:29 -0700 Subject: [PATCH 5/5] Updated metrics --- interface/resources/qml/Stats.qml | 9 +++++++++ interface/src/ui/Stats.cpp | 5 +++++ interface/src/ui/Stats.h | 6 ++++++ libraries/model/src/model/TextureMap.cpp | 8 ++++++++ 4 files changed, 28 insertions(+) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2439aaf5c0..1c5df5c71d 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -205,6 +205,12 @@ Item { StatText { text: " Count: " + root.gpuTextures; } + StatText { + text: " Rectified: " + root.rectifiedTextureCount; + } + StatText { + text: " Decimated: " + root.decimatedTextureCount; + } StatText { text: " Sparse Count: " + root.gpuTexturesSparse; visible: 0 != root.gpuSparseTextureEnabled; @@ -228,6 +234,9 @@ Item { StatText { text: " Count: " + root.gpuTextures; } + StatText { + text: " Memory: " + root.gpuBufferMemory; + } StatText { text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB"; } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 8c4f71bb24..11660a332d 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -96,6 +96,8 @@ bool Stats::includeTimingRecord(const QString& name) { } \ } +extern std::atomic DECIMATED_TEXTURE_COUNT; +extern std::atomic RECTIFIED_TEXTURE_COUNT; void Stats::updateStats(bool force) { if (!force) { @@ -289,6 +291,7 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(gpuBuffers, (int)gpu::Context::getBufferGPUCount()); + STAT_UPDATE(gpuBufferMemory, (int)BYTES_TO_MB(gpu::Context::getBufferGPUMemoryUsage())); STAT_UPDATE(gpuTextures, (int)gpu::Context::getTextureGPUCount()); STAT_UPDATE(gpuTexturesSparse, (int)gpu::Context::getTextureGPUSparseCount()); @@ -301,6 +304,8 @@ void Stats::updateStats(bool force) { STAT_UPDATE(gpuTextureSparseMemory, (int)BYTES_TO_MB(gpu::Texture::getTextureGPUSparseMemoryUsage())); STAT_UPDATE(gpuSparseTextureEnabled, gpu::Texture::getEnableSparseTextures() ? 1 : 0); STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemory())); + STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); + STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load()); // Incoming packets QLocale locale(QLocale::English); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index a98a99f093..76c6effed7 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -87,7 +87,10 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, localElements, 0) STATS_PROPERTY(int, localInternal, 0) STATS_PROPERTY(int, localLeaves, 0) + STATS_PROPERTY(int, rectifiedTextureCount, 0) + STATS_PROPERTY(int, decimatedTextureCount, 0) STATS_PROPERTY(int, gpuBuffers, 0) + STATS_PROPERTY(int, gpuBufferMemory, 0) STATS_PROPERTY(int, gpuTextures, 0) STATS_PROPERTY(int, gpuTexturesSparse, 0) STATS_PROPERTY(int, glContextSwapchainMemory, 0) @@ -186,6 +189,7 @@ signals: void glContextSwapchainMemoryChanged(); void qmlTextureMemoryChanged(); void gpuBuffersChanged(); + void gpuBufferMemoryChanged(); void gpuTexturesChanged(); void gpuTexturesSparseChanged(); void gpuTextureMemoryChanged(); @@ -194,6 +198,8 @@ signals: void gpuTextureSparseMemoryChanged(); void gpuSparseTextureEnabledChanged(); void gpuFreeMemoryChanged(); + void rectifiedTextureCountChanged(); + void decimatedTextureCountChanged(); private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/model/src/model/TextureMap.cpp index 4a6bbf6f9a..3e346a564e 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/model/src/model/TextureMap.cpp @@ -46,6 +46,9 @@ uvec2 rectifyToSparseSize(const uvec2& size) { return result; } +std::atomic DECIMATED_TEXTURE_COUNT { 0 }; +std::atomic RECTIFIED_TEXTURE_COUNT { 0 }; + QImage processSourceImage(const QImage& srcImage, bool cubemap) { const uvec2 srcImageSize = toGlm(srcImage.size()); uvec2 targetSize = srcImageSize; @@ -53,8 +56,12 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { while (glm::any(glm::greaterThan(targetSize, MAX_TEXTURE_SIZE))) { targetSize /= 2; } + if (targetSize != srcImageSize) { + ++DECIMATED_TEXTURE_COUNT; + } if (!cubemap && needsSparseRectification(targetSize)) { + ++RECTIFIED_TEXTURE_COUNT; targetSize = rectifyToSparseSize(targetSize); } @@ -63,6 +70,7 @@ QImage processSourceImage(const QImage& srcImage, bool cubemap) { } if (targetSize != srcImageSize) { + qDebug() << "Resizing texture from " << srcImageSize.x << "x" << srcImageSize.y << " to " << targetSize.x << "x" << targetSize.y; return srcImage.scaled(fromGlm(targetSize)); }