[Fix]: fix iterationT

- implement glCopyImageSubData frontend export and backend dispatch

- add DirectGLES depth-only CopyImageSubData via framebuffer depth blit

- add DirectGLES R32F CopyImageSubData fallback for GLES drivers rejecting native copy

- allocate DirectGLES generated mipmap storage for depth-only and R11FG11FB10F manual generation

- generate DirectGLES depth-only mipmaps with explicit depth blits

- generate DirectGLES R11FG11FB10F mipmaps with explicit color blits

- add DirectVulkan CopyImageSubData with explicit image copy and layout transitions

- use native Vulkan blit for depth-only mipmap generation

- remove unused Vulkan depth mipmap shader fallback path
This commit is contained in:
2026-06-19 08:14:57 +08:00
parent 2bf75537d8
commit 50eda634e0
13 changed files with 754 additions and 485 deletions
@@ -794,10 +794,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
static constexpr Uint kHiddenBlitFragmentShaderId = 0xFFFFFFF2u;
static constexpr Uint kHiddenBlitNearestSamplerId = 0xFFFFFFF3u;
static constexpr Uint kHiddenBlitLinearSamplerId = 0xFFFFFFF4u;
static constexpr Uint kHiddenDepthMipmapProgramId = 0xFFFFFFF5u;
static constexpr Uint kHiddenDepthMipmapVertexShaderId = 0xFFFFFFF6u;
static constexpr Uint kHiddenDepthMipmapFragmentShaderId = 0xFFFFFFF7u;
static constexpr const char* kFullscreenTriangleVertexShaderSource = R"(#version 460 core
uniform vec4 uSrcRect;
uniform vec4 uDstRect;
@@ -840,22 +836,6 @@ layout(location = 0) out vec4 outColor;
void main() {
outColor = texture(uSource, vTexCoord);
}
)";
static constexpr const char* kDepthMipmapFragmentShaderSource = R"(#version 460 core
layout(binding = 0) uniform sampler2D uSource;
layout(location = 0) in vec2 vTexCoord;
uniform ivec2 uSrcTexelSize;
void main() {
ivec2 srcBase = ivec2(gl_FragCoord.xy) * 2;
ivec2 srcMax = max(uSrcTexelSize - ivec2(1), ivec2(0));
float depth0 = texelFetch(uSource, clamp(srcBase + ivec2(0, 0), ivec2(0), srcMax), 0).r;
float depth1 = texelFetch(uSource, clamp(srcBase + ivec2(1, 0), ivec2(0), srcMax), 0).r;
float depth2 = texelFetch(uSource, clamp(srcBase + ivec2(0, 1), ivec2(0), srcMax), 0).r;
float depth3 = texelFetch(uSource, clamp(srcBase + ivec2(1, 1), ivec2(0), srcMax), 0).r;
gl_FragDepth = 0.25 * (depth0 + depth1 + depth2 + depth3);
}
)";
static Uint32 ComputeFullMipLevelCount(const IntVec3& baseTexelSize) {
@@ -1608,7 +1588,6 @@ void main() {
VK_VERIFY(m_frameContext.Initialize(m_device, m_commandPool, m_config.MaxFramesInFlight),
"CreateFrameContexts");
MGLOG_I("CreateFrameContexts completed");
m_deferredDepthMipmapCleanup.assign(m_frameContext.GetFrameCount(), {});
auto succeeded = false;
succeeded = m_bufferManager.Initialize({
.allocator = m_allocator,
@@ -1651,8 +1630,6 @@ void main() {
MOBILEGL_ASSERT(succeeded, "VkSamplerManager initialization failed.");
succeeded = InitializeBlitResources();
MOBILEGL_ASSERT(succeeded, "Blit pipeline resource initialization failed.");
succeeded = InitializeDepthMipmapResources();
MOBILEGL_ASSERT(succeeded, "Depth mipmap pipeline resource initialization failed.");
m_uniformManager = MakeUnique<UniformManager>();
MOBILEGL_ASSERT(m_uniformManager != nullptr, "UniformDescriptorBinder creation failed.");
@@ -1684,12 +1661,10 @@ void main() {
void VulkanRenderer::Shutdown() {
VK_VERIFY(vkDeviceWaitIdle(m_device));
DestroyDeferredDepthMipmapCleanup();
DestroyComputePipelines();
m_pipelineFactory.reset();
ShutdownBlitResources();
ShutdownDepthMipmapResources();
if (m_samplerManager) {
m_samplerManager->Shutdown();
m_samplerManager.reset();
@@ -2074,123 +2049,6 @@ void main() {
m_blitResources = {};
}
Bool VulkanRenderer::InitializeDepthMipmapResources() {
ShutdownDepthMipmapResources();
auto vertexShader = MakeShared<MG_State::GLState::ShaderObject>(ShaderStage::Vertex,
kHiddenDepthMipmapVertexShaderId);
vertexShader->SetShaderSource(kFullscreenTriangleVertexShaderSource);
vertexShader->Compile();
if (!vertexShader->GetCompileStatus()) {
MGLOG_E("InitializeDepthMipmapResources failed: vertex shader compile error: %s",
vertexShader->GetInfoLog().c_str());
return false;
}
auto fragmentShader = MakeShared<MG_State::GLState::ShaderObject>(ShaderStage::Fragment,
kHiddenDepthMipmapFragmentShaderId);
fragmentShader->SetShaderSource(kDepthMipmapFragmentShaderSource);
fragmentShader->Compile();
if (!fragmentShader->GetCompileStatus()) {
MGLOG_E("InitializeDepthMipmapResources failed: fragment shader compile error: %s",
fragmentShader->GetInfoLog().c_str());
return false;
}
m_depthMipmapResources.program = MakeShared<MG_State::GLState::ProgramObject>(kHiddenDepthMipmapProgramId);
m_depthMipmapResources.program->AttachShader(vertexShader);
m_depthMipmapResources.program->AttachShader(fragmentShader);
m_depthMipmapResources.program->Link(false);
if (!m_depthMipmapResources.program->GetLinkStatus()) {
MGLOG_E("InitializeDepthMipmapResources failed: program link error: %s",
m_depthMipmapResources.program->GetInfoLog().c_str());
return false;
}
m_depthMipmapResources.srcRectLocation = m_depthMipmapResources.program->GetUniformLocation("uSrcRect");
m_depthMipmapResources.dstRectLocation = m_depthMipmapResources.program->GetUniformLocation("uDstRect");
m_depthMipmapResources.surfaceTransformLocation =
m_depthMipmapResources.program->GetUniformLocation("uSurfaceTransform");
m_depthMipmapResources.srcTexelSizeLocation =
m_depthMipmapResources.program->GetUniformLocation("uSrcTexelSize");
MOBILEGL_ASSERT(m_depthMipmapResources.srcRectLocation >= 0,
"InitializeDepthMipmapResources: missing uSrcRect");
MOBILEGL_ASSERT(m_depthMipmapResources.dstRectLocation >= 0,
"InitializeDepthMipmapResources: missing uDstRect");
MOBILEGL_ASSERT(m_depthMipmapResources.surfaceTransformLocation >= 0,
"InitializeDepthMipmapResources: missing uSurfaceTransform");
MOBILEGL_ASSERT(m_depthMipmapResources.srcTexelSizeLocation >= 0,
"InitializeDepthMipmapResources: missing uSrcTexelSize");
MOBILEGL_ASSERT(m_depthMipmapResources.program->GetUBOSize() > 0,
"InitializeDepthMipmapResources: depth mipmap program global UBO is empty");
MOBILEGL_ASSERT(m_programFactory != nullptr, "InitializeDepthMipmapResources: program factory is null");
ProgramFactory::CompileOptionFlags depthMipmapTransformFlags = 0;
const auto& depthMipmapProgramObj =
m_programFactory->GetOrCreateProgram(*m_depthMipmapResources.program, depthMipmapTransformFlags);
Bool foundDepthMipmapSamplerBinding = false;
for (Uint32 binding = 0; binding < depthMipmapProgramObj.samplerNameByBinding.size(); ++binding) {
if (depthMipmapProgramObj.bindingKinds[binding] != ProgramFactory::DescriptorBindingKind::CombinedImageSampler) {
continue;
}
if (depthMipmapProgramObj.samplerNameByBinding[binding] == "uSource") {
m_depthMipmapResources.samplerBinding = binding;
foundDepthMipmapSamplerBinding = true;
break;
}
}
MOBILEGL_ASSERT(foundDepthMipmapSamplerBinding,
"InitializeDepthMipmapResources: failed to resolve reflected binding for uSource");
return true;
}
void VulkanRenderer::ShutdownDepthMipmapResources() {
m_depthMipmapResources = {};
}
void VulkanRenderer::CollectDeferredDepthMipmapCleanup(Uint32 frameIndex) {
MOBILEGL_ASSERT(frameIndex < m_deferredDepthMipmapCleanup.size(),
"CollectDeferredDepthMipmapCleanup: frame index %u out of range (size=%zu)",
frameIndex, m_deferredDepthMipmapCleanup.size());
if (m_device == VK_NULL_HANDLE) {
return;
}
auto& cleanup = m_deferredDepthMipmapCleanup[frameIndex];
for (auto framebuffer : cleanup.framebuffers) {
if (framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(m_device, framebuffer, nullptr);
}
}
for (auto pipeline : cleanup.pipelines) {
if (pipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(m_device, pipeline, nullptr);
}
}
for (auto renderPass : cleanup.renderPasses) {
if (renderPass != VK_NULL_HANDLE) {
vkDestroyRenderPass(m_device, renderPass, nullptr);
}
}
for (auto imageView : cleanup.imageViews) {
if (imageView != VK_NULL_HANDLE) {
vkDestroyImageView(m_device, imageView, nullptr);
}
}
cleanup.framebuffers.clear();
cleanup.pipelines.clear();
cleanup.renderPasses.clear();
cleanup.imageViews.clear();
}
void VulkanRenderer::DestroyDeferredDepthMipmapCleanup() {
for (Uint32 frameIndex = 0; frameIndex < m_deferredDepthMipmapCleanup.size(); ++frameIndex) {
CollectDeferredDepthMipmapCleanup(frameIndex);
}
m_deferredDepthMipmapCleanup.clear();
}
VkPipeline VulkanRenderer::GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry) {
MOBILEGL_ASSERT(m_blitResources.program != nullptr, "GetOrCreateBlitPipeline: blit program is null");
MOBILEGL_ASSERT(m_programFactory != nullptr, "GetOrCreateBlitPipeline: program factory is null");
@@ -2238,297 +2096,6 @@ void main() {
return m_pipelineFactory->GetOrCreatePipeline(payload);
}
Bool VulkanRenderer::GenerateDepthMipmapWithShader(FrameContext::FrameData& frame,
MG_State::GLState::ITextureObject& texture,
VkTextureManager::TextureResource& resource,
Uint32 baseMipLevel,
Uint32 generateMipLevelCount,
const IntVec3& storageBaseTexelSize,
VkImageLayout originalLayout,
VkImageLayout finalLayout) {
MOBILEGL_ASSERT(m_depthMipmapResources.program != nullptr,
"GenerateDepthMipmapWithShader: depth mipmap program is null");
MOBILEGL_ASSERT(m_blitResources.nearestSampler != nullptr,
"GenerateDepthMipmapWithShader: helper sampler is null");
MOBILEGL_ASSERT(m_programFactory != nullptr, "GenerateDepthMipmapWithShader: program factory is null");
MOBILEGL_ASSERT(m_uniformManager != nullptr, "GenerateDepthMipmapWithShader: uniform manager is null");
MOBILEGL_ASSERT(texture.GetTarget() == TextureTarget::Texture2D,
"GenerateDepthMipmapWithShader only supports GL_TEXTURE_2D depth textures");
MOBILEGL_ASSERT((resource.aspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0,
"GenerateDepthMipmapWithShader requires a depth aspect");
MOBILEGL_ASSERT(resource.depth == 1 && resource.arrayLayers == 1,
"GenerateDepthMipmapWithShader only supports single-layer depth textures");
MOBILEGL_ASSERT(m_frameContext.GetCurrentFrameIndex() < m_deferredDepthMipmapCleanup.size(),
"GenerateDepthMipmapWithShader: frame index %u out of range (cleanup slots=%zu)",
m_frameContext.GetCurrentFrameIndex(), m_deferredDepthMipmapCleanup.size());
auto& deferredCleanup = m_deferredDepthMipmapCleanup[m_frameContext.GetCurrentFrameIndex()];
static const VkPipelineVertexInputStateCreateInfo kEmptyVertexInputState {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
};
ProgramFactory::CompileOptionFlags depthMipmapTransformFlags = 0;
const auto& programObj = m_programFactory->GetOrCreateProgram(*m_depthMipmapResources.program,
depthMipmapTransformFlags);
VkAttachmentDescription depthAttachment{};
depthAttachment.format = resource.format;
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthAttachmentRef{};
depthAttachmentRef.attachment = 0;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpassDesc{};
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpassDesc.pDepthStencilAttachment = &depthAttachmentRef;
VkRenderPassCreateInfo renderPassCreateInfo{};
renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassCreateInfo.attachmentCount = 1;
renderPassCreateInfo.pAttachments = &depthAttachment;
renderPassCreateInfo.subpassCount = 1;
renderPassCreateInfo.pSubpasses = &subpassDesc;
VkRenderPass renderPass = VK_NULL_HANDLE;
VK_VERIFY(vkCreateRenderPass(m_device, &renderPassCreateInfo, nullptr, &renderPass),
"GenerateDepthMipmapWithShader: vkCreateRenderPass");
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterizationState{};
rasterizationState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationState.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationState.cullMode = VK_CULL_MODE_NONE;
rasterizationState.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizationState.lineWidth = 1.0f;
VkPipelineMultisampleStateCreateInfo multisampleState{};
multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
VkPipelineDepthStencilStateCreateInfo depthStencilState{};
depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilState.depthTestEnable = VK_TRUE;
depthStencilState.depthWriteEnable = VK_TRUE;
depthStencilState.depthCompareOp = VK_COMPARE_OP_ALWAYS;
depthStencilState.minDepthBounds = 0.0f;
depthStencilState.maxDepthBounds = 1.0f;
VkPipelineColorBlendStateCreateInfo colorBlendState{};
colorBlendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
const VkDynamicState dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
VkPipelineDynamicStateCreateInfo dynamicState{};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<Uint32>(std::size(dynamicStates));
dynamicState.pDynamicStates = dynamicStates;
VkGraphicsPipelineCreateInfo pipelineCreateInfo{};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.stageCount = static_cast<Uint32>(programObj.stages.size());
pipelineCreateInfo.pStages = programObj.stages.data();
pipelineCreateInfo.pVertexInputState = &kEmptyVertexInputState;
pipelineCreateInfo.pInputAssemblyState = &inputAssembly;
pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pRasterizationState = &rasterizationState;
pipelineCreateInfo.pMultisampleState = &multisampleState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pColorBlendState = &colorBlendState;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.layout = programObj.pipelineLayout;
pipelineCreateInfo.renderPass = renderPass;
pipelineCreateInfo.subpass = 0;
VkPipeline pipeline = VK_NULL_HANDLE;
VK_VERIFY(vkCreateGraphicsPipelines(m_device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, nullptr, &pipeline),
"GenerateDepthMipmapWithShader: vkCreateGraphicsPipelines");
deferredCleanup.renderPasses.push_back(renderPass);
deferredCleanup.pipelines.push_back(pipeline);
auto createMipView = [&](Uint32 mipLevel, VkImageAspectFlags aspectMask) {
VkImageViewCreateInfo viewCreateInfo{};
viewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewCreateInfo.image = resource.image;
viewCreateInfo.viewType = resource.viewType;
viewCreateInfo.format = resource.format;
viewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
viewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
viewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
viewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
viewCreateInfo.subresourceRange.aspectMask = aspectMask;
viewCreateInfo.subresourceRange.baseMipLevel = mipLevel;
viewCreateInfo.subresourceRange.levelCount = 1;
viewCreateInfo.subresourceRange.baseArrayLayer = 0;
viewCreateInfo.subresourceRange.layerCount = resource.arrayLayers;
VkImageView view = VK_NULL_HANDLE;
VK_VERIFY(vkCreateImageView(m_device, &viewCreateInfo, nullptr, &view),
"GenerateDepthMipmapWithShader: vkCreateImageView");
return view;
};
VkPipelineStageFlags originalSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags originalSrcAccessMask = 0;
GetImageTransitionSourceState(originalLayout, originalSrcStageMask, originalSrcAccessMask);
VkPipelineStageFlags finalDstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags finalDstAccessMask = 0;
GetImageTransitionDestinationState(finalLayout, finalDstStageMask, finalDstAccessMask);
VkPipelineStageFlags depthAttachmentStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags depthAttachmentAccessMask = 0;
GetImageTransitionDestinationState(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
depthAttachmentStageMask, depthAttachmentAccessMask);
if (originalLayout != finalLayout) {
if (baseMipLevel > 0) {
VkImageLayout lowerMipLayout = originalLayout;
const Bool lowerReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource.image, lowerMipLayout, finalLayout,
originalSrcStageMask, finalDstStageMask,
originalSrcAccessMask, finalDstAccessMask,
resource.aspect, 0, baseMipLevel);
MOBILEGL_ASSERT(lowerReady, "%s: failed to transition lower untouched mip levels", __func__);
}
if (generateMipLevelCount < resource.mipLevels) {
VkImageLayout upperMipLayout = originalLayout;
const Bool upperReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource.image, upperMipLayout, finalLayout,
originalSrcStageMask, finalDstStageMask,
originalSrcAccessMask, finalDstAccessMask,
resource.aspect, generateMipLevelCount, resource.mipLevels - generateMipLevelCount);
MOBILEGL_ASSERT(upperReady, "%s: failed to transition upper untouched mip levels", __func__);
}
VkImageLayout srcMipLayout = originalLayout;
const Bool srcReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource.image, srcMipLayout, finalLayout,
originalSrcStageMask, finalDstStageMask,
originalSrcAccessMask, finalDstAccessMask,
resource.aspect, baseMipLevel, 1);
MOBILEGL_ASSERT(srcReady, "%s: failed to transition base mip level to sampled layout", __func__);
}
resource.layout = finalLayout;
auto* depthProgramData = static_cast<Uint8*>(m_depthMipmapResources.program->MapUBO());
MOBILEGL_ASSERT(depthProgramData != nullptr, "GenerateDepthMipmapWithShader: depth mipmap UBO is null");
auto writeUniform = [&](Int location, const void* data, SizeT size) {
MOBILEGL_ASSERT(location >= 0, "GenerateDepthMipmapWithShader: invalid uniform location");
const Uint offset = m_depthMipmapResources.program->GetUniformOffset(static_cast<Uint>(location));
MOBILEGL_ASSERT(offset + size <= m_depthMipmapResources.program->GetUBOSize(),
"GenerateDepthMipmapWithShader: uniform write out of bounds");
memcpy(depthProgramData + offset, data, size);
};
for (Uint32 level = baseMipLevel + 1; level < generateMipLevelCount; ++level) {
VkImageLayout dstMipLayout = originalLayout;
const Bool dstReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource.image, dstMipLayout, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
originalSrcStageMask, depthAttachmentStageMask,
originalSrcAccessMask, depthAttachmentAccessMask,
resource.aspect, level, 1);
MOBILEGL_ASSERT(dstReady, "%s: failed to transition mip level %u to depth attachment layout", __func__, level);
const IntVec3 srcTexelSize = ComputeMipTexelSize(storageBaseTexelSize, level - 1);
const IntVec3 dstTexelSize = ComputeMipTexelSize(storageBaseTexelSize, level);
const Int srcTexelSizeUniform[2] = {srcTexelSize.x(), srcTexelSize.y()};
const VkImageView sourceImageView = createMipView(level - 1, VK_IMAGE_ASPECT_DEPTH_BIT);
const VkImageView depthAttachmentView = createMipView(level, resource.aspect);
deferredCleanup.imageViews.push_back(sourceImageView);
deferredCleanup.imageViews.push_back(depthAttachmentView);
VkFramebufferCreateInfo framebufferCreateInfo{};
framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferCreateInfo.renderPass = renderPass;
framebufferCreateInfo.attachmentCount = 1;
framebufferCreateInfo.pAttachments = &depthAttachmentView;
framebufferCreateInfo.width = static_cast<Uint32>(dstTexelSize.x());
framebufferCreateInfo.height = static_cast<Uint32>(dstTexelSize.y());
framebufferCreateInfo.layers = 1;
VkFramebuffer framebuffer = VK_NULL_HANDLE;
VK_VERIFY(vkCreateFramebuffer(m_device, &framebufferCreateInfo, nullptr, &framebuffer),
"GenerateDepthMipmapWithShader: vkCreateFramebuffer");
deferredCleanup.framebuffers.push_back(framebuffer);
VkRenderPassBeginInfo renderPassBeginInfo{};
renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.framebuffer = framebuffer;
renderPassBeginInfo.renderArea.offset = {0, 0};
renderPassBeginInfo.renderArea.extent = {
static_cast<Uint32>(dstTexelSize.x()), static_cast<Uint32>(dstTexelSize.y())
};
vkCmdBeginRenderPass(frame.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
ApplyGLViewportState(frame.commandBuffer, dstTexelSize.xy());
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = {static_cast<Uint32>(dstTexelSize.x()), static_cast<Uint32>(dstTexelSize.y())};
vkCmdSetScissor(frame.commandBuffer, 0, 1, &scissor);
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
std::fill(depthProgramData,
depthProgramData + m_depthMipmapResources.program->GetUBOSize(),
Uint8{0});
BlitUniformData blitUniformData{};
writeUniform(m_depthMipmapResources.srcRectLocation,
blitUniformData.srcRect,
sizeof(blitUniformData.srcRect));
writeUniform(m_depthMipmapResources.dstRectLocation,
blitUniformData.dstRect,
sizeof(blitUniformData.dstRect));
writeUniform(m_depthMipmapResources.surfaceTransformLocation,
&blitUniformData.surfaceTransform,
sizeof(blitUniformData.surfaceTransform));
writeUniform(m_depthMipmapResources.srcTexelSizeLocation,
srcTexelSizeUniform,
sizeof(srcTexelSizeUniform));
const auto samplerBindingOverride = UniformManager::SamplerBindingOverride{
.binding = m_depthMipmapResources.samplerBinding,
.texture = &texture,
.sampler = m_blitResources.nearestSampler.get(),
.imageView = sourceImageView,
};
const Bool bound = m_uniformManager->BindProgramUniformBuffers(
frame.commandBuffer, *m_depthMipmapResources.program, programObj,
m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_GRAPHICS, &samplerBindingOverride);
MOBILEGL_ASSERT(bound, "GenerateDepthMipmapWithShader: BindProgramUniformBuffers failed");
vkCmdDraw(frame.commandBuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(frame.commandBuffer);
VkImageLayout finishedMipLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
const Bool finishedReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, resource.image, finishedMipLayout, finalLayout,
depthAttachmentStageMask, finalDstStageMask,
depthAttachmentAccessMask, finalDstAccessMask,
resource.aspect, level, 1);
MOBILEGL_ASSERT(finishedReady, "%s: failed to transition mip level %u to sampled layout", __func__, level);
}
return true;
}
VkPipeline VulkanRenderer::GetOrCreatePipeline(
GLenum mode,
const MG_State::GLState::ProgramObject& program,
@@ -4132,6 +3699,152 @@ void main() {
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
}
void VulkanRenderer::CopyImageSubData(const SharedPtr<MG_State::GLState::ITextureObject>& srcTexture,
GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ,
const SharedPtr<MG_State::GLState::ITextureObject>& dstTexture,
GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ,
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
MOBILEGL_ASSERT(srcWidth > 0 && srcHeight > 0 && srcDepth > 0,
"CopyImageSubData requires positive copy dimensions.");
MOBILEGL_ASSERT(srcTexture != nullptr && dstTexture != nullptr,
"CopyImageSubData requires valid source and destination textures.");
const auto srcTextureTarget = MG_Util::ConvertGLEnumToTextureTarget(srcTarget);
const auto dstTextureTarget = MG_Util::ConvertGLEnumToTextureTarget(dstTarget);
MOBILEGL_ASSERT(srcTextureTarget == TextureTarget::Texture2D && dstTextureTarget == TextureTarget::Texture2D,
"CopyImageSubData currently only supports GL_TEXTURE_2D sources and destinations.");
MOBILEGL_ASSERT(srcDepth == 1 && srcZ == 0 && dstZ == 0,
"CopyImageSubData currently only supports single-layer 2D copies.");
MOBILEGL_ASSERT(srcTexture.get() != dstTexture.get(),
"CopyImageSubData does not support in-place texture copies yet.");
auto& frame = m_frameContext.GetCurrent();
if (!frame.isCommandRecording) {
m_frameContext.BeginCommandRecording();
m_uniformManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
}
if (VkRenderPassManager::GetActiveRenderPass() != nullptr) {
VkRenderPassManager::EndRenderPass(frame.commandBuffer);
}
auto* srcResource = m_textureManager->SyncTextureAndGetDescriptor(*srcTexture);
auto* dstResource = m_textureManager->SyncTextureAndGetDescriptor(*dstTexture);
MOBILEGL_ASSERT(srcResource != nullptr && dstResource != nullptr,
"CopyImageSubData failed to sync source or destination texture.");
MOBILEGL_ASSERT(srcLevel >= 0 && dstLevel >= 0 &&
static_cast<Uint32>(srcLevel) < srcResource->mipLevels &&
static_cast<Uint32>(dstLevel) < dstResource->mipLevels,
"CopyImageSubData mip level is out of range.");
const VkImageAspectFlags copyAspectMask =
srcResource->aspect & dstResource->aspect &
(VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT);
MOBILEGL_ASSERT(copyAspectMask != 0 &&
(srcResource->aspect & copyAspectMask) == srcResource->aspect &&
(dstResource->aspect & copyAspectMask) == dstResource->aspect,
"CopyImageSubData source and destination aspects are incompatible.");
const Uint32 srcMipLevel = static_cast<Uint32>(srcLevel);
const Uint32 dstMipLevel = static_cast<Uint32>(dstLevel);
const Uint32 srcMipWidth = std::max(1u, srcResource->extent.width >> srcMipLevel);
const Uint32 srcMipHeight = std::max(1u, srcResource->extent.height >> srcMipLevel);
const Uint32 dstMipWidth = std::max(1u, dstResource->extent.width >> dstMipLevel);
const Uint32 dstMipHeight = std::max(1u, dstResource->extent.height >> dstMipLevel);
MOBILEGL_ASSERT(srcX >= 0 && srcY >= 0 && dstX >= 0 && dstY >= 0 &&
static_cast<Uint32>(srcX + srcWidth) <= srcMipWidth &&
static_cast<Uint32>(srcY + srcHeight) <= srcMipHeight &&
static_cast<Uint32>(dstX + srcWidth) <= dstMipWidth &&
static_cast<Uint32>(dstY + srcHeight) <= dstMipHeight,
"CopyImageSubData region is outside source or destination bounds.");
const Bool clearReady = MaterializePendingClearForTexture(frame.commandBuffer, *srcTexture);
MOBILEGL_ASSERT(clearReady, "%s: failed to materialize pending clear for source textureId=%d",
__func__, srcTexture->GetExternalIndex());
const VkImageLayout srcOriginalLayout = srcResource->layout;
const VkImageLayout dstOriginalLayout = dstResource->layout;
MOBILEGL_ASSERT(srcOriginalLayout != VK_IMAGE_LAYOUT_UNDEFINED,
"CopyImageSubData source image has undefined layout.");
const VkImageLayout dstRestoreLayout = dstOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED
? ((copyAspectMask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0
? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
: dstOriginalLayout;
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcAccessMask = 0;
GetImageTransitionSourceState(srcOriginalLayout, srcStageMask, srcAccessMask);
VkImageLayout srcCopyLayout = srcOriginalLayout;
Bool srcReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcResource->image, srcCopyLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
srcAccessMask, VK_ACCESS_TRANSFER_READ_BIT, copyAspectMask, srcMipLevel, 1);
MOBILEGL_ASSERT(srcReady, "%s: failed to transition source image", __func__);
VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags dstAccessMask = 0;
GetImageTransitionSourceState(dstOriginalLayout, dstStageMask, dstAccessMask);
VkImageLayout dstCopyLayout = dstOriginalLayout;
if (dstOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
Bool dstReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstResource->image, dstResource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT,
dstResource->aspect, 0, dstResource->mipLevels, dstResource->arrayLayers);
MOBILEGL_ASSERT(dstReady, "%s: failed to transition undefined destination image", __func__);
dstCopyLayout = dstResource->layout;
} else {
Bool dstReady = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstResource->image, dstCopyLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstStageMask, VK_PIPELINE_STAGE_TRANSFER_BIT,
dstAccessMask, VK_ACCESS_TRANSFER_WRITE_BIT, copyAspectMask, dstMipLevel, 1);
MOBILEGL_ASSERT(dstReady, "%s: failed to transition destination image", __func__);
}
VkImageCopy copyRegion{};
copyRegion.srcSubresource.aspectMask = copyAspectMask;
copyRegion.srcSubresource.mipLevel = srcMipLevel;
copyRegion.srcSubresource.baseArrayLayer = 0;
copyRegion.srcSubresource.layerCount = 1;
copyRegion.srcOffset = {srcX, srcY, 0};
copyRegion.dstSubresource.aspectMask = copyAspectMask;
copyRegion.dstSubresource.mipLevel = dstMipLevel;
copyRegion.dstSubresource.baseArrayLayer = 0;
copyRegion.dstSubresource.layerCount = 1;
copyRegion.dstOffset = {dstX, dstY, 0};
copyRegion.extent = {static_cast<Uint32>(srcWidth), static_cast<Uint32>(srcHeight), 1};
vkCmdCopyImage(frame.commandBuffer,
srcResource->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstResource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &copyRegion);
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags srcRestoreAccessMask = 0;
GetImageTransitionDestinationState(srcOriginalLayout, srcRestoreStageMask, srcRestoreAccessMask);
Bool srcRestored = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, srcResource->image, srcCopyLayout, srcOriginalLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, srcRestoreStageMask,
VK_ACCESS_TRANSFER_READ_BIT, srcRestoreAccessMask, copyAspectMask, srcMipLevel, 1);
MOBILEGL_ASSERT(srcRestored, "%s: failed to restore source image layout", __func__);
VkPipelineStageFlags dstRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags dstRestoreAccessMask = 0;
GetImageTransitionDestinationState(dstRestoreLayout, dstRestoreStageMask, dstRestoreAccessMask);
if (dstOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
Bool dstRestored = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstResource->image, dstResource->layout, dstRestoreLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask,
VK_ACCESS_TRANSFER_WRITE_BIT, dstRestoreAccessMask,
dstResource->aspect, 0, dstResource->mipLevels, dstResource->arrayLayers);
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore undefined destination image layout", __func__);
} else {
Bool dstRestored = VkTextureManager::TransitionImageLayout(
frame.commandBuffer, dstResource->image, dstCopyLayout, dstRestoreLayout,
VK_PIPELINE_STAGE_TRANSFER_BIT, dstRestoreStageMask,
VK_ACCESS_TRANSFER_WRITE_BIT, dstRestoreAccessMask, copyAspectMask, dstMipLevel, 1);
MOBILEGL_ASSERT(dstRestored, "%s: failed to restore destination image layout", __func__);
}
}
Bool VulkanRenderer::SubmitReadbackCommandsAndWait(FrameContext::FrameData& frame) {
if (frame.isCommandRecording) {
m_frameContext.EndCommandRecording();
@@ -4483,13 +4196,21 @@ void main() {
const VkFormatFeatureFlags optimalTilingFeatures = formatProperties.optimalTilingFeatures;
const Bool isDepthOrStencilTexture =
(resource->aspect & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0;
if (!isDepthOrStencilTexture &&
((optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) == 0 ||
(optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) == 0)) {
const Bool supportsNativeBlit =
(optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0 &&
(optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0;
if (!isDepthOrStencilTexture && !supportsNativeBlit) {
MGLOG_W("GenerateMipmap skipped for textureId=%d because Vulkan format %d does not support blit-based mip generation",
texture->GetExternalIndex(), static_cast<Int>(resource->format));
return;
}
if (isDepthOrStencilTexture) {
MOBILEGL_ASSERT((resource->aspect & VK_IMAGE_ASPECT_STENCIL_BIT) == 0,
"GenerateMipmap: depth-stencil mipmap generation is not supported yet.");
MOBILEGL_ASSERT(supportsNativeBlit,
"GenerateMipmap: depth texture format %d does not support native Vulkan blit.",
static_cast<Int>(resource->format));
}
MOBILEGL_ASSERT(EnsureGenerateMipmapStorageAllocated(*mipmapTexture, uploadTarget, baseMipLevel),
"GenerateMipmap could not allocate a full mip chain for this texture.");
@@ -4522,20 +4243,11 @@ void main() {
const VkImageLayout originalLayout = resource->layout;
const VkImageLayout finalLayout = ResolveGenerateMipmapFinalLayout(resource->aspect);
if (isDepthOrStencilTexture) {
const Bool depthReady = GenerateDepthMipmapWithShader(frame, *texture, *resource,
baseMipLevel, generateMipLevelCount,
storageBaseTexelSize, originalLayout, finalLayout);
MOBILEGL_ASSERT(depthReady,
"GenerateMipmap: depth/stencil fallback failed for textureId=%d target=%d internalFormat=%d vkFormat=%d",
texture->GetExternalIndex(), static_cast<Int>(texture->GetTarget()),
static_cast<Int>(texture->GetFormat()), static_cast<Int>(resource->format));
return;
}
const VkFilter blitFilter = (optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0
? VK_FILTER_LINEAR
: VK_FILTER_NEAREST;
const VkFilter blitFilter = isDepthOrStencilTexture
? VK_FILTER_NEAREST
: ((optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0
? VK_FILTER_LINEAR
: VK_FILTER_NEAREST);
VkPipelineStageFlags originalSrcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkAccessFlags originalSrcAccessMask = 0;
@@ -4986,7 +4698,6 @@ void main() {
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
}
VK_VERIFY(result, "Present, vkAcquireNextImageKHR");
CollectDeferredDepthMipmapCleanup(m_frameContext.GetCurrentFrameIndex());
m_textureManager->BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
m_transientVertexIndexBufferSlicesThisFrame.clear();
@@ -5641,9 +5352,6 @@ void main() {
vkDeviceWaitIdle(m_device);
DestroyDeferredDepthMipmapCleanup();
m_deferredDepthMipmapCleanup.assign(m_frameContext.GetFrameCount(), {});
ShutdownSwapchain();
CreateSwapchain();