Vulkan: Smaller fixes + code cleanup

This commit is contained in:
Exzap 2026-06-27 06:43:42 +02:00
parent 62ac48f0e9
commit 4c4ef50f49
15 changed files with 61 additions and 66 deletions

View File

@ -194,17 +194,23 @@ void LatteBufferCache_Sync(uint32 maxIndex, uint32 baseInstance, uint32 instance
if (isIncremental) if (isIncremental)
{ {
// dont process flush queue and dont process deallocations yet, we are in the middle of a sequence of drawcalls that (most likely) reuse previous bindings // dont process flush queue and dont process deallocations yet, we are in the middle of a sequence of drawcalls that (most likely) reuse previous bindings
uint32 maxInstance = baseInstance + instanceCount - 1;
bool hasBufferChange = attribBufferDirtyMask != 0;
if ( maxIndex > s_vtxStateMaxIndex ) if ( maxIndex > s_vtxStateMaxIndex )
{ {
attribBufferDirtyMask = 0xFFFFFFFF; attribBufferDirtyMask = 0xFFFFFFFF;
s_vtxStateMaxIndex = maxIndex; s_vtxStateMaxIndex = maxIndex;
} }
uint32 maxInstance = baseInstance + instanceCount - 1;
if ( maxInstance > s_vtxStateMaxInstance ) if ( maxInstance > s_vtxStateMaxInstance )
{ {
attribBufferDirtyMask = 0xFFFFFFFF; attribBufferDirtyMask = 0xFFFFFFFF;
s_vtxStateMaxInstance = maxInstance; s_vtxStateMaxInstance = maxInstance;
} }
if (hasBufferChange)
{
s_vtxStateMaxIndex = maxIndex;
s_vtxStateMaxInstance = maxInstance;
}
} }
else else
{ {

View File

@ -1021,7 +1021,6 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
LatteCMDPtr cmd, cmdStart, cmdEnd; LatteCMDPtr cmd, cmdStart, cmdEnd;
if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd))
{ {
// cemuLog_log(LogType::Force, "[FastDrawMode] End due running out of commands");
drawPassCtx.endDrawPass(); drawPassCtx.endDrawPass();
return; return;
} }
@ -1049,7 +1048,6 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
(registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) || (registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) ||
(registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7))) (registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)))
{ {
// cemuLog_log(LogType::Force, "[FastDrawMode] End due to texture change");
drawPassCtx.endDrawPass(); // texture updates end the current draw sequence drawPassCtx.endDrawPass(); // texture updates end the current draw sequence
} }
else if (registerStart >= mmSQ_VTX_ATTRIBUTE_BLOCK_START && registerEnd <= mmSQ_VTX_ATTRIBUTE_BLOCK_END) else if (registerStart >= mmSQ_VTX_ATTRIBUTE_BLOCK_START && registerEnd <= mmSQ_VTX_ATTRIBUTE_BLOCK_END)
@ -1089,7 +1087,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
drawPassCtx.MarkVSAluConstantsDirty(); drawPassCtx.MarkVSAluConstantsDirty();
else else
drawPassCtx.MarkPSAluConstantsDirty(); drawPassCtx.MarkPSAluConstantsDirty();
// todo - we could further optimize by tracking the min/max range of modified ALU constants // todo - we could further optimize by tracking the min/max range of modified ALU constants and only uploading the affected range. Possibly not worth it
}); });
break; break;
} }
@ -1120,11 +1118,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
} }
case IT_SET_CONTEXT_REG: case IT_SET_CONTEXT_REG:
{ {
// LatteCP_itSetRegistersGeneric<LATTE_REG_BASE_CONTEXT>(cmd, nWords);
bool hasChanged = LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_CONTEXT>(cmdData, nWords, [](uint32 registerStart, uint32 registerEnd, bool regValuesChanged){}); bool hasChanged = LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_CONTEXT>(cmdData, nWords, [](uint32 registerStart, uint32 registerEnd, bool regValuesChanged){});
//cemuLog_log(LogType::Force, "[FastDrawMode] Potential end due to SetContextReg (itHeader {:08x} data {:08x}) -> HasChanged {}", itHeader, (uint32)cmdData[0], hasChanged);
// drawPassCtx.endDrawPass();
// drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd);
if (hasChanged) if (hasChanged)
{ {
drawPassCtx.endDrawPass(); drawPassCtx.endDrawPass();
@ -1144,10 +1138,8 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
case IT_SET_SAMPLER: case IT_SET_SAMPLER:
{ {
bool hasChanged = LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_SAMPLER>(cmdData, nWords, [](uint32 registerStart, uint32 registerEnd, bool regValuesChanged){}); bool hasChanged = LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_SAMPLER>(cmdData, nWords, [](uint32 registerStart, uint32 registerEnd, bool regValuesChanged){});
//cemuLog_log(LogType::Force, "[FastDrawMode] Set sampler (reg change = {} - filtered)", hasChanged);
if (hasChanged) if (hasChanged)
{ {
//cemuLog_log(LogType::Force, "[FastDrawMode] End due to sampler change");
drawPassCtx.endDrawPass(); drawPassCtx.endDrawPass();
drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd);
return; return;
@ -1155,9 +1147,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
break; break;
} }
default: default:
// unsupported command for fast draw // unallowed command for fast draw
//cemuLog_log(LogType::Force, "[FastDrawMode] Unallowed command. Data: {:08x} {:08x} {:08x} {:08x} {:08x} {:08x}", (uint32)itHeader, (uint32)cmdData[0], (uint32)cmdData[1], (uint32)cmdData[2], (uint32)cmdData[3], (uint32)cmdData[4], (uint32)cmdData[5]);
//cemuLog_log(LogType::Force, "[FastDrawMode] End due to unallowed packet 0x{:08x}", itHeader);
drawPassCtx.endDrawPass(); drawPassCtx.endDrawPass();
drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd);
return; return;
@ -1169,8 +1159,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
} }
else else
{ {
// unsupported command for fast draw // unallowed command for fast draw
//cemuLog_log(LogType::Force, "[FastDrawMode] End due to uknown packet 0x{:08x}", itHeader);
drawPassCtx.endDrawPass(); drawPassCtx.endDrawPass();
drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd);
return; return;

View File

@ -1062,7 +1062,7 @@ robin_hood::unordered_flat_map<uint64, ShaderStateInfo, ShaderStateDirectHash> s
// folly::F14FastMap<uint64, ShaderStateInfo, FPHDirectHash> // folly::F14FastMap<uint64, ShaderStateInfo, FPHDirectHash>
// robin_hood::unordered_flat_map ended up performing close to best and was choosen because we already have it included in the project anyway // robin_hood::unordered_flat_map ended up performing close to best and was choosen because we already have it included in the project anyway
FORCEINLINE uint64 CalcCombinedAuxHash(LatteFetchShader* fetchShader, LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader) FORCE_INLINE uint64 CalcCombinedAuxHash(LatteFetchShader* fetchShader, LatteDecompilerShader* vertexShader, LatteDecompilerShader* pixelShader)
{ {
uint64 vsAuxHash = vertexShader ? LatteSHRC_CalcVSAuxHash(vertexShader, LatteGPUState.contextRegister) : 0; uint64 vsAuxHash = vertexShader ? LatteSHRC_CalcVSAuxHash(vertexShader, LatteGPUState.contextRegister) : 0;
uint64 psAuxHash = pixelShader ? LatteSHRC_CalcPSAuxHash(pixelShader, LatteGPUState.contextRegister) : 0; uint64 psAuxHash = pixelShader ? LatteSHRC_CalcPSAuxHash(pixelShader, LatteGPUState.contextRegister) : 0;

View File

@ -1027,6 +1027,21 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD
break; break;
} }
} }
// some existing graphic pack replacement shaders rely on uf_fragCoordScale despite the original shader not needing it. We handle these exceptions here
switch (shaderContext->shaderBaseHash)
{
case 0x21e6bc9b0cdbe8d7: case 0x37040a485a29d54e: case 0x37a4ec1a7dbc7391:
case 0x50e29e8929cea348: case 0x572a6cfa3943923d: case 0x59df1c7e1806366c:
case 0x6ea8b1aa69c0b6f7: case 0x88133ee405eaae28: case 0x95a5a89d62998e0d:
case 0x998a9f67e353657b: case 0x9f6adb9a651f84b9: case 0xa5e9d150276a805c:
case 0xa7f4801a8d29e333: case 0xbe99d80628d31127: case 0xc14019840473ff86:
case 0xc612390d4c70f430: case 0xcb0e6e8cbec4502a: case 0xe334517825fdd599:
case 0xe39a2a718bc419fe: case 0xfdf33c607cd1d737: case 0xff71dcd2ad4defdc:
shaderContext->analyzer.hasFragCoordAccess = true;
break;
default:
break;
}
} }
// analyze CF stack // analyze CF stack
sint32 cfCurrentStackDepth = 0; sint32 cfCurrentStackDepth = 0;

View File

@ -50,7 +50,7 @@ std::vector<MetalRenderer::DeviceInfo> MetalRenderer::GetDevices()
return result; return result;
} }
MetalRenderer::MetalRenderer() MetalRenderer::MetalRenderer() : Renderer(RendererAPI::Metal)
{ {
// Options // Options

View File

@ -153,11 +153,6 @@ public:
MetalRenderer(); MetalRenderer();
~MetalRenderer() override; ~MetalRenderer() override;
RendererAPI GetType() override
{
return RendererAPI::Metal;
}
static MetalRenderer* GetInstance() { static MetalRenderer* GetInstance() {
return static_cast<MetalRenderer*>(g_renderer.get()); return static_cast<MetalRenderer*>(g_renderer.get());
} }

View File

@ -111,7 +111,7 @@ static const GLenum glAlphaTestFunc[] =
GL_ALWAYS GL_ALWAYS
}; };
OpenGLRenderer::OpenGLRenderer() OpenGLRenderer::OpenGLRenderer() : Renderer(RendererAPI::OpenGL)
{ {
glRendererState.useTextureUploadBuffer = false; glRendererState.useTextureUploadBuffer = false;
if (glRendererState.useTextureUploadBuffer) if (glRendererState.useTextureUploadBuffer)

View File

@ -35,8 +35,6 @@ public:
OpenGLRenderer(); OpenGLRenderer();
~OpenGLRenderer(); ~OpenGLRenderer();
RendererAPI GetType() override { return RendererAPI::OpenGL; }
static OpenGLRenderer* GetInstance(); static OpenGLRenderer* GetInstance();
// imgui // imgui

View File

@ -50,9 +50,10 @@ public:
U32 U32
}; };
Renderer(RendererAPI api) : m_rendererAPI(api) {};
virtual ~Renderer() = default; virtual ~Renderer() = default;
virtual RendererAPI GetType() = 0; RendererAPI GetType() const { return m_rendererAPI; }
virtual void Initialize(); virtual void Initialize();
virtual void Shutdown(); virtual void Shutdown();
@ -162,6 +163,7 @@ public:
protected: protected:
virtual void GetVendorInformation() { } virtual void GetVendorInformation() { }
RendererAPI m_rendererAPI;
GfxVendor m_vendor = GfxVendor::Generic; GfxVendor m_vendor = GfxVendor::Generic;
static uint8 SRGBComponentToRGB(uint8 ci); static uint8 SRGBComponentToRGB(uint8 ci);

View File

@ -492,7 +492,7 @@ static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanc
#endif #endif
VulkanRenderer::VulkanRenderer() VulkanRenderer::VulkanRenderer() : Renderer(RendererAPI::Vulkan)
{ {
glslang::InitializeProcess(); glslang::InitializeProcess();
@ -908,7 +908,7 @@ VulkanRenderer::~VulkanRenderer()
defaultShaders.copySurface_psDepth2Color = nullptr; defaultShaders.copySurface_psDepth2Color = nullptr;
// destroy misc // destroy misc
for (auto& it : m_cmd_buffer_fences) for (auto& it : m_cmdBufferFences)
{ {
vkDestroyFence(m_logicalDevice, it, nullptr); vkDestroyFence(m_logicalDevice, it, nullptr);
it = VK_NULL_HANDLE; it = VK_NULL_HANDLE;
@ -958,11 +958,8 @@ VulkanRenderer::~VulkanRenderer()
VulkanRenderer* VulkanRenderer::GetInstance() VulkanRenderer* VulkanRenderer::GetInstance()
{ {
#ifdef CEMU_DEBUG_ASSERT cemu_assert_debug(g_renderer->GetType() == RendererAPI::Vulkan);
cemu_assert_debug(g_renderer && dynamic_cast<VulkanRenderer*>(g_renderer.get())); return static_cast<VulkanRenderer*>(g_renderer.get());
// Use #if here because dynamic_casts dont get optimized away even if the result is not stored as with cemu_assert_debug
#endif
return (VulkanRenderer*)g_renderer.get();
} }
void VulkanRenderer::InitializeSurface(const Vector2i& size, bool mainWindow) void VulkanRenderer::InitializeSurface(const Vector2i& size, bool mainWindow)
@ -1664,14 +1661,14 @@ void VulkanRenderer::CreateCommandPool()
void VulkanRenderer::CreateCommandBuffers() void VulkanRenderer::CreateCommandBuffers()
{ {
auto it = m_cmd_buffer_fences.begin(); auto it = m_cmdBufferFences.begin();
VkFenceCreateInfo fenceInfo{}; VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it); vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it);
++it; ++it;
fenceInfo.flags = 0; fenceInfo.flags = 0;
for (; it != m_cmd_buffer_fences.end(); ++it) for (; it != m_cmdBufferFences.end(); ++it)
{ {
vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it); vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it);
} }
@ -2131,7 +2128,7 @@ void VulkanRenderer::InitFirstCommandBuffer()
m_commandBufferSyncIndex = 0; m_commandBufferSyncIndex = 0;
m_state.currentCommandBuffer = m_commandBuffers[m_commandBufferIndex]; m_state.currentCommandBuffer = m_commandBuffers[m_commandBufferIndex];
vkResetFences(m_logicalDevice, 1, &m_cmd_buffer_fences[m_commandBufferIndex]); vkResetFences(m_logicalDevice, 1, &m_cmdBufferFences[m_commandBufferIndex]);
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
@ -2148,7 +2145,7 @@ void VulkanRenderer::ProcessFinishedCommandBuffers()
bool finishedCmdBuffers = false; bool finishedCmdBuffers = false;
while (m_commandBufferSyncIndex != m_commandBufferIndex) while (m_commandBufferSyncIndex != m_commandBufferIndex)
{ {
VkResult fenceStatus = vkGetFenceStatus(m_logicalDevice, m_cmd_buffer_fences[m_commandBufferSyncIndex]); VkResult fenceStatus = vkGetFenceStatus(m_logicalDevice, m_cmdBufferFences[m_commandBufferSyncIndex]);
if (fenceStatus == VK_SUCCESS) if (fenceStatus == VK_SUCCESS)
{ {
ProcessDestructionQueue(); ProcessDestructionQueue();
@ -2177,7 +2174,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer()
{ {
cemu_assert_debug(m_commandBufferSyncIndex != m_commandBufferIndex); cemu_assert_debug(m_commandBufferSyncIndex != m_commandBufferIndex);
// wait on least recently submitted command buffer // wait on least recently submitted command buffer
VkResult result = vkWaitForFences(m_logicalDevice, 1, &m_cmd_buffer_fences[m_commandBufferSyncIndex], true, UINT64_MAX); VkResult result = vkWaitForFences(m_logicalDevice, 1, &m_cmdBufferFences[m_commandBufferSyncIndex], true, UINT64_MAX);
if (result == VK_TIMEOUT) if (result == VK_TIMEOUT)
{ {
cemuLog_log(LogType::Force, "vkWaitForFences: Returned VK_TIMEOUT on infinite fence"); cemuLog_log(LogType::Force, "vkWaitForFences: Returned VK_TIMEOUT on infinite fence");
@ -2230,7 +2227,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor
submitInfo.pWaitDstStageMask = semWaitStageMask; submitInfo.pWaitDstStageMask = semWaitStageMask;
submitInfo.pWaitSemaphores = waitSemArray; submitInfo.pWaitSemaphores = waitSemArray;
const VkResult result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_cmd_buffer_fences[m_commandBufferIndex]); const VkResult result = vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_cmdBufferFences[m_commandBufferIndex]);
if (result != VK_SUCCESS) if (result != VK_SUCCESS)
UnrecoverableError(fmt::format("failed to submit command buffer. Error {}", result).c_str()); UnrecoverableError(fmt::format("failed to submit command buffer. Error {}", result).c_str());
m_numSubmittedCmdBuffers++; m_numSubmittedCmdBuffers++;
@ -2251,7 +2248,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor
m_state.currentCommandBuffer = m_commandBuffers[m_commandBufferIndex]; m_state.currentCommandBuffer = m_commandBuffers[m_commandBufferIndex];
vkResetFences(m_logicalDevice, 1, &m_cmd_buffer_fences[m_commandBufferIndex]); vkResetFences(m_logicalDevice, 1, &m_cmdBufferFences[m_commandBufferIndex]);
vkResetCommandBuffer(m_state.currentCommandBuffer, 0); vkResetCommandBuffer(m_state.currentCommandBuffer, 0);
VkCommandBufferBeginInfo beginInfo{}; VkCommandBufferBeginInfo beginInfo{};

View File

@ -89,7 +89,7 @@ public:
}; };
using DescriptorSetCache = ska::flat_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>>; using DescriptorSetCache = ska::flat_hash_map<uint64, VkDescriptorSetInfo*, direct_hash<uint64>>;
FORCEINLINE DescriptorSetCache& GetDescriptorSetCache(LatteConst::ShaderType shaderType) FORCE_INLINE DescriptorSetCache& GetDescriptorSetCache(LatteConst::ShaderType shaderType)
{ {
cemu_assert_debug(shaderType == LatteConst::ShaderType::Vertex || shaderType == LatteConst::ShaderType::Pixel || shaderType == LatteConst::ShaderType::Geometry); cemu_assert_debug(shaderType == LatteConst::ShaderType::Vertex || shaderType == LatteConst::ShaderType::Pixel || shaderType == LatteConst::ShaderType::Geometry);
return ds_cache[static_cast<size_t>(shaderType)]; return ds_cache[static_cast<size_t>(shaderType)];
@ -189,8 +189,6 @@ public:
VulkanRenderer(); VulkanRenderer();
virtual ~VulkanRenderer(); virtual ~VulkanRenderer();
RendererAPI GetType() override { return RendererAPI::Vulkan; }
static VulkanRenderer* GetInstance(); static VulkanRenderer* GetInstance();
void UnrecoverableError(const char* errMsg) const; void UnrecoverableError(const char* errMsg) const;
@ -661,8 +659,8 @@ private:
size_t m_commandBufferIndex = 0; // current buffer being filled size_t m_commandBufferIndex = 0; // current buffer being filled
size_t m_commandBufferSyncIndex = 0; // latest buffer that finished execution (updated on submit) size_t m_commandBufferSyncIndex = 0; // latest buffer that finished execution (updated on submit)
size_t m_commandBufferIDOfPrevFrame = 0; size_t m_commandBufferIDOfPrevFrame = 0;
std::array<size_t, kCommandBufferPoolSize> m_cmdBufferUniformRingbufIndices {}; // index in the uniform ringbuffer std::array<size_t, kCommandBufferPoolSize> m_cmdBufferUniformRingbufIndices {}; // read index in the uniform ringbuffer after the command buffer finishes
std::array<VkFence, kCommandBufferPoolSize> m_cmd_buffer_fences; std::array<VkFence, kCommandBufferPoolSize> m_cmdBufferFences;
std::array<VkCommandBuffer, kCommandBufferPoolSize> m_commandBuffers; std::array<VkCommandBuffer, kCommandBufferPoolSize> m_commandBuffers;
std::array<VkSemaphore, kCommandBufferPoolSize> m_commandBufferSemaphores; std::array<VkSemaphore, kCommandBufferPoolSize> m_commandBufferSemaphores;

View File

@ -320,23 +320,18 @@ uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span<ui
const uint32 bufferAlignmentM1 = m_featureControl.limits.calcUniformBufferAlignmentM1; const uint32 bufferAlignmentM1 = m_featureControl.limits.calcUniformBufferAlignmentM1;
const uint32 uniformSize = ((uint32)data.size() + bufferAlignmentM1) & ~bufferAlignmentM1; const uint32 uniformSize = ((uint32)data.size() + bufferAlignmentM1) & ~bufferAlignmentM1;
auto waitWhileCondition = [&](std::function<bool()> condition) { auto waitWhileCondition = [&]<class TCondition>(TCondition&& condition) {
while (condition()) while (condition())
{ {
if (m_commandBufferSyncIndex == m_commandBufferIndex) if (m_commandBufferSyncIndex == m_commandBufferIndex)
{ {
if (m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] != m_uniformVarBufferReadIndex) // we caught up to the current command buffer and there is still not enough space in the uniform ringbuffer
{ // in this case we just accept the corruption. In practice this is very unlikely to happen with recent reductions to uniform size
draw_endRenderPass(); // If this does continue to be an issue then the likely easiest solution is to start a new command buffer at the end of a draw sequence if the ringbuffer is filled beyond a certain threshold
SubmitCommandBuffer(); // submitting the command buffere here is complicated because we'd have to restore all of the dynamic state
} cemuLog_log(LogType::Force, "Vulkan: Uniform ring buffer overflowed. Expect visual corruption");
else cemu_assert_suspicious();
{ break;
// submitting work would not change readIndex, so there's no way for conditions based on it to change
cemuLog_log(LogType::Force, "draw call overflowed and corrupted uniform ringbuffer. expect visual corruption");
cemu_assert_suspicious();
break;
}
} }
WaitForNextFinishedCommandBuffer(); WaitForNextFinishedCommandBuffer();
} }
@ -1650,7 +1645,7 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
} }
else else
{ {
__debugbreak(); // should never happen unless draw_prepareDescriptorSets can fail? cemu_assert(false); // should never happen unless draw_prepareDescriptorSets can fail?
} }
draw_setRenderPass(); draw_setRenderPass();

View File

@ -199,10 +199,9 @@ void GX2CopySurfaceInternal(GX2Surface* srcSurface, uint32 srcMip, uint32 srcSli
return; return;
} }
// make sure formats are compatible // make sure formats are compatible
if( srcHwFormat != dstHwFormat ) if( surfOutSrc.bpp != surfOutDst.bpp )
{ {
// mismatching format cemuLog_logDebug(LogType::Force, "GX2CopySurface(): Format bpp mismatch (src=0x{:04x} dst=0x{:04x})", (sint32)srcFormat, (sint32)dstFormat);
cemuLog_logDebug(LogType::Force, "GX2CopySurface(): Format mismatch (src=0x{:04x} dst=0x{:04x})", (sint32)srcFormat, (sint32)dstFormat);
return; return;
} }
// get input pointer // get input pointer

View File

@ -28,6 +28,7 @@ add_library(CemuUtil
helpers/MapAdaptor.h helpers/MapAdaptor.h
helpers/MemoryPool.h helpers/MemoryPool.h
helpers/ringbuffer.h helpers/ringbuffer.h
helpers/StateHasher.h
helpers/Semaphore.h helpers/Semaphore.h
helpers/Serializer.cpp helpers/Serializer.cpp
helpers/Serializer.h helpers/Serializer.h

View File

@ -5,27 +5,27 @@
class DualStateHasher class DualStateHasher
{ {
public: public:
FORCEINLINE DualStateHasher() FORCE_INLINE DualStateHasher()
{ {
m_h0 = 0x9E3779B97F4A7C15; m_h0 = 0x9E3779B97F4A7C15;
m_h1 = 0xC2B2AE3D27D4EB4F; m_h1 = 0xC2B2AE3D27D4EB4F;
}; };
FORCEINLINE void MixIn(uint64 a, uint64 b) FORCE_INLINE void MixIn(uint64 a, uint64 b)
{ {
uint64 tmp = m_h1; uint64 tmp = m_h1;
m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL; m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL;
m_h0 = (tmp ^ b) * 0x165667B19E3779F9ULL; m_h0 = (tmp ^ b) * 0x165667B19E3779F9ULL;
} }
FORCEINLINE void MixInSingle(uint64 a) FORCE_INLINE void MixInSingle(uint64 a)
{ {
uint64 tmp = m_h1; uint64 tmp = m_h1;
m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL; m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL;
m_h0 = tmp; m_h0 = tmp;
} }
FORCEINLINE uint64 Finish() FORCE_INLINE uint64 Finish()
{ {
uint64 combined = m_h0 ^ std::rotl(m_h1, 31); uint64 combined = m_h0 ^ std::rotl(m_h1, 31);
combined ^= combined >> 33; combined ^= combined >> 33;