diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.cpp index 6e609e4f..53dd8b10 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.cpp @@ -80,6 +80,11 @@ void MetalMemoryManager::InitBufferCache(size_t size) void MetalMemoryManager::UploadToBufferCache(const void* data, size_t offset, size_t size) { + if ((offset + size) > m_bufferCache->length()) + { + throw std::runtime_error(std::to_string(offset) + " + " + std::to_string(size) + " > " + std::to_string(m_bufferCache->length())); + } + if (!m_bufferCache) { printf("MetalMemoryManager::UploadToBufferCache: buffer cache not initialized\n"); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 9874f771..055b9aa0 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -29,6 +29,15 @@ MetalRenderer::MetalRenderer() m_nearestSampler = m_device->newSamplerState(samplerDescriptor); m_memoryManager = new MetalMemoryManager(this); + + // Initialize state + for (uint32 i = 0; i < (uint32)LatteConst::ShaderType::TotalCount; i++) + { + for (uint32 j = 0; j < MAX_MTL_BUFFERS; j++) + { + m_state.uniformBufferOffsets[i][j] = INVALID_OFFSET; + } + } } MetalRenderer::~MetalRenderer() @@ -149,7 +158,8 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput MTL::Texture* colorRenderTargets[8] = {nullptr}; colorRenderTargets[0] = m_drawable->texture(); - auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, nullptr); + // If there was already an encoder with these attachment, we should set the viewport and scissor to default, but that shouldn't happen + auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, nullptr, false); // Draw to Metal layer renderCommandEncoder->setRenderPipelineState(m_presentPipeline); @@ -435,7 +445,7 @@ void MetalRenderer::buffer_bindVertexBuffer(uint32 bufferIndex, uint32 offset, u void MetalRenderer::buffer_bindUniformBuffer(LatteConst::ShaderType shaderType, uint32 bufferIndex, uint32 offset, uint32 size) { - printf("MetalRenderer::buffer_bindUniformBuffer not implemented\n"); + m_state.uniformBufferOffsets[(uint32)shaderType][bufferIndex] = offset; } RendererShader* MetalRenderer::shader_create(RendererShader::ShaderType type, uint64 baseHash, uint64 auxHash, const std::string& source, bool isGameShader, bool isGfxPackShader) @@ -872,22 +882,29 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE if (shader->resourceMapping.uniformBuffersBindingPoint[i] >= 0) { uint32 binding = shader->resourceMapping.uniformBuffersBindingPoint[i]; - // TODO: don't hardcode - size_t offset = 0; - switch (shader->shaderType) + if (binding >= MAX_MTL_BUFFERS) { - case LatteConst::ShaderType::Vertex: - { - renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), offset, binding); - break; + printf("too big buffer index (%u), skipping binding\n", binding); + continue; } - case LatteConst::ShaderType::Pixel: + size_t offset = m_state.uniformBufferOffsets[(uint32)shader->shaderType][binding]; + if (offset != INVALID_OFFSET) { - renderCommandEncoder->setFragmentBuffer(m_memoryManager->GetBufferCache(), offset, binding); - break; - } - default: - UNREACHABLE; + switch (shader->shaderType) + { + case LatteConst::ShaderType::Vertex: + { + renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), offset, binding); + break; + } + case LatteConst::ShaderType::Pixel: + { + renderCommandEncoder->setFragmentBuffer(m_memoryManager->GetBufferCache(), offset, binding); + break; + } + default: + UNREACHABLE; + } } } } @@ -896,10 +913,10 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE void MetalRenderer::RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder) { // Viewport - if (m_state.viewport.width != 0.0) - { - printf("setting previous viewport X: %f Y: %f width: %f height %f\n", m_state.viewport.originX, m_state.viewport.originY, m_state.viewport.width, m_state.viewport.height); - renderCommandEncoder->setViewport(m_state.viewport); + //if (m_state.viewport.width != 0.0) + //{ + renderCommandEncoder->setViewport(m_state.viewport); + /* } else { @@ -919,26 +936,27 @@ void MetalRenderer::RebindRenderState(MTL::RenderCommandEncoder* renderCommandEn { framebufferWidth = texture->baseTexture->width; framebufferHeight = texture->baseTexture->height; + break; } } } MTL::Viewport viewport{0, (double)framebufferHeight, (double)framebufferWidth, -(double)framebufferHeight, 0.0, 1.0}; - printf("setting default viewport X: %f Y: %f width: %f height %f\n", viewport.originX, viewport.originY, viewport.width, viewport.height); renderCommandEncoder->setViewport(viewport); } + */ // Scissor - if (m_state.scissor.width != 0) - { - renderCommandEncoder->setScissorRect(m_state.scissor); - } + //if (m_state.scissor.width != 0) + //{ + renderCommandEncoder->setScissorRect(m_state.scissor); + //} // Vertex buffers for (uint8 i = 0; i < MAX_MTL_BUFFERS; i++) { auto& vertexBufferRange = m_state.vertexBuffers[i]; - if (vertexBufferRange.offset != -1) + if (vertexBufferRange.offset != INVALID_OFFSET) { renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i)); vertexBufferRange.needsRebind = false; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h index 2816be19..fff6c6c2 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h @@ -18,10 +18,12 @@ #define MAX_MTL_TEXTURES 31 +constexpr size_t INVALID_OFFSET = std::numeric_limits::max(); + struct MetalBoundBuffer { bool needsRebind = false; - sint32 offset = -1; + size_t offset = INVALID_OFFSET; }; struct MetalState @@ -30,6 +32,7 @@ struct MetalState class CachedFBOMtl* activeFBO = nullptr; MetalBoundBuffer vertexBuffers[MAX_MTL_BUFFERS] = {{}}; class LatteTextureViewMtl* textures[MAX_MTL_TEXTURES] = {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}; @@ -210,7 +213,7 @@ private: MTL::CommandBuffer* m_commandBuffer = nullptr; MetalEncoderType m_encoderType = MetalEncoderType::None; MTL::CommandEncoder* m_commandEncoder = nullptr; - CA::MetalDrawable* m_drawable; + CA::MetalDrawable* m_drawable = nullptr; // State MetalState m_state; @@ -227,7 +230,7 @@ private: } } - MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget) + MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool rebindStateIfNewEncoder = true) { EnsureCommandBuffer(); @@ -274,8 +277,11 @@ private: m_commandEncoder = renderCommandEncoder; m_encoderType = MetalEncoderType::Render; - // Rebind all the render state - RebindRenderState(renderCommandEncoder); + if (rebindStateIfNewEncoder) + { + // Rebind all the render state + RebindRenderState(renderCommandEncoder); + } return renderCommandEncoder; }