mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Perf] (MG_Backend/DirectVulkan): skip per-draw pipeline resolution when pipeline state unchanged; SetupDraw 54%->51%, fps 109->127
This commit is contained in:
@@ -2879,6 +2879,24 @@ void main() {
|
|||||||
return VK_NULL_HANDLE;
|
return VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast path: skip the full pipeline resolution when the pipeline state is unchanged from the
|
||||||
|
// previous draw (the common intra-batch case). The key provably covers every
|
||||||
|
// PipelineCreatePayload field: draw mode (topology + polygon-fill depth-bias gate), program
|
||||||
|
// content hash (folds program identity + link version + transform flags + shader stages),
|
||||||
|
// vertex-input hash (VAO layout), render-pass hash (render targets + the draw-buffer/format
|
||||||
|
// driven blend & write-mask gating), and the render-state version (all fixed-function state).
|
||||||
|
// Reset per-frame and on pipeline destruction so m_lastPipelineResult can never dangle.
|
||||||
|
const Uint64 vertexInputHash = m_vertexInputStateFactory->GetOrComputeHash(vao);
|
||||||
|
const Uint64 renderPassHash = renderPassEntry.hash;
|
||||||
|
const Uint renderStateVersion = MG_State::pGLContext->GetRenderStateParametersVersion();
|
||||||
|
if (m_lastPipelineValid && m_lastPipelineResult != VK_NULL_HANDLE && m_lastPipelineMode == mode &&
|
||||||
|
m_lastPipelineProgramHash == programObj.hash && m_lastPipelineVertexInputHash == vertexInputHash &&
|
||||||
|
m_lastPipelineRenderPassHash == renderPassHash &&
|
||||||
|
m_lastPipelineRenderStateVersion == renderStateVersion &&
|
||||||
|
m_lastPipelineTransformFlags == transformFlags) {
|
||||||
|
return m_lastPipelineResult;
|
||||||
|
}
|
||||||
|
|
||||||
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG
|
||||||
const auto& limits = m_physicalDevice.properties.limits;
|
const auto& limits = m_physicalDevice.properties.limits;
|
||||||
if (programObj.fragmentInputComponentCount != 0) {
|
if (programObj.fragmentInputComponentCount != 0) {
|
||||||
@@ -2917,7 +2935,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto vertexInputHash = m_vertexInputStateFactory->GetOrComputeHash(vao);
|
// vertexInputHash was computed above for the fast-path key; reuse it here.
|
||||||
auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao, vertexInputHash);
|
auto& vis = m_vertexInputStateFactory->GetOrCreateVertexInputState(vao, vertexInputHash);
|
||||||
const Uint32 vertexInputAttribMask = BuildVertexInputAttributeMask(vis.attributes);
|
const Uint32 vertexInputAttribMask = BuildVertexInputAttributeMask(vis.attributes);
|
||||||
const Uint32 activeAttribMask = programObj.activeVertexInputLocationMask;
|
const Uint32 activeAttribMask = programObj.activeVertexInputLocationMask;
|
||||||
@@ -3279,7 +3297,18 @@ void main() {
|
|||||||
MG_Util::ConvertBlendEquationToVkEnum(alphaEquation),
|
MG_Util::ConvertBlendEquationToVkEnum(alphaEquation),
|
||||||
attachmentColorWriteMask);
|
attachmentColorWriteMask);
|
||||||
}
|
}
|
||||||
return m_pipelineFactory->GetOrCreatePipeline(payload);
|
VkPipeline pipeline = m_pipelineFactory->GetOrCreatePipeline(payload);
|
||||||
|
if (pipeline != VK_NULL_HANDLE) {
|
||||||
|
m_lastPipelineValid = true;
|
||||||
|
m_lastPipelineMode = mode;
|
||||||
|
m_lastPipelineProgramHash = programObj.hash;
|
||||||
|
m_lastPipelineVertexInputHash = vertexInputHash;
|
||||||
|
m_lastPipelineRenderPassHash = renderPassHash;
|
||||||
|
m_lastPipelineRenderStateVersion = renderStateVersion;
|
||||||
|
m_lastPipelineTransformFlags = transformFlags;
|
||||||
|
m_lastPipelineResult = pipeline;
|
||||||
|
}
|
||||||
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
|
Bool VulkanRenderer::SetupDraw(FrameContext::FrameData& frame, GLenum mode, Flags<DrawSetupAspect> aspects,
|
||||||
@@ -4715,6 +4744,7 @@ void main() {
|
|||||||
if (frame.isCommandRecording) {
|
if (frame.isCommandRecording) {
|
||||||
m_frameContext.EndCommandRecording();
|
m_frameContext.EndCommandRecording();
|
||||||
frame.hasCommandBufferRecorded = true;
|
frame.hasCommandBufferRecorded = true;
|
||||||
|
m_lastPipelineValid = false; // command-buffer boundary: drop the pipeline memo
|
||||||
}
|
}
|
||||||
if (!frame.hasCommandBufferRecorded) {
|
if (!frame.hasCommandBufferRecorded) {
|
||||||
return true;
|
return true;
|
||||||
@@ -5978,6 +6008,7 @@ void main() {
|
|||||||
if (frame.isCommandRecording) {
|
if (frame.isCommandRecording) {
|
||||||
m_frameContext.EndCommandRecording();
|
m_frameContext.EndCommandRecording();
|
||||||
frame.hasCommandBufferRecorded = true;
|
frame.hasCommandBufferRecorded = true;
|
||||||
|
m_lastPipelineValid = false; // command-buffer boundary: drop the pipeline memo
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
const auto acquiredImageLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
|
||||||
@@ -6854,6 +6885,7 @@ void main() {
|
|||||||
if (m_pipelineFactory) {
|
if (m_pipelineFactory) {
|
||||||
m_pipelineFactory->DestroyAll();
|
m_pipelineFactory->DestroyAll();
|
||||||
}
|
}
|
||||||
|
m_lastPipelineValid = false; // pipelines freed -> the memoized handle would dangle
|
||||||
DestroyComputePipelines();
|
DestroyComputePipelines();
|
||||||
if (m_frameContext.GetFrameCount() > 0) {
|
if (m_frameContext.GetFrameCount() > 0) {
|
||||||
m_frameContext.GetCurrent().isCommandRecording = false;
|
m_frameContext.GetCurrent().isCommandRecording = false;
|
||||||
|
|||||||
@@ -378,6 +378,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
FrameContext m_frameContext;
|
FrameContext m_frameContext;
|
||||||
|
|
||||||
UniquePtr<PipelineFactory> m_pipelineFactory;
|
UniquePtr<PipelineFactory> m_pipelineFactory;
|
||||||
|
// Single-slot "last pipeline" memo: skip the per-draw GetOrCreatePipeline work (state
|
||||||
|
// gather + synthetic vertex-input rebuild + payload hash + lookup) when the full pipeline
|
||||||
|
// state is unchanged from the previous draw. The key provably covers every pipeline field.
|
||||||
|
// Reset per-frame and on pipeline destruction so the cached handle can never dangle.
|
||||||
|
Bool m_lastPipelineValid = false;
|
||||||
|
GLenum m_lastPipelineMode = 0;
|
||||||
|
Uint64 m_lastPipelineProgramHash = 0;
|
||||||
|
Uint64 m_lastPipelineVertexInputHash = 0;
|
||||||
|
Uint64 m_lastPipelineRenderPassHash = 0;
|
||||||
|
Uint m_lastPipelineRenderStateVersion = 0;
|
||||||
|
ProgramFactory::CompileOptionFlags m_lastPipelineTransformFlags = {};
|
||||||
|
VkPipeline m_lastPipelineResult = VK_NULL_HANDLE;
|
||||||
UnorderedMap<ProgramFactory::HashType, VkPipeline> m_computePipelines;
|
UnorderedMap<ProgramFactory::HashType, VkPipeline> m_computePipelines;
|
||||||
UniquePtr<ProgramFactory> m_programFactory;
|
UniquePtr<ProgramFactory> m_programFactory;
|
||||||
UniquePtr<UniformManager> m_uniformManager;
|
UniquePtr<UniformManager> m_uniformManager;
|
||||||
|
|||||||
Reference in New Issue
Block a user