mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Chore] (MG_Backend/DirectVulkan): implements BlitFramebuffer using backend manager APIs
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user