diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp new file mode 100644 index 00000000..a51f7ed9 --- /dev/null +++ b/src/dxvk/dxvk_compute.cpp @@ -0,0 +1,17 @@ +#include "dxvk_compute.h" + +namespace dxvk { + + DxvkComputePipeline::DxvkComputePipeline( + const Rc& vkd) + : m_vkd(vkd) { + // TODO implement + } + + + DxvkComputePipeline::~DxvkComputePipeline() { + 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 new file mode 100644 index 00000000..0a211d70 --- /dev/null +++ b/src/dxvk/dxvk_compute.h @@ -0,0 +1,34 @@ +#pragma once + +#include "dxvk_shader.h" + +namespace dxvk { + + /** + * \brief Compute pipeline + * + * Stores a pipeline object + */ + class DxvkComputePipeline : public RcObject { + + public: + + DxvkComputePipeline(const Rc& vkd); + ~DxvkComputePipeline(); + + /** + * \brief Pipeline handle + * \returns Pipeline handle + */ + VkPipeline handle() const { + return m_pipeline; + } + + private: + + Rc m_vkd; + VkPipeline m_pipeline; + + }; + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 3fc340bf..b4563cda 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -19,13 +19,30 @@ namespace dxvk { TRACE(this, cmdList); m_commandList = cmdList; m_commandList->beginRecording(); + + // Make sure that we apply the current context state + // to the command buffer when recording draw commands. + m_state.g.flags.clr( + DxvkGraphicsPipelineBit::RenderPassBound); + m_state.g.flags.set( + DxvkGraphicsPipelineBit::PipelineDirty, + DxvkGraphicsPipelineBit::PipelineStateDirty, + DxvkGraphicsPipelineBit::DirtyResources, + DxvkGraphicsPipelineBit::DirtyVertexBuffers, + DxvkGraphicsPipelineBit::DirtyIndexBuffer); + + m_state.c.flags.set( + DxvkComputePipelineBit::PipelineDirty, + DxvkComputePipelineBit::DirtyResources); } bool DxvkContext::endRecording() { TRACE(this); - if (m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) + // Any currently active render pass must be + // ended before finalizing the command buffer. + if (m_state.g.flags.test(DxvkGraphicsPipelineBit::RenderPassBound)) this->endRenderPass(); // Finalize the command list @@ -38,8 +55,7 @@ namespace dxvk { void DxvkContext::clearRenderTarget( const VkClearAttachment& attachment, const VkClearRect& clearArea) { - if (!m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) - this->beginRenderPass(); + this->flushGraphicsState(); m_vkd->vkCmdClearAttachments( m_commandList->handle(), @@ -48,12 +64,25 @@ namespace dxvk { } + void DxvkContext::dispatch( + uint32_t wgCountX, + uint32_t wgCountY, + uint32_t wgCountZ) { + this->flushComputeState(); + + m_vkd->vkCmdDispatch( + m_commandList->handle(), + wgCountX, wgCountY, wgCountZ); + } + + void DxvkContext::draw( uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { - this->prepareDraw(); + this->flushGraphicsState(); + m_vkd->vkCmdDraw( m_commandList->handle(), vertexCount, @@ -69,7 +98,8 @@ namespace dxvk { uint32_t firstIndex, uint32_t vertexOffset, uint32_t firstInstance) { - this->prepareDraw(); + this->flushGraphicsState(); + m_vkd->vkCmdDrawIndexed( m_commandList->handle(), indexCount, @@ -84,37 +114,57 @@ namespace dxvk { const Rc& fb) { TRACE(this, fb); - // When changing the framebuffer binding, we end the - // current render pass, but beginning the new render - // pass is deferred until a draw command is called. - if (m_state.fb.framebuffer != fb) { - if (m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) - this->endRenderPass(); + if (m_state.g.fb != fb) { + m_state.g.fb = fb; - m_state.fb.framebuffer = fb; - m_commandList->trackResource(fb); + if (m_state.g.flags.test( + DxvkGraphicsPipelineBit::RenderPassBound)) + this->endRenderPass(); } - } - + void DxvkContext::setShader( VkShaderStageFlagBits stage, const Rc& shader) { TRACE(this, stage, shader); + DxvkShaderState* state = this->getShaderState(stage); + + if (state->shader != shader) { + state->shader = shader; + + if (stage == VK_SHADER_STAGE_COMPUTE_BIT) { + m_state.c.flags.set( + DxvkComputePipelineBit::PipelineDirty, + DxvkComputePipelineBit::DirtyResources); + } else { + m_state.g.flags.set( + DxvkGraphicsPipelineBit::PipelineDirty, + DxvkGraphicsPipelineBit::DirtyResources); + } + } + } + + + void DxvkContext::flushComputeState() { + VkCommandBuffer cmd = m_commandList->handle(); + + if (m_state.c.flags.test(DxvkComputePipelineBit::PipelineDirty) + && m_state.c.pipeline != nullptr) { + m_vkd->vkCmdBindPipeline(cmd, + VK_PIPELINE_BIND_POINT_COMPUTE, + m_state.c.pipeline->handle()); + } + + m_state.c.flags.clr( + DxvkComputePipelineBit::PipelineDirty, + DxvkComputePipelineBit::DirtyResources); } void DxvkContext::flushGraphicsState() { - - } - - - void DxvkContext::prepareDraw() { - this->flushGraphicsState(); - - if (!m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) + if (!m_state.g.flags.test(DxvkGraphicsPipelineBit::RenderPassBound)) this->beginRenderPass(); } @@ -122,22 +172,20 @@ namespace dxvk { void DxvkContext::beginRenderPass() { TRACE(this); - const DxvkFramebufferSize fbsize - = m_state.fb.framebuffer->size(); + DxvkFramebufferSize fbsize + = m_state.g.fb->size(); VkRenderPassBeginInfo info; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.pNext = nullptr; - info.renderPass = m_state.fb.framebuffer->renderPass(); - info.framebuffer = m_state.fb.framebuffer->handle(); + info.renderPass = m_state.g.fb->renderPass(); + info.framebuffer = m_state.g.fb->handle(); info.renderArea = VkRect2D { { 0, 0 }, { fbsize.width, fbsize.height } }; info.clearValueCount = 0; info.pClearValues = nullptr; - m_vkd->vkCmdBeginRenderPass( - m_commandList->handle(), - &info, VK_SUBPASS_CONTENTS_INLINE); - m_state.fb.flags.set(DxvkFbStateFlags::InsideRenderPass); + m_vkd->vkCmdBeginRenderPass(m_commandList->handle(), &info, VK_SUBPASS_CONTENTS_INLINE); + m_state.g.flags.set(DxvkGraphicsPipelineBit::RenderPassBound); } @@ -145,7 +193,33 @@ namespace dxvk { TRACE(this); m_vkd->vkCmdEndRenderPass(m_commandList->handle()); - m_state.fb.flags.clr(DxvkFbStateFlags::InsideRenderPass); + m_state.g.flags.clr(DxvkGraphicsPipelineBit::RenderPassBound); + } + + + DxvkShaderState* DxvkContext::getShaderState(VkShaderStageFlagBits stage) { + switch (stage) { + case VK_SHADER_STAGE_VERTEX_BIT: + return &m_state.g.vs; + + case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: + return &m_state.g.tcs; + + case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: + return &m_state.g.tes; + + case VK_SHADER_STAGE_GEOMETRY_BIT: + return &m_state.g.gs; + + case VK_SHADER_STAGE_FRAGMENT_BIT: + return &m_state.g.fs; + + case VK_SHADER_STAGE_COMPUTE_BIT: + return &m_state.c.cs; + + default: + return nullptr; + } } } \ No newline at end of file diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 828e6d97..3af8bd97 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1,7 +1,5 @@ #pragma once -#define DXVK_ERROR_CHECKING 1 - #include "dxvk_cmdlist.h" #include "dxvk_context_state.h" @@ -61,6 +59,18 @@ namespace dxvk { const VkClearAttachment& attachment, const VkClearRect& clearArea); + /** + * \brief Dispatches compute operations + * + * \param [in] wgCountX Number of X work groups + * \param [in] wgCountY Number of Y work groups + * \param [in] wgCountZ Number of Z work groups + */ + void dispatch( + uint32_t wgCountX, + uint32_t wgCountY, + uint32_t wgCountZ); + /** * \brief Draws primitive without using an index buffer * @@ -118,20 +128,15 @@ namespace dxvk { Rc m_commandList; DxvkContextState m_state; - /** - * \brief Forces a graphics pipeline state flush - * - * Applies current shader bindings, resource bindings - * etc. to the command buffer so that draw calls can - * be executed. Called whenever the need arises. - */ + void flushComputeState(); void flushGraphicsState(); - void prepareDraw(); - void beginRenderPass(); void endRenderPass(); + DxvkShaderState* getShaderState( + VkShaderStageFlagBits stage); + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 9a4b208d..a24f99df 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -1,21 +1,113 @@ #pragma once +#include "dxvk_compute.h" #include "dxvk_framebuffer.h" #include "dxvk_shader.h" namespace dxvk { - enum class DxvkFbStateFlags : uint32_t { - InsideRenderPass = 0, + /** + * \brief Limits of the DXVK API + * + * Stores the number of binding slots + * available for all resource types. + */ + enum DxvkLimits : size_t { + MaxNumRenderTargets = 8, + MaxNumUniformBuffers = 16, + MaxNumSampledImages = 16, + MaxNumStorageBuffers = 128, + MaxNumStorageImages = 128, + MaxNumVertexBuffers = 32, + MaxNumOutputStreams = 4, }; - struct DxvkFramebufferState { - Rc framebuffer; - Flags flags; + + /** + * \brief Graphics pipeline state flags + * + * Stores some information on which state of the + * graphics pipeline has changed and/or needs to + * be updated. + */ + enum class DxvkGraphicsPipelineBit : uint64_t { + RenderPassBound = 0, ///< If set, a render pass instance is currently active + PipelineDirty = 1, ///< If set, the shader pipeline binding is out of date + PipelineStateDirty = 2, ///< If set, another pipeline variant needs to be bound + DirtyResources = 3, ///< If set, the descriptor set must be updated + DirtyVertexBuffers = 4, ///< If set, the vertex buffer bindings need to be updated + DirtyIndexBuffer = 5, ///< If set, the index buffer binding needs to be updated }; + using DxvkGraphicsPipelineFlags = Flags; + + + /** + * \brief Compute pipeline state flags + * + * Stores information on whether the compute shader + * or any of its resource bindings have been updated. + */ + enum class DxvkComputePipelineBit : uint64_t { + PipelineDirty = 0, ///< If set, the shader pipeline binding is out of date + DirtyResources = 1, ///< If set, the descriptor set must be updated + }; + + using DxvkComputePipelineFlags = Flags; + + + /** + * \brief Shader state + * + * Stores the active shader and resources for a single + * shader stage. This includes sampled textures, uniform + * buffers, storage buffers and storage images. + */ + struct DxvkShaderState { + Rc shader; + }; + + + /** + * \brief Graphics pipeline state + * + * Stores everything related to graphics + * operations, including bound resources. + */ + struct DxvkGraphicsPipelineState { + DxvkShaderState vs; + DxvkShaderState tcs; + DxvkShaderState tes; + DxvkShaderState gs; + DxvkShaderState fs; + Rc fb; + DxvkGraphicsPipelineFlags flags; + }; + + + /** + * \brief Compute pipeline state + * + * Stores the active compute pipeline and + * resources bound to the compute shader. + */ + struct DxvkComputePipelineState { + DxvkShaderState cs; + Rc pipeline; + DxvkComputePipelineFlags flags; + }; + + + /** + * \brief DXVK context state + * + * Stores all graphics pipeline state known + * to DXVK. As in Vulkan, graphics and compute + * pipeline states are strictly separated. + */ struct DxvkContextState { - DxvkFramebufferState fb; ///< Framebuffer and render pass + DxvkGraphicsPipelineState g; + DxvkComputePipelineState c; }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_lifetime.cpp b/src/dxvk/dxvk_lifetime.cpp index 648a846f..c99c26de 100644 --- a/src/dxvk/dxvk_lifetime.cpp +++ b/src/dxvk/dxvk_lifetime.cpp @@ -8,13 +8,13 @@ namespace dxvk { void DxvkLifetimeTracker::trackResource(const Rc& rc) { if (m_resources.insert(rc).second) - rc->incUseCount(); + rc->acquire(); } void DxvkLifetimeTracker::reset() { for (auto i = m_resources.cbegin(); i != m_resources.cend(); i++) - (*i)->decUseCount(); + (*i)->release(); m_resources.clear(); } diff --git a/src/dxvk/dxvk_resource.h b/src/dxvk/dxvk_resource.h index a6ca987e..e62fa11c 100644 --- a/src/dxvk/dxvk_resource.h +++ b/src/dxvk/dxvk_resource.h @@ -21,8 +21,8 @@ namespace dxvk { return m_useCount != 0; } - void incUseCount() { m_useCount += 1; } - void decUseCount() { m_useCount -= 1; } + void acquire() { m_useCount += 1; } + void release() { m_useCount -= 1; } private: diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 5a1215b4..922e8414 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -1,6 +1,7 @@ dxvk_src = files([ 'dxvk_adapter.cpp', 'dxvk_cmdlist.cpp', + 'dxvk_compute.cpp', 'dxvk_context.cpp', 'dxvk_device.cpp', 'dxvk_framebuffer.cpp',