diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 7f022d234..98f2195c5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -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"); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 2d64bafb4..6df8f74fd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp @@ -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 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); diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index 70a44cbe4..440f80fa9 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp @@ -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)}; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 25d7c4773..131b475fc 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -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() { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 79ad2b6a1..4daba8903 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -245,7 +245,9 @@ public: boost::container::small_vector interfaces; Id output_position{}; + Id output_point_size{}; Id output_layer{}; + Id output_viewport_index{}; Id primitive_id{}; Id vertex_index{}; Id instance_id{}; diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 6ce4395f2..689264c6a 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -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{}; diff --git a/src/shader_recompiler/ir/attribute.cpp b/src/shader_recompiler/ir/attribute.cpp index 1572ef615..388f8de8c 100644 --- a/src/shader_recompiler/ir/attribute.cpp +++ b/src/shader_recompiler/ir/attribute.cpp @@ -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; } diff --git a/src/shader_recompiler/ir/attribute.h b/src/shader_recompiler/ir/attribute.h index 42466e5bb..28950ab52 100644 --- a/src/shader_recompiler/ir/attribute.h +++ b/src/shader_recompiler/ir/attribute.h @@ -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, }; diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index 397b196f9..a7108a5ef 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -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. diff --git a/src/shader_recompiler/ir/position.h b/src/shader_recompiler/ir/position.h index 41e27c5fe..00da0a68a 100644 --- a/src/shader_recompiler/ir/position.h +++ b/src/shader_recompiler/ir/position.h @@ -3,6 +3,7 @@ #pragma once +#include #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) { + // 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) { - // 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", diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index a204afd36..f532dcbad 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -54,12 +54,12 @@ struct ExportRuntimeInfo { enum class Output : u8 { None, - PointSprite, + PointSize, EdgeFlag, KillFlag, GsCutFlag, - GsMrtIndex, - GsVpIndex, + RenderTargetIndex, + ViewportIndex, CullDist0, CullDist1, CullDist2, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index bcb5062bb..3d8d5ff58 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -42,14 +42,15 @@ static u32 MapOutputs(std::span 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) {