1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-02 19:24:12 +01:00

[dxvk] Fix push constant compatibility for pipeline libraries

When linking pipelines, all pipeline libraries are required to declare
the exact same set of push constants, even for stages not part of the
respective libraries.

This invalidates all fossilize databases.
This commit is contained in:
Philip Rebohle 2024-04-26 19:06:51 +02:00
parent 462165da19
commit 2970645f33
13 changed files with 84 additions and 73 deletions

View File

@ -336,7 +336,7 @@ namespace dxvk {
}
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count) {
uint32_t SetupRenderStateBlock(SpirvModule& spvModule) {
uint32_t floatType = spvModule.defFloatType(32);
uint32_t uintType = spvModule.defIntType(32, 0);
uint32_t vec3Type = spvModule.defVectorType(floatType, 3);
@ -357,7 +357,7 @@ namespace dxvk {
floatType,
}};
uint32_t rsStruct = spvModule.defStructTypeUnique(count, rsMembers.data());
uint32_t rsStruct = spvModule.defStructTypeUnique(rsMembers.size(), rsMembers.data());
uint32_t rsBlock = spvModule.newVar(
spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant),
spv::StorageClassPushConstant);
@ -369,9 +369,6 @@ namespace dxvk {
uint32_t memberIdx = 0;
auto SetMemberName = [&](const char* name, uint32_t offset) {
if (memberIdx >= count)
return;
spvModule.setDebugMemberName (rsStruct, memberIdx, name);
spvModule.memberDecorateOffset (rsStruct, memberIdx, offset);
memberIdx++;
@ -781,8 +778,6 @@ namespace dxvk {
uint32_t m_inputMask = 0u;
uint32_t m_outputMask = 0u;
uint32_t m_flatShadingMask = 0u;
uint32_t m_pushConstOffset = 0u;
uint32_t m_pushConstSize = 0u;
DxsoProgramType m_programType;
D3D9FFShaderKeyVS m_vsKey;
@ -892,8 +887,8 @@ namespace dxvk {
info.inputMask = m_inputMask;
info.outputMask = m_outputMask;
info.flatShadingInputs = m_flatShadingMask;
info.pushConstOffset = m_pushConstOffset;
info.pushConstSize = m_pushConstSize;
info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
info.pushConstSize = sizeof(D3D9RenderStateInfo);
return new DxvkShader(info, m_module.compile());
}
@ -1384,20 +1379,7 @@ namespace dxvk {
void D3D9FFShaderCompiler::setupRenderStateInfo() {
uint32_t count;
if (m_programType == DxsoProgramType::PixelShader) {
m_pushConstOffset = 0;
m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize);
count = 5;
}
else {
m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize);
m_pushConstSize = sizeof(float) * 6;
count = 11;
}
m_rsBlock = SetupRenderStateBlock(m_module, count);
m_rsBlock = SetupRenderStateBlock(m_module);
}

View File

@ -59,7 +59,7 @@ namespace dxvk {
void DoFixedFunctionAlphaTest(SpirvModule& spvModule, const D3D9AlphaTestContext& ctx);
// Returns a render state block
uint32_t SetupRenderStateBlock(SpirvModule& spvModule, uint32_t count);
uint32_t SetupRenderStateBlock(SpirvModule& spvModule);
struct D3D9PointSizeInfoVS {
uint32_t defaultValue;

View File

@ -134,7 +134,7 @@ namespace dxvk {
info.stage = VK_SHADER_STAGE_COMPUTE_BIT;
info.bindingCount = bindings.size();
info.bindings = bindings.data();
info.pushConstOffset = 0;
info.pushConstStages = VK_SHADER_STAGE_COMPUTE_BIT;
info.pushConstSize = sizeof(VkExtent2D);
return new DxvkShader(info, std::move(code));

View File

@ -257,14 +257,13 @@ namespace dxvk {
info.outputMask = m_outputMask;
info.uniformSize = m_immConstData.size();
info.uniformData = m_immConstData.data();
info.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
info.pushConstSize = sizeof(DxbcPushConstants);
info.outputTopology = m_outputTopology;
if (m_programInfo.type() == DxbcProgramType::HullShader)
info.patchVertexCount = m_hs.vertexCountIn;
if (m_programInfo.type() == DxbcProgramType::PixelShader && m_ps.pushConstantId)
info.pushConstSize = sizeof(DxbcPushConstants);
if (m_moduleInfo.xfb) {
info.xfbRasterizedStream = m_moduleInfo.xfb->rasterizedStream;

View File

@ -228,8 +228,8 @@ namespace dxvk {
info.bindings = m_bindings.data();
info.inputMask = m_inputMask;
info.outputMask = m_outputMask;
info.pushConstOffset = m_pushConstOffset;
info.pushConstSize = m_pushConstSize;
info.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
info.pushConstSize = sizeof(D3D9RenderStateInfo);
if (m_programInfo.type() == DxsoProgramTypes::PixelShader)
info.flatShadingInputs = m_ps.flatShadingMask;
@ -3561,30 +3561,7 @@ void DxsoCompiler::emitControlFlowGenericLoop(
void DxsoCompiler::setupRenderStateInfo() {
uint32_t count;
// Only need alpha ref for PS 3.
// No FF fog component.
if (m_programInfo.type() == DxsoProgramType::PixelShader) {
if (m_programInfo.majorVersion() == 3) {
m_pushConstOffset = offsetof(D3D9RenderStateInfo, alphaRef);
m_pushConstSize = sizeof(float);
}
else {
m_pushConstOffset = 0;
m_pushConstSize = offsetof(D3D9RenderStateInfo, pointSize);
}
count = 5;
}
else {
m_pushConstOffset = offsetof(D3D9RenderStateInfo, pointSize);
// Point scale never triggers on programmable
m_pushConstSize = sizeof(float) * 3;
count = 8;
}
m_rsBlock = SetupRenderStateBlock(m_module, count);
m_rsBlock = SetupRenderStateBlock(m_module);
}

View File

@ -344,8 +344,6 @@ namespace dxvk {
// covers vertex input and fragment output.
uint32_t m_inputMask = 0u;
uint32_t m_outputMask = 0u;
uint32_t m_pushConstOffset = 0u;
uint32_t m_pushConstSize = 0u;
///////////////////////////////////
// Shader-specific data structures

View File

@ -4984,7 +4984,7 @@ namespace dxvk {
// Mark compute resources and push constants as dirty
m_descriptorState.dirtyStages(VK_SHADER_STAGE_COMPUTE_BIT);
if (newPipeline->getBindings()->layout().getPushConstantRange().size)
if (newPipeline->getBindings()->layout().getPushConstantRange(true).size)
m_flags.set(DxvkContextFlag::DirtyPushConstants);
m_flags.clr(DxvkContextFlag::CpDirtyPipelineState);
@ -5058,7 +5058,7 @@ namespace dxvk {
m_descriptorState.dirtyStages(VK_SHADER_STAGE_ALL_GRAPHICS);
if (newPipeline->getBindings()->layout().getPushConstantRange().size)
if (newPipeline->getBindings()->layout().getPushConstantRange(true).size)
m_flags.set(DxvkContextFlag::DirtyPushConstants);
m_flags.clr(DxvkContextFlag::GpDirtyPipeline);
@ -5925,16 +5925,19 @@ namespace dxvk {
auto bindings = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
? m_state.gp.pipeline->getBindings()
: m_state.cp.pipeline->getBindings();
VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange();
// Optimized pipelines may have push constants trimmed, so look up
// the exact layout used for the currently bound pipeline.
bool independentSets = BindPoint == VK_PIPELINE_BIND_POINT_GRAPHICS
&& m_flags.test(DxvkContextFlag::GpIndependentSets);
VkPushConstantRange pushConstRange = bindings->layout().getPushConstantRange(independentSets);
if (!pushConstRange.size)
return;
// Push constants should be compatible between complete and
// independent layouts, so always ask for the complete one
m_cmd->cmdPushConstants(
bindings->getPipelineLayout(false),
bindings->getPipelineLayout(independentSets),
pushConstRange.stageFlags,
pushConstRange.offset,
pushConstRange.size,

View File

@ -205,7 +205,7 @@ namespace dxvk {
DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages)
: m_pushConst { 0, 0, 0 }, m_stages(stages) {
: m_pushConst { 0, 0, 0 }, m_pushConstStages(0), m_stages(stages) {
}
@ -249,11 +249,17 @@ namespace dxvk {
}
void DxvkBindingLayout::addPushConstantStage(VkShaderStageFlagBits stage) {
m_pushConstStages |= stage;
}
void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) {
for (uint32_t i = 0; i < layout.m_bindings.size(); i++)
m_bindings[i].merge(layout.m_bindings[i]);
addPushConstantRange(layout.m_pushConst);
m_pushConstStages |= layout.m_pushConstStages;
}
@ -266,6 +272,9 @@ namespace dxvk {
return false;
}
if (m_pushConstStages != other.m_pushConstStages)
return false;
if (m_pushConst.stageFlags != other.m_pushConst.stageFlags
|| m_pushConst.offset != other.m_pushConst.offset
|| m_pushConst.size != other.m_pushConst.size)
@ -282,6 +291,7 @@ namespace dxvk {
for (uint32_t i = 0; i < m_bindings.size(); i++)
hash.add(m_bindings[i].hash());
hash.add(m_pushConstStages);
hash.add(m_pushConst.stageFlags);
hash.add(m_pushConst.offset);
hash.add(m_pushConst.size);
@ -334,15 +344,16 @@ namespace dxvk {
}
// Create pipeline layout objects
VkPushConstantRange pushConst = m_layout.getPushConstantRange();
VkPushConstantRange pushConstComplete = m_layout.getPushConstantRange(false);
VkPushConstantRange pushConstIndependent = m_layout.getPushConstantRange(true);
VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
pipelineLayoutInfo.setLayoutCount = setCount;
pipelineLayoutInfo.pSetLayouts = setLayouts.data();
if (pushConst.stageFlags && pushConst.size) {
if (pushConstComplete.stageFlags && pushConstComplete.size) {
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConst;
pipelineLayoutInfo.pPushConstantRanges = &pushConstComplete;
}
// If the full set is defined, create a layout without INDEPENDENT_SET_BITS
@ -356,6 +367,11 @@ namespace dxvk {
if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) {
pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
if (pushConstIndependent.stageFlags && pushConstIndependent.size) {
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstIndependent;
}
if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout))
throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout");
}

View File

@ -292,8 +292,19 @@ namespace dxvk {
* \brief Retrieves push constant range
* \returns Push constant range
*/
VkPushConstantRange getPushConstantRange() const {
return m_pushConst;
VkPushConstantRange getPushConstantRange(bool independent) const {
VkPushConstantRange result = m_pushConst;
if (!independent) {
result.stageFlags &= m_pushConstStages;
if (!result.stageFlags) {
result.offset = 0;
result.size = 0;
}
}
return result;
}
/**
@ -324,6 +335,12 @@ namespace dxvk {
*/
void addPushConstantRange(VkPushConstantRange range);
/**
* \brief Adds a stage that actively uses push constants
* \param [in] stage Shader stage
*/
void addPushConstantStage(VkShaderStageFlagBits stage);
/**
* \brief Merges binding layouts
*
@ -353,6 +370,7 @@ namespace dxvk {
std::array<DxvkBindingList, DxvkDescriptorSets::SetCount> m_bindings;
VkPushConstantRange m_pushConst;
VkShaderStageFlags m_pushConstStages;
VkShaderStageFlags m_stages;
};

View File

@ -59,8 +59,8 @@ namespace dxvk {
if (info.pushConstSize) {
VkPushConstantRange pushConst;
pushConst.stageFlags = info.stage;
pushConst.offset = info.pushConstOffset;
pushConst.stageFlags = info.pushConstStages;
pushConst.offset = 0;
pushConst.size = info.pushConstSize;
m_bindings.addPushConstantRange(pushConst);
@ -75,6 +75,8 @@ namespace dxvk {
// Run an analysis pass over the SPIR-V code to gather some
// info that we may need during pipeline compilation.
bool usesPushConstants = false;
std::vector<BindingOffsets> bindingOffsets;
std::vector<uint32_t> varIds;
std::vector<uint32_t> sampleMaskIds;
@ -154,6 +156,9 @@ namespace dxvk {
if (std::find(sampleMaskIds.begin(), sampleMaskIds.end(), ins.arg(2)) != sampleMaskIds.end())
m_flags.set(DxvkShaderFlag::ExportsSampleMask);
}
if (ins.arg(3) == spv::StorageClassPushConstant)
usesPushConstants = true;
}
// Ignore the actual shader code, there's nothing interesting for us in there.
@ -169,6 +174,11 @@ namespace dxvk {
m_bindingOffsets.push_back(info);
}
// Set flag for stages that actually use push constants
// so that they can be trimmed for optimized pipelines.
if (usesPushConstants)
m_bindings.addPushConstantStage(info.stage);
// Don't set pipeline library flag if the shader
// doesn't actually support pipeline libraries
m_needsLibraryCompile = canUsePipelineLibrary(true);

View File

@ -52,7 +52,7 @@ namespace dxvk {
/// Flat shading input mask
uint32_t flatShadingInputs = 0;
/// Push constant range
uint32_t pushConstOffset = 0;
VkShaderStageFlags pushConstStages = 0;
uint32_t pushConstSize = 0;
/// Uniform buffer data
uint32_t uniformSize = 0;

View File

@ -329,6 +329,8 @@ namespace dxvk {
DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(PresenterArgs);
vsInfo.outputMask = 0x1;
m_vs = new DxvkShader(vsInfo, std::move(vsCode));
@ -336,6 +338,7 @@ namespace dxvk {
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(PresenterArgs);
fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1;

View File

@ -183,14 +183,17 @@ namespace dxvk::hud {
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.bindingCount = vsBindings.size();
vsInfo.bindings = vsBindings.data();
vsInfo.outputMask = 0x3;
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.pushConstSize = sizeof(HudTextPushConstants);
vsInfo.outputMask = 0x3;
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
DxvkShaderCreateInfo fsInfo;
fsInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.bindingCount = fsBindings.size();
fsInfo.bindings = fsBindings.data();
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT;
fsInfo.pushConstSize = sizeof(HudTextPushConstants);
fsInfo.inputMask = 0x3;
fsInfo.outputMask = 0x1;
result.frag = new DxvkShader(fsInfo, std::move(fsCode));
@ -212,6 +215,7 @@ namespace dxvk::hud {
DxvkShaderCreateInfo vsInfo;
vsInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vsInfo.outputMask = 0x1;
vsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
vsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.vert = new DxvkShader(vsInfo, std::move(vsCode));
@ -221,6 +225,7 @@ namespace dxvk::hud {
fsInfo.bindings = fsBindings.data();
fsInfo.inputMask = 0x1;
fsInfo.outputMask = 0x1;
fsInfo.pushConstStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
fsInfo.pushConstSize = sizeof(HudGraphPushConstants);
result.frag = new DxvkShader(fsInfo, std::move(fsCode));