Kelsidavis-WoWee/include/rendering/vk_frame_data.hpp
Kelsi bbb560f93c fix: data race on collision query profiling counters
queryTimeMs and queryCallCount on WMORenderer and M2Renderer were plain
mutable doubles/uint32s written by getFloorHeight (dispatched on async
threads from CameraController) and read by the main thread. This is
undefined behavior per C++ — thread sanitizer would flag it. Changed to
std::atomic with relaxed ordering (adequate for diagnostics) and updated
QueryTimer to use atomic fetch_add/compare_exchange.
2026-03-29 21:26:11 -07:00

70 lines
2.3 KiB
C++

#pragma once
#include <vulkan/vulkan.h>
#include <glm/glm.hpp>
#include <atomic>
#include <chrono>
namespace wowee {
namespace rendering {
// Must match the PerFrame UBO layout in all shaders (std140 alignment)
struct GPUPerFrameData {
glm::mat4 view;
glm::mat4 projection;
glm::mat4 lightSpaceMatrix;
glm::vec4 lightDir; // xyz = direction, w = unused
glm::vec4 lightColor; // xyz = color, w = unused
glm::vec4 ambientColor; // xyz = color, w = unused
glm::vec4 viewPos; // xyz = camera pos, w = unused
glm::vec4 fogColor; // xyz = color, w = unused
glm::vec4 fogParams; // x = fogStart, y = fogEnd, z = time, w = unused
glm::vec4 shadowParams; // x = enabled(0/1), y = strength, z = unused, w = unused
};
// Push constants for the model matrix (most common case)
struct GPUPushConstants {
glm::mat4 model;
};
// Push constants for shadow rendering passes
struct ShadowPush {
glm::mat4 lightSpaceMatrix;
glm::mat4 model;
};
// Uniform buffer for shadow rendering parameters (matches shader std140 layout)
struct ShadowParamsUBO {
int32_t useBones;
int32_t useTexture;
int32_t alphaTest;
int32_t foliageSway;
float windTime;
float foliageMotionDamp;
};
// Timer utility for performance profiling queries.
// Uses atomics because floor-height queries are dispatched on async threads
// from CameraController while the main thread may read the counters.
struct QueryTimer {
std::atomic<double>* totalMs = nullptr;
std::atomic<uint32_t>* callCount = nullptr;
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
QueryTimer(std::atomic<double>* total, std::atomic<uint32_t>* calls)
: totalMs(total), callCount(calls) {}
~QueryTimer() {
if (callCount) {
callCount->fetch_add(1, std::memory_order_relaxed);
}
if (totalMs) {
auto end = std::chrono::steady_clock::now();
double ms = std::chrono::duration<double, std::milli>(end - start).count();
// Relaxed is fine for diagnostics — exact ordering doesn't matter.
double old = totalMs->load(std::memory_order_relaxed);
while (!totalMs->compare_exchange_weak(old, old + ms, std::memory_order_relaxed)) {}
}
}
};
} // namespace rendering
} // namespace wowee