Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
bc3098b
refactored Command Buffer logic
JeanPhilippeKernel Feb 16, 2026
4d0175d
introduced render thread
JeanPhilippeKernel Feb 24, 2026
52c99ab
fixed clang format
JeanPhilippeKernel Feb 24, 2026
824e006
bounded to 3x buffering
JeanPhilippeKernel Mar 1, 2026
2cd0c6b
improved render thread communication
JeanPhilippeKernel Mar 2, 2026
f4770b4
removed mailbox cond_variable and mutex
JeanPhilippeKernel Mar 2, 2026
e8724a1
improved swapchain resizing logic
JeanPhilippeKernel Mar 3, 2026
825a9b4
fixed resizing issue on macos
JeanPhilippeKernel Mar 6, 2026
2f40f5f
fixed resizing issue on macos
JeanPhilippeKernel Mar 6, 2026
2ebd2f3
Merge branch 'develop' into user/kernel/fix-command-buffer-manager
JeanPhilippeKernel Mar 7, 2026
70f32aa
fixed resizing issue on macos
JeanPhilippeKernel Mar 6, 2026
61910cb
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 7, 2026
a045774
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 7, 2026
c61f469
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 9, 2026
995cbf0
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 7, 2026
328bbb3
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 14, 2026
b7a3161
Merge branch 'develop' into user/kernel/fix-command-buffer-manager
JeanPhilippeKernel Mar 16, 2026
5a00110
switched from combined image to sampled image and sampler
JeanPhilippeKernel Mar 19, 2026
ea32b30
switched from combined image to sampled image and sampler
JeanPhilippeKernel Mar 19, 2026
31a852a
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Mar 27, 2026
45c29bf
fixed gpu crash on intel driver
JeanPhilippeKernel Mar 30, 2026
13075f2
fixed fence hanging
JeanPhilippeKernel Mar 31, 2026
f274291
fixed fence hanging
JeanPhilippeKernel Mar 31, 2026
46bd183
Merge branch 'develop' into user/kernel/fix-command-buffer-manager
JeanPhilippeKernel Apr 1, 2026
a16de16
Merge branch 'user/kernel/fix-command-buffer-manager' of https://gith…
JeanPhilippeKernel Apr 1, 2026
a638a83
fixed code formating
JeanPhilippeKernel Apr 1, 2026
7a73bef
fixed async upload with transfer support
JeanPhilippeKernel Apr 2, 2026
19b60a9
added support of clearing async job
JeanPhilippeKernel Apr 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"BUILD_SHARED_LIBS": "OFF",
"FETCHCONTENT_QUIET": "OFF",

"FMT_MODULE": "OFF",
"VULKAN_HEADERS_ENABLE_INSTALL": "ON",
"ENTT_INCLUDE_HEADERS": "ON",

Expand Down
3 changes: 3 additions & 0 deletions Obelisk/EntryPoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <ZEngine/Applications/GameApplication.h>
#include <ZEngine/Core/Memory/MemoryManager.h>
#include <ZEngine/EngineConfiguration.h>
#include <ZEngine/Helpers/ThreadPool.h>
#include <ZEngine/Logging/Logger.h>

#ifdef ZENGINE_PLATFORM
Expand All @@ -22,6 +23,8 @@ int applicationEntryPoint(int argc, char* argv[])
LoggerConfiguration logger_cfg = {};
Logger::Initialize(arena, logger_cfg);

Helpers::ThreadPoolHelper::Initialize();

GameApplicationPtr app = nullptr;

CLI::App cli{"ObeliskCLI"};
Expand Down
3 changes: 2 additions & 1 deletion Resources/Shaders/fragment_common.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ layout(std140, set = 0, binding = 5) readonly buffer MatSB
}
MaterialDataBuffer;

layout(set = 1, binding = 0) uniform sampler2D TextureArray[];
layout(set = 1, binding = 0) uniform texture2D TextureArray[];
layout(set = 1, binding = 1) uniform sampler LinearWrapSampler;

MaterialData FetchMaterial(uint dataIndex)
{
Expand Down
8 changes: 4 additions & 4 deletions Resources/Shaders/g_buffer.frag
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,18 @@ void main()
if (material.AlbedoMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.AlbedoMap);
OutAlbedo = texture(TextureArray[nonuniformEXT(texId)], TexCoord);
OutAlbedo = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord);
}

if (material.SpecularMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.SpecularMap);
OutSpecular = texture(TextureArray[nonuniformEXT(texId)], TexCoord);
OutSpecular = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord);
}

if (material.NormalMap < INVALID_MAP_HANDLE)
{
uint texId = uint(material.NormalMap);
OutNormal = texture(TextureArray[nonuniformEXT(texId)], TexCoord).rgb;
OutNormal = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), TexCoord).rgb;
}
}
}
17 changes: 7 additions & 10 deletions Resources/Shaders/imgui.frag
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#version 460 core
#extension GL_EXT_nonuniform_qualifier : require
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable

layout(location = 0) out vec4 fColor;
layout(set = 0, binding = 0) uniform sampler2D _unused;
layout(set = 1, binding = 0) uniform sampler2D TextureArray[];
layout(set = 0, binding = 0) uniform sampler _unused;
layout(set = 1, binding = 0) uniform texture2D TextureArray[];
layout(set = 1, binding = 1) uniform sampler LinearWrapSampler;

layout(location = 0) in struct
{
Expand All @@ -14,10 +14,7 @@ layout(location = 0) in struct

void main()
{
uint texId = uint(In.TexData.z);
if (texId < 0xFFFFFFFFu)
{
vec4 texVal = texture(TextureArray[nonuniformEXT(texId)], In.TexData.xy);
fColor = In.Color * texVal;
}
}
uint texId = uint(floor(In.TexData.z + 0.5));
vec4 texVal = texture(sampler2D(TextureArray[nonuniformEXT(texId)], LinearWrapSampler), In.TexData.xy);
fColor = In.Color * texVal;
}
1 change: 1 addition & 0 deletions Resources/Shaders/imgui.vert
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ layout(push_constant) uniform uPushConstant
vec2 uScale;
vec2 uTranslate;
uint index;
uint _padding;
}
pc;

Expand Down
5 changes: 3 additions & 2 deletions Resources/Shaders/skybox.frag
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
layout(location = 0) in vec3 dir;
layout(location = 0) out vec4 outColor;

layout(set = 0, binding = 1) uniform samplerCube EnvMap;
layout(set = 0, binding = 1) uniform textureCube EnvMap;
layout(set = 1, binding = 1) uniform sampler LinearWrapSampler;

void main()
{
outColor = texture(EnvMap, dir);
outColor = texture(samplerCube(EnvMap, LinearWrapSampler), dir);
}
141 changes: 120 additions & 21 deletions ZEngine/ZEngine/Applications/AppRenderPipeline.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
#include <AppRenderPipeline.h>
#include <Core/Containers/Array.h>
#include <Rendering/Specifications/FormatSpecification.h>

using namespace ZEngine::Core::Containers;

namespace ZEngine::Applications
{
void AppRenderPipeline::Initialize(Hardwares::VulkanDevicePtr device)
{
Device = device;
Device = device;
RenderWorkerThreadCount = Device->CommandBufferMgr->TotalThreadCount - 1u;
UICommandBufferIndex = RenderMainThreadIndex + 1u;
Device->Arena->CreateSubArena(ZMega(30), &LocalArena);

SceneRenderer = ZPushStructCtor(Device->Arena, Rendering::Renderers::GraphicRenderer);
ImguiRenderer = ZPushStructCtor(Device->Arena, Rendering::Renderers::ImGUIRenderer);
Device->Arena->CreateSubArena(ZMega(30), &LocalArena);

SceneRenderer->Initialize(Device);
ImguiRenderer->Initialize(Device);

for (size_t i = 0; i < MaxMailBoxBufferCount; ++i)
{
RenderPayloads[i].UIOverlay.IndexedCmds.resize(100);
RenderPayloads[i].UIOverlay.ScissorCmds.resize(100);
RenderPayloads[i].UIOverlay.TextureIds.resize(100);
}
}

void AppRenderPipeline::Shutdown()
Expand All @@ -33,19 +44,38 @@ namespace ZEngine::Applications

void AppRenderPipeline::BeginFrame()
{
Device->NewFrame();
CurrentCmdBuf = Device->GetCommandBuffer();
auto swapchain = Device->SwapchainPtr;

swapchain->AcquireNextImage(CurrentMailBoxBufferHead);

for (uint8_t thread_idx = 0; thread_idx < Device->CommandBufferMgr->TotalThreadCount; ++thread_idx)
{
Device->CommandBufferMgr->ResetPool(swapchain->CurrentFrame->Index, thread_idx);
}

Device->AsyncResLoader->CompleteDeferrals();

// uint8_t render_worker_thread_idx = RenderThreadIndex + 1;
// for (uint8_t worker_thread_idx = 0; worker_thread_idx < RenderWorkerThreadCount; ++worker_thread_idx)
// {
// auto thread_idx = render_worker_thread_idx + worker_thread_idx;
// }
CurrentCmdBuf = Device->CommandBufferMgr->GetCommandBuffer(Rendering::QueueType::GRAPHIC_QUEUE, swapchain->CurrentFrame->Index, RenderMainThreadIndex, 0, true);
}

void AppRenderPipeline::EndFrame()
{
Device->EnqueueCommandBuffer(CurrentCmdBuf);
Device->Present();
Device->CommandBufferMgr->EnqueueBuffer(CurrentCmdBuf);
Device->SwapchainPtr->Present();
}

void AppRenderPipeline::RenderScene(Rendering::Cameras::CameraPtr camera, Rendering::Scenes::RenderScenePtr scene)
{
if (scene->TransformBufferDirty[Device->CurrentFrameIndex].load(std::memory_order_acquire) || scene->MeshAllocationDirty[Device->CurrentFrameIndex].load(std::memory_order_acquire))
auto swpachain = Device->SwapchainPtr;
auto frame_index = swpachain->CurrentFrame->Index;
auto thread_index = RenderMainThreadIndex;

if (scene->TransformBufferDirty[Device->SwapchainPtr->CurrentFrame->Index].load(std::memory_order_acquire) || scene->MeshAllocationDirty[Device->SwapchainPtr->CurrentFrame->Index].load(std::memory_order_acquire))
{
auto gpu_scene_data = SceneRenderer->RenderSceneData;

Expand All @@ -56,21 +86,21 @@ namespace ZEngine::Applications

auto indirect_buffer_set = Device->IndirectBufferSetManager.Access(gpu_scene_data->IndirectBufferHandle);

auto vtx_buffer = vtx_buffer_set->At(Device->CurrentFrameIndex);
auto idx_buffer = idx_buffer_set->At(Device->CurrentFrameIndex);
auto transform_buffer = transform_buffer_set->At(Device->CurrentFrameIndex);
auto rd_buffer = rd_buffer_set->At(Device->CurrentFrameIndex);
auto indirect_buffer = indirect_buffer_set->At(Device->CurrentFrameIndex);
auto vtx_buffer = vtx_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);
auto idx_buffer = idx_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);
auto transform_buffer = transform_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);
auto rd_buffer = rd_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);
auto indirect_buffer = indirect_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);

auto& suballocs = scene->NodeSubMeshesAllocations;

if (scene->TransformBufferDirty[Device->CurrentFrameIndex].exchange(false, std::memory_order_acquire))
if (scene->TransformBufferDirty[Device->SwapchainPtr->CurrentFrame->Index].exchange(false, std::memory_order_acquire))
{
auto transform_data_view = ArrayView{scene->GlobalTransforms};
transform_buffer->Write(transform_data_view);
transform_buffer->Write(frame_index, thread_index, transform_data_view);
}

if (scene->MeshAllocationDirty[Device->CurrentFrameIndex].exchange(false, std::memory_order_acquire))
if (scene->MeshAllocationDirty[Device->SwapchainPtr->CurrentFrame->Index].exchange(false, std::memory_order_acquire))
{
auto scratch = ZGetScratch(&LocalArena);

Expand Down Expand Up @@ -100,29 +130,98 @@ namespace ZEngine::Applications
auto sub_mesh_alloc_view = ArrayView{SubMeshAllocations};
auto indirect_commands_view = ArrayView{DrawIndirectCommands};

vtx_buffer->Write(vertex_data_view);
idx_buffer->Write(index_data_view);
vtx_buffer->Write(frame_index, thread_index, vertex_data_view);
idx_buffer->Write(frame_index, thread_index, index_data_view);

rd_buffer->Write(sub_mesh_alloc_view);
rd_buffer->Write(frame_index, thread_index, sub_mesh_alloc_view);

indirect_buffer->Write(indirect_commands_view);
indirect_buffer->Write(frame_index, thread_index, indirect_commands_view);

ZReleaseScratch(scratch);
}
}

// Todo (Kernel) : When we'll start considering multithreaded support
// we might want to renderer->EnqueueAsync({command_buffer, {camera, frame_data} })
SceneRenderer->DrawScene(CurrentCmdBuf, camera);
SceneRenderer->DrawScene(frame_index, thread_index, CurrentCmdBuf, camera);
}

void AppRenderPipeline::BeginOverlayFrame()
{
ImguiRenderer->NewFrame();
}

void AppRenderPipeline::FillOverlayPayload(Rendering::Renderers::RenderOverlayPayload& payload)
{
ImguiRenderer->PreparePayload(payload);
}

void AppRenderPipeline::RenderOverlay(const Rendering::Renderers::RenderOverlayPayload& payload)
{
if (payload.VertexCount == 0 && payload.IndexCount == 0)
{
return;
}

auto swpachain = Device->SwapchainPtr;
auto frame_index = swpachain->CurrentFrame->Index;
auto thread_index = RenderMainThreadIndex;

auto current_framebuffer = Device->SwapchainPtr->SwapchainFramebuffers[Device->SwapchainPtr->CurrentFrame->ImageIndex];

CurrentCmdBuf->BeginRenderPass(ImguiRenderer->UIPass, current_framebuffer, true);
{
auto vtx_data_view = ArrayView{payload.VertexData.data(), payload.VertexData.size()};
auto idx_data_view = ArrayView{payload.IndexData.data(), payload.IndexData.size()};

auto vertex_buffer_set = Device->VertexBufferSetManager.Access(payload.VBHandle);
auto index_buffer_set = Device->IndexBufferSetManager.Access(payload.IdxBHandle);

auto vertex_buffer = vertex_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);
auto index_buffer = index_buffer_set->At(Device->SwapchainPtr->CurrentFrame->Index);

vertex_buffer->Write(frame_index, thread_index, vtx_data_view);
index_buffer->Write(frame_index, thread_index, idx_data_view);

auto ui_second_cb = Device->CommandBufferMgr->GetCommandBuffer(Rendering::QueueType::GRAPHIC_QUEUE, Device->SwapchainPtr->CurrentFrame->Index, RenderMainThreadIndex, UICommandBufferIndex, false);
ui_second_cb->ResetState();
ui_second_cb->BeginSecondary(ImguiRenderer->UIPass, current_framebuffer);
ui_second_cb->SetViewport(ImguiRenderer->UIPass->GetRenderAreaWidth(), ImguiRenderer->UIPass->GetRenderAreaHeight());

ui_second_cb->BindPipeline(Rendering::Specifications::PipelineBindPoint::GRAPHIC, ImguiRenderer->UIPass->Pipeline);

ui_second_cb->BindVertexBuffer(*vertex_buffer);
ui_second_cb->BindIndexBuffer(*index_buffer, payload.IsIndexBufferUint16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32);

Rendering::Renderers::PushConstantData pc_data = {};
pc_data.Scale[0] = payload.Pc[0];
pc_data.Scale[1] = payload.Pc[1];

pc_data.Translate[0] = payload.Pc[2];
pc_data.Translate[1] = payload.Pc[3];

for (uint32_t i = 0; i < payload.DrawDataIndex; ++i)
{
const auto& scissor_cmd = payload.ScissorCmds[i];
const auto& indexed_cmd = payload.IndexedCmds[i];

ui_second_cb->SetScissor(scissor_cmd.w, scissor_cmd.h, scissor_cmd.x, scissor_cmd.y);
pc_data.TextureId = payload.TextureIds[i];
ui_second_cb->PushConstants(VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(Rendering::Renderers::PushConstantData), &pc_data);
ui_second_cb->BindDescriptorSets(Device->SwapchainPtr->CurrentFrame->Index);
ui_second_cb->DrawIndexed(indexed_cmd.IdxCount, indexed_cmd.InstanceCount, indexed_cmd.FirstIndex, indexed_cmd.VertexOffset, indexed_cmd.FirstInstance);
}

ui_second_cb->End();

CurrentCmdBuf->ExecuteSecondaryCommandBuffers(ArrayView<Hardwares::CommandBuffer>{ui_second_cb, 1});
}

CurrentCmdBuf->EndRenderPass();
}

void AppRenderPipeline::EndOverlayFrame()
{
ImguiRenderer->DrawFrame(CurrentCmdBuf);
ImguiRenderer->EndFrame();
}
} // namespace ZEngine::Applications
44 changes: 38 additions & 6 deletions ZEngine/ZEngine/Applications/AppRenderPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,47 @@
#include <Hardwares/VulkanDevice.h>
#include <Rendering/Renderers/GraphicRenderer.h>
#include <Rendering/Renderers/ImGUIRenderer.h>
#include <new>

namespace ZEngine::Applications
{
struct AppRenderPipeline
#ifdef __cpp_lib_hardware_interference_size
constexpr auto CACHE_LINE_SIZE = std::hardware_destructive_interference_size;
#else
constexpr auto CACHE_LINE_SIZE = 64;
#endif

struct alignas(CACHE_LINE_SIZE) PaddedAtomicInt
{
std::atomic_uint32_t value = 0;
};

struct RenderPayload
{
Hardwares::VulkanDevicePtr Device = nullptr;
Rendering::Renderers::GraphicRendererPtr SceneRenderer = nullptr;
Rendering::Renderers::ImGUIRendererPtr ImguiRenderer = nullptr;
Hardwares::CommandBufferPtr CurrentCmdBuf = nullptr;
bool RenderUIOverlay = false;
bool ResizeRenderTarget = false;
uint32_t RenderTargetW = 0;
uint32_t RenderTargetH = 0;
Rendering::Cameras::CameraPtr Camera = nullptr;
Rendering::Scenes::RenderScenePtr Scene = nullptr;
Rendering::Renderers::RenderOverlayPayload UIOverlay = {};
};

ZEngine::Core::Memory::ArenaAllocator LocalArena = {};
struct AppRenderPipeline
{
const uint8_t MaxMailBoxBufferCount = 3;
const uint8_t RenderMainThreadIndex = 0;
uint8_t RenderWorkerThreadCount = 0;
uint8_t UICommandBufferIndex = 0xff;
uint32_t CurrentMailBoxBufferHead = 0;
PaddedAtomicInt MailBoxBufferHead = {};
PaddedAtomicInt MailBoxBufferTail = {};
RenderPayload RenderPayloads[3] = {};
ZEngine::Core::Memory::ArenaAllocator LocalArena = {};
Hardwares::VulkanDevicePtr Device = nullptr;
Rendering::Renderers::GraphicRendererPtr SceneRenderer = nullptr;
Rendering::Renderers::ImGUIRendererPtr ImguiRenderer = nullptr;
Hardwares::CommandBufferPtr CurrentCmdBuf = nullptr;

void Initialize(Hardwares::VulkanDevicePtr device);
void Shutdown();
Expand All @@ -26,6 +56,8 @@ namespace ZEngine::Applications

void BeginOverlayFrame();
void EndOverlayFrame();
void FillOverlayPayload(Rendering::Renderers::RenderOverlayPayload& payload);
void RenderOverlay(const Rendering::Renderers::RenderOverlayPayload& payload);
};
ZDEFINE_PTR(AppRenderPipeline);

Expand Down
Loading
Loading