mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-01-08 08:00:44 +01:00
implement render pipeline cache
This commit is contained in:
parent
c6f66167a5
commit
d7411e27f7
@ -553,6 +553,8 @@ if(ENABLE_METAL)
|
|||||||
HW/Latte/Renderer/Metal/CachedFBOMtl.h
|
HW/Latte/Renderer/Metal/CachedFBOMtl.h
|
||||||
HW/Latte/Renderer/Metal/MetalMemoryManager.cpp
|
HW/Latte/Renderer/Metal/MetalMemoryManager.cpp
|
||||||
HW/Latte/Renderer/Metal/MetalMemoryManager.h
|
HW/Latte/Renderer/Metal/MetalMemoryManager.h
|
||||||
|
HW/Latte/Renderer/Metal/MetalPipelineCache.cpp
|
||||||
|
HW/Latte/Renderer/Metal/MetalPipelineCache.h
|
||||||
HW/Latte/Renderer/Metal/ShaderSourcePresent.h
|
HW/Latte/Renderer/Metal/ShaderSourcePresent.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -615,7 +615,6 @@ LatteDecompilerShader* LatteShader_CreateShaderFromDecompilerOutput(LatteDecompi
|
|||||||
LatteDecompilerShader* shader = decompilerOutput.shader;
|
LatteDecompilerShader* shader = decompilerOutput.shader;
|
||||||
shader->baseHash = baseHash;
|
shader->baseHash = baseHash;
|
||||||
// copy resource mapping
|
// copy resource mapping
|
||||||
// HACK
|
|
||||||
if (g_renderer->GetType() != RendererAPI::OpenGL)
|
if (g_renderer->GetType() != RendererAPI::OpenGL)
|
||||||
shader->resourceMapping = decompilerOutput.resourceMappingVK;
|
shader->resourceMapping = decompilerOutput.resourceMappingVK;
|
||||||
else
|
else
|
||||||
|
@ -36,7 +36,7 @@ typedef struct
|
|||||||
uint16 mappedIndexOffset; // index in remapped uniform array
|
uint16 mappedIndexOffset; // index in remapped uniform array
|
||||||
}LatteFastAccessRemappedUniformEntry_buffer_t;
|
}LatteFastAccessRemappedUniformEntry_buffer_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint32 texUnit;
|
uint32 texUnit;
|
||||||
sint32 uniformLocation;
|
sint32 uniformLocation;
|
||||||
@ -198,7 +198,7 @@ struct LatteDecompilerShader
|
|||||||
// resource mapping (binding points)
|
// resource mapping (binding points)
|
||||||
LatteDecompilerShaderResourceMapping resourceMapping{};
|
LatteDecompilerShaderResourceMapping resourceMapping{};
|
||||||
// uniforms
|
// uniforms
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
sint32 loc_remapped; // uf_remappedVS/uf_remappedGS/uf_remappedPS
|
sint32 loc_remapped; // uf_remappedVS/uf_remappedGS/uf_remappedPS
|
||||||
sint32 loc_uniformRegister; // uf_uniformRegisterVS/uf_uniformRegisterGS/uf_uniformRegisterPS
|
sint32 loc_uniformRegister; // uf_uniformRegisterVS/uf_uniformRegisterGS/uf_uniformRegisterPS
|
||||||
@ -215,7 +215,7 @@ struct LatteDecompilerShader
|
|||||||
sint32 uniformRangeSize; // entire size of uniform variable block
|
sint32 uniformRangeSize; // entire size of uniform variable block
|
||||||
}uniform{ 0 };
|
}uniform{ 0 };
|
||||||
// fast access
|
// fast access
|
||||||
struct _RemappedUniformBufferGroup
|
struct _RemappedUniformBufferGroup
|
||||||
{
|
{
|
||||||
_RemappedUniformBufferGroup(uint32 _kcacheBankIdOffset) : kcacheBankIdOffset(_kcacheBankIdOffset) {};
|
_RemappedUniformBufferGroup(uint32 _kcacheBankIdOffset) : kcacheBankIdOffset(_kcacheBankIdOffset) {};
|
||||||
|
|
||||||
@ -255,14 +255,14 @@ struct LatteDecompilerOutputUniformOffsets
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LatteDecompilerOptions
|
struct LatteDecompilerOptions
|
||||||
{
|
{
|
||||||
bool usesGeometryShader{ false };
|
bool usesGeometryShader{ false };
|
||||||
// floating point math
|
// floating point math
|
||||||
bool strictMul{}; // if true, 0*anything=0 rule is emulated
|
bool strictMul{}; // if true, 0*anything=0 rule is emulated
|
||||||
// Vulkan-specific
|
// Vulkan-specific
|
||||||
bool useTFViaSSBO{ false };
|
bool useTFViaSSBO{ false };
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
bool hasRoundingModeRTEFloat32{ false };
|
bool hasRoundingModeRTEFloat32{ false };
|
||||||
}spirvInstrinsics;
|
}spirvInstrinsics;
|
||||||
@ -322,4 +322,4 @@ struct LatteParsedGSCopyShader
|
|||||||
};
|
};
|
||||||
|
|
||||||
LatteParsedGSCopyShader* LatteGSCopyShaderParser_parse(uint8* programData, uint32 programSize);
|
LatteParsedGSCopyShader* LatteGSCopyShaderParser_parse(uint8* programData, uint32 programSize);
|
||||||
bool LatteGSCopyShaderParser_getExportTypeByOffset(LatteParsedGSCopyShader* shaderContext, uint32 offset, uint32* exportType, uint32* exportParam);
|
bool LatteGSCopyShaderParser_getExportTypeByOffset(LatteParsedGSCopyShader* shaderContext, uint32 offset, uint32* exportType, uint32* exportParam);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.h"
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||||
#include "Metal/MTLTexture.hpp"
|
|
||||||
|
|
||||||
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_format(format)
|
||||||
|
207
src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp
Normal file
207
src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
|
||||||
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h"
|
||||||
|
#include "HW/Latte/Core/FetchShader.h"
|
||||||
|
#include "HW/Latte/ISA/RegDefines.h"
|
||||||
|
#include "HW/Latte/Renderer/Metal/CachedFBOMtl.h"
|
||||||
|
#include "HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||||
|
#include "HW/Latte/Renderer/Metal/RendererShaderMtl.h"
|
||||||
|
#include "HW/Latte/Renderer/Metal/LatteTextureViewMtl.h"
|
||||||
|
|
||||||
|
MetalPipelineCache::~MetalPipelineCache()
|
||||||
|
{
|
||||||
|
for (auto& pair : m_pipelineCache)
|
||||||
|
{
|
||||||
|
pair.second->release();
|
||||||
|
}
|
||||||
|
m_pipelineCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, CachedFBOMtl* activeFBO, const LatteContextRegister& lcr)
|
||||||
|
{
|
||||||
|
uint64 stateHash = CalculatePipelineHash(fetchShader, vertexShader, pixelShader, activeFBO, lcr);
|
||||||
|
auto& pipeline = m_pipelineCache[stateHash];
|
||||||
|
if (pipeline)
|
||||||
|
{
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertex descriptor
|
||||||
|
MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::alloc()->init();
|
||||||
|
for (auto& bufferGroup : fetchShader->bufferGroups)
|
||||||
|
{
|
||||||
|
std::optional<LatteConst::VertexFetchType2> fetchType;
|
||||||
|
|
||||||
|
for (sint32 j = 0; j < bufferGroup.attribCount; ++j)
|
||||||
|
{
|
||||||
|
auto& attr = bufferGroup.attrib[j];
|
||||||
|
|
||||||
|
uint32 semanticId = vertexShader->resourceMapping.attributeMapping[attr.semanticId];
|
||||||
|
if (semanticId == (uint32)-1)
|
||||||
|
continue; // attribute not used?
|
||||||
|
|
||||||
|
auto attribute = vertexDescriptor->attributes()->object(semanticId);
|
||||||
|
attribute->setOffset(attr.offset);
|
||||||
|
// Bind from the end to not conflict with uniform buffers
|
||||||
|
attribute->setBufferIndex(GET_MTL_VERTEX_BUFFER_INDEX(attr.attributeBufferIndex));
|
||||||
|
attribute->setFormat(GetMtlVertexFormat(attr.format));
|
||||||
|
|
||||||
|
if (fetchType.has_value())
|
||||||
|
cemu_assert_debug(fetchType == attr.fetchType);
|
||||||
|
else
|
||||||
|
fetchType = attr.fetchType;
|
||||||
|
|
||||||
|
if (attr.fetchType == LatteConst::INSTANCE_DATA)
|
||||||
|
{
|
||||||
|
cemu_assert_debug(attr.aluDivisor == 1); // other divisor not yet supported
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
|
||||||
|
uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + bufferIndex * 7;
|
||||||
|
// TODO: is LatteGPUState.contextNew correct?
|
||||||
|
uint32 bufferStride = (LatteGPUState.contextNew.GetRawView()[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF;
|
||||||
|
|
||||||
|
auto layout = vertexDescriptor->layouts()->object(GET_MTL_VERTEX_BUFFER_INDEX(bufferIndex));
|
||||||
|
layout->setStride(bufferStride);
|
||||||
|
if (!fetchType.has_value() || fetchType == LatteConst::VertexFetchType2::VERTEX_DATA)
|
||||||
|
layout->setStepFunction(MTL::VertexStepFunctionPerVertex);
|
||||||
|
else if (fetchType == LatteConst::VertexFetchType2::INSTANCE_DATA)
|
||||||
|
layout->setStepFunction(MTL::VertexStepFunctionPerInstance);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
debug_printf("unimplemented vertex fetch type %u\n", (uint32)fetchType.value());
|
||||||
|
cemu_assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render pipeline state
|
||||||
|
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
|
||||||
|
renderPipelineDescriptor->setVertexFunction(static_cast<RendererShaderMtl*>(vertexShader->shader)->GetFunction());
|
||||||
|
renderPipelineDescriptor->setFragmentFunction(static_cast<RendererShaderMtl*>(pixelShader->shader)->GetFunction());
|
||||||
|
// TODO: don't always set the vertex descriptor
|
||||||
|
renderPipelineDescriptor->setVertexDescriptor(vertexDescriptor);
|
||||||
|
for (uint8 i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
const auto& colorBuffer = activeFBO->colorBuffer[i];
|
||||||
|
auto texture = static_cast<LatteTextureViewMtl*>(colorBuffer.texture);
|
||||||
|
if (!texture)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto colorAttachment = renderPipelineDescriptor->colorAttachments()->object(i);
|
||||||
|
colorAttachment->setPixelFormat(texture->GetTexture()->pixelFormat());
|
||||||
|
|
||||||
|
// Blending
|
||||||
|
const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = LatteGPUState.contextNew.CB_COLOR_CONTROL;
|
||||||
|
uint32 blendEnableMask = colorControlReg.get_BLEND_MASK();
|
||||||
|
uint32 renderTargetMask = LatteGPUState.contextNew.CB_TARGET_MASK.get_MASK();
|
||||||
|
|
||||||
|
bool blendEnabled = ((blendEnableMask & (1 << i))) != 0;
|
||||||
|
if (blendEnabled)
|
||||||
|
{
|
||||||
|
colorAttachment->setBlendingEnabled(true);
|
||||||
|
|
||||||
|
const auto& blendControlReg = LatteGPUState.contextNew.CB_BLENDN_CONTROL[i];
|
||||||
|
|
||||||
|
auto rgbBlendOp = GetMtlBlendOp(blendControlReg.get_COLOR_COMB_FCN());
|
||||||
|
auto srcRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_SRCBLEND());
|
||||||
|
auto dstRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_DSTBLEND());
|
||||||
|
|
||||||
|
colorAttachment->setWriteMask((renderTargetMask >> (i * 4)) & 0xF);
|
||||||
|
colorAttachment->setRgbBlendOperation(rgbBlendOp);
|
||||||
|
colorAttachment->setSourceRGBBlendFactor(srcRgbBlendFactor);
|
||||||
|
colorAttachment->setDestinationRGBBlendFactor(dstRgbBlendFactor);
|
||||||
|
if (blendControlReg.get_SEPARATE_ALPHA_BLEND())
|
||||||
|
{
|
||||||
|
colorAttachment->setAlphaBlendOperation(GetMtlBlendOp(blendControlReg.get_ALPHA_COMB_FCN()));
|
||||||
|
colorAttachment->setSourceAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_SRCBLEND()));
|
||||||
|
colorAttachment->setDestinationAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_DSTBLEND()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
colorAttachment->setAlphaBlendOperation(rgbBlendOp);
|
||||||
|
colorAttachment->setSourceAlphaBlendFactor(srcRgbBlendFactor);
|
||||||
|
colorAttachment->setDestinationAlphaBlendFactor(dstRgbBlendFactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeFBO->depthBuffer.texture)
|
||||||
|
{
|
||||||
|
auto texture = static_cast<LatteTextureViewMtl*>(activeFBO->depthBuffer.texture);
|
||||||
|
renderPipelineDescriptor->setDepthAttachmentPixelFormat(texture->GetTexture()->pixelFormat());
|
||||||
|
}
|
||||||
|
|
||||||
|
NS::Error* error = nullptr;
|
||||||
|
pipeline = m_mtlr->GetDevice()->newRenderPipelineState(renderPipelineDescriptor, &error);
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 MetalPipelineCache::CalculatePipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr)
|
||||||
|
{
|
||||||
|
// Hash
|
||||||
|
uint64 stateHash = 0;
|
||||||
|
for (auto& group : fetchShader->bufferGroups)
|
||||||
|
{
|
||||||
|
uint32 bufferStride = group.getCurrentBufferStride(lcr.GetRawView());
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
stateHash += bufferStride * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
stateHash += fetchShader->getVkPipelineHashFragment();
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
|
||||||
|
stateHash += lcr.GetRawView()[mmVGT_PRIMITIVE_TYPE];
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
|
||||||
|
stateHash += lcr.GetRawView()[mmVGT_STRMOUT_EN];
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
|
||||||
|
if(lcr.PA_CL_CLIP_CNTL.get_DX_RASTERIZATION_KILL())
|
||||||
|
stateHash += 0x333333;
|
||||||
|
|
||||||
|
stateHash = (stateHash >> 8) + (stateHash * 0x370531ull) % 0x7F980D3BF9B4639Dull;
|
||||||
|
|
||||||
|
uint32* ctxRegister = lcr.GetRawView();
|
||||||
|
|
||||||
|
if (vertexShader)
|
||||||
|
stateHash += vertexShader->baseHash;
|
||||||
|
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 13);
|
||||||
|
|
||||||
|
if (pixelShader)
|
||||||
|
stateHash += pixelShader->baseHash + pixelShader->auxHash;
|
||||||
|
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 13);
|
||||||
|
|
||||||
|
uint32 polygonCtrl = lcr.PA_SU_SC_MODE_CNTL.getRawValue();
|
||||||
|
stateHash += polygonCtrl;
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
|
||||||
|
stateHash += ctxRegister[Latte::REGADDR::PA_CL_CLIP_CNTL];
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
|
||||||
|
const auto colorControlReg = ctxRegister[Latte::REGADDR::CB_COLOR_CONTROL];
|
||||||
|
stateHash += colorControlReg;
|
||||||
|
|
||||||
|
stateHash += ctxRegister[Latte::REGADDR::CB_TARGET_MASK];
|
||||||
|
|
||||||
|
const uint32 blendEnableMask = (colorControlReg >> 8) & 0xFF;
|
||||||
|
if (blendEnableMask)
|
||||||
|
{
|
||||||
|
for (auto i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
if (((blendEnableMask & (1 << i))) == 0)
|
||||||
|
continue;
|
||||||
|
stateHash = std::rotl<uint64>(stateHash, 7);
|
||||||
|
stateHash += ctxRegister[Latte::REGADDR::CB_BLEND0_CONTROL + i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stateHash;
|
||||||
|
}
|
22
src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h
Normal file
22
src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Metal/Metal.hpp>
|
||||||
|
|
||||||
|
#include "HW/Latte/ISA/LatteReg.h"
|
||||||
|
#include "HW/Latte/LegacyShaderDecompiler/LatteDecompiler.h"
|
||||||
|
|
||||||
|
class MetalPipelineCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} {}
|
||||||
|
~MetalPipelineCache();
|
||||||
|
|
||||||
|
MTL::RenderPipelineState* GetPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class MetalRenderer* m_mtlr;
|
||||||
|
|
||||||
|
std::map<uint64, MTL::RenderPipelineState*> m_pipelineCache;
|
||||||
|
|
||||||
|
uint64 CalculatePipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr);
|
||||||
|
};
|
@ -4,6 +4,8 @@
|
|||||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h"
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h"
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/CachedFBOMtl.h"
|
||||||
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h"
|
||||||
|
#include "Cafe/HW/Latte/Renderer/Metal/MetalMemoryManager.h"
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h"
|
||||||
|
|
||||||
#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h"
|
#include "Cafe/HW/Latte/Renderer/Metal/ShaderSourcePresent.h"
|
||||||
@ -13,9 +15,11 @@
|
|||||||
#include "Cafe/HW/Latte/Core/LatteIndices.h"
|
#include "Cafe/HW/Latte/Core/LatteIndices.h"
|
||||||
#include "Cemu/Logging/CemuDebugLogging.h"
|
#include "Cemu/Logging/CemuDebugLogging.h"
|
||||||
#include "Foundation/NSTypes.hpp"
|
#include "Foundation/NSTypes.hpp"
|
||||||
|
#include "HW/Latte/Core/Latte.h"
|
||||||
#include "Metal/MTLDepthStencil.hpp"
|
#include "Metal/MTLDepthStencil.hpp"
|
||||||
#include "Metal/MTLRenderCommandEncoder.hpp"
|
#include "Metal/MTLRenderCommandEncoder.hpp"
|
||||||
#include "Metal/MTLRenderPass.hpp"
|
#include "Metal/MTLRenderPass.hpp"
|
||||||
|
#include "Metal/MTLRenderPipeline.hpp"
|
||||||
#include "gui/guiWrapper.h"
|
#include "gui/guiWrapper.h"
|
||||||
|
|
||||||
extern bool hasValidFramebufferAttached;
|
extern bool hasValidFramebufferAttached;
|
||||||
@ -31,6 +35,7 @@ MetalRenderer::MetalRenderer()
|
|||||||
m_nearestSampler = m_device->newSamplerState(samplerDescriptor);
|
m_nearestSampler = m_device->newSamplerState(samplerDescriptor);
|
||||||
|
|
||||||
m_memoryManager = new MetalMemoryManager(this);
|
m_memoryManager = new MetalMemoryManager(this);
|
||||||
|
m_pipelineCache = new MetalPipelineCache(this);
|
||||||
|
|
||||||
// Initialize state
|
// Initialize state
|
||||||
for (uint32 i = 0; i < (uint32)LatteConst::ShaderType::TotalCount; i++)
|
for (uint32 i = 0; i < (uint32)LatteConst::ShaderType::TotalCount; i++)
|
||||||
@ -612,119 +617,8 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
|
|||||||
|
|
||||||
auto fetchShader = vertexShader->compatibleFetchShader;
|
auto fetchShader = vertexShader->compatibleFetchShader;
|
||||||
|
|
||||||
// Vertex descriptor
|
|
||||||
MTL::VertexDescriptor* vertexDescriptor = MTL::VertexDescriptor::alloc()->init();
|
|
||||||
for (auto& bufferGroup : fetchShader->bufferGroups)
|
|
||||||
{
|
|
||||||
std::optional<LatteConst::VertexFetchType2> fetchType;
|
|
||||||
|
|
||||||
for (sint32 j = 0; j < bufferGroup.attribCount; ++j)
|
|
||||||
{
|
|
||||||
auto& attr = bufferGroup.attrib[j];
|
|
||||||
|
|
||||||
uint32 semanticId = vertexShader->resourceMapping.attributeMapping[attr.semanticId];
|
|
||||||
if (semanticId == (uint32)-1)
|
|
||||||
continue; // attribute not used?
|
|
||||||
|
|
||||||
auto attribute = vertexDescriptor->attributes()->object(semanticId);
|
|
||||||
attribute->setOffset(attr.offset);
|
|
||||||
// Bind from the end to not conflict with uniform buffers
|
|
||||||
attribute->setBufferIndex(GET_MTL_VERTEX_BUFFER_INDEX(attr.attributeBufferIndex));
|
|
||||||
attribute->setFormat(GetMtlVertexFormat(attr.format));
|
|
||||||
|
|
||||||
if (fetchType.has_value())
|
|
||||||
cemu_assert_debug(fetchType == attr.fetchType);
|
|
||||||
else
|
|
||||||
fetchType = attr.fetchType;
|
|
||||||
|
|
||||||
if (attr.fetchType == LatteConst::INSTANCE_DATA)
|
|
||||||
{
|
|
||||||
cemu_assert_debug(attr.aluDivisor == 1); // other divisor not yet supported
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
|
|
||||||
uint32 bufferBaseRegisterIndex = mmSQ_VTX_ATTRIBUTE_BLOCK_START + bufferIndex * 7;
|
|
||||||
// TODO: is LatteGPUState.contextNew correct?
|
|
||||||
uint32 bufferStride = (LatteGPUState.contextNew.GetRawView()[bufferBaseRegisterIndex + 2] >> 11) & 0xFFFF;
|
|
||||||
|
|
||||||
auto layout = vertexDescriptor->layouts()->object(GET_MTL_VERTEX_BUFFER_INDEX(bufferIndex));
|
|
||||||
layout->setStride(bufferStride);
|
|
||||||
if (!fetchType.has_value() || fetchType == LatteConst::VertexFetchType2::VERTEX_DATA)
|
|
||||||
layout->setStepFunction(MTL::VertexStepFunctionPerVertex);
|
|
||||||
else if (fetchType == LatteConst::VertexFetchType2::INSTANCE_DATA)
|
|
||||||
layout->setStepFunction(MTL::VertexStepFunctionPerInstance);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
debug_printf("unimplemented vertex fetch type %u\n", (uint32)fetchType.value());
|
|
||||||
cemu_assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render pipeline state
|
// Render pipeline state
|
||||||
MTL::RenderPipelineDescriptor* renderPipelineDescriptor = MTL::RenderPipelineDescriptor::alloc()->init();
|
MTL::RenderPipelineState* renderPipelineState = m_pipelineCache->GetPipelineState(fetchShader, vertexShader, pixelShader, m_state.activeFBO, LatteGPUState.contextNew);
|
||||||
renderPipelineDescriptor->setVertexFunction(static_cast<RendererShaderMtl*>(vertexShader->shader)->GetFunction());
|
|
||||||
renderPipelineDescriptor->setFragmentFunction(static_cast<RendererShaderMtl*>(pixelShader->shader)->GetFunction());
|
|
||||||
// TODO: don't always set the vertex descriptor
|
|
||||||
renderPipelineDescriptor->setVertexDescriptor(vertexDescriptor);
|
|
||||||
for (uint8 i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
const auto& colorBuffer = m_state.activeFBO->colorBuffer[i];
|
|
||||||
auto texture = static_cast<LatteTextureViewMtl*>(colorBuffer.texture);
|
|
||||||
if (!texture)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto colorAttachment = renderPipelineDescriptor->colorAttachments()->object(i);
|
|
||||||
colorAttachment->setPixelFormat(texture->GetTexture()->pixelFormat());
|
|
||||||
|
|
||||||
// Blending
|
|
||||||
const Latte::LATTE_CB_COLOR_CONTROL& colorControlReg = LatteGPUState.contextNew.CB_COLOR_CONTROL;
|
|
||||||
uint32 blendEnableMask = colorControlReg.get_BLEND_MASK();
|
|
||||||
uint32 renderTargetMask = LatteGPUState.contextNew.CB_TARGET_MASK.get_MASK();
|
|
||||||
|
|
||||||
bool blendEnabled = ((blendEnableMask & (1 << i))) != 0;
|
|
||||||
if (blendEnabled)
|
|
||||||
{
|
|
||||||
colorAttachment->setBlendingEnabled(true);
|
|
||||||
|
|
||||||
const auto& blendControlReg = LatteGPUState.contextNew.CB_BLENDN_CONTROL[i];
|
|
||||||
|
|
||||||
auto rgbBlendOp = GetMtlBlendOp(blendControlReg.get_COLOR_COMB_FCN());
|
|
||||||
auto srcRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_SRCBLEND());
|
|
||||||
auto dstRgbBlendFactor = GetMtlBlendFactor(blendControlReg.get_COLOR_DSTBLEND());
|
|
||||||
|
|
||||||
colorAttachment->setWriteMask((renderTargetMask >> (i * 4)) & 0xF);
|
|
||||||
colorAttachment->setRgbBlendOperation(rgbBlendOp);
|
|
||||||
colorAttachment->setSourceRGBBlendFactor(srcRgbBlendFactor);
|
|
||||||
colorAttachment->setDestinationRGBBlendFactor(dstRgbBlendFactor);
|
|
||||||
if (blendControlReg.get_SEPARATE_ALPHA_BLEND())
|
|
||||||
{
|
|
||||||
colorAttachment->setAlphaBlendOperation(GetMtlBlendOp(blendControlReg.get_ALPHA_COMB_FCN()));
|
|
||||||
colorAttachment->setSourceAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_SRCBLEND()));
|
|
||||||
colorAttachment->setDestinationAlphaBlendFactor(GetMtlBlendFactor(blendControlReg.get_ALPHA_DSTBLEND()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
colorAttachment->setAlphaBlendOperation(rgbBlendOp);
|
|
||||||
colorAttachment->setSourceAlphaBlendFactor(srcRgbBlendFactor);
|
|
||||||
colorAttachment->setDestinationAlphaBlendFactor(dstRgbBlendFactor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_state.activeFBO->depthBuffer.texture)
|
|
||||||
{
|
|
||||||
auto texture = static_cast<LatteTextureViewMtl*>(m_state.activeFBO->depthBuffer.texture);
|
|
||||||
renderPipelineDescriptor->setDepthAttachmentPixelFormat(texture->GetTexture()->pixelFormat());
|
|
||||||
}
|
|
||||||
|
|
||||||
NS::Error* error = nullptr;
|
|
||||||
MTL::RenderPipelineState* renderPipelineState = m_device->newRenderPipelineState(renderPipelineDescriptor, &error);
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
renderCommandEncoder->setRenderPipelineState(renderPipelineState);
|
renderCommandEncoder->setRenderPipelineState(renderPipelineState);
|
||||||
|
|
||||||
// Depth stencil state
|
// Depth stencil state
|
||||||
@ -872,6 +766,143 @@ void MetalRenderer::indexData_uploadIndexMemory(uint32 offset, uint32 size)
|
|||||||
debug_printf("MetalRenderer::indexData_uploadIndexMemory not implemented\n");
|
debug_printf("MetalRenderer::indexData_uploadIndexMemory not implemented\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MetalRenderer::EnsureCommandBuffer()
|
||||||
|
{
|
||||||
|
if (!m_commandBuffer)
|
||||||
|
{
|
||||||
|
// Debug
|
||||||
|
m_commandQueue->insertDebugCaptureBoundary();
|
||||||
|
|
||||||
|
m_commandBuffer = m_commandQueue->commandBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some render passes clear the attachments, forceRecreate is supposed to be used in those cases
|
||||||
|
MTL::RenderCommandEncoder* MetalRenderer::GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate, bool rebindStateIfNewEncoder)
|
||||||
|
{
|
||||||
|
EnsureCommandBuffer();
|
||||||
|
|
||||||
|
// Check if we need to begin a new render pass
|
||||||
|
if (m_commandEncoder)
|
||||||
|
{
|
||||||
|
if (!forceRecreate)
|
||||||
|
{
|
||||||
|
if (m_encoderType == MetalEncoderType::Render)
|
||||||
|
{
|
||||||
|
bool needsNewRenderPass = false;
|
||||||
|
for (uint8 i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i]))
|
||||||
|
{
|
||||||
|
needsNewRenderPass = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsNewRenderPass)
|
||||||
|
{
|
||||||
|
if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget))
|
||||||
|
{
|
||||||
|
needsNewRenderPass = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsNewRenderPass)
|
||||||
|
{
|
||||||
|
return (MTL::RenderCommandEncoder*)m_commandEncoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EndEncoding();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
if (rebindStateIfNewEncoder)
|
||||||
|
{
|
||||||
|
// Rebind all the render state
|
||||||
|
RebindRenderState(renderCommandEncoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderCommandEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTL::ComputeCommandEncoder* MetalRenderer::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* MetalRenderer::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 MetalRenderer::EndEncoding()
|
||||||
|
{
|
||||||
|
if (m_commandEncoder)
|
||||||
|
{
|
||||||
|
m_commandEncoder->endEncoding();
|
||||||
|
m_commandEncoder->release();
|
||||||
|
m_commandEncoder = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MetalRenderer::CommitCommandBuffer()
|
||||||
|
{
|
||||||
|
EndEncoding();
|
||||||
|
|
||||||
|
if (m_commandBuffer)
|
||||||
|
{
|
||||||
|
m_commandBuffer->commit();
|
||||||
|
m_commandBuffer->release();
|
||||||
|
m_commandBuffer = nullptr;
|
||||||
|
|
||||||
|
// Reset temporary buffers
|
||||||
|
m_memoryManager->ResetTemporaryBuffers();
|
||||||
|
|
||||||
|
// Debug
|
||||||
|
m_commandQueue->insertDebugCaptureBoundary();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader)
|
void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader)
|
||||||
{
|
{
|
||||||
sint32 textureCount = shader->resourceMapping.getTextureCount();
|
sint32 textureCount = shader->resourceMapping.getTextureCount();
|
||||||
|
@ -5,11 +5,6 @@
|
|||||||
#include <Metal/Metal.hpp>
|
#include <Metal/Metal.hpp>
|
||||||
|
|
||||||
#include "Cafe/HW/Latte/Renderer/Renderer.h"
|
#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"
|
|
||||||
|
|
||||||
#define MAX_MTL_BUFFERS 31
|
#define MAX_MTL_BUFFERS 31
|
||||||
#define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2)
|
#define GET_MTL_VERTEX_BUFFER_INDEX(index) (MAX_MTL_BUFFERS - index - 2)
|
||||||
@ -199,7 +194,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
CA::MetalLayer* m_metalLayer;
|
CA::MetalLayer* m_metalLayer;
|
||||||
|
|
||||||
MetalMemoryManager* m_memoryManager;
|
class MetalMemoryManager* m_memoryManager;
|
||||||
|
class MetalPipelineCache* m_pipelineCache;
|
||||||
|
|
||||||
// Metal objects
|
// Metal objects
|
||||||
MTL::Device* m_device;
|
MTL::Device* m_device;
|
||||||
@ -222,144 +218,13 @@ private:
|
|||||||
MetalState m_state;
|
MetalState m_state;
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
void EnsureCommandBuffer()
|
void EnsureCommandBuffer();
|
||||||
{
|
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true);
|
||||||
if (!m_commandBuffer)
|
MTL::ComputeCommandEncoder* GetComputeCommandEncoder();
|
||||||
{
|
MTL::BlitCommandEncoder* GetBlitCommandEncoder();
|
||||||
// Debug
|
void EndEncoding();
|
||||||
m_commandQueue->insertDebugCaptureBoundary();
|
void CommitCommandBuffer();
|
||||||
|
|
||||||
m_commandBuffer = m_commandQueue->commandBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some render passes clear the attachments, forceRecreate is supposed to be used in those cases
|
|
||||||
MTL::RenderCommandEncoder* GetRenderCommandEncoder(MTL::RenderPassDescriptor* renderPassDescriptor, MTL::Texture* colorRenderTargets[8], MTL::Texture* depthRenderTarget, bool forceRecreate = false, bool rebindStateIfNewEncoder = true)
|
|
||||||
{
|
|
||||||
EnsureCommandBuffer();
|
|
||||||
|
|
||||||
// Check if we need to begin a new render pass
|
|
||||||
if (m_commandEncoder)
|
|
||||||
{
|
|
||||||
if (!forceRecreate)
|
|
||||||
{
|
|
||||||
if (m_encoderType == MetalEncoderType::Render)
|
|
||||||
{
|
|
||||||
bool needsNewRenderPass = false;
|
|
||||||
for (uint8 i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
if (colorRenderTargets[i] && (colorRenderTargets[i] != m_state.colorRenderTargets[i]))
|
|
||||||
{
|
|
||||||
needsNewRenderPass = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsNewRenderPass)
|
|
||||||
{
|
|
||||||
if (depthRenderTarget && (depthRenderTarget != m_state.depthRenderTarget))
|
|
||||||
{
|
|
||||||
needsNewRenderPass = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!needsNewRenderPass)
|
|
||||||
{
|
|
||||||
return (MTL::RenderCommandEncoder*)m_commandEncoder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EndEncoding();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (rebindStateIfNewEncoder)
|
|
||||||
{
|
|
||||||
// Rebind all the render state
|
|
||||||
RebindRenderState(renderCommandEncoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_commandEncoder)
|
|
||||||
{
|
|
||||||
m_commandEncoder->endEncoding();
|
|
||||||
m_commandEncoder->release();
|
|
||||||
m_commandEncoder = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CommitCommandBuffer()
|
|
||||||
{
|
|
||||||
EndEncoding();
|
|
||||||
|
|
||||||
if (m_commandBuffer)
|
|
||||||
{
|
|
||||||
m_commandBuffer->commit();
|
|
||||||
m_commandBuffer->release();
|
|
||||||
m_commandBuffer = nullptr;
|
|
||||||
|
|
||||||
// Reset temporary buffers
|
|
||||||
m_memoryManager->ResetTemporaryBuffers();
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
m_commandQueue->insertDebugCaptureBoundary();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
|
void BindStageResources(MTL::RenderCommandEncoder* renderCommandEncoder, LatteDecompilerShader* shader);
|
||||||
|
|
||||||
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
|
void RebindRenderState(MTL::RenderCommandEncoder* renderCommandEncoder);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user