mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Perf] (MG_Backend/DirectVulkan): skip per-draw render-pass hash when framebuffer state unchanged; SetupDraw 60%->54%, fps 96->109
This commit is contained in:
@@ -210,6 +210,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
RenderPassEntry::s_textureResourcesScratch.clear();
|
RenderPassEntry::s_textureResourcesScratch.clear();
|
||||||
s_activeRenderPass = {};
|
s_activeRenderPass = {};
|
||||||
s_hasActiveRenderPass = false;
|
s_hasActiveRenderPass = false;
|
||||||
|
m_rpFastValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VkRenderPassManager::CollectRenderbufferGarbage() {
|
void VkRenderPassManager::CollectRenderbufferGarbage() {
|
||||||
@@ -305,6 +306,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
|
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
|
||||||
"vmaCreateImage(renderbuffer)");
|
"vmaCreateImage(renderbuffer)");
|
||||||
|
++m_renderbufferImageEpoch; // a new attachment image invalidates cached render passes
|
||||||
|
|
||||||
VkImageViewCreateInfo viewInfo{};
|
VkImageViewCreateInfo viewInfo{};
|
||||||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
@@ -585,6 +587,25 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
// retrieve from cache first
|
// retrieve from cache first
|
||||||
auto* activeRenderPass = GetActiveRenderPass();
|
auto* activeRenderPass = GetActiveRenderPass();
|
||||||
|
|
||||||
|
// Dirty-flag state tracking: when the framebuffer state is provably unchanged since the
|
||||||
|
// render pass was last resolved, the active render pass is still valid -> skip the
|
||||||
|
// expensive per-draw ComputeHash (XXH64 over every attachment + a SyncTexture per
|
||||||
|
// attachment). Correctness signals: same FBO object + GetObjectVersion (attachment /
|
||||||
|
// draw-buffer / read-buffer changes bump it), same swapchain image, no attachment VkImage
|
||||||
|
// recreated since (texture + renderbuffer image epochs), and no pending clear (which alters
|
||||||
|
// load ops). Any of these differing forces the full recompute below. Portable to VK 1.1.
|
||||||
|
if (activeRenderPass != nullptr && m_rpFastValid && m_rpFastFbo == &fbo &&
|
||||||
|
m_rpFastFboVersion == fbo.GetObjectVersion() && m_rpFastSwapchainIndex == swapchainImageIndex &&
|
||||||
|
m_rpFastTexEpoch == m_textureManager.GetTextureImageEpoch() &&
|
||||||
|
m_rpFastRbEpoch == m_renderbufferImageEpoch &&
|
||||||
|
m_rpFastRenderPassHash == activeRenderPass->hash && !hasPendingClearOnFramebuffer()) {
|
||||||
|
auto activeIt = m_renderPasses.find(activeRenderPass->hash);
|
||||||
|
if (activeIt != m_renderPasses.end()) {
|
||||||
|
return activeIt->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
|
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
|
||||||
if (activeRenderPass != nullptr &&
|
if (activeRenderPass != nullptr &&
|
||||||
activeRenderPass->CompatibleWith(compatibilityHash) &&
|
activeRenderPass->CompatibleWith(compatibilityHash) &&
|
||||||
@@ -593,6 +614,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MOBILEGL_ASSERT(activeIt != m_renderPasses.end(),
|
MOBILEGL_ASSERT(activeIt != m_renderPasses.end(),
|
||||||
"GetOrCreateRenderPass: active render pass hash=0x%llx is missing from cache",
|
"GetOrCreateRenderPass: active render pass hash=0x%llx is missing from cache",
|
||||||
static_cast<unsigned long long>(activeRenderPass->hash));
|
static_cast<unsigned long long>(activeRenderPass->hash));
|
||||||
|
// Populate the fast-path memo so subsequent unchanged draws skip ComputeHash. Read the
|
||||||
|
// epochs AFTER ComputeHash: its attachment SyncTexture can create an image (bump the epoch).
|
||||||
|
m_rpFastValid = true;
|
||||||
|
m_rpFastFbo = &fbo;
|
||||||
|
m_rpFastFboVersion = fbo.GetObjectVersion();
|
||||||
|
m_rpFastSwapchainIndex = swapchainImageIndex;
|
||||||
|
m_rpFastTexEpoch = m_textureManager.GetTextureImageEpoch();
|
||||||
|
m_rpFastRbEpoch = m_renderbufferImageEpoch;
|
||||||
|
m_rpFastRenderPassHash = activeRenderPass->hash;
|
||||||
return activeIt->second;
|
return activeIt->second;
|
||||||
}
|
}
|
||||||
auto hash = ComputeHash(fbo, swapchainImageIndex, true);
|
auto hash = ComputeHash(fbo, swapchainImageIndex, true);
|
||||||
@@ -1069,6 +1099,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
|
Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
|
||||||
auto* activeRenderPass = GetActiveRenderPass();
|
auto* activeRenderPass = GetActiveRenderPass();
|
||||||
vkCmdEndRenderPass(commandBuffer);
|
vkCmdEndRenderPass(commandBuffer);
|
||||||
|
// The fast-path memo reuses the ACTIVE render pass; once the pass ends it must not carry
|
||||||
|
// over (the next span may be a different FBO resolved before its render pass is begun).
|
||||||
|
if (s_renderPassManager != nullptr) {
|
||||||
|
s_renderPassManager->m_rpFastValid = false;
|
||||||
|
}
|
||||||
if (activeRenderPass != nullptr && !activeRenderPass->trackedAttachmentLayouts.empty()) {
|
if (activeRenderPass != nullptr && !activeRenderPass->trackedAttachmentLayouts.empty()) {
|
||||||
for (const auto& trackedAttachment : activeRenderPass->trackedAttachmentLayouts) {
|
for (const auto& trackedAttachment : activeRenderPass->trackedAttachmentLayouts) {
|
||||||
switch (trackedAttachment.target) {
|
switch (trackedAttachment.target) {
|
||||||
|
|||||||
@@ -179,6 +179,24 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
SwapchainObject& m_swapchainObject;
|
SwapchainObject& m_swapchainObject;
|
||||||
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
|
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
|
||||||
|
|
||||||
|
// Bumped whenever a renderbuffer VkImage is (re)created; together with the texture
|
||||||
|
// manager's image epoch this invalidates the render-pass fast path on any attachment
|
||||||
|
// image recreation.
|
||||||
|
Uint64 m_renderbufferImageEpoch = 1;
|
||||||
|
|
||||||
|
// Per-draw fast-path memo for GetOrCreateRenderPass (dirty-flag state tracking): when the
|
||||||
|
// framebuffer state is provably unchanged since the last resolution, the active render pass
|
||||||
|
// is reused WITHOUT recomputing the expensive per-draw hash. Invalidated by FBO switch /
|
||||||
|
// version change, swapchain rotation, any attachment image recreation (the two epochs),
|
||||||
|
// or a pending clear. Portable to Vulkan 1.1 (no dynamic_rendering / imageless FB needed).
|
||||||
|
Bool m_rpFastValid = false;
|
||||||
|
const MG_State::GLState::FramebufferObject* m_rpFastFbo = nullptr;
|
||||||
|
Uint16 m_rpFastFboVersion = 0;
|
||||||
|
Uint32 m_rpFastSwapchainIndex = 0;
|
||||||
|
Uint64 m_rpFastTexEpoch = 0;
|
||||||
|
Uint64 m_rpFastRbEpoch = 0;
|
||||||
|
Uint64 m_rpFastRenderPassHash = 0;
|
||||||
|
|
||||||
struct RenderbufferResource {
|
struct RenderbufferResource {
|
||||||
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
WeakPtr<MG_State::GLState::RenderbufferObject> renderbuffer;
|
||||||
VkImage image = VK_NULL_HANDLE;
|
VkImage image = VK_NULL_HANDLE;
|
||||||
|
|||||||
@@ -1252,6 +1252,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
allocationInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||||
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
|
VK_VERIFY(vmaCreateImage(m_allocator, &imageInfo, &allocationInfo, &resource.image, &resource.allocation, nullptr),
|
||||||
"vmaCreateImage(texture)");
|
"vmaCreateImage(texture)");
|
||||||
|
++m_textureImageEpoch; // a new attachment image invalidates cached render passes
|
||||||
|
|
||||||
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
resource.layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||||
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
|
resource.extent = {static_cast<Uint32>(texelSize.x()), static_cast<Uint32>(texelSize.y())};
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ class ITextureObject;
|
|||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
class VkTextureManager {
|
class VkTextureManager {
|
||||||
public:
|
public:
|
||||||
|
// Monotonic epoch bumped whenever a texture VkImage is (re)created. The render-pass
|
||||||
|
// manager keys its per-draw fast path on this so an attachment's image recreation
|
||||||
|
// invalidates the cached render pass (dirty-flag tracking; portable to Vulkan 1.1).
|
||||||
|
Uint64 GetTextureImageEpoch() const { return m_textureImageEpoch; }
|
||||||
|
|
||||||
struct TextureIdentity {
|
struct TextureIdentity {
|
||||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||||
Uint64 lifetimeId = 0;
|
Uint64 lifetimeId = 0;
|
||||||
@@ -225,6 +230,8 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Bumped in SyncTextureResource right after vmaCreateImage(texture). See GetTextureImageEpoch().
|
||||||
|
Uint64 m_textureImageEpoch = 1;
|
||||||
|
|
||||||
Bool SyncTexture(MG_State::GLState::ITextureObject &texture,
|
Bool SyncTexture(MG_State::GLState::ITextureObject &texture,
|
||||||
TextureResource &outResource);
|
TextureResource &outResource);
|
||||||
|
|||||||
Reference in New Issue
Block a user