From f423d151cf475a6f66c3888f670f9f120492ccb8 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Tue, 14 Jan 2025 16:58:30 -0300 Subject: [PATCH] presenter: render the game inside a ImGui window --- src/core/debug_state.h | 6 ++ src/core/devtools/layer.cpp | 11 +-- src/imgui/imgui_config.h | 6 ++ src/imgui/renderer/imgui_core.cpp | 20 +++-- src/imgui/renderer/imgui_core.h | 7 +- src/imgui/renderer/imgui_impl_vulkan.cpp | 48 ++++++------ src/imgui/renderer/imgui_impl_vulkan.h | 18 ++++- src/imgui/renderer/texture_manager.cpp | 5 +- .../renderer_vulkan/vk_instance.cpp | 1 + .../renderer_vulkan/vk_presenter.cpp | 76 +++++++++++++------ src/video_core/renderer_vulkan/vk_presenter.h | 3 + .../renderer_vulkan/vk_swapchain.cpp | 42 ++++++++-- src/video_core/renderer_vulkan/vk_swapchain.h | 7 +- 13 files changed, 175 insertions(+), 75 deletions(-) diff --git a/src/core/debug_state.h b/src/core/debug_state.h index 6a8e15baa..71f48b994 100644 --- a/src/core/debug_state.h +++ b/src/core/debug_state.h @@ -131,6 +131,8 @@ class DebugStateImpl { friend class Core::Devtools::Widget::FrameGraph; friend class Core::Devtools::Widget::ShaderList; + static bool showing_debug_menu_bar; + std::queue debug_message_popup; std::mutex guest_threads_mutex{}; @@ -160,6 +162,10 @@ public: debug_message_popup.push(std::move(message)); } + bool& ShowingDebugMenuBar() { + return showing_debug_menu_bar; + } + void AddCurrentThreadToGuestList(); void RemoveCurrentThreadFromGuestList(); diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 776f3377d..6d0889497 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -24,11 +24,12 @@ using namespace ImGui; using namespace Core::Devtools; using L = Core::Devtools::Layer; +bool DebugStateType::DebugStateImpl::showing_debug_menu_bar = false; + static bool show_simple_fps = false; static bool visibility_toggled = false; static float fps_scale = 1.0f; -static bool show_advanced_debug = false; static int dump_frame_count = 1; static Widget::FrameGraph frame_graph; @@ -265,7 +266,7 @@ static void LoadSettings(const char* line) { return; } if (sscanf(line, "show_advanced_debug=%d", &i) == 1) { - show_advanced_debug = i != 0; + DebugState.ShowingDebugMenuBar() = i != 0; return; } if (sscanf(line, "show_frame_graph=%d", &i) == 1) { @@ -310,7 +311,7 @@ void L::SetupSettings() { handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) { buf->appendf("[%s][Data]\n", handler->TypeName); buf->appendf("fps_scale=%f\n", fps_scale); - buf->appendf("show_advanced_debug=%d\n", show_advanced_debug); + buf->appendf("show_advanced_debug=%d\n", DebugState.ShowingDebugMenuBar()); buf->appendf("show_frame_graph=%d\n", frame_graph.is_open); buf->appendf("dump_frame_count=%d\n", dump_frame_count); buf->append("\n"); @@ -341,7 +342,7 @@ void L::Draw() { if (IsKeyPressed(ImGuiKey_F10, false)) { if (io.KeyCtrl) { - show_advanced_debug = !show_advanced_debug; + DebugState.ShowingDebugMenuBar() ^= true; } else { show_simple_fps = !show_simple_fps; } @@ -376,7 +377,7 @@ void L::Draw() { End(); } - if (show_advanced_debug) { + if (DebugState.ShowingDebugMenuBar()) { PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]); PushID("DevtoolsLayer"); DrawAdvanced(); diff --git a/src/imgui/imgui_config.h b/src/imgui/imgui_config.h index ccb084d94..7b03a4bab 100644 --- a/src/imgui/imgui_config.h +++ b/src/imgui/imgui_config.h @@ -30,6 +30,12 @@ extern void assert_fail_debug_msg(const char* msg); #define IM_VEC4_CLASS_EXTRA \ constexpr ImVec4(float _v) : x(_v), y(_v), z(_v), w(_v) {} +namespace ImGui { +struct Texture; +} +#define ImTextureID ImTextureID +using ImTextureID = ::ImGui::Texture*; + #ifdef IMGUI_USE_WCHAR32 #error "This project uses 16 bits wchar standard like Orbis" #endif \ No newline at end of file diff --git a/src/imgui/renderer/imgui_core.cpp b/src/imgui/renderer/imgui_core.cpp index 335185473..2180b3b4c 100644 --- a/src/imgui/renderer/imgui_core.cpp +++ b/src/imgui/renderer/imgui_core.cpp @@ -6,6 +6,7 @@ #include "common/config.h" #include "common/path_util.h" +#include "core/debug_state.h" #include "core/devtools/layer.h" #include "imgui/imgui_layer.h" #include "imgui_core.h" @@ -167,7 +168,7 @@ bool ProcessEvent(SDL_Event* event) { } } -void NewFrame() { +ImGuiID NewFrame() { { std::scoped_lock lock{change_layers_mutex}; while (!change_layers.empty()) { @@ -185,14 +186,21 @@ void NewFrame() { Sdl::NewFrame(); ImGui::NewFrame(); - DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode); + ImGuiWindowFlags flags = ImGuiDockNodeFlags_PassthruCentralNode; + if (!DebugState.ShowingDebugMenuBar()) { + flags |= ImGuiDockNodeFlags_NoTabBar; + } + ImGuiID dockId = DockSpaceOverViewport(0, GetMainViewport(), flags); for (auto* layer : layers) { layer->Draw(); } + + return dockId; } -void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { +void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, + const vk::Extent2D& extent) { ImGui::Render(); ImDrawData* draw_data = GetDrawData(); if (draw_data->CmdListsCount == 0) { @@ -207,16 +215,16 @@ void Render(const vk::CommandBuffer& cmdbuf, ::Vulkan::Frame* frame) { vk::RenderingAttachmentInfo color_attachments[1]{ { - .imageView = frame->image_view, + .imageView = image_view, .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, - .loadOp = vk::AttachmentLoadOp::eLoad, + .loadOp = vk::AttachmentLoadOp::eClear, .storeOp = vk::AttachmentStoreOp::eStore, }, }; vk::RenderingInfo render_info{}; render_info.renderArea = vk::Rect2D{ .offset = {0, 0}, - .extent = {frame->width, frame->height}, + .extent = extent, }; render_info.layerCount = 1; render_info.colorAttachmentCount = 1; diff --git a/src/imgui/renderer/imgui_core.h b/src/imgui/renderer/imgui_core.h index 9ad708f81..6ad09f4bd 100644 --- a/src/imgui/renderer/imgui_core.h +++ b/src/imgui/renderer/imgui_core.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "video_core/renderer_vulkan/vk_instance.h" #include "vulkan/vulkan_handles.hpp" @@ -24,8 +26,9 @@ void Shutdown(const vk::Device& device); bool ProcessEvent(SDL_Event* event); -void NewFrame(); +ImGuiID NewFrame(); -void Render(const vk::CommandBuffer& cmdbuf, Vulkan::Frame* frame); +void Render(const vk::CommandBuffer& cmdbuf, const vk::ImageView& image_view, + const vk::Extent2D& extent); } // namespace ImGui::Core diff --git a/src/imgui/renderer/imgui_impl_vulkan.cpp b/src/imgui/renderer/imgui_impl_vulkan.cpp index 7f7ade2a5..bd98fa004 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.cpp +++ b/src/imgui/renderer/imgui_impl_vulkan.cpp @@ -57,11 +57,12 @@ struct VkData { vk::DeviceMemory font_memory{}; vk::Image font_image{}; vk::ImageView font_view{}; - vk::DescriptorSet font_descriptor_set{}; + ImTextureID font_texture{}; vk::CommandBuffer font_command_buffer{}; // Render buffers WindowRenderBuffers render_buffers{}; + bool enabled_blending{true}; VkData(const InitInfo init_info) : init_info(init_info) { render_buffers.count = init_info.image_count; @@ -252,8 +253,8 @@ void UploadTextureData::Destroy() { const InitInfo& v = bd->init_info; CheckVkErr(v.device.waitIdle()); - RemoveTexture(descriptor_set); - descriptor_set = VK_NULL_HANDLE; + RemoveTexture(im_texture); + im_texture = nullptr; v.device.destroyImageView(image_view, v.allocator); image_view = VK_NULL_HANDLE; @@ -264,8 +265,8 @@ void UploadTextureData::Destroy() { } // Register a texture -vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, - vk::Sampler sampler) { +ImTextureID AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, + vk::Sampler sampler) { VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; @@ -303,7 +304,9 @@ vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_lay }; v.device.updateDescriptorSets({write_desc}, {}); } - return descriptor_set; + return new Texture{ + .descriptor_set = descriptor_set, + }; } UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height, size_t size) { @@ -370,7 +373,7 @@ UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, } // Create descriptor set (ImTextureID) - info.descriptor_set = AddTexture(info.image_view, vk::ImageLayout::eShaderReadOnlyOptimal); + info.im_texture = AddTexture(info.image_view, vk::ImageLayout::eShaderReadOnlyOptimal); // Create Upload Buffer { @@ -464,10 +467,12 @@ UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, return info; } -void RemoveTexture(vk::DescriptorSet descriptor_set) { +void RemoveTexture(ImTextureID texture) { + IM_ASSERT(texture != nullptr); VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; - v.device.freeDescriptorSets(bd->descriptor_pool, {descriptor_set}); + v.device.freeDescriptorSets(bd->descriptor_pool, {texture->descriptor_set}); + delete texture; } static void CreateOrResizeBuffer(RenderBuffer& rb, size_t new_size, vk::BufferUsageFlagBits usage) { @@ -679,15 +684,11 @@ void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer, command_buffer.setScissor(0, 1, &scissor); // Bind DescriptorSet with font or user texture - vk::DescriptorSet desc_set[1]{(VkDescriptorSet)pcmd->TextureId}; - if (sizeof(ImTextureID) < sizeof(ImU64)) { - // We don't support texture switches if ImTextureID hasn't been redefined to be - // 64-bit. Do a flaky check that other textures haven't been used. - IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->font_descriptor_set); - desc_set[0] = bd->font_descriptor_set; - } + vk::DescriptorSet desc_set[1]{pcmd->TextureId->descriptor_set}; command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, bd->pipeline_layout, 0, {desc_set}, {}); + command_buffer.setColorBlendEnableEXT( + 0, {pcmd->TextureId->disable_blend ? vk::False : vk::True}); // Draw command_buffer.drawIndexed(pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, @@ -709,7 +710,7 @@ static bool CreateFontsTexture() { const InitInfo& v = bd->init_info; // Destroy existing texture (if any) - if (bd->font_view || bd->font_image || bd->font_memory || bd->font_descriptor_set) { + if (bd->font_view || bd->font_image || bd->font_memory || bd->font_texture) { CheckVkErr(v.queue.waitIdle()); DestroyFontsTexture(); } @@ -782,7 +783,7 @@ static bool CreateFontsTexture() { } // Create the Descriptor Set: - bd->font_descriptor_set = AddTexture(bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal); + bd->font_texture = AddTexture(bd->font_view, vk::ImageLayout::eShaderReadOnlyOptimal); // Create the Upload Buffer: vk::DeviceMemory upload_buffer_memory{}; @@ -874,7 +875,7 @@ static bool CreateFontsTexture() { } // Store our identifier - io.Fonts->SetTexID(bd->font_descriptor_set); + io.Fonts->SetTexID(bd->font_texture); // End command buffer vk::SubmitInfo end_info = {}; @@ -898,9 +899,9 @@ static void DestroyFontsTexture() { VkData* bd = GetBackendData(); const InitInfo& v = bd->init_info; - if (bd->font_descriptor_set) { - RemoveTexture(bd->font_descriptor_set); - bd->font_descriptor_set = VK_NULL_HANDLE; + if (bd->font_texture) { + RemoveTexture(bd->font_texture); + bd->font_texture = nullptr; io.Fonts->SetTexID(nullptr); } @@ -1057,9 +1058,10 @@ static void CreatePipeline(vk::Device device, const vk::AllocationCallbacks* all .pAttachments = color_attachment, }; - vk::DynamicState dynamic_states[2]{ + vk::DynamicState dynamic_states[3]{ vk::DynamicState::eViewport, vk::DynamicState::eScissor, + vk::DynamicState::eColorBlendEnableEXT, }; vk::PipelineDynamicStateCreateInfo dynamic_state{ .dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states), diff --git a/src/imgui/renderer/imgui_impl_vulkan.h b/src/imgui/renderer/imgui_impl_vulkan.h index e325e2a8d..05f4489da 100644 --- a/src/imgui/renderer/imgui_impl_vulkan.h +++ b/src/imgui/renderer/imgui_impl_vulkan.h @@ -10,6 +10,13 @@ struct ImDrawData; +namespace ImGui { +struct Texture { + vk::DescriptorSet descriptor_set{nullptr}; + bool disable_blend{false}; +}; +} // namespace ImGui + namespace ImGui::Vulkan { struct InitInfo { @@ -34,29 +41,32 @@ struct InitInfo { struct UploadTextureData { vk::Image image; vk::ImageView image_view; - vk::DescriptorSet descriptor_set; vk::DeviceMemory image_memory; vk::CommandBuffer command_buffer; // Submit to the queue vk::Buffer upload_buffer; vk::DeviceMemory upload_buffer_memory; + ImTextureID im_texture; + void Upload(); void Destroy(); }; -vk::DescriptorSet AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, - vk::Sampler sampler = VK_NULL_HANDLE); +ImTextureID AddTexture(vk::ImageView image_view, vk::ImageLayout image_layout, + vk::Sampler sampler = VK_NULL_HANDLE); UploadTextureData UploadTexture(const void* data, vk::Format format, u32 width, u32 height, size_t size); -void RemoveTexture(vk::DescriptorSet descriptor_set); +void RemoveTexture(ImTextureID descriptor_set); bool Init(InitInfo info); void Shutdown(); void RenderDrawData(ImDrawData& draw_data, vk::CommandBuffer command_buffer, vk::Pipeline pipeline = VK_NULL_HANDLE); +void SetBlendEnabled(bool enabled); + } // namespace ImGui::Vulkan \ No newline at end of file diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index f13c995be..d7516a3a5 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "common/assert.h" #include "common/config.h" #include "common/io_file.h" @@ -123,7 +124,7 @@ static std::deque g_upload_list; namespace Core::TextureManager { Inner::~Inner() { - if (upload_data.descriptor_set != nullptr) { + if (upload_data.im_texture != nullptr) { std::unique_lock lk{g_upload_mtx}; g_upload_list.emplace_back(UploadJob{ .data = this->upload_data, @@ -239,7 +240,7 @@ void Submit() { } if (upload.core != nullptr) { upload.core->upload_data.Upload(); - upload.core->texture_id = upload.core->upload_data.descriptor_set; + upload.core->texture_id = upload.core->upload_data.im_texture; if (upload.core->count.fetch_sub(1) == 1) { delete upload.core; } diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index f5bcb54b6..a9a68d7c7 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -384,6 +384,7 @@ bool Instance::CreateDevice() { }, vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{ .extendedDynamicState3ColorWriteMask = true, + .extendedDynamicState3ColorBlendEnable = true, }, vk::PhysicalDeviceDepthClipControlFeaturesEXT{ .depthClipControl = true, diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index 1679aa691..03e02e5d2 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -20,6 +20,9 @@ #include +#include +#include "imgui/renderer/imgui_impl_vulkan.h" + namespace Vulkan { bool CanBlitToSwapchain(const vk::PhysicalDevice physical_device, vk::Format format) { @@ -324,9 +327,6 @@ Presenter::Presenter(Frontend::WindowSDL& window_, AmdGpu::Liverpool* liverpool_ CreatePostProcessPipeline(); - // Setup ImGui - ImGui::Core::Initialize(instance, window, num_images, - FormatToUnorm(swapchain.GetSurfaceFormat().format)); ImGui::Layer::AddLayer(Common::Singleton::Instance()); } @@ -344,6 +344,9 @@ Presenter::~Presenter() { void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { const vk::Device device = instance.GetDevice(); + if (frame->imgui_texture) { + ImGui::Vulkan::RemoveTexture(frame->imgui_texture); + } if (frame->image_view) { device.destroyImageView(frame->image_view); } @@ -361,7 +364,7 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst | - vk::ImageUsageFlagBits::eTransferSrc, + vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eSampled, }; const VmaAllocationCreateInfo alloc_info = { @@ -403,6 +406,9 @@ void Presenter::RecreateFrame(Frame* frame, u32 width, u32 height) { frame->image_view = view; frame->width = width; frame->height = height; + + frame->imgui_texture = ImGui::Vulkan::AddTexture(view, vk::ImageLayout::eShaderReadOnlyOptimal); + frame->imgui_texture->disable_blend = true; } bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { @@ -499,6 +505,14 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) // Request a free presentation frame. Frame* frame = GetRenderFrame(); + if (image_id != VideoCore::NULL_IMAGE_ID) { + const auto& image = texture_cache.GetImage(image_id); + const auto extent = image.info.size; + if (frame->width != extent.width || frame->height != extent.height) { + RecreateFrame(frame, extent.width, extent.height); + } + } + // EOP flips are triggered from GPU thread so use the drawing scheduler to record // commands. Otherwise we are dealing with a CPU flip which could have arrived // from any guest thread. Use a separate scheduler for that. @@ -636,8 +650,7 @@ void Presenter::Present(Frame* frame) { }; // Recreate the swapchain if the window was resized. - if (window.GetWidth() != swapchain.GetExtent().width || - window.GetHeight() != swapchain.GetExtent().height) { + if (window.GetWidth() != swapchain.GetWidth() || window.GetHeight() != swapchain.GetHeight()) { swapchain.Recreate(window.GetWidth(), window.GetHeight()); } @@ -656,15 +669,14 @@ void Presenter::Present(Frame* frame) { // the frame's present fence and future GetRenderFrame() call will hang waiting for this frame. instance.GetDevice().resetFences(frame->present_done); - ImGui::Core::NewFrame(); + ImGuiID dockId = ImGui::Core::NewFrame(); const vk::Image swapchain_image = swapchain.Image(); + const vk::ImageView swapchain_image_view = swapchain.ImageView(); auto& scheduler = present_scheduler; const auto cmdbuf = scheduler.CommandBuffer(); - ImGui::Core::Render(cmdbuf, frame); - { auto* profiler_ctx = instance.GetProfilerContext(); TracyVkNamedZoneC(profiler_ctx, renderer_gpu_zone, cmdbuf, "Host frame", @@ -674,9 +686,9 @@ void Presenter::Present(Frame* frame) { const std::array pre_barriers{ vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eNone, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = swapchain_image, @@ -690,9 +702,9 @@ void Presenter::Present(Frame* frame) { }, vk::ImageMemoryBarrier{ .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, + .dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead, .oldLayout = vk::ImageLayout::eGeneral, - .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = frame->image, @@ -705,10 +717,11 @@ void Presenter::Present(Frame* frame) { }, }, }; + const vk::ImageMemoryBarrier post_barrier{ - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, + .srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite, .dstAccessMask = vk::AccessFlagBits::eMemoryRead, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::ePresentSrcKHR, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -723,14 +736,29 @@ void Presenter::Present(Frame* frame) { }; cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, - vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::DependencyFlagBits::eByRegion, {}, {}, pre_barriers); - cmdbuf.blitImage( - frame->image, vk::ImageLayout::eTransferSrcOptimal, swapchain_image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitStretch(frame->width, frame->height, extent.width, extent.height), - vk::Filter::eLinear); + { // Draw the game + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{0.0f}); + ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once); + ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav); + + ImVec2 contentArea = ImGui::GetContentRegionMax(); + const vk::Rect2D imgRect = + FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y); + ImGui::SetCursorPos({ + (float)imgRect.offset.x, + (float)imgRect.offset.y, + }); + ImGui::Image(frame->imgui_texture, { + static_cast(imgRect.extent.width), + static_cast(imgRect.extent.height), + }); + ImGui::End(); + ImGui::PopStyleVar(); + } + ImGui::Core::Render(cmdbuf, swapchain_image_view, swapchain.GetExtent()); cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eAllCommands, @@ -790,9 +818,9 @@ Frame* Presenter::GetRenderFrame() { } } - // If the window dimensions changed, recreate this frame - if (frame->width != window.GetWidth() || frame->height != window.GetHeight()) { - RecreateFrame(frame, window.GetWidth(), window.GetHeight()); + // Initialize default frame image + if (frame->width == 0 || frame->height == 0) { + RecreateFrame(frame, 1920, 1080); } return frame; diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index 4c29af0f0..0e0da1867 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -5,6 +5,7 @@ #include +#include "imgui/imgui_config.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -30,6 +31,8 @@ struct Frame { vk::Fence present_done; vk::Semaphore ready_semaphore; u64 ready_tick; + + ImTextureID imgui_texture; }; enum SchedulerType { diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index 8278252ad..1d23e1456 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -4,8 +4,8 @@ #include #include #include "common/assert.h" -#include "common/config.h" #include "common/logging/log.h" +#include "imgui/renderer/imgui_core.h" #include "sdl_window.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_swapchain.h" @@ -15,7 +15,9 @@ namespace Vulkan { Swapchain::Swapchain(const Instance& instance_, const Frontend::WindowSDL& window) : instance{instance_}, surface{CreateSurface(instance.GetInstance(), window)} { FindPresentFormat(); - Create(window.GetWidth(), window.GetHeight(), surface); + + Create(window.GetWidth(), window.GetHeight()); + ImGui::Core::Initialize(instance, window, image_count, surface_format.format); } Swapchain::~Swapchain() { @@ -23,10 +25,9 @@ Swapchain::~Swapchain() { instance.GetInstance().destroySurfaceKHR(surface); } -void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { +void Swapchain::Create(u32 width_, u32 height_) { width = width_; height = height_; - surface = surface_; needs_recreation = false; Destroy(); @@ -86,7 +87,7 @@ void Swapchain::Create(u32 width_, u32 height_, vk::SurfaceKHR surface_) { void Swapchain::Recreate(u32 width_, u32 height_) { LOG_DEBUG(Render_Vulkan, "Recreate the swapchain: width={} height={}", width_, height_); - Create(width_, height_, surface); + Create(width_, height_); } bool Swapchain::AcquireNextImage() { @@ -209,10 +210,14 @@ void Swapchain::Destroy() { if (swapchain) { device.destroySwapchainKHR(swapchain); } - for (u32 i = 0; i < image_count; i++) { - device.destroySemaphore(image_acquired[i]); - device.destroySemaphore(present_ready[i]); + + for (const auto& sem : image_acquired) { + device.destroySemaphore(sem); } + for (const auto& sem : present_ready) { + device.destroySemaphore(sem); + } + image_acquired.clear(); present_ready.clear(); } @@ -249,9 +254,30 @@ void Swapchain::SetupImages() { vk::to_string(images_result)); images = std::move(imgs); image_count = static_cast(images.size()); + images_view.resize(image_count); + for (u32 i = 0; i < image_count; ++i) { + if (images_view[i]) { + device.destroyImageView(images_view[i]); + } + auto [im_view_result, im_view] = device.createImageView(vk::ImageViewCreateInfo{ + .image = images[i], + .viewType = vk::ImageViewType::e2D, + .format = surface_format.format, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .levelCount = 1, + .layerCount = 1, + }, + }); + ASSERT_MSG(im_view_result == vk::Result::eSuccess, "Failed to create image view: {}", + vk::to_string(im_view_result)); + images_view[i] = im_view; + } for (u32 i = 0; i < image_count; ++i) { SetObjectName(device, images[i], "Swapchain Image {}", i); + SetObjectName(device, images_view[i], "Swapchain ImageView {}", i); } } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 19ae9b2d2..a45f83abd 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -23,7 +23,7 @@ public: ~Swapchain(); /// Creates (or recreates) the swapchain with a given size. - void Create(u32 width, u32 height, vk::SurfaceKHR surface); + void Create(u32 width, u32 height); /// Recreates the swapchain with a given size and current surface. void Recreate(u32 width, u32 height); @@ -42,6 +42,10 @@ public: return images[image_index]; } + vk::ImageView ImageView() const { + return images_view[image_index]; + } + vk::SurfaceFormatKHR GetSurfaceFormat() const { return surface_format; } @@ -103,6 +107,7 @@ private: vk::SurfaceTransformFlagBitsKHR transform; vk::CompositeAlphaFlagBitsKHR composite_alpha; std::vector images; + std::vector images_view; std::vector image_acquired; std::vector present_ready; u32 width = 0;