rework command encoder system

This commit is contained in:
Samuliak 2024-07-29 19:00:13 +02:00
parent 89a2c23dd7
commit f01130022a
8 changed files with 149 additions and 77 deletions

View File

@ -134,7 +134,7 @@ namespace LatteDecompiler
//shaderSrc->addFmt("UNIFORM_BUFFER_LAYOUT({}, {}, {}) ", (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i], (sint32)decompilerContext->output->resourceMappingVK.setIndex, (sint32)decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i]);
shaderSrc->addFmt("struct UBuff{} {" _CRLF, i);
shaderSrc->addFmt("struct UBuff{} {{" _CRLF, i);
shaderSrc->addFmt("float4 d{}[{}];" _CRLF, i, decompilerContext->analyzer.uniformBufferAccessTracker[i].DetermineSize(decompilerContext->shaderBaseHash, LATTE_GLSL_DYNAMIC_UNIFORM_BLOCK_SIZE));
shaderSrc->add("};" _CRLF _CRLF);
}
@ -307,7 +307,7 @@ namespace LatteDecompiler
cemu_assert_debug(decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i] >= 0);
cemu_assert_debug(decompilerContext->output->resourceMappingVK.uniformBuffersBindingPoint[i] >= 0);
src->addFmt("constant UBuff{}& ubuff{} [[buffer({})]]" _CRLF, i, i, (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i]);
src->addFmt(", constant UBuff{}& ubuff{} [[buffer({})]]", i, i, (sint32)decompilerContext->output->resourceMappingGL.uniformBuffersBindingPoint[i]);
}
}
}

View File

@ -5,7 +5,7 @@
LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle,
Latte::E_HWTILEMODE tileMode, bool isDepth)
: LatteTexture(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth), m_mtlr(mtlRenderer), m_format(format)
: LatteTexture(dim, physAddress, physMipAddress, format, width, height, depth, pitch, mipLevels, swizzle, tileMode, isDepth), m_mtlr(mtlRenderer), m_format(format), m_isDepth(isDepth)
{
MTL::TextureDescriptor* desc = MTL::TextureDescriptor::alloc()->init();
desc->setStorageMode(MTL::StorageModeShared); // TODO: use private?
@ -34,7 +34,7 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM
desc->setArrayLength(effectiveBaseDepth);
}
auto formatInfo = GetMtlPixelFormatInfo(format);
auto formatInfo = GetMtlPixelFormatInfo(format, isDepth);
desc->setPixelFormat(formatInfo.pixelFormat);
// TODO: is write needed?

View File

@ -21,23 +21,20 @@ public:
return m_format;
}
bool IsDepth() const {
return m_isDepth;
}
void AllocateOnHost() override;
protected:
LatteTextureView* CreateView(Latte::E_DIM dim, Latte::E_GX2SURFFMT format, sint32 firstMip, sint32 mipCount, sint32 firstSlice, sint32 sliceCount) override;
public:
uint64 m_vkFlushIndex{}; // used to track read-write dependencies within the same renderpass
uint64 m_vkFlushIndex_read{};
uint64 m_vkFlushIndex_write{};
uint32 m_collisionCheckIndex{}; // used to track if texture is being both sampled and output to during drawcall
private:
class MetalRenderer* m_mtlr;
MTL::Texture* m_texture;
Latte::E_GX2SURFFMT m_format;
bool m_isDepth;
};

View File

@ -52,7 +52,7 @@ LatteTextureViewMtl::LatteTextureViewMtl(MetalRenderer* mtlRenderer, LatteTextur
// TODO: swizzle
auto formatInfo = GetMtlPixelFormatInfo(format);
auto formatInfo = GetMtlPixelFormatInfo(format, texture->IsDepth());
m_texture = texture->GetTexture()->newTextureView(formatInfo.pixelFormat, textureType, NS::Range::Make(baseLevel, levelCount), NS::Range::Make(baseLayer, layerCount));
}

View File

@ -3,8 +3,7 @@
#include "Metal/MTLPixelFormat.hpp"
#include "Metal/MTLVertexDescriptor.hpp"
// TODO: separate color and depth formats
std::map<Latte::E_GX2SURFFMT, MtlPixelFormatInfo> MTL_FORMAT_TABLE = {
std::map<Latte::E_GX2SURFFMT, MtlPixelFormatInfo> MTL_COLOR_FORMAT_TABLE = {
{Latte::E_GX2SURFFMT::R4_G4_UNORM, {MTL::PixelFormatRG8Unorm, 2}}, // TODO: correct?
{Latte::E_GX2SURFFMT::R5_G6_B5_UNORM, {MTL::PixelFormatB5G6R5Unorm, 2}}, // TODO: correct?
{Latte::E_GX2SURFFMT::R5_G5_B5_A1_UNORM, {MTL::PixelFormatBGR5A1Unorm, 2}}, // TODO: correct?
@ -60,11 +59,6 @@ std::map<Latte::E_GX2SURFFMT, MtlPixelFormatInfo> MTL_FORMAT_TABLE = {
{Latte::E_GX2SURFFMT::R32_G32_B32_A32_UINT, {MTL::PixelFormatRGBA32Uint, 16}},
{Latte::E_GX2SURFFMT::R32_G32_B32_A32_SINT, {MTL::PixelFormatRGBA32Sint, 16}},
{Latte::E_GX2SURFFMT::R32_G32_B32_A32_FLOAT, {MTL::PixelFormatRGBA32Float, 16}},
{Latte::E_GX2SURFFMT::D24_S8_UNORM, {MTL::PixelFormatDepth24Unorm_Stencil8, 4}}, // TODO: not supported on Apple sillicon, maybe find something else
{Latte::E_GX2SURFFMT::D24_S8_FLOAT, {MTL::PixelFormatDepth32Float_Stencil8, 4}}, // TODO: correct?
{Latte::E_GX2SURFFMT::D32_S8_FLOAT, {MTL::PixelFormatDepth32Float_Stencil8, 5}},
{Latte::E_GX2SURFFMT::D16_UNORM, {MTL::PixelFormatDepth16Unorm, 2}},
{Latte::E_GX2SURFFMT::D32_FLOAT, {MTL::PixelFormatDepth32Float, 4}},
{Latte::E_GX2SURFFMT::BC1_UNORM, {MTL::PixelFormatBC1_RGBA, 8, {4, 4}}}, // TODO: correct?
{Latte::E_GX2SURFFMT::BC1_SRGB, {MTL::PixelFormatBC1_RGBA_sRGB, 8, {4, 4}}}, // TODO: correct?
{Latte::E_GX2SURFFMT::BC2_UNORM, {MTL::PixelFormatBC2_RGBA, 16, {4, 4}}}, // TODO: correct?
@ -77,11 +71,29 @@ std::map<Latte::E_GX2SURFFMT, MtlPixelFormatInfo> MTL_FORMAT_TABLE = {
{Latte::E_GX2SURFFMT::BC5_SNORM, {MTL::PixelFormatBC5_RGSnorm, 16, {4, 4}}}, // TODO: correct?
};
const MtlPixelFormatInfo GetMtlPixelFormatInfo(Latte::E_GX2SURFFMT format)
{
cemu_assert_debug(static_cast<size_t>(format) < MTL_FORMAT_TABLE.size());
std::map<Latte::E_GX2SURFFMT, MtlPixelFormatInfo> MTL_DEPTH_FORMAT_TABLE = {
{Latte::E_GX2SURFFMT::D24_S8_UNORM, {MTL::PixelFormatDepth24Unorm_Stencil8, 4}}, // TODO: not supported on Apple sillicon, maybe find something else
{Latte::E_GX2SURFFMT::D24_S8_FLOAT, {MTL::PixelFormatDepth32Float_Stencil8, 4}}, // TODO: correct?
{Latte::E_GX2SURFFMT::D32_S8_FLOAT, {MTL::PixelFormatDepth32Float_Stencil8, 5}},
{Latte::E_GX2SURFFMT::D16_UNORM, {MTL::PixelFormatDepth16Unorm, 2}},
{Latte::E_GX2SURFFMT::D32_FLOAT, {MTL::PixelFormatDepth32Float, 4}},
};
const MtlPixelFormatInfo GetMtlPixelFormatInfo(Latte::E_GX2SURFFMT format, bool isDepth)
{
MtlPixelFormatInfo formatInfo;
if (isDepth)
formatInfo = MTL_DEPTH_FORMAT_TABLE[format];
else
formatInfo = MTL_COLOR_FORMAT_TABLE[format];
// Depth24Unorm_Stencil8 is not supported on Apple sillicon
// TODO: query if format is available instead
if (formatInfo.pixelFormat == MTL::PixelFormatDepth24Unorm_Stencil8)
{
formatInfo.pixelFormat = MTL::PixelFormatDepth32Float_Stencil8;
}
MtlPixelFormatInfo formatInfo = MTL_FORMAT_TABLE[format];
if (formatInfo.pixelFormat == MTL::PixelFormatInvalid)
{
printf("invalid pixel format: %u\n", (uint32)format);
@ -94,16 +106,16 @@ inline uint32 CeilDivide(uint32 a, uint32 b) {
return (a + b - 1) / b;
}
size_t GetMtlTextureBytesPerRow(Latte::E_GX2SURFFMT format, uint32 width)
size_t GetMtlTextureBytesPerRow(Latte::E_GX2SURFFMT format, bool isDepth, uint32 width)
{
const auto& formatInfo = GetMtlPixelFormatInfo(format);
const auto& formatInfo = GetMtlPixelFormatInfo(format, isDepth);
return CeilDivide(width, formatInfo.blockTexelSize.x) * formatInfo.bytesPerBlock;
}
size_t GetMtlTextureBytesPerImage(Latte::E_GX2SURFFMT format, uint32 height, size_t bytesPerRow)
size_t GetMtlTextureBytesPerImage(Latte::E_GX2SURFFMT format, bool isDepth, uint32 height, size_t bytesPerRow)
{
const auto& formatInfo = GetMtlPixelFormatInfo(format);
const auto& formatInfo = GetMtlPixelFormatInfo(format, isDepth);
return CeilDivide(height, formatInfo.blockTexelSize.y) * bytesPerRow;
}

View File

@ -18,11 +18,11 @@ struct MtlPixelFormatInfo {
Uvec2 blockTexelSize = {1, 1};
};
const MtlPixelFormatInfo GetMtlPixelFormatInfo(Latte::E_GX2SURFFMT format);
const MtlPixelFormatInfo GetMtlPixelFormatInfo(Latte::E_GX2SURFFMT format, bool isDepth);
size_t GetMtlTextureBytesPerRow(Latte::E_GX2SURFFMT format, uint32 width);
size_t GetMtlTextureBytesPerRow(Latte::E_GX2SURFFMT format, bool isDepth, uint32 width);
size_t GetMtlTextureBytesPerImage(Latte::E_GX2SURFFMT format, uint32 height, size_t bytesPerRow);
size_t GetMtlTextureBytesPerImage(Latte::E_GX2SURFFMT format, bool isDepth, uint32 height, size_t bytesPerRow);
MTL::PrimitiveType GetMtlPrimitiveType(LattePrimitiveMode mode);

View File

@ -12,6 +12,7 @@
#include "Cafe/HW/Latte/Core/LatteShader.h"
#include "Cafe/HW/Latte/Core/LatteIndices.h"
#include "Foundation/NSTypes.hpp"
#include "Metal/MTLRenderCommandEncoder.hpp"
#include "gui/guiWrapper.h"
extern bool hasValidFramebufferAttached;
@ -147,14 +148,14 @@ void MetalRenderer::DrawBackbufferQuad(LatteTextureView* texView, RendererOutput
MTL::Texture* colorRenderTargets[8] = {nullptr};
colorRenderTargets[0] = m_drawable->texture();
BeginRenderPassIfNeeded(renderPassDescriptor, colorRenderTargets, nullptr);
auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, nullptr);
// Draw to Metal layer
m_renderCommandEncoder->setRenderPipelineState(m_presentPipeline);
m_renderCommandEncoder->setFragmentTexture(presentTexture, 0);
m_renderCommandEncoder->setFragmentSamplerState(m_nearestSampler, 0);
renderCommandEncoder->setRenderPipelineState(m_presentPipeline);
renderCommandEncoder->setFragmentTexture(presentTexture, 0);
renderCommandEncoder->setFragmentSamplerState(m_nearestSampler, 0);
m_renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), NS::UInteger(3));
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangle, NS::UInteger(0), NS::UInteger(3));
}
bool MetalRenderer::BeginFrame(bool mainWindow)
@ -351,8 +352,8 @@ void MetalRenderer::texture_loadSlice(LatteTexture* hostTexture, sint32 width, s
{
auto mtlTexture = (LatteTextureMtl*)hostTexture;
size_t bytesPerRow = GetMtlTextureBytesPerRow(mtlTexture->GetFormat(), width);
size_t bytesPerImage = GetMtlTextureBytesPerImage(mtlTexture->GetFormat(), height, bytesPerRow);
size_t bytesPerRow = GetMtlTextureBytesPerRow(mtlTexture->GetFormat(), mtlTexture->IsDepth(), width);
size_t bytesPerImage = GetMtlTextureBytesPerImage(mtlTexture->GetFormat(), mtlTexture->IsDepth(), height, bytesPerRow);
mtlTexture->GetTexture()->replaceRegion(MTL::Region(0, 0, width, height), mipIndex, sliceIndex, pixelData, bytesPerRow, bytesPerImage);
}
@ -535,7 +536,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
{
depthRenderTarget = depthTexture->GetTexture();
}
BeginRenderPassIfNeeded(renderPassDescriptor, colorRenderTargets, depthRenderTarget);
auto renderCommandEncoder = GetRenderCommandEncoder(renderPassDescriptor, colorRenderTargets, depthRenderTarget);
// Shaders
LatteSHRC_UpdateActiveShaders();
@ -622,7 +623,7 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String());
return;
}
m_renderCommandEncoder->setRenderPipelineState(renderPipelineState);
renderCommandEncoder->setRenderPipelineState(renderPipelineState);
// Primitive type
const LattePrimitiveMode primitiveMode = static_cast<LattePrimitiveMode>(LatteGPUState.contextRegister[mmVGT_PRIMITIVE_TYPE]);
@ -648,25 +649,25 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
auto& vertexBufferRange = m_state.vertexBuffers[i];
if (vertexBufferRange.needsRebind)
{
m_renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i));
renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), vertexBufferRange.offset, GET_MTL_VERTEX_BUFFER_INDEX(i));
// TODO: uncomment
//vertexBufferRange.needsRebind = false;
}
}
// Uniform buffers, textures and samplers
BindStageResources(vertexShader);
BindStageResources(pixelShader);
BindStageResources(renderCommandEncoder, vertexShader);
BindStageResources(renderCommandEncoder, pixelShader);
// Draw
if (hostIndexType != INDEX_TYPE::NONE)
{
auto mtlIndexType = GetMtlIndexType(hostIndexType);
MTL::Buffer* indexBuffer = m_memoryManager->GetBuffer(indexBufferIndex);
m_renderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, hostIndexCount, mtlIndexType, indexBuffer, 0, instanceCount, baseVertex, baseInstance);
renderCommandEncoder->drawIndexedPrimitives(mtlPrimitiveType, hostIndexCount, mtlIndexType, indexBuffer, 0, instanceCount, baseVertex, baseInstance);
} else
{
m_renderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, count, instanceCount, baseInstance);
renderCommandEncoder->drawPrimitives(mtlPrimitiveType, baseVertex, count, instanceCount, baseInstance);
}
}
@ -689,7 +690,7 @@ void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size)
printf("MetalRenderer::indexData_uploadIndexMemory not implemented\n");
}
void MetalRenderer::BindStageResources(LatteDecompilerShader* shader)
void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader)
{
sint32 textureCount = shader->resourceMapping.getTextureCount();
@ -722,12 +723,12 @@ void MetalRenderer::BindStageResources(LatteDecompilerShader* shader)
{
case LatteConst::ShaderType::Vertex:
{
m_renderCommandEncoder->setVertexSamplerState(sampler, binding);
renderCommandEncoder->setVertexSamplerState(sampler, binding);
break;
}
case LatteConst::ShaderType::Pixel:
{
m_renderCommandEncoder->setFragmentSamplerState(sampler, binding);
renderCommandEncoder->setFragmentSamplerState(sampler, binding);
break;
}
default:
@ -739,12 +740,12 @@ void MetalRenderer::BindStageResources(LatteDecompilerShader* shader)
{
case LatteConst::ShaderType::Vertex:
{
m_renderCommandEncoder->setVertexTexture(textureView->GetTexture(), binding);
renderCommandEncoder->setVertexTexture(textureView->GetTexture(), binding);
break;
}
case LatteConst::ShaderType::Pixel:
{
m_renderCommandEncoder->setFragmentTexture(textureView->GetTexture(), binding);
renderCommandEncoder->setFragmentTexture(textureView->GetTexture(), binding);
break;
}
default:
@ -840,12 +841,12 @@ void MetalRenderer::BindStageResources(LatteDecompilerShader* shader)
{
case LatteConst::ShaderType::Vertex:
{
m_renderCommandEncoder->setVertexBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING);
renderCommandEncoder->setVertexBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING);
break;
}
case LatteConst::ShaderType::Pixel:
{
m_renderCommandEncoder->setFragmentBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING);
renderCommandEncoder->setFragmentBytes(supportBufferData, sizeof(supportBufferData), MTL_SUPPORT_BUFFER_BINDING);
break;
}
default:
@ -865,12 +866,12 @@ void MetalRenderer::BindStageResources(LatteDecompilerShader* shader)
{
case LatteConst::ShaderType::Vertex:
{
m_renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), offset, binding);
renderCommandEncoder->setVertexBuffer(m_memoryManager->GetBufferCache(), offset, binding);
break;
}
case LatteConst::ShaderType::Pixel:
{
m_renderCommandEncoder->setFragmentBuffer(m_memoryManager->GetBufferCache(), offset, binding);
renderCommandEncoder->setFragmentBuffer(m_memoryManager->GetBufferCache(), offset, binding);
break;
}
default:

View File

@ -6,6 +6,7 @@
#include "Cafe/HW/Latte/Renderer/Renderer.h"
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
#include "Metal/MTLComputeCommandEncoder.hpp"
#include "Metal/MTLRenderCommandEncoder.hpp"
#include "Metal/MTLRenderPass.hpp"
#include "Metal/MTLRenderPipeline.hpp"
@ -33,6 +34,14 @@ struct MetalState
MTL::Texture* depthRenderTarget = nullptr;
};
enum class MetalEncoderType
{
None,
Render,
Compute,
Blit,
};
class MetalRenderer : public Renderer
{
public:
@ -197,7 +206,8 @@ private:
// Active objects
MTL::CommandBuffer* m_commandBuffer = nullptr;
MTL::RenderCommandEncoder* m_renderCommandEncoder = nullptr;
MetalEncoderType m_encoderType = MetalEncoderType::None;
MTL::CommandEncoder* m_commandEncoder = nullptr;
CA::MetalDrawable* m_drawable;
// State
@ -215,49 +225,101 @@ private:
}
}
void BeginRenderPassIfNeeded(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget)
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget)
{
EnsureCommandBuffer();
// Check if we need to begin a new render pass
if (m_renderCommandEncoder)
if (m_commandEncoder)
{
bool needsNewRenderPass = false;
for (uint8 i = 0; i < 8; i++)
if (m_encoderType == MetalEncoderType::Render)
{
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i]))
bool needsNewRenderPass = false;
for (uint8 i = 0; i < 8; i++)
{
needsNewRenderPass = true;
break;
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i]))
{
needsNewRenderPass = true;
break;
}
}
}
if (!needsNewRenderPass)
{
if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget))
if (!needsNewRenderPass)
{
needsNewRenderPass = true;
if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget))
{
needsNewRenderPass = true;
}
}
}
if (!needsNewRenderPass)
{
return;
if (!needsNewRenderPass)
{
return (MTL::RenderCommandEncoder*)m_commandEncoder;
}
}
EndEncoding();
}
m_renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor);
// Update state
for (uint8 i = 0; i < 8; i++)
{
m_state.colorRenderTargets[i] = colorRenderTargets[i];
}
m_state.depthRenderTarget = depthRenderTarget;
auto renderCommandEncoder = m_commandBuffer->renderCommandEncoder(renderPassDescriptor);
m_commandEncoder = renderCommandEncoder;
m_encoderType = MetalEncoderType::Render;
return renderCommandEncoder;
}
MTL::ComputeCommandEncoder* GetComputeCommandEncoder()
{
if (m_commandEncoder)
{
if (m_encoderType != MetalEncoderType::Compute)
{
return (MTL::ComputeCommandEncoder*)m_commandEncoder;
}
EndEncoding();
}
auto computeCommandEncoder = m_commandBuffer->computeCommandEncoder();
m_commandEncoder = computeCommandEncoder;
m_encoderType = MetalEncoderType::Compute;
return computeCommandEncoder;
}
MTL::BlitCommandEncoder* GetBlitCommandEncoder()
{
if (m_commandEncoder)
{
if (m_encoderType != MetalEncoderType::Blit)
{
return (MTL::BlitCommandEncoder*)m_commandEncoder;
}
EndEncoding();
}
auto blitCommandEncoder = m_commandBuffer->blitCommandEncoder();
m_commandEncoder = blitCommandEncoder;
m_encoderType = MetalEncoderType::Blit;
return blitCommandEncoder;
}
void EndEncoding()
{
if (m_renderCommandEncoder)
if (m_commandEncoder)
{
m_renderCommandEncoder->endEncoding();
m_renderCommandEncoder->release();
m_renderCommandEncoder = nullptr;
m_commandEncoder->endEncoding();
m_commandEncoder->release();
m_commandEncoder = nullptr;
}
}
@ -276,5 +338,5 @@ private:
}
}
void BindStageResources(LatteDecompilerShader* shader);
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
};