diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fdc359a..f069c6fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,7 +238,7 @@ if (ENABLE_CUBEB) option(BUILD_TOOLS "" OFF) option(BUNDLE_SPEEX "" OFF) set(USE_WINMM OFF CACHE BOOL "") - add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL) + add_subdirectory("dependencies/cubeb" EXCLUDE_FROM_ALL SYSTEM) set_property(TARGET cubeb PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") add_library(cubeb::cubeb ALIAS cubeb) endif() diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 1d9ebd06..40d26a67 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.cpp @@ -403,7 +403,7 @@ void cemu_initForGame() // replace any known function signatures with our HLE implementations and patch bugs in the games GamePatch_scan(); } - LatteGPUState.alwaysDisplayDRC = ActiveSettings::DisplayDRCEnabled(); + LatteGPUState.isDRCPrimary = ActiveSettings::DisplayDRCEnabled(); InfoLog_PrintActiveSettings(); Latte_Start(); // check for debugger entrypoint bp diff --git a/src/Cafe/HW/Latte/Core/Latte.h b/src/Cafe/HW/Latte/Core/Latte.h index e8cb2be4..e5e9dd5c 100644 --- a/src/Cafe/HW/Latte/Core/Latte.h +++ b/src/Cafe/HW/Latte/Core/Latte.h @@ -52,7 +52,7 @@ struct LatteGPUState_t uint32 gx2InitCalled; // incremented every time GX2Init() is called // OpenGL control uint32 glVendor; // GLVENDOR_* - bool alwaysDisplayDRC = false; + bool isDRCPrimary = false; // temporary (replace with proper solution later) bool tvBufferUsesSRGB; bool drcBufferUsesSRGB; diff --git a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp index aca80469..b0cc15c6 100644 --- a/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp +++ b/src/Cafe/HW/Latte/Core/LatteRenderTarget.cpp @@ -989,8 +989,6 @@ void LatteRenderTarget_copyToBackbuffer(LatteTextureView* textureView, bool isPa g_renderer->ImguiEnd(); } -bool ctrlTabHotkeyPressed = false; - void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uint32 colorBufferWidth, uint32 colorBufferHeight, uint32 colorBufferSliceIndex, uint32 colorBufferFormat, uint32 colorBufferPitch, Latte::E_HWTILEMODE colorBufferTilemode, uint32 colorBufferSwizzle, uint32 renderTarget) { cemu_assert_debug(colorBufferSliceIndex == 0); // todo - support for non-zero slice @@ -1000,38 +998,31 @@ void LatteRenderTarget_itHLECopyColorBufferToScanBuffer(MPTR colorBufferPtr, uin return; } + auto getVPADScreenActive = [](size_t n) -> std::pair { + auto controller = InputManager::instance().get_vpad_controller(n); + if (!controller) + return {false,false}; + auto pressed = controller->is_screen_active(); + auto toggle = controller->is_screen_active_toggle(); + return {pressed && !toggle, pressed && toggle}; + }; + const bool tabPressed = gui_isKeyDown(PlatformKeyCodes::TAB); const bool ctrlPressed = gui_isKeyDown(PlatformKeyCodes::LCONTROL); + const auto [vpad0Active, vpad0Toggle] = getVPADScreenActive(0); + const auto [vpad1Active, vpad1Toggle] = getVPADScreenActive(1); - bool showDRC = swkbd_hasKeyboardInputHook() == false && tabPressed; - bool& alwaysDisplayDRC = LatteGPUState.alwaysDisplayDRC; + const bool altScreenRequested = (!ctrlPressed && tabPressed) || vpad0Active || vpad1Active; + const bool togglePressed = (ctrlPressed && tabPressed) || vpad0Toggle || vpad1Toggle; + static bool togglePressedLast = false; - if (ctrlPressed && tabPressed) - { - if (ctrlTabHotkeyPressed == false) - { - alwaysDisplayDRC = !alwaysDisplayDRC; - ctrlTabHotkeyPressed = true; - } - } - else - ctrlTabHotkeyPressed = false; + bool& isDRCPrimary = LatteGPUState.isDRCPrimary; - if (alwaysDisplayDRC) - showDRC = !tabPressed; + if(togglePressed && !togglePressedLast) + isDRCPrimary = !isDRCPrimary; + togglePressedLast = togglePressed; - if (!showDRC) - { - auto controller = InputManager::instance().get_vpad_controller(0); - if (controller && controller->is_screen_active()) - showDRC = true; - if (!showDRC) - { - controller = InputManager::instance().get_vpad_controller(1); - if (controller && controller->is_screen_active()) - showDRC = true; - } - } + bool showDRC = swkbd_hasKeyboardInputHook() == false && (isDRCPrimary ^ altScreenRequested); if ((renderTarget & RENDER_TARGET_DRC) && g_renderer->IsPadWindowActive()) LatteRenderTarget_copyToBackbuffer(texView, true); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp index 75ff02ba..56a3ab12 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp @@ -146,8 +146,17 @@ void SwapchainInfoVk::Create() UnrecoverableError("Failed to create semaphore for swapchain acquire"); } + VkFenceCreateInfo fenceInfo = {}; + fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; + result = vkCreateFence(m_logicalDevice, &fenceInfo, nullptr, &m_imageAvailableFence); + if (result != VK_SUCCESS) + UnrecoverableError("Failed to create fence for swapchain"); + m_acquireIndex = 0; hasDefinedSwapchainImage = false; + + m_queueDepth = 0; } void SwapchainInfoVk::Cleanup() @@ -177,6 +186,12 @@ void SwapchainInfoVk::Cleanup() m_swapchainFramebuffers.clear(); + if (m_imageAvailableFence) + { + WaitAvailableFence(); + vkDestroyFence(m_logicalDevice, m_imageAvailableFence, nullptr); + m_imageAvailableFence = nullptr; + } if (m_swapchain) { vkDestroySwapchainKHR(m_logicalDevice, m_swapchain, nullptr); @@ -189,6 +204,18 @@ bool SwapchainInfoVk::IsValid() const return m_swapchain && !m_acquireSemaphores.empty(); } +void SwapchainInfoVk::WaitAvailableFence() +{ + if(m_awaitableFence != VK_NULL_HANDLE) + vkWaitForFences(m_logicalDevice, 1, &m_awaitableFence, VK_TRUE, UINT64_MAX); + m_awaitableFence = VK_NULL_HANDLE; +} + +void SwapchainInfoVk::ResetAvailableFence() const +{ + vkResetFences(m_logicalDevice, 1, &m_imageAvailableFence); +} + VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore() { VkSemaphore ret = m_currentSemaphore; @@ -198,8 +225,10 @@ VkSemaphore SwapchainInfoVk::ConsumeAcquireSemaphore() bool SwapchainInfoVk::AcquireImage() { + ResetAvailableFence(); + VkSemaphore acquireSemaphore = m_acquireSemaphores[m_acquireIndex]; - VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, nullptr, &swapchainImageIndex); + VkResult result = vkAcquireNextImageKHR(m_logicalDevice, m_swapchain, 1'000'000'000, acquireSemaphore, m_imageAvailableFence, &swapchainImageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) m_shouldRecreate = true; if (result == VK_TIMEOUT) @@ -216,6 +245,7 @@ bool SwapchainInfoVk::AcquireImage() return false; } m_currentSemaphore = acquireSemaphore; + m_awaitableFence = m_imageAvailableFence; m_acquireIndex = (m_acquireIndex + 1) % m_swapchainImages.size(); return true; @@ -319,6 +349,7 @@ VkExtent2D SwapchainInfoVk::ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& cap VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector& modes) { + m_maxQueued = 0; const auto vsyncState = (VSync)GetConfig().vsync.GetValue(); if (vsyncState == VSync::MAILBOX) { @@ -345,6 +376,7 @@ VkPresentModeKHR SwapchainInfoVk::ChoosePresentMode(const std::vector m_acquireSemaphores; // indexed by m_acquireIndex + VkFence m_imageAvailableFence{}; + VkFence m_awaitableFence = VK_NULL_HANDLE; VkSemaphore m_currentSemaphore = VK_NULL_HANDLE; std::array m_swapchainQueueFamilyIndices; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h index 0489bb4e..6bde2a0b 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h @@ -188,6 +188,9 @@ VKFUNC_DEVICE(vkCmdPipelineBarrier2KHR); VKFUNC_DEVICE(vkCmdBeginRenderingKHR); VKFUNC_DEVICE(vkCmdEndRenderingKHR); +// khr_present_wait +VKFUNC_DEVICE(vkWaitForPresentKHR); + // transform feedback extension VKFUNC_DEVICE(vkCmdBindTransformFeedbackBuffersEXT); VKFUNC_DEVICE(vkCmdBeginTransformFeedbackEXT); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 2f776f7a..25d37cad 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -47,7 +47,9 @@ const std::vector kOptionalDeviceExtensions = VK_EXT_FILTER_CUBIC_EXTENSION_NAME, // not supported by any device yet VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, - VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME + VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME, + VK_KHR_PRESENT_WAIT_EXTENSION_NAME, + VK_KHR_PRESENT_ID_EXTENSION_NAME }; const std::vector kRequiredDeviceExtensions = @@ -252,12 +254,24 @@ void VulkanRenderer::GetDeviceFeatures() pcc.pNext = prevStruct; prevStruct = &pcc; + VkPhysicalDevicePresentIdFeaturesKHR pidf{}; + pidf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR; + pidf.pNext = prevStruct; + prevStruct = &pidf; + + VkPhysicalDevicePresentWaitFeaturesKHR pwf{}; + pwf.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR; + pwf.pNext = prevStruct; + prevStruct = &pwf; + VkPhysicalDeviceFeatures2 physicalDeviceFeatures2{}; physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; physicalDeviceFeatures2.pNext = prevStruct; vkGetPhysicalDeviceFeatures2(m_physicalDevice, &physicalDeviceFeatures2); + cemuLog_log(LogType::Force, "Vulkan: present_wait extension: {}", (pwf.presentWait && pidf.presentId) ? "supported" : "unsupported"); + /* Get Vulkan device properties and limits */ VkPhysicalDeviceFloatControlsPropertiesKHR pfcp{}; prevStruct = nullptr; @@ -490,6 +504,24 @@ VulkanRenderer::VulkanRenderer() customBorderColorFeature.customBorderColors = VK_TRUE; customBorderColorFeature.customBorderColorWithoutFormat = VK_TRUE; } + // enable VK_KHR_present_id + VkPhysicalDevicePresentIdFeaturesKHR presentIdFeature{}; + if(m_featureControl.deviceExtensions.present_wait) + { + presentIdFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR; + presentIdFeature.pNext = deviceExtensionFeatures; + deviceExtensionFeatures = &presentIdFeature; + presentIdFeature.presentId = VK_TRUE; + } + // enable VK_KHR_present_wait + VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeature{}; + if(m_featureControl.deviceExtensions.present_wait) + { + presentWaitFeature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR; + presentWaitFeature.pNext = deviceExtensionFeatures; + deviceExtensionFeatures = &presentWaitFeature; + presentWaitFeature.presentWait = VK_TRUE; + } std::vector used_extensions; VkDeviceCreateInfo createInfo = CreateDeviceCreateInfo(queueCreateInfos, deviceFeatures, deviceExtensionFeatures, used_extensions); @@ -1047,6 +1079,10 @@ VkDeviceCreateInfo VulkanRenderer::CreateDeviceCreateInfo(const std::vectorcleanupBuffers(m_countCommandBufferFinished); m_countCommandBufferFinished++; @@ -1948,6 +1986,7 @@ void VulkanRenderer::SubmitCommandBuffer(VkSemaphore signalSemaphore, VkSemaphor cemuLog_logDebug(LogType::Force, "Vulkan: Waiting for available command buffer..."); WaitForNextFinishedCommandBuffer(); } + m_cmdBufferUniformRingbufIndices[nextCmdBufferIndex] = m_cmdBufferUniformRingbufIndices[m_commandBufferIndex]; m_commandBufferIndex = nextCmdBufferIndex; @@ -2695,11 +2734,21 @@ void VulkanRenderer::SwapBuffer(bool mainWindow) ClearColorImageRaw(chainInfo.m_swapchainImages[chainInfo.swapchainImageIndex], 0, 0, clearColor, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); } + const size_t currentFrameCmdBufferID = GetCurrentCommandBufferId(); + VkSemaphore presentSemaphore = chainInfo.m_presentSemaphores[chainInfo.swapchainImageIndex]; SubmitCommandBuffer(presentSemaphore); // submit all command and signal semaphore cemu_assert_debug(m_numSubmittedCmdBuffers > 0); + // wait for the previous frame to finish rendering + WaitCommandBufferFinished(m_commandBufferIDOfPrevFrame); + m_commandBufferIDOfPrevFrame = currentFrameCmdBufferID; + + chainInfo.WaitAvailableFence(); + + VkPresentIdKHR presentId = {}; + VkPresentInfoKHR presentInfo = {}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.swapchainCount = 1; @@ -2709,6 +2758,24 @@ void VulkanRenderer::SwapBuffer(bool mainWindow) presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = &presentSemaphore; + // if present_wait is available and enabled, add frame markers to present requests + // and limit the number of queued present operations + if (m_featureControl.deviceExtensions.present_wait && chainInfo.m_maxQueued > 0) + { + presentId.sType = VK_STRUCTURE_TYPE_PRESENT_ID_KHR; + presentId.swapchainCount = 1; + presentId.pPresentIds = &chainInfo.m_presentId; + + presentInfo.pNext = &presentId; + + if(chainInfo.m_queueDepth >= chainInfo.m_maxQueued) + { + uint64 waitFrameId = chainInfo.m_presentId - chainInfo.m_queueDepth; + vkWaitForPresentKHR(m_logicalDevice, chainInfo.m_swapchain, waitFrameId, 40'000'000); + chainInfo.m_queueDepth--; + } + } + VkResult result = vkQueuePresentKHR(m_presentQueue, &presentInfo); if (result < 0 && result != VK_ERROR_OUT_OF_DATE_KHR) { @@ -2717,6 +2784,12 @@ void VulkanRenderer::SwapBuffer(bool mainWindow) if(result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) chainInfo.m_shouldRecreate = true; + if(result >= 0) + { + chainInfo.m_queueDepth++; + chainInfo.m_presentId++; + } + chainInfo.hasDefinedSwapchainImage = false; chainInfo.swapchainImageIndex = -1; @@ -3491,13 +3564,13 @@ void VulkanRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, switch (shaderType) { case LatteConst::ShaderType::Vertex: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].unformBufferOffset[bufferIndex] = offset; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].uniformBufferOffset[bufferIndex] = offset; break; case LatteConst::ShaderType::Geometry: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].unformBufferOffset[bufferIndex] = offset; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].uniformBufferOffset[bufferIndex] = offset; break; case LatteConst::ShaderType::Pixel: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].unformBufferOffset[bufferIndex] = offset; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].uniformBufferOffset[bufferIndex] = offset; break; default: cemu_assert_debug(false); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 2b819e15..867647a3 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -449,6 +449,7 @@ private: bool synchronization2 = false; // VK_KHR_synchronization2 bool dynamic_rendering = false; // VK_KHR_dynamic_rendering bool shader_float_controls = false; // VK_KHR_shader_float_controls + bool present_wait = false; // VK_KHR_present_wait }deviceExtensions; struct @@ -591,6 +592,7 @@ private: bool m_uniformVarBufferMemoryIsCoherent{false}; uint8* m_uniformVarBufferPtr = nullptr; uint32 m_uniformVarBufferWriteIndex = 0; + uint32 m_uniformVarBufferReadIndex = 0; // transform feedback ringbuffer VkBuffer m_xfbRingBuffer = VK_NULL_HANDLE; @@ -636,6 +638,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_commandBuffers; std::array m_commandBufferSemaphores; @@ -658,7 +662,7 @@ private: uint32 uniformVarBufferOffset[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT]; struct { - uint32 unformBufferOffset[LATTE_NUM_MAX_UNIFORM_BUFFERS]; + uint32 uniformBufferOffset[LATTE_NUM_MAX_UNIFORM_BUFFERS]; }shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_COUNT]; }dynamicOffsetInfo{}; diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index d41022ac..63c16595 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -375,24 +375,20 @@ float s_vkUniformData[512 * 4]; void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader) { - auto GET_UNIFORM_DATA_PTR = [&](size_t index) { return s_vkUniformData + (index / 4); }; + auto GET_UNIFORM_DATA_PTR = [](size_t index) { return s_vkUniformData + (index / 4); }; sint32 shaderAluConst; - sint32 shaderUniformRegisterOffset; switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: shaderAluConst = 0x400; - shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START; break; case LatteConst::ShaderType::Pixel: shaderAluConst = 0; - shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START; break; case LatteConst::ShaderType::Geometry: shaderAluConst = 0; // geometry shader has no ALU const - shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START; break; default: UNREACHABLE; @@ -445,7 +441,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt } if (shader->uniform.loc_verticesPerInstance >= 0) { - *(int*)(s_vkUniformData + ((size_t)shader->uniform.loc_verticesPerInstance / 4)) = m_streamoutState.verticesPerInstance; + *(int*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_verticesPerInstance) = m_streamoutState.verticesPerInstance; for (sint32 b = 0; b < LATTE_NUM_STREAMOUT_BUFFER; b++) { if (shader->uniform.loc_streamoutBufferBase[b] >= 0) @@ -455,26 +451,63 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt } } // upload - if ((m_uniformVarBufferWriteIndex + shader->uniform.uniformRangeSize + 1024) > UNIFORMVAR_RINGBUFFER_SIZE) + const uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1; + const uint32 uniformSize = (shader->uniform.uniformRangeSize + bufferAlignmentM1) & ~bufferAlignmentM1; + + auto waitWhileCondition = [&](std::function 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; + } + } + WaitForNextFinishedCommandBuffer(); + } + }; + + // wrap around if it doesnt fit consecutively + if (m_uniformVarBufferWriteIndex + uniformSize > UNIFORMVAR_RINGBUFFER_SIZE) { + waitWhileCondition([&]() { + return m_uniformVarBufferReadIndex > m_uniformVarBufferWriteIndex || m_uniformVarBufferReadIndex == 0; + }); m_uniformVarBufferWriteIndex = 0; } - uint32 bufferAlignmentM1 = std::max(m_featureControl.limits.minUniformBufferOffsetAlignment, m_featureControl.limits.nonCoherentAtomSize) - 1; + + auto ringBufRemaining = [&]() { + ssize_t ringBufferUsedBytes = (ssize_t)m_uniformVarBufferWriteIndex - m_uniformVarBufferReadIndex; + if (ringBufferUsedBytes < 0) + ringBufferUsedBytes += UNIFORMVAR_RINGBUFFER_SIZE; + return UNIFORMVAR_RINGBUFFER_SIZE - 1 - ringBufferUsedBytes; + }; + waitWhileCondition([&]() { + return ringBufRemaining() < uniformSize; + }); + const uint32 uniformOffset = m_uniformVarBufferWriteIndex; memcpy(m_uniformVarBufferPtr + uniformOffset, s_vkUniformData, shader->uniform.uniformRangeSize); - m_uniformVarBufferWriteIndex += shader->uniform.uniformRangeSize; - m_uniformVarBufferWriteIndex = (m_uniformVarBufferWriteIndex + bufferAlignmentM1) & ~bufferAlignmentM1; + m_uniformVarBufferWriteIndex += uniformSize; // update dynamic offset dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformOffset; // flush if not coherent if (!m_uniformVarBufferMemoryIsCoherent) { - uint32 nonCoherentAtomSizeM1 = m_featureControl.limits.nonCoherentAtomSize - 1; VkMappedMemoryRange flushedRange{}; flushedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; flushedRange.memory = m_uniformVarBufferMemory; flushedRange.offset = uniformOffset; - flushedRange.size = (shader->uniform.uniformRangeSize + nonCoherentAtomSizeM1) & ~nonCoherentAtomSizeM1; + flushedRange.size = uniformSize; vkFlushMappedMemoryRanges(m_logicalDevice, 1, &flushedRange); } } @@ -494,7 +527,7 @@ void VulkanRenderer::draw_prepareDynamicOffsetsForDescriptorSet(uint32 shaderSta { for (auto& itr : pipeline_info->dynamicOffsetInfo.list_uniformBuffers[shaderStageIndex]) { - dynamicOffsets[numDynOffsets] = dynamicOffsetInfo.shaderUB[shaderStageIndex].unformBufferOffset[itr]; + dynamicOffsets[numDynOffsets] = dynamicOffsetInfo.shaderUB[shaderStageIndex].uniformBufferOffset[itr]; numDynOffsets++; } } @@ -1357,6 +1390,24 @@ void VulkanRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 return; } + // prepare streamout + m_streamoutState.verticesPerInstance = count; + LatteStreamout_PrepareDrawcall(count, instanceCount); + + // update uniform vars + LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader(); + LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader(); + LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader(); + + if (vertexShader) + uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader); + if (pixelShader) + uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader); + if (geometryShader) + uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader); + // store where the read pointer should go after command buffer execution + m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex; + // process index data const LattePrimitiveMode primitiveMode = static_cast(LatteGPUState.contextRegister[mmVGT_PRIMITIVE_TYPE]); @@ -1410,22 +1461,6 @@ void VulkanRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount); } - // prepare streamout - m_streamoutState.verticesPerInstance = count; - LatteStreamout_PrepareDrawcall(count, instanceCount); - - // update uniform vars - LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader(); - LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader(); - LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader(); - - if (vertexShader) - uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader); - if (pixelShader) - uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader); - if (geometryShader) - uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader); - PipelineInfo* pipeline_info; if (!isFirst) @@ -1613,13 +1648,13 @@ void VulkanRenderer::draw_updateUniformBuffersDirectAccess(LatteDecompilerShader switch (shaderType) { case LatteConst::ShaderType::Vertex: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; break; case LatteConst::ShaderType::Geometry: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; break; case LatteConst::ShaderType::Pixel: - dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].unformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; + dynamicOffsetInfo.shaderUB[VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT].uniformBufferOffset[bufferIndex] = physicalAddr - m_importedMemBaseAddress; break; default: UNREACHABLE; diff --git a/src/Cafe/IOSU/legacy/iosu_boss.cpp b/src/Cafe/IOSU/legacy/iosu_boss.cpp index 760e5b66..7ab25f68 100644 --- a/src/Cafe/IOSU/legacy/iosu_boss.cpp +++ b/src/Cafe/IOSU/legacy/iosu_boss.cpp @@ -137,6 +137,10 @@ namespace iosu this->task_settings.taskType = settings->taskType; curl = std::shared_ptr(curl_easy_init(), curl_easy_cleanup); + if(GetConfig().proxy_server.GetValue() != "") + { + curl_easy_setopt(curl.get(), CURLOPT_PROXY, GetConfig().proxy_server.GetValue().c_str()); + } } }; diff --git a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp index 309394e6..c87cbd39 100644 --- a/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp +++ b/src/Cafe/OS/libs/nn_olv/nn_olv_OfflineDB.cpp @@ -112,7 +112,7 @@ namespace nn nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadPostDataList(coreinit::OSEvent* event, DownloadedTopicData* downloadedTopicData, DownloadedPostData* downloadedPostData, uint32be* postCountOut, uint32 maxCount, DownloadPostDataListParam* param) { - scope_exit _se([&](){coreinit::OSSignalEvent(event);}); + stdx::scope_exit _se([&](){coreinit::OSSignalEvent(event);}); uint64 titleId = CafeSystem::GetForegroundTitleId(); @@ -184,7 +184,7 @@ namespace nn nnResult _Async_OfflineDB_DownloadPostDataListParam_DownloadExternalImageData(coreinit::OSEvent* event, DownloadedDataBase* _this, void* imageDataOut, uint32be* imageSizeOut, uint32 maxSize) { - scope_exit _se([&](){coreinit::OSSignalEvent(event);}); + stdx::scope_exit _se([&](){coreinit::OSSignalEvent(event);}); if (!_this->TestFlags(_this, DownloadedDataBase::FLAGS::HAS_EXTERNAL_IMAGE)) return OLV_RESULT_MISSING_DATA; diff --git a/src/Cafe/OS/libs/nsyshid/Infinity.cpp b/src/Cafe/OS/libs/nsyshid/Infinity.cpp index ab44ef4a..ac793109 100644 --- a/src/Cafe/OS/libs/nsyshid/Infinity.cpp +++ b/src/Cafe/OS/libs/nsyshid/Infinity.cpp @@ -1017,11 +1017,7 @@ namespace nsyshid std::array InfinityUSB::GenerateInfinityFigureKey(const std::vector& sha1Data) { std::array digest = {}; - SHA_CTX ctx; - SHA1_Init(&ctx); - SHA1_Update(&ctx, sha1Data.data(), sha1Data.size()); - SHA1_Final(digest.data(), &ctx); - OPENSSL_cleanse(&ctx, sizeof(ctx)); + SHA1(sha1Data.data(), sha1Data.size(), digest.data()); // Infinity AES keys are the first 16 bytes of the SHA1 Digest, every set of 4 bytes need to be // reversed due to endianness std::array key = {}; diff --git a/src/Cafe/OS/libs/ntag/ntag.cpp b/src/Cafe/OS/libs/ntag/ntag.cpp index 24617791..7c95a1a1 100644 --- a/src/Cafe/OS/libs/ntag/ntag.cpp +++ b/src/Cafe/OS/libs/ntag/ntag.cpp @@ -509,7 +509,7 @@ namespace ntag noftHeader->writeCount = _swapEndianU16(_swapEndianU16(noftHeader->writeCount) + 1); } - memcpy(decryptedBuffer + 0x20, noftHeader, sizeof(noftHeader)); + memcpy(decryptedBuffer + 0x20, noftHeader, sizeof(NTAGNoftHeader)); memcpy(decryptedBuffer + _swapEndianU16(rwHeader->offset), data, dataSize); // Encrypt diff --git a/src/Cafe/OS/libs/snd_core/ax_out.cpp b/src/Cafe/OS/libs/snd_core/ax_out.cpp index 68b05165..40b9c643 100644 --- a/src/Cafe/OS/libs/snd_core/ax_out.cpp +++ b/src/Cafe/OS/libs/snd_core/ax_out.cpp @@ -522,10 +522,10 @@ namespace snd_core // called periodically to check for AX updates void AXOut_update() { - constexpr auto kTimeout = std::chrono::duration_cast(std::chrono::milliseconds(((IAudioAPI::kBlockCount * 3) / 4) * (AX_FRAMES_PER_GROUP * 3))); - constexpr auto kWaitDuration = std::chrono::duration_cast(std::chrono::milliseconds(3)); - constexpr auto kWaitDurationFast = std::chrono::duration_cast(std::chrono::microseconds(2900)); - constexpr auto kWaitDurationMinimum = std::chrono::duration_cast(std::chrono::microseconds(1700)); + constexpr static auto kTimeout = std::chrono::duration_cast(std::chrono::milliseconds(((IAudioAPI::kBlockCount * 3) / 4) * (AX_FRAMES_PER_GROUP * 3))); + constexpr static auto kWaitDuration = std::chrono::duration_cast(std::chrono::milliseconds(3)); + constexpr static auto kWaitDurationFast = std::chrono::duration_cast(std::chrono::microseconds(2900)); + constexpr static auto kWaitDurationMinimum = std::chrono::duration_cast(std::chrono::microseconds(1700)); // if we haven't buffered any blocks, we will wait less time than usual bool additional_blocks_required = false; diff --git a/src/Cemu/Logging/CemuLogging.h b/src/Cemu/Logging/CemuLogging.h index edca3241..337bfa91 100644 --- a/src/Cemu/Logging/CemuLogging.h +++ b/src/Cemu/Logging/CemuLogging.h @@ -92,7 +92,11 @@ bool cemuLog_log(LogType type, std::basic_string formatStr, TArgs&&... args) else { const auto format_view = fmt::basic_string_view(formatStr); +#if FMT_VERSION >= 110000 + const auto text = fmt::vformat(format_view, fmt::make_format_args>(args...)); +#else const auto text = fmt::vformat(format_view, fmt::make_format_args>(args...)); +#endif cemuLog_log(type, std::basic_string_view(text.data(), text.size())); } return true; diff --git a/src/Common/MemPtr.h b/src/Common/MemPtr.h index 142da7e4..2dc92040 100644 --- a/src/Common/MemPtr.h +++ b/src/Common/MemPtr.h @@ -4,7 +4,7 @@ using MPTR = uint32; // generic address in PowerPC memory space -#define MPTR_NULL (0) +#define MPTR_NULL (0) using VAddr = uint32; // virtual address using PAddr = uint32; // physical address @@ -14,137 +14,177 @@ extern uint8* PPCInterpreterGetStackPointer(); extern uint8* PPCInterpreter_PushAndReturnStackPointer(sint32 offset); extern void PPCInterpreterModifyStackPointer(sint32 offset); -class MEMPTRBase {}; +class MEMPTRBase +{ +}; -template +template class MEMPTR : MEMPTRBase { -public: - constexpr MEMPTR() - : m_value(0) { } + public: + constexpr MEMPTR() noexcept + : m_value(0) {} - explicit constexpr MEMPTR(uint32 offset) - : m_value(offset) { } + explicit constexpr MEMPTR(uint32 offset) noexcept + : m_value(offset) {} - explicit constexpr MEMPTR(const uint32be& offset) - : m_value(offset) { } + explicit constexpr MEMPTR(const uint32be& offset) noexcept + : m_value(offset) {} - constexpr MEMPTR(std::nullptr_t) - : m_value(0) { } + constexpr MEMPTR(std::nullptr_t) noexcept + : m_value(0) {} - MEMPTR(T* ptr) + MEMPTR(T* ptr) noexcept { if (ptr == nullptr) m_value = 0; else - { - cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000); - m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base); - } + { + cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000); + m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base); + } } - constexpr MEMPTR(const MEMPTR& memptr) - : m_value(memptr.m_value) { } + constexpr MEMPTR(const MEMPTR&) noexcept = default; - constexpr MEMPTR& operator=(const MEMPTR& memptr) - { - m_value = memptr.m_value; - return *this; - } + constexpr MEMPTR& operator=(const MEMPTR&) noexcept = default; - constexpr MEMPTR& operator=(const uint32& offset) + constexpr MEMPTR& operator=(const uint32& offset) noexcept { m_value = offset; return *this; } - constexpr MEMPTR& operator=(const std::nullptr_t rhs) + constexpr MEMPTR& operator=(std::nullptr_t) noexcept { m_value = 0; return *this; } - MEMPTR& operator=(T* ptr) + MEMPTR& operator=(T* ptr) noexcept { - if (ptr == nullptr) + if (ptr == nullptr) m_value = 0; else - { - cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000); - m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base); - } + { + cemu_assert_debug((uint8*)ptr >= memory_base && (uint8*)ptr <= memory_base + 0x100000000); + m_value = (uint32)((uintptr_t)ptr - (uintptr_t)memory_base); + } return *this; } - bool atomic_compare_exchange(T* comparePtr, T* newPtr) + bool atomic_compare_exchange(T* comparePtr, T* newPtr) noexcept { MEMPTR mp_compare = comparePtr; MEMPTR mp_new = newPtr; - std::atomic* thisValueAtomic = (std::atomic*)&m_value; + auto* thisValueAtomic = reinterpret_cast*>(&m_value); return thisValueAtomic->compare_exchange_strong(mp_compare.m_value, mp_new.m_value); } - explicit constexpr operator bool() const noexcept { return m_value != 0; } - - constexpr operator T*() const noexcept { return GetPtr(); } // allow implicit cast to wrapped pointer type - - - template - explicit operator MEMPTR() const { return MEMPTR(this->m_value); } - - MEMPTR operator+(const MEMPTR& ptr) { return MEMPTR(this->GetMPTR() + ptr.GetMPTR()); } - MEMPTR operator-(const MEMPTR& ptr) { return MEMPTR(this->GetMPTR() - ptr.GetMPTR()); } - - MEMPTR operator+(sint32 v) + explicit constexpr operator bool() const noexcept { - // pointer arithmetic - return MEMPTR(this->GetMPTR() + v * 4); + return m_value != 0; } - MEMPTR operator-(sint32 v) + // allow implicit cast to wrapped pointer type + constexpr operator T*() const noexcept { - // pointer arithmetic - return MEMPTR(this->GetMPTR() - v * 4); + return GetPtr(); } - MEMPTR& operator+=(sint32 v) + template + explicit operator MEMPTR() const noexcept + { + return MEMPTR(this->m_value); + } + + sint32 operator-(const MEMPTR& ptr) noexcept + requires(!std::is_void_v) + { + return static_cast(this->GetMPTR() - ptr.GetMPTR()); + } + + MEMPTR operator+(sint32 v) noexcept + requires(!std::is_void_v) + { + // pointer arithmetic + return MEMPTR(this->GetMPTR() + v * sizeof(T)); + } + + MEMPTR operator-(sint32 v) noexcept + requires(!std::is_void_v) + { + // pointer arithmetic + return MEMPTR(this->GetMPTR() - v * sizeof(T)); + } + + MEMPTR& operator+=(sint32 v) noexcept + requires(!std::is_void_v) { m_value += v * sizeof(T); return *this; } - template - typename std::enable_if::value, Q>::type& - operator*() const { return *GetPtr(); } + template + requires(!std::is_void_v) + Q& operator*() const noexcept + { + return *GetPtr(); + } - T* operator->() const { return GetPtr(); } + constexpr T* operator->() const noexcept + { + return GetPtr(); + } - template - typename std::enable_if::value, Q>::type& - operator[](int index) { return GetPtr()[index]; } + template + requires(!std::is_void_v) + Q& operator[](int index) noexcept + { + return GetPtr()[index]; + } - T* GetPtr() const { return (T*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value); } + T* GetPtr() const noexcept + { + return (T*)(m_value == 0 ? nullptr : memory_base + (uint32)m_value); + } - template - C* GetPtr() const { return (C*)(GetPtr()); } + template + C* GetPtr() const noexcept + { + return static_cast(GetPtr()); + } - constexpr uint32 GetMPTR() const { return m_value.value(); } - constexpr const uint32be& GetBEValue() const { return m_value; } + [[nodiscard]] constexpr uint32 GetMPTR() const noexcept + { + return m_value.value(); + } + [[nodiscard]] constexpr const uint32be& GetBEValue() const noexcept + { + return m_value; + } - constexpr bool IsNull() const { return m_value == 0; } + [[nodiscard]] constexpr bool IsNull() const noexcept + { + return m_value == 0; + } -private: + private: uint32be m_value; }; static_assert(sizeof(MEMPTR) == sizeof(uint32be)); +static_assert(std::is_trivially_copyable_v>); #include "StackAllocator.h" #include "SysAllocator.h" -template +template struct fmt::formatter> : formatter { - template - auto format(const MEMPTR& v, FormatContext& ctx) const -> format_context::iterator { return fmt::format_to(ctx.out(), "{:#x}", v.GetMPTR()); } + template + auto format(const MEMPTR& v, FormatContext& ctx) const -> format_context::iterator + { + return fmt::format_to(ctx.out(), "{:#x}", v.GetMPTR()); + } }; diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index 61707519..d4df4343 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -394,16 +394,10 @@ void vectorRemoveByIndex(std::vector& vec, const size_t index) vec.erase(vec.begin() + index); } -template -int match_any_of(T1 value, T2 compareTo) +template +bool match_any_of(T1&& value, Types&&... others) { - return value == compareTo; -} - -template -bool match_any_of(T1 value, T2 compareTo, Types&&... others) -{ - return value == compareTo || match_any_of(value, others...); + return ((value == others) || ...); } // we cache the frequency in a static variable @@ -501,13 +495,6 @@ bool future_is_ready(std::future& f) #endif } -// replace with std::scope_exit once available -struct scope_exit -{ - std::function f_; - explicit scope_exit(std::function f) noexcept : f_(std::move(f)) {} - ~scope_exit() { if (f_) f_(); } -}; // helper function to cast raw pointers to std::atomic // this is technically not legal but works on most platforms as long as alignment restrictions are met and the implementation of atomic doesnt come with additional members @@ -515,6 +502,8 @@ struct scope_exit template std::atomic* _rawPtrToAtomic(T* ptr) { + static_assert(sizeof(T) == sizeof(std::atomic)); + cemu_assert_debug((reinterpret_cast(ptr) % alignof(std::atomic)) == 0); return reinterpret_cast*>(ptr); } @@ -578,13 +567,34 @@ struct fmt::formatter> : fmt::formatter } }; -// useful C++23 stuff that isn't yet widely supported - -// std::to_underlying +// useful future C++ stuff namespace stdx { + // std::to_underlying template {} >> constexpr std::underlying_type_t to_underlying(EnumT e) noexcept { return static_cast>(e); }; + + // std::scope_exit + template + class scope_exit + { + Fn m_func; + bool m_released = false; + public: + explicit scope_exit(Fn&& f) noexcept + : m_func(std::forward(f)) + {} + ~scope_exit() + { + if (!m_released) m_func(); + } + scope_exit(scope_exit&& other) noexcept + : m_func(std::move(other.m_func)), m_released(std::exchange(other.m_released, true)) + {} + scope_exit(const scope_exit&) = delete; + scope_exit& operator=(scope_exit) = delete; + void release() { m_released = true;} + }; } \ No newline at end of file diff --git a/src/config/CemuConfig.h b/src/config/CemuConfig.h index c231ae08..988916eb 100644 --- a/src/config/CemuConfig.h +++ b/src/config/CemuConfig.h @@ -195,7 +195,7 @@ ENABLE_ENUM_ITERATORS(CrashDump, CrashDump::Disabled, CrashDump::Enabled); template <> struct fmt::formatter : formatter { template - auto format(const PrecompiledShaderOption c, FormatContext &ctx) { + auto format(const PrecompiledShaderOption c, FormatContext &ctx) const { string_view name; switch (c) { @@ -210,7 +210,7 @@ struct fmt::formatter : formatter { template <> struct fmt::formatter : formatter { template - auto format(const AccurateShaderMulOption c, FormatContext &ctx) { + auto format(const AccurateShaderMulOption c, FormatContext &ctx) const { string_view name; switch (c) { @@ -224,7 +224,7 @@ struct fmt::formatter : formatter { template <> struct fmt::formatter : formatter { template - auto format(const CPUMode c, FormatContext &ctx) { + auto format(const CPUMode c, FormatContext &ctx) const { string_view name; switch (c) { @@ -241,7 +241,7 @@ struct fmt::formatter : formatter { template <> struct fmt::formatter : formatter { template - auto format(const CPUModeLegacy c, FormatContext &ctx) { + auto format(const CPUModeLegacy c, FormatContext &ctx) const { string_view name; switch (c) { @@ -258,7 +258,7 @@ struct fmt::formatter : formatter { template <> struct fmt::formatter : formatter { template - auto format(const CafeConsoleRegion v, FormatContext &ctx) { + auto format(const CafeConsoleRegion v, FormatContext &ctx) const { string_view name; switch (v) { diff --git a/src/config/LaunchSettings.cpp b/src/config/LaunchSettings.cpp index bf38b9cf..32a069c6 100644 --- a/src/config/LaunchSettings.cpp +++ b/src/config/LaunchSettings.cpp @@ -199,7 +199,11 @@ bool LaunchSettings::HandleCommandline(const std::vector& args) std::string errorMsg; errorMsg.append("Error while trying to parse command line parameter:\n"); errorMsg.append(ex.what()); +#if BOOST_OS_WINDOWS wxMessageBox(errorMsg, "Parameter error", wxICON_ERROR); +#else + std::cout << errorMsg << std::endl; +#endif return false; } diff --git a/src/gui/CemuApp.cpp b/src/gui/CemuApp.cpp index 322980e9..be0154f7 100644 --- a/src/gui/CemuApp.cpp +++ b/src/gui/CemuApp.cpp @@ -15,6 +15,9 @@ #if BOOST_OS_LINUX && HAS_WAYLAND #include "gui/helpers/wxWayland.h" #endif +#if __WXGTK__ +#include +#endif #include #include diff --git a/src/gui/components/wxGameList.cpp b/src/gui/components/wxGameList.cpp index eedfde5d..6cbb5859 100644 --- a/src/gui/components/wxGameList.cpp +++ b/src/gui/components/wxGameList.cpp @@ -1392,7 +1392,6 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) const auto outputPath = shortcutDialog.GetPath(); std::optional icon_path = std::nullopt; - [&]() { int iconIdx; int smallIconIdx; @@ -1402,15 +1401,13 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) return; } const auto icon = m_image_list->GetIcon(iconIdx); - PWSTR localAppData; - const auto hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &localAppData); - wxBitmap bitmap{}; - auto folder = fs::path(localAppData) / "Cemu" / "icons"; - if (!SUCCEEDED(hres) || (!fs::exists(folder) && !fs::create_directories(folder))) + const auto folder = ActiveSettings::GetUserDataPath("icons"); + if (!fs::exists(folder) && !fs::create_directories(folder)) { cemuLog_log(LogType::Force, "Failed to create icon directory"); return; } + wxBitmap bitmap{}; if (!bitmap.CopyFromIcon(icon)) { cemuLog_log(LogType::Force, "Failed to copy icon"); @@ -1426,7 +1423,7 @@ void wxGameList::CreateShortcut(GameInfo2& gameInfo) icon_path = std::nullopt; cemuLog_log(LogType::Force, "Icon failed to save"); } - }(); + } IShellLinkW* shellLink; HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast(&shellLink)); diff --git a/src/gui/helpers/wxHelpers.h b/src/gui/helpers/wxHelpers.h index fa135cf4..9e00bf48 100644 --- a/src/gui/helpers/wxHelpers.h +++ b/src/gui/helpers/wxHelpers.h @@ -8,7 +8,7 @@ template <> struct fmt::formatter : formatter { template - auto format(const wxString& str, FormatContext& ctx) + auto format(const wxString& str, FormatContext& ctx) const { return formatter::format(str.c_str().AsChar(), ctx); } diff --git a/src/gui/input/InputAPIAddWindow.cpp b/src/gui/input/InputAPIAddWindow.cpp index a6d1f1a9..688ee14e 100644 --- a/src/gui/input/InputAPIAddWindow.cpp +++ b/src/gui/input/InputAPIAddWindow.cpp @@ -114,6 +114,11 @@ InputAPIAddWindow::InputAPIAddWindow(wxWindow* parent, const wxPoint& position, this->Bind(wxControllersRefreshed, &InputAPIAddWindow::on_controllers_refreshed, this); } +InputAPIAddWindow::~InputAPIAddWindow() +{ + discard_thread_result(); +} + void InputAPIAddWindow::on_add_button(wxCommandEvent& event) { const auto selection = m_input_api->GetSelection(); @@ -159,6 +164,8 @@ std::unique_ptr InputAPIAddWindow::get_settings() co void InputAPIAddWindow::on_api_selected(wxCommandEvent& event) { + discard_thread_result(); + if (m_input_api->GetSelection() == wxNOT_FOUND) return; @@ -239,19 +246,25 @@ void InputAPIAddWindow::on_controller_dropdown(wxCommandEvent& event) m_controller_list->Append(_("Searching for controllers..."), (wxClientData*)nullptr); m_controller_list->SetSelection(wxNOT_FOUND); - std::thread([this, provider, selected_uuid]() + m_search_thread_data = std::make_unique(); + std::thread([this, provider, selected_uuid](std::shared_ptr data) { auto available_controllers = provider->get_controllers(); - wxCommandEvent event(wxControllersRefreshed); - event.SetEventObject(m_controller_list); - event.SetClientObject(new wxCustomData(std::move(available_controllers))); - event.SetInt(provider->api()); - event.SetString(selected_uuid); - wxPostEvent(this, event); - - m_search_running = false; - }).detach(); + { + std::lock_guard lock{data->mutex}; + if(!data->discardResult) + { + wxCommandEvent event(wxControllersRefreshed); + event.SetEventObject(m_controller_list); + event.SetClientObject(new wxCustomData(std::move(available_controllers))); + event.SetInt(provider->api()); + event.SetString(selected_uuid); + wxPostEvent(this, event); + m_search_running = false; + } + } + }, m_search_thread_data).detach(); } void InputAPIAddWindow::on_controller_selected(wxCommandEvent& event) @@ -301,3 +314,13 @@ void InputAPIAddWindow::on_controllers_refreshed(wxCommandEvent& event) } } } + +void InputAPIAddWindow::discard_thread_result() +{ + m_search_running = false; + if(m_search_thread_data) + { + std::lock_guard lock{m_search_thread_data->mutex}; + m_search_thread_data->discardResult = true; + } +} diff --git a/src/gui/input/InputAPIAddWindow.h b/src/gui/input/InputAPIAddWindow.h index ebee0592..085dd623 100644 --- a/src/gui/input/InputAPIAddWindow.h +++ b/src/gui/input/InputAPIAddWindow.h @@ -19,6 +19,7 @@ class InputAPIAddWindow : public wxDialog { public: InputAPIAddWindow(wxWindow* parent, const wxPoint& position, const std::vector& controllers); + ~InputAPIAddWindow(); bool is_valid() const { return m_type.has_value() && m_controller != nullptr; } InputAPI::Type get_type() const { return m_type.value(); } @@ -38,6 +39,8 @@ private: void on_controller_selected(wxCommandEvent& event); void on_controllers_refreshed(wxCommandEvent& event); + void discard_thread_result(); + wxChoice* m_input_api; wxComboBox* m_controller_list; wxButton* m_ok_button; @@ -50,4 +53,10 @@ private: std::vector m_controllers; std::atomic_bool m_search_running = false; + struct AsyncThreadData + { + std::atomic_bool discardResult = false; + std::mutex mutex; + }; + std::shared_ptr m_search_thread_data; }; diff --git a/src/gui/input/panels/VPADInputPanel.cpp b/src/gui/input/panels/VPADInputPanel.cpp index fbcdfde4..9e6d75d6 100644 --- a/src/gui/input/panels/VPADInputPanel.cpp +++ b/src/gui/input/panels/VPADInputPanel.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "gui/helpers/wxControlObject.h" @@ -131,11 +132,23 @@ VPADInputPanel::VPADInputPanel(wxWindow* parent) } // Blow Mic - row = 9; + row = 8; add_button_row(main_sizer, row, column, VPADController::kButtonId_Mic, _("blow mic")); row++; add_button_row(main_sizer, row, column, VPADController::kButtonId_Screen, _("show screen")); + row++; + + auto toggleScreenText = new wxStaticText(this, wxID_ANY, _("toggle screen")); + main_sizer->Add(toggleScreenText, + wxGBPosition(row, column), + wxDefaultSpan, + wxALL | wxALIGN_CENTER_VERTICAL, 5); + m_togglePadViewCheckBox = new wxCheckBox(this, wxID_ANY, {}, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + wxString toggleScreenTT = _("Makes the \"show screen\" button toggle between the TV and gamepad screens"); + m_togglePadViewCheckBox->SetToolTip(toggleScreenTT); + toggleScreenText->SetToolTip(toggleScreenTT); + main_sizer->Add(m_togglePadViewCheckBox, wxGBPosition(row,column+1), wxDefaultSpan, wxALL | wxEXPAND, 5); ////////////////////////////////////////////////////////////////// @@ -168,6 +181,8 @@ void VPADInputPanel::on_timer(const EmulatedControllerPtr& emulated_controller, { InputPanel::on_timer(emulated_controller, controller_base); + static_cast(emulated_controller.get())->set_screen_toggle(m_togglePadViewCheckBox->GetValue()); + if(emulated_controller) { const auto axis = emulated_controller->get_axis(); @@ -182,3 +197,10 @@ void VPADInputPanel::OnVolumeChange(wxCommandEvent& event) { } +void VPADInputPanel::load_controller(const EmulatedControllerPtr& controller) +{ + InputPanel::load_controller(controller); + + const bool isToggle = static_cast(controller.get())->is_screen_active_toggle(); + m_togglePadViewCheckBox->SetValue(isToggle); +} diff --git a/src/gui/input/panels/VPADInputPanel.h b/src/gui/input/panels/VPADInputPanel.h index 3513bbf7..477385f4 100644 --- a/src/gui/input/panels/VPADInputPanel.h +++ b/src/gui/input/panels/VPADInputPanel.h @@ -4,6 +4,7 @@ #include "gui/input/panels/InputPanel.h" class wxInputDraw; +class wxCheckBox; class VPADInputPanel : public InputPanel { @@ -11,11 +12,13 @@ public: VPADInputPanel(wxWindow* parent); void on_timer(const EmulatedControllerPtr& emulated_controller, const ControllerPtr& controller) override; + virtual void load_controller(const EmulatedControllerPtr& controller) override; private: void OnVolumeChange(wxCommandEvent& event); wxInputDraw* m_left_draw, * m_right_draw; + wxCheckBox* m_togglePadViewCheckBox; void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id); void add_button_row(wxGridBagSizer *sizer, sint32 row, sint32 column, const VPADController::ButtonId &button_id, const wxString &label); diff --git a/src/input/emulated/EmulatedController.h b/src/input/emulated/EmulatedController.h index 907be07e..c5adf81e 100644 --- a/src/input/emulated/EmulatedController.h +++ b/src/input/emulated/EmulatedController.h @@ -127,7 +127,7 @@ using EmulatedControllerPtr = std::shared_ptr; template <> struct fmt::formatter : formatter { template - auto format(EmulatedController::Type v, FormatContext& ctx) { + auto format(EmulatedController::Type v, FormatContext& ctx) const { switch (v) { case EmulatedController::Type::VPAD: return formatter::format("Wii U Gamepad", ctx); diff --git a/src/input/emulated/VPADController.cpp b/src/input/emulated/VPADController.cpp index aeb56395..f1ab1bc4 100644 --- a/src/input/emulated/VPADController.cpp +++ b/src/input/emulated/VPADController.cpp @@ -686,3 +686,14 @@ bool VPADController::set_default_mapping(const std::shared_ptr& return mapping_updated; } + +void VPADController::load(const pugi::xml_node& node) +{ + if (const auto value = node.child("toggle_display")) + m_screen_active_toggle = ConvertString(value.child_value()); +} + +void VPADController::save(pugi::xml_node& node) +{ + node.append_child("toggle_display").append_child(pugi::node_pcdata).set_value(fmt::format("{}", (int)m_screen_active_toggle).c_str()); +} diff --git a/src/input/emulated/VPADController.h b/src/input/emulated/VPADController.h index 6aef16ae..937062da 100644 --- a/src/input/emulated/VPADController.h +++ b/src/input/emulated/VPADController.h @@ -66,6 +66,8 @@ public: bool is_mic_active() { return m_mic_active; } bool is_screen_active() { return m_screen_active; } + bool is_screen_active_toggle() { return m_screen_active_toggle; } + void set_screen_toggle(bool toggle) {m_screen_active_toggle = toggle;} static std::string_view get_button_name(ButtonId id); @@ -86,9 +88,13 @@ public: bool set_default_mapping(const std::shared_ptr& controller) override; + void load(const pugi::xml_node& node) override; + void save(pugi::xml_node& node) override; + private: bool m_mic_active = false; bool m_screen_active = false; + bool m_screen_active_toggle = false; uint32be m_last_holdvalue = 0; std::chrono::high_resolution_clock::time_point m_last_hold_change{}, m_last_pulse{}; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 5af88176..5ac5ebfd 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -50,7 +50,6 @@ add_library(CemuUtil MemMapper/MemMapper.h SystemInfo/SystemInfo.cpp SystemInfo/SystemInfo.h - ThreadPool/ThreadPool.cpp ThreadPool/ThreadPool.h tinyxml2/tinyxml2.cpp tinyxml2/tinyxml2.h diff --git a/src/util/ThreadPool/ThreadPool.cpp b/src/util/ThreadPool/ThreadPool.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/src/util/containers/robin_hood.h b/src/util/containers/robin_hood.h index 577521b1..4f76519f 100644 --- a/src/util/containers/robin_hood.h +++ b/src/util/containers/robin_hood.h @@ -194,7 +194,7 @@ namespace robin_hood { // workaround missing "is_trivially_copyable" in g++ < 5.0 // See https://stackoverflow.com/a/31798726/48181 -#if defined(__GNUC__) && __GNUC__ < 5 +#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__clang__) # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__) #else # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value diff --git a/src/util/crypto/crc32.cpp b/src/util/crypto/crc32.cpp index 52eb8a88..a5b37a5e 100644 --- a/src/util/crypto/crc32.cpp +++ b/src/util/crypto/crc32.cpp @@ -1,29 +1,6 @@ #include "crc32.h" -#if defined(_MSC_VER) || defined(__MINGW32__) -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __BYTE_ORDER __LITTLE_ENDIAN - -#include -#ifdef __MINGW32__ -#define PREFETCH(location) __builtin_prefetch(location) -#else -#define PREFETCH(location) _mm_prefetch(location, _MM_HINT_T0) -#endif -#else -// defines __BYTE_ORDER as __LITTLE_ENDIAN or __BIG_ENDIAN -#include - -#ifdef __GNUC__ -#define PREFETCH(location) __builtin_prefetch(location) -#else - // no prefetching -#define PREFETCH(location) ; -#endif -#endif - -unsigned int Crc32Lookup[8][256] = +constexpr uint32 Crc32Lookup[8][256] = { { 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3, @@ -301,20 +278,7 @@ unsigned int Crc32Lookup[8][256] = } }; -/// swap endianess -static inline uint32_t swap(uint32_t x) -{ -#if defined(__GNUC__) || defined(__clang__) - return __builtin_bswap32(x); -#else - return (x >> 24) | - ((x >> 8) & 0x0000FF00) | - ((x << 8) & 0x00FF0000) | - (x << 24); -#endif -} - -unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, int length) +uint32 crc32_calc_slice_by_8(uint32 previousCrc32, const void* data, size_t length) { uint32_t crc = ~previousCrc32; // same as previousCrc32 ^ 0xFFFFFFFF const uint32_t* current = (const uint32_t*)data; @@ -323,7 +287,7 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, while (length >= 8) { if constexpr (std::endian::native == std::endian::big){ - uint32_t one = *current++ ^ swap(crc); + uint32_t one = *current++ ^ _swapEndianU32(crc); uint32_t two = *current++; crc = Crc32Lookup[0][two & 0xFF] ^ Crc32Lookup[1][(two >> 8) & 0xFF] ^ @@ -348,13 +312,14 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, Crc32Lookup[7][one & 0xFF]; } else { - cemu_assert(false); + static_assert(std::endian::native == std::endian::big || std::endian::native == std::endian::little, + "Platform byte-order is unsupported"); } length -= 8; } - const uint8_t* currentChar = (const uint8_t*)current; + const uint8* currentChar = (const uint8*)current; // remaining 1 to 7 bytes (standard algorithm) while (length-- != 0) crc = (crc >> 8) ^ Crc32Lookup[0][(crc & 0xFF) ^ *currentChar++]; @@ -362,20 +327,20 @@ unsigned int crc32_calc_slice_by_8(unsigned int previousCrc32, const void* data, return ~crc; // same as crc ^ 0xFFFFFFFF } -unsigned int crc32_calc(unsigned int c, const void* data, int length) +uint32 crc32_calc(uint32 c, const void* data, size_t length) { if (length >= 16) { return crc32_calc_slice_by_8(c, data, length); } - unsigned char* p = (unsigned char*)data; + const uint8* p = (const uint8*)data; if (length == 0) return c; c ^= 0xFFFFFFFF; while (length) { - unsigned char temp = *p; - temp ^= (unsigned char)c; + uint8 temp = *p; + temp ^= (uint8)c; c = (c >> 8) ^ Crc32Lookup[0][temp]; // next length--; diff --git a/src/util/crypto/crc32.h b/src/util/crypto/crc32.h index b8002261..2ab37376 100644 --- a/src/util/crypto/crc32.h +++ b/src/util/crypto/crc32.h @@ -1,8 +1,8 @@ #pragma once -unsigned int crc32_calc(unsigned int c, const void* data, int length); +uint32 crc32_calc(uint32 c, const void* data, size_t length); -inline unsigned int crc32_calc(const void* data, int length) +inline uint32 crc32_calc(const void* data, size_t length) { return crc32_calc(0, data, length); } \ No newline at end of file