diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 4192f7b3be..a911fa80ac 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -164,7 +164,7 @@ public: void DrawIndexed(int indexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount) override; - void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1407,7 +1407,13 @@ void D3D11DrawContext::DrawIndexedUP(const void *vdata, int vertexCount, const v DrawIndexed(indexCount, 0); } -void D3D11DrawContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) { +void D3D11DrawContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) { + if (draws.is_empty() || !vertexCount || !indexCount) { + return; + } + + curPipeline_ = (D3D11Pipeline *)draws[0].pipeline; + int vbyteSize = vertexCount * curPipeline_->input->stride; int ibyteSize = indexCount * sizeof(u16); @@ -1417,9 +1423,16 @@ void D3D11DrawContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCo UpdateBuffer(upIBuffer_, (const uint8_t *)idata, 0, ibyteSize, Draw::UPDATE_DISCARD); BindIndexBuffer(upIBuffer_, 0); + UpdateDynamicUniformBuffer(ub, ubSize); ApplyCurrentState(); for (int i = 0; i < draws.size(); i++) { + if (draws[i].pipeline != curPipeline_) { + curPipeline_ = (D3D11Pipeline *)draws[i].pipeline; + ApplyCurrentState(); + UpdateDynamicUniformBuffer(ub, ubSize); + } + if (draws[i].bindTexture) { ID3D11ShaderResourceView *view = ((D3D11Texture *)draws[i].bindTexture)->View(); context_->PSSetShaderResources(0, 1, &view); @@ -1427,6 +1440,8 @@ void D3D11DrawContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCo ID3D11ShaderResourceView *view = ((D3D11Framebuffer *)draws[i].bindFramebufferAsTex)->colorSRView; context_->PSSetShaderResources(0, 1, &view); } + ID3D11SamplerState *sstate = ((D3D11SamplerState *)draws[i].samplerState)->ss; + context_->PSSetSamplers(0, 1, &sstate); D3D11_RECT rc; rc.left = draws[i].clipx; rc.top = draws[i].clipy; diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 733ec78c2f..72644baff9 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -574,7 +574,7 @@ public: void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount) override; - void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1203,16 +1203,29 @@ void D3D9Context::DrawIndexedUP(const void *vdata, int vertexCount, const void * vdata, curPipeline_->inputLayout->GetStride()); } -void D3D9Context::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) { +void D3D9Context::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) { + if (draws.is_empty() || !vertexCount || !indexCount) { + return; + } + + BindPipeline(draws[0].pipeline); curPipeline_->inputLayout->Apply(device_); curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); ApplyDynamicState(); + UpdateDynamicUniformBuffer(ub, ubSize); - // Suboptimal! + // Suboptimal! Should dirty-track textures. for (int i = 0; i < draws.size(); i++) { + if (draws[i].pipeline != curPipeline_) { + D3D9Pipeline *d3d9Pipeline = (D3D9Pipeline *)draws[i].pipeline; + d3d9Pipeline->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); + curPipeline_ = d3d9Pipeline; + } + if (draws[i].bindTexture) { device_->SetTexture(0, ((D3D9Texture *)draws[i].bindTexture)->TexturePtr()); } else if (draws[i].bindFramebufferAsTex) { + // We ignore aspect in D3D9 :( device_->SetTexture(0, ((D3D9Framebuffer *)draws[i].bindFramebufferAsTex)->tex.Get()); } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index bfb61dbaf6..281eee3789 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -444,7 +444,7 @@ public: void DrawIndexed(int vertexCount, int offset) override; void DrawUP(const void *vdata, int vertexCount) override; void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount) override; - void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1442,7 +1442,14 @@ void OpenGLContext::DrawIndexedUP(const void *vdata, int vertexCount, const void renderManager_.DrawIndexed(curPipeline_->inputLayout->inputLayout_, vbuf, voffset, ibuf, ioffset, curPipeline_->prim, indexCount, GL_UNSIGNED_SHORT, 1); } -void OpenGLContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) { +void OpenGLContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) { + if (draws.is_empty() || !vertexCount || !indexCount) { + return; + } + + BindPipeline(draws[0].pipeline); + UpdateDynamicUniformBuffer(ub, ubSize); + _assert_(curPipeline_->inputLayout != nullptr); int stride = curPipeline_->inputLayout->stride; uint32_t vdataSize = stride * vertexCount; @@ -1463,6 +1470,12 @@ void OpenGLContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount ApplySamplers(); for (auto &draw : draws) { + if (draw.pipeline != curPipeline_) { + OpenGLPipeline *glPipeline = (OpenGLPipeline *)draw.pipeline; + _dbg_assert_(glPipeline->inputLayout->stride == stride); + BindPipeline(glPipeline); // this updated curPipeline_. + UpdateDynamicUniformBuffer(ub, ubSize); + } if (draw.bindTexture) { renderManager_.BindTexture(0, ((OpenGLTexture *)draw.bindTexture)->GetTex()); } else if (draw.bindFramebufferAsTex) { diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index c8f9194251..5d0a8b8c86 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -493,7 +493,7 @@ public: void DrawUP(const void *vdata, int vertexCount) override; void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount) override; // Specialized for quick IMGUI drawing. - void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice, const void *dynUniforms, size_t size) override; void BindCurrentPipeline(); void ApplyDynamicState(); @@ -1557,13 +1557,15 @@ void VKContext::DrawIndexedUP(const void *vdata, int vertexCount, const void *id renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, (int)ibBindOffset, indexCount, 1); } -void VKContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) { +void VKContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *ub, size_t ubSize) { _dbg_assert_(vertexCount >= 0); _dbg_assert_(indexCount >= 0); if (vertexCount <= 0 || indexCount <= 0 || draws.is_empty()) { return; } + curPipeline_ = (VKPipeline *)draws[0].pipeline; + VkBuffer vulkanVbuf, vulkanIbuf, vulkanUBObuf; size_t vdataSize = vertexCount * curPipeline_->stride; uint32_t vbBindOffset; @@ -1579,18 +1581,28 @@ void VKContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, co _assert_(idataPtr != nullptr); memcpy(idataPtr, idata, idataSize); + curPipeline_->SetDynamicUniformData(ub, ubSize); + uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); BindCurrentPipeline(); ApplyDynamicState(); for (auto &draw : draws) { + if (draw.pipeline != curPipeline_) { + VKPipeline *vkPipe = (VKPipeline *)draw.pipeline; + renderManager_.BindPipeline(vkPipe->pipeline, vkPipe->flags, pipelineLayout_); + curPipeline_ = (VKPipeline *)draw.pipeline; + curPipeline_->SetDynamicUniformData(ub, ubSize); + } // TODO: Dirty-check these. if (draw.bindTexture) { BindTexture(0, draw.bindTexture); } else if (draw.bindFramebufferAsTex) { BindFramebufferAsTexture(draw.bindFramebufferAsTex, 0, FBChannel::FB_COLOR_BIT, 0); } + Draw::SamplerState *sstate = draw.samplerState; + BindSamplerStates(0, 1, &sstate); int descSetIndex; PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex); BindDescriptors(vulkanUBObuf, descriptors); diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 3e2076cc8c..c1210c7d1c 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -705,6 +705,8 @@ struct ClippedDraw { s16 cliph; Draw::Texture *bindTexture; Draw::Framebuffer *bindFramebufferAsTex; + Draw::SamplerState *samplerState; + Draw::Pipeline *pipeline; }; class DrawContext { @@ -843,7 +845,7 @@ public: virtual void DrawUP(const void *vdata, int vertexCount) = 0; virtual void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount) = 0; // Intended for ImGui display lists, easier to do optimally this way. - virtual void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws) = 0; + virtual void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, Slice draws, const void *dynUniforms, size_t size) = 0; // Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here. virtual void BeginFrame(DebugFlags debugFlags) = 0; diff --git a/GPU/Common/FramebufferManagerCommon.cpp b/GPU/Common/FramebufferManagerCommon.cpp index bc3674a901..c06e73b7c6 100644 --- a/GPU/Common/FramebufferManagerCommon.cpp +++ b/GPU/Common/FramebufferManagerCommon.cpp @@ -3667,13 +3667,12 @@ static void ApplyKillzoneFramebufferSplit(FramebufferHeuristicParams *params, in void FramebufferManagerCommon::DrawImGuiDebug(int &selected) const { ImGui::BeginTable("framebuffers", 4); - ImGui::TableSetupColumn("Tag"); - ImGui::TableSetupColumn("Color Addr"); - ImGui::TableSetupColumn("Depth Addr"); - ImGui::TableSetupColumn("Size"); + ImGui::TableSetupColumn("Tag", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Color Addr", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Depth Addr", ImGuiTableColumnFlags_WidthFixed); + ImGui::TableSetupColumn("Size", ImGuiTableColumnFlags_WidthFixed); ImGui::TableHeadersRow(); - ImGui::TableSetColumnIndex(0); for (int i = 0; i < (int)vfbs_.size(); i++) { ImGui::TableNextRow(); @@ -3705,10 +3704,15 @@ void FramebufferManagerCommon::DrawImGuiDebug(int &selected) const { } ImGui::EndTable(); + // Fix out-of-bounds issues when framebuffers are removed. + if (selected >= vfbs_.size()) { + selected = -1; + } + if (selected != -1) { // Now, draw the image of the selected framebuffer. Draw::Framebuffer *fb = vfbs_[selected]->fbo; - ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(fb); + ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(fb, Draw::FB_COLOR_BIT, ImGuiPipeline::TexturedOpaque); ImGui::Image(texId, ImVec2(fb->Width(), fb->Height())); } } diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 5607252c2b..6742423dee 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -4,6 +4,7 @@ #include "ext/imgui/imgui_internal.h" #include "Common/StringUtils.h" +#include "Common/Data/Format/IniFile.h" #include "Core/Config.h" #include "Core/System.h" #include "Core/RetroAchievements.h" @@ -230,7 +231,7 @@ static void DrawFilesystemBrowser(ImConfig &cfg) { } static void DrawKernelObjects(ImConfig &cfg) { - if (!ImGui::Begin("Kernel Objects", &cfg.filesystemBrowserOpen)) { + if (!ImGui::Begin("Kernel Objects", &cfg.kernelObjectsOpen)) { ImGui::End(); return; } @@ -691,6 +692,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::EndMenu(); } if (ImGui::BeginMenu("Ge (GPU)")) { + ImGui::MenuItem("Display Output", nullptr, &cfg_.displayOpen); ImGui::MenuItem("Framebuffers", nullptr, &cfg_.framebuffersOpen); // More to come here... ImGui::EndMenu(); @@ -762,6 +764,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu DrawFramebuffersWindow(cfg_, gpuDebug->GetFramebufferManagerCommon()); } + if (cfg_.displayOpen) { + DrawDisplayWindow(cfg_, gpuDebug->GetFramebufferManagerCommon()); + } + if (cfg_.structViewerOpen) { structViewer_.Draw(mipsDebug, &cfg_.structViewerOpen); } @@ -914,3 +920,7 @@ void ImDisasmWindow::Draw(MIPSDebugInterface *mipsDebug, bool *open, CoreState c } ImGui::End(); } + +void ImDebugger::LoadConfig() { + IniFile ini; +} diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index a1ba864dd9..ee2ed91ccc 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -37,6 +37,7 @@ public: void DirtySymbolMap() { symsDirty_ = true; } + private: // We just keep the state directly in the window. Can refactor later. @@ -56,11 +57,6 @@ private: char searchTerm_[64]{}; }; -class ImLuaConsole { -public: - // Stub -}; - struct ImConfig { bool disasmOpen = true; bool demoOpen = false; @@ -73,6 +69,7 @@ struct ImConfig { bool atracOpen = true; bool structViewerOpen = false; bool framebuffersOpen = false; + bool displayOpen = false; bool styleEditorOpen = false; bool filesystemBrowserOpen = false; bool kernelObjectsOpen = false; @@ -86,6 +83,8 @@ struct ImConfig { int selectedFramebuffer = -1; int selectedBreakpoint = -1; int selectedMemCheck = -1; + + bool displayLatched = false; }; enum ImUiCmd { @@ -96,14 +95,20 @@ struct ImUiCommand { ImUiCmd cmd; }; -struct ImDebugger { +class ImDebugger { +public: ImDebugger(); + void Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebug); +private: + // We use a separate ini file from the main PPSSPP config. + void LoadConfig(); + void SaveConfig(); + RequesterToken reqToken_; ImDisasmWindow disasm_; - ImLuaConsole luaConsole_; ImStructViewer structViewer_; // Open variables. diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index 0945030ca0..3b096b650c 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -1,7 +1,11 @@ +#include "ext/imgui/imgui.h" +#include "ext/imgui/imgui_impl_thin3d.h" #include "UI/ImDebugger/ImGe.h" #include "UI/ImDebugger/ImDebugger.h" #include "GPU/Common/FramebufferManagerCommon.h" +#include "Core/HLE/sceDisplay.h" + void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager) { if (!ImGui::Begin("Framebuffers", &cfg.framebuffersOpen)) { ImGui::End(); @@ -12,3 +16,30 @@ void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebuffer ImGui::End(); } + +void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager) { + if (!ImGui::Begin("Display", &cfg.framebuffersOpen)) { + ImGui::End(); + return; + } + + ImGui::Checkbox("Display latched", &cfg.displayLatched); + + PSPPointer topaddr; + u32 linesize; + u32 pixelFormat; + + __DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, cfg.displayLatched); + + VirtualFramebuffer *fb = framebufferManager->GetVFBAt(topaddr.ptr); + if (fb && fb->fbo) { + ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(fb->fbo, Draw::FB_COLOR_BIT, ImGuiPipeline::TexturedOpaque); + ImGui::Image(texId, ImVec2(fb->width, fb->height)); + ImGui::Text("%s - %08x", fb->fbo->Tag(), topaddr.ptr); + } else { + // TODO: Sometimes we should display RAM here. + ImGui::Text("Framebuffer not available to display"); + } + + ImGui::End(); +} diff --git a/UI/ImDebugger/ImGe.h b/UI/ImDebugger/ImGe.h index 958465ba17..08868b7f0a 100644 --- a/UI/ImDebugger/ImGe.h +++ b/UI/ImDebugger/ImGe.h @@ -7,6 +7,7 @@ struct ImConfig; class FramebufferManagerCommon; void DrawFramebuffersWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager); +void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManager); class ImGeDebugger { public: diff --git a/ext/imgui/imgui_impl_thin3d.cpp b/ext/imgui/imgui_impl_thin3d.cpp index ac0fc0a694..18c0a4fef4 100644 --- a/ext/imgui/imgui_impl_thin3d.cpp +++ b/ext/imgui/imgui_impl_thin3d.cpp @@ -16,14 +16,18 @@ struct RegisteredTexture { bool isFramebuffer; union { Draw::Texture *texture; - Draw::Framebuffer *framebuffer; + struct { + Draw::Framebuffer *framebuffer; + Draw::FBChannel aspect; + }; }; + ImGuiPipeline pipeline; }; struct BackendData { Draw::SamplerState *fontSampler = nullptr; Draw::Texture *fontImage = nullptr; - Draw::Pipeline *pipeline = nullptr; + Draw::Pipeline *pipelines[2]{}; std::vector tempTextures; }; @@ -36,39 +40,6 @@ static BackendData *ImGui_ImplThin3d_GetBackendData() { return ImGui::GetCurrentContext() ? (BackendData *)ImGui::GetIO().BackendRendererUserData : nullptr; } -static void ImGui_ImplThin3d_SetupRenderState(Draw::DrawContext *draw, ImDrawData* drawData, Draw::Pipeline *pipeline, int fb_width, int fb_height) { - BackendData *bd = ImGui_ImplThin3d_GetBackendData(); - - // Bind pipeline and texture - draw->BindPipeline(pipeline); - draw->BindSamplerStates(0, 1, &bd->fontSampler); - - // Setup viewport - { - Draw::Viewport viewport; - viewport.TopLeftX = 0; - viewport.TopLeftY = 0; - viewport.Width = (float)fb_width; - viewport.Height = (float)fb_height; - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - draw->SetViewport(viewport); - } - - // Setup scale and translation: - // Our visible imgui space lies from drawData->DisplayPps (top left) to drawData->DisplayPos + drawData->DisplaySize (bottom right). - // DisplayPos is (0,0) for single viewport apps. We currently ignore DisplayPos. - // We probably only need to do this at the start of the frame. - { - Lin::Matrix4x4 mtx = ComputeOrthoMatrix(drawData->DisplaySize.x, drawData->DisplaySize.y, draw->GetDeviceCaps().coordConvention); - - Draw::VsTexColUB ub{}; - memcpy(ub.WorldViewProj, mtx.getReadPtr(), sizeof(Lin::Matrix4x4)); - ub.saturation = 1.0f; - draw->UpdateDynamicUniformBuffer(&ub, sizeof(ub)); - } -} - // Render function void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *draw) { // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) @@ -79,9 +50,23 @@ void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *d } BackendData* bd = ImGui_ImplThin3d_GetBackendData(); + draw->BindSamplerStates(0, 1, &bd->fontSampler); - // Setup desired Vulkan state - ImGui_ImplThin3d_SetupRenderState(draw, draw_data, bd->pipeline, fb_width, fb_height); + // Setup viewport + Draw::Viewport viewport; + viewport.TopLeftX = 0; + viewport.TopLeftY = 0; + viewport.Width = (float)fb_width; + viewport.Height = (float)fb_height; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + draw->SetViewport(viewport); + + Lin::Matrix4x4 mtx = ComputeOrthoMatrix(draw_data->DisplaySize.x, draw_data->DisplaySize.y, draw->GetDeviceCaps().coordConvention); + + Draw::VsTexColUB ub{}; + memcpy(ub.WorldViewProj, mtx.getReadPtr(), sizeof(Lin::Matrix4x4)); + ub.saturation = 1.0f; // Will project scissor/clipping rectangles into framebuffer space ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports @@ -94,6 +79,8 @@ void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *d std::vector draws; Draw::Texture *boundTexture; Draw::Framebuffer *boundFBAsTexture; + Draw::Pipeline *boundPipeline = bd->pipelines[0]; + Draw::SamplerState *boundSampler = bd->fontSampler; // Render command lists for (int n = 0; n < draw_data->CmdListsCount; n++) { @@ -101,55 +88,56 @@ void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *d draws.clear(); for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != nullptr) { - // User callback, registered via ImDrawList::AddCallback() - // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) - if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) { - ImGui_ImplThin3d_SetupRenderState(draw, draw_data, bd->pipeline, fb_width, fb_height); - } else { - pcmd->UserCallback(cmd_list, pcmd); - } + // We don't use the callback mechanism. + _dbg_assert_(pcmd->UserCallback == nullptr); + + // Update the texture pointers. + if (!pcmd->TextureId) { + // Default + boundTexture = bd->fontImage; + boundFBAsTexture = nullptr; + boundPipeline = bd->pipelines[0]; + boundSampler = bd->fontSampler; } else { - // Update the texture pointers. - if (!pcmd->TextureId) { - boundTexture = bd->fontImage; - boundFBAsTexture = nullptr; + size_t index = (size_t)pcmd->TextureId - TEX_ID_OFFSET; + _dbg_assert_(index < bd->tempTextures.size()); + if (bd->tempTextures[index].framebuffer) { + boundFBAsTexture = bd->tempTextures[index].framebuffer; + boundTexture = nullptr; } else { - size_t index = (size_t)pcmd->TextureId - TEX_ID_OFFSET; - _dbg_assert_(index < bd->tempTextures.size()); - if (bd->tempTextures[index].framebuffer) { - boundFBAsTexture = bd->tempTextures[index].framebuffer; - boundTexture = nullptr; - } else { - boundTexture = bd->tempTextures[index].texture; - boundFBAsTexture = nullptr; - } + boundTexture = bd->tempTextures[index].texture; + boundFBAsTexture = nullptr; } - // Project scissor/clipping rectangles into framebuffer space - ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); - ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); - - // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds - if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } - if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } - if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } - if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } - if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) - continue; - - Draw::ClippedDraw clippedDraw; - clippedDraw.bindTexture = boundTexture; - clippedDraw.bindFramebufferAsTex = boundFBAsTexture; - clippedDraw.clipx = clip_min.x; - clippedDraw.clipy = clip_min.y; - clippedDraw.clipw = clip_max.x - clip_min.x; - clippedDraw.cliph = clip_max.y - clip_min.y; - clippedDraw.indexCount = pcmd->ElemCount; - clippedDraw.indexOffset = pcmd->IdxOffset; - draws.push_back(clippedDraw); + boundPipeline = bd->pipelines[(int)bd->tempTextures[index].pipeline]; + boundSampler = bd->fontSampler; } + + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + + // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + Draw::ClippedDraw clippedDraw; + clippedDraw.pipeline = boundPipeline; + clippedDraw.bindTexture = boundTexture; + clippedDraw.bindFramebufferAsTex = boundFBAsTexture; + clippedDraw.samplerState = boundSampler; + clippedDraw.clipx = clip_min.x; + clippedDraw.clipy = clip_min.y; + clippedDraw.clipw = clip_max.x - clip_min.x; + clippedDraw.cliph = clip_max.y - clip_min.y; + clippedDraw.indexCount = pcmd->ElemCount; + clippedDraw.indexOffset = pcmd->IdxOffset; + draws.push_back(clippedDraw); } - draw->DrawIndexedClippedBatchUP(cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.size(), cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.size(), draws); + draw->DrawIndexedClippedBatchUP(cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.size(), cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.size(), draws, &ub, sizeof(ub)); } draw->SetScissorRect(0, 0, fb_width, fb_height); @@ -174,7 +162,7 @@ bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) { bd->fontSampler = draw->CreateSamplerState(desc); } - if (!bd->pipeline) { + if (!bd->pipelines[0]) { BackendData* bd = ImGui_ImplThin3d_GetBackendData(); using namespace Draw; @@ -193,6 +181,7 @@ bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) { BlendFactor::SRC_ALPHA, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendOp::ADD, BlendFactor::ONE, BlendFactor::ONE_MINUS_SRC_ALPHA, BlendOp::ADD, }); + BlendState *blendOpaque = draw->CreateBlendState({ false, 0xF }); DepthStencilStateDesc dsDesc{}; DepthStencilState *depthStencil = draw->CreateDepthStencilState(dsDesc); @@ -211,7 +200,15 @@ bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) { &vsTexColBufDesc }; - bd->pipeline = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline"); + bd->pipelines[0] = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline"); + pipelineDesc.blend = blendOpaque; + bd->pipelines[1] = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline-opaque"); + + inputLayout->Release(); + blend->Release(); + blendOpaque->Release(); + depthStencil->Release(); + rasterNoCull->Release(); } if (!bd->fontImage) { @@ -247,9 +244,11 @@ void ImGui_ImplThin3d_DestroyDeviceObjects() { bd->fontImage = nullptr; io.Fonts->SetTexID(0); } - if (bd->pipeline) { - bd->pipeline->Release(); - bd->pipeline = nullptr; + for (int i = 0; i < ARRAY_SIZE(bd->pipelines); i++) { + if (bd->pipelines[i]) { + bd->pipelines[i]->Release(); + bd->pipelines[i] = nullptr; + } } if (bd->fontSampler) { bd->fontSampler->Release(); @@ -310,21 +309,24 @@ void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw, Lin::Matrix4x4 drawMatri g_drawMatrix = drawMatrix; } -ImTextureID ImGui_ImplThin3d_AddTextureTemp(Draw::Texture *texture) { +ImTextureID ImGui_ImplThin3d_AddTextureTemp(Draw::Texture *texture, ImGuiPipeline pipeline) { BackendData* bd = ImGui_ImplThin3d_GetBackendData(); RegisteredTexture tex{ false }; tex.texture = texture; + tex.pipeline = pipeline; bd->tempTextures.push_back(tex); return (ImTextureID)(uint64_t)(TEX_ID_OFFSET + bd->tempTextures.size() - 1); } -ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer *framebuffer) { +ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer *framebuffer, Draw::FBChannel aspect, ImGuiPipeline pipeline) { BackendData* bd = ImGui_ImplThin3d_GetBackendData(); RegisteredTexture tex{ true }; tex.framebuffer = framebuffer; + tex.aspect = aspect; + tex.pipeline = pipeline; bd->tempTextures.push_back(tex); return (ImTextureID)(uint64_t)(TEX_ID_OFFSET + bd->tempTextures.size() - 1); diff --git a/ext/imgui/imgui_impl_thin3d.h b/ext/imgui/imgui_impl_thin3d.h index 98cad60f64..11096c9dfd 100644 --- a/ext/imgui/imgui_impl_thin3d.h +++ b/ext/imgui/imgui_impl_thin3d.h @@ -34,28 +34,32 @@ #include "Common/Math/lin/matrix4x4.h" // Called by user code. Takes ownership of the font buffer and later deletes it. -IMGUI_IMPL_API bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw, const uint8_t *ttf_font, size_t size); -IMGUI_IMPL_API void ImGui_ImplThin3d_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw, Lin::Matrix4x4 drawMatrix); -IMGUI_IMPL_API void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *draw); -IMGUI_IMPL_API bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw); -IMGUI_IMPL_API void ImGui_ImplThin3d_DestroyDeviceObjects(); +IMGUI_IMPL_API bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw, const uint8_t *ttf_font, size_t size); +IMGUI_IMPL_API void ImGui_ImplThin3d_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw, Lin::Matrix4x4 drawMatrix); +IMGUI_IMPL_API void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *draw); +IMGUI_IMPL_API bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw); +IMGUI_IMPL_API void ImGui_ImplThin3d_DestroyDeviceObjects(); + +enum class ImGuiPipeline { + TexturedAlphaBlend = 0, + TexturedOpaque = 1, +}; // These register a texture for imgui drawing, but just for the current frame. // Textures are unregistered again in RenderDrawData. This is just simpler. -IMGUI_IMPL_API ImTextureID ImGui_ImplThin3d_AddTextureTemp(Draw::Texture *texture); -IMGUI_IMPL_API ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer *framebuffer); +IMGUI_IMPL_API ImTextureID ImGui_ImplThin3d_AddTextureTemp(Draw::Texture *texture, ImGuiPipeline pipeline = ImGuiPipeline::TexturedAlphaBlend); +IMGUI_IMPL_API ImTextureID ImGui_ImplThin3d_AddFBAsTextureTemp(Draw::Framebuffer *framebuffer, Draw::FBChannel aspect = Draw::FB_COLOR_BIT, ImGuiPipeline pipeline = ImGuiPipeline::TexturedAlphaBlend); void ImGui_PushFixedFont(); void ImGui_PopFont(); // Helper structure to hold the data needed by one rendering context into one OS window // (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) -struct ImGui_ImplThin3dH_Window -{ - int Width = 0; - int Height = 0; - bool ClearEnable = true; +struct ImGui_ImplThin3dH_Window { + int Width = 0; + int Height = 0; + bool ClearEnable = true; }; #endif // #ifndef IMGUI_DISABLE