mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
Merge branch 'cemu-project:main' into main
This commit is contained in:
commit
a040b12791
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@ -207,6 +207,7 @@ jobs:
|
||||
build-macos:
|
||||
runs-on: macos-14
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
steps:
|
||||
|
||||
4
BUILD.md
4
BUILD.md
@ -244,9 +244,9 @@ Example usage: `cmake -S . -B build -DCMAKE_BUILD_TYPE=release -DENABLE_SDL=ON -
|
||||
| CEMU_CXX_FLAGS | | Flags passed straight to the compiler, e.g. `-march=native`, `-Wall`, `/W3` | "" | |
|
||||
| ENABLE_CUBEB | | Enable cubeb audio backend | ON | |
|
||||
| ENABLE_DISCORD_RPC | | Enable Discord Rich presence support | ON | |
|
||||
| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | Currently required |
|
||||
| ENABLE_OPENGL | | Enable OpenGL graphics backend | ON | |
|
||||
| ENABLE_HIDAPI | | Enable HIDAPI (used for Wiimote controller API) | ON | |
|
||||
| ENABLE_SDL | | Enable SDLController controller API | ON | Currently required |
|
||||
| ENABLE_SDL | | Enable SDLController controller API | ON | |
|
||||
| ENABLE_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | |
|
||||
| ENABLE_VULKAN | | Enable the Vulkan graphics backend | ON | |
|
||||
| ENABLE_WXWIDGETS | | Enable wxWidgets UI | ON | Currently required |
|
||||
|
||||
@ -110,11 +110,13 @@ endif()
|
||||
|
||||
if (APPLE)
|
||||
set(ENABLE_METAL_DEFAULT ON)
|
||||
set(ENABLE_OPENGL_DEFAULT OFF)
|
||||
else()
|
||||
set(ENABLE_METAL_DEFAULT OFF)
|
||||
set(ENABLE_OPENGL_DEFAULT ON)
|
||||
endif()
|
||||
|
||||
option(ENABLE_OPENGL "Enables the OpenGL backend" ON)
|
||||
option(ENABLE_OPENGL "Enables the OpenGL backend" ${ENABLE_OPENGL_DEFAULT})
|
||||
option(ENABLE_VULKAN "Enables the Vulkan backend" ON)
|
||||
option(ENABLE_METAL "Enables the Metal backend" ${ENABLE_METAL_DEFAULT})
|
||||
option(ENABLE_DISCORD_RPC "Enables the Discord Rich Presence feature" ON)
|
||||
@ -148,7 +150,6 @@ option(ENABLE_CUBEB "Enabled cubeb backend" ON)
|
||||
option(ENABLE_WXWIDGETS "Build with wxWidgets UI (Currently required)" ON)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(CURL REQUIRED)
|
||||
find_package(pugixml REQUIRED)
|
||||
find_package(RapidJSON REQUIRED)
|
||||
@ -162,6 +163,11 @@ find_package(glm REQUIRED)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(PNG REQUIRED)
|
||||
|
||||
if(ENABLE_SDL)
|
||||
find_package(SDL3 REQUIRED CONFIG REQUIRED COMPONENTS SDL3)
|
||||
add_compile_definitions(HAS_SDL)
|
||||
endif()
|
||||
|
||||
# glslang versions older than 11.11.0 define targets without a namespace
|
||||
if (NOT TARGET glslang::SPIRV AND TARGET SPIRV)
|
||||
add_library(glslang::SPIRV ALIAS SPIRV)
|
||||
@ -194,16 +200,19 @@ endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
include_directories("dependencies/Vulkan-Headers/include")
|
||||
add_compile_definitions(ENABLE_VULKAN)
|
||||
add_compile_definitions(VK_NO_PROTOTYPES)
|
||||
endif()
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
find_package(OpenGL REQUIRED)
|
||||
add_compile_definitions(ENABLE_OPENGL)
|
||||
endif()
|
||||
|
||||
if (ENABLE_METAL)
|
||||
include_directories(${CMAKE_SOURCE_DIR}/dependencies/metal-cpp)
|
||||
|
||||
add_definitions(-DENABLE_METAL=1)
|
||||
add_compile_definitions(ENABLE_METAL)
|
||||
endif()
|
||||
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
@ -211,7 +220,6 @@ if (ENABLE_DISCORD_RPC)
|
||||
endif()
|
||||
|
||||
if (ENABLE_HIDAPI)
|
||||
find_package(hidapi REQUIRED)
|
||||
set(SUPPORTS_WIIMOTE ON)
|
||||
add_compile_definitions(HAS_HIDAPI)
|
||||
endif ()
|
||||
|
||||
2
dependencies/vcpkg
vendored
2
dependencies/vcpkg
vendored
@ -1 +1 @@
|
||||
Subproject commit af752f21c9d79ba3df9cb0250ce2233933f58486
|
||||
Subproject commit c3867e714dd3a51c272826eea77267876517ed99
|
||||
@ -13,18 +13,24 @@ if(MSVC)
|
||||
add_compile_definitions(WIN32_LEAN_AND_MEAN CURL_STATICLIB)
|
||||
elseif(UNIX)
|
||||
if(APPLE)
|
||||
add_compile_definitions(
|
||||
_XOPEN_SOURCE
|
||||
VK_USE_PLATFORM_MACOS_MVK
|
||||
VK_USE_PLATFORM_METAL_EXT
|
||||
)
|
||||
if (ENABLE_VULKAN)
|
||||
add_compile_definitions(
|
||||
_XOPEN_SOURCE
|
||||
VK_USE_PLATFORM_MACOS_MVK
|
||||
VK_USE_PLATFORM_METAL_EXT
|
||||
)
|
||||
else()
|
||||
add_compile_definitions(_XOPEN_SOURCE)
|
||||
endif()
|
||||
else()
|
||||
add_compile_definitions(
|
||||
VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces?
|
||||
VK_USE_PLATFORM_XCB_KHR
|
||||
)
|
||||
if (ENABLE_WAYLAND)
|
||||
add_compile_definitions(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
if (ENABLE_VULKAN)
|
||||
add_compile_definitions(
|
||||
VK_USE_PLATFORM_XLIB_KHR # legacy. Do we need to support XLIB surfaces?
|
||||
VK_USE_PLATFORM_XCB_KHR
|
||||
)
|
||||
if (ENABLE_WAYLAND)
|
||||
add_compile_definitions(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
# warnings
|
||||
@ -35,8 +41,6 @@ elseif(UNIX)
|
||||
add_compile_options(-Wno-multichar -Wno-invalid-offsetof -Wno-switch -Wno-ignored-attributes -Wno-deprecated-enum-enum-conversion)
|
||||
endif()
|
||||
|
||||
add_compile_definitions(VK_NO_PROTOTYPES)
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# on msvc we can reuse the PCH from CemuCommon, on other platforms it's not straight forward to reuse the PCH so we opt out of it
|
||||
@ -119,6 +123,16 @@ if (MACOS_BUNDLE)
|
||||
COMMAND ${CMAKE_COMMAND} ARGS -E copy_directory "${CMAKE_SOURCE_DIR}/bin/${folder}" "$<TARGET_BUNDLE_DIR:CemuBin>/Contents/SharedSupport/${folder}")
|
||||
endforeach(folder)
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
if (EXISTS "/usr/local/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib")
|
||||
elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
else()
|
||||
message(FATAL_ERROR "failed to find libMoltenVK.dylib")
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (ENABLE_LIBUSB)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib")
|
||||
@ -127,23 +141,20 @@ if (MACOS_BUNDLE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS "/usr/local/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/usr/local/lib/libMoltenVK.dylib")
|
||||
elseif (EXISTS "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
set(MOLTENVK_PATH "/opt/homebrew/lib/libMoltenVK.dylib")
|
||||
else()
|
||||
message(FATAL_ERROR "failed to find libMoltenVK.dylib")
|
||||
endif ()
|
||||
set(UPDATE_SH_PATH "${CMAKE_SOURCE_DIR}/src/resource/update.sh")
|
||||
|
||||
set(APP_BUNDLE_DIR "$<TARGET_BUNDLE_DIR:CemuBin>")
|
||||
set(FRAMEWORKS_DIR "${APP_BUNDLE_DIR}/Contents/Frameworks")
|
||||
set(RESOURCES_DIR "${APP_BUNDLE_DIR}/Contents/Resources")
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
add_custom_command(TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${MOLTENVK_PATH}"
|
||||
"${FRAMEWORKS_DIR}/libMoltenVK.dylib"
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${MOLTENVK_PATH}"
|
||||
"${FRAMEWORKS_DIR}/libMoltenVK.dylib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${UPDATE_SH_PATH}"
|
||||
"${RESOURCES_DIR}/update.sh"
|
||||
@ -174,20 +185,22 @@ if (MACOS_BUNDLE)
|
||||
endif()
|
||||
else()
|
||||
if(APPLE)
|
||||
find_library(MOLTENVK_LIBRARY
|
||||
NAMES MoltenVK moltenvk libMoltenVK.dylib
|
||||
PATHS /usr/local/lib /opt/homebrew/lib
|
||||
)
|
||||
if(MOLTENVK_LIBRARY)
|
||||
message(STATUS "Found MoltenVK: ${MOLTENVK_LIBRARY}")
|
||||
target_link_libraries(CemuBin PRIVATE ${MOLTENVK_LIBRARY})
|
||||
else()
|
||||
message(WARNING "libMoltenVK.dylib not found")
|
||||
endif()
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH "/usr/local/lib;/opt/homebrew/lib"
|
||||
)
|
||||
if (ENABLE_VULKAN)
|
||||
find_library(MOLTENVK_LIBRARY
|
||||
NAMES MoltenVK moltenvk libMoltenVK.dylib
|
||||
PATHS /usr/local/lib /opt/homebrew/lib
|
||||
)
|
||||
if(MOLTENVK_LIBRARY)
|
||||
message(STATUS "Found MoltenVK: ${MOLTENVK_LIBRARY}")
|
||||
target_link_libraries(CemuBin PRIVATE ${MOLTENVK_LIBRARY})
|
||||
else()
|
||||
message(WARNING "libMoltenVK.dylib not found")
|
||||
endif()
|
||||
set_target_properties(CemuBin PROPERTIES
|
||||
BUILD_WITH_INSTALL_RPATH TRUE
|
||||
INSTALL_RPATH "/usr/local/lib;/opt/homebrew/lib"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -207,9 +220,12 @@ target_link_libraries(CemuBin PRIVATE
|
||||
CemuGui
|
||||
CemuInput
|
||||
CemuUtil
|
||||
SDL2::SDL2
|
||||
)
|
||||
|
||||
if(ENABLE_SDL)
|
||||
target_link_libraries(CemuBin PRIVATE SDL3::SDL3)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
# due to nasm output some linkers will make stack executable
|
||||
# cemu does not require this so we explicity disable it
|
||||
|
||||
@ -106,8 +106,6 @@ add_library(CemuCafe
|
||||
HW/Latte/Core/LatteCachedFBO.h
|
||||
HW/Latte/Core/LatteCommandProcessor.cpp
|
||||
HW/Latte/Core/LatteConst.h
|
||||
HW/Latte/Core/LatteDefaultShaders.cpp
|
||||
HW/Latte/Core/LatteDefaultShaders.h
|
||||
HW/Latte/Core/LatteDraw.h
|
||||
HW/Latte/Core/LatteGSCopyShaderParser.cpp
|
||||
HW/Latte/Core/Latte.h
|
||||
@ -127,7 +125,6 @@ add_library(CemuCafe
|
||||
HW/Latte/Core/LatteShaderCache.cpp
|
||||
HW/Latte/Core/LatteShaderCache.h
|
||||
HW/Latte/Core/LatteShader.cpp
|
||||
HW/Latte/Core/LatteShaderGL.cpp
|
||||
HW/Latte/Core/LatteShader.h
|
||||
HW/Latte/Core/LatteSoftware.cpp
|
||||
HW/Latte/Core/LatteSoftware.h
|
||||
@ -163,58 +160,14 @@ add_library(CemuCafe
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerInstructions.h
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerInternal.h
|
||||
HW/Latte/LegacyShaderDecompiler/LatteDecompilerRegisterDataTypeTracker.cpp
|
||||
HW/Latte/Renderer/OpenGL/CachedFBOGL.h
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureGL.h
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h
|
||||
HW/Latte/Renderer/OpenGL/OpenGLQuery.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRenderer.h
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererStreamout.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererUniformData.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h
|
||||
HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/RendererShaderGL.h
|
||||
HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp
|
||||
HW/Latte/Renderer/Renderer.cpp
|
||||
HW/Latte/Renderer/Renderer.h
|
||||
HW/Latte/Renderer/RendererCore.cpp
|
||||
HW/Latte/Renderer/RendererCore.h
|
||||
HW/Latte/Renderer/RendererOuputShader.cpp
|
||||
HW/Latte/Renderer/RendererOuputShader.h
|
||||
HW/Latte/Renderer/RendererShader.cpp
|
||||
HW/Latte/Renderer/RendererShader.h
|
||||
HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/CachedFBOVk.h
|
||||
HW/Latte/Renderer/Vulkan/CocoaSurface.h
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureVk.h
|
||||
HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/RendererShaderVk.h
|
||||
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h
|
||||
HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/VKRBase.h
|
||||
HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp
|
||||
HW/Latte/Renderer/Vulkan/VKRMemoryManager.h
|
||||
HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp
|
||||
HW/Latte/Renderer/Vulkan/VsyncDriver.cpp
|
||||
HW/Latte/Renderer/Vulkan/VsyncDriver.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanAPI.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanAPI.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanQuery.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRenderer.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h
|
||||
HW/Latte/ShaderInfo/ShaderDescription.cpp
|
||||
HW/Latte/ShaderInfo/ShaderInfo.h
|
||||
HW/Latte/ShaderInfo/ShaderInstanceInfo.cpp
|
||||
@ -538,12 +491,73 @@ add_library(CemuCafe
|
||||
TitleList/TitleList.h
|
||||
)
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Latte/Renderer/OpenGL/CachedFBOGL.h
|
||||
HW/Latte/Renderer/OpenGL/LatteShaderGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureGL.h
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureViewGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h
|
||||
HW/Latte/Renderer/OpenGL/OpenGLQuery.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererCore.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRenderer.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRenderer.h
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererStreamout.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLRendererUniformData.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.cpp
|
||||
HW/Latte/Renderer/OpenGL/OpenGLTextureReadback.h
|
||||
HW/Latte/Renderer/OpenGL/RendererShaderGL.cpp
|
||||
HW/Latte/Renderer/OpenGL/RendererShaderGL.h
|
||||
HW/Latte/Renderer/OpenGL/TextureReadbackGL.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Latte/Renderer/Vulkan/CachedFBOVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/CachedFBOVk.h
|
||||
HW/Latte/Renderer/Vulkan/CocoaSurface.h
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureViewVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureViewVk.h
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/LatteTextureVk.h
|
||||
HW/Latte/Renderer/Vulkan/RendererShaderVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/RendererShaderVk.h
|
||||
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/SwapchainInfoVk.h
|
||||
HW/Latte/Renderer/Vulkan/TextureReadbackVk.cpp
|
||||
HW/Latte/Renderer/Vulkan/VKRBase.h
|
||||
HW/Latte/Renderer/Vulkan/VKRMemoryManager.cpp
|
||||
HW/Latte/Renderer/Vulkan/VKRMemoryManager.h
|
||||
HW/Latte/Renderer/Vulkan/VKRPipelineInfo.cpp
|
||||
HW/Latte/Renderer/Vulkan/VsyncDriver.cpp
|
||||
HW/Latte/Renderer/Vulkan/VsyncDriver.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanAPI.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanAPI.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineCompiler.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanQuery.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanRenderer.h
|
||||
HW/Latte/Renderer/Vulkan/VulkanSurfaceCopy.cpp
|
||||
HW/Latte/Renderer/Vulkan/VulkanTextureReadback.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Latte/Renderer/Vulkan/CocoaSurface.mm
|
||||
HW/Latte/Renderer/MetalView.mm
|
||||
HW/Latte/Renderer/MetalView.h
|
||||
)
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Latte/Renderer/Vulkan/CocoaSurface.mm
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_METAL)
|
||||
|
||||
@ -251,7 +251,7 @@ void InfoLog_PrintActiveSettings()
|
||||
if (!GetConfig().vk_accurate_barriers.GetValue())
|
||||
cemuLog_log(LogType::Force, "Accurate barriers are disabled!");
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
else if (ActiveSettings::GetGraphicsAPI() == GraphicAPI::kMetal)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Async compile: {}", GetConfig().async_compile.GetValue() ? "true" : "false");
|
||||
|
||||
@ -226,7 +226,7 @@ bool GameProfile::Load(uint64_t title_id)
|
||||
m_graphics_api = (GraphicAPI)graphicsApi.value;
|
||||
|
||||
gameProfile_loadEnumOption(iniParser, "accurateShaderMul", m_accurateShaderMul);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
gameProfile_loadBooleanOption2(iniParser, "shaderFastMath", m_shaderFastMath);
|
||||
gameProfile_loadEnumOption(iniParser, "metalBufferCacheMode2", m_metalBufferCacheMode);
|
||||
gameProfile_loadEnumOption(iniParser, "positionInvariance2", m_positionInvariance);
|
||||
@ -311,7 +311,7 @@ void GameProfile::Save(uint64_t title_id)
|
||||
|
||||
fs->writeLine("[Graphics]");
|
||||
WRITE_ENTRY(accurateShaderMul);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
WRITE_ENTRY(shaderFastMath);
|
||||
WRITE_ENTRY_NUMBERED(metalBufferCacheMode, 2);
|
||||
WRITE_ENTRY_NUMBERED(positionInvariance, 2);
|
||||
@ -346,7 +346,7 @@ void GameProfile::ResetOptional()
|
||||
|
||||
// graphic settings
|
||||
m_accurateShaderMul = AccurateShaderMulOption::True;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_shaderFastMath = true;
|
||||
m_metalBufferCacheMode = MetalBufferCacheMode::Auto;
|
||||
m_positionInvariance = PositionInvariance::Auto;
|
||||
@ -371,7 +371,7 @@ void GameProfile::Reset()
|
||||
|
||||
// graphic settings
|
||||
m_accurateShaderMul = AccurateShaderMulOption::True;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_shaderFastMath = true;
|
||||
m_metalBufferCacheMode = MetalBufferCacheMode::Auto;
|
||||
m_positionInvariance = PositionInvariance::Auto;
|
||||
|
||||
@ -29,7 +29,7 @@ public:
|
||||
|
||||
[[nodiscard]] const std::optional<GraphicAPI>& GetGraphicsAPI() const { return m_graphics_api; }
|
||||
[[nodiscard]] const AccurateShaderMulOption& GetAccurateShaderMul() const { return m_accurateShaderMul; }
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
[[nodiscard]] bool GetShaderFastMath() const { return m_shaderFastMath; }
|
||||
[[nodiscard]] MetalBufferCacheMode GetBufferCacheMode() const { return m_metalBufferCacheMode; }
|
||||
[[nodiscard]] PositionInvariance GetPositionInvariance() const { return m_positionInvariance; }
|
||||
@ -57,7 +57,7 @@ private:
|
||||
// graphic settings
|
||||
std::optional<GraphicAPI> m_graphics_api{};
|
||||
AccurateShaderMulOption m_accurateShaderMul = AccurateShaderMulOption::True;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
bool m_shaderFastMath = true;
|
||||
MetalBufferCacheMode m_metalBufferCacheMode = MetalBufferCacheMode::Auto;
|
||||
PositionInvariance m_positionInvariance = PositionInvariance::Auto;
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#include "HW/Latte/Renderer/Renderer.h"
|
||||
#include "util/containers/LookupTableL3.h"
|
||||
#include "util/helpers/fspinlock.h"
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||
#endif
|
||||
#include <openssl/sha.h> /* SHA1_DIGEST_LENGTH */
|
||||
@ -108,21 +108,28 @@ void LatteShader_calculateFSKey(LatteFetchShader* fetchShader)
|
||||
key = std::rotl<uint64>(key, 8);
|
||||
key += (uint64)attrib->semanticId;
|
||||
key = std::rotl<uint64>(key, 8);
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
{
|
||||
key += (uint64)attrib->offset;
|
||||
key = std::rotl<uint64>(key, 7);
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
key += (uint64)(attrib->offset & 3);
|
||||
key = std::rotl<uint64>(key, 2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo - also hash invalid buffer groups?
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
for (sint32 g = 0; g < fetchShader->bufferGroups.size(); g++)
|
||||
@ -171,7 +178,7 @@ void LatteFetchShader::CalculateFetchShaderVkHash()
|
||||
this->vkPipelineHashFragment = h;
|
||||
}
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
void LatteFetchShader::CheckIfVerticesNeedManualFetchMtl(uint32* contextRegister)
|
||||
{
|
||||
for (sint32 g = 0; g < bufferGroups.size(); g++)
|
||||
@ -376,7 +383,7 @@ LatteFetchShader* LatteShaderRecompiler_createFetchShader(LatteFetchShader::Cach
|
||||
// these only make sense when vertex shader does not call FS?
|
||||
LatteShader_calculateFSKey(newFetchShader);
|
||||
newFetchShader->CalculateFetchShaderVkHash();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
newFetchShader->CheckIfVerticesNeedManualFetchMtl(contextRegister);
|
||||
#endif
|
||||
return newFetchShader;
|
||||
@ -438,7 +445,7 @@ LatteFetchShader* LatteShaderRecompiler_createFetchShader(LatteFetchShader::Cach
|
||||
}
|
||||
LatteShader_calculateFSKey(newFetchShader);
|
||||
newFetchShader->CalculateFetchShaderVkHash();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
newFetchShader->CheckIfVerticesNeedManualFetchMtl(contextRegister);
|
||||
#endif
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ struct LatteFetchShader
|
||||
|
||||
void CalculateFetchShaderVkHash();
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
void CheckIfVerticesNeedManualFetchMtl(uint32* contextRegister);
|
||||
#endif
|
||||
|
||||
|
||||
@ -776,9 +776,8 @@ private:
|
||||
{
|
||||
TreeNode* treeNodeParent = (treeNode->parentNodeIndex != INVALID_NODE_INDEX) ? &m_treeNodes[treeNode->parentNodeIndex] : nullptr;
|
||||
ReleaseTreeNode(treeNode, true);
|
||||
if (treeNodeParent && treeNodeParent->usedCount > 0)
|
||||
PropagateMinValue(treeNodeParent);
|
||||
// note - this will never shrink the tree, since there is at least one left or right neighbor
|
||||
if (treeNodeParent)
|
||||
CollapseNode(treeNodeParent, depth - 1);
|
||||
return;
|
||||
}
|
||||
// todo - redistribute values
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
#include "Cafe/GameProfile/GameProfile.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LatteBufferCache.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#endif
|
||||
|
||||
template<int vectorLen>
|
||||
void rectGenerate4thVertex(uint32be* output, uint32be* input0, uint32be* input1, uint32be* input2)
|
||||
@ -196,7 +198,7 @@ bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance
|
||||
fixedBufferSize += 128;
|
||||
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
#if BOOST_OS_MACOS && defined(ENABLE_VULKAN)
|
||||
if(bufferStride % 4 != 0)
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteShader.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h"
|
||||
#include "util/helpers/StringBuf.h"
|
||||
|
||||
LatteDefaultShader_t* _copyShader_depthToColor;
|
||||
LatteDefaultShader_t* _copyShader_colorToDepth;
|
||||
|
||||
void LatteDefaultShader_pixelCopyShader_generateVSBody(StringBuf* vs)
|
||||
{
|
||||
vs->add("#version 420\r\n");
|
||||
vs->add("out vec2 passUV;\r\n");
|
||||
vs->add("uniform vec4 uf_vertexOffsets[4];\r\n");
|
||||
vs->add("\r\n");
|
||||
vs->add("void main(){\r\n");
|
||||
vs->add("int vID = gl_VertexID;\r\n");
|
||||
vs->add("passUV = uf_vertexOffsets[vID].zw;\r\n");
|
||||
vs->add("gl_Position = vec4(uf_vertexOffsets[vID].xy, 0.0, 1.0);\r\n");
|
||||
vs->add("}\r\n");
|
||||
}
|
||||
|
||||
GLuint gxShaderDepr_compileRaw(StringBuf* strSourceVS, StringBuf* strSourceFS);
|
||||
GLuint gxShaderDepr_compileRaw(const std::string& vertex_source, const std::string& fragment_source);
|
||||
|
||||
LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_depthToColor()
|
||||
{
|
||||
if (_copyShader_depthToColor != 0)
|
||||
return _copyShader_depthToColor;
|
||||
catchOpenGLError();
|
||||
LatteDefaultShader_t* defaultShader = (LatteDefaultShader_t*)malloc(sizeof(LatteDefaultShader_t));
|
||||
memset(defaultShader, 0, sizeof(LatteDefaultShader_t));
|
||||
|
||||
StringBuf fCStr_vertexShader(1024 * 16);
|
||||
LatteDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader);
|
||||
|
||||
StringBuf fCStr_defaultFragShader(1024 * 16);
|
||||
fCStr_defaultFragShader.add("#version 420\r\n");
|
||||
fCStr_defaultFragShader.add("in vec2 passUV;\r\n");
|
||||
fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n");
|
||||
fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n");
|
||||
fCStr_defaultFragShader.add("\r\n");
|
||||
fCStr_defaultFragShader.add("void main(){\r\n");
|
||||
fCStr_defaultFragShader.add("colorOut0 = vec4(texture(textureSrc, passUV).r,0.0,0.0,1.0);\r\n");
|
||||
fCStr_defaultFragShader.add("}\r\n");
|
||||
|
||||
defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader);
|
||||
catchOpenGLError();
|
||||
|
||||
defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc");
|
||||
defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets");
|
||||
|
||||
_copyShader_depthToColor = defaultShader;
|
||||
catchOpenGLError();
|
||||
return defaultShader;
|
||||
}
|
||||
|
||||
LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_colorToDepth()
|
||||
{
|
||||
if (_copyShader_colorToDepth != 0)
|
||||
return _copyShader_colorToDepth;
|
||||
catchOpenGLError();
|
||||
LatteDefaultShader_t* defaultShader = (LatteDefaultShader_t*)malloc(sizeof(LatteDefaultShader_t));
|
||||
memset(defaultShader, 0, sizeof(LatteDefaultShader_t));
|
||||
|
||||
StringBuf fCStr_vertexShader(1024 * 16);
|
||||
LatteDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader);
|
||||
|
||||
StringBuf fCStr_defaultFragShader(1024 * 16);
|
||||
fCStr_defaultFragShader.add("#version 420\r\n");
|
||||
fCStr_defaultFragShader.add("in vec2 passUV;\r\n");
|
||||
fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n");
|
||||
fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n");
|
||||
fCStr_defaultFragShader.add("\r\n");
|
||||
fCStr_defaultFragShader.add("void main(){\r\n");
|
||||
fCStr_defaultFragShader.add("gl_FragDepth = texture(textureSrc, passUV).r;\r\n");
|
||||
fCStr_defaultFragShader.add("}\r\n");
|
||||
|
||||
|
||||
defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader);
|
||||
defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc");
|
||||
defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets");
|
||||
|
||||
_copyShader_colorToDepth = defaultShader;
|
||||
catchOpenGLError();
|
||||
return defaultShader;
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLuint glProgamId;
|
||||
struct
|
||||
{
|
||||
GLuint uniformLoc_textureSrc;
|
||||
GLuint uniformLoc_vertexOffsets;
|
||||
}copyShaderUniforms;
|
||||
}LatteDefaultShader_t;
|
||||
|
||||
LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_depthToColor();
|
||||
LatteDefaultShader_t* LatteDefaultShader_getPixelCopyShader_colorToDepth();
|
||||
@ -10,6 +10,7 @@
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "HW/Latte/Renderer/RendererCore.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "WindowSystem.h"
|
||||
#include "Cafe/OS/libs/erreula/erreula.h"
|
||||
@ -694,7 +695,9 @@ void LatteRenderTarget_itHLESwapScanBuffer()
|
||||
performanceMonitor.gpuTime_frameTime.beginMeasuring();
|
||||
|
||||
LatteTC_CleanupUnusedTextures();
|
||||
#ifdef ENABLE_OPENGL
|
||||
LatteDraw_cleanupAfterFrame();
|
||||
#endif
|
||||
LatteQuery_CancelActiveGPU7Queries();
|
||||
LatteBufferCache_notifySwapTVScanBuffer();
|
||||
LattePerformanceMonitor_frameBegin();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
@ -6,16 +6,16 @@
|
||||
#include "Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h"
|
||||
#include "Cafe/HW/Latte/Core/FetchShader.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2.h" // todo - remove dependency
|
||||
#endif
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "HW/Latte/Core/Latte.h"
|
||||
#include "HW/Latte/Renderer/Renderer.h"
|
||||
#include "util/helpers/StringParser.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Cafe/GameProfile/GameProfile.h"
|
||||
#include "util/containers/flat_hash_map.hpp"
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||
#endif
|
||||
#include <cinttypes>
|
||||
@ -376,7 +376,9 @@ void LatteShader_FinishCompilation(LatteDecompilerShader* shader)
|
||||
}
|
||||
shader->shader->WaitForCompiled();
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
LatteShader_prepareSeparableUniforms(shader);
|
||||
#endif
|
||||
LatteShader_CleanupAfterCompile(shader);
|
||||
}
|
||||
|
||||
@ -525,7 +527,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
|
||||
if (LatteGPUState.contextNew.PA_CL_CLIP_CNTL.get_DX_CLIP_SPACE_DEF())
|
||||
vsHash += 0x1537;
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
bool isRectVertexShader = (primitiveType == Latte::LATTE_VGT_PRIMITIVE_TYPE::E_PRIMITIVE_TYPE::RECTS);
|
||||
@ -550,7 +552,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
|
||||
vsHash += 51ULL;
|
||||
|
||||
// Vertex fetch
|
||||
if (_activeFetchShader->mtlFetchVertexManually)
|
||||
if (_activeFetchShader->mtlFetchVertexManually)
|
||||
vsHash += 349ULL;
|
||||
}
|
||||
}
|
||||
@ -650,7 +652,7 @@ uint64 LatteSHRC_CalcPSAuxHash(LatteDecompilerShader* pixelShader, uint32* conte
|
||||
auxHash += (uint64)dim;
|
||||
}
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
// Textures as render targets
|
||||
@ -683,56 +685,75 @@ uint64 LatteSHRC_CalcPSAuxHash(LatteDecompilerShader* pixelShader, uint32* conte
|
||||
return auxHash;
|
||||
}
|
||||
|
||||
static void InitUniformLayoutFromDecompiler(
|
||||
LatteDecompilerShader* shader,
|
||||
const LatteDecompilerOutput_t& decompilerOutput
|
||||
)
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
// hack - for OpenGL these are retrieved in _prepareSeparableUniforms()
|
||||
shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsGL.count_uniformRegister;
|
||||
return;
|
||||
}
|
||||
const auto& offsets = decompilerOutput.uniformOffsetsVK;
|
||||
|
||||
shader->uniform.loc_remapped = offsets.offset_remapped;
|
||||
shader->uniform.loc_uniformRegister = offsets.offset_uniformRegister;
|
||||
shader->uniform.count_uniformRegister = offsets.count_uniformRegister;
|
||||
shader->uniform.loc_windowSpaceToClipSpaceTransform = offsets.offset_windowSpaceToClipSpaceTransform;
|
||||
shader->uniform.loc_alphaTestRef = offsets.offset_alphaTestRef;
|
||||
shader->uniform.loc_pointSize = offsets.offset_pointSize;
|
||||
shader->uniform.loc_fragCoordScale = offsets.offset_fragCoordScale;
|
||||
|
||||
// Texture scale uniforms
|
||||
shader->uniform.list_ufTexRescale.clear();
|
||||
for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++)
|
||||
{
|
||||
if (offsets.offset_texScale[t] >= 0)
|
||||
{
|
||||
LatteUniformTextureScaleEntry_t entry{};
|
||||
entry.texUnit = t;
|
||||
entry.uniformLocation = offsets.offset_texScale[t];
|
||||
shader->uniform.list_ufTexRescale.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
shader->uniform.loc_verticesPerInstance = offsets.offset_verticesPerInstance;
|
||||
|
||||
// Streamout buffers
|
||||
for (sint32 t = 0; t < LATTE_NUM_STREAMOUT_BUFFER; t++)
|
||||
{
|
||||
shader->uniform.loc_streamoutBufferBase[t] = offsets.offset_streamoutBufferBase[t];
|
||||
}
|
||||
|
||||
shader->uniform.uniformRangeSize = offsets.offset_endOfBlock;
|
||||
}
|
||||
|
||||
LatteDecompilerShader* LatteShader_CreateShaderFromDecompilerOutput(LatteDecompilerOutput_t& decompilerOutput, uint64 baseHash, bool calculateAuxHash, uint64 optionalAuxHash, uint32* contextRegister)
|
||||
{
|
||||
LatteDecompilerShader* shader = decompilerOutput.shader;
|
||||
shader->baseHash = baseHash;
|
||||
// copy resource mapping
|
||||
// HACK
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
switch (g_renderer->GetType())
|
||||
{
|
||||
case RendererAPI::OpenGL:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingGL;
|
||||
else if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
break;
|
||||
case RendererAPI::Vulkan:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingVK;
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
break;
|
||||
case RendererAPI::Metal:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingMTL;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
// copy texture info
|
||||
shader->textureUnitMask2 = decompilerOutput.textureUnitMask;
|
||||
// copy streamout info
|
||||
shader->streamoutBufferWriteMask = decompilerOutput.streamoutBufferWriteMask;
|
||||
shader->hasStreamoutBufferWrite = decompilerOutput.streamoutBufferWriteMask.any();
|
||||
// copy uniform offsets
|
||||
// for OpenGL these are retrieved in _prepareSeparableUniforms()
|
||||
// HACK
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan || g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
shader->uniform.loc_remapped = decompilerOutput.uniformOffsetsVK.offset_remapped;
|
||||
shader->uniform.loc_uniformRegister = decompilerOutput.uniformOffsetsVK.offset_uniformRegister;
|
||||
shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsVK.count_uniformRegister;
|
||||
shader->uniform.loc_windowSpaceToClipSpaceTransform = decompilerOutput.uniformOffsetsVK.offset_windowSpaceToClipSpaceTransform;
|
||||
shader->uniform.loc_alphaTestRef = decompilerOutput.uniformOffsetsVK.offset_alphaTestRef;
|
||||
shader->uniform.loc_pointSize = decompilerOutput.uniformOffsetsVK.offset_pointSize;
|
||||
shader->uniform.loc_fragCoordScale = decompilerOutput.uniformOffsetsVK.offset_fragCoordScale;
|
||||
for (sint32 t = 0; t < LATTE_NUM_MAX_TEX_UNITS; t++)
|
||||
{
|
||||
if (decompilerOutput.uniformOffsetsVK.offset_texScale[t] >= 0)
|
||||
{
|
||||
LatteUniformTextureScaleEntry_t entry = { 0 };
|
||||
entry.texUnit = t;
|
||||
entry.uniformLocation = decompilerOutput.uniformOffsetsVK.offset_texScale[t];
|
||||
shader->uniform.list_ufTexRescale.push_back(entry);
|
||||
}
|
||||
}
|
||||
shader->uniform.loc_verticesPerInstance = decompilerOutput.uniformOffsetsVK.offset_verticesPerInstance;
|
||||
for (sint32 t = 0; t < LATTE_NUM_STREAMOUT_BUFFER; t++)
|
||||
shader->uniform.loc_streamoutBufferBase[t] = decompilerOutput.uniformOffsetsVK.offset_streamoutBufferBase[t];
|
||||
shader->uniform.uniformRangeSize = decompilerOutput.uniformOffsetsVK.offset_endOfBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsGL.count_uniformRegister;
|
||||
}
|
||||
InitUniformLayoutFromDecompiler(shader, decompilerOutput);
|
||||
// calculate aux hash
|
||||
if (calculateAuxHash)
|
||||
{
|
||||
@ -766,10 +787,12 @@ void LatteShader_GetDecompilerOptions(LatteDecompilerOptions& options, LatteCons
|
||||
options.usesGeometryShader = geometryShaderEnabled;
|
||||
options.spirvInstrinsics.hasRoundingModeRTEFloat32 = false;
|
||||
options.useTFViaSSBO = g_renderer->UseTFViaSSBO();
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
{
|
||||
options.spirvInstrinsics.hasRoundingModeRTEFloat32 = VulkanRenderer::GetInstance()->HasSPRIVRoundingModeRTE32();
|
||||
}
|
||||
#endif
|
||||
options.strictMul = g_current_game_profile->GetAccurateShaderMul() != AccurateShaderMulOption::False;
|
||||
}
|
||||
|
||||
@ -845,12 +868,14 @@ LatteDecompilerShader* LatteShader_CompileSeparableVertexShader(uint64 baseHash,
|
||||
LatteShader_CreateRendererShader(vertexShader, false);
|
||||
performanceMonitor.numCompiledVS++;
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
if (vertexShader->shader)
|
||||
vertexShader->shader->PreponeCompilation(true);
|
||||
LatteShader_FinishCompilation(vertexShader);
|
||||
}
|
||||
#endif
|
||||
|
||||
LatteSHRC_RegisterShader(vertexShader, vertexShader->baseHash, vertexShader->auxHash);
|
||||
return vertexShader;
|
||||
@ -874,12 +899,14 @@ LatteDecompilerShader* LatteShader_CompileSeparableGeometryShader(uint64 baseHas
|
||||
LatteShader_CreateRendererShader(geometryShader, false);
|
||||
performanceMonitor.numCompiledGS++;
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
if (geometryShader->shader)
|
||||
geometryShader->shader->PreponeCompilation(true);
|
||||
LatteShader_FinishCompilation(geometryShader);
|
||||
}
|
||||
#endif
|
||||
|
||||
LatteSHRC_RegisterShader(geometryShader, geometryShader->baseHash, geometryShader->auxHash);
|
||||
return geometryShader;
|
||||
@ -903,12 +930,14 @@ LatteDecompilerShader* LatteShader_CompileSeparablePixelShader(uint64 baseHash,
|
||||
LatteShaderCache_writeSeparablePixelShader(_shaderBaseHash_ps, psAuxHash, pixelShaderPtr, pixelShaderSize, LatteGPUState.contextRegister, usesGeometryShader);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
if (pixelShader->shader)
|
||||
pixelShader->shader->PreponeCompilation(true);
|
||||
LatteShader_FinishCompilation(pixelShader);
|
||||
}
|
||||
#endif
|
||||
|
||||
LatteSHRC_RegisterShader(pixelShader, _shaderBaseHash_ps, psAuxHash);
|
||||
return pixelShader;
|
||||
|
||||
@ -9,13 +9,17 @@
|
||||
#include "WindowSystem.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h"
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h"
|
||||
#if ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h"
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
|
||||
#endif
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include "imgui/imgui_extension.h"
|
||||
@ -273,14 +277,24 @@ static BootSoundPlayer g_bootSndPlayer;
|
||||
|
||||
void LatteShaderCache_finish()
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
switch (g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
RendererShaderVk::ShaderCacheLoading_end();
|
||||
else if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
RendererShaderGL::ShaderCacheLoading_end();
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
RendererShaderMtl::ShaderCacheLoading_end();
|
||||
return;
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
RendererShaderGL::ShaderCacheLoading_end();
|
||||
return;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
RendererShaderMtl::ShaderCacheLoading_end();
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint32 LatteShaderCache_getShaderCacheExtraVersion(uint64 titleId)
|
||||
@ -359,21 +373,36 @@ void LatteShaderCache_Load()
|
||||
fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec);
|
||||
fs::create_directories(ActiveSettings::GetCachePath("shaderCache/precompiled"), ec);
|
||||
// initialize renderer specific caches
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
RendererShaderVk::ShaderCacheLoading_begin(cacheTitleId);
|
||||
else if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId);
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
RendererShaderMtl::ShaderCacheLoading_begin(cacheTitleId);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
RendererShaderMtl::ShaderCacheLoading_begin(cacheTitleId);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
// get cache file name
|
||||
fs::path pathGeneric;
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
case RendererAPI::Metal:
|
||||
pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_mtlshaders.bin", cacheTitleId);
|
||||
else
|
||||
break;
|
||||
default:
|
||||
pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId);
|
||||
break;
|
||||
}
|
||||
|
||||
// calculate extraVersion for transferable and precompiled shader cache
|
||||
uint32 transferableExtraVersion = SHADER_CACHE_GENERIC_EXTRA_VERSION;
|
||||
@ -471,8 +500,10 @@ void LatteShaderCache_Load()
|
||||
#endif
|
||||
LatteShaderCache_finish();
|
||||
// if Vulkan or Metal then also load pipeline cache
|
||||
#if defined(ENABLE_VULKAN) || defined(ENABLE_METAL)
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan || g_renderer->GetType() == RendererAPI::Metal)
|
||||
LatteShaderCache_LoadPipelineCache(cacheTitleId);
|
||||
#endif
|
||||
|
||||
|
||||
g_renderer->BeginFrame(true);
|
||||
@ -634,31 +665,52 @@ void LatteShaderCache_ShowProgress(const std::function <bool(void)>& loadUpdateF
|
||||
|
||||
void LatteShaderCache_LoadPipelineCache(uint64 cacheTitleId)
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
g_shaderCacheLoaderState.pipelineFileCount = VulkanPipelineStableCache::GetInstance().BeginLoading(cacheTitleId);
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
g_shaderCacheLoaderState.pipelineFileCount = MetalPipelineCache::GetInstance().BeginLoading(cacheTitleId);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
g_shaderCacheLoaderState.loadedPipelines = 0;
|
||||
LatteShaderCache_ShowProgress(LatteShaderCache_updatePipelineLoadingProgress, true);
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
VulkanPipelineStableCache::GetInstance().EndLoading();
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
MetalPipelineCache::GetInstance().EndLoading();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
MetalPipelineCache::GetInstance().EndLoading();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
bool LatteShaderCache_updatePipelineLoadingProgress()
|
||||
{
|
||||
uint32 pipelinesMissingShaders = 0;
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
return VulkanPipelineStableCache::GetInstance().UpdateLoading(g_shaderCacheLoaderState.loadedPipelines, pipelinesMissingShaders);
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
return MetalPipelineCache::GetInstance().UpdateLoading(g_shaderCacheLoaderState.loadedPipelines, pipelinesMissingShaders);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -918,20 +970,37 @@ void LatteShaderCache_Close()
|
||||
delete s_shaderCacheGeneric;
|
||||
s_shaderCacheGeneric = nullptr;
|
||||
}
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
RendererShaderVk::ShaderCacheLoading_Close();
|
||||
else if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
RendererShaderGL::ShaderCacheLoading_Close();
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
RendererShaderMtl::ShaderCacheLoading_Close();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
RendererShaderGL::ShaderCacheLoading_Close();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
RendererShaderMtl::ShaderCacheLoading_Close();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
// if Vulkan or Metal then also close pipeline cache
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
VulkanPipelineStableCache::GetInstance().Close();
|
||||
#if ENABLE_METAL
|
||||
else if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
MetalPipelineCache::GetInstance().Close();
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
VulkanPipelineStableCache::GetInstance().Close();
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
MetalPipelineCache::GetInstance().Close();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteShader.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteTexture.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteSurfaceCopy.h"
|
||||
|
||||
|
||||
@ -567,6 +567,8 @@ bool __LatteTexture_IsBlockedFormatRelation(LatteTexture* texture1, LatteTexture
|
||||
if (texture1->format == Latte::E_GX2SURFFMT::D32_FLOAT && Latte::GetHWFormat(texture2->format) == Latte::E_HWSURFFMT::HWFMT_8_8_8_8)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
// Vulkan has stricter rules
|
||||
if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
{
|
||||
@ -574,6 +576,7 @@ bool __LatteTexture_IsBlockedFormatRelation(LatteTexture* texture1, LatteTexture
|
||||
if (texture1->format == Latte::E_GX2SURFFMT::D32_FLOAT && Latte::GetHWFormat(texture2->format) == Latte::E_HWSURFFMT::HWFMT_8_24)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -4,9 +4,11 @@
|
||||
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h"
|
||||
#endif
|
||||
|
||||
struct TexScaleXY
|
||||
{
|
||||
@ -192,6 +194,7 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u
|
||||
LatteGPUState.repeatTextureInitialization = true;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
// on OpenGL, texture views and sampler parameters are tied together (we are avoiding sampler objects due to driver bugs)
|
||||
@ -214,6 +217,8 @@ void LatteTexture_updateTexturesForStage(LatteDecompilerShader* shaderContext, u
|
||||
textureView->lastTextureBindIndex = LatteGPUState.textureBindCounter;
|
||||
rendererGL->renderstate_updateTextureSettingsGL(shaderContext, textureView, textureIndex + glBackendBaseTexUnit, word4, textureIndex, isDepthSampler);
|
||||
}
|
||||
#endif
|
||||
|
||||
g_renderer->texture_setLatteTexture(textureView, textureIndex + glBackendBaseTexUnit);
|
||||
// update if data changed
|
||||
bool swizzleChanged = false;
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
|
||||
#include "Common/GLInclude/GLInclude.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteTexture.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h"
|
||||
|
||||
#define LOG_READBACK_TIME
|
||||
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "Cafe/OS/libs/gx2/GX2_Event.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
||||
#endif
|
||||
#include "util/highresolutiontimer/HighResolutionTimer.h"
|
||||
#include "config/CemuConfig.h"
|
||||
#include "Cafe/CafeSystem.h"
|
||||
@ -55,8 +57,10 @@ void LatteTiming_EnableHostDrivenVSync()
|
||||
{
|
||||
if (s_usingHostDrivenVSync)
|
||||
return;
|
||||
#ifdef ENABLE_VULKAN
|
||||
VsyncDriver_startThread(LatteTiming_NotifyHostVSync);
|
||||
s_usingHostDrivenVSync = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool LatteTiming_IsUsingHostDrivenVSync()
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
#include "Cafe/HW/Latte/Core/FetchShader.h"
|
||||
#include "Cafe/HW/Latte/Core/LattePerformanceMonitor.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#endif
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
// parse instruction and if valid append it to instructionList
|
||||
@ -1069,12 +1071,18 @@ void _LatteDecompiler_Process(LatteDecompilerShaderContext* shaderContext, uint8
|
||||
// emit code
|
||||
if (shaderContext->shader->hasError == false)
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL || g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader);
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL || g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
{
|
||||
#if defined(ENABLE_OPENGL) || defined(ENABLE_VULKAN)
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
#endif
|
||||
}
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
#ifdef ENABLE_METAL
|
||||
LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
LatteDecompiler_cleanup(shaderContext);
|
||||
// fast access
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Common/MemPtr.h"
|
||||
#include "HW/Latte/ISA/LatteReg.h"
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
#include "HW/Latte/Renderer/Metal/MetalCommon.h"
|
||||
#endif
|
||||
|
||||
@ -403,11 +403,9 @@ void LatteDecompiler_analyzeExport(LatteDecompilerShaderContext* shaderContext,
|
||||
}
|
||||
else if (cfInstruction->exportType == 0 && cfInstruction->exportArrayBase == 61)
|
||||
{
|
||||
#if ENABLE_METAL
|
||||
// Only check for depth buffer mask on Metal, as its not in the PS hash on other backends
|
||||
if (g_renderer->GetType() != RendererAPI::Metal || LatteMRT::GetActiveDepthBufferMask(*shaderContext->contextRegistersNew))
|
||||
shader->depthMask = true;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
debugBreakpoint();
|
||||
@ -512,7 +510,7 @@ namespace LatteDecompiler
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
void _initTextureBindingPointsMTL(LatteDecompilerShaderContext* decompilerContext)
|
||||
{
|
||||
// for Vulkan we use consecutive indices
|
||||
@ -563,7 +561,7 @@ namespace LatteDecompiler
|
||||
{
|
||||
decompilerContext->hasUniformVarBlock = true; // uf_verticesPerInstance and uf_streamoutBufferBase*
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
{
|
||||
bool usesGeometryShader = UseGeometryShader(*decompilerContext->contextRegistersNew, decompilerContext->options->usesGeometryShader);
|
||||
@ -1113,7 +1111,7 @@ void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteD
|
||||
shaderContext->output->resourceMappingVK.setIndex = 2;
|
||||
LatteDecompiler::_initTextureBindingPointsGL(shaderContext);
|
||||
LatteDecompiler::_initTextureBindingPointsVK(shaderContext);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
LatteDecompiler::_initTextureBindingPointsMTL(shaderContext);
|
||||
#endif
|
||||
LatteDecompiler::_initUniformBindingPoints(shaderContext);
|
||||
|
||||
@ -266,7 +266,7 @@ struct LatteDecompilerShaderContext
|
||||
void LatteDecompiler_analyze(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
void LatteDecompiler_analyzeDataTypes(LatteDecompilerShaderContext* shaderContext);
|
||||
void LatteDecompiler_emitGLSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
void LatteDecompiler_emitMSLShader(LatteDecompilerShaderContext* shaderContext, LatteDecompilerShader* shader);
|
||||
#endif
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ extern bool hasValidFramebufferAttached;
|
||||
|
||||
float supportBufferData[512 * 4];
|
||||
|
||||
// Defined in the OpenGL renderer
|
||||
// Defined in the Common renderer
|
||||
void LatteDraw_handleSpecialState8_clearAsDepth();
|
||||
|
||||
std::vector<MetalRenderer::DeviceInfo> MetalRenderer::GetDevices()
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include "Cafe/OS/libs/gx2/GX2.h"
|
||||
|
||||
#include "Cafe/GameProfile/GameProfile.h"
|
||||
#include "HW/Latte/Renderer/RendererCore.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
|
||||
@ -52,7 +53,7 @@ struct
|
||||
uint32 maxIndex;
|
||||
uint32 minIndex;
|
||||
uint8* indexData;
|
||||
// buffer
|
||||
// buffer
|
||||
GLuint glIndexCacheBuffer;
|
||||
VirtualBufferHeap_t* indexBufferVirtualHeap;
|
||||
uint8* mappedIndexBuffer;
|
||||
@ -371,6 +372,8 @@ void _decodeAndUploadIndexData(indexDataCacheEntry2_t* cacheEntry)
|
||||
|
||||
void LatteDraw_cleanupAfterFrame()
|
||||
{
|
||||
if (g_renderer->GetType() != RendererAPI::OpenGL)
|
||||
return;
|
||||
// drop everything from cache that is older than 30 frames
|
||||
uint32 frameCounter = LatteGPUState.frameCounter;
|
||||
while (indexDataCacheFirst)
|
||||
@ -524,70 +527,6 @@ void LatteDrawGL_prepareIndicesWithGPUCache(MPTR indexDataMPTR, _INDEX_TYPE inde
|
||||
indexState.indexData = (uint8*)(size_t)cacheEntry->heapEntry->startOffset;
|
||||
}
|
||||
|
||||
void LatteDraw_handleSpecialState8_clearAsDepth()
|
||||
{
|
||||
if (LatteGPUState.contextNew.GetSpecialStateValues()[0] == 0)
|
||||
cemuLog_logDebug(LogType::Force, "Special state 8 requires special state 0 but it is not set?");
|
||||
// get depth buffer information
|
||||
uint32 regDepthBuffer = LatteGPUState.contextRegister[mmDB_HTILE_DATA_BASE];
|
||||
uint32 regDepthSize = LatteGPUState.contextRegister[mmDB_DEPTH_SIZE];
|
||||
uint32 regDepthBufferInfo = LatteGPUState.contextRegister[mmDB_DEPTH_INFO];
|
||||
// get format and tileMode from info reg
|
||||
uint32 depthBufferTileMode = (regDepthBufferInfo >> 15) & 0xF;
|
||||
|
||||
MPTR depthBufferPhysMem = regDepthBuffer << 8;
|
||||
uint32 depthBufferPitch = (((regDepthSize >> 0) & 0x3FF) + 1);
|
||||
uint32 depthBufferHeight = ((((regDepthSize >> 10) & 0xFFFFF) + 1) / depthBufferPitch);
|
||||
depthBufferPitch <<= 3;
|
||||
depthBufferHeight <<= 3;
|
||||
uint32 depthBufferWidth = depthBufferPitch;
|
||||
|
||||
sint32 sliceIndex = 0; // todo
|
||||
sint32 mipIndex = 0;
|
||||
|
||||
// clear all color buffers that match the format of the depth buffer
|
||||
sint32 searchIndex = 0;
|
||||
bool targetFound = false;
|
||||
while (true)
|
||||
{
|
||||
LatteTextureView* view = LatteTC_LookupTextureByData(depthBufferPhysMem, depthBufferWidth, depthBufferHeight, depthBufferPitch, 0, 1, sliceIndex, 1, &searchIndex);
|
||||
if (!view)
|
||||
{
|
||||
// should we clear in RAM instead?
|
||||
break;
|
||||
}
|
||||
sint32 effectiveClearWidth = view->baseTexture->width;
|
||||
sint32 effectiveClearHeight = view->baseTexture->height;
|
||||
LatteTexture_scaleToEffectiveSize(view->baseTexture, &effectiveClearWidth, &effectiveClearHeight, 0);
|
||||
|
||||
// hacky way to get clear color
|
||||
float* regClearColor = (float*)(LatteGPUState.contextRegister + 0xC000 + 0); // REG_BASE_ALU_CONST
|
||||
|
||||
uint8 clearColor[4] = { 0 };
|
||||
clearColor[0] = (uint8)(regClearColor[0] * 255.0f);
|
||||
clearColor[1] = (uint8)(regClearColor[1] * 255.0f);
|
||||
clearColor[2] = (uint8)(regClearColor[2] * 255.0f);
|
||||
clearColor[3] = (uint8)(regClearColor[3] * 255.0f);
|
||||
|
||||
// todo - use fragment shader software emulation (evoke for one pixel) to determine clear color
|
||||
// todo - dont clear entire slice, use effectiveClearWidth, effectiveClearHeight
|
||||
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
//cemu_assert_debug(false); // implement g_renderer->texture_clearColorSlice properly for OpenGL renderer
|
||||
if (glClearTexSubImage)
|
||||
glClearTexSubImage(((LatteTextureViewGL*)view)->glTexId, mipIndex, 0, 0, 0, effectiveClearWidth, effectiveClearHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (view->baseTexture->isDepth)
|
||||
g_renderer->texture_clearDepthSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, true, view->baseTexture->hasStencil, 0.0f, 0);
|
||||
else
|
||||
g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LatteDrawGL_doDraw(_INDEX_TYPE indexType, uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count)
|
||||
{
|
||||
if (indexType == _INDEX_TYPE::U16_BE)
|
||||
@ -755,10 +694,6 @@ void OpenGLRenderer::_setupVertexAttributes()
|
||||
}
|
||||
}
|
||||
|
||||
void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx);
|
||||
void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant);
|
||||
void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister);
|
||||
|
||||
std::map<uint64, RendererShaderGL*> g_mapGLRectEmulationGS;
|
||||
|
||||
RendererShaderGL* rectsEmulationGS_generateShaderGL(LatteDecompilerShader* vertexShader)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/CachedFBOGL.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureGL.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h"
|
||||
@ -8,7 +9,7 @@
|
||||
#include "Cafe/HW/Latte/Core/LatteShader.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteDraw.h"
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LatteDefaultShaders.h"
|
||||
#include "util/helpers/StringBuf.h"
|
||||
|
||||
void LatteDraw_resetAttributePointerCache();
|
||||
|
||||
@ -66,9 +67,9 @@ void OpenGLRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* s
|
||||
if (destinationTexture->isDepth)
|
||||
renderstate_setAlwaysWriteDepth();
|
||||
// bind format specific copy shader
|
||||
LatteDefaultShader_t* copyShader = LatteDefaultShader_getPixelCopyShader_depthToColor();
|
||||
LatteGLDefaultShader_t* copyShader = LatteGLDefaultShader_getPixelCopyShader_depthToColor();
|
||||
if (destinationTexture->isDepth)
|
||||
copyShader = LatteDefaultShader_getPixelCopyShader_colorToDepth();
|
||||
copyShader = LatteGLDefaultShader_getPixelCopyShader_colorToDepth();
|
||||
glUseProgram(copyShader->glProgamId);
|
||||
catchOpenGLError();
|
||||
// setup uniforms
|
||||
@ -113,4 +114,86 @@ void OpenGLRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* s
|
||||
|
||||
LatteGPUState.repeatTextureInitialization = true;
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
LatteGLDefaultShader_t* _copyShader_depthToColor;
|
||||
LatteGLDefaultShader_t* _copyShader_colorToDepth;
|
||||
|
||||
void LatteGLDefaultShader_pixelCopyShader_generateVSBody(StringBuf* vs)
|
||||
{
|
||||
vs->add("#version 420\r\n");
|
||||
vs->add("out vec2 passUV;\r\n");
|
||||
vs->add("uniform vec4 uf_vertexOffsets[4];\r\n");
|
||||
vs->add("\r\n");
|
||||
vs->add("void main(){\r\n");
|
||||
vs->add("int vID = gl_VertexID;\r\n");
|
||||
vs->add("passUV = uf_vertexOffsets[vID].zw;\r\n");
|
||||
vs->add("gl_Position = vec4(uf_vertexOffsets[vID].xy, 0.0, 1.0);\r\n");
|
||||
vs->add("}\r\n");
|
||||
}
|
||||
|
||||
GLuint gxShaderDepr_compileRaw(StringBuf* strSourceVS, StringBuf* strSourceFS);
|
||||
GLuint gxShaderDepr_compileRaw(const std::string& vertex_source, const std::string& fragment_source);
|
||||
|
||||
LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_depthToColor()
|
||||
{
|
||||
if (_copyShader_depthToColor != 0)
|
||||
return _copyShader_depthToColor;
|
||||
catchOpenGLError();
|
||||
LatteGLDefaultShader_t* defaultShader = (LatteGLDefaultShader_t*)malloc(sizeof(LatteGLDefaultShader_t));
|
||||
memset(defaultShader, 0, sizeof(LatteGLDefaultShader_t));
|
||||
|
||||
StringBuf fCStr_vertexShader(1024 * 16);
|
||||
LatteGLDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader);
|
||||
|
||||
StringBuf fCStr_defaultFragShader(1024 * 16);
|
||||
fCStr_defaultFragShader.add("#version 420\r\n");
|
||||
fCStr_defaultFragShader.add("in vec2 passUV;\r\n");
|
||||
fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n");
|
||||
fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n");
|
||||
fCStr_defaultFragShader.add("\r\n");
|
||||
fCStr_defaultFragShader.add("void main(){\r\n");
|
||||
fCStr_defaultFragShader.add("colorOut0 = vec4(texture(textureSrc, passUV).r,0.0,0.0,1.0);\r\n");
|
||||
fCStr_defaultFragShader.add("}\r\n");
|
||||
|
||||
defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader);
|
||||
catchOpenGLError();
|
||||
|
||||
defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc");
|
||||
defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets");
|
||||
|
||||
_copyShader_depthToColor = defaultShader;
|
||||
catchOpenGLError();
|
||||
return defaultShader;
|
||||
}
|
||||
|
||||
LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_colorToDepth()
|
||||
{
|
||||
if (_copyShader_colorToDepth != 0)
|
||||
return _copyShader_colorToDepth;
|
||||
catchOpenGLError();
|
||||
LatteGLDefaultShader_t* defaultShader = (LatteGLDefaultShader_t*)malloc(sizeof(LatteGLDefaultShader_t));
|
||||
memset(defaultShader, 0, sizeof(LatteGLDefaultShader_t));
|
||||
|
||||
StringBuf fCStr_vertexShader(1024 * 16);
|
||||
LatteGLDefaultShader_pixelCopyShader_generateVSBody(&fCStr_vertexShader);
|
||||
|
||||
StringBuf fCStr_defaultFragShader(1024 * 16);
|
||||
fCStr_defaultFragShader.add("#version 420\r\n");
|
||||
fCStr_defaultFragShader.add("in vec2 passUV;\r\n");
|
||||
fCStr_defaultFragShader.add("uniform sampler2D textureSrc;\r\n");
|
||||
fCStr_defaultFragShader.add("layout(location = 0) out vec4 colorOut0;\r\n");
|
||||
fCStr_defaultFragShader.add("\r\n");
|
||||
fCStr_defaultFragShader.add("void main(){\r\n");
|
||||
fCStr_defaultFragShader.add("gl_FragDepth = texture(textureSrc, passUV).r;\r\n");
|
||||
fCStr_defaultFragShader.add("}\r\n");
|
||||
|
||||
|
||||
defaultShader->glProgamId = gxShaderDepr_compileRaw(&fCStr_vertexShader, &fCStr_defaultFragShader);
|
||||
defaultShader->copyShaderUniforms.uniformLoc_textureSrc = glGetUniformLocation(defaultShader->glProgamId, "textureSrc");
|
||||
defaultShader->copyShaderUniforms.uniformLoc_vertexOffsets = glGetUniformLocation(defaultShader->glProgamId, "uf_vertexOffsets");
|
||||
|
||||
_copyShader_colorToDepth = defaultShader;
|
||||
catchOpenGLError();
|
||||
return defaultShader;
|
||||
}
|
||||
14
src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h
Normal file
14
src/Cafe/HW/Latte/Renderer/OpenGL/OpenGLSurfaceCopy.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GLuint glProgamId;
|
||||
struct
|
||||
{
|
||||
GLuint uniformLoc_textureSrc;
|
||||
GLuint uniformLoc_vertexOffsets;
|
||||
}copyShaderUniforms;
|
||||
}LatteGLDefaultShader_t;
|
||||
|
||||
LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_depthToColor();
|
||||
LatteGLDefaultShader_t* LatteGLDefaultShader_getPixelCopyShader_colorToDepth();
|
||||
132
src/Cafe/HW/Latte/Renderer/RendererCore.cpp
Normal file
132
src/Cafe/HW/Latte/Renderer/RendererCore.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
#include "RendererCore.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/ISA/RegDefines.h"
|
||||
#include "HW/Latte/Core/LatteShader.h"
|
||||
#include "config/CemuConfig.h"
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Common/GLInclude/GLInclude.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h"
|
||||
#endif
|
||||
|
||||
void LatteDraw_handleSpecialState8_clearAsDepth()
|
||||
{
|
||||
if (LatteGPUState.contextNew.GetSpecialStateValues()[0] == 0)
|
||||
cemuLog_logDebug(LogType::Force, "Special state 8 requires special state 0 but it is not set?");
|
||||
// get depth buffer information
|
||||
uint32 regDepthBuffer = LatteGPUState.contextRegister[mmDB_HTILE_DATA_BASE];
|
||||
uint32 regDepthSize = LatteGPUState.contextRegister[mmDB_DEPTH_SIZE];
|
||||
uint32 regDepthBufferInfo = LatteGPUState.contextRegister[mmDB_DEPTH_INFO];
|
||||
// get format and tileMode from info reg
|
||||
uint32 depthBufferTileMode = (regDepthBufferInfo >> 15) & 0xF;
|
||||
|
||||
MPTR depthBufferPhysMem = regDepthBuffer << 8;
|
||||
uint32 depthBufferPitch = (((regDepthSize >> 0) & 0x3FF) + 1);
|
||||
uint32 depthBufferHeight = ((((regDepthSize >> 10) & 0xFFFFF) + 1) / depthBufferPitch);
|
||||
depthBufferPitch <<= 3;
|
||||
depthBufferHeight <<= 3;
|
||||
uint32 depthBufferWidth = depthBufferPitch;
|
||||
|
||||
sint32 sliceIndex = 0; // todo
|
||||
sint32 mipIndex = 0;
|
||||
|
||||
// clear all color buffers that match the format of the depth buffer
|
||||
sint32 searchIndex = 0;
|
||||
bool targetFound = false;
|
||||
while (true)
|
||||
{
|
||||
LatteTextureView* view = LatteTC_LookupTextureByData(depthBufferPhysMem, depthBufferWidth, depthBufferHeight, depthBufferPitch, 0, 1, sliceIndex, 1, &searchIndex);
|
||||
if (!view)
|
||||
{
|
||||
// should we clear in RAM instead?
|
||||
break;
|
||||
}
|
||||
sint32 effectiveClearWidth = view->baseTexture->width;
|
||||
sint32 effectiveClearHeight = view->baseTexture->height;
|
||||
LatteTexture_scaleToEffectiveSize(view->baseTexture, &effectiveClearWidth, &effectiveClearHeight, 0);
|
||||
|
||||
// hacky way to get clear color
|
||||
float* regClearColor = (float*)(LatteGPUState.contextRegister + 0xC000 + 0); // REG_BASE_ALU_CONST
|
||||
|
||||
uint8 clearColor[4] = { 0 };
|
||||
clearColor[0] = (uint8)(regClearColor[0] * 255.0f);
|
||||
clearColor[1] = (uint8)(regClearColor[1] * 255.0f);
|
||||
clearColor[2] = (uint8)(regClearColor[2] * 255.0f);
|
||||
clearColor[3] = (uint8)(regClearColor[3] * 255.0f);
|
||||
|
||||
// todo - use fragment shader software emulation (evoke for one pixel) to determine clear color
|
||||
// todo - dont clear entire slice, use effectiveClearWidth, effectiveClearHeight
|
||||
|
||||
switch (g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
{
|
||||
//cemu_assert_debug(false); // implement g_renderer->texture_clearColorSlice properly for OpenGL renderer
|
||||
if (glClearTexSubImage)
|
||||
glClearTexSubImage(((LatteTextureViewGL*)view)->glTexId, mipIndex, 0, 0, 0, effectiveClearWidth, effectiveClearHeight, 1, GL_RGBA, GL_UNSIGNED_BYTE, clearColor);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
if (view->baseTexture->isDepth)
|
||||
g_renderer->texture_clearDepthSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, true, view->baseTexture->hasStencil, 0.0f, 0);
|
||||
else
|
||||
g_renderer->texture_clearColorSlice(view->baseTexture, sliceIndex + view->firstSlice, mipIndex + view->firstMip, clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* rects emulation */
|
||||
|
||||
void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
auto parameterMask = vertexShader->outputParameterMask;
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
{
|
||||
if ((parameterMask & (1 << i)) == 0)
|
||||
continue;
|
||||
sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i);
|
||||
if (vsSemanticId < 0)
|
||||
continue;
|
||||
// make sure PS has matching input
|
||||
if (!psInputTable->hasPSImportForSemanticId(vsSemanticId))
|
||||
continue;
|
||||
gsSrc.append(fmt::format("passParameterSem{}Out = passParameterSem{}In[{}];\r\n", vsSemanticId, vsSemanticId, vIdx));
|
||||
}
|
||||
gsSrc.append(fmt::format("gl_Position = gl_in[{}].gl_Position;\r\n", vIdx));
|
||||
gsSrc.append("EmitVertex();\r\n");
|
||||
}
|
||||
|
||||
void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
auto parameterMask = vertexShader->outputParameterMask;
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
{
|
||||
if ((parameterMask & (1 << i)) == 0)
|
||||
continue;
|
||||
sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i);
|
||||
if (vsSemanticId < 0)
|
||||
continue;
|
||||
// make sure PS has matching input
|
||||
if (!psInputTable->hasPSImportForSemanticId(vsSemanticId))
|
||||
continue;
|
||||
gsSrc.append(fmt::format("passParameterSem{}Out = gen4thVertex{}(passParameterSem{}In[0], passParameterSem{}In[1], passParameterSem{}In[2]);\r\n", vsSemanticId, variant, vsSemanticId, vsSemanticId, vsSemanticId));
|
||||
}
|
||||
gsSrc.append(fmt::format("gl_Position = gen4thVertex{}(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);\r\n", variant));
|
||||
gsSrc.append("EmitVertex();\r\n");
|
||||
}
|
||||
|
||||
void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
sint32 pList[4] = { p0, p1, p2, p3 };
|
||||
for (sint32 i = 0; i < 4; i++)
|
||||
{
|
||||
if (pList[i] == 3)
|
||||
rectsEmulationGS_outputGeneratedVertex(gsSrc, vertexShader, psInputTable, variant, latteRegister);
|
||||
else
|
||||
rectsEmulationGS_outputSingleVertex(gsSrc, vertexShader, psInputTable, pList[i], latteRegister);
|
||||
}
|
||||
}
|
||||
11
src/Cafe/HW/Latte/Renderer/RendererCore.h
Normal file
11
src/Cafe/HW/Latte/Renderer/RendererCore.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cafe/HW/Latte/Core/LatteRingBuffer.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
|
||||
void LatteDraw_handleSpecialState8_clearAsDepth();
|
||||
|
||||
class LatteShaderPSInputTable;
|
||||
void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx);
|
||||
void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant);
|
||||
void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister);
|
||||
@ -1,5 +1,6 @@
|
||||
#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h"
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
const std::string RendererOutputShader::s_copy_shader_source =
|
||||
@ -246,10 +247,17 @@ fragment float4 main0(VertexOut in [[stage_in]], texture2d<float> textureSrc [[t
|
||||
RendererOutputShader::RendererOutputShader(const std::string& vertex_source, const std::string& fragment_source)
|
||||
{
|
||||
std::string finalFragmentSrc;
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
finalFragmentSrc = fragment_source;
|
||||
else
|
||||
finalFragmentSrc = PrependFragmentPreamble(fragment_source);
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
finalFragmentSrc = fragment_source;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
finalFragmentSrc = PrependFragmentPreamble(fragment_source);
|
||||
break;
|
||||
}
|
||||
|
||||
m_vertex_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kVertex, 0, 0, vertex_source, false, false));
|
||||
m_fragment_shader.reset(g_renderer->shader_create(RendererShader::ShaderType::kFragment, 0, 0, finalFragmentSrc, false, false));
|
||||
@ -484,7 +492,10 @@ void main()
|
||||
}
|
||||
void RendererOutputShader::InitializeStatic()
|
||||
{
|
||||
if (g_renderer->GetType() == RendererAPI::Metal)
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
{
|
||||
std::string vertex_source = GetMetalVertexSource(false);
|
||||
std::string vertex_source_ud = GetMetalVertexSource(true);
|
||||
@ -497,21 +508,17 @@ void RendererOutputShader::InitializeStatic()
|
||||
|
||||
s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source_mtl);
|
||||
s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source_mtl);
|
||||
break;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
{
|
||||
std::string vertex_source, vertex_source_ud;
|
||||
// vertex shader
|
||||
if (g_renderer->GetType() == RendererAPI::OpenGL)
|
||||
{
|
||||
vertex_source = GetOpenGlVertexSource(false);
|
||||
vertex_source_ud = GetOpenGlVertexSource(true);
|
||||
}
|
||||
else if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
{
|
||||
vertex_source = GetVulkanVertexSource(false);
|
||||
vertex_source_ud = GetVulkanVertexSource(true);
|
||||
}
|
||||
vertex_source = GetOpenGlVertexSource(false);
|
||||
vertex_source_ud = GetOpenGlVertexSource(true);
|
||||
|
||||
s_copy_shader = new RendererOutputShader(vertex_source, s_copy_shader_source);
|
||||
s_copy_shader_ud = new RendererOutputShader(vertex_source_ud, s_copy_shader_source);
|
||||
|
||||
@ -520,7 +527,29 @@ void RendererOutputShader::InitializeStatic()
|
||||
|
||||
s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source);
|
||||
s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
{
|
||||
std::string vertex_source, vertex_source_ud;
|
||||
// vertex shader
|
||||
vertex_source = GetVulkanVertexSource(false);
|
||||
vertex_source_ud = GetVulkanVertexSource(true);
|
||||
|
||||
s_copy_shader = new RendererOutputShader(vertex_source, s_copy_shader_source);
|
||||
s_copy_shader_ud = new RendererOutputShader(vertex_source_ud, s_copy_shader_source);
|
||||
|
||||
s_bicubic_shader = new RendererOutputShader(vertex_source, s_bicubic_shader_source);
|
||||
s_bicubic_shader_ud = new RendererOutputShader(vertex_source_ud, s_bicubic_shader_source);
|
||||
|
||||
s_hermit_shader = new RendererOutputShader(vertex_source, s_hermite_shader_source);
|
||||
s_hermit_shader_ud = new RendererOutputShader(vertex_source_ud, s_hermite_shader_source);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOutputShader::ShutdownStatic()
|
||||
|
||||
@ -9,58 +9,7 @@
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "util/helpers/Serializer.h"
|
||||
#include "Cafe/HW/Latte/Common/RegisterSerializer.h"
|
||||
|
||||
/* rects emulation */
|
||||
|
||||
void rectsEmulationGS_outputSingleVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 vIdx, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
auto parameterMask = vertexShader->outputParameterMask;
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
{
|
||||
if ((parameterMask & (1 << i)) == 0)
|
||||
continue;
|
||||
sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i);
|
||||
if (vsSemanticId < 0)
|
||||
continue;
|
||||
// make sure PS has matching input
|
||||
if (!psInputTable->hasPSImportForSemanticId(vsSemanticId))
|
||||
continue;
|
||||
gsSrc.append(fmt::format("passParameterSem{}Out = passParameterSem{}In[{}];\r\n", vsSemanticId, vsSemanticId, vIdx));
|
||||
}
|
||||
gsSrc.append(fmt::format("gl_Position = gl_in[{}].gl_Position;\r\n", vIdx));
|
||||
gsSrc.append("EmitVertex();\r\n");
|
||||
}
|
||||
|
||||
void rectsEmulationGS_outputGeneratedVertex(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, const char* variant, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
auto parameterMask = vertexShader->outputParameterMask;
|
||||
for (uint32 i = 0; i < 32; i++)
|
||||
{
|
||||
if ((parameterMask & (1 << i)) == 0)
|
||||
continue;
|
||||
sint32 vsSemanticId = psInputTable->getVertexShaderOutParamSemanticId(latteRegister.GetRawView(), i);
|
||||
if (vsSemanticId < 0)
|
||||
continue;
|
||||
// make sure PS has matching input
|
||||
if (!psInputTable->hasPSImportForSemanticId(vsSemanticId))
|
||||
continue;
|
||||
gsSrc.append(fmt::format("passParameterSem{}Out = gen4thVertex{}(passParameterSem{}In[0], passParameterSem{}In[1], passParameterSem{}In[2]);\r\n", vsSemanticId, variant, vsSemanticId, vsSemanticId, vsSemanticId));
|
||||
}
|
||||
gsSrc.append(fmt::format("gl_Position = gen4thVertex{}(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_in[2].gl_Position);\r\n", variant));
|
||||
gsSrc.append("EmitVertex();\r\n");
|
||||
}
|
||||
|
||||
void rectsEmulationGS_outputVerticesCode(std::string& gsSrc, LatteDecompilerShader* vertexShader, LatteShaderPSInputTable* psInputTable, sint32 p0, sint32 p1, sint32 p2, sint32 p3, const char* variant, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
sint32 pList[4] = { p0, p1, p2, p3 };
|
||||
for (sint32 i = 0; i < 4; i++)
|
||||
{
|
||||
if (pList[i] == 3)
|
||||
rectsEmulationGS_outputGeneratedVertex(gsSrc, vertexShader, psInputTable, variant, latteRegister);
|
||||
else
|
||||
rectsEmulationGS_outputSingleVertex(gsSrc, vertexShader, psInputTable, pList[i], latteRegister);
|
||||
}
|
||||
}
|
||||
#include "HW/Latte/Renderer/RendererCore.h"
|
||||
|
||||
RendererShaderVk* rectsEmulationGS_generate(LatteDecompilerShader* vertexShader, const LatteContextRegister& latteRegister)
|
||||
{
|
||||
|
||||
@ -1221,6 +1221,7 @@ void VulkanRenderer::draw_endRenderPass()
|
||||
m_state.activeRenderpassFBO = nullptr;
|
||||
}
|
||||
|
||||
// Defined in the Common renderer
|
||||
void LatteDraw_handleSpecialState8_clearAsDepth();
|
||||
|
||||
// transfer depth buffer data to color buffer
|
||||
|
||||
@ -421,6 +421,15 @@ namespace coreinit
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 MCP_DemoLaunchGetRemainder(uint32 mcpHandle, uint64 titleId, uint32be* outRemainder)
|
||||
{
|
||||
// stub to always return 99 uses remaining for game demos.
|
||||
// used by drmapp.rpl to determine remaining uses for game demos
|
||||
if (outRemainder)
|
||||
*outRemainder = 99;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InitializeMCP()
|
||||
{
|
||||
osLib_addFunction("coreinit", "MCP_Open", coreinitExport_MCP_Open);
|
||||
@ -450,6 +459,7 @@ namespace coreinit
|
||||
cafeExportRegister("coreinit", MCP_GetEcoSettings, LogType::Placeholder);
|
||||
|
||||
cafeExportRegister("coreinit", MCP_GetTitleId, LogType::Placeholder);
|
||||
cafeExportRegister("coreinit", MCP_DemoLaunchGetRemainder, LogType::Placeholder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2360,6 +2360,7 @@ bool _ppcAssembler_emitDataDirective(PPCAssemblerContext& internalInfo, ASM_DATA
|
||||
// write string bytes + null-termination character
|
||||
internalInfo.ctx->outputData.insert(internalInfo.ctx->outputData.end(), stringData.data(), stringData.data() + stringData.size());
|
||||
internalInfo.ctx->outputData.emplace_back(0);
|
||||
writeIndex = internalInfo.ctx->outputData.size();
|
||||
continue;
|
||||
}
|
||||
// numeric constants
|
||||
@ -2492,6 +2493,16 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
|
||||
internalInfo.listOperandStr.clear();
|
||||
|
||||
bool isInString = false;
|
||||
auto isEscaped = [](const char* operandStartPtr, const char* ptr)
|
||||
{
|
||||
size_t backslashCount = 0;
|
||||
while (ptr > operandStartPtr && ptr[-1] == '\\')
|
||||
{
|
||||
backslashCount++;
|
||||
ptr--;
|
||||
}
|
||||
return (backslashCount & 1) != 0;
|
||||
};
|
||||
|
||||
while (currentPtr < endPtr)
|
||||
{
|
||||
@ -2500,7 +2511,7 @@ bool ppcAssembler_assembleSingleInstruction(char const* text, PPCAssemblerInOut*
|
||||
// find end of operand
|
||||
while (currentPtr < endPtr)
|
||||
{
|
||||
if (*currentPtr == '"')
|
||||
if (*currentPtr == '"' && !isEscaped(startPtr, currentPtr))
|
||||
isInString=!isInString;
|
||||
|
||||
if (*currentPtr == ',' && !isInString)
|
||||
@ -3587,6 +3598,9 @@ void ppcAsmTestDisassembler()
|
||||
_testAsmArray({ 0x7f }, ".byte 0x7f");
|
||||
_testAsmArray({ 0x74, 0x65, 0x73, 0x74, 0x00 }, ".byte \"test\"");
|
||||
_testAsmArray({ 0x41, 0x42, 0x43, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00 }, ".byte \"ABC\", \"test\"");
|
||||
_testAsmArray({ 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x00, 0x00 }, R"(.byte "hello\"world", 0)");
|
||||
_testAsmArray({ 0x61, 0x5c, 0x00, 0x00 }, R"(.byte "a\\", 0)");
|
||||
_testAsmArray({ 0x61, 0x5c, 0x22, 0x62, 0x00, 0x00 }, R"(.byte "a\\\"b", 0)");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#include "Cafe/GameProfile/GameProfile.h"
|
||||
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#endif
|
||||
#include "Cafe/CafeSystem.h"
|
||||
#include "Cemu/Logging/CemuLogging.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
@ -106,12 +108,28 @@ bool ActiveSettings::WaitForGX2DrawDoneEnabled()
|
||||
|
||||
GraphicAPI ActiveSettings::GetGraphicsAPI()
|
||||
{
|
||||
GraphicAPI api = g_current_game_profile->GetGraphicsAPI().value_or(GetConfig().graphic_api);
|
||||
// check if vulkan even available
|
||||
if (api == kVulkan && !g_vulkan_available)
|
||||
api = kOpenGL;
|
||||
|
||||
return api;
|
||||
const GraphicAPI api = g_current_game_profile->GetGraphicsAPI().value_or(GetConfig().graphic_api);
|
||||
std::optional<GraphicAPI> fallbackAPI;
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (g_vulkan_available)
|
||||
{
|
||||
if (api == kVulkan)
|
||||
return api;
|
||||
fallbackAPI = kVulkan;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
if (api == kMetal)
|
||||
return api;
|
||||
fallbackAPI = fallbackAPI.value_or(kMetal);
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (api == kOpenGL)
|
||||
return api;
|
||||
fallbackAPI = fallbackAPI.value_or(kOpenGL);
|
||||
#endif
|
||||
cemu_assert(fallbackAPI.has_value());
|
||||
return *fallbackAPI;
|
||||
}
|
||||
|
||||
float ActiveSettings::GetTVGamma()
|
||||
|
||||
@ -123,7 +123,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
|
||||
|
||||
// graphics
|
||||
auto graphic = parser.get("Graphic");
|
||||
graphic_api = graphic.get("api", kOpenGL);
|
||||
graphic_api = graphic.get("api", kDefaultGraphicsAPI);
|
||||
graphic.get("device", legacy_graphic_device_uuid);
|
||||
if (graphic.get("vkDevice").valid())
|
||||
graphic.get("vkDevice", vk_graphic_device_uuid);
|
||||
@ -144,7 +144,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
|
||||
fullscreen_scaling = graphic.get("FullscreenScaling", kKeepAspectRatio);
|
||||
async_compile = graphic.get("AsyncCompile", async_compile);
|
||||
vk_accurate_barriers = graphic.get("vkAccurateBarriers", true); // this used to be "VulkanAccurateBarriers" but because we changed the default to true in 1.27.1 the option name had to be changed
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
force_mesh_shaders = graphic.get("ForceMeshShaders", false);
|
||||
#endif
|
||||
|
||||
@ -273,7 +273,7 @@ XMLConfigParser CemuConfig::Load(XMLConfigParser& parser)
|
||||
crash_dump = debug.get("CrashDumpUnix", crash_dump);
|
||||
#endif
|
||||
gdb_port = debug.get("GDBPort", 1337);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
gpu_capture_dir = debug.get("GPUCaptureDir", "");
|
||||
framebuffer_fetch = debug.get("FramebufferFetch", true);
|
||||
#endif
|
||||
@ -368,7 +368,7 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
|
||||
graphic.set("OverrideGammaValue", overrideGammaValue);
|
||||
graphic.set("UserDisplayGamma", userDisplayGamma);
|
||||
graphic.set("GX2DrawdoneSync", gx2drawdone_sync);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
graphic.set("ForceMeshShaders", force_mesh_shaders);
|
||||
#endif
|
||||
//graphic.set("PrecompiledShaders", precompiled_shaders.GetValue());
|
||||
@ -437,7 +437,7 @@ XMLConfigParser CemuConfig::Save(XMLConfigParser& parser)
|
||||
debug.set("CrashDumpUnix", crash_dump.GetValue());
|
||||
#endif
|
||||
debug.set("GDBPort", gdb_port);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
debug.set("GPUCaptureDir", gpu_capture_dir);
|
||||
debug.set("FramebufferFetch", framebuffer_fetch);
|
||||
#endif
|
||||
|
||||
@ -70,8 +70,17 @@ enum GraphicAPI
|
||||
kOpenGL = 0,
|
||||
kVulkan,
|
||||
kMetal,
|
||||
COUNT
|
||||
};
|
||||
|
||||
#if defined(ENABLE_VULKAN)
|
||||
constexpr GraphicAPI kDefaultGraphicsAPI = kVulkan;
|
||||
#elif defined(ENABLE_METAL)
|
||||
constexpr GraphicAPI kDefaultGraphicsAPI = kMetal;
|
||||
#elif defined(ENABLE_OPENGL)
|
||||
constexpr GraphicAPI kDefaultGraphicsAPI = kOpenGL;
|
||||
#endif
|
||||
|
||||
enum AudioChannels
|
||||
{
|
||||
kMono = 0,
|
||||
@ -429,7 +438,7 @@ struct CemuConfig
|
||||
ConfigValueBounds<CafeConsoleLanguage> console_language{ CafeConsoleLanguage::EN };
|
||||
|
||||
// graphics
|
||||
ConfigValue<GraphicAPI> graphic_api{ kVulkan };
|
||||
ConfigValue<GraphicAPI> graphic_api{ kDefaultGraphicsAPI };
|
||||
std::array<uint8, 16> legacy_graphic_device_uuid{}; // placeholder option for backwards compatibility with settings from 2.6 and before (renamed to "vkDevice")
|
||||
std::array<uint8, 16> vk_graphic_device_uuid;
|
||||
uint64 mtl_graphic_device_uuid{ 0 };
|
||||
@ -437,7 +446,7 @@ struct CemuConfig
|
||||
ConfigValue<bool> gx2drawdone_sync { true };
|
||||
ConfigValue<bool> render_upside_down{ false };
|
||||
ConfigValue<bool> async_compile{ true };
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
ConfigValue<bool> force_mesh_shaders{ false };
|
||||
#endif
|
||||
|
||||
@ -503,7 +512,7 @@ struct CemuConfig
|
||||
// debug
|
||||
ConfigValueBounds<CrashDump> crash_dump{ CrashDump::Disabled };
|
||||
ConfigValue<uint16> gdb_port{ 1337 };
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
ConfigValue<std::string> gpu_capture_dir{ "" };
|
||||
ConfigValue<bool> framebuffer_fetch{ true };
|
||||
#endif
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
add_library(CemuWxGui STATIC
|
||||
canvas/IRenderCanvas.h
|
||||
canvas/OpenGLCanvas.cpp
|
||||
canvas/OpenGLCanvas.h
|
||||
canvas/VulkanCanvas.cpp
|
||||
canvas/VulkanCanvas.h
|
||||
CemuApp.cpp
|
||||
CemuApp.h
|
||||
CemuUpdateWindow.cpp
|
||||
@ -118,6 +114,20 @@ add_library(CemuWxGui STATIC
|
||||
wxHelper.h
|
||||
)
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_sources(CemuWxGui PRIVATE
|
||||
canvas/OpenGLCanvas.cpp
|
||||
canvas/OpenGLCanvas.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(CemuWxGui PRIVATE
|
||||
canvas/VulkanCanvas.cpp
|
||||
canvas/VulkanCanvas.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_METAL)
|
||||
target_sources(CemuWxGui PRIVATE
|
||||
canvas/MetalCanvas.cpp
|
||||
@ -147,7 +157,6 @@ target_link_libraries(CemuWxGui PRIVATE
|
||||
libzip::zip
|
||||
ZArchive::zarchive
|
||||
CemuComponents
|
||||
SDL2::SDL2
|
||||
pugixml::pugixml
|
||||
CemuCafe
|
||||
PUBLIC
|
||||
@ -166,6 +175,10 @@ if(ENABLE_CUBEB)
|
||||
target_link_libraries(CemuWxGui PRIVATE cubeb::cubeb)
|
||||
endif()
|
||||
|
||||
if(ENABLE_SDL)
|
||||
target_link_libraries(CemuWxGui PRIVATE SDL3::SDL3)
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
if(ENABLE_FERAL_GAMEMODE)
|
||||
target_link_libraries(CemuWxGui PRIVATE gamemode)
|
||||
|
||||
@ -3,12 +3,15 @@
|
||||
#include "wxgui/MainWindow.h"
|
||||
#include "wxgui/wxgui.h"
|
||||
#include "config/CemuConfig.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#endif
|
||||
#include "Cafe/HW/Latte/Core/LatteOverlay.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "config/LaunchSettings.h"
|
||||
#include "wxgui/GettingStartedDialog.h"
|
||||
#include "input/InputManager.h"
|
||||
#include "input/api/SDL/SDLControllerProvider.h"
|
||||
#include "wxgui/helpers/wxHelpers.h"
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "wxgui/input/HotkeySettings.h"
|
||||
@ -26,6 +29,7 @@
|
||||
#include <wx/filename.h>
|
||||
#include <wx/stdpaths.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/timer.h>
|
||||
#include "wxHelper.h"
|
||||
|
||||
#include "Cafe/TitleList/TitleList.h"
|
||||
@ -332,8 +336,18 @@ bool CemuApp::OnInit()
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
UnitTests();
|
||||
#endif
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
SDLControllerProvider::InitSDL();
|
||||
#endif
|
||||
CemuCommonInit();
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
m_sdlEventPumpTimer = new wxTimer(this);
|
||||
Bind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
|
||||
m_sdlEventPumpTimer->Start(5, wxTIMER_CONTINUOUS);
|
||||
#endif
|
||||
|
||||
wxInitAllImageHandlers();
|
||||
|
||||
// fill colour db
|
||||
@ -349,7 +363,9 @@ bool CemuApp::OnInit()
|
||||
__fastfail(0);
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
InitializeGlobalVulkan();
|
||||
#endif
|
||||
|
||||
Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this);
|
||||
|
||||
@ -389,8 +405,22 @@ bool CemuApp::OnInit()
|
||||
|
||||
int CemuApp::OnExit()
|
||||
{
|
||||
#if BOOST_OS_MACOS
|
||||
if (m_sdlEventPumpTimer)
|
||||
{
|
||||
m_sdlEventPumpTimer->Stop();
|
||||
Unbind(wxEVT_TIMER, &CemuApp::OnSDLEventPumpTimer, this);
|
||||
delete m_sdlEventPumpTimer;
|
||||
m_sdlEventPumpTimer = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
wxApp::OnExit();
|
||||
wxTheClipboard->Flush();
|
||||
InputManager::instance().Shutdown();
|
||||
#if BOOST_OS_MACOS
|
||||
SDLControllerProvider::ShutdownSDL();
|
||||
#endif
|
||||
#if BOOST_OS_WINDOWS
|
||||
ExitProcess(0);
|
||||
#else
|
||||
@ -398,6 +428,15 @@ int CemuApp::OnExit()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
void CemuApp::OnSDLEventPumpTimer(wxTimerEvent& event)
|
||||
{
|
||||
// this callback is only used on macOS where SDL event functions need to be called on the main thread
|
||||
// on other platforms SDLControllerProvider creates a separate thread for SDL event polling
|
||||
SDLControllerProvider::PumpSDLEvents();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
void DumpThreadStackTrace();
|
||||
#endif
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
#include <wx/app.h>
|
||||
|
||||
class MainWindow;
|
||||
class wxTimer;
|
||||
class wxTimerEvent;
|
||||
|
||||
class CemuApp : public wxApp
|
||||
{
|
||||
@ -30,6 +32,10 @@ private:
|
||||
static std::vector<const wxLanguageInfo*> GetAvailableTranslationLanguages(wxTranslations* translationsMgr);
|
||||
|
||||
MainWindow* m_mainFrame = nullptr;
|
||||
#if BOOST_OS_MACOS
|
||||
void OnSDLEventPumpTimer(wxTimerEvent& event);
|
||||
wxTimer* m_sdlEventPumpTimer = nullptr;
|
||||
#endif
|
||||
|
||||
wxLocale m_locale;
|
||||
std::vector<const wxLanguageInfo*> m_availableTranslations;
|
||||
|
||||
@ -113,7 +113,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
|
||||
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
wxString gapi_values[] = { "", "OpenGL", "Vulkan",
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
"Metal"
|
||||
#endif
|
||||
};
|
||||
@ -127,7 +127,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
|
||||
m_shader_mul_accuracy->SetToolTip(_("EXPERT OPTION\nControls the accuracy of floating point multiplication in shaders.\n\nRecommended: true"));
|
||||
first_row->Add(m_shader_mul_accuracy, 0, wxALL, 5);
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader fast math")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
wxString math_values[] = { _("false"), _("true") };
|
||||
@ -296,7 +296,7 @@ void GameProfileWindow::ApplyProfile()
|
||||
else
|
||||
m_graphic_api->SetSelection(1 + m_game_profile.m_graphics_api.value()); // "", OpenGL, Vulkan, Metal
|
||||
m_shader_mul_accuracy->SetSelection((int)m_game_profile.m_accurateShaderMul);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_shader_fast_math->SetSelection((int)m_game_profile.m_shaderFastMath);
|
||||
m_metal_buffer_cache_mode->SetSelection((int)m_game_profile.m_metalBufferCacheMode);
|
||||
m_position_invariance->SetSelection((int)m_game_profile.m_positionInvariance);
|
||||
@ -362,7 +362,7 @@ void GameProfileWindow::SaveProfile()
|
||||
m_game_profile.m_accurateShaderMul = (AccurateShaderMulOption)m_shader_mul_accuracy->GetSelection();
|
||||
if (m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::False && m_game_profile.m_accurateShaderMul != AccurateShaderMulOption::True)
|
||||
m_game_profile.m_accurateShaderMul = AccurateShaderMulOption::True; // force a legal value
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_game_profile.m_shaderFastMath = (bool)m_shader_fast_math->GetSelection();
|
||||
m_game_profile.m_metalBufferCacheMode = (MetalBufferCacheMode)m_metal_buffer_cache_mode->GetSelection();
|
||||
m_game_profile.m_positionInvariance = (PositionInvariance)m_position_invariance->GetSelection();
|
||||
|
||||
@ -40,7 +40,7 @@ private:
|
||||
wxChoice* m_graphic_api;
|
||||
|
||||
wxChoice* m_shader_mul_accuracy;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
wxChoice* m_shader_fast_math;
|
||||
wxChoice* m_metal_buffer_cache_mode;
|
||||
wxChoice* m_position_invariance;
|
||||
|
||||
@ -27,9 +27,11 @@
|
||||
|
||||
#include "audio/IAudioInputAPI.h"
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanAPI.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h"
|
||||
#if ENABLE_METAL
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||
#endif
|
||||
#include "Cafe/Account/Account.h"
|
||||
@ -87,6 +89,7 @@ private:
|
||||
IAudioInputAPI::DeviceDescriptionPtr m_description;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
class wxVulkanUUID : public wxClientData
|
||||
{
|
||||
public:
|
||||
@ -97,8 +100,9 @@ public:
|
||||
private:
|
||||
VulkanRenderer::DeviceInfo m_device_info;
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
class wxMetalUUID : public wxClientData
|
||||
{
|
||||
public:
|
||||
@ -352,15 +356,25 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
|
||||
|
||||
row->Add(new wxStaticText(box, wxID_ANY, _("Graphics API")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
||||
|
||||
sint32 api_size = 1;
|
||||
wxString choices[3] = { "OpenGL" };
|
||||
sint32 api_size = 0;
|
||||
wxString choices[size_t(GraphicAPI::COUNT)];
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
choices[api_size++] = "OpenGL";
|
||||
m_api_map.push_back(GraphicAPI::kOpenGL);
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (g_vulkan_available)
|
||||
{
|
||||
choices[api_size++] = "Vulkan";
|
||||
m_api_map.push_back(GraphicAPI::kVulkan);
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
choices[api_size++] = "Metal";
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
choices[api_size++] = "Metal";
|
||||
m_api_map.push_back(GraphicAPI::kMetal);
|
||||
#endif
|
||||
wxASSERT(api_size > 0);
|
||||
|
||||
m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices);
|
||||
m_graphic_api->SetSelection(0);
|
||||
@ -400,7 +414,7 @@ wxPanel* GeneralSettings2::AddGraphicsPage(wxNotebook* notebook)
|
||||
m_gx2drawdone_sync->SetToolTip(_("If synchronization is requested by the game, the emulated CPU will wait for the GPU to finish all operations.\nThis is more accurate behavior, but may cause lower performance"));
|
||||
graphic_misc_row->Add(m_gx2drawdone_sync, 0, wxALL, 5);
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_force_mesh_shaders = new wxCheckBox(box, wxID_ANY, _("Force mesh shaders"));
|
||||
m_force_mesh_shaders->SetToolTip(_("Force mesh shaders on all GPUs that support them. Mesh shaders are disabled by default on Intel GPUs due to potential stability issues.\nMetal only"));
|
||||
graphic_misc_row->Add(m_force_mesh_shaders, 0, wxALL, 5);
|
||||
@ -1028,7 +1042,7 @@ wxPanel* GeneralSettings2::AddDebugPage(wxNotebook* notebook)
|
||||
debug_panel_sizer->Add(debug_row, 0, wxALL | wxEXPAND, 5);
|
||||
}
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
{
|
||||
auto* debug_row = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
debug_row->SetFlexibleDirection(wxBOTH);
|
||||
@ -1216,9 +1230,10 @@ void GeneralSettings2::StoreConfig()
|
||||
}
|
||||
|
||||
// graphics
|
||||
config.graphic_api = (GraphicAPI)m_graphic_api->GetSelection();
|
||||
config.graphic_api = m_api_map[m_graphic_api->GetSelection()];
|
||||
|
||||
selection = m_graphic_device->GetSelection();
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (config.graphic_api == GraphicAPI::kVulkan)
|
||||
{
|
||||
if (selection != wxNOT_FOUND)
|
||||
@ -1232,25 +1247,26 @@ void GeneralSettings2::StoreConfig()
|
||||
else
|
||||
config.vk_graphic_device_uuid = {};
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
else if (config.graphic_api == GraphicAPI::kMetal)
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
if (config.graphic_api == GraphicAPI::kMetal)
|
||||
{
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection);
|
||||
if (info)
|
||||
config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid;
|
||||
else
|
||||
config.mtl_graphic_device_uuid = {};
|
||||
}
|
||||
else
|
||||
config.mtl_graphic_device_uuid = {};
|
||||
if (selection != wxNOT_FOUND)
|
||||
{
|
||||
const auto* info = (wxMetalUUID*)m_graphic_device->GetClientObject(selection);
|
||||
if (info)
|
||||
config.mtl_graphic_device_uuid = info->GetDeviceInfo().uuid;
|
||||
else
|
||||
config.mtl_graphic_device_uuid = {};
|
||||
}
|
||||
else
|
||||
config.mtl_graphic_device_uuid = {};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
config.force_mesh_shaders = m_force_mesh_shaders->IsChecked();
|
||||
#endif
|
||||
config.async_compile = m_async_compile->IsChecked();
|
||||
@ -1281,7 +1297,7 @@ void GeneralSettings2::StoreConfig()
|
||||
// debug
|
||||
config.crash_dump = (CrashDump)m_crash_dump->GetSelection();
|
||||
config.gdb_port = m_gdb_port->GetValue();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
config.gpu_capture_dir = m_gpu_capture_dir->GetValue().utf8_string();
|
||||
config.framebuffer_fetch = m_framebuffer_fetch->IsChecked();
|
||||
#endif
|
||||
@ -1721,7 +1737,12 @@ void GeneralSettings2::HandleGraphicsApiSelection()
|
||||
selection = GetConfig().vsync;
|
||||
|
||||
m_vsync->Clear();
|
||||
if (m_graphic_api->GetSelection() == 0)
|
||||
|
||||
auto api = m_api_map[m_graphic_api->GetSelection()];
|
||||
switch (api)
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case GraphicAPI::kOpenGL:
|
||||
{
|
||||
// OpenGL
|
||||
m_vsync->AppendString(_("Off"));
|
||||
@ -1736,16 +1757,19 @@ void GeneralSettings2::HandleGraphicsApiSelection()
|
||||
|
||||
m_gx2drawdone_sync->Enable();
|
||||
m_async_compile->Disable();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_force_mesh_shaders->Disable();
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
else if (m_graphic_api->GetSelection() == 1)
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case GraphicAPI::kVulkan:
|
||||
{
|
||||
// Vulkan
|
||||
m_gx2drawdone_sync->Disable();
|
||||
m_async_compile->Enable();
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_force_mesh_shaders->Disable();
|
||||
#endif
|
||||
|
||||
@ -1779,9 +1803,11 @@ void GeneralSettings2::HandleGraphicsApiSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case GraphicAPI::kMetal:
|
||||
{
|
||||
// Metal
|
||||
m_gx2drawdone_sync->Disable();
|
||||
@ -1815,8 +1841,10 @@ void GeneralSettings2::HandleGraphicsApiSelection()
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GeneralSettings2::ApplyConfig()
|
||||
@ -1870,7 +1898,14 @@ void GeneralSettings2::ApplyConfig()
|
||||
}
|
||||
|
||||
// graphics
|
||||
m_graphic_api->SetSelection(config.graphic_api);
|
||||
for (int i = 0; i < (int)m_api_map.size(); ++i)
|
||||
{
|
||||
if (m_api_map[i] == config.graphic_api)
|
||||
{
|
||||
m_graphic_api->SetSelection(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_vsync->SetSelection(config.vsync);
|
||||
m_overrideGamma->SetValue(config.overrideAppGammaPreference);
|
||||
m_overrideGammaValue->SetValue(config.overrideGammaValue);
|
||||
@ -1883,7 +1918,7 @@ void GeneralSettings2::ApplyConfig()
|
||||
}
|
||||
m_async_compile->SetValue(config.async_compile);
|
||||
m_gx2drawdone_sync->SetValue(config.gx2drawdone_sync);
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_force_mesh_shaders->SetValue(config.force_mesh_shaders);
|
||||
#endif
|
||||
m_upscale_filter->SetSelection(config.upscale_filter);
|
||||
@ -2022,7 +2057,7 @@ void GeneralSettings2::ApplyConfig()
|
||||
// debug
|
||||
m_crash_dump->SetSelection((int)config.crash_dump.GetValue());
|
||||
m_gdb_port->SetValue(config.gdb_port.GetValue());
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
m_gpu_capture_dir->SetValue(wxString::FromUTF8(config.gpu_capture_dir.GetValue()));
|
||||
m_framebuffer_fetch->SetValue(config.framebuffer_fetch);
|
||||
#endif
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "config/CemuConfig.h"
|
||||
#include <wx/collpane.h>
|
||||
#include <wx/propgrid/propgrid.h>
|
||||
#include <Cafe/Account/Account.h>
|
||||
@ -36,6 +37,7 @@ private:
|
||||
bool m_game_launched;
|
||||
|
||||
bool m_has_account_change = false; // keep track of dirty state of accounts
|
||||
std::vector<GraphicAPI> m_api_map; // map from dropdown index to GraphicsAPISetting, used in HandleGraphicsApiSelection
|
||||
|
||||
|
||||
wxPanel* AddGeneralPage(wxNotebook* notebook);
|
||||
@ -71,7 +73,7 @@ private:
|
||||
wxCheckBox* m_userDisplayisSRGB;
|
||||
|
||||
wxCheckBox *m_async_compile, *m_gx2drawdone_sync;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
wxCheckBox *m_force_mesh_shaders;
|
||||
#endif
|
||||
wxRadioBox* m_upscale_filter, *m_downscale_filter, *m_fullscreen_scaling;
|
||||
@ -99,7 +101,7 @@ private:
|
||||
// Debug
|
||||
wxChoice* m_crash_dump;
|
||||
wxSpinCtrl* m_gdb_port;
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
wxTextCtrl* m_gpu_capture_dir;
|
||||
wxCheckBox* m_framebuffer_fetch;
|
||||
#endif
|
||||
|
||||
@ -3,18 +3,21 @@
|
||||
#include "wxCemuConfig.h"
|
||||
#include "wxgui/wxgui.h"
|
||||
#include "wxgui/MainWindow.h"
|
||||
|
||||
#include <wx/mstream.h>
|
||||
|
||||
#include "wxgui/GameUpdateWindow.h"
|
||||
#include "wxgui/PadViewFrame.h"
|
||||
#include "wxgui/windows/TextureRelationViewer/TextureRelationWindow.h"
|
||||
#include "wxgui/windows/PPCThreadsViewer/DebugPPCThreadsWindow.h"
|
||||
#include "AudioDebuggerWindow.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "wxgui/canvas/OpenGLCanvas.h"
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "wxgui/canvas/VulkanCanvas.h"
|
||||
#if ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
#include "wxgui/canvas/MetalCanvas.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||
#endif
|
||||
#include "Cafe/OS/libs/nfc/nfc.h"
|
||||
#include "Cafe/OS/libs/swkbd/swkbd.h"
|
||||
@ -42,7 +45,6 @@
|
||||
#include "wxgui/DownloadGraphicPacksWindow.h"
|
||||
#include "wxgui/GettingStartedDialog.h"
|
||||
#include "wxgui/helpers/wxHelpers.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
||||
#include "wxgui/input/InputSettings2.h"
|
||||
#include "wxgui/input/HotkeySettings.h"
|
||||
#include "input/InputManager.h"
|
||||
@ -66,10 +68,6 @@
|
||||
#include "gamemode_client.h"
|
||||
#endif
|
||||
|
||||
#if ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||
#endif
|
||||
|
||||
#include "Cafe/TitleList/TitleInfo.h"
|
||||
#include "Cafe/TitleList/TitleList.h"
|
||||
#include "wxHelper.h"
|
||||
@ -1043,7 +1041,7 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event)
|
||||
if(!GetConfig().vk_accurate_barriers)
|
||||
wxMessageBox(_("Warning: Disabling the accurate barriers option will lead to flickering graphics but may improve performance. It is highly recommended to leave it turned on."), _("Accurate barriers are off"), wxOK);
|
||||
}
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
else if (event.GetId() == MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE)
|
||||
{
|
||||
cemu_assert_debug(g_renderer->GetType() == RendererAPI::Metal);
|
||||
@ -1607,14 +1605,21 @@ void MainWindow::CreateCanvas()
|
||||
this->GetSizer()->Add(m_game_panel, 1, wxEXPAND);
|
||||
|
||||
// create canvas
|
||||
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
||||
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
|
||||
m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true);
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
||||
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
if (ActiveSettings::GetGraphicsAPI() == kMetal)
|
||||
m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
#endif
|
||||
if (!m_render_canvas)
|
||||
cemu_assert(false && "Failed to create canvas or invalid graphics API selected");
|
||||
cemu_assert(m_render_canvas != nullptr);
|
||||
|
||||
// mouse events
|
||||
m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this);
|
||||
@ -1676,7 +1681,9 @@ void MainWindow::OnSizeEvent(wxSizeEvent& event)
|
||||
|
||||
event.Skip();
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
VsyncDriver_notifyWindowPosChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::OnDPIChangedEvent(wxDPIChangedEvent& event)
|
||||
@ -1697,7 +1704,9 @@ void MainWindow::OnMove(wxMoveEvent& event)
|
||||
|
||||
if (m_debugger_window && m_debugger_window->IsShown())
|
||||
m_debugger_window->OnParentMove(GetPosition(), GetSize());
|
||||
#ifdef ENABLE_VULKAN
|
||||
VsyncDriver_notifyWindowPosChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::OnDebuggerClose(wxCloseEvent& event)
|
||||
@ -2351,7 +2360,7 @@ void MainWindow::RecreateMenu()
|
||||
auto accurateBarriers = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_VK_ACCURATE_BARRIERS, _("&Accurate barriers (Vulkan)"));
|
||||
accurateBarriers->Check(GetConfig().vk_accurate_barriers);
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
auto gpuCapture = debugMenu->Append(MAINFRAME_MENU_ID_DEBUG_GPU_CAPTURE, _("&GPU capture (Metal)"));
|
||||
gpuCapture->Enable(m_game_launched && g_renderer->GetType() == RendererAPI::Metal);
|
||||
#endif
|
||||
|
||||
@ -6,9 +6,13 @@
|
||||
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Cafe/OS/libs/swkbd/swkbd.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "wxgui/canvas/OpenGLCanvas.h"
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "wxgui/canvas/VulkanCanvas.h"
|
||||
#if ENABLE_METAL
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
#include "wxgui/canvas/MetalCanvas.h"
|
||||
#endif
|
||||
#include "config/CemuConfig.h"
|
||||
@ -75,16 +79,21 @@ void PadViewFrame::InitializeRenderCanvas()
|
||||
{
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
||||
m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false);
|
||||
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
|
||||
#endif
|
||||
#ifdef ENABLE_OPENGL
|
||||
if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
|
||||
m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false);
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
if (ActiveSettings::GetGraphicsAPI() == kMetal)
|
||||
m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false);
|
||||
#endif
|
||||
sizer->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr);
|
||||
}
|
||||
cemu_assert(m_render_canvas != nullptr);
|
||||
SetSizer(sizer);
|
||||
Layout();
|
||||
|
||||
|
||||
@ -101,12 +101,10 @@ void WindowSystem::UpdateWindowTitles(bool isIdle, bool isLoading, double fps)
|
||||
case RendererAPI::Vulkan:
|
||||
renderer = "[Vulkan]";
|
||||
break;
|
||||
#if ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
renderer = "[Metal]";
|
||||
break;
|
||||
#endif
|
||||
default:;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
add_library(imguiImpl
|
||||
imgui_impl_opengl3.cpp
|
||||
imgui_impl_opengl3.h
|
||||
imgui_impl_vulkan.cpp
|
||||
imgui_impl_vulkan.h
|
||||
imgui_extension.cpp
|
||||
imgui_extension.h
|
||||
)
|
||||
|
||||
if (ENABLE_OPENGL)
|
||||
target_sources(imguiImpl PRIVATE
|
||||
imgui_impl_opengl3.cpp
|
||||
imgui_impl_opengl3.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
target_sources(imguiImpl PRIVATE
|
||||
imgui_impl_vulkan.cpp
|
||||
imgui_impl_vulkan.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (ENABLE_METAL)
|
||||
target_sources(imguiImpl PRIVATE
|
||||
imgui_impl_metal.mm
|
||||
|
||||
@ -2,9 +2,13 @@
|
||||
#include "WindowSystem.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "resource/IconsFontAwesome5.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "resource/resource.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "imgui_impl_vulkan.h"
|
||||
#endif
|
||||
#include "input/InputManager.h"
|
||||
|
||||
// <imgui_internal.h>
|
||||
|
||||
@ -15,10 +15,6 @@ add_library(CemuInput
|
||||
api/DSU/DSUControllerProvider.h
|
||||
api/DSU/DSUMessages.h
|
||||
api/DSU/DSUMessages.cpp
|
||||
api/SDL/SDLController.cpp
|
||||
api/SDL/SDLControllerProvider.cpp
|
||||
api/SDL/SDLController.h
|
||||
api/SDL/SDLControllerProvider.h
|
||||
api/Keyboard/KeyboardControllerProvider.h
|
||||
api/Keyboard/KeyboardControllerProvider.cpp
|
||||
api/Keyboard/KeyboardController.cpp
|
||||
@ -92,6 +88,16 @@ if (SUPPORTS_WIIMOTE)
|
||||
endif ()
|
||||
|
||||
|
||||
if(ENABLE_SDL)
|
||||
target_sources(CemuInput PRIVATE
|
||||
api/SDL/SDLController.cpp
|
||||
api/SDL/SDLControllerProvider.cpp
|
||||
api/SDL/SDLController.h
|
||||
api/SDL/SDLControllerProvider.h
|
||||
)
|
||||
target_link_libraries(CemuInput PRIVATE SDL3::SDL3)
|
||||
endif()
|
||||
|
||||
target_include_directories(CemuInput PUBLIC "../")
|
||||
|
||||
target_link_libraries(CemuInput PRIVATE
|
||||
@ -99,10 +105,6 @@ target_link_libraries(CemuInput PRIVATE
|
||||
CemuGui
|
||||
)
|
||||
|
||||
if (ENABLE_HIDAPI)
|
||||
target_link_libraries(CemuInput PRIVATE hidapi::hidapi)
|
||||
endif()
|
||||
|
||||
if (ENABLE_BLUEZ)
|
||||
target_link_libraries(CemuInput PRIVATE bluez::bluez)
|
||||
endif ()
|
||||
|
||||
@ -5,7 +5,6 @@
|
||||
#include "input/emulated/ClassicController.h"
|
||||
#include "input/emulated/WiimoteController.h"
|
||||
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#include "input/api/Keyboard/KeyboardController.h"
|
||||
#include "input/api/DSU/DSUController.h"
|
||||
#include "input/api/GameCube/GameCubeController.h"
|
||||
@ -19,6 +18,10 @@
|
||||
#include "input/api/Wiimote/NativeWiimoteController.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SDL
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#endif
|
||||
|
||||
ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::string_view uuid,
|
||||
std::string_view display_name)
|
||||
{
|
||||
@ -59,7 +62,7 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin
|
||||
return std::make_shared<XInputController>(index);
|
||||
}
|
||||
#endif
|
||||
#if HAS_SDL
|
||||
#ifdef HAS_SDL
|
||||
case InputAPI::SDLController:
|
||||
{
|
||||
// diid_guid
|
||||
@ -68,7 +71,7 @@ ControllerPtr ControllerFactory::CreateController(InputAPI::Type api, std::strin
|
||||
throw std::invalid_argument(fmt::format("invalid sdl uuid format: {}", uuid));
|
||||
|
||||
const auto guid_index = ConvertString<size_t>(uuid.substr(0, index));
|
||||
const auto guid = SDL_JoystickGetGUIDFromString(std::string{uuid.substr(index + 1)}.c_str());
|
||||
const auto guid = SDL_StringToGUID(std::string{uuid.substr(index + 1)}.c_str());
|
||||
|
||||
if (display_name.empty())
|
||||
return std::make_shared<SDLController>(guid, guid_index);
|
||||
@ -137,7 +140,7 @@ ControllerProviderPtr ControllerFactory::CreateControllerProvider(InputAPI::Type
|
||||
case InputAPI::Keyboard:
|
||||
return std::make_shared<KeyboardControllerProvider>();
|
||||
#endif
|
||||
#if HAS_SDL
|
||||
#ifdef HAS_SDL
|
||||
case InputAPI::SDLController:
|
||||
return std::make_shared<SDLControllerProvider>();
|
||||
#endif
|
||||
|
||||
@ -28,7 +28,7 @@ InputManager::InputManager()
|
||||
#if HAS_KEYBOARD
|
||||
create_provider<KeyboardControllerProvider>();
|
||||
#endif
|
||||
#if HAS_SDL
|
||||
#ifdef HAS_SDL
|
||||
create_provider<SDLControllerProvider>();
|
||||
#endif
|
||||
#if HAS_XINPUT
|
||||
@ -53,8 +53,7 @@ InputManager::InputManager()
|
||||
|
||||
InputManager::~InputManager()
|
||||
{
|
||||
m_update_thread_shutdown.store(true);
|
||||
m_update_thread.join();
|
||||
// destructors will not invoked forever, so we manually release resources in Shutdown().
|
||||
}
|
||||
|
||||
void InputManager::load() noexcept
|
||||
@ -952,3 +951,21 @@ void InputManager::update_thread()
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void InputManager::Shutdown()
|
||||
{
|
||||
m_update_thread_shutdown = true;
|
||||
|
||||
if (m_update_thread.joinable())
|
||||
{
|
||||
m_update_thread.join();
|
||||
}
|
||||
|
||||
for (auto& pad : m_vpad) pad.reset();
|
||||
for (auto& pad : m_wpad) pad.reset();
|
||||
|
||||
for (auto& providers : m_api_available)
|
||||
{
|
||||
providers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,9 +9,10 @@
|
||||
#include "input/api/Wiimote/WiimoteControllerProvider.h"
|
||||
#endif
|
||||
|
||||
#include "util/helpers/Singleton.h"
|
||||
|
||||
#ifdef HAS_SDL
|
||||
#include "input/api/SDL/SDLControllerProvider.h"
|
||||
#endif
|
||||
|
||||
#include "input/api/Keyboard/KeyboardControllerProvider.h"
|
||||
#include "input/api/DSU/DSUControllerProvider.h"
|
||||
#include "input/api/GameCube/GameCubeControllerProvider.h"
|
||||
@ -19,8 +20,7 @@
|
||||
#include "input/emulated/VPADController.h"
|
||||
#include "input/emulated/WPADController.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include "util/helpers/Singleton.h"
|
||||
|
||||
class InputManager : public Singleton<InputManager>
|
||||
{
|
||||
@ -46,6 +46,8 @@ public:
|
||||
|
||||
bool is_gameprofile_set(size_t player_index) const;
|
||||
|
||||
void Shutdown();
|
||||
|
||||
EmulatedControllerPtr set_controller(EmulatedControllerPtr controller);
|
||||
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type);
|
||||
EmulatedControllerPtr set_controller(size_t player_index, EmulatedController::Type type, const std::shared_ptr<ControllerBase>& controller);
|
||||
@ -106,16 +108,14 @@ private:
|
||||
|
||||
std::array<bool, kMaxController> m_is_gameprofile_set{};
|
||||
|
||||
template <typename TProvider>
|
||||
void create_provider() // lambda templates only work in c++20 -> define locally in ctor
|
||||
template<std::derived_from<ControllerProviderBase> TProvider>
|
||||
void create_provider()
|
||||
{
|
||||
static_assert(std::is_base_of_v<ControllerProviderBase, TProvider>);
|
||||
try
|
||||
{
|
||||
auto controller = std::make_shared<TProvider>();
|
||||
m_api_available[controller->api()] = std::vector<ControllerProviderPtr>{ controller };
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
m_api_available[controller->api()] = std::vector<ControllerProviderPtr>{controller};
|
||||
} catch (const std::exception& ex)
|
||||
{
|
||||
cemuLog_log(LogType::Force, ex.what());
|
||||
}
|
||||
|
||||
@ -7,6 +7,19 @@ ControllerBase::ControllerBase(std::string_view uuid, std::string_view display_n
|
||||
{
|
||||
}
|
||||
|
||||
inline void apply_axis_button(ControllerButtonState& buttons, const glm::vec2& axis, int flag)
|
||||
{
|
||||
if (axis.x < -ControllerState::kAxisThreshold)
|
||||
buttons.SetButtonState(flag + (kAxisXN - kAxisXP), true);
|
||||
else if (axis.x > ControllerState::kAxisThreshold)
|
||||
buttons.SetButtonState(flag, true);
|
||||
|
||||
if (axis.y < -ControllerState::kAxisThreshold)
|
||||
buttons.SetButtonState(flag + 1 + (kAxisXN - kAxisXP), true);
|
||||
else if (axis.y > ControllerState::kAxisThreshold)
|
||||
buttons.SetButtonState(flag + 1, true);
|
||||
}
|
||||
|
||||
const ControllerState& ControllerBase::update_state()
|
||||
{
|
||||
if (!m_is_calibrated)
|
||||
@ -21,26 +34,9 @@ const ControllerState& ControllerBase::update_state()
|
||||
apply_axis_setting(result.rotation, m_default_state.rotation, m_settings.rotation);
|
||||
apply_axis_setting(result.trigger, m_default_state.trigger, m_settings.trigger);
|
||||
|
||||
#define APPLY_AXIS_BUTTON(_axis_, _flag_) \
|
||||
if (result._axis_.x < -ControllerState::kAxisThreshold) \
|
||||
result.buttons.SetButtonState((_flag_) + (kAxisXN - kAxisXP), true); \
|
||||
else if (result._axis_.x > ControllerState::kAxisThreshold) \
|
||||
result.buttons.SetButtonState((_flag_), true); \
|
||||
if (result._axis_.y < -ControllerState::kAxisThreshold) \
|
||||
result.buttons.SetButtonState((_flag_) + 1 + (kAxisXN - kAxisXP), true); \
|
||||
else if (result._axis_.y > ControllerState::kAxisThreshold) \
|
||||
result.buttons.SetButtonState((_flag_) + 1, true);
|
||||
|
||||
if (result.axis.x < -ControllerState::kAxisThreshold)
|
||||
result.buttons.SetButtonState((kAxisXP) + (kAxisXN - kAxisXP), true);
|
||||
else if (result.axis.x > ControllerState::kAxisThreshold)
|
||||
result.buttons.SetButtonState((kAxisXP), true);
|
||||
if (result.axis.y < -ControllerState::kAxisThreshold)
|
||||
result.buttons.SetButtonState((kAxisXP) + 1 + (kAxisXN - kAxisXP), true);
|
||||
else if (result.axis.y > ControllerState::kAxisThreshold)
|
||||
result.buttons.SetButtonState((kAxisXP) + 1, true);
|
||||
APPLY_AXIS_BUTTON(rotation, kRotationXP);
|
||||
APPLY_AXIS_BUTTON(trigger, kTriggerXP);
|
||||
apply_axis_button(result.buttons, result.axis, kAxisXP);
|
||||
apply_axis_button(result.buttons, result.rotation, kRotationXP);
|
||||
apply_axis_button(result.buttons, result.trigger, kTriggerXP);
|
||||
|
||||
/*
|
||||
// positive values
|
||||
@ -64,9 +60,6 @@ const ControllerState& ControllerBase::update_state()
|
||||
kTriggerYN,
|
||||
*/
|
||||
|
||||
|
||||
#undef APPLY_AXIS_BUTTON
|
||||
|
||||
WindowSystem::CaptureInput(result, m_last_state);
|
||||
|
||||
m_last_state = std::move(result);
|
||||
@ -200,7 +193,6 @@ std::string ControllerBase::get_button_name(uint64 button) const
|
||||
case kTriggerYN: return "y-Trigger-";
|
||||
}
|
||||
|
||||
|
||||
return fmt::format("Button {}", (uint64)button);
|
||||
}
|
||||
|
||||
|
||||
@ -168,14 +168,13 @@ protected:
|
||||
Settings m_settings{};
|
||||
};
|
||||
|
||||
template<class TProvider>
|
||||
template<std::derived_from<ControllerProviderBase> TProvider>
|
||||
class Controller : public ControllerBase
|
||||
{
|
||||
public:
|
||||
Controller(std::string_view uuid, std::string_view display_name)
|
||||
: ControllerBase(uuid, display_name)
|
||||
{
|
||||
static_assert(std::is_base_of_v<ControllerProviderBase, TProvider>);
|
||||
m_provider = std::dynamic_pointer_cast<TProvider>(InputManager::instance().get_api_provider(TProvider::kAPIType));
|
||||
cemu_assert_debug(m_provider != nullptr);
|
||||
}
|
||||
@ -183,7 +182,6 @@ public:
|
||||
Controller(std::string_view uuid, std::string_view display_name, const ControllerProviderSettings& settings)
|
||||
: ControllerBase(uuid, display_name)
|
||||
{
|
||||
static_assert(std::is_base_of_v<ControllerProviderBase, TProvider>);
|
||||
m_provider = std::dynamic_pointer_cast<TProvider>(InputManager::instance().get_api_provider(TProvider::kAPIType, settings));
|
||||
cemu_assert_debug(m_provider != nullptr);
|
||||
}
|
||||
|
||||
@ -42,8 +42,24 @@ DSUControllerProvider::~DSUControllerProvider()
|
||||
if (m_running)
|
||||
{
|
||||
m_running = false;
|
||||
m_writer_thread.join();
|
||||
// wake up the reader thread by sending a packet to self
|
||||
if (m_socketWakeupEndpoint.port() != 0)
|
||||
{
|
||||
boost::asio::io_context io_context;
|
||||
boost::asio::ip::udp::socket socket(io_context);
|
||||
boost::system::error_code ec;
|
||||
socket.open(m_socketWakeupEndpoint.protocol(), ec);
|
||||
if (!ec)
|
||||
{
|
||||
std::array<char, 1> data{};
|
||||
socket.send_to(boost::asio::buffer(data), m_socketWakeupEndpoint, 0, ec);
|
||||
}
|
||||
else
|
||||
cemuLog_log(LogType::Force, "DSUControllerProvider wakeup failed");
|
||||
}
|
||||
m_reader_thread.join();
|
||||
m_writerJobs.push(nullptr); // wake up writer thread by pushing an empty message
|
||||
m_writer_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,14 +100,8 @@ bool DSUControllerProvider::connect()
|
||||
m_socket.close();
|
||||
|
||||
m_socket.open(ip::udp::v4());
|
||||
|
||||
// set timeout for our threads to give a chance to exit
|
||||
#if BOOST_OS_WINDOWS
|
||||
m_socket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_RCVTIMEO>{200});
|
||||
#elif BOOST_OS_LINUX || BOOST_OS_MACOS
|
||||
timeval timeout{.tv_usec = 200 * 1000};
|
||||
setsockopt(m_socket.native_handle(), SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval));
|
||||
#endif
|
||||
m_socket.bind(ip::udp::endpoint(ip::udp::v4(), 0));
|
||||
m_socketWakeupEndpoint = ip::udp::endpoint(boost::asio::ip::address_v4::loopback(), m_socket.local_endpoint().port());
|
||||
// reset data
|
||||
m_state = {};
|
||||
m_prev_state = {};
|
||||
@ -189,19 +199,13 @@ uint32_t DSUControllerProvider::get_packet_index(uint8_t index) const
|
||||
void DSUControllerProvider::request_version()
|
||||
{
|
||||
auto msg = std::make_unique<VersionRequest>(m_uid);
|
||||
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_jobs.push(std::move(msg));
|
||||
m_writer_cond.notify_one();
|
||||
m_writerJobs.push(std::move(msg));
|
||||
}
|
||||
|
||||
void DSUControllerProvider::request_pad_info()
|
||||
{
|
||||
auto msg = std::make_unique<ListPorts>(m_uid, 4, std::array<uint8_t, 4>{0, 1, 2, 3});
|
||||
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_jobs.push(std::move(msg));
|
||||
m_writer_cond.notify_one();
|
||||
m_writerJobs.push(std::move(msg));
|
||||
}
|
||||
|
||||
void DSUControllerProvider::request_pad_info(uint8_t index)
|
||||
@ -210,19 +214,13 @@ void DSUControllerProvider::request_pad_info(uint8_t index)
|
||||
return;
|
||||
|
||||
auto msg = std::make_unique<ListPorts>(m_uid, 1, std::array<uint8_t, 4>{index});
|
||||
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_jobs.push(std::move(msg));
|
||||
m_writer_cond.notify_one();
|
||||
m_writerJobs.push(std::move(msg));
|
||||
}
|
||||
|
||||
void DSUControllerProvider::request_pad_data()
|
||||
{
|
||||
auto msg = std::make_unique<DataRequest>(m_uid);
|
||||
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_jobs.push(std::move(msg));
|
||||
m_writer_cond.notify_one();
|
||||
m_writerJobs.push(std::move(msg));
|
||||
}
|
||||
|
||||
void DSUControllerProvider::request_pad_data(uint8_t index)
|
||||
@ -231,10 +229,7 @@ void DSUControllerProvider::request_pad_data(uint8_t index)
|
||||
return;
|
||||
|
||||
auto msg = std::make_unique<DataRequest>(m_uid, index);
|
||||
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_jobs.push(std::move(msg));
|
||||
m_writer_cond.notify_one();
|
||||
m_writerJobs.push(std::move(msg));
|
||||
}
|
||||
|
||||
MotionSample DSUControllerProvider::get_motion_sample(uint8_t index) const
|
||||
@ -259,8 +254,11 @@ void DSUControllerProvider::reader_thread()
|
||||
boost::asio::ip::udp::endpoint sender_endpoint;
|
||||
boost::system::error_code ec{};
|
||||
const size_t len = m_socket.receive_from(boost::asio::buffer(recv_buf), sender_endpoint, 0, ec);
|
||||
if (!m_running.load(std::memory_order_relaxed))
|
||||
break;
|
||||
if (ec)
|
||||
{
|
||||
|
||||
#ifdef DEBUG_DSU_CLIENT
|
||||
printf(" DSUControllerProvider::ReaderThread: exception %s\n", ec.what());
|
||||
#endif
|
||||
@ -336,6 +334,7 @@ void DSUControllerProvider::reader_thread()
|
||||
}
|
||||
|
||||
index = info->GetIndex();
|
||||
cemu_assert(index < kMaxClients);
|
||||
#ifdef DEBUG_DSU_CLIENT
|
||||
printf(" DSUControllerProvider::ReaderThread: received PortInfo for index %d\n", index);
|
||||
#endif
|
||||
@ -359,6 +358,7 @@ void DSUControllerProvider::reader_thread()
|
||||
}
|
||||
|
||||
index = rsp->GetIndex();
|
||||
cemu_assert(index < kMaxClients);
|
||||
#ifdef DEBUG_DSU_CLIENT
|
||||
printf(" DSUControllerProvider::ReaderThread: received DataResponse for index %d\n", index);
|
||||
#endif
|
||||
@ -384,20 +384,10 @@ void DSUControllerProvider::writer_thread()
|
||||
SetThreadName("DSU-writer");
|
||||
while (m_running.load(std::memory_order_relaxed))
|
||||
{
|
||||
std::unique_lock lock(m_writer_mutex);
|
||||
while (m_writer_jobs.empty())
|
||||
{
|
||||
if (m_writer_cond.wait_for(lock, std::chrono::milliseconds(250)) == std::cv_status::timeout)
|
||||
{
|
||||
if (!m_running.load(std::memory_order_relaxed))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto msg = std::move(m_writer_jobs.front());
|
||||
m_writer_jobs.pop();
|
||||
lock.unlock();
|
||||
|
||||
std::unique_ptr<ClientMessage> msg = m_writerJobs.pop();
|
||||
if (!m_running.load(std::memory_order_relaxed))
|
||||
return;
|
||||
cemu_assert_debug(msg.get());
|
||||
#ifdef DEBUG_DSU_CLIENT
|
||||
printf(" DSUControllerProvider::WriterThread: sending message: 0x%x (len: 0x%x)\n", (int)msg->GetMessageType(), msg->GetSize());
|
||||
#endif
|
||||
@ -457,4 +447,4 @@ DSUControllerProvider::ControllerState& DSUControllerProvider::ControllerState::
|
||||
this->operator=(static_cast<const PortInfo&>(data_response));
|
||||
data = data_response.GetData();
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
#include "input/api/DSU/DSUMessages.h"
|
||||
|
||||
#include "input/api/ControllerProvider.h"
|
||||
#include "util/helpers/ConcurrentQueue.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
@ -87,7 +88,6 @@ public:
|
||||
void request_pad_data();
|
||||
void request_pad_data(uint8_t index);
|
||||
|
||||
|
||||
private:
|
||||
uint16 m_server_version = 0;
|
||||
|
||||
@ -98,20 +98,18 @@ private:
|
||||
void writer_thread();
|
||||
void integrate_motion(uint8_t index, const DataResponse& data_response);
|
||||
|
||||
std::mutex m_writer_mutex;
|
||||
std::condition_variable m_writer_cond;
|
||||
|
||||
uint32 m_uid;
|
||||
boost::asio::io_context m_io_service;
|
||||
boost::asio::ip::udp::endpoint m_receiver_endpoint;
|
||||
boost::asio::ip::udp::socket m_socket;
|
||||
boost::asio::ip::udp::endpoint m_socketWakeupEndpoint;
|
||||
|
||||
std::array<ControllerState, kMaxClients> m_state{};
|
||||
std::array<ControllerState, kMaxClients> m_prev_state{};
|
||||
mutable std::array<std::mutex, kMaxClients> m_mutex;
|
||||
mutable std::array<std::condition_variable, kMaxClients> m_wait_cond;
|
||||
|
||||
std::queue<std::unique_ptr<ClientMessage>> m_writer_jobs;
|
||||
ConcurrentQueue<std::unique_ptr<ClientMessage>> m_writerJobs;
|
||||
|
||||
std::array<WiiUMotionHandler, kMaxClients> m_motion_handler;
|
||||
std::array<uint64, kMaxClients> m_last_motion_timestamp{};
|
||||
|
||||
@ -2,27 +2,30 @@
|
||||
|
||||
#include "input/api/SDL/SDLControllerProvider.h"
|
||||
|
||||
SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index)
|
||||
SDLController::SDLController(const SDL_GUID& guid, size_t guid_index)
|
||||
: base_type(fmt::format("{}_", guid_index), fmt::format("Controller {}", guid_index + 1)), m_guid_index(guid_index),
|
||||
m_guid(guid)
|
||||
{
|
||||
char tmp[64];
|
||||
SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp));
|
||||
SDL_GUIDToString(m_guid, tmp, std::size(tmp));
|
||||
m_uuid += tmp;
|
||||
}
|
||||
|
||||
SDLController::SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name)
|
||||
SDLController::SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name)
|
||||
: base_type(fmt::format("{}_", guid_index), display_name), m_guid_index(guid_index), m_guid(guid)
|
||||
{
|
||||
char tmp[64];
|
||||
SDL_JoystickGetGUIDString(m_guid, tmp, std::size(tmp));
|
||||
SDL_GUIDToString(m_guid, tmp, std::size(tmp));
|
||||
m_uuid += tmp;
|
||||
}
|
||||
|
||||
SDLController::~SDLController()
|
||||
{
|
||||
if (m_controller)
|
||||
SDL_GameControllerClose(m_controller);
|
||||
{
|
||||
SDL_CloseGamepad(m_controller);
|
||||
m_controller = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool SDLController::is_connected()
|
||||
@ -33,8 +36,9 @@ bool SDLController::is_connected()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SDL_GameControllerGetAttached(m_controller))
|
||||
if (!SDL_GamepadConnected(m_controller))
|
||||
{
|
||||
SDL_CloseGamepad(m_controller);
|
||||
m_controller = nullptr;
|
||||
return false;
|
||||
}
|
||||
@ -45,49 +49,39 @@ bool SDLController::is_connected()
|
||||
bool SDLController::connect()
|
||||
{
|
||||
if (is_connected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_has_rumble = false;
|
||||
|
||||
const auto index = m_provider->get_index(m_guid_index, m_guid);
|
||||
|
||||
std::scoped_lock lock(m_controller_mutex);
|
||||
m_diid = SDL_JoystickGetDeviceInstanceID(index);
|
||||
if (m_diid == -1)
|
||||
|
||||
int gamepad_count = 0;
|
||||
|
||||
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
||||
|
||||
if (!gamepad_ids || index < 0 || index >= gamepad_count)
|
||||
return false;
|
||||
|
||||
m_controller = SDL_GameControllerOpen(index);
|
||||
m_diid = gamepad_ids[index];
|
||||
SDL_free(gamepad_ids);
|
||||
|
||||
m_controller = SDL_OpenGamepad(m_diid);
|
||||
|
||||
if (!m_controller)
|
||||
return false;
|
||||
|
||||
if (const char* name = SDL_GameControllerName(m_controller))
|
||||
if (const char* name = SDL_GetGamepadName(m_controller))
|
||||
m_display_name = name;
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i)
|
||||
{
|
||||
m_buttons[i] = SDL_GameControllerHasButton(m_controller, (SDL_GameControllerButton)i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; ++i)
|
||||
{
|
||||
m_axis[i] = SDL_GameControllerHasAxis(m_controller, (SDL_GameControllerAxis)i);
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_ACCEL))
|
||||
{
|
||||
m_has_accel = true;
|
||||
SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_ACCEL, SDL_TRUE);
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasSensor(m_controller, SDL_SENSOR_GYRO))
|
||||
{
|
||||
m_has_gyro = true;
|
||||
SDL_GameControllerSetSensorEnabled(m_controller, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||
}
|
||||
|
||||
m_has_rumble = SDL_GameControllerRumble(m_controller, 0, 0, 0) == 0;
|
||||
for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i)
|
||||
m_buttons[i] = SDL_GamepadHasButton(m_controller, (SDL_GamepadButton)i);
|
||||
for (size_t i = 0; i < SDL_GAMEPAD_AXIS_COUNT; ++i)
|
||||
m_axis[i] = SDL_GamepadHasAxis(m_controller, (SDL_GamepadAxis)i);
|
||||
if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_ACCEL))
|
||||
m_has_accel = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_ACCEL, true);
|
||||
if (SDL_GamepadHasSensor(m_controller, SDL_SENSOR_GYRO))
|
||||
m_has_gyro = SDL_SetGamepadSensorEnabled(m_controller, SDL_SENSOR_GYRO, true);
|
||||
m_has_rumble = SDL_RumbleGamepad(m_controller, 0, 0, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -96,12 +90,9 @@ void SDLController::start_rumble()
|
||||
std::scoped_lock lock(m_controller_mutex);
|
||||
if (is_connected() && !m_has_rumble)
|
||||
return;
|
||||
|
||||
if (m_settings.rumble <= 0)
|
||||
return;
|
||||
|
||||
SDL_GameControllerRumble(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF),
|
||||
5 * 1000);
|
||||
SDL_RumbleGamepad(m_controller, (Uint16)(m_settings.rumble * 0xFFFF), (Uint16)(m_settings.rumble * 0xFFFF), 5 * 1000);
|
||||
}
|
||||
|
||||
void SDLController::stop_rumble()
|
||||
@ -109,59 +100,47 @@ void SDLController::stop_rumble()
|
||||
std::scoped_lock lock(m_controller_mutex);
|
||||
if (is_connected() && !m_has_rumble)
|
||||
return;
|
||||
|
||||
SDL_GameControllerRumble(m_controller, 0, 0, 0);
|
||||
SDL_RumbleGamepad(m_controller, 0, 0, 0);
|
||||
}
|
||||
|
||||
MotionSample SDLController::get_motion_sample()
|
||||
{
|
||||
if (is_connected() && has_motion())
|
||||
{
|
||||
return m_provider->motion_sample(m_diid);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string SDLController::get_button_name(uint64 button) const
|
||||
{
|
||||
if (const char* name = SDL_GameControllerGetStringForButton((SDL_GameControllerButton)button))
|
||||
if (const char* name = SDL_GetGamepadStringForButton((SDL_GamepadButton)button))
|
||||
return name;
|
||||
|
||||
return base_type::get_button_name(button);
|
||||
}
|
||||
|
||||
ControllerState SDLController::raw_state()
|
||||
{
|
||||
ControllerState result{};
|
||||
|
||||
std::scoped_lock lock(m_controller_mutex);
|
||||
if (!is_connected())
|
||||
return result;
|
||||
|
||||
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i)
|
||||
for (size_t i = 0; i < SDL_GAMEPAD_BUTTON_COUNT; ++i)
|
||||
{
|
||||
if (m_buttons[i] && SDL_GameControllerGetButton(m_controller, (SDL_GameControllerButton)i))
|
||||
if (m_buttons[i] && SDL_GetGamepadButton(m_controller, (SDL_GamepadButton)i))
|
||||
result.buttons.SetButtonState(i, true);
|
||||
}
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_LEFTX])
|
||||
result.axis.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTX) / 32767.0f;
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_LEFTY])
|
||||
result.axis.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_LEFTY) / 32767.0f;
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_RIGHTX])
|
||||
result.rotation.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTX) / 32767.0f;
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_RIGHTY])
|
||||
result.rotation.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_RIGHTY) / 32767.0f;
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERLEFT])
|
||||
result.trigger.x = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) / 32767.0f;
|
||||
|
||||
if (m_axis[SDL_CONTROLLER_AXIS_TRIGGERRIGHT])
|
||||
result.trigger.y = (float)SDL_GameControllerGetAxis(m_controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_LEFTX])
|
||||
result.axis.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTX) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_LEFTY])
|
||||
result.axis.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFTY) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_RIGHTX])
|
||||
result.rotation.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTX) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_RIGHTY])
|
||||
result.rotation.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHTY) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_LEFT_TRIGGER])
|
||||
result.trigger.x = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) / 32767.0f;
|
||||
if (m_axis[SDL_GAMEPAD_AXIS_RIGHT_TRIGGER])
|
||||
result.trigger.y = (float)SDL_GetGamepadAxis(m_controller, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) / 32767.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
#include "input/api/Controller.h"
|
||||
#include "input/api/SDL/SDLControllerProvider.h"
|
||||
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
|
||||
class SDLController : public Controller<SDLControllerProvider>
|
||||
{
|
||||
public:
|
||||
SDLController(const SDL_JoystickGUID& guid, size_t guid_index);
|
||||
SDLController(const SDL_JoystickGUID& guid, size_t guid_index, std::string_view display_name);
|
||||
SDLController(const SDL_GUID& guid, size_t guid_index);
|
||||
SDLController(const SDL_GUID& guid, size_t guid_index, std::string_view display_name);
|
||||
|
||||
~SDLController() override;
|
||||
|
||||
@ -32,29 +32,29 @@ public:
|
||||
MotionSample get_motion_sample() override;
|
||||
|
||||
std::string get_button_name(uint64 button) const override;
|
||||
const SDL_JoystickGUID& get_guid() const { return m_guid; }
|
||||
const SDL_GUID& get_guid() const { return m_guid; }
|
||||
|
||||
constexpr static SDL_JoystickGUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 };
|
||||
constexpr static SDL_JoystickGUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
|
||||
constexpr static SDL_JoystickGUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
|
||||
constexpr static SDL_GUID kLeftJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00,0x68 ,0x00 };
|
||||
constexpr static SDL_GUID kRightJoyCon{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x07, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
|
||||
constexpr static SDL_GUID kSwitchProController{ 0x03, 0x00, 0x00, 0x00, 0x7e, 0x05, 0x00, 0x00, 0x09, 0x20, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00 };
|
||||
|
||||
protected:
|
||||
ControllerState raw_state() override;
|
||||
|
||||
private:
|
||||
inline static SDL_JoystickGUID kEmptyGUID{};
|
||||
inline static SDL_GUID kEmptyGUID{};
|
||||
|
||||
size_t m_guid_index;
|
||||
SDL_JoystickGUID m_guid;
|
||||
SDL_GUID m_guid;
|
||||
std::recursive_mutex m_controller_mutex;
|
||||
SDL_GameController* m_controller = nullptr;
|
||||
SDL_Gamepad* m_controller = nullptr;
|
||||
SDL_JoystickID m_diid = -1;
|
||||
|
||||
bool m_has_gyro = false;
|
||||
bool m_has_accel = false;
|
||||
bool m_has_rumble = false;
|
||||
|
||||
std::array<bool, SDL_CONTROLLER_BUTTON_MAX> m_buttons{};
|
||||
std::array<bool, SDL_CONTROLLER_AXIS_MAX> m_axis{};
|
||||
std::array<bool, SDL_GAMEPAD_BUTTON_COUNT> m_buttons{};
|
||||
std::array<bool, SDL_GAMEPAD_AXIS_COUNT> m_axis{};
|
||||
};
|
||||
|
||||
|
||||
@ -3,12 +3,12 @@
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#include "util/helpers/TempState.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <boost/functional/hash.hpp>
|
||||
|
||||
struct SDL_JoystickGUIDHash
|
||||
{
|
||||
std::size_t operator()(const SDL_JoystickGUID& guid) const
|
||||
std::size_t operator()(const SDL_GUID& guid) const
|
||||
{
|
||||
return boost::hash_value(guid.data);
|
||||
}
|
||||
@ -16,229 +16,285 @@ struct SDL_JoystickGUIDHash
|
||||
|
||||
SDLControllerProvider::SDLControllerProvider()
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
|
||||
|
||||
if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS) < 0)
|
||||
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
|
||||
|
||||
|
||||
if (SDL_GameControllerEventState(SDL_ENABLE) < 0) {
|
||||
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
|
||||
#if !BOOST_OS_MACOS
|
||||
std::scoped_lock _l(s_mutex);
|
||||
if (s_initCount.fetch_add(1) == 0)
|
||||
{
|
||||
s_running = true;
|
||||
s_thread = std::thread(&SDLControllerProvider::event_thread, this);
|
||||
}
|
||||
|
||||
m_running = true;
|
||||
m_thread = std::thread(&SDLControllerProvider::event_thread, this);
|
||||
#endif
|
||||
}
|
||||
|
||||
SDLControllerProvider::~SDLControllerProvider()
|
||||
{
|
||||
if (m_running)
|
||||
#if !BOOST_OS_MACOS
|
||||
bool shutdownSDL = false;
|
||||
{
|
||||
m_running = false;
|
||||
// wake the thread with a quit event if it's currently waiting for events
|
||||
SDL_Event evt;
|
||||
evt.type = SDL_QUIT;
|
||||
SDL_PushEvent(&evt);
|
||||
// wait until thread exited
|
||||
m_thread.join();
|
||||
std::scoped_lock _l(s_mutex);
|
||||
if (s_initCount.fetch_sub(1) == 1)
|
||||
{
|
||||
cemu_assert_debug(s_running);
|
||||
s_running = false;
|
||||
shutdownSDL = true;
|
||||
}
|
||||
}
|
||||
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS);
|
||||
if (shutdownSDL)
|
||||
{
|
||||
// wake the thread with a quit event if it's currently waiting for events
|
||||
SDL_Event evt;
|
||||
SDL_zero(evt);
|
||||
evt.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&evt);
|
||||
if (s_thread.joinable())
|
||||
{
|
||||
s_thread.join();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> SDLControllerProvider::get_controllers()
|
||||
{
|
||||
std::vector<std::shared_ptr<ControllerBase>> result;
|
||||
|
||||
std::unordered_map<SDL_JoystickGUID, size_t, SDL_JoystickGUIDHash> guid_counter;
|
||||
std::unordered_map<SDL_GUID, size_t, SDL_JoystickGUIDHash> guid_counter;
|
||||
|
||||
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||
int gamepad_count = 0;
|
||||
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
||||
if (gamepad_ids)
|
||||
{
|
||||
if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER)
|
||||
for (size_t i = 0; i < gamepad_count; ++i)
|
||||
{
|
||||
const auto guid = SDL_JoystickGetDeviceGUID(i);
|
||||
|
||||
const auto guid = SDL_GetGamepadGUIDForID(gamepad_ids[i]);
|
||||
const auto it = guid_counter.try_emplace(guid, 0);
|
||||
|
||||
if (auto* controller = SDL_GameControllerOpen(i))
|
||||
{
|
||||
const char* name = SDL_GameControllerName(controller);
|
||||
|
||||
if (const char* name = SDL_GetGamepadNameForID(gamepad_ids[i]))
|
||||
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second, name));
|
||||
SDL_GameControllerClose(controller);
|
||||
}
|
||||
else
|
||||
result.emplace_back(std::make_shared<SDLController>(guid, it.first->second));
|
||||
|
||||
++it.first->second;
|
||||
}
|
||||
SDL_free(gamepad_ids);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int SDLControllerProvider::get_index(size_t guid_index, const SDL_JoystickGUID& guid) const
|
||||
int SDLControllerProvider::get_index(size_t guid_index, const SDL_GUID& guid) const
|
||||
{
|
||||
size_t index = 0;
|
||||
|
||||
int gamepad_count = 0;
|
||||
TempState lock(SDL_LockJoysticks, SDL_UnlockJoysticks);
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||
SDL_JoystickID *gamepad_ids = SDL_GetGamepads(&gamepad_count);
|
||||
if (gamepad_ids)
|
||||
{
|
||||
if (SDL_JoystickGetDeviceType(i) == SDL_JOYSTICK_TYPE_GAMECONTROLLER)
|
||||
for (size_t i = 0; i < gamepad_count; ++i)
|
||||
{
|
||||
if(guid == SDL_JoystickGetDeviceGUID(i))
|
||||
if (guid == SDL_GetGamepadGUIDForID(gamepad_ids[i]))
|
||||
{
|
||||
if (index == guid_index)
|
||||
{
|
||||
SDL_free(gamepad_ids);
|
||||
return i;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
}
|
||||
SDL_free(gamepad_ids);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
MotionSample SDLControllerProvider::motion_sample(int diid)
|
||||
MotionSample SDLControllerProvider::motion_sample(SDL_JoystickID diid)
|
||||
{
|
||||
std::scoped_lock lock(m_motion_data_mtx[diid]);
|
||||
return m_motion_data[diid];
|
||||
std::shared_lock lock(s_mutex);
|
||||
auto it = s_motion_states.find(diid);
|
||||
return (it != s_motion_states.end()) ? it->second.data : MotionSample{};
|
||||
}
|
||||
|
||||
void SDLControllerProvider::InitSDL()
|
||||
{
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ENHANCED_REPORTS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH2, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STADIA, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_STEAM, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_LUNA, "1");
|
||||
|
||||
if (!SDL_InitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC))
|
||||
{
|
||||
throw std::runtime_error(fmt::format("couldn't initialize SDL: {}", SDL_GetError()));
|
||||
}
|
||||
|
||||
SDL_SetGamepadEventsEnabled(true);
|
||||
if (!SDL_GamepadEventsEnabled())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Couldn't enable SDL gamecontroller event polling: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
void SDLControllerProvider::ShutdownSDL()
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_GAMEPAD | SDL_INIT_HAPTIC);
|
||||
}
|
||||
|
||||
#if BOOST_OS_MACOS
|
||||
void SDLControllerProvider::PumpSDLEvents()
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
HandleSDLEvent(event);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SDLControllerProvider::HandleSDLEvent(SDL_Event& event)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_EVENT_QUIT:
|
||||
{
|
||||
std::scoped_lock _l(s_mutex);
|
||||
s_running = false;
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION: /**< Game controller axis motion */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN: /**< Game controller button pressed */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_BUTTON_UP: /**< Game controller button released */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_ADDED: /**< A new Game controller has been inserted into the system */
|
||||
{
|
||||
std::scoped_lock _l(s_mutex);
|
||||
InputManager::instance().on_device_changed();
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMOVED: /**< An opened Game controller has been removed */
|
||||
{
|
||||
std::scoped_lock _l(s_mutex);
|
||||
InputManager::instance().on_device_changed();
|
||||
s_motion_states.erase(event.gdevice.which);
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_REMAPPED: /**< The controller mapping was updated */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN: /**< Game controller touchpad was touched */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION: /**< Game controller touchpad finger was moved */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_TOUCHPAD_UP: /**< Game controller touchpad finger was lifted */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE: /**< Game controller sensor was updated */
|
||||
{
|
||||
SDL_JoystickID id = event.gsensor.which;
|
||||
uint64_t ts = event.gsensor.timestamp;
|
||||
std::scoped_lock _l(s_mutex);
|
||||
auto& state = s_motion_states[id];
|
||||
auto& tracking = state.tracking;
|
||||
|
||||
if (event.gsensor.sensor == SDL_SENSOR_ACCEL)
|
||||
{
|
||||
const auto dif = ts - tracking.lastTimestampAccel;
|
||||
if (dif <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (dif >= 10000000000)
|
||||
{
|
||||
tracking.hasAcc = false;
|
||||
tracking.hasGyro = false;
|
||||
tracking.lastTimestampAccel = ts;
|
||||
break;
|
||||
}
|
||||
|
||||
tracking.lastTimestampAccel = ts;
|
||||
tracking.acc[0] = -event.gsensor.data[0] / 9.81f;
|
||||
tracking.acc[1] = -event.gsensor.data[1] / 9.81f;
|
||||
tracking.acc[2] = -event.gsensor.data[2] / 9.81f;
|
||||
tracking.hasAcc = true;
|
||||
}
|
||||
if (event.gsensor.sensor == SDL_SENSOR_GYRO)
|
||||
{
|
||||
const auto dif = ts - tracking.lastTimestampGyro;
|
||||
if (dif <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (dif >= 10000000000)
|
||||
{
|
||||
tracking.hasAcc = false;
|
||||
tracking.hasGyro = false;
|
||||
tracking.lastTimestampGyro = ts;
|
||||
break;
|
||||
}
|
||||
|
||||
tracking.lastTimestampGyro = ts;
|
||||
tracking.gyro[0] = event.gsensor.data[0];
|
||||
tracking.gyro[1] = -event.gsensor.data[1];
|
||||
tracking.gyro[2] = -event.gsensor.data[2];
|
||||
tracking.hasGyro = true;
|
||||
}
|
||||
if (tracking.hasAcc && tracking.hasGyro)
|
||||
{
|
||||
auto ts = std::max(tracking.lastTimestampGyro, tracking.lastTimestampAccel);
|
||||
|
||||
if (ts > tracking.lastTimestampIntegrate)
|
||||
{
|
||||
const auto tsDif = ts - tracking.lastTimestampIntegrate;
|
||||
tracking.lastTimestampIntegrate = ts;
|
||||
float tsDifD = (float)tsDif / 1000000000.0f;
|
||||
|
||||
if (tsDifD >= 1.0f)
|
||||
{
|
||||
tsDifD = 1.0f;
|
||||
}
|
||||
|
||||
state.handler.processMotionSample(tsDifD, tracking.gyro.x, tracking.gyro.y, tracking.gyro.z, tracking.acc.x, -tracking.acc.y, -tracking.acc.z);
|
||||
state.data = state.handler.getMotionSample();
|
||||
}
|
||||
|
||||
tracking.hasAcc = false;
|
||||
tracking.hasGyro = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDLControllerProvider::event_thread()
|
||||
{
|
||||
#if BOOST_OS_MACOS
|
||||
cemu_assert(false);
|
||||
#endif
|
||||
SetThreadName("SDL_events");
|
||||
while (m_running.load(std::memory_order_relaxed))
|
||||
InitSDL();
|
||||
while (s_running.load(std::memory_order_relaxed))
|
||||
{
|
||||
SDL_Event event{};
|
||||
SDL_WaitEvent(&event);
|
||||
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
m_running = false;
|
||||
return;
|
||||
|
||||
case SDL_CONTROLLERAXISMOTION: /**< Game controller axis motion */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERBUTTONDOWN: /**< Game controller button pressed */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERBUTTONUP: /**< Game controller button released */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERDEVICEADDED: /**< A new Game controller has been inserted into the system */
|
||||
{
|
||||
InputManager::instance().on_device_changed();
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERDEVICEREMOVED: /**< An opened Game controller has been removed */
|
||||
{
|
||||
InputManager::instance().on_device_changed();
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERDEVICEREMAPPED: /**< The controller mapping was updated */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERTOUCHPADDOWN: /**< Game controller touchpad was touched */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERTOUCHPADMOTION: /**< Game controller touchpad finger was moved */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERTOUCHPADUP: /**< Game controller touchpad finger was lifted */
|
||||
{
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERSENSORUPDATE: /**< Game controller sensor was updated */
|
||||
{
|
||||
const auto index = event.csensor.which;
|
||||
const auto ts = event.csensor.timestamp;
|
||||
auto& motionTracking = m_motion_tracking[index];
|
||||
|
||||
if (event.csensor.sensor == SDL_SENSOR_ACCEL)
|
||||
{
|
||||
const auto dif = ts - motionTracking.lastTimestampAccel;
|
||||
if (dif <= 0)
|
||||
break;
|
||||
|
||||
if (dif >= 10000)
|
||||
{
|
||||
motionTracking.hasAcc = false;
|
||||
motionTracking.hasGyro = false;
|
||||
motionTracking.lastTimestampAccel = ts;
|
||||
break;
|
||||
}
|
||||
|
||||
motionTracking.lastTimestampAccel = ts;
|
||||
motionTracking.acc[0] = -event.csensor.data[0] / 9.81f;
|
||||
motionTracking.acc[1] = -event.csensor.data[1] / 9.81f;
|
||||
motionTracking.acc[2] = -event.csensor.data[2] / 9.81f;
|
||||
motionTracking.hasAcc = true;
|
||||
}
|
||||
if (event.csensor.sensor == SDL_SENSOR_GYRO)
|
||||
{
|
||||
const auto dif = ts - motionTracking.lastTimestampGyro;
|
||||
if (dif <= 0)
|
||||
break;
|
||||
|
||||
if (dif >= 10000)
|
||||
{
|
||||
motionTracking.hasAcc = false;
|
||||
motionTracking.hasGyro = false;
|
||||
motionTracking.lastTimestampGyro = ts;
|
||||
break;
|
||||
}
|
||||
motionTracking.lastTimestampGyro = ts;
|
||||
motionTracking.gyro[0] = event.csensor.data[0];
|
||||
motionTracking.gyro[1] = -event.csensor.data[1];
|
||||
motionTracking.gyro[2] = -event.csensor.data[2];
|
||||
motionTracking.hasGyro = true;
|
||||
}
|
||||
if (motionTracking.hasAcc && motionTracking.hasGyro)
|
||||
{
|
||||
auto ts = std::max(motionTracking.lastTimestampGyro, motionTracking.lastTimestampAccel);
|
||||
if (ts > motionTracking.lastTimestampIntegrate)
|
||||
{
|
||||
const auto tsDif = ts - motionTracking.lastTimestampIntegrate;
|
||||
motionTracking.lastTimestampIntegrate = ts;
|
||||
float tsDifD = (float)tsDif / 1000.0f;
|
||||
if (tsDifD >= 1.0f)
|
||||
tsDifD = 1.0f;
|
||||
m_motion_handler[index].processMotionSample(tsDifD, motionTracking.gyro.x, motionTracking.gyro.y, motionTracking.gyro.z, motionTracking.acc.x, -motionTracking.acc.y, -motionTracking.acc.z);
|
||||
|
||||
std::scoped_lock lock(m_motion_data_mtx[index]);
|
||||
m_motion_data[index] = m_motion_handler[index].getMotionSample();
|
||||
}
|
||||
motionTracking.hasAcc = false;
|
||||
motionTracking.hasGyro = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
HandleSDLEvent(event);
|
||||
}
|
||||
ShutdownSDL();
|
||||
}
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
#pragma once
|
||||
#include <SDL2/SDL_joystick.h>
|
||||
#include <SDL3/SDL_joystick.h>
|
||||
#include "input/motion/MotionHandler.h"
|
||||
#include "input/api/ControllerProvider.h"
|
||||
|
||||
#ifndef HAS_SDL
|
||||
#define HAS_SDL 1
|
||||
#endif
|
||||
|
||||
static bool operator==(const SDL_JoystickGUID& g1, const SDL_JoystickGUID& g2)
|
||||
static bool operator==(const SDL_GUID& g1, const SDL_GUID& g2)
|
||||
{
|
||||
return memcmp(&g1, &g2, sizeof(SDL_JoystickGUID)) == 0;
|
||||
return memcmp(&g1, &g2, sizeof(SDL_GUID)) == 0;
|
||||
}
|
||||
|
||||
class SDLControllerProvider : public ControllerProviderBase
|
||||
@ -24,19 +20,30 @@ public:
|
||||
|
||||
std::vector<std::shared_ptr<ControllerBase>> get_controllers() override;
|
||||
|
||||
int get_index(size_t guid_index, const SDL_JoystickGUID& guid) const;
|
||||
int get_index(size_t guid_index, const SDL_GUID& guid) const;
|
||||
|
||||
MotionSample motion_sample(int diid);
|
||||
MotionSample motion_sample(SDL_JoystickID diid);
|
||||
|
||||
// exposed for manual event handling on macOS
|
||||
#if BOOST_OS_MACOS
|
||||
static void InitSDL();
|
||||
static void ShutdownSDL();
|
||||
static void PumpSDLEvents();
|
||||
#endif
|
||||
|
||||
private:
|
||||
void event_thread();
|
||||
|
||||
std::atomic_bool m_running = false;
|
||||
std::thread m_thread;
|
||||
static void HandleSDLEvent(union SDL_Event& event);
|
||||
#if !BOOST_OS_MACOS
|
||||
static void InitSDL();
|
||||
static void ShutdownSDL();
|
||||
#endif
|
||||
|
||||
std::array<WiiUMotionHandler, 8> m_motion_handler{};
|
||||
std::array<MotionSample, 8> m_motion_data{};
|
||||
std::array<std::mutex, 8> m_motion_data_mtx{};
|
||||
// there is only one SDL instance, for this reason all of our state can be static
|
||||
inline static std::atomic_int s_initCount{0};
|
||||
inline static std::shared_mutex s_mutex;
|
||||
inline static std::atomic_bool s_running = false;
|
||||
inline static std::thread s_thread;
|
||||
|
||||
struct MotionInfoTracking
|
||||
{
|
||||
@ -49,6 +56,14 @@ private:
|
||||
glm::vec3 acc{};
|
||||
};
|
||||
|
||||
std::array<MotionInfoTracking, 8> m_motion_tracking{};
|
||||
struct MotionState
|
||||
{
|
||||
WiiUMotionHandler handler;
|
||||
MotionSample data;
|
||||
MotionInfoTracking tracking;
|
||||
|
||||
MotionState() = default;
|
||||
};
|
||||
|
||||
inline static std::unordered_map<SDL_JoystickID, MotionState> s_motion_states{};
|
||||
};
|
||||
|
||||
@ -25,9 +25,31 @@ WiimoteControllerProvider::~WiimoteControllerProvider()
|
||||
if (m_running)
|
||||
{
|
||||
m_running = false;
|
||||
m_writer_thread.join();
|
||||
m_reader_thread.join();
|
||||
m_connectionThread.join();
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_writer_mutex);
|
||||
m_writer_cond.notify_all();
|
||||
}
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_connectionMutex);
|
||||
m_connectionCond.notify_all();
|
||||
}
|
||||
|
||||
if (m_writer_thread.joinable())
|
||||
{
|
||||
m_writer_thread.join();
|
||||
}
|
||||
|
||||
if (m_reader_thread.joinable())
|
||||
{
|
||||
m_reader_thread.join();
|
||||
}
|
||||
|
||||
if (m_connectionThread.joinable())
|
||||
{
|
||||
m_connectionThread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +191,8 @@ void WiimoteControllerProvider::connectionThread()
|
||||
m_connectedDevices.clear();
|
||||
std::ranges::move(devices, std::back_inserter(m_connectedDevices));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
std::unique_lock<std::mutex> lock(m_connectionMutex);
|
||||
m_connectionCond.wait_for(lock, std::chrono::seconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -80,6 +80,8 @@ private:
|
||||
std::shared_mutex m_device_mutex;
|
||||
|
||||
std::thread m_connectionThread;
|
||||
std::mutex m_connectionMutex;
|
||||
std::condition_variable m_connectionCond;
|
||||
std::vector<WiimoteDevicePtr> m_connectedDevices;
|
||||
std::mutex m_connectedDeviceMutex;
|
||||
struct Wiimote
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "HidapiWiimote.h"
|
||||
#include <SDL3/SDL.h>
|
||||
#include <cwchar>
|
||||
|
||||
static constexpr uint16 WIIMOTE_VENDOR_ID = 0x057e;
|
||||
@ -7,18 +8,18 @@ static constexpr uint16 WIIMOTE_MP_PRODUCT_ID = 0x0330;
|
||||
static constexpr uint16 WIIMOTE_MAX_INPUT_REPORT_LENGTH = 22;
|
||||
static constexpr auto PRO_CONTROLLER_NAME = L"Nintendo RVL-CNT-01-UC";
|
||||
|
||||
HidapiWiimote::HidapiWiimote(hid_device* dev, std::string_view path)
|
||||
HidapiWiimote::HidapiWiimote(SDL_hid_device* dev, std::string_view path)
|
||||
: m_handle(dev), m_path(path) {
|
||||
|
||||
}
|
||||
|
||||
bool HidapiWiimote::write_data(const std::vector<uint8> &data) {
|
||||
return hid_write(m_handle, data.data(), data.size()) >= 0;
|
||||
return SDL_hid_write(m_handle, data.data(), data.size()) >= 0;
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> HidapiWiimote::read_data() {
|
||||
std::array<uint8, WIIMOTE_MAX_INPUT_REPORT_LENGTH> read_data{};
|
||||
const auto result = hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH);
|
||||
const auto result = SDL_hid_read(m_handle, read_data.data(), WIIMOTE_MAX_INPUT_REPORT_LENGTH);
|
||||
if (result < 0)
|
||||
return {};
|
||||
return {{read_data.cbegin(), read_data.cbegin() + result}};
|
||||
@ -26,24 +27,24 @@ std::optional<std::vector<uint8>> HidapiWiimote::read_data() {
|
||||
|
||||
std::vector<WiimoteDevicePtr> HidapiWiimote::get_devices() {
|
||||
std::vector<WiimoteDevicePtr> wiimote_devices;
|
||||
hid_init();
|
||||
const auto device_enumeration = hid_enumerate(WIIMOTE_VENDOR_ID, 0x0);
|
||||
SDL_hid_init();
|
||||
const auto device_enumeration = SDL_hid_enumerate(WIIMOTE_VENDOR_ID, 0x0);
|
||||
|
||||
for (auto it = device_enumeration; it != nullptr; it = it->next){
|
||||
if (it->product_id != WIIMOTE_PRODUCT_ID && it->product_id != WIIMOTE_MP_PRODUCT_ID)
|
||||
continue;
|
||||
if (std::wcscmp(it->product_string, PRO_CONTROLLER_NAME) == 0)
|
||||
continue;
|
||||
auto dev = hid_open_path(it->path);
|
||||
auto dev = SDL_hid_open_path(it->path);
|
||||
if (!dev){
|
||||
cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, boost::nowide::narrow(hid_error(nullptr)));
|
||||
cemuLog_logDebug(LogType::Force, "Unable to open Wiimote device at {}: {}", it->path, SDL_GetError());
|
||||
}
|
||||
else {
|
||||
hid_set_nonblocking(dev, true);
|
||||
SDL_hid_set_nonblocking(dev, true);
|
||||
wiimote_devices.push_back(std::make_shared<HidapiWiimote>(dev, it->path));
|
||||
}
|
||||
}
|
||||
hid_free_enumeration(device_enumeration);
|
||||
SDL_hid_free_enumeration(device_enumeration);
|
||||
return wiimote_devices;
|
||||
}
|
||||
|
||||
@ -55,5 +56,5 @@ bool HidapiWiimote::operator==(const WiimoteDevice& rhs) const {
|
||||
}
|
||||
|
||||
HidapiWiimote::~HidapiWiimote() {
|
||||
hid_close(m_handle);
|
||||
SDL_hid_close(m_handle);
|
||||
}
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <api/Wiimote/WiimoteDevice.h>
|
||||
#include <hidapi.h>
|
||||
|
||||
class HidapiWiimote : public WiimoteDevice {
|
||||
public:
|
||||
HidapiWiimote(hid_device* dev, std::string_view path);
|
||||
HidapiWiimote(struct SDL_hid_device* dev, std::string_view path);
|
||||
~HidapiWiimote() override;
|
||||
|
||||
bool write_data(const std::vector<uint8> &data) override;
|
||||
@ -15,7 +14,7 @@ public:
|
||||
static std::vector<WiimoteDevicePtr> get_devices();
|
||||
|
||||
private:
|
||||
hid_device* m_handle;
|
||||
struct SDL_hid_device* m_handle;
|
||||
const std::string m_path;
|
||||
|
||||
};
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#include "input/emulated/ClassicController.h"
|
||||
|
||||
#include "input/api/Controller.h"
|
||||
#ifdef HAS_SDL
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#endif
|
||||
|
||||
ClassicController::ClassicController(size_t player_index)
|
||||
: WPADController(player_index, kDataFormat_CLASSIC)
|
||||
@ -130,6 +132,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr<ControllerBase
|
||||
std::vector<std::pair<uint64, uint64>> mapping;
|
||||
switch (controller->api())
|
||||
{
|
||||
#ifdef HAS_SDL
|
||||
case InputAPI::SDLController: {
|
||||
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
|
||||
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
|
||||
@ -206,6 +209,7 @@ bool ClassicController::set_default_mapping(const std::shared_ptr<ControllerBase
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
case InputAPI::XInput:
|
||||
{
|
||||
mapping =
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
#include "input/emulated/ProController.h"
|
||||
|
||||
#include "input/api/Controller.h"
|
||||
#ifdef HAS_SDL
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#endif
|
||||
|
||||
ProController::ProController(size_t player_index)
|
||||
: WPADController(player_index, kDataFormat_URCC)
|
||||
@ -135,6 +137,7 @@ bool ProController::set_default_mapping(const std::shared_ptr<ControllerBase>& c
|
||||
std::vector<std::pair<uint64, uint64>> mapping;
|
||||
switch (controller->api())
|
||||
{
|
||||
#ifdef HAS_SDL
|
||||
case InputAPI::SDLController: {
|
||||
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
|
||||
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
|
||||
@ -219,6 +222,7 @@ bool ProController::set_default_mapping(const std::shared_ptr<ControllerBase>& c
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case InputAPI::XInput:
|
||||
{
|
||||
mapping =
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#include "input/emulated/VPADController.h"
|
||||
#include "input/api/Controller.h"
|
||||
#ifdef HAS_SDL
|
||||
#include "input/api/SDL/SDLController.h"
|
||||
#endif
|
||||
#include "WindowSystem.h"
|
||||
#include "input/InputManager.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
@ -510,6 +512,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr<ControllerBase>&
|
||||
std::vector<std::pair<uint64, uint64>> mapping;
|
||||
switch (controller->api())
|
||||
{
|
||||
#ifdef HAS_SDL
|
||||
case InputAPI::SDLController: {
|
||||
const auto sdl_controller = std::static_pointer_cast<SDLController>(controller);
|
||||
if (sdl_controller->get_guid() == SDLController::kLeftJoyCon)
|
||||
@ -633,6 +636,7 @@ bool VPADController::set_default_mapping(const std::shared_ptr<ControllerBase>&
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case InputAPI::XInput:
|
||||
{
|
||||
mapping =
|
||||
|
||||
19
src/main.cpp
19
src/main.cpp
@ -19,7 +19,6 @@
|
||||
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
||||
|
||||
#include "Cafe/IOSU/legacy/iosu_crypto.h"
|
||||
#include "Cafe/OS/libs/vpad/vpad.h"
|
||||
@ -30,8 +29,11 @@
|
||||
#pragma comment(lib,"Dbghelp.lib")
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SDL
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <SDL3/SDL_main.h>
|
||||
#endif
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
#define _putenv(__s) putenv((char*)(__s))
|
||||
@ -67,7 +69,8 @@ void _putenvSafe(const char* c)
|
||||
|
||||
void reconfigureGLDrivers()
|
||||
{
|
||||
// reconfigure GL drivers to store
|
||||
#ifdef ENABLE_OPENGL
|
||||
// reconfigure GL drivers to store
|
||||
const fs::path nvCacheDir = ActiveSettings::GetCachePath("shaderCache/driver/nvidia/");
|
||||
|
||||
std::error_code err;
|
||||
@ -83,13 +86,15 @@ void reconfigureGLDrivers()
|
||||
_putenvSafe(nvCacheDirEnvOption.c_str());
|
||||
#endif
|
||||
_putenvSafe("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void reconfigureVkDrivers()
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
_putenvSafe("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1=1");
|
||||
_putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1");
|
||||
#endif
|
||||
}
|
||||
|
||||
void WindowsInitCwd()
|
||||
@ -234,7 +239,9 @@ int wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
||||
{
|
||||
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
|
||||
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
|
||||
#ifdef HAS_SDL
|
||||
SDL_SetMainReady();
|
||||
#endif
|
||||
if (!LaunchSettings::HandleCommandline(lpCmdLine))
|
||||
return 0;
|
||||
WindowSystem::Create();
|
||||
@ -246,7 +253,9 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
|
||||
cemuLog_log(LogType::Force, "CoInitializeEx() failed");
|
||||
#ifdef HAS_SDL
|
||||
SDL_SetMainReady();
|
||||
#endif
|
||||
if (!LaunchSettings::HandleCommandline(argc, argv))
|
||||
return 0;
|
||||
WindowSystem::Create();
|
||||
@ -258,7 +267,7 @@ int main(int argc, char* argv[])
|
||||
int BreathOfTheWildChildProcessMain();
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#if BOOST_OS_LINUX
|
||||
#if BOOST_OS_LINUX && defined(ENABLE_VULKAN)
|
||||
if (getenv("CEMU_DETECT_RADV") != nullptr)
|
||||
return BreathOfTheWildChildProcessMain();
|
||||
#endif
|
||||
|
||||
@ -1,40 +1,72 @@
|
||||
#include "Cemu/Logging/CemuLogging.h"
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#ifdef HAS_SDL
|
||||
#include <SDL3/SDL.h>
|
||||
#endif
|
||||
|
||||
class ScreenSaver
|
||||
{
|
||||
public:
|
||||
static void SetInhibit(bool inhibit)
|
||||
{
|
||||
// temporary workaround because feature crashes on macOS
|
||||
#if BOOST_OS_MACOS
|
||||
return;
|
||||
#ifdef HAS_SDL
|
||||
public:
|
||||
static void SetInhibit(bool inhibit)
|
||||
{
|
||||
bool* inhibitArg = new bool(inhibit);
|
||||
|
||||
if (!SDL_RunOnMainThread(SetInhibitCallback, inhibitArg, false))
|
||||
{
|
||||
delete inhibitArg;
|
||||
cemuLog_log(LogType::Force, "Failed to schedule screen saver logic on main thread: {}", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void SDLCALL SetInhibitCallback(void* userdata)
|
||||
{
|
||||
if (!userdata)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* inhibitArg = static_cast<bool*>(userdata);
|
||||
bool inhibit = *inhibitArg;
|
||||
delete inhibitArg;
|
||||
|
||||
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
||||
{
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO))
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error: {})", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (inhibit)
|
||||
{
|
||||
if (!SDL_DisableScreenSaver())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not disable screen saver: {}", SDL_GetError());
|
||||
}
|
||||
else if (SDL_ScreenSaverEnabled())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned true)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!SDL_EnableScreenSaver())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not enable screen saver: {}", SDL_GetError());
|
||||
}
|
||||
else if (!SDL_ScreenSaverEnabled())
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned false)");
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public:
|
||||
static void SetInhibit(bool /*inhibit*/)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
// Initialize video subsystem if necessary
|
||||
if (SDL_WasInit(SDL_INIT_VIDEO) == 0)
|
||||
{
|
||||
int initErr = SDL_InitSubSystem(SDL_INIT_VIDEO);
|
||||
if (initErr)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not disable screen saver (SDL video subsystem initialization error)");
|
||||
}
|
||||
}
|
||||
// Toggle SDL's screen saver inhibition
|
||||
if (inhibit)
|
||||
{
|
||||
SDL_DisableScreenSaver();
|
||||
if (SDL_IsScreenSaverEnabled() == SDL_TRUE)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not verify if screen saver was disabled (`SDL_IsScreenSaverEnabled()` returned SDL_TRUE)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_EnableScreenSaver();
|
||||
if (SDL_IsScreenSaverEnabled() == SDL_FALSE)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Could not verify if screen saver was re-enabled (`SDL_IsScreenSaverEnabled()` returned SDL_FALSE)");
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -62,7 +62,7 @@ public:
|
||||
m_condVar.wait(mlock);
|
||||
}
|
||||
|
||||
auto val = m_queue.front();
|
||||
auto val = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
return val;
|
||||
}
|
||||
@ -75,7 +75,7 @@ public:
|
||||
m_condVar.wait(mlock);
|
||||
}
|
||||
|
||||
item = m_queue.front();
|
||||
item = std::move(m_queue.front());
|
||||
m_queue.pop();
|
||||
}
|
||||
|
||||
|
||||
18
vcpkg.json
18
vcpkg.json
@ -11,7 +11,10 @@
|
||||
"default-features": false
|
||||
},
|
||||
"rapidjson",
|
||||
"sdl2",
|
||||
{
|
||||
"name": "sdl3",
|
||||
"default-features": false
|
||||
},
|
||||
"boost-tokenizer",
|
||||
"boost-container",
|
||||
"boost-program-options",
|
||||
@ -29,7 +32,6 @@
|
||||
"name": "fmt",
|
||||
"version>=": "12.1.0"
|
||||
},
|
||||
"hidapi",
|
||||
"libpng",
|
||||
"glm",
|
||||
{
|
||||
@ -37,7 +39,13 @@
|
||||
"default-features": false
|
||||
},
|
||||
"zstd",
|
||||
"wxwidgets",
|
||||
{
|
||||
"name": "wxwidgets",
|
||||
"default-features": false,
|
||||
"features": [
|
||||
"debug-support"
|
||||
]
|
||||
},
|
||||
"openssl",
|
||||
{
|
||||
"name": "curl",
|
||||
@ -67,8 +75,8 @@
|
||||
"version": "15.1.0"
|
||||
},
|
||||
{
|
||||
"name": "sdl2",
|
||||
"version": "2.32.10"
|
||||
"name": "sdl3",
|
||||
"version": "3.4.2"
|
||||
},
|
||||
{
|
||||
"name": "wxwidgets",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user