Merge branch 'dev' into Feat/Backend-Direct-Vulkan

This commit is contained in:
BZLZHH
2026-02-21 15:02:59 +08:00
66 changed files with 6120 additions and 6407 deletions
@@ -0,0 +1,115 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#include "BackendObject_DirectVulkan.h"
#include "MG_Backend/BackendObject.h"
#include "DirectVulkan.h"
namespace MobileGL::MG_Backend::DirectVulkan {
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
void BackendObject_DirectVulkan::InitWindowSurface() {
auto nativeWindow = reinterpret_cast<NativeWindowType>(m_windowHandle.Handle);
pVulkanRenderer = MakeUnique<MG_Backend::DirectVulkan::VulkanRenderer>(nativeWindow);
pVulkanRenderer->Initialize();
}
void BackendObject_DirectVulkan::Initialize() {
m_initialized = true;
}
void BackendObject_DirectVulkan::InitCapabilities() {
if (!m_initialized) {
MGLOG_E("Cannot initialize capabilities before backend is initialized");
return;
}
MG_Util::BackendLoader::FillInVulkanCapabilities(m_vulkanCaps, pVulkanRenderer->GetPhysicalDevice().properties);
UpdateDynamicBackendParameters();
}
const RendererInfo& BackendObject_DirectVulkan::GetRendererInfo() const {
static RendererInfo RendererInfo = {
.RendererName = "Magma", // Renderer Name
.BackendName = "Direct (Vulkan)", // Backend Name
.ExtraVendor = Nullopt, // Extra vendor
.RendererGLInfo =
{
.TargetGLVersion = {3, 3, 0}, // Target OpenGL Version
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
.Extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32, // OpenGL Extensions
V_OpenGL33},
.IsCompatibilityProfile = false // Is Compatibility Profile
},
.StaticBackendCapability = {.AllowVSOnlyPrograms = false} // Backend Capability
};
return RendererInfo;
}
String BackendObject_DirectVulkan::GetBackendAPIVersionString() const {
if (!m_initialized) {
return "<uninitialized DirectVulkan backend>";
}
// Format:
// <GPU Name>, Vulkan <Vulkan Version>, Driver <Driver Version>
String str = m_vulkanCaps.DeviceName + ", Vulkan " + m_vulkanCaps.VulkanAPIVersion.toString() + ", Driver " +
m_vulkanCaps.DriverVersionString;
return str;
}
BackendType BackendObject_DirectVulkan::GetBackendType() const {
return BackendType::DirectVulkan;
}
const GlobalBackendFunctionsTable& BackendObject_DirectVulkan::GetBackendFunctions() const {
static GlobalBackendFunctionsTable funcsTable;
static Bool funcsTableInitialized = false;
if (!funcsTableInitialized) {
funcsTable.Present = Present;
funcsTable.GL.DrawArrays = DrawArrays;
funcsTable.GL.DrawElements = DrawElements;
funcsTable.GL.DrawElementsBaseVertex = DrawElementsBaseVertex;
funcsTable.GL.MultiDrawElements = MultiDrawElements;
funcsTable.GL.MultiDrawElementsBaseVertex = MultiDrawElementsBaseVertex;
funcsTable.GL.MultiDrawElementsIndirect = MultiDrawElementsIndirect;
funcsTable.GL.MultiDrawArraysIndirect = MultiDrawArraysIndirect;
funcsTable.GL.DrawRangeElementsBaseVertex = DrawRangeElementsBaseVertex;
funcsTable.GL.DrawRangeElements = DrawRangeElements;
funcsTable.GL.DrawElementsInstancedBaseVertexBaseInstance = DrawElementsInstancedBaseVertexBaseInstance;
funcsTable.GL.DrawElementsInstancedBaseVertex = DrawElementsInstancedBaseVertex;
funcsTable.GL.DrawElementsInstancedBaseInstance = DrawElementsInstancedBaseInstance;
funcsTable.GL.DrawElementsInstanced = DrawElementsInstanced;
funcsTable.GL.DrawArraysInstancedBaseInstance = DrawArraysInstancedBaseInstance;
funcsTable.GL.DrawArraysInstanced = DrawArraysInstanced;
funcsTable.GL.DrawElementsIndirect = DrawElementsIndirect;
funcsTable.GL.DrawArraysIndirect = DrawArraysIndirect;
funcsTable.GL.Clear = Clear;
funcsTable.GL.ClearBufferfi = ClearBufferfi;
funcsTable.GL.ClearBufferfv = ClearBufferfv;
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
funcsTable.GL.ClearBufferiv = ClearBufferiv;
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
funcsTable.GL.CopyTexSubImage2D = CopyTexSubImage2D;
funcsTable.GL.GenerateMipmap = GenerateMipmap;
funcsTable.GL.ReadPixels = ReadPixels;
funcsTable.GL.GetTexImage = GetTexImage;
funcsTableInitialized = true;
}
return funcsTable;
}
const DynamicBackendParameters& BackendObject_DirectVulkan::GetDynamicParameters() const {
return m_dynamicParameters;
}
void BackendObject_DirectVulkan::UpdateDynamicBackendParameters() {
m_dynamicParameters.UniformBufferOffsetAlignment = m_vulkanCaps.UniformBufferOffsetAlignment;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -0,0 +1,36 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/BackendObject_DirectVulkan.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
#pragma once
#include <Includes.h>
#include "../BackendObject.h"
#include <MG_Util/BackendLoaders/Vulkan/Loader.h>
namespace MobileGL::MG_Backend::DirectVulkan {
class BackendObject_DirectVulkan : public BackendObject {
public:
~BackendObject_DirectVulkan() override;
void Initialize() override;
void InitWindowSurface() override;
void InitCapabilities() override;
const RendererInfo& GetRendererInfo() const override;
String GetBackendAPIVersionString() const override;
const GlobalBackendFunctionsTable& GetBackendFunctions() const override;
const DynamicBackendParameters& GetDynamicParameters() const override;
BackendType GetBackendType() const override;
private:
void UpdateDynamicBackendParameters();
Bool m_initialized = false;
DynamicBackendParameters m_dynamicParameters;
MG_External::VulkanCapabilities m_vulkanCaps;
};
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -13,6 +13,40 @@
namespace MobileGL::MG_Backend::DirectVulkan {
UniquePtr<VulkanRenderer> pVulkanRenderer = nullptr;
void ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) {}
void ClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {}
void ClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {}
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {}
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex) {}
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount) {}
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
GLsizei drawcount, const GLint* basevertex) {}
void MultiDrawElementsIndirect(GLenum mode, GLenum type, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void MultiDrawArraysIndirect(GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) {}
void DrawRangeElementsBaseVertex(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type,
const void* indices, GLint basevertex) {}
void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) {}
void DrawElementsInstancedBaseVertexBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex, GLuint baseinstance) {}
void DrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLint basevertex) {}
void DrawElementsInstancedBaseInstance(GLenum mode, GLsizei count, GLenum type, const void* indices,
GLsizei instancecount, GLuint baseinstance) {}
void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount) {}
void DrawElementsIndirect(GLenum mode, GLenum type, const void* indirect) {}
void DrawArraysInstancedBaseInstance(GLenum mode, GLint first, GLsizei count, GLsizei instancecount,
GLuint baseinstance) {}
void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount) {}
void DrawArraysIndirect(GLenum mode, const void* indirect) {}
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
GLsizei height, GLint border) {}
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height) {}
void GenerateMipmap(GLenum target) {}
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels) {}
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {}
void Clear(GLbitfield mask) {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Clear called with null VulkanRenderer");
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::Clear called with null GL context");
@@ -85,11 +119,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return;
}
const SizeT byteOffset = reinterpret_cast<SizeT>(indices);
const auto byteOffset = reinterpret_cast<SizeT>(indices);
const SizeT requiredBytes = static_cast<SizeT>(count) * indexSize;
if (byteOffset + requiredBytes > indexBuffer->GetSize()) {
MGLOG_W("DrawElements skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)",
byteOffset, requiredBytes, indexBuffer->GetSize());
MGLOG_W("DrawElements skipped: index range out of bounds (offset=%zu, size=%zu, buffer=%zu)", byteOffset,
requiredBytes, indexBuffer->GetSize());
return;
}
@@ -170,5 +204,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
pVulkanRenderer->BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter,
readFboExternalIndex, drawFboExternalIndex, readIsDefault, drawIsDefault);
}
} // namespace MobileGL::MG_Backend::DirectVulkan
void Present() {
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::Present called with null VulkanRenderer");
pVulkanRenderer->Present();
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -49,5 +49,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
GLsizei height);
void GenerateMipmap(GLenum target);
const GLubyte* GetString(GLenum name);
} // namespace MobileGL::MG_Backend::DirectVulkan
void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void* pixels);
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels);
void Present();
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -61,12 +61,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Shutdown();
}
VkPipeline VulkanRenderer::GetOrCreatePipeline(
const MG_State::GLState::ProgramObject& program, VkPipelineLayout pipelineLayout, Uint64 vertexInputHash,
const VkPipelineVertexInputStateCreateInfo& vertexInputState) {
VkPipeline VulkanRenderer::GetOrCreatePipeline(const MG_State::GLState::ProgramObject& program,
VkPipelineLayout pipelineLayout, Uint64 vertexInputHash,
const VkPipelineVertexInputStateCreateInfo& vertexInputState) {
MOBILEGL_ASSERT(m_pipelineFactory != nullptr, "PipelineFactory is not initialized");
MOBILEGL_ASSERT(m_programFactory != nullptr, "ProgramFactory is not initialized");
auto& stages = m_programFactory->GetOrCreatePipelineShaderStages(program, ProgramFactory::CompileOptionBit::None);
auto& stages =
m_programFactory->GetOrCreatePipelineShaderStages(program, ProgramFactory::CompileOptionBit::None);
if (stages.empty()) {
MGLOG_D("GetOrCreatePipeline skipped: program has no shader stages");
return VK_NULL_HANDLE;
@@ -74,34 +75,58 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const Uint64 programHash = m_programFactory->ComputeHash(program, ProgramFactory::CompileOptionBit::None);
auto toVkCompareOp = [](DepthTestFunc func) -> VkCompareOp {
switch (func) {
case DepthTestFunc::Never: return VK_COMPARE_OP_NEVER;
case DepthTestFunc::Less: return VK_COMPARE_OP_LESS;
case DepthTestFunc::Equal: return VK_COMPARE_OP_EQUAL;
case DepthTestFunc::LessEqual: return VK_COMPARE_OP_LESS_OR_EQUAL;
case DepthTestFunc::Greater: return VK_COMPARE_OP_GREATER;
case DepthTestFunc::NotEqual: return VK_COMPARE_OP_NOT_EQUAL;
case DepthTestFunc::GreaterEqual: return VK_COMPARE_OP_GREATER_OR_EQUAL;
case DepthTestFunc::Always: return VK_COMPARE_OP_ALWAYS;
default: return VK_COMPARE_OP_ALWAYS;
case DepthTestFunc::Never:
return VK_COMPARE_OP_NEVER;
case DepthTestFunc::Less:
return VK_COMPARE_OP_LESS;
case DepthTestFunc::Equal:
return VK_COMPARE_OP_EQUAL;
case DepthTestFunc::LessEqual:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case DepthTestFunc::Greater:
return VK_COMPARE_OP_GREATER;
case DepthTestFunc::NotEqual:
return VK_COMPARE_OP_NOT_EQUAL;
case DepthTestFunc::GreaterEqual:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case DepthTestFunc::Always:
return VK_COMPARE_OP_ALWAYS;
default:
return VK_COMPARE_OP_ALWAYS;
}
};
auto toVkBlendFactor = [](BlendFactor factor) -> VkBlendFactor {
switch (factor) {
case BlendFactor::Zero: return VK_BLEND_FACTOR_ZERO;
case BlendFactor::One: return VK_BLEND_FACTOR_ONE;
case BlendFactor::SrcColor: return VK_BLEND_FACTOR_SRC_COLOR;
case BlendFactor::OneMinusSrcColor: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor: return VK_BLEND_FACTOR_DST_COLOR;
case BlendFactor::OneMinusDstColor: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha: return VK_BLEND_FACTOR_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha: return VK_BLEND_FACTOR_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BlendFactor::ConstantColor: return VK_BLEND_FACTOR_CONSTANT_COLOR;
case BlendFactor::OneMinusConstantColor: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case BlendFactor::ConstantAlpha: return VK_BLEND_FACTOR_CONSTANT_ALPHA;
case BlendFactor::OneMinusConstantAlpha: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
default: return VK_BLEND_FACTOR_ONE;
case BlendFactor::Zero:
return VK_BLEND_FACTOR_ZERO;
case BlendFactor::One:
return VK_BLEND_FACTOR_ONE;
case BlendFactor::SrcColor:
return VK_BLEND_FACTOR_SRC_COLOR;
case BlendFactor::OneMinusSrcColor:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BlendFactor::DstColor:
return VK_BLEND_FACTOR_DST_COLOR;
case BlendFactor::OneMinusDstColor:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BlendFactor::SrcAlpha:
return VK_BLEND_FACTOR_SRC_ALPHA;
case BlendFactor::OneMinusSrcAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BlendFactor::DstAlpha:
return VK_BLEND_FACTOR_DST_ALPHA;
case BlendFactor::OneMinusDstAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BlendFactor::ConstantColor:
return VK_BLEND_FACTOR_CONSTANT_COLOR;
case BlendFactor::OneMinusConstantColor:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case BlendFactor::ConstantAlpha:
return VK_BLEND_FACTOR_CONSTANT_ALPHA;
case BlendFactor::OneMinusConstantAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
default:
return VK_BLEND_FACTOR_ONE;
}
};
@@ -153,8 +178,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VulkanRenderer::CreateFrameContexts() {
VK_VERIFY(m_frameContext.Initialize(m_device, m_commandPool, m_config.MaxFramesInFlight),
"CreateFrameContexts");
VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores(
m_device, static_cast<Uint32>(m_swapchainObject.GetImageCount())),
VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores(m_device,
static_cast<Uint32>(m_swapchainObject.GetImageCount())),
"CreateFrameContexts, InitializeSwapchainSemaphores");
MGLOG_I("CreateFrameContexts completed");
}
@@ -186,8 +211,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
m_config.MaxFramesInFlight, 16, 64, 4 * 1024 * 1024,
m_textureSamplerManager.get(),
m_framebufferManager.get())) {
m_textureSamplerManager.get(), m_framebufferManager.get())) {
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
m_uniformDescriptorBinder.reset();
}
@@ -341,8 +365,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
m_swapchainObject.SetImageLayout(imageIndex, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
}
@@ -361,7 +385,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkCmdClearAttachments(commandBuffer, 1, &clearAttachment, 1, &clearRect);
}
void VulkanRenderer::RecordDepthStencilClear(VkCommandBuffer commandBuffer, GLbitfield mask, Float depth, Uint32 stencil) {
void VulkanRenderer::RecordDepthStencilClear(VkCommandBuffer commandBuffer, GLbitfield mask, Float depth,
Uint32 stencil) {
if (m_activeDepthStencilFormat == VK_FORMAT_UNDEFINED) {
return;
}
@@ -404,7 +429,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask =
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
barrier.oldLayout = oldLayout;
barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
@@ -420,8 +446,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
barrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, 0,
0, nullptr, 0, nullptr, 1, &barrier);
m_depthStencilImageLayouts[imageIndex] = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
@@ -432,13 +458,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return;
}
const auto drawFbo = MG_State::pGLContext
? MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()
: nullptr;
const auto drawFbo =
MG_State::pGLContext
? MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject()
: nullptr;
const auto defaultFboInfo = MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo;
const auto defaultFbo = defaultFboInfo ? defaultFboInfo->defaultFBO : nullptr;
const Bool drawTargetsDefault = (drawFbo == defaultFbo) || (drawFbo == nullptr && defaultFbo != nullptr);
const Uint drawFboExternalIndex = drawFbo ? drawFbo->GetExternalIndex() : (defaultFbo ? defaultFbo->GetExternalIndex() : 0U);
const Uint drawFboExternalIndex =
drawFbo ? drawFbo->GetExternalIndex() : (defaultFbo ? defaultFbo->GetExternalIndex() : 0U);
const Uint64 drawTargetKey = BuildPendingClearKey(drawFboExternalIndex, drawTargetsDefault);
if (frame.isCommandRecording && m_isMainRenderPassActive) {
@@ -502,8 +530,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
presentBarrier.subresourceRange.levelCount = 1;
presentBarrier.subresourceRange.baseArrayLayer = 0;
presentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier);
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
&presentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
@@ -667,8 +696,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
Bool VulkanRenderer::UploadAndBindVertexStreams(
const VertexInputStateFactory::BackendVertexInputState& vertexInputState,
const DrawArrayPayload& payload,
const VertexInputStateFactory::BackendVertexInputState& vertexInputState, const DrawArrayPayload& payload,
VkCommandBuffer commandBuffer) {
if (vertexInputState.bindings.empty()) {
return true;
@@ -783,8 +811,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
}
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.program, pipelineLayoutToUse, vertexInputHash,
*vertexInputInfo);
VkPipeline pipelineToBind =
GetOrCreatePipeline(*payload.program, pipelineLayoutToUse, vertexInputHash, *vertexInputInfo);
if (pipelineToBind == VK_NULL_HANDLE) {
MGLOG_D("DrawArrays skipped: failed to create/get pipeline");
return;
@@ -898,8 +926,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
}
VkPipeline pipelineToBind = GetOrCreatePipeline(*payload.drawArray.program, pipelineLayoutToUse, vertexInputHash,
*vertexInputInfo);
VkPipeline pipelineToBind =
GetOrCreatePipeline(*payload.drawArray.program, pipelineLayoutToUse, vertexInputHash, *vertexInputInfo);
if (pipelineToBind == VK_NULL_HANDLE) {
MGLOG_D("DrawElements skipped: failed to create/get pipeline");
return;
@@ -927,10 +955,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
const auto activeExtent = m_activeRenderExtent;
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
if (m_uniformDescriptorBinder &&
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, pipelineLayoutToUse,
*payload.drawArray.program,
m_frameContext.GetCurrentFrameIndex())) {
if (m_uniformDescriptorBinder && !m_uniformDescriptorBinder->BindProgramUniformBuffers(
commandBuffer, pipelineLayoutToUse, *payload.drawArray.program,
m_frameContext.GetCurrentFrameIndex())) {
MGLOG_D("DrawElements skipped: failed to bind uniform descriptors");
return;
}
@@ -1044,8 +1071,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
toTransferDstBarrier.subresourceRange.levelCount = 1;
toTransferDstBarrier.subresourceRange.baseArrayLayer = 0;
toTransferDstBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 1, &toTransferDstBarrier);
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toTransferDstBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
}
@@ -1064,8 +1091,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
vkCmdBlitImage(commandBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_swapchainObject.GetImage(m_imageIndexAcquired), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1, &blitRegion, (filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST));
m_swapchainObject.GetImage(m_imageIndexAcquired), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
&blitRegion, (filter == GL_LINEAR ? VK_FILTER_LINEAR : VK_FILTER_NEAREST));
VkImageMemoryBarrier toPresentBarrier{};
toPresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
@@ -1081,8 +1108,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
toPresentBarrier.subresourceRange.levelCount = 1;
toPresentBarrier.subresourceRange.baseArrayLayer = 0;
toPresentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &toPresentBarrier);
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0,
nullptr, 0, nullptr, 1, &toPresentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
(void)srcExtent;
@@ -1115,11 +1142,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_pendingClears.clear();
} else if (!m_pendingClears.empty()) {
auto activateTarget = [&](Bool targetIsDefault, Uint targetFboExternalIndex) -> Bool {
const Bool alreadyMatched =
frame.isCommandRecording &&
m_isMainRenderPassActive &&
(targetIsDefault == m_activeRenderTargetIsDefault) &&
(targetIsDefault || targetFboExternalIndex == m_activeDrawFboExternalIndex);
const Bool alreadyMatched = frame.isCommandRecording && m_isMainRenderPassActive &&
(targetIsDefault == m_activeRenderTargetIsDefault) &&
(targetIsDefault || targetFboExternalIndex == m_activeDrawFboExternalIndex);
if (alreadyMatched) {
return true;
}
@@ -1179,7 +1204,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (!m_framebufferManager->EnsureOffscreenColorTarget(targetFboExternalIndex, *pendingFbo)) {
return false;
}
if (!m_framebufferManager->TransitionOffscreenColorToAttachment(commandBuffer, targetFboExternalIndex)) {
if (!m_framebufferManager->TransitionOffscreenColorToAttachment(commandBuffer,
targetFboExternalIndex)) {
return false;
}
@@ -1211,8 +1237,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
presentBarrier.subresourceRange.baseArrayLayer = 0;
presentBarrier.subresourceRange.layerCount = 1;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0, 0, nullptr, 0, nullptr, 1, &presentBarrier);
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
&presentBarrier);
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
}
@@ -1706,11 +1732,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
VkFormat VulkanRenderer::FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice) {
const VkFormat candidates[] = {
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D32_SFLOAT
};
const VkFormat candidates[] = {VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT};
for (VkFormat format : candidates) {
VkFormatProperties props{};
vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
@@ -1759,9 +1781,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkMemoryAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &m_depthStencilImageMemories[i]), "vkAllocateMemory(depth)");
VK_VERIFY(vkBindImageMemory(m_device, m_depthStencilImages[i], m_depthStencilImageMemories[i], 0), "vkBindImageMemory(depth)");
allocInfo.memoryTypeIndex =
FindMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VK_VERIFY(vkAllocateMemory(m_device, &allocInfo, nullptr, &m_depthStencilImageMemories[i]),
"vkAllocateMemory(depth)");
VK_VERIFY(vkBindImageMemory(m_device, m_depthStencilImages[i], m_depthStencilImageMemories[i], 0),
"vkBindImageMemory(depth)");
VkImageViewCreateInfo viewInfo{};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
@@ -1776,7 +1801,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &m_depthStencilImageViews[i]), "vkCreateImageView(depth)");
VK_VERIFY(vkCreateImageView(m_device, &viewInfo, nullptr, &m_depthStencilImageViews[i]),
"vkCreateImageView(depth)");
}
}
@@ -1994,7 +2020,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void VulkanRenderer::RecreateSwapchain() {
// Handle cases like minimize on Windows, where swapchain could return a 0x0 extent
const auto swapchainCapabilities = SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
const auto swapchainCapabilities =
SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
if (swapchainCapabilities.capabilities.currentExtent.width == 0 ||
swapchainCapabilities.capabilities.currentExtent.height == 0) {
return;
@@ -2005,8 +2032,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
ShutdownSwapchain();
CreateSwapchain();
VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores(
m_device, static_cast<Uint32>(m_swapchainObject.GetImageCount())),
VK_VERIFY(m_frameContext.InitializeSwapchainSemaphores(m_device,
static_cast<Uint32>(m_swapchainObject.GetImageCount())),
"RecreateSwapchain, InitializeSwapchainSemaphores");
CreateDepthStencilResources();
CreateDefaultRenderPass();
@@ -2033,4 +2060,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_pendingClears.clear();
}
const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const {
return m_physicalDevice;
}
} // namespace MobileGL::MG_Backend::DirectVulkan
@@ -26,7 +26,7 @@
namespace MobileGL::MG_State::GLState {
class ProgramObject;
class VertexArrayObject;
}
} // namespace MobileGL::MG_State::GLState
namespace MobileGL::MG_Backend::DirectVulkan {
struct DrawArrayPayload {
@@ -43,6 +43,21 @@ namespace MobileGL::MG_Backend::DirectVulkan {
SizeT indexByteOffset = 0;
};
struct QueueFamilyIndices {
Int32 graphicsFamily = -1;
Int32 presentFamily = -1;
};
struct PhysicalDevice {
QueueFamilyIndices queueFamilies;
VkPhysicalDeviceProperties properties;
VkPhysicalDevice handle = VK_NULL_HANDLE;
Bool IsComplete() const {
return handle != VK_NULL_HANDLE && queueFamilies.graphicsFamily != -1 && queueFamilies.presentFamily != -1;
}
};
class VulkanRenderer {
public:
VulkanRenderer(NativeWindowType window, const VulkanRendererConfig& cfg = {});
@@ -63,6 +78,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void Render();
void Present();
const PhysicalDevice& GetPhysicalDevice() const;
void RecreateSwapchain();
private:
@@ -75,23 +92,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Bool targetsDefaultFramebuffer = true;
};
struct QueueFamilyIndices {
Int32 graphicsFamily = -1;
Int32 presentFamily = -1;
};
struct PhysicalDevice {
QueueFamilyIndices queueFamilies;
VkPhysicalDeviceProperties properties;
VkPhysicalDevice handle = VK_NULL_HANDLE;
Bool IsComplete() const {
return handle != VK_NULL_HANDLE &&
queueFamilies.graphicsFamily != -1 &&
queueFamilies.presentFamily != -1;
}
};
NativeWindowType m_window = 0;
VulkanRendererConfig m_config;
@@ -178,41 +178,33 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void CollectDeferredBufferReleases(Uint32 frameIndex);
Bool EnsureFrameUploadBufferCapacity(Uint32 frameIndex, Bool isIndexBuffer, VkDeviceSize requiredEndOffset,
VkDeviceSize minCapacity, VkBufferUsageFlags usage);
Bool UploadAndBindVertexStreams(
const VertexInputStateFactory::BackendVertexInputState& vertexInputState,
const DrawArrayPayload& payload,
VkCommandBuffer commandBuffer);
Bool UploadAndBindVertexStreams(const VertexInputStateFactory::BackendVertexInputState& vertexInputState,
const DrawArrayPayload& payload, VkCommandBuffer commandBuffer);
void ShutdownSwapchain();
static Int GetPresentQueueFamilyIndex(
const PhysicalDevice& physicalDevice, VkSurfaceKHR surface,
const Vector<VkQueueFamilyProperties>& queueFamilies, Int preferredFamilyIndex = -1);
static Int GetPresentQueueFamilyIndex(const PhysicalDevice& physicalDevice, VkSurfaceKHR surface,
const Vector<VkQueueFamilyProperties>& queueFamilies,
Int preferredFamilyIndex = -1);
static Vector<VkQueueFamilyProperties> GetQueueFamilyFromPhysicalDevice(VkPhysicalDevice device);
static Int GetQueueFamilyIndex(const Vector<VkQueueFamilyProperties>& queueFamilies, VkQueueFlagBits flag);
static Vector<VkExtensionProperties> EnumerateInstanceExtensions();
static Bool IsNecessaryDeviceExtensionSupported(VkPhysicalDevice device);
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, const PhysicalDevice& compareWithDevice, PhysicalDevice& outBetterDevice);
static Bool GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface,
const PhysicalDevice& compareWithDevice,
PhysicalDevice& outBetterDevice);
Uint32 FindMemoryType(Uint32 typeFilter, VkMemoryPropertyFlags properties) const;
static Uint64 BuildPendingClearKey(Uint drawFboExternalIndex, Bool targetsDefaultFramebuffer);
static Bool HasStencilComponent(VkFormat format);
static VkFormat FindSupportedDepthStencilFormat(VkPhysicalDevice physicalDevice);
static constexpr VkDynamicState s_dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
static constexpr const char* s_validationLayerNames[] = {
"VK_LAYER_KHRONOS_validation"
};
static constexpr const char* s_deviceExtensionNames[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
static constexpr VkDynamicState s_dynamicStates[] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
static constexpr const char* s_validationLayerNames[] = {"VK_LAYER_KHRONOS_validation"};
static constexpr const char* s_deviceExtensionNames[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
static Bool CheckValidationLayerSupport();
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData);
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData);
};
} // namespace MobileGL::MG_Backend::DirectVulkan