mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
graphics: support RDNA2 custom pixel interpolation
Co-authored-by: Senaxx <Senaxx@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user