From 17a7c80cb169c3ac757781f2bbfadeae87f6e0c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 Oct 2024 19:36:54 +0100 Subject: [PATCH] Get it rendering on Vulkan and D3D11 (OpenGL is bugged though) --- Common/GPU/D3D11/thin3d_d3d11.cpp | 26 ++++++++++++ Common/GPU/OpenGL/thin3d_gl.cpp | 33 ++++++++++++++- Common/GPU/Vulkan/thin3d_vulkan.cpp | 39 ++++++++++++++++++ Common/GPU/thin3d.h | 10 +++++ UI/EmuScreen.cpp | 1 + ext/imgui/imgui_impl_thin3d.cpp | 63 +++++++++++++++-------------- ext/imgui/imgui_impl_thin3d.h | 3 +- 7 files changed, 143 insertions(+), 32 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 52a510dc5c..f61606415c 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -132,6 +132,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, IndexFormat ifmt) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice draws) override; void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1378,6 +1379,31 @@ 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, IndexFormat ifmt, Slice draws) { + int vbyteSize = vertexCount * curPipeline_->input->stride; + int ibyteSize = indexCount * (ifmt == IndexFormat::U32 ? 4 : 2); + + UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, vbyteSize, Draw::UPDATE_DISCARD); + BindVertexBuffer(upBuffer_, 0); + + UpdateBuffer(upIBuffer_, (const uint8_t *)idata, 0, ibyteSize, Draw::UPDATE_DISCARD); + BindIndexBuffer(upIBuffer_, 0); + // Override the index buffer format. + nextIndexBufferFormat_ = ifmt == IndexFormat::U32 ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT; + + ApplyCurrentState(); + + for (int i = 0; i < draws.size(); i++) { + D3D11_RECT rc; + rc.left = draws[i].clipx; + rc.top = draws[i].clipy; + rc.right = draws[i].clipx + draws[i].clipw; + rc.bottom = draws[i].clipy + draws[i].cliph; + context_->RSSetScissorRects(1, &rc); + context_->DrawIndexed(draws[i].indexCount, draws[i].indexOffset, 0); + } +} + uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const { DXGI_FORMAT giFmt = dataFormatToD3D11(fmt); if (giFmt == DXGI_FORMAT_UNKNOWN) diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index c9fb9eda47..e7e0ecc7f8 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -444,6 +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, IndexFormat ifmt) override; + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice draws); void Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1438,10 +1439,40 @@ void OpenGLContext::DrawIndexedUP(const void *vdata, int vertexCount, const void memcpy(dest, idata, idataSize); ApplySamplers(); - _assert_(curPipeline_->inputLayout); renderManager_.DrawIndexed(curPipeline_->inputLayout->inputLayout_, vbuf, voffset, ibuf, ioffset, curPipeline_->prim, 0, ifmt == IndexFormat::U32 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, vertexCount); } +void OpenGLContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice draws) { + _assert_(curPipeline_->inputLayout != nullptr); + int stride = curPipeline_->inputLayout->stride; + uint32_t vdataSize = stride * vertexCount; + int indexSize = (ifmt == IndexFormat::U32 ? 4 : 2); + uint32_t idataSize = indexCount * indexSize; + + FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; + + GLRBuffer *vbuf; + uint32_t voffset; + uint8_t *dest = frameData.push->Allocate(vdataSize, 4, &vbuf, &voffset); + memcpy(dest, vdata, vdataSize); + + GLRBuffer *ibuf; + uint32_t ioffset; + dest = frameData.push->Allocate(idataSize, 4, &ibuf, &ioffset); + memcpy(dest, idata, idataSize); + + ApplySamplers(); + for (auto &draw : draws) { + GLRect2D scissor; + scissor.x = draw.clipx; + scissor.y = draw.clipy; + scissor.w = draw.clipw; + scissor.h = draw.cliph; + renderManager_.SetScissor(scissor); + renderManager_.DrawIndexed(curPipeline_->inputLayout->inputLayout_, vbuf, voffset, ibuf, ioffset + draw.indexOffset * indexSize, curPipeline_->prim, 0, ifmt == IndexFormat::U32 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT, draw.indexCount); + } +} + void OpenGLContext::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { float col[4]; Uint8x4ToFloat4(col, colorval); diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 5538d78e54..316f976457 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -492,6 +492,8 @@ 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, IndexFormat ifmt) override; + // Specialized for quick IMGUI drawing. + void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice) override; void BindCurrentPipeline(); void ApplyDynamicState(); @@ -1555,6 +1557,43 @@ 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, ifmt == IndexFormat::U32 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16); } +void VKContext::DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice draws) { + _dbg_assert_(vertexCount >= 0); + _dbg_assert_(indexCount >= 0); + if (vertexCount <= 0 || indexCount <= 0 || draws.is_empty()) { + return; + } + + VkBuffer vulkanVbuf, vulkanIbuf, vulkanUBObuf; + size_t vdataSize = vertexCount * curPipeline_->stride; + uint32_t vbBindOffset; + uint8_t *vdataPtr = push_->Allocate(vdataSize, 4, &vulkanVbuf, &vbBindOffset); + _assert_(vdataPtr != nullptr); + memcpy(vdataPtr, vdata, vdataSize); + + int indexSize = (ifmt == IndexFormat::U32 ? 4 : 2); + + size_t idataSize = indexCount * indexSize; + uint32_t ibBindOffset; + uint8_t *idataPtr = push_->Allocate(idataSize, 4, &vulkanIbuf, &ibBindOffset); + _assert_(idataPtr != nullptr); + memcpy(idataPtr, idata, idataSize); + + uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); + + BindCurrentPipeline(); + ApplyDynamicState(); + int descSetIndex; + PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex); + + for (auto &draw : draws) { + BindDescriptors(vulkanUBObuf, descriptors); + renderManager_.SetScissor(draw.clipx, draw.clipy, draw.clipw, draw.cliph); + renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vulkanIbuf, + (int)ibBindOffset + draw.indexOffset * indexSize, draw.indexCount, 1, ifmt == IndexFormat::U32 ? VK_INDEX_TYPE_UINT32 : VK_INDEX_TYPE_UINT16); + } +} + void VKContext::BindCurrentPipeline() { renderManager_.BindPipeline(curPipeline_->pipeline, curPipeline_->flags, pipelineLayout_); } diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 6dd2a7a3b1..d58a97246b 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -699,6 +699,15 @@ struct BackendState { bool valid; }; +struct ClippedDraw { + int indexOffset; + int indexCount; + s16 clipx; + s16 clipy; + s16 clipw; + s16 cliph; +}; + class DrawContext { public: virtual ~DrawContext() = default; @@ -834,6 +843,7 @@ public: virtual void DrawIndexed(int vertexCount, int offset) = 0; // Always 16-bit indices. virtual void DrawUP(const void *vdata, int vertexCount) = 0; virtual void DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt) = 0; // Supports 32-bit indices, for IMGUI use. + virtual void DrawIndexedClippedBatchUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt, Slice draws) {} // 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/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index e60864bb6c..75bafe3d39 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -1590,6 +1590,7 @@ ScreenRenderFlags EmuScreen::render(ScreenRenderMode mode) { } ImGui_ImplPlatform_NewFrame(); + ImGui_ImplThin3d_NewFrame(draw, ui_draw2d.GetDrawMatrix()); // Draw imgui on top ImGui::NewFrame(); diff --git a/ext/imgui/imgui_impl_thin3d.cpp b/ext/imgui/imgui_impl_thin3d.cpp index 803c14470d..1a1b876f14 100644 --- a/ext/imgui/imgui_impl_thin3d.cpp +++ b/ext/imgui/imgui_impl_thin3d.cpp @@ -5,12 +5,14 @@ #include #include "Common/System/Display.h" +#include "Common/Math/lin/matrix4x4.h" // Forward Declarations - bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw); void ImGui_ImplThin3d_DestroyDeviceObjects(Draw::DrawContext *draw); +Lin::Matrix4x4 g_drawMatrix; + struct ImGui_ImplThin3d_Data { Draw::SamplerState *fontSampler = nullptr; Draw::Texture *fontImage = nullptr; @@ -33,10 +35,10 @@ void ImGui_ImplPlatform_NewFrame() { static void ImGui_ImplThin3d_SetupRenderState(Draw::DrawContext *draw, ImDrawData* draw_data, Draw::Pipeline *pipeline, int fb_width, int fb_height) { ImGui_ImplThin3d_Data* bd = ImGui_ImplThin3d_GetBackendData(); - // Bind pipeline: - { - draw->BindPipeline(pipeline); - } + // Bind pipeline and texture + draw->BindPipeline(pipeline); + draw->BindTexture(0, bd->fontImage); + draw->BindSamplerStates(0, 1, &bd->fontSampler); // Setup viewport: { @@ -52,16 +54,14 @@ static void ImGui_ImplThin3d_SetupRenderState(Draw::DrawContext *draw, ImDrawDat // Setup scale and translation: // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + // We currently ignore DisplayPos. { - float scale[2]; - scale[0] = 2.0f / draw_data->DisplaySize.x; - scale[1] = 2.0f / draw_data->DisplaySize.y; - float translate[2]; - translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; - translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; + Lin::Matrix4x4 mtx = ComputeOrthoMatrix(draw_data->DisplaySize.x, draw_data->DisplaySize.y, draw->GetDeviceCaps().coordConvention); - // vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); - // vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + Draw::VsTexColUB ub{}; + memcpy(ub.WorldViewProj, mtx.getReadPtr(), sizeof(Lin::Matrix4x4)); + ub.saturation = 1.0f; + draw->UpdateDynamicUniformBuffer(&ub, sizeof(ub)); } } @@ -82,9 +82,13 @@ void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *d ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + const Draw::IndexFormat idxFormat = sizeof(ImDrawIdx) == 2 ? Draw::IndexFormat::U16 : Draw::IndexFormat::U32; + + std::vector draws; // Render command lists for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; + 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) { @@ -108,17 +112,17 @@ void ImGui_ImplThin3d_RenderDrawData(ImDrawData* draw_data, Draw::DrawContext *d if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) continue; - // Apply scissor/clipping rectangle - draw->SetScissorRect(clip_min.x, clip_min.y, clip_max.x - clip_min.x, clip_max.y - clip_min.y); - draw->BindTexture(0, (Draw::Texture *)pcmd->TextureId); - - - // Draw - // vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); + Draw::ClippedDraw draw; + draw.clipx = clip_min.x; + draw.clipy = clip_min.y; + draw.clipw = clip_max.x - clip_min.x; + draw.cliph = clip_max.y - clip_min.y; + draw.indexCount = pcmd->ElemCount; + draw.indexOffset = pcmd->IdxOffset; + draws.push_back(draw); } } - // global_idx_offset += cmd_list->IdxBuffer.Size; - // global_vtx_offset += cmd_list->VtxBuffer.Size; + draw->DrawIndexedClippedBatchUP(cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.size(), cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.size(), idxFormat, draws); } draw->SetScissorRect(0, 0, fb_width, fb_height); @@ -159,8 +163,7 @@ bool ImGui_ImplThin3d_CreateFontsTexture(Draw::DrawContext *draw) { } // You probably never need to call this, as it is called by ImGui_ImplThin3d_CreateFontsTexture() and ImGui_ImplThin3d_Shutdown(). -void ImGui_ImplThin3d_DestroyFontsTexture(Draw::DrawContext *draw) -{ +void ImGui_ImplThin3d_DestroyFontsTexture(Draw::DrawContext *draw) { ImGuiIO& io = ImGui::GetIO(); ImGui_ImplThin3d_Data* bd = ImGui_ImplThin3d_GetBackendData(); if (bd->fontImage) { @@ -209,8 +212,7 @@ static void ImGui_ImplThin3d_CreatePipeline(Draw::DrawContext *draw) { bd->pipeline = draw->CreateGraphicsPipeline(pipelineDesc, "imgui-pipeline"); } -bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) -{ +bool ImGui_ImplThin3d_CreateDeviceObjects(Draw::DrawContext *draw) { ImGui_ImplThin3d_Data* bd = ImGui_ImplThin3d_GetBackendData(); if (!bd->fontSampler) { @@ -237,6 +239,8 @@ void ImGui_ImplThin3d_DestroyDeviceObjects(Draw::DrawContext *draw) { ImGui_ImplThin3d_DestroyFontsTexture(draw); bd->pipeline->Release(); bd->pipeline = nullptr; + bd->fontSampler->Release(); + bd->fontSampler = nullptr; } bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw) { @@ -253,8 +257,7 @@ bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw) { return true; } -void ImGui_ImplThin3d_Shutdown(Draw::DrawContext *draw) -{ +void ImGui_ImplThin3d_Shutdown(Draw::DrawContext *draw) { ImGui_ImplThin3d_Data* bd = ImGui_ImplThin3d_GetBackendData(); IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); @@ -266,12 +269,12 @@ void ImGui_ImplThin3d_Shutdown(Draw::DrawContext *draw) IM_DELETE(bd); } -void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw) -{ +void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw, Lin::Matrix4x4 drawMatrix) { ImGui_ImplThin3d_Data* bd = ImGui_ImplThin3d_GetBackendData(); IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplThin3d_Init()?"); if (!bd->fontImage) ImGui_ImplThin3d_CreateFontsTexture(draw); + g_drawMatrix = drawMatrix; } // Register a texture. No-op. diff --git a/ext/imgui/imgui_impl_thin3d.h b/ext/imgui/imgui_impl_thin3d.h index 5e3c615744..03ae1c750a 100644 --- a/ext/imgui/imgui_impl_thin3d.h +++ b/ext/imgui/imgui_impl_thin3d.h @@ -31,11 +31,12 @@ #include "imgui.h" // IMGUI_IMPL_API #include "Common/GPU/thin3d.h" +#include "Common/Math/lin/matrix4x4.h" // Called by user code IMGUI_IMPL_API bool ImGui_ImplThin3d_Init(Draw::DrawContext *draw); IMGUI_IMPL_API void ImGui_ImplThin3d_Shutdown(Draw::DrawContext *draw); -IMGUI_IMPL_API void ImGui_ImplThin3d_NewFrame(Draw::DrawContext *draw); +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_CreateFontsTexture(Draw::DrawContext *draw); IMGUI_IMPL_API void ImGui_ImplThin3d_DestroyFontsTexture(Draw::DrawContext *draw);