mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-04-26 04:45:18 -06:00
Merge 4eb92d20b2 into 6f6c1299e2
This commit is contained in:
commit
1d4675a4b5
2
BUILD.md
2
BUILD.md
@ -244,7 +244,7 @@ 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_VCPKG | | Use VCPKG package manager to obtain dependencies | ON | |
|
||||
|
||||
@ -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)
|
||||
@ -189,16 +191,18 @@ endif()
|
||||
|
||||
if (ENABLE_VULKAN)
|
||||
include_directories("dependencies/Vulkan-Headers/include")
|
||||
add_compile_definitions(ENABLE_VULKAN)
|
||||
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)
|
||||
|
||||
@ -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,7 +41,9 @@ 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)
|
||||
if (ENABLE_VULKAN)
|
||||
add_compile_definitions(VK_NO_PROTOTYPES)
|
||||
endif()
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
@ -119,29 +127,27 @@ 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(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/debug/lib/libusb-1.0.0.dylib")
|
||||
else()
|
||||
set(LIBUSB_PATH "${CMAKE_BINARY_DIR}/vcpkg_installed/${VCPKG_TARGET_TRIPLET}/lib/libusb-1.0.0.dylib")
|
||||
endif()
|
||||
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 (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")
|
||||
|
||||
add_custom_command(TARGET CemuBin POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${MOLTENVK_PATH}"
|
||||
"${FRAMEWORKS_DIR}/libMoltenVK.dylib"
|
||||
if (ENABLE_VULKAN)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${MOLTENVK_PATH}"
|
||||
"${FRAMEWORKS_DIR}/libMoltenVK.dylib"
|
||||
endif()
|
||||
COMMAND ${CMAKE_COMMAND} -E copy
|
||||
"${LIBUSB_PATH}"
|
||||
"${FRAMEWORKS_DIR}/libusb-1.0.0.dylib"
|
||||
@ -165,20 +171,22 @@ if (MACOS_BUNDLE)
|
||||
)
|
||||
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()
|
||||
|
||||
|
||||
@ -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
|
||||
@ -162,58 +159,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
|
||||
@ -539,12 +492,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
|
||||
|
||||
|
||||
@ -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,7 +6,9 @@
|
||||
#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"
|
||||
#endif
|
||||
#include "Cafe/OS/libs/gx2/GX2.h" // todo - remove dependency
|
||||
#include "Cafe/GraphicPack/GraphicPack2.h"
|
||||
#include "HW/Latte/Core/Latte.h"
|
||||
@ -15,7 +17,7 @@
|
||||
#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 +378,9 @@ void LatteShader_FinishCompilation(LatteDecompilerShader* shader)
|
||||
}
|
||||
shader->shader->WaitForCompiled();
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
LatteShader_prepareSeparableUniforms(shader);
|
||||
#endif
|
||||
LatteShader_CleanupAfterCompile(shader);
|
||||
}
|
||||
|
||||
@ -525,7 +529,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 +554,7 @@ void LatteSHRC_UpdateVSBaseHash(uint8* vertexShaderPtr, uint32 vertexShaderSize,
|
||||
vsHash += 51ULL;
|
||||
|
||||
// Vertex fetch
|
||||
if (_activeFetchShader->mtlFetchVertexManually)
|
||||
if (_activeFetchShader->mtlFetchVertexManually)
|
||||
vsHash += 349ULL;
|
||||
}
|
||||
}
|
||||
@ -650,7 +654,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,20 +687,69 @@ uint64 LatteSHRC_CalcPSAuxHash(LatteDecompilerShader* pixelShader, uint32* conte
|
||||
return auxHash;
|
||||
}
|
||||
|
||||
void InitUniformLayoutFromDecompiler(
|
||||
LatteDecompilerShader* shader,
|
||||
const LatteDecompilerOutput_t& decompilerOutput
|
||||
)
|
||||
{
|
||||
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())
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingGL;
|
||||
else if (g_renderer->GetType() == RendererAPI::Vulkan)
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingVK;
|
||||
#if ENABLE_METAL
|
||||
else
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingMTL;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingVK;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
shader->resourceMapping = decompilerOutput.resourceMappingMTL;
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
// copy texture info
|
||||
shader->textureUnitMask2 = decompilerOutput.textureUnitMask;
|
||||
// copy streamout info
|
||||
@ -705,33 +758,23 @@ LatteDecompilerShader* LatteShader_CreateShaderFromDecompilerOutput(LatteDecompi
|
||||
// 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
|
||||
switch (g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
shader->uniform.count_uniformRegister = decompilerOutput.uniformOffsetsGL.count_uniformRegister;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
InitUniformLayoutFromDecompiler(shader, decompilerOutput);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
InitUniformLayoutFromDecompiler(shader, decompilerOutput);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
// calculate aux hash
|
||||
if (calculateAuxHash)
|
||||
@ -766,10 +809,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 +890,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 +921,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 +952,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,38 @@ 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())
|
||||
{
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_mtlshaders.bin", cacheTitleId);
|
||||
else
|
||||
break;
|
||||
#endif
|
||||
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 +502,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 +667,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 +972,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/Renderer/Renderer.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;
|
||||
|
||||
@ -6,7 +6,9 @@
|
||||
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Core/LatteTexture.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/LatteTextureViewGL.h"
|
||||
#endif
|
||||
|
||||
#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,24 @@ 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);
|
||||
switch(g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
LatteDecompiler_emitGLSLShader(shaderContext, shaderContext->shader);
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
LatteDecompiler_emitMSLShader(shaderContext, shaderContext->shader);
|
||||
break;
|
||||
#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,9 @@
|
||||
#include "Cafe/HW/Latte/Renderer/RendererOuputShader.h"
|
||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
||||
#include "Cafe/HW/Latte/Core/Latte.h"
|
||||
#ifdef ENABLE_OPENGL
|
||||
#include "Cafe/HW/Latte/Renderer/OpenGL/OpenGLRenderer.h"
|
||||
#endif
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
const std::string RendererOutputShader::s_copy_shader_source =
|
||||
@ -246,10 +250,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 +495,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 +511,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 +530,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
|
||||
|
||||
@ -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"
|
||||
@ -107,9 +109,12 @@ bool ActiveSettings::WaitForGX2DrawDoneEnabled()
|
||||
GraphicAPI ActiveSettings::GetGraphicsAPI()
|
||||
{
|
||||
GraphicAPI api = g_current_game_profile->GetGraphicsAPI().value_or(GetConfig().graphic_api);
|
||||
|
||||
#if defined(ENABLE_VULKAN) && defined(ENABLE_OPENGL)
|
||||
// check if vulkan even available
|
||||
if (api == kVulkan && !g_vulkan_available)
|
||||
api = kOpenGL;
|
||||
#endif
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -65,6 +65,7 @@ struct GraphicPackEntry
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
#define GRAPHIC_API_COUNT 3
|
||||
enum GraphicAPI
|
||||
{
|
||||
kOpenGL = 0,
|
||||
@ -429,7 +430,13 @@ struct CemuConfig
|
||||
ConfigValueBounds<CafeConsoleLanguage> console_language{ CafeConsoleLanguage::EN };
|
||||
|
||||
// graphics
|
||||
#if defined(ENABLE_VULKAN)
|
||||
ConfigValue<GraphicAPI> graphic_api{ kVulkan };
|
||||
#elif defined(ENABLE_METAL)
|
||||
ConfigValue<GraphicAPI> graphic_api{ kMetal };
|
||||
#elif defined(ENABLE_OPENGL)
|
||||
ConfigValue<GraphicAPI> graphic_api{ kOpenGL };
|
||||
#endif
|
||||
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 +444,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 +510,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
|
||||
|
||||
@ -3,7 +3,9 @@
|
||||
#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"
|
||||
@ -349,7 +351,9 @@ bool CemuApp::OnInit()
|
||||
__fastfail(0);
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
InitializeGlobalVulkan();
|
||||
#endif
|
||||
|
||||
Bind(wxEVT_ACTIVATE_APP, &CemuApp::ActivateApp, this);
|
||||
|
||||
|
||||
@ -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[GRAPHIC_API_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
|
||||
|
||||
@ -11,9 +11,13 @@
|
||||
#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
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
#include "wxgui/canvas/MetalCanvas.h"
|
||||
#endif
|
||||
#include "Cafe/OS/libs/nfc/nfc.h"
|
||||
@ -42,7 +46,9 @@
|
||||
#include "wxgui/DownloadGraphicPacksWindow.h"
|
||||
#include "wxgui/GettingStartedDialog.h"
|
||||
#include "wxgui/helpers/wxHelpers.h"
|
||||
#ifdef ENABLE_VULKAN
|
||||
#include "Cafe/HW/Latte/Renderer/Vulkan/VsyncDriver.h"
|
||||
#endif
|
||||
#include "wxgui/input/InputSettings2.h"
|
||||
#include "wxgui/input/HotkeySettings.h"
|
||||
#include "input/InputManager.h"
|
||||
@ -66,7 +72,7 @@
|
||||
#include "gamemode_client.h"
|
||||
#endif
|
||||
|
||||
#if ENABLE_METAL
|
||||
#ifdef ENABLE_METAL
|
||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||
#endif
|
||||
|
||||
@ -1037,7 +1043,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);
|
||||
@ -1601,14 +1607,29 @@ 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)
|
||||
switch (ActiveSettings::GetGraphicsAPI())
|
||||
{
|
||||
case kOpenGL:
|
||||
#ifdef ENABLE_OPENGL
|
||||
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);
|
||||
break;
|
||||
#endif
|
||||
case kVulkan:
|
||||
#ifdef ENABLE_VULKAN
|
||||
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
break;
|
||||
#endif
|
||||
case kMetal:
|
||||
#ifdef ENABLE_METAL
|
||||
m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
cemu_assert(false && "Invalid graphics API selected");
|
||||
break;
|
||||
}
|
||||
|
||||
cemu_assert(m_render_canvas != nullptr);
|
||||
|
||||
// mouse events
|
||||
m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this);
|
||||
@ -1670,7 +1691,9 @@ void MainWindow::OnSizeEvent(wxSizeEvent& event)
|
||||
|
||||
event.Skip();
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
VsyncDriver_notifyWindowPosChanged();
|
||||
#endif
|
||||
}
|
||||
|
||||
void MainWindow::OnDPIChangedEvent(wxDPIChangedEvent& event)
|
||||
@ -1691,7 +1714,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)
|
||||
@ -2345,7 +2370,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"
|
||||
@ -76,15 +80,24 @@ void PadViewFrame::InitializeRenderCanvas()
|
||||
auto sizer = new wxBoxSizer(wxVERTICAL);
|
||||
{
|
||||
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
|
||||
{
|
||||
#ifdef ENABLE_VULKAN
|
||||
m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false);
|
||||
#endif
|
||||
}
|
||||
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false);
|
||||
#if ENABLE_METAL
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_METAL
|
||||
else
|
||||
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();
|
||||
|
||||
|
||||
@ -95,13 +95,17 @@ void WindowSystem::UpdateWindowTitles(bool isIdle, bool isLoading, double fps)
|
||||
{
|
||||
switch (g_renderer->GetType())
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
case RendererAPI::OpenGL:
|
||||
renderer = "[OpenGL]";
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
case RendererAPI::Vulkan:
|
||||
renderer = "[Vulkan]";
|
||||
break;
|
||||
#if ENABLE_METAL
|
||||
#endif
|
||||
#ifdef ENABLE_METAL
|
||||
case RendererAPI::Metal:
|
||||
renderer = "[Metal]";
|
||||
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>
|
||||
|
||||
11
src/main.cpp
11
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"
|
||||
@ -65,6 +64,7 @@ void _putenvSafe(const char* c)
|
||||
_putenv(s->c_str());
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL
|
||||
void reconfigureGLDrivers()
|
||||
{
|
||||
// reconfigure GL drivers to store
|
||||
@ -85,12 +85,15 @@ void reconfigureGLDrivers()
|
||||
_putenvSafe("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1");
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_VULKAN
|
||||
void reconfigureVkDrivers()
|
||||
{
|
||||
_putenvSafe("DISABLE_LAYER_AMD_SWITCHABLE_GRAPHICS_1=1");
|
||||
_putenvSafe("DISABLE_VK_LAYER_VALVE_steam_fossilize_1=1");
|
||||
}
|
||||
#endif
|
||||
|
||||
void WindowsInitCwd()
|
||||
{
|
||||
@ -109,8 +112,12 @@ void WindowsInitCwd()
|
||||
|
||||
void CemuCommonInit()
|
||||
{
|
||||
#ifdef ENABLE_OPENGL
|
||||
reconfigureGLDrivers();
|
||||
#endif
|
||||
#ifdef ENABLE_VULKAN
|
||||
reconfigureVkDrivers();
|
||||
#endif
|
||||
// crypto init
|
||||
AES128_init();
|
||||
// init PPC timer
|
||||
@ -258,7 +265,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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user