diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp index 3ce1c8e5c4..1e0dd08ae1 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexture.cpp @@ -116,7 +116,7 @@ float GLTexture::getMemoryPressure() { } // Return the consumed texture memory divided by the available texture memory. - auto consumedGpuMemory = Context::getTextureGPUSparseMemoryUsage(); + auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; static Context::Size lastConsumedGpuMemory = 0; if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h index 643d54af6a..438b1e9454 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h +++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h @@ -94,6 +94,7 @@ public: TransferState _transferState; uint32_t _allocatedPages { 0 }; uint32_t _lastMipAllocatedPages { 0 }; + uint16_t _mipOffset { 0 }; friend class GL45Backend; }; diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index ac9a84513e..d316116561 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -282,20 +282,22 @@ GL45Texture::~GL45Texture() { if (!_gpuObject.getUsage().isExternal()) { qCDebug(gpugl45logging) << "Destroying texture " << _id << " from source " << _source.c_str(); } - if (_sparseInfo.sparse) { - Backend::decrementTextureGPUSparseCount(); - // Remove this texture from the candidate list of derezzable textures - { - auto mipLevels = usedMipLevels(); - Lock lock(texturesByMipCountsMutex); - if (texturesByMipCounts.count(mipLevels)) { - auto& textures = texturesByMipCounts[mipLevels]; - textures.erase(this); - if (textures.empty()) { - texturesByMipCounts.erase(mipLevels); - } + + // Remove this texture from the candidate list of derezzable textures + if (_transferrable) { + auto mipLevels = usedMipLevels(); + Lock lock(texturesByMipCountsMutex); + if (texturesByMipCounts.count(mipLevels)) { + auto& textures = texturesByMipCounts[mipLevels]; + textures.erase(this); + if (textures.empty()) { + texturesByMipCounts.erase(mipLevels); } } + } + + if (_sparseInfo.sparse) { + Backend::decrementTextureGPUSparseCount(); // Experimenation suggests that allocating sparse textures on one context/thread and deallocating // them on another is buggy. So for sparse textures we need to queue a lambda with the deallocation @@ -355,7 +357,7 @@ void GL45Texture::allocateStorage() const { glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); + Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip + _mipOffset); glTextureStorage2D(_id, usedMipLevels(), _internalFormat, dimensions.x, dimensions.y); (void)CHECK_GL_ERROR(); } @@ -370,7 +372,7 @@ void GL45Texture::updateSize() const { Backend::updateTextureGPUSparseMemoryUsage(_size, size); setSize(_allocatedPages * _sparseInfo.pageBytes); } else { - setSize(_virtualSize); + setSize(_gpuObject.evalTotalSize(_mipOffset)); } } @@ -481,35 +483,30 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTextureParameteri(_id, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); glTextureParameterfv(_id, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + // FIXME account for mip offsets here auto baseMip = std::max(sampler.getMipOffset(), _minMip); glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, baseMip); glTextureParameterf(_id, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); - glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + glTextureParameterf(_id, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip() - _mipOffset)); glTextureParameterf(_id, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); } void GL45Texture::postTransfer() { Parent::postTransfer(); - if (_sparseInfo.sparse) { - auto mipLevels = usedMipLevels(); - if (mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { - Lock lock(texturesByMipCountsMutex); - texturesByMipCounts[mipLevels].insert(this); - } + auto mipLevels = usedMipLevels(); + if (_transferrable && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { + Lock lock(texturesByMipCountsMutex); + texturesByMipCounts[mipLevels].insert(this); } } void GL45Texture::stripToMip(uint16_t newMinMip) { - if (!_sparseInfo.sparse) { - return; - } - if (newMinMip < _minMip) { qCWarning(gpugl45logging) << "Cannot decrease the min mip"; return; } - if (newMinMip > _sparseInfo.maxSparseLevel) { + if (_sparseInfo.sparse && newMinMip > _sparseInfo.maxSparseLevel) { qCWarning(gpugl45logging) << "Cannot increase the min mip into the mip tail"; return; } @@ -533,19 +530,53 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { uint8_t maxFace = (uint8_t)((_target == GL_TEXTURE_CUBE_MAP) ? GLTexture::CUBE_NUM_FACES : 1); - for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { - auto id = _id; - auto mipDimensions = _gpuObject.evalMipDimensions(mip); - _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { - glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); - }); + if (_sparseInfo.sparse) { + for (uint16_t mip = _minMip; mip < newMinMip; ++mip) { + auto id = _id; + auto mipDimensions = _gpuObject.evalMipDimensions(mip); + _textureTransferHelper->queueExecution([id, mip, mipDimensions, maxFace] { + glTexturePageCommitmentEXT(id, mip, 0, 0, 0, mipDimensions.x, mipDimensions.y, maxFace, GL_FALSE); + }); - auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; - assert(deallocatedPages < _allocatedPages); - _allocatedPages -= deallocatedPages; + auto deallocatedPages = _sparseInfo.getPageCount(mipDimensions) * maxFace; + assert(deallocatedPages < _allocatedPages); + _allocatedPages -= deallocatedPages; + } + _minMip = newMinMip; + } else { + GLuint oldId = _id; + // Find the distance between the old min mip and the new one + uint16 mipDelta = newMinMip - _minMip; + _mipOffset += mipDelta; + const_cast(_maxMip) -= mipDelta; + auto newLevels = usedMipLevels(); + + // Create and setup the new texture (allocate) + glCreateTextures(_target, 1, &const_cast(_id)); + glTextureParameteri(_id, GL_TEXTURE_BASE_LEVEL, 0); + glTextureParameteri(_id, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); + Vec3u newDimensions = _gpuObject.evalMipDimensions(_mipOffset); + glTextureStorage2D(_id, newLevels, _internalFormat, newDimensions.x, newDimensions.y); + + // Copy the contents of the old texture to the new + GLuint fbo { 0 }; + glCreateFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + for (uint16 targetMip = _minMip; targetMip <= _maxMip; ++targetMip) { + uint16 sourceMip = targetMip + mipDelta; + Vec3u mipDimensions = _gpuObject.evalMipDimensions(targetMip + _mipOffset); + for (GLenum target : getFaceTargets(_target)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, oldId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTextureSubImage2D(_id, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); + glDeleteTextures(1, &oldId); } - _minMip = newMinMip; // Re-sync the sampler to force access to the new mip level syncSampler(); updateSize(); @@ -553,7 +584,7 @@ void GL45Texture::stripToMip(uint16_t newMinMip) { // Re-insert into the texture-by-mips map if appropriate mipLevels = usedMipLevels(); - if (_sparseInfo.sparse && mipLevels > 1 && _minMip < _sparseInfo.maxSparseLevel) { + if (mipLevels > 1 && (!_sparseInfo.sparse || _minMip < _sparseInfo.maxSparseLevel)) { Lock lock(texturesByMipCountsMutex); texturesByMipCounts[mipLevels].insert(this); } @@ -570,8 +601,9 @@ void GL45Texture::updateMips() { } void GL45Texture::derez() { - assert(_sparseInfo.sparse); - assert(_minMip < _sparseInfo.maxSparseLevel); + if (_sparseInfo.sparse) { + assert(_minMip < _sparseInfo.maxSparseLevel); + } assert(_minMip < _maxMip); assert(_transferrable); stripToMip(_minMip + 1); @@ -595,7 +627,7 @@ void GL45Backend::derezTextures() const { } qCDebug(gpugl45logging) << "Allowed texture memory " << Texture::getAllowedGPUMemoryUsage(); - qCDebug(gpugl45logging) << "Used texture memory " << Context::getTextureGPUMemoryUsage(); + qCDebug(gpugl45logging) << "Used texture memory " << (Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage()); GL45Texture* targetTexture = nullptr; { @@ -605,5 +637,5 @@ void GL45Backend::derezTextures() const { } lock.unlock(); targetTexture->derez(); - qCDebug(gpugl45logging) << "New Used texture memory " << Context::getTextureGPUMemoryUsage(); + qCDebug(gpugl45logging) << "New Used texture memory " << (Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage()); } diff --git a/libraries/gpu/src/gpu/Texture.cpp b/libraries/gpu/src/gpu/Texture.cpp index 3283f5a4d9..7a13c792cb 100755 --- a/libraries/gpu/src/gpu/Texture.cpp +++ b/libraries/gpu/src/gpu/Texture.cpp @@ -15,6 +15,7 @@ #include "Texture.h" #include +#include #include @@ -418,12 +419,18 @@ uint16 Texture::evalDimNumMips(uint16 size) { return 1 + (uint16) val; } +static const double LOG_2 = log(2.0); + +uint16 Texture::evalNumMips(const Vec3u& dimensions) { + double largerDim = glm::compMax(dimensions); + double val = log(largerDim) / LOG_2; + return 1 + (uint16)val; +} + // The number mips that the texture could have if all existed // = log2(max(width, height, depth)) uint16 Texture::evalNumMips() const { - double largerDim = std::max(std::max(_width, _height), _depth); - double val = log(largerDim)/log(2.0); - return 1 + (uint16) val; + return evalNumMips({ _width, _height, _depth }); } bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) { diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 1eacb46d77..7233c5b098 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -369,9 +369,12 @@ public: // = 1 + log2(max(width, height, depth)) uint16 evalNumMips() const; + static uint16 evalNumMips(const Vec3u& dimnsions); + // Eval the size that the mips level SHOULD have // not the one stored in the Texture static const uint MIN_DIMENSION = 1; + Vec3u evalMipDimensions(uint16 level) const; uint16 evalMipWidth(uint16 level) const { return std::max(_width >> level, 1); } uint16 evalMipHeight(uint16 level) const { return std::max(_height >> level, 1); } @@ -388,9 +391,9 @@ public: uint32 evalStoredMipFaceSize(uint16 level, const Element& format) const { return evalMipFaceNumTexels(level) * format.getSize(); } uint32 evalStoredMipSize(uint16 level, const Element& format) const { return evalMipNumTexels(level) * format.getSize(); } - uint32 evalTotalSize() const { + uint32 evalTotalSize(uint16 startingMip = 0) const { uint32 size = 0; - uint16 minMipLevel = minMip(); + uint16 minMipLevel = std::max(minMip(), startingMip); uint16 maxMipLevel = maxMip(); for (uint16 l = minMipLevel; l <= maxMipLevel; l++) { size += evalMipSize(l); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index d4a8322f8a..b152c4b4bf 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -599,7 +599,7 @@ protected: return; case Qt::Key_End: - gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(256)); + gpu::Texture::setAllowedGPUMemoryUsage(MB_TO_BYTES(64)); return;