Skip to content

Commit

Permalink
[gui] AssetPicker: load textures way faster
Browse files Browse the repository at this point in the history
Loading a few out of the queue every tick works well
  • Loading branch information
AlpyneDreams committed Feb 9, 2024
1 parent 15d8083 commit adc0a85
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 10 deletions.
117 changes: 107 additions & 10 deletions src/gui/AssetPicker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,53 @@

#include <misc/cpp/imgui_stdlib.h>
#include <string>
#include <unordered_set>

namespace chisel
{
static const uint2 AssetPadding { 16, 16 };

static std::unordered_set<AssetPicker*> s_AssetPickers;

AssetPicker::AssetPicker() : GUI::Window(ICON_MC_FOLDER, "Assets", 1024, 512, false, ImGuiWindowFlags_MenuBar)
{
if (static bool s_Registered = false; !s_Registered)
{
Assets.OnRefresh += [] {
for (auto picker : s_AssetPickers)
picker->Refresh();
};
s_Registered = true;
}

s_AssetPickers.insert(this);
m_LastWindowSize = uint2(1024, 512);
m_AssetsPerRow = uint(floor(m_LastWindowSize.x / (AssetThumbnailSize.x + AssetPadding.x)));
Refresh();
}

AssetPicker::~AssetPicker()
{
if (s_AssetPickers.contains(this))
s_AssetPickers.erase(this);
}

bool AssetPicker::IsAssetVisible(uint index) const
{
return index > m_FirstVisibleAsset
&& index < m_FirstVisibleAsset + (m_AssetsPerRow * m_NumVisibleRows);
}

bool AssetPicker::IsAssetAlmostVisible(uint index) const
{
return index > m_FirstVisibleAsset - (m_AssetsPerRow * m_NumVisibleRows * 2)
&& index < m_FirstVisibleAsset + (m_AssetsPerRow * m_NumVisibleRows * 2);
}

void AssetPicker::Draw()
{
if (ImGui::BeginMenuBar())
{
ImGui::Text("Loaded %u/%llu", m_LoadedAssetCount, m_materials.size());
// Right side
ImGui::Spacing();
ImGui::SameLine(ImGui::GetWindowWidth() - 200);
Expand All @@ -44,18 +74,17 @@ namespace chisel

float scroll = ImGui::GetScrollY();

uint numVisibleRows = uint(uint(ImGui::GetWindowSize().y) / (AssetThumbnailSize.y + AssetPadding.y)) + 2;
m_NumVisibleRows = uint(uint(ImGui::GetWindowSize().y) / (AssetThumbnailSize.y + AssetPadding.y)) + 2;

uint xAssetRow = uint(scroll / (AssetThumbnailSize.y + AssetPadding.y));
uint xAsset = xAssetRow * m_AssetsPerRow;
m_FirstVisibleAsset = xAssetRow * m_AssetsPerRow;

auto render = [&]()
{
if (m_materials.size() == 0)
return;

uint currentAsset = xAsset;
for (uint row = 0; row < numVisibleRows; row++)
uint currentAsset = m_FirstVisibleAsset;
for (uint row = 0; row < m_NumVisibleRows; row++)
{
for (uint column = 0; column < m_AssetsPerRow; column++)
{
Expand All @@ -64,7 +93,6 @@ namespace chisel
ImVec2 basePos = ImVec2(column * (AssetThumbnailSize.x + AssetPadding.x) + initialXPadding, (xAssetRow + row) * (AssetThumbnailSize.y + AssetPadding.y) + initialYPadding);

auto& material = m_materials[currentAsset];
material.Load();

if (material.thing != nullptr && material.thing->baseTexture != nullptr && material.thing->baseTexture->srvLinear != nullptr)
{
Expand All @@ -83,6 +111,29 @@ namespace chisel
if (selected)
ImGui::PopStyleColor();
}
else
{
ImGui::SetCursorPos(basePos);

if (ImGui::ImageButton(material.path.c_str(), (ImTextureID)nullptr,
ImVec2(float(AssetThumbnailSize.x), float(AssetThumbnailSize.y)), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 0), ImVec4(1, 1, 1, 1)))
{
Chisel.activeMaterial = material.thing;
}

if (!material.triedToLoad && !m_thumbnailQueueSet.contains(currentAsset))
{
m_thumbnailQueue.push_front(currentAsset);
m_thumbnailQueueSet.insert(currentAsset);
}
}

if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", material.path.c_str());
ImGui::EndTooltip();
}

ImVec2 textPos = ImVec2(basePos.x, basePos.y + AssetThumbnailSize.y);
ImGui::SetCursorPos(textPos);
Expand All @@ -100,11 +151,43 @@ namespace chisel
}
}
};
render();

ImGui::PopFont();
}

void AssetPicker::Tick()
{
if (!open)
return;

int numLoaded = 0;
if (m_thumbnailQueue.size() > 0)
{
// Load 8 textures per tick as we scroll in new textures
while ( m_thumbnailQueue.size() > 0 && numLoaded++ < 8 )
{
uint index = m_thumbnailQueue.front();
m_materials[index].Load();
m_thumbnailQueue.pop_front();
m_LoadedAssetCount++;
m_thumbnailQueueSet.erase(index);
}
}
else
{
// Otherwise, load 4 models per tick from off-screen
for ( uint i = 0; i < m_materials.size() && numLoaded <= 4; i++ )
{
if ( !m_materials[i].triedToLoad && IsAssetAlmostVisible(i) )
{
m_materials[i].Load();
m_LoadedAssetCount++;
numLoaded++;
}
}
}
}

bool AssetPicker::OverrideContentSize(uint2& size)
{
uint count = uint(m_materials.size());
Expand All @@ -117,16 +200,30 @@ namespace chisel
void AssetPicker::Refresh()
{
m_materials.clear();
m_thumbnailQueue.clear();

Assets.ForEachFile<Material>(
[&](const fs::Path& p)
{
AssetPickerAsset<Material>& asset = m_materials.emplace_back();
asset.path = std::string(p);

fs::Path subpath;
std::filesystem::path path = p;
bool foundMaterials = false;
for (auto& part : path)
{
if (foundMaterials)
subpath /= part;
if (part == "materials")
foundMaterials = true;
}
if (!foundMaterials)
subpath = p.dirname().filename() / p.filename();

// Remove materials/ and .vmt for display name
std::string_view name = asset.path;
if ((name[9] == '/' || name[9] == '\\') && name.starts_with("materials"))
std::string_view name = subpath;
if (name.starts_with("materials") && (name[9] == '/' || name[9] == '\\'))
name.remove_prefix(10);
name.remove_suffix(std::string_view(p.ext()).size());
asset.name = name;
Expand Down
12 changes: 12 additions & 0 deletions src/gui/AssetPicker.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "gui/Window.h"
#include "assets/Assets.h"

#include <deque>
#include <unordered_set>
namespace chisel
{
template <typename T>
Expand All @@ -27,19 +29,29 @@ namespace chisel
struct AssetPicker : public GUI::Window
{
AssetPicker();
~AssetPicker();

void Draw() override;
void Tick() override;

bool OverrideContentSize(uint2& size) override;

void Refresh();

private:
bool IsAssetVisible(uint index) const;
bool IsAssetAlmostVisible(uint index) const;

std::vector<AssetPickerAsset<Material>> m_materials;
std::deque<uint> m_thumbnailQueue;
std::unordered_set<uint> m_thumbnailQueueSet;

uint2 m_LastWindowSize;
int ThumbnailScale = 7; // size = 16 * scale
uint2 AssetThumbnailSize = { 128, 128 };
uint m_AssetsPerRow;
uint m_FirstVisibleAsset = 0;
uint m_NumVisibleRows = 0;
uint m_LoadedAssetCount = 0;
};
}

0 comments on commit adc0a85

Please sign in to comment.