From 8f8598c347ea14d8ec6d92bf0dfa77c8d9e6f577 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 30 Oct 2024 10:11:05 +0100 Subject: [PATCH] thin3d: Add partial support for 32-bit index buffers. Add DrawIndexedUP. --- Common/GPU/D3D11/thin3d_d3d11.cpp | 25 +++++++++++++++--- Common/GPU/D3D9/thin3d_d3d9.cpp | 11 ++++++++ Common/GPU/OpenGL/thin3d_gl.cpp | 24 +++++++++++++++++ Common/GPU/Vulkan/VulkanQueueRunner.cpp | 2 +- Common/GPU/Vulkan/VulkanQueueRunner.h | 1 + Common/GPU/Vulkan/VulkanRenderManager.h | 3 ++- Common/GPU/Vulkan/thin3d_vulkan.cpp | 35 +++++++++++++++++++++++-- Common/GPU/thin3d.h | 8 +++++- GPU/Vulkan/DrawEngineVulkan.cpp | 4 +-- 9 files changed, 103 insertions(+), 10 deletions(-) diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 0bdb88354e..b4304d1226 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -131,6 +131,8 @@ public: void Draw(int vertexCount, int offset) override; 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 Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; void BeginFrame(DebugFlags debugFlags) override; @@ -218,6 +220,7 @@ private: bool dirtyIndexBuffer_ = false; ID3D11Buffer *nextIndexBuffer_ = nullptr; + DXGI_FORMAT nextIndexBufferFormat_ = DXGI_FORMAT_R16_UINT; UINT nextIndexBufferOffset_ = 0; InvalidationCallback invalidationCallback_; @@ -235,6 +238,7 @@ private: // Temporaries ID3D11Texture2D *packTexture_ = nullptr; Buffer *upBuffer_ = nullptr; + Buffer *upIBuffer_ = nullptr; // System info D3D_FEATURE_LEVEL featureLevel_; @@ -351,6 +355,7 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de const size_t UP_MAX_BYTES = 65536 * 24; upBuffer_ = D3D11DrawContext::CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::VERTEXDATA); + upIBuffer_ = D3D11DrawContext::CreateBuffer(UP_MAX_BYTES, BufferUsageFlag::DYNAMIC | BufferUsageFlag::INDEXDATA); IDXGIDevice1 *dxgiDevice1 = nullptr; hr = device_->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast(&dxgiDevice1)); @@ -364,6 +369,7 @@ D3D11DrawContext::~D3D11DrawContext() { DestroyPresets(); upBuffer_->Release(); + upIBuffer_->Release(); packTexture_->Release(); // Release references. @@ -1255,7 +1261,7 @@ void D3D11DrawContext::ApplyCurrentState() { context_->IASetVertexBuffers(0, 1, &nextVertexBuffer_, &curPipeline_->input->stride, &nextVertexBufferOffset_); } if (dirtyIndexBuffer_) { - context_->IASetIndexBuffer(nextIndexBuffer_, DXGI_FORMAT_R16_UINT, nextIndexBufferOffset_); + context_->IASetIndexBuffer(nextIndexBuffer_, nextIndexBufferFormat_, nextIndexBufferOffset_); dirtyIndexBuffer_ = false; } if (curPipeline_->dynamicUniforms) { @@ -1334,6 +1340,7 @@ void D3D11DrawContext::BindIndexBuffer(Buffer *indexBuffer, int offset) { dirtyIndexBuffer_ = true; nextIndexBuffer_ = buf ? buf->buf : 0; nextIndexBufferOffset_ = buf ? offset : 0; + nextIndexBufferFormat_ = DXGI_FORMAT_R16_UINT; } void D3D11DrawContext::Draw(int vertexCount, int offset) { @@ -1347,8 +1354,6 @@ void D3D11DrawContext::DrawIndexed(int indexCount, int offset) { } void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) { - ApplyCurrentState(); - int byteSize = vertexCount * curPipeline_->input->stride; UpdateBuffer(upBuffer_, (const uint8_t *)vdata, 0, byteSize, Draw::UPDATE_DISCARD); @@ -1357,6 +1362,20 @@ void D3D11DrawContext::DrawUP(const void *vdata, int vertexCount) { Draw(vertexCount, offset); } +void D3D11DrawContext::DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt) { + 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; + DrawIndexed(indexCount, 0); +} + uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const { DXGI_FORMAT giFmt = dataFormatToD3D11(fmt); if (giFmt == DXGI_FORMAT_UNKNOWN) diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 3d2b0ebfd3..3072ed2f50 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -573,6 +573,7 @@ public: void Draw(int vertexCount, int offset) override; 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 Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; uint64_t GetNativeObject(NativeObject obj, void *srcObject) override { @@ -1172,6 +1173,16 @@ void D3D9Context::DrawUP(const void *vdata, int vertexCount) { device_->DrawPrimitiveUP(curPipeline_->prim, D3DPrimCount(curPipeline_->prim, vertexCount), vdata, curPipeline_->inputLayout->GetStride()); } +void D3D9Context::DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt) { + curPipeline_->inputLayout->Apply(device_); + curPipeline_->Apply(device_, stencilRef_, stencilWriteMask_, stencilCompareMask_); + ApplyDynamicState(); + + device_->DrawIndexedPrimitiveUP(curPipeline_->prim, 0, vertexCount, D3DPrimCount(curPipeline_->prim, indexCount), + idata, ifmt == IndexFormat::U32 ? D3DFMT_INDEX32 : D3DFMT_INDEX16, + vdata, curPipeline_->inputLayout->GetStride()); +} + static uint32_t SwapRB(uint32_t c) { return (c & 0xFF00FF00) | ((c >> 16) & 0xFF) | ((c << 16) & 0xFF0000); } diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 591f2eeffd..3b149699fe 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -443,6 +443,7 @@ public: void Draw(int vertexCount, int offset) override; 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 Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) override; @@ -1417,6 +1418,29 @@ void OpenGLContext::DrawUP(const void *vdata, int vertexCount) { renderManager_.Draw(curPipeline_->inputLayout->inputLayout_, buf, offset, curPipeline_->prim, 0, vertexCount); } +void OpenGLContext::DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt) { + _assert_(curPipeline_->inputLayout != nullptr); + int stride = curPipeline_->inputLayout->stride; + uint32_t vdataSize = stride * vertexCount; + uint32_t idataSize = indexCount * (ifmt == IndexFormat::U32 ? 4 : 2); + + 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(); + _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::Clear(int mask, uint32_t colorval, float depthVal, int stencilVal) { float col[4]; Uint8x4ToFloat4(col, colorval); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.cpp b/Common/GPU/Vulkan/VulkanQueueRunner.cpp index 867dccacf9..e8f742cb21 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.cpp +++ b/Common/GPU/Vulkan/VulkanQueueRunner.cpp @@ -1165,7 +1165,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c VkDescriptorSet set = (*descSets)[c.drawIndexed.descSetIndex].set; _dbg_assert_(set != VK_NULL_HANDLE); vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &set, c.drawIndexed.numUboOffsets, c.drawIndexed.uboOffsets); - vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, VK_INDEX_TYPE_UINT16); + vkCmdBindIndexBuffer(cmd, c.drawIndexed.ibuffer, c.drawIndexed.ioffset, c.drawIndexed.itype); VkDeviceSize voffset = c.drawIndexed.voffset; vkCmdBindVertexBuffers(cmd, 0, 1, &c.drawIndexed.vbuffer, &voffset); vkCmdDrawIndexed(cmd, c.drawIndexed.count, c.drawIndexed.instances, 0, 0, 0); diff --git a/Common/GPU/Vulkan/VulkanQueueRunner.h b/Common/GPU/Vulkan/VulkanQueueRunner.h index bef9ec7ec3..6e6abcad2a 100644 --- a/Common/GPU/Vulkan/VulkanQueueRunner.h +++ b/Common/GPU/Vulkan/VulkanQueueRunner.h @@ -80,6 +80,7 @@ struct VkRenderData { uint32_t voffset; uint32_t ioffset; uint32_t count; + VkIndexType itype; // TODO: pack this into one of the others, it's really a single bit } drawIndexed; struct { uint32_t clearColor; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index e68f521faf..9d6fa7dadc 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -471,7 +471,7 @@ public: curRenderStep_->render.numDraws++; } - void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances) { + void DrawIndexed(int descSetIndex, int numUboOffsets, const uint32_t *uboOffsets, VkBuffer vbuffer, int voffset, VkBuffer ibuffer, int ioffset, int count, int numInstances, VkIndexType itype) { _dbg_assert_(curRenderStep_ && curRenderStep_->stepType == VKRStepType::RENDER && curStepHasViewport_ && curStepHasScissor_); VkRenderData &data = curRenderStep_->commands.push_uninitialized(); data.cmd = VKRRenderCommand::DRAW_INDEXED; @@ -483,6 +483,7 @@ public: data.drawIndexed.ibuffer = ibuffer; data.drawIndexed.ioffset = ioffset; data.drawIndexed.numUboOffsets = numUboOffsets; + data.drawIndexed.itype = itype; _dbg_assert_(numUboOffsets <= ARRAY_SIZE(data.drawIndexed.uboOffsets)); for (int i = 0; i < numUboOffsets; i++) data.drawIndexed.uboOffsets[i] = uboOffsets[i]; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index 8a296cac34..5943c197f3 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -491,6 +491,7 @@ public: void Draw(int vertexCount, int offset) override; 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 BindCurrentPipeline(); void ApplyDynamicState(); @@ -1497,7 +1498,7 @@ void VKContext::DrawIndexed(int vertexCount, int offset) { int descSetIndex; PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex); BindDescriptors(vulkanUBObuf, descriptors); - renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1); + renderManager_.DrawIndexed(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vulkanIbuf, (int)ibBindOffset + offset * sizeof(uint32_t), vertexCount, 1, VK_INDEX_TYPE_UINT16); } void VKContext::DrawUP(const void *vdata, int vertexCount) { @@ -1520,7 +1521,37 @@ void VKContext::DrawUP(const void *vdata, int vertexCount) { int descSetIndex; PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex); BindDescriptors(vulkanUBObuf, descriptors); - renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset + curVBufferOffset_, vertexCount); + renderManager_.Draw(descSetIndex, 1, &ubo_offset, vulkanVbuf, (int)vbBindOffset, vertexCount); +} + +void VKContext::DrawIndexedUP(const void *vdata, int vertexCount, const void *idata, int indexCount, IndexFormat ifmt) { + _dbg_assert_(vertexCount >= 0); + _dbg_assert_(indexCount >= 0); + if (vertexCount <= 0 || indexCount <= 0) { + 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); + + size_t idataSize = indexCount * (ifmt == IndexFormat::U32 ? 4 : 2); + uint32_t ibBindOffset; + uint8_t *idataPtr = push_->Allocate(idataSize, 4, &vulkanIbuf, &ibBindOffset); + _assert_(idataPtr != nullptr); + memcpy(idataPtr, vdata, idataSize); + + uint32_t ubo_offset = (uint32_t)curPipeline_->PushUBO(push_, vulkan_, &vulkanUBObuf); + + BindCurrentPipeline(); + ApplyDynamicState(); + int descSetIndex; + PackedDescriptor *descriptors = renderManager_.PushDescriptorSet(4, &descSetIndex); + BindDescriptors(vulkanUBObuf, descriptors); + 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::BindCurrentPipeline() { diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index f4377cdd06..02241df43a 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -180,6 +180,11 @@ enum class Facing { CW, }; +enum class IndexFormat { + U16, + U32, +}; + enum BorderColor { DONT_CARE, TRANSPARENT_BLACK, @@ -827,7 +832,8 @@ public: virtual void Draw(int vertexCount, int offset) = 0; 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. + // Frame management (for the purposes of sync and resource management, necessary with modern APIs). Default implementations here. virtual void BeginFrame(DebugFlags debugFlags) = 0; virtual void EndFrame() = 0; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 66f146e662..e5e207f90d 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -374,7 +374,7 @@ void DrawEngineVulkan::DoFlush() { if (!ibuf) { ibOffset = (uint32_t)pushIndex_->Push(decIndex_, sizeof(uint16_t) * vertexCount, 4, &ibuf); } - renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1); + renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, vertexCount, 1, VK_INDEX_TYPE_UINT16); } else { renderManager->Draw(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, vertexCount); } @@ -539,7 +539,7 @@ void DrawEngineVulkan::DoFlush() { VkBuffer vbuf, ibuf; vbOffset = (uint32_t)pushVertex_->Push(result.drawBuffer, numDecodedVerts_ * sizeof(TransformedVertex), 4, &vbuf); ibOffset = (uint32_t)pushIndex_->Push(inds, sizeof(short) * result.drawNumTrans, 4, &ibuf); - renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1); + renderManager->DrawIndexed(descSetIndex, ARRAY_SIZE(dynamicUBOOffsets), dynamicUBOOffsets, vbuf, vbOffset, ibuf, ibOffset, result.drawNumTrans, 1, VK_INDEX_TYPE_UINT16); } else if (result.action == SW_CLEAR) { // Note: we won't get here if the clear is alpha but not color, or color but not alpha. bool clearColor = gstate.isClearModeColorMask();