mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Chore] (MG_Test/Backend/DirectVulkan): encapsulate FrameContext (cont.)
This commit is contained in:
@@ -210,6 +210,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
|
MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp
|
||||||
MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp
|
MobileGL/MG_Backend/DirectVulkan/Renderer/SwapchainObject.cpp
|
||||||
|
MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.cpp
|
||||||
# MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.cpp
|
# MobileGL/MG_Backend/DirectVulkan/Managers/ProgramManager.cpp
|
||||||
# MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.cpp
|
# MobileGL/MG_Backend/DirectVulkan/Managers/VertexInputStateManager.cpp
|
||||||
# MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.cpp
|
# MobileGL/MG_Backend/DirectVulkan/Managers/RenderStateManager.cpp
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.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 "FrameContext.h"
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
VkResult FrameContext::Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount) {
|
||||||
|
Destroy(device, commandPool);
|
||||||
|
commandBuffers.resize(frameCount, VK_NULL_HANDLE);
|
||||||
|
imageAvailableSemaphores.resize(frameCount, VK_NULL_HANDLE);
|
||||||
|
renderFinishedSemaphores.resize(frameCount, VK_NULL_HANDLE);
|
||||||
|
imageInFlightFences.resize(frameCount, VK_NULL_HANDLE);
|
||||||
|
hasCommandBufferRecorded.assign(frameCount, false);
|
||||||
|
currentFrameIndex = 0;
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo allocInfo{};
|
||||||
|
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
allocInfo.commandPool = commandPool;
|
||||||
|
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
allocInfo.commandBufferCount = frameCount;
|
||||||
|
VkResult result = vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data());
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
||||||
|
VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||||
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||||
|
|
||||||
|
for (Uint32 i = 0; i < frameCount; ++i) {
|
||||||
|
result = CreateSyncObjectsForFrame(device, i, semaphoreInfo, fenceInfo);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
Destroy(device, commandPool);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::Destroy(VkDevice device, VkCommandPool commandPool) {
|
||||||
|
const Uint32 frameCount = static_cast<Uint32>(commandBuffers.size());
|
||||||
|
for (Uint32 i = 0; i < frameCount; ++i) {
|
||||||
|
DestroySyncObjectsForFrame(device, i);
|
||||||
|
}
|
||||||
|
if (device != VK_NULL_HANDLE && commandPool != VK_NULL_HANDLE && !commandBuffers.empty()) {
|
||||||
|
vkFreeCommandBuffers(device, commandPool, frameCount, commandBuffers.data());
|
||||||
|
}
|
||||||
|
commandBuffers.clear();
|
||||||
|
imageAvailableSemaphores.clear();
|
||||||
|
renderFinishedSemaphores.clear();
|
||||||
|
imageInFlightFences.clear();
|
||||||
|
hasCommandBufferRecorded.clear();
|
||||||
|
currentFrameIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::AdvanceFrame() {
|
||||||
|
MOBILEGL_ASSERT(!commandBuffers.empty(), "FrameContext is not initialized");
|
||||||
|
currentFrameIndex = (currentFrameIndex + 1) % static_cast<Uint32>(commandBuffers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 FrameContext::GetCurrentFrameIndex() const {
|
||||||
|
return currentFrameIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint32 FrameContext::GetFrameCount() const {
|
||||||
|
return static_cast<Uint32>(commandBuffers.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer& FrameContext::GetCommandBuffer(Uint32 frameIndex) {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return commandBuffers[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphore& FrameContext::GetImageAvailableSemaphore(Uint32 frameIndex) {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return imageAvailableSemaphores[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphore& FrameContext::GetRenderFinishedSemaphore(Uint32 frameIndex) {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return renderFinishedSemaphores[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkFence& FrameContext::GetImageInFlightFence(Uint32 frameIndex) {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return imageInFlightFences[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetHasCommandBufferRecorded(Uint32 frameIndex, Bool value) {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
hasCommandBufferRecorded[frameIndex] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetCurrentCommandBuffer(VkCommandBuffer value) {
|
||||||
|
SetCurrentHasCommandBufferRecorded(false);
|
||||||
|
GetCommandBuffer(GetCurrentFrameIndex()) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetCurrentImageAvailableSemaphore(VkSemaphore value) {
|
||||||
|
GetImageAvailableSemaphore(GetCurrentFrameIndex()) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetCurrentRenderFinishedSemaphore(VkSemaphore value) {
|
||||||
|
GetRenderFinishedSemaphore(GetCurrentFrameIndex()) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetCurrentImageInFlightFence(VkFence value) {
|
||||||
|
GetImageInFlightFence(GetCurrentFrameIndex()) = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::SetCurrentHasCommandBufferRecorded(Bool value) {
|
||||||
|
SetHasCommandBufferRecorded(GetCurrentFrameIndex(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer& FrameContext::GetCurrentCommandBuffer() {
|
||||||
|
return GetCommandBuffer(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphore& FrameContext::GetCurrentImageAvailableSemaphore() {
|
||||||
|
return GetImageAvailableSemaphore(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSemaphore& FrameContext::GetCurrentRenderFinishedSemaphore() {
|
||||||
|
return GetRenderFinishedSemaphore(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
VkFence& FrameContext::GetCurrentImageInFlightFence() {
|
||||||
|
return GetImageInFlightFence(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool FrameContext::GetCurrentHasCommandBufferRecorded() const {
|
||||||
|
return GetHasCommandBufferRecorded(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkCommandBuffer& FrameContext::GetCommandBuffer(Uint32 frameIndex) const {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return commandBuffers[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkSemaphore& FrameContext::GetImageAvailableSemaphore(Uint32 frameIndex) const {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return imageAvailableSemaphores[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkSemaphore& FrameContext::GetRenderFinishedSemaphore(Uint32 frameIndex) const {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return renderFinishedSemaphores[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkFence& FrameContext::GetImageInFlightFence(Uint32 frameIndex) const {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return imageInFlightFences[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool FrameContext::GetHasCommandBufferRecorded(Uint32 frameIndex) const {
|
||||||
|
AssertValidFrameIndex(frameIndex);
|
||||||
|
return hasCommandBufferRecorded[frameIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkCommandBuffer& FrameContext::GetCurrentCommandBuffer() const {
|
||||||
|
return GetCommandBuffer(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkSemaphore& FrameContext::GetCurrentImageAvailableSemaphore() const {
|
||||||
|
return GetImageAvailableSemaphore(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkSemaphore& FrameContext::GetCurrentRenderFinishedSemaphore() const {
|
||||||
|
return GetRenderFinishedSemaphore(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
const VkFence& FrameContext::GetCurrentImageInFlightFence() const {
|
||||||
|
return GetImageInFlightFence(GetCurrentFrameIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::ResetPerFrameState() {
|
||||||
|
for (SizeT i = 0; i < hasCommandBufferRecorded.size(); ++i) {
|
||||||
|
hasCommandBufferRecorded[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::AssertValidFrameIndex(Uint32 frameIndex) const {
|
||||||
|
MOBILEGL_ASSERT(frameIndex < commandBuffers.size(), "FrameContext index out of range");
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult FrameContext::CreateSyncObjectsForFrame(VkDevice device, Uint32 frameIndex,
|
||||||
|
const VkSemaphoreCreateInfo& semaphoreInfo,
|
||||||
|
const VkFenceCreateInfo& fenceInfo) {
|
||||||
|
DestroySyncObjectsForFrame(device, frameIndex);
|
||||||
|
|
||||||
|
VkResult result =
|
||||||
|
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[frameIndex]);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[frameIndex]);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr);
|
||||||
|
imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = vkCreateFence(device, &fenceInfo, nullptr, &imageInFlightFences[frameIndex]);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
vkDestroySemaphore(device, renderFinishedSemaphores[frameIndex], nullptr);
|
||||||
|
vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr);
|
||||||
|
renderFinishedSemaphores[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasCommandBufferRecorded[frameIndex] = false;
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FrameContext::DestroySyncObjectsForFrame(VkDevice device, Uint32 frameIndex) {
|
||||||
|
if (device != VK_NULL_HANDLE && imageInFlightFences[frameIndex] != VK_NULL_HANDLE) {
|
||||||
|
vkDestroyFence(device, imageInFlightFences[frameIndex], nullptr);
|
||||||
|
}
|
||||||
|
imageInFlightFences[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
if (device != VK_NULL_HANDLE && renderFinishedSemaphores[frameIndex] != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySemaphore(device, renderFinishedSemaphores[frameIndex], nullptr);
|
||||||
|
}
|
||||||
|
renderFinishedSemaphores[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
if (device != VK_NULL_HANDLE && imageAvailableSemaphores[frameIndex] != VK_NULL_HANDLE) {
|
||||||
|
vkDestroySemaphore(device, imageAvailableSemaphores[frameIndex], nullptr);
|
||||||
|
}
|
||||||
|
imageAvailableSemaphores[frameIndex] = VK_NULL_HANDLE;
|
||||||
|
hasCommandBufferRecorded[frameIndex] = false;
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Backend/DirectVulkan/Renderer/FrameContext.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 "../VkIncludes.h"
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
|
class FrameContext {
|
||||||
|
public:
|
||||||
|
VkResult Initialize(VkDevice device, VkCommandPool commandPool, Uint32 frameCount);
|
||||||
|
void Destroy(VkDevice device, VkCommandPool commandPool);
|
||||||
|
void AdvanceFrame();
|
||||||
|
void ResetPerFrameState();
|
||||||
|
|
||||||
|
Uint32 GetCurrentFrameIndex() const;
|
||||||
|
Uint32 GetFrameCount() const;
|
||||||
|
|
||||||
|
VkCommandBuffer& GetCommandBuffer(Uint32 frameIndex);
|
||||||
|
VkSemaphore& GetImageAvailableSemaphore(Uint32 frameIndex);
|
||||||
|
VkSemaphore& GetRenderFinishedSemaphore(Uint32 frameIndex);
|
||||||
|
VkFence& GetImageInFlightFence(Uint32 frameIndex);
|
||||||
|
void SetHasCommandBufferRecorded(Uint32 frameIndex, Bool value);
|
||||||
|
void SetCurrentCommandBuffer(VkCommandBuffer value);
|
||||||
|
void SetCurrentImageAvailableSemaphore(VkSemaphore value);
|
||||||
|
void SetCurrentRenderFinishedSemaphore(VkSemaphore value);
|
||||||
|
void SetCurrentImageInFlightFence(VkFence value);
|
||||||
|
void SetCurrentHasCommandBufferRecorded(Bool value);
|
||||||
|
|
||||||
|
VkCommandBuffer& GetCurrentCommandBuffer();
|
||||||
|
VkSemaphore& GetCurrentImageAvailableSemaphore();
|
||||||
|
VkSemaphore& GetCurrentRenderFinishedSemaphore();
|
||||||
|
VkFence& GetCurrentImageInFlightFence();
|
||||||
|
Bool GetCurrentHasCommandBufferRecorded() const;
|
||||||
|
|
||||||
|
const VkCommandBuffer& GetCommandBuffer(Uint32 frameIndex) const;
|
||||||
|
const VkSemaphore& GetImageAvailableSemaphore(Uint32 frameIndex) const;
|
||||||
|
const VkSemaphore& GetRenderFinishedSemaphore(Uint32 frameIndex) const;
|
||||||
|
const VkFence& GetImageInFlightFence(Uint32 frameIndex) const;
|
||||||
|
Bool GetHasCommandBufferRecorded(Uint32 frameIndex) const;
|
||||||
|
const VkCommandBuffer& GetCurrentCommandBuffer() const;
|
||||||
|
const VkSemaphore& GetCurrentImageAvailableSemaphore() const;
|
||||||
|
const VkSemaphore& GetCurrentRenderFinishedSemaphore() const;
|
||||||
|
const VkFence& GetCurrentImageInFlightFence() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AssertValidFrameIndex(Uint32 frameIndex) const;
|
||||||
|
|
||||||
|
VkResult CreateSyncObjectsForFrame(VkDevice device, Uint32 frameIndex,
|
||||||
|
const VkSemaphoreCreateInfo& semaphoreInfo,
|
||||||
|
const VkFenceCreateInfo& fenceInfo);
|
||||||
|
void DestroySyncObjectsForFrame(VkDevice device, Uint32 frameIndex);
|
||||||
|
|
||||||
|
Vector<VkCommandBuffer> commandBuffers;
|
||||||
|
Vector<VkSemaphore> imageAvailableSemaphores;
|
||||||
|
Vector<VkSemaphore> renderFinishedSemaphores;
|
||||||
|
Vector<VkFence> imageInFlightFences;
|
||||||
|
Vector<Bool> hasCommandBufferRecorded;
|
||||||
|
Uint32 currentFrameIndex = 0;
|
||||||
|
};
|
||||||
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
@@ -209,22 +209,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MGLOG_I("PrepareDemoPipeline completed");
|
MGLOG_I("PrepareDemoPipeline completed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateSyncObjects() {
|
void VulkanRenderer::CreateFrameContexts() {
|
||||||
VkSemaphoreCreateInfo semaphoreInfo{VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
|
VK_VERIFY(m_frameContext.Initialize(m_device, m_commandPool, m_config.MaxFramesInFlight),
|
||||||
VkFenceCreateInfo fenceInfo{VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
"CreateFrameContexts");
|
||||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
MGLOG_I("CreateFrameContexts completed");
|
||||||
|
|
||||||
m_frameContexts.resize(m_config.MaxFramesInFlight);
|
|
||||||
|
|
||||||
for (SizeT i = 0; i < m_config.MaxFramesInFlight; i++) {
|
|
||||||
auto& frameContext = m_frameContexts[i];
|
|
||||||
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &frameContext.imageAvailableSemaphore));
|
|
||||||
VK_VERIFY(vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &frameContext.renderFinishedSemaphore));
|
|
||||||
VK_VERIFY(vkCreateFence(m_device, &fenceInfo, nullptr, &frameContext.imageInFlightFence));
|
|
||||||
frameContext.hasCommandBufferRecorded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MGLOG_I("CreateSyncObjects completed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Initialize() {
|
void VulkanRenderer::Initialize() {
|
||||||
@@ -234,19 +222,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
CreateLogicalDeviceAndQueues();
|
CreateLogicalDeviceAndQueues();
|
||||||
|
|
||||||
CreateCommandPool();
|
CreateCommandPool();
|
||||||
CreateCommandBuffers();
|
|
||||||
|
|
||||||
RecreateSwapchain();
|
RecreateSwapchain();
|
||||||
|
|
||||||
PrepareDemoPipeline();
|
PrepareDemoPipeline();
|
||||||
CreateSyncObjects();
|
CreateFrameContexts();
|
||||||
|
|
||||||
// Prime the first frame so Render() always targets an acquired swapchain image.
|
// Prime the first frame so Render() always targets an acquired swapchain image.
|
||||||
auto& frameContext = m_frameContexts[m_currentFrameIndex];
|
VK_VERIFY(vkWaitForFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence(), VK_TRUE, UINT64_MAX));
|
||||||
VK_VERIFY(vkWaitForFences(m_device, 1, &frameContext.imageInFlightFence, VK_TRUE, UINT64_MAX));
|
VK_VERIFY(vkResetFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence()));
|
||||||
VK_VERIFY(vkResetFences(m_device, 1, &frameContext.imageInFlightFence));
|
|
||||||
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
||||||
frameContext.imageAvailableSemaphore, VK_NULL_HANDLE,
|
m_frameContext.GetCurrentImageAvailableSemaphore(), VK_NULL_HANDLE,
|
||||||
&m_imageIndexAcquired));
|
&m_imageIndexAcquired));
|
||||||
|
|
||||||
MGLOG_D("VulkanRenderer initialized");
|
MGLOG_D("VulkanRenderer initialized");
|
||||||
@@ -255,22 +241,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void VulkanRenderer::Shutdown() {
|
void VulkanRenderer::Shutdown() {
|
||||||
VK_VERIFY(vkDeviceWaitIdle(m_device));
|
VK_VERIFY(vkDeviceWaitIdle(m_device));
|
||||||
|
|
||||||
for (auto& frameContext : m_frameContexts) {
|
m_frameContext.Destroy(m_device, m_commandPool);
|
||||||
if (frameContext.imageInFlightFence != VK_NULL_HANDLE) {
|
|
||||||
vkDestroyFence(m_device, frameContext.imageInFlightFence, nullptr);
|
|
||||||
frameContext.imageInFlightFence = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (frameContext.renderFinishedSemaphore != VK_NULL_HANDLE) {
|
|
||||||
vkDestroySemaphore(m_device, frameContext.renderFinishedSemaphore, nullptr);
|
|
||||||
frameContext.renderFinishedSemaphore = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (frameContext.imageAvailableSemaphore != VK_NULL_HANDLE) {
|
|
||||||
vkDestroySemaphore(m_device, frameContext.imageAvailableSemaphore, nullptr);
|
|
||||||
frameContext.imageAvailableSemaphore = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
frameContext.hasCommandBufferRecorded = false;
|
|
||||||
}
|
|
||||||
m_frameContexts.clear();
|
|
||||||
|
|
||||||
if (m_pipeline != VK_NULL_HANDLE) {
|
if (m_pipeline != VK_NULL_HANDLE) {
|
||||||
vkDestroyPipeline(m_device, m_pipeline, nullptr);
|
vkDestroyPipeline(m_device, m_pipeline, nullptr);
|
||||||
@@ -310,15 +281,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Render() {
|
void VulkanRenderer::Render() {
|
||||||
auto& frameContext = m_frameContexts[m_currentFrameIndex];
|
VkCommandBuffer& commandBuffer = m_frameContext.GetCurrentCommandBuffer();
|
||||||
VK_VERIFY(vkResetCommandBuffer(frameContext.commandBuffer, 0));
|
VK_VERIFY(vkResetCommandBuffer(commandBuffer, 0));
|
||||||
|
|
||||||
// Begin command buffer
|
// Begin command buffer
|
||||||
VkCommandBufferBeginInfo beginInfo{};
|
VkCommandBufferBeginInfo beginInfo{};
|
||||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
beginInfo.flags = 0;
|
beginInfo.flags = 0;
|
||||||
beginInfo.pInheritanceInfo = nullptr;
|
beginInfo.pInheritanceInfo = nullptr;
|
||||||
VK_VERIFY(vkBeginCommandBuffer(frameContext.commandBuffer, &beginInfo));
|
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo));
|
||||||
|
|
||||||
// Begin render pass
|
// Begin render pass
|
||||||
VkRenderPassBeginInfo renderPassInfo{};
|
VkRenderPassBeginInfo renderPassInfo{};
|
||||||
@@ -331,10 +302,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
VkClearValue clearColor = {{{0.0f, 0.0f, 0.0f, 1.0f}}};
|
||||||
renderPassInfo.clearValueCount = 1;
|
renderPassInfo.clearValueCount = 1;
|
||||||
renderPassInfo.pClearValues = &clearColor;
|
renderPassInfo.pClearValues = &clearColor;
|
||||||
vkCmdBeginRenderPass(frameContext.commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
|
||||||
// Render commands
|
// Render commands
|
||||||
vkCmdBindPipeline(frameContext.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
|
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
|
||||||
|
|
||||||
VkViewport viewport{};
|
VkViewport viewport{};
|
||||||
viewport.x = 0.0f;
|
viewport.x = 0.0f;
|
||||||
@@ -343,28 +314,28 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
viewport.height = static_cast<float>(swapchainExtent.height);
|
viewport.height = static_cast<float>(swapchainExtent.height);
|
||||||
viewport.minDepth = 0.0f;
|
viewport.minDepth = 0.0f;
|
||||||
viewport.maxDepth = 1.0f;
|
viewport.maxDepth = 1.0f;
|
||||||
vkCmdSetViewport(frameContext.commandBuffer, 0, 1, &viewport);
|
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
|
||||||
|
|
||||||
VkRect2D scissor{};
|
VkRect2D scissor{};
|
||||||
scissor.offset = {0, 0};
|
scissor.offset = {0, 0};
|
||||||
scissor.extent = swapchainExtent;
|
scissor.extent = swapchainExtent;
|
||||||
vkCmdSetScissor(frameContext.commandBuffer, 0, 1, &scissor);
|
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
|
||||||
|
|
||||||
vkCmdDraw(frameContext.commandBuffer, 3, 1, 0, 0);
|
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
|
||||||
|
|
||||||
// End render pass
|
// End render pass
|
||||||
vkCmdEndRenderPass(frameContext.commandBuffer);
|
vkCmdEndRenderPass(commandBuffer);
|
||||||
|
|
||||||
// End command buffer
|
// End command buffer
|
||||||
VK_VERIFY(vkEndCommandBuffer(frameContext.commandBuffer));
|
VK_VERIFY(vkEndCommandBuffer(commandBuffer));
|
||||||
frameContext.hasCommandBufferRecorded = true;
|
m_frameContext.SetCurrentHasCommandBufferRecorded(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Present() {
|
void VulkanRenderer::Present() {
|
||||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
||||||
"Present, acquired image index out of range");
|
"Present, acquired image index out of range");
|
||||||
auto& frameContext = m_frameContexts[m_currentFrameIndex];
|
VkCommandBuffer& commandBuffer = m_frameContext.GetCurrentCommandBuffer();
|
||||||
const Bool hasRecordedWork = frameContext.hasCommandBufferRecorded;
|
const Bool hasRecordedWork = m_frameContext.GetCurrentHasCommandBufferRecorded();
|
||||||
Bool needsLayoutTransitionForPresent = false;
|
Bool needsLayoutTransitionForPresent = false;
|
||||||
|
|
||||||
if (!hasRecordedWork) {
|
if (!hasRecordedWork) {
|
||||||
@@ -373,12 +344,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
acquiredImageLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
acquiredImageLayout != VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR) {
|
||||||
needsLayoutTransitionForPresent = true;
|
needsLayoutTransitionForPresent = true;
|
||||||
|
|
||||||
VK_VERIFY(vkResetCommandBuffer(frameContext.commandBuffer, 0),
|
VK_VERIFY(vkResetCommandBuffer(commandBuffer, 0),
|
||||||
"Present, vkResetCommandBuffer(layout transition)");
|
"Present, vkResetCommandBuffer(layout transition)");
|
||||||
|
|
||||||
VkCommandBufferBeginInfo beginInfo{};
|
VkCommandBufferBeginInfo beginInfo{};
|
||||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
VK_VERIFY(vkBeginCommandBuffer(frameContext.commandBuffer, &beginInfo),
|
VK_VERIFY(vkBeginCommandBuffer(commandBuffer, &beginInfo),
|
||||||
"Present, vkBeginCommandBuffer(layout transition)");
|
"Present, vkBeginCommandBuffer(layout transition)");
|
||||||
|
|
||||||
VkImageMemoryBarrier presentBarrier{};
|
VkImageMemoryBarrier presentBarrier{};
|
||||||
@@ -395,11 +366,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
presentBarrier.subresourceRange.levelCount = 1;
|
presentBarrier.subresourceRange.levelCount = 1;
|
||||||
presentBarrier.subresourceRange.baseArrayLayer = 0;
|
presentBarrier.subresourceRange.baseArrayLayer = 0;
|
||||||
presentBarrier.subresourceRange.layerCount = 1;
|
presentBarrier.subresourceRange.layerCount = 1;
|
||||||
vkCmdPipelineBarrier(frameContext.commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
||||||
&presentBarrier);
|
&presentBarrier);
|
||||||
|
|
||||||
VK_VERIFY(vkEndCommandBuffer(frameContext.commandBuffer),
|
VK_VERIFY(vkEndCommandBuffer(commandBuffer),
|
||||||
"Present, vkEndCommandBuffer(layout transition)");
|
"Present, vkEndCommandBuffer(layout transition)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -409,18 +380,18 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// 1) Submit current frame work.
|
// 1) Submit current frame work.
|
||||||
VkSubmitInfo submitInfo{};
|
VkSubmitInfo submitInfo{};
|
||||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||||
VkSemaphore waitSemaphores[] = {frameContext.imageAvailableSemaphore};
|
VkSemaphore waitSemaphores[] = {m_frameContext.GetCurrentImageAvailableSemaphore()};
|
||||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
|
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
|
||||||
submitInfo.waitSemaphoreCount = 1;
|
submitInfo.waitSemaphoreCount = 1;
|
||||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||||
submitInfo.pWaitDstStageMask = waitStages;
|
submitInfo.pWaitDstStageMask = waitStages;
|
||||||
submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U;
|
submitInfo.commandBufferCount = shouldSubmitCommandBuffer ? 1U : 0U;
|
||||||
submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &frameContext.commandBuffer : nullptr;
|
submitInfo.pCommandBuffers = shouldSubmitCommandBuffer ? &commandBuffer : nullptr;
|
||||||
VkSemaphore signalSemaphores[] = {frameContext.renderFinishedSemaphore};
|
VkSemaphore signalSemaphores[] = {m_frameContext.GetCurrentRenderFinishedSemaphore()};
|
||||||
submitInfo.signalSemaphoreCount = 1;
|
submitInfo.signalSemaphoreCount = 1;
|
||||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||||
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, frameContext.imageInFlightFence));
|
VK_VERIFY(vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, m_frameContext.GetCurrentImageInFlightFence()));
|
||||||
frameContext.hasCommandBufferRecorded = false;
|
m_frameContext.SetCurrentHasCommandBufferRecorded(false);
|
||||||
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
m_swapchainObject.SetImageLayout(m_imageIndexAcquired, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||||
|
|
||||||
// 2) Present current frame.
|
// 2) Present current frame.
|
||||||
@@ -442,16 +413,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
||||||
|
|
||||||
// 3) Advance frame slot.
|
// 3) Advance frame slot.
|
||||||
m_currentFrameIndex = (m_currentFrameIndex + 1) % m_config.MaxFramesInFlight;
|
m_frameContext.AdvanceFrame();
|
||||||
|
|
||||||
// 4) Wait/reset/acquire for next frame.
|
// 4) Wait/reset/acquire for next frame.
|
||||||
auto& nextFrameContext = m_frameContexts[m_currentFrameIndex];
|
VK_VERIFY(vkWaitForFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence(), VK_TRUE, UINT64_MAX),
|
||||||
VK_VERIFY(vkWaitForFences(m_device, 1, &nextFrameContext.imageInFlightFence, VK_TRUE, UINT64_MAX),
|
|
||||||
"Present, vkWaitForFences");
|
"Present, vkWaitForFences");
|
||||||
VK_VERIFY(vkResetFences(m_device, 1, &nextFrameContext.imageInFlightFence), "Present, vkResetFences");
|
VK_VERIFY(vkResetFences(m_device, 1, &m_frameContext.GetCurrentImageInFlightFence()),
|
||||||
|
"Present, vkResetFences");
|
||||||
result =
|
result =
|
||||||
vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
vkAcquireNextImageKHR(m_device, m_swapchainObject.GetHandle(), UINT64_MAX,
|
||||||
nextFrameContext.imageAvailableSemaphore, VK_NULL_HANDLE,
|
m_frameContext.GetCurrentImageAvailableSemaphore(), VK_NULL_HANDLE,
|
||||||
&m_imageIndexAcquired);
|
&m_imageIndexAcquired);
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
||||||
@@ -804,21 +775,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MGLOG_I("Command pool created");
|
MGLOG_I("Command pool created");
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::CreateCommandBuffers() {
|
|
||||||
m_frameContexts.resize(m_config.MaxFramesInFlight);
|
|
||||||
Vector<VkCommandBuffer> commandBuffers(m_config.MaxFramesInFlight, VK_NULL_HANDLE);
|
|
||||||
VkCommandBufferAllocateInfo allocInfo{};
|
|
||||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
||||||
allocInfo.commandPool = m_commandPool;
|
|
||||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
||||||
allocInfo.commandBufferCount = m_config.MaxFramesInFlight;
|
|
||||||
VK_VERIFY(vkAllocateCommandBuffers(m_device, &allocInfo, commandBuffers.data()));
|
|
||||||
for (SizeT i = 0; i < m_config.MaxFramesInFlight; ++i) {
|
|
||||||
m_frameContexts[i].commandBuffer = commandBuffers[i];
|
|
||||||
}
|
|
||||||
MGLOG_I("Command buffer created");
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanRenderer::CreateDefaultRenderPass() {
|
void VulkanRenderer::CreateDefaultRenderPass() {
|
||||||
VkAttachmentDescription color{};
|
VkAttachmentDescription color{};
|
||||||
color.format = m_swapchainObject.GetSurfaceFormat().format;
|
color.format = m_swapchainObject.GetSurfaceFormat().format;
|
||||||
@@ -986,9 +942,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
CreateSwapchain();
|
CreateSwapchain();
|
||||||
CreateDefaultRenderPass();
|
CreateDefaultRenderPass();
|
||||||
CreateDefaultFramebuffers();
|
CreateDefaultFramebuffers();
|
||||||
for (auto& frameContext : m_frameContexts) {
|
m_frameContext.ResetPerFrameState();
|
||||||
frameContext.hasCommandBufferRecorded = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace MobileGL::MG_Backend::DirectVulkan
|
} // namespace MobileGL::MG_Backend::DirectVulkan
|
||||||
|
|||||||
@@ -85,8 +85,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||||
|
|
||||||
Uint m_imageIndexAcquired = 0;
|
Uint m_imageIndexAcquired = 0;
|
||||||
Uint m_currentFrameIndex = 0;
|
FrameContext m_frameContext;
|
||||||
Vector<FrameContext> m_frameContexts;
|
|
||||||
|
|
||||||
void CreateInstance();
|
void CreateInstance();
|
||||||
VkResult SetupDebugMessenger();
|
VkResult SetupDebugMessenger();
|
||||||
@@ -97,11 +96,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void CreateLogicalDeviceAndQueues();
|
void CreateLogicalDeviceAndQueues();
|
||||||
void CreateSwapchain();
|
void CreateSwapchain();
|
||||||
void CreateCommandPool();
|
void CreateCommandPool();
|
||||||
void CreateCommandBuffers();
|
void CreateFrameContexts();
|
||||||
void CreateDefaultRenderPass();
|
void CreateDefaultRenderPass();
|
||||||
void CreateDefaultFramebuffers();
|
void CreateDefaultFramebuffers();
|
||||||
void PrepareDemoPipeline();
|
void PrepareDemoPipeline();
|
||||||
void CreateSyncObjects();
|
|
||||||
|
|
||||||
void ShutdownSwapchain();
|
void ShutdownSwapchain();
|
||||||
|
|
||||||
|
|||||||
@@ -72,9 +72,12 @@ int main() {
|
|||||||
EGLSurface surface = eglCreateWindowSurface(display, config, nativewindow, nullptr);
|
EGLSurface surface = eglCreateWindowSurface(display, config, nativewindow, nullptr);
|
||||||
eglMakeCurrent(display, surface, surface, context);
|
eglMakeCurrent(display, surface, surface, context);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
while(!glfwWindowShouldClose(window)) {
|
while(!glfwWindowShouldClose(window)) {
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
MobileGL::MG_Backend::DirectVulkan::pVulkanRenderer->Render();
|
|
||||||
|
if (i % 2 == 0)
|
||||||
|
MobileGL::MG_Backend::DirectVulkan::pVulkanRenderer->Render();
|
||||||
eglSwapBuffers(display, surface);
|
eglSwapBuffers(display, surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user