1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 19:54:19 +01:00

[dxvk] Fix handling of undefined shader inputs

If the previous stage or the input layout does not define an input,
D3D11 will read zeroes wheras the result is undefined in Vulkan.

Fixes performance degradation and rendering issue in Final Fantasy XV
with the "Geomapping" (terrain tessellation) option enabled.
This commit is contained in:
Philip Rebohle 2019-11-19 13:32:51 +01:00
parent a1f55330ee
commit 4fff2343c1
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
2 changed files with 68 additions and 28 deletions

View File

@ -180,18 +180,11 @@ namespace dxvk {
VkSpecializationInfo specInfo = specData.getSpecInfo();
DxvkShaderModuleCreateInfo moduleInfo;
moduleInfo.fsDualSrcBlend = state.omBlend[0].blendEnable() && (
util::isDualSourceBlendFactor(state.omBlend[0].srcColorBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].dstColorBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].srcAlphaBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].dstAlphaBlendFactor()));
auto vsm = createShaderModule(m_shaders.vs, moduleInfo);
auto gsm = createShaderModule(m_shaders.gs, moduleInfo);
auto tcsm = createShaderModule(m_shaders.tcs, moduleInfo);
auto tesm = createShaderModule(m_shaders.tes, moduleInfo);
auto fsm = createShaderModule(m_shaders.fs, moduleInfo);
auto vsm = createShaderModule(m_shaders.vs, state);
auto tcsm = createShaderModule(m_shaders.tcs, state);
auto tesm = createShaderModule(m_shaders.tes, state);
auto gsm = createShaderModule(m_shaders.gs, state);
auto fsm = createShaderModule(m_shaders.fs, state);
std::vector<VkPipelineShaderStageCreateInfo> stages;
if (vsm) stages.push_back(vsm.stageInfo(&specInfo));
@ -433,25 +426,69 @@ namespace dxvk {
DxvkShaderModule DxvkGraphicsPipeline::createShaderModule(
const Rc<DxvkShader>& shader,
const DxvkShaderModuleCreateInfo& info) const {
return shader != nullptr
? shader->createShaderModule(m_vkd, m_slotMapping, info)
: DxvkShaderModule();
const DxvkGraphicsPipelineStateInfo& state) const {
if (shader == nullptr)
return DxvkShaderModule();
DxvkShaderModuleCreateInfo info;
// Fix up fragment shader outputs for dual-source blending
if (shader->stage() == VK_SHADER_STAGE_FRAGMENT_BIT) {
info.fsDualSrcBlend = state.omBlend[0].blendEnable() && (
util::isDualSourceBlendFactor(state.omBlend[0].srcColorBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].dstColorBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].srcAlphaBlendFactor()) ||
util::isDualSourceBlendFactor(state.omBlend[0].dstAlphaBlendFactor()));
}
// Deal with undefined shader inputs
uint32_t consumedInputs = shader->interfaceSlots().inputSlots;
uint32_t providedInputs = 0;
if (shader->stage() == VK_SHADER_STAGE_VERTEX_BIT) {
for (uint32_t i = 0; i < state.il.attributeCount(); i++)
providedInputs |= 1u << state.ilAttributes[i].location();
} else if (shader->stage() != VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
auto prevStage = getPrevStageShader(shader->stage());
providedInputs = prevStage->interfaceSlots().outputSlots;
} else {
// Technically not correct, but this
// would need a lot of extra care
providedInputs = consumedInputs;
}
info.undefinedInputs = (providedInputs & consumedInputs) ^ consumedInputs;
return shader->createShaderModule(m_vkd, m_slotMapping, info);
}
Rc<DxvkShader> DxvkGraphicsPipeline::getPrevStageShader(VkShaderStageFlagBits stage) const {
if (stage == VK_SHADER_STAGE_VERTEX_BIT)
return nullptr;
if (stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
return m_shaders.tcs;
Rc<DxvkShader> result = m_shaders.vs;
if (stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
return result;
if (m_shaders.tes != nullptr)
result = m_shaders.tes;
if (stage == VK_SHADER_STAGE_GEOMETRY_BIT)
return result;
if (m_shaders.gs != nullptr)
result = m_shaders.gs;
return result;
}
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.il.attributeCount(); i++)
providedVertexInputs |= 1u << state.ilAttributes[i].location();
if ((providedVertexInputs & m_vsIn) != m_vsIn)
return false;
// Tessellation shaders and patches must be used together
bool hasPatches = state.ia.primitiveTopology() == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;

View File

@ -240,8 +240,11 @@ namespace dxvk {
DxvkShaderModule createShaderModule(
const Rc<DxvkShader>& shader,
const DxvkShaderModuleCreateInfo& info) const;
const DxvkGraphicsPipelineStateInfo& state) const;
Rc<DxvkShader> getPrevStageShader(
VkShaderStageFlagBits stage) const;
bool validatePipelineState(
const DxvkGraphicsPipelineStateInfo& state) const;
@ -252,7 +255,7 @@ namespace dxvk {
void logPipelineState(
LogLevel level,
const DxvkGraphicsPipelineStateInfo& state) const;
};
}