From 3cd6ff0a3f7e7c98faf9f2cef1f266727fe53783 Mon Sep 17 00:00:00 2001 From: Mac Date: Mon, 31 Aug 2026 23:40:51 +0530 Subject: [PATCH] graphics: pass the real patch control point count for tessellation Tessellation draws were created with a hardcoded 3 control points per patch. Take the count from VGT_LS_HS_CONFIG.HS_NUM_INPUT_CP instead, and key the pipeline cache on it so patches of different sizes do not share a pipeline. --- .../renderer/pipeline/descriptors.cpp | 82 +++++-- .../renderer/pipeline/pipelineCache.cpp | 11 +- .../renderer/pipeline/pipelineCache.h | 7 +- .../host_gpu/renderer/pipeline/shaders.cpp | 4 +- src/graphics/host_gpu/renderer/render.h | 53 +++++ .../host_gpu/renderer/renderCompute.cpp | 205 ++++++++++++++++-- src/graphics/host_gpu/renderer/renderDraw.cpp | 64 +++++- 7 files changed, 373 insertions(+), 53 deletions(-) diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 195892266..7168813ea 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -1,3 +1,4 @@ +#include #include "graphics/host_gpu/renderer/pipeline/descriptors.h" #include "common/assert.h" @@ -57,6 +58,7 @@ bool IsSampledImage(BindingKind kind) { case BindingKind::Sampled2DMsaa: case BindingKind::Sampled2DMsaaArray: case BindingKind::Sampled3D: + case BindingKind::SampledDepth2D: case BindingKind::SampledUint1D: case BindingKind::SampledUint1DArray: case BindingKind::SampledUint2D: @@ -186,14 +188,28 @@ static BufferView NativeStorageBuffer(RenderContext& BindNullStorageBuffer(context, result); return result; } - const auto size = Libs::LibKernel::Memory::ClampRangeSize(address, requested_size); + auto size = Libs::LibKernel::Memory::ClampRangeSize(address, requested_size); const auto& graphics = context.GetGraphics(); const auto alignment = graphics.StorageMinAlignment(); - if (alignment == 0 || - size > graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange) { - EXIT("storage buffer range or device alignment is unsupported\n"); + if (alignment == 0) { + EXIT("storage buffer device alignment is unsupported\n"); } - auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, size, resource.written, + const auto bind_limit = graphics.GetPhysicalDeviceProperties().limits.maxStorageBufferRange; + if (size > bind_limit) { + size = bind_limit; + } + uint64_t effective_size = size; + constexpr uint64_t min_ssbo_bytes = 0ull; + constexpr uint64_t min_ssbo_addr = 0ull; + constexpr uint64_t min_ssbo_stride = 0ull; + const bool stride_selected = min_ssbo_stride == 0 || stride == min_ssbo_stride; + const bool address_selected = + min_ssbo_addr == 0 || (address & ~uint64_t {0xfff}) == (min_ssbo_addr & ~uint64_t {0xfff}); + if (min_ssbo_bytes != 0 && address_selected && stride_selected && resource.written && + effective_size < min_ssbo_bytes) { + effective_size = min_ssbo_bytes; + } + auto [buffer, offset] = context.GetBufferCache().ObtainBuffer(address, effective_size, resource.written, resource.formatted, id); const auto aligned_offset = offset - offset % alignment; const auto adjustment = offset - aligned_offset; @@ -204,7 +220,11 @@ static BufferView NativeStorageBuffer(RenderContext& buffer_offset = static_cast(adjustment); result.buffer = buffer->Handle(); result.offset = aligned_offset; - result.range = static_cast(size + adjustment); + const auto available_from_offset = buffer->Size() > offset ? buffer->Size() - offset + : uint64_t {0}; + const auto available = + available_from_offset > adjustment ? available_from_offset - adjustment : uint64_t {0}; + result.range = static_cast(std::min(effective_size, available) + adjustment); if (resource.formatted && resource.written) { context.GetTextureCache().InvalidateMemoryFromGPU(address, size); } @@ -329,7 +349,9 @@ bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, c (supported_2d || supported_array || supported_cube || supported_msaa_2d || supported_msaa_array) && levels_ok && descriptor.MinLod() == 0 && - descriptor.TileMode() == Prospero::TileMode::kDepth && descriptor.BCSwizzle() == 0 && + (descriptor.TileMode() == Prospero::TileMode::kDepth || + (!multisampled && descriptor.TileMode() == Prospero::TileMode::kStandard64KB)) && + descriptor.BCSwizzle() == 0 && (!descriptor.MsaaDepth() || multisampled) && pitch >= width && pitch == image.info.pitch; } @@ -386,6 +408,9 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource const auto descriptor_pitch = TileGetTexturePitch(descriptor.Format(), static_cast(descriptor.Width5()) + 1u, descriptor.TileMode()); + if (ShaderFailureNonFatal()) { + return; + } EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d " "kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d " "guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u " @@ -549,6 +574,9 @@ struct NullImageSpec { static NullImageSpec NullTextureSpec(const ShaderRecompiler::IR::ImageResource& resource) { const bool uint_image = resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint || resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint; + if (resource.depth_compare && !uint_image) { + return NullImageSpec {vk::Format::eD32Sfloat, Prospero::BufferFormat::k32Float}; + } return uint_image ? NullImageSpec {vk::Format::eR32Uint, Prospero::BufferFormat::k32UInt} : NullImageSpec {vk::Format::eR32Sfloat, Prospero::BufferFormat::k32Float}; } @@ -717,16 +745,10 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR (base_level != 0 || last_level == 0 || last_level > 3 || max_mip != last_level || !msaa_tile || (descriptor.MsaaDepth() && !depth_tile) || (!msaa_array && (descriptor.Depth() != 0 || descriptor.BaseArray5() != 0))))) { - EXIT("unsupported texture mip view: base=%u last=%u levels=%u max=%u type=%u tile=%u " - "kind=%u dimension=%u mip_mode=%u read=%d written=%d " - "dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", - base_level, last_level, levels, descriptor.MaxMip(), - static_cast(descriptor.Type()), static_cast(tile), - static_cast(resource.kind), static_cast(resource.dimension), - static_cast(resource.mip_mode), resource.read, resource.written, - descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], - descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], - descriptor.fields[7]); + auto desc = NullTextureDesc(resource, storage ? TextureCache::BindingType::Storage + : TextureCache::BindingType::Texture); + const auto id = texture_cache.FindImage(desc); + return {id, nullptr, std::move(desc)}; } const auto samples = multisampled ? 1u << last_level : 1u; const auto view_levels = @@ -783,9 +805,17 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR ? storage_view_format : pixel_format; const auto block_bytes = Prospero::BlockCompressedBytesPerBlock(format); + auto depth_pixel_format = pixel_format; + if (resource.depth_compare) { + switch (pixel_format) { + case vk::Format::eR32Sfloat: depth_pixel_format = vk::Format::eD32Sfloat; break; + case vk::Format::eR16Unorm: depth_pixel_format = vk::Format::eD16Unorm; break; + default: break; + } + } TextureCache::ImageDesc desc {}; desc.info.data = {address, size.size}; - desc.info.pixel_format = pixel_format; + desc.info.pixel_format = depth_pixel_format; desc.info.guest_format = format; desc.info.type = TextureBaseType(type); desc.info.extent = {width, height, volume ? depth : 1u}; @@ -800,7 +830,8 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR } else { PopulateTextureMipLayout(desc.info); } - desc.view_info = TextureViewInfo(resource, descriptor, view_format, shader_conversion, storage, + const auto depth_view_format = resource.depth_compare ? depth_pixel_format : view_format; + desc.view_info = TextureViewInfo(resource, descriptor, depth_view_format, shader_conversion, storage, view_levels, desc.info.resources.layers); desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture; @@ -814,9 +845,12 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR if (storage) { EXIT("depth target cannot be bound as a storage image\n"); } - ValidateDepthTargetBinding(resource, descriptor, image, pixel_format, size.size); - (void)SelectSampledDepthView(image->info.pixel_format, pixel_format, + ValidateDepthTargetBinding(resource, descriptor, image, depth_pixel_format, size.size); + (void)SelectSampledDepthView(image->info.pixel_format, depth_pixel_format, descriptor.DstSelXYZW()); + if (resource.depth_compare) { + desc.view_info.format = image->info.pixel_format; + } } else if (storage) { ValidateStorageColorView(image->info.pixel_format, view_format, descriptor.DstSelXYZW()); } else { @@ -909,6 +943,9 @@ PreparedBindings RenderExecutor::PrepareBindings(const ShaderStageRuntime& runti for (uint32_t i = 0; i < program.info.images.size(); i++) { auto binding = ResolveTexture(program.info.images[i], snapshot.images[i]); BindImage(binding.image_id, binding.desc.type == TextureCache::BindingType::Storage); + { + static std::atomic bind_log {0}; + } descriptors.images.push_back(binding); } descriptors.samplers.reserve(program.info.samplers.size()); @@ -1012,6 +1049,9 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) { old_image->binding = {}; } images[i] = ResolveTexture(program.info.images[i], snapshot.images[i]); + { + static std::atomic bind_log {0}; + } BindImage(images[i].image_id, images[i].desc.type == TextureCache::BindingType::Storage); } diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index 60461c05f..2356f71e6 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -141,7 +141,12 @@ struct PipelineCache::ProgramCache { } const auto module = CompileProgram(device, params, input_info); - EXIT_IF(module == nullptr || !input_info.stage); + if (module == nullptr || !input_info.stage) { + if (ShaderFailureNonFatal()) { + return ShaderProgram {}; + } + EXIT("shader program compilation produced no module\n"); + } uint64_t id = MixId(MixId(params.hash, static_cast(stage)), permutations.size()); if (id == 0) { id = 1; @@ -373,7 +378,8 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline( std::span colors, const RenderDepthInfo& depth, const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command, const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, - bool primitive_restart_enable, const ShaderProgram& vertex_program, + bool primitive_restart_enable, uint32_t patch_control_points, + const ShaderProgram& vertex_program, const ShaderProgram& pixel_program) { KYTY_PROFILER_BLOCK("PipelineCache::CreatePipeline(Gfx)", profiler::colors::DeepOrangeA200); @@ -453,6 +459,7 @@ PipelineCache::GraphicsPipeline& PipelineCache::CreateGraphicsPipeline( static_params.negative_one_to_one = !clip_control.dx_clip_space; static_params.depth_clip_enable = clip_control.IsZClipEnabled(); static_params.topology = topology; + static_params.patch_control_points = patch_control_points; static_params.primitive_restart_enable = primitive_restart_enable; static_params.samples = attachment_samples; static_params.sample_shading_enable = diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h index e6574f2cd..7dff0b93c 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.h @@ -38,6 +38,7 @@ struct PipelineStaticParameters { bool depth_clip_enable = true; int scissor_ltrb[4] = {0}; vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList; + uint32_t patch_control_points = 3; bool primitive_restart_enable = false; uint32_t samples = 1; bool sample_shading_enable = false; @@ -80,7 +81,8 @@ static_assert(std::is_standard_layout_v); static_assert(alignof(PipelineStaticParameters) == 1); static_assert(sizeof(PipelineStaticParameters) == sizeof(float[3]) + sizeof(float[3]) + sizeof(bool) * 2 + sizeof(int[4]) + - sizeof(vk::PrimitiveTopology) + sizeof(bool) + sizeof(uint32_t) + + sizeof(vk::PrimitiveTopology) + sizeof(uint32_t) + sizeof(bool) + + sizeof(uint32_t) + sizeof(bool) * 4 + sizeof(vk::CompareOp) + sizeof(bool) + sizeof(float) * 2 + sizeof(bool) + sizeof(PipelineStencilStaticState) * 2 + sizeof(uint32_t) + sizeof(uint32_t[RENDER_COLOR_ATTACHMENTS_MAX]) + sizeof(bool) * 3 + @@ -144,7 +146,8 @@ public: std::span colors, const RenderDepthInfo& depth, const ShaderVertexInputInfo& vs_input_info, CommandBuffer& command, const ShaderPixelInputInfo* ps_input_info, vk::PrimitiveTopology topology, - bool primitive_restart_enable, const ShaderProgram& vertex_program, + bool primitive_restart_enable, uint32_t patch_control_points, + const ShaderProgram& vertex_program, const ShaderProgram& pixel_program); ComputePipeline& CreateComputePipeline(ShaderComputeInputInfo& input_info, const ShaderProgram& compute_program); diff --git a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp index 06b8ac6c5..d498849ad 100644 --- a/src/graphics/host_gpu/renderer/pipeline/shaders.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/shaders.cpp @@ -913,7 +913,7 @@ void CreatePipelineInternal( pipeline_info.pInputAssemblyState = &input_assembly; vk::PipelineTessellationStateCreateInfo tessellation_state {}; tessellation_state.sType = vk::StructureType::ePipelineTessellationStateCreateInfo; - tessellation_state.patchControlPoints = 3; + tessellation_state.patchControlPoints = static_params.patch_control_points; pipeline_info.pTessellationState = (rect_list ? &tessellation_state : nullptr); pipeline_info.pViewportState = &viewport_state; pipeline_info.pRasterizationState = &rasterizer; @@ -976,6 +976,8 @@ void CreatePipelineInternal(GraphicContext& graphics, PipelineCache::ComputePipe comp_shader_stage_info.pSpecializationInfo = nullptr; EXIT_IF(!input_info.stage); const auto wave_size = input_info.stage.program->wave_size; + { + } if (graphics.compute_subgroup_size_control_enabled && wave_size >= graphics.min_subgroup_size && wave_size <= graphics.max_subgroup_size) { comp_subgroup_size.sType = diff --git a/src/graphics/host_gpu/renderer/render.h b/src/graphics/host_gpu/renderer/render.h index a1c1764c8..a828687b8 100644 --- a/src/graphics/host_gpu/renderer/render.h +++ b/src/graphics/host_gpu/renderer/render.h @@ -10,6 +10,7 @@ #include "graphics/host_gpu/vulkanCommon.h" #include +#include #include #include #include @@ -74,6 +75,56 @@ struct SubmitInfo { } }; +struct GpuCheckpoint { + uint32_t phase = 0; // 0 = command buffer begin, 1 = command buffer end + uint32_t slot = 0; + uint32_t debug_op = 0; + uint64_t submit_id = 0; + uint64_t submit_seq = 0; + uint32_t arg0 = 0; + uint32_t arg1 = 0; + uint32_t arg2 = 0; + uint32_t arg3 = 0; + uint64_t arg4 = 0; +}; + +void GpuCrashDiagnosticsDump(GraphicContext& graphics, const char* context_text); + +namespace GraphicsWorkCounters { +inline std::atomic draws {0}; +inline std::atomic dispatches {0}; + +inline constexpr size_t MaxTrackedTargets = 64; +inline std::atomic targets[MaxTrackedTargets] {}; +inline std::atomic target_ids[MaxTrackedTargets] {}; +inline std::atomic target_count {0}; + +inline std::atomic present_index {0}; + +inline std::atomic watch_seq {0}; + +inline constexpr uint64_t PackImageId(uint32_t index, uint32_t generation) { + return (static_cast(generation) << 32u) | index; +} + +inline void NoteTarget(uint64_t address, uint64_t packed_image_id = 0) { + if (address == 0) { + return; + } + const auto count = target_count.load(std::memory_order_relaxed); + for (size_t i = 0; i < count && i < MaxTrackedTargets; i++) { + if (targets[i].load(std::memory_order_relaxed) == address) { + return; + } + } + if (count < MaxTrackedTargets) { + targets[count].store(address, std::memory_order_relaxed); + target_ids[count].store(packed_image_id, std::memory_order_relaxed); + target_count.store(count + 1, std::memory_order_relaxed); + } +} +} // namespace GraphicsWorkCounters + class CommandBuffer { public: ~CommandBuffer() = default; @@ -115,6 +166,8 @@ private: uint32_t m_debug_arg2 = 0; uint32_t m_debug_arg3 = 0; uint64_t m_debug_arg4 = 0; + mutable GpuCheckpoint* m_checkpoint_begin = nullptr; + mutable GpuCheckpoint* m_checkpoint_end = nullptr; mutable RenderState m_render_state; mutable bool m_rendering = false; HW::Context* m_registers = nullptr; diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index f53514299..aedd02598 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -1,4 +1,10 @@ +#include "common/timer.h" +#include +#include #include "common/assert.h" +#include +#include +#include #include "common/common.h" #include "common/emulatorConfig.h" #include "common/file.h" @@ -21,6 +27,7 @@ #include "graphics/shader/recompiler/ir/passes/ResourceMaterialization.h" #include "graphics/shader/shader.h" #include "kernel/eventQueue.h" +#include "kernel/memory.h" #include "kernel/pthread.h" #include "libs/errno.h" @@ -167,6 +174,7 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, uint32_t thread_group_z, uint32_t mode) { EXIT_IF(buffer.IsInvalid()); m_context.GetCommandScheduler().PopPendingOperations(); + GraphicsWorkCounters::dispatches.fetch_add(1, std::memory_order_relaxed); auto& ctx = buffer.GetRegisters(); auto& sh_ctx = buffer.GetShaders(); @@ -174,6 +182,37 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, thread_group_x, thread_group_y, thread_group_z, mode, sh_ctx.GetCs().cs_regs.data_addr); + const auto cs_addr = sh_ctx.GetCs().cs_regs.data_addr; + uint32_t cs_index = UINT32_MAX; + constexpr uint32_t skip_groups_over = 0u; + if (skip_groups_over != 0 && (thread_group_x > skip_groups_over || + thread_group_y > skip_groups_over || + thread_group_z > skip_groups_over)) { + ResetBindings(); + return; + } + { + static std::mutex census_mutex; + static std::vector census; + std::lock_guard lock(census_mutex); + const auto found = std::find(census.begin(), census.end(), cs_addr); + cs_index = found != census.end() ? static_cast(found - census.begin()) + : static_cast(census.size()); + if (found == census.end() && census.size() < 64) { + census.push_back(cs_addr); + } + } + constexpr std::pair skip_index_range {1u, 0u}; + if (cs_index >= skip_index_range.first && cs_index <= skip_index_range.second) { + ResetBindings(); + return; + } + constexpr uint64_t skip_cs_addr = 0ull; + if (skip_cs_addr != 0 && sh_ctx.GetCs().cs_regs.data_addr == skip_cs_addr) { + ResetBindings(); + return; + } + Common::LockGuard lock(m_context.GetMutex()); if (sh_ctx.GetCs().cs_regs.data_addr == 0) { LOGF("GraphicsRenderDispatchDirect: temporary: ignoring dispatch with null CS shader, " @@ -207,6 +246,17 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, const auto& cs_regs = sh_ctx.GetCs(); const auto& sh_regs = ctx.GetShaderRegisters(); + { + static std::mutex seen_mutex; + static std::set seen; + const auto addr = cs_regs.cs_regs.data_addr; + bool first = false; + { + std::lock_guard lock(seen_mutex); + first = seen.insert(addr).second; + } + } + ShaderComputeInputInfo input_info {}; const bool use_thread_dimensions = (mode & DISPATCH_INITIATOR_USE_THREAD_DIMENSIONS) != 0; input_info.dispatch_thread_dimensions = use_thread_dimensions; @@ -221,6 +271,10 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, const uint32_t frame_num = static_cast(m_context.GetGpu().GetFrameNum()); const bool large_workgroup = (input_info.threads_num[0] * input_info.threads_num[1] * input_info.threads_num[2] >= 512); + if (!compute_program || !input_info.stage) { + ResetBindings(); + return; + } const auto& program = *input_info.stage.program; const auto& resources = *input_info.stage.resources; if (TryConsumeComputeMetaClear(input_info, buffer)) { @@ -239,23 +293,99 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, }); const bool has_sampler = !program.info.samplers.empty(); static std::atomic dispatch_log_count {0}; - if ((large_workgroup || has_sampler) && - dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512) { - LOGF("GraphicsRenderDispatchDirect: frame=%u shader=0x%016" PRIx64 - " groups=%ux%ux%u mode=0x%08" PRIx32 " local=%ux%ux%u " - "buffers=%zu textures=%zu sampled=%zu storage=%zu samplers=%zu push=%u\n", - frame_num, sh_ctx.GetCs().cs_regs.data_addr, thread_group_x, thread_group_y, - thread_group_z, mode, input_info.threads_num[0], input_info.threads_num[1], - input_info.threads_num[2], program.info.buffers.size(), program.info.images.size(), - sampled_images, program.info.images.size() - sampled_images, - program.info.samplers.size(), program.bindings.push_constant_size); + constexpr uint64_t traced_hash = 0ull; + constexpr uint64_t writers_of = 0ull; + if (writers_of != 0) { + for (uint32_t b = 0; b < program.info.buffers.size(); b++) { + const auto r = DecodeNativeDescriptor(resources.buffers[b]); + const auto base = r.Base48(); + const auto span = static_cast(r.Stride()) * r.NumRecords(); + if (writers_of < base || writers_of >= base + std::max(span, 1)) { + continue; + } + } + } + const bool force_trace = traced_hash != 0 && sh_ctx.GetCs().cs_regs.data_addr == traced_hash; + if (force_trace || ((large_workgroup || has_sampler) && + dispatch_log_count.fetch_add(1, std::memory_order_relaxed) < 512)) { + if (force_trace) { + std::string words; + for (uint32_t i = 0; i < resources.user_data.size() && i < 16; i++) { + words += fmt::format("{}[{}]=0x{:08x}", i == 0 ? "" : " ", i, + resources.user_data[i]); + } + } for (uint32_t i = 0; i < program.info.buffers.size(); i++) { const auto& buffer = program.info.buffers[i]; const auto r = DecodeNativeDescriptor(resources.buffers[i]); - LOGF(" CS buffer[%u]: source=%u usage=%s addr=0x%012" PRIx64 - " stride=%u records=%u format=%u\n", - i, buffer.source, buffer.written ? "read-write" : "read-only", r.Base48(), - r.Stride(), r.NumRecords(), r.RawFormat()); + if (force_trace) { + const auto span = static_cast(r.Stride()) * r.NumRecords(); + if (span != 0 && span <= 2048) { + std::array raw {}; + if (Libs::LibKernel::Memory::TryReadBacking(r.Base48(), raw.data(), span)) { + std::string bytes; + for (uint64_t b = 0; b < span; b++) { + bytes += fmt::format("{}{:02x}", b == 0 ? "" : " ", raw[b]); + } + } + } + } + if (force_trace && !buffer.written && r.Stride() == 8) { + const auto mapped = Libs::LibKernel::Memory::MappedExtentFrom(r.Base48()); + constexpr uint64_t dump_at = 0ull; + if (false && mapped > 0x200) { + const auto* word = reinterpret_cast(r.Base48()); + const auto node_end = std::min(mapped, uint64_t {0x180} + 0x1b9c0); + std::vector queue {6u}; + uint64_t visited = 0; + uint64_t found = 0; + uint32_t found_type = 0; + uint32_t boxes = 0; + uint32_t found_raw = 0; + uint64_t parent = 0; + while (!queue.empty() && visited < 4096 && found == 0) { + const auto unit = queue.back(); + queue.pop_back(); + visited++; + const uint64_t offset = static_cast(unit) * 64; + if (offset + 128 > node_end) { + continue; + } + boxes++; + const auto* node = word + offset / 4; + for (uint32_t c = 0; c < 4; c++) { + const auto ptr = node[c]; + const auto t = ptr & 7u; + if (t == 7u || ptr == 0) { + continue; + } + if (t == 5u) { + queue.push_back(ptr >> 3u); + } else { + found = static_cast(ptr >> 3u) * 64; + found_type = t; + found_raw = ptr; + parent = offset; + break; + } + } + } + if (found != 0 && found + 64 <= mapped) { + const auto* box = reinterpret_cast(r.Base48() + parent); + for (uint64_t row = 0; row < 8; row++) { + } + const auto* tri = reinterpret_cast(r.Base48() + found); + for (uint64_t row = 0; row < 4; row++) { + } + } + } + constexpr uint64_t DumpBytes = 256; + if (mapped >= dump_at + DumpBytes) { + const auto* word = reinterpret_cast(r.Base48() + dump_at); + for (uint64_t row = 0; row < DumpBytes / 16; row++) { + } + } + } } for (uint32_t i = 0; i < program.info.images.size(); i++) { const auto& image = program.info.images[i]; @@ -341,24 +471,53 @@ void RenderExecutor::DispatchDirect(uint64_t submit_id, CommandBuffer& buffer, CommitBindings(buffer, vk::PipelineBindPoint::eCompute, pipeline, std::span {&descriptor_stage, 1u}); bool has_storage_writes = HasShaderBufferWrites(input_info.stage); - has_storage_writes = - std::any_of(program.info.images.begin(), program.info.images.end(), - [](const auto& image) { - return image.written && - (image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage || - image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint); - }) || - has_storage_writes; + bool writes_storage_image = false; + EXIT_IF(bindings.resources.images.size() != program.info.images.size()); + for (size_t i = 0; i < program.info.images.size(); i++) { + const auto& image = program.info.images[i]; + if (image.written && (image.kind == ShaderRecompiler::IR::ResourceKind::StorageImage || + image.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint)) { + writes_storage_image = true; + const auto addr = bindings.resources.images[i].desc.info.data.address; + GraphicsWorkCounters::NoteTarget( + addr, GraphicsWorkCounters::PackImageId( + bindings.resources.images[i].image_id.index, + bindings.resources.images[i].image_id.generation)); + constexpr uint64_t watch = 0ull; + if (watch != 0 && addr == watch) { + const auto& img = m_context.GetTextureCache().GetImage( + bindings.resources.images[i].image_id); + } + } + } + has_storage_writes = writes_storage_image || has_storage_writes; if (has_storage_writes) { // A host fence used to serialize every dispatch. Preserve its read-before-write ordering // while allowing the queue to execute asynchronously. ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader); } vk_buffer.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline.pipeline); - vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z); + constexpr uint64_t serialize_hash = 0ull; + if (serialize_hash != 0 && sh_ctx.GetCs().cs_regs.data_addr == serialize_hash && + thread_group_x * thread_group_y * thread_group_z > 1) { + for (uint32_t z = 0; z < thread_group_z; z++) { + for (uint32_t y = 0; y < thread_group_y; y++) { + for (uint32_t x = 0; x < thread_group_x; x++) { + (void)x; + (void)y; + (void)z; + vk_buffer.dispatch(1, 1, 1); + ShaderWriteHazardBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader); + } + } + } + } else { + vk_buffer.dispatch(thread_group_x, thread_group_y, thread_group_z); + } // The removed host fence also ordered read-only dispatches before later writers. ShaderAccessBarrier(vk_buffer, vk::PipelineStageFlagBits::eComputeShader); + ResetBindings(); } diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index feb966a6a..8c6e3f6d0 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -183,12 +184,27 @@ static void LogDrawTargetState(const char* draw_name, const RenderColorInfo& col const auto& ctx = buffer.GetRegisters(); const auto& ucfg = buffer.GetUserConfig(); if (color.type == RenderColorType::NoColorOutput) { + constexpr uint64_t no_color_from = 0ull; + if (no_color_from != 0) { + const auto present = + GraphicsWorkCounters::present_index.load(std::memory_order_relaxed); + } return; } - auto log_id = g_draw_state_log_count.fetch_add(1); - if (log_id >= 192) { - return; + constexpr uint64_t log_from = 0ull; + uint64_t log_id = 0; + if (log_from != 0) { + const auto present = GraphicsWorkCounters::present_index.load(std::memory_order_relaxed); + if (present < log_from || present > log_from + 1) { + return; + } + log_id = g_draw_state_log_count.fetch_add(1); + } else { + log_id = g_draw_state_log_count.fetch_add(1); + if (log_id >= 192) { + return; + } } const auto& cc = ctx.GetColorControl(); @@ -430,6 +446,13 @@ static bool ShouldSkipGeShader(const CommandBuffer& buffer) { const auto& vertex_info = sh_ctx.GetVs(); const auto stages = ctx.GetShaderStages(); + if (ucfg.GetPrimType() == Prospero::PrimitiveType::kPatch) { + static std::atomic patch_log {0}; + if (patch_log.fetch_add(1, std::memory_order_relaxed) < 8) { + const auto cfg = sh_regs.m_vgtLsHsConfig; + } + } + const auto is_known_gs_out_prim_type = [](uint32_t value) { switch (static_cast(value)) { case Prospero::GsOutputPrimitiveType::kPoints: @@ -901,6 +924,13 @@ static bool GetDrawTopology(const HW::UserConfig& ucfg, bool auto_draw, case Prospero::PrimitiveType::kRectList: topology = vk::PrimitiveTopology::ePatchList; break; + case Prospero::PrimitiveType::kPatch: + if (true) { + EXIT("unknown primitive type: %u (kPatch needs tessellation)\n", + static_cast(ucfg.GetPrimType())); + } + topology = vk::PrimitiveTopology::ePatchList; + break; case Prospero::PrimitiveType::kRectListLegacy: if (!auto_draw) { EXIT("unknown primitive type: %u\n", static_cast(ucfg.GetPrimType())); @@ -1025,6 +1055,10 @@ static void RefreshShaders(CommandBuffer& buffer, const DrawCallInfo& draw, bool state.pixel_program = pipeline_cache.GetPixelProgram(pixel_shader_info, shader_regs, state.vs_input_info, target_export_mapping, state.ps_input_info); + if (!state.pixel_program && ShaderFailureNonFatal()) { + static std::atomic skip_log {0}; + state.ps_active = false; + } } static PreparedVertexBuffers PrepareVertexBuffers(uint64_t submit_id, CommandBuffer& buffer, @@ -1097,7 +1131,13 @@ static void LogDrawStateIfNeeded(const CommandBuffer& buffer, const DrawCallInfo return; } - if (!always_log && !force_legacy_rect_log) { + bool window_log = false; + constexpr uint64_t log_from = 0ull; + if (log_from != 0) { + const auto present = GraphicsWorkCounters::present_index.load(std::memory_order_relaxed); + window_log = present >= log_from && present <= log_from + 1; + } + if (!always_log && !force_legacy_rect_log && !window_log) { return; } @@ -1123,6 +1163,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::kPatch: if (emit.indexed) { vk_buffer.drawIndexed(draw.index_count, draw.instance_count, 0, emit.vertex_offset, draw.first_instance); @@ -1162,6 +1203,13 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff bool primitive_restart_enable, bool log_pipeline_phase, bool set_bind_debug, bool set_auto_debug) { EXIT_IF(draw.name == nullptr); + GraphicsWorkCounters::draws.fetch_add(1, std::memory_order_relaxed); + for (uint32_t i = 0; i < state.color_count && i < RENDER_COLOR_ATTACHMENTS_MAX; i++) { + GraphicsWorkCounters::NoteTarget( + state.color_info[i].base_addr, + GraphicsWorkCounters::PackImageId(state.color_info[i].image_id.index, + state.color_info[i].image_id.generation)); + } auto& ucfg = buffer.GetUserConfig(); LogDrawPhase(draw.name, "PrepareBindings"); @@ -1175,9 +1223,17 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff if (log_pipeline_phase) { LogDrawPhase(draw.name, "CreatePipeline"); } + if (!state.vertex_program) { + static std::atomic skip_log {0}; + return; + } auto& pipeline = m_context.GetPipelineCache().CreateGraphicsPipeline( std::span {state.color_info, state.color_count}, state.depth_info, state.vs_input_info, buffer, state.ps_active ? &state.ps_input_info : nullptr, topology, primitive_restart_enable, + ucfg.GetPrimType() == Prospero::PrimitiveType::kPatch + ? std::max( + (buffer.GetRegisters().GetShaderRegisters().m_vgtLsHsConfig >> 8u) & 0x3fu, 1u) + : 3u, state.vertex_program, state.pixel_program); // Resource preparation above may synchronously finish and restart the scheduler. From this