Skip to content

Commit dd9713a

Browse files
committed
Refactor Vulkan memory handling, physics GPU initialization, and collision detection
- Improved memory mapping reliability by enforcing VK_WHOLE_SIZE flush alignment. - Transitioned to quaternions for stable rotation in transformation updates. - Adjust bounciness to make more interesting. - Enhanced GPU-only physics setup with better Vulkan resource management. - Fixed semaphore usage issues in frame rendering pipeline. - Simplified texture fallback loading with assertion-based policy.
1 parent 14dcb35 commit dd9713a

File tree

11 files changed

+166
-81
lines changed

11 files changed

+166
-81
lines changed

attachments/simple_engine/engine.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,8 @@ void Engine::GenerateBallMaterial() {
562562
// Random emissive color (usually subtle)
563563
ballMaterial.emissive = glm::vec3(dis(gen) * 0.3f, dis(gen) * 0.3f, dis(gen) * 0.3f);
564564

565-
// Very low bounciness (0.05 to 0.15 for 85-95% momentum loss per bounce)
566-
ballMaterial.bounciness = 0.05f + dis(gen) * 0.1f;
565+
// Decent bounciness (0.6 to 0.9) so bounces are clearly visible
566+
ballMaterial.bounciness = 0.6f + dis(gen) * 0.3f;
567567
}
568568

569569
void Engine::InitializePhysicsScaling() {

attachments/simple_engine/fetch_bistro_assets.bat

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ REM Fetch the Bistro example assets into the desired assets directory.
55
REM Default target: assets\bistro at the repository root.
66
REM Usage:
77
REM fetch_bistro_assets.bat [target-dir]
8-
REM Examples:
8+
REM Example:
99
REM fetch_bistro_assets.bat
10-
REM fetch_bistro_assets.bat attachments\simple_engine\Assets\bistro
1110

1211
set REPO_SSH=[email protected]:gpx1000/bistro.git
1312
set REPO_HTTPS=https://github.com/gpx1000/bistro.git
1413

1514
if "%~1"=="" (
16-
set TARGET_DIR=assets\bistro
15+
set TARGET_DIR=Assets\bistro
1716
) else (
1817
set TARGET_DIR=%~1
1918
)

attachments/simple_engine/fetch_bistro_assets.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ set -euo pipefail
1010

1111
REPO_SSH="[email protected]:gpx1000/bistro.git"
1212
REPO_HTTPS="https://github.com/gpx1000/bistro.git"
13-
TARGET_DIR="${1:-assets/bistro}"
13+
TARGET_DIR="${1:-Assets/bistro}"
1414

1515
mkdir -p "$(dirname "${TARGET_DIR}")"
1616

attachments/simple_engine/model_loader.h

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -259,17 +259,6 @@ class ModelLoader {
259259
*/
260260
bool ParseGLTF(const std::string& filename, Model* model);
261261

262-
/**
263-
* @brief Load textures for a PBR material.
264-
* @param material The material to populate.
265-
* @param albedoMap The path to the albedo texture.
266-
* @param normalMap The path to the normal texture.
267-
* @param metallicRoughnessMap The path to the metallic-roughness texture.
268-
* @param aoMap The path to the ambient occlusion texture.
269-
* @param emissiveMap The path to the emissive texture.
270-
* @return True if loading was successful, false otherwise.
271-
*/
272-
273262
/**
274263
* @brief Extract lights from GLTF punctual lights extension.
275264
* @param gltfModel The loaded GLTF model.

attachments/simple_engine/physics_system.cpp

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,21 @@ PhysicsSystem::~PhysicsSystem() {
180180
}
181181

182182
bool PhysicsSystem::Initialize() {
183-
// This is a placeholder implementation
184-
// In a real implementation, this would initialize the physics engine
185-
186-
// Initialize Vulkan resources if GPU acceleration is enabled and the renderer is set
187-
if (gpuAccelerationEnabled && renderer) {
188-
if (!InitializeVulkanResources()) {
189-
std::cerr << "Failed to initialize Vulkan resources for physics system" << std::endl;
190-
gpuAccelerationEnabled = false;
191-
}
183+
// Enforce GPU-only physics. If GPU resources cannot be initialized, initialization fails.
184+
185+
// Renderer must be set for GPU compute physics
186+
if (!renderer) {
187+
std::cerr << "PhysicsSystem::Initialize: Renderer is not set. GPU-only physics cannot proceed." << std::endl;
188+
return false;
189+
}
190+
191+
// Always keep GPU acceleration enabled (CPU fallback is not allowed)
192+
gpuAccelerationEnabled = true;
193+
194+
// Initialize Vulkan resources; fail hard if not available
195+
if (!InitializeVulkanResources()) {
196+
std::cerr << "PhysicsSystem::Initialize: Failed to initialize Vulkan resources for physics (GPU-only)." << std::endl;
197+
return false;
192198
}
193199

194200
initialized = true;
@@ -292,7 +298,7 @@ bool PhysicsSystem::Raycast(const glm::vec3& origin, const glm::vec3& direction,
292298
switch (shape) {
293299
case CollisionShape::Sphere: {
294300
// Sphere intersection test
295-
float radius = 0.5f; // Default radius
301+
float radius = 0.0335f; // Tennis ball radius to match actual ball
296302

297303
// Calculate coefficients for quadratic equation
298304
glm::vec3 oc = origin - position;
@@ -765,14 +771,10 @@ bool PhysicsSystem::InitializeVulkanResources() {
765771

766772
// Create persistent mapped memory pointers for improved performance
767773
try {
768-
// Map physics buffer memory persistently
769-
vulkanResources.persistentPhysicsMemory = vulkanResources.physicsBufferMemory.mapMemory(0, physicsBufferSize);
770-
771-
// Map counter buffer memory persistently
772-
vulkanResources.persistentCounterMemory = vulkanResources.counterBufferMemory.mapMemory(0, counterBufferSize);
773-
774-
// Map params buffer memory persistently
775-
vulkanResources.persistentParamsMemory = vulkanResources.paramsBufferMemory.mapMemory(0, paramsBufferSize);
774+
// Map entire memory objects persistently to satisfy VK_WHOLE_SIZE flush alignment requirements
775+
vulkanResources.persistentPhysicsMemory = vulkanResources.physicsBufferMemory.mapMemory(0, VK_WHOLE_SIZE);
776+
vulkanResources.persistentCounterMemory = vulkanResources.counterBufferMemory.mapMemory(0, VK_WHOLE_SIZE);
777+
vulkanResources.persistentParamsMemory = vulkanResources.paramsBufferMemory.mapMemory(0, VK_WHOLE_SIZE);
776778
} catch (const std::exception& e) {
777779
throw std::runtime_error("Failed to create persistent mapped memory: " + std::string(e.what()));
778780
}
@@ -877,10 +879,10 @@ bool PhysicsSystem::InitializeVulkanResources() {
877879

878880
raiiDevice.updateDescriptorSets(descriptorWrites, nullptr);
879881

880-
// Create a command pool
882+
// Create a command pool bound to the compute queue family used by the renderer
881883
vk::CommandPoolCreateInfo commandPoolInfo;
882884
commandPoolInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
883-
commandPoolInfo.queueFamilyIndex = 0; // Assuming the compute queue family index is 0
885+
commandPoolInfo.queueFamilyIndex = renderer->GetComputeQueueFamilyIndex();
884886
vulkanResources.commandPool = vk::raii::CommandPool(raiiDevice, commandPoolInfo);
885887

886888
// Allocate command buffer
@@ -1009,8 +1011,10 @@ void PhysicsSystem::UpdateGPUPhysicsData(float deltaTime) const {
10091011

10101012
// For dynamic bodies (balls), allow forces to be applied by
10111013
// The shader will add gravity and other forces each frame
1012-
gpuData[i].force = glm::vec4(initialForce, concreteRigidBody->IsKinematic() ? 1.0f : 0.0f);
1013-
gpuData[i].torque = glm::vec4(initialTorque, 1.0f); // Always use gravity
1014+
bool isKinematic = concreteRigidBody->IsKinematic();
1015+
gpuData[i].force = glm::vec4(initialForce, isKinematic ? 1.0f : 0.0f);
1016+
// Use gravity only for dynamic bodies
1017+
gpuData[i].torque = glm::vec4(initialTorque, isKinematic ? 0.0f : 1.0f);
10141018

10151019
// Set collider data based on a collider type
10161020
switch (concreteRigidBody->GetShape()) {
@@ -1040,7 +1044,9 @@ void PhysicsSystem::UpdateGPUPhysicsData(float deltaTime) const {
10401044
glm::vec3 localCenter = 0.5f * (localMin + localMax);
10411045
glm::vec3 localHalfExtents = 0.5f * (localMax - localMin);
10421046

1043-
glm::mat4 model = xform->GetModelMatrix();
1047+
glm::mat4 model = (meshComp->GetInstanceCount() > 0)
1048+
? meshComp->GetInstance(0).getModelMatrix()
1049+
: xform->GetModelMatrix();
10441050
glm::vec3 centerWS = glm::vec3(model * glm::vec4(localCenter, 1.0f));
10451051

10461052
glm::mat3 RS = glm::mat3(model);
@@ -1057,7 +1063,8 @@ void PhysicsSystem::UpdateGPUPhysicsData(float deltaTime) const {
10571063
}
10581064
}
10591065

1060-
gpuData[i].colliderData = glm::vec4(halfExtents, static_cast<float>(2)); // 2 = Mesh (as Box)
1066+
// Encode Mesh collider as Mesh (type=2) for GPU narrowphase handling (sphere vs mesh)
1067+
gpuData[i].colliderData = glm::vec4(halfExtents, static_cast<float>(2)); // 2 = Mesh (represented as world AABB)
10611068
gpuData[i].colliderData2 = glm::vec4(localOffset, 0.0f);
10621069
}
10631070
break;
@@ -1089,14 +1096,26 @@ void PhysicsSystem::UpdateGPUPhysicsData(float deltaTime) const {
10891096
// Use VK_WHOLE_SIZE to avoid nonCoherentAtomSize alignment validation errors
10901097
try {
10911098
const vk::raii::Device& device = renderer->GetRaiiDevice();
1092-
vk::MappedMemoryRange flushRange;
1093-
flushRange.memory = *vulkanResources.paramsBufferMemory;
1094-
flushRange.offset = 0;
1095-
flushRange.size = VK_WHOLE_SIZE;
1096-
1097-
device.flushMappedMemoryRanges(flushRange);
1099+
// Flush params buffer
1100+
vk::MappedMemoryRange flushRangeParams;
1101+
flushRangeParams.memory = *vulkanResources.paramsBufferMemory;
1102+
flushRangeParams.offset = 0;
1103+
flushRangeParams.size = VK_WHOLE_SIZE;
1104+
device.flushMappedMemoryRanges(flushRangeParams);
1105+
// Flush physics buffer (object data)
1106+
vk::MappedMemoryRange flushRangePhysics;
1107+
flushRangePhysics.memory = *vulkanResources.physicsBufferMemory;
1108+
flushRangePhysics.offset = 0;
1109+
flushRangePhysics.size = VK_WHOLE_SIZE;
1110+
device.flushMappedMemoryRanges(flushRangePhysics);
1111+
// Flush counter buffer (pair and collision counters)
1112+
vk::MappedMemoryRange flushRangeCounter;
1113+
flushRangeCounter.memory = *vulkanResources.counterBufferMemory;
1114+
flushRangeCounter.offset = 0;
1115+
flushRangeCounter.size = VK_WHOLE_SIZE;
1116+
device.flushMappedMemoryRanges(flushRangeCounter);
10981117
} catch (const std::exception& e) {
1099-
fprintf(stderr, "WARNING: Failed to flush params buffer memory: %s", e.what());
1118+
fprintf(stderr, "WARNING: Failed to flush mapped physics memory: %s", e.what());
11001119
}
11011120
}
11021121

@@ -1118,6 +1137,39 @@ void PhysicsSystem::ReadbackGPUPhysicsData() const {
11181137
return;
11191138
}
11201139

1140+
// Ensure GPU writes to HOST_VISIBLE memory are visible to the host before reading
1141+
try {
1142+
vk::MappedMemoryRange invalidateRangePhysics;
1143+
invalidateRangePhysics.memory = *vulkanResources.physicsBufferMemory;
1144+
invalidateRangePhysics.offset = 0;
1145+
invalidateRangePhysics.size = VK_WHOLE_SIZE;
1146+
1147+
vk::MappedMemoryRange invalidateRangeCounter;
1148+
invalidateRangeCounter.memory = *vulkanResources.counterBufferMemory;
1149+
invalidateRangeCounter.offset = 0;
1150+
invalidateRangeCounter.size = VK_WHOLE_SIZE;
1151+
1152+
device.invalidateMappedMemoryRanges({invalidateRangePhysics, invalidateRangeCounter});
1153+
} catch (const std::exception&) {
1154+
// On HOST_COHERENT heaps this may not be required; ignore errors
1155+
}
1156+
1157+
// Optional debug: read and log pair/collision counters for a few frames
1158+
if (vulkanResources.persistentCounterMemory) {
1159+
static uint32_t lastPairCount = UINT32_MAX;
1160+
static uint32_t lastCollisionCount = UINT32_MAX;
1161+
static int debugFrames = 0;
1162+
const uint32_t* counters = static_cast<const uint32_t*>(vulkanResources.persistentCounterMemory);
1163+
uint32_t pairCount = counters[0];
1164+
uint32_t collisionCount = counters[1];
1165+
if (debugFrames < 120 && (pairCount != lastPairCount || collisionCount != lastCollisionCount)) {
1166+
std::cout << "Physics GPU counters - pairs: " << pairCount << ", collisions: " << collisionCount << std::endl;
1167+
lastPairCount = pairCount;
1168+
lastCollisionCount = collisionCount;
1169+
debugFrames++;
1170+
}
1171+
}
1172+
11211173
// Skip physics buffer operations if no rigid bodies exist
11221174
if (!rigidBodies.empty()) {
11231175
// Use persistent mapped memory for physics buffer readback
@@ -1176,11 +1228,11 @@ void PhysicsSystem::SimulatePhysicsOnGPU(const float deltaTime) const {
11761228
nullptr
11771229
);
11781230

1179-
// Add a memory barrier to ensure host-written uniform buffer data is visible to shader
1180-
// This ensures the PhysicsParams data uploaded from CPU is properly synchronized before shader execution
1231+
// Add a memory barrier to ensure all host-written buffer data (uniform + storage) is visible to compute shaders
1232+
// We use ShaderRead | ShaderWrite since compute will read and write storage buffers
11811233
vk::MemoryBarrier hostToDeviceBarrier;
11821234
hostToDeviceBarrier.srcAccessMask = vk::AccessFlagBits::eHostWrite;
1183-
hostToDeviceBarrier.dstAccessMask = vk::AccessFlagBits::eUniformRead;
1235+
hostToDeviceBarrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
11841236

11851237
vulkanResources.commandBuffer.pipelineBarrier(
11861238
vk::PipelineStageFlagBits::eHost,

attachments/simple_engine/physics_system.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,11 @@ class PhysicsSystem {
256256
* @brief Enable or disable GPU acceleration.
257257
* @param enabled Whether GPU acceleration is enabled.
258258
*/
259-
void SetGPUAccelerationEnabled(bool enabled) { gpuAccelerationEnabled = enabled; }
259+
void SetGPUAccelerationEnabled(bool enabled) {
260+
// Enforce GPU-only policy: disabling GPU acceleration is not allowed in this project.
261+
// Ignore attempts to disable and keep GPU acceleration enabled.
262+
gpuAccelerationEnabled = true;
263+
}
260264

261265
/**
262266
* @brief Check if GPU acceleration is enabled.

attachments/simple_engine/renderer_rendering.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ void Renderer::Render(const std::vector<Entity*>& entities, CameraComponent* cam
745745
.commandBufferCount = 1,
746746
.pCommandBuffers = &*commandBuffers[currentFrame],
747747
.signalSemaphoreCount = 1,
748-
.pSignalSemaphores = &*renderFinishedSemaphores[currentFrame]
748+
.pSignalSemaphores = &*renderFinishedSemaphores[imageIndex]
749749
};
750750

751751
// Use mutex to ensure thread-safe access to graphics queue
@@ -757,7 +757,7 @@ void Renderer::Render(const std::vector<Entity*>& entities, CameraComponent* cam
757757
// Present the image
758758
vk::PresentInfoKHR presentInfo{
759759
.waitSemaphoreCount = 1,
760-
.pWaitSemaphores = &*renderFinishedSemaphores[currentFrame],
760+
.pWaitSemaphores = &*renderFinishedSemaphores[imageIndex],
761761
.swapchainCount = 1,
762762
.pSwapchains = &*swapChain,
763763
.pImageIndices = &imageIndex

attachments/simple_engine/renderer_resources.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,23 +160,8 @@ bool Renderer::createTextureImage(const std::string& texturePath_, TextureResour
160160
if (loaded) break;
161161
}
162162
}
163-
if (result != KTX_SUCCESS) {
164-
// Last-ditch fallback: try alternate image formats (.png/.jpg/.jpeg) with same basename
165-
std::filesystem::path orig = std::filesystem::path(texturePath);
166-
std::vector<std::string> altExts = {".png", ".jpg", ".jpeg", ".PNG", ".JPG", ".JPEG", ".dds", ".DDS", ".ktx", ".KTX"};
167-
for (const auto& ext : altExts) {
168-
std::filesystem::path cand = orig;
169-
cand.replace_extension(ext);
170-
if (std::filesystem::exists(cand)) {
171-
// Alternate non-KTX fallback removed per simplification policy
172-
(void)ext; (void)orig; (void)cand; // suppress unused warnings
173-
}
174-
}
175-
std::cerr << "Failed to load KTX2 texture: " << texturePath << " (error: " << result << ")" << std::endl;
176-
return false;
177-
}
163+
assert (result != KTX_SUCCESS);
178164
}
179-
ktx2_or_alt_loaded:;
180165

181166
// Cache header-provided VkFormat
182167
ktxHeaderVkFormat = static_cast<VkFormat>(ktxTex->vkFormat);

attachments/simple_engine/scene_loading.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ void LoadGLTFModel(Engine* engine, const std::string& modelPath,
113113

114114
// Apply rotation from GLTF camera
115115
glm::vec3 eulerAngles = glm::eulerAngles(gltfCamera.rotation);
116-
cameraTransform->SetRotation(glm::degrees(eulerAngles));
116+
cameraTransform->SetRotation(eulerAngles);
117117
}
118118

119119
// Update the camera component with GLTF properties
@@ -156,7 +156,7 @@ void LoadGLTFModel(Engine* engine, const std::string& modelPath,
156156
// Add a transform component with provided parameters
157157
auto* transform = materialEntity->AddComponent<TransformComponent>();
158158
transform->SetPosition(position);
159-
transform->SetRotation(rotation);
159+
transform->SetRotation(glm::radians(rotation));
160160
transform->SetScale(scale);
161161

162162
// Add a mesh component with material-specific data

0 commit comments

Comments
 (0)