diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp index 40eacdf5..e5913f5d 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.cpp @@ -1,6 +1,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #include "Common/precompiled.h" #include "Metal/MTLDepthStencil.hpp" +#include "Metal/MTLSampler.hpp" std::map MTL_COLOR_FORMAT_TABLE = { {Latte::E_GX2SURFFMT::R4_G4_UNORM, {MTL::PixelFormatRG8Unorm, 2}}, // TODO: correct? @@ -272,3 +273,21 @@ MTL::CompareFunction GetMtlCompareFunc(Latte::E_COMPAREFUNC func) cemu_assert_debug((uint32)func < std::size(MTL_COMPARE_FUNCTIONS)); return MTL_COMPARE_FUNCTIONS[(uint32)func]; } + +// TODO: clamp to border color? (should be fine though) +const MTL::SamplerAddressMode MTL_SAMPLER_ADDRESS_MODES[] = { + MTL::SamplerAddressModeRepeat, // WRAP + MTL::SamplerAddressModeMirrorRepeat, // MIRROR + MTL::SamplerAddressModeClampToEdge, // CLAMP_LAST_TEXEL + MTL::SamplerAddressModeMirrorClampToEdge, // MIRROR_ONCE_LAST_TEXEL + MTL::SamplerAddressModeClampToEdge, // unsupported HALF_BORDER + MTL::SamplerAddressModeClampToBorderColor, // unsupported MIRROR_ONCE_HALF_BORDER + MTL::SamplerAddressModeClampToBorderColor, // CLAMP_BORDER + MTL::SamplerAddressModeClampToBorderColor // MIRROR_ONCE_BORDER +}; + +MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_CLAMP clamp) +{ + cemu_assert_debug((uint32)clamp < std::size(MTL_SAMPLER_ADDRESS_MODES)); + return MTL_SAMPLER_ADDRESS_MODES[(uint32)clamp]; +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h index ed99098d..153b90a0 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h @@ -7,6 +7,7 @@ //#include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Metal/MTLDepthStencil.hpp" +#include "Metal/MTLSampler.hpp" struct Uvec2 { uint32 x; @@ -36,3 +37,5 @@ MTL::BlendOperation GetMtlBlendOp(Latte::LATTE_CB_BLENDN_CONTROL::E_COMBINEFUNC MTL::BlendFactor GetMtlBlendFactor(Latte::LATTE_CB_BLENDN_CONTROL::E_BLENDFACTOR factor); MTL::CompareFunction GetMtlCompareFunc(Latte::E_COMPAREFUNC func); + +MTL::SamplerAddressMode GetMtlSamplerAddressMode(Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_CLAMP clamp); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 2c5b7d61..395f3fd3 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -15,8 +15,8 @@ #include "Cafe/HW/Latte/Core/LatteIndices.h" #include "Cemu/Logging/CemuDebugLogging.h" #include "HW/Latte/Core/Latte.h" +#include "HW/Latte/ISA/LatteReg.h" #include "gui/guiWrapper.h" -#include extern bool hasValidFramebufferAttached; @@ -863,7 +863,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE continue; } - //LatteTexture* baseTexture = textureView->baseTexture; + LatteTexture* baseTexture = textureView->baseTexture; // get texture register word 0 uint32 word4 = LatteGPUState.contextRegister[texUnitRegIndex + 4]; @@ -878,8 +878,108 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE uint32 stageSamplerIndex = shader->textureUnitSamplerAssignment[relative_textureUnit]; if (stageSamplerIndex != LATTE_DECOMPILER_SAMPLER_NONE) { - // TODO: bind the actual sampler - MTL::SamplerState* sampler = m_nearestSampler; + uint32 samplerIndex = stageSamplerIndex + LatteDecompiler_getTextureSamplerBaseIndex(shader->shaderType); + const _LatteRegisterSetSampler* samplerWords = LatteGPUState.contextNew.SQ_TEX_SAMPLER + samplerIndex; + + // TODO: cache this instead + MTL::SamplerDescriptor* samplerDescriptor = MTL::SamplerDescriptor::alloc()->init(); + + // lod + uint32 iMinLOD = samplerWords->WORD1.get_MIN_LOD(); + uint32 iMaxLOD = samplerWords->WORD1.get_MAX_LOD(); + sint32 iLodBias = samplerWords->WORD1.get_LOD_BIAS(); + + // apply relative lod bias from graphic pack + if (baseTexture->overwriteInfo.hasRelativeLodBias) + iLodBias += baseTexture->overwriteInfo.relativeLodBias; + // apply absolute lod bias from graphic pack + if (baseTexture->overwriteInfo.hasLodBias) + iLodBias = baseTexture->overwriteInfo.lodBias; + + auto filterMip = samplerWords->WORD0.get_MIP_FILTER(); + if (filterMip == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_Z_FILTER::NONE) + { + samplerDescriptor->setMipFilter(MTL::SamplerMipFilterNearest); + samplerDescriptor->setLodMinClamp(0.0f); + samplerDescriptor->setLodMaxClamp(0.25f); + } + else if (filterMip == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_Z_FILTER::POINT) + { + samplerDescriptor->setMipFilter(MTL::SamplerMipFilterNearest); + samplerDescriptor->setLodMinClamp((float)iMinLOD / 64.0f); + samplerDescriptor->setLodMaxClamp((float)iMaxLOD / 64.0f); + } + else if (filterMip == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_Z_FILTER::LINEAR) + { + samplerDescriptor->setMipFilter(MTL::SamplerMipFilterLinear); + samplerDescriptor->setLodMinClamp((float)iMinLOD / 64.0f); + samplerDescriptor->setLodMaxClamp((float)iMaxLOD / 64.0f); + } + else + { + // fallback for invalid constants + samplerDescriptor->setMipFilter(MTL::SamplerMipFilterLinear); + samplerDescriptor->setLodMinClamp((float)iMinLOD / 64.0f); + samplerDescriptor->setLodMaxClamp((float)iMaxLOD / 64.0f); + } + + auto filterMin = samplerWords->WORD0.get_XY_MIN_FILTER(); + cemu_assert_debug(filterMin != Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER::BICUBIC); // todo + samplerDescriptor->setMinFilter((filterMin == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER::POINT || filterMin == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER::ANISO_POINT) ? MTL::SamplerMinMagFilterNearest : MTL::SamplerMinMagFilterLinear); + + auto filterMag = samplerWords->WORD0.get_XY_MAG_FILTER(); + samplerDescriptor->setMagFilter((filterMag == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER::POINT || filterMin == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_XY_FILTER::ANISO_POINT) ? MTL::SamplerMinMagFilterNearest : MTL::SamplerMinMagFilterLinear); + + auto filterZ = samplerWords->WORD0.get_Z_FILTER(); + // todo: z-filter for texture array samplers is customizable for GPU7 but OpenGL/Vulkan doesn't expose this functionality? + + auto clampX = samplerWords->WORD0.get_CLAMP_X(); + 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)); + + auto maxAniso = samplerWords->WORD0.get_MAX_ANISO_RATIO(); + + if (baseTexture->overwriteInfo.anisotropicLevel >= 0) + maxAniso = baseTexture->overwriteInfo.anisotropicLevel; + + if (maxAniso > 0) + { + samplerDescriptor->setMaxAnisotropy(1 << maxAniso); + } + + // TODO: set lod bias + //samplerInfo.mipLodBias = (float)iLodBias / 64.0f; + + // depth compare + uint8 depthCompareMode = shader->textureUsesDepthCompare[relative_textureUnit] ? 1 : 0; + if (depthCompareMode == 1) + { + // TODO: is it okay to just cast? + samplerDescriptor->setCompareFunction(GetMtlCompareFunc((Latte::E_COMPAREFUNC)samplerWords->WORD0.get_DEPTH_COMPARE_FUNCTION())); + } + + // border + auto borderType = samplerWords->WORD0.get_BORDER_COLOR_TYPE(); + + if (borderType == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_BORDER_COLOR_TYPE::TRANSPARENT_BLACK) + samplerDescriptor->setBorderColor(MTL::SamplerBorderColorTransparentBlack); + else if (borderType == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_BORDER_COLOR_TYPE::OPAQUE_BLACK) + samplerDescriptor->setBorderColor(MTL::SamplerBorderColorOpaqueBlack); + else if (borderType == Latte::LATTE_SQ_TEX_SAMPLER_WORD0_0::E_BORDER_COLOR_TYPE::OPAQUE_WHITE) + samplerDescriptor->setBorderColor(MTL::SamplerBorderColorOpaqueWhite); + else + { + // Metal doesn't support custom border color + samplerDescriptor->setBorderColor(MTL::SamplerBorderColorOpaqueBlack); + } + + MTL::SamplerState* sampler = m_device->newSamplerState(samplerDescriptor); + samplerDescriptor->release(); + switch (shader->shaderType) { case LatteConst::ShaderType::Vertex: @@ -895,6 +995,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE default: UNREACHABLE; } + sampler->release(); } switch (shader->shaderType)