mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-07 19:58:32 +09:00
[Feat] (MG_Backend/DirectVulkan): per-fbo pending clear state
This commit is contained in:
@@ -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[] = {
|
||||
|
||||
Reference in New Issue
Block a user