[Chore] (MG_Backend/DirectVulkan): implements BlitFramebuffer using backend manager APIs

This commit is contained in:
2026-03-07 19:29:33 +08:00
parent f3b0b242dd
commit 5652f9ae5f
4 changed files with 178 additions and 187 deletions
@@ -538,6 +538,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return outImageInfo.sampler != VK_NULL_HANDLE; return outImageInfo.sampler != VK_NULL_HANDLE;
} }
Bool UniformDescriptorBinder::ResolveSamplerDescriptorOverride(
const SamplerBindingOverride& samplerBindingOverride,
VkDescriptorImageInfo& outImageInfo) const {
MOBILEGL_ASSERT(m_textureManager != nullptr, "ResolveSamplerDescriptorOverride: texture manager is null");
MOBILEGL_ASSERT(m_samplerManager != nullptr, "ResolveSamplerDescriptorOverride: sampler manager is null");
if (samplerBindingOverride.texture == nullptr || samplerBindingOverride.sampler == nullptr) {
return false;
}
auto* resource = m_textureManager->SyncTextureAndGetDescriptor(*samplerBindingOverride.texture);
if (resource == nullptr || !IsValidSampledImageLayout(resource->layout)) {
return false;
}
outImageInfo = {
.sampler = m_samplerManager->GetOrCreateSampler(*samplerBindingOverride.sampler),
.imageView = resource->view,
.imageLayout = resource->layout,
};
return outImageInfo.sampler != VK_NULL_HANDLE;
}
Bool UniformDescriptorBinder::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program, Bool UniformDescriptorBinder::ResolveSamplerTexture(const MG_State::GLState::ProgramObject& program,
const ProgramLayout& layout, Uint32 binding, const ProgramLayout& layout, Uint32 binding,
SharedPtr<MG_State::GLState::ITextureObject>& outTexture) const { SharedPtr<MG_State::GLState::ITextureObject>& outTexture) const {
@@ -786,7 +808,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program, const MG_State::GLState::ProgramObject& program,
Uint32 frameIndex) { Uint32 frameIndex) {
return BindProgramUniformBuffers(commandBuffer, program, frameIndex, nullptr);
}
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program,
Uint32 frameIndex,
const SamplerBindingOverride* samplerBindingOverride) {
ProgramLayout* layout = GetOrCreateProgramLayout(program); ProgramLayout* layout = GetOrCreateProgramLayout(program);
MOBILEGL_ASSERT(layout != nullptr,
"UniformDescriptorBinder::BindProgramUniformBuffers: program layout is null");
auto& frame = m_frames[frameIndex]; auto& frame = m_frames[frameIndex];
if (frame.descriptorPools.empty()) { if (frame.descriptorPools.empty()) {
@@ -903,7 +934,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset)); dynamicOffsets.push_back(static_cast<Uint32>(payloadOffset));
} else { } else {
VkDescriptorImageInfo imageInfo{}; VkDescriptorImageInfo imageInfo{};
Bool hasImage = ResolveSamplerDescriptor(commandBuffer, program, *layout, binding, imageInfo); Bool hasImage = false;
if (samplerBindingOverride != nullptr &&
samplerBindingOverride->binding == binding &&
samplerBindingOverride->texture != nullptr &&
samplerBindingOverride->sampler != nullptr) {
hasImage = ResolveSamplerDescriptorOverride(*samplerBindingOverride, imageInfo);
} else {
hasImage = ResolveSamplerDescriptor(commandBuffer, program, *layout, binding, imageInfo);
}
if (!hasImage) { if (!hasImage) {
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor", MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: sampler binding %u has no valid texture descriptor",
binding); binding);
@@ -16,7 +16,9 @@
#include <vk_mem_alloc.h> #include <vk_mem_alloc.h>
namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_State::GLState {
class ITextureObject;
class ProgramObject; class ProgramObject;
class SamplerObject;
} }
namespace MobileGL::MG_Backend::DirectVulkan { namespace MobileGL::MG_Backend::DirectVulkan {
@@ -28,6 +30,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
CombinedImageSampler CombinedImageSampler
}; };
struct SamplerBindingOverride {
Uint32 binding = 0;
MG_State::GLState::ITextureObject* texture = nullptr;
const MG_State::GLState::SamplerObject* sampler = nullptr;
};
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment, Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64, Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024, VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024,
@@ -40,6 +48,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Vector<MG_State::GLState::ITextureObject*>& outTextures); Vector<MG_State::GLState::ITextureObject*>& outTextures);
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex); const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer,
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex,
const SamplerBindingOverride* samplerBindingOverride);
private: private:
struct DescriptorPoolBucket { struct DescriptorPoolBucket {
@@ -79,6 +90,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program, Bool ResolveSamplerDescriptor(VkCommandBuffer commandBuffer, const MG_State::GLState::ProgramObject& program,
const ProgramLayout& layout, Uint32 binding, const ProgramLayout& layout, Uint32 binding,
VkDescriptorImageInfo& outImageInfo) const; VkDescriptorImageInfo& outImageInfo) const;
Bool ResolveSamplerDescriptorOverride(const SamplerBindingOverride& samplerBindingOverride,
VkDescriptorImageInfo& outImageInfo) const;
Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const; Bool ReflectBindingKinds(const MG_State::GLState::ProgramObject& program, Vector<BindingKind>& outKinds) const;
ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program); ProgramLayout* GetOrCreateProgramLayout(const MG_State::GLState::ProgramObject& program);
Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset); Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset);
@@ -12,7 +12,8 @@
#include "MG_State/GLState/Core.h" #include "MG_State/GLState/Core.h"
#include "MG_State/GLState/ProgramState/ProgramObject.h" #include "MG_State/GLState/ProgramState/ProgramObject.h"
#include "MG_Util/ShaderTranspiler/ShaderCompiler.h" #include "MG_State/GLState/ProgramState/ShaderObject.h"
#include "MG_State/GLState/SamplerState/SamplerObject.h"
#include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h" #include "MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h"
#include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h" #include "MG_Util/Converters/MGToVk/RenderStateEnumConverter.h"
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
@@ -74,6 +75,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
namespace { namespace {
static constexpr Uint kHiddenBlitProgramId = 0xFFFFFFF0u;
static constexpr Uint kHiddenBlitVertexShaderId = 0xFFFFFFF1u;
static constexpr Uint kHiddenBlitFragmentShaderId = 0xFFFFFFF2u;
static constexpr Uint kHiddenBlitNearestSamplerId = 0xFFFFFFF3u;
static constexpr Uint kHiddenBlitLinearSamplerId = 0xFFFFFFF4u;
enum class BlitSurfaceTransform : Uint32 { enum class BlitSurfaceTransform : Uint32 {
Identity = 0, Identity = 0,
Rotate90 = 1, Rotate90 = 1,
@@ -513,25 +520,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool VulkanRenderer::InitializeBlitResources() { Bool VulkanRenderer::InitializeBlitResources() {
ShutdownBlitResources(); ShutdownBlitResources();
using namespace MG_Util::ShaderTranspiler; static constexpr const char* kVertexShaderSource = R"(#version 460 core
uniform vec4 uSrcRect;
static constexpr StringView kVertexShaderSource = R"(#version 450 uniform vec4 uDstRect;
layout(push_constant) uniform BlitPushConstants { uniform int uSurfaceTransform;
vec4 srcRect;
vec4 dstRect;
uint surfaceTransform;
} pc;
layout(location = 0) out vec2 vTexCoord; layout(location = 0) out vec2 vTexCoord;
vec2 ApplySurfaceTransform(vec2 position, uint transform) { vec2 ApplySurfaceTransform(vec2 position, int transform) {
vec2 p = position; vec2 p = position;
p.y = -p.y; p.y = -p.y;
if (transform == 1u) { if (transform == 1) {
p = vec2(-p.y, p.x); p = vec2(-p.y, p.x);
} else if (transform == 2u) { } else if (transform == 2) {
p = -p; p = -p;
} else if (transform == 3u) { } else if (transform == 3) {
p = vec2(p.y, -p.x); p = vec2(p.y, -p.x);
} }
return p; return p;
@@ -543,16 +546,16 @@ void main() {
vec2(2.0, 0.0), vec2(2.0, 0.0),
vec2(0.0, 2.0) vec2(0.0, 2.0)
); );
vec2 uv = uvTri[gl_VertexIndex]; vec2 uv = uvTri[gl_VertexID];
vec2 dst = pc.dstRect.xy + uv * pc.dstRect.zw; vec2 dst = uDstRect.xy + uv * uDstRect.zw;
vec2 clip = dst * 2.0 - 1.0; vec2 clip = dst * 2.0 - 1.0;
clip = ApplySurfaceTransform(clip, pc.surfaceTransform); clip = ApplySurfaceTransform(clip, uSurfaceTransform);
gl_Position = vec4(clip, 0.0, 1.0); gl_Position = vec4(clip, 0.0, 1.0);
vTexCoord = pc.srcRect.xy + uv * pc.srcRect.zw; vTexCoord = uSrcRect.xy + uv * uSrcRect.zw;
} }
)"; )";
static constexpr StringView kFragmentShaderSource = R"(#version 450 static constexpr const char* kFragmentShaderSource = R"(#version 460 core
layout(set = 0, binding = 0) uniform sampler2D uSource; layout(binding = 0) uniform sampler2D uSource;
layout(location = 0) in vec2 vTexCoord; layout(location = 0) in vec2 vTexCoord;
layout(location = 0) out vec4 outColor; layout(location = 0) out vec4 outColor;
@@ -561,145 +564,77 @@ void main() {
} }
)"; )";
auto vs = ShaderCompiler::CompileShader({GL_VERTEX_SHADER, kVertexShaderSource, {}}); auto vertexShader = MakeShared<MG_State::GLState::ShaderObject>(ShaderStage::Vertex, kHiddenBlitVertexShaderId);
if (!vs) { vertexShader->SetShaderSource(kVertexShaderSource);
MGLOG_E("InitializeBlitResources failed: vertex shader compile error: %s", vs.error().log.c_str()); vertexShader->Compile();
return false; if (!vertexShader->GetCompileStatus()) {
} MGLOG_E("InitializeBlitResources failed: vertex shader compile error: %s", vertexShader->GetInfoLog().c_str());
auto fs = ShaderCompiler::CompileShader({GL_FRAGMENT_SHADER, kFragmentShaderSource, {}});
if (!fs) {
MGLOG_E("InitializeBlitResources failed: fragment shader compile error: %s", fs.error().log.c_str());
return false;
}
auto program = ShaderCompiler::LinkProgram({{vs.value(), fs.value()}, {}, {}});
if (!program) {
MGLOG_E("InitializeBlitResources failed: link error: %s", program.error().log.c_str());
return false;
}
auto spirv = ShaderCompiler::GetSpirvBinaryFromProgram({{GL_VERTEX_SHADER, GL_FRAGMENT_SHADER}, *program.value()});
if (!spirv || spirv->size() != 2) {
MGLOG_E("InitializeBlitResources failed: SPIR-V generation failed");
return false; return false;
} }
for (SizeT i = 0; i < spirv->size(); ++i) { auto fragmentShader = MakeShared<MG_State::GLState::ShaderObject>(ShaderStage::Fragment, kHiddenBlitFragmentShaderId);
const auto& moduleSpv = spirv.value()[i]; fragmentShader->SetShaderSource(kFragmentShaderSource);
VkShaderModuleCreateInfo smci{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO}; fragmentShader->Compile();
smci.codeSize = moduleSpv.size() * sizeof(Uint); if (!fragmentShader->GetCompileStatus()) {
smci.pCode = moduleSpv.data(); MGLOG_E("InitializeBlitResources failed: fragment shader compile error: %s", fragmentShader->GetInfoLog().c_str());
return false;
VkShaderModule module = VK_NULL_HANDLE;
VK_VERIFY(vkCreateShaderModule(m_device, &smci, nullptr, &module), "InitializeBlitResources, vkCreateShaderModule");
m_blitPipelineResources.shaderModules.push_back(module);
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
stage.stage = (i == 0) ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT;
stage.module = module;
stage.pName = "main";
m_blitPipelineResources.shaderStages.push_back(stage);
} }
VkDescriptorSetLayoutBinding binding{}; m_blitResources.program = MakeShared<MG_State::GLState::ProgramObject>(kHiddenBlitProgramId);
binding.binding = 0; m_blitResources.program->AttachShader(vertexShader);
binding.descriptorCount = 1; m_blitResources.program->AttachShader(fragmentShader);
binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; m_blitResources.program->Link(false);
binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; if (!m_blitResources.program->GetLinkStatus()) {
MGLOG_E("InitializeBlitResources failed: program link error: %s", m_blitResources.program->GetInfoLog().c_str());
return false;
}
VkDescriptorSetLayoutCreateInfo setLayoutInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; m_blitResources.srcRectLocation = m_blitResources.program->GetUniformLocation("uSrcRect");
setLayoutInfo.bindingCount = 1; m_blitResources.dstRectLocation = m_blitResources.program->GetUniformLocation("uDstRect");
setLayoutInfo.pBindings = &binding; m_blitResources.surfaceTransformLocation = m_blitResources.program->GetUniformLocation("uSurfaceTransform");
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &m_blitPipelineResources.descriptorSetLayout), MOBILEGL_ASSERT(m_blitResources.srcRectLocation >= 0, "InitializeBlitResources: missing uSrcRect");
"InitializeBlitResources, vkCreateDescriptorSetLayout"); MOBILEGL_ASSERT(m_blitResources.dstRectLocation >= 0, "InitializeBlitResources: missing uDstRect");
MOBILEGL_ASSERT(m_blitResources.surfaceTransformLocation >= 0,
"InitializeBlitResources: missing uSurfaceTransform");
MOBILEGL_ASSERT(m_blitResources.program->GetUBOSize() > 0,
"InitializeBlitResources: blit program global UBO is empty");
VkPushConstantRange pushConstantRange{}; auto createSampler = [](Uint externalIndex, SamplerFilterMode filter) {
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; auto sampler = MakeShared<MG_State::GLState::SamplerObject>(externalIndex);
pushConstantRange.offset = 0; sampler->SetWrapS(SamplerWrapMode::ClampToEdge);
pushConstantRange.size = sizeof(BlitPushConstants); sampler->SetWrapT(SamplerWrapMode::ClampToEdge);
sampler->SetWrapR(SamplerWrapMode::ClampToEdge);
VkPipelineLayoutCreateInfo pipelineLayoutInfo{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}; sampler->SetMinFilter(filter);
pipelineLayoutInfo.setLayoutCount = 1; sampler->SetMagFilter(filter);
pipelineLayoutInfo.pSetLayouts = &m_blitPipelineResources.descriptorSetLayout; sampler->SetMipmapMode(SamplerMipmapMode::None);
pipelineLayoutInfo.pushConstantRangeCount = 1; sampler->SetLodRange(0.0f, 0.0f);
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange; return sampler;
VK_VERIFY(vkCreatePipelineLayout(m_device, &pipelineLayoutInfo, nullptr, &m_blitPipelineResources.pipelineLayout),
"InitializeBlitResources, vkCreatePipelineLayout");
VkDescriptorPoolSize poolSize{};
poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSize.descriptorCount = m_frameContext.GetFrameCount() > 0 ? m_frameContext.GetFrameCount() : m_config.MaxFramesInFlight;
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
poolInfo.maxSets = poolSize.descriptorCount;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_blitPipelineResources.descriptorPool),
"InitializeBlitResources, vkCreateDescriptorPool");
const Uint32 setCount = std::max<Uint32>(1, m_config.MaxFramesInFlight);
m_blitPipelineResources.descriptorSets.resize(setCount, VK_NULL_HANDLE);
Vector<VkDescriptorSetLayout> layouts(setCount, m_blitPipelineResources.descriptorSetLayout);
VkDescriptorSetAllocateInfo allocInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
allocInfo.descriptorPool = m_blitPipelineResources.descriptorPool;
allocInfo.descriptorSetCount = setCount;
allocInfo.pSetLayouts = layouts.data();
VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, m_blitPipelineResources.descriptorSets.data()),
"InitializeBlitResources, vkAllocateDescriptorSets");
auto createSampler = [&](VkFilter filter, VkSampler& outSampler) {
VkSamplerCreateInfo samplerInfo{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO};
samplerInfo.magFilter = filter;
samplerInfo.minFilter = filter;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.minLod = 0.f;
samplerInfo.maxLod = 0.f;
samplerInfo.maxAnisotropy = 1.f;
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
VK_VERIFY(vkCreateSampler(m_device, &samplerInfo, nullptr, &outSampler),
"InitializeBlitResources, vkCreateSampler");
}; };
createSampler(VK_FILTER_NEAREST, m_blitPipelineResources.nearestSampler);
createSampler(VK_FILTER_LINEAR, m_blitPipelineResources.linearSampler); m_blitResources.nearestSampler = createSampler(kHiddenBlitNearestSamplerId, SamplerFilterMode::Nearest);
m_blitResources.linearSampler = createSampler(kHiddenBlitLinearSamplerId, SamplerFilterMode::Linear);
m_blitResources.samplerBinding = 0;
return true; return true;
} }
void VulkanRenderer::ShutdownBlitResources() { void VulkanRenderer::ShutdownBlitResources() {
if (m_device == VK_NULL_HANDLE) { m_blitResources = {};
m_blitPipelineResources = {};
return;
}
if (m_blitPipelineResources.nearestSampler != VK_NULL_HANDLE) {
vkDestroySampler(m_device, m_blitPipelineResources.nearestSampler, nullptr);
}
if (m_blitPipelineResources.linearSampler != VK_NULL_HANDLE) {
vkDestroySampler(m_device, m_blitPipelineResources.linearSampler, nullptr);
}
if (m_blitPipelineResources.descriptorPool != VK_NULL_HANDLE) {
vkDestroyDescriptorPool(m_device, m_blitPipelineResources.descriptorPool, nullptr);
}
if (m_blitPipelineResources.pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(m_device, m_blitPipelineResources.pipelineLayout, nullptr);
}
if (m_blitPipelineResources.descriptorSetLayout != VK_NULL_HANDLE) {
vkDestroyDescriptorSetLayout(m_device, m_blitPipelineResources.descriptorSetLayout, nullptr);
}
for (auto module : m_blitPipelineResources.shaderModules) {
if (module != VK_NULL_HANDLE) {
vkDestroyShaderModule(m_device, module, nullptr);
}
}
m_blitPipelineResources = {};
} }
VkPipeline VulkanRenderer::GetOrCreateBlitPipeline(const RenderPassEntry& renderPassEntry) { 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");
MOBILEGL_ASSERT(m_uniformDescriptorBinder != nullptr, "GetOrCreateBlitPipeline: descriptor binder is null");
static const VkPipelineVertexInputStateCreateInfo kEmptyVertexInputState { static const VkPipelineVertexInputStateCreateInfo kEmptyVertexInputState {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
}; };
ProgramFactory::CompileOptionFlags transformFlags = 0;
auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(*m_blitResources.program, transformFlags);
PipelineFactory::PipelineCreatePayload payload{ PipelineFactory::PipelineCreatePayload payload{
.programHash = 0xB17FB17FULL, .programHash = m_programFactory->ComputeHash(*m_blitResources.program, transformFlags),
.vertexInputHash = 0, .vertexInputHash = 0,
.pipelineLayout = m_blitPipelineResources.pipelineLayout, .pipelineLayout = m_uniformDescriptorBinder->GetOrCreatePipelineLayout(*m_blitResources.program),
.renderPass = renderPassEntry.renderPass, .renderPass = renderPassEntry.renderPass,
.subpass = 0, .subpass = 0,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
@@ -715,7 +650,7 @@ void main() {
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.stages = &m_blitPipelineResources.shaderStages, .stages = &stages,
.vertexInputState = &kEmptyVertexInputState .vertexInputState = &kEmptyVertexInputState
}; };
return m_pipelineFactory->GetOrCreatePipeline(payload); return m_pipelineFactory->GetOrCreatePipeline(payload);
@@ -952,8 +887,7 @@ void main() {
sourceTexture->GetExternalIndex()); sourceTexture->GetExternalIndex());
return false; return false;
} }
auto* sourceResource = m_textureManager->SyncTextureAndGetDescriptor(*sourceTexture); if (m_textureManager->SyncTextureAndGetDescriptor(*sourceTexture) == nullptr) {
if (sourceResource == nullptr) {
MGLOG_E("BlitFramebuffer skipped: failed to resolve source textureId=%d after sampling transition", MGLOG_E("BlitFramebuffer skipped: failed to resolve source textureId=%d after sampling transition",
sourceTexture->GetExternalIndex()); sourceTexture->GetExternalIndex());
return false; return false;
@@ -977,30 +911,16 @@ void main() {
scissor.extent = {static_cast<Uint32>(renderPassEntry.extent.x()), static_cast<Uint32>(renderPassEntry.extent.y())}; scissor.extent = {static_cast<Uint32>(renderPassEntry.extent.x()), static_cast<Uint32>(renderPassEntry.extent.y())};
vkCmdSetScissor(frame.commandBuffer, 0, 1, &scissor); vkCmdSetScissor(frame.commandBuffer, 0, 1, &scissor);
MOBILEGL_ASSERT(m_blitResources.program != nullptr, "TryBlitToDefaultFramebufferWithShader: blit program is null");
const VkPipeline pipeline = GetOrCreateBlitPipeline(renderPassEntry); const VkPipeline pipeline = GetOrCreateBlitPipeline(renderPassEntry);
MOBILEGL_ASSERT(pipeline != VK_NULL_HANDLE, "TryBlitToDefaultFramebufferWithShader: blit pipeline is null");
vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
const Uint32 frameIndex = m_frameContext.GetCurrentFrameIndex() % static_cast<Uint32>(m_blitPipelineResources.descriptorSets.size()); auto* blitProgramData = static_cast<Uint8*>(m_blitResources.program->MapUBO());
const VkSampler sampler = (filter == GL_LINEAR) ? m_blitPipelineResources.linearSampler MOBILEGL_ASSERT(blitProgramData != nullptr, "TryBlitToDefaultFramebufferWithShader: blit UBO is null");
: m_blitPipelineResources.nearestSampler; std::fill(blitProgramData, blitProgramData + m_blitResources.program->GetUBOSize(), Uint8{0});
VkDescriptorImageInfo imageInfo{
.sampler = sampler,
.imageView = sourceResource->view,
.imageLayout = sourceResource->layout,
};
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
write.dstSet = m_blitPipelineResources.descriptorSets[frameIndex];
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &imageInfo;
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
vkCmdBindDescriptorSets(frame.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, BlitUniformData blitUniformData{};
m_blitPipelineResources.pipelineLayout, 0, 1,
&m_blitPipelineResources.descriptorSets[frameIndex], 0, nullptr);
BlitPushConstants pushConstants{};
const float srcWidth = static_cast<float>(srcBinding.extent.x()); const float srcWidth = static_cast<float>(srcBinding.extent.x());
const float srcHeight = static_cast<float>(srcBinding.extent.y()); const float srcHeight = static_cast<float>(srcBinding.extent.y());
const float dstWidth = static_cast<float>(dstBinding.extent.x()); const float dstWidth = static_cast<float>(dstBinding.extent.x());
@@ -1016,19 +936,38 @@ void main() {
default: default:
break; break;
} }
pushConstants.srcRect[0] = static_cast<float>(srcX0) / srcWidth; blitUniformData.srcRect[0] = static_cast<float>(srcX0) / srcWidth;
pushConstants.srcRect[1] = static_cast<float>(srcY0) / srcHeight; blitUniformData.srcRect[1] = static_cast<float>(srcY0) / srcHeight;
pushConstants.srcRect[2] = static_cast<float>(srcX1 - srcX0) / srcWidth; blitUniformData.srcRect[2] = static_cast<float>(srcX1 - srcX0) / srcWidth;
pushConstants.srcRect[3] = static_cast<float>(srcY1 - srcY0) / srcHeight; blitUniformData.srcRect[3] = static_cast<float>(srcY1 - srcY0) / srcHeight;
pushConstants.dstRect[0] = static_cast<float>(dstX0) / dstNormWidth; blitUniformData.dstRect[0] = static_cast<float>(dstX0) / dstNormWidth;
pushConstants.dstRect[1] = static_cast<float>(dstY0) / dstNormHeight; blitUniformData.dstRect[1] = static_cast<float>(dstY0) / dstNormHeight;
pushConstants.dstRect[2] = static_cast<float>(dstX1 - dstX0) / dstNormWidth; blitUniformData.dstRect[2] = static_cast<float>(dstX1 - dstX0) / dstNormWidth;
pushConstants.dstRect[3] = static_cast<float>(dstY1 - dstY0) / dstNormHeight; blitUniformData.dstRect[3] = static_cast<float>(dstY1 - dstY0) / dstNormHeight;
pushConstants.surfaceTransform = static_cast<Uint32>(ToBlitSurfaceTransform(m_swapchainObject.GetPreTransform())); blitUniformData.surfaceTransform = static_cast<Int>(ToBlitSurfaceTransform(m_swapchainObject.GetPreTransform()));
vkCmdPushConstants(frame.commandBuffer, m_blitPipelineResources.pipelineLayout, auto writeUniform = [&](Int location, const void* data, SizeT size) {
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, MOBILEGL_ASSERT(location >= 0, "TryBlitToDefaultFramebufferWithShader: invalid uniform location");
sizeof(pushConstants), &pushConstants); const Uint offset = m_blitResources.program->GetUniformOffset(static_cast<Uint>(location));
MOBILEGL_ASSERT(offset + size <= m_blitResources.program->GetUBOSize(),
"TryBlitToDefaultFramebufferWithShader: uniform write out of bounds");
memcpy(blitProgramData + offset, data, size);
};
writeUniform(m_blitResources.srcRectLocation, blitUniformData.srcRect, sizeof(blitUniformData.srcRect));
writeUniform(m_blitResources.dstRectLocation, blitUniformData.dstRect, sizeof(blitUniformData.dstRect));
writeUniform(m_blitResources.surfaceTransformLocation, &blitUniformData.surfaceTransform,
sizeof(blitUniformData.surfaceTransform));
const auto samplerBindingOverride = UniformDescriptorBinder::SamplerBindingOverride{
.binding = m_blitResources.samplerBinding,
.texture = sourceTexture.get(),
.sampler = (filter == GL_LINEAR ? m_blitResources.linearSampler.get()
: m_blitResources.nearestSampler.get()),
};
const Bool bound = m_uniformDescriptorBinder->BindProgramUniformBuffers(
frame.commandBuffer, *m_blitResources.program, m_frameContext.GetCurrentFrameIndex(),
&samplerBindingOverride);
MOBILEGL_ASSERT(bound, "TryBlitToDefaultFramebufferWithShader: BindProgramUniformBuffers failed");
vkCmdDraw(frame.commandBuffer, 3, 1, 0, 0); vkCmdDraw(frame.commandBuffer, 3, 1, 0, 0);
return true; return true;
} }
@@ -28,6 +28,7 @@
namespace MobileGL::MG_State::GLState { namespace MobileGL::MG_State::GLState {
class FramebufferObject; class FramebufferObject;
class ProgramObject; class ProgramObject;
class SamplerObject;
class VertexArrayObject; class VertexArrayObject;
} // namespace MobileGL::MG_State::GLState } // namespace MobileGL::MG_State::GLState
@@ -96,22 +97,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void RecreateSwapchain(); void RecreateSwapchain();
private: private:
struct BlitPushConstants { struct BlitUniformData {
float srcRect[4] = {0.f, 0.f, 1.f, 1.f}; float srcRect[4] = {0.f, 0.f, 1.f, 1.f};
float dstRect[4] = {0.f, 0.f, 1.f, 1.f}; float dstRect[4] = {0.f, 0.f, 1.f, 1.f};
Uint32 surfaceTransform = 0; Int surfaceTransform = 0;
Uint32 padding[3] = {0, 0, 0}; Int padding[3] = {0, 0, 0};
}; };
struct BlitPipelineResources { struct BlitResources {
VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE; SharedPtr<MG_State::GLState::ProgramObject> program;
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE; SharedPtr<MG_State::GLState::SamplerObject> nearestSampler;
VkDescriptorPool descriptorPool = VK_NULL_HANDLE; SharedPtr<MG_State::GLState::SamplerObject> linearSampler;
Vector<VkDescriptorSet> descriptorSets; Int srcRectLocation = -1;
Vector<VkPipelineShaderStageCreateInfo> shaderStages; Int dstRectLocation = -1;
Vector<VkShaderModule> shaderModules; Int surfaceTransformLocation = -1;
VkSampler nearestSampler = VK_NULL_HANDLE; Uint32 samplerBinding = 0;
VkSampler linearSampler = VK_NULL_HANDLE;
}; };
NativeWindowType m_window = 0; NativeWindowType m_window = 0;
@@ -156,7 +156,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
UniquePtr<VkRenderPassManager> m_renderPassManager; UniquePtr<VkRenderPassManager> m_renderPassManager;
UniquePtr<VkTextureManager> m_textureManager; UniquePtr<VkTextureManager> m_textureManager;
UniquePtr<VkSamplerManager> m_samplerManager; UniquePtr<VkSamplerManager> m_samplerManager;
BlitPipelineResources m_blitPipelineResources; BlitResources m_blitResources;
void CreateInstance(); void CreateInstance();
VkResult SetupDebugMessenger(); VkResult SetupDebugMessenger();