[Feat] (MG_Backend/DirectVulkan): per-fbo pending clear state

This commit is contained in:
2026-02-17 20:36:49 +08:00
parent e132e9edc1
commit 377b967996
4 changed files with 641 additions and 142 deletions
@@ -41,30 +41,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
return RecreateOffscreenColorTarget(target, colorAttachment, objectVersion);
return RecreateOffscreenColorTarget(target, glFbo, colorAttachment, objectVersion);
}
Bool VkFramebufferManager::ClearColor(VkCommandBuffer commandBuffer, Uint glFboExternalIndex,
const VkClearColorValue& clearColor) {
Bool VkFramebufferManager::TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::ClearColor skipped: no offscreen target for FBO %u", glFboExternalIndex);
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToAttachment skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (!TransitionColorTargetForClear(commandBuffer, target)) {
return false;
}
VkImageSubresourceRange subresourceRange{};
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresourceRange.baseMipLevel = 0;
subresourceRange.levelCount = 1;
subresourceRange.baseArrayLayer = 0;
subresourceRange.layerCount = 1;
vkCmdClearColorImage(commandBuffer, target.image, target.layout, &clearColor, 1, &subresourceRange);
return true;
return TransitionColorTargetForAttachment(commandBuffer, it->second) &&
TransitionDepthStencilTargetForAttachment(commandBuffer, it->second);
}
Bool VkFramebufferManager::TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer,
@@ -89,8 +78,24 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true;
}
Bool VkFramebufferManager::GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass,
VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
VkFormat& outDepthStencilFormat) const {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end() || it->second.framebuffer == VK_NULL_HANDLE ||
it->second.renderPassLoad == VK_NULL_HANDLE) {
return false;
}
outRenderPass = it->second.renderPassLoad;
outFramebuffer = it->second.framebuffer;
outExtent = it->second.extent;
outDepthStencilFormat = it->second.depthStencilFormat;
return true;
}
Bool VkFramebufferManager::RecreateOffscreenColorTarget(
OffscreenColorTarget& target, const MG_State::GLState::FramebufferAttachmentObject& colorAttachment,
OffscreenColorTarget& target, const MG_State::GLState::FramebufferObject& glFbo,
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment,
Uint16 glObjectVersion) {
DestroyOffscreenColorTarget(target);
@@ -144,43 +149,202 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &target.imageView), "vkCreateImageView(offscreen color)");
const auto& depthAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Depth);
const auto& stencilAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Stencil);
const Bool requestedDepthStencil =
(depthAttachment.IsValid() && !depthAttachment.IsEmpty()) ||
(stencilAttachment.IsValid() && !stencilAttachment.IsEmpty());
VkFormat depthStencilFormat = ResolveDepthStencilFormat(depthAttachment, stencilAttachment);
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT});
} else if (depthStencilFormat == VK_FORMAT_D32_SFLOAT) {
depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM});
}
const Bool hasDepthStencil = (depthStencilFormat != VK_FORMAT_UNDEFINED);
if (requestedDepthStencil && !hasDepthStencil) {
MGLOG_W("VkFramebufferManager: FBO %u depth/stencil attachment exists but format is unsupported",
glFbo.GetExternalIndex());
}
if (hasDepthStencil) {
VkImageCreateInfo depthImageInfo{};
depthImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
depthImageInfo.imageType = VK_IMAGE_TYPE_2D;
depthImageInfo.extent.width = static_cast<Uint32>(size.x());
depthImageInfo.extent.height = static_cast<Uint32>(size.y());
depthImageInfo.extent.depth = 1;
depthImageInfo.mipLevels = 1;
depthImageInfo.arrayLayers = 1;
depthImageInfo.format = depthStencilFormat;
depthImageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
depthImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthImageInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
depthImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
depthImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &depthImageInfo, nullptr, &target.depthStencilImage),
"vkCreateImage(offscreen depth/stencil)");
VkMemoryRequirements depthMemoryRequirements{};
vkGetImageMemoryRequirements(m_device, target.depthStencilImage, &depthMemoryRequirements);
VkMemoryAllocateInfo depthAllocInfo{};
depthAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
depthAllocInfo.allocationSize = depthMemoryRequirements.size;
depthAllocInfo.memoryTypeIndex =
FindMemoryType(depthMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &depthAllocInfo, nullptr, &target.depthStencilMemory),
"vkAllocateMemory(offscreen depth/stencil)");
VK_VERIFY(vkBindImageMemory(m_device, target.depthStencilImage, target.depthStencilMemory, 0),
"vkBindImageMemory(offscreen depth/stencil)");
VkImageAspectFlags depthAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT || depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
VkImageViewCreateInfo depthViewInfo{};
depthViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
depthViewInfo.image = target.depthStencilImage;
depthViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthViewInfo.format = depthStencilFormat;
depthViewInfo.subresourceRange.aspectMask = depthAspectMask;
depthViewInfo.subresourceRange.baseMipLevel = 0;
depthViewInfo.subresourceRange.levelCount = 1;
depthViewInfo.subresourceRange.baseArrayLayer = 0;
depthViewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &depthViewInfo, nullptr, &target.depthStencilImageView),
"vkCreateImageView(offscreen depth/stencil)");
}
VkAttachmentDescription colorAttachmentDesc{};
colorAttachmentDesc.format = format;
colorAttachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
colorAttachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkAttachmentDescription depthAttachmentDesc{};
VkAttachmentReference depthAttachmentRef{};
Array<VkAttachmentDescription, 2> attachments{};
attachments[0] = colorAttachmentDesc;
Uint32 attachmentCount = 1;
if (hasDepthStencil) {
depthAttachmentDesc.format = depthStencilFormat;
depthAttachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
depthAttachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachmentDesc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachmentDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
attachments[1] = depthAttachmentDesc;
attachmentCount = 2;
}
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = attachmentCount;
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
VK_VERIFY(vkCreateRenderPass(m_device, &renderPassInfo, nullptr, &target.renderPassLoad),
"vkCreateRenderPass(offscreen)");
Array<VkImageView, 2> framebufferAttachments{};
framebufferAttachments[0] = target.imageView;
Uint32 framebufferAttachmentCount = 1;
if (hasDepthStencil) {
framebufferAttachments[1] = target.depthStencilImageView;
framebufferAttachmentCount = 2;
}
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = target.renderPassLoad;
framebufferInfo.attachmentCount = framebufferAttachmentCount;
framebufferInfo.pAttachments = framebufferAttachments.data();
framebufferInfo.width = static_cast<Uint32>(size.x());
framebufferInfo.height = static_cast<Uint32>(size.y());
framebufferInfo.layers = 1;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferInfo, nullptr, &target.framebuffer),
"vkCreateFramebuffer(offscreen)");
target.layout = VK_IMAGE_LAYOUT_UNDEFINED;
target.extent = {static_cast<Uint32>(size.x()), static_cast<Uint32>(size.y())};
target.format = format;
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
target.depthStencilFormat = depthStencilFormat;
target.glObjectVersion = glObjectVersion;
return true;
}
void VkFramebufferManager::DestroyOffscreenColorTarget(OffscreenColorTarget& target) {
if (target.framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(m_device, target.framebuffer, nullptr);
target.framebuffer = VK_NULL_HANDLE;
}
if (target.renderPassLoad != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, target.renderPassLoad, nullptr);
target.renderPassLoad = VK_NULL_HANDLE;
}
if (target.imageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, target.imageView, nullptr);
target.imageView = VK_NULL_HANDLE;
}
if (target.depthStencilImageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, target.depthStencilImageView, nullptr);
target.depthStencilImageView = VK_NULL_HANDLE;
}
if (target.image != VK_NULL_HANDLE) {
vkDestroyImage(m_device, target.image, nullptr);
target.image = VK_NULL_HANDLE;
}
if (target.depthStencilImage != VK_NULL_HANDLE) {
vkDestroyImage(m_device, target.depthStencilImage, nullptr);
target.depthStencilImage = VK_NULL_HANDLE;
}
if (target.memory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, target.memory, nullptr);
target.memory = VK_NULL_HANDLE;
}
if (target.depthStencilMemory != VK_NULL_HANDLE) {
vkFreeMemory(m_device, target.depthStencilMemory, nullptr);
target.depthStencilMemory = VK_NULL_HANDLE;
}
target.layout = VK_IMAGE_LAYOUT_UNDEFINED;
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
target.extent = {0, 0};
target.format = VK_FORMAT_UNDEFINED;
target.depthStencilFormat = VK_FORMAT_UNDEFINED;
target.glObjectVersion = 0;
}
Bool VkFramebufferManager::TransitionColorTargetForClear(VkCommandBuffer commandBuffer, OffscreenColorTarget& target) {
if (target.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
Bool VkFramebufferManager::TransitionColorTargetForAttachment(VkCommandBuffer commandBuffer,
OffscreenColorTarget& target) {
if (target.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
return true;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.oldLayout = target.layout;
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = target.image;
@@ -189,10 +353,46 @@ namespace MobileGL::MG_Backend::DirectVulkan {
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
target.layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
target.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
return true;
}
Bool VkFramebufferManager::TransitionDepthStencilTargetForAttachment(VkCommandBuffer commandBuffer,
OffscreenColorTarget& target) {
if (target.depthStencilImage == VK_NULL_HANDLE) {
return true;
}
if (target.depthStencilLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
return true;
}
VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (target.depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
target.depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barrier.oldLayout = target.depthStencilLayout;
barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = target.depthStencilImage;
barrier.subresourceRange.aspectMask = aspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
target.depthStencilLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
return true;
}
@@ -255,4 +455,59 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return VK_FORMAT_UNDEFINED;
}
}
VkFormat VkFramebufferManager::ResolveDepthStencilFormat(
const MG_State::GLState::FramebufferAttachmentObject& depthAttachment,
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment) {
const auto resolveAttachmentFormat = [](const MG_State::GLState::FramebufferAttachmentObject& attachment) {
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
if (attachment.IsTexture()) {
const auto texture = attachment.GetTexture();
internalFormat = texture ? texture->GetFormat() : TextureInternalFormat::Unknown;
} else if (attachment.IsRenderbuffer()) {
const auto renderbuffer = attachment.GetRenderbuffer();
internalFormat = renderbuffer ? renderbuffer->GetInternalFormat() : TextureInternalFormat::Unknown;
}
return internalFormat;
};
const auto depthFormat = resolveAttachmentFormat(depthAttachment);
const auto stencilFormat = resolveAttachmentFormat(stencilAttachment);
switch (depthFormat) {
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return VK_FORMAT_D24_UNORM_S8_UINT;
case TextureInternalFormat::DepthComponent16:
return VK_FORMAT_D16_UNORM;
case TextureInternalFormat::DepthComponent24:
case TextureInternalFormat::DepthComponent32:
case TextureInternalFormat::DepthComponent32F:
case TextureInternalFormat::DepthComponent:
return VK_FORMAT_D32_SFLOAT;
default:
break;
}
switch (stencilFormat) {
case TextureInternalFormat::Depth24Stencil8:
case TextureInternalFormat::Depth32FStencil8:
case TextureInternalFormat::DepthStencil:
return VK_FORMAT_D24_UNORM_S8_UINT;
default:
return VK_FORMAT_UNDEFINED;
}
}
VkFormat VkFramebufferManager::FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const {
for (auto format : candidates) {
VkFormatProperties properties{};
vkGetPhysicalDeviceFormatProperties(m_physicalDevice, format, &properties);
if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) {
return format;
}
}
return VK_FORMAT_UNDEFINED;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -27,9 +27,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Shutdown();
Bool EnsureOffscreenColorTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo);
Bool ClearColor(VkCommandBuffer commandBuffer, Uint glFboExternalIndex, const VkClearColorValue& clearColor);
Bool TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent) const;
Bool GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const;
private:
struct OffscreenColorTarget {
@@ -39,17 +41,29 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkExtent2D extent = {0, 0};
VkFormat format = VK_FORMAT_UNDEFINED;
VkImage depthStencilImage = VK_NULL_HANDLE;
VkDeviceMemory depthStencilMemory = VK_NULL_HANDLE;
VkImageView depthStencilImageView = VK_NULL_HANDLE;
VkImageLayout depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
VkFormat depthStencilFormat = VK_FORMAT_UNDEFINED;
VkRenderPass renderPassLoad = VK_NULL_HANDLE;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
Uint16 glObjectVersion = 0;
};
Bool RecreateOffscreenColorTarget(OffscreenColorTarget& target,
const MG_State::GLState::FramebufferObject& glFbo,
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment,
Uint16 glObjectVersion);
void DestroyOffscreenColorTarget(OffscreenColorTarget& target);
Bool TransitionColorTargetForClear(VkCommandBuffer commandBuffer, OffscreenColorTarget& target);
Bool TransitionColorTargetForAttachment(VkCommandBuffer commandBuffer, OffscreenColorTarget& target);
Bool TransitionDepthStencilTargetForAttachment(VkCommandBuffer commandBuffer, OffscreenColorTarget& target);
Bool TransitionColorTargetForBlitSrc(VkCommandBuffer commandBuffer, OffscreenColorTarget& target);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static VkFormat ResolveColorFormat(const MG_State::GLState::FramebufferAttachmentObject& colorAttachment);
static VkFormat ResolveDepthStencilFormat(const MG_State::GLState::FramebufferAttachmentObject& depthAttachment,
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment);
VkFormat FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const;
VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE;
@@ -12,6 +12,7 @@
#include "MG_State/GLState/Core.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
namespace MobileGL::MG_Backend::DirectVulkan {
VkBool32 VulkanRenderer::DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
@@ -76,7 +77,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
payload.programHash = programHash;
payload.vertexInputHash = vertexInputHash;
payload.pipelineLayout = m_pipelineLayout;
payload.renderPass = m_renderPassLoad;
payload.renderPass = (m_activeRenderPass != VK_NULL_HANDLE) ? m_activeRenderPass : m_renderPassLoad;
payload.subpass = 0;
payload.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
payload.stages = &stages;
@@ -215,39 +216,39 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return;
}
if (m_pendingClearMask != 0 &&
(m_pendingClearDrawFboExternalIndex != drawFboExternalIndex ||
m_pendingClearTargetsDefaultFramebuffer != isDefaultFramebufferTarget)) {
MGLOG_W("Pending clear target changed from FBO %u to FBO %u before execution; dropping previous pending clear",
m_pendingClearDrawFboExternalIndex, drawFboExternalIndex);
m_pendingClearMask = 0;
}
const Uint64 pendingKey = BuildPendingClearKey(drawFboExternalIndex, isDefaultFramebufferTarget);
auto& pending = m_pendingClears[pendingKey];
if ((requestMask & GL_COLOR_BUFFER_BIT) != 0) {
m_pendingClearColor.float32[0] = color.x();
m_pendingClearColor.float32[1] = color.y();
m_pendingClearColor.float32[2] = color.z();
m_pendingClearColor.float32[3] = color.w();
pending.color.float32[0] = color.x();
pending.color.float32[1] = color.y();
pending.color.float32[2] = color.z();
pending.color.float32[3] = color.w();
}
if ((requestMask & GL_DEPTH_BUFFER_BIT) != 0) {
m_pendingClearDepth = depth;
pending.depth = depth;
}
if ((requestMask & GL_STENCIL_BUFFER_BIT) != 0) {
m_pendingClearStencil = stencil;
pending.stencil = stencil;
}
m_pendingClearDrawFboExternalIndex = drawFboExternalIndex;
m_pendingClearTargetsDefaultFramebuffer = isDefaultFramebufferTarget;
m_pendingClearMask |= requestMask;
pending.drawFboExternalIndex = drawFboExternalIndex;
pending.targetsDefaultFramebuffer = isDefaultFramebufferTarget;
pending.mask |= requestMask;
}
Bool VulkanRenderer::ConsumePendingColorClear(VkClearColorValue& outClearColor) {
if ((m_pendingClearMask & GL_COLOR_BUFFER_BIT) == 0) {
return false;
for (auto it = m_pendingClears.begin(); it != m_pendingClears.end(); ++it) {
if (!it->second.targetsDefaultFramebuffer || (it->second.mask & GL_COLOR_BUFFER_BIT) == 0) {
continue;
}
outClearColor = it->second.color;
it->second.mask &= ~GL_COLOR_BUFFER_BIT;
if (it->second.mask == 0) {
m_pendingClears.erase(it);
}
return true;
}
outClearColor = m_pendingClearColor;
m_pendingClearMask &= ~GL_COLOR_BUFFER_BIT;
return true;
return false;
}
void VulkanRenderer::TransitionSwapchainImageToColorAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex) {
@@ -284,7 +285,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkClearRect clearRect{};
clearRect.rect.offset = {0, 0};
clearRect.rect.extent = m_swapchainObject.GetExtent();
clearRect.rect.extent = m_activeRenderExtent;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = 1;
@@ -292,11 +293,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
void VulkanRenderer::RecordDepthStencilClear(VkCommandBuffer commandBuffer, GLbitfield mask, Float depth, Uint32 stencil) {
if (m_activeDepthStencilFormat == VK_FORMAT_UNDEFINED) {
return;
}
VkImageAspectFlags aspectMask = 0;
if ((mask & GL_DEPTH_BUFFER_BIT) != 0) {
aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if ((mask & GL_STENCIL_BUFFER_BIT) != 0 && HasStencilComponent(m_depthStencilFormat)) {
if ((mask & GL_STENCIL_BUFFER_BIT) != 0 && HasStencilComponent(m_activeDepthStencilFormat)) {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
if (aspectMask == 0) {
@@ -310,7 +315,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkClearRect clearRect{};
clearRect.rect.offset = {0, 0};
clearRect.rect.extent = m_swapchainObject.GetExtent();
clearRect.rect.extent = m_activeRenderExtent;
clearRect.baseArrayLayer = 0;
clearRect.layerCount = 1;
@@ -351,50 +356,59 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_depthStencilImageLayouts[imageIndex] = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
Bool VulkanRenderer::RecordOffscreenColorClear(VkCommandBuffer commandBuffer) {
if (!m_framebufferManager || !MG_State::pGLContext) {
return false;
}
const auto fbo = MG_State::pGLContext->GetFramebufferObject(m_pendingClearDrawFboExternalIndex);
if (!fbo) {
MGLOG_W("RecordOffscreenColorClear skipped: draw FBO %u not found", m_pendingClearDrawFboExternalIndex);
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(m_pendingClearDrawFboExternalIndex, *fbo)) {
return false;
}
return m_framebufferManager->ClearColor(commandBuffer, m_pendingClearDrawFboExternalIndex, m_pendingClearColor);
}
void VulkanRenderer::EnsureFrameRecordingStarted() {
auto& frame = m_frameContext.GetCurrent();
if (frame.isCommandRecording) {
return;
}
if (frame.hasCommandBufferRecorded) {
MGLOG_W("EnsureFrameRecordingStarted skipped: current frame command buffer is already finalized");
return;
}
VkCommandBuffer& commandBuffer = m_frameContext.BeginCommandRecording();
if (m_uniformDescriptorBinder) {
m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex());
const auto drawFbo = MG_State::pGLContext
? MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()
: nullptr;
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool drawTargetsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
const Uint drawFboExternalIndex = drawFbo ? drawFbo->GetExternalIndex() : (defaultFbo ? defaultFbo->GetExternalIndex() : 0U);
const Uint64 drawTargetKey = BuildPendingClearKey(drawFboExternalIndex, drawTargetsDefault);
if (frame.isCommandRecording && m_isMainRenderPassActive) {
const Bool activeTargetMismatch =
(drawTargetsDefault != m_activeRenderTargetIsDefault) ||
(!drawTargetsDefault && m_activeDrawFboExternalIndex != drawFboExternalIndex);
if (activeTargetMismatch) {
vkCmdEndRenderPass(frame.commandBuffer);
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
} else {
return;
}
}
if (m_pendingClearMask != 0 && !m_pendingClearTargetsDefaultFramebuffer) {
if ((m_pendingClearMask & GL_COLOR_BUFFER_BIT) != 0) {
if (!RecordOffscreenColorClear(commandBuffer)) {
MGLOG_W("Failed to clear non-default FBO %u color attachment",
m_pendingClearDrawFboExternalIndex);
}
m_pendingClearMask &= ~GL_COLOR_BUFFER_BIT;
VkCommandBuffer* commandBufferPtr = nullptr;
if (frame.isCommandRecording) {
commandBufferPtr = &frame.commandBuffer;
} else {
commandBufferPtr = &m_frameContext.BeginCommandRecording();
if (m_uniformDescriptorBinder) {
m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
if ((m_pendingClearMask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
MGLOG_W("Depth/stencil clear on non-default FBO is not implemented yet (FBO %u)",
m_pendingClearDrawFboExternalIndex);
m_pendingClearMask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
VkCommandBuffer& commandBuffer = *commandBufferPtr;
if (!drawTargetsDefault) {
if (!m_framebufferManager || !drawFbo) {
MGLOG_W("EnsureFrameRecordingStarted skipped: offscreen draw target is unavailable");
return;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(drawFboExternalIndex, *drawFbo)) {
MGLOG_W("EnsureFrameRecordingStarted skipped: failed to materialize offscreen target for FBO %u",
drawFboExternalIndex);
return;
}
// This frame touched only offscreen resources. Present still requires
@@ -420,33 +434,90 @@ namespace MobileGL::MG_Backend::DirectVulkan {
0, 0, nullptr, 0, nullptr, 1, &presentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
if (!m_framebufferManager->TransitionOffscreenColorToAttachment(commandBuffer, drawFboExternalIndex)) {
MGLOG_W("EnsureFrameRecordingStarted skipped: failed to transition offscreen FBO %u for attachment",
drawFboExternalIndex);
return;
}
VkRenderPass offscreenRenderPass = VK_NULL_HANDLE;
VkFramebuffer offscreenFramebuffer = VK_NULL_HANDLE;
VkExtent2D offscreenExtent{};
VkFormat offscreenDepthStencilFormat = VK_FORMAT_UNDEFINED;
if (!m_framebufferManager->GetOffscreenRenderTarget(drawFboExternalIndex, offscreenRenderPass,
offscreenFramebuffer, offscreenExtent,
offscreenDepthStencilFormat)) {
MGLOG_W("EnsureFrameRecordingStarted skipped: offscreen render target for FBO %u is unavailable",
drawFboExternalIndex);
return;
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = offscreenRenderPass;
renderPassInfo.framebuffer = offscreenFramebuffer;
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = offscreenExtent;
renderPassInfo.clearValueCount = 0;
renderPassInfo.pClearValues = nullptr;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
m_isMainRenderPassActive = true;
m_activeRenderPass = offscreenRenderPass;
m_activeRenderExtent = offscreenExtent;
m_activeDepthStencilFormat = offscreenDepthStencilFormat;
m_activeRenderTargetIsDefault = false;
m_activeDrawFboExternalIndex = drawFboExternalIndex;
auto pendingIt = m_pendingClears.find(drawTargetKey);
if (pendingIt != m_pendingClears.end()) {
if ((pendingIt->second.mask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(commandBuffer, pendingIt->second.color);
pendingIt->second.mask &= ~GL_COLOR_BUFFER_BIT;
}
if ((pendingIt->second.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(commandBuffer, pendingIt->second.mask, pendingIt->second.depth,
pendingIt->second.stencil);
pendingIt->second.mask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
if (pendingIt->second.mask == 0) {
m_pendingClears.erase(pendingIt);
}
}
return;
}
TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired);
TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired);
const Bool useRenderPassClearValue = (m_pendingClearMask & GL_COLOR_BUFFER_BIT) != 0;
VkClearValue clearValues[1]{};
if (useRenderPassClearValue) {
clearValues[0].color = m_pendingClearColor;
m_pendingClearMask &= ~GL_COLOR_BUFFER_BIT;
}
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = useRenderPassClearValue ? m_renderPassClear : m_renderPassLoad;
renderPassInfo.renderPass = m_renderPassLoad;
renderPassInfo.framebuffer = m_framebuffers[m_imageIndexAcquired];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = m_swapchainObject.GetExtent();
renderPassInfo.clearValueCount = useRenderPassClearValue ? 1 : 0;
renderPassInfo.pClearValues = useRenderPassClearValue ? clearValues : nullptr;
renderPassInfo.clearValueCount = 0;
renderPassInfo.pClearValues = nullptr;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
m_isMainRenderPassActive = true;
if ((m_pendingClearMask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(commandBuffer, m_pendingClearMask, m_pendingClearDepth, m_pendingClearStencil);
m_pendingClearMask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
m_activeRenderPass = renderPassInfo.renderPass;
m_activeRenderExtent = renderPassInfo.renderArea.extent;
m_activeDepthStencilFormat = m_depthStencilFormat;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = drawFboExternalIndex;
auto pendingIt = m_pendingClears.find(drawTargetKey);
if (pendingIt != m_pendingClears.end()) {
if ((pendingIt->second.mask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(commandBuffer, pendingIt->second.color);
pendingIt->second.mask &= ~GL_COLOR_BUFFER_BIT;
}
if ((pendingIt->second.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(commandBuffer, pendingIt->second.mask, pendingIt->second.depth,
pendingIt->second.stencil);
pendingIt->second.mask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
if (pendingIt->second.mask == 0) {
m_pendingClears.erase(pendingIt);
}
}
}
@@ -459,6 +530,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (m_isMainRenderPassActive) {
vkCmdEndRenderPass(frame.commandBuffer);
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
}
m_frameContext.EndCommandRecording();
}
@@ -559,7 +635,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
VkCommandBuffer& commandBuffer = frame.commandBuffer;
const auto swapchainExtent = m_swapchainObject.GetExtent();
const auto activeExtent = m_activeRenderExtent;
if (payload.program == nullptr) {
MGLOG_W("DrawArrays skipped: no current program is bound");
@@ -601,15 +677,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapchainExtent.width);
viewport.height = static_cast<float>(swapchainExtent.height);
viewport.width = static_cast<float>(activeExtent.width);
viewport.height = static_cast<float>(activeExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapchainExtent;
scissor.extent = activeExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdDraw(commandBuffer, static_cast<Uint32>(payload.count), 1, static_cast<Uint32>(payload.first), 0);
@@ -687,7 +763,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
VkCommandBuffer& commandBuffer = frame.commandBuffer;
const auto swapchainExtent = m_swapchainObject.GetExtent();
const auto activeExtent = m_activeRenderExtent;
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
if (m_uniformDescriptorBinder &&
@@ -711,15 +787,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = static_cast<float>(swapchainExtent.width);
viewport.height = static_cast<float>(swapchainExtent.height);
viewport.width = static_cast<float>(activeExtent.width);
viewport.height = static_cast<float>(activeExtent.height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapchainExtent;
scissor.extent = activeExtent;
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer.GetHandle(), 0, vkIndexType);
@@ -756,6 +832,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (m_isMainRenderPassActive) {
vkCmdEndRenderPass(commandBuffer);
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
}
if (!m_framebufferManager || !MG_State::pGLContext) {
@@ -865,37 +946,166 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
"Present, acquired image index out of range");
auto& frame = m_frameContext.GetCurrent();
if (m_pendingClearMask != 0 && frame.hasCommandBufferRecorded) {
MGLOG_W("Dropping pending clear for current frame because command buffer is already finalized");
m_pendingClearMask = 0;
} else if (m_pendingClearMask != 0 && frame.isCommandRecording) {
if (m_pendingClearTargetsDefaultFramebuffer && m_isMainRenderPassActive) {
if ((m_pendingClearMask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(frame.commandBuffer, m_pendingClearColor);
if (!m_pendingClears.empty() && frame.hasCommandBufferRecorded) {
MGLOG_W("Dropping pending clears for current frame because command buffer is already finalized");
m_pendingClears.clear();
} else if (!m_pendingClears.empty()) {
auto activateTarget = [&](Bool targetIsDefault, Uint targetFboExternalIndex) -> Bool {
const Bool alreadyMatched =
frame.isCommandRecording &&
m_isMainRenderPassActive &&
(targetIsDefault == m_activeRenderTargetIsDefault) &&
(targetIsDefault || targetFboExternalIndex == m_activeDrawFboExternalIndex);
if (alreadyMatched) {
return true;
}
if ((m_pendingClearMask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(frame.commandBuffer, m_pendingClearMask, m_pendingClearDepth, m_pendingClearStencil);
if (frame.hasCommandBufferRecorded) {
return false;
}
m_pendingClearMask = 0;
} else if (!m_pendingClearTargetsDefaultFramebuffer) {
if ((m_pendingClearMask & GL_COLOR_BUFFER_BIT) != 0) {
if (!RecordOffscreenColorClear(frame.commandBuffer)) {
MGLOG_W("Present: failed to clear non-default FBO %u color attachment",
m_pendingClearDrawFboExternalIndex);
if (!frame.isCommandRecording) {
m_frameContext.BeginCommandRecording();
if (m_uniformDescriptorBinder) {
m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
m_pendingClearMask &= ~GL_COLOR_BUFFER_BIT;
}
if ((m_pendingClearMask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
MGLOG_W("Present: depth/stencil clear on non-default FBO is not implemented yet (FBO %u)",
m_pendingClearDrawFboExternalIndex);
m_pendingClearMask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
VkCommandBuffer commandBuffer = frame.commandBuffer;
if (m_isMainRenderPassActive) {
vkCmdEndRenderPass(commandBuffer);
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
}
if (targetIsDefault) {
TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired);
TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired);
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = m_renderPassLoad;
renderPassInfo.framebuffer = m_framebuffers[m_imageIndexAcquired];
renderPassInfo.renderArea.offset = {0, 0};
renderPassInfo.renderArea.extent = m_swapchainObject.GetExtent();
renderPassInfo.clearValueCount = 0;
renderPassInfo.pClearValues = nullptr;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
m_isMainRenderPassActive = true;
m_activeRenderPass = renderPassInfo.renderPass;
m_activeRenderExtent = renderPassInfo.renderArea.extent;
m_activeDepthStencilFormat = m_depthStencilFormat;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
return true;
}
if (!m_framebufferManager || !MG_State::pGLContext) {
return false;
}
const auto pendingFbo = MG_State::pGLContext->GetFramebufferObject(targetFboExternalIndex);
if (!pendingFbo) {
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(targetFboExternalIndex, *pendingFbo)) {
return false;
}
if (!m_framebufferManager->TransitionOffscreenColorToAttachment(commandBuffer, targetFboExternalIndex)) {
return false;
}
VkRenderPass offscreenRenderPass = VK_NULL_HANDLE;
VkFramebuffer offscreenFramebuffer = VK_NULL_HANDLE;
VkExtent2D offscreenExtent{};
VkFormat offscreenDepthStencilFormat = VK_FORMAT_UNDEFINED;
if (!m_framebufferManager->GetOffscreenRenderTarget(targetFboExternalIndex, offscreenRenderPass,
offscreenFramebuffer, offscreenExtent,
offscreenDepthStencilFormat)) {
return false;
}
const auto swapchainOldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
if (swapchainOldLayout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR &&
swapchainOldLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
VkImageMemoryBarrier presentBarrier{};
presentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
presentBarrier.srcAccessMask = 0;
presentBarrier.dstAccessMask = 0;
presentBarrier.oldLayout = swapchainOldLayout;
presentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
presentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
presentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
presentBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
presentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
presentBarrier.subresourceRange.baseMipLevel = 0;
presentBarrier.subresourceRange.levelCount = 1;
presentBarrier.subresourceRange.baseArrayLayer = 0;
presentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
VkRenderPassBeginInfo offscreenPassInfo{};
offscreenPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
offscreenPassInfo.renderPass = offscreenRenderPass;
offscreenPassInfo.framebuffer = offscreenFramebuffer;
offscreenPassInfo.renderArea.offset = {0, 0};
offscreenPassInfo.renderArea.extent = offscreenExtent;
offscreenPassInfo.clearValueCount = 0;
offscreenPassInfo.pClearValues = nullptr;
vkCmdBeginRenderPass(commandBuffer, &offscreenPassInfo, VK_SUBPASS_CONTENTS_INLINE);
m_isMainRenderPassActive = true;
m_activeRenderPass = offscreenRenderPass;
m_activeRenderExtent = offscreenExtent;
m_activeDepthStencilFormat = offscreenDepthStencilFormat;
m_activeRenderTargetIsDefault = false;
m_activeDrawFboExternalIndex = targetFboExternalIndex;
return true;
};
while (!m_pendingClears.empty()) {
auto pendingIt = m_pendingClears.end();
if (m_isMainRenderPassActive) {
const Uint64 activeKey =
BuildPendingClearKey(m_activeDrawFboExternalIndex, m_activeRenderTargetIsDefault);
pendingIt = m_pendingClears.find(activeKey);
}
if (pendingIt == m_pendingClears.end()) {
pendingIt = m_pendingClears.begin();
}
const auto pendingTarget = pendingIt->second;
if (!activateTarget(pendingTarget.targetsDefaultFramebuffer, pendingTarget.drawFboExternalIndex)) {
MGLOG_W("Present: dropping pending clear because target activation failed (FBO %u, default=%d)",
pendingTarget.drawFboExternalIndex, pendingTarget.targetsDefaultFramebuffer);
m_pendingClears.erase(pendingIt);
continue;
}
const Uint64 activeKey =
BuildPendingClearKey(m_activeDrawFboExternalIndex, m_activeRenderTargetIsDefault);
auto activePendingIt = m_pendingClears.find(activeKey);
if (activePendingIt == m_pendingClears.end()) {
continue;
}
if ((activePendingIt->second.mask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(frame.commandBuffer, activePendingIt->second.color);
activePendingIt->second.mask &= ~GL_COLOR_BUFFER_BIT;
}
if ((activePendingIt->second.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(frame.commandBuffer, activePendingIt->second.mask,
activePendingIt->second.depth, activePendingIt->second.stencil);
activePendingIt->second.mask &= ~(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
if (activePendingIt->second.mask == 0) {
m_pendingClears.erase(activePendingIt);
}
} else {
MGLOG_W("Present: pending default-FBO clear cannot execute because no render pass is active");
m_pendingClearMask = 0;
}
} else if (m_pendingClearMask != 0) {
EnsureFrameRecordingStarted();
}
EndFrameRecordingIfNeeded();
@@ -1312,6 +1522,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return 0;
}
Uint64 VulkanRenderer::BuildPendingClearKey(Uint drawFboExternalIndex, Bool targetsDefaultFramebuffer) {
return (static_cast<Uint64>(targetsDefaultFramebuffer ? 1 : 0) << 63) |
static_cast<Uint64>(drawFboExternalIndex);
}
Bool VulkanRenderer::HasStencilComponent(VkFormat format) {
return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
}
@@ -1630,6 +1845,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_frameContext.GetCurrent().hasCommandBufferRecorded = false;
}
m_isMainRenderPassActive = false;
m_activeRenderPass = VK_NULL_HANDLE;
m_activeRenderExtent = {0, 0};
m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
m_activeRenderTargetIsDefault = true;
m_activeDrawFboExternalIndex = 0;
m_pendingClears.clear();
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -74,6 +74,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void RecreateSwapchain();
private:
struct PendingClearState {
GLbitfield mask = 0;
VkClearColorValue color = {{0.0f, 0.0f, 0.0f, 1.0f}};
Float depth = 1.0f;
Uint32 stencil = 0;
Uint drawFboExternalIndex = 0;
Bool targetsDefaultFramebuffer = true;
};
struct QueueFamilyIndices {
Int32 graphicsFamily = -1;
Int32 presentFamily = -1;
@@ -129,13 +138,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint m_imageIndexAcquired = 0;
FrameContext m_frameContext;
GLbitfield m_pendingClearMask = 0;
VkClearColorValue m_pendingClearColor = {{0.0f, 0.0f, 0.0f, 1.0f}};
Float m_pendingClearDepth = 1.0f;
Uint32 m_pendingClearStencil = 0;
Uint m_pendingClearDrawFboExternalIndex = 0;
Bool m_pendingClearTargetsDefaultFramebuffer = true;
UnorderedMap<Uint64, PendingClearState> m_pendingClears;
Bool m_isMainRenderPassActive = false;
VkRenderPass m_activeRenderPass = VK_NULL_HANDLE;
VkExtent2D m_activeRenderExtent = {0, 0};
VkFormat m_activeDepthStencilFormat = VK_FORMAT_UNDEFINED;
Bool m_activeRenderTargetIsDefault = true;
Uint m_activeDrawFboExternalIndex = 0;
UniquePtr<PipelineFactory> m_pipelineFactory;
UniquePtr<ProgramFactory> m_programFactory;
@@ -167,7 +176,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void TransitionDepthStencilImageToAttachment(VkCommandBuffer commandBuffer, Uint32 imageIndex);
void RecordColorClear(VkCommandBuffer commandBuffer, const VkClearColorValue& clearColor);
void RecordDepthStencilClear(VkCommandBuffer commandBuffer, GLbitfield mask, Float depth, Uint32 stencil);
Bool RecordOffscreenColorClear(VkCommandBuffer commandBuffer);
void EndFrameRecordingIfNeeded();
Bool UploadAndBindVertexStreams(
const VertexInputStateFactory::BackendVertexInputState& vertexInputState,
@@ -185,6 +193,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static Bool IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device);
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, const PhysicalDevice& compareWithDevice, PhysicalDevice& outBetterDevice);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static Uint64 BuildPendingClearKey(Uint drawFboExternalIndex, Bool targetsDefaultFramebuffer);
static Bool HasStencilComponent(VkFormat format);
static VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice);
static constexpr VkDynamicState s_dynamicStates[] = {