fix texture free asan

This commit is contained in:
2026-02-19 03:47:11 -06:00
parent d509f7cbb8
commit 5975298ec4

79
gpu.c
View File

@@ -4,6 +4,21 @@
#include <SDL3/SDL_gpu.h>
#include "cell.h"
// Shared flag so GPU resource wrappers know if the device is still alive.
// Allocated once per device; all wrappers hold a pointer to it.
// The device finalizer sets alive = 0 before calling SDL_DestroyGPUDevice,
// so wrapper finalizers that run later (arbitrary GC order) skip SDL_Release.
// Reference counted so the last user frees it.
typedef struct {
int alive;
int refcount;
} gpu_device_state;
static void gpu_device_state_ref(gpu_device_state *s) { if (s) s->refcount++; }
static void gpu_device_state_unref(gpu_device_state *s) {
if (s && --s->refcount <= 0) free(s);
}
// Macro for GPU wrapper classes that need cleanup
#define QJSCLASSGPUWRAPPER(WRAPPERTYPE, SDLTYPE) \
typedef struct { \
@@ -11,13 +26,17 @@ typedef struct { \
JSValue js_device; \
SDL_##SDLTYPE *type; \
int owned; \
gpu_device_state *state; \
} WRAPPERTYPE; \
JSClassID js_SDL_##SDLTYPE##_id; \
static void js_SDL_##SDLTYPE##_finalizer(JSRuntime *rt, JSValue val) { \
WRAPPERTYPE *wrapper = JS_GetOpaque(val, js_SDL_##SDLTYPE##_id); \
JS_FreeValueRT(rt, wrapper->js_device); \
if (wrapper && wrapper->device && wrapper->type && wrapper->owned) \
if (!wrapper) return; \
if (wrapper->state && wrapper->state->alive && \
wrapper->device && wrapper->type && wrapper->owned) \
SDL_Release##SDLTYPE(wrapper->device, wrapper->type); \
gpu_device_state_unref(wrapper->state); \
JS_FreeValueRT(rt, wrapper->js_device); \
free(wrapper); \
} \
static JSClassDef js_SDL_##SDLTYPE##_class = { \
@@ -35,6 +54,8 @@ JSValue SDL_##SDLTYPE##2js(JSContext *js, JSValue device, SDL_##SDLTYPE *member)
wrapper->device = js2SDL_GPUDevice(js, device); \
wrapper->type = member; \
wrapper->owned = 1; \
wrapper->state = js2gpu_device_state(js, device); \
gpu_device_state_ref(wrapper->state); \
JSValue j = JS_NewObjectClass(js, js_SDL_##SDLTYPE##_id); \
JS_SetOpaque(j, wrapper); \
return j; \
@@ -47,11 +68,6 @@ const char *js2cstring(JSContext *js, JSValue v)
}
// GPU Free functions
void SDL_GPUDevice_free(JSRuntime *rt, SDL_GPUDevice *d)
{
SDL_DestroyGPUDevice(d);
}
void SDL_GPUCommandBuffer_free(JSRuntime *rt, void *w)
{
}
@@ -66,8 +82,52 @@ void SDL_GPUComputePass_free(JSRuntime *rt, SDL_GPUComputePass *c) { }
void SDL_GPUCopyPass_free(JSRuntime *rt, SDL_GPUCopyPass *c) { }
void SDL_GPURenderPass_free(JSRuntime *rt, SDL_GPURenderPass *c) { }
// GPU Class definitions
QJSCLASS(SDL_GPUDevice,)
// GPU Device: custom implementation with shared alive flag
typedef struct {
SDL_GPUDevice *device;
gpu_device_state *state;
} gpu_device_handle;
JSClassID js_SDL_GPUDevice_id;
static void js_SDL_GPUDevice_finalizer(JSRuntime *rt, JSValue val) {
gpu_device_handle *h = JS_GetOpaque(val, js_SDL_GPUDevice_id);
if (h) {
if (h->state) h->state->alive = 0;
if (h->device) SDL_DestroyGPUDevice(h->device);
gpu_device_state_unref(h->state);
free(h);
}
}
static JSClassDef js_SDL_GPUDevice_class = {
.class_name = "SDL_GPUDevice",
.finalizer = js_SDL_GPUDevice_finalizer,
};
SDL_GPUDevice *js2SDL_GPUDevice(JSContext *js, JSValue val) {
if (JS_GetClassID(val) != js_SDL_GPUDevice_id) return NULL;
gpu_device_handle *h = JS_GetOpaque(val, js_SDL_GPUDevice_id);
return h ? h->device : NULL;
}
gpu_device_state *js2gpu_device_state(JSContext *js, JSValue val) {
if (JS_GetClassID(val) != js_SDL_GPUDevice_id) return NULL;
gpu_device_handle *h = JS_GetOpaque(val, js_SDL_GPUDevice_id);
return h ? h->state : NULL;
}
JSValue SDL_GPUDevice2js(JSContext *js, SDL_GPUDevice *d) {
gpu_device_handle *h = malloc(sizeof(gpu_device_handle));
h->device = d;
h->state = malloc(sizeof(gpu_device_state));
h->state->alive = 1;
h->state->refcount = 1;
JSValue j = JS_NewObjectClass(js, js_SDL_GPUDevice_id);
JS_SetOpaque(j, h);
return j;
}
QJSCLASSGPUWRAPPER(gpu_buffer_wrapper, GPUBuffer)
QJSCLASSGPUWRAPPER(gpu_compute_pipeline_wrapper, GPUComputePipeline)
QJSCLASSGPUWRAPPER(gpu_graphics_pipeline_wrapper, GPUGraphicsPipeline)
@@ -1452,6 +1512,7 @@ JSC_CCALL(cmd_acquire_swapchain_texture,
wrapper->device = NULL; // We don't need the device pointer as we don't release
wrapper->type = swapchainTexture;
wrapper->owned = 0; // CRITICAL: Do not release this texture
wrapper->state = NULL; // No device state needed for non-owned textures
JS_FRAME(js);
JS_LOCAL(jstex, JS_NewObjectClass(js, js_SDL_GPUTexture_id));