mirror of
https://github.com/stenzek/duckstation.git
synced 2025-06-06 03:25:36 +00:00
ImGuiManager: Move drawing out of GPUDevice
This commit is contained in:
parent
46e11d96da
commit
df2a5a5e67
@ -1032,6 +1032,8 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
||||
// acquire for IO.MousePos and system state.
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
|
||||
ImGuiManager::RenderDebugWindows();
|
||||
|
||||
FullscreenUI::Render();
|
||||
|
||||
if (backend)
|
||||
@ -1046,7 +1048,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
||||
if (backend && !GPUThread::IsSystemPaused())
|
||||
ImGuiManager::RenderSoftwareCursors();
|
||||
|
||||
ImGuiManager::RenderDebugWindows();
|
||||
ImGuiManager::CreateDrawLists();
|
||||
|
||||
// render offscreen for transitions
|
||||
if (FullscreenUI::IsTransitionActive())
|
||||
@ -1060,7 +1062,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
||||
g_gpu_device->ClearRenderTarget(rtex, GPUDevice::DEFAULT_CLEAR_COLOR);
|
||||
|
||||
g_gpu_device->SetRenderTarget(rtex);
|
||||
g_gpu_device->RenderImGui(rtex);
|
||||
ImGuiManager::RenderDrawLists(rtex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1079,7 +1081,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
|
||||
if (FullscreenUI::IsTransitionActive())
|
||||
FullscreenUI::RenderTransitionBlend(swap_chain);
|
||||
else
|
||||
g_gpu_device->RenderImGui(swap_chain);
|
||||
ImGuiManager::RenderDrawLists(swap_chain);
|
||||
|
||||
const GPUDevice::Features features = g_gpu_device->GetFeatures();
|
||||
const bool scheduled_present = (present_time != 0);
|
||||
|
@ -1273,7 +1273,7 @@ void GPUThread::DisplayWindowResizedOnThread()
|
||||
// our imgui stuff can't cope with 0x0/hidden windows
|
||||
const float f_width = static_cast<float>(std::max(swap_chain->GetWidth(), 1u));
|
||||
const float f_height = static_cast<float>(std::max(swap_chain->GetHeight(), 1u));
|
||||
ImGuiManager::WindowResized(f_width, f_height);
|
||||
ImGuiManager::WindowResized(swap_chain->GetFormat(), f_width, f_height);
|
||||
InputManager::SetDisplayWindowSize(f_width, f_height);
|
||||
|
||||
if (s_state.gpu_backend)
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "imgui.h"
|
||||
#include "shaderc/shaderc.h"
|
||||
#include "spirv_cross_c.h"
|
||||
#include "xxhash.h"
|
||||
@ -669,57 +668,6 @@ bool GPUDevice::CreateResources(Error* error)
|
||||
}
|
||||
GL_OBJECT_NAME(m_nearest_sampler, "Nearest Sampler");
|
||||
GL_OBJECT_NAME(m_linear_sampler, "Nearest Sampler");
|
||||
|
||||
const RenderAPI render_api = GetRenderAPI();
|
||||
ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), m_features.dual_source_blend,
|
||||
m_features.framebuffer_fetch);
|
||||
|
||||
std::unique_ptr<GPUShader> imgui_vs =
|
||||
CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(), shadergen.GenerateImGuiVertexShader(), error);
|
||||
std::unique_ptr<GPUShader> imgui_fs =
|
||||
CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(), shadergen.GenerateImGuiFragmentShader(), error);
|
||||
if (!imgui_vs || !imgui_fs)
|
||||
{
|
||||
Error::AddPrefix(error, "Failed to compile ImGui shaders: ");
|
||||
return false;
|
||||
}
|
||||
GL_OBJECT_NAME(imgui_vs, "ImGui Vertex Shader");
|
||||
GL_OBJECT_NAME(imgui_fs, "ImGui Fragment Shader");
|
||||
|
||||
static constexpr GPUPipeline::VertexAttribute imgui_attributes[] = {
|
||||
GPUPipeline::VertexAttribute::Make(0, GPUPipeline::VertexAttribute::Semantic::Position, 0,
|
||||
GPUPipeline::VertexAttribute::Type::Float, 2, OFFSETOF(ImDrawVert, pos)),
|
||||
GPUPipeline::VertexAttribute::Make(1, GPUPipeline::VertexAttribute::Semantic::TexCoord, 0,
|
||||
GPUPipeline::VertexAttribute::Type::Float, 2, OFFSETOF(ImDrawVert, uv)),
|
||||
GPUPipeline::VertexAttribute::Make(2, GPUPipeline::VertexAttribute::Semantic::Color, 0,
|
||||
GPUPipeline::VertexAttribute::Type::UNorm8, 4, OFFSETOF(ImDrawVert, col)),
|
||||
};
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||
plconfig.input_layout.vertex_attributes = imgui_attributes;
|
||||
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||
plconfig.blend.write_mask = 0x7;
|
||||
plconfig.SetTargetFormats(m_main_swap_chain ? m_main_swap_chain->GetFormat() : GPUTexture::Format::RGBA8);
|
||||
plconfig.samples = 1;
|
||||
plconfig.per_sample_shading = false;
|
||||
plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||
plconfig.vertex_shader = imgui_vs.get();
|
||||
plconfig.geometry_shader = nullptr;
|
||||
plconfig.fragment_shader = imgui_fs.get();
|
||||
|
||||
m_imgui_pipeline = CreatePipeline(plconfig, error);
|
||||
if (!m_imgui_pipeline)
|
||||
{
|
||||
Error::AddPrefix(error, "Failed to compile ImGui pipeline: ");
|
||||
return false;
|
||||
}
|
||||
GL_OBJECT_NAME(m_imgui_pipeline, "ImGui Pipeline");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -727,11 +675,6 @@ void GPUDevice::DestroyResources()
|
||||
{
|
||||
m_empty_texture.reset();
|
||||
|
||||
m_imgui_font_texture.reset();
|
||||
m_imgui_pipeline.reset();
|
||||
|
||||
m_imgui_pipeline.reset();
|
||||
|
||||
m_linear_sampler = nullptr;
|
||||
m_nearest_sampler = nullptr;
|
||||
m_sampler_map.clear();
|
||||
@ -739,131 +682,6 @@ void GPUDevice::DestroyResources()
|
||||
m_shader_cache.Close();
|
||||
}
|
||||
|
||||
void GPUDevice::RenderImGui(GPUSwapChain* swap_chain)
|
||||
{
|
||||
GL_SCOPE("RenderImGui");
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0 || !swap_chain)
|
||||
return;
|
||||
|
||||
const s32 post_rotated_height = swap_chain->GetPostRotatedHeight();
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
SetViewport(0, 0, swap_chain->GetPostRotatedWidth(), post_rotated_height);
|
||||
|
||||
const bool prerotated = (swap_chain->GetPreRotation() != WindowInfo::PreRotation::Identity);
|
||||
GSMatrix4x4 mproj = GSMatrix4x4::OffCenterOrthographicProjection(
|
||||
0.0f, 0.0f, static_cast<float>(swap_chain->GetWidth()), static_cast<float>(swap_chain->GetHeight()), 0.0f, 1.0f);
|
||||
if (prerotated)
|
||||
mproj = GSMatrix4x4::RotationZ(WindowInfo::GetZRotationForPreRotation(swap_chain->GetPreRotation())) * mproj;
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
|
||||
// Render command lists
|
||||
const bool flip = UsesLowerLeftOrigin();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
static_assert(sizeof(ImDrawIdx) == sizeof(DrawIndex));
|
||||
|
||||
u32 base_vertex, base_index;
|
||||
UploadVertexBuffer(cmd_list->VtxBuffer.Data, sizeof(ImDrawVert), cmd_list->VtxBuffer.Size, &base_vertex);
|
||||
UploadIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size, &base_index);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
|
||||
if ((pcmd->ElemCount == 0 && !pcmd->UserCallback) || pcmd->ClipRect.z <= pcmd->ClipRect.x ||
|
||||
pcmd->ClipRect.w <= pcmd->ClipRect.y)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i clip = GSVector4i(GSVector4::load<false>(&pcmd->ClipRect.x));
|
||||
|
||||
if (prerotated)
|
||||
clip = GPUSwapChain::PreRotateClipRect(swap_chain->GetPreRotation(), swap_chain->GetSizeVec(), clip);
|
||||
if (flip)
|
||||
clip = FlipToLowerLeft(clip, post_rotated_height);
|
||||
|
||||
SetScissor(clip);
|
||||
SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->TextureId), m_linear_sampler);
|
||||
|
||||
if (pcmd->UserCallback) [[unlikely]]
|
||||
{
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawIndexed(pcmd->ElemCount, base_index + pcmd->IdxOffset, base_vertex + pcmd->VtxOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::RenderImGui(GPUTexture* texture)
|
||||
{
|
||||
GL_SCOPE("RenderImGui");
|
||||
|
||||
ImGui::Render();
|
||||
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
SetViewport(0, 0, texture->GetWidth(), texture->GetHeight());
|
||||
|
||||
const GSMatrix4x4 mproj = GSMatrix4x4::OffCenterOrthographicProjection(
|
||||
0.0f, 0.0f, static_cast<float>(texture->GetWidth()), static_cast<float>(texture->GetHeight()), 0.0f, 1.0f);
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
|
||||
// Render command lists
|
||||
const bool flip = UsesLowerLeftOrigin();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
static_assert(sizeof(ImDrawIdx) == sizeof(DrawIndex));
|
||||
|
||||
u32 base_vertex, base_index;
|
||||
UploadVertexBuffer(cmd_list->VtxBuffer.Data, sizeof(ImDrawVert), cmd_list->VtxBuffer.Size, &base_vertex);
|
||||
UploadIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size, &base_index);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
|
||||
if ((pcmd->ElemCount == 0 && !pcmd->UserCallback) || pcmd->ClipRect.z <= pcmd->ClipRect.x ||
|
||||
pcmd->ClipRect.w <= pcmd->ClipRect.y)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i clip = GSVector4i(GSVector4::load<false>(&pcmd->ClipRect.x));
|
||||
if (flip)
|
||||
clip = FlipToLowerLeft(clip, texture->GetHeight());
|
||||
|
||||
SetScissor(clip);
|
||||
SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->TextureId), m_linear_sampler);
|
||||
|
||||
if (pcmd->UserCallback) [[unlikely]]
|
||||
{
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
SetPipeline(m_imgui_pipeline.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawIndexed(pcmd->ElemCount, base_index + pcmd->IdxOffset, base_vertex + pcmd->VtxOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GPUDevice::UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex)
|
||||
{
|
||||
void* map;
|
||||
@ -1041,40 +859,6 @@ std::array<float, 4> GPUDevice::RGBA8ToFloat(u32 rgba)
|
||||
static_cast<float>(rgba >> 24) * (1.0f / 255.0f)};
|
||||
}
|
||||
|
||||
bool GPUDevice::UpdateImGuiFontTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
const u32 pitch = sizeof(u32) * width;
|
||||
|
||||
if (m_imgui_font_texture && m_imgui_font_texture->GetWidth() == static_cast<u32>(width) &&
|
||||
m_imgui_font_texture->GetHeight() == static_cast<u32>(height) &&
|
||||
m_imgui_font_texture->Update(0, 0, static_cast<u32>(width), static_cast<u32>(height), pixels, pitch))
|
||||
{
|
||||
io.Fonts->SetTexID(m_imgui_font_texture.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
Error error;
|
||||
std::unique_ptr<GPUTexture> new_font =
|
||||
FetchTexture(width, height, 1, 1, 1, GPUTexture::Type::Texture, GPUTexture::Format::RGBA8, GPUTexture::Flags::None,
|
||||
pixels, pitch, &error);
|
||||
if (!new_font) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to create new ImGui font texture: {}", error.GetDescription());
|
||||
return false;
|
||||
}
|
||||
|
||||
RecycleTexture(std::move(m_imgui_font_texture));
|
||||
m_imgui_font_texture = std::move(new_font);
|
||||
io.Fonts->SetTexID(m_imgui_font_texture.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPUDevice::UsesLowerLeftOrigin() const
|
||||
{
|
||||
const RenderAPI api = GetRenderAPI();
|
||||
@ -1327,7 +1111,8 @@ void GPUDevice::TrimTexturePool()
|
||||
}
|
||||
|
||||
bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, bool preserve /* = true */)
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, bool preserve /* = true */,
|
||||
Error* error /* = nullptr */)
|
||||
{
|
||||
GPUTexture* old_tex = tex->get();
|
||||
if (old_tex && old_tex->GetWidth() == new_width && old_tex->GetHeight() == new_height && old_tex->GetType() == type &&
|
||||
@ -1337,38 +1122,67 @@ bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u
|
||||
}
|
||||
|
||||
DebugAssert(!old_tex || (old_tex->GetLayers() == 1 && old_tex->GetLevels() == 1 && old_tex->GetSamples() == 1));
|
||||
std::unique_ptr<GPUTexture> new_tex = FetchTexture(new_width, new_height, 1, 1, 1, type, format, flags);
|
||||
std::unique_ptr<GPUTexture> new_tex =
|
||||
FetchTexture(new_width, new_height, 1, 1, 1, type, format, flags, nullptr, 0, error);
|
||||
if (!new_tex) [[unlikely]]
|
||||
{
|
||||
ERROR_LOG("Failed to create new {}x{} texture", new_width, new_height);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (old_tex)
|
||||
if (preserve)
|
||||
{
|
||||
if (old_tex->GetState() == GPUTexture::State::Cleared)
|
||||
if (old_tex)
|
||||
{
|
||||
if (type == GPUTexture::Type::RenderTarget)
|
||||
ClearRenderTarget(new_tex.get(), old_tex->GetClearColor());
|
||||
if (old_tex->GetState() == GPUTexture::State::Cleared)
|
||||
{
|
||||
if (type == GPUTexture::Type::RenderTarget)
|
||||
ClearRenderTarget(new_tex.get(), old_tex->GetClearColor());
|
||||
}
|
||||
else if (old_tex->GetState() == GPUTexture::State::Dirty)
|
||||
{
|
||||
const u32 copy_width = std::min(new_width, old_tex->GetWidth());
|
||||
const u32 copy_height = std::min(new_height, old_tex->GetHeight());
|
||||
if (type == GPUTexture::Type::RenderTarget)
|
||||
ClearRenderTarget(new_tex.get(), 0);
|
||||
|
||||
if (old_tex->GetFormat() == new_tex->GetFormat())
|
||||
CopyTextureRegion(new_tex.get(), 0, 0, 0, 0, old_tex, 0, 0, 0, 0, copy_width, copy_height);
|
||||
}
|
||||
}
|
||||
else if (old_tex->GetState() == GPUTexture::State::Dirty)
|
||||
else
|
||||
{
|
||||
const u32 copy_width = std::min(new_width, old_tex->GetWidth());
|
||||
const u32 copy_height = std::min(new_height, old_tex->GetHeight());
|
||||
// If we're expecting data to be there, make sure to clear it.
|
||||
if (type == GPUTexture::Type::RenderTarget)
|
||||
ClearRenderTarget(new_tex.get(), 0);
|
||||
|
||||
if (old_tex->GetFormat() == new_tex->GetFormat())
|
||||
CopyTextureRegion(new_tex.get(), 0, 0, 0, 0, old_tex, 0, 0, 0, 0, copy_width, copy_height);
|
||||
}
|
||||
}
|
||||
else if (preserve)
|
||||
|
||||
RecycleTexture(std::move(*tex));
|
||||
*tex = std::move(new_tex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPUDevice::ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, const void* replace_data,
|
||||
u32 replace_data_pitch, Error* error /* = nullptr */)
|
||||
{
|
||||
GPUTexture* old_tex = tex->get();
|
||||
if (old_tex && old_tex->GetWidth() == new_width && old_tex->GetHeight() == new_height && old_tex->GetType() == type &&
|
||||
old_tex->GetFormat() == format && old_tex->GetFlags() == flags)
|
||||
{
|
||||
// If we're expecting data to be there, make sure to clear it.
|
||||
if (type == GPUTexture::Type::RenderTarget)
|
||||
ClearRenderTarget(new_tex.get(), 0);
|
||||
if (replace_data && !old_tex->Update(0, 0, new_width, new_height, replace_data, replace_data_pitch))
|
||||
{
|
||||
Error::SetStringView(error, "Texture update failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DebugAssert(!old_tex || (old_tex->GetLayers() == 1 && old_tex->GetLevels() == 1 && old_tex->GetSamples() == 1));
|
||||
std::unique_ptr<GPUTexture> new_tex =
|
||||
FetchTexture(new_width, new_height, 1, 1, 1, type, format, flags, replace_data, replace_data_pitch, error);
|
||||
if (!new_tex) [[unlikely]]
|
||||
return false;
|
||||
|
||||
RecycleTexture(std::move(*tex));
|
||||
*tex = std::move(new_tex);
|
||||
return true;
|
||||
|
@ -870,18 +870,16 @@ public:
|
||||
virtual void EndPresent(GPUSwapChain* swap_chain, bool explicit_submit, u64 submit_time = 0) = 0;
|
||||
virtual void SubmitPresent(GPUSwapChain* swap_chain) = 0;
|
||||
|
||||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderImGui(GPUSwapChain* swap_chain);
|
||||
void RenderImGui(GPUTexture* texture);
|
||||
|
||||
ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
|
||||
ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
|
||||
|
||||
bool UpdateImGuiFontTexture();
|
||||
bool UsesLowerLeftOrigin() const;
|
||||
static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height);
|
||||
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, bool preserve = true);
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, bool preserve = true, Error* error = nullptr);
|
||||
bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
|
||||
GPUTexture::Format format, GPUTexture::Flags flags, const void* replace_data,
|
||||
u32 replace_data_pitch, Error* error = nullptr);
|
||||
|
||||
virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
|
||||
|
||||
@ -991,9 +989,6 @@ private:
|
||||
|
||||
static size_t s_total_vram_usage;
|
||||
|
||||
std::unique_ptr<GPUPipeline> m_imgui_pipeline;
|
||||
std::unique_ptr<GPUTexture> m_imgui_font_texture;
|
||||
|
||||
SamplerMap m_sampler_map;
|
||||
|
||||
TexturePool m_texture_pool;
|
||||
|
@ -3329,16 +3329,16 @@ void ImGuiFullscreen::RenderLoadingScreen(std::string_view image, std::string_vi
|
||||
|
||||
DrawLoadingScreen(image, message, progress_min, progress_max, progress_value, false);
|
||||
|
||||
ImGui::EndFrame();
|
||||
ImGuiManager::CreateDrawLists();
|
||||
|
||||
GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
if (g_gpu_device->BeginPresent(swap_chain) == GPUDevice::PresentResult::OK)
|
||||
{
|
||||
g_gpu_device->RenderImGui(swap_chain);
|
||||
ImGuiManager::RenderDrawLists(swap_chain);
|
||||
g_gpu_device->EndPresent(swap_chain, false);
|
||||
}
|
||||
|
||||
ImGui::NewFrame();
|
||||
ImGuiManager::NewFrame();
|
||||
}
|
||||
|
||||
void ImGuiFullscreen::OpenOrUpdateLoadingScreen(std::string_view image, std::string_view message,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "imgui_fullscreen.h"
|
||||
#include "imgui_glyph_ranges.inl"
|
||||
#include "input_manager.h"
|
||||
#include "shadergen.h"
|
||||
|
||||
// TODO: Remove me when GPUDevice config is also cleaned up.
|
||||
#include "core/fullscreen_ui.h"
|
||||
@ -79,6 +80,9 @@ static bool AddImGuiFonts(bool debug_font, bool fullscreen_fonts);
|
||||
static ImFont* AddTextFont(float size, const ImWchar* glyph_range);
|
||||
static ImFont* AddFixedFont(float size);
|
||||
static bool AddIconFonts(float size, const ImWchar* emoji_range);
|
||||
static bool CompilePipelines(Error* error);
|
||||
static void RenderDrawLists(u32 window_width, u32 window_height, WindowInfo::PreRotation prerotation);
|
||||
static bool UpdateImGuiFontTexture();
|
||||
static void SetCommonIOOptions(ImGuiIO& io);
|
||||
static void SetImKeyState(ImGuiIO& io, ImGuiKey imkey, bool pressed);
|
||||
static const char* GetClipboardTextImpl(void* userdata);
|
||||
@ -122,11 +126,15 @@ struct ALIGN_TO_CACHE_LINE State
|
||||
|
||||
float window_width = 0.0f;
|
||||
float window_height = 0.0f;
|
||||
GPUTexture::Format window_format = GPUTexture::Format::Unknown;
|
||||
bool scale_changed = false;
|
||||
|
||||
// we maintain a second copy of the stick state here so we can map it to the dpad
|
||||
std::array<s8, 2> left_stick_axis_state = {};
|
||||
|
||||
std::unique_ptr<GPUPipeline> imgui_pipeline;
|
||||
std::unique_ptr<GPUTexture> imgui_font_texture;
|
||||
|
||||
ImFont* debug_font = nullptr;
|
||||
ImFont* osd_font = nullptr;
|
||||
ImFont* fixed_font = nullptr;
|
||||
@ -228,9 +236,10 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUSwapChain* const main_swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
|
||||
s_state.global_prescale = global_scale;
|
||||
s_state.global_scale = std::max(
|
||||
(g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetScale() : 1.0f) * global_scale, 1.0f);
|
||||
s_state.global_scale = std::max((main_swap_chain ? main_swap_chain->GetScale() : 1.0f) * global_scale, 1.0f);
|
||||
s_state.screen_margin = std::max(screen_margin, 0.0f);
|
||||
s_state.scale_changed = false;
|
||||
|
||||
@ -249,10 +258,9 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
|
||||
SetCommonIOOptions(io);
|
||||
|
||||
s_state.last_render_time = Timer::GetCurrentValue();
|
||||
s_state.window_width =
|
||||
g_gpu_device->HasMainSwapChain() ? static_cast<float>(g_gpu_device->GetMainSwapChain()->GetWidth()) : 0.0f;
|
||||
s_state.window_height =
|
||||
g_gpu_device->HasMainSwapChain() ? static_cast<float>(g_gpu_device->GetMainSwapChain()->GetHeight()) : 0.0f;
|
||||
s_state.window_format = main_swap_chain ? main_swap_chain->GetFormat() : GPUTexture::Format::RGBA8;
|
||||
s_state.window_width = main_swap_chain ? static_cast<float>(main_swap_chain->GetWidth()) : 0.0f;
|
||||
s_state.window_height = main_swap_chain ? static_cast<float>(main_swap_chain->GetHeight()) : 0.0f;
|
||||
io.DisplayFramebufferScale = ImVec2(1, 1); // We already scale things ourselves, this would double-apply scaling
|
||||
io.DisplaySize = ImVec2(s_state.window_width, s_state.window_height);
|
||||
|
||||
@ -260,10 +268,12 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
|
||||
SetStyle(s_state.imgui_context->Style, s_state.global_scale);
|
||||
FullscreenUI::SetTheme();
|
||||
|
||||
if (!AddImGuiFonts(false, false) || !g_gpu_device->UpdateImGuiFontTexture())
|
||||
if (!CompilePipelines(error))
|
||||
return false;
|
||||
|
||||
if (!AddImGuiFonts(false, false) || !UpdateImGuiFontTexture())
|
||||
{
|
||||
Error::SetString(error, "Failed to create ImGui font text");
|
||||
ImGui::DestroyContext();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -280,17 +290,20 @@ void ImGuiManager::Shutdown()
|
||||
{
|
||||
DestroySoftwareCursorTextures();
|
||||
|
||||
if (s_state.imgui_context)
|
||||
{
|
||||
ImGui::DestroyContext(s_state.imgui_context);
|
||||
s_state.imgui_context = nullptr;
|
||||
}
|
||||
|
||||
s_state.debug_font = nullptr;
|
||||
s_state.fixed_font = nullptr;
|
||||
s_state.medium_font = nullptr;
|
||||
s_state.large_font = nullptr;
|
||||
ImGuiFullscreen::SetFonts(nullptr, nullptr);
|
||||
|
||||
s_state.imgui_pipeline.reset();
|
||||
g_gpu_device->RecycleTexture(std::move(s_state.imgui_font_texture));
|
||||
|
||||
if (s_state.imgui_context)
|
||||
{
|
||||
ImGui::DestroyContext(s_state.imgui_context);
|
||||
s_state.imgui_context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
ImGuiContext* ImGuiManager::GetMainContext()
|
||||
@ -313,8 +326,20 @@ float ImGuiManager::GetWindowHeight()
|
||||
return s_state.window_height;
|
||||
}
|
||||
|
||||
void ImGuiManager::WindowResized(float width, float height)
|
||||
void ImGuiManager::WindowResized(GPUTexture::Format format, float width, float height)
|
||||
{
|
||||
if (s_state.window_format != format) [[unlikely]]
|
||||
{
|
||||
Error error;
|
||||
s_state.window_format = format;
|
||||
if (!CompilePipelines(&error))
|
||||
{
|
||||
error.AddPrefix("Failed to compile pipelines after window format change:\n");
|
||||
GPUThread::ReportFatalErrorAndShutdown(error.GetDescription());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
s_state.window_width = width;
|
||||
s_state.window_height = height;
|
||||
ImGui::GetMainViewport()->Size = ImGui::GetIO().DisplaySize = ImVec2(width, height);
|
||||
@ -342,10 +367,12 @@ void ImGuiManager::UpdateScale()
|
||||
SetStyle(s_state.imgui_context->Style, s_state.global_scale);
|
||||
|
||||
if (!AddImGuiFonts(HasDebugFont(), HasFullscreenFonts()))
|
||||
Panic("Failed to create ImGui font text");
|
||||
{
|
||||
GPUThread::ReportFatalErrorAndShutdown("Failed to create ImGui font text");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_gpu_device->UpdateImGuiFontTexture())
|
||||
Panic("Failed to recreate font texture after scale+resize");
|
||||
UpdateImGuiFontTexture();
|
||||
}
|
||||
|
||||
void ImGuiManager::NewFrame()
|
||||
@ -381,6 +408,167 @@ void ImGuiManager::NewFrame()
|
||||
}
|
||||
}
|
||||
|
||||
bool ImGuiManager::CompilePipelines(Error* error)
|
||||
{
|
||||
const RenderAPI render_api = g_gpu_device->GetRenderAPI();
|
||||
const GPUDevice::Features features = g_gpu_device->GetFeatures();
|
||||
const ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), features.dual_source_blend,
|
||||
features.framebuffer_fetch);
|
||||
|
||||
std::unique_ptr<GPUShader> imgui_vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
|
||||
shadergen.GenerateImGuiVertexShader(), error);
|
||||
std::unique_ptr<GPUShader> imgui_fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
|
||||
shadergen.GenerateImGuiFragmentShader(), error);
|
||||
if (!imgui_vs || !imgui_fs)
|
||||
{
|
||||
Error::AddPrefix(error, "Failed to compile ImGui shaders: ");
|
||||
return false;
|
||||
}
|
||||
GL_OBJECT_NAME(imgui_vs, "ImGui Vertex Shader");
|
||||
GL_OBJECT_NAME(imgui_fs, "ImGui Fragment Shader");
|
||||
|
||||
static constexpr GPUPipeline::VertexAttribute imgui_attributes[] = {
|
||||
GPUPipeline::VertexAttribute::Make(0, GPUPipeline::VertexAttribute::Semantic::Position, 0,
|
||||
GPUPipeline::VertexAttribute::Type::Float, 2, OFFSETOF(ImDrawVert, pos)),
|
||||
GPUPipeline::VertexAttribute::Make(1, GPUPipeline::VertexAttribute::Semantic::TexCoord, 0,
|
||||
GPUPipeline::VertexAttribute::Type::Float, 2, OFFSETOF(ImDrawVert, uv)),
|
||||
GPUPipeline::VertexAttribute::Make(2, GPUPipeline::VertexAttribute::Semantic::Color, 0,
|
||||
GPUPipeline::VertexAttribute::Type::UNorm8, 4, OFFSETOF(ImDrawVert, col)),
|
||||
};
|
||||
|
||||
GPUPipeline::GraphicsConfig plconfig;
|
||||
plconfig.layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
|
||||
plconfig.input_layout.vertex_attributes = imgui_attributes;
|
||||
plconfig.input_layout.vertex_stride = sizeof(ImDrawVert);
|
||||
plconfig.primitive = GPUPipeline::Primitive::Triangles;
|
||||
plconfig.rasterization = GPUPipeline::RasterizationState::GetNoCullState();
|
||||
plconfig.depth = GPUPipeline::DepthState::GetNoTestsState();
|
||||
plconfig.blend = GPUPipeline::BlendState::GetAlphaBlendingState();
|
||||
plconfig.blend.write_mask = 0x7;
|
||||
plconfig.SetTargetFormats(s_state.window_format);
|
||||
plconfig.samples = 1;
|
||||
plconfig.per_sample_shading = false;
|
||||
plconfig.render_pass_flags = GPUPipeline::NoRenderPassFlags;
|
||||
plconfig.vertex_shader = imgui_vs.get();
|
||||
plconfig.geometry_shader = nullptr;
|
||||
plconfig.fragment_shader = imgui_fs.get();
|
||||
|
||||
s_state.imgui_pipeline = g_gpu_device->CreatePipeline(plconfig, error);
|
||||
if (!s_state.imgui_pipeline)
|
||||
{
|
||||
Error::AddPrefix(error, "Failed to compile ImGui pipeline: ");
|
||||
return false;
|
||||
}
|
||||
|
||||
GL_OBJECT_NAME(s_state.imgui_pipeline, "ImGui Pipeline");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImGuiManager::UpdateImGuiFontTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
||||
unsigned char* pixels;
|
||||
int width, height;
|
||||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
Error error;
|
||||
const bool result = g_gpu_device->ResizeTexture(
|
||||
&s_state.imgui_font_texture, static_cast<u32>(width), static_cast<u32>(height), GPUTexture::Type::Texture,
|
||||
GPUTexture::Format::RGBA8, GPUTexture::Flags::None, pixels, sizeof(u32) * width, &error);
|
||||
if (!result) [[unlikely]]
|
||||
ERROR_LOG("Failed to resize ImGui font texture: {}", error.GetDescription());
|
||||
|
||||
// always update pointer, it could change
|
||||
io.Fonts->SetTexID(s_state.imgui_font_texture.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
void ImGuiManager::CreateDrawLists()
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
ImGui::Render();
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderDrawLists(u32 window_width, u32 window_height, WindowInfo::PreRotation prerotation)
|
||||
{
|
||||
const ImDrawData* draw_data = ImGui::GetDrawData();
|
||||
if (draw_data->CmdListsCount == 0)
|
||||
return;
|
||||
|
||||
const GSVector2i window_size = GSVector2i(static_cast<s32>(window_width), static_cast<s32>(window_height));
|
||||
const u32 post_rotated_width =
|
||||
WindowInfo::ShouldSwapDimensionsForPreRotation(prerotation) ? window_height : window_width;
|
||||
const u32 post_rotated_height =
|
||||
WindowInfo::ShouldSwapDimensionsForPreRotation(prerotation) ? window_width : window_height;
|
||||
|
||||
g_gpu_device->SetViewport(0, 0, static_cast<s32>(post_rotated_width), static_cast<s32>(post_rotated_height));
|
||||
g_gpu_device->SetPipeline(s_state.imgui_pipeline.get());
|
||||
|
||||
const bool prerotated = (prerotation != WindowInfo::PreRotation::Identity);
|
||||
GSMatrix4x4 mproj = GSMatrix4x4::OffCenterOrthographicProjection(0.0f, 0.0f, static_cast<float>(window_width),
|
||||
static_cast<float>(window_height), 0.0f, 1.0f);
|
||||
if (prerotated)
|
||||
mproj = GSMatrix4x4::RotationZ(WindowInfo::GetZRotationForPreRotation(prerotation)) * mproj;
|
||||
g_gpu_device->PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
|
||||
// Render command lists
|
||||
const bool flip = g_gpu_device->UsesLowerLeftOrigin();
|
||||
for (int n = 0; n < draw_data->CmdListsCount; n++)
|
||||
{
|
||||
const ImDrawList* cmd_list = draw_data->CmdLists[n];
|
||||
static_assert(sizeof(ImDrawIdx) == sizeof(GPUDevice::DrawIndex));
|
||||
|
||||
u32 base_vertex, base_index;
|
||||
g_gpu_device->UploadVertexBuffer(cmd_list->VtxBuffer.Data, sizeof(ImDrawVert), cmd_list->VtxBuffer.Size,
|
||||
&base_vertex);
|
||||
g_gpu_device->UploadIndexBuffer(cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size, &base_index);
|
||||
|
||||
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
|
||||
{
|
||||
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
|
||||
|
||||
if ((pcmd->ElemCount == 0 && !pcmd->UserCallback) || pcmd->ClipRect.z <= pcmd->ClipRect.x ||
|
||||
pcmd->ClipRect.w <= pcmd->ClipRect.y)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GSVector4i clip = GSVector4i(GSVector4::load<false>(&pcmd->ClipRect.x));
|
||||
|
||||
if (prerotated)
|
||||
clip = GPUSwapChain::PreRotateClipRect(prerotation, window_size, clip);
|
||||
if (flip)
|
||||
clip = g_gpu_device->FlipToLowerLeft(clip, post_rotated_height);
|
||||
|
||||
g_gpu_device->SetScissor(clip);
|
||||
g_gpu_device->SetTextureSampler(0, reinterpret_cast<GPUTexture*>(pcmd->TextureId),
|
||||
g_gpu_device->GetLinearSampler());
|
||||
|
||||
if (pcmd->UserCallback) [[unlikely]]
|
||||
{
|
||||
pcmd->UserCallback(cmd_list, pcmd);
|
||||
g_gpu_device->PushUniformBuffer(&mproj, sizeof(mproj));
|
||||
g_gpu_device->SetPipeline(s_state.imgui_pipeline.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
g_gpu_device->DrawIndexed(pcmd->ElemCount, base_index + pcmd->IdxOffset, base_vertex + pcmd->VtxOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderDrawLists(GPUSwapChain* swap_chain)
|
||||
{
|
||||
RenderDrawLists(swap_chain->GetWidth(), swap_chain->GetHeight(), swap_chain->GetPreRotation());
|
||||
}
|
||||
|
||||
void ImGuiManager::RenderDrawLists(GPUTexture* texture)
|
||||
{
|
||||
RenderDrawLists(texture->GetWidth(), texture->GetHeight(), WindowInfo::PreRotation::Identity);
|
||||
}
|
||||
|
||||
void ImGuiManager::SetStyle(ImGuiStyle& style, float scale)
|
||||
{
|
||||
style = ImGuiStyle();
|
||||
@ -785,14 +973,18 @@ void ImGuiManager::ReloadFontDataIfActive()
|
||||
ImGui::EndFrame();
|
||||
|
||||
if (!LoadFontData(nullptr))
|
||||
Panic("Failed to load font data");
|
||||
{
|
||||
GPUThread::ReportFatalErrorAndShutdown("Failed to load font data");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AddImGuiFonts(HasDebugFont(), HasFullscreenFonts()))
|
||||
Panic("Failed to create ImGui font text");
|
||||
|
||||
if (!g_gpu_device->UpdateImGuiFontTexture())
|
||||
Panic("Failed to recreate font texture after scale+resize");
|
||||
{
|
||||
GPUThread::ReportFatalErrorAndShutdown("Failed to create ImGui font text");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateImGuiFontTexture();
|
||||
NewFrame();
|
||||
}
|
||||
|
||||
@ -807,11 +999,11 @@ bool ImGuiManager::AddFullscreenFontsIfMissing()
|
||||
const bool debug_font = HasDebugFont();
|
||||
if (!AddImGuiFonts(debug_font, true))
|
||||
{
|
||||
ERROR_LOG("Failed to lazily allocate fullscreen fonts.");
|
||||
GPUThread::ReportFatalErrorAndShutdown("Failed to lazily allocate fullscreen fonts.");
|
||||
AddImGuiFonts(debug_font, false);
|
||||
}
|
||||
|
||||
g_gpu_device->UpdateImGuiFontTexture();
|
||||
UpdateImGuiFontTexture();
|
||||
NewFrame();
|
||||
|
||||
return HasFullscreenFonts();
|
||||
@ -837,7 +1029,7 @@ bool ImGuiManager::AddDebugFontIfMissing()
|
||||
AddImGuiFonts(true, fullscreen_font);
|
||||
}
|
||||
|
||||
g_gpu_device->UpdateImGuiFontTexture();
|
||||
UpdateImGuiFontTexture();
|
||||
NewFrame();
|
||||
|
||||
return HasDebugFont();
|
||||
@ -1592,16 +1784,14 @@ bool ImGuiManager::RenderAuxiliaryRenderWindow(AuxiliaryRenderWindowState* state
|
||||
ImGui::End();
|
||||
ImGui::PopFont();
|
||||
|
||||
CreateDrawLists();
|
||||
|
||||
const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(state->swap_chain.get());
|
||||
if (pres == GPUDevice::PresentResult::OK)
|
||||
{
|
||||
g_gpu_device->RenderImGui(state->swap_chain.get());
|
||||
RenderDrawLists(state->swap_chain.get());
|
||||
g_gpu_device->EndPresent(state->swap_chain.get(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::EndFrame();
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(GetMainContext());
|
||||
return true;
|
||||
|
@ -1,8 +1,10 @@
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/gpu_texture.h"
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
#include <memory>
|
||||
@ -14,6 +16,7 @@ class Error;
|
||||
struct WindowInfo;
|
||||
|
||||
class GPUSwapChain;
|
||||
class GPUTexture;
|
||||
|
||||
struct ImGuiContext;
|
||||
struct ImFont;
|
||||
@ -91,7 +94,7 @@ float GetWindowWidth();
|
||||
float GetWindowHeight();
|
||||
|
||||
/// Updates internal state when the window is size.
|
||||
void WindowResized(float width, float height);
|
||||
void WindowResized(GPUTexture::Format format, float width, float height);
|
||||
|
||||
/// Updates scaling of the on-screen elements.
|
||||
void RequestScaleUpdate();
|
||||
@ -99,6 +102,13 @@ void RequestScaleUpdate();
|
||||
/// Call at the beginning of the frame to set up ImGui state.
|
||||
void NewFrame();
|
||||
|
||||
/// Creates the draw list for the frame, akin to ImGui::Render().
|
||||
void CreateDrawLists();
|
||||
|
||||
/// Renders ImGui screen elements. Call before EndPresent().
|
||||
void RenderDrawLists(GPUSwapChain* swap_chain);
|
||||
void RenderDrawLists(GPUTexture* texture);
|
||||
|
||||
/// Renders any on-screen display elements.
|
||||
void RenderOSDMessages();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user