mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 20:58:31 +09:00
[Feat] (MG_Backend/DirectVulkan): implements uniform, ubo
This commit is contained in:
@@ -215,6 +215,7 @@ set(SOURCE_FILES
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/PipelineFactory.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/ProgramFactory.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateBuilder.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VertexInputStateFactory.cpp
|
||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VkBufferObject.cpp
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.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 "UniformDescriptorBinder.h"
|
||||
|
||||
#include "MG_State/GLState/Core.h"
|
||||
#include "MG_State/GLState/ProgramState/ProgramObject.h"
|
||||
#include "MG_Util/ShaderTranspiler/Types.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDeviceSize UniformDescriptorBinder::AlignUp(VkDeviceSize value, VkDeviceSize alignment) {
|
||||
if (alignment == 0) {
|
||||
return value;
|
||||
}
|
||||
return (value + alignment - 1) / alignment * alignment;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::Initialize(VkDevice device, VmaAllocator allocator,
|
||||
VkDeviceSize minUniformBufferOffsetAlignment, Uint32 frameCount,
|
||||
Uint32 maxBindings, Uint32 setsPerFrame, VkDeviceSize perFrameUploadBytes) {
|
||||
Shutdown();
|
||||
|
||||
MOBILEGL_ASSERT(device != VK_NULL_HANDLE, "UniformDescriptorBinder::Initialize requires valid VkDevice");
|
||||
MOBILEGL_ASSERT(allocator != nullptr, "UniformDescriptorBinder::Initialize requires valid VMA allocator");
|
||||
MOBILEGL_ASSERT(frameCount > 0, "UniformDescriptorBinder::Initialize requires frameCount > 0");
|
||||
MOBILEGL_ASSERT(maxBindings > 0, "UniformDescriptorBinder::Initialize requires maxBindings > 0");
|
||||
MOBILEGL_ASSERT(setsPerFrame > 0, "UniformDescriptorBinder::Initialize requires setsPerFrame > 0");
|
||||
|
||||
m_device = device;
|
||||
m_allocator = allocator;
|
||||
m_minDynamicOffsetAlignment = std::max<VkDeviceSize>(1, minUniformBufferOffsetAlignment);
|
||||
m_perFrameUploadBytes = perFrameUploadBytes;
|
||||
m_frameCount = frameCount;
|
||||
m_maxBindings = maxBindings;
|
||||
m_setsPerFrame = setsPerFrame;
|
||||
|
||||
Vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
bindings.reserve(m_maxBindings);
|
||||
for (Uint32 bindingIndex = 0; bindingIndex < m_maxBindings; ++bindingIndex) {
|
||||
VkDescriptorSetLayoutBinding binding{};
|
||||
binding.binding = bindingIndex;
|
||||
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
binding.descriptorCount = 1;
|
||||
binding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS;
|
||||
binding.pImmutableSamplers = nullptr;
|
||||
bindings.push_back(binding);
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo setLayoutInfo{};
|
||||
setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
setLayoutInfo.bindingCount = static_cast<Uint32>(bindings.size());
|
||||
setLayoutInfo.pBindings = bindings.data();
|
||||
VK_VERIFY(vkCreateDescriptorSetLayout(m_device, &setLayoutInfo, nullptr, &m_descriptorSetLayout),
|
||||
"UniformDescriptorBinder::Initialize, vkCreateDescriptorSetLayout");
|
||||
|
||||
VkDescriptorPoolSize poolSize{};
|
||||
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
poolSize.descriptorCount = m_frameCount * m_setsPerFrame * m_maxBindings;
|
||||
|
||||
VkDescriptorPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
poolInfo.maxSets = m_frameCount * m_setsPerFrame;
|
||||
poolInfo.poolSizeCount = 1;
|
||||
poolInfo.pPoolSizes = &poolSize;
|
||||
VK_VERIFY(vkCreateDescriptorPool(m_device, &poolInfo, nullptr, &m_descriptorPool),
|
||||
"UniformDescriptorBinder::Initialize, vkCreateDescriptorPool");
|
||||
|
||||
m_descriptorSets.resize(m_frameCount * m_setsPerFrame, VK_NULL_HANDLE);
|
||||
Vector<VkDescriptorSetLayout> layouts(m_descriptorSets.size(), m_descriptorSetLayout);
|
||||
VkDescriptorSetAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
allocInfo.descriptorPool = m_descriptorPool;
|
||||
allocInfo.descriptorSetCount = static_cast<Uint32>(layouts.size());
|
||||
allocInfo.pSetLayouts = layouts.data();
|
||||
VK_VERIFY(vkAllocateDescriptorSets(m_device, &allocInfo, m_descriptorSets.data()),
|
||||
"UniformDescriptorBinder::Initialize, vkAllocateDescriptorSets");
|
||||
|
||||
m_frames.resize(m_frameCount);
|
||||
for (Uint32 frameIndex = 0; frameIndex < m_frameCount; ++frameIndex) {
|
||||
auto& frame = m_frames[frameIndex];
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
const Bool created = frame.uploadBuffer.Create(
|
||||
m_allocator, m_perFrameUploadBytes, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_AUTO,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT);
|
||||
if (!created) {
|
||||
MGLOG_E("UniformDescriptorBinder::Initialize failed: cannot create frame upload buffer %u", frameIndex);
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UniformDescriptorBinder::Shutdown() {
|
||||
for (auto& frame : m_frames) {
|
||||
frame.uploadBuffer.Destroy();
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
}
|
||||
m_frames.clear();
|
||||
m_descriptorSets.clear();
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_descriptorPool != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);
|
||||
}
|
||||
m_descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
if (m_device != VK_NULL_HANDLE && m_descriptorSetLayout != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr);
|
||||
}
|
||||
m_descriptorSetLayout = VK_NULL_HANDLE;
|
||||
|
||||
m_allocator = nullptr;
|
||||
m_device = VK_NULL_HANDLE;
|
||||
m_minDynamicOffsetAlignment = 1;
|
||||
m_perFrameUploadBytes = 0;
|
||||
m_frameCount = 0;
|
||||
m_maxBindings = 0;
|
||||
m_setsPerFrame = 0;
|
||||
}
|
||||
|
||||
void UniformDescriptorBinder::BeginFrame(Uint32 frameIndex) {
|
||||
MOBILEGL_ASSERT(frameIndex < m_frames.size(), "UniformDescriptorBinder::BeginFrame invalid frame index");
|
||||
auto& frame = m_frames[frameIndex];
|
||||
frame.writeCursor = 0;
|
||||
frame.descriptorCursor = 0;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset) {
|
||||
const VkDeviceSize alignedOffset = AlignUp(frame.writeCursor, m_minDynamicOffsetAlignment);
|
||||
if (alignedOffset + size > m_perFrameUploadBytes) {
|
||||
return false;
|
||||
}
|
||||
outOffset = alignedOffset;
|
||||
frame.writeCursor = alignedOffset + size;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::GatherBindingPayloads(const MG_State::GLState::ProgramObject& program,
|
||||
Vector<const void*>& outData,
|
||||
Vector<VkDeviceSize>& outSizes) const {
|
||||
outData.assign(m_maxBindings, nullptr);
|
||||
outSizes.assign(m_maxBindings, 0);
|
||||
|
||||
if (MG_State::pGLContext == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Uint32 activeUniformBlockCount = static_cast<Uint32>(program.GetActiveUniformBlocksCount());
|
||||
const Uint32 uniformBindingPointCount =
|
||||
static_cast<Uint32>(MG_State::pGLContext->GetBufferBindingPointCount(BufferTarget::Uniform));
|
||||
|
||||
for (Uint32 blockIndex = 0; blockIndex < activeUniformBlockCount; ++blockIndex) {
|
||||
const Uint32 binding = program.GetUniformBlockBinding(blockIndex);
|
||||
if (binding >= m_maxBindings) {
|
||||
continue;
|
||||
}
|
||||
|
||||
VkDeviceSize blockSize = static_cast<VkDeviceSize>(program.GetUBOSizeAt(blockIndex));
|
||||
if (blockSize == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& blockName = program.GetUniformBlockName(blockIndex);
|
||||
if (blockName == MG_Util::ShaderTranspiler::GLOBAL_UBO_NAME) {
|
||||
const void* globalUboData = program.GetUBOData();
|
||||
const VkDeviceSize globalUboSize = static_cast<VkDeviceSize>(program.GetUBOSize());
|
||||
if (globalUboData == nullptr || globalUboSize == 0) {
|
||||
continue;
|
||||
}
|
||||
outData[binding] = globalUboData;
|
||||
outSizes[binding] = std::min(blockSize, globalUboSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (binding >= uniformBindingPointCount) {
|
||||
continue;
|
||||
}
|
||||
auto& bindingPoint = MG_State::pGLContext->GetBufferBindingPoint(BufferTarget::Uniform, binding);
|
||||
const auto bufferObject = bindingPoint.GetBoundObject();
|
||||
if (!bufferObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto bufferData = bufferObject->GetDataReadOnly();
|
||||
if (!bufferData || bufferData->empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto range = bindingPoint.GetRange();
|
||||
const VkDeviceSize bufferSize = static_cast<VkDeviceSize>(bufferObject->GetSize());
|
||||
VkDeviceSize rangeStart = static_cast<VkDeviceSize>(range.start);
|
||||
VkDeviceSize rangeEnd = static_cast<VkDeviceSize>(range.end);
|
||||
|
||||
if (rangeStart >= bufferSize) {
|
||||
continue;
|
||||
}
|
||||
if (rangeEnd <= rangeStart || rangeEnd > bufferSize) {
|
||||
rangeEnd = bufferSize;
|
||||
}
|
||||
|
||||
VkDeviceSize available = rangeEnd - rangeStart;
|
||||
if (available == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
outData[binding] = bufferData->data() + static_cast<SizeT>(rangeStart);
|
||||
outSizes[binding] = std::min(blockSize, available);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool UniformDescriptorBinder::BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||
const MG_State::GLState::ProgramObject& program,
|
||||
Uint32 frameIndex) {
|
||||
if (m_descriptorSetLayout == VK_NULL_HANDLE || m_descriptorPool == VK_NULL_HANDLE) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: binder is not initialized");
|
||||
return false;
|
||||
}
|
||||
if (frameIndex >= m_frames.size()) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: invalid frame index %u", frameIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& frame = m_frames[frameIndex];
|
||||
if (frame.descriptorCursor >= m_setsPerFrame) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: per-frame descriptor set budget exceeded");
|
||||
return false;
|
||||
}
|
||||
|
||||
const Uint32 descriptorSetIndex = frameIndex * m_setsPerFrame + frame.descriptorCursor;
|
||||
const VkDescriptorSet descriptorSet = m_descriptorSets[descriptorSetIndex];
|
||||
++frame.descriptorCursor;
|
||||
|
||||
Vector<const void*> bindingData;
|
||||
Vector<VkDeviceSize> bindingSizes;
|
||||
if (!GatherBindingPayloads(program, bindingData, bindingSizes)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: cannot gather UBO payloads");
|
||||
return false;
|
||||
}
|
||||
|
||||
static const Uint8 kFallbackData[16] = {};
|
||||
|
||||
Vector<VkDescriptorBufferInfo> bufferInfos(m_maxBindings);
|
||||
Vector<VkWriteDescriptorSet> writes;
|
||||
writes.reserve(m_maxBindings);
|
||||
Vector<Uint32> dynamicOffsets(m_maxBindings, 0);
|
||||
|
||||
for (Uint32 binding = 0; binding < m_maxBindings; ++binding) {
|
||||
const void* payload = bindingData[binding];
|
||||
VkDeviceSize payloadSize = bindingSizes[binding];
|
||||
if (payload == nullptr || payloadSize == 0) {
|
||||
payload = kFallbackData;
|
||||
payloadSize = sizeof(kFallbackData);
|
||||
}
|
||||
|
||||
VkDeviceSize payloadOffset = 0;
|
||||
if (!AllocateUploadRegion(frame, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: frame upload buffer exhausted");
|
||||
return false;
|
||||
}
|
||||
if (!frame.uploadBuffer.Upload(payload, payloadSize, payloadOffset)) {
|
||||
MGLOG_E("UniformDescriptorBinder::BindProgramUniformBuffers failed: upload failed on binding %u", binding);
|
||||
return false;
|
||||
}
|
||||
|
||||
bufferInfos[binding].buffer = frame.uploadBuffer.GetHandle();
|
||||
bufferInfos[binding].offset = 0;
|
||||
bufferInfos[binding].range = payloadSize;
|
||||
dynamicOffsets[binding] = static_cast<Uint32>(payloadOffset);
|
||||
|
||||
VkWriteDescriptorSet write{};
|
||||
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write.dstSet = descriptorSet;
|
||||
write.dstBinding = binding;
|
||||
write.dstArrayElement = 0;
|
||||
write.descriptorCount = 1;
|
||||
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
write.pBufferInfo = &bufferInfos[binding];
|
||||
writes.push_back(write);
|
||||
}
|
||||
|
||||
vkUpdateDescriptorSets(m_device, static_cast<Uint32>(writes.size()), writes.data(), 0, nullptr);
|
||||
|
||||
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSet,
|
||||
static_cast<Uint32>(dynamicOffsets.size()), dynamicOffsets.data());
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/UniformDescriptorBinder.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 "VkBufferObject.h"
|
||||
#include "../VkIncludes.h"
|
||||
#include <Includes.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
namespace MobileGL::MG_State::GLState {
|
||||
class ProgramObject;
|
||||
}
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
class UniformDescriptorBinder {
|
||||
public:
|
||||
Bool Initialize(VkDevice device, VmaAllocator allocator, VkDeviceSize minUniformBufferOffsetAlignment,
|
||||
Uint32 frameCount, Uint32 maxBindings = 16, Uint32 setsPerFrame = 64,
|
||||
VkDeviceSize perFrameUploadBytes = 4 * 1024 * 1024);
|
||||
void Shutdown();
|
||||
|
||||
void BeginFrame(Uint32 frameIndex);
|
||||
Bool BindProgramUniformBuffers(VkCommandBuffer commandBuffer, VkPipelineLayout pipelineLayout,
|
||||
const MG_State::GLState::ProgramObject& program, Uint32 frameIndex);
|
||||
|
||||
VkDescriptorSetLayout GetDescriptorSetLayout() const { return m_descriptorSetLayout; }
|
||||
|
||||
private:
|
||||
struct FrameResources {
|
||||
VkBufferObject uploadBuffer;
|
||||
VkDeviceSize writeCursor = 0;
|
||||
Uint32 descriptorCursor = 0;
|
||||
};
|
||||
|
||||
static VkDeviceSize AlignUp(VkDeviceSize value, VkDeviceSize alignment);
|
||||
Bool AllocateUploadRegion(FrameResources& frame, VkDeviceSize size, VkDeviceSize& outOffset);
|
||||
Bool GatherBindingPayloads(const MG_State::GLState::ProgramObject& program, Vector<const void*>& outData,
|
||||
Vector<VkDeviceSize>& outSizes) const;
|
||||
|
||||
VkDevice m_device = VK_NULL_HANDLE;
|
||||
VmaAllocator m_allocator = nullptr;
|
||||
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
Vector<VkDescriptorSet> m_descriptorSets;
|
||||
Vector<FrameResources> m_frames;
|
||||
|
||||
VkDeviceSize m_minDynamicOffsetAlignment = 1;
|
||||
VkDeviceSize m_perFrameUploadBytes = 0;
|
||||
Uint32 m_frameCount = 0;
|
||||
Uint32 m_maxBindings = 0;
|
||||
Uint32 m_setsPerFrame = 0;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
@@ -9,6 +9,37 @@
|
||||
#include "VkBufferObject.h"
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkBufferObject::VkBufferObject(VkBufferObject&& other) noexcept {
|
||||
m_allocator = other.m_allocator;
|
||||
m_buffer = other.m_buffer;
|
||||
m_allocation = other.m_allocation;
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
other.m_allocation = nullptr;
|
||||
other.m_size = 0;
|
||||
}
|
||||
|
||||
VkBufferObject& VkBufferObject::operator=(VkBufferObject&& other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Destroy();
|
||||
|
||||
m_allocator = other.m_allocator;
|
||||
m_buffer = other.m_buffer;
|
||||
m_allocation = other.m_allocation;
|
||||
m_size = other.m_size;
|
||||
|
||||
other.m_allocator = nullptr;
|
||||
other.m_buffer = VK_NULL_HANDLE;
|
||||
other.m_allocation = nullptr;
|
||||
other.m_size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
VkBufferObject::~VkBufferObject() {
|
||||
Destroy();
|
||||
}
|
||||
@@ -77,4 +108,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return true;
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
VkBufferObject(const VkBufferObject&) = delete;
|
||||
VkBufferObject& operator=(const VkBufferObject&) = delete;
|
||||
VkBufferObject(VkBufferObject&& other) noexcept;
|
||||
VkBufferObject& operator=(VkBufferObject&& other) noexcept;
|
||||
|
||||
Bool Create(VmaAllocator allocator, VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VmaMemoryUsage memoryUsage, VmaAllocationCreateFlags allocationFlags = 0);
|
||||
@@ -38,4 +40,3 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
VkDeviceSize m_size = 0;
|
||||
};
|
||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||
|
||||
|
||||
@@ -87,6 +87,14 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
MGLOG_D("PrepareDemoPipeline called");
|
||||
|
||||
VkPipelineLayoutCreateInfo plci{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
VkDescriptorSetLayout descriptorSetLayouts[1] = {VK_NULL_HANDLE};
|
||||
if (m_uniformDescriptorBinder) {
|
||||
descriptorSetLayouts[0] = m_uniformDescriptorBinder->GetDescriptorSetLayout();
|
||||
}
|
||||
if (descriptorSetLayouts[0] != VK_NULL_HANDLE) {
|
||||
plci.setLayoutCount = 1;
|
||||
plci.pSetLayouts = descriptorSetLayouts;
|
||||
}
|
||||
VK_VERIFY(vkCreatePipelineLayout(m_device, &plci, nullptr, &m_pipelineLayout), "vkCreatePipelineLayout");
|
||||
|
||||
MGLOG_I("PrepareDemoPipeline completed");
|
||||
@@ -111,6 +119,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
m_pipelineFactory = MakeUnique<PipelineFactory>(m_device, m_config);
|
||||
m_programFactory = MakeUnique<ProgramFactory>(m_device, m_config);
|
||||
m_uniformDescriptorBinder = MakeUnique<UniformDescriptorBinder>();
|
||||
if (!m_uniformDescriptorBinder->Initialize(m_device, m_allocator,
|
||||
m_physicalDevice.properties.limits.minUniformBufferOffsetAlignment,
|
||||
m_config.MaxFramesInFlight)) {
|
||||
MGLOG_E("UniformDescriptorBinder initialization failed. UBO sync on Vulkan backend is disabled.");
|
||||
m_uniformDescriptorBinder.reset();
|
||||
}
|
||||
m_vertexInputStateFactory = MakeUnique<VertexInputStateFactory>(m_config);
|
||||
|
||||
PrepareDemoPipeline();
|
||||
@@ -143,6 +158,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
|
||||
m_pipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
if (m_uniformDescriptorBinder) {
|
||||
m_uniformDescriptorBinder->Shutdown();
|
||||
m_uniformDescriptorBinder.reset();
|
||||
}
|
||||
|
||||
ShutdownSwapchain();
|
||||
|
||||
@@ -320,6 +339,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
VkCommandBuffer& commandBuffer = m_frameContext.BeginCommandRecording();
|
||||
if (m_uniformDescriptorBinder) {
|
||||
m_uniformDescriptorBinder->BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||
}
|
||||
TransitionSwapchainImageToColorAttachment(commandBuffer, m_imageIndexAcquired);
|
||||
TransitionDepthStencilImageToAttachment(commandBuffer, m_imageIndexAcquired);
|
||||
|
||||
@@ -478,6 +500,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
}
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||
if (m_uniformDescriptorBinder &&
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout, *payload.program,
|
||||
m_frameContext.GetCurrentFrameIndex())) {
|
||||
MGLOG_W("DrawArrays skipped: failed to bind uniform descriptors");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vertexInputState && !vertexInputState->bindings.empty()) {
|
||||
if (!payload.vertexArray) {
|
||||
@@ -581,6 +609,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
const auto swapchainExtent = m_swapchainObject.GetExtent();
|
||||
|
||||
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineToBind);
|
||||
if (m_uniformDescriptorBinder &&
|
||||
!m_uniformDescriptorBinder->BindProgramUniformBuffers(commandBuffer, m_pipelineLayout,
|
||||
*payload.drawArray.program,
|
||||
m_frameContext.GetCurrentFrameIndex())) {
|
||||
MGLOG_W("DrawElements skipped: failed to bind uniform descriptors");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vertexInputState && !vertexInputState->bindings.empty()) {
|
||||
if (!payload.drawArray.vertexArray) {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "PipelineFactory.h"
|
||||
#include "ProgramFactory.h"
|
||||
#include "SwapchainObject.h"
|
||||
#include "UniformDescriptorBinder.h"
|
||||
#include "VertexInputStateFactory.h"
|
||||
#include "VkBufferObject.h"
|
||||
#include "MG_Util/Math/VectorTypes.h"
|
||||
@@ -131,6 +132,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
|
||||
UniquePtr<PipelineFactory> m_pipelineFactory;
|
||||
UniquePtr<ProgramFactory> m_programFactory;
|
||||
UniquePtr<UniformDescriptorBinder> m_uniformDescriptorBinder;
|
||||
UniquePtr<VertexInputStateFactory> m_vertexInputStateFactory;
|
||||
|
||||
void CreateInstance();
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace MobileGL {
|
||||
GLenum GetAttribType(Uint index) const { return m_attribTypes[index]; }
|
||||
const String& GetAttribName(Uint index) const { return m_attribs[index]; }
|
||||
void* MapUBO() { return m_uboScratch.data(); }
|
||||
const void* GetUBOData() const { return m_uboScratch.data(); }
|
||||
Uint GetUBOSize() const { return static_cast<Uint>(m_uboScratch.size()); }
|
||||
|
||||
void SetUniformSamplerOrImageUnitIndex(Uint location, Int unit) {
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
|
||||
#include <vulkan/vulkan.h>
|
||||
#define GLFW_INCLUDE_NONE
|
||||
@@ -180,15 +182,22 @@ static constexpr const char* kVertexShaderSource = R"(#version 330 core
|
||||
layout(location = 0) in vec2 aPos;
|
||||
layout(location = 1) in vec3 aColor;
|
||||
out vec3 vColor;
|
||||
uniform float iTime;
|
||||
void main() {
|
||||
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||
float wobble = 0.05 * sin(iTime);
|
||||
gl_Position = vec4(aPos.x, aPos.y + wobble, 0.0, 1.0);
|
||||
vColor = aColor;
|
||||
})";
|
||||
static constexpr const char* kFragmentShaderSource = R"(#version 330 core
|
||||
in vec3 vColor;
|
||||
layout(location = 0) out vec4 outColor;
|
||||
layout(std140, binding = 1) uniform ColorBlock {
|
||||
vec3 uColor;
|
||||
};
|
||||
uniform float iTime;
|
||||
void main() {
|
||||
outColor = vec4(vColor, 1.0);
|
||||
float pulse = 0.5 + 0.5 * sin(iTime * 1.7);
|
||||
outColor = vec4(vColor * uColor * pulse, 1.0);
|
||||
})";
|
||||
|
||||
const GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
||||
@@ -216,6 +225,29 @@ void main() {
|
||||
glDeleteShader(vs);
|
||||
glDeleteShader(fs);
|
||||
|
||||
const GLint iTimeLocation = glGetUniformLocation(program, "iTime");
|
||||
if (iTimeLocation < 0) {
|
||||
std::cerr << "Failed to get uniform location for iTime" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const GLuint colorBlockIndex = glGetUniformBlockIndex(program, "ColorBlock");
|
||||
if (colorBlockIndex == GL_INVALID_INDEX) {
|
||||
std::cerr << "Failed to get uniform block index for ColorBlock" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
glUniformBlockBinding(program, colorBlockIndex, 1);
|
||||
|
||||
GLuint colorUbo = 0;
|
||||
glGenBuffers(1, &colorUbo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, colorUbo);
|
||||
alignas(16) GLfloat uboColorData[4] = {1.0f, 0.6f, 0.2f, 0.0f};
|
||||
glBufferData(GL_UNIFORM_BUFFER, sizeof(uboColorData), uboColorData, GL_DYNAMIC_DRAW);
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 1, colorUbo);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
const auto startTime = std::chrono::steady_clock::now();
|
||||
|
||||
int i = 0;
|
||||
while(!glfwWindowShouldClose(window)) {
|
||||
glfwPollEvents();
|
||||
@@ -227,6 +259,16 @@ void main() {
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
if (i % 500 > 250) {
|
||||
glUseProgram(program);
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const float t = std::chrono::duration<float>(now - startTime).count();
|
||||
glUniform1f(iTimeLocation, t);
|
||||
|
||||
uboColorData[0] = 0.5f + 0.5f * std::sin(t * 0.7f);
|
||||
uboColorData[1] = 0.5f + 0.5f * std::sin(t * 1.1f + 1.2f);
|
||||
uboColorData[2] = 0.5f + 0.5f * std::sin(t * 1.5f + 2.4f);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, colorUbo);
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(uboColorData), uboColorData);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
|
||||
}
|
||||
eglSwapBuffers(display, surface);
|
||||
@@ -234,6 +276,7 @@ void main() {
|
||||
}
|
||||
|
||||
glDeleteProgram(program);
|
||||
glDeleteBuffers(1, &colorUbo);
|
||||
glDeleteBuffers(1, &positionVbo);
|
||||
glDeleteBuffers(1, &colorVbo);
|
||||
glDeleteBuffers(1, &ebo);
|
||||
|
||||
Reference in New Issue
Block a user