diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index c4c310586..27f23af6b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -271,6 +271,9 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct 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"); 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 f3a8c518c..370ff89cd 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 @@ -16,39 +16,6 @@ namespace Shader::Backend::SPIRV { namespace { -Id VsOutputAttrPointer(EmitContext& ctx, VsOutput output) { - switch (output) { - case VsOutput::ClipDist0: - case VsOutput::ClipDist1: - case VsOutput::ClipDist2: - case VsOutput::ClipDist3: - case VsOutput::ClipDist4: - case VsOutput::ClipDist5: - case VsOutput::ClipDist6: - case VsOutput::ClipDist7: { - const u32 index = u32(output) - u32(VsOutput::ClipDist0); - const Id clip_num{ctx.ConstU32(index)}; - ASSERT_MSG(Sirit::ValidId(ctx.clip_distances), "Clip distance used but not defined"); - return ctx.OpAccessChain(ctx.output_f32, ctx.clip_distances, clip_num); - } - case VsOutput::CullDist0: - case VsOutput::CullDist1: - case VsOutput::CullDist2: - case VsOutput::CullDist3: - case VsOutput::CullDist4: - case VsOutput::CullDist5: - case VsOutput::CullDist6: - case VsOutput::CullDist7: { - const u32 index = u32(output) - u32(VsOutput::CullDist0); - const Id cull_num{ctx.ConstU32(index)}; - ASSERT_MSG(Sirit::ValidId(ctx.cull_distances), "Cull distance used but not defined"); - return ctx.OpAccessChain(ctx.output_f32, ctx.cull_distances, cull_num); - } - default: - UNREACHABLE(); - } -} - Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { if (IR::IsParam(attr)) { const u32 attr_index{u32(attr) - u32(IR::Attribute::Param0)}; @@ -76,19 +43,18 @@ Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr, u32 element) { } } switch (attr) { - case IR::Attribute::Position0: { + case IR::Attribute::Position0: return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, ctx.ConstU32(element)); - } - case IR::Attribute::Position1: - case IR::Attribute::Position2: - case IR::Attribute::Position3: { - const u32 index = u32(attr) - u32(IR::Attribute::Position1); - return VsOutputAttrPointer(ctx, ctx.runtime_info.vs_info.outputs[index][element]); - } + case IR::Attribute::ClipDistance: + 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: + return ctx.output_layer; case IR::Attribute::Depth: return ctx.frag_depth; default: - throw NotImplementedException("Write attribute {}", attr); + UNREACHABLE_MSG("Write attribute {}", attr); } } @@ -109,13 +75,15 @@ std::pair OutputAttrComponentType(EmitContext& ctx, IR::Attribute attr } switch (attr) { case IR::Attribute::Position0: - case IR::Attribute::Position1: - case IR::Attribute::Position2: - case IR::Attribute::Position3: + case IR::Attribute::ClipDistance: + case IR::Attribute::CullDistance: case IR::Attribute::Depth: return {ctx.F32[1], false}; + case IR::Attribute::RenderTargetId: + case IR::Attribute::ViewportId: + return {ctx.S32[1], true}; default: - throw NotImplementedException("Write attribute {}", attr); + UNREACHABLE_MSG("Write attribute {}", attr); } } } // Anonymous namespace @@ -350,14 +318,10 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) { } void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 element) { - if (attr == IR::Attribute::Position1) { - LOG_WARNING(Render_Vulkan, "Ignoring pos1 export"); - return; - } const Id pointer{OutputAttrPointer(ctx, attr, element)}; - const auto component_type{OutputAttrComponentType(ctx, attr)}; - if (component_type.second) { - ctx.OpStore(pointer, ctx.OpBitcast(component_type.first, value)); + const auto [component_type, is_integer]{OutputAttrComponentType(ctx, attr)}; + if (is_integer) { + ctx.OpStore(pointer, ctx.OpBitcast(component_type, value)); } else { ctx.OpStore(pointer, value); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 6a731d05c..e50080823 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -556,23 +556,25 @@ void EmitContext::DefineInputs() { } } +void EmitContext::DefineVertexBlock() { + output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); + if (info.stores.GetAny(IR::Attribute::ClipDistance)) { + clip_distances = DefineVariable(TypeArray(F32[1], ConstU32(8U)), spv::BuiltIn::ClipDistance, + spv::StorageClass::Output); + } + if (info.stores.GetAny(IR::Attribute::CullDistance)) { + cull_distances = DefineVariable(TypeArray(F32[1], ConstU32(8U)), spv::BuiltIn::CullDistance, + spv::StorageClass::Output); + } + if (info.stores.GetAny(IR::Attribute::RenderTargetId)) { + output_layer = DefineVariable(S32[1], spv::BuiltIn::Layer, spv::StorageClass::Output); + } +} + void EmitContext::DefineOutputs() { switch (l_stage) { case LogicalStage::Vertex: { - // No point in defining builtin outputs (i.e. position) unless next stage is fragment? - // Might cause problems linking with tcs - - output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); - const bool has_extra_pos_stores = info.stores.Get(IR::Attribute::Position1) || - info.stores.Get(IR::Attribute::Position2) || - info.stores.Get(IR::Attribute::Position3); - if (has_extra_pos_stores) { - const Id type{TypeArray(F32[1], ConstU32(8U))}; - clip_distances = - DefineVariable(type, spv::BuiltIn::ClipDistance, spv::StorageClass::Output); - cull_distances = - DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output); - } + DefineVertexBlock(); if (stage == Shader::Stage::Local && runtime_info.ls_info.links_with_tcs) { const u32 num_attrs = Common::AlignUp(runtime_info.ls_info.ls_stride, 16) >> 4; if (num_attrs > 0) { @@ -632,17 +634,7 @@ void EmitContext::DefineOutputs() { break; } case LogicalStage::TessellationEval: { - output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); - const bool has_extra_pos_stores = info.stores.Get(IR::Attribute::Position1) || - info.stores.Get(IR::Attribute::Position2) || - info.stores.Get(IR::Attribute::Position3); - if (has_extra_pos_stores) { - const Id type{TypeArray(F32[1], ConstU32(8U))}; - clip_distances = - DefineVariable(type, spv::BuiltIn::ClipDistance, spv::StorageClass::Output); - cull_distances = - DefineVariable(type, spv::BuiltIn::CullDistance, spv::StorageClass::Output); - } + DefineVertexBlock(); for (u32 i = 0; i < IR::NumParams; i++) { const IR::Attribute param{IR::Attribute::Param0 + i}; if (!info.stores.GetAny(param)) { @@ -682,8 +674,7 @@ void EmitContext::DefineOutputs() { break; } case LogicalStage::Geometry: { - output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); - + DefineVertexBlock(); for (u32 attr_id = 0; attr_id < info.gs_copy_data.num_attrs; attr_id++) { const Id id{DefineOutput(F32[4], attr_id)}; Name(id, fmt::format("out_attr{}", attr_id)); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 28e9099d8..55ebbd95a 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -245,6 +245,7 @@ public: boost::container::small_vector interfaces; Id output_position{}; + Id output_layer{}; Id primitive_id{}; Id vertex_index{}; Id instance_id{}; @@ -383,6 +384,7 @@ private: void DefineArithmeticTypes(); void DefineInterfaces(); void DefineInputs(); + void DefineVertexBlock(); void DefineOutputs(); void DefinePushDataBlock(); void DefineBuffers(); diff --git a/src/shader_recompiler/frontend/translate/export.cpp b/src/shader_recompiler/frontend/translate/export.cpp index 8a99f38a9..8152e1906 100644 --- a/src/shader_recompiler/frontend/translate/export.cpp +++ b/src/shader_recompiler/frontend/translate/export.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "shader_recompiler/frontend/translate/translate.h" +#include "shader_recompiler/ir/position.h" #include "shader_recompiler/ir/reinterpret.h" #include "shader_recompiler/runtime_info.h" @@ -126,8 +127,10 @@ void Translator::ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U3 void Translator::ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { if (IsMrt(attribute)) { - ExportMrtUncompressed(attribute, comp, value); - return; + return ExportMrtUncompressed(attribute, comp, value); + } + if (IsPosition(attribute)) { + return IR::ExportPosition(ir, runtime_info.vs_info, attribute, comp, value); } ir.SetAttribute(attribute, value, comp); } diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 4b5ff827b..ae5257a98 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -317,6 +317,7 @@ private: const PsColorBuffer& color_buffer); void ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); void ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); + void ExportPosition(IR::Attribute attribute, u32 comp, const IR::F32& value); void ExportCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value); void ExportUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value); diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 9703643e8..16ce10352 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -215,6 +215,7 @@ struct Info { bool has_image_query{}; bool has_perspective_interp{}; bool has_linear_interp{}; + bool has_layer_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/passes/ring_access_elimination.cpp b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp index b292b41b9..e077e04f1 100644 --- a/src/shader_recompiler/ir/passes/ring_access_elimination.cpp +++ b/src/shader_recompiler/ir/passes/ring_access_elimination.cpp @@ -4,6 +4,7 @@ #include "common/assert.h" #include "shader_recompiler/ir/ir_emitter.h" #include "shader_recompiler/ir/opcodes.h" +#include "shader_recompiler/ir/position.h" #include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/reg.h" #include "shader_recompiler/recompiler.h" @@ -145,11 +146,12 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim ASSERT(it != info.gs_copy_data.attr_map.cend()); const auto& [attr, comp] = it->second; - inst.ReplaceOpcode(IR::Opcode::SetAttribute); - inst.ClearArgs(); - inst.SetArg(0, IR::Value{attr}); - inst.SetArg(1, data); - inst.SetArg(2, ir.Imm32(comp)); + inst.Invalidate(); + if (IsPosition(attr)) { + ExportPosition(ir, runtime_info.gs_info, attr, comp, data); + } else { + ir.SetAttribute(attr, data, comp); + } break; } default: 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 079827866..f6fd951f3 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -157,6 +157,10 @@ void CollectShaderInfoPass(IR::Program& program, const Profile& profile) { } } + if (info.stores.GetAny(IR::Attribute::RenderTargetId)) { + info.has_layer_output = true; + } + // In case Flatbuf has not already been bound by IR and is needed // to query buffer sizes, bind it now. if (!profile.supports_robust_buffer_access && !info.uses_dma) { diff --git a/src/shader_recompiler/ir/position.h b/src/shader_recompiler/ir/position.h new file mode 100644 index 000000000..0fdeb0eb8 --- /dev/null +++ b/src/shader_recompiler/ir/position.h @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "shader_recompiler/ir/ir_emitter.h" +#include "shader_recompiler/runtime_info.h" + +namespace Shader::IR { + +/// Maps special position export to builtin attribute stores +inline void ExportPosition(IREmitter& ir, const auto& stage, Attribute attribute, u32 comp, + const IR::F32& value) { + if (attribute == Attribute::Position0) { + ir.SetAttribute(attribute, value, comp); + return; + } + const u32 index = u32(attribute) - u32(Attribute::Position1); + const auto output = stage.outputs[index][comp]; + switch (output) { + case Output::ClipDist0: + case Output::ClipDist1: + case Output::ClipDist2: + case Output::ClipDist3: + case Output::ClipDist4: + case Output::ClipDist5: + case Output::ClipDist6: + case Output::ClipDist7: { + const u32 index = u32(output) - u32(Output::ClipDist0); + ir.SetAttribute(IR::Attribute::ClipDistance, value, index); + break; + } + case Output::CullDist0: + case Output::CullDist1: + case Output::CullDist2: + case Output::CullDist3: + case Output::CullDist4: + case Output::CullDist5: + case Output::CullDist6: + case Output::CullDist7: { + const u32 index = u32(output) - u32(Output::CullDist0); + ir.SetAttribute(IR::Attribute::CullDistance, value, index); + break; + } + case Output::GsMrtIndex: + ir.SetAttribute(IR::Attribute::RenderTargetId, value); + break; + default: + UNREACHABLE_MSG("Unhandled output {} on attribute {}", u32(output), u32(attribute)); + } +} + +} // namespace Shader::IR diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index 5a0408e2c..3ca01bac6 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -53,7 +53,7 @@ struct ExportRuntimeInfo { auto operator<=>(const ExportRuntimeInfo&) const noexcept = default; }; -enum class VsOutput : u8 { +enum class Output : u8 { None, PointSprite, EdgeFlag, @@ -78,11 +78,11 @@ enum class VsOutput : u8 { ClipDist6, ClipDist7, }; -using VsOutputMap = std::array; +using OutputMap = std::array; struct VertexRuntimeInfo { u32 num_outputs; - std::array outputs; + std::array outputs; bool emulate_depth_negative_one_to_one{}; bool clip_disable{}; // Domain @@ -143,6 +143,8 @@ struct HullRuntimeInfo { static constexpr auto GsMaxOutputStreams = 4u; using GsOutputPrimTypes = std::array; struct GeometryRuntimeInfo { + u32 num_outputs; + std::array outputs; u32 num_invocations{}; u32 output_vertices{}; u32 in_vertex_data_size{}; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 237fa202d..1b976ea24 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -397,6 +397,7 @@ bool Instance::CreateDevice() { .hostQueryReset = vk12_features.hostQueryReset, .timelineSemaphore = vk12_features.timelineSemaphore, .bufferDeviceAddress = vk12_features.bufferDeviceAddress, + .shaderOutputLayer = vk12_features.shaderOutputLayer, }, vk::PhysicalDeviceVulkan13Features{ .robustImageAccess = vk13_features.robustImageAccess, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7dd468f9a..e386b279d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -23,8 +23,8 @@ extern std::unique_ptr presenter; namespace Vulkan { using Shader::LogicalStage; +using Shader::Output; using Shader::Stage; -using Shader::VsOutput; constexpr static auto SpirvVersion1_6 = 0x00010600U; @@ -35,49 +35,55 @@ constexpr static std::array DescriptorHeapSizes = { vk::DescriptorPoolSize{vk::DescriptorType::eSampler, 1024}, }; -void GatherVertexOutputs(Shader::VertexRuntimeInfo& info, - const AmdGpu::Liverpool::VsOutputControl& ctl) { - const auto add_output = [&](VsOutput x, VsOutput y, VsOutput z, VsOutput w) { - if (x != VsOutput::None || y != VsOutput::None || z != VsOutput::None || - w != VsOutput::None) { - info.outputs[info.num_outputs++] = Shader::VsOutputMap{x, y, z, w}; - } - }; - // VS_OUT_MISC_VEC - add_output(ctl.use_vtx_point_size ? VsOutput::PointSprite : VsOutput::None, - ctl.use_vtx_edge_flag - ? VsOutput::EdgeFlag - : (ctl.use_vtx_gs_cut_flag ? VsOutput::GsCutFlag : VsOutput::None), - ctl.use_vtx_kill_flag - ? VsOutput::KillFlag - : (ctl.use_vtx_render_target_idx ? VsOutput::GsMrtIndex : VsOutput::None), - ctl.use_vtx_viewport_idx ? VsOutput::GsVpIndex : VsOutput::None); - // VS_OUT_CCDIST0 - add_output(ctl.IsClipDistEnabled(0) - ? VsOutput::ClipDist0 - : (ctl.IsCullDistEnabled(0) ? VsOutput::CullDist0 : VsOutput::None), - ctl.IsClipDistEnabled(1) - ? VsOutput::ClipDist1 - : (ctl.IsCullDistEnabled(1) ? VsOutput::CullDist1 : VsOutput::None), - ctl.IsClipDistEnabled(2) - ? VsOutput::ClipDist2 - : (ctl.IsCullDistEnabled(2) ? VsOutput::CullDist2 : VsOutput::None), - ctl.IsClipDistEnabled(3) - ? VsOutput::ClipDist3 - : (ctl.IsCullDistEnabled(3) ? VsOutput::CullDist3 : VsOutput::None)); - // VS_OUT_CCDIST1 - add_output(ctl.IsClipDistEnabled(4) - ? VsOutput::ClipDist4 - : (ctl.IsCullDistEnabled(4) ? VsOutput::CullDist4 : VsOutput::None), - ctl.IsClipDistEnabled(5) - ? VsOutput::ClipDist5 - : (ctl.IsCullDistEnabled(5) ? VsOutput::CullDist5 : VsOutput::None), - ctl.IsClipDistEnabled(6) - ? VsOutput::ClipDist6 - : (ctl.IsCullDistEnabled(6) ? VsOutput::CullDist6 : VsOutput::None), - ctl.IsClipDistEnabled(7) - ? VsOutput::ClipDist7 - : (ctl.IsCullDistEnabled(7) ? VsOutput::CullDist7 : VsOutput::None)); +u32 MapOutputs(std::span outputs, + const AmdGpu::Liverpool::VsOutputControl& ctl) { + u32 num_outputs = 0; + + 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[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; + } + + if (ctl.vs_out_ccdist0_enable) { + auto& ccdist0 = outputs[num_outputs++]; + ccdist0[0] = ctl.IsClipDistEnabled(0) + ? Output::ClipDist0 + : (ctl.IsCullDistEnabled(0) ? Output::CullDist0 : Output::None); + ccdist0[1] = ctl.IsClipDistEnabled(1) + ? Output::ClipDist1 + : (ctl.IsCullDistEnabled(1) ? Output::CullDist1 : Output::None); + ccdist0[2] = ctl.IsClipDistEnabled(2) + ? Output::ClipDist2 + : (ctl.IsCullDistEnabled(2) ? Output::CullDist2 : Output::None); + ccdist0[3] = ctl.IsClipDistEnabled(3) + ? Output::ClipDist3 + : (ctl.IsCullDistEnabled(3) ? Output::CullDist3 : Output::None); + } + + if (ctl.vs_out_ccdist1_enable) { + auto& ccdist1 = outputs[num_outputs++]; + ccdist1[0] = ctl.IsClipDistEnabled(4) + ? Output::ClipDist4 + : (ctl.IsCullDistEnabled(4) ? Output::CullDist4 : Output::None); + ccdist1[1] = ctl.IsClipDistEnabled(5) + ? Output::ClipDist5 + : (ctl.IsCullDistEnabled(5) ? Output::CullDist5 : Output::None); + ccdist1[2] = ctl.IsClipDistEnabled(6) + ? Output::ClipDist6 + : (ctl.IsCullDistEnabled(6) ? Output::CullDist6 : Output::None); + ccdist1[3] = ctl.IsClipDistEnabled(7) + ? Output::ClipDist7 + : (ctl.IsCullDistEnabled(7) ? Output::CullDist7 : Output::None); + } + + return num_outputs; } const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalStage l_stage) { @@ -121,7 +127,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS } case Stage::Vertex: { BuildCommon(regs.vs_program); - GatherVertexOutputs(info.vs_info, regs.vs_output_control); + info.vs_info.num_outputs = MapOutputs(info.vs_info.outputs, regs.vs_output_control); info.vs_info.emulate_depth_negative_one_to_one = !instance.IsDepthClipControlSupported() && regs.clipper_control.clip_space == Liverpool::ClipSpace::MinusWToW; @@ -136,6 +142,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS case Stage::Geometry: { BuildCommon(regs.gs_program); auto& gs_info = info.gs_info; + gs_info.num_outputs = MapOutputs(gs_info.outputs, regs.vs_output_control); gs_info.output_vertices = regs.vgt_gs_max_vert_out; gs_info.num_invocations = regs.vgt_gs_instance_cnt.IsEnabled() ? regs.vgt_gs_instance_cnt.count : 1; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index cca193831..a37e2f813 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -162,6 +162,7 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { const auto mip = image_view.info.range.base.level; state.width = std::min(state.width, std::max(image.info.size.width >> mip, 1u)); state.height = std::min(state.height, std::max(image.info.size.height >> mip, 1u)); + state.num_layers = std::min(state.num_layers, image_view.info.range.extent.layers); state.color_attachments[state.num_color_attachments++] = { .imageView = *image_view.image_view, .imageLayout = vk::ImageLayout::eUndefined, @@ -195,6 +196,7 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { state.height = std::min(state.height, image.info.size.height); state.has_depth = regs.depth_buffer.DepthValid(); state.has_stencil = regs.depth_buffer.StencilValid(); + state.num_layers = std::min(state.num_layers, image_view.info.range.extent.layers); if (state.has_depth) { state.depth_attachment = { .imageView = *image_view.image_view, @@ -218,6 +220,10 @@ RenderState Rasterizer::PrepareRenderState(u32 mrt_mask) { texture_cache.TouchMeta(htile_address, slice, false); } + if (state.num_layers == std::numeric_limits::max()) { + state.num_layers = 1; + } + return state; } diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 4c4e17fe4..86c5ee728 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -4,6 +4,7 @@ #include #include "common/assert.h" #include "common/debug.h" +#include "common/logging/log.h" #include "imgui/renderer/texture_manager.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -34,9 +35,9 @@ void Scheduler::BeginRendering(const RenderState& new_state) { is_rendering = true; render_state = new_state; - const auto width = + const u32 width = render_state.width != std::numeric_limits::max() ? render_state.width : 1; - const auto height = + const u32 height = render_state.height != std::numeric_limits::max() ? render_state.height : 1; const vk::RenderingInfo rendering_info = { @@ -45,7 +46,7 @@ void Scheduler::BeginRendering(const RenderState& new_state) { .offset = {0, 0}, .extent = {width, height}, }, - .layerCount = 1, + .layerCount = render_state.num_layers, .colorAttachmentCount = render_state.num_color_attachments, .pColorAttachments = render_state.num_color_attachments > 0 ? render_state.color_attachments.data() diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index bd6fb549a..d8e935535 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -24,6 +24,7 @@ struct RenderState { vk::RenderingAttachmentInfo depth_attachment{}; vk::RenderingAttachmentInfo stencil_attachment{}; u32 num_color_attachments{}; + u32 num_layers = std::numeric_limits::max(); bool has_depth{}; bool has_stencil{}; u32 width = std::numeric_limits::max();