Latte: Simplify code

This commit is contained in:
Exzap 2026-06-20 17:34:25 +02:00
parent 3923bfcc01
commit 57bc919e7c
9 changed files with 61 additions and 193 deletions

View File

@ -76,12 +76,12 @@ struct LatteDrawcallContext
{ {
bool isFirst{}; // first _execute() in current sequence bool isFirst{}; // first _execute() in current sequence
// these are only valid if isFirst is false: // these are only valid if isFirst is false:
mutable uint32 vertexBufferDirtyMask{}; // mask of vertex buffer indices which have been modified since last draw execute. It's the responsibility of the backend to unset bits mutable uint32 vertexBufferDirtyMask{}; // mask of vertex buffer indices which have been modified since last draw execute
mutable uint32 vsUniformBufferDirtyMask{}; // mask of uniform buffer indices which have been modified (changed address or size) since last draw execute. It's the responsibility of the backend to unset bits uint32 vsUniformBufferDirtyMask{}; // mask of uniform buffer indices which have been modified (changed address or size) since last draw execute
mutable uint32 psUniformBufferDirtyMask{}; uint32 psUniformBufferDirtyMask{};
mutable uint32 gsUniformBufferDirtyMask{}; uint32 gsUniformBufferDirtyMask{};
mutable bool aluConstVSDirty{}; bool aluConstVSDirty{};
mutable bool aluConstPSDirty{}; bool aluConstPSDirty{};
}; };
// texture // texture
@ -168,10 +168,8 @@ void LatteCP_ProcessRingbuffer();
// buffer cache // buffer cache
bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount); void LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, uint32 attribBufferDirtyMask, uint32 vsUniformBufferDirtyMask, uint32 psUniformBufferDirtyMask, uint32 gsUniformBufferDirtyMask, uint8& stageUniformModifiedMask, bool isIncremental = false);
void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, const LatteDrawcallContext& drawcallContext, uint8& stageUniformModifiedMask); bool LatteBufferCache_LoadRemappedUniforms(struct LatteDecompilerShader* shader, float* uniformData, bool aluConstDirty, uint32 uniformBufferDirtyMask);
void LatteBufferCache_LoadRemappedUniforms(struct LatteDecompilerShader* shader, float* uniformData);
bool LatteBufferCache_LoadRemappedUniformsIncremental(LatteDecompilerShader* shader, float* uniformData, const LatteDrawcallContext& drawcallContext);
void LatteRenderTarget_updateViewport(); void LatteRenderTarget_updateViewport();

View File

@ -70,101 +70,32 @@ void rectGenerate4thVertex(uint32be* output, uint32be* input0, uint32be* input1,
output[f] = _swapEndianU32(output[f]); output[f] = _swapEndianU32(output[f]);
} }
void LatteBufferCache_LoadRemappedUniforms(LatteDecompilerShader* shader, float* uniformData) bool LatteBufferCache_LoadRemappedUniforms(LatteDecompilerShader* shader, float* uniformData, bool aluConstDirty, uint32 uniformBufferDirtyMask)
{
uint32 shaderAluConst;
uint32 shaderUniformRegisterOffset;
switch (shader->shaderType)
{
case LatteConst::ShaderType::Vertex:
shaderAluConst = 0x400;
shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START;
break;
case LatteConst::ShaderType::Pixel:
shaderAluConst = 0;
shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START;
break;
case LatteConst::ShaderType::Geometry:
shaderAluConst = 0; // geometry shader has no ALU const
shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START;
break;
default:
UNREACHABLE;
}
// sourced from uniform registers
uint32* aluConstBase = LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst;
for (auto it : shader->list_remappedUniformEntries_register)
{
uint64* uniformRegData = (uint64*)(aluConstBase + it.indexOffset / 4);
uint64* regDest = (uint64*)((uint8*)uniformData + it.mappedIndexOffset);
regDest[0] = uniformRegData[0];
regDest[1] = uniformRegData[1];
}
// sourced from uniform buffers
for (auto& bufferGroup : shader->list_remappedUniformEntries_bufferGroups)
{
MPTR physicalAddr = LatteGPUState.contextRegister[shaderUniformRegisterOffset + bufferGroup.kcacheBankIdOffset / 4];
if (physicalAddr)
{
uint8* __restrict uniformBase = memory_base + physicalAddr;
for (auto& it : bufferGroup.entries)
{
uint64* __restrict regDest = (uint64*)((uint8*)uniformData + it.mappedIndexOffset);
uint64* __restrict uniformEntrySrc = (uint64*)(uniformBase + it.indexOffset);
memcpy(regDest, uniformEntrySrc, 16);
}
}
else
{
for (auto& it : bufferGroup.entries)
{
uint64* regDest = (uint64*)((uint8*)uniformData + it.mappedIndexOffset);
regDest[0] = 0;
regDest[1] = 0;
}
}
}
}
// like LatteBufferCache_LoadRemappedUniforms but only updates data that is flagged as dirty in drawcallContext, returns true if any uniform value was updated.
bool LatteBufferCache_LoadRemappedUniformsIncremental(LatteDecompilerShader* shader, float* uniformData, const LatteDrawcallContext& drawcallContext)
{ {
bool hasChange = false; bool hasChange = false;
uint32 shaderAluConst; uint32 shaderAluConst;
uint32 shaderUniformRegisterOffset; uint32 shaderUniformRegisterOffset;
bool hasDirtyAluConst = false;
uint32 uniformBufferDirtyMask = 0;
switch (shader->shaderType) switch (shader->shaderType)
{ {
case LatteConst::ShaderType::Vertex: case LatteConst::ShaderType::Vertex:
shaderAluConst = 0x400; shaderAluConst = 0x400;
shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START; shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START;
hasDirtyAluConst = drawcallContext.aluConstVSDirty;
uniformBufferDirtyMask = drawcallContext.vsUniformBufferDirtyMask;
drawcallContext.vsUniformBufferDirtyMask = 0;
break; break;
case LatteConst::ShaderType::Pixel: case LatteConst::ShaderType::Pixel:
shaderAluConst = 0; shaderAluConst = 0;
shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START; shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START;
hasDirtyAluConst = drawcallContext.aluConstPSDirty;
uniformBufferDirtyMask = drawcallContext.psUniformBufferDirtyMask;
drawcallContext.psUniformBufferDirtyMask = 0;
break; break;
case LatteConst::ShaderType::Geometry: case LatteConst::ShaderType::Geometry:
shaderAluConst = 0; // geometry shader has no ALU const shaderAluConst = 0; // geometry shader has no ALU const
shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START; shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START;
uniformBufferDirtyMask = drawcallContext.gsUniformBufferDirtyMask;
drawcallContext.gsUniformBufferDirtyMask = 0;
break; break;
default: default:
UNREACHABLE; UNREACHABLE;
} }
// sourced from uniform registers // sourced from uniform registers
if (hasDirtyAluConst) if (aluConstDirty)
{ {
uint32* aluConstBase = LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst; uint32* aluConstBase = LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst;
for (auto it : shader->list_remappedUniformEntries_register) for (auto it : shader->list_remappedUniformEntries_register)
@ -238,100 +169,34 @@ bool LatteBufferCache_syncGPUUniformBuffers(LatteDecompilerShader* shader, const
static sint32 s_vtxStateMaxIndex{}; static sint32 s_vtxStateMaxIndex{};
static sint32 s_vtxStateMaxInstance{}; static sint32 s_vtxStateMaxInstance{};
// upload vertex and uniform buffers void LatteBufferCache_ProcessQueues()
bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount)
{ {
static uint32 s_syncBufferCounter = 0; static uint32 s_syncBufferCounter = 0;
s_vtxStateMaxIndex = maxIndex;
s_vtxStateMaxInstance = baseInstance + instanceCount - 1;
s_syncBufferCounter++; s_syncBufferCounter++;
if (s_syncBufferCounter >= 20) if (s_syncBufferCounter >= 25)
{ {
LatteBufferCache_incrementalCleanup(); LatteBufferCache_incrementalCleanup();
s_syncBufferCounter = 0; s_syncBufferCounter = 0;
} }
LatteBufferCache_processDCFlushQueue(); LatteBufferCache_processDCFlushQueue();
// process queued deallocations from previous drawcall // process queued deallocations from previous drawcall
LatteBufferCache_processDeallocations(); LatteBufferCache_processDeallocations();
// sync and bind vertex buffers
LatteFetchShader* parsedFetchShader = LatteSHRC_GetActiveFetchShader();
if (!parsedFetchShader)
return false;
uint32* __restrict bufferRegStartPtr = LatteGPUState.contextRegister + mmSQ_VTX_ATTRIBUTE_BLOCK_START;
for (auto& bufferGroup : parsedFetchShader->bufferGroups)
{
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
uint32* __restrict bufferRegs = bufferRegStartPtr + bufferIndex * 7;
MPTR bufferAddress = bufferRegs[0];
/* uint32 bufferSize = bufferRegs[1] + 1; */
uint32 bufferStride = (bufferRegs[2] >> 11) & 0xFFFF;
if (bufferAddress == MPTR_NULL) [[unlikely]]
{
g_renderer->buffer_bindVertexBuffer(bufferIndex, 0, 0);
continue;
} }
// dont rely on buffer size given by game // upload vertex and uniform buffers and update bindings
uint32 fixedBufferSize = 0; void LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, uint32 attribBufferDirtyMask, uint32 vsUniformBufferDirtyMask, uint32 psUniformBufferDirtyMask, uint32 gsUniformBufferDirtyMask, uint8& stageUniformModifiedMask, bool isIncremental)
if (bufferGroup.hasVtxIndexAccess)
fixedBufferSize = bufferStride * (maxIndex + 1) + bufferGroup.maxOffset;
if (bufferGroup.hasInstanceIndexAccess)
{ {
uint32 fixedBufferSizeInstance = bufferStride * ((baseInstance + instanceCount) + 1) + bufferGroup.maxOffset;
fixedBufferSize = std::max(fixedBufferSize, fixedBufferSizeInstance);
}
if (fixedBufferSize == 0 || bufferStride == 0)
fixedBufferSize += 128;
#if BOOST_OS_MACOS && defined(ENABLE_VULKAN)
if(bufferStride % 4 != 0)
{
if (g_renderer->GetType() == RendererAPI::Vulkan)
{
if (VulkanRenderer* vkRenderer = VulkanRenderer::GetInstance())
{
auto fixedBuffer = vkRenderer->buffer_genStrideWorkaroundVertexBuffer(bufferAddress, fixedBufferSize, bufferStride);
vkRenderer->buffer_bindVertexStrideWorkaroundBuffer(fixedBuffer.first, fixedBuffer.second, bufferIndex, fixedBufferSize);
continue;
}
}
}
#endif
uint32 bindOffset = LatteBufferCache_retrieveDataInCache(bufferAddress, fixedBufferSize);
g_renderer->buffer_bindVertexBuffer(bufferIndex, bindOffset, fixedBufferSize);
}
// sync uniform buffers
LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader();
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader();
if (vertexShader && vertexShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
LatteBufferCache_syncGPUUniformBuffers(vertexShader, mmSQ_VTX_UNIFORM_BLOCK_START, LatteConst::ShaderType::Vertex, 0xFFFFFFFF);
if (pixelShader && pixelShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
LatteBufferCache_syncGPUUniformBuffers(pixelShader, mmSQ_PS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Pixel, 0xFFFFFFFF);
if (geometryShader && geometryShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
LatteBufferCache_syncGPUUniformBuffers(geometryShader, mmSQ_GS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Geometry, 0xFFFFFFFF);
return true;
}
// similar to LatteBufferCache_SyncIncremental but updates only the bindings that are marked in the dirty masks
void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, const LatteDrawcallContext& drawcallContext, uint8& stageUniformModifiedMask)
{
// uint32 attribBufferDirtyMask, uint32 uniformBufferVSDirtyMask, uint32 uniformBufferPSDirtyMask, uint32 uniformBufferGSDirtyMask
LatteFetchShader* parsedFetchShader = LatteSHRC_GetActiveFetchShader(); LatteFetchShader* parsedFetchShader = LatteSHRC_GetActiveFetchShader();
cemu_assert_debug(parsedFetchShader); cemu_assert_debug(parsedFetchShader);
uint32 attribBufferDirtyMask = drawcallContext.vertexBufferDirtyMask;
// todo - vertex attribute offsets may eventually be allowed to change between incremental draws, we should set the attrib dirty bits in that case
if (isIncremental)
{
// dont process flush queue and dont process deallocations yet, we are in the middle of a sequence of drawcalls that (most likely) reuse previous bindings
if ( maxIndex > s_vtxStateMaxIndex ) if ( maxIndex > s_vtxStateMaxIndex )
{ {
attribBufferDirtyMask = 0xFFFFFFFF; attribBufferDirtyMask = 0xFFFFFFFF;
// todo - if the fetch shader can change between incremental calls, then we should also mark all of drawcallContext.vertexBufferDirtyMask as dirty. Or we could track the count per-buffer instead of as max index and max instance
// but for simplicity sake we should disallow fetch shader changes that use a differing set of buffers (this is already the case)
s_vtxStateMaxIndex = maxIndex; s_vtxStateMaxIndex = maxIndex;
} }
uint32 maxInstance = baseInstance + instanceCount - 1; uint32 maxInstance = baseInstance + instanceCount - 1;
@ -340,10 +205,16 @@ void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 b
attribBufferDirtyMask = 0xFFFFFFFF; attribBufferDirtyMask = 0xFFFFFFFF;
s_vtxStateMaxInstance = maxInstance; s_vtxStateMaxInstance = maxInstance;
} }
}
else
{
LatteBufferCache_ProcessQueues();
s_vtxStateMaxIndex = maxIndex;
uint32 maxInstance = baseInstance + instanceCount - 1;
s_vtxStateMaxInstance = maxInstance;
}
attribBufferDirtyMask &= parsedFetchShader->attributeBufferMask; attribBufferDirtyMask &= parsedFetchShader->attributeBufferMask;
// dont process flush queue and dont process deallocations yet, we are in the middle of a sequence of drawcalls that (most likely) reuse previous bindings
// sync and bind dirty vertex buffers // sync and bind dirty vertex buffers
if (attribBufferDirtyMask != 0) if (attribBufferDirtyMask != 0)
{ {
@ -353,7 +224,6 @@ void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 b
uint32 bufferIndex = bufferGroup.attributeBufferIndex; uint32 bufferIndex = bufferGroup.attributeBufferIndex;
if ((attribBufferDirtyMask&(1<<bufferIndex)) == 0) if ((attribBufferDirtyMask&(1<<bufferIndex)) == 0)
continue; continue;
drawcallContext.vertexBufferDirtyMask &= ~(1<<bufferIndex);
uint32* __restrict bufferRegs = bufferRegStartPtr + bufferIndex * 7; uint32* __restrict bufferRegs = bufferRegStartPtr + bufferIndex * 7;
MPTR bufferAddress = bufferRegs[0]; MPTR bufferAddress = bufferRegs[0];
uint32 bufferStride = (bufferRegs[2] >> 11) & 0xFFFF; uint32 bufferStride = (bufferRegs[2] >> 11) & 0xFFFF;
@ -403,17 +273,17 @@ void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 b
// todo - if we AND the shader uniform buffer mask and the dirty mask we can completely skip calling syncGPUUniformBuffers if no relevant buffer was updated // todo - if we AND the shader uniform buffer mask and the dirty mask we can completely skip calling syncGPUUniformBuffers if no relevant buffer was updated
if (vertexShader && vertexShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) if (vertexShader && vertexShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
{ {
if (LatteBufferCache_syncGPUUniformBuffers(vertexShader, mmSQ_VTX_UNIFORM_BLOCK_START, LatteConst::ShaderType::Vertex, drawcallContext.vsUniformBufferDirtyMask)) if (LatteBufferCache_syncGPUUniformBuffers(vertexShader, mmSQ_VTX_UNIFORM_BLOCK_START, LatteConst::ShaderType::Vertex, vsUniformBufferDirtyMask))
stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX); stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX);
} }
if (pixelShader && pixelShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) if (pixelShader && pixelShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
{ {
if (LatteBufferCache_syncGPUUniformBuffers(pixelShader, mmSQ_PS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Pixel, drawcallContext.psUniformBufferDirtyMask)) if (LatteBufferCache_syncGPUUniformBuffers(pixelShader, mmSQ_PS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Pixel, psUniformBufferDirtyMask))
stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT); // todo - move this enum to Latte? stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT); // todo - move this enum to Latte?
} }
if (geometryShader && geometryShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK) if (geometryShader && geometryShader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
{ {
if ( LatteBufferCache_syncGPUUniformBuffers(geometryShader, mmSQ_GS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Geometry, drawcallContext.gsUniformBufferDirtyMask) ) if ( LatteBufferCache_syncGPUUniformBuffers(geometryShader, mmSQ_GS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Geometry, gsUniformBufferDirtyMask) )
stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY); stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY);
} }
} }

View File

@ -84,6 +84,11 @@ public:
performanceMonitor.cycle[performanceMonitor.cycleIndex].fastDrawCallCounter++; performanceMonitor.cycle[performanceMonitor.cycleIndex].fastDrawCallCounter++;
m_drawcallContext.isFirst = false; m_drawcallContext.isFirst = false;
m_drawcallContext.vertexBufferDirtyMask = 0; m_drawcallContext.vertexBufferDirtyMask = 0;
m_drawcallContext.vsUniformBufferDirtyMask = 0;
m_drawcallContext.psUniformBufferDirtyMask = 0;
m_drawcallContext.gsUniformBufferDirtyMask = 0;
m_drawcallContext.aluConstVSDirty = false;
m_drawcallContext.aluConstPSDirty = false;
} }
void endDrawPass() void endDrawPass()

View File

@ -10,7 +10,7 @@
#define LATTE_NUM_COLOR_TARGET 8 #define LATTE_NUM_COLOR_TARGET 8
#define LATTE_NUM_MAX_TEX_UNITS 18 // number of available texture units per shader stage (this might be higher than 18? BotW is the only game which uses more than 16?) #define LATTE_NUM_MAX_TEX_UNITS 18 // number of available texture units per shader stage (this might be higher than 18? BotW is the only game which uses more than 16?)
#define LATTE_NUM_MAX_UNIFORM_BUFFERS 16 // number of supported uniform buffer binding locations #define LATTE_NUM_MAX_UNIFORM_BUFFERS 16 // number of supported uniform buffer binding locations per shader stage
#define LATTE_VS_ATTRIBUTE_LIMIT 32 // todo: verify #define LATTE_VS_ATTRIBUTE_LIMIT 32 // todo: verify
#define LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS 256 // should this be 128 since there are only 128 GPRs? #define LATTE_NUM_MAX_ATTRIBUTE_LOCATIONS 256 // should this be 128 since there are only 128 GPRs?

View File

@ -1164,7 +1164,8 @@ void MetalRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32
{ {
// synchronize vertex and uniform cache and update buffer bindings // synchronize vertex and uniform cache and update buffer bindings
// We need to call this before getting the render command encoder, since it can cause buffer copies // We need to call this before getting the render command encoder, since it can cause buffer copies
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount); uint8 stageUniformModifiedMask = 0;
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, stageUniformModifiedMask);
} }
// Render pass // Render pass
@ -2138,7 +2139,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
} }
if (shader->uniform.loc_remapped >= 0) if (shader->uniform.loc_remapped >= 0)
{ {
LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped)); LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped), true, (1<<LATTE_NUM_MAX_UNIFORM_BUFFERS)-1);
} }
if (shader->uniform.loc_uniformRegister >= 0) if (shader->uniform.loc_uniformRegister >= 0)
{ {

View File

@ -975,7 +975,8 @@ void OpenGLRenderer::draw_genericDrawHandler(uint32 baseVertex, uint32 baseInsta
endPerfMonProfiling(performanceMonitor.gpuTime_dcStageIndexMgr); endPerfMonProfiling(performanceMonitor.gpuTime_dcStageIndexMgr);
// synchronize vertex and uniform buffers // synchronize vertex and uniform buffers
LatteBufferCache_Sync(indexState.minIndex + baseVertex, indexState.maxIndex + baseVertex, baseInstance, instanceCount); uint8 stageUniformModifiedMask = 0;
LatteBufferCache_Sync(indexState.minIndex + baseVertex, indexState.maxIndex + baseVertex, baseInstance, instanceCount, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, stageUniformModifiedMask);
_setupVertexAttributes(); _setupVertexAttributes();

View File

@ -36,7 +36,7 @@ void OpenGLRenderer::uniformData_update()
auto& list_uniformMapping = shader->list_remappedUniformEntries; auto& list_uniformMapping = shader->list_remappedUniformEntries;
cemu_assert_debug(list_uniformMapping.size() <= 256); cemu_assert_debug(list_uniformMapping.size() <= 256);
sint32 remappedArraySize = (sint32)list_uniformMapping.size(); sint32 remappedArraySize = (sint32)list_uniformMapping.size();
LatteBufferCache_LoadRemappedUniforms(shader, (float*)(_gl_remappedUniformData)); LatteBufferCache_LoadRemappedUniforms(shader, (float*)(_gl_remappedUniformData), true, (1<<LATTE_NUM_MAX_UNIFORM_BUFFERS)-1);
// update values only when the hash changed // update values only when the hash changed
if (remappedArraySize > 0) if (remappedArraySize > 0)
{ {

View File

@ -563,7 +563,7 @@ private:
// uniform // uniform
uint32 uniformData_uploadUniformDataBufferGetOffset(std::span<uint8, std::dynamic_extent> data); uint32 uniformData_uploadUniformDataBufferGetOffset(std::span<uint8, std::dynamic_extent> data);
void uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader, float* __restrict uniformBuf); void uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader, float* __restrict uniformBuf);
void uniformData_updateUniformVarsIncremental(uint32 shaderStageIndex, LatteDecompilerShader* shader, const LatteDrawcallContext& drawcallContext, uint8& stageUniformModifiedMask, float* __restrict uniformBuf); void uniformData_updateUniformVarsIncremental(uint32 shaderStageIndex, LatteDecompilerShader* shader, uint8& stageUniformModifiedMask, float* __restrict uniformBuf, bool aluConstDirty, uint32 uniformBufferDirtyMask);
// misc // misc
void CreatePipelineCache(); void CreatePipelineCache();

View File

@ -405,7 +405,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
} }
if (shader->uniform.loc_remapped >= 0) if (shader->uniform.loc_remapped >= 0)
{ {
LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped)); LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped), true, (1<<LATTE_NUM_MAX_UNIFORM_BUFFERS)-1);
} }
if (shader->uniform.loc_uniformRegister >= 0) if (shader->uniform.loc_uniformRegister >= 0)
{ {
@ -454,7 +454,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)uniformBuf, shader->uniform.uniformRangeSize}); dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)uniformBuf, shader->uniform.uniformRangeSize});
} }
void VulkanRenderer::uniformData_updateUniformVarsIncremental(uint32 shaderStageIndex, LatteDecompilerShader* shader, const LatteDrawcallContext& drawcallContext, uint8& stageUniformModifiedMask, float* __restrict uniformBuf) void VulkanRenderer::uniformData_updateUniformVarsIncremental(uint32 shaderStageIndex, LatteDecompilerShader* shader, uint8& stageUniformModifiedMask, float* __restrict uniformBuf, bool aluConstDirty, uint32 uniformBufferDirtyMask)
{ {
// similar to uniformData_updateUniformVars, but checks only the fields that can change during a fast draw sequence // similar to uniformData_updateUniformVars, but checks only the fields that can change during a fast draw sequence
auto GET_UNIFORM_DATA_PTR = [&uniformBuf](size_t index) { return uniformBuf + (index / 4); }; auto GET_UNIFORM_DATA_PTR = [&uniformBuf](size_t index) { return uniformBuf + (index / 4); };
@ -470,22 +470,19 @@ void VulkanRenderer::uniformData_updateUniformVarsIncremental(uint32 shaderStage
if (shader->uniform.loc_remapped >= 0) if (shader->uniform.loc_remapped >= 0)
{ {
cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED); cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_REMAPPED);
hasChange = LatteBufferCache_LoadRemappedUniformsIncremental(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped), drawcallContext); hasChange = LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped), aluConstDirty, uniformBufferDirtyMask);
} }
if (shader->uniform.loc_uniformRegister >= 0) if (shader->uniform.loc_uniformRegister >= 0 && aluConstDirty)
{ {
cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE); cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE);
bool isDirty = false;
sint32 shaderAluConst; sint32 shaderAluConst;
switch (shader->shaderType) switch (shader->shaderType)
{ {
case LatteConst::ShaderType::Vertex: case LatteConst::ShaderType::Vertex:
shaderAluConst = 0x400; shaderAluConst = 0x400;
isDirty = drawcallContext.aluConstVSDirty;
break; break;
case LatteConst::ShaderType::Pixel: case LatteConst::ShaderType::Pixel:
shaderAluConst = 0; shaderAluConst = 0;
isDirty = drawcallContext.aluConstPSDirty;
break; break;
case LatteConst::ShaderType::Geometry: case LatteConst::ShaderType::Geometry:
cemu_assert_suspicious(); cemu_assert_suspicious();
@ -495,13 +492,10 @@ void VulkanRenderer::uniformData_updateUniformVarsIncremental(uint32 shaderStage
default: default:
UNREACHABLE; UNREACHABLE;
} }
if (isDirty)
{
hasChange = true; hasChange = true;
uint32* uniformRegData = (uint32*)(LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst); uint32* uniformRegData = (uint32*)(LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst);
memcpy(GET_UNIFORM_DATA_PTR(shader->uniform.loc_uniformRegister), uniformRegData, shader->uniform.count_uniformRegister * 16); memcpy(GET_UNIFORM_DATA_PTR(shader->uniform.loc_uniformRegister), uniformRegData, shader->uniform.count_uniformRegister * 16);
} }
}
if (hasChange) if (hasChange)
{ {
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)uniformBuf, shader->uniform.uniformRangeSize}); dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)uniformBuf, shader->uniform.uniformRangeSize});
@ -1417,7 +1411,8 @@ void VulkanRenderer::draw_execute_first(uint32 baseVertex, uint32 baseInstance,
else else
{ {
// synchronize vertex and uniform cache and update buffer bindings // synchronize vertex and uniform cache and update buffer bindings
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount); uint8 stageUniformModifiedMask = 0;
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, stageUniformModifiedMask);
} }
PipelineInfo* pipeline_info = draw_getOrCreateGraphicsPipeline(count); PipelineInfo* pipeline_info = draw_getOrCreateGraphicsPipeline(count);
@ -1529,13 +1524,11 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
uint8 stageUniformModifiedMask = 0; // one bit for each stage (using SHADER_STAGE_INDEX_* as bit index). Set if any uniform data has been modified and needs to be reuploaded uint8 stageUniformModifiedMask = 0; // one bit for each stage (using SHADER_STAGE_INDEX_* as bit index). Set if any uniform data has been modified and needs to be reuploaded
if (vertexShader) if (vertexShader)
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataVS); uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader, stageUniformModifiedMask, s_vkUniformDataVS, drawcallContext.aluConstVSDirty, drawcallContext.vsUniformBufferDirtyMask);
if (pixelShader) if (pixelShader)
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataPS); uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader, stageUniformModifiedMask, s_vkUniformDataPS, drawcallContext.aluConstPSDirty, drawcallContext.psUniformBufferDirtyMask);
if (geometryShader) if (geometryShader)
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataGS); uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, stageUniformModifiedMask, s_vkUniformDataGS, false, drawcallContext.gsUniformBufferDirtyMask);
drawcallContext.aluConstVSDirty = false;
drawcallContext.aluConstPSDirty = false;
// store where the read pointer should go after command buffer execution // store where the read pointer should go after command buffer execution
m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex; m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex;
@ -1587,7 +1580,7 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
else else
{ {
// synchronize vertex and uniform cache and update buffer bindings // synchronize vertex and uniform cache and update buffer bindings
LatteBufferCache_SyncIncremental(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount, drawcallContext, stageUniformModifiedMask); LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount, drawcallContext.vertexBufferDirtyMask, drawcallContext.vsUniformBufferDirtyMask, drawcallContext.psUniformBufferDirtyMask, drawcallContext.gsUniformBufferDirtyMask, stageUniformModifiedMask, true);
} }
m_state.descriptorSetsChanged = false; m_state.descriptorSetsChanged = false;