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

@ -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);
}

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
};

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()

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,

View File

@ -74,6 +74,7 @@ enum GraphicAPI
{
kOpenGL = 0,
kVulkan,
kMetal,
};
enum AudioChannels
@ -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

@ -4,6 +4,8 @@ add_library(CemuGui
canvas/OpenGLCanvas.h
canvas/VulkanCanvas.cpp
canvas/VulkanCanvas.h
canvas/MetalCanvas.cpp
canvas/MetalCanvas.h
CemuApp.cpp
CemuApp.h
CemuUpdateWindow.cpp

View File

@ -112,7 +112,11 @@ 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);

View File

@ -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);

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);
@ -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"
@ -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);
@ -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
@ -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);

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

@ -85,6 +85,9 @@ void gui_updateWindowTitles(bool isIdle, bool isLoading, double fps)
case RendererAPI::Vulkan:
renderer = "[Vulkan]";
break;
case RendererAPI::Metal:
renderer = "[Metal]";
break;
default: ;
}
}

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 "../")