From 94af8140d22505c0dc43a14dc7039da682ae503f Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 11 Oct 2017 23:29:05 +0200 Subject: [PATCH] [dxvk] DxvkContext now supports clears and actual state tracking --- src/dxvk/dxvk_context.cpp | 134 +++++++++++++++++----- src/dxvk/dxvk_context.h | 73 +++++++++++- src/dxvk/dxvk_context_state.cpp | 0 src/dxvk/dxvk_context_state.h | 14 ++- src/dxvk/dxvk_device.h | 10 ++ src/dxvk/dxvk_include.h | 1 + src/dxvk/meson.build | 4 +- src/dxvk/spirv/dxvk_spirv_code_buffer.cpp | 107 +++++++++++++++++ src/dxvk/spirv/dxvk_spirv_code_buffer.h | 131 +++++++++++++++++++++ tests/dxvk/test_dxvk_triangle.cpp | 26 ++++- 10 files changed, 463 insertions(+), 37 deletions(-) delete mode 100644 src/dxvk/dxvk_context_state.cpp create mode 100644 src/dxvk/spirv/dxvk_spirv_code_buffer.cpp create mode 100644 src/dxvk/spirv/dxvk_spirv_code_buffer.h diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 03c1f835..3fc340bf 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -24,56 +24,128 @@ namespace dxvk { bool DxvkContext::endRecording() { TRACE(this); + + if (m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) + this->endRenderPass(); + + // Finalize the command list m_commandList->endRecording(); m_commandList = nullptr; return true; } + + + void DxvkContext::clearRenderTarget( + const VkClearAttachment& attachment, + const VkClearRect& clearArea) { + if (!m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) + this->beginRenderPass(); + m_vkd->vkCmdClearAttachments( + m_commandList->handle(), + 1, &attachment, + 1, &clearArea); + } + + + void DxvkContext::draw( + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance) { + this->prepareDraw(); + m_vkd->vkCmdDraw( + m_commandList->handle(), + vertexCount, + instanceCount, + firstVertex, + firstInstance); + } + + + void DxvkContext::drawIndexed( + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + uint32_t vertexOffset, + uint32_t firstInstance) { + this->prepareDraw(); + m_vkd->vkCmdDrawIndexed( + m_commandList->handle(), + indexCount, + instanceCount, + firstIndex, + vertexOffset, + firstInstance); + } + void DxvkContext::setFramebuffer( const Rc& fb) { TRACE(this, fb); - const DxvkFramebufferSize fbSize = fb->size(); - // TODO implement properly - VkRect2D renderArea; - renderArea.offset.x = 0; - renderArea.offset.y = 0; - renderArea.extent.width = fbSize.width; - renderArea.extent.height = fbSize.height; + // 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(); + + m_state.fb.framebuffer = fb; + m_commandList->trackResource(fb); + } + } + + + void DxvkContext::setShader( + VkShaderStageFlagBits stage, + const Rc& shader) { + TRACE(this, stage, shader); + + } + + + void DxvkContext::flushGraphicsState() { + + } + + + void DxvkContext::prepareDraw() { + this->flushGraphicsState(); + + if (!m_state.fb.flags.test(DxvkFbStateFlags::InsideRenderPass)) + this->beginRenderPass(); + } + + + void DxvkContext::beginRenderPass() { + TRACE(this); + + const DxvkFramebufferSize fbsize + = m_state.fb.framebuffer->size(); + VkRenderPassBeginInfo info; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.pNext = nullptr; - info.renderPass = fb->renderPass(); - info.framebuffer = fb->handle(); - info.renderArea = renderArea; + info.renderPass = m_state.fb.framebuffer->renderPass(); + info.framebuffer = m_state.fb.framebuffer->handle(); + info.renderArea = VkRect2D { { 0, 0 }, { fbsize.width, fbsize.height } }; info.clearValueCount = 0; info.pClearValues = nullptr; - // This is for testing purposes only. - VkClearAttachment attachment; - attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - attachment.colorAttachment = 0; - attachment.clearValue.color.float32[0] = 1.0f; - attachment.clearValue.color.float32[1] = 1.0f; - attachment.clearValue.color.float32[2] = 1.0f; - attachment.clearValue.color.float32[3] = 1.0f; - - VkClearRect clearRect; - clearRect.rect = renderArea; - clearRect.baseArrayLayer = 0; - clearRect.layerCount = fbSize.layers; - m_vkd->vkCmdBeginRenderPass( - m_commandList->handle(), &info, - VK_SUBPASS_CONTENTS_INLINE); - m_vkd->vkCmdClearAttachments( m_commandList->handle(), - 1, &attachment, - 1, &clearRect); - m_vkd->vkCmdEndRenderPass( - m_commandList->handle()); + &info, VK_SUBPASS_CONTENTS_INLINE); + m_state.fb.flags.set(DxvkFbStateFlags::InsideRenderPass); + } + + + void DxvkContext::endRenderPass() { + TRACE(this); + + m_vkd->vkCmdEndRenderPass(m_commandList->handle()); + m_state.fb.flags.clr(DxvkFbStateFlags::InsideRenderPass); } } \ No newline at end of file diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 33cc2d7e..828e6d97 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -1,7 +1,9 @@ #pragma once +#define DXVK_ERROR_CHECKING 1 + #include "dxvk_cmdlist.h" -#include "dxvk_framebuffer.h" +#include "dxvk_context_state.h" namespace dxvk { @@ -49,6 +51,46 @@ namespace dxvk { */ bool endRecording(); + /** + * \brief Clears an active render target + * + * \param [in] attachment Attachment to clear + * \param [in] clearArea Rectangular area to clear + */ + void clearRenderTarget( + const VkClearAttachment& attachment, + const VkClearRect& clearArea); + + /** + * \brief Draws primitive without using an index buffer + * + * \param [in] vertexCount Number of vertices to draw + * \param [in] instanceCount Number of instances to render + * \param [in] firstVertex First vertex in vertex buffer + * \param [in] firstInstance First instance ID + */ + void draw( + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + + /** + * \brief Draws primitives using an index buffer + * + * \param [in] indexCount Number of indices to draw + * \param [in] instanceCount Number of instances to render + * \param [in] firstIndex First index within the index buffer + * \param [in] vertexOffset Vertex ID that corresponds to index 0 + * \param [in] firstInstance First instance ID + */ + void drawIndexed( + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + uint32_t vertexOffset, + uint32_t firstInstance); + /** * \brief Sets framebuffer * \param [in] fb Framebuffer @@ -56,10 +98,39 @@ namespace dxvk { 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: Rc m_vkd; 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 flushGraphicsState(); + + void prepareDraw(); + + void beginRenderPass(); + void endRenderPass(); }; diff --git a/src/dxvk/dxvk_context_state.cpp b/src/dxvk/dxvk_context_state.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 02c506e2..9a4b208d 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -1,9 +1,21 @@ #pragma once -#include "dxvk_cmdlist.h" +#include "dxvk_framebuffer.h" +#include "dxvk_shader.h" namespace dxvk { + enum class DxvkFbStateFlags : uint32_t { + InsideRenderPass = 0, + }; + struct DxvkFramebufferState { + Rc framebuffer; + Flags flags; + }; + + struct DxvkContextState { + DxvkFramebufferState fb; ///< Framebuffer and render pass + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 3c21f193..d9391d7b 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -6,6 +6,7 @@ #include "dxvk_framebuffer.h" #include "dxvk_memory.h" #include "dxvk_renderpass.h" +#include "dxvk_shader.h" #include "dxvk_swapchain.h" #include "dxvk_sync.h" @@ -111,6 +112,15 @@ namespace dxvk { */ Rc createSemaphore(); + /** + * \brief Creates a shader module + * + * \param [in] code SPIR-V code + * \returns Shader module + */ + Rc createShader( + const SpirvCodeBuffer& code); + /** * \brief Creates a swap chain * diff --git a/src/dxvk/dxvk_include.h b/src/dxvk/dxvk_include.h index a5a9e2f2..90ca4ee1 100644 --- a/src/dxvk/dxvk_include.h +++ b/src/dxvk/dxvk_include.h @@ -4,6 +4,7 @@ #include "../util/log/log_debug.h" #include "../util/util_error.h" +#include "../util/util_flags.h" #include "../util/util_string.h" #include "../util/rc/util_rc.h" diff --git a/src/dxvk/meson.build b/src/dxvk/meson.build index 4e619bd8..5a1215b4 100644 --- a/src/dxvk/meson.build +++ b/src/dxvk/meson.build @@ -2,7 +2,6 @@ dxvk_src = files([ 'dxvk_adapter.cpp', 'dxvk_cmdlist.cpp', 'dxvk_context.cpp', - 'dxvk_context_state.cpp', 'dxvk_device.cpp', 'dxvk_framebuffer.cpp', 'dxvk_image.cpp', @@ -12,10 +11,13 @@ dxvk_src = files([ 'dxvk_memory.cpp', 'dxvk_renderpass.cpp', 'dxvk_resource.cpp', + 'dxvk_shader.cpp', 'dxvk_surface.cpp', 'dxvk_swapchain.cpp', 'dxvk_sync.cpp', + 'spirv/dxvk_spirv_code_buffer.cpp', + 'vulkan/dxvk_vulkan_extensions.cpp', 'vulkan/dxvk_vulkan_loader.cpp', 'vulkan/dxvk_vulkan_names.cpp', diff --git a/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp new file mode 100644 index 00000000..b898c954 --- /dev/null +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.cpp @@ -0,0 +1,107 @@ +#include +#include + +#include "dxvk_spirv_code_buffer.h" + +namespace dxvk { + + SpirvCodeBuffer:: SpirvCodeBuffer() { } + SpirvCodeBuffer::~SpirvCodeBuffer() { } + + + SpirvCodeBuffer::SpirvCodeBuffer( + std::basic_istream& stream) + : m_code( + std::istreambuf_iterator(stream), + std::istreambuf_iterator()) { } + + + void SpirvCodeBuffer::append(const SpirvCodeBuffer& other) { + const size_t size = m_code.size(); + m_code.resize(size + other.m_code.size()); + + uint32_t* dst = this->m_code.data(); + const uint32_t* src = other.m_code.data(); + + std::memcpy(dst + size, src, sizeof(uint32_t) * size); + } + + + void SpirvCodeBuffer::putWord(uint32_t word) { + m_code.push_back(word); + } + + + void SpirvCodeBuffer::putIns(spv::Op opCode, uint16_t wordCount) { + this->putWord( + (static_cast(opCode) << 0) + | (static_cast(wordCount) << 16)); + } + + + void SpirvCodeBuffer::putInt32(uint32_t word) { + this->putWord(word); + } + + + void SpirvCodeBuffer::putInt64(uint64_t value) { + this->putWord(value >> 0); + this->putWord(value >> 32); + } + + + void SpirvCodeBuffer::putFloat32(float value) { + uint32_t tmp; + static_assert(sizeof(tmp) == sizeof(value)); + std::memcpy(&tmp, &value, sizeof(value)); + this->putInt32(tmp); + } + + + void SpirvCodeBuffer::putFloat64(double value) { + uint64_t tmp; + static_assert(sizeof(tmp) == sizeof(value)); + std::memcpy(&tmp, &value, sizeof(value)); + this->putInt64(tmp); + } + + + void SpirvCodeBuffer::putStr(const char* str) { + uint32_t word = 0; + uint32_t nbit = 0; + + for (uint32_t i = 0; str[i] != '\0'; str++) { + word |= (static_cast(str[i]) & 0xFF) << nbit; + + if ((nbit += 8) == 32) { + this->putWord(word); + word = 0; + nbit = 0; + } + } + + // Commit current word + this->putWord(word); + } + + + void SpirvCodeBuffer::putHeader(uint32_t boundIds) { + this->putWord(spv::MagicNumber); + this->putWord(spv::Version); + this->putWord(0); // Generator + this->putWord(boundIds); + this->putWord(0); // Schema + } + + + uint32_t SpirvCodeBuffer::strLen(const char* str) { + // Null-termination plus padding + return (std::strlen(str) + 4) / 4; + } + + + void SpirvCodeBuffer::store(std::basic_ostream& stream) const { + stream.write(m_code.data(), 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 new file mode 100644 index 00000000..7b9236b6 --- /dev/null +++ b/src/dxvk/spirv/dxvk_spirv_code_buffer.h @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include +#include + +#include "../dxvk_include.h" + +namespace dxvk { + + /** + * \brief SPIR-V code buffer + * + * Helper class for generating SPIR-V shaders. + * Stores arbitrary SPIR-V instructions in a + * format that can be read by Vulkan drivers. + */ + class SpirvCodeBuffer { + + public: + + SpirvCodeBuffer(); + SpirvCodeBuffer( + std::basic_istream& stream); + ~SpirvCodeBuffer(); + + /** + * \brief Code + * \returns Code + */ + const uint32_t* code() const { + return m_code.data(); + } + + /** + * \brief Code size, in bytes + * \returns Code size, in bytes + */ + size_t size() const { + return m_code.size(); + } + + /** + * \brief Merges two code buffers + * + * This is useful to generate declarations or + * the SPIR-V header at the same time as the + * code when doing so in advance is impossible. + * \param [in] other Code buffer to append + */ + void append(const SpirvCodeBuffer& other); + + /** + * \brief Appends an 32-bit word to the buffer + * \param [in] word The word to append + */ + void putWord(uint32_t word); + + /** + * \brief Appends an instruction word to the buffer + * + * Adds a single word containing both the word count + * and the op code number for a single instruction. + * \param [in] opCode Operand code + * \param [in] wordCount Number of words + */ + void putIns(spv::Op opCode, uint16_t wordCount); + + /** + * \brief Appends a 32-bit integer to the buffer + * \param [in] value The number to add + */ + void putInt32(uint32_t word); + + /** + * \brief Appends a 64-bit integer to the buffer + * + * A 64-bit integer will take up two 32-bit words. + * \param [in] value 64-bit value to add + */ + void putInt64(uint64_t value); + + /** + * \brief Appends a 32-bit float to the buffer + * \param [in] value The number to add + */ + void putFloat32(float value); + + /** + * \brief Appends a 64-bit float to the buffer + * \param [in] value The number to add + */ + void putFloat64(double value); + + /** + * \brief Appends a literal string to the buffer + * \param [in] str String to append to the buffer + */ + void putStr(const char* str); + + /** + * \brief Adds the header to the buffer + * \param [in] boundIds Number of bound IDs + */ + void putHeader(uint32_t boundIds); + + /** + * \brief Computes length of a literal string + * + * \param [in] str The string to check + * \returns Number of words consumed by a string + */ + uint32_t strLen(const char* str); + + /** + * \brief Stores the SPIR-V module to a stream + * + * The ability to save modules to a file + * exists mostly for debugging purposes. + * \param [in] stream Output stream + */ + void store(std::basic_ostream& stream) const; + + private: + + std::vector m_code; + + }; + +} \ No newline at end of file diff --git a/tests/dxvk/test_dxvk_triangle.cpp b/tests/dxvk/test_dxvk_triangle.cpp index 069c0d55..1c8f8403 100644 --- a/tests/dxvk/test_dxvk_triangle.cpp +++ b/tests/dxvk/test_dxvk_triangle.cpp @@ -36,9 +36,28 @@ public: auto sync1 = m_dxvkDevice->createSemaphore(); auto sync2 = m_dxvkDevice->createSemaphore(); + auto fb = m_dxvkSwapchain->getFramebuffer(sync1); + auto fbSize = fb->size(); + m_dxvkContext->beginRecording(m_dxvkCommandList); - m_dxvkContext->setFramebuffer( - m_dxvkSwapchain->getFramebuffer(sync1)); + m_dxvkContext->setFramebuffer(fb); + + VkClearAttachment clearAttachment; + clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + clearAttachment.colorAttachment = 0; + clearAttachment.clearValue.color.float32[0] = 1.0f; + clearAttachment.clearValue.color.float32[1] = 1.0f; + clearAttachment.clearValue.color.float32[2] = 1.0f; + clearAttachment.clearValue.color.float32[3] = 1.0f; + + VkClearRect clearArea; + clearArea.rect = VkRect2D { { 0, 0 }, fbSize.width, fbSize.height }; + clearArea.baseArrayLayer = 0; + clearArea.layerCount = fbSize.layers; + + m_dxvkContext->clearRenderTarget( + clearAttachment, + clearArea); m_dxvkContext->endRecording(); auto fence = m_dxvkDevice->submitCommandList( @@ -57,7 +76,8 @@ private: Rc m_dxvkContext; Rc m_dxvkCommandList; - Rc m_vertexBuffer; + Rc m_vertShader; + Rc m_fragShader; };