KRTexture::getMemRequiredForLod and KRTexture::getLodData now interpret the passed lod level as a single mip rather than the mip chain starting at the passed lod level.
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, macos-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Has been cancelled
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Has been cancelled

Added KRTexture::getMemRequiredForLodRange
This commit is contained in:
2026-03-19 00:54:36 -07:00
parent d560ba46c7
commit b205fe2e0d
12 changed files with 77 additions and 187 deletions

View File

@@ -126,7 +126,7 @@ void KRTexture::resize(int lod)
if (m_new_lod != target_lod || m_handles.empty()) { if (m_new_lod != target_lod || m_handles.empty()) {
assert(m_newTextureMemUsed == 0); assert(m_newTextureMemUsed == 0);
m_newTextureMemUsed = getMemRequiredForLod(target_lod); m_newTextureMemUsed = getMemRequiredForLodRange(target_lod);
getContext().getTextureManager()->memoryChanged(m_newTextureMemUsed); getContext().getTextureManager()->memoryChanged(m_newTextureMemUsed);
getContext().getTextureManager()->addMemoryTransferredThisFrame(m_newTextureMemUsed); getContext().getTextureManager()->addMemoryTransferredThisFrame(m_newTextureMemUsed);
@@ -294,6 +294,8 @@ bool KRTexture::allocate(KRDevice& device, int target_lod, VkImageCreateFlags im
#endif #endif
) )
{ {
int min_mip = KRMIN(target_lod, m_lod_count - 1);
int mip_count = m_lod_count - min_mip;
hydra::Vector3i dimensions = getDimensions() / (1 << target_lod); hydra::Vector3i dimensions = getDimensions() / (1 << target_lod);
VkImageCreateInfo imageInfo{}; VkImageCreateInfo imageInfo{};
@@ -302,7 +304,7 @@ bool KRTexture::allocate(KRDevice& device, int target_lod, VkImageCreateFlags im
imageInfo.extent.width = static_cast<uint32_t>(dimensions.x); imageInfo.extent.width = static_cast<uint32_t>(dimensions.x);
imageInfo.extent.height = static_cast<uint32_t>(dimensions.y); imageInfo.extent.height = static_cast<uint32_t>(dimensions.y);
imageInfo.extent.depth = static_cast<uint32_t>(dimensions.z); imageInfo.extent.depth = static_cast<uint32_t>(dimensions.z);
imageInfo.mipLevels = KRMAX(m_lod_count - target_lod, 1); imageInfo.mipLevels = mip_count;
imageInfo.arrayLayers = 1; imageInfo.arrayLayers = 1;
imageInfo.format = getFormat(); imageInfo.format = getFormat();
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
@@ -330,3 +332,16 @@ bool KRTexture::allocate(KRDevice& device, int target_lod, VkImageCreateFlags im
#endif #endif
return true; return true;
} }
long KRTexture::getMemRequiredForLodRange(int min_lod, int max_lod /* = 0xff */)
{
int target_max_lod = KRMIN(max_lod, m_lod_count - 1);
int target_min_lod = KRMIN(min_lod, m_lod_count - 1);
long memRequired = 0;
for (int lod = target_min_lod; lod <= target_max_lod; lod++) {
memRequired +=
(lod);
}
return memRequired;
}

View File

@@ -53,6 +53,7 @@ public:
virtual long getReferencedMemSize(); virtual long getReferencedMemSize();
virtual long getMemRequiredForLod(int lod) = 0; virtual long getMemRequiredForLod(int lod) = 0;
long getMemRequiredForLodRange(int min_lod, int max_lod = 0xff);
virtual void resize(int lod); virtual void resize(int lod);
long getLastFrameUsed(); long getLastFrameUsed();

View File

@@ -53,7 +53,7 @@ bool KRTexture2D::createGPUTexture(int lod)
} }
Vector3i dimensions = getDimensions(); Vector3i dimensions = getDimensions();
size_t bufferSize = getMemRequiredForLod(lod); size_t bufferSize = getMemRequiredForLodRange(lod);
void* buffer = malloc(bufferSize); void* buffer = malloc(bufferSize);
if (!getLodData(buffer, lod)) { if (!getLodData(buffer, lod)) {
@@ -85,6 +85,9 @@ bool KRTexture2D::createGPUTexture(int lod)
break; break;
} }
int min_mip = KRMIN(target_lod, m_lod_count - 1);
int mip_count = m_lod_count - min_mip;
VkImageViewCreateInfo viewInfo{}; VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture.image; viewInfo.image = texture.image;
@@ -92,7 +95,7 @@ bool KRTexture2D::createGPUTexture(int lod)
viewInfo.format = getFormat(); viewInfo.format = getFormat();
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0; viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = KRMAX(m_lod_count - target_lod, 1); viewInfo.subresourceRange.levelCount = mip_count;
viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
VkResult res = vkCreateImageView(device.m_logicalDevice, &viewInfo, nullptr, &texture.fullImageView); VkResult res = vkCreateImageView(device.m_logicalDevice, &viewInfo, nullptr, &texture.fullImageView);
@@ -101,24 +104,32 @@ bool KRTexture2D::createGPUTexture(int lod)
break; break;
} }
VkBufferImageCopy region{}; std::vector<VkBufferImageCopy> regions;
region.bufferOffset = 0; regions.resize(mip_count, VkBufferImageCopy{});
region.bufferRowLength = 0; int bufferOffset = 0;
region.bufferImageHeight = 0; for (int mip = min_mip; mip < min_mip + mip_count - 1; mip++) {
VkBufferImageCopy& region = regions[mip];
region.bufferOffset = bufferOffset;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0; region.imageSubresource.mipLevel = mip - min_mip;
region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1; region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 }; region.imageOffset = { 0, 0, 0 };
region.imageExtent = { region.imageExtent = {
(unsigned int)dimensions.x, (unsigned int)dimensions.x,
(unsigned int)dimensions.y, (unsigned int)dimensions.y,
(unsigned int)dimensions.z (unsigned int)dimensions.z
}; };
regions.push_back(region);
device.streamUpload((void*)buffer, bufferSize, texture.image, &region, 1); bufferOffset += getMemRequiredForLod(mip);
}
device.streamUpload((void*)buffer, bufferSize, texture.image, regions.data(), regions.size());
} }
delete buffer; delete buffer;

View File

@@ -93,7 +93,7 @@ bool KRTextureCube::createGPUTexture(int lod)
size_t bufferSizes[6] = {}; size_t bufferSizes[6] = {};
void* buffers[6] = {}; void* buffers[6] = {};
for (int i = 0; i < 6; i++) { for (int i = 0; i < 6; i++) {
bufferSizes[i] = getMemRequiredForLod(lod); bufferSizes[i] = getMemRequiredForLodRange(lod);
buffers[i] = malloc(bufferSizes[i]); buffers[i] = malloc(bufferSizes[i]);
m_textures[i]->getLodData(buffers[i], lod); m_textures[i]->getLodData(buffers[i], lod);
} }
@@ -170,7 +170,6 @@ long KRTextureCube::getMemRequiredForLod(int lod)
return memoryRequired; return memoryRequired;
} }
void KRTextureCube::requestResidency(float lodCoverage, texture_usage_t textureUsage) void KRTextureCube::requestResidency(float lodCoverage, texture_usage_t textureUsage)
{ {
KRTexture::requestResidency(lodCoverage, textureUsage); KRTexture::requestResidency(lodCoverage, textureUsage);

View File

@@ -82,7 +82,7 @@ KRTextureKTX::KRTextureKTX(KRContext& context, Block* data, std::string name) :
} }
} }
m_lod_count = (int)KRMAX(m_header.numberOfMipmapLevels, 1) - 1; m_lod_count = (int)KRMAX(m_header.numberOfMipmapLevels, 1);
} }
KRTextureKTX::KRTextureKTX(KRContext& context, std::string name, unsigned int internal_format, unsigned int base_internal_format, int width, int height, const std::list<Block*>& blocks) : KRTexture2D(context, new Block(), name) KRTextureKTX::KRTextureKTX(KRContext& context, std::string name, unsigned int internal_format, unsigned int base_internal_format, int width, int height, const std::list<Block*>& blocks) : KRTexture2D(context, new Block(), name)
@@ -119,7 +119,7 @@ KRTextureKTX::KRTextureKTX(KRContext& context, std::string name, unsigned int in
KRTextureKTX::~KRTextureKTX() KRTextureKTX::~KRTextureKTX()
{ {
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) { for (std::vector<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr; Block* block = *itr;
delete block; delete block;
} }
@@ -362,74 +362,20 @@ VkFormat KRTextureKTX::getFormat() const
long KRTextureKTX::getMemRequiredForLod(int lod) long KRTextureKTX::getMemRequiredForLod(int lod)
{ {
// Determine how much memory will be consumed int target_lod = KRMIN(lod, m_lod_count - 1);
return (long)m_blocks[target_lod]->getSize();
long memoryRequired = 0;
int level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (level >= lod) {
memoryRequired += (long)block->getSize();
}
level++;
}
return memoryRequired;
} }
bool KRTextureKTX::getLodData(void* buffer, int lod) bool KRTextureKTX::getLodData(void* buffer, int lod)
{ {
unsigned char* converted_image = (unsigned char*)buffer;
int target_lod = KRMIN(lod, m_lod_count);
if (m_blocks.size() == 0) { if (m_blocks.size() == 0) {
return false; return false;
} }
// Determine how much memory will be consumed int target_lod = KRMIN(lod, m_lod_count - 1);
long memoryRequired = 0; m_blocks[target_lod]->copy(buffer);
long memoryTransferred = 0;
// Upload texture data
int level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (level >= target_lod) {
/*
if (width > current_lod_max_dim) {
current_lod_max_dim = width;
}
if (height > current_lod_max_dim) {
current_lod_max_dim = height;
}
*/
block->lock();
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, (unsigned int)m_header.glInternalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
block->unlock();
memoryTransferred += (long)block->getSize(); // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)block->getSize();
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
}
level++;
}
return true; return true;
} }
std::string KRTextureKTX::getExtension() std::string KRTextureKTX::getExtension()

View File

@@ -52,7 +52,7 @@ public:
protected: protected:
std::list<Block*> m_blocks; std::vector<Block*> m_blocks;
typedef struct _KTXHeader typedef struct _KTXHeader
{ {

View File

@@ -68,7 +68,7 @@ KRTextureKTX2::KRTextureKTX2(KRContext& context, Block* data, std::string name)
if (height < 1) { if (height < 1) {
height = 1; height = 1;
} }
m_lod_count = (int)KRMAX(m_header.levelCount, 1) - 1; m_lod_count = (int)KRMAX(m_header.levelCount, 1);
} }
KRTextureKTX2::~KRTextureKTX2() KRTextureKTX2::~KRTextureKTX2()
@@ -82,63 +82,26 @@ Vector3i KRTextureKTX2::getDimensions() const
long KRTextureKTX2::getMemRequiredForLod(int lod) long KRTextureKTX2::getMemRequiredForLod(int lod)
{ {
int target_lod = KRMIN(lod, m_lod_count); int target_lod = KRMIN(lod, m_lod_count - 1);
// Determine how much memory will be consumed KTX2LevelIndex levelIndex;
long memoryRequired = 0; m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * target_lod, sizeof(KTX2LevelIndex));
int level = 0; return (long)levelIndex.byteLength;
for (__uint32_t level = 0; level < m_header.levelCount; level++) {
KTX2LevelIndex levelIndex;
m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * level, sizeof(KTX2LevelIndex));
if (level >= target_lod) {
memoryRequired += (long)levelIndex.byteLength;
}
level++;
}
return memoryRequired;
} }
bool KRTextureKTX2::getLodData(void* buffer, int lod) bool KRTextureKTX2::getLodData(void* buffer, int lod)
{ {
unsigned char* converted_image = (unsigned char*)buffer; unsigned char* converted_image = (unsigned char*)buffer;
int target_lod = KRMIN(lod, m_lod_count); int target_lod = KRMIN(lod, m_lod_count - 1);
// Determine how much memory will be consumed KTX2LevelIndex levelIndex;
long memoryRequired = 0; m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * target_lod, sizeof(KTX2LevelIndex));
long memoryTransferred = 0;
int level = 0; // TODO - Implement copy of buffer data
assert(false);
for (__uint32_t level = 0; level < m_header.levelCount; level++) {
KTX2LevelIndex levelIndex;
m_pData->copy(&levelIndex, sizeof(m_header) + sizeof(KTX2LevelIndex) * level, sizeof(KTX2LevelIndex));
if (level >= target_lod) {
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, (unsigned int)m_header.glInternalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
memoryTransferred += (long)levelIndex.byteLength; // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)levelIndex.byteLength;
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
}
level++;
}
return true; return true;
} }
std::string KRTextureKTX2::getExtension() std::string KRTextureKTX2::getExtension()

View File

@@ -303,7 +303,7 @@ void KRTextureManager::balanceTextureMemory(long& memoryRemaining, long& memoryR
for (auto itr = m_activeTextures_streamer.begin(); itr != m_activeTextures_streamer.end(); itr++) { for (auto itr = m_activeTextures_streamer.begin(); itr != m_activeTextures_streamer.end(); itr++) {
KRTexture* texture = (*itr).second; KRTexture* texture = (*itr).second;
int min_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_LQ_LOD, texture->getLodCount()); int min_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_LQ_LOD, texture->getLodCount());
long minLodMem = texture->getMemRequiredForLod(min_lod_level); long minLodMem = texture->getMemRequiredForLodRange(min_lod_level);
memoryRemaining -= minLodMem; memoryRemaining -= minLodMem;
if (memoryRemainingThisFrame > minLodMem && (texture->getNewLod() != -1 || texture->getNewLod() > min_lod_level)) { if (memoryRemainingThisFrame > minLodMem && (texture->getNewLod() != -1 || texture->getNewLod() > min_lod_level)) {
@@ -333,8 +333,8 @@ void KRTextureManager::balanceTextureMemory(long& memoryRemaining, long& memoryR
KRTexture* texture = (*itr).second; KRTexture* texture = (*itr).second;
int min_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_LQ_LOD, texture->getLodCount()); int min_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_LQ_LOD, texture->getLodCount());
int target_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_HQ_LOD + mip_drop, texture->getLodCount()); int target_lod_level = KRMIN(getContext().KRENGINE_TEXTURE_HQ_LOD + mip_drop, texture->getLodCount());
long targetMem = texture->getMemRequiredForLod(target_lod_level); long targetMem = texture->getMemRequiredForLodRange(target_lod_level);
long additionalMemRequired = targetMem - texture->getMemRequiredForLod(min_lod_level); long additionalMemRequired = targetMem - texture->getMemRequiredForLodRange(min_lod_level);
memoryRemainingThisMip -= additionalMemRequired; memoryRemainingThisMip -= additionalMemRequired;
memoryRemaining -= additionalMemRequired; memoryRemaining -= additionalMemRequired;
if (memoryRemainingThisMip > 0 && memoryRemainingThisFrame > targetMem) { if (memoryRemainingThisMip > 0 && memoryRemainingThisFrame > targetMem) {
@@ -347,12 +347,12 @@ void KRTextureManager::balanceTextureMemory(long& memoryRemaining, long& memoryR
} else if (current_lod_level == (target_lod_level + 2)) { } else if (current_lod_level == (target_lod_level + 2)) {
// We are two lod levels away from the target. // We are two lod levels away from the target.
// Advance to the lod level inbetween. // Advance to the lod level inbetween.
memoryRemainingThisFrame -= texture->getMemRequiredForLod(target_lod_level + 1); memoryRemainingThisFrame -= texture->getMemRequiredForLodRange(target_lod_level + 1);
texture->resize(target_lod_level + 1); texture->resize(target_lod_level + 1);
} else if (current_lod_level > (target_lod_level + 2)) { } else if (current_lod_level > (target_lod_level + 2)) {
// We are more than two lod levels away from the target. // We are more than two lod levels away from the target.
// Advance directly to the lod 2 levels away from the target. // Advance directly to the lod 2 levels away from the target.
memoryRemainingThisFrame -= texture->getMemRequiredForLod(target_lod_level + 2); memoryRemainingThisFrame -= texture->getMemRequiredForLodRange(target_lod_level + 2);
texture->resize(target_lod_level + 2); texture->resize(target_lod_level + 2);
} }
} }

View File

@@ -77,7 +77,7 @@ KRTexturePNG::KRTexturePNG(KRContext& context, Block* data, std::string name) :
m_dimensions.x = SWAP_4(pHeader->chunk_IHDR.width); m_dimensions.x = SWAP_4(pHeader->chunk_IHDR.width);
m_dimensions.y = SWAP_4(pHeader->chunk_IHDR.height); m_dimensions.y = SWAP_4(pHeader->chunk_IHDR.height);
m_lod_count = 0; // Mipmaps not yet supported for PNG images m_lod_count = 1; // Mipmaps not yet supported for PNG images
switch (pHeader->chunk_IHDR.colorType) { switch (pHeader->chunk_IHDR.colorType) {
case 0: case 0:
// greyscale // greyscale

View File

@@ -142,7 +142,7 @@ KRTexturePVR::KRTexturePVR(KRContext& context, Block* data, std::string name) :
KRTexturePVR::~KRTexturePVR() KRTexturePVR::~KRTexturePVR()
{ {
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) { for (std::vector<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr; Block* block = *itr;
delete block; delete block;
} }
@@ -174,64 +174,19 @@ VkFormat KRTexturePVR::getFormat() const
long KRTexturePVR::getMemRequiredForLod(int lod) long KRTexturePVR::getMemRequiredForLod(int lod)
{ {
// Determine how much memory will be consumed int target_lod = KRMIN(lod, m_lod_count - 1);
long memoryRequired = 0; return m_blocks[target_lod]->getSize();
int level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (level >= lod) {
memoryRequired += (long)block->getSize();
}
level++;
}
return memoryRequired;
} }
bool KRTexturePVR::getLodData(void* buffer, int lod) bool KRTexturePVR::getLodData(void* buffer, int lod)
{ {
int target_lod = KRMIN(lod, m_lod_count);
if (m_blocks.size() == 0) { if (m_blocks.size() == 0) {
return false; return false;
} }
// Determine how much memory will be consumed int target_lod = KRMIN(lod, m_lod_count - 1);
int width = m_iWidth; m_blocks[target_lod]->copy(buffer);
int height = m_iHeight;
long memoryRequired = 0;
long memoryTransferred = 0;
// Upload texture data
int level = 0;
for (std::list<Block*>::iterator itr = m_blocks.begin(); itr != m_blocks.end(); itr++) {
Block* block = *itr;
if (level >= target_lod) {
block->lock();
/*
* TODO - Vulkan Refactoring
GLDEBUG(glCompressedTexImage2D(target, destination_level, m_internalFormat, width, height, 0, (int)block->getSize(), block->getStart()));
*/
block->unlock();
memoryTransferred += (long)block->getSize(); // memoryTransferred does not include throughput of mipmap levels copied through glCopyTextureLevelsAPPLE
memoryRequired += (long)block->getSize();
//
// err = glGetError();
// if (err != GL_NO_ERROR) {
// assert(false);
// return false;
// }
//
}
level++;
}
return true; return true;
} }
std::string KRTexturePVR::getExtension() std::string KRTexturePVR::getExtension()

View File

@@ -54,5 +54,5 @@ protected:
unsigned int m_internalFormat; unsigned int m_internalFormat;
bool m_bHasAlpha; bool m_bHasAlpha;
std::list<mimir::Block*> m_blocks; std::vector<mimir::Block*> m_blocks;
}; };

View File

@@ -79,7 +79,7 @@ KRTextureTGA::KRTextureTGA(KRContext& context, Block* data, std::string name) :
m_dimensions.x = pHeader->width; m_dimensions.x = pHeader->width;
m_dimensions.y = pHeader->height; m_dimensions.y = pHeader->height;
m_lod_count = 0; // Mipmaps not yet supported for TGA images m_lod_count = 1; // Mipmaps not yet supported for TGA images
switch (pHeader->imagetype) { switch (pHeader->imagetype) {
case 2: // rgb case 2: // rgb
case 10: // rgb + rle case 10: // rgb + rle