From e433c01ad4db60da965d0300a4158b4d0124809e Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 15 Oct 2017 17:56:06 +0200 Subject: [PATCH] [dxvk] Some more work on shader resources and resource bindings --- src/dxvk/dxvk_barrier.cpp | 70 +++++++++++ src/dxvk/dxvk_barrier.h | 54 +++++++++ src/dxvk/dxvk_buffer.h | 60 ++++++++++ src/dxvk/dxvk_cmdlist.cpp | 18 +++ src/dxvk/dxvk_cmdlist.h | 11 ++ src/dxvk/dxvk_compute.cpp | 2 + src/dxvk/dxvk_context.cpp | 135 +++++++++++++++------- src/dxvk/dxvk_context.h | 75 ++++++++---- src/dxvk/dxvk_context_state.h | 7 +- src/dxvk/dxvk_device.cpp | 19 +-- src/dxvk/dxvk_device.h | 7 +- src/dxvk/dxvk_graphics.cpp | 10 +- src/dxvk/dxvk_graphics.h | 12 +- src/dxvk/dxvk_memory.h | 2 +- src/dxvk/dxvk_pipemgr.cpp | 7 +- src/dxvk/dxvk_pipemgr.h | 1 + src/dxvk/dxvk_recorder.h | 11 ++ src/dxvk/dxvk_renderpass.h | 2 +- src/dxvk/dxvk_shader.h | 14 +++ src/dxvk/dxvk_util.cpp | 23 ++++ src/dxvk/dxvk_util.h | 16 +++ src/dxvk/meson.build | 2 + src/dxvk/spirv/dxvk_spirv_code_buffer.cpp | 25 ++-- src/dxvk/spirv/dxvk_spirv_code_buffer.h | 7 +- tests/dxvk/test_dxvk_triangle.cpp | 47 +++++++- 25 files changed, 532 insertions(+), 105 deletions(-) create mode 100644 src/dxvk/dxvk_barrier.cpp create mode 100644 src/dxvk/dxvk_barrier.h create mode 100644 src/dxvk/dxvk_util.cpp create mode 100644 src/dxvk/dxvk_util.h diff --git a/src/dxvk/dxvk_barrier.cpp b/src/dxvk/dxvk_barrier.cpp new file mode 100644 index 000000000..2977bc4bf --- /dev/null +++ b/src/dxvk/dxvk_barrier.cpp @@ -0,0 +1,70 @@ +#include "dxvk_barrier.h" + +namespace dxvk { + + DxvkBarrierSet:: DxvkBarrierSet() { } + DxvkBarrierSet::~DxvkBarrierSet() { } + + bool DxvkBarrierSet::hasBarriers() const { + return (m_srcFlags | m_dstFlags) != 0; + } + + + void DxvkBarrierSet::addMemoryBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkMemoryBarrier& barrier) { + m_srcFlags |= srcFlags; + m_dstFlags |= dstFlags; + m_memory.push_back(barrier); + } + + + void DxvkBarrierSet::addBufferBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkBufferMemoryBarrier& barrier) { + m_srcFlags |= srcFlags; + m_dstFlags |= dstFlags; + m_buffer.push_back(barrier); + } + + + void DxvkBarrierSet::addImageBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkImageMemoryBarrier& barrier) { + m_srcFlags |= srcFlags; + m_dstFlags |= dstFlags; + m_image.push_back(barrier); + } + + + void DxvkBarrierSet::recordCommands( + DxvkRecorder& recorder) { + VkPipelineStageFlags srcFlags = m_srcFlags; + VkPipelineStageFlags dstFlags = m_dstFlags; + + if (srcFlags == 0) srcFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + if (dstFlags == 0) dstFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + recorder.cmdPipelineBarrier( + srcFlags, dstFlags, 0, + m_memory.size(), m_memory.data(), + m_buffer.size(), m_buffer.data(), + m_image.size(), m_image.data()); + + this->reset(); + } + + + void DxvkBarrierSet::reset() { + m_srcFlags = 0; + m_dstFlags = 0; + + m_memory.resize(0); + m_buffer.resize(0); + m_image .resize(0); + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_barrier.h b/src/dxvk/dxvk_barrier.h new file mode 100644 index 000000000..5604a5e73 --- /dev/null +++ b/src/dxvk/dxvk_barrier.h @@ -0,0 +1,54 @@ +#pragma once + +#include "dxvk_recorder.h" + +namespace dxvk { + + /** + * \brief Barrier set + * + * Accumulates memory barriers and provides a + * method to record all those barriers into a + * command buffer at once. + */ + class DxvkBarrierSet { + + public: + + DxvkBarrierSet(); + ~DxvkBarrierSet(); + + bool hasBarriers() const; + + void addMemoryBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkMemoryBarrier& barrier); + + void addBufferBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkBufferMemoryBarrier& barrier); + + void addImageBarrier( + VkPipelineStageFlags srcFlags, + VkPipelineStageFlags dstFlags, + const VkImageMemoryBarrier& barrier); + + void recordCommands( + DxvkRecorder& recorder); + + void reset(); + + private: + + VkPipelineStageFlags m_srcFlags = 0; + VkPipelineStageFlags m_dstFlags = 0; + + std::vector m_memory; + std::vector m_buffer; + std::vector m_image; + + }; + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_buffer.h b/src/dxvk/dxvk_buffer.h index 6c9936105..0705d9050 100644 --- a/src/dxvk/dxvk_buffer.h +++ b/src/dxvk/dxvk_buffer.h @@ -19,6 +19,13 @@ namespace dxvk { /// Buffer usage flags VkBufferUsageFlags usage; + /// Pipeline stages that can access + /// the contents of the buffer. + VkPipelineStageFlags stages; + + /// Allowed access patterns + VkAccessFlags access; + }; @@ -77,4 +84,57 @@ namespace dxvk { }; + + /** + * \brief Buffer binding + * + * Stores the buffer and the sub-range of the buffer + * to bind. Bindings are considered equal if all three + * parameters are the same. + */ + class DxvkBufferBinding { + + public: + + DxvkBufferBinding() { } + DxvkBufferBinding( + const Rc& buffer, + VkDeviceSize rangeOffset, + VkDeviceSize rangeLength) + : m_buffer(buffer), + m_offset(rangeOffset), + m_length(rangeLength) { } + + Rc resource() const { + return m_buffer; + } + + VkDescriptorBufferInfo descriptorInfo() const { + VkDescriptorBufferInfo info; + info.buffer = m_buffer->handle(); + info.offset = m_offset; + info.range = m_length; + return info; + } + + bool operator == (const DxvkBufferBinding& other) const { + return this->m_buffer == other.m_buffer + && this->m_offset == other.m_offset + && this->m_length == other.m_length; + } + + bool operator != (const DxvkBufferBinding& other) const { + return this->m_buffer != other.m_buffer + || this->m_offset != other.m_offset + || this->m_length != other.m_length; + } + + private: + + Rc m_buffer = nullptr; + VkDeviceSize m_offset = 0; + VkDeviceSize m_length = 0; + + }; + } \ No newline at end of file diff --git a/src/dxvk/dxvk_cmdlist.cpp b/src/dxvk/dxvk_cmdlist.cpp index 389e2c1e2..34bf1d9a1 100644 --- a/src/dxvk/dxvk_cmdlist.cpp +++ b/src/dxvk/dxvk_cmdlist.cpp @@ -138,4 +138,22 @@ namespace dxvk { m_vkd->vkCmdEndRenderPass(m_buffer); } + + void DxvkCommandList::cmdPipelineBarrier( + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) { + m_vkd->vkCmdPipelineBarrier(m_buffer, + dstStageMask, srcStageMask, dependencyFlags, + memoryBarrierCount, pMemoryBarriers, + bufferMemoryBarrierCount, pBufferMemoryBarriers, + imageMemoryBarrierCount, pImageMemoryBarriers); + } + } \ No newline at end of file diff --git a/src/dxvk/dxvk_cmdlist.h b/src/dxvk/dxvk_cmdlist.h index c11b439c4..782d0a887 100644 --- a/src/dxvk/dxvk_cmdlist.h +++ b/src/dxvk/dxvk_cmdlist.h @@ -104,6 +104,17 @@ namespace dxvk { void cmdEndRenderPass() final; + void cmdPipelineBarrier( + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) final; + private: Rc m_vkd; diff --git a/src/dxvk/dxvk_compute.cpp b/src/dxvk/dxvk_compute.cpp index c9808b8f1..b0bdb93fa 100644 --- a/src/dxvk/dxvk_compute.cpp +++ b/src/dxvk/dxvk_compute.cpp @@ -6,6 +6,7 @@ namespace dxvk { const Rc& vkd, const Rc& shader) : m_vkd(vkd) { + TRACE(this, shader); std::vector bindings; @@ -80,6 +81,7 @@ namespace dxvk { DxvkComputePipeline::~DxvkComputePipeline() { + TRACE(this); this->destroyObjects(); } diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 93ed7f91b..0cf9562f4 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -4,8 +4,11 @@ namespace dxvk { - DxvkContext::DxvkContext(const Rc& device) - : m_device(device) { + DxvkContext::DxvkContext( + const Rc& device, + const Rc& pipeMgr) + : m_device (device), + m_pipeMgr (pipeMgr) { TRACE(this, device); } @@ -53,6 +56,57 @@ namespace dxvk { } + void DxvkContext::bindFramebuffer( + const Rc& fb) { + TRACE(this, fb); + + if (m_state.g.fb != fb) { + m_state.g.fb = fb; + + if (m_state.g.flags.test( + DxvkGraphicsPipelineBit::RenderPassBound)) + this->endRenderPass(); + } + } + + + void DxvkContext::bindShader( + VkShaderStageFlagBits stage, + const Rc& shader) { + TRACE(this, stage, shader); + + DxvkShaderState* state = this->getShaderState(stage); + + if (state->shader != shader) { + state->shader = shader; + this->setPipelineDirty(stage); + } + } + + + void DxvkContext::bindStorageBuffer( + VkShaderStageFlagBits stage, + uint32_t slot, + const Rc& buffer, + VkDeviceSize offset, + VkDeviceSize length) { + TRACE(this, stage, slot); + + DxvkBufferBinding binding(buffer, offset, length); + DxvkShaderState* state = this->getShaderState(stage); + + // TODO investigate whether it is worth checking whether + // the shader actually uses the resource. However, if the + // application is not completely retarded, always setting + // the 'resources dirty' flag should be the best option. + if (state->boundStorageBuffers.at(slot) != binding) { + state->boundStorageBuffers.at(slot) = binding; + this->setResourcesDirty(stage); + m_cmd->trackResource(binding.resource()); + } + } + + void DxvkContext::clearRenderTarget( const VkClearAttachment& attachment, const VkClearRect& clearArea) { @@ -67,6 +121,8 @@ namespace dxvk { uint32_t wgCountX, uint32_t wgCountY, uint32_t wgCountZ) { + TRACE(this, wgCountX, wgCountY, wgCountZ); + this->endRenderPass(); this->flushComputeState(); m_cmd->cmdDispatch( @@ -79,6 +135,8 @@ namespace dxvk { uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) { + TRACE(this, vertexCount, instanceCount, + firstVertex, firstInstance); this->flushGraphicsState(); m_cmd->cmdDraw( @@ -93,6 +151,8 @@ namespace dxvk { uint32_t firstIndex, uint32_t vertexOffset, uint32_t firstInstance) { + TRACE(this, indexCount, instanceCount, + firstIndex, vertexOffset, firstInstance); this->flushGraphicsState(); m_cmd->cmdDrawIndexed( @@ -102,49 +162,15 @@ namespace dxvk { } - void DxvkContext::setFramebuffer( - const Rc& fb) { - TRACE(this, fb); - - if (m_state.g.fb != fb) { - m_state.g.fb = 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() { - if (m_state.c.flags.test(DxvkComputePipelineBit::PipelineDirty) - && m_state.c.pipeline != nullptr) { - m_cmd->cmdBindPipeline( - VK_PIPELINE_BIND_POINT_COMPUTE, - m_state.c.pipeline->getPipelineHandle()); + if (m_state.c.flags.test(DxvkComputePipelineBit::PipelineDirty)) { + m_state.c.pipeline = m_pipeMgr->getComputePipeline(m_state.c.cs.shader); + + if (m_state.c.pipeline != nullptr) { + m_cmd->cmdBindPipeline( + VK_PIPELINE_BIND_POINT_COMPUTE, + m_state.c.pipeline->getPipelineHandle()); + } } m_state.c.flags.clr( @@ -187,6 +213,27 @@ namespace dxvk { } + void DxvkContext::setPipelineDirty(VkShaderStageFlagBits stage) { + 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::setResourcesDirty(VkShaderStageFlagBits stage) { + if (stage == VK_SHADER_STAGE_COMPUTE_BIT) + m_state.c.flags.set(DxvkComputePipelineBit::DirtyResources); + else + m_state.g.flags.set(DxvkGraphicsPipelineBit::DirtyResources); + } + + DxvkShaderState* DxvkContext::getShaderState(VkShaderStageFlagBits stage) { switch (stage) { case VK_SHADER_STAGE_VERTEX_BIT: diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index be27bce9f..1a6cabba3 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1,8 +1,11 @@ #pragma once +#include "dxvk_barrier.h" #include "dxvk_cmdlist.h" #include "dxvk_context_state.h" #include "dxvk_deferred.h" +#include "dxvk_pipemgr.h" +#include "dxvk_util.h" namespace dxvk { @@ -18,7 +21,8 @@ namespace dxvk { public: DxvkContext( - const Rc& device); + const Rc& device, + const Rc& pipeMgr); ~DxvkContext(); /** @@ -51,6 +55,41 @@ namespace dxvk { */ bool endRecording(); + /** + * \brief Sets framebuffer + * \param [in] fb Framebuffer + */ + void bindFramebuffer( + const Rc& fb); + + /** + * \brief Sets shader for a given shader stage + * + * Binds a shader to a given stage, while unbinding the + * existing one. If \c nullptr is passed as the shader + * to bind, the given shader stage will be disabled. + * When drawing, at least a vertex shader must be bound. + * \param [in] stage The shader stage + * \param [in] shader The shader to set + */ + void bindShader( + VkShaderStageFlagBits stage, + const Rc& shader); + + /** + * \brief Binds a storage buffer + * + * \param [in] stage Shader stage for this binding + * \param [in] slot Binding slot index + * \param [in] buffer Buffer binding info + */ + void bindStorageBuffer( + VkShaderStageFlagBits stage, + uint32_t slot, + const Rc& buffer, + VkDeviceSize offset, + VkDeviceSize length); + /** * \brief Clears an active render target * @@ -103,30 +142,10 @@ namespace dxvk { uint32_t vertexOffset, uint32_t firstInstance); - /** - * \brief Sets framebuffer - * \param [in] fb Framebuffer - */ - void setFramebuffer( - const Rc& fb); - - /** - * \brief Sets shader for a given shader stage - * - * Binds a shader to a given stage, while unbinding the - * existing one. If \c nullptr is passed as the shader - * to bind, the given shader stage will be disabled. - * When drawing, at least a vertex shader must be bound. - * \param [in] stage The shader stage - * \param [in] shader The shader to set - */ - void setShader( - VkShaderStageFlagBits stage, - const Rc& shader); - private: - const Rc m_device; + const Rc m_device; + const Rc m_pipeMgr; Rc m_cmd; DxvkContextState m_state; @@ -137,9 +156,19 @@ namespace dxvk { void beginRenderPass(); void endRenderPass(); + void setPipelineDirty(VkShaderStageFlagBits stage); + void setResourcesDirty(VkShaderStageFlagBits stage); + + void shaderResourceBarriers( + DxvkBarrierSet& barriers, + VkShaderStageFlagBits stage); + DxvkShaderState* getShaderState( VkShaderStageFlagBits stage); + VkPipelineStageFlags pipelineStage( + VkShaderStageFlags shaderStage) const; + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 8d4f63632..c2b4a5c10 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -1,7 +1,9 @@ #pragma once +#include "dxvk_buffer.h" #include "dxvk_compute.h" #include "dxvk_framebuffer.h" +#include "dxvk_image.h" #include "dxvk_limits.h" #include "dxvk_shader.h" @@ -48,7 +50,10 @@ namespace dxvk { * buffers, storage buffers and storage images. */ struct DxvkShaderState { - Rc shader; + Rc shader; + + std::array boundStorageBuffers; + std::array boundUniformBuffers; }; diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 7bc3109da..cce5a601d 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -6,10 +6,11 @@ namespace dxvk { DxvkDevice::DxvkDevice( const Rc& adapter, const Rc& vkd) - : m_adapter (adapter), - m_vkd (vkd), - m_memory (adapter, vkd), - m_renderPassPool(vkd) { + : m_adapter (adapter), + m_vkd (vkd), + m_memory (new DxvkMemoryAllocator(adapter, vkd)), + m_renderPassPool (new DxvkRenderPassPool (vkd)), + m_pipelineManager (new DxvkPipelineManager(vkd)) { TRACE(this, adapter); m_vkd->vkGetDeviceQueue(m_vkd->device(), @@ -23,6 +24,10 @@ namespace dxvk { DxvkDevice::~DxvkDevice() { TRACE(this); + m_pipelineManager = nullptr; + m_renderPassPool = nullptr; + m_memory = nullptr; + m_vkd->vkDeviceWaitIdle(m_vkd->device()); m_vkd->vkDestroyDevice(m_vkd->device(), nullptr); } @@ -35,14 +40,14 @@ namespace dxvk { Rc DxvkDevice::createContext() { - return new DxvkContext(this); + return new DxvkContext(this, m_pipelineManager); } Rc DxvkDevice::createFramebuffer( const DxvkRenderTargets& renderTargets) { auto format = renderTargets.renderPassFormat(); - auto renderPass = m_renderPassPool.getRenderPass(format); + auto renderPass = m_renderPassPool->getRenderPass(format); return new DxvkFramebuffer(m_vkd, renderPass, renderTargets); } @@ -51,7 +56,7 @@ namespace dxvk { const DxvkBufferCreateInfo& createInfo, VkMemoryPropertyFlags memoryType) { return new DxvkBuffer(m_vkd, - createInfo, m_memory, memoryType); + createInfo, *m_memory, memoryType); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index e8a73f506..e6b37969c 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -6,6 +6,7 @@ #include "dxvk_context.h" #include "dxvk_framebuffer.h" #include "dxvk_memory.h" +#include "dxvk_pipemgr.h" #include "dxvk_renderpass.h" #include "dxvk_shader.h" #include "dxvk_swapchain.h" @@ -179,13 +180,13 @@ namespace dxvk { Rc m_adapter; Rc m_vkd; - DxvkMemoryAllocator m_memory; - DxvkRenderPassPool m_renderPassPool; + Rc m_memory; + Rc m_renderPassPool; + Rc m_pipelineManager; VkQueue m_graphicsQueue; VkQueue m_presentQueue; - }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index f85261300..21311899b 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -2,17 +2,17 @@ namespace dxvk { - size_t DxvkGraphicsPipelineState::hash() const { + size_t DxvkGraphicsPipelineStateInfo::hash() const { // TODO implement } - bool DxvkGraphicsPipelineState::operator == (const DxvkGraphicsPipelineState& other) const { + bool DxvkGraphicsPipelineStateInfo::operator == (const DxvkGraphicsPipelineStateInfo& other) const { // TODO implement } - bool DxvkGraphicsPipelineState::operator != (const DxvkGraphicsPipelineState& other) const { + bool DxvkGraphicsPipelineStateInfo::operator != (const DxvkGraphicsPipelineStateInfo& other) const { return !this->operator == (other); } @@ -36,7 +36,7 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::getPipelineHandle( - const DxvkGraphicsPipelineState& state) { + const DxvkGraphicsPipelineStateInfo& state) { std::lock_guard lock(m_mutex); auto pair = m_pipelines.find(state); @@ -50,7 +50,7 @@ namespace dxvk { VkPipeline DxvkGraphicsPipeline::compilePipeline( - const DxvkGraphicsPipelineState& state) const { + const DxvkGraphicsPipelineStateInfo& state) const { } diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 7cf001f21..d00249676 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -9,13 +9,13 @@ namespace dxvk { - struct DxvkGraphicsPipelineState { + struct DxvkGraphicsPipelineStateInfo { VkRenderPass renderPass; size_t hash() const; - bool operator == (const DxvkGraphicsPipelineState& other) const; - bool operator != (const DxvkGraphicsPipelineState& other) const; + bool operator == (const DxvkGraphicsPipelineStateInfo& other) const; + bool operator != (const DxvkGraphicsPipelineStateInfo& other) const; }; /** @@ -43,7 +43,7 @@ namespace dxvk { } VkPipeline getPipelineHandle( - const DxvkGraphicsPipelineState& state); + const DxvkGraphicsPipelineStateInfo& state); private: @@ -60,11 +60,11 @@ namespace dxvk { std::mutex m_mutex; std::unordered_map< - DxvkGraphicsPipelineState, + DxvkGraphicsPipelineStateInfo, VkPipeline, DxvkHash> m_pipelines; VkPipeline compilePipeline( - const DxvkGraphicsPipelineState& state) const; + const DxvkGraphicsPipelineStateInfo& state) const; }; diff --git a/src/dxvk/dxvk_memory.h b/src/dxvk/dxvk_memory.h index c44f4ea3f..d7ffb72a4 100644 --- a/src/dxvk/dxvk_memory.h +++ b/src/dxvk/dxvk_memory.h @@ -68,7 +68,7 @@ namespace dxvk { * Allocates device memory for Vulkan resources. * Memory objects will be destroyed automatically. */ - class DxvkMemoryAllocator { + class DxvkMemoryAllocator : public RcObject { friend class DxvkMemory; public: diff --git a/src/dxvk/dxvk_pipemgr.cpp b/src/dxvk/dxvk_pipemgr.cpp index 1b7df0d50..6f6410dc3 100644 --- a/src/dxvk/dxvk_pipemgr.cpp +++ b/src/dxvk/dxvk_pipemgr.cpp @@ -14,8 +14,9 @@ namespace dxvk { } - Rc DxvkPipelineManager::getComputePipeline( - const Rc& cs) { + Rc DxvkPipelineManager::getComputePipeline(const Rc& cs) { + if (cs == nullptr) + return nullptr; DxvkPipelineKey<1> key; key.setShader(0, cs); @@ -38,6 +39,8 @@ namespace dxvk { const Rc& tes, const Rc& gs, const Rc& fs) { + if (vs == nullptr) + return nullptr; DxvkPipelineKey<5> key; key.setShader(0, vs); diff --git a/src/dxvk/dxvk_pipemgr.h b/src/dxvk/dxvk_pipemgr.h index c625bba80..f4f973055 100644 --- a/src/dxvk/dxvk_pipemgr.h +++ b/src/dxvk/dxvk_pipemgr.h @@ -73,6 +73,7 @@ namespace dxvk { * shader. If no such pipeline object exists, a new * one will be created. * \param [in] cs Compute shader + * \returns Compute pipeline */ Rc getComputePipeline( const Rc& cs); diff --git a/src/dxvk/dxvk_recorder.h b/src/dxvk/dxvk_recorder.h index b92723e5d..73cd272cc 100644 --- a/src/dxvk/dxvk_recorder.h +++ b/src/dxvk/dxvk_recorder.h @@ -61,6 +61,17 @@ namespace dxvk { virtual void cmdEndRenderPass() = 0; + virtual void cmdPipelineBarrier( + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers) = 0; + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_renderpass.h b/src/dxvk/dxvk_renderpass.h index e5be00464..4ced82b86 100644 --- a/src/dxvk/dxvk_renderpass.h +++ b/src/dxvk/dxvk_renderpass.h @@ -140,7 +140,7 @@ namespace dxvk { * Thread-safe class that manages the render pass * objects that are used within an application. */ - class DxvkRenderPassPool { + class DxvkRenderPassPool : public RcObject { public: diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index cebea04b8..63ad49961 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -8,6 +8,19 @@ namespace dxvk { + /** + * \brief Resource access mode + * + * Defines whether a resource will be + * used for reading, writing, or both. + */ + enum class DxvkResourceModeBit : uint32_t { + Read = 0, + Write = 1, + }; + + using DxvkResourceMode = Flags; + /** * \brief Shader resource type * @@ -27,6 +40,7 @@ namespace dxvk { * \brief Resource slot */ struct DxvkResourceSlot{ + DxvkResourceMode mode; DxvkResourceType type; uint32_t slot; }; diff --git a/src/dxvk/dxvk_util.cpp b/src/dxvk/dxvk_util.cpp new file mode 100644 index 000000000..88f4b22d0 --- /dev/null +++ b/src/dxvk/dxvk_util.cpp @@ -0,0 +1,23 @@ +#include "dxvk_util.h" + +namespace dxvk::util { + + VkPipelineStageFlags pipelineStages( + VkShaderStageFlags shaderStages) { + VkPipelineStageFlags result = 0; + if (shaderStages & VK_SHADER_STAGE_COMPUTE_BIT) + result |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (shaderStages & VK_SHADER_STAGE_VERTEX_BIT) + result |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; + if (shaderStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) + result |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT; + if (shaderStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) + result |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; + if (shaderStages & VK_SHADER_STAGE_GEOMETRY_BIT) + result |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + if (shaderStages & VK_SHADER_STAGE_FRAGMENT_BIT) + result |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + return result; + } + +} \ No newline at end of file diff --git a/src/dxvk/dxvk_util.h b/src/dxvk/dxvk_util.h new file mode 100644 index 000000000..50195d7f3 --- /dev/null +++ b/src/dxvk/dxvk_util.h @@ -0,0 +1,16 @@ +#pragma once + +#include "dxvk_include.h" + +namespace dxvk::util { + + /** + * \brief Gets pipeline stage flags for shader stages + * + * \param [in] shaderStages Shader stage flags + * \returns Corresponding pipeline stage flags + */ + VkPipelineStageFlags pipelineStages( + VkShaderStageFlags shaderStages); + +} \ No newline at end of file diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index f0a49921e..1ede34bb2 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -1,5 +1,6 @@ dxvk_src = files([ 'dxvk_adapter.cpp', + 'dxvk_barrier.cpp', 'dxvk_buffer.cpp', 'dxvk_cmdlist.cpp', 'dxvk_compute.cpp', @@ -21,6 +22,7 @@ dxvk_src = files([ 'dxvk_surface.cpp', 'dxvk_swapchain.cpp', 'dxvk_sync.cpp', + 'dxvk_util.cpp', 'spirv/dxvk_spirv_code_buffer.cpp', diff --git a/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp index 9aeae2b81..11c6e194b 100644 --- a/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp @@ -9,11 +9,20 @@ namespace dxvk { DxvkSpirvCodeBuffer::~DxvkSpirvCodeBuffer() { } - DxvkSpirvCodeBuffer::DxvkSpirvCodeBuffer( - std::basic_istream& stream) - : m_code( - std::istreambuf_iterator(stream), - std::istreambuf_iterator()) { } + DxvkSpirvCodeBuffer::DxvkSpirvCodeBuffer(std::istream&& stream) { + stream.ignore(std::numeric_limits::max()); + std::streamsize length = stream.gcount(); + stream.clear(); + stream.seekg(0, std::ios_base::beg); + + std::vector buffer(length); + stream.read(buffer.data(), length); + buffer.resize(stream.gcount()); + + m_code.resize(buffer.size() / sizeof(uint32_t)); + std::memcpy(reinterpret_cast(m_code.data()), + buffer.data(), m_code.size() * sizeof(uint32_t)); + } void DxvkSpirvCodeBuffer::append(const DxvkSpirvCodeBuffer& other) { @@ -100,8 +109,10 @@ namespace dxvk { } - void DxvkSpirvCodeBuffer::store(std::basic_ostream& stream) const { - stream.write(m_code.data(), m_code.size()); + void DxvkSpirvCodeBuffer::store(std::ostream&& stream) const { + stream.write( + reinterpret_cast(m_code.data()), + sizeof(uint32_t) * m_code.size()); } } \ No newline at end of file diff --git a/src/dxvk/spirv/dxvk_spirv_code_buffer.h b/src/dxvk/spirv/dxvk_spirv_code_buffer.h index e9907a237..a6e0d4b53 100644 --- a/src/dxvk/spirv/dxvk_spirv_code_buffer.h +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.h @@ -21,8 +21,7 @@ namespace dxvk { public: DxvkSpirvCodeBuffer(); - DxvkSpirvCodeBuffer( - std::basic_istream& stream); + DxvkSpirvCodeBuffer(std::istream&& stream); ~DxvkSpirvCodeBuffer(); /** @@ -38,7 +37,7 @@ namespace dxvk { * \returns Code size, in bytes */ size_t size() const { - return m_code.size(); + return m_code.size() * sizeof(uint32_t); } /** @@ -120,7 +119,7 @@ namespace dxvk { * exists mostly for debugging purposes. * \param [in] stream Output stream */ - void store(std::basic_ostream& stream) const; + void store(std::ostream&& stream) const; private: diff --git a/tests/dxvk/test_dxvk_triangle.cpp b/tests/dxvk/test_dxvk_triangle.cpp index 1c8f84037..9e94134bf 100644 --- a/tests/dxvk/test_dxvk_triangle.cpp +++ b/tests/dxvk/test_dxvk_triangle.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + #include #include @@ -26,6 +29,37 @@ public: m_dxvkContext (m_dxvkDevice->createContext()), m_dxvkCommandList (m_dxvkDevice->createCommandList()) { + DxvkBufferCreateInfo bufferInfo; + bufferInfo.size = sizeof(m_testData); + bufferInfo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + bufferInfo.stages = VK_PIPELINE_STAGE_HOST_BIT + | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + bufferInfo.access = VK_ACCESS_HOST_WRITE_BIT + | VK_ACCESS_HOST_READ_BIT + | VK_ACCESS_SHADER_WRITE_BIT + | VK_ACCESS_SHADER_READ_BIT; + + m_testBuffer = m_dxvkDevice->createBuffer(bufferInfo, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + for (size_t i = 0; i < 64; i++) + m_testData[i] = static_cast(i); + std::memcpy(m_testBuffer->mapPtr(), + m_testData, sizeof(m_testData)); + + DxvkResourceSlot computeBufferSlot; + computeBufferSlot.mode.set( + DxvkResourceModeBit::Read, + DxvkResourceModeBit::Write); + computeBufferSlot.type = DxvkResourceType::StorageBuffer; + computeBufferSlot.slot = 0; + + DxvkSpirvCodeBuffer code(std::ifstream("comp.spv", std::ios::binary)); + code.store(std::ofstream("comp.2.spv", std::ios::binary)); + + m_compShader = m_dxvkDevice->createShader( + VK_SHADER_STAGE_COMPUTE_BIT, std::move(code), + 1, &computeBufferSlot); } ~TriangleApp() { @@ -40,7 +74,7 @@ public: auto fbSize = fb->size(); m_dxvkContext->beginRecording(m_dxvkCommandList); - m_dxvkContext->setFramebuffer(fb); + m_dxvkContext->bindFramebuffer(fb); VkClearAttachment clearAttachment; clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -58,6 +92,13 @@ public: m_dxvkContext->clearRenderTarget( clearAttachment, clearArea); + m_dxvkContext->bindShader( + VK_SHADER_STAGE_COMPUTE_BIT, + m_compShader); + m_dxvkContext->bindStorageBuffer( + VK_SHADER_STAGE_COMPUTE_BIT, 0, + m_testBuffer, 0, sizeof(m_testData)); + m_dxvkContext->dispatch(1, 1, 1); m_dxvkContext->endRecording(); auto fence = m_dxvkDevice->submitCommandList( @@ -76,9 +117,13 @@ private: Rc m_dxvkContext; Rc m_dxvkCommandList; + Rc m_testBuffer; + Rc m_compShader; Rc m_vertShader; Rc m_fragShader; + int m_testData[64]; + }; LRESULT CALLBACK WindowProc(HWND hWnd,