[Chore] (MG_Backend/DirectVulkan): get rid of more junk

This commit is contained in:
2026-02-25 12:46:47 +08:00
parent b4be1292b8
commit 6e7907f183
3 changed files with 7 additions and 633 deletions
@@ -310,8 +310,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto& clearColor = MG_State::pGLContext->GetClearColor(); const auto& clearColor = MG_State::pGLContext->GetClearColor();
const auto clearDepth = MG_State::pGLContext->GetClearDepth(); const auto clearDepth = MG_State::pGLContext->GetClearDepth();
const auto clearStencil = static_cast<Uint32>(MG_State::pGLContext->GetClearStencil()); const auto clearStencil = static_cast<Uint32>(MG_State::pGLContext->GetClearStencil());
pVulkanRenderer->RequestClear(mask, clearColor, clearDepth, clearStencil, drawFboExternalIndex, pVulkanRenderer->QueueClearRequest(mask, clearColor, clearDepth, clearStencil,
isDefaultFboTarget); drawFboExternalIndex,
isDefaultFboTarget);
} }
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) { void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
@@ -422,35 +423,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter) { GLint dstY1, GLbitfield mask, GLenum filter) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::BlitFramebuffer called with null GL context");
const auto& readFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
const auto& drawFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
const auto readFbo = readFboSlot.GetBoundObject();
const auto drawFbo = drawFboSlot.GetBoundObject();
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool readIsDefault = (readFbo == defaultFbo) || (readFbo == nullptr && defaultFbo != nullptr);
const Bool drawIsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
Uint readFboExternalIndex = 0;
if (readFbo) {
readFboExternalIndex = readFbo->GetExternalIndex();
} else if (defaultFbo) {
readFboExternalIndex = defaultFbo->GetExternalIndex();
}
Uint drawFboExternalIndex = 0;
if (drawFbo) {
drawFboExternalIndex = drawFbo->GetExternalIndex();
} else if (defaultFbo) {
drawFboExternalIndex = defaultFbo->GetExternalIndex();
}
pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter,
readFboExternalIndex, drawFboExternalIndex, readIsDefault, drawIsDefault);
} }
void Present() { void Present() {
@@ -327,8 +327,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_I("VulkanRenderer shut down completed"); MGLOG_I("VulkanRenderer shut down completed");
} }
void VulkanRenderer::RequestClear(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, void VulkanRenderer::QueueClearRequest(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil,
Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget) { Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget) {
const GLbitfield supportedMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; const GLbitfield supportedMask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
const GLbitfield requestMask = (mask & supportedMask); const GLbitfield requestMask = (mask & supportedMask);
if (requestMask == 0) { if (requestMask == 0) {
@@ -1190,600 +1190,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
} }
Bool VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter,
Uint readFboExternalIndex, Uint drawFboExternalIndex,
Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer) {
if ((mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) == 0) {
MGLOG_D("BlitFramebuffer skipped: empty mask");
return false;
}
VkFilter vkFilter = VK_FILTER_NEAREST;
if (filter == GL_NEAREST) {
vkFilter = VK_FILTER_NEAREST;
} else if (filter == GL_LINEAR) {
vkFilter = VK_FILTER_LINEAR;
} else {
MGLOG_W("BlitFramebuffer skipped: filter %u is not supported", filter);
return false;
}
auto& frame = m_frameContext.GetCurrent();
if (frame.hasCommandBufferRecorded) {
MGLOG_D("BlitFramebuffer skipped: current frame command buffer already finalized");
return false;
}
if (!frame.isCommandRecording) {
m_frameContext.BeginCommandRecording();
MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "BlitFramebuffer: binder is null");
m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
VkCommandBuffer commandBuffer = frame.commandBuffer;
if (m_isMainRenderPassActive) {
MOBILEGL_ASSERT(m_renderPassManager != nullptr, "BlitFramebuffer: render pass manager is null");
m_renderPassManager->EndRenderPass(commandBuffer);
if (m_activeRenderTargetIsDefault) {
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
}
MOBILEGL_ASSERT(m_framebufferManager != nullptr, "BlitFramebuffer: framebuffer manager not available");
MOBILEGL_ASSERT(m_renderPassManager != nullptr, "BlitFramebuffer: renderpass manager is null");
auto consumePendingClearForTarget = [&](Bool targetIsDefault, Uint targetFboExternalIndex) {
const Uint64 pendingKey = BuildPendingClearKey(targetFboExternalIndex, targetIsDefault);
auto pendingIt = m_pendingClears.find(pendingKey);
if (pendingIt == m_pendingClears.end()) {
return;
}
const PendingClearState pending = pendingIt->second;
m_pendingClears.erase(pendingIt);
const auto prevExtent = m_activeRenderExtent;
const auto prevDepthStencilFormat = m_activeDepthStencilFormat;
if (targetIsDefault) {
TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired);
TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired);
VkRenderPass renderPass = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
VkExtent2D extent{};
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
if (!GetDefaultRenderTargetForCurrentImage(renderPass, framebuffer, extent, depthStencilFormat)) {
return;
}
m_renderPassManager->BeginRenderPass(commandBuffer, renderPass, framebuffer, extent);
m_activeRenderExtent = extent;
m_activeDepthStencilFormat = depthStencilFormat;
if ((pending.mask & GL_COLOR_BUFFER_BIT) != 0) {
m_renderPassManager->RecordColorClear(commandBuffer, m_activeRenderExtent, pending.color);
}
if ((pending.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
m_renderPassManager->RecordDepthStencilClear(commandBuffer, m_activeRenderExtent, pending.mask,
pending.depth, pending.stencil,
m_activeDepthStencilFormat);
}
m_renderPassManager->EndRenderPass(commandBuffer);
} else {
const auto targetFbo = MG_State::pGLContext->GetFramebufferObject(targetFboExternalIndex);
VkRenderPass offscreenRenderPass = VK_NULL_HANDLE;
VkFramebuffer offscreenFramebuffer = VK_NULL_HANDLE;
VkExtent2D offscreenExtent{};
VkFormat offscreenDepthStencilFormat = VK_FORMAT_UNDEFINED;
if (targetFbo &&
EnsureOffscreenRenderTarget(targetFboExternalIndex, *targetFbo, offscreenRenderPass,
offscreenFramebuffer, offscreenExtent, offscreenDepthStencilFormat) &&
m_framebufferManager->Transition(commandBuffer,
VkRenderTargetManager::TransitionResource::OffscreenColor,
VkRenderTargetManager::TransitionUsage::Attachment,
targetFboExternalIndex)) {
m_renderPassManager->BeginRenderPass(commandBuffer, offscreenRenderPass, offscreenFramebuffer,
offscreenExtent);
m_activeRenderExtent = offscreenExtent;
m_activeDepthStencilFormat = offscreenDepthStencilFormat;
if ((pending.mask & GL_COLOR_BUFFER_BIT) != 0) {
m_renderPassManager->RecordColorClear(commandBuffer, m_activeRenderExtent, pending.color);
}
if ((pending.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
m_renderPassManager->RecordDepthStencilClear(commandBuffer, m_activeRenderExtent, pending.mask,
pending.depth, pending.stencil,
m_activeDepthStencilFormat);
}
m_renderPassManager->EndRenderPass(commandBuffer);
}
}
m_activeRenderExtent = prevExtent;
m_activeDepthStencilFormat = prevDepthStencilFormat;
};
consumePendingClearForTarget(drawIsDefaultFramebuffer, drawFboExternalIndex);
auto selectSrcStageAccess = [](VkImageLayout layout, VkPipelineStageFlags& outStage, VkAccessFlags& outAccess) {
switch (layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
outStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
outAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
default:
outStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
outAccess = 0;
break;
}
};
auto transitionSwapchainLayout = [&](VkImageLayout newLayout, VkPipelineStageFlags dstStage,
VkAccessFlags dstAccess) {
const auto oldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
if (oldLayout == newLayout) {
return;
}
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccess = 0;
selectSrcStageAccess(oldLayout, srcStage, srcAccess);
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, newLayout);
};
auto clampCoord = [](GLint value, GLint minValue, GLint maxValue) -> GLint {
if (value < minValue) {
return minValue;
}
if (value > maxValue) {
return maxValue;
}
return value;
};
const Bool wantColor = (mask & GL_COLOR_BUFFER_BIT) != 0;
if (wantColor) {
VkImage srcImage = VK_NULL_HANDLE;
VkImage dstImage = VK_NULL_HANDLE;
VkExtent2D srcExtent{};
VkExtent2D dstExtent{};
if (readIsDefaultFramebuffer) {
srcImage = m_swapchainObject.GetImage(m_imageIndexAcquired);
srcExtent = m_swapchainObject.GetExtent();
} else {
const auto readFbo = MG_State::pGLContext->GetFramebufferObject(readFboExternalIndex);
if (!readFbo) {
MGLOG_W("BlitFramebuffer skipped: read FBO %u not found", readFboExternalIndex);
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(readFboExternalIndex, *readFbo)) {
MGLOG_W("BlitFramebuffer skipped: failed to materialize read FBO %u", readFboExternalIndex);
return false;
}
if (!m_framebufferManager->GetOffscreenColorImage(readFboExternalIndex, srcImage, srcExtent)) {
MGLOG_W("BlitFramebuffer skipped: read FBO %u has no color target", readFboExternalIndex);
return false;
}
}
if (drawIsDefaultFramebuffer) {
dstImage = m_swapchainObject.GetImage(m_imageIndexAcquired);
dstExtent = m_swapchainObject.GetExtent();
} else {
const auto drawFbo = MG_State::pGLContext->GetFramebufferObject(drawFboExternalIndex);
if (!drawFbo) {
MGLOG_W("BlitFramebuffer skipped: draw FBO %u not found", drawFboExternalIndex);
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(drawFboExternalIndex, *drawFbo)) {
MGLOG_W("BlitFramebuffer skipped: failed to materialize draw FBO %u", drawFboExternalIndex);
return false;
}
if (!m_framebufferManager->GetOffscreenColorImage(drawFboExternalIndex, dstImage, dstExtent)) {
MGLOG_W("BlitFramebuffer skipped: draw FBO %u has no color target", drawFboExternalIndex);
return false;
}
}
if (srcImage == VK_NULL_HANDLE || dstImage == VK_NULL_HANDLE) {
MGLOG_W("BlitFramebuffer skipped: missing source/destination image");
return false;
}
const Bool sameImage = (srcImage == dstImage);
if (readIsDefaultFramebuffer) {
if (sameImage) {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT);
}
} else if (!m_framebufferManager->Transition(commandBuffer,
VkRenderTargetManager::TransitionResource::OffscreenColor,
VkRenderTargetManager::TransitionUsage::TransferSrc,
readFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition read FBO %u to transfer src",
readFboExternalIndex);
return false;
}
if (drawIsDefaultFramebuffer) {
if (sameImage) {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
} else if (sameImage) {
if (!m_framebufferManager->Transition(commandBuffer,
VkRenderTargetManager::TransitionResource::OffscreenColor,
VkRenderTargetManager::TransitionUsage::General,
drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition draw FBO %u to general",
drawFboExternalIndex);
return false;
}
} else if (!m_framebufferManager->Transition(commandBuffer,
VkRenderTargetManager::TransitionResource::OffscreenColor,
VkRenderTargetManager::TransitionUsage::TransferDst,
drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition draw FBO %u to transfer dst",
drawFboExternalIndex);
return false;
}
GLint srcX0Clamped = clampCoord(srcX0, 0, static_cast<GLint>(srcExtent.width));
GLint srcX1Clamped = clampCoord(srcX1, 0, static_cast<GLint>(srcExtent.width));
GLint srcY0Clamped = clampCoord(srcY0, 0, static_cast<GLint>(srcExtent.height));
GLint srcY1Clamped = clampCoord(srcY1, 0, static_cast<GLint>(srcExtent.height));
if (readIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(srcExtent.height);
srcY0Clamped = height - srcY0Clamped;
srcY1Clamped = height - srcY1Clamped;
srcY0Clamped = clampCoord(srcY0Clamped, 0, height);
srcY1Clamped = clampCoord(srcY1Clamped, 0, height);
}
GLint dstX0Clamped = clampCoord(dstX0, 0, static_cast<GLint>(dstExtent.width));
GLint dstX1Clamped = clampCoord(dstX1, 0, static_cast<GLint>(dstExtent.width));
GLint dstY0Clamped = clampCoord(dstY0, 0, static_cast<GLint>(dstExtent.height));
GLint dstY1Clamped = clampCoord(dstY1, 0, static_cast<GLint>(dstExtent.height));
if (drawIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(dstExtent.height);
dstY0Clamped = height - dstY0Clamped;
dstY1Clamped = height - dstY1Clamped;
dstY0Clamped = clampCoord(dstY0Clamped, 0, height);
dstY1Clamped = clampCoord(dstY1Clamped, 0, height);
}
if (srcX0Clamped == srcX1Clamped || srcY0Clamped == srcY1Clamped || dstX0Clamped == dstX1Clamped ||
dstY0Clamped == dstY1Clamped) {
MGLOG_D("BlitFramebuffer skipped: empty blit region");
return true;
}
VkImageBlit blit{};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = 0;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.srcOffsets[0] = {srcX0Clamped, srcY0Clamped, 0};
blit.srcOffsets[1] = {srcX1Clamped, srcY1Clamped, 1};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = 0;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
blit.dstOffsets[0] = {dstX0Clamped, dstY0Clamped, 0};
blit.dstOffsets[1] = {dstX1Clamped, dstY1Clamped, 1};
const VkImageLayout srcLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
const VkImageLayout dstLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdBlitImage(commandBuffer, srcImage, srcLayout, dstImage, dstLayout, 1, &blit, vkFilter);
}
auto hasStencilComponent = [](VkFormat format) -> Bool {
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
};
const Bool wantDepth = (mask & GL_DEPTH_BUFFER_BIT) != 0;
const Bool wantStencil = (mask & GL_STENCIL_BUFFER_BIT) != 0;
if (wantDepth || wantStencil) {
if (filter != GL_NEAREST) {
MGLOG_W("BlitFramebuffer: depth/stencil blit requires GL_NEAREST, overriding filter");
vkFilter = VK_FILTER_NEAREST;
}
VkImage srcDepthImage = VK_NULL_HANDLE;
VkImage dstDepthImage = VK_NULL_HANDLE;
VkExtent2D srcDepthExtent{};
VkExtent2D dstDepthExtent{};
VkFormat srcDepthFormat = VK_FORMAT_UNDEFINED;
VkFormat dstDepthFormat = VK_FORMAT_UNDEFINED;
if (readIsDefaultFramebuffer) {
if (m_depthStencilImages.empty() || m_depthStencilFormat == VK_FORMAT_UNDEFINED) {
MGLOG_W("BlitFramebuffer: default framebuffer has no depth/stencil image");
} else {
srcDepthImage = m_depthStencilImages[m_imageIndexAcquired];
srcDepthExtent = m_swapchainObject.GetExtent();
srcDepthFormat = m_depthStencilFormat;
}
} else {
const auto readFbo = MG_State::pGLContext->GetFramebufferObject(readFboExternalIndex);
if (!readFbo) {
MGLOG_W("BlitFramebuffer: read FBO %u not found", readFboExternalIndex);
} else if (!m_framebufferManager->EnsureOffscreenColorTarget(readFboExternalIndex, *readFbo)) {
MGLOG_W("BlitFramebuffer: failed to materialize read FBO %u for depth/stencil",
readFboExternalIndex);
} else if (!m_framebufferManager->GetOffscreenDepthStencilImage(readFboExternalIndex, srcDepthImage,
srcDepthExtent, srcDepthFormat)) {
MGLOG_W("BlitFramebuffer: read FBO %u has no depth/stencil image", readFboExternalIndex);
}
}
if (drawIsDefaultFramebuffer) {
if (m_depthStencilImages.empty() || m_depthStencilFormat == VK_FORMAT_UNDEFINED) {
MGLOG_W("BlitFramebuffer: default framebuffer has no depth/stencil image");
} else {
dstDepthImage = m_depthStencilImages[m_imageIndexAcquired];
dstDepthExtent = m_swapchainObject.GetExtent();
dstDepthFormat = m_depthStencilFormat;
}
} else {
const auto drawFbo = MG_State::pGLContext->GetFramebufferObject(drawFboExternalIndex);
if (!drawFbo) {
MGLOG_W("BlitFramebuffer: draw FBO %u not found", drawFboExternalIndex);
} else if (!m_framebufferManager->EnsureOffscreenColorTarget(drawFboExternalIndex, *drawFbo)) {
MGLOG_W("BlitFramebuffer: failed to materialize draw FBO %u for depth/stencil",
drawFboExternalIndex);
} else if (!m_framebufferManager->GetOffscreenDepthStencilImage(drawFboExternalIndex, dstDepthImage,
dstDepthExtent, dstDepthFormat)) {
MGLOG_W("BlitFramebuffer: draw FBO %u has no depth/stencil image", drawFboExternalIndex);
}
}
if (srcDepthImage != VK_NULL_HANDLE && dstDepthImage != VK_NULL_HANDLE) {
if (srcDepthFormat != dstDepthFormat) {
MGLOG_W("BlitFramebuffer: depth/stencil format mismatch (src=%d, dst=%d), skipping", srcDepthFormat,
dstDepthFormat);
} else {
VkImageAspectFlags srcAspectMask = 0;
VkImageAspectFlags dstAspectMask = 0;
if (wantDepth) {
srcAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
dstAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (wantStencil && hasStencilComponent(srcDepthFormat)) {
srcAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
dstAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
} else if (wantStencil) {
MGLOG_W("BlitFramebuffer: stencil bit requested but format has no stencil component");
}
const VkImageAspectFlags aspectMask = srcAspectMask & dstAspectMask;
if (aspectMask != 0) {
const Bool sameDepthImage = (srcDepthImage == dstDepthImage);
const VkImageAspectFlags fullAspectMask =
VK_IMAGE_ASPECT_DEPTH_BIT |
(hasStencilComponent(srcDepthFormat) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
auto transitionDefaultDepthStencil = [&](VkImageLayout newLayout, VkPipelineStageFlags dstStage,
VkAccessFlags dstAccess) {
if (m_depthStencilImages.empty()) {
return;
}
const auto oldLayout = m_depthStencilImageLayouts[m_imageIndexAcquired];
if (oldLayout == newLayout) {
return;
}
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccess = 0;
switch (oldLayout) {
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
srcAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
break;
default:
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
srcAccess = 0;
break;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_depthStencilImages[m_imageIndexAcquired];
barrier.subresourceRange.aspectMask = fullAspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1,
&barrier);
m_depthStencilImageLayouts[m_imageIndexAcquired] = newLayout;
};
if (readIsDefaultFramebuffer) {
if (sameDepthImage) {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT);
}
} else if (!m_framebufferManager->Transition(
commandBuffer, VkRenderTargetManager::TransitionResource::OffscreenDepthStencil,
VkRenderTargetManager::TransitionUsage::TransferSrc, readFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition read depth/stencil FBO %u to transfer src",
readFboExternalIndex);
}
if (drawIsDefaultFramebuffer) {
if (sameDepthImage) {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
} else if (sameDepthImage) {
if (!m_framebufferManager->Transition(
commandBuffer, VkRenderTargetManager::TransitionResource::OffscreenDepthStencil,
VkRenderTargetManager::TransitionUsage::General, drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition draw depth/stencil FBO %u to general",
drawFboExternalIndex);
}
} else if (!m_framebufferManager->Transition(
commandBuffer, VkRenderTargetManager::TransitionResource::OffscreenDepthStencil,
VkRenderTargetManager::TransitionUsage::TransferDst, drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition draw depth/stencil FBO %u to transfer dst",
drawFboExternalIndex);
}
GLint srcDX0 = clampCoord(srcX0, 0, static_cast<GLint>(srcDepthExtent.width));
GLint srcDX1 = clampCoord(srcX1, 0, static_cast<GLint>(srcDepthExtent.width));
GLint srcDY0 = clampCoord(srcY0, 0, static_cast<GLint>(srcDepthExtent.height));
GLint srcDY1 = clampCoord(srcY1, 0, static_cast<GLint>(srcDepthExtent.height));
if (readIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(srcDepthExtent.height);
srcDY0 = height - srcDY0;
srcDY1 = height - srcDY1;
srcDY0 = clampCoord(srcDY0, 0, height);
srcDY1 = clampCoord(srcDY1, 0, height);
}
GLint dstDX0 = clampCoord(dstX0, 0, static_cast<GLint>(dstDepthExtent.width));
GLint dstDX1 = clampCoord(dstX1, 0, static_cast<GLint>(dstDepthExtent.width));
GLint dstDY0 = clampCoord(dstY0, 0, static_cast<GLint>(dstDepthExtent.height));
GLint dstDY1 = clampCoord(dstY1, 0, static_cast<GLint>(dstDepthExtent.height));
if (drawIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(dstDepthExtent.height);
dstDY0 = height - dstDY0;
dstDY1 = height - dstDY1;
dstDY0 = clampCoord(dstDY0, 0, height);
dstDY1 = clampCoord(dstDY1, 0, height);
}
if (srcDX0 != srcDX1 && srcDY0 != srcDY1 && dstDX0 != dstDX1 && dstDY0 != dstDY1) {
VkImageBlit depthBlit{};
depthBlit.srcSubresource.aspectMask = aspectMask;
depthBlit.srcSubresource.mipLevel = 0;
depthBlit.srcSubresource.baseArrayLayer = 0;
depthBlit.srcSubresource.layerCount = 1;
depthBlit.srcOffsets[0] = {srcDX0, srcDY0, 0};
depthBlit.srcOffsets[1] = {srcDX1, srcDY1, 1};
depthBlit.dstSubresource.aspectMask = aspectMask;
depthBlit.dstSubresource.mipLevel = 0;
depthBlit.dstSubresource.baseArrayLayer = 0;
depthBlit.dstSubresource.layerCount = 1;
depthBlit.dstOffsets[0] = {dstDX0, dstDY0, 0};
depthBlit.dstOffsets[1] = {dstDX1, dstDY1, 1};
const VkImageLayout depthSrcLayout =
sameDepthImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
const VkImageLayout depthDstLayout =
sameDepthImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdBlitImage(commandBuffer, srcDepthImage, depthSrcLayout, dstDepthImage, depthDstLayout,
1, &depthBlit, vkFilter);
}
}
}
}
}
if (readIsDefaultFramebuffer || drawIsDefaultFramebuffer) {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0);
}
return true;
}
void VulkanRenderer::Render() {
// Route test rendering through the same frame-start logic used by draw calls,
// so pending glClear() state can be consumed consistently.
EnsureFrameRecordingStarted();
auto& frame = m_frameContext.GetCurrent();
if (!frame.isCommandRecording || !m_isMainRenderPassActive) {
MGLOG_D("Render skipped: frame recording was not started");
return;
}
VkCommandBuffer& commandBuffer = frame.commandBuffer;
const auto swapchainExtent = m_swapchainObject.GetExtent();
(void)commandBuffer;
(void)swapchainExtent;
}
void VulkanRenderer::Present() { void VulkanRenderer::Present() {
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(), MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
"Present, acquired image index out of range"); "Present, acquired image index out of range");
@@ -70,17 +70,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Initialize(); void Initialize();
void Shutdown(); void Shutdown();
void RequestClear(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil, void QueueClearRequest(GLbitfield mask, const FloatVec4& color, Float depth, Uint32 stencil,
Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget); Uint drawFboExternalIndex, Bool isDefaultFramebufferTarget);
Bool ConsumePendingColorClear(VkClearColorValue& outClearColor); Bool ConsumePendingColorClear(VkClearColorValue& outClearColor);
void EnsureFrameRecordingStarted(); void EnsureFrameRecordingStarted();
void DrawArrays(const DrawArrayPayload& payload); void DrawArrays(const DrawArrayPayload& payload);
void DrawElements(const DrawElementPayload& payload); void DrawElements(const DrawElementPayload& payload);
void MultiDrawElements(const Vector<DrawElementPayload>& payloads); void MultiDrawElements(const Vector<DrawElementPayload>& payloads);
Bool BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
GLint dstY1, GLbitfield mask, GLenum filter, Uint readFboExternalIndex,
Uint drawFboExternalIndex, Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer);
void Render();
void Present(); void Present();
const PhysicalDevice& GetPhysicalDevice() const; const PhysicalDevice& GetPhysicalDevice() const;