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:
parent
64eb0f909d
commit
5dd9fea011
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
}
|
@ -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]);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user