Merge branch 'stenzek:master' into master

pull/3784/head
fakkuyuu 1 month ago committed by GitHub
commit 84d3e2c593
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -12,6 +12,7 @@ add_executable(common-tests
gsvector_yuvtorgb_test.cpp
hash_tests.cpp
heap_array_tests.cpp
lru_cache_tests.cpp
misc_tests.cpp
path_tests.cpp
rectangle_tests.cpp

@ -8,6 +8,7 @@
<ClCompile Include="file_system_tests.cpp" />
<ClCompile Include="gsvector_tests.cpp" />
<ClCompile Include="heap_array_tests.cpp" />
<ClCompile Include="lru_cache_tests.cpp" />
<ClCompile Include="misc_tests.cpp" />
<ClCompile Include="path_tests.cpp" />
<ClCompile Include="rectangle_tests.cpp" />
@ -39,4 +40,4 @@
</Link>
</ItemDefinitionGroup>
<Import Project="..\..\dep\vsprops\Targets.props" />
</Project>
</Project>

@ -13,7 +13,8 @@
<ClCompile Include="small_string_tests.cpp" />
<ClCompile Include="binary_reader_writer_tests.cpp" />
<ClCompile Include="heap_array_tests.cpp" />
<ClCompile Include="lru_cache_tests.cpp" />
<ClCompile Include="string_pool_tests.cpp" />
<ClCompile Include="misc_tests.cpp" />
</ItemGroup>
</Project>
</Project>

@ -0,0 +1,252 @@
// SPDX-FileCopyrightText: 2019-2026 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "common/lru_cache.h"
#include <gtest/gtest.h>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace {
struct TrackedValue
{
TrackedValue(int value_, int* destruction_count_) : value(value_), destruction_count(destruction_count_) {}
~TrackedValue() { (*destruction_count)++; }
int value;
int* destruction_count;
};
struct PoolDeleter
{
void operator()(std::unique_ptr<TrackedValue>&& value) const { pool->push_back(std::move(value)); }
std::vector<std::unique_ptr<TrackedValue>>* pool;
};
} // namespace
TEST(LRUCache, InsertLookupClearAndCapacityAccessors)
{
LRUCache<int, int> cache(2);
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_EQ(cache.GetMaxCapacity(), 2u);
EXPECT_EQ(cache.Lookup(1), nullptr);
int* value = cache.Insert(1, 10);
ASSERT_NE(value, nullptr);
EXPECT_EQ(*value, 10);
EXPECT_EQ(cache.GetSize(), 1u);
value = cache.Lookup(1);
ASSERT_NE(value, nullptr);
EXPECT_EQ(*value, 10);
cache.Clear();
EXPECT_EQ(cache.GetSize(), 0u);
EXPECT_EQ(cache.Lookup(1), nullptr);
}
TEST(LRUCache, LookupUpdatesLeastRecentlyUsedOrder)
{
LRUCache<int, int> cache(2);
cache.Insert(1, 10);
cache.Insert(2, 20);
ASSERT_NE(cache.Lookup(1), nullptr);
cache.Insert(3, 30);
EXPECT_NE(cache.Lookup(1), nullptr);
EXPECT_EQ(cache.Lookup(2), nullptr);
EXPECT_NE(cache.Lookup(3), nullptr);
}
TEST(LRUCache, LookupDistinguishesMissingRawPointerFromCachedNullPointer)
{
LRUCache<int, int*> cache(1);
EXPECT_EQ(cache.Lookup(1), nullptr);
int** inserted_value = cache.Insert(1, nullptr);
ASSERT_NE(inserted_value, nullptr);
EXPECT_EQ(*inserted_value, nullptr);
int** cached_value = cache.Lookup(1);
ASSERT_NE(cached_value, nullptr);
EXPECT_EQ(*cached_value, nullptr);
}
TEST(LRUCache, SetMaxCapacityEvictsLeastRecentlyUsedItems)
{
LRUCache<int, int> cache(3);
cache.Insert(1, 10);
cache.Insert(2, 20);
cache.Insert(3, 30);
ASSERT_NE(cache.Lookup(1), nullptr);
cache.SetMaxCapacity(2);
EXPECT_EQ(cache.GetMaxCapacity(), 2u);
EXPECT_EQ(cache.GetSize(), 2u);
EXPECT_NE(cache.Lookup(1), nullptr);
EXPECT_EQ(cache.Lookup(2), nullptr);
EXPECT_NE(cache.Lookup(3), nullptr);
cache.SetMaxCapacity(0);
EXPECT_EQ(cache.GetMaxCapacity(), 0u);
EXPECT_EQ(cache.GetSize(), 0u);
}
TEST(LRUCache, EvictRemovesRequestedNumberOfItems)
{
LRUCache<int, int> cache(4);
cache.Insert(1, 10);
cache.Insert(2, 20);
cache.Insert(3, 30);
cache.Insert(4, 40);
ASSERT_NE(cache.Lookup(1), nullptr);
cache.Evict(0);
EXPECT_EQ(cache.GetSize(), 4u);
cache.Evict(2);
EXPECT_EQ(cache.GetSize(), 2u);
EXPECT_NE(cache.Lookup(1), nullptr);
EXPECT_EQ(cache.Lookup(2), nullptr);
EXPECT_EQ(cache.Lookup(3), nullptr);
EXPECT_NE(cache.Lookup(4), nullptr);
cache.Evict(10);
EXPECT_EQ(cache.GetSize(), 0u);
cache.Evict();
EXPECT_EQ(cache.GetSize(), 0u);
}
TEST(LRUCache, RemoveAndRemoveMatchingItems)
{
LRUCache<int, int> cache(5);
for (int i = 1; i <= 5; i++)
cache.Insert(i, i * 10);
EXPECT_FALSE(cache.Remove(6));
EXPECT_TRUE(cache.Remove(1));
EXPECT_FALSE(cache.Remove(1));
EXPECT_EQ(cache.RemoveMatchingItems([](int key) { return (key % 2) == 0; }), 2u);
EXPECT_EQ(cache.RemoveMatchingItems([](int) { return false; }), 0u);
EXPECT_EQ(cache.GetSize(), 2u);
EXPECT_NE(cache.Lookup(3), nullptr);
EXPECT_NE(cache.Lookup(5), nullptr);
}
TEST(LRUCache, ManualEvictionCanTemporarilyExceedCapacity)
{
LRUCache<int, int> cache(2, true);
cache.Insert(1, 10);
cache.Insert(2, 20);
cache.Insert(3, 30);
EXPECT_EQ(cache.GetSize(), 3u);
ASSERT_NE(cache.Lookup(1), nullptr);
cache.ManualEvict();
EXPECT_EQ(cache.GetSize(), 2u);
EXPECT_NE(cache.Lookup(1), nullptr);
EXPECT_EQ(cache.Lookup(2), nullptr);
EXPECT_NE(cache.Lookup(3), nullptr);
cache.SetManualEvict(true);
cache.Insert(4, 40);
EXPECT_EQ(cache.GetSize(), 3u);
ASSERT_NE(cache.Lookup(1), nullptr);
cache.SetManualEvict(false);
EXPECT_EQ(cache.GetSize(), 2u);
EXPECT_NE(cache.Lookup(1), nullptr);
EXPECT_EQ(cache.Lookup(3), nullptr);
EXPECT_NE(cache.Lookup(4), nullptr);
}
TEST(LRUCache, ApplyVisitsAndCanModifyEveryItem)
{
LRUCache<int, int> cache(3);
cache.Insert(3, 30);
cache.Insert(1, 10);
cache.Insert(2, 20);
std::vector<int> visited_keys;
cache.Apply([&visited_keys](const int& key, int& value) {
visited_keys.push_back(key);
value += key;
});
EXPECT_EQ(visited_keys, (std::vector<int>{1, 2, 3}));
EXPECT_EQ(*cache.Lookup(1), 11);
EXPECT_EQ(*cache.Lookup(2), 22);
EXPECT_EQ(*cache.Lookup(3), 33);
}
TEST(LRUCache, StringKeysSupportHeterogeneousLookupAndRemoval)
{
LRUCache<std::string, int> cache(2);
cache.Insert("first", 1);
cache.Insert("second", 2);
const std::string_view first_key = "first";
ASSERT_NE(cache.Lookup(first_key), nullptr);
EXPECT_EQ(*cache.Lookup(first_key), 1);
EXPECT_TRUE(cache.Remove(first_key));
EXPECT_EQ(cache.Lookup(first_key), nullptr);
EXPECT_EQ(cache.GetSize(), 1u);
}
TEST(LRUCache, DefaultDeleterDeletesRawPointers)
{
int destruction_count = 0;
{
LRUCache<int, TrackedValue*> cache(1);
cache.Insert(1, new TrackedValue(1, &destruction_count));
cache.Insert(2, new TrackedValue(2, &destruction_count));
EXPECT_EQ(destruction_count, 1);
EXPECT_TRUE(cache.Remove(2));
EXPECT_EQ(destruction_count, 2);
}
EXPECT_EQ(destruction_count, 2);
}
TEST(LRUCache, DefaultDeleterAllowsUniquePointersToDestroyNormally)
{
int destruction_count = 0;
{
LRUCache<int, std::unique_ptr<TrackedValue>> cache(1);
cache.Insert(1, std::make_unique<TrackedValue>(1, &destruction_count));
cache.Insert(1, std::make_unique<TrackedValue>(2, &destruction_count));
EXPECT_EQ(cache.GetSize(), 1u);
EXPECT_EQ(destruction_count, 1);
}
EXPECT_EQ(destruction_count, 2);
}
TEST(LRUCache, CustomDeleterCanPoolUniquePointers)
{
int destruction_count = 0;
std::vector<std::unique_ptr<TrackedValue>> pool;
{
LRUCache<int, std::unique_ptr<TrackedValue>, PoolDeleter> cache(2, false, PoolDeleter{&pool});
cache.Insert(1, std::make_unique<TrackedValue>(1, &destruction_count));
cache.Insert(2, std::make_unique<TrackedValue>(2, &destruction_count));
cache.Insert(1, std::make_unique<TrackedValue>(3, &destruction_count));
ASSERT_EQ(pool.size(), 1u);
EXPECT_EQ(pool.front()->value, 1);
EXPECT_NE(cache.Lookup(2), nullptr);
EXPECT_EQ(destruction_count, 0);
cache.Evict();
cache.Clear();
EXPECT_EQ(pool.size(), 3u);
EXPECT_EQ(destruction_count, 0);
}
pool.clear();
EXPECT_EQ(destruction_count, 3);
}

@ -3,10 +3,24 @@
#pragma once
#include "heterogeneous_containers.h"
#include "types.h"
#include <cstdint>
#include <map>
#include <memory>
#include <type_traits>
#include <utility>
template<class K, class V>
template<class V>
struct LRUCacheDefaultDeleter
{
void operator()(V&& value) const noexcept
{
if constexpr (std::is_pointer_v<V>)
std::default_delete<std::remove_pointer_t<V>>{}(value);
}
};
template<class K, class V, class Deleter = LRUCacheDefaultDeleter<V>>
class LRUCache
{
using CounterType = std::uint64_t;
@ -20,16 +34,24 @@ class LRUCache
using MapType = std::conditional_t<std::is_same_v<K, std::string>, StringMap<Item>, std::map<K, Item>>;
public:
LRUCache(std::size_t max_capacity = 16, bool manual_evict = false)
: m_max_capacity(max_capacity), m_manual_evict(manual_evict)
LRUCache(std::size_t max_capacity = 16, bool manual_evict = false, Deleter deleter = Deleter())
: m_max_capacity(max_capacity), m_deleter(std::move(deleter)), m_manual_evict(manual_evict)
{
}
~LRUCache()
{
Clear();
}
~LRUCache() = default;
std::size_t GetSize() const { return m_items.size(); }
std::size_t GetMaxCapacity() const { return m_max_capacity; }
void Clear() { m_items.clear(); }
void Clear()
{
for (auto it = m_items.rbegin(); it != m_items.rend(); ++it)
m_deleter(std::move(it->second.value));
m_items.clear();
}
void SetMaxCapacity(std::size_t capacity)
{
@ -51,17 +73,18 @@ public:
V* Insert(K key, V value)
{
ShrinkForNewItem();
auto iter = m_items.find(key);
if (iter != m_items.end())
{
m_deleter(std::move(iter->second.value));
iter->second.value = std::move(value);
iter->second.last_access = ++m_last_counter;
return &iter->second.value;
}
else
{
ShrinkForNewItem();
Item it;
it.last_access = ++m_last_counter;
it.value = std::move(value);
@ -80,6 +103,7 @@ public:
if (lowest == m_items.end() || iter->second.last_access < lowest->second.last_access)
lowest = iter;
}
m_deleter(std::move(lowest->second.value));
m_items.erase(lowest);
count--;
}
@ -93,6 +117,7 @@ public:
{
if (pred(iter->first))
{
m_deleter(std::move(iter->second.value));
iter = m_items.erase(iter);
removed_count++;
}
@ -110,6 +135,7 @@ public:
auto iter = m_items.find(key);
if (iter == m_items.end())
return false;
m_deleter(std::move(iter->second.value));
m_items.erase(iter);
return true;
}
@ -136,7 +162,7 @@ public:
private:
void ShrinkForNewItem()
{
if (m_items.size() < m_max_capacity)
if (m_manual_evict || m_items.size() < m_max_capacity)
return;
Evict(m_items.size() - (m_max_capacity - 1));
@ -145,5 +171,6 @@ private:
MapType m_items;
CounterType m_last_counter = 0;
std::size_t m_max_capacity = 0;
NO_UNIQUE_ADDRESS Deleter m_deleter;
bool m_manual_evict = false;
};
};

@ -1087,7 +1087,8 @@ bool Bus::InjectCPE(std::span<const u8> buffer, bool set_pc, Error* error)
case 0x02:
{
// Run address, ignored
DEV_LOG("Ignoring run address 0x{:X}", reader.ReadU32());
const u32 run_address = reader.ReadU32();
DEV_LOG("Ignoring run address 0x{:X}", run_address);
}
break;
@ -1131,14 +1132,16 @@ bool Bus::InjectCPE(std::span<const u8> buffer, bool set_pc, Error* error)
case 0x07:
{
// Select workspace
DEV_LOG("Ignoring set workspace 0x{:X}", reader.ReadU32());
const u32 workspace = reader.ReadU32();
DEV_LOG("Ignoring set workspace 0x{:X}", workspace);
}
break;
case 0x08:
{
// Select unit
DEV_LOG("Ignoring select unit 0x{:X}", reader.ReadU8());
const u8 unit = reader.ReadU8();
DEV_LOG("Ignoring select unit 0x{:X}", unit);
}
break;

@ -2316,7 +2316,7 @@ void FullscreenUI::DrawResumeStateSelector()
TextAlignedMultiLine(0.5f, IMSTR_START_END(sick));
ImGui::PopFont();
const GPUTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get();
const GPUTexture* image = entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture();
const float image_height = LayoutScale(400.0f);
const float image_width =
image_height * (static_cast<float>(image->GetWidth()) / static_cast<float>(image->GetHeight()));
@ -2324,7 +2324,7 @@ void FullscreenUI::DrawResumeStateSelector()
ImVec2((ImGui::GetCurrentWindow()->WorkRect.GetWidth() - image_width) * 0.5f, LayoutScale(20.0f)));
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
ImGui::GetWindowDrawList()->AddImage(
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get()),
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture()),
image_bb.Min, image_bb.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)));

@ -1161,7 +1161,7 @@ GPUTexture* FullscreenUI::GetGameListCoverTrophy(const GameList::Entry* entry, c
static_cast<u32>(trophy_size.x), static_cast<u32>(trophy_size.y));
// don't draw the placeholder, it's way too large
return (texture == GetPlaceholderTexture().get()) ? nullptr : texture;
return (texture == GetPlaceholderTexture()) ? nullptr : texture;
}
std::string_view FullscreenUI::GetKeyForGameListEntry(const GameList::Entry* entry)

@ -87,11 +87,11 @@ enum class SplitWindowFocusChange : u8
static std::optional<Image> LoadTextureImage(std::string_view path, u32 svg_width, u32 svg_height);
static std::optional<Image> LoadTextureImage(std::string_view filename, std::span<const u8> buffer, u32 svg_width,
u32 svg_height);
static std::shared_ptr<GPUTexture> UploadTexture(std::string_view path, const Image& image);
static std::unique_ptr<GPUTexture> UploadTexture(std::string_view path, const Image& image);
static void QueueTextureUploadFromBuffer(std::string_view filename, const std::span<const u8>& buffer,
std::string&& insert_name, u32 svg_width, u32 svg_height,
bool use_task_for_decode);
static std::shared_ptr<GPUTexture> LoadTexture(std::string_view path, std::string_view name, u32 svg_width,
static std::unique_ptr<GPUTexture> LoadTexture(std::string_view path, std::string_view name, u32 svg_width,
u32 svg_height);
static GPUTexture* LookupCachedTextureAsync(std::string_view path, std::string_view name, u32 svg_width,
u32 svg_height);
@ -495,8 +495,8 @@ struct WidgetsState
ImVec2 horizontal_menu_button_size = {};
LRUCache<std::string, std::shared_ptr<GPUTexture>> texture_cache{128, true};
std::shared_ptr<GPUTexture> placeholder_texture;
LRUCache<std::string, GPUTexture*, GPUDevice::PooledTextureDeleter> texture_cache{128, true};
std::unique_ptr<GPUTexture> placeholder_texture;
std::deque<std::pair<std::string, Image>> texture_upload_queue;
std::vector<std::unique_ptr<GPUTexture>> texture_recycle_queue;
@ -732,9 +732,9 @@ GPUPipeline* FullscreenUI::GetPresentCopyPipeline()
return s_state.present_copy_pipeline.get();
}
const std::shared_ptr<GPUTexture>& FullscreenUI::GetPlaceholderTexture()
GPUTexture* FullscreenUI::GetPlaceholderTexture()
{
return s_state.placeholder_texture;
return s_state.placeholder_texture.get();
}
std::optional<Image> FullscreenUI::LoadTextureImage(std::string_view path, u32 svg_width, u32 svg_height)
@ -831,7 +831,7 @@ std::optional<Image> FullscreenUI::LoadTextureImage(std::string_view filename, s
return image;
}
std::shared_ptr<GPUTexture> FullscreenUI::UploadTexture(std::string_view path, const Image& image)
std::unique_ptr<GPUTexture> FullscreenUI::UploadTexture(std::string_view path, const Image& image)
{
Error error;
std::unique_ptr<GPUTexture> texture =
@ -843,7 +843,7 @@ std::shared_ptr<GPUTexture> FullscreenUI::UploadTexture(std::string_view path, c
}
DEV_LOG("Uploaded texture resource '{}' ({}x{})", path, image.GetWidth(), image.GetHeight());
return std::shared_ptr<GPUTexture>(texture.release(), GPUDevice::PooledTextureDeleter());
return texture;
}
void FullscreenUI::QueueTextureUploadFromBuffer(std::string_view filename, const std::span<const u8>& buffer,
@ -873,7 +873,7 @@ void FullscreenUI::QueueTextureUploadFromBuffer(std::string_view filename, const
s_state.texture_upload_queue.emplace_back(std::move(insert_name), std::move(image.value()));
}
std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std::string_view name, u32 svg_width,
std::unique_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std::string_view name, u32 svg_width,
u32 svg_height)
{
if (HTTPCache::IsHTTPURL(path))
@ -900,11 +900,7 @@ std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std
{
const std::optional<Image> image = LoadTextureImage(filename, result.value().cspan(), svg_width, svg_height);
if (image.has_value())
{
std::shared_ptr<GPUTexture> ret = UploadTexture(path, image.value());
if (ret)
return ret;
}
return UploadTexture(path, image.value());
}
break;
@ -923,31 +919,27 @@ std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std
DefaultCaseIsUnreachable();
}
return s_state.placeholder_texture;
return nullptr;
}
std::optional<Image> image(LoadTextureImage(path, svg_width, svg_height));
if (image.has_value())
{
std::shared_ptr<GPUTexture> ret(UploadTexture(path, image.value()));
if (ret)
return ret;
}
return UploadTexture(path, image.value());
return s_state.placeholder_texture;
return nullptr;
}
std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name)
std::unique_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name)
{
return LoadTexture(name, {}, 0, 0);
}
std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std::string_view name)
std::unique_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view path, std::string_view name)
{
return LoadTexture(path, name, 0, 0);
}
std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name, u32 svg_width, u32 svg_height)
std::unique_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name, u32 svg_width, u32 svg_height)
{
// ignore size hints if it's not needed, don't duplicate
if (!TextureNeedsSVGDimensions(name))
@ -957,15 +949,16 @@ std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name, u32
return LoadTexture(name, wh_name, svg_width, svg_height);
}
std::shared_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name, const ImVec2& size)
std::unique_ptr<GPUTexture> FullscreenUI::LoadTexture(std::string_view name, const ImVec2& size)
{
return LoadTexture(name, name, static_cast<u32>(size.x), static_cast<u32>(size.y));
}
GPUTexture* FullscreenUI::FindCachedTexture(std::string_view name)
{
std::shared_ptr<GPUTexture>* tex_ptr = s_state.texture_cache.Lookup(name);
return tex_ptr ? tex_ptr->get() : nullptr;
// We want to return the placeholder if it's currently async loading.
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(name);
return !tex_ptr ? nullptr : (*tex_ptr ? *tex_ptr : s_state.placeholder_texture.get());
}
GPUTexture* FullscreenUI::FindCachedTexture(std::string_view name, u32 svg_width, u32 svg_height)
@ -975,8 +968,8 @@ GPUTexture* FullscreenUI::FindCachedTexture(std::string_view name, u32 svg_width
return FindCachedTexture(name);
const SmallString wh_name = SmallString::from_format("{}#{}x{}", name, svg_width, svg_height);
std::shared_ptr<GPUTexture>* tex_ptr = s_state.texture_cache.Lookup(wh_name.view());
return tex_ptr ? tex_ptr->get() : nullptr;
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(wh_name.view());
return !tex_ptr ? nullptr : (*tex_ptr ? *tex_ptr : s_state.placeholder_texture.get());
}
GPUTexture* FullscreenUI::FindCachedTexture(std::string_view name, const ImVec2& size)
@ -991,14 +984,15 @@ GPUTexture* FullscreenUI::GetCachedTexture(std::string_view name)
GPUTexture* FullscreenUI::GetCachedTexture(std::string_view path, std::string_view name)
{
std::shared_ptr<GPUTexture>* tex_ptr = s_state.texture_cache.Lookup(name);
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(name);
if (!tex_ptr)
{
std::shared_ptr<GPUTexture> tex = LoadTexture(path);
tex_ptr = s_state.texture_cache.Insert(std::string(name), std::move(tex));
std::unique_ptr<GPUTexture> tex = LoadTexture(path);
if (tex)
tex_ptr = s_state.texture_cache.Insert(std::string(name), tex.release());
}
return tex_ptr->get();
return tex_ptr ? *tex_ptr : s_state.placeholder_texture.get();
}
GPUTexture* FullscreenUI::GetCachedTexture(std::string_view name, u32 svg_width, u32 svg_height)
@ -1008,14 +1002,15 @@ GPUTexture* FullscreenUI::GetCachedTexture(std::string_view name, u32 svg_width,
return GetCachedTexture(name);
const SmallString wh_name = SmallString::from_format("{}#{}x{}", name, svg_width, svg_height);
std::shared_ptr<GPUTexture>* tex_ptr = s_state.texture_cache.Lookup(wh_name.view());
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(wh_name.view());
if (!tex_ptr)
{
std::shared_ptr<GPUTexture> tex = LoadTexture(name, svg_width, svg_height);
tex_ptr = s_state.texture_cache.Insert(std::string(wh_name.view()), std::move(tex));
std::unique_ptr<GPUTexture> tex = LoadTexture(name, svg_width, svg_height);
if (tex)
tex_ptr = s_state.texture_cache.Insert(std::string(wh_name.view()), tex.release());
}
return tex_ptr->get();
return tex_ptr ? *tex_ptr : s_state.placeholder_texture.get();
}
GPUTexture* FullscreenUI::GetCachedTexture(std::string_view name, const ImVec2& size)
@ -1027,12 +1022,12 @@ GPUTexture* FullscreenUI::LookupCachedTextureAsync(std::string_view path, std::s
u32 svg_height)
{
const std::string_view lookup_name = name.empty() ? path : name;
std::shared_ptr<GPUTexture>* tex_ptr = s_state.texture_cache.Lookup(lookup_name);
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(lookup_name);
if (tex_ptr)
return tex_ptr->get();
return *tex_ptr ? *tex_ptr : s_state.placeholder_texture.get();
// insert the placeholder
tex_ptr = s_state.texture_cache.Insert(std::string(lookup_name), s_state.placeholder_texture);
// insert the placeholder so the load won't insert if it's already evicted
s_state.texture_cache.Insert(std::string(lookup_name), nullptr);
// queue load
Host::QueueAsyncTask([path = std::string(path), name = std::string(name), svg_width, svg_height]() mutable {
@ -1078,7 +1073,7 @@ GPUTexture* FullscreenUI::LookupCachedTextureAsync(std::string_view path, std::s
}
});
return tex_ptr->get();
return s_state.placeholder_texture.get();
}
GPUTexture* FullscreenUI::GetCachedTextureAsync(std::string_view name)
@ -1127,9 +1122,13 @@ void FullscreenUI::UploadAsyncTextures()
s_state.texture_upload_queue.pop_front();
lock.unlock();
std::shared_ptr<GPUTexture> tex = UploadTexture(it.first.c_str(), it.second);
if (tex)
s_state.texture_cache.Insert(std::move(it.first), std::move(tex));
// did it get evicted in the meantime? if so, don't bother uploading it
GPUTexture** tex_ptr = s_state.texture_cache.Lookup(it.first);
if (tex_ptr && !*tex_ptr)
{
std::unique_ptr<GPUTexture> tex = UploadTexture(it.first.c_str(), it.second);
*tex_ptr = *s_state.texture_cache.Insert(std::move(it.first), tex.release());
}
lock.lock();
}

@ -256,11 +256,11 @@ void SetFont(ImFont* ui_font);
bool UpdateLayoutScale();
/// Texture cache.
const std::shared_ptr<GPUTexture>& GetPlaceholderTexture();
std::shared_ptr<GPUTexture> LoadTexture(std::string_view path);
std::shared_ptr<GPUTexture> LoadTexture(std::string_view path, std::string_view name);
std::shared_ptr<GPUTexture> LoadTexture(std::string_view path, u32 svg_width, u32 svg_height);
std::shared_ptr<GPUTexture> LoadTexture(std::string_view path, const ImVec2& size);
GPUTexture* GetPlaceholderTexture();
std::unique_ptr<GPUTexture> LoadTexture(std::string_view path);
std::unique_ptr<GPUTexture> LoadTexture(std::string_view path, std::string_view name);
std::unique_ptr<GPUTexture> LoadTexture(std::string_view path, u32 svg_width, u32 svg_height);
std::unique_ptr<GPUTexture> LoadTexture(std::string_view path, const ImVec2& size);
GPUTexture* FindCachedTexture(std::string_view name);
GPUTexture* FindCachedTexture(std::string_view name, u32 svg_width, u32 svg_height);
GPUTexture* FindCachedTexture(std::string_view name, const ImVec2& size);

Loading…
Cancel
Save