mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Avoid regenerating the UI Atlas if unnecessary (faster resize)
This commit is contained in:
@@ -100,12 +100,12 @@ inline bool CompareByArea(const Data& lhs, const Data& rhs) {
|
||||
return lhs.w * lhs.h > rhs.w * rhs.h;
|
||||
}
|
||||
|
||||
std::vector<Data> Bucket::Resolve(int image_width, Image &dest) {
|
||||
std::vector<Data> Bucket::Resolve(int image_width, Image *dest) {
|
||||
// Place all the little images - whatever they are.
|
||||
// Uses greedy fill algorithm. Slow but works surprisingly well, CPUs are fast.
|
||||
ImageU8 masq;
|
||||
masq.resize(image_width, 1);
|
||||
dest.resize(image_width, 1);
|
||||
dest->resize(image_width, 1);
|
||||
std::sort(data.begin(), data.end(), CompareByArea);
|
||||
for (int i = 0; i < (int)data.size(); i++) {
|
||||
if ((i + 1) % 2000 == 0) {
|
||||
@@ -116,10 +116,10 @@ std::vector<Data> Bucket::Resolve(int image_width, Image &dest) {
|
||||
if (idx > 1 && idy > 1) {
|
||||
assert(idx <= image_width);
|
||||
for (int ty = 0; ty < 2047; ty++) {
|
||||
if (ty + idy + 1 > (int)dest.height()) {
|
||||
if (ty + idy + 1 > (int)dest->height()) {
|
||||
// Every 16 lines of new space needed, grow the image.
|
||||
masq.resize(image_width, ty + idy + 16);
|
||||
dest.resize(image_width, ty + idy + 16);
|
||||
dest->resize(image_width, ty + idy + 16);
|
||||
}
|
||||
// Brute force packing.
|
||||
int sz = (int)data[i].w;
|
||||
@@ -160,7 +160,7 @@ std::vector<Data> Bucket::Resolve(int image_width, Image &dest) {
|
||||
|
||||
// Actually copy the image data in place, after doing the layout.
|
||||
for (int i = 0; i < (int)data.size(); i++) {
|
||||
dest.copyfrom(images[i], data[i].sx, data[i].sy, data[i].redToWhiteAlpha);
|
||||
dest->copyfrom(images[i], data[i].sx, data[i].sy, data[i].redToWhiteAlpha);
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
@@ -46,6 +46,12 @@ struct Image {
|
||||
Image(Image &&) = default;
|
||||
Image &operator=(Image &&) = default;
|
||||
|
||||
void clear() {
|
||||
dat.clear();
|
||||
w = 0;
|
||||
h = 0;
|
||||
}
|
||||
|
||||
float scale = 1.0f;
|
||||
|
||||
int w = 0;
|
||||
@@ -118,7 +124,7 @@ struct Bucket {
|
||||
data.emplace_back(dat);
|
||||
}
|
||||
void AddImage(Image &&img, int id);
|
||||
std::vector<Data> Resolve(int image_width, Image &dest);
|
||||
std::vector<Data> Resolve(int image_width, Image *dest);
|
||||
};
|
||||
|
||||
AtlasImage ToAtlasImage(int id, std::string_view name, float tw, float th, const std::vector<Data> &results);
|
||||
|
||||
@@ -46,7 +46,7 @@ void UIContext::BeginFrame() {
|
||||
if (uitexture_) {
|
||||
uitexture_->Release();
|
||||
}
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::General, 1.0f / g_display.dpi_scale_x);
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::General, 1.0f / g_display.dpi_scale_x, atlasInvalid_);
|
||||
uitexture_ = data.texture;
|
||||
_dbg_assert_(uitexture_);
|
||||
ui_draw2d.SetAtlas(data.atlas);
|
||||
@@ -54,7 +54,7 @@ void UIContext::BeginFrame() {
|
||||
}
|
||||
|
||||
if (!fontTexture_) {
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::Font, 1.0f / g_display.dpi_scale_x);
|
||||
AtlasData data = atlasProvider_(draw_, AtlasChoice::Font, 1.0f / g_display.dpi_scale_x, false);
|
||||
fontTexture_ = data.texture;
|
||||
_dbg_assert_(fontTexture_);
|
||||
ui_draw2d.SetFontAtlas(data.atlas);
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ struct AtlasData {
|
||||
Draw::Texture *texture;
|
||||
};
|
||||
|
||||
typedef std::function<AtlasData(Draw::DrawContext *, AtlasChoice, float dpiScale)> UIAtlasProviderFunc;
|
||||
typedef std::function<AtlasData(Draw::DrawContext *, AtlasChoice, float dpiScale, bool invalidate)> UIAtlasProviderFunc;
|
||||
|
||||
class UIContext {
|
||||
public:
|
||||
|
||||
+26
-12
@@ -178,7 +178,7 @@ static bool IsImageID(std::string_view id) {
|
||||
return GetImageIndex(id) != -1;
|
||||
}
|
||||
|
||||
Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas, float dpiScale) {
|
||||
static bool GenerateUIAtlasImage(Atlas *atlas, float dpiScale, Image *dest) {
|
||||
Bucket bucket;
|
||||
|
||||
// Script fully read, now read images and rasterize the fonts.
|
||||
@@ -368,7 +368,6 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas, float dpiS
|
||||
INFO_LOG(Log::G3D, " - Added %zu images to bucket in %.2f ms", bucket.data.size(), addStart.ElapsedMs());
|
||||
|
||||
int image_width = 512;
|
||||
Image dest;
|
||||
|
||||
Instant bucketStart = Instant::Now();
|
||||
std::vector<Data> results = bucket.Resolve(image_width, dest);
|
||||
@@ -379,7 +378,7 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas, float dpiS
|
||||
std::vector<AtlasImage> genAtlasImages;
|
||||
genAtlasImages.reserve(ARRAY_SIZE(imageIDs));
|
||||
for (int i = 0; i < ARRAY_SIZE(imageIDs); i++) {
|
||||
genAtlasImages.push_back(ToAtlasImage(resultIds[i], imageIDs[i].id, (float)dest.width(), (float)dest.height(), results));
|
||||
genAtlasImages.push_back(ToAtlasImage(resultIds[i], imageIDs[i].id, (float)dest->width(), (float)dest->height(), results));
|
||||
}
|
||||
|
||||
atlas->Clear();
|
||||
@@ -389,21 +388,36 @@ Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas, float dpiS
|
||||
|
||||
// For debug, write out the atlas.
|
||||
if (SAVE_DEBUG_IMAGES) {
|
||||
dest.SavePNG("../ui_atlas_gen.png");
|
||||
dest->SavePNG("../ui_atlas_gen.png");
|
||||
}
|
||||
INFO_LOG(Log::G3D, "UI atlas generated in %.2f ms, size %dx%d with %zu images\n", svgStart.ElapsedMs(), dest->width(), dest->height(), genAtlasImages.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
// Then, create the texture too.
|
||||
static Image g_cachedUIAtlasImage;
|
||||
static float g_cachedDpiScale = 0.0f;
|
||||
|
||||
// The caller must cache the Atlas.
|
||||
Draw::Texture *GenerateUIAtlas(Draw::DrawContext *draw, Atlas *atlas, float dpiScale, bool invalidate) {
|
||||
if (g_cachedUIAtlasImage.IsEmpty() || dpiScale != g_cachedDpiScale || invalidate) {
|
||||
g_cachedUIAtlasImage.clear();
|
||||
if (!GenerateUIAtlasImage(atlas, dpiScale, &g_cachedUIAtlasImage)) {
|
||||
ERROR_LOG(Log::G3D, "Failed to generate UI atlas!");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
g_cachedDpiScale = dpiScale;
|
||||
|
||||
// Create the texture.
|
||||
Draw::TextureDesc desc{};
|
||||
desc.width = image_width;
|
||||
desc.height = dest.height();
|
||||
desc.width = g_cachedUIAtlasImage.width();
|
||||
desc.height = g_cachedUIAtlasImage.height();
|
||||
desc.depth = 1;
|
||||
desc.mipLevels = 1;
|
||||
desc.format = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
desc.type = Draw::TextureType::LINEAR2D;
|
||||
desc.initData.push_back((const u8 *)dest.data());
|
||||
desc.initData.push_back((const u8 *)g_cachedUIAtlasImage.data());
|
||||
desc.tag = "UIAtlas";
|
||||
|
||||
INFO_LOG(Log::G3D, "UI atlas generated in %.2f ms, size %dx%d with %zu images\n", svgStart.ElapsedMs(), desc.width, desc.height, genAtlasImages.size());
|
||||
return draw->CreateTexture(desc);
|
||||
}
|
||||
|
||||
@@ -418,7 +432,7 @@ static void LoadAtlasMetadata(Atlas &metadata, const char *filename) {
|
||||
delete[] atlas_data;
|
||||
}
|
||||
|
||||
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas, float dpiScale) {
|
||||
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas, float dpiScale, bool invalidate) {
|
||||
// Clamp the dpiScale to sane values. Might increase the range later.
|
||||
dpiScale = std::clamp(dpiScale, 0.5f, 4.0f);
|
||||
|
||||
@@ -426,7 +440,7 @@ AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas, float dpiSca
|
||||
case AtlasChoice::General:
|
||||
{
|
||||
// Generate the atlas from scratch.
|
||||
Draw::Texture *tex = GenerateUIAtlas(draw, &ui_atlas, dpiScale);
|
||||
Draw::Texture *tex = GenerateUIAtlas(draw, &ui_atlas, dpiScale, invalidate);
|
||||
return {&ui_atlas, tex};
|
||||
}
|
||||
case AtlasChoice::Font:
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@
|
||||
|
||||
const Atlas *GetFontAtlas();
|
||||
Atlas *GetUIAtlas();
|
||||
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas, float dpiScale);
|
||||
AtlasData AtlasProvider(Draw::DrawContext *draw, AtlasChoice atlas, float dpiScale, bool invalidate);
|
||||
|
||||
@@ -654,7 +654,7 @@ int GenerateFromScript(const char *script_file, const char *atlas_name, bool hig
|
||||
// Place things on the bitmap.
|
||||
printf("Resolving...\n");
|
||||
|
||||
std::vector<Data> results = bucket.Resolve(image_width, dest);
|
||||
std::vector<Data> results = bucket.Resolve(image_width, &dest);
|
||||
if (highcolor) {
|
||||
printf("Writing .ZIM %ix%i RGBA8888...\n", dest.width(), dest.height());
|
||||
dest.SaveZIM(image_name.c_str(), ZIM_RGBA8888 | ZIM_ZSTD_COMPRESSED);
|
||||
|
||||
Reference in New Issue
Block a user