diff --git a/src/graphics/guest_gpu/gpu_defs.h b/src/graphics/guest_gpu/gpu_defs.h index 46afb4e0f..507ff702d 100644 --- a/src/graphics/guest_gpu/gpu_defs.h +++ b/src/graphics/guest_gpu/gpu_defs.h @@ -26,6 +26,10 @@ enum class PrimitiveType : uint32_t { kPolygon = 21, }; +constexpr bool IsRectList(PrimitiveType type) { + return type == PrimitiveType::kRectList || type == PrimitiveType::kRectListLegacy; +} + enum class IndexType : uint32_t { kIndex16 = 0, kIndex32 = 1, diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index d60a90c88..32765ec13 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -755,8 +755,7 @@ PipelineCache::Pipeline& PipelineCache::GetGraphicsPipeline( static_params.depth_bounds_test_enable = depth.depth_bounds_test_enable; static_params.depth_min_bounds = depth.depth_min_bounds; static_params.depth_max_bounds = depth.depth_max_bounds; - const bool rect_list = - command.GetUserConfig().GetPrimType() == Prospero::PrimitiveType::kRectList; + const bool rect_list = Prospero::IsRectList(command.GetUserConfig().GetPrimType()); static_params.cull_back = !rect_list && mc.cull_back; static_params.cull_front = !rect_list && mc.cull_front; static_params.face = mc.face; diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index bc699aeec..5429aca9d 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -790,8 +790,7 @@ static void SetDrawDebugPhase(CommandBuffer& buffer, uint64_t submit_id, const D draw.instance_count, draw.first_instance); } -static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, - vk::PrimitiveTopology& topology) { +static bool GetDrawTopology(const HW::UserConfig& ucfg, vk::PrimitiveTopology& topology) { topology = vk::PrimitiveTopology::ePointList; @@ -817,13 +816,8 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, if (!Config::TessellationEnabled()) return false; [[fallthrough]]; case Prospero::PrimitiveType::kRectList: - topology = vk::PrimitiveTopology::ePatchList; - break; case Prospero::PrimitiveType::kRectListLegacy: - if (!auto_draw) { - EXIT("unknown primitive type: %u\n", static_cast(ucfg.GetPrimType())); - } - topology = vk::PrimitiveTopology::eTriangleStrip; + topology = vk::PrimitiveTopology::ePatchList; break; case Prospero::PrimitiveType::kQuadListLegacy: topology = vk::PrimitiveTopology::eTriangleFan; @@ -1002,8 +996,7 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo return; } - if (!draw.IsIndexed() && - buffer.GetUserConfig().GetPrimType() != Prospero::PrimitiveType::kRectListLegacy) { + if (!draw.IsIndexed() && !Prospero::IsRectList(buffer.GetUserConfig().GetPrimType())) { return; } @@ -1016,8 +1009,7 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo } static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_buffer, - const ShaderVertexInputInfo& vs_input_info, const DrawCallInfo& draw, - const DrawEmitInfo& emit) { + const DrawCallInfo& draw, const DrawEmitInfo& emit) { switch (ucfg.GetPrimType()) { case Prospero::PrimitiveType::kPointList: case Prospero::PrimitiveType::kLineList: @@ -1026,6 +1018,7 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_ case Prospero::PrimitiveType::kTriFan: case Prospero::PrimitiveType::kTriStrip: case Prospero::PrimitiveType::kRectList: + case Prospero::PrimitiveType::kRectListLegacy: case Prospero::PrimitiveType::kPatch: if (draw.IsIndexed()) { vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset, @@ -1035,14 +1028,6 @@ static void EmitDrawPrimitives(const HW::UserConfig& ucfg, vk::CommandBuffer vk_ emit.first_instance); } break; - case Prospero::PrimitiveType::kRectListLegacy: - if (draw.IsIndexed()) { - EXIT("unknown primitive type: %u\n", static_cast(ucfg.GetPrimType())); - } - // Sarah - EXIT_NOT_IMPLEMENTED(!(draw.index_count == 3 && vs_input_info.buffers_num == 0)); - vk_buffer.draw(4, draw.instance_count, emit.first_vertex, emit.first_instance); - break; case Prospero::PrimitiveType::kQuadListLegacy: EXIT_NOT_IMPLEMENTED((draw.index_count & 0x3u) != 0); for (uint32_t i = 0; i < draw.index_count; i += 4) { @@ -1185,7 +1170,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff if (mesh_active) { vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1); } else { - EmitDrawPrimitives(ucfg, vk_buffer, state.vertex_info[0], draw, emit); + EmitDrawPrimitives(ucfg, vk_buffer, draw, emit); } if (!draw.IsIndexed()) { @@ -1261,7 +1246,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer, hw_check(buffer); vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; - if (!GetDrawTopology(ucfg, false, topology)) { + if (!GetDrawTopology(ucfg, topology)) { return; } @@ -1371,7 +1356,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D args.vertex_count, args.instance_count, args.first_instance}; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; - if (!GetDrawTopology(ucfg, true, topology)) { + if (!GetDrawTopology(ucfg, topology)) { ResetBindings(); return; } @@ -1381,7 +1366,7 @@ void RenderExecutor::DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const D return; } - const bool rect_list = ucfg.GetPrimType() == Prospero::PrimitiveType::kRectList; + const bool rect_list = Prospero::IsRectList(ucfg.GetPrimType()); if (rect_list && state.vertex_info[0].buffers_num == 0 && state.vertex_info[0].stage.program->param_export_mask == 0 && state.ps_input_info.input_num != 0) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index b4ef12ada..92f7e47f1 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -12831,6 +12831,7 @@ public: const uint32_t extent = depth_feedback ? 8 : 32; constexpr uintptr_t depth_address = 0x0000000204400000ull; constexpr uint64_t allocation_size = 0x40000; + constexpr uint64_t rect_address = depth_address + 0x8000; EnsureRuntimeContext(); RenderContext context(m_runtime_context); auto &scheduler = context.GetCommandScheduler(); @@ -12865,6 +12866,15 @@ public: static_cast(mapped)[y * 64 + x] = initial_depth(x, y); } } + if (!depth_feedback && !packed_vertex_color) { + // Rectangle input contains three position/UV records. + constexpr std::array rect_vertices{ + -0.75f, -0.75f, 0, 0, 0, 0, 0, 0, + 0.75f, -0.75f, 0, 0, 1, 0, 0, 0, + -0.75f, 0.75f, 0, 0, 0, 1, 0, 0}; + std::memcpy(reinterpret_cast(rect_address), rect_vertices.data(), + sizeof(rect_vertices)); + } resources.MapMemory(depth_address, allocation_size); if (depth_feedback) { depth.desc.type = BindingType::DepthTarget; @@ -13521,6 +13531,82 @@ public: Require(name, "sparse MRT3 output", sparse_pixels[component] == expected, "MRT3 was compacted to a different slot or clipped by unused MRT0"); } + + // A fourth VS invocation reads beyond the descriptor instead of reconstructing the corner. + const ShaderBufferResource rect_buffer{{ + static_cast(rect_address), + static_cast(rect_address >> 32u) | (32u << 16u), 3, + DstSel(4, 5, 6, 7) | + (static_cast(Prospero::BufferFormat::k32Float) << 12u)}}; + static const auto rect_vertex_code = [] { + std::vector code{ + EncodeMubuf0(0x0d, 0, true, false), EncodeMubuf1(0, 2, 5), + EncodeMubuf0(0x0d, 16, true, false), EncodeMubuf1(2, 2, 5)}; + AppendVMovU32(&code, 4, 0); + AppendVMovLiteral(&code, 6, 0x3f800000u); + code.insert(code.end(), {EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 4, 6), + EncodeExp0(0x20, 0xf), EncodeExp1(2, 3, 4, 6)}); + AppendEnd(&code); + return code; + }(); + static const auto rect_pixel_code = [] { + std::vector code; + for (u32 component = 0; component < 2; component++) { + code.push_back(EncodeVintrp(0, 20 + component, 0, component, 0)); + code.push_back(EncodeVintrp(1, 20 + component, 0, component, 1)); + } + AppendVMovU32(&code, 22, 0); + AppendVMovLiteral(&code, 23, 0x3f800000u); + code.insert(code.end(), {EncodeExp0(0x03, 0xf), EncodeExp1(20, 21, 22, 23)}); + AppendEnd(&code); + return code; + }(); + const auto rect_vs = reinterpret_cast(rect_vertex_code.data()); + const auto rect_ps = reinterpret_cast(rect_pixel_code.data()); + ShaderMapUserData(rect_vs, + {.type = Prospero::ShaderBinaryType::kGs, .user_data = &native_user_data, + .code_size_bytes = static_cast(rect_vertex_code.size() * sizeof(u32))}); + ShaderMapUserData(rect_ps, + {.type = Prospero::ShaderBinaryType::kPs, + .code_size_bytes = static_cast(rect_pixel_code.size() * sizeof(u32))}); + shaders.SetEsShaderBase(rect_vs); + shaders.SetPsShaderBase(rect_ps); + shaders.SetGsShaderResource2({.user_sgpr = 4}); + for (u32 i = 0; i < 4; i++) { + shaders.SetGsUserSgpr(i, rect_buffer.fields[i], HW::UserSgprType::Vsharp); + } + registers.SetPsInControl(0x8001); + registers.SetPsInputEna(2); + registers.SetPsInputAddr(2); + registers.SetPsInputSettings(0, 0); + for (const auto primitive : {Prospero::PrimitiveType::kRectList, + Prospero::PrimitiveType::kRectListLegacy}) { + const char *rect_name = primitive == Prospero::PrimitiveType::kRectList + ? "RectListThreeRecords" : "LegacyRectListThreeRecords"; + user_config.SetPrimitiveType(primitive); + registers.SetModeControl({}); + TextureCacheTestAccess::ClearImage(cache, scheduler.Current(), sparse_color.image_id, + {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, {}); + RenderExecutorTestAccess::DrawAuto( + executor, scheduler.Current(), {.vertex_count = 3, .instance_count = 1}); + const auto pixels = ReadCachedTexel(name, context, sparse_color.image_id, + {}, {extent, extent, 1}); + for (u32 y = 0; y < extent; y++) { + for (u32 x = 0; x < extent; x++) { + const bool inside = x >= 4 && x < 28 && y >= 4 && y < 28; + const auto offset = 4 * (y * extent + x); + const std::array expected{ + inside ? (x + 0.5f - 4) / 24 : 0, + inside ? (y + 0.5f - 4) / 24 : 0, 0, inside ? 1.0f : 0}; + for (u32 component = 0; component < 4; component++) { + Require(rect_name, "three-vertex rectangle coverage and UV", + std::abs(std::bit_cast(pixels[offset + component]) - + expected[component]) < 0.0001f, + "rectangle reconstruction lost its fourth corner or interpolated UVs"); + } + } + } + } DestroyBuffer(&stencil_readback); vertex_shader = owned_vertex_shader; @@ -15011,6 +15097,7 @@ private: Require("VulkanHarness", "graphics", available_features12.shaderOutputLayer == true, "vertex layer output is not supported"); Require("VulkanHarness", "graphics", available_features.fillModeNonSolid && + available_features.tessellationShader && available_depth_clip.depthClipEnable && available_clip_control.depthClipControl && available_color_write.colorWriteEnable && available_feedback_layout.attachmentFeedbackLoopLayout && @@ -15076,6 +15163,7 @@ private: device_features.sampleRateShading = true; device_features.shaderInt64 = true; device_features.fillModeNonSolid = true; + device_features.tessellationShader = true; device_info.pEnabledFeatures = &device_features; constexpr const char *device_extensions[] = { VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,