graphics: support RDNA2 custom pixel interpolation

Co-authored-by: Senaxx <Senaxx@users.noreply.github.com>
This commit is contained in:
nmzik
2026-08-21 23:33:40 +02:00
parent 17c2b4cc7d
commit 5e64efce4f
14 changed files with 414 additions and 28 deletions
@@ -199,7 +199,15 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
vk::PhysicalDeviceVulkan12Features features12 {}; vk::PhysicalDeviceVulkan12Features features12 {};
features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features; features12.sType = vk::StructureType::ePhysicalDeviceVulkan12Features;
#if defined(__APPLE__)
features12.pNext = &depth_clip_control; features12.pNext = &depth_clip_control;
#else
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_barycentric {};
fragment_barycentric.sType =
vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR;
fragment_barycentric.pNext = &depth_clip_control;
features12.pNext = &fragment_barycentric;
#endif
features13.pNext = &features12; features13.pNext = &features12;
device_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2; device_features2.sType = vk::StructureType::ePhysicalDeviceFeatures2;
@@ -231,6 +239,12 @@ static void VulkanFindPhysicalDevice(vk::Instance instance, vk::SurfaceKHR surfa
skip_device = true; skip_device = true;
#endif #endif
} }
#if !defined(__APPLE__)
if (fragment_barycentric.fragmentShaderBarycentric != VK_TRUE) {
LOGF("fragmentShaderBarycentric is not supported\n");
skip_device = true;
}
#endif
if (features12.samplerMirrorClampToEdge != VK_TRUE) { if (features12.samplerMirrorClampToEdge != VK_TRUE) {
LOGF("samplerMirrorClampToEdge is not supported\n"); LOGF("samplerMirrorClampToEdge is not supported\n");
@@ -522,6 +536,14 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
supported_features12.pNext = nullptr; supported_features12.pNext = nullptr;
supported_features13.pNext = &supported_features12; supported_features13.pNext = &supported_features12;
#if !defined(__APPLE__)
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR supported_fragment_barycentric {};
supported_fragment_barycentric.sType =
vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR;
supported_fragment_barycentric.pNext = nullptr;
supported_features12.pNext = &supported_fragment_barycentric;
#endif
const auto robustness2_ext_enabled = const auto robustness2_ext_enabled =
HasExtension(device_extensions, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); HasExtension(device_extensions, VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
@@ -529,7 +551,11 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
supported_robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT; supported_robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT;
supported_robustness2.pNext = nullptr; supported_robustness2.pNext = nullptr;
if (robustness2_ext_enabled) { if (robustness2_ext_enabled) {
#if defined(__APPLE__)
supported_features12.pNext = &supported_robustness2; supported_features12.pNext = &supported_robustness2;
#else
supported_fragment_barycentric.pNext = &supported_robustness2;
#endif
} }
vk::PhysicalDeviceFeatures2 supported_features2 {}; vk::PhysicalDeviceFeatures2 supported_features2 {};
@@ -544,6 +570,9 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
supported_features13.synchronization2 != VK_TRUE); supported_features13.synchronization2 != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.sampleRateShading != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.sampleRateShading != VK_TRUE);
EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE); EXIT_NOT_IMPLEMENTED(supported_features2.features.depthBiasClamp != VK_TRUE);
#if !defined(__APPLE__)
EXIT_NOT_IMPLEMENTED(supported_fragment_barycentric.fragmentShaderBarycentric != VK_TRUE);
#endif
features12.timelineSemaphore = VK_TRUE; features12.timelineSemaphore = VK_TRUE;
if (Config::GpuAssistedValidationEnabled()) { if (Config::GpuAssistedValidationEnabled()) {
@@ -576,7 +605,16 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
vk::PhysicalDeviceRobustness2FeaturesEXT robustness2 {}; vk::PhysicalDeviceRobustness2FeaturesEXT robustness2 {};
robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT; robustness2.sType = vk::StructureType::ePhysicalDeviceRobustness2FeaturesEXT;
#if defined(__APPLE__)
robustness2.pNext = &features12; robustness2.pNext = &features12;
#else
vk::PhysicalDeviceFragmentShaderBarycentricFeaturesKHR fragment_barycentric {};
fragment_barycentric.sType =
vk::StructureType::ePhysicalDeviceFragmentShaderBarycentricFeaturesKHR;
fragment_barycentric.pNext = &features12;
fragment_barycentric.fragmentShaderBarycentric = VK_TRUE;
robustness2.pNext = &fragment_barycentric;
#endif
if (robustness2_ext_enabled) { if (robustness2_ext_enabled) {
robustness2.robustBufferAccess2 = supported_robustness2.robustBufferAccess2; robustness2.robustBufferAccess2 = supported_robustness2.robustBufferAccess2;
robustness2.robustImageAccess2 = supported_robustness2.robustImageAccess2; robustness2.robustImageAccess2 = supported_robustness2.robustImageAccess2;
@@ -586,9 +624,14 @@ static vk::Device VulkanCreateDevice(vk::PhysicalDevice physical_device, const V
const bool subgroup_size_control_enabled = graphics.subgroup_size_control_enabled && const bool subgroup_size_control_enabled = graphics.subgroup_size_control_enabled &&
supported_features13.subgroupSizeControl == VK_TRUE; supported_features13.subgroupSizeControl == VK_TRUE;
auto features13 = required_features13; auto features13 = required_features13;
features13.pNext = robustness2_ext_enabled ? static_cast<void*>(&robustness2) #if defined(__APPLE__)
: static_cast<void*>(&features12); features13.pNext = robustness2_ext_enabled ? static_cast<void*>(&robustness2)
: static_cast<void*>(&features12);
#else
features13.pNext = robustness2_ext_enabled ? static_cast<void*>(&robustness2)
: static_cast<void*>(&fragment_barycentric);
#endif
features13.robustImageAccess = supported_features13.robustImageAccess; features13.robustImageAccess = supported_features13.robustImageAccess;
features13.subgroupSizeControl = subgroup_size_control_enabled ? VK_TRUE : VK_FALSE; features13.subgroupSizeControl = subgroup_size_control_enabled ? VK_TRUE : VK_FALSE;
@@ -922,6 +965,7 @@ void WindowContext::CreateVulkan() {
#else #else
device_extensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME); device_extensions.push_back(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME);
device_extensions.push_back(VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME); device_extensions.push_back(VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
device_extensions.push_back(VK_KHR_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME);
#endif #endif
#ifdef KYTY_ENABLE_DEBUG_PRINTF #ifdef KYTY_ENABLE_DEBUG_PRINTF
@@ -32,6 +32,11 @@ bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr) {
ShaderPixelParameterIsFlat(*state.input_info.pixel, attr); ShaderPixelParameterIsFlat(*state.input_info.pixel, attr);
} }
bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr) {
return state.stage == ShaderType::Pixel &&
ShaderPixelParameterIsCustom(*state.input_info.pixel, attr);
}
void SetError(std::string* error, const char* message) { void SetError(std::string* error, const char* message) {
if (error != nullptr) { if (error != nullptr) {
*error = message; *error = message;
@@ -47,8 +52,8 @@ bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind ki
void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program) { void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program) {
for (const auto& input: program.info.inputs) { for (const auto& input: program.info.inputs) {
state.inputs.push_back( state.inputs.push_back({input.kind, input.location, input.component_count, 0,
{input.kind, input.location, input.component_count, 0, input.debug_name}); input.debug_name, input.per_vertex});
} }
for (const auto& output: program.info.outputs) { for (const auto& output: program.info.outputs) {
if (HasOutput(state.outputs, output.kind, output.index)) { if (HasOutput(state.outputs, output.kind, output.index)) {
@@ -93,6 +93,18 @@ uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t
state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value}); state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value});
return bits; return bits;
} }
if (kind == IR::StageInputKind::BaryCoordSmooth ||
kind == IR::StageInputKind::BaryCoordNoPerspective) {
const auto pointer = state.builder.AllocateId();
const auto value = state.builder.AllocateId();
const auto bits = state.builder.AllocateId();
state.builder.AddFunction({OpAccessChain,
TypePointer(state, StorageClassInput, TypeF32(state)), pointer,
variable, ConstantU32(state, component + 1u)});
state.builder.AddFunction({OpLoad, TypeF32(state), value, pointer});
state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value});
return bits;
}
return EmitInputComponentU32(state, kind, component); return EmitInputComponentU32(state, kind, component);
} }
@@ -187,6 +199,43 @@ uint32_t EmitAttribute(ValueEmitContext& ctx, uint32_t attr, uint32_t chan) {
if (state.stage == ShaderType::Vertex) { if (state.stage == ShaderType::Vertex) {
return EmitVertexParameterComponentU32(state, *input, chan & 3u); return EmitVertexParameterComponentU32(state, *input, chan & 3u);
} }
const auto load_per_vertex = [&](uint32_t vertex) {
const auto pointer = state.builder.AllocateId();
const auto value = state.builder.AllocateId();
state.builder.AddFunction(
{OpAccessChain, TypePointer(state, StorageClassInput, TypeF32(state)), pointer,
input->variable_id, ConstantU32(state, vertex), ConstantU32(state, chan & 3u)});
state.builder.AddFunction({OpLoad, TypeF32(state), value, pointer});
return value;
};
if (input->per_vertex) {
const auto barycentric_kind = state.input_info.pixel->ps_no_perspective
? IR::StageInputKind::BaryCoordNoPerspective
: IR::StageInputKind::BaryCoordSmooth;
const auto barycentric = InputVariableForKind(state, barycentric_kind);
uint32_t sum = 0;
for (uint32_t vertex = 0; vertex < 3u; vertex++) {
const auto pointer = state.builder.AllocateId();
const auto weight = state.builder.AllocateId();
const auto product = state.builder.AllocateId();
state.builder.AddFunction({OpAccessChain,
TypePointer(state, StorageClassInput, TypeF32(state)),
pointer, barycentric, ConstantU32(state, vertex)});
state.builder.AddFunction({OpLoad, TypeF32(state), weight, pointer});
state.builder.AddFunction(
{OpFMul, TypeF32(state), product, load_per_vertex(vertex), weight});
if (vertex == 0u) {
sum = product;
} else {
const auto next = state.builder.AllocateId();
state.builder.AddFunction({OpFAdd, TypeF32(state), next, sum, product});
sum = next;
}
}
const auto bits = state.builder.AllocateId();
state.builder.AddFunction({OpBitcast, TypeU32(state), bits, sum});
return bits;
}
const auto vector = state.builder.AllocateId(); const auto vector = state.builder.AllocateId();
const auto component = state.builder.AllocateId(); const auto component = state.builder.AllocateId();
const auto bits = state.builder.AllocateId(); const auto bits = state.builder.AllocateId();
@@ -196,6 +245,35 @@ uint32_t EmitAttribute(ValueEmitContext& ctx, uint32_t attr, uint32_t chan) {
return bits; return bits;
} }
uint32_t EmitInterpolationParameter(ValueEmitContext& ctx, uint32_t attr, uint32_t chan,
uint32_t mode) {
auto& state = ctx.state;
const auto* input = InputBindingForParameter(state, attr);
if (!input->per_vertex) {
return EmitAttribute(ctx, attr, chan);
}
const auto load_vertex = [&](uint32_t vertex) {
const auto pointer = state.builder.AllocateId();
const auto value = state.builder.AllocateId();
state.builder.AddFunction(
{OpAccessChain, TypePointer(state, StorageClassInput, TypeF32(state)), pointer,
input->variable_id, ConstantU32(state, vertex), ConstantU32(state, chan & 3u)});
state.builder.AddFunction({OpLoad, TypeF32(state), value, pointer});
return value;
};
const auto selected_vertex = (mode + 1u) % 3u;
uint32_t value = load_vertex(selected_vertex);
if (!PixelParameterIsCustom(state, attr) && mode < 2u) {
const auto delta = state.builder.AllocateId();
state.builder.AddFunction({OpFSub, TypeF32(state), delta, value, load_vertex(0)});
value = delta;
}
const auto bits = state.builder.AllocateId();
state.builder.AddFunction({OpBitcast, TypeU32(state), bits, value});
return bits;
}
uint32_t MrtOutputMode(const EmitterState& state, const IR::ExportInfo& exp) { uint32_t MrtOutputMode(const EmitterState& state, const IR::ExportInfo& exp) {
if (state.stage != ShaderType::Pixel || exp.kind != IR::ExportTargetKind::Mrt || if (state.stage != ShaderType::Pixel || exp.kind != IR::ExportTargetKind::Mrt ||
exp.index >= std::size(state.input_info.pixel->target_output_mode)) { exp.index >= std::size(state.input_info.pixel->target_output_mode)) {
@@ -515,6 +593,10 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
case IR::ValueOpcode::GetAttribute: case IR::ValueOpcode::GetAttribute:
ctx.Define(inst, EmitAttribute(ctx, inst.Arg(0).U32(), inst.Arg(1).U32())); ctx.Define(inst, EmitAttribute(ctx, inst.Arg(0).U32(), inst.Arg(1).U32()));
return true; return true;
case IR::ValueOpcode::GetInterpolationParameter:
ctx.Define(inst, EmitInterpolationParameter(ctx, inst.Arg(0).U32(), inst.Arg(1).U32(),
inst.Arg(2).U32()));
return true;
case IR::ValueOpcode::SetAttribute: EmitExport(ctx, inst); return true; case IR::ValueOpcode::SetAttribute: EmitExport(ctx, inst); return true;
case IR::ValueOpcode::GetShaderBase: case IR::ValueOpcode::GetShaderBase:
// Guest S_GETPC values stay shader-relative in SPIR-V, matching the runtime ABI. The // Guest S_GETPC values stay shader-relative in SPIR-V, matching the runtime ABI. The
@@ -47,6 +47,7 @@ enum : uint32_t {
CapabilityGroupNonUniformBallot = 64, CapabilityGroupNonUniformBallot = 64,
CapabilityGroupNonUniformShuffle = 65, CapabilityGroupNonUniformShuffle = 65,
CapabilitySignedZeroInfNanPreserve = 4466, CapabilitySignedZeroInfNanPreserve = 4466,
CapabilityFragmentBarycentricKHR = 5284,
CapabilityComputeDerivativeGroupQuadsKHR = 5288, CapabilityComputeDerivativeGroupQuadsKHR = 5288,
StorageClassUniformConstant = 0, StorageClassUniformConstant = 0,
StorageClassInput = 1, StorageClassInput = 1,
@@ -71,6 +72,7 @@ enum : uint32_t {
DecorationBinding = 33, DecorationBinding = 33,
DecorationDescriptorSet = 34, DecorationDescriptorSet = 34,
DecorationOffset = 35, DecorationOffset = 35,
DecorationPerVertexKHR = 5285,
}; };
enum : uint32_t { enum : uint32_t {
@@ -86,6 +88,8 @@ enum : uint32_t {
BuiltInSubgroupLocalInvocationId = 41, BuiltInSubgroupLocalInvocationId = 41,
BuiltInVertexIndex = 42, BuiltInVertexIndex = 42,
BuiltInInstanceIndex = 43, BuiltInInstanceIndex = 43,
BuiltInBaryCoordKHR = 5286,
BuiltInBaryCoordNoPerspKHR = 5287,
}; };
enum : uint32_t { enum : uint32_t {
@@ -283,6 +287,7 @@ struct InputBinding {
uint32_t component_count = 1; uint32_t component_count = 1;
uint32_t variable_id = 0; uint32_t variable_id = 0;
std::string debug_name; std::string debug_name;
bool per_vertex = false;
}; };
struct OutputBinding { struct OutputBinding {
@@ -564,6 +569,8 @@ uint32_t PixelParameterLocation(const EmitterState& state, uint32_t attr);
bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr); bool PixelParameterIsFlat(const EmitterState& state, uint32_t attr);
bool PixelParameterIsCustom(const EmitterState& state, uint32_t attr);
VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location); VertexInputScalarKind VertexParameterScalarKind(const EmitterState& state, uint32_t location);
uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input); uint32_t VertexParameterComponentCount(const EmitterState& state, const InputBinding& input);
@@ -441,6 +441,8 @@ uint32_t BuiltInForInput(IR::StageInputKind kind) {
case IR::StageInputKind::InstanceIndex: return BuiltInInstanceIndex; case IR::StageInputKind::InstanceIndex: return BuiltInInstanceIndex;
case IR::StageInputKind::FragCoord: return BuiltInFragCoord; case IR::StageInputKind::FragCoord: return BuiltInFragCoord;
case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing; case IR::StageInputKind::FrontFacing: return BuiltInFrontFacing;
case IR::StageInputKind::BaryCoordSmooth: return BuiltInBaryCoordKHR;
case IR::StageInputKind::BaryCoordNoPerspective: return BuiltInBaryCoordNoPerspKHR;
case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId; case IR::StageInputKind::WorkgroupId: return BuiltInWorkgroupId;
case IR::StageInputKind::LocalInvocationId: return BuiltInLocalInvocationId; case IR::StageInputKind::LocalInvocationId: return BuiltInLocalInvocationId;
case IR::StageInputKind::LocalInvocationIndex: return BuiltInLocalInvocationIndex; case IR::StageInputKind::LocalInvocationIndex: return BuiltInLocalInvocationIndex;
@@ -464,11 +466,14 @@ void AddInputAnnotationsAndNames(EmitterState& state) {
state.builder.AddName(input.variable_id, input.debug_name.c_str()); state.builder.AddName(input.variable_id, input.debug_name.c_str());
if (input.kind == IR::StageInputKind::Parameter) { if (input.kind == IR::StageInputKind::Parameter) {
const auto flat = PixelParameterIsFlat(state, input.location); const auto flat = PixelParameterIsFlat(state, input.location);
if (flat) { if (input.per_vertex) {
state.builder.AddAnnotation(
{OpDecorate, input.variable_id, DecorationPerVertexKHR});
} else if (flat) {
state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat}); state.builder.AddAnnotation({OpDecorate, input.variable_id, DecorationFlat});
} }
if (state.stage == ShaderType::Pixel && state.input_info.pixel->ps_no_perspective && if (state.stage == ShaderType::Pixel && state.input_info.pixel->ps_no_perspective &&
!flat) { !flat && !input.per_vertex) {
state.builder.AddAnnotation( state.builder.AddAnnotation(
{OpDecorate, input.variable_id, DecorationNoPerspective}); {OpDecorate, input.variable_id, DecorationNoPerspective});
} }
@@ -625,6 +630,16 @@ void DefineModule(EmitterState& state) {
state.builder.RequireCapability(CapabilityComputeDerivativeGroupQuadsKHR); state.builder.RequireCapability(CapabilityComputeDerivativeGroupQuadsKHR);
state.builder.RequireExtension("SPV_KHR_compute_shader_derivatives"); state.builder.RequireExtension("SPV_KHR_compute_shader_derivatives");
} }
const bool fragment_barycentric =
state.stage == ShaderType::Pixel &&
std::any_of(state.inputs.begin(), state.inputs.end(), [](const InputBinding& input) {
return input.per_vertex || input.kind == IR::StageInputKind::BaryCoordSmooth ||
input.kind == IR::StageInputKind::BaryCoordNoPerspective;
});
if (fragment_barycentric) {
state.builder.RequireCapability(CapabilityFragmentBarycentricKHR);
state.builder.RequireExtension("SPV_KHR_fragment_shader_barycentric");
}
state.builder.RequireExtension("SPV_KHR_float_controls"); state.builder.RequireExtension("SPV_KHR_float_controls");
state.builder.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450}); state.builder.AddMemoryModel({AddressingModelLogical, MemoryModelGLSL450});
state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main", state.builder.AddEntryPoint(ExecutionModelForStage(state.stage), state.main_func, "main",
@@ -689,6 +704,10 @@ void DefineModule(EmitterState& state) {
case IR::StageInputKind::FragCoord: case IR::StageInputKind::FragCoord:
ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4)); ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4));
break; break;
case IR::StageInputKind::BaryCoordSmooth:
case IR::StageInputKind::BaryCoordNoPerspective:
ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 3));
break;
case IR::StageInputKind::FrontFacing: case IR::StageInputKind::FrontFacing:
ptr_type = TypePointer(state, StorageClassInput, TypeBool(state)); ptr_type = TypePointer(state, StorageClassInput, TypeBool(state));
break; break;
@@ -697,6 +716,10 @@ void DefineModule(EmitterState& state) {
const auto kind = VertexParameterScalarKind(state, input.location); const auto kind = VertexParameterScalarKind(state, input.location);
const auto components = VertexParameterComponentCount(state, input); const auto components = VertexParameterComponentCount(state, input);
ptr_type = VertexParameterInputPointerType(state, kind, components); ptr_type = VertexParameterInputPointerType(state, kind, components);
} else if (input.per_vertex) {
const auto array_type = state.builder.Type(
OpTypeArray, {TypeF32Vector(state, 4), ConstantU32(state, 3)});
ptr_type = TypePointer(state, StorageClassInput, array_type);
} else { } else {
ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4)); ptr_type = TypePointer(state, StorageClassInput, TypeF32Vector(state, 4));
} }
@@ -14,10 +14,14 @@ IR::ExportFlags Translator::AddExportInfo(const IR::Instruction& inst) {
bool Translator::TranslateAttributeOperation(const IR::Instruction& inst) { bool Translator::TranslateAttributeOperation(const IR::Instruction& inst) {
if (inst.op == IR::Opcode::LoadInputF32) { if (inst.op == IR::Opcode::LoadInputF32) {
for (uint32_t component = 0; component < inst.input_info.component_count; component++) { for (uint32_t component = 0; component < inst.input_info.component_count; component++) {
WriteOperand(OffsetOperand(inst.dst, component), const auto chan = inst.input_info.chan + component;
ir.Emit(IR::ValueOpcode::GetAttribute, const auto value = inst.input_info.interpolation_mode < 3u
{IR::Value(inst.input_info.attr), ? ir.Emit(IR::ValueOpcode::GetInterpolationParameter,
IR::Value(inst.input_info.chan + component)})); {IR::Value(inst.input_info.attr), IR::Value(chan),
IR::Value(inst.input_info.interpolation_mode)})
: ir.Emit(IR::ValueOpcode::GetAttribute,
{IR::Value(inst.input_info.attr), IR::Value(chan)});
WriteOperand(OffsetOperand(inst.dst, component), value);
} }
return true; return true;
} }
@@ -901,8 +901,15 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
first_bit)); first_bit));
} }
} else if (source.stage == ShaderType::Pixel) { } else if (source.stage == ShaderType::Pixel) {
const auto* ps = pixel_input_info; const auto* ps = pixel_input_info;
uint32_t reg = ps->ps_system_input_base; if (ps->ps_perspective_center_vgpr != UINT32_MAX) {
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(ps->ps_perspective_center_vgpr),
builtin(IR::StageInputKind::BaryCoordSmooth, 0));
entry_ir.SetVectorReg(
static_cast<IR::VectorReg>(ps->ps_perspective_center_vgpr + 1u),
builtin(IR::StageInputKind::BaryCoordSmooth, 1));
}
uint32_t reg = ps->ps_system_input_base;
if (ps->ps_pos_x) { if (ps->ps_pos_x) {
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++), entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
builtin(IR::StageInputKind::FragCoord, 0)); builtin(IR::StageInputKind::FragCoord, 0));
@@ -866,9 +866,9 @@ bool LowerVInterpP1F32(const Decoder::Instruction& decoded, BasicBlock& block) {
bool LowerVInterpLoadF32(const Decoder::Instruction& decoded, BasicBlock& block, bool LowerVInterpLoadF32(const Decoder::Instruction& decoded, BasicBlock& block,
std::string* error) { std::string* error) {
if (decoded.opcode == Decoder::Opcode::V_INTERP_MOV_F32 && decoded.src0.value != 2u) { if (decoded.opcode == Decoder::Opcode::V_INTERP_MOV_F32 && decoded.src0.value >= 3u) {
if (error != nullptr) { if (error != nullptr) {
*error = fmt::format("v_interp_mov_f32 mode {} is not implemented at pc 0x{:08x}", *error = fmt::format("v_interp_mov_f32 mode {} is reserved at pc 0x{:08x}",
decoded.src0.value, decoded.pc); decoded.src0.value, decoded.pc);
} }
return false; return false;
@@ -879,6 +879,9 @@ bool LowerVInterpLoadF32(const Decoder::Instruction& decoded, BasicBlock& block,
inst.op = Opcode::LoadInputF32; inst.op = Opcode::LoadInputF32;
inst.input_info.attr = decoded.src1.value; inst.input_info.attr = decoded.src1.value;
inst.input_info.chan = decoded.src2.value; inst.input_info.chan = decoded.src2.value;
if (decoded.opcode == Decoder::Opcode::V_INTERP_MOV_F32) {
inst.input_info.interpolation_mode = decoded.src0.value;
}
if (!LowerRegisterOperand(decoded.dst, inst.dst, error)) { if (!LowerRegisterOperand(decoded.dst, inst.dst, error)) {
return false; return false;
} }
+7 -3
View File
@@ -144,9 +144,10 @@ struct ExportInfo {
}; };
struct InputInfo { struct InputInfo {
uint32_t attr = 0; uint32_t attr = 0;
uint32_t chan = 0; uint32_t chan = 0;
uint32_t component_count = 1; uint32_t component_count = 1;
uint32_t interpolation_mode = 3;
bool operator==(const InputInfo& other) const = default; bool operator==(const InputInfo& other) const = default;
}; };
@@ -272,6 +273,8 @@ enum class StageInputKind {
InstanceIndex, InstanceIndex,
FragCoord, FragCoord,
FrontFacing, FrontFacing,
BaryCoordSmooth,
BaryCoordNoPerspective,
WorkgroupId, WorkgroupId,
LocalInvocationId, LocalInvocationId,
LocalInvocationIndex, LocalInvocationIndex,
@@ -286,6 +289,7 @@ struct StageInput {
uint32_t location = 0; uint32_t location = 0;
uint32_t component_count = 1; uint32_t component_count = 1;
std::string debug_name; std::string debug_name;
bool per_vertex = false;
bool operator==(const StageInput& other) const = default; bool operator==(const StageInput& other) const = default;
}; };
@@ -274,6 +274,7 @@ VALUE_OPCODE(ImageAtomicAnd32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicOr32, U32, ImageResource, ImageAddress, U32, U1) VALUE_OPCODE(ImageAtomicOr32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(ImageAtomicXor32, U32, ImageResource, ImageAddress, U32, U1) VALUE_OPCODE(ImageAtomicXor32, U32, ImageResource, ImageAddress, U32, U1)
VALUE_OPCODE(GetAttribute, U32, U32, U32) VALUE_OPCODE(GetAttribute, U32, U32, U32)
VALUE_OPCODE(GetInterpolationParameter, U32, U32, U32, U32)
VALUE_OPCODE(SetAttribute, Void, U32x4, U1) VALUE_OPCODE(SetAttribute, Void, U32x4, U1)
VALUE_OPCODE(ControlNop, Void) VALUE_OPCODE(ControlNop, Void)
@@ -9,14 +9,15 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
namespace { namespace {
void AddInput(ShaderInfo& info, StageInputKind kind, uint32_t location, uint32_t components, void AddInput(ShaderInfo& info, StageInputKind kind, uint32_t location, uint32_t components,
std::string name) { std::string name, bool per_vertex = false) {
const auto input = std::find_if(info.inputs.begin(), info.inputs.end(), [=](const auto& value) { const auto input = std::find_if(info.inputs.begin(), info.inputs.end(), [=](const auto& value) {
return value.kind == kind && value.location == location; return value.kind == kind && value.location == location;
}); });
if (input == info.inputs.end()) { if (input == info.inputs.end()) {
info.inputs.push_back({kind, location, components, std::move(name)}); info.inputs.push_back({kind, location, components, std::move(name), per_vertex});
} else { } else {
input->component_count = std::max(input->component_count, components); input->component_count = std::max(input->component_count, components);
input->per_vertex = input->per_vertex || per_vertex;
} }
} }
@@ -79,6 +80,24 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
static_cast<uint32_t>(options.vertex->resources_num))) { static_cast<uint32_t>(options.vertex->resources_num))) {
return Fail("vertex input reference is out of range"); return Fail("vertex input reference is out of range");
} }
if (program.stage == ShaderType::Pixel &&
(inst.Arg(1).U32() >= 4u ||
inst.Arg(0).U32() >= options.pixel->input_num)) {
return Fail("pixel input reference is out of range");
}
break;
}
case ValueOpcode::GetInterpolationParameter: {
if (program.stage != ShaderType::Pixel || !inst.Arg(0).IsImmediate() ||
inst.Arg(0).GetType() != Type::U32 || !inst.Arg(1).IsImmediate() ||
inst.Arg(1).GetType() != Type::U32 || !inst.Arg(2).IsImmediate() ||
inst.Arg(2).GetType() != Type::U32) {
return Fail("interpolation parameter reference is invalid");
}
if (inst.Arg(0).U32() >= options.pixel->input_num || inst.Arg(1).U32() >= 4u ||
inst.Arg(2).U32() >= 3u) {
return Fail("interpolation parameter reference is out of range");
}
break; break;
} }
case ValueOpcode::GetBuiltin: { case ValueOpcode::GetBuiltin: {
@@ -102,6 +121,12 @@ bool ValidateValueReferences(const Program& program, const ShaderInfoOptions& op
return Fail("typed fragment-coordinate component is out of range"); return Fail("typed fragment-coordinate component is out of range");
} }
break; break;
case StageInputKind::BaryCoordSmooth:
case StageInputKind::BaryCoordNoPerspective:
if (component >= 2u) {
return Fail("typed barycentric component is out of range");
}
break;
case StageInputKind::WorkgroupId: case StageInputKind::WorkgroupId:
case StageInputKind::LocalInvocationId: case StageInputKind::LocalInvocationId:
case StageInputKind::GlobalInvocationId: case StageInputKind::GlobalInvocationId:
@@ -150,15 +175,40 @@ void CollectVertexInputs(const Program& program, const ShaderVertexInputInfo* ve
} }
} }
void CollectPixelInputs(const ShaderPixelInputInfo* pixel, ShaderInfo& info) { void CollectPixelInputs(const Program& program, const ShaderPixelInputInfo* pixel,
ShaderInfo& info) {
if (pixel->HasPositionInput()) { if (pixel->HasPositionInput()) {
AddInput(info, StageInputKind::FragCoord, 0, 4, "gl_FragCoord"); AddInput(info, StageInputKind::FragCoord, 0, 4, "gl_FragCoord");
} }
if (pixel->ps_front_face) { if (pixel->ps_front_face) {
AddInput(info, StageInputKind::FrontFacing, 0, 1, "gl_FrontFacing"); AddInput(info, StageInputKind::FrontFacing, 0, 1, "gl_FrontFacing");
} }
std::array<bool, 32> per_vertex {};
std::array<bool, 32> interpolated {};
for (const auto* block: program.values->blocks) {
for (const auto& inst: *block) {
if (inst.GetOpcode() == ValueOpcode::GetAttribute) {
interpolated[inst.Arg(0).U32()] = true;
} else if (inst.GetOpcode() == ValueOpcode::GetInterpolationParameter) {
const auto input = inst.Arg(0).U32();
const auto mode = inst.Arg(2).U32();
per_vertex[input] =
per_vertex[input] || mode < 2u || !ShaderPixelParameterIsFlat(*pixel, input);
}
}
}
for (uint32_t input = 0; input < pixel->input_num; input++) { for (uint32_t input = 0; input < pixel->input_num; input++) {
AddInput(info, StageInputKind::Parameter, input, 4, fmt::format("in_param_{}", input)); AddInput(info, StageInputKind::Parameter, input, 4, fmt::format("in_param_{}", input),
per_vertex[input]);
}
for (uint32_t input = 0; input < pixel->input_num; input++) {
if (interpolated[input] && per_vertex[input]) {
const auto kind = pixel->ps_no_perspective ? StageInputKind::BaryCoordNoPerspective
: StageInputKind::BaryCoordSmooth;
AddInput(info, kind, 0, 3,
pixel->ps_no_perspective ? "gl_BaryCoordNoPerspKHR" : "gl_BaryCoordKHR");
break;
}
} }
} }
@@ -195,6 +245,12 @@ void CollectBuiltinInputs(const Program& program, ShaderInfo& info) {
case StageInputKind::FrontFacing: case StageInputKind::FrontFacing:
AddInput(info, kind, 0, 1, "gl_FrontFacing"); AddInput(info, kind, 0, 1, "gl_FrontFacing");
break; break;
case StageInputKind::BaryCoordSmooth:
AddInput(info, kind, 0, 3, "gl_BaryCoordKHR");
break;
case StageInputKind::BaryCoordNoPerspective:
AddInput(info, kind, 0, 3, "gl_BaryCoordNoPerspKHR");
break;
case StageInputKind::WorkgroupId: case StageInputKind::WorkgroupId:
AddInput(info, kind, 0, 3, "gl_WorkGroupID"); AddInput(info, kind, 0, 3, "gl_WorkGroupID");
break; break;
@@ -287,7 +343,7 @@ bool CollectShaderInfo(Program& program, const ShaderInfoOptions& options, std::
}); });
switch (program.stage) { switch (program.stage) {
case ShaderType::Vertex: CollectVertexInputs(program, options.vertex, next); break; case ShaderType::Vertex: CollectVertexInputs(program, options.vertex, next); break;
case ShaderType::Pixel: CollectPixelInputs(options.pixel, next); break; case ShaderType::Pixel: CollectPixelInputs(program, options.pixel, next); break;
case ShaderType::Compute: CollectComputeInputs(options.compute, next); break; case ShaderType::Compute: CollectComputeInputs(options.compute, next); break;
default: return false; default: return false;
} }
+24 -5
View File
@@ -85,7 +85,12 @@ uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
} }
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) { bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input) {
return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0; return input < info.input_num && (info.interpolator_settings[input] & PsInputFlatShade) != 0 &&
!ShaderPixelParameterIsCustom(info, input);
}
bool ShaderPixelParameterIsCustom(const ShaderPixelInputInfo& info, uint32_t input) {
return input < 32u && (info.custom_interpolation_mask & (1u << input)) != 0;
} }
struct ShaderBinaryInfo { struct ShaderBinaryInfo {
@@ -854,9 +859,18 @@ static void ShaderGetStaticInputInfoPS(
// SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control // SPI_PS_IN_CONTROL.NUM_INTERP occupies bits 5:0. Keep the remaining control
// flags in the hardware state and extract only the input count here. // flags in the hardware state and extract only the input count here.
ps_info.input_num = sh.ps_in_control & 0x3fu; ps_info.input_num = sh.ps_in_control & 0x3fu;
ps_info.ps_system_input_base = ShaderCalcPsSystemInputBase(sh); ps_info.ps_system_input_base = ShaderCalcPsSystemInputBase(sh);
const uint32_t active_inputs = sh.ps_input_ena & sh.ps_input_addr; const uint32_t active_inputs = sh.ps_input_ena & sh.ps_input_addr;
if ((active_inputs & 0x00000002u) != 0) {
ps_info.ps_perspective_center_vgpr = (active_inputs & 0x00000001u) != 0 ? 2u : 0u;
}
for (uint32_t i = 0; i < data.num_input_semantics && i < ps_info.input_num && i < 32u; i++) {
const auto& semantic = data.input_semantics[i];
if (semantic.is_custom != 0 && semantic.is_f16 == 0) {
ps_info.custom_interpolation_mask |= 1u << i;
}
}
ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0; ps_info.ps_pos_x = (active_inputs & 0x00000100u) != 0;
ps_info.ps_pos_y = (active_inputs & 0x00000200u) != 0; ps_info.ps_pos_y = (active_inputs & 0x00000200u) != 0;
ps_info.ps_pos_xy = ps_info.ps_pos_x && ps_info.ps_pos_y; ps_info.ps_pos_xy = ps_info.ps_pos_x && ps_info.ps_pos_y;
@@ -1297,6 +1311,8 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
LOGF("\t input_num = %u\n" LOGF("\t input_num = %u\n"
"\t ps_system_input_base = %u\n" "\t ps_system_input_base = %u\n"
"\t custom_interpolation_mask = 0x%08" PRIx32 "\n"
"\t ps_perspective_center_vgpr = %" PRIu32 "\n"
"\t ps_pos_x = %s\n" "\t ps_pos_x = %s\n"
"\t ps_pos_y = %s\n" "\t ps_pos_y = %s\n"
"\t ps_pos_z = %s\n" "\t ps_pos_z = %s\n"
@@ -1307,7 +1323,8 @@ void ShaderDbgDumpInputInfo(const ShaderPixelInputInfo& info) {
"\t ps_pixel_kill_enable = %s\n" "\t ps_pixel_kill_enable = %s\n"
"\t ps_early_z = %s\n" "\t ps_early_z = %s\n"
"\t ps_execute_on_noop = %s\n", "\t ps_execute_on_noop = %s\n",
info.input_num, info.ps_system_input_base, info.ps_pos_x ? "true" : "false", info.input_num, info.ps_system_input_base, info.custom_interpolation_mask,
info.ps_perspective_center_vgpr, info.ps_pos_x ? "true" : "false",
info.ps_pos_y ? "true" : "false", info.ps_pos_z ? "true" : "false", info.ps_pos_y ? "true" : "false", info.ps_pos_z ? "true" : "false",
info.ps_pos_w ? "true" : "false", info.ps_front_face ? "true" : "false", info.ps_pos_w ? "true" : "false", info.ps_front_face ? "true" : "false",
info.ps_sample_shading ? "true" : "false", info.ps_no_perspective ? "true" : "false", info.ps_sample_shading ? "true" : "false", info.ps_no_perspective ? "true" : "false",
@@ -1657,6 +1674,8 @@ ShaderId ShaderGetIdPS(const HW::PixelShaderInfo& regs, const ShaderPixelInputIn
ret.ids.push_back(input_info.scratch_size_dwords); ret.ids.push_back(input_info.scratch_size_dwords);
ret.ids.push_back(input_info.input_num); ret.ids.push_back(input_info.input_num);
ret.ids.push_back(input_info.ps_system_input_base); ret.ids.push_back(input_info.ps_system_input_base);
ret.ids.push_back(input_info.custom_interpolation_mask);
ret.ids.push_back(input_info.ps_perspective_center_vgpr);
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_x)); ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_x));
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_y)); ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_y));
ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z)); ret.ids.push_back(static_cast<uint32_t>(input_info.ps_pos_z));
+3
View File
@@ -117,6 +117,8 @@ struct ShaderPixelInputInfo {
uint32_t interpolator_settings[32] = {0}; uint32_t interpolator_settings[32] = {0};
uint32_t input_num = 0; uint32_t input_num = 0;
uint32_t ps_system_input_base = 0; uint32_t ps_system_input_base = 0;
uint32_t custom_interpolation_mask = 0;
uint32_t ps_perspective_center_vgpr = UINT32_MAX;
uint8_t target_output_mode[8] = {}; uint8_t target_output_mode[8] = {};
std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {}; std::array<Prospero::ColorComponentMapping, 8> target_export_mapping = {};
uint32_t mrt_output_mask = 0; uint32_t mrt_output_mask = 0;
@@ -150,6 +152,7 @@ uint32_t ShaderPixelParameterMappedLocation(const ShaderPixelInputInfo& info, ui
uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info, uint32_t ShaderPixelParameterLocation(const ShaderPixelInputInfo& info,
std::span<const uint32_t> active_inputs, uint32_t input); std::span<const uint32_t> active_inputs, uint32_t input);
bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input); bool ShaderPixelParameterIsFlat(const ShaderPixelInputInfo& info, uint32_t input);
bool ShaderPixelParameterIsCustom(const ShaderPixelInputInfo& info, uint32_t input);
struct ShaderSharp { struct ShaderSharp {
uint16_t offset_dw : 15; uint16_t offset_dw : 15;
+128
View File
@@ -5377,6 +5377,133 @@ void TestNewShaderRecompilerVintrpLowering() {
CheckSpirvBinaryValidates(no_perspective_result.spirv); CheckSpirvBinaryValidates(no_perspective_result.spirv);
} }
void TestCustomVintrpMovLowering() {
const uint32_t shader[] = {
EncodeVintrp(2, 12, 0, 3, 2), EncodeVintrp(2, 13, 0, 3, 0),
EncodeVintrp(2, 14, 0, 3, 1), EncodeVop2(0x03, 15, 12 + 256, 0),
EncodeVop2(0x03, 16, 13 + 256, 1), EncodeExp0(0x00, 0xf),
EncodeExp1(15, 16, 14, 12), 0xbf810000u,
};
ShaderPixelInputInfo custom_ps_info{};
custom_ps_info.input_num = 1;
custom_ps_info.ps_system_input_base = 2;
custom_ps_info.custom_interpolation_mask = 1;
custom_ps_info.ps_perspective_center_vgpr = 0;
SetIdentityInterpolatorSettings(&custom_ps_info);
custom_ps_info.interpolator_settings[0] = 0x00000420u;
auto options = MakeCompileOptions(ShaderType::Pixel);
options.input_info.pixel = &custom_ps_info;
options.dump_ir = true;
ShaderRecompiler::CompileResult custom_result;
std::string error;
Check(ShaderRecompiler::TryRecompile(shader, options, custom_result, &error),
error.c_str());
Check(Common::ContainsStr(
custom_result.ir_dump,
"GetInterpolationParameter 0x00000000, 0x00000003, 0x00000000"),
"custom VINTRP did not preserve P10 mode");
Check(Common::ContainsStr(
custom_result.ir_dump,
"GetInterpolationParameter 0x00000000, 0x00000003, 0x00000001"),
"custom VINTRP did not preserve P20 mode");
Check(Common::ContainsStr(
custom_result.ir_dump,
"GetInterpolationParameter 0x00000000, 0x00000003, 0x00000002"),
"custom VINTRP did not preserve P0 mode");
Check(SpirvContainsCapability(custom_result.spirv, 5284u),
"custom VINTRP did not enable FragmentBarycentricKHR");
Check(SpirvHasDecorationValueWithDecoration(custom_result.spirv, 30u, 0u,
5285u),
"custom VINTRP input did not emit PerVertexKHR");
Check(
!SpirvHasDecorationValueWithDecoration(custom_result.spirv, 30u, 0u, 14u),
"custom VINTRP input was misclassified as flat");
Check(SpirvHasDecorationValue(custom_result.spirv, 11u, 5286u),
"perspective-center VGPRs did not use BaryCoordKHR");
const auto source = DisassembleSpirvBinary(custom_result.spirv);
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"in_param_0 %uint_0 %uint_3"),
"custom VINTRP P0 did not select vertex 0");
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"in_param_0 %uint_1 %uint_3"),
"custom VINTRP P10 did not select vertex 1");
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"in_param_0 %uint_2 %uint_3"),
"custom VINTRP P20 did not select vertex 2");
Check(!Common::ContainsStr(source, "OpFSub"),
"custom VINTRP incorrectly applied hardware delta subtraction");
Check(SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"gl_BaryCoordKHR %uint_1") &&
SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"gl_BaryCoordKHR %uint_2") &&
!SpirvSourceHasInstructionUsing(source, "OpAccessChain",
"gl_BaryCoordKHR %uint_0"),
"guest perspective-center I/J did not map to BaryCoordKHR Y/Z");
CheckSpirvBinaryValidates(custom_result.spirv);
custom_ps_info.custom_interpolation_mask = 0;
custom_ps_info.interpolator_settings[0] = 0;
ShaderRecompiler::CompileResult standard_result;
Check(
ShaderRecompiler::TryRecompile(shader, options, standard_result, &error),
error.c_str());
Check(SpirvInstructionOpcodeCount(standard_result.spirv, 131u) == 2u,
"standard VINTRP P10/P20 did not subtract P0 exactly once each");
CheckSpirvBinaryValidates(standard_result.spirv);
const uint32_t flat_shader[] = {
EncodeVintrp(2, 12, 0, 3, 2),
EncodeExp0(0x00, 0x1),
EncodeExp1(12, 0, 0, 0),
0xbf810000u,
};
ShaderPixelInputInfo flat_ps_info{};
flat_ps_info.input_num = 1;
flat_ps_info.interpolator_settings[0] = 0x00000400u;
options.input_info.pixel = &flat_ps_info;
ShaderRecompiler::CompileResult flat_result;
Check(
ShaderRecompiler::TryRecompile(flat_shader, options, flat_result, &error),
error.c_str());
Check(!SpirvContainsCapability(flat_result.spirv, 5284u),
"flat P0 unexpectedly required fragment barycentric support");
Check(SpirvHasDecorationValueWithDecoration(flat_result.spirv, 30u, 0u, 14u),
"flat P0 did not retain ordinary flat interpolation");
CheckSpirvBinaryValidates(flat_result.spirv);
const uint32_t mixed_shader[] = {
EncodeVintrp(0, 12, 0, 3, 0),
EncodeVintrp(1, 12, 0, 3, 0),
EncodeVintrp(2, 13, 0, 3, 2),
EncodeVop2(0x03, 14, 12 + 256, 13),
EncodeExp0(0x00, 0x1),
EncodeExp1(14, 0, 0, 0),
0xbf810000u,
};
ShaderPixelInputInfo mixed_ps_info{};
mixed_ps_info.input_num = 1;
options.input_info.pixel = &mixed_ps_info;
ShaderRecompiler::CompileResult mixed_result;
Check(ShaderRecompiler::TryRecompile(mixed_shader, options, mixed_result,
&error),
error.c_str());
Check(SpirvHasDecorationValue(mixed_result.spirv, 11u, 5286u) &&
SpirvInstructionOpcodeCount(mixed_result.spirv, 133u) == 3u,
"mixed ordinary/P0 input was not interpolated from per-vertex values");
CheckSpirvBinaryValidates(mixed_result.spirv);
mixed_ps_info.ps_no_perspective = true;
ShaderRecompiler::CompileResult mixed_linear_result;
Check(ShaderRecompiler::TryRecompile(mixed_shader, options,
mixed_linear_result, &error),
error.c_str());
Check(SpirvHasDecorationValue(mixed_linear_result.spirv, 11u, 5287u),
"mixed linear input did not use BaryCoordNoPerspKHR");
CheckSpirvBinaryValidates(mixed_linear_result.spirv);
}
void TestPsInputCountRegisterDecode() { void TestPsInputCountRegisterDecode() {
HW::Context context; HW::Context context;
// NUM_INTERP is 3 while bit 14 is an independent control flag that must be // NUM_INTERP is 3 while bit 14 is an independent control flag that must be
@@ -11623,6 +11750,7 @@ int main() {
TestLowerProgramResetsAnalysisState(); TestLowerProgramResetsAnalysisState();
TestNewShaderRecompilerNativeBindingPlan(); TestNewShaderRecompilerNativeBindingPlan();
TestNewShaderRecompilerStageInputInfo(); TestNewShaderRecompilerStageInputInfo();
TestCustomVintrpMovLowering();
TestGraphicsCreateInterpolantMapping(); TestGraphicsCreateInterpolantMapping();
TestNewShaderRecompilerPixelPipelineEntry(); TestNewShaderRecompilerPixelPipelineEntry();
TestComputeLdsAllocationIdentity(); TestComputeLdsAllocationIdentity();