implement texture swizzle

This commit is contained in:
Samuliak 2024-08-06 10:23:57 +02:00
parent d64e64e5ef
commit 0a7f30c6a4
7 changed files with 164 additions and 68 deletions

View File

@ -15,7 +15,7 @@ void CachedFBOMtl::CreateRenderPass()
continue; continue;
} }
auto colorAttachment = m_renderPassDescriptor->colorAttachments()->object(i); auto colorAttachment = m_renderPassDescriptor->colorAttachments()->object(i);
colorAttachment->setTexture(textureView->GetTexture()); colorAttachment->setTexture(textureView->GetRGBAView());
colorAttachment->setLoadAction(MTL::LoadActionLoad); colorAttachment->setLoadAction(MTL::LoadActionLoad);
colorAttachment->setStoreAction(MTL::StoreActionStore); colorAttachment->setStoreAction(MTL::StoreActionStore);
} }
@ -25,7 +25,7 @@ void CachedFBOMtl::CreateRenderPass()
{ {
auto textureView = static_cast<LatteTextureViewMtl*>(depthBuffer.texture); auto textureView = static_cast<LatteTextureViewMtl*>(depthBuffer.texture);
auto depthAttachment = m_renderPassDescriptor->depthAttachment(); auto depthAttachment = m_renderPassDescriptor->depthAttachment();
depthAttachment->setTexture(textureView->GetTexture()); depthAttachment->setTexture(textureView->GetRGBAView());
depthAttachment->setLoadAction(MTL::LoadActionLoad); depthAttachment->setLoadAction(MTL::LoadActionLoad);
depthAttachment->setStoreAction(MTL::StoreActionStore); depthAttachment->setStoreAction(MTL::StoreActionStore);
} }

View File

@ -4,58 +4,123 @@
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #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) 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() 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;
} }

View File

@ -1,27 +1,37 @@
#pragma once #pragma once
#include <Metal/Metal.hpp> #include <Metal/Metal.hpp>
#include <unordered_map>
#include "Cafe/HW/Latte/Core/LatteTexture.h" #include "Cafe/HW/Latte/Core/LatteTexture.h"
#define RGBA_SWIZZLE 0x06880000
#define INVALID_SWIZZLE 0xFFFFFFFF
// TODO: test the swizzle
class LatteTextureViewMtl : public LatteTextureView class LatteTextureViewMtl : public LatteTextureView
{ {
public: 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(class MetalRenderer* mtlRenderer, class LatteTextureMtl* texture, Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount);
~LatteTextureViewMtl(); ~LatteTextureViewMtl();
MTL::Texture* GetTexture() const { MTL::Texture* GetSwizzledView(uint32 gpuSamplerSwizzle);
return m_texture;
}
Latte::E_GX2SURFFMT GetFormat() const { MTL::Texture* GetRGBAView()
return m_format; {
return GetSwizzledView(RGBA_SWIZZLE);
} }
private: private:
class MetalRenderer* m_mtlr; 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<uint32, MTL::Texture*> m_fallbackViewCache;
MTL::Texture* CreateSwizzledView(uint32 gpuSamplerSwizzle);
}; };

View File

@ -419,3 +419,20 @@ MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WOR
cemu_assert_debug((uint32)clamp < std::size(MTL_SAMPLER_ADDRESS_MODES)); cemu_assert_debug((uint32)clamp < std::size(MTL_SAMPLER_ADDRESS_MODES));
return MTL_SAMPLER_ADDRESS_MODES[(uint32)clamp]; 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];
}

View File

@ -8,6 +8,7 @@
#include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Metal/MTLDepthStencil.hpp" #include "Metal/MTLDepthStencil.hpp"
#include "Metal/MTLSampler.hpp" #include "Metal/MTLSampler.hpp"
#include "Metal/MTLTexture.hpp"
struct Uvec2 { struct Uvec2 {
uint32 x; uint32 x;
@ -41,3 +42,5 @@ MTL::BlendFactor GetMtlBlendFactor(Latte::LATTE_CB_BLENDN_CONTROL::E_BLENDFACTOR
MTL::CompareFunction GetMtlCompareFunc(Latte::E_COMPAREFUNC func); MTL::CompareFunction GetMtlCompareFunc(Latte::E_COMPAREFUNC func);
MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_CLAMP clamp); MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_CLAMP clamp);
MTL::TextureSwizzle GetMtlTextureSwizzle(uint32 swizzle);

View File

@ -88,7 +88,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS
continue; continue;
} }
auto colorAttachment = desc->colorAttachments()->object(i); auto colorAttachment = desc->colorAttachments()->object(i);
colorAttachment->setPixelFormat(texture->GetTexture()->pixelFormat()); colorAttachment->setPixelFormat(texture->GetRGBAView()->pixelFormat());
// Blending // Blending
const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = LatteGPUState.contextNew.CB_COLOR_CONTROL; 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) if (activeFBO->depthBuffer.texture)
{ {
auto texture = static_cast<LatteTextureViewMtl*>(activeFBO->depthBuffer.texture); auto texture = static_cast<LatteTextureViewMtl*>(activeFBO->depthBuffer.texture);
desc->setDepthAttachmentPixelFormat(texture->GetTexture()->pixelFormat()); desc->setDepthAttachmentPixelFormat(texture->GetRGBAView()->pixelFormat());
// TODO: stencil pixel format // TODO: stencil pixel format
} }

View File

@ -167,7 +167,7 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput
if (!AcquireNextDrawable()) if (!AcquireNextDrawable())
return; return;
MTL::Texture* presentTexture = static_cast<LatteTextureViewMtl*>(texView)->GetTexture(); MTL::Texture* presentTexture = static_cast<LatteTextureViewMtl*>(texView)->GetRGBAView();
// Create render pass // Create render pass
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init(); MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
@ -550,13 +550,13 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
auto colorTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->colorBuffer[i].texture); auto colorTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->colorBuffer[i].texture);
if (colorTexture) if (colorTexture)
{ {
colorRenderTargets[i] = colorTexture->GetTexture(); colorRenderTargets[i] = colorTexture->GetRGBAView();
} }
} }
auto depthTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->depthBuffer.texture); auto depthTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->depthBuffer.texture);
if (depthTexture) if (depthTexture)
{ {
depthRenderTarget = depthTexture->GetTexture(); depthRenderTarget = depthTexture->GetRGBAView();
} }
auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, depthRenderTarget); auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, depthRenderTarget);
@ -919,9 +919,9 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
auto clampY = samplerWords->WORD0.get_CLAMP_Y(); auto clampY = samplerWords->WORD0.get_CLAMP_Y();
auto clampZ = samplerWords->WORD0.get_CLAMP_Z(); auto clampZ = samplerWords->WORD0.get_CLAMP_Z();
samplerDescriptor->setRAddressMode(GetMtlSamplerAddressMode(clampX)); samplerDescriptor->setSAddressMode(GetMtlSamplerAddressMode(clampX));
samplerDescriptor->setSAddressMode(GetMtlSamplerAddressMode(clampY)); samplerDescriptor->setTAddressMode(GetMtlSamplerAddressMode(clampY));
samplerDescriptor->setTAddressMode(GetMtlSamplerAddressMode(clampZ)); samplerDescriptor->setRAddressMode(GetMtlSamplerAddressMode(clampZ));
auto maxAniso = samplerWords->WORD0.get_MAX_ANISO_RATIO(); auto maxAniso = samplerWords->WORD0.get_MAX_ANISO_RATIO();
@ -980,16 +980,17 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
sampler->release(); sampler->release();
} }
MTL::Texture* mtlTexture = textureView->GetSwizzledView(word4);
switch (shader->shaderType) switch (shader->shaderType)
{ {
case LatteConst::ShaderType::Vertex: case LatteConst::ShaderType::Vertex:
{ {
renderCommandEncoder->setVertexTexture(textureView->GetTexture(), binding); renderCommandEncoder->setVertexTexture(mtlTexture, binding);
break; break;
} }
case LatteConst::ShaderType::Pixel: case LatteConst::ShaderType::Pixel:
{ {
renderCommandEncoder->setFragmentTexture(textureView->GetTexture(), binding); renderCommandEncoder->setFragmentTexture(mtlTexture, binding);
break; break;
} }
default: default: