[dxvk] DxvkContext now supports clears and actual state tracking

This commit is contained in:
Philip Rebohle 2017-10-11 23:29:05 +02:00
parent 20048db69d
commit 94af8140d2
10 changed files with 463 additions and 37 deletions

View File

@ -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<DxvkFramebuffer>& 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<DxvkShader>& 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);
}
}

View File

@ -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<DxvkFramebuffer>& 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<DxvkShader>& shader);
private:
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkCommandList> 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();
};

View File

@ -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<DxvkFramebuffer> framebuffer;
Flags<DxvkFbStateFlags> flags;
};
struct DxvkContextState {
DxvkFramebufferState fb; ///< Framebuffer and render pass
};
}

View File

@ -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<DxvkSemaphore> createSemaphore();
/**
* \brief Creates a shader module
*
* \param [in] code SPIR-V code
* \returns Shader module
*/
Rc<DxvkShader> createShader(
const SpirvCodeBuffer& code);
/**
* \brief Creates a swap chain
*

View File

@ -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"

View File

@ -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',

View File

@ -0,0 +1,107 @@
#include <array>
#include <cstring>
#include "dxvk_spirv_code_buffer.h"
namespace dxvk {
SpirvCodeBuffer:: SpirvCodeBuffer() { }
SpirvCodeBuffer::~SpirvCodeBuffer() { }
SpirvCodeBuffer::SpirvCodeBuffer(
std::basic_istream<uint32_t>& stream)
: m_code(
std::istreambuf_iterator<uint32_t>(stream),
std::istreambuf_iterator<uint32_t>()) { }
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<uint32_t>(opCode) << 0)
| (static_cast<uint32_t>(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<uint32_t>(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<uint32_t>& stream) const {
stream.write(m_code.data(), m_code.size());
}
}

View File

@ -0,0 +1,131 @@
#pragma once
#include <spirv/spirv.hpp>
#include <iostream>
#include <vector>
#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<uint32_t>& 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<uint32_t>& stream) const;
private:
std::vector<uint32_t> m_code;
};
}

View File

@ -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<DxvkContext> m_dxvkContext;
Rc<DxvkCommandList> m_dxvkCommandList;
Rc<DxvkBuffer> m_vertexBuffer;
Rc<DxvkShader> m_vertShader;
Rc<DxvkShader> m_fragShader;
};