From 5dd9fea0117973cc89c1d1e139fcc1499c80daa6 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Fri, 12 Jan 2018 14:25:26 +0100 Subject: [PATCH] [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. --- src/dxbc/dxbc_compiler.cpp | 7 ++++++ src/dxbc/dxbc_compiler.h | 5 ++++ src/dxgi/dxgi_presenter.cpp | 4 +++- src/dxvk/dxvk_context.cpp | 48 ++++++++++++++++++++++++------------- src/dxvk/dxvk_context.h | 3 +++ src/dxvk/dxvk_device.cpp | 3 ++- src/dxvk/dxvk_device.h | 4 ++++ src/dxvk/dxvk_graphics.cpp | 27 ++++++++++++++++++++- src/dxvk/dxvk_graphics.h | 7 ++++++ src/dxvk/dxvk_shader.cpp | 3 ++- src/dxvk/dxvk_shader.h | 26 ++++++++++++++++++++ 11 files changed, 116 insertions(+), 21 deletions(-) diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index e23db876f..061c68e73 100644 --- a/src/dxbc/dxbc_compiler.cpp +++ b/src/dxbc/dxbc_compiler.cpp @@ -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; } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 1af4a4cc4..305b8b1ff 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -311,6 +311,11 @@ namespace dxvk { std::vector 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; diff --git a/src/dxgi/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 74620ac9c..0f18b0bc2 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -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()); } diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 46b9cbe10..c97408619 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -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); + } } } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index c9a8405f6..000399a6f 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -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 m_rc; std::array m_descriptors; diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index a3950de7c..cafb28874 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -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); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 42680b82a..fc042d9f5 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -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); /** diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 077f11997..0f092778c 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -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; + } + } \ No newline at end of file diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 8491b7644..d664d48c2 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -131,6 +131,9 @@ namespace dxvk { Rc m_gs; Rc m_fs; + uint32_t m_vsIn = 0; + uint32_t m_fsOut = 0; + std::mutex m_mutex; std::vector m_pipelines; @@ -139,8 +142,12 @@ namespace dxvk { VkPipeline compilePipeline( const DxvkGraphicsPipelineStateInfo& state, VkPipeline baseHandle) const; + void destroyPipelines(); + bool validatePipelineState( + const DxvkGraphicsPipelineStateInfo& state) const; + }; } \ No newline at end of file diff --git a/src/dxvk/dxvk_shader.cpp b/src/dxvk/dxvk_shader.cpp index 4b5a1da05..91e1e683f 100644 --- a/src/dxvk/dxvk_shader.cpp +++ b/src/dxvk/dxvk_shader.cpp @@ -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]); } diff --git a/src/dxvk/dxvk_shader.h b/src/dxvk/dxvk_shader.h index ae8821201..561e8ff83 100644 --- a/src/dxvk/dxvk_shader.h +++ b/src/dxvk/dxvk_shader.h @@ -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& 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 m_slots; + DxvkInterfaceSlots m_interface; };