From 44c19af7dbebd019f4d305caef0fe1a8c209327f Mon Sep 17 00:00:00 2001 From: 1e1 <1e1@users.noreply.github.com> Date: Tue, 30 Jun 2026 21:36:33 +0200 Subject: [PATCH] Latte: fix shader-cache hang, redundant vertex rebind, and latent UB - Fix an infinite loop when removing a shader from the middle of a cache chain (the list traversal never advanced). - Vulkan direct-access path: record the bound vertex-buffer offset so the redundant-bind check works; previously one vkCmdBindVertexBuffers per buffer group was issued on every draw. - Fix an out-of-bounds __m256i lane read in the clang texture-hash path. - Fix a use-after-free read in the texture-readback queue and an inverted fetch-shader cache assertion. - Initialize MetalRenderer commit-threshold fields; avoid re-strlen'ing the shader source when assigning it. --- src/Cafe/HW/Latte/Core/FetchShader.cpp | 13 ++++++++----- src/Cafe/HW/Latte/Core/LatteShader.cpp | 3 ++- src/Cafe/HW/Latte/Core/LatteTextureCache.cpp | 2 +- src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp | 2 +- src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h | 6 +++--- .../HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp | 1 + 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/FetchShader.cpp b/src/Cafe/HW/Latte/Core/FetchShader.cpp index 0779fac9..0244dccd 100644 --- a/src/Cafe/HW/Latte/Core/FetchShader.cpp +++ b/src/Cafe/HW/Latte/Core/FetchShader.cpp @@ -170,10 +170,13 @@ void LatteFetchShader::CalculateFetchShaderVkHash() EVP_DigestFinal_ex(ctx, shaDigest, NULL); EVP_MD_CTX_free(ctx); - // fold SHA1 hash into a 64bit value - uint64 h = *(uint64*)(shaDigest + 0); - h += *(uint64*)(shaDigest + 8); - h += (uint64)*(uint32*)(shaDigest + 16); + // fold SHA1 hash into a 64bit value (use memcpy to avoid unaligned/strict-aliasing UB) + uint64 shaLow, shaMid; + uint32 shaHigh; + std::memcpy(&shaLow, shaDigest + 0, sizeof(shaLow)); + std::memcpy(&shaMid, shaDigest + 8, sizeof(shaMid)); + std::memcpy(&shaHigh, shaDigest + 16, sizeof(shaHigh)); + uint64 h = shaLow + shaMid + (uint64)shaHigh; this->vkPipelineHashFragment = h; } @@ -604,7 +607,7 @@ void LatteFetchShader::UnregisterInCache() return; s_spinlockFetchShaderCache.lock(); auto itr = s_fetchShaderByHash.find(m_cacheHash); - cemu_assert(itr == s_fetchShaderByHash.end()); + cemu_assert(itr != s_fetchShaderByHash.end()); s_fetchShaderByHash.erase(itr); s_spinlockFetchShaderCache.unlock(); } diff --git a/src/Cafe/HW/Latte/Core/LatteShader.cpp b/src/Cafe/HW/Latte/Core/LatteShader.cpp index 4e89b02f..49d370b5 100644 --- a/src/Cafe/HW/Latte/Core/LatteShader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShader.cpp @@ -187,6 +187,7 @@ void LatteSHRC_RemoveFromCaches(LatteDecompilerShader* shader) removed = true; break; } + shaderChain = shaderChain->next; } } cemu_assert(removed); @@ -355,7 +356,7 @@ void LatteShader_CreateRendererShader(LatteDecompilerShader* shader, bool compil shader->isCustomShader = true; } else - shaderSrc.assign(shader->strBuf_shaderSource->c_str()); + shaderSrc.assign(shader->strBuf_shaderSource->c_str(), shader->strBuf_shaderSource->getLen()); if (shaderType == RendererShader::ShaderType::kVertex && (shader->baseHash == 0x15bc7edf9de2ed30 || shader->baseHash == 0x83a697d61a3b9202 || diff --git a/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp b/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp index 3145e90e..e784a2f9 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureCache.cpp @@ -159,7 +159,7 @@ uint32 LatteTexture_CalculateTextureDataHash(LatteTexture* hostTexture) h256 = _mm256_xor_si256(h256, temp); } #ifdef __clang__ - hashVal = h256[0] + h256[1] + h256[2] + h256[3] + h256[4] + h256[5] + h256[6] + h256[7]; + hashVal = (uint32)h256[0] + (uint32)(h256[0] >> 32) + (uint32)h256[1] + (uint32)(h256[1] >> 32) + (uint32)h256[2] + (uint32)(h256[2] >> 32) + (uint32)h256[3] + (uint32)(h256[3] >> 32); #else hashVal = h256.m256i_u32[0] + h256.m256i_u32[1] + h256.m256i_u32[2] + h256.m256i_u32[3] + h256.m256i_u32[4] + h256.m256i_u32[5] + h256.m256i_u32[6] + h256.m256i_u32[7]; #endif diff --git a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp index 862964ba..30ea3bb0 100644 --- a/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp +++ b/src/Cafe/HW/Latte/Core/LatteTextureReadback.cpp @@ -151,11 +151,11 @@ void LatteTextureReadback_UpdateFinishedTransfers(bool forceFinish) LatteTextureView* origTexView = LatteTextureViewLookupCache::lookupSlice(readbackInfo->hostTextureCopy.physAddress, readbackInfo->hostTextureCopy.width, readbackInfo->hostTextureCopy.height, readbackInfo->hostTextureCopy.pitch, 0, 0, readbackInfo->hostTextureCopy.format); if (origTexView) LatteTC_ResetTextureChangeTracker(origTexView->baseTexture, true); - delete readbackInfo; // remove from queue cemu_assert_debug(!sTextureActiveReadbackQueue.empty()); cemu_assert_debug(readbackInfo == sTextureActiveReadbackQueue.front()); sTextureActiveReadbackQueue.pop(); + delete readbackInfo; } performanceMonitor.gpuTime_waitForAsync.endMeasuring(); } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index c75f12bf..c6d84f4c 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -535,9 +535,9 @@ private: MetalEncoderType m_encoderType = MetalEncoderType::None; MTL::CommandEncoder* m_commandEncoder = nullptr; - uint32 m_recordedDrawcalls; - uint32 m_defaultCommitTreshlod; - uint32 m_commitTreshold; + uint32 m_recordedDrawcalls = 0; + uint32 m_defaultCommitTreshlod = 0; + uint32 m_commitTreshold = 0; // State MetalState m_state; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 2b50d88f..4457b0ab 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -1772,6 +1772,7 @@ void VulkanRenderer::draw_updateVertexBuffersDirectAccess() VkBuffer attrBuffer = m_importedMem; VkDeviceSize attrOffset = bufferAddress - m_importedMemBaseAddress; vkCmdBindVertexBuffers(m_state.currentCommandBuffer, bufferIndex, 1, &attrBuffer, &attrOffset); + m_state.currentVertexBinding[bufferIndex].offset = bufferAddress; } }