From 19851c8432f33846f239de313a45b929b20b191e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Thu, 7 Dec 2017 09:38:31 +0100 Subject: [PATCH] [dxvk] Re-implemented pipeline creation within the backend --- build-win64.txt | 2 +- src/dxgi/dxgi_presenter.cpp | 49 +++----- src/dxgi/dxgi_presenter.h | 4 - src/dxgi/dxgi_swapchain.cpp | 1 + src/dxvk/dxvk_cmdlist.h | 2 +- src/dxvk/dxvk_compute.cpp | 35 ++++-- src/dxvk/dxvk_compute.h | 11 +- src/dxvk/dxvk_context.cpp | 70 ++++++----- src/dxvk/dxvk_context.h | 24 ++-- src/dxvk/dxvk_context_state.h | 28 ++++- src/dxvk/dxvk_device.cpp | 35 +++--- src/dxvk/dxvk_device.h | 38 +++--- src/dxvk/dxvk_graphics.cpp | 31 +++-- src/dxvk/dxvk_graphics.h | 25 ++-- ...{dxvk_pipeline.cpp => dxvk_pipelayout.cpp} | 37 +++++- .../{dxvk_pipeline.h => dxvk_pipelayout.h} | 65 ++++++++++ src/dxvk/dxvk_pipemanager.cpp | 96 ++++++++++++++ src/dxvk/dxvk_pipemanager.h | 118 ++++++++++++++++++ src/dxvk/dxvk_shader.cpp | 37 +++++- src/dxvk/dxvk_shader.h | 65 ++++++++-- src/dxvk/meson.build | 3 +- src/spirv/spirv_instruction.h | 4 +- tests/dxvk/test_dxvk_triangle.cpp | 26 ++-- tests/meson.build | 2 +- 24 files changed, 612 insertions(+), 196 deletions(-) rename src/dxvk/{dxvk_pipeline.cpp => dxvk_pipelayout.cpp} (68%) rename src/dxvk/{dxvk_pipeline.h => dxvk_pipelayout.h} (53%) create mode 100644 src/dxvk/dxvk_pipemanager.cpp create mode 100644 src/dxvk/dxvk_pipemanager.h diff --git a/build-win64.txt b/build-win64.txt index 14680fe5c..6203d34cb 100644 --- a/build-win64.txt +++ b/build-win64.txt @@ -9,7 +9,7 @@ exe_wrapper = 'wine' c_args = ['-Og', '-ggdb'] c_link_args = ['-static', '-static-libgcc'] -cpp_args = ['-std=c++17', '-Og', '-ggdb'] +cpp_args = ['-std=c++17', '-Og', '-gstabs'] cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++'] [host_machine] diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 956c34f03..dee4b2126 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -55,8 +55,6 @@ namespace dxvk { // Set up context state. The shader bindings and the // constant state objects will never be modified. - m_context->bindGraphicsPipeline(createPipeline()); - m_context->setInputAssemblyState( new DxvkInputAssemblyState( VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, @@ -111,6 +109,14 @@ namespace dxvk { new DxvkBlendState( VK_FALSE, VK_LOGIC_OP_NO_OP, 1, &blendAttachment)); + + m_context->bindShader( + VK_SHADER_STAGE_VERTEX_BIT, + this->createVertexShader()); + + m_context->bindShader( + VK_SHADER_STAGE_FRAGMENT_BIT, + this->createFragmentShader()); } @@ -329,7 +335,8 @@ namespace dxvk { // Create the actual shader module return m_device->createShader( - VK_SHADER_STAGE_VERTEX_BIT, module.compile()); + VK_SHADER_STAGE_VERTEX_BIT, + 0, nullptr, module.compile()); } @@ -397,42 +404,24 @@ namespace dxvk { module.opReturn(); module.functionEnd(); - // Register function entry point std::array interfaces = { inTexCoord, outColor }; module.addEntryPoint(entryPointId, spv::ExecutionModelFragment, "main", interfaces.size(), interfaces.data()); + // Shader resource slots + std::array resourceSlots = {{ + { BindingIds::Sampler, VK_DESCRIPTOR_TYPE_SAMPLER }, + { BindingIds::Texture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE }, + }}; // Create the actual shader module return m_device->createShader( - VK_SHADER_STAGE_FRAGMENT_BIT, module.compile()); - } - - - Rc DxgiPresenter::createBindingLayout() { - std::array bindings; - bindings.at(BindingIds::Sampler).slot = BindingIds::Sampler; - bindings.at(BindingIds::Sampler).type = VK_DESCRIPTOR_TYPE_SAMPLER; - bindings.at(BindingIds::Sampler).stages = VK_SHADER_STAGE_FRAGMENT_BIT; - - bindings.at(BindingIds::Texture).slot = BindingIds::Texture; - bindings.at(BindingIds::Texture).type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - bindings.at(BindingIds::Texture).stages = VK_SHADER_STAGE_FRAGMENT_BIT; - - return m_device->createBindingLayout( - bindings.size(), bindings.data()); - } - - - Rc DxgiPresenter::createPipeline() { - const Rc vs = this->createVertexShader(); - const Rc fs = this->createFragmentShader(); - - return m_device->createGraphicsPipeline( - this->createBindingLayout(), - vs, nullptr, nullptr, nullptr, fs); + VK_SHADER_STAGE_FRAGMENT_BIT, + resourceSlots.size(), + resourceSlots.data(), + module.compile()); } } diff --git a/src/dxgi/dxgi_presenter.h b/src/dxgi/dxgi_presenter.h index 5c8b9da85..d246806c8 100644 --- a/src/dxgi/dxgi_presenter.h +++ b/src/dxgi/dxgi_presenter.h @@ -76,10 +76,6 @@ namespace dxvk { Rc createVertexShader(); Rc createFragmentShader(); - Rc createBindingLayout(); - - Rc createPipeline(); - }; } diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index 52bf6342b..7a807aa27 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -336,6 +336,7 @@ namespace dxvk { DxvkImageCreateInfo imageInfo; imageInfo.type = VK_IMAGE_TYPE_2D; imageInfo.format = bufferFormat.actual; + imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT; imageInfo.extent.width = m_desc.BufferDesc.Width; imageInfo.extent.height = m_desc.BufferDesc.Height; diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index 205c5ade5..ab72142da 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -4,7 +4,7 @@ #include "dxvk_descriptor.h" #include "dxvk_lifetime.h" -#include "dxvk_pipeline.h" +#include "dxvk_pipelayout.h" namespace dxvk { diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index 801ce28c6..0fa2ce49e 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -3,17 +3,36 @@ namespace dxvk { DxvkComputePipeline::DxvkComputePipeline( - const Rc& vkd, - const Rc& layout, - const Rc& cs) - : m_vkd(vkd), m_layout(layout), m_cs(cs) { + const Rc& vkd, + const Rc& cs) + : m_vkd(vkd) { + DxvkDescriptorSlotMapping slotMapping; + cs->defineResourceSlots(slotMapping); + + m_layout = new DxvkBindingLayout(vkd, + slotMapping.bindingCount(), + slotMapping.bindingInfos()); + + m_cs = cs->createShaderModule(slotMapping); + + this->compilePipeline(); + } + + + DxvkComputePipeline::~DxvkComputePipeline() { + if (m_pipeline != VK_NULL_HANDLE) + m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr); + } + + + void DxvkComputePipeline::compilePipeline() { std::vector bindings; VkComputePipelineCreateInfo info; info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; info.pNext = nullptr; info.flags = 0; - info.stage = cs->stageInfo(); + info.stage = m_cs->stageInfo(); info.layout = m_layout->pipelineLayout(); info.basePipelineHandle = VK_NULL_HANDLE; info.basePipelineIndex = 0; @@ -23,10 +42,4 @@ namespace dxvk { throw DxvkError("DxvkComputePipeline::DxvkComputePipeline: Failed to compile pipeline"); } - - DxvkComputePipeline::~DxvkComputePipeline() { - if (m_pipeline != VK_NULL_HANDLE) - m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr); - } - } \ No newline at end of file diff --git a/src/dxvk/dxvk_compute.h b/src/dxvk/dxvk_compute.h index ca658f854..2140fe7fb 100644 --- a/src/dxvk/dxvk_compute.h +++ b/src/dxvk/dxvk_compute.h @@ -1,6 +1,6 @@ #pragma once -#include "dxvk_pipeline.h" +#include "dxvk_pipelayout.h" #include "dxvk_resource.h" #include "dxvk_shader.h" @@ -19,9 +19,8 @@ namespace dxvk { public: DxvkComputePipeline( - const Rc& vkd, - const Rc& layout, - const Rc& cs); + const Rc& vkd, + const Rc& cs); ~DxvkComputePipeline(); /** @@ -48,10 +47,12 @@ namespace dxvk { Rc m_vkd; Rc m_layout; - Rc m_cs; + Rc m_cs; VkPipeline m_pipeline = VK_NULL_HANDLE; + void compilePipeline(); + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 41d583341..a23d2ea44 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -53,32 +53,6 @@ namespace dxvk { } - void DxvkContext::bindComputePipeline( - const Rc& pipeline) { - if (m_state.cPipe != pipeline) { - m_state.cPipe = pipeline; - - m_flags.set( - DxvkContextFlag::CpDirtyPipeline, - DxvkContextFlag::CpDirtyResources); - } - } - - - void DxvkContext::bindGraphicsPipeline( - const Rc& pipeline) { - if (m_state.gPipe != pipeline) { - m_state.gPipe = pipeline; - - m_flags.set( - DxvkContextFlag::GpDirtyPipeline, - DxvkContextFlag::GpDirtyResources, - DxvkContextFlag::GpDirtyVertexBuffers, - DxvkContextFlag::GpDirtyIndexBuffer); - } - } - - void DxvkContext::bindIndexBuffer( const DxvkBufferBinding& buffer) { if (m_state.vi.indexBuffer != buffer) { @@ -178,6 +152,31 @@ namespace dxvk { } + void DxvkContext::bindShader( + VkShaderStageFlagBits stage, + const Rc& shader) { + DxvkShaderStage* shaderStage = nullptr; + + switch (stage) { + case VK_SHADER_STAGE_VERTEX_BIT: shaderStage = &m_state.gp.vs; break; + case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: shaderStage = &m_state.gp.tcs; break; + case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: shaderStage = &m_state.gp.tes; break; + case VK_SHADER_STAGE_GEOMETRY_BIT: shaderStage = &m_state.gp.gs; break; + case VK_SHADER_STAGE_FRAGMENT_BIT: shaderStage = &m_state.gp.fs; break; + case VK_SHADER_STAGE_COMPUTE_BIT: shaderStage = &m_state.cp.cs; break; + default: return; + } + + if (shaderStage->shader != shader) { + shaderStage->shader = shader; + + m_flags.set(stage == VK_SHADER_STAGE_COMPUTE_BIT + ? DxvkContextFlag::CpDirtyPipeline + : DxvkContextFlag::GpDirtyPipeline); + } + } + + void DxvkContext::bindVertexBuffer( uint32_t binding, const DxvkBufferBinding& buffer) { @@ -445,9 +444,12 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::CpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::CpDirtyPipeline); + m_state.cp.pipeline = m_device->createComputePipeline( + m_state.cp.cs.shader); + m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, - m_state.cPipe->getPipelineHandle()); - m_cmd->trackResource(m_state.cPipe); + m_state.cp.pipeline->getPipelineHandle()); + m_cmd->trackResource(m_state.cp.pipeline); } } @@ -456,6 +458,10 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::GpDirtyPipeline); + m_state.gp.pipeline = m_device->createGraphicsPipeline( + m_state.gp.vs.shader, m_state.gp.tcs.shader, m_state.gp.tes.shader, + m_state.gp.gs.shader, m_state.gp.fs.shader); + DxvkGraphicsPipelineStateInfo gpState; gpState.inputAssemblyState = m_state.co.inputAssemblyState; gpState.inputLayout = m_state.co.inputLayout; @@ -467,8 +473,8 @@ namespace dxvk { gpState.viewportCount = m_state.vp.viewportCount; m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, - m_state.gPipe->getPipelineHandle(gpState)); - m_cmd->trackResource(m_state.gPipe); + m_state.gp.pipeline->getPipelineHandle(gpState)); + m_cmd->trackResource(m_state.gp.pipeline); } } @@ -477,7 +483,7 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::CpDirtyResources)) { m_flags.clr(DxvkContextFlag::CpDirtyResources); - auto layout = m_state.cPipe->layout(); + auto layout = m_state.cp.pipeline->layout(); m_cmd->bindResourceDescriptors( VK_PIPELINE_BIND_POINT_COMPUTE, @@ -494,7 +500,7 @@ namespace dxvk { if (m_flags.test(DxvkContextFlag::GpDirtyResources)) { m_flags.clr(DxvkContextFlag::GpDirtyResources); - auto layout = m_state.gPipe->layout(); + auto layout = m_state.gp.pipeline->layout(); m_cmd->bindResourceDescriptors( VK_PIPELINE_BIND_POINT_GRAPHICS, diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 64b26c5c7..52f5f48de 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -54,20 +54,6 @@ namespace dxvk { void bindFramebuffer( const Rc& fb); - /** - * \brief Binds compute pipeline - * \param [in] pipeline The pipeline to bind - */ - void bindComputePipeline( - const Rc& pipeline); - - /** - * \brief Binds graphics pipeline - * \param [in] pipeline The pipeline to bind - */ - void bindGraphicsPipeline( - const Rc& pipeline); - /** * \brief Binds index buffer * @@ -133,6 +119,16 @@ namespace dxvk { uint32_t slot, const Rc& sampler); + /** + * \brief Binds a shader to a given state + * + * \param [in] stage Target shader stage + * \param [in] shader The shader to bind + */ + void bindShader( + VkShaderStageFlagBits stage, + const Rc& shader); + /** * \brief Binds vertex buffer * diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 898289d36..3f6910e44 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -7,7 +7,7 @@ #include "dxvk_graphics.h" #include "dxvk_image.h" #include "dxvk_limits.h" -#include "dxvk_pipeline.h" +#include "dxvk_pipelayout.h" #include "dxvk_sampler.h" #include "dxvk_shader.h" @@ -54,6 +54,28 @@ namespace dxvk { }; + struct DxvkShaderStage { + Rc shader; + }; + + + struct DxvkGraphicsPipelineState { + DxvkShaderStage vs; + DxvkShaderStage tcs; + DxvkShaderStage tes; + DxvkShaderStage gs; + DxvkShaderStage fs; + + Rc pipeline; + }; + + + struct DxvkComputePipelineState { + DxvkShaderStage cs; + Rc pipeline; + }; + + /** * \brief Pipeline state * @@ -66,8 +88,8 @@ namespace dxvk { DxvkOutputMergerState om; DxvkConstantStateObjects co; - Rc gPipe; - Rc cPipe; + DxvkGraphicsPipelineState gp; + DxvkComputePipelineState cp; }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index d3732ea03..91e92a705 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -11,7 +11,8 @@ namespace dxvk { m_vkd (vkd), m_features (features), m_memory (new DxvkMemoryAllocator(adapter, vkd)), - m_renderPassPool (new DxvkRenderPassPool (vkd)) { + m_renderPassPool (new DxvkRenderPassPool (vkd)), + m_pipelineManager (new DxvkPipelineManager(vkd)) { m_vkd->vkGetDeviceQueue(m_vkd->device(), m_adapter->graphicsQueueFamily(), 0, &m_graphicsQueue); @@ -23,6 +24,7 @@ namespace dxvk { DxvkDevice::~DxvkDevice() { m_renderPassPool = nullptr; + m_pipelineManager = nullptr; m_memory = nullptr; m_vkd->vkDeviceWaitIdle(m_vkd->device()); @@ -92,34 +94,27 @@ namespace dxvk { Rc DxvkDevice::createShader( VkShaderStageFlagBits stage, + uint32_t slotCount, + const DxvkResourceSlot* slotInfos, const SpirvCodeBuffer& code) { - return new DxvkShader(m_vkd, stage, code); - } - - - Rc DxvkDevice::createBindingLayout( - uint32_t bindingCount, - const DxvkDescriptorSlot* bindingInfos) { - return new DxvkBindingLayout(m_vkd, bindingCount, bindingInfos); + return new DxvkShader(m_vkd, stage, + slotCount, slotInfos, code); } Rc DxvkDevice::createComputePipeline( - const Rc& layout, - const Rc& cs) { - return new DxvkComputePipeline(m_vkd, layout, cs); + const Rc& cs) { + return m_pipelineManager->createComputePipeline(cs); } Rc DxvkDevice::createGraphicsPipeline( - const Rc& layout, - const Rc& vs, - const Rc& tcs, - const Rc& tes, - const Rc& gs, - const Rc& fs) { - return new DxvkGraphicsPipeline(m_vkd, - layout, vs, tcs, tes, gs, fs); + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs) { + return m_pipelineManager->createGraphicsPipeline(vs, tcs, tes, gs, fs); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index a54ad2f5c..f65a323c2 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -8,6 +8,7 @@ #include "dxvk_framebuffer.h" #include "dxvk_image.h" #include "dxvk_memory.h" +#include "dxvk_pipemanager.h" #include "dxvk_renderpass.h" #include "dxvk_sampler.h" #include "dxvk_shader.h" @@ -166,48 +167,36 @@ namespace dxvk { */ Rc createShader( VkShaderStageFlagBits stage, + uint32_t slotCount, + const DxvkResourceSlot* slotInfos, const SpirvCodeBuffer& code); /** - * \brief Creates binding layout - * - * \param [in] bindingCount Number of bindings - * \param [in] bindingInfos Binding descriptions - * \returns New binding layout - */ - Rc createBindingLayout( - uint32_t bindingCount, - const DxvkDescriptorSlot* bindingInfos); - - /** - * \brief Creates a compute pipeline + * \brief Retrieves a compute pipeline * * \param [in] layout Pipeline binding layout * \param [in] cs Compute shader - * \returns New compute pipeline + * \returns The compute pipeline */ Rc createComputePipeline( - const Rc& layout, - const Rc& cs); + const Rc& cs); /** - * \brief Creates a graphics pipeline + * \brief Retrieves a graphics pipeline object * - * \param [in] layout Pipeline binding layout * \param [in] vs Vertex shader * \param [in] tcs Tessellation control shader * \param [in] tes Tessellation evaluation shader * \param [in] gs Geometry shader * \param [in] fs Fragment shader - * \returns New graphics pipeline + * \returns The graphics pipeline */ Rc createGraphicsPipeline( - const Rc& layout, - const Rc& vs, - const Rc& tcs, - const Rc& tes, - const Rc& gs, - const Rc& fs); + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs); /** * \brief Creates a swap chain @@ -252,6 +241,7 @@ namespace dxvk { Rc m_memory; Rc m_renderPassPool; + Rc m_pipelineManager; VkQueue m_graphicsQueue; VkQueue m_presentQueue; diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index e9dabab7d..50b9d1f0c 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -39,16 +39,29 @@ namespace dxvk { DxvkGraphicsPipeline::DxvkGraphicsPipeline( - const Rc& vkd, - const Rc& layout, - const Rc& vs, - const Rc& tcs, - const Rc& tes, - const Rc& gs, - const Rc& fs) - : m_vkd(vkd), m_layout(layout), - m_vs(vs), m_tcs(tcs), m_tes(tes), m_gs(gs), m_fs(fs) { + const Rc& vkd, + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs) + : m_vkd(vkd) { + DxvkDescriptorSlotMapping slotMapping; + if (vs != nullptr) vs ->defineResourceSlots(slotMapping); + if (tcs != nullptr) tcs->defineResourceSlots(slotMapping); + if (tes != nullptr) tes->defineResourceSlots(slotMapping); + if (gs != nullptr) gs ->defineResourceSlots(slotMapping); + if (fs != nullptr) fs ->defineResourceSlots(slotMapping); + m_layout = new DxvkBindingLayout(vkd, + slotMapping.bindingCount(), + slotMapping.bindingInfos()); + + if (vs != nullptr) m_vs = vs ->createShaderModule(slotMapping); + if (tcs != nullptr) m_tcs = tcs->createShaderModule(slotMapping); + if (tes != nullptr) m_tes = tes->createShaderModule(slotMapping); + if (gs != nullptr) m_gs = gs ->createShaderModule(slotMapping); + if (fs != nullptr) m_fs = fs ->createShaderModule(slotMapping); } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 06cb05295..83d5d6115 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -5,7 +5,7 @@ #include "dxvk_constant_state.h" #include "dxvk_hash.h" -#include "dxvk_pipeline.h" +#include "dxvk_pipelayout.h" #include "dxvk_resource.h" #include "dxvk_shader.h" @@ -49,13 +49,12 @@ namespace dxvk { public: DxvkGraphicsPipeline( - const Rc& vkd, - const Rc& layout, - const Rc& vs, - const Rc& tcs, - const Rc& tes, - const Rc& gs, - const Rc& fs); + const Rc& vkd, + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs); ~DxvkGraphicsPipeline(); /** @@ -82,11 +81,11 @@ namespace dxvk { Rc m_vkd; Rc m_layout; - Rc m_vs; - Rc m_tcs; - Rc m_tes; - Rc m_gs; - Rc m_fs; + Rc m_vs; + Rc m_tcs; + Rc m_tes; + Rc m_gs; + Rc m_fs; std::mutex m_mutex; diff --git a/src/dxvk/dxvk_pipeline.cpp b/src/dxvk/dxvk_pipelayout.cpp similarity index 68% rename from src/dxvk/dxvk_pipeline.cpp rename to src/dxvk/dxvk_pipelayout.cpp index 1c98aa8b1..77c25160e 100644 --- a/src/dxvk/dxvk_pipeline.cpp +++ b/src/dxvk/dxvk_pipelayout.cpp @@ -1,9 +1,44 @@ #include -#include "dxvk_pipeline.h" +#include "dxvk_pipelayout.h" namespace dxvk { + DxvkDescriptorSlotMapping:: DxvkDescriptorSlotMapping() { } + DxvkDescriptorSlotMapping::~DxvkDescriptorSlotMapping() { } + + + void DxvkDescriptorSlotMapping::defineSlot( + uint32_t slot, + VkDescriptorType type, + VkShaderStageFlagBits stage) { + uint32_t bindingId = this->getBindingId(slot); + + if (bindingId != InvalidBinding) { + m_descriptorSlots.at(bindingId).stages |= stage; + } else { + DxvkDescriptorSlot slotInfo; + slotInfo.slot = slot; + slotInfo.type = type; + slotInfo.stages = stage; + m_descriptorSlots.push_back(slotInfo); + } + } + + + uint32_t DxvkDescriptorSlotMapping::getBindingId(uint32_t slot) { + // This won't win a performance competition, but the number + // of bindings used by a shader is usually much smaller than + // the number of resource slots available to the system. + for (uint32_t i = 0; i < m_descriptorSlots.size(); i++) { + if (m_descriptorSlots.at(i).slot == slot) + return i; + } + + return InvalidBinding; + } + + DxvkBindingLayout::DxvkBindingLayout( const Rc& vkd, uint32_t bindingCount, diff --git a/src/dxvk/dxvk_pipeline.h b/src/dxvk/dxvk_pipelayout.h similarity index 53% rename from src/dxvk/dxvk_pipeline.h rename to src/dxvk/dxvk_pipelayout.h index 664347fa1..d12eae021 100644 --- a/src/dxvk/dxvk_pipeline.h +++ b/src/dxvk/dxvk_pipelayout.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "dxvk_include.h" namespace dxvk { @@ -18,6 +20,69 @@ namespace dxvk { }; + /** + * \brief Descriptor slot mapping + * + * Convenience class that generates descriptor slot + * index to binding index mappings. This is required + * when generating Vulkan pipeline and descriptor set + * layouts. + */ + class DxvkDescriptorSlotMapping { + constexpr static uint32_t InvalidBinding = 0xFFFFFFFFu; + public: + + DxvkDescriptorSlotMapping(); + ~DxvkDescriptorSlotMapping(); + + /** + * \brief Number of descriptor bindings + * \returns Descriptor binding count + */ + uint32_t bindingCount() const { + return m_descriptorSlots.size(); + } + + /** + * \brief Descriptor binding infos + * \returns Descriptor binding infos + */ + const DxvkDescriptorSlot* bindingInfos() const { + return m_descriptorSlots.data(); + } + + /** + * \brief Defines a new slot + * + * Adds a slot to the mapping. If the slot is already + * defined by another shader stage, this will extend + * the stage mask by the given stage. Otherwise, an + * entirely new binding is added. + * \param [in] slot Resource slot + * \param [in] type Resource type + * \param [in] stage Shader stage + */ + void defineSlot( + uint32_t slot, + VkDescriptorType type, + VkShaderStageFlagBits stage); + + /** + * \brief Gets binding ID for a slot + * + * \param [in] slot Resource slot + * \returns Binding index, or \c InvalidBinding + */ + uint32_t getBindingId( + uint32_t slot); + + private: + + std::vector m_descriptorSlots; + + }; + + /** * \brief Shader interface * diff --git a/src/dxvk/dxvk_pipemanager.cpp b/src/dxvk/dxvk_pipemanager.cpp new file mode 100644 index 000000000..b4477c661 --- /dev/null +++ b/src/dxvk/dxvk_pipemanager.cpp @@ -0,0 +1,96 @@ +#include "dxvk_pipemanager.h" + +namespace dxvk { + + size_t DxvkPipelineKeyHash::operator () (const DxvkComputePipelineKey& key) const { + std::hash hash; + return hash(key.cs.ptr()); + } + + + size_t DxvkPipelineKeyHash::operator () (const DxvkGraphicsPipelineKey& key) const { + DxvkHashState state; + + std::hash hash; + state.add(hash(key.vs.ptr())); + state.add(hash(key.tcs.ptr())); + state.add(hash(key.tes.ptr())); + state.add(hash(key.gs.ptr())); + state.add(hash(key.fs.ptr())); + return state; + } + + + bool DxvkPipelineKeyEq::operator () (const DxvkComputePipelineKey& a, const DxvkComputePipelineKey& b) const { + return a.cs == b.cs; + } + + + bool DxvkPipelineKeyEq::operator () (const DxvkGraphicsPipelineKey& a, const DxvkGraphicsPipelineKey& b) const { + return a.vs == b.vs + && a.tcs == b.tcs + && a.tes == b.tes + && a.gs == b.gs + && a.fs == b.fs; + } + + + DxvkPipelineManager::DxvkPipelineManager(const Rc& vkd) + : m_vkd(vkd) { } + + + DxvkPipelineManager::~DxvkPipelineManager() { + + } + + + Rc DxvkPipelineManager::createComputePipeline( + const Rc& cs) { + if (cs == nullptr) + return nullptr; + + DxvkComputePipelineKey key; + key.cs = cs; + + std::lock_guard lock(m_mutex); + auto pair = m_computePipelines.find(key); + + if (pair != m_computePipelines.end()) + return pair->second; + + const Rc pipeline + = new DxvkComputePipeline(m_vkd, cs); + m_computePipelines.insert(std::make_pair(key, pipeline)); + return pipeline; + } + + + Rc DxvkPipelineManager::createGraphicsPipeline( + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs) { + if (vs == nullptr) + return nullptr; + + DxvkGraphicsPipelineKey key; + key.vs = vs; + key.tcs = tcs; + key.tes = tes; + key.gs = gs; + key.fs = fs; + + std::lock_guard lock(m_mutex); + auto pair = m_graphicsPipelines.find(key); + + if (pair != m_graphicsPipelines.end()) + return pair->second; + + const Rc pipeline + = new DxvkGraphicsPipeline(m_vkd, vs, tcs, tes, gs, fs); + m_graphicsPipelines.insert(std::make_pair(key, pipeline)); + return pipeline; + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_pipemanager.h b/src/dxvk/dxvk_pipemanager.h new file mode 100644 index 000000000..003f9a13d --- /dev/null +++ b/src/dxvk/dxvk_pipemanager.h @@ -0,0 +1,118 @@ +#pragma once + +#include +#include + +#include "dxvk_compute.h" +#include "dxvk_graphics.h" + +namespace dxvk { + + /** + * \brief Compute pipeline key + * + * Identifier for a compute pipeline object. + * Consists of the compute shader itself. + */ + struct DxvkComputePipelineKey { + Rc cs; + }; + + + /** + * \brief Graphics pipeline key + * + * Identifier for a graphics pipeline object. + * Consists of all graphics pipeline shaders. + */ + struct DxvkGraphicsPipelineKey { + Rc vs; + Rc tcs; + Rc tes; + Rc gs; + Rc fs; + }; + + + struct DxvkPipelineKeyHash { + size_t operator () (const DxvkComputePipelineKey& key) const; + size_t operator () (const DxvkGraphicsPipelineKey& key) const; + }; + + + struct DxvkPipelineKeyEq { + bool operator () (const DxvkComputePipelineKey& a, const DxvkComputePipelineKey& b) const; + bool operator () (const DxvkGraphicsPipelineKey& a, const DxvkGraphicsPipelineKey& b) const; + }; + + + /** + * \brief Pipeline manager + * + * Creates and stores graphics pipelines and compute + * pipelines for each combination of shaders that is + * used within the application. This is necessary + * because DXVK does not expose the concept of shader + * pipeline objects to the client API. + */ + class DxvkPipelineManager : public RcObject { + + public: + + DxvkPipelineManager( + const Rc& vkd); + ~DxvkPipelineManager(); + + /** + * \brief Retrieves a compute pipeline object + * + * If a pipeline for the given shader stage object + * already exists, it will be returned. Otherwise, + * a new pipeline will be created. + * \param [in] cs Compute shader + * \returns Compute pipeline object + */ + Rc createComputePipeline( + const Rc& cs); + + /** + * \brief Retrieves a graphics pipeline object + * + * If a pipeline for the given shader stage objects + * already exists, it will be returned. Otherwise, + * a new pipeline will be created. + * \param [in] vs Vertex shader + * \param [in] tcs Tessellation control shader + * \param [in] tes Tessellation evaluation shader + * \param [in] gs Geometry shader + * \param [in] fs Fragment shader + * \returns Graphics pipeline object + */ + Rc createGraphicsPipeline( + const Rc& vs, + const Rc& tcs, + const Rc& tes, + const Rc& gs, + const Rc& fs); + + private: + + const Rc m_vkd; + + std::mutex m_mutex; + + std::unordered_map< + DxvkComputePipelineKey, + Rc, + DxvkPipelineKeyHash, + DxvkPipelineKeyEq> m_computePipelines; + + std::unordered_map< + DxvkGraphicsPipelineKey, + Rc, + DxvkPipelineKeyHash, + DxvkPipelineKeyEq> m_graphicsPipelines; + + }; + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index b52cbc928..d9c1e1c6d 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -2,7 +2,7 @@ namespace dxvk { - DxvkShader::DxvkShader( + DxvkShaderModule::DxvkShaderModule( const Rc& vkd, VkShaderStageFlagBits stage, const SpirvCodeBuffer& code) @@ -20,13 +20,13 @@ namespace dxvk { } - DxvkShader::~DxvkShader() { + DxvkShaderModule::~DxvkShaderModule() { m_vkd->vkDestroyShaderModule( m_vkd->device(), m_module, nullptr); } - VkPipelineShaderStageCreateInfo DxvkShader::stageInfo() const { + VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo() const { VkPipelineShaderStageCreateInfo info; info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -39,4 +39,35 @@ namespace dxvk { return info; } + + DxvkShader::DxvkShader( + const Rc& vkd, + VkShaderStageFlagBits stage, + uint32_t slotCount, + const DxvkResourceSlot* slotInfos, + const SpirvCodeBuffer& code) + : m_vkd(vkd), m_stage(stage), m_code(code) { + for (uint32_t i = 0; i < slotCount; i++) + m_slots.push_back(slotInfos[i]); + } + + + DxvkShader::~DxvkShader() { + + } + + + void DxvkShader::defineResourceSlots( + DxvkDescriptorSlotMapping& mapping) const { + for (const auto& slot : m_slots) + mapping.defineSlot(slot.slot, slot.type, m_stage); + } + + + Rc DxvkShader::createShaderModule( + const DxvkDescriptorSlotMapping& mapping) const { + // TODO apply mapping + return new DxvkShaderModule(m_vkd, m_stage, m_code); + } + } \ No newline at end of file diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index 2da757f9f..22dac7bad 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -3,6 +3,7 @@ #include #include "dxvk_include.h" +#include "dxvk_pipelayout.h" #include "../spirv/spirv_code_buffer.h" @@ -15,30 +16,31 @@ namespace dxvk { * binding that a shader can access. */ struct DxvkResourceSlot { - uint32_t binding; + uint32_t slot; VkDescriptorType type; }; /** - * \brief Shader module + * \brief Shader module object * * Manages a Vulkan shader module. This will not - * perform any sort of shader compilation. Instead, - * the context will create pipeline objects on the + * perform any shader compilation. Instead, the + * context will create pipeline objects on the * fly when executing draw calls. */ - class DxvkShader : public RcObject { + class DxvkShaderModule : public RcObject { public: - DxvkShader( + DxvkShaderModule( const Rc& vkd, VkShaderStageFlagBits stage, const SpirvCodeBuffer& code); - ~DxvkShader(); - VkShaderModule module() const { + ~DxvkShaderModule(); + + VkShaderModule handle() const { return m_module; } @@ -52,4 +54,51 @@ namespace dxvk { }; + + /** + * \brief Shader object + * + * Stores a SPIR-V shader and information on the + * bindings that the shader uses. In order to use + * the shader with a pipeline, a shader module + * needs to be created from he shader object. + */ + class DxvkShader : public RcObject { + + public: + + DxvkShader( + const Rc& vkd, + VkShaderStageFlagBits stage, + uint32_t slotCount, + const DxvkResourceSlot* slotInfos, + const SpirvCodeBuffer& code); + ~DxvkShader(); + + /** + * \brief + */ + void defineResourceSlots( + DxvkDescriptorSlotMapping& mapping) const; + + /** + * \brief Creates a shader module + * + * Maps the binding slot numbers + * \param [in] mapping Resource slot mapping + * \returns The shader module + */ + Rc createShaderModule( + const DxvkDescriptorSlotMapping& mapping) const; + + private: + + Rc m_vkd; + VkShaderStageFlagBits m_stage; + SpirvCodeBuffer m_code; + + std::vector m_slots; + + }; + } \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 52a0df48f..b8a0098bb 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -16,7 +16,8 @@ dxvk_src = files([ 'dxvk_lifetime.cpp', 'dxvk_main.cpp', 'dxvk_memory.cpp', - 'dxvk_pipeline.cpp', + 'dxvk_pipelayout.cpp', + 'dxvk_pipemanager.cpp', 'dxvk_renderpass.cpp', 'dxvk_resource.cpp', 'dxvk_sampler.cpp', diff --git a/src/spirv/spirv_instruction.h b/src/spirv/spirv_instruction.h index b22f07524..b1e448de6 100644 --- a/src/spirv/spirv_instruction.h +++ b/src/spirv/spirv_instruction.h @@ -29,7 +29,7 @@ namespace dxvk { */ spv::Op opCode() const { return static_cast( - m_code[0] & spv::OpCodeMask); + this->arg(0) & spv::OpCodeMask); } /** @@ -37,7 +37,7 @@ namespace dxvk { * \returns Number of DWORDs */ uint32_t length() const { - return m_code[0] >> spv::WordCountShift; + return this->arg(0) >> spv::WordCountShift; } /** diff --git a/tests/dxvk/test_dxvk_triangle.cpp b/tests/dxvk/test_dxvk_triangle.cpp index af0986e79..f7c18071c 100644 --- a/tests/dxvk/test_dxvk_triangle.cpp +++ b/tests/dxvk/test_dxvk_triangle.cpp @@ -113,18 +113,12 @@ public: 0, nullptr)); m_dxvkVertexShader = m_dxvkDevice->createShader( - VK_SHADER_STAGE_VERTEX_BIT, + VK_SHADER_STAGE_VERTEX_BIT, 0, nullptr, SpirvCodeBuffer(_countof(vsCode), vsCode)); + m_dxvkFragmentShader = m_dxvkDevice->createShader( - VK_SHADER_STAGE_FRAGMENT_BIT, + VK_SHADER_STAGE_FRAGMENT_BIT, 0, nullptr, SpirvCodeBuffer(_countof(fsCode), fsCode)); - - m_dxvkBindingLayout = m_dxvkDevice->createBindingLayout(0, nullptr); - - m_dxvkPipeline = m_dxvkDevice->createGraphicsPipeline(m_dxvkBindingLayout, - m_dxvkVertexShader, nullptr, nullptr, nullptr, m_dxvkFragmentShader); - - m_dxvkContext->bindGraphicsPipeline(m_dxvkPipeline); } ~TriangleApp() { @@ -158,6 +152,14 @@ public: m_dxvkContext->setViewports(1, &viewport, &scissor); + m_dxvkContext->bindShader( + VK_SHADER_STAGE_VERTEX_BIT, + m_dxvkVertexShader); + + m_dxvkContext->bindShader( + VK_SHADER_STAGE_FRAGMENT_BIT, + m_dxvkFragmentShader); + VkClearAttachment clearAttachment; clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; clearAttachment.colorAttachment = 0; @@ -198,10 +200,8 @@ private: Rc m_dxvkSwapchain; Rc m_dxvkContext; - Rc m_dxvkVertexShader; - Rc m_dxvkFragmentShader; - Rc m_dxvkBindingLayout; - Rc m_dxvkPipeline; + Rc m_dxvkVertexShader; + Rc m_dxvkFragmentShader; }; diff --git a/tests/meson.build b/tests/meson.build index b6efa3777..16eeef6ca 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,6 +1,6 @@ lib_d3d11 = dxvk_compiler.find_library('d3d11') lib_dxgi = dxvk_compiler.find_library('dxgi') -lib_d3dcompiler_47 = dxvk_compiler.find_library('d3dcompiler_47') +lib_d3dcompiler_47 = dxvk_compiler.find_library('d3dcompiler') subdir('d3d11') subdir('dxbc')