From c31e64692147ffc45d340e8338e021f7c9cb5162 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Mon, 30 Jul 2018 20:15:19 +0200 Subject: [PATCH] [dxvk] Introduce concept of shader constants Large constant arrays should be moved to a uniform buffer instead of being baked directly into the shader code. --- src/d3d11/d3d11_context.cpp | 17 +++++++-- src/d3d11/d3d11_device.cpp | 2 +- src/d3d11/d3d11_shader.cpp | 5 ++- src/d3d11/d3d11_shader.h | 9 +++-- src/dxbc/dxbc_compiler.cpp | 3 +- src/dxvk/dxvk_device.cpp | 3 +- src/dxvk/dxvk_shader.cpp | 42 +++++++++++++++++++++-- src/dxvk/dxvk_shader.h | 55 +++++++++++++++++++++++++++++- src/dxvk/hud/dxvk_hud_renderer.cpp | 6 ++-- 9 files changed, 128 insertions(+), 14 deletions(-) diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index f299df2c3..8dc5fcc0c 100644 --- a/src/d3d11/d3d11_context.cpp +++ b/src/d3d11/d3d11_context.cpp @@ -2557,11 +2557,24 @@ namespace dxvk { void D3D11DeviceContext::BindShader( DxbcProgramType ShaderStage, const D3D11CommonShader* pShaderModule) { + // Bind the shader and the ICB at once + const uint32_t slotId = computeResourceSlotId( + ShaderStage, DxbcBindingType::ConstantBuffer, + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT); + EmitCs([ + cSlotId = slotId, cStage = GetShaderStage(ShaderStage), - cShader = pShaderModule != nullptr ? pShaderModule->GetShader() : nullptr + cSlice = pShaderModule != nullptr + && pShaderModule->GetIcb() != nullptr + ? DxvkBufferSlice(pShaderModule->GetIcb()) + : DxvkBufferSlice(), + cShader = pShaderModule != nullptr + ? pShaderModule->GetShader() + : nullptr ] (DxvkContext* ctx) { - ctx->bindShader(cStage, cShader); + ctx->bindShader (cStage, cShader); + ctx->bindResourceBuffer(cSlotId, cSlice); }); } diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index 3696f1773..a1a48e698 100644 --- a/src/d3d11/d3d11_device.cpp +++ b/src/d3d11/d3d11_device.cpp @@ -1862,7 +1862,7 @@ namespace dxvk { Logger::warn("D3D11Device::CreateShaderModule: Class linkage not supported"); try { - *pShaderModule = m_shaderModules.GetShaderModule( + *pShaderModule = m_shaderModules.GetShaderModule(this, pModuleInfo, pShaderBytecode, BytecodeLength, ProgramType); return S_OK; } catch (const DxvkError& e) { diff --git a/src/d3d11/d3d11_shader.cpp b/src/d3d11/d3d11_shader.cpp index 9e8093966..c33695748 100644 --- a/src/d3d11/d3d11_shader.cpp +++ b/src/d3d11/d3d11_shader.cpp @@ -39,6 +39,7 @@ namespace dxvk { D3D11CommonShader::D3D11CommonShader( + D3D11Device* pDevice, const D3D11ShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, @@ -92,6 +93,7 @@ namespace dxvk { D3D11CommonShader D3D11ShaderModuleSet::GetShaderModule( + D3D11Device* pDevice, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength, @@ -108,7 +110,8 @@ namespace dxvk { // This shader has not been compiled yet, so we have to create a // new module. This takes a while, so we won't lock the structure. - D3D11CommonShader module(&key, pDxbcModuleInfo, pShaderBytecode, BytecodeLength); + D3D11CommonShader module(pDevice, &key, + pDxbcModuleInfo, pShaderBytecode, BytecodeLength); // Insert the new module into the lookup table. If another thread // has compiled the same shader in the meantime, we should return diff --git a/src/d3d11/d3d11_shader.h b/src/d3d11/d3d11_shader.h index 2c9423487..5ec88a693 100644 --- a/src/d3d11/d3d11_shader.h +++ b/src/d3d11/d3d11_shader.h @@ -69,17 +69,20 @@ namespace dxvk { D3D11CommonShader(); D3D11CommonShader( + D3D11Device* pDevice, const D3D11ShaderKey* pShaderKey, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength); ~D3D11CommonShader(); - DxbcProgramType GetProgramType() const; - Rc GetShader() const { return m_shader; } + + Rc GetIcb() const { + return m_buffer; + } std::string GetName() const { return m_name; @@ -89,6 +92,7 @@ namespace dxvk { std::string m_name; Rc m_shader; + Rc m_buffer; }; @@ -163,6 +167,7 @@ namespace dxvk { ~D3D11ShaderModuleSet(); D3D11CommonShader GetShaderModule( + D3D11Device* pDevice, const DxbcModuleInfo* pDxbcModuleInfo, const void* pShaderBytecode, size_t BytecodeLength, diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 0545c3d19..194d37e6e 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -205,7 +205,8 @@ namespace dxvk { m_resourceSlots.size(), m_resourceSlots.data(), m_interfaceSlots, - m_module.compile()); + m_module.compile(), + DxvkShaderConstData()); } diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index a9cfc738a..e0cae8ce7 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -190,7 +190,8 @@ namespace dxvk { const DxvkInterfaceSlots& iface, const SpirvCodeBuffer& code) { return new DxvkShader(stage, - slotCount, slotInfos, iface, code); + slotCount, slotInfos, iface, + code, DxvkShaderConstData()); } diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index a83895573..3979519c1 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -2,6 +2,42 @@ namespace dxvk { + DxvkShaderConstData::DxvkShaderConstData() { + + } + + + DxvkShaderConstData::DxvkShaderConstData( + size_t dwordCount, + const uint32_t* dwordArray) + : m_size(dwordCount), m_data(new uint32_t[dwordCount]) { + for (size_t i = 0; i < dwordCount; i++) + m_data[i] = dwordArray[i]; + } + + + DxvkShaderConstData::DxvkShaderConstData(DxvkShaderConstData&& other) + : m_size(other.m_size), m_data(other.m_data) { + other.m_size = 0; + other.m_data = nullptr; + } + + + DxvkShaderConstData& DxvkShaderConstData::operator = (DxvkShaderConstData&& other) { + delete[] m_data; + this->m_size = other.m_size; + this->m_data = other.m_data; + other.m_size = 0; + other.m_data = nullptr; + return *this; + } + + + DxvkShaderConstData::~DxvkShaderConstData() { + delete[] m_data; + } + + DxvkShaderModule::DxvkShaderModule( const Rc& vkd, const Rc& shader, @@ -44,8 +80,10 @@ namespace dxvk { uint32_t slotCount, const DxvkResourceSlot* slotInfos, const DxvkInterfaceSlots& iface, - const SpirvCodeBuffer& code) - : m_stage(stage), m_code(code), m_interface(iface) { + const SpirvCodeBuffer& code, + DxvkShaderConstData&& constData) + : m_stage(stage), m_code(code), m_interface(iface), + m_constData(std::move(constData)) { // Write back resource slot infos for (uint32_t i = 0; i < slotCount; i++) m_slots.push_back(slotInfos[i]); diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index 0aa092dc7..5c7878e77 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -38,6 +38,45 @@ namespace dxvk { uint32_t inputSlots = 0; uint32_t outputSlots = 0; }; + + + /** + * \brief Shader constants + * + * Each shader can have constant data associated + * with it, which needs to be copied to a uniform + * buffer. The client API must then bind that buffer + * to an API-specific buffer binding when using the + * shader for rendering. + */ + class DxvkShaderConstData { + + public: + + DxvkShaderConstData(); + DxvkShaderConstData( + size_t dwordCount, + const uint32_t* dwordArray); + + DxvkShaderConstData (DxvkShaderConstData&& other); + DxvkShaderConstData& operator = (DxvkShaderConstData&& other); + + ~DxvkShaderConstData(); + + const uint32_t* data() const { + return m_data; + } + + size_t sizeInBytes() const { + return m_size * sizeof(uint32_t); + } + + private: + + size_t m_size = 0; + uint32_t* m_data = nullptr; + + }; /** @@ -110,7 +149,8 @@ namespace dxvk { uint32_t slotCount, const DxvkResourceSlot* slotInfos, const DxvkInterfaceSlots& iface, - const SpirvCodeBuffer& code); + const SpirvCodeBuffer& code, + DxvkShaderConstData&& constData); ~DxvkShader(); @@ -166,6 +206,18 @@ namespace dxvk { DxvkInterfaceSlots interfaceSlots() const { return m_interface; } + + /** + * \brief Shader constant data + * + * Returns a read-only reference to the + * constant data associated with this + * shader object. + * \returns Shader constant data + */ + const DxvkShaderConstData& shaderConstants() const { + return m_constData; + } /** * \brief Dumps SPIR-V shader @@ -210,6 +262,7 @@ namespace dxvk { std::vector m_slots; std::vector m_idOffsets; DxvkInterfaceSlots m_interface; + DxvkShaderConstData m_constData; std::string m_debugName; }; diff --git a/src/dxvk/hud/dxvk_hud_renderer.cpp b/src/dxvk/hud/dxvk_hud_renderer.cpp index 5fa083ddc..0430ad349 100644 --- a/src/dxvk/hud/dxvk_hud_renderer.cpp +++ b/src/dxvk/hud/dxvk_hud_renderer.cpp @@ -190,7 +190,7 @@ namespace dxvk::hud { { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_IMAGE_VIEW_TYPE_MAX_ENUM }, }}; - return new DxvkShader( + return device->createShader( VK_SHADER_STAGE_VERTEX_BIT, resourceSlots.size(), resourceSlots.data(), @@ -208,7 +208,7 @@ namespace dxvk::hud { { 2, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_IMAGE_VIEW_TYPE_2D }, }}; - return new DxvkShader( + return device->createShader( VK_SHADER_STAGE_FRAGMENT_BIT, resourceSlots.size(), resourceSlots.data(), @@ -220,7 +220,7 @@ namespace dxvk::hud { Rc HudRenderer::createLineShader(const Rc& device) { const SpirvCodeBuffer codeBuffer(hud_line); - return new DxvkShader( + return device->createShader( VK_SHADER_STAGE_FRAGMENT_BIT, 0, nullptr, { 0x2, 0x1 }, codeBuffer);