diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp index ebc5a825dd..728d405674 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp @@ -169,11 +169,36 @@ std::map GenerateLegacyToGenericMappings( return mapping; } +struct PassthroughVertices { + u32 count; + u32 first; + u32 stride; +}; + +PassthroughVertices GetPassthroughVertices(InputTopology input_topology) { + switch (input_topology) { + case InputTopology::Points: + return {1, 0, 1}; + case InputTopology::Lines: + return {2, 0, 1}; + case InputTopology::LinesAdjacency: + return {2, 1, 1}; + case InputTopology::Triangles: + return {3, 0, 1}; + case InputTopology::TrianglesAdjacency: + return {3, 0, 2}; + } + return {3, 0, 1}; +} + void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState& passthrough_mask, bool passthrough_position, - std::optional passthrough_layer_attr) { - for (u32 i = 0; i < program.output_vertices; i++) { + std::optional passthrough_layer_attr, + InputTopology input_topology) { + const PassthroughVertices vertices{GetPassthroughVertices(input_topology)}; + for (u32 vertex = 0; vertex < vertices.count; vertex++) { + const u32 i = vertices.first + vertex * vertices.stride; // Assign generics from input for (u32 j = 0; j < 32; j++) { if (!passthrough_mask.Generic(j)) { @@ -208,25 +233,16 @@ void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, ir.EndPrimitive(ir.Imm32(0)); } -u32 GetOutputTopologyVertices(OutputTopology output_topology) { - switch (output_topology) { - case OutputTopology::PointList: - return 1; - case OutputTopology::LineStrip: - return 2; - default: - return 3; - } -} - -void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) { +void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info, + InputTopology input_topology) { for (IR::Block* const block : program.blocks) { for (IR::Inst& inst : block->Instructions()) { if (inst.GetOpcode() == IR::Opcode::Epilogue) { IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)}; EmitGeometryPassthrough( ir, program, program.info.passthrough, - program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}); + program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {}, + input_topology); } } } @@ -235,7 +251,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf } // Anonymous namespace IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, - Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { + Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology) { HostTranslateInfo normalized_host_info{host_info}; normalized_host_info.ApplyDescriptorLimitPolicy(); @@ -264,8 +281,9 @@ IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& inst_pool, ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology) { + Shader::OutputTopology output_topology, + InputTopology input_topology) { IR::Program program; program.stage = Stage::Geometry; program.output_topology = output_topology; - program.output_vertices = GetOutputTopologyVertices(output_topology); + program.output_vertices = GetPassthroughVertices(input_topology).count; program.is_geometry_passthrough = false; program.info.loads.mask = source_program.info.stores.mask; @@ -433,7 +452,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool& inst_pool, IR::IREmitter ir{*current_block}; EmitGeometryPassthrough(ir, program, program.info.stores, true, - source_program.info.emulated_layer); + source_program.info.emulated_layer, input_topology); IR::Block* return_block{block_pool.Create(inst_pool)}; IR::IREmitter{*return_block}.Epilogue(); diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h index 497afe7cb9..49d2800d08 100644 --- a/src/shader_recompiler/frontend/maxwell/translate_program.h +++ b/src/shader_recompiler/frontend/maxwell/translate_program.h @@ -18,7 +18,8 @@ namespace Shader::Maxwell { [[nodiscard]] IR::Program TranslateProgram(ObjectPool& inst_pool, ObjectPool& block_pool, Environment& env, - Flow::CFG& cfg, const HostTranslateInfo& host_info); + Flow::CFG& cfg, const HostTranslateInfo& host_info, + InputTopology input_topology); [[nodiscard]] IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b, Environment& env_vertex_b); @@ -32,6 +33,7 @@ void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_inf ObjectPool& block_pool, const HostTranslateInfo& host_info, IR::Program& source_program, - Shader::OutputTopology output_topology); + Shader::OutputTopology output_topology, + InputTopology input_topology); } // namespace Shader::Maxwell diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index cde03d5822..1218c8580c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -72,6 +72,25 @@ Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topolo } } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, const Shader::IR::Program& program, const Shader::IR::Program* previous_program, @@ -127,33 +146,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, default: break; } - switch (key.gs_input_topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.gs_input_topology); info.glasm_use_storage_buffers = glasm_use_storage_buffers; return info; } @@ -483,8 +476,10 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( && 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); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.gs_input_topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -502,13 +497,15 @@ std::unique_ptr ShaderCache::CreateGraphicsPipeline( if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology)); 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)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.gs_input_topology))}; total_storage_buffers += Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -597,7 +594,8 @@ std::unique_ptr ShaderCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const u32 num_storage_buffers{Shader::NumDescriptors(program.info.storage_buffers_descriptors)}; Shader::RuntimeInfo info; info.glasm_use_storage_buffers = num_storage_buffers <= device.GetMaxGLASMStorageBufferBlocks(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index acb4219213..697540981d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -152,6 +152,25 @@ Shader::AttributeType AttributeType(const FixedPipelineState& state, size_t inde return Shader::AttributeType::Disabled; } +Shader::InputTopology MaxwellToInputTopology(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Shader::InputTopology::Points; + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineLoop: + case Maxwell::PrimitiveTopology::LineStrip: + return Shader::InputTopology::Lines; + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Shader::InputTopology::LinesAdjacency; + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Shader::InputTopology::TrianglesAdjacency; + default: + return Shader::InputTopology::Triangles; + } +} + Shader::RuntimeInfo MakeRuntimeInfo(std::span programs, const GraphicsPipelineCacheKey& key, const Shader::IR::Program& program, @@ -270,33 +289,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program default: break; } - switch (key.state.topology) { - case Maxwell::PrimitiveTopology::Points: - info.input_topology = Shader::InputTopology::Points; - break; - case Maxwell::PrimitiveTopology::Lines: - case Maxwell::PrimitiveTopology::LineLoop: - case Maxwell::PrimitiveTopology::LineStrip: - info.input_topology = Shader::InputTopology::Lines; - break; - case Maxwell::PrimitiveTopology::Triangles: - case Maxwell::PrimitiveTopology::TriangleStrip: - case Maxwell::PrimitiveTopology::TriangleFan: - case Maxwell::PrimitiveTopology::Quads: - case Maxwell::PrimitiveTopology::QuadStrip: - case Maxwell::PrimitiveTopology::Polygon: - case Maxwell::PrimitiveTopology::Patches: - info.input_topology = Shader::InputTopology::Triangles; - break; - case Maxwell::PrimitiveTopology::LinesAdjacency: - case Maxwell::PrimitiveTopology::LineStripAdjacency: - info.input_topology = Shader::InputTopology::LinesAdjacency; - break; - case Maxwell::PrimitiveTopology::TrianglesAdjacency: - case Maxwell::PrimitiveTopology::TriangleStripAdjacency: - info.input_topology = Shader::InputTopology::TrianglesAdjacency; - break; - } + info.input_topology = MaxwellToInputTopology(key.state.topology); info.force_early_z = key.state.early_z != 0; info.y_negate = key.state.y_negate != 0; return info; @@ -793,8 +786,10 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( 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); + programs[index] = + GenerateGeometryPassthrough(pools.inst, pools.block, host_info, + *layer_source_program, topology, + MaxwellToInputTopology(key.state.topology)); continue; } if (key.unique_hashes[index] == 0) { @@ -807,11 +802,13 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); if (!uses_vertex_a || index != 1) { // Normal path - programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info); + programs[index] = TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology)); } else { // VertexB path when VertexA is present. auto& program_va{programs[0]}; - auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + MaxwellToInputTopology(key.state.topology))}; programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); } @@ -957,7 +954,8 @@ std::unique_ptr PipelineCache::CreateComputePipeline( env.Dump(hash, key.unique_hash); } - auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; + auto program{TranslateProgram(pools.inst, pools.block, env, cfg, host_info, + Shader::InputTopology::Points)}; const VkDriverIdKHR driver_id = device.GetDriverID(); const bool needs_shared_mem_clamp = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 3601a3e891..f1f04d0b8c 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -410,8 +410,17 @@ void Scheduler::EndRenderPass() Record([num_images = num_renderpass_images, images = renderpass_images, ranges = renderpass_image_ranges, + consumer_stages = device.AttachmentConsumerStages(), has_transform_feedback = device.IsExtTransformFeedbackSupported()]( vk::CommandBuffer cmdbuf) { + static constexpr VkAccessFlags SHADER_ACCESS = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + static constexpr VkAccessFlags COLOR_ACCESS = + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + static constexpr VkAccessFlags DEPTH_STENCIL_ACCESS = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + std::array barriers; for (size_t i = 0; i < num_images; ++i) { const VkImageSubresourceRange& range = ranges[i]; @@ -421,24 +430,25 @@ void Scheduler::EndRenderPass() | VK_IMAGE_ASPECT_STENCIL_BIT)) !=0; VkAccessFlags src_access = 0; + VkAccessFlags dst_access = SHADER_ACCESS; - if (is_color) + if (is_color) { src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - else if (is_depth_stencil) + dst_access |= COLOR_ACCESS; + } else if (is_depth_stencil) { src_access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - else + dst_access |= DEPTH_STENCIL_ACCESS; + } else { src_access |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + dst_access |= COLOR_ACCESS | DEPTH_STENCIL_ACCESS; + } barriers[i] = VkImageMemoryBarrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = src_access, - .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT - | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT - | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT - | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, + .dstAccessMask = dst_access, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -449,7 +459,7 @@ void Scheduler::EndRenderPass() } cmdbuf.EndRenderPass(); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, consumer_stages, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images)); if (has_transform_feedback) { static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{ diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 3f61bd36fd..28a8ad45f9 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -961,6 +961,21 @@ FN_MAX_LIMIT_LIST return features2.features.multiViewport; } + VkPipelineStageFlags AttachmentConsumerStages() const { + VkPipelineStageFlags stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT | + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT | + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + if (features2.features.geometryShader) { + stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; + } + return stages; + } + /// Returns true if the device supports VK_KHR_maintenance1. bool IsKhrMaintenance1Supported() const { return extensions.maintenance1;