diff --git a/.gitmodules b/.gitmodules index f352d478..e5fb2a50 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "dependencies/imgui"] path = dependencies/imgui url = https://github.com/ocornut/imgui +[submodule "dependencies/metal-cpp"] + path = dependencies/metal-cpp + url = https://github.com/bkaradzic/metal-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index c3b940f9..e5dadd98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -166,6 +166,11 @@ if (ENABLE_OPENGL) find_package(OpenGL REQUIRED) endif() +# TODO: handle this differently? +if (ENABLE_METAL AND APPLE) + include_directories(${CMAKE_SOURCE_DIR}/dependencies/metal-cpp) +endif() + if (ENABLE_DISCORD_RPC) add_compile_definitions(ENABLE_DISCORD_RPC) add_subdirectory(dependencies/discord-rpc EXCLUDE_FROM_ALL) diff --git a/dependencies/metal-cpp b/dependencies/metal-cpp new file mode 160000 index 00000000..a63bd172 --- /dev/null +++ b/dependencies/metal-cpp @@ -0,0 +1 @@ +Subproject commit a63bd172ddcba73a3d87ca32032b66ad41ddb9a6 diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index f7f25644..9da5caa9 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -528,7 +528,16 @@ if(ENABLE_METAL) if(APPLE) target_sources(CemuCafe PRIVATE HW/Latte/Renderer/Metal/MetalRenderer.cpp + HW/Latte/Renderer/Metal/MetalRenderer.h + HW/Latte/Renderer/Metal/MetalCppImpl.cpp + HW/Latte/Renderer/Metal/LatteTextureMtl.cpp + HW/Latte/Renderer/Metal/LatteTextureMtl.h ) + + #target_link_libraries(CemuCafe PRIVATE + # "-framework Metal" + # "-framework QuartzCore" + #) else() message(FATAL_ERROR "Metal is only supported on macOS") endif() diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp new file mode 100644 index 00000000..d6a9ac4a --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp @@ -0,0 +1,92 @@ +#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h" +//#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" + +LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, 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) + : LatteTexture(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth), m_mtlr(mtlRenderer) +{ + MTL::TextureDescriptor* desc = MTL::TextureDescriptor::alloc()->init(); + sint32 effectiveBaseWidth = width; + sint32 effectiveBaseHeight = height; + sint32 effectiveBaseDepth = depth; + if (overwriteInfo.hasResolutionOverwrite) + { + effectiveBaseWidth = overwriteInfo.width; + effectiveBaseHeight = overwriteInfo.height; + effectiveBaseDepth = overwriteInfo.depth; + } + effectiveBaseDepth = std::max(1, effectiveBaseDepth); + + desc->setWidth(effectiveBaseWidth); + desc->setHeight(effectiveBaseHeight); + desc->setMipmapLevelCount(mipLevels); + + if (dim == Latte::E_DIM::DIM_3D) + { + desc->setDepth(effectiveBaseDepth); + } + else + { + desc->setArrayLength(effectiveBaseDepth); + } + + // TODO: uncomment + //MetalRenderer::FormatInfoMTL texFormatInfo; + //mtlRenderer->GetTextureFormatInfoMTL(format, isDepth, dim, effectiveBaseWidth, effectiveBaseHeight, &texFormatInfo); + //cemu_assert_debug(hasStencil == ((texFormatInfo.vkImageAspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0)); + //imageInfo.format = texFormatInfo.mtlPixelFormat; + desc->setPixelFormat(MTL::PixelFormatRGBA8Unorm); + + // TODO: is write needed? + MTL::TextureUsage usage = MTL::TextureUsageShaderRead | MTL::TextureUsageShaderWrite; + // TODO: add more conditions + if (Latte::IsCompressedFormat(format) == false) + { + usage |= MTL::TextureUsageRenderTarget; + } + desc->setUsage(usage); + + if (dim == Latte::E_DIM::DIM_2D) + desc->setTextureType(MTL::TextureType2D); + else if (dim == Latte::E_DIM::DIM_1D) + desc->setTextureType(MTL::TextureType1D); + else if (dim == Latte::E_DIM::DIM_3D) + desc->setTextureType(MTL::TextureType3D); + else if (dim == Latte::E_DIM::DIM_2D_ARRAY) + desc->setTextureType(MTL::TextureType2DArray); + else if (dim == Latte::E_DIM::DIM_CUBEMAP) + desc->setTextureType(MTL::TextureTypeCube); // TODO: is this correct? + else if (dim == Latte::E_DIM::DIM_2D_MSAA) + desc->setTextureType(MTL::TextureType2D); + else + { + cemu_assert_unimplemented(); + } + + m_texture = mtlRenderer->GetDevice()->newTexture(desc); + desc->release(); +} + +LatteTextureMtl::~LatteTextureMtl() +{ + m_texture->release(); +} + +LatteTextureView* LatteTextureMtl::CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) +{ + cemu_assert_debug(mipCount > 0); + cemu_assert_debug(sliceCount > 0); + cemu_assert_debug((firstMip + mipCount) <= this->mipLevels); + cemu_assert_debug((firstSlice + sliceCount) <= this->depth); + + //return new LatteTextureViewMtl(m_mtlr, this, dim, format, firstMip, mipCount, firstSlice, sliceCount); + cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; +} + +void LatteTextureMtl::AllocateOnHost() +{ + cemuLog_logDebug(LogType::Force, "not implemented"); +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h new file mode 100644 index 00000000..266a69d1 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include "Cafe/HW/Latte/Core/LatteTexture.h" +#include "util/ChunkedHeap/ChunkedHeap.h" + +class LatteTextureMtl : public LatteTexture +{ +public: + LatteTextureMtl(class MetalRenderer* vkRenderer, 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); + + ~LatteTextureMtl(); + + void AllocateOnHost() override; + +protected: + LatteTextureView* CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) override; + +public: + uint64 m_vkFlushIndex{}; // used to track read-write dependencies within the same renderpass + + uint64 m_vkFlushIndex_read{}; + uint64 m_vkFlushIndex_write{}; + + uint32 m_collisionCheckIndex{}; // used to track if texture is being both sampled and output to during drawcall + +private: + class MetalRenderer* m_mtlr; + + MTL::Texture* m_texture; +}; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalCppImpl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalCppImpl.cpp new file mode 100644 index 00000000..13cd9dd6 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalCppImpl.cpp @@ -0,0 +1,6 @@ +#define NS_PRIVATE_IMPLEMENTATION +#define CA_PRIVATE_IMPLEMENTATION +#define MTL_PRIVATE_IMPLEMENTATION +#include +#include +#include diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 0e0bfb9a..8e203c50 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -1,5 +1,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" +#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h" + void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow) { /* const auto& windowInfo = gui_getWindowInfo().window_main; @@ -21,7 +23,8 @@ void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow) { } void MetalRenderer::Initialize() { - cemuLog_logDebug(LogType::Force, "not implemented"); + m_device = MTL::CreateSystemDefaultDevice(); + m_commandQueue = m_device->newCommandQueue(); } void MetalRenderer::Shutdown() { @@ -30,10 +33,17 @@ void MetalRenderer::Shutdown() { bool MetalRenderer::IsPadWindowActive() { cemuLog_logDebug(LogType::Force, "not implemented"); + + return false; } bool MetalRenderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const { cemuLog_logDebug(LogType::Force, "not implemented"); + + usageInMB = 1024; + totalInMB = 1024; + + return false; } void MetalRenderer::ClearColorbuffer(bool padView) { @@ -55,6 +65,8 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput } bool MetalRenderer::BeginFrame(bool mainWindow) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return false; } void MetalRenderer::Flush(bool waitIdle) { @@ -79,6 +91,8 @@ void MetalRenderer::renderTarget_setScissor(sint32 scissorX, sint32 scissorY, si LatteCachedFBO* MetalRenderer::rendertarget_createCachedFBO(uint64 key) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; } void MetalRenderer::rendertarget_deleteCachedFBO(LatteCachedFBO* fbo) { @@ -91,6 +105,8 @@ void MetalRenderer::rendertarget_bindFramebufferObject(LatteCachedFBO* cfbo) { void* MetalRenderer::texture_acquireTextureUploadBuffer(uint32 size) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; } void MetalRenderer::texture_releaseTextureUploadBuffer(uint8* mem) { @@ -99,6 +115,8 @@ void MetalRenderer::texture_releaseTextureUploadBuffer(uint8* mem) { TextureDecoder* MetalRenderer::texture_chooseDecodedFormat(Latte::E_GX2SURFFMT format, bool isDepth, Latte::E_DIM dim, uint32 width, uint32 height) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; } void MetalRenderer::texture_clearSlice(LatteTexture* hostTexture, sint32 sliceIndex, sint32 mipIndex) { @@ -118,7 +136,7 @@ void MetalRenderer::texture_clearDepthSlice(LatteTexture* hostTexture, uint32 sl } 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"); + return new LatteTextureMtl(this, dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth); } void MetalRenderer::texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit) { @@ -131,6 +149,8 @@ void MetalRenderer::texture_copyImageSubData(LatteTexture* src, sint32 srcMip, s LatteTextureReadbackInfo* MetalRenderer::texture_createReadback(LatteTextureView* textureView) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; } void MetalRenderer::surfaceCopy_copySurfaceWithFormatConversion(LatteTexture* sourceTexture, sint32 srcMip, sint32 srcSlice, LatteTexture* destinationTexture, sint32 dstMip, sint32 dstSlice, sint32 width, sint32 height) { @@ -163,6 +183,8 @@ void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, 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"); + + return nullptr; } void MetalRenderer::streamout_setupXfbBuffer(uint32 bufferIndex, sint32 ringBufferOffset, uint32 rangeAddr, uint32 rangeSize) { @@ -191,6 +213,8 @@ void MetalRenderer::draw_endSequence() { void* MetalRenderer::indexData_reserveIndexMemory(uint32 size, uint32& offset, uint32& bufferIndex) { cemuLog_logDebug(LogType::Force, "not implemented"); + + return nullptr; } void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size) { diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index 017c32fd..dd214a9c 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -1,5 +1,9 @@ #pragma once +#include +#include +#include + #include "Cafe/HW/Latte/Renderer/Renderer.h" class MetalRenderer : public Renderer @@ -16,6 +20,11 @@ public: return static_cast(g_renderer.get()); } + // Helper functions + MTL::Device* GetDevice() const { + return m_device; + } + void InitializeLayer(const Vector2i& size, bool mainWindow); void Initialize() override; @@ -44,6 +53,8 @@ public: // imgui bool ImguiBegin(bool mainWindow) override { cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal"); + + return false; }; void ImguiEnd() override { @@ -52,6 +63,8 @@ public: ImTextureID GenerateTexture(const std::vector& data, const Vector2i& size) override { cemuLog_logDebug(LogType::Force, "Imgui is not yet supported on Metal"); + + return nullptr; }; void DeleteTexture(ImTextureID id) override { @@ -122,6 +135,8 @@ public: // occlusion queries LatteQueryObject* occlusionQuery_create() override { cemuLog_logDebug(LogType::Force, "Occlusion queries are not yet supported on Metal"); + + return nullptr; } void occlusionQuery_destroy(LatteQueryObject* queryObj) override { @@ -137,6 +152,10 @@ public: } -protected: - //CA::MetalLayer* m_metalLayer; +private: + CA::MetalLayer* m_metalLayer; + + // Metal objects + MTL::Device* m_device; + MTL::CommandQueue* m_commandQueue; }; diff --git a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp index cdbeb3f3..ab468055 100644 --- a/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp +++ b/src/Cafe/HW/Latte/Renderer/RendererOuputShader.cpp @@ -83,7 +83,7 @@ void main(){ const std::string RendererOutputShader::s_hermite_shader_source = R"(#version 420 -in vec4 gl_FragCoord; +in vec4 gl_FragCoord; in vec2 passUV; layout(binding=0) uniform sampler2D textureSrc; uniform vec2 textureSrcResolution; @@ -100,7 +100,7 @@ vec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t) vec3 b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0; vec3 c = -A/2.0 + C/2.0; vec3 d = B; - + return a*t3 + b*t2 + c*t + d; } @@ -108,36 +108,36 @@ vec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t) vec3 BicubicHermiteTexture(vec2 uv, vec4 texelSize) { vec2 pixel = uv*texelSize.zw + 0.5; - vec2 frac = fract(pixel); + vec2 frac = fract(pixel); pixel = floor(pixel) / texelSize.zw - vec2(texelSize.xy/2.0); - + vec4 doubleSize = texelSize*texelSize; vec3 C00 = texture(textureSrc, pixel + vec2(-texelSize.x ,-texelSize.y)).rgb; vec3 C10 = texture(textureSrc, pixel + vec2( 0.0 ,-texelSize.y)).rgb; vec3 C20 = texture(textureSrc, pixel + vec2( texelSize.x ,-texelSize.y)).rgb; vec3 C30 = texture(textureSrc, pixel + vec2( doubleSize.x,-texelSize.y)).rgb; - + vec3 C01 = texture(textureSrc, pixel + vec2(-texelSize.x , 0.0)).rgb; vec3 C11 = texture(textureSrc, pixel + vec2( 0.0 , 0.0)).rgb; vec3 C21 = texture(textureSrc, pixel + vec2( texelSize.x , 0.0)).rgb; - vec3 C31 = texture(textureSrc, pixel + vec2( doubleSize.x, 0.0)).rgb; - + vec3 C31 = texture(textureSrc, pixel + vec2( doubleSize.x, 0.0)).rgb; + vec3 C02 = texture(textureSrc, pixel + vec2(-texelSize.x , texelSize.y)).rgb; vec3 C12 = texture(textureSrc, pixel + vec2( 0.0 , texelSize.y)).rgb; vec3 C22 = texture(textureSrc, pixel + vec2( texelSize.x , texelSize.y)).rgb; - vec3 C32 = texture(textureSrc, pixel + vec2( doubleSize.x, texelSize.y)).rgb; - + vec3 C32 = texture(textureSrc, pixel + vec2( doubleSize.x, texelSize.y)).rgb; + vec3 C03 = texture(textureSrc, pixel + vec2(-texelSize.x , doubleSize.y)).rgb; vec3 C13 = texture(textureSrc, pixel + vec2( 0.0 , doubleSize.y)).rgb; vec3 C23 = texture(textureSrc, pixel + vec2( texelSize.x , doubleSize.y)).rgb; - vec3 C33 = texture(textureSrc, pixel + vec2( doubleSize.x, doubleSize.y)).rgb; - + vec3 C33 = texture(textureSrc, pixel + vec2( doubleSize.x, doubleSize.y)).rgb; + vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x); vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x); vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x); vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x); - + return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y); } @@ -190,7 +190,7 @@ void RendererOutputShader::SetUniformParameters(const LatteTextureView& texture_ float res[2]; // vertex shader if (m_attributes[0].m_loc_texture_src_resolution != -1) - { + { res[0] = (float)texture_view.baseTexture->width; res[1] = (float)texture_view.baseTexture->height; m_vertex_shader->SetUniform2fv(m_attributes[0].m_loc_texture_src_resolution, res, 1); @@ -250,9 +250,9 @@ std::string RendererOutputShader::GetOpenGlVertexSource(bool render_upside_down) R"(#version 400 out vec2 passUV; -out gl_PerVertex -{ - vec4 gl_Position; +out gl_PerVertex +{ + vec4 gl_Position; }; void main(){ @@ -286,7 +286,7 @@ void main(){ vertex_source << R"( passUV = vUV; - gl_Position = vec4(vPos, 0.0, 1.0); + gl_Position = vec4(vPos, 0.0, 1.0); } )"; return vertex_source.str(); @@ -300,9 +300,9 @@ std::string RendererOutputShader::GetVulkanVertexSource(bool render_upside_down) R"(#version 450 layout(location = 0) out vec2 passUV; -out gl_PerVertex -{ - vec4 gl_Position; +out gl_PerVertex +{ + vec4 gl_Position; }; void main(){ @@ -336,7 +336,7 @@ void main(){ vertex_source << R"( passUV = vUV; - gl_Position = vec4(vPos, 0.0, 1.0); + gl_Position = vec4(vPos, 0.0, 1.0); } )"; return vertex_source.str(); @@ -359,7 +359,7 @@ 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); } - else + else if (g_renderer->GetType() == RendererAPI::Vulkan) { vertex_source = GetVulkanVertexSource(false); vertex_source_ud = GetVulkanVertexSource(true); @@ -372,5 +372,7 @@ 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);*/ + } else { + cemuLog_logDebug(LogType::Force, "Output shader not implemented for Metal"); } }