shader_recompiler: Support PointSize and ViewportIndex attributes. (#3541)

This commit is contained in:
squidbus
2025-09-06 20:11:46 -07:00
committed by GitHub
parent 081d52e615
commit 38e6dd49b1
12 changed files with 83 additions and 36 deletions

View File

@@ -272,9 +272,6 @@ void SetupCapabilities(const Info& info, const Profile& profile, const RuntimeIn
if (info.has_image_query) {
ctx.AddCapability(spv::Capability::ImageQuery);
}
if (info.has_layer_output) {
ctx.AddCapability(spv::Capability::ShaderLayer);
}
if ((info.uses_image_atomic_float_min_max && profile.supports_image_fp32_atomic_min_max) ||
(info.uses_buffer_atomic_float_min_max && profile.supports_buffer_fp32_atomic_min_max)) {
ctx.AddExtension("SPV_EXT_shader_atomic_float_min_max");
@@ -312,6 +309,17 @@ void SetupCapabilities(const Info& info, const Profile& profile, const RuntimeIn
if (stage == LogicalStage::TessellationControl || stage == LogicalStage::TessellationEval) {
ctx.AddCapability(spv::Capability::Tessellation);
}
if (stage == LogicalStage::Vertex || stage == LogicalStage::TessellationControl ||
stage == LogicalStage::TessellationEval) {
if (info.has_layer_output) {
ctx.AddCapability(spv::Capability::ShaderLayer);
}
if (info.has_viewport_index_output) {
ctx.AddCapability(spv::Capability::ShaderViewportIndex);
}
} else if (stage == LogicalStage::Geometry && info.has_viewport_index_output) {
ctx.AddCapability(spv::Capability::MultiViewport);
}
if (info.uses_dma) {
ctx.AddCapability(spv::Capability::PhysicalStorageBufferAddresses);
ctx.AddExtension("SPV_KHR_physical_storage_buffer");

View File

@@ -49,8 +49,12 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) {
return ctx.OpAccessChain(ctx.output_f32, ctx.clip_distances, ctx.ConstU32(element));
case IR::Attribute::CullDistance:
return ctx.OpAccessChain(ctx.output_f32, ctx.cull_distances, ctx.ConstU32(element));
case IR::Attribute::RenderTargetId:
case IR::Attribute::PointSize:
return ctx.output_point_size;
case IR::Attribute::RenderTargetIndex:
return ctx.output_layer;
case IR::Attribute::ViewportIndex:
return ctx.output_viewport_index;
case IR::Attribute::Depth:
return ctx.frag_depth;
default:
@@ -74,9 +78,10 @@ std::pair<Id, bool> OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr
case IR::Attribute::ClipDistance:
case IR::Attribute::CullDistance:
case IR::Attribute::Depth:
case IR::Attribute::PointSize:
return {ctx.F32[1], false};
case IR::Attribute::RenderTargetId:
case IR::Attribute::ViewportId:
case IR::Attribute::RenderTargetIndex:
case IR::Attribute::ViewportIndex:
return {ctx.S32[1], true};
default:
UNREACHABLE_MSG("Write attribute {}", attr);

View File

@@ -28,6 +28,9 @@ void ConvertDepthMode(EmitContext& ctx) {
}
void ConvertPositionToClipSpace(EmitContext& ctx) {
ASSERT_MSG(!ctx.info.has_viewport_index_output,
"Multi-viewport with shader clip space conversion not yet implemented.");
const Id type{ctx.F32[1]};
Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)};
const Id x{ctx.OpCompositeExtract(type, position, 0u)};

View File

@@ -555,9 +555,17 @@ void EmitContext::DefineVertexBlock() {
cull_distances = DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output,
initializer);
}
if (info.stores.GetAny(IR::Attribute::RenderTargetId)) {
if (info.stores.GetAny(IR::Attribute::PointSize)) {
output_point_size =
DefineVariable(F32[1], spv::BuiltIn::PointSize, spv::StorageClass::Output);
}
if (info.stores.GetAny(IR::Attribute::RenderTargetIndex)) {
output_layer = DefineVariable(S32[1], spv::BuiltIn::Layer, spv::StorageClass::Output);
}
if (info.stores.GetAny(IR::Attribute::ViewportIndex)) {
output_viewport_index =
DefineVariable(S32[1], spv::BuiltIn::ViewportIndex, spv::StorageClass::Output);
}
}
void EmitContext::DefineOutputs() {

View File

@@ -245,7 +245,9 @@ public:
boost::container::small_vector<Id, 16> interfaces;
Id output_position{};
Id output_point_size{};
Id output_layer{};
Id output_viewport_index{};
Id primitive_id{};
Id vertex_index{};
Id instance_id{};

View File

@@ -211,6 +211,7 @@ struct Info {
bool has_image_gather{};
bool has_image_query{};
bool has_layer_output{};
bool has_viewport_index_output{};
bool uses_buffer_atomic_float_min_max{};
bool uses_image_atomic_float_min_max{};
bool uses_lane_id{};

View File

@@ -104,10 +104,10 @@ std::string NameOf(Attribute attribute) {
return "ClipDistanace";
case Attribute::CullDistance:
return "CullDistance";
case Attribute::RenderTargetId:
return "RenderTargetId";
case Attribute::ViewportId:
return "ViewportId";
case Attribute::RenderTargetIndex:
return "RenderTargetIndex";
case Attribute::ViewportIndex:
return "ViewportIndex";
case Attribute::VertexId:
return "VertexId";
case Attribute::PrimitiveId:
@@ -158,6 +158,8 @@ std::string NameOf(Attribute attribute) {
return "PackedHullInvocationInfo";
case Attribute::TessFactorsBufferBase:
return "TessFactorsBufferBase";
case Attribute::PointSize:
return "PointSize";
default:
break;
}

View File

@@ -60,8 +60,8 @@ enum class Attribute : u64 {
// System values
ClipDistance = 64,
CullDistance = 65,
RenderTargetId = 66,
ViewportId = 67,
RenderTargetIndex = 66,
ViewportIndex = 67,
VertexId = 68,
PrimitiveId = 69,
InstanceId = 70,
@@ -87,6 +87,7 @@ enum class Attribute : u64 {
PackedHullInvocationInfo = 90, // contains patch id within the VGT and invocation ID
OffChipLdsBase = 91,
TessFactorsBufferBase = 92,
PointSize = 93,
Max,
};

View File

@@ -160,9 +160,12 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) {
}
}
if (info.stores.GetAny(IR::Attribute::RenderTargetId)) {
if (info.stores.GetAny(IR::Attribute::RenderTargetIndex)) {
info.has_layer_output = true;
}
if (info.stores.GetAny(IR::Attribute::ViewportIndex)) {
info.has_viewport_index_output = true;
}
// In case Flatbuf has not already been bound by IR and is needed
// to query buffer sizes, bind it now.

View File

@@ -3,6 +3,7 @@
#pragma once
#include <magic_enum/magic_enum.hpp>
#include "common/logging/log.h"
#include "shader_recompiler/ir/ir_emitter.h"
#include "shader_recompiler/runtime_info.h"
@@ -17,8 +18,26 @@ inline void ExportPosition(IREmitter& ir, const StageRuntimeInfo& stage, Attribu
ir.SetAttribute(attribute, value, comp);
return;
}
const u32 index = u32(attribute) - u32(Attribute::Position1);
const auto output = stage.outputs[index][comp];
if constexpr (std::is_same_v<StageRuntimeInfo, VertexRuntimeInfo>) {
// Certain outputs are supposed to be set by the last pre-rasterization stage. We don't
// currently have a mechanism for passing these on when emulating rect/quad lists using
// tessellation, which comes after, so just ignore the export for now. Note that this
// only matters for vertex shaders, as geometry shaders come last in pre-rasterization.
const auto last_stage_required = output == Output::PointSize ||
output == Output::RenderTargetIndex ||
output == Output::ViewportIndex;
if (stage.tess_emulated_primitive && last_stage_required) {
LOG_WARNING(Render,
"{} is exported in vertex shader but tessellation-based primitive "
"emulation is active. Not implemented yet.",
magic_enum::enum_name(output));
return;
}
}
switch (output) {
case Output::ClipDist0:
case Output::ClipDist1:
@@ -44,20 +63,14 @@ inline void ExportPosition(IREmitter& ir, const StageRuntimeInfo& stage, Attribu
ir.SetAttribute(IR::Attribute::CullDistance, value, index);
break;
}
case Output::GsMrtIndex:
if constexpr (std::is_same_v<StageRuntimeInfo, VertexRuntimeInfo>) {
// When using tessellation, layer is supposed to be set by the tessellation evaluation
// stage. We don't currently have a mechanism for that when emulating rect/quad lists
// using tessellation, so just ignore the write for now. Note that this only matters
// for vertex shaders, as geometry shaders come last in the pre-rasterization stage.
if (stage.tess_emulated_primitive) {
LOG_WARNING(Render,
"Exporting Layer from a vertex shader when using tessellation-based "
"primitive emulation is currently unsupported.");
return;
}
}
ir.SetAttribute(IR::Attribute::RenderTargetId, value);
case Output::PointSize:
ir.SetAttribute(IR::Attribute::PointSize, value);
break;
case Output::RenderTargetIndex:
ir.SetAttribute(IR::Attribute::RenderTargetIndex, value);
break;
case Output::ViewportIndex:
ir.SetAttribute(IR::Attribute::ViewportIndex, value);
break;
case Output::None:
LOG_WARNING(Render_Recompiler, "The {} component of {} isn't mapped, skipping",

View File

@@ -54,12 +54,12 @@ struct ExportRuntimeInfo {
enum class Output : u8 {
None,
PointSprite,
PointSize,
EdgeFlag,
KillFlag,
GsCutFlag,
GsMrtIndex,
GsVpIndex,
RenderTargetIndex,
ViewportIndex,
CullDist0,
CullDist1,
CullDist2,

View File

@@ -42,14 +42,15 @@ static u32 MapOutputs(std::span<Shader::OutputMap, 3> outputs,
if (ctl.vs_out_misc_enable) {
auto& misc_vec = outputs[num_outputs++];
misc_vec[0] = ctl.use_vtx_point_size ? Output::PointSprite : Output::None;
misc_vec[0] = ctl.use_vtx_point_size ? Output::PointSize : Output::None;
misc_vec[1] = ctl.use_vtx_edge_flag
? Output::EdgeFlag
: (ctl.use_vtx_gs_cut_flag ? Output::GsCutFlag : Output::None);
misc_vec[2] = ctl.use_vtx_kill_flag
? Output::KillFlag
: (ctl.use_vtx_render_target_idx ? Output::GsMrtIndex : Output::None);
misc_vec[3] = ctl.use_vtx_viewport_idx ? Output::GsVpIndex : Output::None;
misc_vec[2] =
ctl.use_vtx_kill_flag
? Output::KillFlag
: (ctl.use_vtx_render_target_idx ? Output::RenderTargetIndex : Output::None);
misc_vec[3] = ctl.use_vtx_viewport_idx ? Output::ViewportIndex : Output::None;
}
if (ctl.vs_out_ccdist0_enable) {