mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-10 01:24:41 -06:00
Latte+Vulkan: Optimize by using incremental state update checking
This commit is contained in:
parent
0d9eda1654
commit
d86e701b34
@ -240,6 +240,7 @@ void _fetchShaderDecompiler_parseInstruction_VTX_SEMANTIC(LatteFetchShader* pars
|
||||
else
|
||||
attribGroup = &parsedFetchShader->bufferGroupsInvalid.emplace_back();
|
||||
|
||||
parsedFetchShader->attributeBufferMask |= (1 << bufferIndex);
|
||||
attribGroup->attributeBufferIndex = bufferIndex;
|
||||
attribGroup->minOffset = offset;
|
||||
attribGroup->maxOffset = offset;
|
||||
|
||||
@ -42,6 +42,7 @@ struct LatteFetchShader
|
||||
std::vector<LatteParsedFetchShaderBufferGroup_t> bufferGroupsInvalid; // groups with buffer index not being a valid buffer (dst components of these can affect shader code, but no actual vertex imports are done)
|
||||
|
||||
uint64 key{};
|
||||
uint32 attributeBufferMask{}; // mask of buffers sourced by this fetch shader
|
||||
|
||||
// Vulkan
|
||||
uint64 vkPipelineHashFragment{}; // hash of all fetch shader state that influences the Vulkan graphics pipeline
|
||||
|
||||
@ -76,7 +76,12 @@ struct LatteDrawcallContext
|
||||
{
|
||||
bool isFirst{}; // first _execute() in current sequence
|
||||
// these are only valid if isFirst is false:
|
||||
// todo
|
||||
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{};
|
||||
};
|
||||
|
||||
// texture
|
||||
@ -164,7 +169,9 @@ 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 LatteRenderTarget_updateViewport();
|
||||
|
||||
|
||||
@ -128,34 +128,125 @@ void LatteBufferCache_LoadRemappedUniforms(LatteDecompilerShader* shader, float*
|
||||
}
|
||||
}
|
||||
|
||||
void LatteBufferCache_syncGPUUniformBuffers(LatteDecompilerShader* shader, const uint32 uniformBufferRegOffset, LatteConst::ShaderType shaderType)
|
||||
// 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)
|
||||
{
|
||||
if (shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK)
|
||||
bool hasChange = false;
|
||||
uint32 shaderAluConst;
|
||||
uint32 shaderUniformRegisterOffset;
|
||||
|
||||
bool hasDirtyAluConst = false;
|
||||
uint32 uniformBufferDirtyMask = 0;
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
for(const auto& buf : shader->list_quickBufferList)
|
||||
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)
|
||||
{
|
||||
uint32* aluConstBase = LatteGPUState.contextRegister + mmSQ_ALU_CONSTANT0_0 + shaderAluConst;
|
||||
for (auto it : shader->list_remappedUniformEntries_register)
|
||||
{
|
||||
sint32 i = buf.index;
|
||||
MPTR physicalAddr = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 0];
|
||||
uint32 uniformSize = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 1] + 1;
|
||||
if (physicalAddr == MPTR_NULL) [[unlikely]]
|
||||
{
|
||||
g_renderer->buffer_bindUniformBuffer(shaderType, i, 0, 0);
|
||||
uint64* __restrict uniformRegData = (uint64*)(aluConstBase + it.indexOffset / 4);
|
||||
uint64* __restrict regDest = (uint64*)((uint8*)uniformData + it.mappedIndexOffset);
|
||||
regDest[0] = uniformRegData[0];
|
||||
regDest[1] = uniformRegData[1];
|
||||
}
|
||||
if (!shader->list_remappedUniformEntries_register.empty())
|
||||
hasChange = true;
|
||||
}
|
||||
// sourced from uniform buffers
|
||||
if (uniformBufferDirtyMask)
|
||||
{
|
||||
for (auto& bufferGroup : shader->list_remappedUniformEntries_bufferGroups)
|
||||
{
|
||||
if ((uniformBufferDirtyMask&(1<<bufferGroup.bufferId)) == 0)
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
}
|
||||
uniformSize = std::min<uint32>(uniformSize, buf.size);
|
||||
uint32 bindOffset = LatteBufferCache_retrieveDataInCache(physicalAddr, uniformSize);
|
||||
g_renderer->buffer_bindUniformBuffer(shaderType, i, bindOffset, uniformSize);
|
||||
else
|
||||
{
|
||||
for (auto& it : bufferGroup.entries)
|
||||
{
|
||||
uint64* regDest = (uint64*)((uint8*)uniformData + it.mappedIndexOffset);
|
||||
regDest[0] = 0;
|
||||
regDest[1] = 0;
|
||||
}
|
||||
}
|
||||
hasChange = true;
|
||||
}
|
||||
}
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
bool LatteBufferCache_syncGPUUniformBuffers(LatteDecompilerShader* shader, const uint32 uniformBufferRegOffset, LatteConst::ShaderType shaderType, uint32 bufferDirtyMask)
|
||||
{
|
||||
cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CBANK);
|
||||
bool hasChange = false;
|
||||
for(const auto& buf : shader->list_quickBufferList)
|
||||
{
|
||||
sint32 i = buf.index;
|
||||
if ((bufferDirtyMask&(1<<i)) == 0)
|
||||
continue;
|
||||
hasChange = true;
|
||||
MPTR physicalAddr = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 0];
|
||||
uint32 uniformSize = LatteGPUState.contextRegister[uniformBufferRegOffset + i * 7 + 1] + 1;
|
||||
if (physicalAddr == MPTR_NULL) [[unlikely]]
|
||||
{
|
||||
g_renderer->buffer_bindUniformBuffer(shaderType, i, 0, 0);
|
||||
continue;
|
||||
}
|
||||
uniformSize = std::min<uint32>(uniformSize, buf.size);
|
||||
uint32 bindOffset = LatteBufferCache_retrieveDataInCache(physicalAddr, uniformSize);
|
||||
g_renderer->buffer_bindUniformBuffer(shaderType, i, bindOffset, uniformSize);
|
||||
}
|
||||
return hasChange;
|
||||
}
|
||||
|
||||
// for detecting when vertex buffer size needs to be extended during incremental rendering
|
||||
static sint32 s_vtxStateMaxIndex{};
|
||||
static sint32 s_vtxStateMaxInstance{};
|
||||
|
||||
// upload vertex and uniform buffers
|
||||
bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance, uint32 instanceCount)
|
||||
{
|
||||
static uint32 s_syncBufferCounter = 0;
|
||||
s_vtxStateMaxIndex = maxIndex;
|
||||
s_vtxStateMaxInstance = baseInstance + instanceCount - 1;
|
||||
|
||||
s_syncBufferCounter++;
|
||||
if (s_syncBufferCounter >= 30)
|
||||
if (s_syncBufferCounter >= 20)
|
||||
{
|
||||
LatteBufferCache_incrementalCleanup();
|
||||
s_syncBufferCounter = 0;
|
||||
@ -217,13 +308,112 @@ bool LatteBufferCache_Sync(uint32 minIndex, uint32 maxIndex, uint32 baseInstance
|
||||
}
|
||||
// sync uniform buffers
|
||||
LatteDecompilerShader* vertexShader = LatteSHRC_GetActiveVertexShader();
|
||||
if (vertexShader)
|
||||
LatteBufferCache_syncGPUUniformBuffers(vertexShader, mmSQ_VTX_UNIFORM_BLOCK_START, LatteConst::ShaderType::Vertex);
|
||||
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
if (geometryShader)
|
||||
LatteBufferCache_syncGPUUniformBuffers(geometryShader, mmSQ_GS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Geometry);
|
||||
LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader();
|
||||
if (pixelShader)
|
||||
LatteBufferCache_syncGPUUniformBuffers(pixelShader, mmSQ_PS_UNIFORM_BLOCK_START, LatteConst::ShaderType::Pixel);
|
||||
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();
|
||||
cemu_assert_debug(parsedFetchShader);
|
||||
uint32 attribBufferDirtyMask = drawcallContext.vertexBufferDirtyMask;
|
||||
|
||||
if ( maxIndex > s_vtxStateMaxIndex )
|
||||
{
|
||||
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;
|
||||
}
|
||||
uint32 maxInstance = baseInstance + instanceCount - 1;
|
||||
if ( maxInstance > s_vtxStateMaxInstance )
|
||||
{
|
||||
attribBufferDirtyMask = 0xFFFFFFFF;
|
||||
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)
|
||||
{
|
||||
uint32* __restrict bufferRegStartPtr = LatteGPUState.contextRegister + mmSQ_VTX_ATTRIBUTE_BLOCK_START;
|
||||
for (auto& bufferGroup : parsedFetchShader->bufferGroups)
|
||||
{
|
||||
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;
|
||||
|
||||
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();
|
||||
// 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))
|
||||
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))
|
||||
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) )
|
||||
stageUniformModifiedMask |= (1<<VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,8 +52,12 @@ public:
|
||||
{
|
||||
m_drawPassActive = true;
|
||||
m_drawcallContext.isFirst = true;
|
||||
m_vertexBufferChanged = true;
|
||||
m_uniformBufferChanged = true;
|
||||
m_drawcallContext.vertexBufferDirtyMask = 0;
|
||||
m_drawcallContext.vsUniformBufferDirtyMask = 0;
|
||||
m_drawcallContext.psUniformBufferDirtyMask = 0;
|
||||
m_drawcallContext.gsUniformBufferDirtyMask = 0;
|
||||
m_drawcallContext.aluConstVSDirty = false;
|
||||
m_drawcallContext.aluConstPSDirty = false;
|
||||
g_renderer->draw_beginSequence();
|
||||
}
|
||||
|
||||
@ -79,8 +83,7 @@ public:
|
||||
if (!m_drawcallContext.isFirst)
|
||||
performanceMonitor.cycle[performanceMonitor.cycleIndex].fastDrawCallCounter++;
|
||||
m_drawcallContext.isFirst = false;
|
||||
m_vertexBufferChanged = false;
|
||||
m_uniformBufferChanged = false;
|
||||
m_drawcallContext.vertexBufferDirtyMask = 0;
|
||||
}
|
||||
|
||||
void endDrawPass()
|
||||
@ -89,14 +92,34 @@ public:
|
||||
m_drawPassActive = false;
|
||||
}
|
||||
|
||||
void notifyModifiedVertexBuffer()
|
||||
void MarkVertexBufferDirty(uint32 index)
|
||||
{
|
||||
m_vertexBufferChanged = true;
|
||||
m_drawcallContext.vertexBufferDirtyMask |= (1<<index);
|
||||
}
|
||||
|
||||
void notifyModifiedUniformBuffer()
|
||||
void MarkVSAluConstantsDirty()
|
||||
{
|
||||
m_uniformBufferChanged = true;
|
||||
m_drawcallContext.aluConstVSDirty = true;
|
||||
}
|
||||
|
||||
void MarkPSAluConstantsDirty()
|
||||
{
|
||||
m_drawcallContext.aluConstPSDirty = true;
|
||||
}
|
||||
|
||||
void MarkVSUniformBufferDirty(uint32 index)
|
||||
{
|
||||
m_drawcallContext.vsUniformBufferDirtyMask |= (1 << index);
|
||||
}
|
||||
|
||||
void MarkPSUniformBufferDirty(uint32 index)
|
||||
{
|
||||
m_drawcallContext.psUniformBufferDirtyMask |= (1 << index);
|
||||
}
|
||||
|
||||
void MarkGSUniformBufferDirty(uint32 index)
|
||||
{
|
||||
m_drawcallContext.gsUniformBufferDirtyMask |= (1 << index);
|
||||
}
|
||||
|
||||
// command buffer processing position
|
||||
@ -120,8 +143,6 @@ public:
|
||||
private:
|
||||
bool m_drawPassActive{ false };
|
||||
LatteDrawcallContext m_drawcallContext{};
|
||||
bool m_vertexBufferChanged{ false };
|
||||
bool m_uniformBufferChanged{ false };
|
||||
boost::container::static_vector<CmdQueuePos, 4> m_queuePosStack;
|
||||
};
|
||||
|
||||
@ -334,7 +355,7 @@ bool LatteCP_itSetRegistersGeneric2(LatteCMDPtr cmd, uint32 nWords, TRegRangeCal
|
||||
const uint32 registerOffset = LatteReadCMD();
|
||||
const uint32 registerIndex = TRegisterBase + registerOffset;
|
||||
const uint32 registerStartIndex = registerIndex;
|
||||
const uint32 registerEndIndex = registerStartIndex + nWords;
|
||||
const uint32 registerEndIndex = registerStartIndex + nWords - 1;
|
||||
cemu_assert_debug((registerIndex + nWords) <= LATTE_MAX_REGISTER);
|
||||
|
||||
uint32* outputReg = (uint32*)(LatteGPUState.contextRegister + registerIndex);
|
||||
@ -996,25 +1017,34 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
|
||||
{
|
||||
LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_RESOURCE>(cmdData, nWords, [&drawPassCtx](uint32 registerStart, uint32 registerEnd, bool regValuesChanged)
|
||||
{
|
||||
if (!regValuesChanged)
|
||||
return;
|
||||
if ((registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_PS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) ||
|
||||
(registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_VS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)) ||
|
||||
(registerStart >= Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS && registerStart < (Latte::REGADDR::SQ_TEX_RESOURCE_WORD0_N_GS + Latte::GPU_LIMITS::NUM_TEXTURES_PER_STAGE * 7)))
|
||||
{
|
||||
if (regValuesChanged)
|
||||
{
|
||||
// cemuLog_log(LogType::Force, "[FastDrawMode] End due to texture change");
|
||||
drawPassCtx.endDrawPass(); // texture updates end the current draw sequence
|
||||
}
|
||||
// cemuLog_log(LogType::Force, "[FastDrawMode] End due to texture change");
|
||||
drawPassCtx.endDrawPass(); // texture updates end the current draw sequence
|
||||
}
|
||||
else if (registerStart >= mmSQ_VTX_ATTRIBUTE_BLOCK_START && registerEnd <= mmSQ_VTX_ATTRIBUTE_BLOCK_END)
|
||||
{
|
||||
if (regValuesChanged)
|
||||
drawPassCtx.notifyModifiedVertexBuffer();
|
||||
uint32 bufferIndex = (registerStart - mmSQ_VTX_ATTRIBUTE_BLOCK_START) / 7;
|
||||
drawPassCtx.MarkVertexBufferDirty(bufferIndex);
|
||||
}
|
||||
else
|
||||
else if (registerStart >= mmSQ_VTX_UNIFORM_BLOCK_START && registerEnd <= mmSQ_VTX_UNIFORM_BLOCK_END)
|
||||
{
|
||||
if (regValuesChanged)
|
||||
drawPassCtx.notifyModifiedUniformBuffer();
|
||||
uint32 bufferIndex = (registerStart - mmSQ_VTX_UNIFORM_BLOCK_START) / 7;
|
||||
drawPassCtx.MarkVSUniformBufferDirty(bufferIndex);
|
||||
}
|
||||
else if (registerStart >= mmSQ_PS_UNIFORM_BLOCK_START && registerEnd <= mmSQ_PS_UNIFORM_BLOCK_END)
|
||||
{
|
||||
uint32 bufferIndex = (registerStart - mmSQ_PS_UNIFORM_BLOCK_START) / 7;
|
||||
drawPassCtx.MarkPSUniformBufferDirty(bufferIndex);
|
||||
}
|
||||
else if (registerStart >= mmSQ_GS_UNIFORM_BLOCK_START && registerEnd <= mmSQ_GS_UNIFORM_BLOCK_END)
|
||||
{
|
||||
uint32 bufferIndex = (registerStart - mmSQ_GS_UNIFORM_BLOCK_START) / 7;
|
||||
drawPassCtx.MarkGSUniformBufferDirty(bufferIndex);
|
||||
}
|
||||
});
|
||||
if (!drawPassCtx.isWithinDrawPass())
|
||||
@ -1026,7 +1056,15 @@ void LatteCP_processCommandBuffer_continuousDrawPass(DrawPassContext& drawPassCt
|
||||
}
|
||||
case IT_SET_ALU_CONST: // uniform register
|
||||
{
|
||||
LatteCP_itSetRegistersGeneric<LATTE_REG_BASE_ALU_CONST>(cmdData, nWords);
|
||||
LatteCP_itSetRegistersGeneric2<LATTE_REG_BASE_ALU_CONST>(cmdData, nWords, [&drawPassCtx](uint32 registerStart, uint32 registerEnd, bool regValuesChanged) {
|
||||
if (!regValuesChanged)
|
||||
return;
|
||||
if ( registerStart >= (mmSQ_ALU_CONSTANT0_0 + 0x400) )
|
||||
drawPassCtx.MarkVSAluConstantsDirty();
|
||||
else
|
||||
drawPassCtx.MarkPSAluConstantsDirty();
|
||||
// todo - we could further optimize by tracking the min/max range of modified ALU constants
|
||||
});
|
||||
break;
|
||||
}
|
||||
case IT_SET_CTL_CONST:
|
||||
|
||||
@ -1038,7 +1038,7 @@ void _LatteDecompiler_GenerateDataForFastAccess(LatteDecompilerShader* shader)
|
||||
else
|
||||
{
|
||||
LatteFastAccessRemappedUniformEntry_buffer_t entryBuf;
|
||||
uint32 kcacheBankIdOffset = entry->kcacheBankId* (7 * 4);
|
||||
uint32 kcacheBankIdOffset = entry->kcacheBankId * (7 * 4);
|
||||
entryBuf.indexOffset = entry->index * 16;
|
||||
entryBuf.mappedIndexOffset = entry->mappedIndex * 16;
|
||||
// find or create buffer group
|
||||
@ -1049,7 +1049,7 @@ void _LatteDecompiler_GenerateDataForFastAccess(LatteDecompilerShader* shader)
|
||||
}
|
||||
else
|
||||
{
|
||||
shader->list_remappedUniformEntries_bufferGroups.emplace_back(kcacheBankIdOffset).entries.emplace_back(entryBuf);
|
||||
shader->list_remappedUniformEntries_bufferGroups.emplace_back(entry->kcacheBankId, kcacheBankIdOffset).entries.emplace_back(entryBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,9 +209,9 @@ struct LatteDecompilerShader
|
||||
// fast access
|
||||
struct _RemappedUniformBufferGroup
|
||||
{
|
||||
_RemappedUniformBufferGroup(uint32 _kcacheBankIdOffset) : kcacheBankIdOffset(_kcacheBankIdOffset) {};
|
||||
|
||||
uint32 kcacheBankIdOffset;
|
||||
_RemappedUniformBufferGroup(uint16 bufferId, uint16 _kcacheBankIdOffset) : bufferId(bufferId), kcacheBankIdOffset(_kcacheBankIdOffset) {};
|
||||
uint16 bufferId;
|
||||
uint16 kcacheBankIdOffset;
|
||||
std::vector<LatteFastAccessRemappedUniformEntry_buffer_t> entries;
|
||||
};
|
||||
std::vector<LatteFastAccessRemappedUniformEntry_register_t> list_remappedUniformEntries_register;
|
||||
|
||||
@ -369,7 +369,6 @@ private:
|
||||
VkDescriptorSetInfo* activeGeometryDS{ nullptr };
|
||||
bool descriptorSetsChanged{ false };
|
||||
bool hasRenderSelfDependency{ false }; // set if current drawcall samples textures which are also output as a rendertarget
|
||||
|
||||
// viewport and scissor box
|
||||
VkViewport currentViewport{};
|
||||
VkRect2D currentScissorRect{};
|
||||
@ -551,7 +550,8 @@ private:
|
||||
|
||||
// uniform
|
||||
uint32 uniformData_uploadUniformDataBufferGetOffset(std::span<uint8, std::dynamic_extent> data);
|
||||
void uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader);
|
||||
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);
|
||||
|
||||
// misc
|
||||
void CreatePipelineCache();
|
||||
|
||||
@ -311,7 +311,9 @@ void VulkanRenderer::indexData_uploadIndexMemory(IndexAllocation& allocation)
|
||||
memoryManager->GetIndexAllocator().FlushReservation((VKRSynchronizedHeapAllocator::AllocatorReservation*)allocation.rendererInternal);
|
||||
}
|
||||
|
||||
float s_vkUniformData[512 * 4];
|
||||
float s_vkUniformDataVS[512 * 4];
|
||||
float s_vkUniformDataPS[512 * 4];
|
||||
float s_vkUniformDataGS[512 * 4];
|
||||
|
||||
uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span<uint8> data)
|
||||
{
|
||||
@ -375,84 +377,135 @@ uint32 VulkanRenderer::uniformData_uploadUniformDataBufferGetOffset(std::span<ui
|
||||
return uniformOffset;
|
||||
}
|
||||
|
||||
void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader)
|
||||
void VulkanRenderer::uniformData_updateUniformVars(uint32 shaderStageIndex, LatteDecompilerShader* shader, float* __restrict uniformBuf)
|
||||
{
|
||||
auto GET_UNIFORM_DATA_PTR = [](size_t index) { return s_vkUniformData + (index / 4); };
|
||||
|
||||
sint32 shaderAluConst;
|
||||
|
||||
switch (shader->shaderType)
|
||||
auto GET_UNIFORM_DATA_PTR = [&uniformBuf](size_t index) { return uniformBuf + (index / 4); };
|
||||
if (shader->resourceMapping.uniformVarsBufferBindingPoint < 0)
|
||||
return;
|
||||
if (shader->uniform.list_ufTexRescale.empty() == false)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
shaderAluConst = 0x400;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
shaderAluConst = 0;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
shaderAluConst = 0; // geometry shader has no ALU const
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
if (shader->resourceMapping.uniformVarsBufferBindingPoint >= 0)
|
||||
{
|
||||
if (shader->uniform.list_ufTexRescale.empty() == false)
|
||||
for (auto& entry : shader->uniform.list_ufTexRescale)
|
||||
{
|
||||
for (auto& entry : shader->uniform.list_ufTexRescale)
|
||||
float* xyScale = LatteTexture_getEffectiveTextureScale(shader->shaderType, entry.texUnit);
|
||||
memcpy(entry.currentValue, xyScale, sizeof(float) * 2);
|
||||
memcpy(GET_UNIFORM_DATA_PTR(entry.uniformLocation), xyScale, sizeof(float) * 2);
|
||||
}
|
||||
}
|
||||
if (shader->uniform.loc_alphaTestRef >= 0)
|
||||
{
|
||||
*GET_UNIFORM_DATA_PTR(shader->uniform.loc_alphaTestRef) = LatteGPUState.contextNew.SX_ALPHA_REF.get_ALPHA_TEST_REF();
|
||||
}
|
||||
if (shader->uniform.loc_pointSize >= 0)
|
||||
{
|
||||
const auto& pointSizeReg = LatteGPUState.contextNew.PA_SU_POINT_SIZE;
|
||||
float pointWidth = (float)pointSizeReg.get_WIDTH() / 8.0f;
|
||||
if (pointWidth == 0.0f)
|
||||
pointWidth = 1.0f / 8.0f; // minimum size
|
||||
*GET_UNIFORM_DATA_PTR(shader->uniform.loc_pointSize) = pointWidth;
|
||||
}
|
||||
if (shader->uniform.loc_remapped >= 0)
|
||||
{
|
||||
LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped));
|
||||
}
|
||||
if (shader->uniform.loc_uniformRegister >= 0)
|
||||
{
|
||||
sint32 shaderAluConst;
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
case LatteConst::ShaderType::Vertex:
|
||||
shaderAluConst = 0x400;
|
||||
break;
|
||||
case LatteConst::ShaderType::Pixel:
|
||||
shaderAluConst = 0;
|
||||
break;
|
||||
case LatteConst::ShaderType::Geometry:
|
||||
shaderAluConst = 0; // geometry shader has no ALU const
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
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 (shader->uniform.loc_windowSpaceToClipSpaceTransform >= 0)
|
||||
{
|
||||
sint32 viewportWidth;
|
||||
sint32 viewportHeight;
|
||||
LatteRenderTarget_GetCurrentVirtualViewportSize(&viewportWidth, &viewportHeight); // always call after _updateViewport()
|
||||
float* v = GET_UNIFORM_DATA_PTR(shader->uniform.loc_windowSpaceToClipSpaceTransform);
|
||||
v[0] = 2.0f / (float)viewportWidth;
|
||||
v[1] = 2.0f / (float)viewportHeight;
|
||||
}
|
||||
if (shader->uniform.loc_fragCoordScale >= 0)
|
||||
{
|
||||
LatteMRT::GetCurrentFragCoordScale(GET_UNIFORM_DATA_PTR(shader->uniform.loc_fragCoordScale));
|
||||
}
|
||||
if (shader->uniform.loc_verticesPerInstance >= 0)
|
||||
{
|
||||
*(int*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_verticesPerInstance) = m_streamoutState.verticesPerInstance;
|
||||
for (sint32 b = 0; b < LATTE_NUM_STREAMOUT_BUFFER; b++)
|
||||
{
|
||||
if (shader->uniform.loc_streamoutBufferBase[b] >= 0)
|
||||
{
|
||||
float* xyScale = LatteTexture_getEffectiveTextureScale(shader->shaderType, entry.texUnit);
|
||||
memcpy(entry.currentValue, xyScale, sizeof(float) * 2);
|
||||
memcpy(GET_UNIFORM_DATA_PTR(entry.uniformLocation), xyScale, sizeof(float) * 2);
|
||||
*(uint32*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_streamoutBufferBase[b]) = m_streamoutState.buffer[b].ringBufferOffset;
|
||||
}
|
||||
}
|
||||
if (shader->uniform.loc_alphaTestRef >= 0)
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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); };
|
||||
if (shader->resourceMapping.uniformVarsBufferBindingPoint < 0)
|
||||
return;
|
||||
// list_ufTexRescale -> Skipped because textures do not change
|
||||
// loc_alphaTestRef -> Skipped because modifying SX_ALPHA_REF ends sequence
|
||||
// loc_pointSize -> Skipped because modifying PA_SU_POINT_SIZE ends sequence
|
||||
// loc_windowSpaceToClipSpaceTransform/loc_fragCoordScale/ -> Skipped because viewport doesn't change
|
||||
// loc_verticesPerInstance -> Skipped because sequences with streamout enabled are not allowed
|
||||
|
||||
bool hasChange = false; // todo - For loc_remapped and loc_uniformRegister we want to pass a mask of dirty uniform var / buffers. If nothing is dirty we can also skip it
|
||||
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);
|
||||
}
|
||||
if (shader->uniform.loc_uniformRegister >= 0)
|
||||
{
|
||||
cemu_assert_debug(shader->uniformMode == LATTE_DECOMPILER_UNIFORM_MODE_FULL_CFILE);
|
||||
bool isDirty = false;
|
||||
sint32 shaderAluConst;
|
||||
switch (shader->shaderType)
|
||||
{
|
||||
*GET_UNIFORM_DATA_PTR(shader->uniform.loc_alphaTestRef) = LatteGPUState.contextNew.SX_ALPHA_REF.get_ALPHA_TEST_REF();
|
||||
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();
|
||||
shaderAluConst = 0;
|
||||
UNREACHABLE; // geometry shader has no ALU const
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
if (shader->uniform.loc_pointSize >= 0)
|
||||
{
|
||||
const auto& pointSizeReg = LatteGPUState.contextNew.PA_SU_POINT_SIZE;
|
||||
float pointWidth = (float)pointSizeReg.get_WIDTH() / 8.0f;
|
||||
if (pointWidth == 0.0f)
|
||||
pointWidth = 1.0f / 8.0f; // minimum size
|
||||
*GET_UNIFORM_DATA_PTR(shader->uniform.loc_pointSize) = pointWidth;
|
||||
}
|
||||
if (shader->uniform.loc_remapped >= 0)
|
||||
{
|
||||
LatteBufferCache_LoadRemappedUniforms(shader, GET_UNIFORM_DATA_PTR(shader->uniform.loc_remapped));
|
||||
}
|
||||
if (shader->uniform.loc_uniformRegister >= 0)
|
||||
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);
|
||||
}
|
||||
if (shader->uniform.loc_windowSpaceToClipSpaceTransform >= 0)
|
||||
{
|
||||
sint32 viewportWidth;
|
||||
sint32 viewportHeight;
|
||||
LatteRenderTarget_GetCurrentVirtualViewportSize(&viewportWidth, &viewportHeight); // always call after _updateViewport()
|
||||
float* v = GET_UNIFORM_DATA_PTR(shader->uniform.loc_windowSpaceToClipSpaceTransform);
|
||||
v[0] = 2.0f / (float)viewportWidth;
|
||||
v[1] = 2.0f / (float)viewportHeight;
|
||||
}
|
||||
if (shader->uniform.loc_fragCoordScale >= 0)
|
||||
{
|
||||
LatteMRT::GetCurrentFragCoordScale(GET_UNIFORM_DATA_PTR(shader->uniform.loc_fragCoordScale));
|
||||
}
|
||||
if (shader->uniform.loc_verticesPerInstance >= 0)
|
||||
{
|
||||
*(int*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_verticesPerInstance) = m_streamoutState.verticesPerInstance;
|
||||
for (sint32 b = 0; b < LATTE_NUM_STREAMOUT_BUFFER; b++)
|
||||
{
|
||||
if (shader->uniform.loc_streamoutBufferBase[b] >= 0)
|
||||
{
|
||||
*(uint32*)GET_UNIFORM_DATA_PTR(shader->uniform.loc_streamoutBufferBase[b]) = m_streamoutState.buffer[b].ringBufferOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)s_vkUniformData, shader->uniform.uniformRangeSize});
|
||||
}
|
||||
if (hasChange)
|
||||
{
|
||||
dynamicOffsetInfo.uniformVarBufferOffset[shaderStageIndex] = uniformData_uploadUniformDataBufferGetOffset({(uint8*)uniformBuf, shader->uniform.uniformRangeSize});
|
||||
stageUniformModifiedMask |= (1 << shaderStageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1331,11 +1384,11 @@ void VulkanRenderer::draw_execute_first(uint32 baseVertex, uint32 baseInstance,
|
||||
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
|
||||
if (vertexShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader);
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader, s_vkUniformDataVS);
|
||||
if (pixelShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader);
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader, s_vkUniformDataPS);
|
||||
if (geometryShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader);
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, s_vkUniformDataGS);
|
||||
// store where the read pointer should go after command buffer execution
|
||||
m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex;
|
||||
|
||||
@ -1497,12 +1550,15 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
|
||||
LatteDecompilerShader* pixelShader = LatteSHRC_GetActivePixelShader();
|
||||
LatteDecompilerShader* geometryShader = LatteSHRC_GetActiveGeometryShader();
|
||||
|
||||
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_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader);
|
||||
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, vertexShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataVS);
|
||||
if (pixelShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader);
|
||||
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, pixelShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataPS);
|
||||
if (geometryShader)
|
||||
uniformData_updateUniformVars(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader);
|
||||
uniformData_updateUniformVarsIncremental(VulkanRendererConst::SHADER_STAGE_INDEX_GEOMETRY, geometryShader, drawcallContext, stageUniformModifiedMask, s_vkUniformDataGS);
|
||||
drawcallContext.aluConstVSDirty = false;
|
||||
drawcallContext.aluConstPSDirty = false;
|
||||
// store where the read pointer should go after command buffer execution
|
||||
m_cmdBufferUniformRingbufIndices[m_commandBufferIndex] = m_uniformVarBufferWriteIndex;
|
||||
|
||||
@ -1554,20 +1610,25 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
|
||||
else
|
||||
{
|
||||
// synchronize vertex and uniform cache and update buffer bindings
|
||||
LatteBufferCache_Sync(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount);
|
||||
LatteBufferCache_SyncIncremental(indexMin + baseVertex, indexMax + baseVertex, baseInstance, instanceCount, drawcallContext, stageUniformModifiedMask);
|
||||
}
|
||||
|
||||
PipelineInfo* pipeline_info;
|
||||
m_state.descriptorSetsChanged = false;
|
||||
PipelineInfo* pipeline_info = m_state.activePipelineInfo;
|
||||
// recalculate the part of the pipeline hash that can change during fast draws
|
||||
if (m_state.activePipelineInfo->minimalStateHash != draw_calculateMinimalGraphicsPipelineHash(vertexShader->compatibleFetchShader, LatteGPUState.contextNew))
|
||||
if (m_state.activePipelineInfo->minimalStateHash != draw_calculateMinimalGraphicsPipelineHash(vertexShader->compatibleFetchShader, LatteGPUState.contextNew)) [[unlikely]]
|
||||
{
|
||||
// pipeline changed
|
||||
pipeline_info = draw_getOrCreateGraphicsPipeline(count);
|
||||
m_state.activePipelineInfo = pipeline_info;
|
||||
// if the pipeline is changed then we also need to get the descriptor sets
|
||||
draw_prepareDescriptorSets(pipeline_info, m_state.activeVertexDS, m_state.activePixelDS, m_state.activeGeometryDS);
|
||||
m_state.descriptorSetsChanged = true;
|
||||
stageUniformModifiedMask = 0x7;
|
||||
}
|
||||
else
|
||||
{
|
||||
pipeline_info = m_state.activePipelineInfo;
|
||||
// sanity check that we are using the right pipeline
|
||||
#ifdef CEMU_DEBUG_ASSERT
|
||||
auto pipeline_info2 = draw_getOrCreateGraphicsPipeline(count);
|
||||
if (pipeline_info != pipeline_info2)
|
||||
@ -1581,7 +1642,7 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
|
||||
if (vkObjPipeline->GetPipeline() == VK_NULL_HANDLE)
|
||||
{
|
||||
// invalid/uninitialized pipeline
|
||||
m_state.activeVertexDS = nullptr;
|
||||
//m_state.activeVertexDS = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1591,15 +1652,10 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
|
||||
vertexDS = m_state.activeVertexDS;
|
||||
pixelDS = m_state.activePixelDS;
|
||||
geometryDS = m_state.activeGeometryDS;
|
||||
m_state.descriptorSetsChanged = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
draw_prepareDescriptorSets(pipeline_info, vertexDS, pixelDS, geometryDS);
|
||||
m_state.activeVertexDS = vertexDS;
|
||||
m_state.activePixelDS = pixelDS;
|
||||
m_state.activeGeometryDS = geometryDS;
|
||||
m_state.descriptorSetsChanged = true;
|
||||
__debugbreak(); // should never happen unless draw_prepareDescriptorSets can fail?
|
||||
}
|
||||
|
||||
draw_setRenderPass();
|
||||
@ -1625,32 +1681,58 @@ void VulkanRenderer::draw_execute_continued(uint32 baseVertex, uint32 baseInstan
|
||||
|
||||
// update descriptor sets
|
||||
uint32_t dynamicOffsets[17 * 2];
|
||||
if (vertexDS && pixelDS)
|
||||
VkDescriptorSet dsArray[2];
|
||||
sint32 dsArraySize = 0;
|
||||
sint32 dsArrayBase = 0;
|
||||
sint32 numDynOffsets = 0;
|
||||
if (vertexDS && (stageUniformModifiedMask&(1<<VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX)))
|
||||
{
|
||||
// update vertex and pixel descriptor set in a single call to vkCmdBindDescriptorSets
|
||||
sint32 numDynOffsetsVS;
|
||||
sint32 numDynOffsetsPS;
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, dynamicOffsets, numDynOffsetsVS, pipeline_info);
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, dynamicOffsets + numDynOffsetsVS, numDynOffsetsPS, pipeline_info);
|
||||
|
||||
VkDescriptorSet dsArray[2];
|
||||
sint32 tmpNumDynOffsets = 0;
|
||||
dsArray[0] = vertexDS->m_vkObjDescriptorSet->descriptorSet;
|
||||
dsArray[1] = pixelDS->m_vkObjDescriptorSet->descriptorSet;
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, dynamicOffsets, tmpNumDynOffsets, pipeline_info);
|
||||
dsArraySize = 1;
|
||||
numDynOffsets += tmpNumDynOffsets;
|
||||
}
|
||||
if (pixelDS && (stageUniformModifiedMask&(1<<VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT)))
|
||||
{
|
||||
sint32 tmpNumDynOffsets = 0;
|
||||
dsArrayBase = 1 - dsArraySize;
|
||||
dsArray[dsArraySize] = pixelDS->m_vkObjDescriptorSet->descriptorSet;
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, dynamicOffsets + numDynOffsets, tmpNumDynOffsets, pipeline_info);
|
||||
dsArraySize++;
|
||||
numDynOffsets += tmpNumDynOffsets;
|
||||
}
|
||||
if (dsArraySize > 0)
|
||||
{
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, dsArrayBase, dsArraySize, dsArray, numDynOffsets, dynamicOffsets);
|
||||
}
|
||||
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 0, 2, dsArray, numDynOffsetsVS + numDynOffsetsPS, dynamicOffsets);
|
||||
}
|
||||
else if (vertexDS)
|
||||
{
|
||||
sint32 numDynOffsets;
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, dynamicOffsets, numDynOffsets, pipeline_info);
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 0, 1, &vertexDS->m_vkObjDescriptorSet->descriptorSet, numDynOffsets, dynamicOffsets);
|
||||
}
|
||||
else if (pixelDS)
|
||||
{
|
||||
sint32 numDynOffsets;
|
||||
draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, dynamicOffsets, numDynOffsets, pipeline_info);
|
||||
vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 1, 1, &pixelDS->m_vkObjDescriptorSet->descriptorSet, numDynOffsets, dynamicOffsets);
|
||||
}
|
||||
// if (vertexDS && pixelDS)
|
||||
// {
|
||||
// // update vertex and pixel descriptor set in a single call to vkCmdBindDescriptorSets
|
||||
// sint32 numDynOffsetsVS;
|
||||
// sint32 numDynOffsetsPS;
|
||||
// draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, dynamicOffsets, numDynOffsetsVS, pipeline_info);
|
||||
// draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, dynamicOffsets + numDynOffsetsVS, numDynOffsetsPS, pipeline_info);
|
||||
//
|
||||
// VkDescriptorSet dsArray[2];
|
||||
// dsArray[0] = vertexDS->m_vkObjDescriptorSet->descriptorSet;
|
||||
// dsArray[1] = pixelDS->m_vkObjDescriptorSet->descriptorSet;
|
||||
//
|
||||
// vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 0, 2, dsArray, numDynOffsetsVS + numDynOffsetsPS, dynamicOffsets);
|
||||
// }
|
||||
// else if (vertexDS)
|
||||
// {
|
||||
// sint32 numDynOffsets;
|
||||
// draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_VERTEX, dynamicOffsets, numDynOffsets, pipeline_info);
|
||||
// vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 0, 1, &vertexDS->m_vkObjDescriptorSet->descriptorSet, numDynOffsets, dynamicOffsets);
|
||||
// }
|
||||
// else if (pixelDS)
|
||||
// {
|
||||
// sint32 numDynOffsets;
|
||||
// draw_prepareDynamicOffsetsForDescriptorSet(VulkanRendererConst::SHADER_STAGE_INDEX_FRAGMENT, dynamicOffsets, numDynOffsets, pipeline_info);
|
||||
// vkCmdBindDescriptorSets(m_state.currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, vkObjPipeline->m_pipelineLayout, 1, 1, &pixelDS->m_vkObjDescriptorSet->descriptorSet, numDynOffsets, dynamicOffsets);
|
||||
// }
|
||||
if (geometryDS)
|
||||
{
|
||||
sint32 numDynOffsets;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user