diff --git a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp index e7fb4893..71b4a145 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferData.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferData.cpp @@ -194,17 +194,23 @@ void LatteBufferCache_Sync(uint32 maxIndex, uint32 baseInstance, uint32 instance 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 + uint32 maxInstance = baseInstance + instanceCount - 1; + bool hasBufferChange = attribBufferDirtyMask != 0; if ( maxIndex > s_vtxStateMaxIndex ) { attribBufferDirtyMask = 0xFFFFFFFF; s_vtxStateMaxIndex = maxIndex; } - uint32 maxInstance = baseInstance + instanceCount - 1; if ( maxInstance > s_vtxStateMaxInstance ) { attribBufferDirtyMask = 0xFFFFFFFF; s_vtxStateMaxInstance = maxInstance; } + if (hasBufferChange) + { + s_vtxStateMaxIndex = maxIndex; + s_vtxStateMaxInstance = maxInstance; + } } else { diff --git a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp index 42ade0a6..82cf041c 100644 --- a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp +++ b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp @@ -1021,7 +1021,6 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt LatteCMDPtr cmd, cmdStart, cmdEnd; if (!drawPassCtx.PopCurrentCommandQueuePos(cmd, cmdStart, cmdEnd)) { - // cemuLog_log(LogType::Force, "[FastDrawMode] End due running out of commands"); drawPassCtx.endDrawPass(); 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_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 } 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(); else 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; } @@ -1120,11 +1118,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt } case IT_SET_CONTEXT_REG: { - // LatteCP_itSetRegistersGeneric(cmd, nWords); bool hasChanged = LatteCP_itSetRegistersGeneric2(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) { drawPassCtx.endDrawPass(); @@ -1144,10 +1138,8 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt case IT_SET_SAMPLER: { bool hasChanged = LatteCP_itSetRegistersGeneric2(cmdData, nWords, [](uint32 registerStart, uint32 registerEnd, bool regValuesChanged){}); - //cemuLog_log(LogType::Force, "[FastDrawMode] Set sampler (reg change = {} - filtered)", hasChanged); if (hasChanged) { - //cemuLog_log(LogType::Force, "[FastDrawMode] End due to sampler change"); drawPassCtx.endDrawPass(); drawPassCtx.PushCurrentCommandQueuePos(cmd, cmdStart, cmdEnd); return; @@ -1155,9 +1147,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt break; } default: - // unsupported 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); + // unallowed command for fast draw drawPassCtx.endDrawPass(); drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); return; @@ -1169,8 +1159,7 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt } else { - // unsupported command for fast draw - //cemuLog_log(LogType::Force, "[FastDrawMode] End due to uknown packet 0x{:08x}", itHeader); + // unallowed command for fast draw drawPassCtx.endDrawPass(); drawPassCtx.PushCurrentCommandQueuePos(cmdBeforeCommand, cmdStart, cmdEnd); return; diff --git a/src/Cafe/HW/Latte/Core/LatteShader.cpp b/src/Cafe/HW/Latte/Core/LatteShader.cpp index 38fc5504..4e89b02f 100644 --- a/src/Cafe/HW/Latte/Core/LatteShader.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShader.cpp @@ -1062,7 +1062,7 @@ robin_hood::unordered_flat_map s // folly::F14FastMap // 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 psAuxHash = pixelShader ? LatteSHRC_CalcPSAuxHash(pixelShader, LatteGPUState.contextRegister) : 0; diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp index 6982b408..6fc279ba 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerAnalyzer.cpp @@ -1027,6 +1027,21 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD 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 sint32 cfCurrentStackDepth = 0; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index a694411b..878bcb9e 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -50,7 +50,7 @@ std::vector MetalRenderer::GetDevices() return result; } -MetalRenderer::MetalRenderer() +MetalRenderer::MetalRenderer() : Renderer(RendererAPI::Metal) { // Options diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index eadad94f..c75f12bf 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -153,11 +153,6 @@ public: MetalRenderer(); ~MetalRenderer() override; - RendererAPI GetType() override - { - return RendererAPI::Metal; - } - static MetalRenderer* GetInstance() { return static_cast(g_renderer.get()); } diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp index a4d1966d..9d30da31 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp @@ -111,7 +111,7 @@ static const GLenum glAlphaTestFunc[] = GL_ALWAYS }; -OpenGLRenderer::OpenGLRenderer() +OpenGLRenderer::OpenGLRenderer() : Renderer(RendererAPI::OpenGL) { glRendererState.useTextureUploadBuffer = false; if (glRendererState.useTextureUploadBuffer) diff --git a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h index f691d8da..435ea427 100644 --- a/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h @@ -35,8 +35,6 @@ public: OpenGLRenderer(); ~OpenGLRenderer(); - RendererAPI GetType() override { return RendererAPI::OpenGL; } - static OpenGLRenderer* GetInstance(); // imgui diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.h b/src/Cafe/HW/Latte/Renderer/Renderer.h index 6a792fa2..6482f37a 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.h +++ b/src/Cafe/HW/Latte/Renderer/Renderer.h @@ -50,9 +50,10 @@ public: U32 }; + Renderer(RendererAPI api) : m_rendererAPI(api) {}; virtual ~Renderer() = default; - virtual RendererAPI GetType() = 0; + RendererAPI GetType() const { return m_rendererAPI; } virtual void Initialize(); virtual void Shutdown(); @@ -162,6 +163,7 @@ public: protected: virtual void GetVendorInformation() { } + RendererAPI m_rendererAPI; GfxVendor m_vendor = GfxVendor::Generic; static uint8 SRGBComponentToRGB(uint8 ci); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index bf9ae10c..eeba119e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -492,7 +492,7 @@ static void LinuxBreathOfTheWildWorkaround(VkInstance& instance, const VkInstanc #endif -VulkanRenderer::VulkanRenderer() +VulkanRenderer::VulkanRenderer() : Renderer(RendererAPI::Vulkan) { glslang::InitializeProcess(); @@ -908,7 +908,7 @@ VulkanRenderer::~VulkanRenderer() defaultShaders.copySurface_psDepth2Color = nullptr; // destroy misc - for (auto& it : m_cmd_buffer_fences) + for (auto& it : m_cmdBufferFences) { vkDestroyFence(m_logicalDevice, it, nullptr); it = VK_NULL_HANDLE; @@ -958,11 +958,8 @@ VulkanRenderer::~VulkanRenderer() VulkanRenderer* VulkanRenderer::GetInstance() { -#ifdef CEMU_DEBUG_ASSERT - cemu_assert_debug(g_renderer && dynamic_cast(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(); + cemu_assert_debug(g_renderer->GetType() == RendererAPI::Vulkan); + return static_cast(g_renderer.get()); } void VulkanRenderer::InitializeSurface(const Vector2i& size, bool mainWindow) @@ -1664,14 +1661,14 @@ void VulkanRenderer::CreateCommandPool() void VulkanRenderer::CreateCommandBuffers() { - auto it = m_cmd_buffer_fences.begin(); + auto it = m_cmdBufferFences.begin(); VkFenceCreateInfo fenceInfo{}; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it); ++it; fenceInfo.flags = 0; - for (; it != m_cmd_buffer_fences.end(); ++it) + for (; it != m_cmdBufferFences.end(); ++it) { vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &*it); } @@ -2131,7 +2128,7 @@ void VulkanRenderer::InitFirstCommandBuffer() m_commandBufferSyncIndex = 0; 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{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; @@ -2148,7 +2145,7 @@ void VulkanRenderer::ProcessFinishedCommandBuffers() bool finishedCmdBuffers = false; 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) { ProcessDestructionQueue(); @@ -2177,7 +2174,7 @@ void VulkanRenderer::WaitForNextFinishedCommandBuffer() { cemu_assert_debug(m_commandBufferSyncIndex != m_commandBufferIndex); // 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) { 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.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) UnrecoverableError(fmt::format("failed to submit command buffer. Error {}", result).c_str()); m_numSubmittedCmdBuffers++; @@ -2251,7 +2248,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor 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); VkCommandBufferBeginInfo beginInfo{}; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 4581e9e7..2909440f 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -89,7 +89,7 @@ public: }; using DescriptorSetCache = ska::flat_hash_map>; - 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); return ds_cache[static_cast(shaderType)]; @@ -189,8 +189,6 @@ public: VulkanRenderer(); virtual ~VulkanRenderer(); - RendererAPI GetType() override { return RendererAPI::Vulkan; } - static VulkanRenderer* GetInstance(); void UnrecoverableError(const char* errMsg) const; @@ -661,8 +659,8 @@ private: 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_commandBufferIDOfPrevFrame = 0; - std::array m_cmdBufferUniformRingbufIndices {}; // index in the uniform ringbuffer - std::array m_cmd_buffer_fences; + std::array m_cmdBufferUniformRingbufIndices {}; // read index in the uniform ringbuffer after the command buffer finishes + std::array m_cmdBufferFences; std::array m_commandBuffers; std::array m_commandBufferSemaphores; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 717d14fe..2b50d88f 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -320,23 +320,18 @@ uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span condition) { + auto waitWhileCondition = [&](TCondition&& condition) { while (condition()) { if (m_commandBufferSyncIndex == m_commandBufferIndex) { - if (m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] != m_uniformVarBufferReadIndex) - { - draw_endRenderPass(); - SubmitCommandBuffer(); - } - else - { - // 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; - } + // 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 + // 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 + // 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"); + cemu_assert_suspicious(); + break; } WaitForNextFinishedCommandBuffer(); } @@ -1650,7 +1645,7 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan } else { - __debugbreak(); // should never happen unless draw_prepareDescriptorSets can fail? + cemu_assert(false); // should never happen unless draw_prepareDescriptorSets can fail? } draw_setRenderPass(); diff --git a/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp b/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp index e4ac4443..09964f99 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Surface_Copy.cpp @@ -199,10 +199,9 @@ void GX2CopySurfaceInternal(GX2Surface* srcSurface, uint32 srcMip, uint32 srcSli return; } // make sure formats are compatible - if( srcHwFormat != dstHwFormat ) + if( surfOutSrc.bpp != surfOutDst.bpp ) { - // mismatching format - cemuLog_logDebug(LogType::Force, "GX2CopySurface(): Format mismatch (src=0x{:04x} dst=0x{:04x})", (sint32)srcFormat, (sint32)dstFormat); + cemuLog_logDebug(LogType::Force, "GX2CopySurface(): Format bpp mismatch (src=0x{:04x} dst=0x{:04x})", (sint32)srcFormat, (sint32)dstFormat); return; } // get input pointer diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index c170adc5..a23754ef 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -28,6 +28,7 @@ add_library(CemuUtil helpers/MapAdaptor.h helpers/MemoryPool.h helpers/ringbuffer.h + helpers/StateHasher.h helpers/Semaphore.h helpers/Serializer.cpp helpers/Serializer.h diff --git a/src/util/helpers/StateHasher.h b/src/util/helpers/StateHasher.h index e46a49ea..c061e9e5 100644 --- a/src/util/helpers/StateHasher.h +++ b/src/util/helpers/StateHasher.h @@ -5,27 +5,27 @@ class DualStateHasher { public: - FORCEINLINE DualStateHasher() + FORCE_INLINE DualStateHasher() { m_h0 = 0x9E3779B97F4A7C15; m_h1 = 0xC2B2AE3D27D4EB4F; }; - FORCEINLINE void MixIn(uint64 a, uint64 b) + FORCE_INLINE void MixIn(uint64 a, uint64 b) { uint64 tmp = m_h1; m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL; m_h0 = (tmp ^ b) * 0x165667B19E3779F9ULL; } - FORCEINLINE void MixInSingle(uint64 a) + FORCE_INLINE void MixInSingle(uint64 a) { uint64 tmp = m_h1; m_h1 = (m_h0 ^ a) * 0x85EBCA77C2B2AE63ULL; m_h0 = tmp; } - FORCEINLINE uint64 Finish() + FORCE_INLINE uint64 Finish() { uint64 combined = m_h0 ^ std::rotl(m_h1, 31); combined ^= combined >> 33;