start metal backend

This commit is contained in:
Samuliak 2024-07-25 11:18:35 +02:00
parent 4b9c7c0d30
commit 2477bad06b
19 changed files with 586 additions and 129 deletions

View File

@ -78,6 +78,7 @@ endif()
option(ENABLE_OPENGL "Enables the OpenGL backend" ON)
option(ENABLE_VULKAN "Enables the Vulkan backend" ON)
option(ENABLE_METAL "Enables the Metal backend" ON)
option(ENABLE_DISCORD_RPC "Enables the Discord Rich Presence feature" ON)
@ -190,9 +191,9 @@ if (ENABLE_WXWIDGETS)
endif()
if (ENABLE_CUBEB)
if (NOT ENABLE_VCPKG)
find_package(cubeb)
endif()
#if (NOT ENABLE_VCPKG)
#find_package(cubeb)
#endif()
if (NOT cubeb_FOUND)
option(BUILD_TESTS "" OFF)
option(BUILD_TOOLS "" OFF)

View File

@ -524,6 +524,16 @@ if(APPLE)
target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm")
endif()
if(ENABLE_METAL)
if(APPLE)
target_sources(CemuCafe PRIVATE
HW/Latte/Renderer/Metal/MetalRenderer.cpp
)
else()
message(FATAL_ERROR "Metal is only supported on macOS")
endif()
endif()
set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(CemuCafe PUBLIC "../")

View File

@ -109,7 +109,7 @@ bool GraphicPack2::LoadGraphicPack(const fs::path& rulesPath, IniParser& rules)
gp->SetActivePreset(kv.first, kv.second, false);
}
gp->SetEnabled(enabled);
}
@ -141,7 +141,7 @@ bool GraphicPack2::DeactivateGraphicPack(const std::shared_ptr<GraphicPack2>& gr
if (!graphic_pack->IsActivated())
return false;
const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(),
const auto it = std::find_if(s_active_graphic_packs.begin(), s_active_graphic_packs.end(),
[graphic_pack](const GraphicPackPtr& gp)
{
return gp->GetNormalizedPathString() == graphic_pack->GetNormalizedPathString();
@ -269,6 +269,8 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
m_renderer_api = RendererAPI::Vulkan;
else if (boost::iequals(*option_rendererFilter, "opengl"))
m_renderer_api = RendererAPI::OpenGL;
else if (boost::iequals(*option_rendererFilter, "metal"))
m_renderer_api = RendererAPI::Metal;
else
cemuLog_log(LogType::Force, "Unknown value '{}' for rendererFilter option", *option_rendererFilter);
}
@ -348,7 +350,7 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
cemuLog_log(LogType::Force, "Graphic pack \"{}\": Preset in line {} skipped because it has no name option defined", m_name, rules.GetCurrentSectionLineNumber());
continue;
}
const auto category = rules.FindOption("category");
const auto condition = rules.FindOption("condition");
const auto default_selected = rules.FindOption("default");
@ -420,12 +422,12 @@ GraphicPack2::GraphicPack2(fs::path rulesPath, IniParser& rules)
{
// store by category
std::unordered_map<std::string, std::vector<PresetPtr>> tmp_map;
// all vars must be defined in the default preset vars before
for (const auto& entry : m_presets)
{
tmp_map[entry->category].emplace_back(entry);
for (auto& kv : entry->variables)
{
const auto it = m_preset_vars.find(kv.first);
@ -560,7 +562,7 @@ void GraphicPack2::ValidatePresetSelections()
//
// example: a preset category might be hidden entirely (e.g. due to a separate advanced options dropdown)
// how to handle: leave the previously selected preset
//
//
// the logic is therefore as follows:
// if there is a preset category with at least 1 visible preset entry then make sure one of those is actually selected
// for completely hidden preset categories we leave the selection as-is
@ -624,17 +626,17 @@ bool GraphicPack2::SetActivePreset(std::string_view category, std::string_view n
// disable currently active preset
std::for_each(m_presets.begin(), m_presets.end(), [category](PresetPtr& p)
{
if(p->category == category)
if(p->category == category)
p->active = false;
});
if (name.empty())
return true;
// enable new preset
const auto it = std::find_if(m_presets.cbegin(), m_presets.cend(), [category, name](const PresetPtr& preset)
{
return preset->category == category && preset->name == name;
return preset->category == category && preset->name == name;
});
bool result;
@ -775,7 +777,7 @@ std::optional<GraphicPack2::PresetVar> GraphicPack2::GetPresetVariable(const std
return it->second;
}
}
for (const auto& preset : presets)
{
if (!preset->visible)
@ -785,7 +787,7 @@ std::optional<GraphicPack2::PresetVar> GraphicPack2::GetPresetVariable(const std
return it->second;
}
}
const auto it = std::find_if(m_preset_vars.cbegin(), m_preset_vars.cend(), [&var_name](auto p) { return p.first == var_name; });
if (it != m_preset_vars.cend())
{
@ -831,7 +833,7 @@ void GraphicPack2::_iterateReplacedFiles(const fs::path& currentPath, bool isAOC
virtualMountPath = fs::path("vol/content/") / virtualMountPath;
}
fscDeviceRedirect_add(virtualMountPath.generic_string(), it.path().generic_string(), m_fs_priority);
}
}
}
}
@ -851,7 +853,7 @@ void GraphicPack2::LoadReplacedFiles()
std::error_code ec;
if (fs::exists(contentPath, ec))
{
// setup redirections
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(contentPath, false);
}
@ -864,7 +866,7 @@ void GraphicPack2::LoadReplacedFiles()
uint64 aocTitleId = CafeSystem::GetForegroundTitleId();
aocTitleId = aocTitleId & 0xFFFFFFFFULL;
aocTitleId |= 0x0005000c00000000ULL;
// setup redirections
// setup redirections
fscDeviceRedirect_map();
_iterateReplacedFiles(aocPath, true);
}
@ -980,7 +982,7 @@ bool GraphicPack2::Activate()
// enable patch groups
EnablePatches();
// load replaced files
LoadReplacedFiles();
@ -1026,7 +1028,7 @@ bool GraphicPack2::Deactivate()
m_output_shader_source.clear();
m_upscaling_shader_source.clear();
m_downscaling_shader_source.clear();
if (HasCustomVSyncFrequency())
{
m_vsync_frequency = -1;
@ -1058,7 +1060,7 @@ const std::string* GraphicPack2::FindCustomShaderSource(uint64 shaderBaseHash, u
std::unordered_map<std::string, std::vector<GraphicPack2::PresetPtr>> GraphicPack2::GetCategorizedPresets(std::vector<std::string>& order) const
{
order.clear();
std::unordered_map<std::string, std::vector<PresetPtr>> result;
for(const auto& entry : m_presets)
{
@ -1067,13 +1069,13 @@ std::unordered_map<std::string, std::vector<GraphicPack2::PresetPtr>> GraphicPac
if (it == order.cend())
order.emplace_back(entry->category);
}
return result;
}
bool GraphicPack2::HasShaders() const
{
return !GetCustomShaders().empty()
return !GetCustomShaders().empty()
|| !m_output_shader_source.empty() || !m_upscaling_shader_source.empty() || !m_downscaling_shader_source.empty();
}

View File

@ -0,0 +1,198 @@
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow) {
/*
const auto& windowInfo = gui_getWindowInfo().window_main;
NSView* view = (NS::View*)handle;
MetalView* childView = [[MetalView alloc] initWithFrame:view.bounds];
childView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
childView.wantsLayer = YES;
[view addSubview:childView];
VkMetalSurfaceCreateInfoEXT surface;
surface.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
surface.pNext = NULL;
surface.flags = 0;
surface.pLayer = (CAMetalLayer*)childView.layer;
*/
}
void MetalRenderer::Initialize() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::Shutdown() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
bool MetalRenderer::IsPadWindowActive() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
bool MetalRenderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::ClearColorbuffer(bool padView) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::DrawEmptyFrame(bool mainWindow) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::SwapBuffers(bool swapTV, bool swapDRC) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter,
sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight,
bool padView, bool clearBackground) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
bool MetalRenderer::BeginFrame(bool mainWindow) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::Flush(bool waitIdle) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::NotifyLatteCommandProcessorIdle() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::AppendOverlayDebugInfo() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::renderTarget_setViewport(float x, float y, float width, float height, float nearZ, float farZ, bool halfZ) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::renderTarget_setScissor(sint32 scissorX, sint32 scissorY, sint32 scissorWidth, sint32 scissorHeight) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
LatteCachedFBO* MetalRenderer::rendertarget_createCachedFBO(uint64 key) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::rendertarget_deleteCachedFBO(LatteCachedFBO* fbo) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::rendertarget_bindFramebufferObject(LatteCachedFBO* cfbo) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void* MetalRenderer::texture_acquireTextureUploadBuffer(uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_releaseTextureUploadBuffer(uint8* mem) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
TextureDecoder* MetalRenderer::texture_chooseDecodedFormat(Latte::E_GX2SURFFMT format, bool isDepth, Latte::E_DIM dim, uint32 width, uint32 height) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_clearSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 compressedImageSize) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_clearColorSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sliceIndex, sint32 mipIndex, bool clearDepth, bool clearStencil, float depthValue, uint32 stencilValue) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
LatteTexture* MetalRenderer::texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView* textureView) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::bufferCache_init(const sint32 bufferSize) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::bufferCache_upload(uint8* buffer, sint32 size, uint32 bufferOffset) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::bufferCache_copy(uint32 srcOffset, uint32 dstOffset, uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::bufferCache_copyStreamoutToMainBuffer(uint32 srcOffset, uint32 dstOffset, uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
RendererShader* MetalRenderer::shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool compileAsync, bool isGfxPackSource) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::streamout_begin() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::streamout_rendererFinishDrawcall() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::draw_beginSequence() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count, MPTR indexDataMPTR, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE indexType, bool isFirst) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::draw_endSequence() {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void* MetalRenderer::indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex) {
cemuLog_logDebug(LogType::Force, "not implemented");
}
void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size) {
cemuLog_logDebug(LogType::Force, "not implemented");
}

View File

@ -0,0 +1,142 @@
#pragma once
#include "Cafe/HW/Latte/Renderer/Renderer.h"
class MetalRenderer : public Renderer
{
public:
~MetalRenderer() = default;
RendererAPI GetType() override
{
return RendererAPI::Metal;
}
static MetalRenderer* GetInstance() {
return static_cast<MetalRenderer*>(g_renderer.get());
}
void InitializeLayer(const Vector2i& size, bool mainWindow);
void Initialize() override;
void Shutdown() override;
bool IsPadWindowActive() override;
bool GetVRAMInfo(int& usageInMB, int& totalInMB) const override;
void ClearColorbuffer(bool padView) override;
void DrawEmptyFrame(bool mainWindow) override;
void SwapBuffers(bool swapTV, bool swapDRC) override;
void HandleScreenshotRequest(LatteTextureView* texView, bool padView) override {
cemuLog_logDebug(LogType::Force, "Screenshots are not yet supported on Metal");
}
void DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter,
sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight,
bool padView, bool clearBackground) override;
bool BeginFrame(bool mainWindow) override;
// flush control
void Flush(bool waitIdle = false) override; // called when explicit flush is required (e.g. by imgui)
void NotifyLatteCommandProcessorIdle() override; // called when command processor has no more commands available or when stalled
// imgui
bool ImguiBegin(bool mainWindow) override {
cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal");
};
void ImguiEnd() override {
cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal");
};
ImTextureID GenerateTexture(const std::vector<uint8>& data, const Vector2i& size) override {
cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal");
};
void DeleteTexture(ImTextureID id) override {
cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal");
};
void DeleteFontTextures() override {
cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal");
};
void AppendOverlayDebugInfo() override;
// rendertarget
void renderTarget_setViewport(float x, float y, float width, float height, float nearZ, float farZ, bool halfZ = false) override;
void renderTarget_setScissor(sint32 scissorX, sint32 scissorY, sint32 scissorWidth, sint32 scissorHeight) override;
LatteCachedFBO* rendertarget_createCachedFBO(uint64 key) override;
void rendertarget_deleteCachedFBO(LatteCachedFBO* fbo) override;
void rendertarget_bindFramebufferObject(LatteCachedFBO* cfbo) override;
// texture functions
void* texture_acquireTextureUploadBuffer(uint32 size) override;
void texture_releaseTextureUploadBuffer(uint8* mem) override;
TextureDecoder* texture_chooseDecodedFormat(Latte::E_GX2SURFFMT format, bool isDepth, Latte::E_DIM dim, uint32 width, uint32 height) override;
void texture_clearSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex) override;
void texture_loadSlice(LatteTexture* hostTexture, sint32 width, sint32 height, sint32 depth, void* pixelData, sint32 sliceIndex, sint32 mipIndex, uint32 compressedImageSize) override;
void texture_clearColorSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex, float r, float g, float b, float a) override;
void texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sliceIndex, sint32 mipIndex, bool clearDepth, bool clearStencil, float depthValue, uint32 stencilValue) override;
LatteTexture* texture_createTextureEx(Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) override;
void texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) override;
void texture_copyImageSubData(LatteTexture* src, sint32 srcMip, sint32 effectiveSrcX, sint32 effectiveSrcY, sint32 srcSlice, LatteTexture* dst, sint32 dstMip, sint32 effectiveDstX, sint32 effectiveDstY, sint32 dstSlice, sint32 effectiveCopyWidth, sint32 effectiveCopyHeight, sint32 srcDepth) override;
LatteTextureReadbackInfo* texture_createReadback(LatteTextureView* textureView) override;
// surface copy
void surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height) override;
// buffer cache
void bufferCache_init(const sint32 bufferSize) override;
void bufferCache_upload(uint8* buffer, sint32 size, uint32 bufferOffset) override;
void bufferCache_copy(uint32 srcOffset, uint32 dstOffset, uint32 size) override;
void bufferCache_copyStreamoutToMainBuffer(uint32 srcOffset, uint32 dstOffset, uint32 size) override;
void buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, uint32 size) override;
void buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size) override;
// shader
RendererShader* shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool compileAsync, bool isGfxPackSource) override;
// streamout
void streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) override;
void streamout_begin() override;
void streamout_rendererFinishDrawcall() override;
// core drawing logic
void draw_beginSequence() override;
void draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 instanceCount, uint32 count, MPTR indexDataMPTR, Latte::LATTE_VGT_DMA_INDEX_TYPE::E_INDEX_TYPE indexType, bool isFirst) override;
void draw_endSequence() override;
// index
void* indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex) override;
void indexData_uploadIndexMemory(uint32 offset, uint32 size) override;
// occlusion queries
LatteQueryObject* occlusionQuery_create() override {
cemuLog_logDebug(LogType::Force, "Occlusion queries are not yet supported on Metal");
}
void occlusionQuery_destroy(LatteQueryObject* queryObj) override {
cemuLog_logDebug(LogType::Force, "Occlusion queries are not yet supported on Metal");
}
void occlusionQuery_flush() override {
cemuLog_logDebug(LogType::Force, "Occlusion queries are not yet supported on Metal");
}
void occlusionQuery_updateState() override {
cemuLog_logDebug(LogType::Force, "Occlusion queries are not yet supported on Metal");
}
protected:
//CA::MetalLayer* m_metalLayer;
};

View File

@ -33,6 +33,7 @@ enum class RendererAPI
{
OpenGL,
Vulkan,
Metal,
MAX
};
@ -66,9 +67,9 @@ public:
virtual void SwapBuffers(bool swapTV, bool swapDRC) = 0;
virtual void HandleScreenshotRequest(LatteTextureView* texView, bool padView){}
virtual void DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter,
sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight,
virtual void DrawBackbufferQuad(LatteTextureView* texView, RendererOutputShader* shader, bool useLinearTexFilter,
sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight,
bool padView, bool clearBackground) = 0;
virtual bool BeginFrame(bool mainWindow) = 0;

View File

@ -59,6 +59,7 @@ const std::map<LogType, std::string> g_logging_window_mapping
{LogType::TextureReadback, "Texture readback"},
{LogType::OpenGLLogging, "OpenGL debug output"},
{LogType::VulkanValidation, "Vulkan validation layer"},
{LogType::MetalLogging, "Metal debug output"},
};
bool cemuLog_advancedPPCLoggingEnabled()
@ -158,7 +159,7 @@ bool cemuLog_log(LogType type, std::string_view text)
bool cemuLog_log(LogType type, std::u8string_view text)
{
std::basic_string_view<char> s((char*)text.data(), text.size());
std::basic_string_view<char> s((char*)text.data(), text.size());
return cemuLog_log(type, s);
}

View File

@ -20,6 +20,7 @@ enum class LogType : sint32
OpenGLLogging = 10, // OpenGL debug logging
TextureCache = 11, // texture cache warnings and info
VulkanValidation = 12, // Vulkan validation layer
MetalLogging = 13, // Metal debug logging
Patches = 14,
CoreinitMem = 8, // coreinit memory functions
CoreinitMP = 15,
@ -52,7 +53,7 @@ enum class LogType : sint32
template <>
struct fmt::formatter<std::u8string_view> : formatter<string_view> {
template <typename FormatContext>
auto format(std::u8string_view v, FormatContext& ctx)
auto format(std::u8string_view v, FormatContext& ctx)
{
string_view s((char*)v.data(), v.size());
return formatter<string_view>::format(s, ctx);
@ -96,7 +97,7 @@ bool cemuLog_log(LogType type, std::basic_string<T> formatStr, TArgs&&... args)
}
return true;
}
template<typename T, typename ... TArgs>
bool cemuLog_log(LogType type, const T* format, TArgs&&... args)
{

View File

@ -32,7 +32,7 @@ struct GameEntry
std::wstring save_folder;
std::wstring update_folder;
std::wstring dlc_folder;
uint64 legacy_time_played = 0;
uint64 legacy_last_played = 0;
@ -74,6 +74,7 @@ enum GraphicAPI
{
kOpenGL = 0,
kVulkan,
kMetal,
};
enum AudioChannels
@ -105,7 +106,7 @@ enum class ScreenPosition
kTopRight,
kBottomLeft,
kBottomCenter,
kBottomRight,
kBottomRight,
};
enum class PrecompiledShaderOption
@ -134,7 +135,7 @@ enum class CPUMode
ENABLE_ENUM_ITERATORS(CPUMode, CPUMode::SinglecoreInterpreter, CPUMode::Auto);
enum class CPUModeLegacy
enum class CPUModeLegacy
{
SinglecoreInterpreter = 0,
SinglecoreRecompiler = 1,
@ -270,7 +271,7 @@ struct fmt::formatter<CafeConsoleRegion> : formatter<string_view> {
case CafeConsoleRegion::TWN: name = wxTRANSLATE("Taiwan"); break;
case CafeConsoleRegion::Auto: name = wxTRANSLATE("Auto"); break;
default: name = wxTRANSLATE("many"); break;
}
return formatter<string_view>::format(name, ctx);
}
@ -312,7 +313,7 @@ struct fmt::formatter<CrashDump> : formatter<string_view> {
case CrashDump::Lite: name = "Lite"; break;
case CrashDump::Full: name = "Full"; break;
default: name = "unknown"; break;
}
return formatter<string_view>::format(name, ctx);
}
@ -363,7 +364,7 @@ struct CemuConfig
ConfigValue<bool> advanced_ppc_logging{ false };
ConfigValue<bool> permanent_storage{ true };
ConfigValue<sint32> language{ wxLANGUAGE_DEFAULT };
ConfigValue<bool> use_discord_presence{ true };
ConfigValue<std::string> mlc_path{};
@ -387,7 +388,7 @@ struct CemuConfig
// optimized access
std::set<uint64> game_cache_favorites; // per titleId
struct _path_hash {
std::size_t operator()(const fs::path& path) const {
return fs::hash_value(path);
@ -514,7 +515,7 @@ struct CemuConfig
NetworkService GetAccountNetworkService(uint32 persistentId);
void SetAccountSelectedService(uint32 persistentId, NetworkService serviceIndex);
// emulated usb devices
struct
{
@ -530,5 +531,3 @@ struct CemuConfig
typedef XMLDataConfig<CemuConfig, &CemuConfig::Load, &CemuConfig::Save> XMLCemuConfig_t;
extern XMLCemuConfig_t g_config;
inline CemuConfig& GetConfig() { return g_config.data(); }

View File

@ -1,9 +1,11 @@
add_library(CemuGui
add_library(CemuGui
canvas/IRenderCanvas.h
canvas/OpenGLCanvas.cpp
canvas/OpenGLCanvas.h
canvas/VulkanCanvas.cpp
canvas/VulkanCanvas.h
canvas/MetalCanvas.cpp
canvas/MetalCanvas.h
CemuApp.cpp
CemuApp.h
CemuUpdateWindow.cpp

View File

@ -61,7 +61,7 @@ GameProfileWindow::GameProfileWindow(wxWindow* parent, uint64_t title_id)
const sint32 m_cpu_modeNChoices = std::size(cpu_modes);
m_cpu_mode = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cpu_modeNChoices, cpu_modes, 0);
m_cpu_mode->SetToolTip(_("Set the CPU emulation mode"));
first_row->Add(m_cpu_mode, 0, wxALL, 5);
first_row->Add(m_cpu_mode, 0, wxALL, 5);
first_row->Add(new wxStaticText(box, wxID_ANY, _("Thread quantum")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
@ -112,10 +112,14 @@ 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" };
wxString gapi_values[] = { "", "OpenGL", "Vulkan",
#ifdef __APPLE__
"Metal"
#endif
};
m_graphic_api = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, (int)std::size(gapi_values), gapi_values);
first_row->Add(m_graphic_api, 0, wxALL, 5);
first_row->Add(new wxStaticText(panel, wxID_ANY, _("Shader multiplication accuracy")), 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
wxString mul_values[] = { _("false"), _("true")};
@ -249,7 +253,7 @@ void GameProfileWindow::ApplyProfile()
// general
m_load_libs->SetValue(m_game_profile.m_loadSharedLibraries.value());
m_start_with_padview->SetValue(m_game_profile.m_startWithPadView);
// cpu
// wxString cpu_modes[] = { _("Singlecore-Interpreter"), _("Singlecore-Recompiler"), _("Triplecore-Recompiler"), _("Auto (recommended)") };
switch(m_game_profile.m_cpuMode.value())
@ -258,9 +262,9 @@ void GameProfileWindow::ApplyProfile()
case CPUMode::SinglecoreRecompiler: m_cpu_mode->SetSelection(1); break;
case CPUMode::DualcoreRecompiler: m_cpu_mode->SetSelection(2); break;
case CPUMode::MulticoreRecompiler: m_cpu_mode->SetSelection(2); break;
default: m_cpu_mode->SetSelection(3);
default: m_cpu_mode->SetSelection(3);
}
m_thread_quantum->SetStringSelection(fmt::format("{}", m_game_profile.m_threadQuantum));
// gpu
@ -275,7 +279,7 @@ void GameProfileWindow::ApplyProfile()
// controller
auto profiles = InputManager::get_profiles();
for (const auto& cb : m_controller_profile)
{
cb->Clear();
@ -293,7 +297,7 @@ void GameProfileWindow::ApplyProfile()
const auto& v = m_game_profile.m_controllerProfile[i].value();
m_controller_profile[i]->SetStringSelection(wxString::FromUTF8(v));
}
else
m_controller_profile[i]->SetSelection(wxNOT_FOUND);
}
@ -317,7 +321,7 @@ void GameProfileWindow::SaveProfile()
m_game_profile.m_cpuMode = CPUMode::Auto;
}
const wxString thread_quantum = m_thread_quantum->GetStringSelection();
if (!thread_quantum.empty())
{
@ -365,4 +369,4 @@ void GameProfileWindow::SetSliderValue(wxSlider* slider, sint32 new_value) const
slider_event.SetEventObject(slider);
slider_event.SetClientData((void*)IsFrozen());
wxPostEvent(slider->GetEventHandler(), slider_event);
}
}

View File

@ -101,7 +101,7 @@ public:
Account& GetAccount() { return m_account; }
const Account& GetAccount() const { return m_account; }
private:
Account m_account;
};
@ -165,11 +165,11 @@ wxPanel* GeneralSettings2::AddGeneralPage(wxNotebook* notebook)
m_auto_update = new wxCheckBox(box, wxID_ANY, _("Automatically check for updates"));
m_auto_update->SetToolTip(_("Automatically checks for new cemu versions on startup"));
second_row->Add(m_auto_update, 0, botflag, 5);
#if BOOST_OS_LINUX
#if BOOST_OS_LINUX
if (!std::getenv("APPIMAGE")) {
m_auto_update->Disable();
}
#endif
}
#endif
second_row->AddSpacer(10);
m_save_screenshot = new wxCheckBox(box, wxID_ANY, _("Save screenshot"));
m_save_screenshot->SetToolTip(_("Pressing the screenshot key (F12) will save a screenshot directly to the screenshots folder"));
@ -276,12 +276,14 @@ 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[2] = { "OpenGL" };
wxString choices[3] = { "OpenGL" };
if (g_vulkan_available)
{
choices[1] = "Vulkan";
api_size = 2;
choices[api_size++] = "Vulkan";
}
#ifdef __APPLE__
choices[api_size++] = "Metal";
#endif
m_graphic_api = new wxChoice(box, wxID_ANY, wxDefaultPosition, wxDefaultSize, api_size, choices);
m_graphic_api->SetSelection(0);
@ -728,7 +730,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
auto* row = new wxFlexGridSizer(0, 2, 0, 0);
row->SetFlexibleDirection(wxBOTH);
row->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
const wxImage tmp = wxBITMAP_PNG_FROM_DATA(PNG_ERROR).ConvertToImage();
m_validate_online = new wxBitmapButton(box, wxID_ANY, tmp.Scale(16, 16));
m_validate_online->Bind(wxEVT_BUTTON, &GeneralSettings2::OnShowOnlineValidator, this);
@ -738,7 +740,7 @@ wxPanel* GeneralSettings2::AddAccountPage(wxNotebook* notebook)
row->Add(m_online_status, 1, wxALL | wxALIGN_CENTRE_VERTICAL, 5);
box_sizer->Add(row, 1, wxEXPAND, 5);
auto* tutorial_link = new wxHyperlinkCtrl(box, wxID_ANY, _("Online play tutorial"), "https://cemu.info/online-guide");
box_sizer->Add(tutorial_link, 0, wxALL, 5);
@ -856,14 +858,14 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
notebook->AddPage(AddGeneralPage(notebook), _("General"));
notebook->AddPage(AddGraphicsPage(notebook), _("Graphics"));
notebook->AddPage(AddAudioPage(notebook), _("Audio"));
notebook->AddPage(AddAudioPage(notebook), _("Audio"));
notebook->AddPage(AddOverlayPage(notebook), _("Overlay"));
notebook->AddPage(AddAccountPage(notebook), _("Account"));
notebook->AddPage(AddDebugPage(notebook), _("Debug"));
Bind(wxEVT_CLOSE_WINDOW, &GeneralSettings2::OnClose, this);
//
//
sizer->Add(notebook, 1, wxEXPAND | wxALL, 5);
@ -878,7 +880,7 @@ GeneralSettings2::GeneralSettings2(wxWindow* parent, bool game_launched)
ApplyConfig();
HandleGraphicsApiSelection();
DisableSettings(game_launched);
}
@ -890,7 +892,7 @@ uint32 GeneralSettings2::GetSelectedAccountPersistentId()
return dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(active_account))->GetAccount().GetPersistentId();
}
void GeneralSettings2::StoreConfig()
void GeneralSettings2::StoreConfig()
{
auto* app = (CemuApp*)wxTheApp;
auto& config = GetConfig();
@ -908,7 +910,7 @@ void GeneralSettings2::StoreConfig()
{
ScreenSaver::SetInhibit(config.disable_screensaver);
}
// -1 is default wx widget value -> set to dummy 0 so mainwindow and padwindow will update it
config.window_position = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
config.window_size = m_save_window_position_size->IsChecked() ? Vector2i{ 0,0 } : Vector2i{-1,-1};
@ -951,7 +953,7 @@ void GeneralSettings2::StoreConfig()
config.pad_channels = kStereo; // (AudioChannels)m_pad_channels->GetSelection();
//config.input_channels = (AudioChannels)m_input_channels->GetSelection();
config.input_channels = kMono; // (AudioChannels)m_input_channels->GetSelection();
config.tv_volume = m_tv_volume->GetValue();
config.pad_volume = m_pad_volume->GetValue();
config.input_volume = m_input_volume->GetValue();
@ -997,16 +999,16 @@ void GeneralSettings2::StoreConfig()
}
else
config.graphic_device_uuid = {};
config.vsync = m_vsync->GetSelection();
config.gx2drawdone_sync = m_gx2drawdone_sync->IsChecked();
config.async_compile = m_async_compile->IsChecked();
config.upscale_filter = m_upscale_filter->GetSelection();
config.downscale_filter = m_downscale_filter->GetSelection();
config.fullscreen_scaling = m_fullscreen_scaling->GetSelection();
config.overlay.position = (ScreenPosition)m_overlay_position->GetSelection(); wxASSERT((int)config.overlay.position <= (int)ScreenPosition::kBottomRight);
config.overlay.text_color = m_overlay_font_color->GetColour().GetRGBA();
config.overlay.text_scale = m_overlay_scale->GetSelection() * 25 + 50;
@ -1064,7 +1066,7 @@ void GeneralSettings2::ValidateConfig()
void GeneralSettings2::DisableSettings(bool game_launched)
{
}
void GeneralSettings2::OnAudioLatencyChanged(wxCommandEvent& event)
@ -1075,7 +1077,7 @@ void GeneralSettings2::OnAudioLatencyChanged(wxCommandEvent& event)
void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
{
if(event.GetEventObject() == m_input_volume)
{
std::shared_lock lock(g_audioInputMutex);
@ -1099,7 +1101,7 @@ void GeneralSettings2::OnVolumeChanged(wxCommandEvent& event)
g_tvAudio->SetVolume(event.GetInt());
}
}
event.Skip();
}
@ -1112,7 +1114,7 @@ void GeneralSettings2::OnInputVolumeChanged(wxCommandEvent& event)
g_padAudio->SetInputVolume(event.GetInt());
g_padVolume = event.GetInt();
}
event.Skip();
}
@ -1190,7 +1192,7 @@ void GeneralSettings2::UpdateAudioDeviceList()
// todo reset global instance of audio device
}
void GeneralSettings2::ResetAccountInformation()
void GeneralSettings2::ResetAccountInformation()
{
m_account_grid->SetSplitterPosition(100);
m_active_account->SetSelection(0);
@ -1218,7 +1220,7 @@ void GeneralSettings2::OnAccountCreate(wxCommandEvent& event)
Account account(dialog.GetPersistentId(), dialog.GetMiiName().ToStdWstring());
account.Save();
Account::RefreshAccounts();
const int index = m_active_account->Append(account.ToString(), new wxAccountData(account));
// update ui
@ -1227,7 +1229,7 @@ void GeneralSettings2::OnAccountCreate(wxCommandEvent& event)
m_create_account->Enable(m_active_account->GetCount() < 0xC);
m_delete_account->Enable(m_active_account->GetCount() > 1);
// send main window event
wxASSERT(GetParent());
wxCommandEvent refresh_event(wxEVT_ACCOUNTLIST_REFRESH);
@ -1257,7 +1259,7 @@ void GeneralSettings2::OnAccountDelete(wxCommandEvent& event)
return;
// todo: ask if saves should be deleted too?
const fs::path path = account.GetFileName();
try
{
@ -1275,7 +1277,7 @@ void GeneralSettings2::OnAccountDelete(wxCommandEvent& event)
SystemException sys(ex);
cemuLog_log(LogType::Force, sys.what());
}
}
void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
@ -1330,7 +1332,7 @@ void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
else if (property->GetName() == kPropertyEmail)
{
account.SetEmail(value.As<wxString>().ToStdString());
}
else if (property->GetName() == kPropertyCountry)
{
@ -1338,7 +1340,7 @@ void GeneralSettings2::OnAccountSettingsChanged(wxPropertyGridEvent& event)
}
else
cemu_assert_debug(false);
account.Save();
Account::RefreshAccounts(); // refresh internal account list
UpdateAccountInformation(); // refresh on invalid values
@ -1378,7 +1380,7 @@ void GeneralSettings2::UpdateAccountInformation()
gender_property->SetChoiceSelection(std::min(gender_property->GetChoices().GetCount() - 1, (uint32)account.GetGender()));
m_account_grid->GetProperty(kPropertyEmail)->SetValueFromString(std::string{ account.GetEmail() });
auto* country_property = dynamic_cast<wxEnumProperty*>(m_account_grid->GetProperty(kPropertyCountry));
wxASSERT(country_property);
int index = (country_property)->GetIndexForValue(account.GetCountry());
@ -1462,7 +1464,7 @@ void GeneralSettings2::HandleGraphicsApiSelection()
int selection = m_vsync->GetSelection();
if(selection == wxNOT_FOUND)
selection = GetConfig().vsync;
m_vsync->Clear();
if(m_graphic_api->GetSelection() == 0)
{
@ -1494,7 +1496,7 @@ void GeneralSettings2::HandleGraphicsApiSelection()
#endif
m_vsync->Select(selection);
m_graphic_device->Enable();
auto devices = VulkanRenderer::GetDevices();
m_graphic_device->Clear();
@ -1618,7 +1620,7 @@ void GeneralSettings2::ApplyConfig()
m_pad_channels->SetSelection(0);
//m_input_channels->SetSelection(config.pad_channels);
m_input_channels->SetSelection(0);
SendSliderEvent(m_tv_volume, config.tv_volume);
if (!config.tv_device.empty() && m_tv_device->HasClientObjectData())
@ -1635,7 +1637,7 @@ void GeneralSettings2::ApplyConfig()
}
else
m_tv_device->SetSelection(0);
SendSliderEvent(m_pad_volume, config.pad_volume);
if (!config.pad_device.empty() && m_pad_device->HasClientObjectData())
{
@ -1768,7 +1770,7 @@ void GeneralSettings2::UpdateAudioDevice()
}
}
}
// pad audio device
{
const auto selection = m_pad_device->GetSelection();
@ -1884,14 +1886,14 @@ void GeneralSettings2::OnAudioChannelsSelected(wxCommandEvent& event)
{
if (config.tv_channels == (AudioChannels)obj->GetSelection())
return;
config.tv_channels = (AudioChannels)obj->GetSelection();
}
else if (obj == m_pad_channels)
{
if (config.pad_channels == (AudioChannels)obj->GetSelection())
return;
config.pad_channels = (AudioChannels)obj->GetSelection();
}
else
@ -2034,23 +2036,23 @@ void GeneralSettings2::OnShowOnlineValidator(wxCommandEvent& event)
const auto selection = m_active_account->GetSelection();
if (selection == wxNOT_FOUND)
return;
const auto* obj = dynamic_cast<wxAccountData*>(m_active_account->GetClientObject(selection));
wxASSERT(obj);
const auto& account = obj->GetAccount();
const auto validator = account.ValidateOnlineFiles();
if (validator) // everything valid? shouldn't happen
return;
wxString err;
err << _("The following error(s) have been found:") << '\n';
if (validator.otp == OnlineValidator::FileState::Missing)
err << _("otp.bin missing in Cemu directory") << '\n';
else if(validator.otp == OnlineValidator::FileState::Corrupted)
err << _("otp.bin is invalid") << '\n';
if (validator.seeprom == OnlineValidator::FileState::Missing)
err << _("seeprom.bin missing in Cemu directory") << '\n';
else if(validator.seeprom == OnlineValidator::FileState::Corrupted)

View File

@ -21,7 +21,7 @@ LoggingWindow::LoggingWindow(wxFrame* parent)
filter_row->Add(new wxStaticText( this, wxID_ANY, _("Filter")), 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
wxString choices[] = {"Unsupported APIs calls", "Coreinit Logging", "Coreinit File-Access", "Coreinit Thread-Synchronization", "Coreinit Memory", "Coreinit MP", "Coreinit Thread", "nn::nfp", "GX2", "Audio", "Input", "Socket", "Save", "H264", "Graphic pack patches", "Texture cache", "Texture readback", "OpenGL debug output", "Vulkan validation layer"};
wxString choices[] = {"Unsupported APIs calls", "Coreinit Logging", "Coreinit File-Access", "Coreinit Thread-Synchronization", "Coreinit Memory", "Coreinit MP", "Coreinit Thread", "nn::nfp", "GX2", "Audio", "Input", "Socket", "Save", "H264", "Graphic pack patches", "Texture cache", "Texture readback", "OpenGL debug output", "Vulkan validation layer", "Metal debug output"};
m_filter = new wxComboBox( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, std::size(choices), choices, 0 );
m_filter->Bind(wxEVT_COMBOBOX, &LoggingWindow::OnFilterChange, this);
m_filter->Bind(wxEVT_TEXT, &LoggingWindow::OnFilterChange, this);
@ -83,7 +83,7 @@ void LoggingWindow::Log(std::string_view filter, std::wstring_view message)
void LoggingWindow::OnLogMessage(wxLogEvent& event)
{
m_log_list->PushEntry(event.GetFilter(), event.GetMessage());
m_log_list->PushEntry(event.GetFilter(), event.GetMessage());
}
void LoggingWindow::OnFilterChange(wxCommandEvent& event)
@ -97,4 +97,3 @@ void LoggingWindow::OnFilterMessageChange(wxCommandEvent& event)
m_log_list->SetFilterMessage(m_filter_message->GetValue());
event.Skip();
}

View File

@ -12,6 +12,7 @@
#include "audio/audioDebuggerWindow.h"
#include "gui/canvas/OpenGLCanvas.h"
#include "gui/canvas/VulkanCanvas.h"
#include "gui/canvas/MetalCanvas.h"
#include "Cafe/OS/libs/nfc/nfc.h"
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "gui/debugger/DebuggerWindow2.h"
@ -93,7 +94,7 @@ enum
// options -> account
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_1 = 20350,
MAINFRAME_MENU_ID_OPTIONS_ACCOUNT_12 = 20350 + 11,
// options -> system language
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_JAPANESE = 20500,
MAINFRAME_MENU_ID_OPTIONS_LANGUAGE_ENGLISH,
@ -243,7 +244,7 @@ public:
{
if(!m_window->IsGameLaunched() && filenames.GetCount() == 1)
return m_window->FileLoad(_utf8ToPath(filenames[0].utf8_string()), wxLaunchGameEvent::INITIATED_BY::DRAG_AND_DROP);
return false;
}
@ -455,7 +456,7 @@ bool MainWindow::InstallUpdate(const fs::path& metaFilePath)
{
throw std::runtime_error(frame.GetExceptionMessage());
}
}
}
}
catch(const AbortException&)
{
@ -639,13 +640,13 @@ void MainWindow::OnFileMenu(wxCommandEvent& event)
_("Wii U executable (*.rpx, *.elf)"),
_("All files (*.*)")
);
wxFileDialog openFileDialog(this, _("Open file to launch"), wxEmptyString, wxEmptyString, wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL || openFileDialog.GetPath().IsEmpty())
return;
const wxString wxStrFilePath = openFileDialog.GetPath();
const wxString wxStrFilePath = openFileDialog.GetPath();
FileLoad(_utf8ToPath(wxStrFilePath.utf8_string()), wxLaunchGameEvent::INITIATED_BY::MENU);
}
else if (menuId >= MAINFRAME_MENU_ID_FILE_RECENT_0 && menuId <= MAINFRAME_MENU_ID_FILE_RECENT_LAST)
@ -784,7 +785,7 @@ void MainWindow::TogglePadView()
{
if (m_padView)
return;
m_padView = new PadViewFrame(this);
m_padView->Bind(wxEVT_CLOSE_WINDOW, &MainWindow::OnPadClose, this);
@ -992,7 +993,7 @@ void MainWindow::OnConsoleLanguage(wxCommandEvent& event)
// GetConfig().cpu_mode = CPUMode::TriplecoreRecompiler;
// else
// cemu_assert_debug(false);
//
//
// g_config.Save();
//}
@ -1056,7 +1057,7 @@ void MainWindow::OnDebugSetting(wxCommandEvent& event)
ActiveSettings::SetTimerShiftFactor(6);
else
cemu_assert_debug(false);
g_config.Save();
}
@ -1132,7 +1133,7 @@ void MainWindow::OnLoggingWindow(wxCommandEvent& event)
return;
m_logging_window = new LoggingWindow(this);
m_logging_window->Bind(wxEVT_CLOSE_WINDOW,
m_logging_window->Bind(wxEVT_CLOSE_WINDOW,
[this](wxCloseEvent& event) {
m_logging_window = nullptr;
event.Skip();
@ -1307,7 +1308,7 @@ void MainWindow::SaveSettings()
{
auto lock = g_config.Lock();
auto& config = GetConfig();
if (config.window_position != Vector2i{ -1,-1 })
{
config.window_position.x = m_restored_position.x;
@ -1344,7 +1345,7 @@ void MainWindow::SaveSettings()
if(m_game_list)
m_game_list->SaveConfig();
g_config.Save();
}
@ -1374,14 +1375,14 @@ void MainWindow::OnMouseMove(wxMouseEvent& event)
void MainWindow::OnMouseLeft(wxMouseEvent& event)
{
auto& instance = InputManager::instance();
std::scoped_lock lock(instance.m_main_mouse.m_mutex);
instance.m_main_mouse.left_down = event.ButtonDown(wxMOUSE_BTN_LEFT);
auto physPos = ToPhys(event.GetPosition());
instance.m_main_mouse.position = { physPos.x, physPos.y };
if (event.ButtonDown(wxMOUSE_BTN_LEFT))
instance.m_main_mouse.left_down_toggle = true;
event.Skip();
}
@ -1395,7 +1396,7 @@ void MainWindow::OnMouseRight(wxMouseEvent& event)
instance.m_main_mouse.position = { physPos.x, physPos.y };
if(event.ButtonDown(wxMOUSE_BTN_RIGHT))
instance.m_main_mouse.right_down_toggle = true;
event.Skip();
}
@ -1443,7 +1444,7 @@ void MainWindow::OnKeyUp(wxKeyEvent& event)
void MainWindow::OnKeyDown(wxKeyEvent& event)
{
if ((event.AltDown() && event.GetKeyCode() == WXK_F4) ||
if ((event.AltDown() && event.GetKeyCode() == WXK_F4) ||
(event.CmdDown() && event.GetKeyCode() == 'Q'))
{
Close(true);
@ -1458,7 +1459,7 @@ void MainWindow::OnChar(wxKeyEvent& event)
{
if (swkbd_hasKeyboardInputHook())
swkbd_keyInput(event.GetUnicodeKey());
// event.Skip();
}
@ -1483,7 +1484,7 @@ void MainWindow::OnToolsInput(wxCommandEvent& event)
case MAINFRAME_MENU_ID_TOOLS_DOWNLOAD_MANAGER:
{
const auto default_tab = id == MAINFRAME_MENU_ID_TOOLS_TITLE_MANAGER ? TitleManagerPage::TitleManager : TitleManagerPage::DownloadManager;
if (m_title_manager)
m_title_manager->SetFocusAndTab(default_tab);
else
@ -1533,7 +1534,7 @@ void MainWindow::OnGesturePan(wxPanGestureEvent& event)
instance.m_main_touch.left_down = event.IsGestureStart() || !event.IsGestureEnd();
if (event.IsGestureStart() || !event.IsGestureEnd())
instance.m_main_touch.left_down_toggle = true;
event.Skip();
}
@ -1567,8 +1568,10 @@ void MainWindow::CreateCanvas()
// create canvas
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
m_render_canvas = new VulkanCanvas(m_game_panel, wxSize(1280, 720), true);
else
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
m_render_canvas = GLCanvas_Create(m_game_panel, wxSize(1280, 720), true);
else
m_render_canvas = new MetalCanvas(m_game_panel, wxSize(1280, 720), true);
// mouse events
m_render_canvas->Bind(wxEVT_MOTION, &MainWindow::OnMouseMove, this);
@ -1748,10 +1751,10 @@ void MainWindow::UpdateNFCMenu()
const auto& entry = config.recent_nfc_files[i];
if (entry.empty())
continue;
if (!fs::exists(_utf8ToPath(entry)))
continue;
if (recentFileIndex == 0)
m_nfcMenuSeparator0 = m_nfcMenu->AppendSeparator();
@ -1802,7 +1805,7 @@ void MainWindow::OnTimer(wxTimerEvent& event)
{
ShowCursor(false);
}
}
#define BUILD_DATE __DATE__ " " __TIME__
@ -2061,9 +2064,9 @@ void MainWindow::RecreateMenu()
m_menuBar->Destroy();
m_menuBar = nullptr;
}
auto& config = GetConfig();
m_menuBar = new wxMenuBar();
// file submenu
m_fileMenu = new wxMenu();
@ -2115,7 +2118,7 @@ void MainWindow::RecreateMenu()
item->Check(account_id == account.GetPersistentId());
if (m_game_launched || LaunchSettings::GetPersistentId().has_value())
item->Enable(false);
++index;
}
@ -2145,8 +2148,8 @@ void MainWindow::RecreateMenu()
// options submenu
wxMenu* optionsMenu = new wxMenu();
m_fullscreenMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_FULLSCREEN, _("&Fullscreen"), wxEmptyString);
m_fullscreenMenuItem->Check(ActiveSettings::FullscreenEnabled());
m_fullscreenMenuItem->Check(ActiveSettings::FullscreenEnabled());
optionsMenu->Append(MAINFRAME_MENU_ID_OPTIONS_GRAPHIC_PACKS2, _("&Graphic packs"));
m_padViewMenuItem = optionsMenu->AppendCheckItem(MAINFRAME_MENU_ID_OPTIONS_SECOND_WINDOW_PADVIEW, _("&Separate GamePad view"), wxEmptyString);
m_padViewMenuItem->Check(GetConfig().pad_open);
@ -2227,6 +2230,7 @@ void MainWindow::RecreateMenu()
debugLoggingMenu->AppendSeparator();
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), _("&OpenGL debug output"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::OpenGLLogging));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), _("&Vulkan validation layer (slow)"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::VulkanValidation));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::MetalLogging), _("&Metal debug output"), wxEmptyString)->Check(cemuLog_isLoggingEnabled(LogType::MetalLogging));
debugLoggingMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_ADVANCED_PPC_INFO, _("&Log PPC context for API"), wxEmptyString)->Check(cemuLog_advancedPPCLoggingEnabled());
m_loggingSubmenu = debugLoggingMenu;
// debug->dump submenu
@ -2240,7 +2244,7 @@ void MainWindow::RecreateMenu()
debugMenu->AppendSubMenu(debugLoggingMenu, _("&Logging"));
debugMenu->AppendSubMenu(debugDumpMenu, _("&Dump"));
debugMenu->AppendSeparator();
auto upsidedownItem = debugMenu->AppendCheckItem(MAINFRAME_MENU_ID_DEBUG_RENDER_UPSIDE_DOWN, _("&Render upside-down"), wxEmptyString);
upsidedownItem->Check(ActiveSettings::RenderUpsideDownEnabled());
if(LaunchSettings::RenderUpsideDownEnabled().has_value())
@ -2296,6 +2300,7 @@ void MainWindow::RecreateMenu()
// these options cant be toggled after the renderer backend is initialized:
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::OpenGLLogging), false);
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::VulkanValidation), false);
m_loggingSubmenu->Enable(MAINFRAME_MENU_ID_DEBUG_LOGGING0 + stdx::to_underlying(LogType::MetalLogging), false);
UpdateNFCMenu();
}

View File

@ -8,6 +8,7 @@
#include "Cafe/OS/libs/swkbd/swkbd.h"
#include "gui/canvas/OpenGLCanvas.h"
#include "gui/canvas/VulkanCanvas.h"
#include "gui/canvas/MetalCanvas.h"
#include "config/CemuConfig.h"
#include "gui/MainWindow.h"
#include "gui/helpers/wxHelpers.h"
@ -74,8 +75,10 @@ void PadViewFrame::InitializeRenderCanvas()
{
if (ActiveSettings::GetGraphicsAPI() == kVulkan)
m_render_canvas = new VulkanCanvas(this, wxSize(854, 480), false);
else
else if (ActiveSettings::GetGraphicsAPI() == kOpenGL)
m_render_canvas = GLCanvas_Create(this, wxSize(854, 480), false);
else
m_render_canvas = new MetalCanvas(this, wxSize(854, 480), false);
sizer->Add(m_render_canvas, 1, wxEXPAND, 0, nullptr);
}
SetSizer(sizer);
@ -173,7 +176,7 @@ void PadViewFrame::OnChar(wxKeyEvent& event)
{
if (swkbd_hasKeyboardInputHook())
swkbd_keyInput(event.GetUnicodeKey());
event.Skip();
}
@ -198,7 +201,7 @@ void PadViewFrame::OnMouseLeft(wxMouseEvent& event)
instance.m_pad_mouse.position = { physPos.x, physPos.y };
if (event.ButtonDown(wxMOUSE_BTN_LEFT))
instance.m_pad_mouse.left_down_toggle = true;
}
void PadViewFrame::OnMouseRight(wxMouseEvent& event)

View File

@ -0,0 +1,63 @@
#include "gui/canvas/MetalCanvas.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
#include "gui/guiWrapper.h"
#include <wx/msgdlg.h>
#include <helpers/wxHelpers.h>
MetalCanvas::MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window)
: IRenderCanvas(is_main_window), wxWindow(parent, wxID_ANY, wxDefaultPosition, size, wxNO_FULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
{
Bind(wxEVT_PAINT, &MetalCanvas::OnPaint, this);
Bind(wxEVT_SIZE, &MetalCanvas::OnResize, this);
WindowHandleInfo& canvas = is_main_window ? gui_getWindowInfo().canvas_main : gui_getWindowInfo().canvas_pad;
gui_initHandleContextFromWxWidgetsWindow(canvas, this);
try
{
if (is_main_window)
g_renderer = std::make_unique<MetalRenderer>();
auto metal_renderer = MetalRenderer::GetInstance();
metal_renderer->InitializeLayer({size.x, size.y}, is_main_window);
}
catch(const std::exception& ex)
{
cemuLog_log(LogType::Force, "Error when initializing Metal renderer: {}", ex.what());
auto msg = formatWxString(_("Error when initializing Metal renderer:\n{}"), ex.what());
wxMessageDialog dialog(this, msg, _("Error"), wxOK | wxCENTRE | wxICON_ERROR);
dialog.ShowModal();
exit(0);
}
wxWindow::EnableTouchEvents(wxTOUCH_PAN_GESTURES);
}
MetalCanvas::~MetalCanvas()
{
Unbind(wxEVT_PAINT, &MetalCanvas::OnPaint, this);
Unbind(wxEVT_SIZE, &MetalCanvas::OnResize, this);
if(!m_is_main_window)
{
// TODO
//MetalRenderer* vkr = (MetalRenderer*)g_renderer.get();
//if(vkr)
// vkr->StopUsingPadAndWait();
}
}
void MetalCanvas::OnPaint(wxPaintEvent& event)
{
}
void MetalCanvas::OnResize(wxSizeEvent& event)
{
const wxSize size = GetSize();
if (size.GetWidth() == 0 || size.GetHeight() == 0)
return;
const wxRect refreshRect(size);
RefreshRect(refreshRect, false);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "gui/canvas/IRenderCanvas.h"
#include <wx/frame.h>
#include <set>
class MetalCanvas : public IRenderCanvas, public wxWindow
{
public:
MetalCanvas(wxWindow* parent, const wxSize& size, bool is_main_window);
~MetalCanvas();
private:
void OnPaint(wxPaintEvent& event);
void OnResize(wxSizeEvent& event);
};

View File

@ -82,11 +82,14 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps)
case RendererAPI::OpenGL:
renderer = "[OpenGL]";
break;
case RendererAPI::Vulkan:
case RendererAPI::Vulkan:
renderer = "[Vulkan]";
break;
case RendererAPI::Metal:
renderer = "[Metal]";
break;
default: ;
}
}
}
// get GPU vendor/mode
@ -217,7 +220,7 @@ void gui_initHandleContextFromWxWidgetsWindow(WindowHandleInfo& handleInfoOut, c
cemuLog_log(LogType::Force, "Unable to get xlib display");
}
}
else
else
#ifdef HAS_WAYLAND
if(GDK_IS_WAYLAND_WINDOW(gdkWindow))
{

View File

@ -7,6 +7,8 @@ add_library(imguiImpl
imgui_extension.h
)
# TODO: add Metal
set_property(TARGET imguiImpl PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
target_include_directories(imguiImpl PUBLIC "../")