diff --git a/libvita2d/include/vita2d.h b/libvita2d/include/vita2d.h index b1e48cd..6c59183 100644 --- a/libvita2d/include/vita2d.h +++ b/libvita2d/include/vita2d.h @@ -105,8 +105,16 @@ vita2d_texture *vita2d_create_empty_texture(unsigned int w, unsigned int h); vita2d_texture *vita2d_create_empty_texture_format(unsigned int w, unsigned int h, SceGxmTextureFormat format); vita2d_texture *vita2d_create_empty_texture_rendertarget(unsigned int w, unsigned int h, SceGxmTextureFormat format); +// Mark texture for deletion; will be deleted at next vita2d_swap_buffers() or +// when vita2d_gc_textures() is called (to avoid GPU crashes when deleting +// textures still required by the GPU to finish rendering the current frame) void vita2d_free_texture(vita2d_texture *texture); +// This will be called automatically by vita2d_swap_buffers(), but you can call +// it earlier in case you allocate/deallocate many textures in a single frame; +// returns the number of texture objects freed +int vita2d_gc_textures(); + unsigned int vita2d_texture_get_width(const vita2d_texture *texture); unsigned int vita2d_texture_get_height(const vita2d_texture *texture); unsigned int vita2d_texture_get_stride(const vita2d_texture *texture); diff --git a/libvita2d/source/vita2d.c b/libvita2d/source/vita2d.c index d64988e..a893ba2 100755 --- a/libvita2d/source/vita2d.c +++ b/libvita2d/source/vita2d.c @@ -738,6 +738,10 @@ int vita2d_fini() // wait until rendering is done sceGxmFinish(_vita2d_context); + // in case vita2d_free_texture() was called after the last vita2d_swap_buffers(), + // we need to GC textures here again, otherwise we would leak texture objects + vita2d_gc_textures(); + // clean up allocations sceGxmShaderPatcherReleaseFragmentProgram(shaderPatcher, clearFragmentProgram); sceGxmShaderPatcherReleaseVertexProgram(shaderPatcher, clearVertexProgram); @@ -844,6 +848,9 @@ void vita2d_swap_buffers() frontBufferIndex = backBufferIndex; backBufferIndex = (backBufferIndex + 1) % DISPLAY_BUFFER_COUNT; } + + // free any textures marked for deletion + vita2d_gc_textures(); } void vita2d_start_drawing() diff --git a/libvita2d/source/vita2d_texture.c b/libvita2d/source/vita2d_texture.c index 5202a95..8d2ffa3 100755 --- a/libvita2d/source/vita2d_texture.c +++ b/libvita2d/source/vita2d_texture.c @@ -197,7 +197,7 @@ vita2d_texture * vita2d_create_empty_texture_rendertarget(unsigned int w, unsign return _vita2d_create_empty_texture_format_advanced(w, h, format, 1); } -void vita2d_free_texture(vita2d_texture *texture) +static void _vita2d_free_texture_internal(vita2d_texture *texture) { if (texture) { if (texture->gxm_rtgt) { @@ -214,6 +214,47 @@ void vita2d_free_texture(vita2d_texture *texture) } } +typedef struct texture_freelist_entry texture_freelist_entry; + +// TODO: If we store the "next" pointer in vita2d_texture, we can +// use it for an intrusive linked list and don't need to malloc +struct texture_freelist_entry { + vita2d_texture *texture; + texture_freelist_entry *next; +}; + +// Note: Operations on _texture_freelist are not threadsafe at the moment +static texture_freelist_entry *_texture_freelist = NULL; + +void vita2d_free_texture(vita2d_texture *texture) +{ + texture_freelist_entry *entry = malloc(sizeof(texture_freelist_entry)); + entry->texture = texture; + entry->next = _texture_freelist; + _texture_freelist = entry; +} + +int vita2d_gc_textures() +{ + if (_texture_freelist == NULL) { + // Nothing to do, exit early without waiting + return 0; + } + + // Need to free textures, wait for GPU first + vita2d_wait_rendering_done(); + + int res = 0; + while (_texture_freelist != NULL) { + texture_freelist_entry *entry = _texture_freelist; + _texture_freelist = entry->next; + _vita2d_free_texture_internal(entry->texture); + free(entry); + ++res; + } + return res; +} + unsigned int vita2d_texture_get_width(const vita2d_texture *texture) { return sceGxmTextureGetWidth(&texture->gxm_tex);