diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp index ffe5cd116c..8c69aa643b 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp @@ -217,6 +217,22 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr, ctx.AddF32("{}={}{}.{};", inst, input_decorator, ctx.position_name, swizzle); break; } + case IR::Attribute::ClipDistance0: + case IR::Attribute::ClipDistance1: + case IR::Attribute::ClipDistance2: + case IR::Attribute::ClipDistance3: + case IR::Attribute::ClipDistance4: + case IR::Attribute::ClipDistance5: + case IR::Attribute::ClipDistance6: + case IR::Attribute::ClipDistance7: { + const u32 index{static_cast(attr) - static_cast(IR::Attribute::ClipDistance0)}; + if (IsInputArray(ctx.stage)) { + ctx.AddF32("{}=gl_in[{}].gl_ClipDistance[{}];", inst, vertex, index); + } else { + ctx.AddF32("{}=gl_ClipDistance[{}];", inst, index); + } + break; + } case IR::Attribute::PointSpriteS: case IR::Attribute::PointSpriteT: ctx.AddF32("{}=gl_PointCoord.{};", inst, swizzle); diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp index a2df6159fb..729e09eb4b 100644 --- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp +++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp @@ -306,9 +306,12 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile } else if (program.is_geometry_passthrough && !profile.support_geometry_shader_passthrough) { LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported"); } - header += fmt::format( - "layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];", - OutputPrimitive(program.output_topology), program.output_vertices); + header += fmt::format("layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;", + OutputPrimitive(program.output_topology), program.output_vertices); + if (program.info.loads.ClipDistances()) { + header += "float gl_ClipDistance[];"; + } + header += "}gl_in[];"; break; case Stage::Fragment: stage_name = "fs"; 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 28f9869e22..56b2c13fbd 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 @@ -367,6 +367,23 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face), ctx.OpBitcast(ctx.F32[1], ctx.Const((std::numeric_limits::max)())), ctx.f32_zero_value); + case IR::Attribute::ClipDistance0: + case IR::Attribute::ClipDistance1: + case IR::Attribute::ClipDistance2: + case IR::Attribute::ClipDistance3: + case IR::Attribute::ClipDistance4: + case IR::Attribute::ClipDistance5: + case IR::Attribute::ClipDistance6: + case IR::Attribute::ClipDistance7: { + const u32 base{static_cast(IR::Attribute::ClipDistance0)}; + const u32 index{static_cast(attr) - base}; + if (!ValidId(ctx.input_clip_distances) || index >= ctx.profile.max_user_clip_distances) { + return ctx.Const(1.0f); + } + const Id pointer{ + AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_clip_distances, ctx.Const(index))}; + return ctx.OpLoad(ctx.F32[1], pointer); + } case IR::Attribute::PointSpriteS: return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value)); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b562e82983..f6ed8b7f0f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1522,6 +1522,12 @@ void EmitContext::DefineInputs(const IR::Program& program) { } } } + if (stage == Stage::Geometry && info.loads.ClipDistances() && + profile.max_user_clip_distances > 0) { + const u32 used{(std::min)(profile.max_user_clip_distances, 8u)}; + const Id type{TypeArray(F32[1], Const(used))}; + input_clip_distances = DefineInput(*this, type, true, spv::BuiltIn::ClipDistance); + } if (loads[IR::Attribute::InstanceId]) { if (profile.support_vertex_instance_id) { instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index fb20966b67..16301f2d08 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -285,6 +285,7 @@ public: Id point_coord{}; Id tess_coord{}; Id clip_distances{}; + Id input_clip_distances{}; Id layer{}; Id viewport_index{}; Id viewport_mask{}; diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index ce4b67fc19..44635a1425 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -178,6 +178,20 @@ struct PassthroughVertices { u32 stride; }; +OutputTopology GetPassthroughTopology(InputTopology input_topology) { + switch (input_topology) { + case InputTopology::Points: + return OutputTopology::PointList; + case InputTopology::Lines: + case InputTopology::LinesAdjacency: + return OutputTopology::LineStrip; + case InputTopology::Triangles: + case InputTopology::TrianglesAdjacency: + return OutputTopology::TriangleStrip; + } + return OutputTopology::TriangleStrip; +} + PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { switch (input_topology) { case InputTopology::Points: @@ -239,6 +253,14 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, copy_vec4(attr); } + for (u32 j = 0; j < 8; j++) { + const IR::Attribute attr = IR::Attribute::ClipDistance0 + j; + if (!passthrough_mask[attr]) { + continue; + } + ir.SetAttribute(attr, ir.GetAttribute(attr, ir.Imm32(i)), ir.Imm32(0)); + } + if (passthrough_position) { // Assign position from input copy_vec4(IR::Attribute::PositionX); @@ -304,6 +326,7 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } -Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { +Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) { switch (topology) { - case Maxwell::PrimitiveTopology::Points: + case Shader::InputTopology::Points: return Shader::OutputTopology::PointList; - case Maxwell::PrimitiveTopology::LineStrip: + case Shader::InputTopology::Lines: + case Shader::InputTopology::LinesAdjacency: return Shader::OutputTopology::LineStrip; - default: + case Shader::InputTopology::Triangles: + case Shader::InputTopology::TrianglesAdjacency: return Shader::OutputTopology::TriangleStrip; } + return Shader::OutputTopology::TriangleStrip; } Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { @@ -92,6 +95,16 @@ Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology } } +Shader::InputTopology GeometryInputTopology(const GraphicsPipelineKey& key) { + if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) { + return MaxwellToInputTopology(key.gs_input_topology); + } + if (key.tessellation_primitive == Maxwell::Tessellation::DomainType::Isolines) { + return Shader::InputTopology::Lines; + } + return Shader::InputTopology::Triangles; +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -147,7 +160,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - info.input_topology = MaxwellToInputTopology(key.gs_input_topology); + info.input_topology = GeometryInputTopology(key); info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -476,11 +489,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( const bool is_emulated_stage = layer_source_program != nullptr && index == u32(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { - auto topology = MaxwellToOutputTopology(key.gs_input_topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.gs_input_topology)); + const auto input_topology = GeometryInputTopology(key); + programs[index] = GenerateGeometryPassthrough( + pools.inst, pools.block, host_info, *layer_source_program, + InputToOutputTopology(input_topology), input_topology); continue; } if (key.unique_hashes[index] == 0) { @@ -499,14 +511,14 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology)); + GeometryInputTopology(key)); total_storage_buffers += Shader::NumDescriptors(programs[index].info.storage_buffers_descriptors); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.gs_input_topology))}; + GeometryInputTopology(key))}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 606396529b..7fd27a7d83 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -63,7 +63,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 21; +constexpr u32 CACHE_VERSION = 22; constexpr size_t VULKAN_CACHE_FLUSH_PIPELINES = 128; constexpr size_t VULKAN_CACHE_FLUSH_MIN_SECONDS = 30; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; @@ -73,15 +73,18 @@ auto MakeSpan(Container& container) { return std::span(container.data(), container.size()); } -Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) { +Shader::OutputTopology InputToOutputTopology(Shader::InputTopology topology) { switch (topology) { - case Maxwell::PrimitiveTopology::Points: + case Shader::InputTopology::Points: return Shader::OutputTopology::PointList; - case Maxwell::PrimitiveTopology::LineStrip: + case Shader::InputTopology::Lines: + case Shader::InputTopology::LinesAdjacency: return Shader::OutputTopology::LineStrip; - default: + case Shader::InputTopology::Triangles: + case Shader::InputTopology::TrianglesAdjacency: return Shader::OutputTopology::TriangleStrip; } + return Shader::OutputTopology::TriangleStrip; } Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { @@ -172,6 +175,18 @@ Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology } } +Shader::InputTopology GeometryInputTopology(const GraphicsPipelineCacheKey& key) { + if (key.unique_hashes[static_cast(Maxwell::ShaderType::Tessellation)] == 0) { + return MaxwellToInputTopology(key.state.topology); + } + const auto domain{ + static_cast(key.state.tessellation_primitive.Value())}; + if (domain == Maxwell::Tessellation::DomainType::Isolines) { + return Shader::InputTopology::Lines; + } + return Shader::InputTopology::Triangles; +} + Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -290,7 +305,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - info.input_topology = MaxwellToInputTopology(key.state.topology); + info.input_topology = GeometryInputTopology(key); info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -786,11 +801,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( const bool is_emulated_stage = layer_source_program != nullptr && index == static_cast(Maxwell::ShaderType::Geometry); if (key.unique_hashes[index] == 0 && is_emulated_stage) { - auto topology = MaxwellToOutputTopology(key.state.topology); - programs[index] = - GenerateGeometryPassthrough(pools.inst, pools.block, host_info, - *layer_source_program, topology, - MaxwellToInputTopology(key.state.topology)); + const auto input_topology = GeometryInputTopology(key); + programs[index] = GenerateGeometryPassthrough( + pools.inst, pools.block, host_info, *layer_source_program, + InputToOutputTopology(input_topology), input_topology); continue; } if (key.unique_hashes[index] == 0) { @@ -804,12 +818,12 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology)); + GeometryInputTopology(key)); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, - MaxwellToInputTopology(key.state.topology))}; + GeometryInputTopology(key))}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); }