Latte: Simplify code

This commit is contained in:
Exzap 2026-06-20 17:34:25 +02:00
parent 39693d0db5
commit 55469c408f
9 changed files with 61 additions and 193 deletions

View File

@ -76,12 +76,12 @@ struct LatteDrawcallContext
{
bool isFirst{}; // first _execute() in current sequence
// 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 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
mutable uint32 psUniformBufferDirtyMask{};
mutable uint32 gsUniformBufferDirtyMask{};
mutable bool aluConstVSDirty{};
mutable bool aluConstPSDirty{};
mutable uint32 vertexBufferDirtyMask{}; // mask of vertex buffer indices which have been modified since last draw execute
uint32 vsUniformBufferDirtyMask{}; // mask of uniform buffer indices which have been modified (changed address or size) since last draw execute
uint32 psUniformBufferDirtyMask{};
uint32 gsUniformBufferDirtyMask{};
bool aluConstVSDirty{};
bool aluConstPSDirty{};
};
// texture
@ -168,10 +168,8 @@ void LatteCP_ProcessRingbuffer();
// buffer cache
bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount);
void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, const LatteDrawcallContext& drawcallContext, uint8& stageUniformModifiedMask);
void LatteBufferCache_LoadRemappedUniforms(struct LatteDecompilerShader* shader, float* uniformData);
bool LatteBufferCache_LoadRemappedUniformsIncremental(LatteDecompilerShader* shader, float* uniformData, const LatteDrawcallContext& drawcallContext);
void LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, uint32 attribBufferDirtyMask, uint32 vsUniformBufferDirtyMask, uint32 psUniformBufferDirtyMask, uint32 gsUniformBufferDirtyMask, uint8& stageUniformModifiedMask, bool isIncremental = false);
bool LatteBufferCache_LoadRemappedUniforms(struct LatteDecompilerShader* shader, float* uniformData, bool aluConstDirty, uint32 uniformBufferDirtyMask);
void LatteRenderTarget_updateViewport();

View File

@ -70,101 +70,32 @@ void rectGenerate4thVertex(uint32be* output, uint32be* input0, uint32be* input1,
output[f] = _swapEndianU32(output[f]);
}
void LatteBufferCache_LoadRemappedUniforms(LatteDecompilerShader* shader, float* uniformData)
{
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 LatteBufferCache_LoadRemappedUniforms(LatteDecompilerShader* shader, float* uniformData, bool aluConstDirty, uint32 uniformBufferDirtyMask)
{
bool hasChange = false;
uint32 shaderAluConst;
uint32 shaderUniformRegisterOffset;
bool hasDirtyAluConst = false;
uint32 uniformBufferDirtyMask = 0;
switch (shader->shaderType)
{
case LatteConst::ShaderType::Vertex:
shaderAluConst = 0x400;
shaderUniformRegisterOffset = mmSQ_VTX_UNIFORM_BLOCK_START;
hasDirtyAluConst = drawcallContext.aluConstVSDirty;
uniformBufferDirtyMask = drawcallContext.vsUniformBufferDirtyMask;
drawcallContext.vsUniformBufferDirtyMask = 0;
break;
case LatteConst::ShaderType::Pixel:
shaderAluConst = 0;
shaderUniformRegisterOffset = mmSQ_PS_UNIFORM_BLOCK_START;
hasDirtyAluConst = drawcallContext.aluConstPSDirty;
uniformBufferDirtyMask = drawcallContext.psUniformBufferDirtyMask;
drawcallContext.psUniformBufferDirtyMask = 0;
break;
case LatteConst::ShaderType::Geometry:
shaderAluConst = 0; // geometry shader has no ALU const
shaderUniformRegisterOffset = mmSQ_GS_UNIFORM_BLOCK_START;
uniformBufferDirtyMask = drawcallContext.gsUniformBufferDirtyMask;
drawcallContext.gsUniformBufferDirtyMask = 0;
break;
default:
UNREACHABLE;
}
// sourced from uniform registers
if (hasDirtyAluConst)
if (aluConstDirty)
{
uint32* aluConstBase = LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst;
for (auto it : shader->list_remappedUniformEntries_register)
@ -238,112 +169,52 @@ bool LatteBufferCache_syncGPUUniformBuffers(LatteDecompilerShader* shader, const
static sint32 s_vtxStateMaxIndex{};
static sint32 s_vtxStateMaxInstance{};
// upload vertex and uniform buffers
bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount)
void LatteBufferCache_ProcessQueues()
{
static uint32 s_syncBufferCounter = 0;
s_vtxStateMaxIndex = maxIndex;
s_vtxStateMaxInstance = baseInstance + instanceCount - 1;
s_syncBufferCounter++;
if (s_syncBufferCounter >= 20)
if (s_syncBufferCounter >= 25)
{
LatteBufferCache_incrementalCleanup();
s_syncBufferCounter = 0;
}
LatteBufferCache_processDCFlushQueue();
// process queued deallocations from previous drawcall
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
uint32 fixedBufferSize = 0;
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)
// upload vertex and uniform buffers and update bindings
void LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount, uint32 attribBufferDirtyMask, uint32 vsUniformBufferDirtyMask, uint32 psUniformBufferDirtyMask, uint32 gsUniformBufferDirtyMask, uint8& stageUniformModifiedMask, bool isIncremental)
{
// uint32 attribBufferDirtyMask, uint32 uniformBufferVSDirtyMask, uint32 uniformBufferPSDirtyMask, uint32 uniformBufferGSDirtyMask
LatteFetchShader* parsedFetchShader = LatteSHRC_GetActiveFetchShader();
cemu_assert_debug(parsedFetchShader);
uint32 attribBufferDirtyMask = drawcallContext.vertexBufferDirtyMask;
if ( maxIndex > s_vtxStateMaxIndex )
// 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)
{
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;
// 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 )
{
attribBufferDirtyMask = 0xFFFFFFFF;
s_vtxStateMaxIndex = maxIndex;
}
uint32 maxInstance = baseInstance + instanceCount - 1;
if ( maxInstance > s_vtxStateMaxInstance )
{
attribBufferDirtyMask = 0xFFFFFFFF;
s_vtxStateMaxInstance = maxInstance;
}
}
uint32 maxInstance = baseInstance + instanceCount - 1;
if ( maxInstance > s_vtxStateMaxInstance )
else
{
attribBufferDirtyMask = 0xFFFFFFFF;
LatteBufferCache_ProcessQueues();
s_vtxStateMaxIndex = maxIndex;
uint32 maxInstance = baseInstance + instanceCount - 1;
s_vtxStateMaxInstance = maxInstance;
}
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
if (attribBufferDirtyMask != 0)
{
@ -353,7 +224,6 @@ void LatteBufferCache_SyncIncremental(uint32 minIndex, uint32 maxIndex, uint32 b
uint32 bufferIndex = bufferGroup.attributeBufferIndex;
if ((attribBufferDirtyMask&(1<<bufferIndex)) == 0)
continue;
drawcallContext.vertexBufferDirtyMask &= ~(1<<bufferIndex);
uint32* __restrict bufferRegs = bufferRegStartPtr + bufferIndex * 7;
MPTR bufferAddress = bufferRegs[0];
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
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);
}
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?
}
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);
}
}

View File

@ -84,6 +84,11 @@ public:
performanceMonitor.cycle[performanceMonitor.cycleIndex].fastDrawCallCounter++;
m_drawcallContext.isFirst = false;
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()

View File

@ -10,7 +10,7 @@
#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_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_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
// 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
@ -2138,7 +2139,7 @@ void MetalRenderer::BindStageResources(MTL::RenderCommandEncoder* renderCommandE
}
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)
{

View File

@ -975,7 +975,8 @@ void OpenGLRenderer::draw_genericDrawHandler(uint32 baseVertex, uint32 baseInsta
endPerfMonProfiling(performanceMonitor.gpuTime_dcStageIndexMgr);
// 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();

View File

@ -36,7 +36,7 @@ void OpenGLRenderer::uniformData_update()
auto& list_uniformMapping = shader->list_remappedUniformEntries;
cemu_assert_debug(list_uniformMapping.size() <= 256);
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
if (remappedArraySize > 0)
{

View File

@ -563,7 +563,7 @@ private:
// uniform
uint32 uniformData_uploadUniformDataBufferGetOffset(std::span<uint8, std::dynamic_extent> data);
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
void CreatePipelineCache();

View File

@ -405,7 +405,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
}
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)
{
@ -454,7 +454,7 @@ void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, Latt
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
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)
{
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);
bool isDirty = false;
sint32 shaderAluConst;
switch (shader->shaderType)
{
case LatteConst::ShaderType::Vertex:
shaderAluConst = 0x400;
isDirty = drawcallContext.aluConstVSDirty;
break;
case LatteConst::ShaderType::Pixel:
shaderAluConst = 0;
isDirty = drawcallContext.aluConstPSDirty;
break;
case LatteConst::ShaderType::Geometry:
cemu_assert_suspicious();
@ -495,12 +492,9 @@ void VulkanRenderer::uniformData_updateUniformVarsIncremental(uint32 shaderStage
default:
UNREACHABLE;
}
if (isDirty)
{
hasChange = true;
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);
}
hasChange = true;
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);
}
if (hasChange)
{
@ -1417,7 +1411,8 @@ void VulkanRenderer::draw_execute_first(uint32 baseVertex, uint32 baseInstance,
else
{
// 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);
@ -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
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)
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)
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataGS);
drawcallContext.aluConstVSDirty = false;
drawcallContext.aluConstPSDirty = false;
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, stageUniformModifiedMask, s_vkUniformDataGS, false, drawcallContext.gsUniformBufferDirtyMask);
// store where the read pointer should go after command buffer execution
m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex;
@ -1587,7 +1580,7 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
else
{
// 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;