4#include "flint/core/log.h"
6#include <SDL3/SDL_gpu.h>
9#include <unordered_map>
11#ifdef FLINT_ENABLE_GPU_TRACKER
22class GPUResourceTracker {
27 static GPUResourceTracker &instance() {
28 static GPUResourceTracker tracker;
38 void trackBuffer(SDL_GPUBuffer *buffer,
const std::string &location) {
42 m_buffers[buffer] = location;
43 FLINT_ENGINE_INFO(
"[GPU TRACK] Buffer {} CREATED at {}",
static_cast<void *
>(buffer),
55 void releaseBuffer(SDL_GPUBuffer *buffer,
const std::string &location) {
59 auto it = m_buffers.find(buffer);
60 if (it != m_buffers.end()) {
61 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Buffer {} RELEASED at {} (was created at {})",
62 static_cast<void *
>(buffer), location, it->second);
66 "[GPU TRACK] Buffer {} RELEASED at {} but was NOT TRACKED (SDL internal?)",
67 static_cast<void *
>(buffer), location);
77 void trackTexture(SDL_GPUTexture *texture,
const std::string &location) {
81 m_textures[texture] = location;
82 FLINT_ENGINE_INFO(
"[GPU TRACK] Texture {} CREATED at {}",
static_cast<void *
>(texture),
92 void releaseTexture(SDL_GPUTexture *texture,
const std::string &location) {
96 auto it = m_textures.find(texture);
97 if (it != m_textures.end()) {
98 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Texture {} RELEASED at {} (was created at {})",
99 static_cast<void *
>(texture), location, it->second);
100 m_textures.erase(it);
102 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Texture {} RELEASED at {} but was NOT TRACKED",
103 static_cast<void *
>(texture), location);
113 void trackSampler(SDL_GPUSampler *sampler,
const std::string &location) {
117 m_samplers[sampler] = location;
118 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Sampler {} CREATED at {}",
static_cast<void *
>(sampler),
128 void releaseSampler(SDL_GPUSampler *sampler,
const std::string &location) {
132 auto it = m_samplers.find(sampler);
133 if (it != m_samplers.end()) {
134 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Sampler {} RELEASED at {} (was created at {})",
135 static_cast<void *
>(sampler), location, it->second);
136 m_samplers.erase(it);
138 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Sampler {} RELEASED at {} but was NOT TRACKED",
139 static_cast<void *
>(sampler), location);
149 void trackShader(SDL_GPUShader *shader,
const std::string &location) {
153 m_shaders[shader] = location;
154 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Shader {} CREATED at {}",
static_cast<void *
>(shader),
164 void releaseShader(SDL_GPUShader *shader,
const std::string &location) {
168 auto it = m_shaders.find(shader);
169 if (it != m_shaders.end()) {
170 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Shader {} RELEASED at {} (was created at {})",
171 static_cast<void *
>(shader), location, it->second);
174 FLINT_ENGINE_DEBUG(
"[GPU TRACK] Shader {} RELEASED at {} but was NOT TRACKED",
175 static_cast<void *
>(shader), location);
190 bool hasLeaks =
false;
192 if (!m_buffers.empty()) {
194 FLINT_ENGINE_ERROR(
"╔═══════════════════════════════════════════╗");
195 FLINT_ENGINE_ERROR(
"║ GPU BUFFER LEAKS DETECTED: {} ║", m_buffers.size());
196 FLINT_ENGINE_ERROR(
"╚═══════════════════════════════════════════╝");
197 for (
const auto &[buffer, location] : m_buffers) {
198 FLINT_ENGINE_ERROR(
" → Buffer {} created at {}",
static_cast<void *
>(buffer), location);
202 if (!m_textures.empty()) {
204 FLINT_ENGINE_ERROR(
"╔═══════════════════════════════════════════╗");
205 FLINT_ENGINE_ERROR(
"║ GPU TEXTURE LEAKS DETECTED: {} ║", m_textures.size());
206 FLINT_ENGINE_ERROR(
"╚═══════════════════════════════════════════╝");
207 for (
const auto &[texture, location] : m_textures) {
208 FLINT_ENGINE_ERROR(
" → Texture {} created at {}",
static_cast<void *
>(texture),
213 if (!m_samplers.empty()) {
215 FLINT_ENGINE_ERROR(
"╔═══════════════════════════════════════════╗");
216 FLINT_ENGINE_ERROR(
"║ GPU SAMPLER LEAKS DETECTED: {} ║", m_samplers.size());
217 FLINT_ENGINE_ERROR(
"╚═══════════════════════════════════════════╝");
218 for (
const auto &[sampler, location] : m_samplers) {
219 FLINT_ENGINE_ERROR(
" → Sampler {} created at {}",
static_cast<void *
>(sampler),
224 if (!m_shaders.empty()) {
226 FLINT_ENGINE_ERROR(
"╔═══════════════════════════════════════════╗");
227 FLINT_ENGINE_ERROR(
"║ GPU SHADER LEAKS DETECTED: {} ║", m_shaders.size());
228 FLINT_ENGINE_ERROR(
"╚═══════════════════════════════════════════╝");
229 for (
const auto &[shader, location] : m_shaders) {
230 FLINT_ENGINE_ERROR(
" → Shader {} created at {}",
static_cast<void *
>(shader), location);
235 FLINT_ENGINE_INFO(
"╔═══════════════════════════════════════════╗");
236 FLINT_ENGINE_INFO(
"║ ✓ NO GPU RESOURCE LEAKS DETECTED! ║");
237 FLINT_ENGINE_INFO(
"╚═══════════════════════════════════════════╝");
239 FLINT_ENGINE_ERROR(
"");
240 FLINT_ENGINE_ERROR(
"NOTE: If Vulkan validation shows leaks but tracker doesn't,");
241 FLINT_ENGINE_ERROR(
" those are SDL3/driver internal resources, not your code!");
246 std::unordered_map<SDL_GPUBuffer *, std::string> m_buffers;
247 std::unordered_map<SDL_GPUTexture *, std::string> m_textures;
248 std::unordered_map<SDL_GPUSampler *, std::string> m_samplers;
249 std::unordered_map<SDL_GPUShader *, std::string> m_shaders;
252 #define GPU_TRACK_LOCATION (std::string(__FILE__) + ":" + std::to_string(__LINE__))
254 #define GPU_TRACK_BUFFER(buffer, name) \
255 GPUResourceTracker::instance().trackBuffer(buffer, GPU_TRACK_LOCATION)
257 #define GPU_RELEASE_BUFFER(device, buffer) \
259 GPUResourceTracker::instance().releaseBuffer(buffer, GPU_TRACK_LOCATION); \
260 SDL_ReleaseGPUBuffer(device, buffer); \
263 #define GPU_TRACK_TEXTURE(texture, name) \
264 GPUResourceTracker::instance().trackTexture(texture, GPU_TRACK_LOCATION)
266 #define GPU_RELEASE_TEXTURE(device, texture) \
268 GPUResourceTracker::instance().releaseTexture(texture, GPU_TRACK_LOCATION); \
269 SDL_ReleaseGPUTexture(device, texture); \
272 #define GPU_TRACK_SAMPLER(sampler, name) \
273 GPUResourceTracker::instance().trackSampler(sampler, GPU_TRACK_LOCATION)
275 #define GPU_RELEASE_SAMPLER(device, sampler) \
277 GPUResourceTracker::instance().releaseSampler(sampler, GPU_TRACK_LOCATION); \
278 SDL_ReleaseGPUSampler(device, sampler); \
281 #define GPU_TRACK_SHADER(shader, name) \
282 GPUResourceTracker::instance().trackShader(shader, GPU_TRACK_LOCATION)
284 #define GPU_RELEASE_SHADER(device, shader) \
286 GPUResourceTracker::instance().releaseShader(shader, GPU_TRACK_LOCATION); \
287 SDL_ReleaseGPUShader(device, shader); \
290 #define GPU_REPORT_LEAKS() GPUResourceTracker::instance().reportLeaks()
294 #define GPU_TRACK_BUFFER(buffer, name)
295 #define GPU_RELEASE_BUFFER(device, buffer) SDL_ReleaseGPUBuffer(device, buffer)
296 #define GPU_TRACK_TEXTURE(texture, name)
297 #define GPU_RELEASE_TEXTURE(device, texture) SDL_ReleaseGPUTexture(device, texture)
298 #define GPU_TRACK_SAMPLER(sampler, name)
299 #define GPU_RELEASE_SAMPLER(device, sampler) SDL_ReleaseGPUSampler(device, sampler)
300 #define GPU_TRACK_SHADER(shader, name)
301 #define GPU_RELEASE_SHADER(device, shader) SDL_ReleaseGPUShader(device, shader)
302 #define GPU_REPORT_LEAKS()