diff --git a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp index e9b310d77..8d9d60b1c 100644 --- a/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/pipelineCache.cpp @@ -658,6 +658,19 @@ ShaderProgram PipelineCache::GetComputeProgram(const HW::ComputeShaderInfo& regs ShaderComputeInputInfo& input_info) { input_info.host_subgroup_size = m_graphics.SupportsComputeWave64() ? 64u : 32u; const auto params = PrepareProgram(regs, sh, input_info); + // Use one effective size for the cache key, LDS declaration, and access bounds. + const auto max_lds_dwords = + m_graphics.GetPhysicalDeviceProperties().limits.maxComputeSharedMemorySize / 4u; + if (input_info.lds_size_dwords > max_lds_dwords) { + static std::atomic_bool warned = false; + if (!warned.exchange(true, std::memory_order_relaxed)) { + PipelineCacheLog("GPU warning: game compute shader requests {} bytes of LDS, but " + "the Vulkan device limit is {} bytes. Clamping LDS; rendering may " + "be incorrect.", + input_info.lds_size_dwords * 4u, max_lds_dwords * 4u); + } + } + input_info.lds_size_dwords = std::min(input_info.lds_size_dwords, max_lds_dwords); uint32_t push_data_cursor = 0; return m_program_cache->Get(params, input_info, push_data_cursor); } diff --git a/src/graphics/presentation/window/window.cpp b/src/graphics/presentation/window/window.cpp index 5494ff0f4..320563c58 100644 --- a/src/graphics/presentation/window/window.cpp +++ b/src/graphics/presentation/window/window.cpp @@ -306,14 +306,20 @@ static void GameEventController([[maybe_unused]] const EventController& f) { if (f.added) { auto* pad = SDL_OpenGamepad(f.id); - EXIT_NOT_IMPLEMENTED(pad == nullptr); + if (pad == nullptr) { + LOGF("Controller: ignoring gamepad %d that could not be opened: %s\n", f.id, + SDL_GetError()); + return; + } int id = SDL_GetJoystickID(SDL_GetGamepadJoystick(pad)); Controller::Connect(id); } if (f.removed) { - Controller::Disconnect(f.id); - SDL_CloseGamepad(SDL_GetGamepadFromID(f.id)); + if (auto* pad = SDL_GetGamepadFromID(f.id); pad != nullptr) { + Controller::Disconnect(f.id); + SDL_CloseGamepad(pad); + } } if (f.down || f.up) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 7644326f8..233eff1c9 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -31769,6 +31769,54 @@ std::vector MakeGraphicsCases() { }; } +void CheckComputeLdsLimit(VulkanHarness &vulkan) { + constexpr const char *name = "ComputeLdsLimit"; + std::vector code; + AppendVMovU32(&code, 1, 7); + code.push_back(EncodeDs0(0x0d)); // ds_write_b32 v0, v1 + code.push_back(EncodeDs1(0, 1, 0)); + AppendEnd(&code); + ShaderMapUserData(reinterpret_cast(code.data()), + {.type = Prospero::ShaderBinaryType::kCs, + .code_size_bytes = static_cast(code.size() * sizeof(u32))}); + + GraphicContext graphics; + graphics.device = vulkan.Device(); + graphics.physical_device_properties = + vulkan.RuntimeContext().GetPhysicalDeviceProperties(); + auto &limit = graphics.physical_device_properties.limits.maxComputeSharedMemorySize; + // Reproduce a 48-KiB host, or a smaller host limit when the test device requires it. + limit = std::min(limit, 48u * 1024u) & ~511u; + const auto limit_units = static_cast(limit / 512u); + HW::ComputeShaderInfo regs{}; + regs.cs_regs.data_addr = reinterpret_cast(code.data()); + regs.cs_regs.num_thread_x = 1; + regs.cs_regs.num_thread_y = 1; + regs.cs_regs.num_thread_z = 1; + regs.cs_regs.wave_size = 32; + PipelineCache cache(graphics); + ShaderProgram at_limit; + for (const auto units : {static_cast(limit_units - 1u), limit_units, + uint16_t{112}, uint16_t{128}}) { + regs.cs_regs.lds_size = units; + ShaderComputeInputInfo input{}; + const auto program = cache.GetComputeProgram(regs, {}, input); + const auto expected = units < limit_units ? units * 128u : limit / 4u; + Require(name, "device allocation", input.lds_size_dwords == expected, + "compute LDS allocation did not respect the device limit"); + Require(name, "pipeline creation", + cache.GetComputePipeline(input, program).pipeline != nullptr, + "clamped LDS shader did not create a Vulkan compute pipeline"); + if (units == limit_units) { + at_limit = program; + } else if (units > limit_units) { + Require(name, "effective-size cache reuse", program.id == at_limit.id, + "equivalent clamped LDS allocations compiled separate shaders"); + } + } + std::printf("[gpu] %-32s ok\n", name); +} + void CheckPs5GameExampleImageClearRuntimeShape() { const auto MakeCode = [] { std::vector code; @@ -36397,6 +36445,14 @@ int main(int argc, char **argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); EnsureConfigInitialized(); CheckLeastRecentlyUsedCacheOrdering(); + if (argc == 2 && std::strcmp(argv[1], "--lds-limit-only") == 0) { + VulkanHarness vulkan; + CheckComputeLdsLimit(vulkan); + RunCase(&vulkan, DsWideLdsPartialBounds()); + RunCase(&vulkan, DsAtomic64Bounds(false)); + RunCase(&vulkan, DsAtomic64Bounds(true)); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--new-opcodes-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, VectorCompareInteger64Edges()); @@ -37130,6 +37186,7 @@ int main(int argc, char **argv) { vulkan.CheckStreamBufferRing(); vulkan.CheckGpuTilerCpuParity(); vulkan.CheckNativeIndirectDispatch(); + CheckComputeLdsLimit(vulkan); vulkan.CheckUnifiedTextureCacheFlow(); vulkan.CheckUnifiedImageViewCache(); vulkan.CheckPackedTextureComponents();