fix: srgb

This commit is contained in:
Samuliak 2024-08-12 11:36:12 +02:00
parent 5e748cdb48
commit 34d8076ab6
2 changed files with 101 additions and 112 deletions

View File

@ -15,6 +15,7 @@
#include "Cafe/HW/Latte/Core/LatteShader.h"
#include "Cafe/HW/Latte/Core/LatteIndices.h"
#include "Cemu/Logging/CemuDebugLogging.h"
#include "Metal/MTLPixelFormat.hpp"
#include "gui/guiWrapper.h"
extern bool hasValidFramebufferAttached;
@ -45,13 +46,13 @@ MetalRenderer::MetalRenderer()
{
for (uint32 j = 0; j < MAX_MTL_BUFFERS; j++)
{
m_state.uniformBufferOffsets[i][j] = INVALID_OFFSET;
m_state.m_uniformBufferOffsets[i][j] = INVALID_OFFSET;
}
}
// Utility shader source
NS::Error* error = nullptr;
m_utilityLibrary = m_device->newLibrary(NS::String::string(utilityShaderSource, NS::ASCIIStringEncoding), nullptr, &error);
MTL::Library* utilityLibrary = m_device->newLibrary(NS::String::string(utilityShaderSource, NS::ASCIIStringEncoding), nullptr, &error);
if (error)
{
debug_printf("failed to create present library (error: %s)\n", error->localizedDescription()->utf8String());
@ -59,17 +60,46 @@ MetalRenderer::MetalRenderer()
return;
}
// Present pipeline
MTL::Function* presentVertexFunction = utilityLibrary->newFunction(NS::String::string("vertexFullscreen", NS::ASCIIStringEncoding));
MTL::Function* presentFragmentFunction = utilityLibrary->newFunction(NS::String::string("fragmentPresent", NS::ASCIIStringEncoding));
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
renderPipelineDescriptor->setVertexFunction(presentVertexFunction);
renderPipelineDescriptor->setFragmentFunction(presentFragmentFunction);
presentVertexFunction->release();
presentFragmentFunction->release();
error = nullptr;
renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(MTL::PixelFormatRGBA8Unorm);
m_presentPipelineLinear = m_device->newRenderPipelineState(renderPipelineDescriptor, &error);
if (error)
{
debug_printf("failed to create linear present pipeline (error: %s)\n", error->localizedDescription()->utf8String());
error->release();
}
error = nullptr;
renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(MTL::PixelFormatRGBA8Unorm_sRGB);
m_presentPipelineSRGB = m_device->newRenderPipelineState(renderPipelineDescriptor, &error);
renderPipelineDescriptor->release();
if (error)
{
debug_printf("failed to create sRGB present pipeline (error: %s)\n", error->localizedDescription()->utf8String());
error->release();
}
// Hybrid pipelines
m_copyTextureToTexturePipeline = new MetalHybridComputePipeline(this, m_utilityLibrary, "vertexCopyTextureToTexture", "kernelCopyTextureToTexture");
m_copyTextureToTexturePipeline = new MetalHybridComputePipeline(this, utilityLibrary, "vertexCopyTextureToTexture", "kernelCopyTextureToTexture");
utilityLibrary->release();
}
MetalRenderer::~MetalRenderer()
{
delete m_copyTextureToTexturePipeline;
m_presentPipeline->release();
m_utilityLibrary->release();
m_presentPipelineLinear->release();
m_presentPipelineSRGB->release();
delete m_depthStencilCache;
delete m_pipelineCache;
@ -90,29 +120,6 @@ void MetalRenderer::InitializeLayer(const Vector2i& size, bool mainWindow)
m_metalLayer = (CA::MetalLayer*)CreateMetalLayer(windowInfo.handle);
m_metalLayer->setDevice(m_device);
// TODO: don't always force sRGB
// TODO: shouldn't this be handled differently?
m_metalLayer->setPixelFormat(MTL::PixelFormatRGBA8Unorm/*_sRGB*/);
// Present pipeline
MTL::Function* presentVertexFunction = m_utilityLibrary->newFunction(NS::String::string("vertexFullscreen", NS::ASCIIStringEncoding));
MTL::Function* presentFragmentFunction = m_utilityLibrary->newFunction(NS::String::string("fragmentPresent", NS::ASCIIStringEncoding));
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
renderPipelineDescriptor->setVertexFunction(presentVertexFunction);
renderPipelineDescriptor->setFragmentFunction(presentFragmentFunction);
renderPipelineDescriptor->colorAttachments()->object(0)->setPixelFormat(m_metalLayer->pixelFormat());
NS::Error* error = nullptr;
m_presentPipeline = m_device->newRenderPipelineState(renderPipelineDescriptor, &error);
renderPipelineDescriptor->release();
presentVertexFunction->release();
presentFragmentFunction->release();
if (error)
{
debug_printf("failed to create present pipeline (error: %s)\n", error->localizedDescription()->utf8String());
error->release();
}
}
void MetalRenderer::Initialize()
@ -146,7 +153,7 @@ bool MetalRenderer::GetVRAMInfo(int& usageInMB, int& totalInMB) const
void MetalRenderer::ClearColorbuffer(bool padView)
{
if (!AcquireNextDrawable())
if (!AcquireNextDrawable(!padView))
return;
ClearColorTextureInternal(m_drawable->texture(), 0, 0, 0.0f, 0.0f, 0.0f, 0.0f);
@ -183,9 +190,12 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput
sint32 imageX, sint32 imageY, sint32 imageWidth, sint32 imageHeight,
bool padView, bool clearBackground)
{
if (!AcquireNextDrawable())
if (!AcquireNextDrawable(!padView))
return;
if (clearBackground)
ClearColorbuffer(padView);
MTL::Texture* presentTexture = static_cast<LatteTextureViewMtl*>(texView)->GetRGBAView();
// Create render pass
@ -199,7 +209,7 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput
renderPassDescriptor->release();
// Draw to Metal layer
renderCommandEncoder->setRenderPipelineState(m_presentPipeline);
renderCommandEncoder->setRenderPipelineState(m_state.m_usesSRGB ? m_presentPipelineSRGB : m_presentPipelineLinear);
renderCommandEncoder->setFragmentTexture(presentTexture, 0);
renderCommandEncoder->setFragmentSamplerState(m_nearestSampler, 0);
@ -208,7 +218,7 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput
bool MetalRenderer::BeginFrame(bool mainWindow)
{
return AcquireNextDrawable();
return AcquireNextDrawable(mainWindow);
}
void MetalRenderer::Flush(bool waitIdle)
@ -234,19 +244,19 @@ void MetalRenderer::AppendOverlayDebugInfo()
void MetalRenderer::renderTarget_setViewport(float x, float y, float width, float height, float nearZ, float farZ, bool halfZ)
{
m_state.viewport = MTL::Viewport{x, y, width, height, nearZ, farZ};
m_state.m_viewport = MTL::Viewport{x, y, width, height, nearZ, farZ};
if (m_encoderType == MetalEncoderType::Render)
{
static_cast<MTL::RenderCommandEncoder*>(m_commandEncoder)->setViewport(m_state.viewport);
static_cast<MTL::RenderCommandEncoder*>(m_commandEncoder)->setViewport(m_state.m_viewport);
}
}
void MetalRenderer::renderTarget_setScissor(sint32 scissorX, sint32 scissorY, sint32 scissorWidth, sint32 scissorHeight)
{
m_state.scissor = MTL::ScissorRect{NS::UInteger(scissorX), NS::UInteger(scissorY), NS::UInteger(scissorWidth), NS::UInteger(scissorHeight)};
m_state.m_scissor = MTL::ScissorRect{NS::UInteger(scissorX), NS::UInteger(scissorY), NS::UInteger(scissorWidth), NS::UInteger(scissorHeight)};
if (m_encoderType == MetalEncoderType::Render)
{
static_cast<MTL::RenderCommandEncoder*>(m_commandEncoder)->setScissorRect(m_state.scissor);
static_cast<MTL::RenderCommandEncoder*>(m_commandEncoder)->setScissorRect(m_state.m_scissor);
}
}
@ -257,13 +267,13 @@ LatteCachedFBO* MetalRenderer::rendertarget_createCachedFBO(uint64 key)
void MetalRenderer::rendertarget_deleteCachedFBO(LatteCachedFBO* cfbo)
{
if (cfbo == (LatteCachedFBO*)m_state.activeFBO)
m_state.activeFBO = nullptr;
if (cfbo == (LatteCachedFBO*)m_state.m_activeFBO)
m_state.m_activeFBO = nullptr;
}
void MetalRenderer::rendertarget_bindFramebufferObject(LatteCachedFBO* cfbo)
{
m_state.activeFBO = (CachedFBOMtl*)cfbo;
m_state.m_activeFBO = (CachedFBOMtl*)cfbo;
}
void* MetalRenderer::texture_acquireTextureUploadBuffer(uint32 size)
@ -347,7 +357,7 @@ LatteTexture* MetalRenderer::texture_createTextureEx(Latte::E_DIM dim, MPTR phys
void MetalRenderer::texture_setLatteTexture(LatteTextureView* textureView, uint32 textureUnit)
{
m_state.textures[textureUnit] = static_cast<LatteTextureViewMtl*>(textureView);
m_state.m_textures[textureUnit] = static_cast<LatteTextureViewMtl*>(textureView);
}
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_)
@ -531,7 +541,7 @@ void MetalRenderer::bufferCache_copyStreamoutToMainBuffer(uint32 srcOffset, uint
void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, uint32 size)
{
cemu_assert_debug(bufferIndex < LATTE_MAX_VERTEX_BUFFERS);
auto& buffer = m_state.vertexBuffers[bufferIndex];
auto& buffer = m_state.m_vertexBuffers[bufferIndex];
if (buffer.offset == offset && buffer.size == size)
return;
@ -550,7 +560,7 @@ void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, u
void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size)
{
m_state.uniformBufferOffsets[(uint32)shaderType][bufferIndex] = offset;
m_state.m_uniformBufferOffsets[(uint32)shaderType][bufferIndex] = offset;
}
RendererShader* MetalRenderer::shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool isGameShader, bool isGfxPackShader)
@ -575,14 +585,14 @@ void MetalRenderer::streamout_rendererFinishDrawcall()
void MetalRenderer::draw_beginSequence()
{
m_state.skipDrawSequence = false;
m_state.m_skipDrawSequence = false;
// update shader state
LatteSHRC_UpdateActiveShaders();
if (LatteGPUState.activeShaderHasError)
{
debug_printf("Skipping drawcalls due to shader error\n");
m_state.skipDrawSequence = true;
m_state.m_skipDrawSequence = true;
cemu_assert_debug(false);
return;
}
@ -595,14 +605,14 @@ void MetalRenderer::draw_beginSequence()
if (!LatteMRT::UpdateCurrentFBO())
{
debug_printf("Rendertarget invalid\n");
m_state.skipDrawSequence = true;
m_state.m_skipDrawSequence = true;
return; // no render target
}
if (!hasValidFramebufferAttached)
{
debug_printf("Drawcall with no color buffer or depth buffer attached\n");
m_state.skipDrawSequence = true;
m_state.m_skipDrawSequence = true;
return; // no render target
}
LatteTexture_updateTextures();
@ -626,7 +636,7 @@ void MetalRenderer::draw_beginSequence()
rasterizerEnable = true;
if (!rasterizerEnable == false)
m_state.skipDrawSequence = true;
m_state.m_skipDrawSequence = true;
}
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)
@ -637,24 +647,24 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
//}
// Render pass
if (!m_state.activeFBO)
if (!m_state.m_activeFBO)
{
debug_printf("no active FBO, skipping draw\n");
return;
}
auto renderPassDescriptor = m_state.activeFBO->GetRenderPassDescriptor();
auto renderPassDescriptor = m_state.m_activeFBO->GetRenderPassDescriptor();
MTL::Texture* colorRenderTargets[8] = {nullptr};
MTL::Texture* depthRenderTarget = nullptr;
for (uint32 i = 0; i < 8; i++)
{
auto colorTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->colorBuffer[i].texture);
auto colorTexture = static_cast<LatteTextureViewMtl*>(m_state.m_activeFBO->colorBuffer[i].texture);
if (colorTexture)
{
colorRenderTargets[i] = colorTexture->GetRGBAView();
}
}
auto depthTexture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->depthBuffer.texture);
auto depthTexture = static_cast<LatteTextureViewMtl*>(m_state.m_activeFBO->depthBuffer.texture);
if (depthTexture)
{
depthRenderTarget = depthTexture->GetRGBAView();
@ -672,7 +682,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
const auto fetchShader = LatteSHRC_GetActiveFetchShader();
// Render pipeline state
MTL::RenderPipelineState* renderPipelineState = m_pipelineCache->GetPipelineState(fetchShader, vertexShader, pixelShader, m_state.lastUsedFBO, LatteGPUState.contextNew);
MTL::RenderPipelineState* renderPipelineState = m_pipelineCache->GetPipelineState(fetchShader, vertexShader, pixelShader, m_state.m_lastUsedFBO, LatteGPUState.contextNew);
renderCommandEncoder->setRenderPipelineState(renderPipelineState);
// Depth stencil state
@ -758,7 +768,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
// Vertex buffers
for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++)
{
auto& vertexBufferRange = m_state.vertexBuffers[i];
auto& vertexBufferRange = m_state.m_vertexBuffers[i];
if (vertexBufferRange.offset != INVALID_OFFSET)
{
// Restride
@ -846,7 +856,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
bool needsNewRenderPass = false;
for (uint8 i = 0; i < 8; i++)
{
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i]))
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.m_colorRenderTargets[i]))
{
needsNewRenderPass = true;
break;
@ -855,7 +865,7 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
if (!needsNewRenderPass)
{
if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget))
if (depthRenderTarget && (depthRenderTarget != m_state.m_depthRenderTarget))
{
needsNewRenderPass = true;
}
@ -872,12 +882,12 @@ MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPas
}
// Update state
m_state.lastUsedFBO = m_state.activeFBO;
m_state.m_lastUsedFBO = m_state.m_activeFBO;
for (uint8 i = 0; i < 8; i++)
{
m_state.colorRenderTargets[i] = colorRenderTargets[i];
m_state.m_colorRenderTargets[i] = colorRenderTargets[i];
}
m_state.depthRenderTarget = depthRenderTarget;
m_state.m_depthRenderTarget = depthRenderTarget;
auto renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor);
m_commandEncoder = renderCommandEncoder;
@ -959,8 +969,15 @@ void MetalRenderer::CommitCommandBuffer()
}
}
bool MetalRenderer::AcquireNextDrawable()
bool MetalRenderer::AcquireNextDrawable(bool mainWindow)
{
const bool latteBufferUsesSRGB = mainWindow ? LatteGPUState.tvBufferUsesSRGB : LatteGPUState.drcBufferUsesSRGB;
if (latteBufferUsesSRGB != m_state.m_usesSRGB)
{
m_metalLayer->setPixelFormat(latteBufferUsesSRGB ? MTL::PixelFormatRGBA8Unorm_sRGB : MTL::PixelFormatRGBA8Unorm);
m_state.m_usesSRGB = latteBufferUsesSRGB;
}
if (m_drawable)
{
// TODO: should this be true?
@ -1005,7 +1022,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
UNREACHABLE;
}
auto textureView = m_state.textures[hostTextureUnit];
auto textureView = m_state.m_textures[hostTextureUnit];
if (!textureView)
{
debug_printf("invalid bound texture view %u\n", hostTextureUnit);
@ -1283,7 +1300,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
debug_printf("too big buffer index (%u), skipping binding\n", binding);
continue;
}
size_t offset = m_state.uniformBufferOffsets[(uint32)shader->shaderType][i];
size_t offset = m_state.m_uniformBufferOffsets[(uint32)shader->shaderType][i];
if (offset != INVALID_OFFSET)
{
switch (shader->shaderType)
@ -1329,49 +1346,15 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
void MetalRenderer::RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder)
{
// Viewport
//if (m_state.viewport.width != 0.0)
//{
renderCommandEncoder->setViewport(m_state.viewport);
/*
}
else
{
// Find the framebuffer dimensions
uint32 framebufferWidth = 0, framebufferHeight = 0;
if (m_state.activeFBO->hasDepthBuffer())
{
framebufferHeight = m_state.activeFBO->depthBuffer.texture->baseTexture->width;
framebufferHeight = m_state.activeFBO->depthBuffer.texture->baseTexture->height;
}
else
{
for (uint8 i = 0; i < 8; i++)
{
auto texture = m_state.activeFBO->colorBuffer[i].texture;
if (texture)
{
framebufferWidth = texture->baseTexture->width;
framebufferHeight = texture->baseTexture->height;
break;
}
}
}
MTL::Viewport viewport{0, (double)framebufferHeight, (double)framebufferWidth, -(double)framebufferHeight, 0.0, 1.0};
renderCommandEncoder->setViewport(viewport);
}
*/
renderCommandEncoder->setViewport(m_state.m_viewport);
// Scissor
//if (m_state.scissor.width != 0)
//{
renderCommandEncoder->setScissorRect(m_state.scissor);
//}
renderCommandEncoder->setScissorRect(m_state.m_scissor);
// Vertex buffers
for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++)
{
auto& vertexBufferRange = m_state.vertexBuffers[i];
auto& vertexBufferRange = m_state.m_vertexBuffers[i];
if (vertexBufferRange.offset != INVALID_OFFSET)
vertexBufferRange.needsRebind = true;
}

View File

@ -27,18 +27,24 @@ struct MetalBoundBuffer
struct MetalState
{
bool skipDrawSequence = false;
class CachedFBOMtl* activeFBO = nullptr;
bool m_usesSRGB = false;
bool m_skipDrawSequence = false;
class CachedFBOMtl* m_activeFBO = nullptr;
// If the FBO changes, but it's the same FBO as the last one with some omitted attachments, this FBO doesn't change'
class CachedFBOMtl* lastUsedFBO = nullptr;
MetalBoundBuffer vertexBuffers[MAX_MTL_BUFFERS] = {{}};
class CachedFBOMtl* m_lastUsedFBO = nullptr;
MetalBoundBuffer m_vertexBuffers[MAX_MTL_BUFFERS] = {{}};
// TODO: find out what is the max number of bound textures on the Wii U
class LatteTextureViewMtl* textures[64] = {nullptr};
size_t uniformBufferOffsets[(uint32)LatteConst::ShaderType::TotalCount][MAX_MTL_BUFFERS];
MTL::Texture* colorRenderTargets[8] = {nullptr};
MTL::Texture* depthRenderTarget = nullptr;
MTL::Viewport viewport = {0, 0, 0, 0, 0, 0};
MTL::ScissorRect scissor = {0, 0, 0, 0};
class LatteTextureViewMtl* m_textures[64] = {nullptr};
size_t m_uniformBufferOffsets[(uint32)LatteConst::ShaderType::TotalCount][MAX_MTL_BUFFERS];
MTL::Texture* m_colorRenderTargets[8] = {nullptr};
MTL::Texture* m_depthRenderTarget = nullptr;
MTL::Viewport m_viewport = {0, 0, 0, 0, 0, 0};
MTL::ScissorRect m_scissor = {0, 0, 0, 0};
};
enum class MetalEncoderType
@ -232,7 +238,7 @@ public:
void EndEncoding();
void CommitCommandBuffer();
bool AcquireNextDrawable();
bool AcquireNextDrawable(bool mainWindow);
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
@ -257,8 +263,8 @@ private:
MTL::CommandQueue* m_commandQueue;
// Pipelines
MTL::Library* m_utilityLibrary;
MTL::RenderPipelineState* m_presentPipeline;
MTL::RenderPipelineState* m_presentPipelineLinear;
MTL::RenderPipelineState* m_presentPipelineSRGB;
// Hybrid pipelines
class MetalHybridComputePipeline* m_copyTextureToTexturePipeline;