diff --git a/src/Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.cpp index 2a0715b6..f3b98f15 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.cpp @@ -15,7 +15,7 @@ void CachedFBOMtl::CreateRenderPass() continue; } auto colorAttachment = m_renderPassDescriptor->colorAttachments()->object(i); - colorAttachment->setTexture(textureView->GetTexture()); + colorAttachment->setTexture(textureView->GetRGBAView()); colorAttachment->setLoadAction(MTL::LoadActionLoad); colorAttachment->setStoreAction(MTL::StoreActionStore); } @@ -25,7 +25,7 @@ void CachedFBOMtl::CreateRenderPass() { auto textureView = static_cast(depthBuffer.texture); auto depthAttachment = m_renderPassDescriptor->depthAttachment(); - depthAttachment->setTexture(textureView->GetTexture()); + depthAttachment->setTexture(textureView->GetRGBAView()); depthAttachment->setLoadAction(MTL::LoadActionLoad); depthAttachment->setStoreAction(MTL::StoreActionStore); } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp index d48b17cc..ce2fec62 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.cpp @@ -4,58 +4,123 @@ #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" LatteTextureViewMtl::LatteTextureViewMtl(MetalRenderer* mtlRenderer, LatteTextureMtl* texture, Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) - : LatteTextureView(texture, firstMip, mipCount, firstSlice, sliceCount, dim, format), m_mtlr(mtlRenderer), m_format(format) + : LatteTextureView(texture, firstMip, mipCount, firstSlice, sliceCount, dim, format), m_mtlr(mtlRenderer), m_baseTexture(texture) { - MTL::TextureType textureType; - switch (dim) - { - case Latte::E_DIM::DIM_1D: - textureType = MTL::TextureType1D; - break; - case Latte::E_DIM::DIM_2D: - case Latte::E_DIM::DIM_2D_MSAA: - textureType = MTL::TextureType2D; - break; - case Latte::E_DIM::DIM_2D_ARRAY: - textureType = MTL::TextureType2DArray; - break; - case Latte::E_DIM::DIM_3D: - textureType = MTL::TextureType3D; - break; - case Latte::E_DIM::DIM_CUBEMAP: - textureType = MTL::TextureTypeCube; // TODO: check this - break; - default: - cemu_assert_unimplemented(); - textureType = MTL::TextureType2D; - break; - } - - uint32 baseLevel = firstMip; - uint32 levelCount = this->numMip; - uint32 baseLayer; - uint32 layerCount; - // TODO: check if base texture is 3D texture as well - if (textureType == MTL::TextureType3D) - { - cemu_assert_debug(firstMip == 0); - cemu_assert_debug(this->numSlice == baseTexture->depth); - baseLayer = 0; - layerCount = 1; - } - else - { - baseLayer = firstSlice; - layerCount = this->numSlice; - } - - // TODO: swizzle - - auto formatInfo = GetMtlPixelFormatInfo(format, texture->IsDepth()); - m_texture = texture->GetTexture()->newTextureView(formatInfo.pixelFormat, textureType, NS::Range::Make(baseLevel, levelCount), NS::Range::Make(baseLayer, layerCount)); } LatteTextureViewMtl::~LatteTextureViewMtl() { - m_texture->release(); + for (sint32 i = 0; i < std::size(m_viewCache); i++) + { + if (m_viewCache[i].key != INVALID_SWIZZLE) + m_viewCache[i].texture->release(); + } + + for (auto& [key, texture] : m_fallbackViewCache) + { + texture->release(); + } +} + +MTL::Texture* LatteTextureViewMtl::GetSwizzledView(uint32 gpuSamplerSwizzle) +{ + // Mask out + gpuSamplerSwizzle &= 0x0FFF0000; + + if (gpuSamplerSwizzle == RGBA_SWIZZLE) + { + return m_baseTexture->GetTexture(); + } + else + { + // First, try to find a view in the cache + + // Fast cache + sint32 freeIndex = -1; + for (sint32 i = 0; i < std::size(m_viewCache); i++) + { + if (m_viewCache[i].key == gpuSamplerSwizzle) + { + return m_viewCache[i].texture; + } + else if (m_viewCache[i].key == INVALID_SWIZZLE && freeIndex == -1) + { + freeIndex = i; + } + } + + // Fallback cache + auto it = m_fallbackViewCache.find(gpuSamplerSwizzle); + if (it != m_fallbackViewCache.end()) + { + return it->second; + } + + MTL::Texture* texture = CreateSwizzledView(gpuSamplerSwizzle); + if (freeIndex != -1) + m_viewCache[freeIndex] = {gpuSamplerSwizzle, texture}; + else + it->second = texture; + + return texture; + } +} + +MTL::Texture* LatteTextureViewMtl::CreateSwizzledView(uint32 gpuSamplerSwizzle) +{ + uint32 compSelR = (gpuSamplerSwizzle >> 16) & 0x7; + uint32 compSelG = (gpuSamplerSwizzle >> 19) & 0x7; + uint32 compSelB = (gpuSamplerSwizzle >> 22) & 0x7; + uint32 compSelA = (gpuSamplerSwizzle >> 25) & 0x7; + // TODO: adjust + + MTL::TextureType textureType; + switch (dim) + { + case Latte::E_DIM::DIM_1D: + textureType = MTL::TextureType1D; + break; + case Latte::E_DIM::DIM_2D: + case Latte::E_DIM::DIM_2D_MSAA: + textureType = MTL::TextureType2D; + break; + case Latte::E_DIM::DIM_2D_ARRAY: + textureType = MTL::TextureType2DArray; + break; + case Latte::E_DIM::DIM_3D: + textureType = MTL::TextureType3D; + break; + case Latte::E_DIM::DIM_CUBEMAP: + textureType = MTL::TextureTypeCube; // TODO: check this + break; + default: + cemu_assert_unimplemented(); + textureType = MTL::TextureType2D; + break; + } + + uint32 baseLevel = firstMip; + uint32 levelCount = this->numMip; + uint32 baseLayer; + uint32 layerCount; + // TODO: check if base texture is 3D texture as well + if (textureType == MTL::TextureType3D) + { + cemu_assert_debug(firstMip == 0); + cemu_assert_debug(this->numSlice == baseTexture->depth); + baseLayer = 0; + layerCount = 1; + } + else + { + baseLayer = firstSlice; + layerCount = this->numSlice; + } + + // TODO: swizzle + + auto formatInfo = GetMtlPixelFormatInfo(format, m_baseTexture->IsDepth()); + MTL::Texture* texture = m_baseTexture->GetTexture()->newTextureView(formatInfo.pixelFormat, textureType, NS::Range::Make(baseLevel, levelCount), NS::Range::Make(baseLayer, layerCount)); + + return texture; } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h index 7df74b4f..eb224180 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h @@ -1,27 +1,37 @@ #pragma once #include +#include #include "Cafe/HW/Latte/Core/LatteTexture.h" +#define RGBA_SWIZZLE 0x06880000 +#define INVALID_SWIZZLE 0xFFFFFFFF + +// TODO: test the swizzle class LatteTextureViewMtl : public LatteTextureView { public: LatteTextureViewMtl(class MetalRenderer* mtlRenderer, class LatteTextureMtl* texture, Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount); ~LatteTextureViewMtl(); - MTL::Texture* GetTexture() const { - return m_texture; - } + MTL::Texture* GetSwizzledView(uint32 gpuSamplerSwizzle); - Latte::E_GX2SURFFMT GetFormat() const { - return m_format; + MTL::Texture* GetRGBAView() + { + return GetSwizzledView(RGBA_SWIZZLE); } private: class MetalRenderer* m_mtlr; - MTL::Texture* m_texture; + class LatteTextureMtl* m_baseTexture; - Latte::E_GX2SURFFMT m_format; + struct { + uint32 key; + MTL::Texture* texture; + } m_viewCache[4] = {{INVALID_SWIZZLE, nullptr}}; + std::unordered_map m_fallbackViewCache; + + MTL::Texture* CreateSwizzledView(uint32 gpuSamplerSwizzle); }; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp index f522439c..fdf18ef5 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp @@ -419,3 +419,20 @@ MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WOR cemu_assert_debug((uint32)clamp < std::size(MTL_SAMPLER_ADDRESS_MODES)); return MTL_SAMPLER_ADDRESS_MODES[(uint32)clamp]; } + +const MTL::TextureSwizzle MTL_TEXTURE_SWIZZLES[] = { + MTL::TextureSwizzleRed, + MTL::TextureSwizzleGreen, + MTL::TextureSwizzleBlue, + MTL::TextureSwizzleAlpha, + MTL::TextureSwizzleZero, + MTL::TextureSwizzleOne, + MTL::TextureSwizzleZero, + MTL::TextureSwizzleZero +}; + +MTL::TextureSwizzle GetMtlTextureSwizzle(uint32 swizzle) +{ + cemu_assert_debug(swizzle < std::size(MTL_TEXTURE_SWIZZLES)); + return MTL_TEXTURE_SWIZZLES[swizzle]; +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h index f0348303..7e75c35d 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h @@ -8,6 +8,7 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Metal/MTLDepthStencil.hpp" #include "Metal/MTLSampler.hpp" +#include "Metal/MTLTexture.hpp" struct Uvec2 { uint32 x; @@ -41,3 +42,5 @@ MTL::BlendFactor GetMtlBlendFactor(Latte::LATTE_CB_BLENDN_CONTROL::E_BLENDFACTOR MTL::CompareFunction GetMtlCompareFunc(Latte::E_COMPAREFUNC func); MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_CLAMP clamp); + +MTL::TextureSwizzle GetMtlTextureSwizzle(uint32 swizzle); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp index 049d738b..d18f8bb9 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp @@ -88,7 +88,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS continue; } auto colorAttachment = desc->colorAttachments()->object(i); - colorAttachment->setPixelFormat(texture->GetTexture()->pixelFormat()); + colorAttachment->setPixelFormat(texture->GetRGBAView()->pixelFormat()); // Blending const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = LatteGPUState.contextNew.CB_COLOR_CONTROL; @@ -127,7 +127,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS if (activeFBO->depthBuffer.texture) { auto texture = static_cast(activeFBO->depthBuffer.texture); - desc->setDepthAttachmentPixelFormat(texture->GetTexture()->pixelFormat()); + desc->setDepthAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat()); // TODO: stencil pixel format } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index e8ce23f3..5ff58c68 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -167,7 +167,7 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput if (!AcquireNextDrawable()) return; - MTL::Texture* presentTexture = static_cast(texView)->GetTexture(); + MTL::Texture* presentTexture = static_cast(texView)->GetRGBAView(); // Create render pass MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); @@ -550,13 +550,13 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 auto colorTexture = static_cast(m_state.activeFBO->colorBuffer[i].texture); if (colorTexture) { - colorRenderTargets[i] = colorTexture->GetTexture(); + colorRenderTargets[i] = colorTexture->GetRGBAView(); } } auto depthTexture = static_cast(m_state.activeFBO->depthBuffer.texture); if (depthTexture) { - depthRenderTarget = depthTexture->GetTexture(); + depthRenderTarget = depthTexture->GetRGBAView(); } auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, depthRenderTarget); @@ -919,9 +919,9 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE auto clampY = samplerWords->WORD0.get_CLAMP_Y(); auto clampZ = samplerWords->WORD0.get_CLAMP_Z(); - samplerDescriptor->setRAddressMode(GetMtlSamplerAddressMode(clampX)); - samplerDescriptor->setSAddressMode(GetMtlSamplerAddressMode(clampY)); - samplerDescriptor->setTAddressMode(GetMtlSamplerAddressMode(clampZ)); + samplerDescriptor->setSAddressMode(GetMtlSamplerAddressMode(clampX)); + samplerDescriptor->setTAddressMode(GetMtlSamplerAddressMode(clampY)); + samplerDescriptor->setRAddressMode(GetMtlSamplerAddressMode(clampZ)); auto maxAniso = samplerWords->WORD0.get_MAX_ANISO_RATIO(); @@ -980,16 +980,17 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE sampler->release(); } + MTL::Texture* mtlTexture = textureView->GetSwizzledView(word4); switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: { - renderCommandEncoder->setVertexTexture(textureView->GetTexture(), binding); + renderCommandEncoder->setVertexTexture(mtlTexture, binding); break; } case LatteConst::ShaderType::Pixel: { - renderCommandEncoder->setFragmentTexture(textureView->GetTexture(), binding); + renderCommandEncoder->setFragmentTexture(mtlTexture, binding); break; } default: