perf(texture): cap and downscale theme images on load
Theme images were decoded straight to RGBA at their full source size and uploaded to a GPU texture as-is. A large or over-sized image wasted VRAM and could exceed the GL maximum texture size, and each code path also hand-managed the decoded pixel buffer with its own free calls. - Loading from file and from memory now goes through the shared size-aware decoder with a 2048-pixel cap on the long side. An image larger than that is downscaled with its aspect ratio kept; an image whose area is absurd is rejected; a small image is left untouched. - The decoder owns the pixel buffer for the life of the upload, so the manual free calls are gone. Tests cover downscaling a wide image to a 2048 long side with the aspect kept, rejecting an over-area image, and leaving an under-cap image as-is, plus the fit-dims math at the theme-art cap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+18
-20
@@ -34,29 +34,30 @@ static ID3D11Device* GetImGuiD3D11Device()
|
||||
#endif
|
||||
|
||||
#include "../util/logger.h"
|
||||
#include "image_decode_core.h"
|
||||
#include "image_decode_policy.h"
|
||||
|
||||
namespace {
|
||||
constexpr int kMaxLoadedTextureSide = 2048; // GL_MAX_TEXTURE_SIZE-safe; caps VRAM for theme art
|
||||
}
|
||||
|
||||
namespace dragonx {
|
||||
namespace util {
|
||||
|
||||
bool LoadTextureFromFile(const char* path, ImTextureID* outTexID, int* outW, int* outH)
|
||||
{
|
||||
std::vector<unsigned char> fileData;
|
||||
if (!ReadFileToBuffer(path, fileData)) {
|
||||
DEBUG_LOGF("LoadTextureFromFile: failed to read '%s'\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = 0, h = 0;
|
||||
unsigned char* pixels = DecodeImageRGBA(fileData.data(), fileData.size(), &w, &h);
|
||||
if (!pixels) {
|
||||
DEBUG_LOGF("LoadTextureFromFile: decode failed for '%s'\n", path);
|
||||
ImageLimits lim; lim.stillMax = kMaxLoadedTextureSide; lim.animMax = kMaxLoadedTextureSide;
|
||||
AnimFrames af;
|
||||
if (LoadAnimatedRGBAEx(path, 1, lim, nullptr, af) != ImageStatus::Ok || af.frames.empty()) {
|
||||
DEBUG_LOGF("LoadTextureFromFile: decode/probe failed for '%s'\n", path);
|
||||
return false;
|
||||
}
|
||||
const unsigned char* pixels = af.frames[0].data();
|
||||
const int w = af.w, h = af.h;
|
||||
|
||||
#ifdef DRAGONX_USE_DX11
|
||||
ID3D11Device* device = GetImGuiD3D11Device();
|
||||
if (!device) {
|
||||
FreeRawPixels(pixels);
|
||||
DEBUG_LOGF("LoadTextureFromFile: no D3D11 device available\n");
|
||||
return false;
|
||||
}
|
||||
@@ -77,7 +78,6 @@ bool LoadTextureFromFile(const char* path, ImTextureID* outTexID, int* outW, int
|
||||
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture);
|
||||
FreeRawPixels(pixels);
|
||||
if (FAILED(hr) || !texture) {
|
||||
DEBUG_LOGF("LoadTextureFromFile: CreateTexture2D failed for '%s'\n", path);
|
||||
return false;
|
||||
@@ -115,7 +115,6 @@ bool LoadTextureFromFile(const char* path, ImTextureID* outTexID, int* outW, int
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
FreeRawPixels(pixels);
|
||||
|
||||
*outTexID = (ImTextureID)(intptr_t)tex;
|
||||
*outW = w;
|
||||
@@ -133,17 +132,18 @@ bool LoadTextureFromMemory(const unsigned char* data, size_t dataSize,
|
||||
return false;
|
||||
}
|
||||
|
||||
int w = 0, h = 0;
|
||||
unsigned char* pixels = DecodeImageRGBA(data, dataSize, &w, &h);
|
||||
if (!pixels) {
|
||||
DEBUG_LOGF("LoadTextureFromMemory: decode failed\n");
|
||||
ImageLimits lim; lim.stillMax = kMaxLoadedTextureSide; lim.animMax = kMaxLoadedTextureSide;
|
||||
AnimFrames af;
|
||||
if (DecodeAnimatedFromMemory(data, dataSize, 1, lim, nullptr, af) != ImageStatus::Ok || af.frames.empty()) {
|
||||
DEBUG_LOGF("LoadTextureFromMemory: decode/probe failed\n");
|
||||
return false;
|
||||
}
|
||||
const unsigned char* pixels = af.frames[0].data();
|
||||
const int w = af.w, h = af.h;
|
||||
|
||||
#ifdef DRAGONX_USE_DX11
|
||||
ID3D11Device* device = GetImGuiD3D11Device();
|
||||
if (!device) {
|
||||
FreeRawPixels(pixels);
|
||||
DEBUG_LOGF("LoadTextureFromMemory: no D3D11 device available\n");
|
||||
return false;
|
||||
}
|
||||
@@ -164,7 +164,6 @@ bool LoadTextureFromMemory(const unsigned char* data, size_t dataSize,
|
||||
|
||||
ID3D11Texture2D* texture = nullptr;
|
||||
HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture);
|
||||
FreeRawPixels(pixels);
|
||||
if (FAILED(hr) || !texture) {
|
||||
DEBUG_LOGF("LoadTextureFromMemory: CreateTexture2D failed\n");
|
||||
return false;
|
||||
@@ -201,7 +200,6 @@ bool LoadTextureFromMemory(const unsigned char* data, size_t dataSize,
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
FreeRawPixels(pixels);
|
||||
|
||||
*outTexID = (ImTextureID)(intptr_t)tex;
|
||||
*outW = w;
|
||||
|
||||
Reference in New Issue
Block a user