From fe1b593c15e9f7a46f5c4abafd10d94bf32fddbb Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 8 Feb 2017 12:26:48 +0100 Subject: [PATCH] DrawContext: Initial implementation of dynamic UBO support --- ext/native/thin3d/thin3d.cpp | 14 +++--- ext/native/thin3d/thin3d.h | 28 ++++++++++-- ext/native/thin3d/thin3d_d3d11.cpp | 69 +++++++++++++++++++++++------ ext/native/thin3d/thin3d_d3d9.cpp | 28 ++++++++++++ ext/native/thin3d/thin3d_gl.cpp | 37 +++++++++++++--- ext/native/thin3d/thin3d_vulkan.cpp | 24 +++++++--- 6 files changed, 164 insertions(+), 36 deletions(-) diff --git a/ext/native/thin3d/thin3d.cpp b/ext/native/thin3d/thin3d.cpp index 753ac1762a..5137ee8fa7 100644 --- a/ext/native/thin3d/thin3d.cpp +++ b/ext/native/thin3d/thin3d.cpp @@ -184,10 +184,9 @@ static const std::vector vsCol = { } }; -static const UniformBufferDesc vsColBuf { { { 0, UniformType::MATRIX4X4, 0 } } }; -struct VsColUB { - float WorldViewProj[16]; -}; +const UniformBufferDesc vsColBufDesc { sizeof(VsColUB), { + { "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 } +} }; static const std::vector vsTexCol = { { ShaderLanguage::GLSL_ES_200, @@ -248,10 +247,9 @@ static const std::vector vsTexCol = { } }; -static const UniformBufferDesc vsTexColDesc{ { { 0, UniformType::MATRIX4X4, 0 } } }; -struct VsTexColUB { - float WorldViewProj[16]; -}; +const UniformBufferDesc vsTexColBufDesc{ sizeof(VsTexColUB),{ + { "WorldViewProj", 0, -1, UniformType::MATRIX4X4, 0 } +} }; static ShaderModule *CreateShader(DrawContext *draw, ShaderStage stage, const std::vector &sources) { uint32_t supported = draw->GetSupportedShaderLanguages(); diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 96880f3357..257c89ffb0 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -430,20 +430,22 @@ struct InputLayoutDesc { class InputLayout : public RefCountedObject { }; enum class UniformType : int8_t { - FLOAT, FLOAT2, FLOAT3, FLOAT4, + FLOAT4, MATRIX4X4, }; // For emulation of uniform buffers on D3D9/GL struct UniformDesc { - int16_t offset; + const char *name; // For GL + int16_t vertexReg; // For D3D + int16_t fragmentReg; // For D3D UniformType type; - int8_t reg; // For D3D - + int16_t offset; // TODO: Support array elements etc. }; struct UniformBufferDesc { + size_t uniformBufferSize; std::vector uniforms; }; @@ -520,6 +522,7 @@ struct PipelineDesc { DepthStencilState *depthStencil; BlendState *blend; RasterState *raster; + UniformBufferDesc *uniformDesc; }; struct DeviceCaps { @@ -602,6 +605,10 @@ public: virtual void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) = 0; virtual void BindIndexBuffer(Buffer *indexBuffer, int offset) = 0; + // Only supports a single dynamic uniform buffer, for maximum compatibility with the old APIs and ease of emulation. + // More modern methods will be added later. + virtual void UpdateDynamicUniformBuffer(const void *ub, size_t size) = 0; + void BindTexture(int stage, Texture *texture) { BindTextures(stage, 1, &texture); } // from sampler 0 and upwards @@ -645,6 +652,8 @@ size_t DataFormatSizeInBytes(DataFormat fmt); DrawContext *T3DCreateGLContext(); +extern const UniformBufferDesc UBPresetDesc; + #ifdef _WIN32 DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapterId, IDirect3DDevice9 *device, IDirect3DDevice9Ex *deviceEx); DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *context, HWND hWnd); @@ -652,4 +661,15 @@ DrawContext *T3DCreateD3D11Context(ID3D11Device *device, ID3D11DeviceContext *co DrawContext *T3DCreateVulkanContext(VulkanContext *context); +// UBs for the preset shaders + +struct VsTexColUB { + float WorldViewProj[16]; +}; +extern const UniformBufferDesc vsTexColBufDesc; +struct VsColUB { + float WorldViewProj[16]; +}; +extern const UniformBufferDesc vsColBufDesc; + } // namespace Draw diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index e037f16308..fb9f116fef 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -60,9 +60,11 @@ public: void BindTextures(int start, int count, Texture **textures) override; void BindSamplerStates(int start, int count, SamplerState **states) override; - void BindPipeline(Pipeline *pipeline) override; void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override; void BindIndexBuffer(Buffer *indexBuffer, int offset) override; + void BindPipeline(Pipeline *pipeline) override; + + void UpdateDynamicUniformBuffer(const void *ub, size_t size) override; // Raster state void SetScissorRect(int left, int top, int width, int height) override; @@ -503,11 +505,20 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) { class D3D11Pipeline : public Pipeline { public: ~D3D11Pipeline() { - input->Release(); - blend->Release(); - depth->Release(); - raster->Release(); - il->Release(); + if (input) + input->Release(); + if (blend) + blend->Release(); + if (depth) + depth->Release(); + if (raster) + raster->Release(); + if (il) + il->Release(); + if (dynamicUniforms) + dynamicUniforms->Release(); + if (dynamicUniformsView) + dynamicUniformsView->Release(); } // TODO: Refactor away these. void SetVector(const char *name, float *value, int n) { } @@ -516,15 +527,19 @@ public: return true; } - D3D11InputLayout *input; + D3D11InputLayout *input = nullptr; ID3D11InputLayout *il = nullptr; - D3D11BlendState *blend; - D3D11DepthStencilState *depth; - D3D11RasterState *raster; - ID3D11VertexShader *vs; - ID3D11PixelShader *ps; - ID3D11GeometryShader *gs; - D3D11_PRIMITIVE_TOPOLOGY topology; + D3D11BlendState *blend = nullptr; + D3D11DepthStencilState *depth = nullptr; + D3D11RasterState *raster = nullptr; + ID3D11VertexShader *vs = nullptr; + ID3D11PixelShader *ps = nullptr; + ID3D11GeometryShader *gs = nullptr; + D3D11_PRIMITIVE_TOPOLOGY topology = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + + size_t dynamicUniformsSize = 0; + ID3D11Buffer *dynamicUniforms = nullptr; + ID3D11ShaderResourceView *dynamicUniformsView = nullptr; }; class D3D11Texture : public Texture { @@ -710,6 +725,22 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { dPipeline->input->AddRef(); dPipeline->raster->AddRef(); dPipeline->topology = primToD3D11[(int)desc.prim]; + if (desc.uniformDesc) { + dPipeline->dynamicUniformsSize = desc.uniformDesc->uniformBufferSize; + D3D11_BUFFER_DESC bufdesc{}; + bufdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + bufdesc.ByteWidth = dPipeline->dynamicUniformsSize; + bufdesc.StructureByteStride = dPipeline->dynamicUniformsSize; + bufdesc.Usage = D3D11_USAGE_DYNAMIC; + bufdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + HRESULT hr = device_->CreateBuffer(&bufdesc, nullptr, &dPipeline->dynamicUniforms); + D3D11_SHADER_RESOURCE_VIEW_DESC bufview{}; + bufview.Buffer.ElementOffset = 0; + bufview.Buffer.ElementWidth = dPipeline->dynamicUniformsSize; + bufview.Buffer.FirstElement = 0; + bufview.Buffer.NumElements = 1; + hr = device_->CreateShaderResourceView(dPipeline->dynamicUniforms, &bufview, &dPipeline->dynamicUniformsView); + } std::vector shaders; D3D11ShaderModule *vshader = nullptr; @@ -746,6 +777,16 @@ Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) { return dPipeline; } +void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { + if (curPipeline_->dynamicUniformsSize != size) { + Crash(); + } + D3D11_MAPPED_SUBRESOURCE map{}; + context_->Map(curPipeline_->dynamicUniforms, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + memcpy(map.pData, ub, size); + context_->Unmap(curPipeline_->dynamicUniforms, 0); +} + void D3D11DrawContext::BindPipeline(Pipeline *pipeline) { D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline; if (curPipeline_ == dPipeline) diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 8e7bad916b..99b3f7ff39 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -291,6 +291,7 @@ public: D3D9DepthStencilState *depthStencil = nullptr; D3D9BlendState *blend = nullptr; D3D9RasterState *raster = nullptr; + UniformBufferDesc dynamicUniforms; void Apply(LPDIRECT3DDEVICE9 device); void SetVector(const char *name, float *value, int n) { vshader->SetVector(device_, name, value, n); pshader->SetVector(device_, name, value, n); } @@ -520,6 +521,8 @@ public: curPipeline_ = (D3D9Pipeline *)pipeline; } + void UpdateDynamicUniformBuffer(const void *ub, size_t size) override; + // Raster state void SetScissorRect(int left, int top, int width, int height) override; void SetViewports(int count, Viewport *viewports) override; @@ -647,6 +650,8 @@ Pipeline *D3D9Context::CreateGraphicsPipeline(const PipelineDesc &desc) { pipeline->blend->AddRef(); pipeline->raster->AddRef(); pipeline->inputLayout->AddRef(); + if (desc.uniformDesc) + pipeline->dynamicUniforms = *desc.uniformDesc; return pipeline; } @@ -811,6 +816,29 @@ Buffer *D3D9Context::CreateBuffer(size_t size, uint32_t usageFlags) { return new D3D9Buffer(device_, size, usageFlags); } +void D3D9Context::UpdateDynamicUniformBuffer(const void *ub, size_t size) { + if (size != curPipeline_->dynamicUniforms.uniformBufferSize) + Crash(); + for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) { + int count = 0; + switch (uniform.type) { + case UniformType::FLOAT4: + count = 1; + break; + case UniformType::MATRIX4X4: + count = 4; + break; + } + const float *srcPtr = (const float *)((uint8_t *)ub + uniform.offset); + if (uniform.vertexReg != -1) { + device_->SetVertexShaderConstantF(uniform.vertexReg, srcPtr, count); + } + if (uniform.fragmentReg != -1) { + device_->SetPixelShaderConstantF(uniform.fragmentReg, srcPtr, count); + } + } +} + void D3D9Context::UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) { D3D9Buffer *buf = (D3D9Buffer *)buffer; if (!size) diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 22dc8e4e3c..fed3b7aa25 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -381,7 +381,6 @@ struct UniformInfo { int loc_; }; -// TODO: Add Uniform Buffer support. class OpenGLPipeline : public Pipeline, GfxResourceHolder { public: OpenGLPipeline() { @@ -434,9 +433,12 @@ public: OpenGLBlendState *blend = nullptr; OpenGLRasterState *raster = nullptr; + // TODO: Optimize by getting the locations first and putting in a custom struct + UniformBufferDesc dynamicUniforms; + private: GLuint program_; - std::map uniforms_; + std::map uniformCache_; }; class OpenGLFramebuffer; @@ -538,6 +540,8 @@ public: curIBufferOffset_ = offset; } + void UpdateDynamicUniformBuffer(const void *ub, size_t size); + // TODO: Add more sophisticated draws. void Draw(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override; @@ -965,6 +969,8 @@ Pipeline *OpenGLContext::CreateGraphicsPipeline(const PipelineDesc &desc) { pipeline->blend->AddRef(); pipeline->raster->AddRef(); pipeline->inputLayout->AddRef(); + if (desc.uniformDesc) + pipeline->dynamicUniforms = *desc.uniformDesc; return pipeline; } else { delete pipeline; @@ -1046,15 +1052,15 @@ bool OpenGLPipeline::LinkShaders() { } int OpenGLPipeline::GetUniformLoc(const char *name) { - auto iter = uniforms_.find(name); + auto iter = uniformCache_.find(name); int loc = -1; - if (iter != uniforms_.end()) { + if (iter != uniformCache_.end()) { loc = iter->second.loc_; } else { loc = glGetUniformLocation(program_, name); UniformInfo info; info.loc_ = loc; - uniforms_[name] = info; + uniformCache_[name] = info; } return loc; } @@ -1095,6 +1101,27 @@ void OpenGLContext::BindPipeline(Pipeline *pipeline) { curPipeline_->raster->Apply(); } +void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { + if (curPipeline_->dynamicUniforms.uniformBufferSize != size) { + Crash(); + } + + for (auto &uniform : curPipeline_->dynamicUniforms.uniforms) { + GLuint loc = curPipeline_->GetUniformLoc(uniform.name); + if (loc == -1) + Crash(); + const float *data = (const float *)((uint8_t *)ub + uniform.offset); + switch (uniform.type) { + case UniformType::FLOAT4: + glUniform1fv(loc, 4, data); + break; + case UniformType::MATRIX4X4: + glUniformMatrix4fv(loc, 1, false, data); + break; + } + } +} + void OpenGLContext::Draw(int vertexCount, int offset) { curVBuffers_[0]->Bind(curVBufferOffsets_[0]); curPipeline_->inputLayout->Apply(); diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index 21da29347b..13148faa33 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -262,15 +262,18 @@ public: class VKPipeline : public Pipeline { public: - VKPipeline() { - // HACK! Hardcoded - uboSize_ = 16 * sizeof(float); // WorldViewProj + VKPipeline(size_t size) { + uboSize_ = size; ubo_ = new uint8_t[uboSize_]; } ~VKPipeline() { delete[] ubo_; } + void SetDynamicUniformData(const void *data, size_t size) { + memcpy(ubo_, data, size); + } + // Returns the binding offset, and the VkBuffer to bind. size_t PushUBO(VulkanPushBuffer *buf, VulkanContext *vulkan, VkBuffer *vkbuf) { return buf->PushAligned(ubo_, uboSize_, vulkan->GetPhysicalDeviceProperties().limits.minUniformBufferOffsetAlignment, vkbuf); @@ -289,7 +292,8 @@ public: } VkPipeline vkpipeline; - int stride[4]; + int stride[4]{}; + int dynamicUniformSize = 0; private: uint8_t *ubo_; @@ -376,6 +380,8 @@ public: curIBufferOffset_ = offset; } + void UpdateDynamicUniformBuffer(const void *ub, size_t size); + // TODO: Add more sophisticated draws. void Draw(int vertexCount, int offset) override; void DrawIndexed(int vertexCount, int offset) override; @@ -827,7 +833,7 @@ VkDescriptorSet VKContext::GetOrCreateDescriptorSet(VkBuffer buf) { } Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { - VKPipeline *pipeline = new VKPipeline(); + VKPipeline *pipeline = new VKPipeline(desc.uniformDesc ? desc.uniformDesc->uniformBufferSize : 16 * sizeof(float)); VKInputLayout *input = (VKInputLayout *)desc.inputLayout; VKBlendState *blend = (VKBlendState *)desc.blend; VKDepthStencilState *depth = (VKDepthStencilState *)desc.depthStencil; @@ -900,6 +906,10 @@ Pipeline *VKContext::CreateGraphicsPipeline(const PipelineDesc &desc) { delete pipeline; return nullptr; } + if (desc.uniformDesc) { + pipeline->dynamicUniformSize = desc.uniformDesc->uniformBufferSize; + } + return pipeline; } @@ -1112,6 +1122,10 @@ inline VkPrimitiveTopology PrimToVK(Primitive prim) { } } +void VKContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { + curPipeline_->SetDynamicUniformData(ub, size); +} + void VKContext::Draw(int vertexCount, int offset) { ApplyDynamicState();