[Feat|Fix] (MG_Backend/DirectVulkan): Impl (Multi)DrawElementsBaseVertex. Fix Blitframebuffer.

This commit is contained in:
BZLZHH
2026-02-21 17:37:50 +08:00
parent 07b7f7eba5
commit d84b4da766
5 changed files with 875 additions and 161 deletions
@@ -17,10 +17,165 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {} void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {} void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {} void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {} void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::DrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::DrawElementsBaseVertex called with null GL context");
if (count < 0) {
MGLOG_W("DrawElementsBaseVertex skipped: count (%d) must be non-negative", count);
return;
}
if (count == 0) {
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("DrawElementsBaseVertex skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("DrawElementsBaseVertex skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("DrawElementsBaseVertex skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("DrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("DrawElementsBaseVertex skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const SizeT byteOffset = reinterpret_cast<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(count) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("DrawElementsBaseVertex skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)",
byteOffset, requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count;
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
payload.baseVertex = basevertex;
pVulkanRenderer->DrawElements(payload);
}
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices, void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) {} GLsizei drawcount, const GLint* basevertex) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::MultiDrawElementsBaseVertex called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::MultiDrawElementsBaseVertex called with null GL context");
if (drawcount < 0) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: drawcount (%d) must be non-negative", drawcount);
return;
}
if (drawcount == 0) {
return;
}
if (!count || !indices || !basevertex) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: count/indices/basevertex pointer is null");
return;
}
if (mode != GL_TRIANGLES) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: primitive mode %u is not supported yet", mode);
return;
}
SizeT indexSize = 0;
switch (type) {
case GL_UNSIGNED_SHORT:
indexSize = sizeof(Uint16);
break;
case GL_UNSIGNED_INT:
indexSize = sizeof(Uint32);
break;
default:
MGLOG_W("MultiDrawElementsBaseVertex skipped: index type %u is not supported yet", type);
return;
}
const auto vao = MG_State::pGLContext->GetBoundVertexArray();
if (!vao) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound VAO");
return;
}
const auto indexBuffer = vao->GetIndexBufferBindingSlot().GetBoundObject();
if (!indexBuffer) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: no bound ELEMENT_ARRAY_BUFFER");
return;
}
const auto indexData = indexBuffer->GetDataReadOnly();
if (!indexData || indexData->empty()) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: ELEMENT_ARRAY_BUFFER has no data");
return;
}
const auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
Vector<DrawElementPayload> payloads;
payloads.reserve(static_cast<SizeT>(drawcount));
for (GLsizei i = 0; i < drawcount; ++i) {
if (count[i] < 0) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: count[%d] (%d) must be non-negative", i, count[i]);
return;
}
if (count[i] == 0) {
continue;
}
const SizeT byteOffset = reinterpret_cast<SizeT>(indices[i]);
const SizeT requiredBytes = static_cast<SizeT>(count[i]) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("MultiDrawElementsBaseVertex skipped: draw[%d] index range out of bounds (offset=%zu, "
"size=%zu, buffer=%zu)",
i, byteOffset, requiredBytes, indexBuffer->GetSize());
return;
}
DrawElementPayload payload{};
payload.drawArray.mode = mode;
payload.drawArray.first = 0;
payload.drawArray.count = count[i];
payload.drawArray.program = currentProgram ? currentProgram.get() : nullptr;
payload.drawArray.vertexArray = vao.get();
payload.indexType = type;
payload.indexByteOffset = byteOffset;
payload.baseVertex = basevertex[i];
payloads.push_back(payload);
}
if (payloads.empty()) {
return;
}
pVulkanRenderer->MultiDrawElements(payloads);
}
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {} void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {} void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
@@ -270,20 +425,24 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer"); MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::BlitFramebuffer called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::BlitFramebuffer called with null GL context"); MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::BlitFramebuffer called with null GL context");
const auto readFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject(); const auto& readFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read);
const auto drawFbo = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject(); const auto& drawFboSlot = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw);
const auto readFbo = readFboSlot.GetBoundObject();
const auto drawFbo = drawFboSlot.GetBoundObject();
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo; const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr; const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool readIsDefault = (readFbo == defaultFbo) || (readFbo == nullptr && defaultFbo != nullptr); const Bool readIsDefault = (readFbo == defaultFbo) || (readFbo == nullptr && defaultFbo != nullptr);
const Bool drawIsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr); const Bool drawIsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
Uint readFboExternalIndex = 0; Uint readFboExternalIndex = 0;
Uint drawFboExternalIndex = 0;
if (readFbo) { if (readFbo) {
readFboExternalIndex = readFbo->GetExternalIndex(); readFboExternalIndex = readFbo->GetExternalIndex();
} else if (defaultFbo) { } else if (defaultFbo) {
readFboExternalIndex = defaultFbo->GetExternalIndex(); readFboExternalIndex = defaultFbo->GetExternalIndex();
} }
Uint drawFboExternalIndex = 0;
if (drawFbo) { if (drawFbo) {
drawFboExternalIndex = drawFbo->GetExternalIndex(); drawFboExternalIndex = drawFbo->GetExternalIndex();
} else if (defaultFbo) { } else if (defaultFbo) {
@@ -53,13 +53,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; return false;
} }
auto& target = it->second; auto& target = it->second;
if (!TransitionImageLayout(commandBuffer, if (!TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
target.image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
target.layout,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_IMAGE_ASPECT_COLOR_BIT)) { VK_IMAGE_ASPECT_COLOR_BIT)) {
return false; return false;
@@ -75,16 +70,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
} }
return TransitionImageLayout(commandBuffer, return TransitionImageLayout(
target.depthStencilImage, commandBuffer, target.depthStencilImage, target.depthStencilLayout,
target.depthStencilLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, 0,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, aspectMask);
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0,
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
aspectMask);
} }
Bool VkFramebufferManager::TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Bool VkFramebufferManager::TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer,
@@ -96,37 +86,121 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return false; return false;
} }
auto& target = it->second; auto& target = it->second;
return TransitionImageLayout(commandBuffer, return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
target.image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
target.layout, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, }
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, Bool VkFramebufferManager::TransitionOffscreenColorToTransferDst(VkCommandBuffer commandBuffer,
0, Uint glFboExternalIndex) {
VK_ACCESS_TRANSFER_READ_BIT, auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToTransferDst skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
}
Bool VkFramebufferManager::TransitionOffscreenColorToGeneral(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenColorToGeneral skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
return TransitionImageLayout(commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT,
VK_IMAGE_ASPECT_COLOR_BIT); VK_IMAGE_ASPECT_COLOR_BIT);
} }
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToTransferSrc(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToTransferSrc skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
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;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_READ_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToTransferDst(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToTransferDst skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
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;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenDepthStencilToGeneral(VkCommandBuffer commandBuffer,
Uint glFboExternalIndex) {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end()) {
MGLOG_W("VkFramebufferManager::TransitionOffscreenDepthStencilToGeneral skipped: FBO %u not found",
glFboExternalIndex);
return false;
}
auto& target = it->second;
if (target.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
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;
}
return TransitionImageLayout(commandBuffer, target.depthStencilImage, target.depthStencilLayout,
VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, aspectMask);
}
Bool VkFramebufferManager::TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer, Bool VkFramebufferManager::TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer,
Uint textureExternalIndex) { Uint textureExternalIndex) {
for (auto& [_, target] : m_offscreenColorTargets) { for (auto& [_, target] : m_offscreenColorTargets) {
if (target.colorTextureExternalIndex != textureExternalIndex || target.image == VK_NULL_HANDLE) { if (target.colorTextureExternalIndex != textureExternalIndex || target.image == VK_NULL_HANDLE) {
continue; continue;
} }
const Bool fromUndefined = (target.layout == VK_IMAGE_LAYOUT_UNDEFINED); const Bool fromUndefined = (target.layout == VK_IMAGE_LAYOUT_UNDEFINED);
return TransitionImageLayout(commandBuffer, return TransitionImageLayout(
target.image, commandBuffer, target.image, target.layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
target.layout, fromUndefined ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, : (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT),
fromUndefined ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
: (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | fromUndefined ? 0 : (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT),
VK_PIPELINE_STAGE_TRANSFER_BIT), VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT);
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
fromUndefined ? 0
: (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT),
VK_ACCESS_SHADER_READ_BIT,
VK_IMAGE_ASPECT_COLOR_BIT);
} }
return false; return false;
} }
@@ -142,7 +216,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true; return true;
} }
Bool VkFramebufferManager::GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const { Bool VkFramebufferManager::GetOffscreenDepthStencilImage(Uint glFboExternalIndex, VkImage& outImage,
VkExtent2D& outExtent, VkFormat& outFormat) const {
auto it = m_offscreenColorTargets.find(glFboExternalIndex);
if (it == m_offscreenColorTargets.end() || it->second.depthStencilImage == VK_NULL_HANDLE) {
return false;
}
outImage = it->second.depthStencilImage;
outExtent = it->second.extent;
outFormat = it->second.depthStencilFormat;
return true;
}
Bool VkFramebufferManager::GetOffscreenColorViewByTexture(Uint textureExternalIndex,
VkImageView& outImageView) const {
for (const auto& [_, target] : m_offscreenColorTargets) { for (const auto& [_, target] : m_offscreenColorTargets) {
if (target.colorTextureExternalIndex != textureExternalIndex || target.imageView == VK_NULL_HANDLE) { if (target.colorTextureExternalIndex != textureExternalIndex || target.imageView == VK_NULL_HANDLE) {
continue; continue;
@@ -170,8 +257,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool VkFramebufferManager::RecreateOffscreenColorTarget( Bool VkFramebufferManager::RecreateOffscreenColorTarget(
OffscreenColorTarget& target, const MG_State::GLState::FramebufferObject& glFbo, OffscreenColorTarget& target, const MG_State::GLState::FramebufferObject& glFbo,
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment, const MG_State::GLState::FramebufferAttachmentObject& colorAttachment, Uint16 glObjectVersion) {
Uint16 glObjectVersion) {
DestroyOffscreenColorTarget(target); DestroyOffscreenColorTarget(target);
const auto size = colorAttachment.GetSize(); const auto size = colorAttachment.GetSize();
@@ -198,7 +284,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | imageInfo.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &target.image), "vkCreateImage(offscreen color)"); VK_VERIFY(vkCreateImage(m_device, &imageInfo, nullptr, &target.image), "vkCreateImage(offscreen color)");
@@ -209,7 +295,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkMemoryAllocateInfo allocInfo{}; VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memoryRequirements.size; allocInfo.allocationSize = memoryRequirements.size;
allocInfo.memoryTypeIndex = FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); allocInfo.memoryTypeIndex =
FindMemoryType(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &target.memory), "vkAllocateMemory(offscreen color)"); VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &target.memory), "vkAllocateMemory(offscreen color)");
VK_VERIFY(vkBindImageMemory(m_device, target.image, target.memory, 0), "vkBindImageMemory(offscreen color)"); VK_VERIFY(vkBindImageMemory(m_device, target.image, target.memory, 0), "vkBindImageMemory(offscreen color)");
@@ -223,16 +310,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewInfo.subresourceRange.levelCount = 1; viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1; viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &target.imageView), "vkCreateImageView(offscreen color)"); VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &target.imageView),
"vkCreateImageView(offscreen color)");
const auto& depthAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Depth); const auto& depthAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Depth);
const auto& stencilAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Stencil); const auto& stencilAttachment = glFbo.GetAttachment(FramebufferAttachmentType::Stencil);
const Bool requestedDepthStencil = const Bool requestedDepthStencil = (depthAttachment.IsValid() && !depthAttachment.IsEmpty()) ||
(depthAttachment.IsValid() && !depthAttachment.IsEmpty()) || (stencilAttachment.IsValid() && !stencilAttachment.IsEmpty());
(stencilAttachment.IsValid() && !stencilAttachment.IsEmpty());
VkFormat depthStencilFormat = ResolveDepthStencilFormat(depthAttachment, stencilAttachment); VkFormat depthStencilFormat = ResolveDepthStencilFormat(depthAttachment, stencilAttachment);
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT) { if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT) {
depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT}); depthStencilFormat =
FindSupportedDepthStencilFormat({VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT});
} else if (depthStencilFormat == VK_FORMAT_D32_SFLOAT) { } else if (depthStencilFormat == VK_FORMAT_D32_SFLOAT) {
depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM}); depthStencilFormat = FindSupportedDepthStencilFormat({VK_FORMAT_D32_SFLOAT, VK_FORMAT_D16_UNORM});
} }
@@ -272,7 +360,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
"vkBindImageMemory(offscreen depth/stencil)"); "vkBindImageMemory(offscreen depth/stencil)");
VkImageAspectFlags depthAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; VkImageAspectFlags depthAspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT || depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) { if (depthStencilFormat == VK_FORMAT_D24_UNORM_S8_UINT ||
depthStencilFormat == VK_FORMAT_D32_SFLOAT_S8_UINT) {
depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT; depthAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
} }
VkImageViewCreateInfo depthViewInfo{}; VkImageViewCreateInfo depthViewInfo{};
@@ -365,9 +454,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED; target.depthStencilLayout = VK_IMAGE_LAYOUT_UNDEFINED;
target.depthStencilFormat = depthStencilFormat; target.depthStencilFormat = depthStencilFormat;
target.glObjectVersion = glObjectVersion; target.glObjectVersion = glObjectVersion;
target.colorTextureExternalIndex = target.colorTextureExternalIndex = (colorAttachment.IsTexture() && colorAttachment.GetTexture())
(colorAttachment.IsTexture() && colorAttachment.GetTexture()) ? colorAttachment.GetTexture()->GetExternalIndex() ? colorAttachment.GetTexture()->GetExternalIndex()
: 0; : 0;
return true; return true;
} }
@@ -413,15 +502,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
target.colorTextureExternalIndex = 0; target.colorTextureExternalIndex = 0;
} }
Bool VkFramebufferManager::TransitionImageLayout(VkCommandBuffer commandBuffer, Bool VkFramebufferManager::TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image,
VkImage image, VkImageLayout& trackedLayout, VkImageLayout newLayout,
VkImageLayout& trackedLayout,
VkImageLayout newLayout,
VkPipelineStageFlags srcStageMask, VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask) {
VkAccessFlags dstAccessMask,
VkImageAspectFlags aspectMask) {
if (image == VK_NULL_HANDLE) { if (image == VK_NULL_HANDLE) {
return false; return false;
} }
@@ -29,11 +29,19 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool EnsureOffscreenColorTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo); Bool EnsureOffscreenColorTarget(Uint glFboExternalIndex, const MG_State::GLState::FramebufferObject& glFbo);
Bool TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer, Uint glFboExternalIndex); Bool TransitionOffscreenColorToAttachment(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex); Bool TransitionOffscreenColorToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToTransferDst(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorToGeneral(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToTransferSrc(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToTransferDst(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenDepthStencilToGeneral(VkCommandBuffer commandBuffer, Uint glFboExternalIndex);
Bool TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer, Uint textureExternalIndex); Bool TransitionOffscreenColorTextureToShaderRead(VkCommandBuffer commandBuffer, Uint textureExternalIndex);
Bool GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent) const; Bool GetOffscreenColorImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent) const;
Bool GetOffscreenDepthStencilImage(Uint glFboExternalIndex, VkImage& outImage, VkExtent2D& outExtent,
VkFormat& outFormat) const;
Bool GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const; Bool GetOffscreenColorViewByTexture(Uint textureExternalIndex, VkImageView& outImageView) const;
Bool GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass, VkFramebuffer& outFramebuffer, Bool GetOffscreenRenderTarget(Uint glFboExternalIndex, VkRenderPass& outRenderPass,
VkExtent2D& outExtent, VkFormat& outDepthStencilFormat) const; VkFramebuffer& outFramebuffer, VkExtent2D& outExtent,
VkFormat& outDepthStencilFormat) const;
private: private:
struct OffscreenColorTarget { struct OffscreenColorTarget {
@@ -59,19 +67,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const MG_State::GLState::FramebufferAttachmentObject& colorAttachment, const MG_State::GLState::FramebufferAttachmentObject& colorAttachment,
Uint16 glObjectVersion); Uint16 glObjectVersion);
void DestroyOffscreenColorTarget(OffscreenColorTarget& target); void DestroyOffscreenColorTarget(OffscreenColorTarget& target);
Bool TransitionImageLayout(VkCommandBuffer commandBuffer, Bool TransitionImageLayout(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout& trackedLayout,
VkImage image, VkImageLayout newLayout, VkPipelineStageFlags srcStageMask,
VkImageLayout& trackedLayout, VkPipelineStageFlags dstStageMask, VkAccessFlags srcAccessMask,
VkImageLayout newLayout, VkAccessFlags dstAccessMask, VkImageAspectFlags aspectMask);
VkPipelineStageFlags srcStageMask,
VkPipelineStageFlags dstStageMask,
VkAccessFlags srcAccessMask,
VkAccessFlags dstAccessMask,
VkImageAspectFlags aspectMask);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const; Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static VkFormat ResolveColorFormat(const MG_State::GLState::FramebufferAttachmentObject& colorAttachment); static VkFormat ResolveColorFormat(const MG_State::GLState::FramebufferAttachmentObject& colorAttachment);
static VkFormat ResolveDepthStencilFormat(const MG_State::GLState::FramebufferAttachmentObject& depthAttachment, static VkFormat ResolveDepthStencilFormat(
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment); const MG_State::GLState::FramebufferAttachmentObject& depthAttachment,
const MG_State::GLState::FramebufferAttachmentObject& stencilAttachment);
VkFormat FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const; VkFormat FindSupportedDepthStencilFormat(const Vector<VkFormat>& candidates) const;
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
@@ -998,7 +998,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
vkCmdBindIndexBuffer(commandBuffer, frameIndexUploadBuffer.GetHandle(), writeOffset, vkIndexType); vkCmdBindIndexBuffer(commandBuffer, frameIndexUploadBuffer.GetHandle(), writeOffset, vkIndexType);
vkCmdDrawIndexed(commandBuffer, static_cast<Uint32>(payload.drawArray.count), 1, 0, 0, 0); vkCmdDrawIndexed(commandBuffer, static_cast<Uint32>(payload.drawArray.count), 1, 0,
static_cast<Int32>(payload.baseVertex), 0);
} }
void VulkanRenderer::MultiDrawElements(const Vector<DrawElementPayload>& payloads) { void VulkanRenderer::MultiDrawElements(const Vector<DrawElementPayload>& payloads) {
@@ -1060,6 +1061,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
SizeT sourceOffset = 0; SizeT sourceOffset = 0;
SizeT sourceByteCount = 0; SizeT sourceByteCount = 0;
Uint32 firstIndex = 0; Uint32 firstIndex = 0;
Int32 vertexOffset = 0;
}; };
Vector<PreparedDraw> preparedDraws; Vector<PreparedDraw> preparedDraws;
preparedDraws.reserve(payloads.size()); preparedDraws.reserve(payloads.size());
@@ -1086,6 +1088,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
draw.sourceOffset = payload.indexByteOffset; draw.sourceOffset = payload.indexByteOffset;
draw.sourceByteCount = drawByteCount; draw.sourceByteCount = drawByteCount;
draw.firstIndex = firstIndex; draw.firstIndex = firstIndex;
draw.vertexOffset = static_cast<Int32>(payload.baseVertex);
preparedDraws.push_back(draw); preparedDraws.push_back(draw);
firstIndex += draw.indexCount; firstIndex += draw.indexCount;
@@ -1189,7 +1192,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
indirectCommands[i].indexCount = preparedDraws[i].indexCount; indirectCommands[i].indexCount = preparedDraws[i].indexCount;
indirectCommands[i].instanceCount = 1; indirectCommands[i].instanceCount = 1;
indirectCommands[i].firstIndex = preparedDraws[i].firstIndex; indirectCommands[i].firstIndex = preparedDraws[i].firstIndex;
indirectCommands[i].vertexOffset = 0; indirectCommands[i].vertexOffset = preparedDraws[i].vertexOffset;
indirectCommands[i].firstInstance = 0; indirectCommands[i].firstInstance = 0;
} }
@@ -1226,26 +1229,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
// Fallback // Fallback
for (const auto& draw : preparedDraws) { for (const auto& draw : preparedDraws) {
vkCmdDrawIndexed(commandBuffer, draw.indexCount, 1, draw.firstIndex, 0, 0); vkCmdDrawIndexed(commandBuffer, draw.indexCount, 1, draw.firstIndex, draw.vertexOffset, 0);
} }
} }
Bool VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, Bool VulkanRenderer::BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter,
Uint readFboExternalIndex, Uint drawFboExternalIndex, Uint readFboExternalIndex, Uint drawFboExternalIndex,
Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer) { Bool readIsDefaultFramebuffer, Bool drawIsDefaultFramebuffer) {
if ((mask & GL_COLOR_BUFFER_BIT) == 0 || (mask & ~GL_COLOR_BUFFER_BIT) != 0) { if ((mask & (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) == 0) {
MGLOG_D("BlitFramebuffer skipped: only GL_COLOR_BUFFER_BIT is supported in Vulkan backend for now"); MGLOG_D("BlitFramebuffer skipped: empty mask");
return false; return false;
} }
if (readIsDefaultFramebuffer || !drawIsDefaultFramebuffer) {
MGLOG_D("BlitFramebuffer skipped: currently only read=offscreen FBO and draw=default FBO is supported"); VkFilter vkFilter = VK_FILTER_NEAREST;
if (filter == GL_NEAREST) {
vkFilter = VK_FILTER_NEAREST;
} else if (filter == GL_LINEAR) {
vkFilter = VK_FILTER_LINEAR;
} else {
MGLOG_W("BlitFramebuffer skipped: filter %u is not supported", filter);
return false; return false;
} }
auto& frame = m_frameContext.GetCurrent(); auto& frame = m_frameContext.GetCurrent();
if (frame.hasCommandBufferRecorded) { if (frame.hasCommandBufferRecorded) {
MGLOG_D("BlitFramebuffer skipped: current frame command buffer is already finalized"); MGLOG_D("BlitFramebuffer skipped: current frame command buffer already finalized");
return false; return false;
} }
@@ -1270,89 +1280,544 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_activeDrawFboExternalIndex = 0; m_activeDrawFboExternalIndex = 0;
} }
if (!m_framebufferManager || !MG_State::pGLContext) { if (!m_framebufferManager && (!readIsDefaultFramebuffer || !drawIsDefaultFramebuffer)) {
MGLOG_D("BlitFramebuffer skipped: framebuffer manager is unavailable"); MGLOG_W("BlitFramebuffer skipped: offscreen framebuffer manager not available");
return false; return false;
} }
(void)drawFboExternalIndex; auto consumePendingClearForTarget = [&](Bool targetIsDefault, Uint targetFboExternalIndex) {
const auto readFbo = MG_State::pGLContext->GetFramebufferObject(readFboExternalIndex); const Uint64 pendingKey = BuildPendingClearKey(targetFboExternalIndex, targetIsDefault);
if (!readFbo) { auto pendingIt = m_pendingClears.find(pendingKey);
MGLOG_D("BlitFramebuffer skipped: read FBO %u not found", readFboExternalIndex); if (pendingIt == m_pendingClears.end()) {
return false; return;
} }
if (!m_framebufferManager->EnsureOffscreenColorTarget(readFboExternalIndex, *readFbo)) {
return false; const PendingClearState pending = pendingIt->second;
} m_pendingClears.erase(pendingIt);
if (!m_framebufferManager->TransitionOffscreenColorToTransferSrc(commandBuffer, readFboExternalIndex)) {
return false; const auto prevExtent = m_activeRenderExtent;
const auto prevDepthStencilFormat = m_activeDepthStencilFormat;
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_activeRenderExtent = renderPassInfo.renderArea.extent;
m_activeDepthStencilFormat = m_depthStencilFormat;
if ((pending.mask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(commandBuffer, pending.color);
}
if ((pending.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(commandBuffer, pending.mask, pending.depth, pending.stencil);
}
vkCmdEndRenderPass(commandBuffer);
} else if (m_framebufferManager && MG_State::pGLContext) {
const auto targetFbo = MG_State::pGLContext->GetFramebufferObject(targetFboExternalIndex);
if (targetFbo && m_framebufferManager->EnsureOffscreenColorTarget(targetFboExternalIndex, *targetFbo) &&
m_framebufferManager->TransitionOffscreenColorToAttachment(commandBuffer, targetFboExternalIndex)) {
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)) {
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_activeRenderExtent = offscreenPassInfo.renderArea.extent;
m_activeDepthStencilFormat = offscreenDepthStencilFormat;
if ((pending.mask & GL_COLOR_BUFFER_BIT) != 0) {
RecordColorClear(commandBuffer, pending.color);
}
if ((pending.mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
RecordDepthStencilClear(commandBuffer, pending.mask, pending.depth, pending.stencil);
}
vkCmdEndRenderPass(commandBuffer);
}
}
}
m_activeRenderExtent = prevExtent;
m_activeDepthStencilFormat = prevDepthStencilFormat;
};
consumePendingClearForTarget(drawIsDefaultFramebuffer, drawFboExternalIndex);
auto selectSrcStageAccess = [](VkImageLayout layout, VkPipelineStageFlags& outStage, VkAccessFlags& outAccess) {
switch (layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
outStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
outAccess = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
outStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
outAccess = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
default:
outStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
outAccess = 0;
break;
}
};
auto transitionSwapchainLayout = [&](VkImageLayout newLayout, VkPipelineStageFlags dstStage,
VkAccessFlags dstAccess) {
const auto oldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired);
if (oldLayout == newLayout) {
return;
}
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccess = 0;
selectSrcStageAccess(oldLayout, srcStage, srcAccess);
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, newLayout);
};
auto clampCoord = [](GLint value, GLint minValue, GLint maxValue) -> GLint {
if (value < minValue) {
return minValue;
}
if (value > maxValue) {
return maxValue;
}
return value;
};
const Bool wantColor = (mask & GL_COLOR_BUFFER_BIT) != 0;
if (wantColor) {
VkImage srcImage = VK_NULL_HANDLE;
VkImage dstImage = VK_NULL_HANDLE;
VkExtent2D srcExtent{};
VkExtent2D dstExtent{};
if (readIsDefaultFramebuffer) {
srcImage = m_swapchainObject.GetImage(m_imageIndexAcquired);
srcExtent = m_swapchainObject.GetExtent();
} else {
if (!MG_State::pGLContext) {
MGLOG_W("BlitFramebuffer skipped: GL context unavailable for read FBO");
return false;
}
const auto readFbo = MG_State::pGLContext->GetFramebufferObject(readFboExternalIndex);
if (!readFbo) {
MGLOG_W("BlitFramebuffer skipped: read FBO %u not found", readFboExternalIndex);
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(readFboExternalIndex, *readFbo)) {
MGLOG_W("BlitFramebuffer skipped: failed to materialize read FBO %u", readFboExternalIndex);
return false;
}
if (!m_framebufferManager->GetOffscreenColorImage(readFboExternalIndex, srcImage, srcExtent)) {
MGLOG_W("BlitFramebuffer skipped: read FBO %u has no color target", readFboExternalIndex);
return false;
}
}
if (drawIsDefaultFramebuffer) {
dstImage = m_swapchainObject.GetImage(m_imageIndexAcquired);
dstExtent = m_swapchainObject.GetExtent();
} else {
if (!MG_State::pGLContext) {
MGLOG_W("BlitFramebuffer skipped: GL context unavailable for draw FBO");
return false;
}
const auto drawFbo = MG_State::pGLContext->GetFramebufferObject(drawFboExternalIndex);
if (!drawFbo) {
MGLOG_W("BlitFramebuffer skipped: draw FBO %u not found", drawFboExternalIndex);
return false;
}
if (!m_framebufferManager->EnsureOffscreenColorTarget(drawFboExternalIndex, *drawFbo)) {
MGLOG_W("BlitFramebuffer skipped: failed to materialize draw FBO %u", drawFboExternalIndex);
return false;
}
if (!m_framebufferManager->GetOffscreenColorImage(drawFboExternalIndex, dstImage, dstExtent)) {
MGLOG_W("BlitFramebuffer skipped: draw FBO %u has no color target", drawFboExternalIndex);
return false;
}
}
if (srcImage == VK_NULL_HANDLE || dstImage == VK_NULL_HANDLE) {
MGLOG_W("BlitFramebuffer skipped: missing source/destination image");
return false;
}
const Bool sameImage = (srcImage == dstImage);
if (readIsDefaultFramebuffer) {
if (sameImage) {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT);
}
} else if (!m_framebufferManager->TransitionOffscreenColorToTransferSrc(commandBuffer,
readFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition read FBO %u to transfer src",
readFboExternalIndex);
return false;
}
if (drawIsDefaultFramebuffer) {
if (sameImage) {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionSwapchainLayout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
} else if (sameImage) {
if (!m_framebufferManager->TransitionOffscreenColorToGeneral(commandBuffer, drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition draw FBO %u to general",
drawFboExternalIndex);
return false;
}
} else if (!m_framebufferManager->TransitionOffscreenColorToTransferDst(commandBuffer,
drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer skipped: failed to transition draw FBO %u to transfer dst",
drawFboExternalIndex);
return false;
}
GLint srcX0Clamped = clampCoord(srcX0, 0, static_cast<GLint>(srcExtent.width));
GLint srcX1Clamped = clampCoord(srcX1, 0, static_cast<GLint>(srcExtent.width));
GLint srcY0Clamped = clampCoord(srcY0, 0, static_cast<GLint>(srcExtent.height));
GLint srcY1Clamped = clampCoord(srcY1, 0, static_cast<GLint>(srcExtent.height));
if (readIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(srcExtent.height);
srcY0Clamped = height - srcY0Clamped;
srcY1Clamped = height - srcY1Clamped;
srcY0Clamped = clampCoord(srcY0Clamped, 0, height);
srcY1Clamped = clampCoord(srcY1Clamped, 0, height);
}
GLint dstX0Clamped = clampCoord(dstX0, 0, static_cast<GLint>(dstExtent.width));
GLint dstX1Clamped = clampCoord(dstX1, 0, static_cast<GLint>(dstExtent.width));
GLint dstY0Clamped = clampCoord(dstY0, 0, static_cast<GLint>(dstExtent.height));
GLint dstY1Clamped = clampCoord(dstY1, 0, static_cast<GLint>(dstExtent.height));
if (drawIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(dstExtent.height);
dstY0Clamped = height - dstY0Clamped;
dstY1Clamped = height - dstY1Clamped;
dstY0Clamped = clampCoord(dstY0Clamped, 0, height);
dstY1Clamped = clampCoord(dstY1Clamped, 0, height);
}
if (srcX0Clamped == srcX1Clamped || srcY0Clamped == srcY1Clamped || dstX0Clamped == dstX1Clamped ||
dstY0Clamped == dstY1Clamped) {
MGLOG_D("BlitFramebuffer skipped: empty blit region");
return true;
}
VkImageBlit blit{};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = 0;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.srcOffsets[0] = {srcX0Clamped, srcY0Clamped, 0};
blit.srcOffsets[1] = {srcX1Clamped, srcY1Clamped, 1};
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = 0;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
blit.dstOffsets[0] = {dstX0Clamped, dstY0Clamped, 0};
blit.dstOffsets[1] = {dstX1Clamped, dstY1Clamped, 1};
const VkImageLayout srcLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
const VkImageLayout dstLayout = sameImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdBlitImage(commandBuffer, srcImage, srcLayout, dstImage, dstLayout, 1, &blit, vkFilter);
} }
VkImage srcImage = VK_NULL_HANDLE; auto hasStencilComponent = [](VkFormat format) -> Bool {
VkExtent2D srcExtent{}; return format == VK_FORMAT_D24_UNORM_S8_UINT || format == VK_FORMAT_D32_SFLOAT_S8_UINT;
if (!m_framebufferManager->GetOffscreenColorImage(readFboExternalIndex, srcImage, srcExtent)) { };
MGLOG_D("BlitFramebuffer skipped: offscreen image for FBO %u is unavailable", readFboExternalIndex);
return false; const Bool wantDepth = (mask & GL_DEPTH_BUFFER_BIT) != 0;
const Bool wantStencil = (mask & GL_STENCIL_BUFFER_BIT) != 0;
if (wantDepth || wantStencil) {
if (filter != GL_NEAREST) {
MGLOG_W("BlitFramebuffer: depth/stencil blit requires GL_NEAREST, overriding filter");
vkFilter = VK_FILTER_NEAREST;
}
VkImage srcDepthImage = VK_NULL_HANDLE;
VkImage dstDepthImage = VK_NULL_HANDLE;
VkExtent2D srcDepthExtent{};
VkExtent2D dstDepthExtent{};
VkFormat srcDepthFormat = VK_FORMAT_UNDEFINED;
VkFormat dstDepthFormat = VK_FORMAT_UNDEFINED;
if (readIsDefaultFramebuffer) {
if (m_depthStencilImages.empty() || m_depthStencilFormat == VK_FORMAT_UNDEFINED) {
MGLOG_W("BlitFramebuffer: default framebuffer has no depth/stencil image");
} else {
srcDepthImage = m_depthStencilImages[m_imageIndexAcquired];
srcDepthExtent = m_swapchainObject.GetExtent();
srcDepthFormat = m_depthStencilFormat;
}
} else if (m_framebufferManager) {
if (!MG_State::pGLContext) {
MGLOG_W("BlitFramebuffer: GL context unavailable for read depth/stencil FBO");
} else {
const auto readFbo = MG_State::pGLContext->GetFramebufferObject(readFboExternalIndex);
if (!readFbo) {
MGLOG_W("BlitFramebuffer: read FBO %u not found", readFboExternalIndex);
} else if (!m_framebufferManager->EnsureOffscreenColorTarget(readFboExternalIndex, *readFbo)) {
MGLOG_W("BlitFramebuffer: failed to materialize read FBO %u for depth/stencil",
readFboExternalIndex);
} else if (!m_framebufferManager->GetOffscreenDepthStencilImage(readFboExternalIndex, srcDepthImage,
srcDepthExtent, srcDepthFormat)) {
MGLOG_W("BlitFramebuffer: read FBO %u has no depth/stencil image", readFboExternalIndex);
}
}
}
if (drawIsDefaultFramebuffer) {
if (m_depthStencilImages.empty() || m_depthStencilFormat == VK_FORMAT_UNDEFINED) {
MGLOG_W("BlitFramebuffer: default framebuffer has no depth/stencil image");
} else {
dstDepthImage = m_depthStencilImages[m_imageIndexAcquired];
dstDepthExtent = m_swapchainObject.GetExtent();
dstDepthFormat = m_depthStencilFormat;
}
} else if (m_framebufferManager) {
if (!MG_State::pGLContext) {
MGLOG_W("BlitFramebuffer: GL context unavailable for draw depth/stencil FBO");
} else {
const auto drawFbo = MG_State::pGLContext->GetFramebufferObject(drawFboExternalIndex);
if (!drawFbo) {
MGLOG_W("BlitFramebuffer: draw FBO %u not found", drawFboExternalIndex);
} else if (!m_framebufferManager->EnsureOffscreenColorTarget(drawFboExternalIndex, *drawFbo)) {
MGLOG_W("BlitFramebuffer: failed to materialize draw FBO %u for depth/stencil",
drawFboExternalIndex);
} else if (!m_framebufferManager->GetOffscreenDepthStencilImage(drawFboExternalIndex, dstDepthImage,
dstDepthExtent, dstDepthFormat)) {
MGLOG_W("BlitFramebuffer: draw FBO %u has no depth/stencil image", drawFboExternalIndex);
}
}
}
if (srcDepthImage != VK_NULL_HANDLE && dstDepthImage != VK_NULL_HANDLE) {
if (srcDepthFormat != dstDepthFormat) {
MGLOG_W("BlitFramebuffer: depth/stencil format mismatch (src=%d, dst=%d), skipping", srcDepthFormat,
dstDepthFormat);
} else {
VkImageAspectFlags srcAspectMask = 0;
VkImageAspectFlags dstAspectMask = 0;
if (wantDepth) {
srcAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
dstAspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
if (wantStencil && hasStencilComponent(srcDepthFormat)) {
srcAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
dstAspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
} else if (wantStencil) {
MGLOG_W("BlitFramebuffer: stencil bit requested but format has no stencil component");
}
const VkImageAspectFlags aspectMask = srcAspectMask & dstAspectMask;
if (aspectMask != 0) {
const Bool sameDepthImage = (srcDepthImage == dstDepthImage);
const VkImageAspectFlags fullAspectMask =
VK_IMAGE_ASPECT_DEPTH_BIT |
(hasStencilComponent(srcDepthFormat) ? VK_IMAGE_ASPECT_STENCIL_BIT : 0);
auto transitionDefaultDepthStencil = [&](VkImageLayout newLayout, VkPipelineStageFlags dstStage,
VkAccessFlags dstAccess) {
if (m_depthStencilImages.empty()) {
return;
}
const auto oldLayout = m_depthStencilImageLayouts[m_imageIndexAcquired];
if (oldLayout == newLayout) {
return;
}
VkPipelineStageFlags srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccess = 0;
switch (oldLayout) {
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
srcAccess = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_READ_BIT;
break;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_GENERAL:
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
srcAccess = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
break;
default:
srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
srcAccess = 0;
break;
}
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = srcAccess;
barrier.dstAccessMask = dstAccess;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = m_depthStencilImages[m_imageIndexAcquired];
barrier.subresourceRange.aspectMask = fullAspectMask;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1,
&barrier);
m_depthStencilImageLayouts[m_imageIndexAcquired] = newLayout;
};
if (readIsDefaultFramebuffer) {
if (sameDepthImage) {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT);
}
} else if (!m_framebufferManager->TransitionOffscreenDepthStencilToTransferSrc(
commandBuffer, readFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition read depth/stencil FBO %u to transfer src",
readFboExternalIndex);
}
if (drawIsDefaultFramebuffer) {
if (sameDepthImage) {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT |
VK_ACCESS_TRANSFER_WRITE_BIT);
} else {
transitionDefaultDepthStencil(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
}
} else if (sameDepthImage) {
if (!m_framebufferManager->TransitionOffscreenDepthStencilToGeneral(commandBuffer,
drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition draw depth/stencil FBO %u to general",
drawFboExternalIndex);
}
} else if (!m_framebufferManager->TransitionOffscreenDepthStencilToTransferDst(
commandBuffer, drawFboExternalIndex)) {
MGLOG_W("BlitFramebuffer: failed to transition draw depth/stencil FBO %u to transfer dst",
drawFboExternalIndex);
}
GLint srcDX0 = clampCoord(srcX0, 0, static_cast<GLint>(srcDepthExtent.width));
GLint srcDX1 = clampCoord(srcX1, 0, static_cast<GLint>(srcDepthExtent.width));
GLint srcDY0 = clampCoord(srcY0, 0, static_cast<GLint>(srcDepthExtent.height));
GLint srcDY1 = clampCoord(srcY1, 0, static_cast<GLint>(srcDepthExtent.height));
if (readIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(srcDepthExtent.height);
srcDY0 = height - srcDY0;
srcDY1 = height - srcDY1;
srcDY0 = clampCoord(srcDY0, 0, height);
srcDY1 = clampCoord(srcDY1, 0, height);
}
GLint dstDX0 = clampCoord(dstX0, 0, static_cast<GLint>(dstDepthExtent.width));
GLint dstDX1 = clampCoord(dstX1, 0, static_cast<GLint>(dstDepthExtent.width));
GLint dstDY0 = clampCoord(dstY0, 0, static_cast<GLint>(dstDepthExtent.height));
GLint dstDY1 = clampCoord(dstY1, 0, static_cast<GLint>(dstDepthExtent.height));
if (drawIsDefaultFramebuffer) {
const GLint height = static_cast<GLint>(dstDepthExtent.height);
dstDY0 = height - dstDY0;
dstDY1 = height - dstDY1;
dstDY0 = clampCoord(dstDY0, 0, height);
dstDY1 = clampCoord(dstDY1, 0, height);
}
if (srcDX0 != srcDX1 && srcDY0 != srcDY1 && dstDX0 != dstDX1 && dstDY0 != dstDY1) {
VkImageBlit depthBlit{};
depthBlit.srcSubresource.aspectMask = aspectMask;
depthBlit.srcSubresource.mipLevel = 0;
depthBlit.srcSubresource.baseArrayLayer = 0;
depthBlit.srcSubresource.layerCount = 1;
depthBlit.srcOffsets[0] = {srcDX0, srcDY0, 0};
depthBlit.srcOffsets[1] = {srcDX1, srcDY1, 1};
depthBlit.dstSubresource.aspectMask = aspectMask;
depthBlit.dstSubresource.mipLevel = 0;
depthBlit.dstSubresource.baseArrayLayer = 0;
depthBlit.dstSubresource.layerCount = 1;
depthBlit.dstOffsets[0] = {dstDX0, dstDY0, 0};
depthBlit.dstOffsets[1] = {dstDX1, dstDY1, 1};
const VkImageLayout depthSrcLayout =
sameDepthImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
const VkImageLayout depthDstLayout =
sameDepthImage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
vkCmdBlitImage(commandBuffer, srcDepthImage, depthSrcLayout, dstDepthImage, depthDstLayout,
1, &depthBlit, vkFilter);
}
}
}
}
} }
const auto swapchainOldLayout = m_swapchainObject.GetImageLayout(m_imageIndexAcquired); if (readIsDefaultFramebuffer || drawIsDefaultFramebuffer) {
if (swapchainOldLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { transitionSwapchainLayout(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0);
VkImageMemoryBarrier toTransferDstBarrier{};
toTransferDstBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toTransferDstBarrier.srcAccessMask = 0;
toTransferDstBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toTransferDstBarrier.oldLayout = swapchainOldLayout;
toTransferDstBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toTransferDstBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDstBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toTransferDstBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
toTransferDstBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toTransferDstBarrier.subresourceRange.baseMipLevel = 0;
toTransferDstBarrier.subresourceRange.levelCount = 1;
toTransferDstBarrier.subresourceRange.baseArrayLayer = 0;
toTransferDstBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toTransferDstBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
} }
VkImageBlit blitRegion{};
blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blitRegion.srcSubresource.mipLevel = 0;
blitRegion.srcSubresource.baseArrayLayer = 0;
blitRegion.srcSubresource.layerCount = 1;
blitRegion.srcOffsets[0] = {srcX0, srcY0, 0};
blitRegion.srcOffsets[1] = {srcX1, srcY1, 1};
blitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blitRegion.dstSubresource.mipLevel = 0;
blitRegion.dstSubresource.baseArrayLayer = 0;
blitRegion.dstSubresource.layerCount = 1;
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
vkCmdBlitImage(commandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_swapchainObject.GetImage(m_imageIndexAcquired), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&blitRegion, (filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST));
VkImageMemoryBarrier toPresentBarrier{};
toPresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
toPresentBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
toPresentBarrier.dstAccessMask = 0;
toPresentBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
toPresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
toPresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toPresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
toPresentBarrier.image = m_swapchainObject.GetImage(m_imageIndexAcquired);
toPresentBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
toPresentBarrier.subresourceRange.baseMipLevel = 0;
toPresentBarrier.subresourceRange.levelCount = 1;
toPresentBarrier.subresourceRange.baseArrayLayer = 0;
toPresentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toPresentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
(void)srcExtent;
return true; return true;
} }
@@ -41,6 +41,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
DrawArrayPayload drawArray; DrawArrayPayload drawArray;
GLenum indexType = GL_UNSIGNED_SHORT; GLenum indexType = GL_UNSIGNED_SHORT;
SizeT indexByteOffset = 0; SizeT indexByteOffset = 0;
GLint baseVertex = 0;
}; };
struct QueueFamilyIndices { struct QueueFamilyIndices {