[dxvk] Implementing unbound resource handling (1/4)

In order to emulate D3D11 behaviour when a resource is not bound,
we use specialization constants. This requires further changes in
the shader compiler.
This commit is contained in:
Philip Rebohle 2018-01-10 11:44:40 +01:00
parent 38ee8f4f95
commit c64103c73f
9 changed files with 161 additions and 17 deletions

View File

@ -3,12 +3,93 @@
#include "dxvk_buffer.h"
#include "dxvk_descriptor.h"
#include "dxvk_image.h"
#include "dxvk_limits.h"
#include "dxvk_sampler.h"
namespace dxvk {
/**
* \brief Bound shader resource
* \brief Binding state
*
* Used to track which resource slots have a compatible
* binding and which ones don't. This is used to set up
* binding-related specialization constants in shaders.
* \tparam N Number of binding slots
*/
class DxvkBindingState {
constexpr static uint32_t BitCount = 32;
constexpr static uint32_t IntCount = (MaxNumActiveBindings + BitCount - 1) / BitCount;
public:
/**
* \brief Tests whether a binding is active
*
* \param [in] slot The binding ID
* \returns \c true if the binding is active
*/
bool isBound(uint32_t slot) const {
const uint32_t intId = slot / BitCount;
const uint32_t bitId = slot % BitCount;
const uint32_t bitMask = 1u << bitId;
return (m_slots[intId] & bitMask) != 0;
}
/**
* \brief Marks a binding as active
*
* \param [in] slot The binding ID
* \returns \c true if the state has changed
*/
bool setBound(uint32_t slot) {
const uint32_t intId = slot / BitCount;
const uint32_t bitId = slot % BitCount;
const uint32_t bitMask = 1u << bitId;
const uint32_t prev = m_slots[intId];
m_slots[intId] = prev | bitMask;
return (prev & bitMask) == 0;
}
/**
* \brief Marks a binding as inactive
*
* \param [in] slot The binding ID
* \returns \c true if the state has changed
*/
bool setUnbound(uint32_t slot) {
const uint32_t intId = slot / BitCount;
const uint32_t bitId = slot % BitCount;
const uint32_t bitMask = 1u << bitId;
const uint32_t prev = m_slots[intId];
m_slots[intId] = prev & ~bitMask;
return (prev & bitMask) != 0;
}
/**
* \brief Clears binding state
*
* Useful to zero out any bindings
* that are not used by a pipeline.
*/
void clear() {
for (uint32_t i = 0; i < IntCount; i++)
m_slots[i] = 0;
}
private:
uint32_t m_slots[IntCount];
};
/**
* \brief Bound shader resources
*
* Stores the resources bound to a binding
* slot in DXVK. These are used to create
* descriptor sets.
*/
struct DxvkShaderResourceSlot {
Rc<DxvkSampler> sampler;

View File

@ -32,7 +32,7 @@ namespace dxvk {
info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.stage = m_cs->stageInfo();
info.stage = m_cs->stageInfo(nullptr);
info.layout = m_layout->pipelineLayout();
info.basePipelineHandle = VK_NULL_HANDLE;
info.basePipelineIndex = 0;

View File

@ -926,12 +926,14 @@ namespace dxvk {
if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {
m_flags.clr(DxvkContextFlag::GpDirtyPipeline);
m_state.gp.bs.clear();
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.bsBindingState = m_state.gp.bs;
gpState.iaPrimitiveTopology = m_state.ia.primitiveTopology;
gpState.iaPrimitiveRestart = m_state.ia.primitiveRestart;
@ -1033,6 +1035,13 @@ namespace dxvk {
void DxvkContext::updateShaderResources(
VkPipelineBindPoint bindPoint,
const Rc<DxvkBindingLayout>& layout) {
DxvkBindingState& bs =
bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
? m_state.gp.bs
: m_state.cp.bs;
bool updatePipelineState = false;
// TODO recreate resource views if the underlying
// resource was marked as dirty after invalidation
for (uint32_t i = 0; i < layout->bindingCount(); i++) {
@ -1042,6 +1051,8 @@ namespace dxvk {
switch (binding.type) {
case VK_DESCRIPTOR_TYPE_SAMPLER:
if (res.sampler != nullptr) {
updatePipelineState |= bs.setBound(i);
m_descriptors[i].image.sampler = res.sampler->handle();
m_descriptors[i].image.imageView = VK_NULL_HANDLE;
m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@ -1049,6 +1060,8 @@ namespace dxvk {
m_cmd->trackResource(res.sampler);
} else {
Logger::err("DxvkContext: Unbound sampler descriptor");
updatePipelineState |= bs.setUnbound(i);
m_descriptors[i].image.sampler = VK_NULL_HANDLE;
m_descriptors[i].image.imageView = VK_NULL_HANDLE;
m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@ -1056,7 +1069,9 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
if (res.imageView != nullptr && res.imageView->type() != binding.view) {
if (res.imageView != nullptr/* && res.imageView->type() != binding.view*/) {
updatePipelineState |= bs.setBound(i);
m_descriptors[i].image.sampler = VK_NULL_HANDLE;
m_descriptors[i].image.imageView = res.imageView->handle();
m_descriptors[i].image.imageLayout = res.imageView->imageInfo().layout;
@ -1065,6 +1080,8 @@ namespace dxvk {
m_cmd->trackResource(res.imageView->image());
} else {
Logger::err("DxvkContext: Unbound or incompatible image descriptor");
updatePipelineState |= bs.setUnbound(i);
m_descriptors[i].image.sampler = VK_NULL_HANDLE;
m_descriptors[i].image.imageView = VK_NULL_HANDLE;
m_descriptors[i].image.imageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
@ -1073,22 +1090,30 @@ namespace dxvk {
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
if (res.bufferView != nullptr) {
updatePipelineState |= bs.setBound(i);
m_descriptors[i].texelBuffer = res.bufferView->handle();
m_cmd->trackResource(res.bufferView);
m_cmd->trackResource(res.bufferView->buffer()->resource());
} else {
Logger::err("DxvkContext: Unbound texel buffer");
updatePipelineState |= bs.setUnbound(i);
m_descriptors[i].texelBuffer = VK_NULL_HANDLE;
} break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
if (res.bufferSlice.handle() != VK_NULL_HANDLE) {
updatePipelineState |= bs.setBound(i);
m_descriptors[i].buffer = res.bufferSlice.descriptorInfo();
m_cmd->trackResource(res.bufferSlice.resource());
} else {
Logger::err("DxvkContext: Unbound buffer");
updatePipelineState |= bs.setUnbound(i);
m_descriptors[i].buffer.buffer = VK_NULL_HANDLE;
m_descriptors[i].buffer.offset = 0;
m_descriptors[i].buffer.range = 0;
@ -1106,6 +1131,12 @@ namespace dxvk {
layout->bindingCount(),
layout->bindings(),
m_descriptors.data());
if (updatePipelineState) {
m_flags.set(bindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
? DxvkContextFlag::GpDirtyPipelineState
: DxvkContextFlag::CpDirtyPipelineState);
}
}
@ -1178,6 +1209,7 @@ namespace dxvk {
void DxvkContext::commitComputeState() {
// TODO handle CpDirtyPipelineState
this->renderPassEnd();
this->updateComputePipeline();
this->updateComputeShaderResources();

View File

@ -30,6 +30,7 @@ namespace dxvk {
GpDirtyIndexBuffer, ///< Index buffer binding are out of date
CpDirtyPipeline, ///< Compute pipeline binding are out of date
CpDirtyPipelineState, ///< Compute pipeline needs to be recompiled
CpDirtyResources, ///< Compute pipeline resource bindings are out of date
};
@ -70,17 +71,19 @@ namespace dxvk {
struct DxvkGraphicsPipelineState {
DxvkShaderStage vs;
DxvkShaderStage tcs;
DxvkShaderStage tes;
DxvkShaderStage gs;
DxvkShaderStage fs;
DxvkBindingState bs;
DxvkShaderStage vs;
DxvkShaderStage tcs;
DxvkShaderStage tes;
DxvkShaderStage gs;
DxvkShaderStage fs;
Rc<DxvkGraphicsPipeline> pipeline;
};
struct DxvkComputePipelineState {
DxvkBindingState bs;
DxvkShaderStage cs;
Rc<DxvkComputePipeline> pipeline;
};

View File

@ -91,13 +91,27 @@ namespace dxvk {
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
};
std::array<VkBool32, MaxNumActiveBindings> specData;
std::array<VkSpecializationMapEntry, MaxNumActiveBindings> specMap;
for (uint32_t i = 0; i < MaxNumActiveBindings; i++) {
specData[i] = state.bsBindingState.isBound(i) ? VK_TRUE : VK_FALSE;
specMap [i] = { i, static_cast<uint32_t>(sizeof(VkBool32)) * i, sizeof(VkBool32) };
}
VkSpecializationInfo specInfo;
specInfo.mapEntryCount = specMap.size();
specInfo.pMapEntries = specMap.data();
specInfo.dataSize = specData.size() * sizeof(VkBool32);
specInfo.pData = specData.data();
std::vector<VkPipelineShaderStageCreateInfo> stages;
if (m_vs != nullptr) stages.push_back(m_vs->stageInfo());
if (m_tcs != nullptr) stages.push_back(m_tcs->stageInfo());
if (m_tes != nullptr) stages.push_back(m_tes->stageInfo());
if (m_gs != nullptr) stages.push_back(m_gs->stageInfo());
if (m_fs != nullptr) stages.push_back(m_fs->stageInfo());
if (m_vs != nullptr) stages.push_back(m_vs->stageInfo(&specInfo));
if (m_tcs != nullptr) stages.push_back(m_tcs->stageInfo(&specInfo));
if (m_tes != nullptr) stages.push_back(m_tes->stageInfo(&specInfo));
if (m_gs != nullptr) stages.push_back(m_gs->stageInfo(&specInfo));
if (m_fs != nullptr) stages.push_back(m_fs->stageInfo(&specInfo));
VkPipelineVertexInputStateCreateInfo viInfo;
viInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;

View File

@ -3,6 +3,7 @@
#include <mutex>
#include <unordered_map>
#include "dxvk_binding.h"
#include "dxvk_constant_state.h"
#include "dxvk_hash.h"
#include "dxvk_pipelayout.h"
@ -27,6 +28,8 @@ namespace dxvk {
DxvkGraphicsPipelineStateInfo& operator = (
const DxvkGraphicsPipelineStateInfo& other);
DxvkBindingState bsBindingState;
VkPrimitiveTopology iaPrimitiveTopology;
VkBool32 iaPrimitiveRestart;

View File

@ -11,6 +11,7 @@ namespace dxvk {
MaxNumOutputStreams = 4,
MaxNumViewports = 16,
MaxNumResourceSlots = 1024,
MaxNumActiveBindings = 128,
};
}

View File

@ -26,16 +26,15 @@ namespace dxvk {
}
VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo() const {
VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo(const VkSpecializationInfo* specInfo) const {
VkPipelineShaderStageCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.stage = m_stage;
info.module = m_module;
info.pName = "main";
info.pSpecializationInfo = nullptr;
info.pSpecializationInfo = specInfo;
return info;
}

View File

@ -28,11 +28,22 @@ namespace dxvk {
~DxvkShaderModule();
/**
* \brief Shader module handle
* \returns Shader module handle
*/
VkShaderModule handle() const {
return m_module;
}
VkPipelineShaderStageCreateInfo stageInfo() const;
/**
* \brief Shader stage creation info
*
* \param [in] specInfo Specialization info
* \returns Shader stage create info
*/
VkPipelineShaderStageCreateInfo stageInfo(
const VkSpecializationInfo* specInfo) const;
private: