Overlays: upload the pack once, switch pages without uploads

The overlay interface gains load_textures(): a page's textures by
handle, from which the driver takes only its per-image geometry and
alpha, uploading and owning nothing. When the driver has it, the
frontend uploads every unique image of the pack once through
video_driver_texture_load() on enable - the loader already
deduplicates by path, so a page's entry shares its pixels with one
unique image - and every page gets a list of handles into that set,
so a page switch decodes nothing, uploads nothing, creates and
destroys no texture and waits for no GPU. The handles are unloaded by
the disable, while the driver is still there to do so; a driver that
declines the handles (the wrapper answers for any driver) puts the
pack back on load() for that enable, and drivers without the entry
are unchanged.

gl2 and gl3 keep the pack's names in their overlay block behind an
OVERLAY_BORROWED flag so the page's free deletes nothing, gl3 no
longer generating a spare set of names it then overwrote. Vulkan,
D3D11 and D3D12 draw from copies of the pack's texture structs, again
never released with the page: Vulkan's vkQueueWaitIdle and D3D12's
queue drain stay on the pixel path only, and both D3D drivers reuse
the page's sprite buffer while it is big enough and free the sprite
array a switch used to leak. Metal's Overlay retains the pack's
MTLTextures in its array. The threaded wrapper marshals the new call
beside CMD_OVERLAY_LOAD.
This commit is contained in:
libretroadmin committed 2026-09-17 07:22:42 +02:00
1 parent 3049113116
commit df4b5a7e0b
26 files changed
+630 -152

No files matched your search

+3
View File
@@ -72,6 +72,9 @@ enum gl2_flags
* live settings from the frame path (the video thread, with the
* main thread running free). */
GL2_FLAG_HW_RING_EXPECTED = (1 << 27),
/* overlay_tex names belong to the overlay pack (load_textures):
* not deleted with the page. */
GL2_FLAG_OVERLAY_BORROWED = (1 << 28),
/* GPU recording is on: taken from the frame the frontend hands over,
* so this thread never reads the recording state the main thread
* writes (video_frame_info_t::gpu_recording). */
+3
View File
@@ -62,6 +62,9 @@ enum gl3_flags
* live settings from the frame path (the video thread, with the
* main thread running free). */
GL3_FLAG_HW_RING_EXPECTED = (1 << 16),
/* overlay_tex names belong to the overlay pack (load_textures):
* not deleted with the page. */
GL3_FLAG_OVERLAY_BORROWED = (1 << 17),
/* GPU recording is on: taken from the frame the frontend hands over,
* so this thread never reads the recording state the main thread
* writes (video_frame_info_t::gpu_recording). */
+1
View File
@@ -2923,6 +2923,7 @@ static void ctr_render_overlay(ctr_video_t *ctr)
static const video_overlay_interface_t ctr_overlay = {
ctr_overlay_enable,
ctr_overlay_load,
NULL, /* load_textures */
ctr_overlay_tex_geom,
ctr_overlay_vertex_geom,
ctr_overlay_full_screen,
+1 -1
View File
@@ -1488,7 +1488,7 @@ static void d3d10_overlay_full_screen(void* data, bool enable)
static void d3d10_get_overlay_interface(void* data, const video_overlay_interface_t** iface)
{
static const video_overlay_interface_t overlay_interface = {
d3d10_overlay_enable, d3d10_overlay_load, d3d10_overlay_tex_geom,
d3d10_overlay_enable, d3d10_overlay_load, NULL, /* load_textures */ d3d10_overlay_tex_geom,
d3d10_overlay_vertex_geom, d3d10_overlay_full_screen, d3d10_overlay_set_alpha,
};
+104 -37
View File
@@ -427,6 +427,10 @@ typedef struct
D3D11Buffer vbo;
d3d11_texture_t* textures;
int count;
int vbo_capacity; /* sprites the vbo holds */
/* textures are copies of the overlay pack's (load_textures):
* drawn from, never released here. */
bool borrowed;
} overlays;
#endif
@@ -1765,10 +1769,71 @@ static uint32_t d3d11_get_flags(void *data)
static void d3d11_free_overlays(d3d11_video_t* d3d11)
{
int i;
for (i = 0; i < d3d11->overlays.count; i++)
d3d11_release_texture(&d3d11->overlays.textures[i]);
if (!d3d11->overlays.borrowed)
for (i = 0; i < d3d11->overlays.count; i++)
d3d11_release_texture(&d3d11->overlays.textures[i]);
free(d3d11->overlays.textures);
d3d11->overlays.textures = NULL;
d3d11->overlays.count = 0;
d3d11->overlays.borrowed = false;
Release(d3d11->overlays.vbo);
d3d11->overlays.vbo = NULL;
d3d11->overlays.vbo_capacity = 0;
}
/* A page's sprite buffer, reused across pages while it is big enough,
* and its sprites reset to the whole screen in white. */
static d3d11_sprite_t *d3d11_overlay_sprites_begin(d3d11_video_t *d3d11,
unsigned num, D3D11_MAPPED_SUBRESOURCE *mapped_vbo)
{
unsigned i;
d3d11_sprite_t *sprites;
if (!d3d11->overlays.vbo || d3d11->overlays.vbo_capacity < (int)num)
{
D3D11_BUFFER_DESC desc;
Release(d3d11->overlays.vbo);
d3d11->overlays.vbo = NULL;
desc.ByteWidth = sizeof(d3d11_sprite_t) * num;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
if (FAILED(d3d11->device->lpVtbl->CreateBuffer(d3d11->device,
&desc, NULL, &d3d11->overlays.vbo)))
return NULL;
d3d11->overlays.vbo_capacity = num;
}
if (FAILED(d3d11->context->lpVtbl->Map(d3d11->context,
(D3D11Resource)d3d11->overlays.vbo, 0,
D3D11_MAP_WRITE_DISCARD, 0, mapped_vbo)))
return NULL;
sprites = (d3d11_sprite_t*)mapped_vbo->pData;
for (i = 0; i < num; i++)
{
sprites[i].pos.x = 0.0f;
sprites[i].pos.y = 0.0f;
sprites[i].pos.w = 1.0f;
sprites[i].pos.h = 1.0f;
sprites[i].coords.u = 0.0f;
sprites[i].coords.v = 0.0f;
sprites[i].coords.w = 1.0f;
sprites[i].coords.h = 1.0f;
sprites[i].params.scaling = 1;
sprites[i].params.rotation = 0;
sprites[i].colors[0] = 0xFFFFFFFF;
sprites[i].colors[1] = sprites[i].colors[0];
sprites[i].colors[2] = sprites[i].colors[0];
sprites[i].colors[3] = sprites[i].colors[0];
}
return sprites;
}
static void d3d11_overlay_vertex_geom(
@@ -1837,10 +1902,8 @@ static void d3d11_overlay_set_alpha(void* data, unsigned index, float mod)
static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_images)
{
D3D11_BUFFER_DESC desc;
D3D11_MAPPED_SUBRESOURCE mapped_vbo;
unsigned i;
d3d11_sprite_t* sprites;
d3d11_video_t* d3d11 = (d3d11_video_t*)data;
const struct texture_image* images = (const struct texture_image*)image_data;
@@ -1850,24 +1913,15 @@ static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_
d3d11_free_overlays(d3d11);
d3d11->overlays.textures = (d3d11_texture_t*)calloc(
num_images, sizeof(d3d11_texture_t));
if (!d3d11->overlays.textures)
return false;
d3d11->overlays.count = num_images;
desc.ByteWidth = sizeof(d3d11_sprite_t) * num_images;
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.MiscFlags = 0;
desc.StructureByteStride = 0;
d3d11->device->lpVtbl->CreateBuffer(d3d11->device, &desc, NULL,
&d3d11->overlays.vbo);
d3d11->context->lpVtbl->Map(
d3d11->context, (D3D11Resource)d3d11->overlays.vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_vbo);
sprites = (d3d11_sprite_t*)mapped_vbo.pData;
if (!d3d11_overlay_sprites_begin(d3d11, num_images, &mapped_vbo))
return false;
for (i = 0; i < num_images; i++)
{
d3d11->overlays.textures[i].desc.Width = images[i].width;
d3d11->overlays.textures[i].desc.Height = images[i].height;
d3d11->overlays.textures[i].desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
@@ -1880,30 +1934,42 @@ static bool d3d11_overlay_load(void* data, const void* image_data, unsigned num_
d3d11->context, images[i].width,
images[i].height, 0, DXGI_FORMAT_B8G8R8A8_UNORM,
images[i].pixels, &d3d11->overlays.textures[i]);
sprites[i].pos.x = 0.0f;
sprites[i].pos.y = 0.0f;
sprites[i].pos.w = 1.0f;
sprites[i].pos.h = 1.0f;
sprites[i].coords.u = 0.0f;
sprites[i].coords.v = 0.0f;
sprites[i].coords.w = 1.0f;
sprites[i].coords.h = 1.0f;
sprites[i].params.scaling = 1;
sprites[i].params.rotation = 0;
sprites[i].colors[0] = 0xFFFFFFFF;
sprites[i].colors[1] = sprites[i].colors[0];
sprites[i].colors[2] = sprites[i].colors[0];
sprites[i].colors[3] = sprites[i].colors[0];
}
d3d11->context->lpVtbl->Unmap(d3d11->context, (D3D11Resource)d3d11->overlays.vbo, 0);
return true;
}
/* A page of the pack's textures: copies of d3d11_gfx_load_texture's
* d3d11_texture_t per image to draw from, the sprite buffer reused
* and reset, and nothing uploaded, created or released. */
static bool d3d11_overlay_load_textures(void* data,
const uintptr_t* textures, unsigned num_textures)
{
D3D11_MAPPED_SUBRESOURCE mapped_vbo;
unsigned i;
d3d11_video_t* d3d11 = (d3d11_video_t*)data;
if (!d3d11)
return false;
d3d11_free_overlays(d3d11);
d3d11->overlays.textures = (d3d11_texture_t*)calloc(
num_textures, sizeof(d3d11_texture_t));
if (!d3d11->overlays.textures)
return false;
d3d11->overlays.count = num_textures;
d3d11->overlays.borrowed = true;
if (!d3d11_overlay_sprites_begin(d3d11, num_textures, &mapped_vbo))
return false;
for (i = 0; i < num_textures; i++)
d3d11->overlays.textures[i] = *(const d3d11_texture_t*)textures[i];
d3d11->context->lpVtbl->Unmap(d3d11->context, (D3D11Resource)d3d11->overlays.vbo, 0);
return true;
}
static void d3d11_overlay_enable(void* data, bool state)
{
d3d11_video_t* d3d11 = (d3d11_video_t*)data;
@@ -1935,8 +2001,9 @@ static void d3d11_get_overlay_interface(
void* data, const video_overlay_interface_t** iface)
{
static const video_overlay_interface_t overlay_interface = {
d3d11_overlay_enable, d3d11_overlay_load, d3d11_overlay_tex_geom,
d3d11_overlay_vertex_geom, d3d11_overlay_full_screen, d3d11_overlay_set_alpha,
d3d11_overlay_enable, d3d11_overlay_load, d3d11_overlay_load_textures,
d3d11_overlay_tex_geom, d3d11_overlay_vertex_geom, d3d11_overlay_full_screen,
d3d11_overlay_set_alpha,
};
*iface = &overlay_interface;
+111 -37
View File
@@ -441,6 +441,10 @@ typedef struct
D3D12_VERTEX_BUFFER_VIEW vbo_view;
d3d12_texture_t* textures;
int count;
int vbo_capacity; /* sprites the vbo holds */
/* textures are copies of the overlay pack's (load_textures):
* drawn from, never released here. */
bool borrowed;
} overlays;
#endif
@@ -2087,10 +2091,73 @@ static uint32_t d3d12_get_flags(void *data)
static void d3d12_free_overlays(d3d12_video_t* d3d12)
{
size_t i;
for (i = 0; i < (unsigned)d3d12->overlays.count; i++)
d3d12_release_texture(&d3d12->overlays.textures[i]);
if (!d3d12->overlays.borrowed)
for (i = 0; i < (unsigned)d3d12->overlays.count; i++)
d3d12_release_texture(&d3d12->overlays.textures[i]);
free(d3d12->overlays.textures);
d3d12->overlays.textures = NULL;
d3d12->overlays.count = 0;
d3d12->overlays.borrowed = false;
Release(d3d12->overlays.vbo);
d3d12->overlays.vbo = NULL;
d3d12->overlays.vbo_capacity = 0;
}
/* A page's sprite buffer, mapped: reused across pages while it is big
* enough (the queue is drained at the top of every frame, so between
* frames nothing reads it), else made anew behind a drain of the
* frame that may still read the old one. Sprites reset to the whole
* screen in white. */
static d3d12_sprite_t *d3d12_overlay_sprites_begin(d3d12_video_t *d3d12,
unsigned num)
{
D3D12_RANGE range;
unsigned i;
d3d12_sprite_t *sprites = NULL;
if (!d3d12->overlays.vbo || d3d12->overlays.vbo_capacity < (int)num)
{
d3d12_queue_drain(d3d12);
Release(d3d12->overlays.vbo);
d3d12->overlays.vbo = NULL;
d3d12->overlays.vbo_view.SizeInBytes = sizeof(d3d12_sprite_t) * num;
d3d12->overlays.vbo_view.StrideInBytes = sizeof(d3d12_sprite_t);
d3d12->overlays.vbo_view.BufferLocation = d3d12_create_buffer(
d3d12->device, d3d12->overlays.vbo_view.SizeInBytes,
&d3d12->overlays.vbo);
if (!d3d12->overlays.vbo)
return NULL;
d3d12->overlays.vbo_capacity = num;
}
range.Begin = 0;
range.End = 0;
if (FAILED(D3D12Map(d3d12->overlays.vbo, 0, &range, (void**)&sprites))
|| !sprites)
return NULL;
for (i = 0; i < num; i++)
{
sprites[i].pos.x = 0.0f;
sprites[i].pos.y = 0.0f;
sprites[i].pos.w = 1.0f;
sprites[i].pos.h = 1.0f;
sprites[i].coords.u = 0.0f;
sprites[i].coords.v = 0.0f;
sprites[i].coords.w = 1.0f;
sprites[i].coords.h = 1.0f;
sprites[i].params.scaling = 1;
sprites[i].params.rotation = 0;
sprites[i].colors[0] = 0xFFFFFFFF;
sprites[i].colors[1] = sprites[i].colors[0];
sprites[i].colors[2] = sprites[i].colors[0];
sprites[i].colors[3] = sprites[i].colors[0];
}
return sprites;
}
static void
@@ -2166,31 +2233,24 @@ static void d3d12_overlay_set_alpha(void* data, unsigned index, float mod)
static bool d3d12_overlay_load(void* data, const void* image_data, unsigned num_images)
{
size_t i;
D3D12_RANGE range;
d3d12_sprite_t* sprites = NULL;
d3d12_video_t* d3d12 = (d3d12_video_t*)data;
const struct texture_image* images = (const struct texture_image*)image_data;
if (!d3d12)
return false;
{
D3D12Fence fence = d3d12->queue.fence;
d3d12_queue_drain(d3d12);
}
/* The textures of the page going away may still be read by the
* frame in flight. */
d3d12_queue_drain(d3d12);
d3d12_free_overlays(d3d12);
d3d12->overlays.textures = (d3d12_texture_t*)calloc(num_images, sizeof(d3d12_texture_t));
if (!d3d12->overlays.textures)
return false;
d3d12->overlays.count = num_images;
d3d12->overlays.count = num_images;
d3d12->overlays.vbo_view.SizeInBytes = sizeof(d3d12_sprite_t) * num_images;
d3d12->overlays.vbo_view.StrideInBytes = sizeof(d3d12_sprite_t);
d3d12->overlays.vbo_view.BufferLocation = d3d12_create_buffer(
d3d12->device, d3d12->overlays.vbo_view.SizeInBytes, &d3d12->overlays.vbo);
range.Begin = 0;
range.End = 0;
D3D12Map(d3d12->overlays.vbo, 0, &range, (void**)&sprites);
if (!d3d12_overlay_sprites_begin(d3d12, num_images))
return false;
for (i = 0; i < num_images; i++)
{
@@ -2206,30 +2266,43 @@ static bool d3d12_overlay_load(void* data, const void* image_data, unsigned num_
images[i].width, images[i].height,
0, DXGI_FORMAT_B8G8R8A8_UNORM, images[i].pixels,
&d3d12->overlays.textures[i]);
sprites[i].pos.x = 0.0f;
sprites[i].pos.y = 0.0f;
sprites[i].pos.w = 1.0f;
sprites[i].pos.h = 1.0f;
sprites[i].coords.u = 0.0f;
sprites[i].coords.v = 0.0f;
sprites[i].coords.w = 1.0f;
sprites[i].coords.h = 1.0f;
sprites[i].params.scaling = 1;
sprites[i].params.rotation = 0;
sprites[i].colors[0] = 0xFFFFFFFF;
sprites[i].colors[1] = sprites[i].colors[0];
sprites[i].colors[2] = sprites[i].colors[0];
sprites[i].colors[3] = sprites[i].colors[0];
}
D3D12Unmap(d3d12->overlays.vbo, 0, NULL);
return true;
}
/* A page of the pack's textures: copies of d3d12_gfx_load_texture's
* d3d12_texture_t per image to draw from (a copy still marked dirty
* has its upload recorded by the first draw, as any texture), the
* sprite buffer reused and reset, and no upload, no resource made
* or released and no drain: the textures the old page copied stay
* the pack's. */
static bool d3d12_overlay_load_textures(void* data,
const uintptr_t* textures, unsigned num_textures)
{
size_t i;
d3d12_video_t* d3d12 = (d3d12_video_t*)data;
if (!d3d12)
return false;
d3d12_free_overlays(d3d12);
d3d12->overlays.textures = (d3d12_texture_t*)calloc(num_textures, sizeof(d3d12_texture_t));
if (!d3d12->overlays.textures)
return false;
d3d12->overlays.count = num_textures;
d3d12->overlays.borrowed = true;
if (!d3d12_overlay_sprites_begin(d3d12, num_textures))
return false;
for (i = 0; i < num_textures; i++)
d3d12->overlays.textures[i] = *(const d3d12_texture_t*)textures[i];
D3D12Unmap(d3d12->overlays.vbo, 0, NULL);
return true;
}
static void d3d12_overlay_enable(void* data, bool state)
{
d3d12_video_t* d3d12 = (d3d12_video_t*)data;
@@ -2260,8 +2333,9 @@ static void d3d12_overlay_full_screen(void* data, bool enable)
static void d3d12_get_overlay_interface(void* data, const video_overlay_interface_t** iface)
{
static const video_overlay_interface_t overlay_interface = {
d3d12_overlay_enable, d3d12_overlay_load, d3d12_overlay_tex_geom,
d3d12_overlay_vertex_geom, d3d12_overlay_full_screen, d3d12_overlay_set_alpha,
d3d12_overlay_enable, d3d12_overlay_load, d3d12_overlay_load_textures,
d3d12_overlay_tex_geom, d3d12_overlay_vertex_geom, d3d12_overlay_full_screen,
d3d12_overlay_set_alpha,
};
*iface = &overlay_interface;
+1
View File
@@ -2793,6 +2793,7 @@ static void d3d8_overlay_set_alpha(void *data, unsigned index, float mod)
static const video_overlay_interface_t d3d8_overlay_interface = {
d3d8_overlay_enable,
d3d8_overlay_load,
NULL, /* load_textures */
d3d8_overlay_tex_geom,
d3d8_overlay_vertex_geom,
d3d8_overlay_full_screen,
+1
View File
@@ -4228,6 +4228,7 @@ static void d3d9_cg_overlay_set_alpha(void *data, unsigned index, float mod)
static const video_overlay_interface_t d3d9_cg_overlay_interface = {
d3d9_cg_overlay_enable,
d3d9_cg_overlay_load,
NULL, /* load_textures */
d3d9_cg_overlay_tex_geom,
d3d9_cg_overlay_vertex_geom,
d3d9_cg_overlay_full_screen,
+1
View File
@@ -7535,6 +7535,7 @@ static void d3d9_hlsl_overlay_set_alpha(void *data, unsigned index, float mod)
static const video_overlay_interface_t d3d9_hlsl_overlay_interface = {
d3d9_hlsl_overlay_enable,
d3d9_hlsl_overlay_load,
NULL, /* load_textures */
d3d9_hlsl_overlay_tex_geom,
d3d9_hlsl_overlay_vertex_geom,
d3d9_hlsl_overlay_full_screen,
+1
View File
@@ -3837,6 +3837,7 @@ static void gdi_overlays_render(gdi_t *gdi,
static const video_overlay_interface_t gdi_overlay_interface = {
gdi_overlay_enable,
gdi_overlay_load,
NULL, /* load_textures */
gdi_overlay_tex_geom,
gdi_overlay_vertex_geom,
gdi_overlay_full_screen,
+1
View File
@@ -3358,6 +3358,7 @@ static void gl1_overlay_set_alpha(void *data, unsigned image, float mod)
static const video_overlay_interface_t gl1_overlay_interface = {
gl1_overlay_enable,
gl1_overlay_load,
NULL, /* load_textures */
gl1_overlay_tex_geom,
gl1_overlay_vertex_geom,
gl1_overlay_full_screen,
+73 -43
View File
@@ -6198,11 +6198,57 @@ unsigned *height_p, size_t *pitch_p)
#endif
#ifdef HAVE_OVERLAY
static bool gl2_overlay_load(void *data,
const void *image_data, unsigned num_images)
/* The texture names and the vertex, texture and colour coordinate
* arrays of a page's images come out of one zeroed block, each region
* starting on a 64-byte boundary; overlay_tex owns it. Geometry starts
* as the whole screen, colour as opaque white. Names come from
* @textures when the page shows the pack's textures, else are made
* here for the upload that follows. */
static bool gl2_overlay_alloc(gl2_t *gl, unsigned num_images,
const uintptr_t *textures)
{
size_t o_vertex, o_tex, o_color;
unsigned i, j;
gl2_free_overlay(gl);
o_vertex = ((num_images * sizeof(GLuint)) + 63) & ~(size_t)63;
o_tex = o_vertex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
o_color = o_tex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
gl->overlay_tex = (GLuint*)
calloc(1, o_color + 4 * 4 * num_images * sizeof(GLfloat));
if (!gl->overlay_tex)
return false;
gl->overlay_vertex_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_vertex);
gl->overlay_tex_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_tex);
gl->overlay_color_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_color);
gl->overlays = num_images;
if (textures)
{
for (i = 0; i < num_images; i++)
gl->overlay_tex[i] = (GLuint)textures[i];
gl->flags |= GL2_FLAG_OVERLAY_BORROWED;
}
else
glGenTextures(num_images, gl->overlay_tex);
for (i = 0; i < num_images; i++)
{
gl2_overlay_tex_geom(gl, i, 0, 0, 1, 1);
gl2_overlay_vertex_geom(gl, i, 0, 0, 1, 1);
for (j = 0; j < 16; j++)
gl->overlay_color_coord[16 * i + j] = 1.0f;
}
return true;
}
static bool gl2_overlay_load(void *data,
const void *image_data, unsigned num_images)
{
unsigned i;
bool ok;
gl2_t *gl = (gl2_t*)data;
const struct texture_image *images =
(const struct texture_image*)image_data;
@@ -6213,54 +6259,37 @@ static bool gl2_overlay_load(void *data,
if (gl->flags & GL2_FLAG_SHARED_CONTEXT_USE)
gl->ctx_driver->bind_hw_render(gl->ctx_data, false);
gl2_free_overlay(gl);
/* The texture names and the vertex, texture and colour coordinate
* arrays of all overlay images come out of one zeroed block, each
* region starting on a 64-byte boundary; overlay_tex owns it. */
o_vertex = ((num_images * sizeof(GLuint)) + 63) & ~(size_t)63;
o_tex = o_vertex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
o_color = o_tex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
gl->overlay_tex = (GLuint*)
calloc(1, o_color + 4 * 4 * num_images * sizeof(GLfloat));
if (!gl->overlay_tex)
if ((ok = gl2_overlay_alloc(gl, num_images, NULL)))
{
if ( (gl->flags & GL2_FLAG_SHARED_CONTEXT_USE)
&& !gl2_core_context_is_mains(gl))
gl->ctx_driver->bind_hw_render(gl->ctx_data, true);
return false;
}
for (i = 0; i < num_images; i++)
{
unsigned alignment = gl2_get_alignment(images[i].width
* sizeof(uint32_t));
gl->overlay_vertex_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_vertex);
gl->overlay_tex_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_tex);
gl->overlay_color_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_color);
gl->overlays = num_images;
glGenTextures(num_images, gl->overlay_tex);
for (i = 0; i < num_images; i++)
{
unsigned alignment = gl2_get_alignment(images[i].width
* sizeof(uint32_t));
gl_load_texture_data(gl->overlay_tex[i],
RARCH_WRAP_EDGE, TEXTURE_FILTER_LINEAR,
alignment,
images[i].width, images[i].height, images[i].pixels,
sizeof(uint32_t));
/* Default. Stretch to whole screen. */
gl2_overlay_tex_geom(gl, i, 0, 0, 1, 1);
gl2_overlay_vertex_geom(gl, i, 0, 0, 1, 1);
for (j = 0; j < 16; j++)
gl->overlay_color_coord[16 * i + j] = 1.0f;
gl_load_texture_data(gl->overlay_tex[i],
RARCH_WRAP_EDGE, TEXTURE_FILTER_LINEAR,
alignment,
images[i].width, images[i].height, images[i].pixels,
sizeof(uint32_t));
}
}
if ( (gl->flags & GL2_FLAG_SHARED_CONTEXT_USE)
&& !gl2_core_context_is_mains(gl))
gl->ctx_driver->bind_hw_render(gl->ctx_data, true);
return true;
return ok;
}
/* A page of the pack's textures: no upload, no GL call but the
* geometry setup. The names are gl2_load_texture's, valid on this
* context. */
static bool gl2_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
gl2_t *gl = (gl2_t*)data;
if (!gl)
return false;
return gl2_overlay_alloc(gl, num_textures, textures);
}
static void gl2_overlay_enable(void *data, bool state)
@@ -6308,6 +6337,7 @@ static void gl2_overlay_set_alpha(void *data, unsigned image, float mod)
static const video_overlay_interface_t gl2_overlay_interface = {
gl2_overlay_enable,
gl2_overlay_load,
gl2_overlay_load_textures,
gl2_overlay_tex_geom,
gl2_overlay_vertex_geom,
gl2_overlay_full_screen,
+52 -24
View File
@@ -1796,8 +1796,9 @@ static void gl3_fence_iterate(gl3_t *gl, unsigned hard_sync_frames)
#ifdef HAVE_OVERLAY
static void gl3_free_overlay(gl3_t *gl)
{
if (gl->overlay_tex)
if (gl->overlay_tex && !(gl->flags & GL3_FLAG_OVERLAY_BORROWED))
glDeleteTextures(gl->overlays, gl->overlay_tex);
gl->flags &= ~GL3_FLAG_OVERLAY_BORROWED;
/* The three coordinate arrays are views into the overlay_tex block. */
free(gl->overlay_tex);
@@ -3573,24 +3574,19 @@ static void video_texture_load_gl3(
}
#ifdef HAVE_OVERLAY
static bool gl3_overlay_load(void *data,
const void *image_data, unsigned num_images)
/* The texture names and the vertex, texture and colour coordinate
* arrays of a page's images come out of one zeroed block, each region
* starting on a 64-byte boundary; overlay_tex owns it. Geometry starts
* as the whole screen, colour as opaque white. Names come from
* @textures when the page shows the pack's textures, else are made
* by the upload that follows. */
static bool gl3_overlay_alloc(gl3_t *gl, unsigned num_images,
const uintptr_t *textures)
{
size_t o_vertex, o_tex, o_color;
size_t i;
int j;
GLuint id;
gl3_t *gl = (gl3_t*)data;
const struct texture_image *images =
(const struct texture_image*)image_data;
if (!gl)
return false;
unsigned i, j;
gl3_free_overlay(gl);
/* The texture names and the vertex, texture and colour coordinate
* arrays of all overlay images come out of one zeroed block, each
* region starting on a 64-byte boundary; overlay_tex owns it. */
o_vertex = ((num_images * sizeof(GLuint)) + 63) & ~(size_t)63;
o_tex = o_vertex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
o_color = o_tex + ((2 * 4 * num_images * sizeof(GLfloat) + 63) & ~(size_t)63);
@@ -3605,24 +3601,55 @@ static bool gl3_overlay_load(void *data,
gl->overlay_color_coord = (GLfloat*)((uint8_t*)gl->overlay_tex + o_color);
gl->overlays = num_images;
glGenTextures(num_images, gl->overlay_tex);
if (textures)
{
for (i = 0; i < num_images; i++)
gl->overlay_tex[i] = (GLuint)textures[i];
gl->flags |= GL3_FLAG_OVERLAY_BORROWED;
}
for (i = 0; i < num_images; i++)
{
gl3_overlay_tex_geom (gl, i, 0, 0, 1, 1);
gl3_overlay_vertex_geom(gl, i, 0, 0, 1, 1);
for (j = 0; j < 16; j++)
gl->overlay_color_coord[16 * i + j] = 1.0f;
}
return true;
}
static bool gl3_overlay_load(void *data,
const void *image_data, unsigned num_images)
{
unsigned i;
GLuint id;
gl3_t *gl = (gl3_t*)data;
const struct texture_image *images =
(const struct texture_image*)image_data;
if (!gl || !gl3_overlay_alloc(gl, num_images, NULL))
return false;
for (i = 0; i < num_images; i++)
{
video_texture_load_gl3(&images[i], TEXTURE_FILTER_LINEAR, &id);
gl->overlay_tex[i] = id;
/* Default. Stretch to whole screen. */
gl3_overlay_tex_geom (gl, (unsigned)i, 0, 0, 1, 1);
gl3_overlay_vertex_geom(gl, (unsigned)i, 0, 0, 1, 1);
for (j = 0; j < 16; j++)
gl->overlay_color_coord[16 * i + j] = 1.0f;
}
return true;
}
/* A page of the pack's textures: no upload, no GL call but the
* geometry setup. The names are gl3_load_texture's, valid on this
* context. */
static bool gl3_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
gl3_t *gl = (gl3_t*)data;
if (!gl)
return false;
return gl3_overlay_alloc(gl, num_textures, textures);
}
static void gl3_overlay_enable(void *data, bool state)
{
gl3_t *gl = (gl3_t*)data;
@@ -3669,6 +3696,7 @@ static void gl3_overlay_set_alpha(void *data, unsigned image, float mod)
static const video_overlay_interface_t gl3_overlay_interface = {
gl3_overlay_enable,
gl3_overlay_load,
gl3_overlay_load_textures,
gl3_overlay_tex_geom,
gl3_overlay_vertex_geom,
gl3_overlay_full_screen,
+1
View File
@@ -1537,6 +1537,7 @@ static const video_overlay_interface_t gx2_overlay_interface =
{
gx2_overlay_enable,
gx2_overlay_load,
NULL, /* load_textures */
gx2_overlay_tex_geom,
gx2_overlay_vertex_geom,
gx2_overlay_full_screen,
+1
View File
@@ -1517,6 +1517,7 @@ static void gx_render_overlay(void *data)
static const video_overlay_interface_t gx_overlay_interface = {
gx_overlay_enable,
gx_overlay_load,
NULL, /* load_textures */
gx_overlay_tex_geom,
gx_overlay_vertex_geom,
gx_overlay_full_screen,
+1
View File
@@ -2964,6 +2964,7 @@ static void gxm_render_overlay(void *data)
static const video_overlay_interface_t gxm_overlay_interface = {
gxm_overlay_enable,
gxm_overlay_load,
NULL, /* load_textures */
gxm_overlay_tex_geom,
gxm_overlay_vertex_geom,
gxm_overlay_full_screen,
+1
View File
@@ -1148,6 +1148,7 @@ static void hub75_overlay_set_alpha(void *data, unsigned index, float mod)
static const video_overlay_interface_t hub75_overlay_interface = {
hub75_overlay_enable,
hub75_overlay_load,
NULL, /* load_textures */
hub75_overlay_tex_geom,
hub75_overlay_vertex_geom,
hub75_overlay_full_screen,
+41
View File
@@ -413,6 +413,9 @@ typedef NS_ENUM(NSInteger, ViewDrawState)
@property(nonatomic, readwrite) bool fullscreen;
- (bool)loadImages:(const struct texture_image *)images count:(NSUInteger)count;
/* A page of the overlay pack's textures (metal_load_texture handles):
* the array retains what it shows and nothing is uploaded. */
- (bool)loadTextures:(const uintptr_t *)textures count:(NSUInteger)count;
- (void)updateVertexX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index;
- (void)updateTextureCoordsX:(float)x y:(float)y w:(float)w h:(float)h index:(NSUInteger)index;
- (void)updateAlpha:(float)alpha index:(NSUInteger)index;
@@ -6144,6 +6147,33 @@ typedef struct MTLALIGN(16)
return YES;
}
- (bool)loadTextures:(const uintptr_t *)textures count:(NSUInteger)count
{
size_t i;
[self _freeImages];
RARCH_ASSIGN(_images, [NSMutableArray arrayWithCapacity:count]);
NSUInteger needed = sizeof(SpriteVertex) * count * 4;
if (!_vert || _vert.length < needed)
RARCH_ASSIGN(_vert, RARCH_AUTORELEASE_R([_context.device newBufferWithLength:needed options:PLATFORM_METAL_RESOURCE_STORAGE_MODE]));
for (i = 0; i < count; i++)
{
Texture *t = (__bridge Texture *)(void *)textures[i];
if (!t.texture)
return NO;
_images[i] = t.texture;
[self updateVertexX:0 y:0 w:1 h:1 index:i];
[self updateTextureCoordsX:0 y:0 w:1 h:1 index:i];
[self _updateColorRed:1.0 green:1.0 blue:1.0 alpha:1.0 index:i];
}
_vertDirty = YES;
return YES;
}
- (void)drawWithEncoder:(id<MTLRenderCommandEncoder>)rce
{
size_t i;
@@ -6970,6 +7000,16 @@ static bool metal_overlay_load(void *data,
return [md.overlay loadImages:(const struct texture_image *)images count:num_images];
}
static bool metal_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
MetalDriver *md = (__bridge MetalDriver *)data;
if (!md)
return NO;
return [md.overlay loadTextures:textures count:num_textures];
}
static void metal_overlay_tex_geom(void *data, unsigned index,
float x, float y, float w, float h)
{
@@ -7003,6 +7043,7 @@ static void metal_overlay_set_alpha(void *data, unsigned index, float mod)
static const video_overlay_interface_t metal_overlay_interface = {
metal_overlay_enable,
metal_overlay_load,
metal_overlay_load_textures,
metal_overlay_tex_geom,
metal_overlay_vertex_geom,
metal_overlay_full_screen,
+1
View File
@@ -2227,6 +2227,7 @@ static const video_overlay_interface_t rsx_overlay_interface =
{
rsx_overlay_enable,
rsx_overlay_load,
NULL, /* load_textures */
rsx_overlay_tex_geom,
rsx_overlay_vertex_geom,
rsx_overlay_full_screen,
+1
View File
@@ -2208,6 +2208,7 @@ static void sdl2_overlays_render(sdl2_video_t *vid)
static const video_overlay_interface_t sdl2_overlay_iface = {
sdl2_overlay_enable,
sdl2_overlay_load,
NULL, /* load_textures */
sdl2_overlay_tex_geom,
sdl2_overlay_vertex_geom,
sdl2_overlay_full_screen,
+1
View File
@@ -902,6 +902,7 @@ static void switch_overlay_set_alpha(void *data, unsigned idx, float mod) { }
static const video_overlay_interface_t switch_overlay = {
switch_overlay_enable,
switch_overlay_load,
NULL, /* load_textures */
switch_overlay_tex_geom,
switch_overlay_vertex_geom,
switch_overlay_full_screen,
+65 -5
View File
@@ -377,6 +377,9 @@ typedef struct vk
struct vk_texture *images;
struct vk_vertex *vertex;
unsigned count;
/* images are copies of the overlay pack's textures
* (load_textures): drawn from, never destroyed here. */
bool borrowed;
/* What a batch of overlays is staged into before it is written and
* drawn. Here rather than on the stack of the function that fills
* it: the batch alone is better than four kilobytes, and a frame
@@ -10676,11 +10679,12 @@ static void vulkan_overlay_free(vk_t *vk)
return;
free(vk->overlay.vertex);
for (i = 0; i < (int) vk->overlay.count; i++)
if (vk->overlay.images[i].memory != VK_NULL_HANDLE)
vulkan_destroy_texture(
vk->context->device,
&vk->overlay.images[i]);
if (!vk->overlay.borrowed)
for (i = 0; i < (int) vk->overlay.count; i++)
if (vk->overlay.images[i].memory != VK_NULL_HANDLE)
vulkan_destroy_texture(
vk->context->device,
&vk->overlay.images[i]);
if (vk->overlay.images)
free(vk->overlay.images);
@@ -10925,6 +10929,61 @@ static void vulkan_overlay_tex_geom(void *data, unsigned image,
pv[3].tex_y = y + h;
}
/* A page of the pack's textures: copies of vulkan_load_texture's
* vk_texture per image to draw from, geometry reset, and no upload,
* no device object made and no queue wait - the page's previous
* copies are dropped, and the textures they copied are the pack's
* until the frontend unloads them, deferred as any unload is. */
static bool vulkan_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
int i;
bool old_enabled = false;
vk_t *vk = (vk_t*)data;
static const struct vk_color white = {
1.0f, 1.0f, 1.0f, 1.0f,
};
if (!vk)
return false;
if (vk->flags & VK_FLAG_OVERLAY_ENABLE)
old_enabled = true;
vulkan_overlay_free(vk);
if (!(vk->overlay.images = (struct vk_texture*)
calloc(num_textures, sizeof(*vk->overlay.images))))
goto error;
vk->overlay.count = num_textures;
vk->overlay.borrowed = true;
if (!(vk->overlay.vertex = (struct vk_vertex*)
calloc(4 * num_textures, sizeof(*vk->overlay.vertex))))
goto error;
for (i = 0; i < (int) num_textures; i++)
{
int j;
vk->overlay.images[i] = *(const struct vk_texture*)textures[i];
vulkan_overlay_tex_geom(vk, i, 0, 0, 1, 1);
vulkan_overlay_vertex_geom(vk, i, 0, 0, 1, 1);
for (j = 0; j < 4; j++)
vk->overlay.vertex[4 * i + j].color = white;
}
if (old_enabled)
vk->flags |= VK_FLAG_OVERLAY_ENABLE;
else
vk->flags &= ~VK_FLAG_OVERLAY_ENABLE;
return true;
error:
vulkan_overlay_free(vk);
return false;
}
static bool vulkan_overlay_load(void *data,
const void *image_data, unsigned num_images)
{
@@ -10989,6 +11048,7 @@ error:
static const video_overlay_interface_t vulkan_overlay_interface = {
vulkan_overlay_enable,
vulkan_overlay_load,
vulkan_overlay_load_textures,
vulkan_overlay_tex_geom,
vulkan_overlay_vertex_geom,
vulkan_overlay_full_screen,
+30 -4
View File
@@ -641,14 +641,20 @@ static bool video_thread_handle_packet(
break;
case CMD_OVERLAY_LOAD:
case CMD_OVERLAY_LOAD_TEXTURES:
{
unsigned tmp_alpha_mods = pkt.data.image.num;
if (thr->driver_data && thr->overlay && thr->overlay->load)
pkt.data.b = thr->overlay->load(thr->driver_data,
pkt.data.image.data, pkt.data.image.num);
else
if (!thr->driver_data || !thr->overlay)
pkt.data.b = false;
else if (pkt.type == CMD_OVERLAY_LOAD_TEXTURES)
pkt.data.b = thr->overlay->load_textures
&& thr->overlay->load_textures(thr->driver_data,
pkt.data.image.textures, pkt.data.image.num);
else
pkt.data.b = thr->overlay->load
&& thr->overlay->load(thr->driver_data,
pkt.data.image.data, pkt.data.image.num);
if (tmp_alpha_mods > 0)
{
@@ -2840,6 +2846,25 @@ static bool thread_overlay_load(void *data,
return pkt.data.b;
}
static bool thread_overlay_load_textures(void *data,
const uintptr_t *textures, unsigned num_textures)
{
thread_packet_t pkt;
thread_video_t *thr = (thread_video_t*)data;
if (!thr)
return false;
pkt.type = CMD_OVERLAY_LOAD_TEXTURES;
pkt.data.image.data = NULL;
pkt.data.image.textures = textures;
pkt.data.image.num = num_textures;
video_thread_send_and_wait_user_to_thread(thr, &pkt);
return pkt.data.b;
}
static void thread_overlay_tex_geom(void *data,
unsigned idx, float x, float y, float w, float h)
{
@@ -2920,6 +2945,7 @@ static void thread_overlay_set_alpha(void *data, unsigned idx, float mod)
static const video_overlay_interface_t thread_overlay = {
thread_overlay_enable,
thread_overlay_load,
thread_overlay_load_textures,
thread_overlay_tex_geom,
thread_overlay_vertex_geom,
thread_overlay_full_screen,
+2
View File
@@ -49,6 +49,7 @@ enum thread_cmd
CMD_OVERLAY_ENABLE,
CMD_OVERLAY_LOAD,
CMD_OVERLAY_LOAD_TEXTURES,
CMD_OVERLAY_TEX_GEOM,
CMD_OVERLAY_VERTEX_GEOM,
CMD_OVERLAY_FULL_SCREEN,
@@ -123,6 +124,7 @@ typedef struct thread_packet
struct
{
const struct texture_image *data;
const uintptr_t *textures;
unsigned num;
} image;
+106
View File
@@ -3482,14 +3482,115 @@ void input_overlay_set_scale_factor(
input_overlay_set_vertex_geom(ol);
}
/* Every unique image of the pack becomes one texture, and every page
* a list of handles into that set, so that switching pages uploads
* nothing. Only for a driver with load_textures; others keep taking
* the pixels through load() on each switch. The handles live until
* input_overlay_release_textures(), which the disable runs while the
* driver is still there to unload them. Returns false when the pack
* cannot be uploaded this way, in which case load() is used. */
static bool input_overlay_upload_textures(input_overlay_t *ol)
{
uintptr_t *tex;
size_t i, j, k, total = ol->num_images;
if (ol->page_textures || !ol->images || !ol->num_images)
return ol->page_textures != NULL;
for (i = 0; i < ol->size; i++)
total += ol->overlays[i].load_images_size;
if (!(tex = (uintptr_t*)calloc(total, sizeof(*tex))))
return false;
ol->page_textures = tex;
for (i = 0; i < ol->num_images; i++)
{
if ( !video_driver_texture_load(ol->images[i],
TEXTURE_FILTER_LINEAR, &tex[i])
|| !tex[i])
{
input_overlay_release_textures(ol);
return false;
}
}
/* The loader deduplicated by path, so a page's entry shares its
* pixels with exactly one unique image. */
k = ol->num_images;
for (i = 0; i < ol->size; i++)
{
struct overlay *o = &ol->overlays[i];
o->textures = &tex[k];
for (j = 0; j < o->load_images_size; j++, k++)
{
size_t u;
for (u = 0; u < ol->num_images; u++)
{
if (ol->images[u]->pixels == o->load_images[j].pixels)
{
tex[k] = tex[u];
break;
}
}
if (u == ol->num_images)
{
input_overlay_release_textures(ol);
return false;
}
}
}
return true;
}
void input_overlay_release_textures(input_overlay_t *ol)
{
size_t i;
if (!ol || !ol->page_textures)
return;
for (i = 0; i < ol->num_images; i++)
if (ol->page_textures[i])
video_driver_texture_unload(&ol->page_textures[i]);
for (i = 0; i < ol->size; i++)
ol->overlays[i].textures = NULL;
free(ol->page_textures);
ol->page_textures = NULL;
}
static void input_overlay_load_active_geom(
enum overlay_visibility *visibility,
input_overlay_t *ol, float opacity);
void input_overlay_load_active(
enum overlay_visibility *visibility,
input_overlay_t *ol, float opacity)
{
if ( ol->iface->load_textures
&& !(ol->flags & INPUT_OVERLAY_TEXTURES_DECLINED)
&& input_overlay_upload_textures(ol))
{
if (ol->iface->load_textures(ol->iface_data,
ol->active->textures, ol->active->load_images_size))
{
input_overlay_load_active_geom(visibility, ol, opacity);
return;
}
/* The wrapper's table answers for any driver; the one beneath
* it may have no such path. Not held for nothing. */
input_overlay_release_textures(ol);
ol->flags |= INPUT_OVERLAY_TEXTURES_DECLINED;
}
if (ol->iface->load)
ol->iface->load(ol->iface_data, ol->active->load_images,
ol->active->load_images_size);
input_overlay_load_active_geom(visibility, ol, opacity);
}
/* The per-page state that follows either load: alpha, geometry,
* full-screen. */
static void input_overlay_load_active_geom(
enum overlay_visibility *visibility,
input_overlay_t *ol, float opacity)
{
input_overlay_set_alpha_mod(visibility, ol, opacity);
input_overlay_set_vertex_geom(ol);
@@ -3654,6 +3755,7 @@ static void input_overlay_free(input_overlay_t *ol)
if (!ol)
return;
input_overlay_release_textures(ol);
input_overlay_free_images(ol);
input_overlay_free_overlays(ol);
@@ -6328,6 +6430,7 @@ static void input_overlay_enable_(bool enable)
}
/* Load last-active overlay */
ol->flags &= ~INPUT_OVERLAY_TEXTURES_DECLINED;
input_overlay_load_active(input_st->overlay_visibility, ol, opacity);
/* Adjust to current settings */
@@ -6360,6 +6463,9 @@ static void input_overlay_enable_(bool enable)
if (ol->iface && ol->iface->enable)
ol->iface->enable(ol->iface_data, false);
/* The driver has let go of the pack's textures with the page:
* unload them while it is still here to do so. */
input_overlay_release_textures(ol);
ol->iface = NULL;
memset(&ol->overlay_state, 0, sizeof(input_overlay_state_t));
+26 -1
View File
@@ -120,7 +120,10 @@ enum INPUT_OVERLAY_FLAGS
INPUT_OVERLAY_ALIVE = (1 << 1),
INPUT_OVERLAY_BLOCKED = (1 << 2),
INPUT_OVERLAY_IS_OSK = (1 << 3),
INPUT_OVERLAY_GAMEPAD_HIDDEN = (1 << 4)
INPUT_OVERLAY_GAMEPAD_HIDDEN = (1 << 4),
/* The driver declined the pack's textures (load_textures): pages
* go through load() until the next enable. */
INPUT_OVERLAY_TEXTURES_DECLINED = (1 << 5)
};
enum OVERLAY_FLAGS
@@ -187,6 +190,15 @@ typedef struct video_overlay_interface
void (*enable)(void *data, bool state);
bool (*load)(void *data,
const void *images, unsigned num_images);
/* Show @num_textures textures video_driver_texture_load() made,
* in place of load(): the driver takes the same per-image
* geometry and alpha as for load() but uploads nothing and owns
* nothing - the textures are the overlay pack's, uploaded once
* for every page and unloaded by the frontend after enable(false).
* A page switch is then a pass over indices. Optional; a driver
* without it takes load() on every switch as before. */
bool (*load_textures)(void *data,
const uintptr_t *textures, unsigned num_textures);
void (*tex_geom)(void *data, unsigned image,
float x, float y, float w, float h);
void (*vertex_geom)(void *data, unsigned image,
@@ -268,6 +280,10 @@ struct overlay
{
struct overlay_desc *descs;
struct texture_image *load_images;
/* The pack's texture handle of each load_images entry, valid while
* the overlay is enabled on a driver with load_textures; views
* into input_overlay_t::page_textures. */
uintptr_t *textures;
struct texture_image image;
@@ -395,6 +411,11 @@ struct input_overlay
input_overlay_state_t overlay_state;
input_overlay_pointer_state_t pointer_state;
struct texture_image **images;
/* One block: the pack's texture handle per unique image (the first
* num_images entries), then each page's handle list in page order,
* which overlay::textures point into. Built by the enable on a
* driver with load_textures, unloaded and freed by the disable. */
uintptr_t *page_textures;
size_t num_images;
size_t index;
@@ -466,6 +487,10 @@ void input_overlay_auto_rotate_(
bool input_overlay_enable,
input_overlay_t *ol);
/* Unload the pack's textures (see video_overlay_interface::load_textures)
* and forget the page lists. Safe to call with none uploaded. */
void input_overlay_release_textures(input_overlay_t *ol);
void input_overlay_load_active(
enum overlay_visibility *visibility,
input_overlay_t *ol, float opacity);