1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-18 02:52:10 +01:00

[dxvk] Implemented input layout validation

Checks whether all input slots consumed by the vertex shader
are provided by the input layout, and disables rendering in
case the state validation fails. This should hopefully fix
GPU lockups in Nier:Automata.
This commit is contained in:
Philip Rebohle 2018-01-12 14:25:26 +01:00
parent 64eb0f909d
commit 5dd9fea011
11 changed files with 116 additions and 21 deletions

View File

@ -183,6 +183,7 @@ namespace dxvk {
m_version.shaderStage(),
m_resourceSlots.size(),
m_resourceSlots.data(),
m_interfaceSlots,
m_module.compile());
}
@ -468,6 +469,9 @@ namespace dxvk {
if (im == DxbcInterpolationMode::LinearSample
|| im == DxbcInterpolationMode::LinearNoPerspectiveSample)
m_module.decorate(varId, spv::DecorationSample);
// Declare the input slot as defined
m_interfaceSlots.inputSlots |= 1u << regIdx;
} else if (sv != DxbcSystemValue::None) {
// Add a new system value mapping if needed
m_vMappings.push_back({ regIdx, regMask, sv });
@ -498,6 +502,9 @@ namespace dxvk {
m_entryPointInterfaces.push_back(varId);
m_oRegs.at(regIdx) = varId;
// Declare the output slot as defined
m_interfaceSlots.outputSlots |= 1u << regIdx;
}

View File

@ -311,6 +311,11 @@ namespace dxvk {
std::vector<uint32_t> m_entryPointInterfaces;
uint32_t m_entryPointId = 0;
////////////////////////////////////////////
// Inter-stage shader interface slots. Also
// covers vertex input and fragment output.
DxvkInterfaceSlots m_interfaceSlots;
///////////////////////////////////
// Shader-specific data structures
DxbcCompilerVsPart m_vs;

View File

@ -438,7 +438,8 @@ namespace dxvk {
// Create the actual shader module
return m_device->createShader(
VK_SHADER_STAGE_VERTEX_BIT,
0, nullptr, module.compile());
0, nullptr, { 0u, 1u },
module.compile());
}
@ -524,6 +525,7 @@ namespace dxvk {
VK_SHADER_STAGE_FRAGMENT_BIT,
resourceSlots.size(),
resourceSlots.data(),
{ 1u, 1u },
module.compile());
}

View File

@ -463,9 +463,11 @@ namespace dxvk {
uint32_t firstInstance) {
this->commitGraphicsState();
m_cmd->cmdDraw(
vertexCount, instanceCount,
firstVertex, firstInstance);
if (m_gpActivePipeline != VK_NULL_HANDLE) {
m_cmd->cmdDraw(
vertexCount, instanceCount,
firstVertex, firstInstance);
}
}
@ -475,10 +477,12 @@ namespace dxvk {
uint32_t stride) {
this->commitGraphicsState();
m_cmd->cmdDrawIndirect(
buffer.handle(),
buffer.offset(),
count, stride);
if (m_gpActivePipeline != VK_NULL_HANDLE) {
m_cmd->cmdDrawIndirect(
buffer.handle(),
buffer.offset(),
count, stride);
}
}
@ -490,10 +494,12 @@ namespace dxvk {
uint32_t firstInstance) {
this->commitGraphicsState();
m_cmd->cmdDrawIndexed(
indexCount, instanceCount,
firstIndex, vertexOffset,
firstInstance);
if (m_gpActivePipeline != VK_NULL_HANDLE) {
m_cmd->cmdDrawIndexed(
indexCount, instanceCount,
firstIndex, vertexOffset,
firstInstance);
}
}
@ -503,10 +509,12 @@ namespace dxvk {
uint32_t stride) {
this->commitGraphicsState();
m_cmd->cmdDrawIndexedIndirect(
buffer.handle(),
buffer.offset(),
count, stride);
if (m_gpActivePipeline != VK_NULL_HANDLE) {
m_cmd->cmdDrawIndexedIndirect(
buffer.handle(),
buffer.offset(),
count, stride);
}
}
@ -997,8 +1005,14 @@ namespace dxvk {
for (uint32_t i = m_state.gp.state.ilBindingCount; i < MaxNumVertexBindings; i++)
m_state.gp.state.ilBindings[i].stride = 0;
m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
m_state.gp.pipeline->getPipelineHandle(m_state.gp.state));
m_gpActivePipeline = m_state.gp.pipeline
->getPipelineHandle(m_state.gp.state);
if (m_gpActivePipeline != VK_NULL_HANDLE) {
m_cmd->cmdBindPipeline(
VK_PIPELINE_BIND_POINT_GRAPHICS,
m_gpActivePipeline);
}
}
}

View File

@ -485,6 +485,9 @@ namespace dxvk {
DxvkContextState m_state;
DxvkBarrierSet m_barriers;
VkPipeline m_gpActivePipeline = VK_NULL_HANDLE;
// VkPipeline m_cpActivePipeline = VK_NULL_HANDLE; /* will be used later */
std::array<DxvkShaderResourceSlot, MaxNumResourceSlots> m_rc;
std::array<DxvkDescriptorInfo, MaxNumResourceSlots> m_descriptors;

View File

@ -152,9 +152,10 @@ namespace dxvk {
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const DxvkInterfaceSlots& iface,
const SpirvCodeBuffer& code) {
return new DxvkShader(stage,
slotCount, slotInfos, code);
slotCount, slotInfos, iface, code);
}

View File

@ -201,6 +201,9 @@ namespace dxvk {
* \brief Creates a shader module
*
* \param [in] stage Shader stage
* \param [in] slotCount Resource slot count
* \param [in] slotInfos Resource slot descriptions
* \param [in] iface Inter-stage interface slots
* \param [in] code Shader code
* \returns New shader module
*/
@ -208,6 +211,7 @@ namespace dxvk {
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const DxvkInterfaceSlots& iface,
const SpirvCodeBuffer& code);
/**

View File

@ -56,6 +56,9 @@ namespace dxvk {
if (tes != nullptr) m_tes = tes->createShaderModule(vkd, slotMapping);
if (gs != nullptr) m_gs = gs ->createShaderModule(vkd, slotMapping);
if (fs != nullptr) m_fs = fs ->createShaderModule(vkd, slotMapping);
m_vsIn = vs != nullptr ? vs->interfaceSlots().inputSlots : 0;
m_fsOut = fs != nullptr ? fs->interfaceSlots().outputSlots : 0;
}
@ -73,7 +76,10 @@ namespace dxvk {
return pair.pipeline;
}
VkPipeline pipeline = this->compilePipeline(state, m_basePipeline);
VkPipeline pipeline = this->validatePipelineState(state)
? this->compilePipeline(state, m_basePipeline)
: VK_NULL_HANDLE;
m_pipelines.push_back({ state, pipeline });
if (m_basePipeline == VK_NULL_HANDLE)
@ -234,4 +240,23 @@ namespace dxvk {
m_vkd->vkDestroyPipeline(m_vkd->device(), pair.pipeline, nullptr);
}
bool DxvkGraphicsPipeline::validatePipelineState(
const DxvkGraphicsPipelineStateInfo& state) const {
// Validate vertex input - each input slot consumed by the
// vertex shader must be provided by the input layout.
uint32_t providedVertexInputs = 0;
for (uint32_t i = 0; i < state.ilAttributeCount; i++)
providedVertexInputs |= 1u << state.ilAttributes[i].location;
if ((providedVertexInputs & m_vsIn) != m_vsIn) {
Logger::err("DxvkGraphicsPipeline: Input layout mismatches vertex shader input");
return false;
}
// No errors
return true;
}
}

View File

@ -131,6 +131,9 @@ namespace dxvk {
Rc<DxvkShaderModule> m_gs;
Rc<DxvkShaderModule> m_fs;
uint32_t m_vsIn = 0;
uint32_t m_fsOut = 0;
std::mutex m_mutex;
std::vector<PipelineStruct> m_pipelines;
@ -139,8 +142,12 @@ namespace dxvk {
VkPipeline compilePipeline(
const DxvkGraphicsPipelineStateInfo& state,
VkPipeline baseHandle) const;
void destroyPipelines();
bool validatePipelineState(
const DxvkGraphicsPipelineStateInfo& state) const;
};
}

View File

@ -43,8 +43,9 @@ namespace dxvk {
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const DxvkInterfaceSlots& iface,
const SpirvCodeBuffer& code)
: m_stage(stage), m_code(code) {
: m_stage(stage), m_code(code), m_interface(iface) {
for (uint32_t i = 0; i < slotCount; i++)
m_slots.push_back(slotInfos[i]);
}

View File

@ -9,6 +9,19 @@
namespace dxvk {
/**
* \brief Shader interface slots
*
* Stores a bit mask of which shader
* interface slots are defined. Used
* purely for validation purposes.
*/
struct DxvkInterfaceSlots {
uint32_t inputSlots = 0;
uint32_t outputSlots = 0;
};
/**
* \brief Shader module object
*
@ -70,6 +83,7 @@ namespace dxvk {
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const DxvkInterfaceSlots& iface,
const SpirvCodeBuffer& code);
~DxvkShader();
@ -96,6 +110,17 @@ namespace dxvk {
const Rc<vk::DeviceFn>& vkd,
const DxvkDescriptorSlotMapping& mapping) const;
/**
* \brief Inter-stage interface slots
*
* Retrieves the input and output
* registers used by the shader.
* \returns Shader interface slots
*/
DxvkInterfaceSlots interfaceSlots() const {
return m_interface;
}
/**
* \brief Dumps SPIR-V shader
*
@ -118,6 +143,7 @@ namespace dxvk {
SpirvCodeBuffer m_code;
std::vector<DxvkResourceSlot> m_slots;
DxvkInterfaceSlots m_interface;
};