diff --git a/relower_words.cpp b/relower_words.cpp new file mode 100644 index 0000000000..71045b4139 --- /dev/null +++ b/relower_words.cpp @@ -0,0 +1,19 @@ +// Reads "w0 w1 w2 w3" hex lines on stdin; lowers each instruction followed by EXIT. +// Build: c++ -std=c++20 -O1 -w -I /src relower_words.cpp -lfmt -o relower +#include +#include +#include "shader_recompiler/frontend/ampere/lower_to_maxwell.h" + +using namespace Shader::Ampere; + +int main(int argc, char**) { + const bool tensor = argc > 1; // any argument: emulate HMMA as EDEN_NX2_TENSOR would + unsigned w0, w1, w2, w3; + while (std::scanf("%x %x %x %x", &w0, &w1, &w2, &w3) == 4) { + const std::vector code{Instruction{{w0, w1, w2, w3}}, + Instruction{{0x0000794d, 0, 0x03800000, 0}}}; + const auto lowered = LowerToMaxwell(code, LoweringOptions{.tensor = tensor}); + std::printf("%08x %08x %08x %08x: %s%.*s\n", w0, w1, w2, w3, lowered ? "OK" : "FAIL ", + static_cast(lowered.error.size()), lowered.error.data()); + } +} diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h index ec3ca0af49..a88713665d 100644 --- a/src/common/thread_worker.h +++ b/src/common/thread_worker.h @@ -117,6 +117,13 @@ class StatefulThreadWorker { }); } + // Drops tasks no worker has started. Running tasks still finish. + void ClearRequests() { + std::scoped_lock lock{queue_mutex}; + work_scheduled -= requests.size(); + requests = std::queue{}; + } + private: std::queue requests; std::mutex queue_mutex; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_mesh.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_mesh.cpp index 6c599528cc..e0f3f437f6 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_mesh.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_mesh.cpp @@ -207,6 +207,13 @@ void EmitIsbeWriteMapU32(EmitContext& ctx, const IR::Value& address, Id value) { } void EmitMeshPrologue(EmitContext& ctx) { + if (ctx.stage == Stage::Task) { + // A task that culls all its threads never writes the mesh workgroup count, and the payload + // starts undefined. Intel's garbage there launches enough workgroups to lose the device. + ctx.OpStore(PayloadWord(ctx, ctx.Const(1U)), ctx.u32_zero_value); + Barrier(ctx); + return; + } if (ctx.stage != Stage::Mesh) { return; } diff --git a/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp index c3ed27f4f2..8a792b2ff2 100644 --- a/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp +++ b/src/shader_recompiler/ir_opt/conditional_barrier_pass.cpp @@ -31,6 +31,7 @@ void ConditionalBarrierPass(IR::Program& program) { inst.GetOpcode() == IR::Opcode::Barrier) { LOG_WARNING(Shader, "Barrier within conditional control flow"); inst.ReplaceOpcode(IR::Opcode::Identity); + program.info.removed_conditional_barrier = true; } } break; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 4e5fb5c6e4..3e38f37751 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -320,6 +320,8 @@ struct Info { bool uses_sparse_residency{}; bool uses_demote_to_helper_invocation{}; bool uses_subgroup_vote{}; + /// Set when a barrier in conditional control flow was dropped, see ConditionalBarrierPass. + bool removed_conditional_barrier{}; bool uses_subgroup_mask{}; bool uses_fswzadd{}; bool uses_derivatives{}; @@ -374,6 +376,13 @@ struct Info { /// and storage buffer words (see EvaluateHandleProgram), evaluated for each draw. std::vector> handle_programs; ImageDescriptors image_descriptors; + + /// True when the shader only runs correctly if a subgroup spans a whole guest warp. + [[nodiscard]] bool UsesWarpOps() const noexcept { + return removed_conditional_barrier || uses_subgroup_vote || uses_subgroup_invocation_id || + uses_subgroup_shuffles || uses_subgroup_mask || uses_quad_shuffles || + uses_fswzadd; + } }; template diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index ae008b48a7..682360e07e 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -88,9 +88,10 @@ ComputePipeline::ComputePipeline(const Device& device_, Scheduler& scheduler, vk }; // Only warp-level ops observe the subgroup size. Forcing 32 on other shaders makes Intel // compile SIMD32, which can spill a register-heavy kernel for minutes (Mario Kart World). - const bool uses_warp_ops = info.uses_subgroup_vote || info.uses_subgroup_invocation_id || - info.uses_subgroup_shuffles || info.uses_subgroup_mask || - info.uses_quad_shuffles || info.uses_fswzadd; + // A shader that lost a conditional barrier counts too: it relies on a whole warp running + // in step, and at a smaller subgroup size it reads stale shared memory, which shows as + // banding over the image in Mario Kart World on Intel's Windows driver. + const bool uses_warp_ops = info.UsesWarpOps(); VkPipelineCreateFlags flags{}; if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index d3ffc661c2..e742e268c4 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -1138,7 +1138,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .dynamicStateCount = static_cast(dynamic_states.size()), .pDynamicStates = dynamic_states.data(), }; - [[maybe_unused]] const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{ + const VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroup_size_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT, .pNext = nullptr, .requiredSubgroupSize = GuestWarpSize, @@ -1148,7 +1148,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { if (!spv_modules[stage]) { continue; } - [[maybe_unused]] auto& stage_ci = + auto& stage_ci = shader_stages.emplace_back(VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, @@ -1159,6 +1159,13 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pName = "main", .pSpecializationInfo = nullptr, }); + const Shader::Info& info{stage_infos[stage]}; + if ((stage_ci.stage == VK_SHADER_STAGE_MESH_BIT_EXT || + stage_ci.stage == VK_SHADER_STAGE_TASK_BIT_EXT) && + info.UsesWarpOps() && device.IsExtSubgroupSizeControlSupported() && + device.IsGuestWarpSizeSupported(stage_ci.stage)) { + stage_ci.pNext = &subgroup_size_ci; + } } VkPipelineCreateFlags flags{}; if (device.IsKhrPipelineExecutablePropertiesEnabled() && Settings::values.renderer_debug.GetValue()) { diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 18aa74b29e..e926a0a1f8 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -661,6 +661,11 @@ bool PreloadAllPipelines() { PipelineCache::~PipelineCache() { SaveUsedPipelines(); if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { + if (!device.CanReadPipelineCacheDuringBuilds()) { + // Drop queued builds, as destroying the workers would; running ones must end first. + workers.ClearRequests(); + workers.WaitForRequests(); + } SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, CACHE_VERSION); } @@ -912,7 +917,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading } void PipelineCache::QueueVulkanPipelineCacheFlush() { - if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty()) { + if (!use_vulkan_pipeline_cache || vulkan_pipeline_cache_filename.empty() || + !device.CanReadPipelineCacheDuringBuilds()) { return; } if (++pipelines_since_flush < VULKAN_CACHE_FLUSH_PIPELINES) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index bb8c7f57a3..f51182f044 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -246,6 +246,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra RasterizerVulkan::~RasterizerVulkan() { VideoCommon::GpuSplit::Get().SetIdleHook(nullptr, nullptr); + // The channels are gone by now, and submitting invalidates the state tracker's flags. + state_tracker.ClearChannel(); scheduler.WaitWorker(); scheduler.Finish(); } @@ -2553,9 +2555,13 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { query_cache.BindToChannel(channel_id); state_tracker.ChangeChannel(channel); state_tracker.InvalidateState(); + bound_channel_id = channel_id; } void RasterizerVulkan::ReleaseChannel(s32 channel_id) { + if (channel_id == bound_channel_id) { + state_tracker.ClearChannel(); + } EraseChannel(channel_id); { std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 1e550a27ca..65de3105d2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -224,6 +224,7 @@ class RasterizerVulkan final : public VideoCore::RasterizerInterface, const Device& device; MemoryAllocator& memory_allocator; StateTracker& state_tracker; + s32 bound_channel_id = -1; Scheduler& scheduler; StagingBufferPool staging_pool; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 6b47ba4176..d47fa9e0e2 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -290,6 +290,11 @@ class StateTracker { void ChangeChannel(Tegra::Control::ChannelState& channel_state); + /// Stops using the bound channel's flags, for when it goes away. + void ClearChannel() { + flags = &default_flags; + } + void InvalidateState(); private: diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index f3f9209c99..c69edb6d2d 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -369,6 +369,12 @@ class Device { id != VK_DRIVER_ID_MESA_RADV; } + /// Whether a pipeline cache can be read while pipelines build into it, as Vulkan allows. Intel's + /// Windows driver crashes in vkGetPipelineCacheData when a build is running. + bool CanReadPipelineCacheDuringBuilds() const { + return GetDriverID() != VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; + } + /// Returns true for tile-based deferred renderers. bool IsTiler() const { switch (GetDriverID()) {