From ec5816a2789c8548462937e98b51bbad1489944d Mon Sep 17 00:00:00 2001 From: "Herman S." <429230+has207@users.noreply.github.com> Date: Mon, 3 Nov 2025 00:29:25 +0900 Subject: [PATCH] [Vulkan] Use vertex_fetch_bitmap instead of vertex_bindings() Avoids using cached/stale vertex binding indices --- .../gpu/vulkan/vulkan_command_processor.cc | 104 ++++++++++++------ 1 file changed, 68 insertions(+), 36 deletions(-) diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index fb7bc4d8b..019fc3d6a 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -2516,46 +2516,78 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type, // Ensure vertex buffers are resident. // TODO(Triang3l): Cache residency for ranges in a way similar to how texture // validity is tracked. + // + // Use the vertex_fetch_bitmap instead of vertex_bindings() to avoid using + // cached/stale vertex binding indices. The bitmap is populated during shader + // translation and represents which fetch constant indices the shader actually + // references, allowing us to check the current register values at draw time. + const Shader::ConstantRegisterMap& constant_map_vertex = + vertex_shader->constant_register_map(); uint64_t vertex_buffers_resident[2] = {}; - for (const Shader::VertexBinding& vertex_binding : - vertex_shader->vertex_bindings()) { - uint32_t vfetch_index = vertex_binding.fetch_constant; - if (vertex_buffers_resident[vfetch_index >> 6] & - (uint64_t(1) << (vfetch_index & 63))) { - continue; - } - xenos::xe_gpu_vertex_fetch_t vfetch_constant = - regs.GetVertexFetch(vfetch_index); - switch (vfetch_constant.type) { - case xenos::FetchConstantType::kVertex: - break; - case xenos::FetchConstantType::kInvalidVertex: - if (cvars::gpu_allow_invalid_fetch_constants) { + for (uint32_t i = 0; i < xe::countof(constant_map_vertex.vertex_fetch_bitmap); + ++i) { + uint32_t vfetch_bits_remaining = constant_map_vertex.vertex_fetch_bitmap[i]; + uint32_t j; + while (xe::bit_scan_forward(vfetch_bits_remaining, &j)) { + vfetch_bits_remaining = xe::clear_lowest_bit(vfetch_bits_remaining); + uint32_t vfetch_index = i * 32 + j; + if (vertex_buffers_resident[vfetch_index >> 6] & + (uint64_t(1) << (vfetch_index & 63))) { + continue; + } + xenos::xe_gpu_vertex_fetch_t vfetch_constant = + regs.GetVertexFetch(vfetch_index); + switch (vfetch_constant.type) { + case xenos::FetchConstantType::kVertex: break; - } - XELOGW( - "Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" type! " - "This " - "is incorrect behavior, but you can try bypassing this by " - "launching Xenia with --gpu_allow_invalid_fetch_constants=true.", - vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); - return false; - default: - XELOGW( - "Vertex fetch constant {} ({:08X} {:08X}) is completely invalid!", - vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); + case xenos::FetchConstantType::kInvalidVertex: + if (cvars::gpu_allow_invalid_fetch_constants) { + break; + } + XELOGW( + "Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" type! " + "This " + "is incorrect behavior, but you can try bypassing this by " + "launching Xenia with --gpu_allow_invalid_fetch_constants=true.", + vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1); + return false; + default: + // Type is kTexture (2) or kInvalidTexture (3) - completely wrong for + // vertex data + if (cvars::gpu_allow_invalid_fetch_constants) { + XELOGW( + "Vertex fetch constant {} ({:08X} {:08X}) has wrong type {} " + "(texture fetch constant in vertex slot) - allowing due to " + "--gpu_allow_invalid_fetch_constants=true. This will likely " + "crash " + "or produce garbage!", + vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1, + static_cast(vfetch_constant.type)); + break; + } + XELOGW( + "Vertex fetch constant {} ({:08X} {:08X}) is completely invalid! " + "Type={} - this slot contains a texture fetch constant (type 2), " + "not a " + "vertex fetch constant (type 0). This may indicate the shader is " + "reading " + "from the wrong fetch constant index, or the game has a bug.", + vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1, + static_cast(vfetch_constant.type)); + return false; + } + if (!shared_memory_->RequestRange(vfetch_constant.address << 2, + vfetch_constant.size << 2)) { + XELOGE( + "Failed to request vertex buffer at 0x{:08X} (size {}) in the " + "shared " + "memory", + vfetch_constant.address << 2, vfetch_constant.size << 2); return false; + } + vertex_buffers_resident[vfetch_index >> 6] |= uint64_t(1) + << (vfetch_index & 63); } - if (!shared_memory_->RequestRange(vfetch_constant.address << 2, - vfetch_constant.size << 2)) { - XELOGE( - "Failed to request vertex buffer at 0x{:08X} (size {}) in the shared " - "memory", - vfetch_constant.address << 2, vfetch_constant.size << 2); - return false; - } - vertex_buffers_resident[vfetch_index >> 6] |= uint64_t(1) - << (vfetch_index & 63); } // Synchronize the memory pages backing memory scatter export streams, and