Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
12f357f
First commit of the new "simple engine" tutorial. This is a VERY ear…
gpx1000 Jul 14, 2025
7e419d5
Refactor GPU optimization sections to generalize vendor-specific reco…
gpx1000 Jul 14, 2025
4644950
Refactor mobile GPU optimization sections and enhance Vulkan extensio…
gpx1000 Jul 14, 2025
2edbd75
Add detailed section on rendergraphs and synchronization in Vulkan
gpx1000 Jul 14, 2025
b476bd1
Add architectural pattern diagrams and refine related sections
gpx1000 Jul 18, 2025
f1d10cf
Add explanatory note on windowing libraries in input handling tutorial
gpx1000 Jul 18, 2025
2a350b2
closer to getting engine working.
gpx1000 Jul 20, 2025
2d46280
address feedback
gpx1000 Jul 22, 2025
3b3dc05
address feedback
gpx1000 Jul 22, 2025
6cf03fc
address feedback
gpx1000 Jul 22, 2025
50d78a7
add the flowchart to the rendering pipeline.
gpx1000 Jul 22, 2025
9b2c781
describe Rendergraphs and Dynamic Rendering
gpx1000 Jul 22, 2025
ddd1177
address comments.
gpx1000 Jul 22, 2025
5ec2035
add the requested appendix.adoc
gpx1000 Jul 22, 2025
31ded49
add the requested appendix.adoc
gpx1000 Jul 23, 2025
659ecb1
address the requested items
gpx1000 Jul 23, 2025
bd98b2d
address the requested items
gpx1000 Jul 23, 2025
8b83ead
address the requested items
gpx1000 Jul 23, 2025
9b2343a
address the requested items
gpx1000 Jul 23, 2025
7734f21
Engine now builds and runs. IMGUI having issues.
gpx1000 Jul 23, 2025
b8a9b39
Engine now renders. Bunch of validation errors and it looks like img…
gpx1000 Jul 23, 2025
e773353
address comments.
gpx1000 Jul 24, 2025
6256f77
address comments.
gpx1000 Jul 24, 2025
e6b8583
It runs now, IMGUI is happier.
gpx1000 Jul 24, 2025
e3f6fc6
everything runs and can close without validation warnings other than …
gpx1000 Jul 24, 2025
4a3b450
everything runs and can close without validation warnings other than …
gpx1000 Jul 24, 2025
0833166
audio HRTF works well and debug switch to turn on CPU only mode vs GP…
gpx1000 Jul 24, 2025
9513597
audio fully works, GPU HRTF works as well as CPU HRTF.
gpx1000 Jul 25, 2025
86c396a
The bistro scene now loads. Still have some issues with PBR but I th…
gpx1000 Jul 25, 2025
a62e4a9
make the hrtf listener sync with the camera location and rotation.
gpx1000 Jul 25, 2025
14fc6b1
Lighting working for non PBR code, and PBR code seems to be relativel…
gpx1000 Jul 25, 2025
b998a13
fix for formating the list.
gpx1000 Jul 29, 2025
8be59f4
PBR is working. KTX2 with BasisU is working.
gpx1000 Jul 29, 2025
be45542
Update 04_rendering_approaches.adoc
gpx1000 Jul 29, 2025
8bdf3d1
Update 04_rendering_approaches.adoc
gpx1000 Jul 30, 2025
e80fd23
address comments
gpx1000 Jul 30, 2025
20a9ade
Merge remote-tracking branch 'origin/Simple-Game-Engine' into Simple-…
gpx1000 Jul 30, 2025
d834c39
address comments
gpx1000 Jul 31, 2025
ea39e65
address comments
gpx1000 Jul 31, 2025
858b792
address comments
gpx1000 Jul 31, 2025
815aa79
Need to investigate a few things, (physics isn't colliding against th…
gpx1000 Jul 31, 2025
bdc1845
add in the dependences install scripts.
gpx1000 Jul 31, 2025
04c8b38
Merge branch 'refs/heads/upstream-main' into Simple-Game-Engine
gpx1000 Jul 31, 2025
cdb01db
attempt to fancy fix the windows install script.
gpx1000 Aug 1, 2025
16019ca
CMake: use PLATFORM_* defines via ANDROID, fix glfw condition, make s…
gpx1000 Aug 8, 2025
4b8198d
Fix the pathing for install_dependencies_windows.bat also fix CMakeLi…
gpx1000 Aug 8, 2025
14dcb35
Add instancing support, remove unused STB dependency, and fetch Bistr…
gpx1000 Aug 10, 2025
dd9713a
Refactor Vulkan memory handling, physics GPU initialization, and coll…
gpx1000 Aug 10, 2025
27715c0
address several comments (more still need to be addressed). And fix …
gpx1000 Aug 10, 2025
067ea74
Move Renderdoc out of the debug_system.h and into a "real world" rend…
gpx1000 Aug 11, 2025
a5edf28
the lighting in the PBR scene *looks* better. There's still a bit th…
gpx1000 Aug 11, 2025
adf461e
Remove shadows for now. We'll concentrate on them in the samples ver…
gpx1000 Aug 12, 2025
e57bc3d
Fix the string lights.
gpx1000 Aug 13, 2025
e1d7059
committing requested changes in stages.
gpx1000 Aug 15, 2025
1f17074
committing requested changes in stages.
gpx1000 Aug 15, 2025
acfbd6e
committing requested changes in stages.
gpx1000 Aug 15, 2025
5e38540
committing requested changes in stages.
gpx1000 Aug 15, 2025
a58b884
committing requested changes in stages.
gpx1000 Aug 15, 2025
4b4a243
committing requested changes in stages.
gpx1000 Aug 15, 2025
6ec7321
committing requested changes in stages.
gpx1000 Aug 15, 2025
8ecae4e
committing requested changes in stages.
gpx1000 Aug 15, 2025
345b137
committing requested changes in stages.
gpx1000 Aug 15, 2025
11e9a65
committing requested changes in stages.
gpx1000 Aug 15, 2025
5e6209c
committing requested changes in stages.
gpx1000 Aug 15, 2025
25e66d5
committing requested changes in stages.
gpx1000 Aug 15, 2025
5849fa5
switch from link to xref.
gpx1000 Aug 15, 2025
f1df3d2
addressing more comments.
gpx1000 Aug 15, 2025
7eaa47e
addressing more comments.
gpx1000 Aug 15, 2025
92c7f7d
addressing more comments.
gpx1000 Aug 15, 2025
625eb71
addressing more comments.
gpx1000 Aug 15, 2025
60ee95d
addressing more comments.
gpx1000 Aug 15, 2025
23952bc
addressing more comments.
gpx1000 Aug 15, 2025
65e5db3
addressing more comments.
gpx1000 Aug 15, 2025
e564b29
addressing more comments.
gpx1000 Aug 15, 2025
7b3e016
addressing more comments.
gpx1000 Aug 15, 2025
7bf9dab
addressing more comments.
gpx1000 Aug 15, 2025
47a6baa
addressing more comments.
gpx1000 Aug 15, 2025
44e7f38
addressing more comments.
gpx1000 Aug 15, 2025
dd51a38
addressing more comments.
gpx1000 Aug 15, 2025
f713e9c
addressing more comments.
gpx1000 Aug 15, 2025
9705b60
addressing more comments.
gpx1000 Aug 15, 2025
921eed4
addressing more comments.
gpx1000 Aug 15, 2025
2a22079
addressing more comments.
gpx1000 Aug 15, 2025
41e336e
addressing more comments.
gpx1000 Aug 15, 2025
b78f8b2
addressing more comments.
gpx1000 Aug 16, 2025
5c7a132
addressing more comments.
gpx1000 Aug 16, 2025
560b9f3
addressing more comments.
gpx1000 Aug 16, 2025
6db6b9d
addressing more comments.
gpx1000 Aug 16, 2025
02baf4d
addressing more comments.
gpx1000 Aug 16, 2025
0b0dddb
addressing more comments.
gpx1000 Aug 16, 2025
ccb5c15
addressing more comments.
gpx1000 Aug 19, 2025
aa321c0
addressing more comments.
gpx1000 Aug 19, 2025
5c4ec27
addressing more comments.
gpx1000 Aug 19, 2025
b4d6226
addressing more comments.
gpx1000 Aug 19, 2025
a8d11c6
addressing more comments.
gpx1000 Aug 19, 2025
b167c70
addressing more comments.
gpx1000 Aug 19, 2025
276d1cb
addressing more comments.
gpx1000 Aug 21, 2025
6e8adb4
implementing Spec-Gloss workflow and transmission materials.
gpx1000 Aug 25, 2025
e1ac508
updating nlohmann_json to v3.12.0.
gpx1000 Aug 25, 2025
bbbae41
update the default screenshot.
gpx1000 Aug 25, 2025
03d05cf
implementing support for asynchronous texture loading and improved Vu…
gpx1000 Aug 26, 2025
0ef0316
Working through some of the PR comments. Concentrating on the text o…
gpx1000 Oct 17, 2025
277a817
Down to 78 comments left outstanding from the PR in the tutorial text…
gpx1000 Oct 17, 2025
ce7c00d
This doesn't currently fix the bugs still in the engine, but it's a c…
gpx1000 Oct 17, 2025
6378bef
Add deferred mesh uploads, batch resource staging, priority-based tex…
gpx1000 Dec 1, 2025
0e9721e
Merge branch 'upstream-main' into Simple-Game-Engine
gpx1000 Dec 1, 2025
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
5 changes: 5 additions & 0 deletions attachments/simple_engine/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,11 @@ void Engine::handleMouseInput(float x, float y, uint32_t buttons) {
// Check if ImGui wants to capture mouse input first
bool imguiWantsMouse = imguiSystem && imguiSystem->WantCaptureMouse();

// Suppress right-click while loading
if (renderer && renderer->IsLoading()) {
buttons &= ~2u; // clear right button bit
}

if (!imguiWantsMouse) {
// Handle mouse click for ball throwing (right mouse button)
if (buttons & 2) { // Right mouse button (bit 1)
Expand Down
62 changes: 62 additions & 0 deletions attachments/simple_engine/imgui_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,55 @@ void ImGuiSystem::NewFrame() {

ImGui::NewFrame();

// Loading overlay: show only a fullscreen progress bar while model/textures are loading
if (renderer) {
const uint32_t scheduled = renderer->GetTextureTasksScheduled();
const uint32_t completed = renderer->GetTextureTasksCompleted();
const bool modelLoading = renderer->IsLoading();
if (modelLoading || (scheduled > 0 && completed < scheduled)) {
ImGuiIO& io = ImGui::GetIO();
// Suppress right-click while loading
if (io.MouseDown[1]) io.MouseDown[1] = false;

const ImVec2 dispSize = io.DisplaySize;

ImGui::SetNextWindowPos(ImVec2(0, 0));
ImGui::SetNextWindowSize(dispSize);
ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoSavedSettings |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoNav;

if (ImGui::Begin("##LoadingOverlay", nullptr, flags)) {
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
// Center the progress elements
const float barWidth = dispSize.x * 0.8f;
const float barX = (dispSize.x - barWidth) * 0.5f;
const float barY = dispSize.y * 0.45f;
ImGui::SetCursorPos(ImVec2(barX, barY));
ImGui::BeginGroup();
float frac = (scheduled > 0) ? (float)completed / (float)scheduled : 0.0f;
ImGui::ProgressBar(frac, ImVec2(barWidth, 0.0f));
ImGui::Dummy(ImVec2(0.0f, 10.0f));
ImGui::SetCursorPosX(barX);
if (modelLoading) {
ImGui::Text("Loading model...");
} else {
ImGui::Text("Loading textures: %u / %u", completed, scheduled);
}
ImGui::EndGroup();
ImGui::PopStyleVar();
}
ImGui::End();
// Do not draw the rest of the UI until loading finishes
return;
}
}

// Create HRTF Audio Control UI
ImGui::Begin("HRTF Audio Controls");
ImGui::Text("Hello, Vulkan!");
Expand Down Expand Up @@ -359,6 +408,19 @@ void ImGuiSystem::NewFrame() {
ImGui::Text("Camera: Manual control (WASD + mouse)");
}

// Texture loading progress
if (renderer) {
const uint32_t scheduled = renderer->GetTextureTasksScheduled();
const uint32_t completed = renderer->GetTextureTasksCompleted();
if (scheduled > 0 && completed < scheduled) {
ImGui::Separator();
float frac = scheduled ? (float)completed / (float)scheduled : 1.0f;
ImGui::Text("Loading textures: %u / %u", completed, scheduled);
ImGui::ProgressBar(frac, ImVec2(-FLT_MIN, 0.0f));
ImGui::Text("You can continue interacting while textures stream in...");
}
}

ImGui::End();
}

Expand Down
10 changes: 8 additions & 2 deletions attachments/simple_engine/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <iostream>
#include <stdexcept>
#include <thread>

// Constants
constexpr int WINDOW_WIDTH = 800;
Expand Down Expand Up @@ -38,8 +39,13 @@ void SetupScene(Engine* engine) {
// Set the camera as the active camera
engine->SetActiveCamera(camera);

// Load GLTF model synchronously on the main thread
LoadGLTFModel(engine, "../Assets/bistro/bistro.gltf");
// Kick off GLTF model loading on a background thread so the main loop can start and render the UI/progress bar
if (auto* renderer = engine->GetRenderer()) {
renderer->SetLoading(true);
}
std::thread([engine]{
LoadGLTFModel(engine, "../Assets/bistro/bistro.gltf");
}).detach();
}

#if defined(PLATFORM_ANDROID)
Expand Down
131 changes: 109 additions & 22 deletions attachments/simple_engine/memory_pool.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#include "memory_pool.h"
#include <iostream>
#include <algorithm>
#include <vulkan/vulkan.hpp>

MemoryPool::MemoryPool(const vk::raii::Device& device, const vk::raii::PhysicalDevice& physicalDevice)
: device(device), physicalDevice(physicalDevice) {
}


MemoryPool::~MemoryPool() {
// RAII will handle cleanup automatically
std::lock_guard lock(poolMutex);
Expand Down Expand Up @@ -153,6 +155,48 @@ std::unique_ptr<MemoryPool::MemoryBlock> MemoryPool::createMemoryBlock(PoolType
return block;
}

std::unique_ptr<MemoryPool::MemoryBlock> MemoryPool::createMemoryBlockWithType(PoolType poolType, vk::DeviceSize size, uint32_t memoryTypeIndex) {
auto configIt = poolConfigs.find(poolType);
if (configIt == poolConfigs.end()) {
throw std::runtime_error("Pool type not configured");
}
const PoolConfig& config = configIt->second;

// Allocate the memory block with the exact requested size
vk::MemoryAllocateInfo allocInfo{
.allocationSize = size,
.memoryTypeIndex = memoryTypeIndex
};

// Determine properties from the chosen memory type
const auto memProps = physicalDevice.getMemoryProperties();
if (memoryTypeIndex >= memProps.memoryTypeCount) {
throw std::runtime_error("Invalid memoryTypeIndex for createMemoryBlockWithType");
}
const vk::MemoryPropertyFlags typeProps = memProps.memoryTypes[memoryTypeIndex].propertyFlags;

auto block = std::unique_ptr<MemoryBlock>(new MemoryBlock{
.memory = vk::raii::DeviceMemory(device, allocInfo),
.size = size,
.used = 0,
.memoryTypeIndex = memoryTypeIndex,
.isMapped = false,
.mappedPtr = nullptr,
.freeList = {},
.allocationUnit = config.allocationUnit
});

block->isMapped = (typeProps & vk::MemoryPropertyFlagBits::eHostVisible) != vk::MemoryPropertyFlags{};
if (block->isMapped) {
block->mappedPtr = block->memory.mapMemory(0, size);
}

const size_t numUnits = static_cast<size_t>(block->size / config.allocationUnit);
block->freeList.resize(numUnits, true);

return block;
}

std::pair<MemoryPool::MemoryBlock*, size_t> MemoryPool::findSuitableBlock(PoolType poolType, vk::DeviceSize size, vk::DeviceSize alignment) {
auto poolIt = pools.find(poolType);
if (poolIt == pools.end()) {
Expand All @@ -162,27 +206,42 @@ std::pair<MemoryPool::MemoryBlock*, size_t> MemoryPool::findSuitableBlock(PoolTy
auto& poolBlocks = poolIt->second;
const PoolConfig& config = poolConfigs[poolType];

// Calculate required units (accounting for alignment)
// Calculate required units (accounting for size alignment)
const vk::DeviceSize alignedSize = ((size + alignment - 1) / alignment) * alignment;
const size_t requiredUnits = (alignedSize + config.allocationUnit - 1) / config.allocationUnit;
const size_t requiredUnits = static_cast<size_t>((alignedSize + config.allocationUnit - 1) / config.allocationUnit);

// Search existing blocks for sufficient free space
// Search existing blocks for sufficient free space with proper offset alignment
for (const auto& block : poolBlocks) {
// Find consecutive free units
size_t consecutiveFree = 0;
size_t startUnitCandidate = 0;
for (size_t i = 0; i < block->freeList.size(); ++i) {
if (block->freeList[i]) {
if (consecutiveFree == 0) {
startUnitCandidate = i;
}
consecutiveFree++;
if (consecutiveFree >= requiredUnits) {
return {block.get(), startUnitCandidate};
}
} else {
consecutiveFree = 0;
const vk::DeviceSize unit = config.allocationUnit;
const size_t totalUnits = block->freeList.size();

size_t i = 0;
while (i < totalUnits) {
// Ensure starting unit produces an offset aligned to 'alignment'
vk::DeviceSize startOffset = static_cast<vk::DeviceSize>(i) * unit;
if ((alignment > 0) && (startOffset % alignment != 0)) {
// Advance i to the next unit that aligns with 'alignment'
const vk::DeviceSize remainder = startOffset % alignment;
const vk::DeviceSize advanceBytes = alignment - remainder;
const size_t advanceUnits = static_cast<size_t>((advanceBytes + unit - 1) / unit);
i += std::max<size_t>(advanceUnits, 1);
continue;
}

// From aligned i, check for consecutive free units
size_t consecutiveFree = 0;
size_t j = i;
while (j < totalUnits && block->freeList[j] && consecutiveFree < requiredUnits) {
++consecutiveFree;
++j;
}

if (consecutiveFree >= requiredUnits) {
return {block.get(), i};
}

// Move past the checked range
i = (j > i) ? j : (i + 1);
}
}

Expand Down Expand Up @@ -333,13 +392,41 @@ std::pair<vk::raii::Image, std::unique_ptr<MemoryPool::Allocation>> MemoryPool::

vk::raii::Image image(device, imageInfo);

// Get memory requirements
// Get memory requirements for this image
vk::MemoryRequirements memRequirements = image.getMemoryRequirements();

// Allocate from texture pool
auto allocation = allocate(PoolType::TEXTURE_IMAGE, memRequirements.size, memRequirements.alignment);
if (!allocation) {
throw std::runtime_error("Failed to allocate memory from texture pool");
// Pick a memory type compatible with this image
uint32_t memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);

// Create a dedicated memory block for this image with the exact type and size
std::unique_ptr<Allocation> allocation;
{
std::lock_guard<std::mutex> lock(poolMutex);
auto poolIt = pools.find(PoolType::TEXTURE_IMAGE);
if (poolIt == pools.end()) {
poolIt = pools.try_emplace(PoolType::TEXTURE_IMAGE).first;
}
auto& poolBlocks = poolIt->second;
auto block = createMemoryBlockWithType(PoolType::TEXTURE_IMAGE, memRequirements.size, memoryTypeIndex);

// Prepare allocation that uses the new block from offset 0
allocation = std::make_unique<Allocation>();
allocation->memory = *block->memory;
allocation->offset = 0;
allocation->size = memRequirements.size;
allocation->memoryTypeIndex = memoryTypeIndex;
allocation->isMapped = block->isMapped;
allocation->mappedPtr = block->mappedPtr;

// Mark the entire block as used
block->used = memRequirements.size;
const size_t units = block->freeList.size();
for (size_t i = 0; i < units; ++i) {
block->freeList[i] = false;
}

// Keep the block owned by the pool for lifetime management and deallocation support
poolBlocks.push_back(std::move(block));
}

// Bind memory to image
Expand Down
4 changes: 4 additions & 0 deletions attachments/simple_engine/memory_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ class MemoryPool {
private:
const vk::raii::Device& device;
const vk::raii::PhysicalDevice& physicalDevice;
vk::PhysicalDeviceMemoryProperties memPropsCache{};


// Pool configurations
struct PoolConfig {
Expand All @@ -78,6 +80,8 @@ class MemoryPool {
// Helper methods
uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) const;
std::unique_ptr<MemoryBlock> createMemoryBlock(PoolType poolType, vk::DeviceSize size);
// Create a memory block with an explicit memory type index (used for images requiring a specific type)
std::unique_ptr<MemoryBlock> createMemoryBlockWithType(PoolType poolType, vk::DeviceSize size, uint32_t memoryTypeIndex);
std::pair<MemoryBlock*, size_t> findSuitableBlock(PoolType poolType, vk::DeviceSize size, vk::DeviceSize alignment);

public:
Expand Down
Loading
Loading