[Fix] (MG_Backend/DirectVulkan): use hash to save active render pass

This commit is contained in:
2026-03-06 16:17:56 +08:00
parent 8097c0d7b1
commit bca7328421
4 changed files with 65 additions and 24 deletions
@@ -99,7 +99,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto* activeRenderPass = GetActiveRenderPass();
auto compatibilityHash = ComputeHash(fbo, swapchainImageIndex, false);
if (activeRenderPass != nullptr && activeRenderPass->CompatibleWith(compatibilityHash)) {
return *activeRenderPass;
auto activeIt = m_renderPasses.find(activeRenderPass->hash);
MOBILEGL_ASSERT(activeIt != m_renderPasses.end(),
"GetOrCreateRenderPass: active render pass hash=0x%llx is missing from cache",
static_cast<unsigned long long>(activeRenderPass->hash));
return activeIt->second;
}
auto hash = ComputeHash(fbo, swapchainImageIndex, true);
auto it = m_renderPasses.find(hash);
@@ -157,9 +161,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
desc.initialLayout = hasClear ?
VK_IMAGE_LAYOUT_UNDEFINED :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
desc.finalLayout = isDefaultFbo ?
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
@@ -178,6 +179,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto& swapchainViews = m_swapchainObject.GetImageViews();
MOBILEGL_ASSERT(swapchainImageIndex < swapchainViews.size(),
"GetOrCreateRenderPass: swapchain image index out of range");
desc.initialLayout = hasClear ?
VK_IMAGE_LAYOUT_UNDEFINED :
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachmentViews[i] = swapchainViews[swapchainImageIndex];
} else {
textureResources[i] = m_textureManager.SyncTextureAndGetDescriptor(*texture);
@@ -186,6 +190,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(hasClear || textureResources[i]->layout != VK_IMAGE_LAYOUT_UNDEFINED,
"GetOrCreateRenderPass: color attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD (attachment=%d)",
texture->GetExternalIndex(), i);
desc.initialLayout = hasClear ?
VK_IMAGE_LAYOUT_UNDEFINED :
textureResources[i]->layout;
trackedAttachmentLayouts.emplace_back(TrackedAttachmentLayoutInfo {
.texture = texture,
.finalLayout = desc.finalLayout,
@@ -219,6 +226,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool hasClear = m_clearManager.GetPendingClear(&texture, clearPayload);
Bool clearDepth = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Depth;
Bool clearStencil = hasClear && clearPayload.attachmentType == FramebufferAttachmentType::Stencil;
VkImageLayout trackedDepthLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
if (!isDefaultFbo) {
depthTextureResource = m_textureManager.SyncTextureAndGetDescriptor(texture);
MOBILEGL_ASSERT(depthTextureResource,
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment");
trackedDepthLayout = depthTextureResource->layout;
}
depthAttachmentDescription.flags = 0;
depthAttachmentDescription.format =
MG_Util::ConvertTextureInternalFormatToVkEnum(texture.GetFormat());
@@ -231,15 +245,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_ATTACHMENT_LOAD_OP_CLEAR :
VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachmentDescription.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
// UNDEFINED is only valid when both depth and stencil are discarded/cleared.
depthAttachmentDescription.initialLayout = (clearDepth && clearStencil) ?
VK_IMAGE_LAYOUT_UNDEFINED :
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachmentDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
MOBILEGL_ASSERT(!(depthAttachmentDescription.stencilLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD &&
depthAttachmentDescription.initialLayout == VK_IMAGE_LAYOUT_UNDEFINED),
"GetOrCreateRenderPass: invalid depth-stencil state (stencil LOAD + initialLayout UNDEFINED), textureId=%d",
texture.GetExternalIndex());
if (!isDefaultFbo && trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
if (!clearDepth) {
depthAttachmentDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
if (!clearStencil) {
depthAttachmentDescription.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
}
}
depthAttachmentDescription.initialLayout =
(clearDepth && clearStencil) || (!isDefaultFbo && trackedDepthLayout == VK_IMAGE_LAYOUT_UNDEFINED) ?
VK_IMAGE_LAYOUT_UNDEFINED :
trackedDepthLayout;
if (hasClear) {
pendingClearAttachments.emplace_back(PendingClearAttachmentInfo {
.attachmentIndex = depthAttachmentIndex,
@@ -249,9 +267,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (isDefaultFbo) {
attachmentViews.emplace_back(m_swapchainObject.GetDepthStencilImageView(swapchainImageIndex));
} else {
depthTextureResource = m_textureManager.SyncTextureAndGetDescriptor(texture);
MOBILEGL_ASSERT(depthTextureResource,
"GetOrCreateRenderPass: SyncTextureAndGetDescriptor failed at depth attachment");
MOBILEGL_ASSERT(clearDepth || clearStencil || depthTextureResource->layout != VK_IMAGE_LAYOUT_UNDEFINED,
"GetOrCreateRenderPass: depth attachment textureId=%d has undefined tracked layout with LOAD_OP_LOAD",
texture.GetExternalIndex());
@@ -314,6 +329,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer));
IntVec2 extent = {width, height};
RenderPassEntry renderPassEntry {
hash,
renderPass,
framebuffer,
compatibilityHash,
@@ -372,13 +388,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
for (const auto& pending: renderPassEntry.pendingClearAttachments) {
s_clearManager->PopPendingClear(pending.texture);
}
s_activeRenderPass = &renderPassEntry;
s_activeRenderPass.hash = renderPassEntry.hash;
s_activeRenderPass.compatibilityHash = renderPassEntry.compatibilityHash;
s_activeRenderPass.trackedAttachmentLayouts = renderPassEntry.trackedAttachmentLayouts;
s_activeRenderPass.extent = renderPassEntry.extent;
s_hasActiveRenderPass = true;
return true;
}
Bool VkRenderPassManager::EndRenderPass(VkCommandBuffer commandBuffer) {
auto* activeRenderPass = s_activeRenderPass;
auto* activeRenderPass = GetActiveRenderPass();
vkCmdEndRenderPass(commandBuffer);
if (activeRenderPass != nullptr && !activeRenderPass->trackedAttachmentLayouts.empty()) {
MOBILEGL_ASSERT(s_textureManager != nullptr, "EndRenderPass: texture manager is null");
@@ -386,11 +406,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
s_textureManager->UpdateTrackedImageLayout(trackedAttachment.texture, trackedAttachment.finalLayout);
}
}
s_activeRenderPass = nullptr;
s_activeRenderPass = {};
s_hasActiveRenderPass = false;
return true;
}
RenderPassEntry* VkRenderPassManager::GetActiveRenderPass() {
return s_activeRenderPass;
ActiveRenderPassInfo* VkRenderPassManager::GetActiveRenderPass() {
return s_hasActiveRenderPass ? &s_activeRenderPass : nullptr;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -32,6 +32,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
struct RenderPassEntry {
static inline VkDevice s_device;
static inline Vector<VkTextureManager::TextureResource*> s_textureResourcesScratch;
Uint64 hash = 0;
VkRenderPass renderPass = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
Uint64 compatibilityHash = 0;
@@ -44,6 +45,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
RenderPassEntry() = default;
RenderPassEntry(const RenderPassEntry&) = delete;
RenderPassEntry(RenderPassEntry&& that) noexcept {
std::swap(hash, that.hash);
std::swap(renderPass, that.renderPass);
std::swap(framebuffer, that.framebuffer);
std::swap(compatibilityHash, that.compatibilityHash);
@@ -54,6 +56,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
std::swap(subpass, that.subpass);
}
RenderPassEntry(
Uint64 hash,
VkRenderPass renderpass,
VkFramebuffer framebuffer,
Uint64 compatibilityHash,
@@ -61,6 +64,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Vector<TrackedAttachmentLayoutInfo>& trackedAttachmentLayouts,
Uint32 attachmentCount,
IntVec2 extent, int subpass):
hash(hash),
renderPass(renderpass),
framebuffer(framebuffer),
compatibilityHash(compatibilityHash),
@@ -89,6 +93,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
};
struct ActiveRenderPassInfo {
Uint64 hash = 0;
Uint64 compatibilityHash = 0;
Vector<TrackedAttachmentLayoutInfo> trackedAttachmentLayouts;
IntVec2 extent = {0, 0};
Bool CompatibleWith(const RenderPassEntry& that) const {
return compatibilityHash == that.compatibilityHash;
}
Bool CompatibleWith(Uint64 thatCompatibilityHash) const {
return compatibilityHash == thatCompatibilityHash;
}
};
class VkRenderPassManager {
public:
using HashType = Uint64;
@@ -107,7 +126,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
RenderPassEntry& GetOrCreateRenderPass(const MG_State::GLState::FramebufferObject& fbo, Uint32 swapchainImageIndex);
static Bool BeginRenderPass(VkCommandBuffer commandBuffer, RenderPassEntry& renderPassEntry);
static Bool EndRenderPass(VkCommandBuffer commandBuffer);
static RenderPassEntry* GetActiveRenderPass();
static ActiveRenderPassInfo* GetActiveRenderPass();
private:
VkDevice m_device = VK_NULL_HANDLE;
const VulkanRendererConfig& m_config;
@@ -116,7 +135,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const SwapchainObject& m_swapchainObject;
UnorderedMap<Uint64, RenderPassEntry> m_renderPasses;
static inline XXH64_state_t* m_hashState = XXH64_createState();
static inline RenderPassEntry* s_activeRenderPass = nullptr;
static inline ActiveRenderPassInfo s_activeRenderPass{};
static inline Bool s_hasActiveRenderPass = false;
static inline VkClearManager* s_clearManager = nullptr;
static inline VkTextureManager* s_textureManager = nullptr;
};
@@ -242,7 +242,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
resource.~TextureResource();
resource.Reset();
auto aspect = GetAspectMaskForFormat(format);
@@ -425,7 +425,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Begin render pass, and handle clear
if (activeRenderPass && (activeRenderPass == &renderPassEntry || activeRenderPass->CompatibleWith(renderPassEntry))) {
if (activeRenderPass && activeRenderPass->CompatibleWith(renderPassEntry)) {
ClearAttachmentsOnActiveRenderPass(frame.commandBuffer, renderPassEntry);
} else {
// No active render pass or active one not compatible.