Merge pull request #352 from libretro/overlay-rework

Overlay rework
This commit is contained in:
Squarepusher 2013-10-21 06:35:19 -07:00
commit 345afd0431
13 changed files with 475 additions and 212 deletions

View File

@ -332,14 +332,21 @@ typedef struct input_driver
struct rarch_viewport;
#ifdef HAVE_OVERLAY
struct video_overlay_image
{
const uint32_t *image;
unsigned width;
unsigned height;
};
typedef struct video_overlay_interface
{
void (*enable)(void *data, bool state);
bool (*load)(void *data, const uint32_t *image, unsigned width, unsigned height);
void (*tex_geom)(void *data, float x, float y, float w, float h);
void (*vertex_geom)(void *data, float x, float y, float w, float h);
bool (*load)(void *data, const struct video_overlay_image *images, unsigned num_images);
void (*tex_geom)(void *data, unsigned image, float x, float y, float w, float h);
void (*vertex_geom)(void *data, unsigned image, float x, float y, float w, float h);
void (*full_screen)(void *data, bool enable);
void (*set_alpha)(void *data, float mod);
void (*set_alpha)(void *data, unsigned image, float mod);
} video_overlay_interface_t;
#endif

View File

@ -431,15 +431,11 @@ RECT D3DVideo::monitor_rect()
D3DVideo::D3DVideo(const video_info_t *info) :
g_pD3D(nullptr), dev(nullptr), font(nullptr),
rotation(0), needs_restore(false), cgCtx(nullptr)
rotation(0), needs_restore(false), cgCtx(nullptr), overlays_enabled(false)
{
should_resize = false;
gfx_set_dwm();
#ifdef HAVE_OVERLAY
std::memset(&overlay, 0, sizeof(overlay));
#endif
#ifdef HAVE_RGUI
std::memset(&rgui, 0, sizeof(rgui));
rgui.tex_coords.x = 0;
@ -508,7 +504,7 @@ D3DVideo::D3DVideo(const video_info_t *info) :
show_cursor(!info->fullscreen
#ifdef HAVE_OVERLAY
|| overlay.enabled
|| overlays_enabled
#endif
);
Callback::quit = false;
@ -544,20 +540,31 @@ void D3DVideo::deinit()
needs_restore = false;
}
D3DVideo::~D3DVideo()
{
deinit();
#ifdef HAVE_OVERLAY
void D3DVideo::free_overlays()
{
for (unsigned i = 0; i < overlays.size(); i++)
free_overlay(overlays[i]);
overlays.clear();
}
#endif
void D3DVideo::free_overlay(overlay_t &overlay)
{
if (overlay.tex)
overlay.tex->Release();
if (overlay.vert_buf)
overlay.vert_buf->Release();
}
D3DVideo::~D3DVideo()
{
deinit();
#ifdef HAVE_OVERLAY
free_overlays();
#endif
#ifdef HAVE_RGUI
if (rgui.tex)
rgui.tex->Release();
if (rgui.vert_buf)
rgui.vert_buf->Release();
free_overlay(rgui);
#endif
if (dev)
dev->Release();
@ -652,8 +659,11 @@ bool D3DVideo::frame(const void *frame,
#endif
#ifdef HAVE_OVERLAY
if (overlay.enabled)
overlay_render(overlay);
if (overlays_enabled)
{
for (unsigned i = 0; i < overlays.size(); i++)
overlay_render(overlays[i]);
}
#endif
RARCH_PERFORMANCE_STOP(d3d_frame);
@ -1037,70 +1047,81 @@ void D3DVideo::resize(unsigned new_width, unsigned new_height)
}
#ifdef HAVE_OVERLAY
bool D3DVideo::overlay_load(const uint32_t *image, unsigned width, unsigned height)
bool D3DVideo::overlay_load(const video_overlay_image *images, unsigned num_images)
{
if (overlay.tex)
overlay.tex->Release();
if (FAILED(dev->CreateTexture(width, height, 1,
0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
&overlay.tex, nullptr)))
{
RARCH_ERR("[D3D9]: Failed to create overlay texture\n");
return false;
}
D3DLOCKED_RECT d3dlr;
if (SUCCEEDED(overlay.tex->LockRect(0, &d3dlr, nullptr, D3DLOCK_NOSYSLOCK)))
{
std::memcpy(d3dlr.pBits, image, height * d3dlr.Pitch);
overlay.tex->UnlockRect(0);
}
free_overlays();
overlays.resize(num_images);
overlay.tex_w = width;
overlay.tex_h = height;
for (unsigned i = 0; i < num_images; i++)
{
unsigned width = images[i].width;
unsigned height = images[i].height;
overlay_t &overlay = overlays[i];
if (FAILED(dev->CreateTexture(width, height, 1,
0,
D3DFMT_A8R8G8B8,
D3DPOOL_MANAGED,
&overlay.tex, nullptr)))
{
RARCH_ERR("[D3D9]: Failed to create overlay texture\n");
return false;
}
overlay_tex_geom(0, 0, 1, 1); // Default. Stretch to whole screen.
overlay_vertex_geom(0, 0, 1, 1);
D3DLOCKED_RECT d3dlr;
if (SUCCEEDED(overlay.tex->LockRect(0, &d3dlr, nullptr, D3DLOCK_NOSYSLOCK)))
{
uint32_t *dst = static_cast<uint32_t*>(d3dlr.pBits);
const uint32_t *src = images[i].image;
unsigned pitch = d3dlr.Pitch >> 2;
for (unsigned y = 0; y < height; y++, dst += pitch, src += width)
std::memcpy(dst, src, width << 2);
overlay.tex->UnlockRect(0);
}
overlay.tex_w = width;
overlay.tex_h = height;
overlay_tex_geom(i, 0, 0, 1, 1); // Default. Stretch to whole screen.
overlay_vertex_geom(i, 0, 0, 1, 1);
}
return true;
}
void D3DVideo::overlay_tex_geom(float x, float y, float w, float h)
void D3DVideo::overlay_tex_geom(unsigned index, float x, float y, float w, float h)
{
overlay.tex_coords.x = x;
overlay.tex_coords.y = y;
overlay.tex_coords.w = w;
overlay.tex_coords.h = h;
overlays[index].tex_coords.x = x;
overlays[index].tex_coords.y = y;
overlays[index].tex_coords.w = w;
overlays[index].tex_coords.h = h;
}
void D3DVideo::overlay_vertex_geom(float x, float y, float w, float h)
void D3DVideo::overlay_vertex_geom(unsigned index, float x, float y, float w, float h)
{
y = 1.0f - y;
h = -h;
overlay.vert_coords.x = x;
overlay.vert_coords.y = y;
overlay.vert_coords.w = w;
overlay.vert_coords.h = h;
overlays[index].vert_coords.x = x;
overlays[index].vert_coords.y = y;
overlays[index].vert_coords.w = w;
overlays[index].vert_coords.h = h;
}
void D3DVideo::overlay_enable(bool state)
{
overlay.enabled = state;
for (unsigned i = 0; i < overlays.size(); i++)
overlays_enabled = state;
show_cursor(state);
}
void D3DVideo::overlay_full_screen(bool enable)
{
overlay.fullscreen = enable;
for (unsigned i = 0; i < overlays.size(); i++)
overlays[i].fullscreen = enable;
}
void D3DVideo::overlay_set_alpha(float mod)
void D3DVideo::overlay_set_alpha(unsigned index, float mod)
{
overlay.alpha_mod = mod;
overlays[index].alpha_mod = mod;
}
#endif
void D3DVideo::overlay_render(overlay_t &overlay)
@ -1178,7 +1199,7 @@ void D3DVideo::overlay_render(overlay_t &overlay)
{0, 20, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
D3DDECL_END()
};
IDirect3DVertexDeclaration9 * vertex_decl;
IDirect3DVertexDeclaration9 *vertex_decl;
dev->CreateVertexDeclaration(vElems, &vertex_decl);
dev->SetVertexDeclaration(vertex_decl);
vertex_decl->Release();
@ -1210,7 +1231,7 @@ void D3DVideo::overlay_render(overlay_t &overlay)
dev->EndScene();
}
//restore previous state
// restore previous state
dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
dev->SetViewport(&final_viewport);
}
@ -1367,23 +1388,25 @@ static void d3d9_get_poke_interface(void *data, const video_poke_interface_t **i
#endif
#ifdef HAVE_OVERLAY
static bool d3d9_overlay_load(void *data, const uint32_t *image, unsigned width, unsigned height)
static bool d3d9_overlay_load(void *data, const video_overlay_image *images, unsigned num_images)
{
return reinterpret_cast<D3DVideo*>(data)->overlay_load(image, width, height);
return reinterpret_cast<D3DVideo*>(data)->overlay_load(images, num_images);
}
static void d3d9_overlay_tex_geom(void *data,
unsigned index,
float x, float y,
float w, float h)
{
return reinterpret_cast<D3DVideo*>(data)->overlay_tex_geom(x, y, w, h);
return reinterpret_cast<D3DVideo*>(data)->overlay_tex_geom(index, x, y, w, h);
}
static void d3d9_overlay_vertex_geom(void *data,
unsigned index,
float x, float y,
float w, float h)
{
return reinterpret_cast<D3DVideo*>(data)->overlay_vertex_geom(x, y, w, h);
return reinterpret_cast<D3DVideo*>(data)->overlay_vertex_geom(index, x, y, w, h);
}
static void d3d9_overlay_enable(void *data, bool state)
@ -1396,9 +1419,9 @@ static void d3d9_overlay_full_screen(void *data, bool enable)
return reinterpret_cast<D3DVideo*>(data)->overlay_full_screen(enable);
}
static void d3d9_overlay_set_alpha(void *data, float mod)
static void d3d9_overlay_set_alpha(void *data, unsigned index, float mod)
{
return reinterpret_cast<D3DVideo*>(data)->overlay_set_alpha(mod);
return reinterpret_cast<D3DVideo*>(data)->overlay_set_alpha(index, mod);
}
static const video_overlay_interface_t d3d9_overlay_interface = {

View File

@ -45,8 +45,8 @@ typedef struct
Coords tex_coords;
Coords vert_coords;
unsigned tex_w, tex_h;
bool enabled;
bool fullscreen;
bool enabled;
float alpha_mod;
IDirect3DTexture9 *tex;
IDirect3DVertexBuffer9 *vert_buf;
@ -77,12 +77,12 @@ class D3DVideo
void overlay_render(overlay_t &overlay);
#ifdef HAVE_OVERLAY
bool overlay_load(const uint32_t *image, unsigned width, unsigned height);
void overlay_tex_geom(float x, float y, float w, float h);
void overlay_vertex_geom(float x, float y, float w, float h);
bool overlay_load(const video_overlay_image *images, unsigned num_images);
void overlay_tex_geom(unsigned index, float x, float y, float w, float h);
void overlay_vertex_geom(unsigned index, float x, float y, float w, float h);
void overlay_enable(bool state);
void overlay_full_screen(bool enable);
void overlay_set_alpha(float mod);
void overlay_set_alpha(unsigned index, float mod);
#endif
#ifdef HAVE_RGUI
@ -153,9 +153,13 @@ class D3DVideo
void update_title();
#ifdef HAVE_OVERLAY
overlay_t overlay;
bool overlays_enabled;
std::vector<overlay_t> overlays;
void free_overlays();
#endif
void free_overlay(overlay_t &overlay);
#ifdef HAVE_RGUI
overlay_t rgui;
#endif

115
gfx/gl.c
View File

@ -120,8 +120,10 @@ static inline bool gl_query_extension(gl_t *gl, const char *ext)
#ifdef HAVE_OVERLAY
static void gl_render_overlay(void *data);
static void gl_overlay_vertex_geom(void *data,
unsigned image,
float x, float y, float w, float h);
static void gl_overlay_tex_geom(void *data,
unsigned image,
float x, float y, float w, float h);
#endif
@ -1547,6 +1549,19 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
return true;
}
#ifdef HAVE_OVERLAY
static void gl_free_overlay(gl_t *gl)
{
for (unsigned i = 0; i < gl->overlays; i++)
if (gl->overlay[i].tex)
glDeleteTextures(1, &gl->overlay[i].tex);
free(gl->overlay);
gl->overlay = NULL;
gl->overlays = 0;
}
#endif
static void gl_free(void *data)
{
#ifdef RARCH_CONSOLE
@ -1584,8 +1599,7 @@ static void gl_free(void *data)
#endif
#ifdef HAVE_OVERLAY
if (gl->tex_overlay)
glDeleteTextures(1, &gl->tex_overlay);
gl_free_overlay(gl);
#endif
#if defined(HAVE_PSGL)
@ -2404,58 +2418,73 @@ static void gl_restart(void)
#endif
#ifdef HAVE_OVERLAY
static bool gl_overlay_load(void *data, const uint32_t *image, unsigned width, unsigned height)
static void gl_free_overlay(gl_t *gl);
static bool gl_overlay_load(void *data, const struct video_overlay_image *images, unsigned num_images)
{
gl_t *gl = (gl_t*)data;
if (!gl->tex_overlay)
glGenTextures(1, &gl->tex_overlay);
gl_free_overlay(gl);
gl->overlay = (struct gl_overlay_data*)calloc(num_images, sizeof(*gl->overlay));
if (!gl->overlay)
return false;
glBindTexture(GL_TEXTURE_2D, gl->tex_overlay);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->overlays = num_images;
for (unsigned i = 0; i < num_images; i++)
{
struct gl_overlay_data *data = &gl->overlay[i];
glGenTextures(1, &data->tex);
glBindTexture(GL_TEXTURE_2D, data->tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
#ifndef HAVE_PSGL
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t)));
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(images[i].width * sizeof(uint32_t)));
#endif
glTexImage2D(GL_TEXTURE_2D, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32,
width, height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32,
RARCH_GL_FORMAT32, image);
glTexImage2D(GL_TEXTURE_2D, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_INTERNAL_FORMAT32,
images[i].width, images[i].height, 0, driver.gfx_use_rgba ? GL_RGBA : RARCH_GL_TEXTURE_TYPE32,
RARCH_GL_FORMAT32, images[i].image);
gl_overlay_tex_geom(gl, 0, 0, 1, 1); // Default. Stretch to whole screen.
gl_overlay_vertex_geom(gl, 0, 0, 1, 1);
gl_overlay_tex_geom(gl, i, 0, 0, 1, 1); // Default. Stretch to whole screen.
gl_overlay_vertex_geom(gl, i, 0, 0, 1, 1);
gl->overlay[i].alpha_mod = 1.0f;
}
return true;
}
static void gl_overlay_tex_geom(void *data,
unsigned image,
GLfloat x, GLfloat y,
GLfloat w, GLfloat h)
{
gl_t *gl = (gl_t*)data;
struct gl_overlay_data *o = &gl->overlay[image];
gl->overlay_tex_coord[0] = x; gl->overlay_tex_coord[1] = y;
gl->overlay_tex_coord[2] = x + w; gl->overlay_tex_coord[3] = y;
gl->overlay_tex_coord[4] = x; gl->overlay_tex_coord[5] = y + h;
gl->overlay_tex_coord[6] = x + w; gl->overlay_tex_coord[7] = y + h;
o->tex_coord[0] = x; o->tex_coord[1] = y;
o->tex_coord[2] = x + w; o->tex_coord[3] = y;
o->tex_coord[4] = x; o->tex_coord[5] = y + h;
o->tex_coord[6] = x + w; o->tex_coord[7] = y + h;
}
static void gl_overlay_vertex_geom(void *data,
unsigned image,
float x, float y,
float w, float h)
{
gl_t *gl = (gl_t*)data;
struct gl_overlay_data *o = &gl->overlay[image];
// Flipped, so we preserve top-down semantics.
y = 1.0f - y;
h = -h;
gl->overlay_vertex_coord[0] = x; gl->overlay_vertex_coord[1] = y;
gl->overlay_vertex_coord[2] = x + w; gl->overlay_vertex_coord[3] = y;
gl->overlay_vertex_coord[4] = x; gl->overlay_vertex_coord[5] = y + h;
gl->overlay_vertex_coord[6] = x + w; gl->overlay_vertex_coord[7] = y + h;
o->vertex_coord[0] = x; o->vertex_coord[1] = y;
o->vertex_coord[2] = x + w; o->vertex_coord[3] = y;
o->vertex_coord[4] = x; o->vertex_coord[5] = y + h;
o->vertex_coord[6] = x + w; o->vertex_coord[7] = y + h;
}
static void gl_overlay_enable(void *data, bool state)
@ -2472,49 +2501,49 @@ static void gl_overlay_full_screen(void *data, bool enable)
gl->overlay_full_screen = enable;
}
static void gl_overlay_set_alpha(void *data, float mod)
static void gl_overlay_set_alpha(void *data, unsigned image, float mod)
{
gl_t *gl = (gl_t*)data;
gl->overlay_alpha_mod = mod;
gl->overlay[image].alpha_mod = mod;
}
static void gl_render_overlay(void *data)
{
gl_t *gl = (gl_t*)data;
glBindTexture(GL_TEXTURE_2D, gl->tex_overlay);
const GLfloat white_color_mod[16] = {
1.0f, 1.0f, 1.0f, gl->overlay_alpha_mod,
1.0f, 1.0f, 1.0f, gl->overlay_alpha_mod,
1.0f, 1.0f, 1.0f, gl->overlay_alpha_mod,
1.0f, 1.0f, 1.0f, gl->overlay_alpha_mod,
GLfloat white_color_mod[16] = {
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
};
if (gl->shader)
gl->shader->use(GL_SHADER_STOCK_BLEND);
glEnable(GL_BLEND);
gl->coords.vertex = gl->overlay_vertex_coord;
gl->coords.tex_coord = gl->overlay_tex_coord;
gl->coords.color = white_color_mod;
gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot);
if (gl->overlay_full_screen)
{
glViewport(0, 0, gl->win_width, gl->win_height);
for (unsigned i = 0; i < gl->overlays; i++)
{
glBindTexture(GL_TEXTURE_2D, gl->overlay[i].tex);
for (unsigned j = 0; j < 4; j++)
white_color_mod[3 + j * 4] = gl->overlay[i].alpha_mod;
gl->coords.vertex = gl->overlay[i].vertex_coord;
gl->coords.tex_coord = gl->overlay[i].tex_coord;
gl->coords.color = white_color_mod;
gl_shader_set_coords(gl, &gl->coords, &gl->mvp_no_rot);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glViewport(gl->vp.x, gl->vp.y, gl->vp.width, gl->vp.height);
}
else
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_BLEND);
gl->coords.vertex = gl->vertex_ptr;
gl->coords.tex_coord = gl->tex_coords;
gl->coords.color = gl->white_color_ptr;
if (gl->overlay_full_screen)
glViewport(gl->vp.x, gl->vp.y, gl->vp.width, gl->vp.height);
}
static const video_overlay_interface_t gl_overlay_interface = {

View File

@ -136,6 +136,14 @@ typedef struct gl_shader_backend gl_shader_backend_t;
#define MAX_SHADERS 16
#define MAX_TEXTURES 8
struct gl_overlay_data
{
GLuint tex;
GLfloat tex_coord[8];
GLfloat vertex_coord[8];
GLfloat alpha_mod;
};
typedef struct gl
{
const gfx_ctx_driver_t *ctx_driver;
@ -219,13 +227,10 @@ typedef struct gl
video_info_t video_info;
#ifdef HAVE_OVERLAY
// Overlay rendering
struct gl_overlay_data *overlay;
unsigned overlays;
bool overlay_enable;
bool overlay_full_screen;
GLuint tex_overlay;
GLfloat overlay_tex_coord[8];
GLfloat overlay_vertex_coord[8];
GLfloat overlay_alpha_mod;
#endif
#if !defined(HAVE_OPENGLES) && defined(HAVE_FFMPEG)

View File

@ -112,14 +112,20 @@ typedef struct thread_video
struct
{
unsigned index;
float x, y, w, h;
} rect;
struct
{
const uint32_t *data;
unsigned width;
unsigned height;
unsigned index;
float mod;
} alpha;
struct
{
const struct video_overlay_image *data;
unsigned num;
} image;
struct
@ -242,13 +248,13 @@ static void thread_loop(void *data)
case CMD_OVERLAY_LOAD:
thr->cmd_data.b = thr->overlay->load(thr->driver_data,
thr->cmd_data.image.data,
thr->cmd_data.image.width,
thr->cmd_data.image.height);
thr->cmd_data.image.num);
thread_reply(thr, CMD_OVERLAY_LOAD);
break;
case CMD_OVERLAY_TEX_GEOM:
thr->overlay->tex_geom(thr->driver_data,
thr->cmd_data.rect.index,
thr->cmd_data.rect.x,
thr->cmd_data.rect.y,
thr->cmd_data.rect.w,
@ -258,6 +264,7 @@ static void thread_loop(void *data)
case CMD_OVERLAY_VERTEX_GEOM:
thr->overlay->vertex_geom(thr->driver_data,
thr->cmd_data.rect.index,
thr->cmd_data.rect.x,
thr->cmd_data.rect.y,
thr->cmd_data.rect.w,
@ -271,7 +278,7 @@ static void thread_loop(void *data)
break;
case CMD_OVERLAY_SET_ALPHA:
thr->overlay->set_alpha(thr->driver_data, thr->cmd_data.f);
thr->overlay->set_alpha(thr->driver_data, thr->cmd_data.alpha.index, thr->cmd_data.alpha.mod);
thread_reply(thr, CMD_OVERLAY_SET_ALPHA);
break;
#endif
@ -573,20 +580,20 @@ static void thread_overlay_enable(void *data, bool state)
thread_wait_reply(thr, CMD_OVERLAY_ENABLE);
}
static bool thread_overlay_load(void *data, const uint32_t *image, unsigned width, unsigned height)
static bool thread_overlay_load(void *data, const struct video_overlay_image *images, unsigned num_images)
{
thread_video_t *thr = (thread_video_t*)data;
thr->cmd_data.image.data = image;
thr->cmd_data.image.width = width;
thr->cmd_data.image.height = height;
thr->cmd_data.image.data = images;
thr->cmd_data.image.num = num_images;
thread_send_cmd(thr, CMD_OVERLAY_LOAD);
thread_wait_reply(thr, CMD_OVERLAY_LOAD);
return thr->cmd_data.b;
}
static void thread_overlay_tex_geom(void *data, float x, float y, float w, float h)
static void thread_overlay_tex_geom(void *data, unsigned index, float x, float y, float w, float h)
{
thread_video_t *thr = (thread_video_t*)data;
thr->cmd_data.rect.index = index;
thr->cmd_data.rect.x = x;
thr->cmd_data.rect.y = y;
thr->cmd_data.rect.w = w;
@ -595,9 +602,10 @@ static void thread_overlay_tex_geom(void *data, float x, float y, float w, float
thread_wait_reply(thr, CMD_OVERLAY_TEX_GEOM);
}
static void thread_overlay_vertex_geom(void *data, float x, float y, float w, float h)
static void thread_overlay_vertex_geom(void *data, unsigned index, float x, float y, float w, float h)
{
thread_video_t *thr = (thread_video_t*)data;
thr->cmd_data.rect.index = index;
thr->cmd_data.rect.x = x;
thr->cmd_data.rect.y = y;
thr->cmd_data.rect.w = w;
@ -614,10 +622,11 @@ static void thread_overlay_full_screen(void *data, bool enable)
thread_wait_reply(thr, CMD_OVERLAY_FULL_SCREEN);
}
static void thread_overlay_set_alpha(void *data, float mod)
static void thread_overlay_set_alpha(void *data, unsigned index, float mod)
{
thread_video_t *thr = (thread_video_t*)data;
thr->cmd_data.f = mod;
thr->cmd_data.alpha.index = index;
thr->cmd_data.alpha.mod = mod;
thread_send_cmd(thr, CMD_OVERLAY_SET_ALPHA);
thread_wait_reply(thr, CMD_OVERLAY_SET_ALPHA);
}

View File

@ -45,6 +45,8 @@ struct overlay_desc
enum overlay_hitbox hitbox;
float range_x, range_y;
float range_x_mod, range_y_mod;
float mod_x, mod_y, mod_w, mod_h;
enum overlay_type type;
uint64_t key_mask;
@ -52,6 +54,12 @@ struct overlay_desc
unsigned next_index;
char next_index_name[64];
struct video_overlay_image image;
unsigned image_index;
float alpha_mod;
float range_mod;
};
struct overlay
@ -59,9 +67,7 @@ struct overlay
struct overlay_desc *descs;
size_t size;
uint32_t *image;
unsigned width;
unsigned height;
struct video_overlay_image image;
bool block_scale;
float mod_x, mod_y, mod_w, mod_h;
@ -72,6 +78,9 @@ struct overlay
bool full_screen;
char name[64];
struct video_overlay_image *load_images;
unsigned load_images_size;
};
struct input_overlay
@ -88,6 +97,7 @@ struct input_overlay
size_t size;
unsigned next_index;
char *overlay_path;
};
struct str_to_bind_map
@ -169,19 +179,43 @@ static unsigned input_str_to_bind(const char *str)
static void input_overlay_scale(struct overlay *overlay, float scale)
{
if (overlay->block_scale)
scale = 1.0f;
overlay->scale = scale;
overlay->mod_w = overlay->w * scale;
overlay->mod_h = overlay->h * scale;
overlay->mod_x = overlay->center_x + (overlay->x - overlay->center_x) * scale;
overlay->mod_y = overlay->center_y + (overlay->y - overlay->center_y) * scale;
for (size_t i = 0; i < overlay->size; i++)
{
overlay->mod_x = overlay->x;
overlay->mod_y = overlay->y;
overlay->mod_w = overlay->w;
overlay->mod_h = overlay->h;
struct overlay_desc *desc = &overlay->descs[i];
float scale_w = overlay->mod_w * desc->range_x;
float scale_h = overlay->mod_h * desc->range_y;
desc->mod_w = 2.0f * scale_w;
desc->mod_h = 2.0f * scale_h;
float adj_center_x = overlay->mod_x + desc->x * overlay->mod_w;
float adj_center_y = overlay->mod_y + desc->y * overlay->mod_h;
desc->mod_x = adj_center_x - scale_w;
desc->mod_y = adj_center_y - scale_h;
}
else
}
static void input_overlay_set_vertex_geom(input_overlay_t *ol)
{
if (ol->active->image.image)
ol->iface->vertex_geom(ol->iface_data, 0,
ol->active->mod_x, ol->active->mod_y, ol->active->mod_w, ol->active->mod_h);
for (size_t i = 0; i < ol->active->size; i++)
{
overlay->scale = scale;
overlay->mod_w = overlay->w * scale;
overlay->mod_h = overlay->h * scale;
overlay->mod_x = overlay->center_x + (overlay->x - overlay->center_x) * scale;
overlay->mod_y = overlay->center_y + (overlay->y - overlay->center_y) * scale;
struct overlay_desc *desc = &ol->active->descs[i];
if (desc->image.image)
ol->iface->vertex_geom(ol->iface_data, desc->image_index,
desc->mod_x, desc->mod_y, desc->mod_w, desc->mod_h);
}
}
@ -190,14 +224,16 @@ void input_overlay_set_scale_factor(input_overlay_t *ol, float scale)
for (size_t i = 0; i < ol->size; i++)
input_overlay_scale(&ol->overlays[i], scale);
ol->iface->vertex_geom(ol->iface_data,
ol->active->mod_x, ol->active->mod_y, ol->active->mod_w, ol->active->mod_h);
input_overlay_set_vertex_geom(ol);
}
static void input_overlay_free_overlay(struct overlay *overlay)
{
for (size_t i = 0; i < overlay->size; i++)
free((void*)overlay->descs[i].image.image);
free(overlay->load_images);
free(overlay->descs);
free(overlay->image);
free((void*)overlay->image.image);
}
static void input_overlay_free_overlays(input_overlay_t *ol)
@ -207,14 +243,46 @@ static void input_overlay_free_overlays(input_overlay_t *ol)
free(ol->overlays);
}
static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *desc,
static bool input_overlay_load_desc(input_overlay_t *ol, config_file_t *conf, struct overlay_desc *desc,
unsigned ol_index, unsigned desc_index,
unsigned width, unsigned height)
unsigned width, unsigned height,
bool normalized, float alpha_mod, float range_mod)
{
bool ret = true;
char overlay_desc_key[64];
snprintf(overlay_desc_key, sizeof(overlay_desc_key), "overlay%u_desc%u", ol_index, desc_index);
char overlay_desc_image_key[64];
snprintf(overlay_desc_image_key, sizeof(overlay_desc_image_key),
"overlay%u_desc%u_overlay", ol_index, desc_index);
char image_path[PATH_MAX];
if (config_get_path(conf, overlay_desc_image_key, image_path, sizeof(image_path)))
{
char path[PATH_MAX];
fill_pathname_resolve_relative(path, ol->overlay_path, image_path, sizeof(path));
struct texture_image img = {0};
if (texture_image_load(path, &img))
{
desc->image.image = img.pixels;
desc->image.width = img.width;
desc->image.height = img.height;
}
}
char overlay_desc_normalized_key[64];
snprintf(overlay_desc_normalized_key, sizeof(overlay_desc_normalized_key),
"overlay%u_desc%u_normalized", ol_index, desc_index);
config_get_bool(conf, overlay_desc_normalized_key, &normalized);
bool by_pixel = !normalized;
if (by_pixel && (width == 0 || height == 0))
{
RARCH_ERR("[Overlay]: Base overlay is not set and not using normalized coordinates.\n");
return false;
}
char overlay[256];
if (!config_get_array(conf, overlay_desc_key, overlay, sizeof(overlay)))
{
@ -252,7 +320,10 @@ static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *de
{
desc->type = OVERLAY_TYPE_BUTTONS;
for (const char *tmp = strtok_r(key, "|", &save); tmp; tmp = strtok_r(NULL, "|", &save))
desc->key_mask |= UINT64_C(1) << input_str_to_bind(tmp);
{
if (strcmp(tmp, "nul") != 0)
desc->key_mask |= UINT64_C(1) << input_str_to_bind(tmp);
}
if (desc->key_mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT))
{
@ -262,8 +333,11 @@ static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *de
}
}
desc->x = strtod(x, NULL) / width;
desc->y = strtod(y, NULL) / height;
float width_mod = by_pixel ? (1.0f / width) : 1.0f;
float height_mod = by_pixel ? (1.0f / height) : 1.0f;
desc->x = (float)strtod(x, NULL) * width_mod;
desc->y = (float)strtod(y, NULL) * height_mod;
if (!strcmp(box, "radial"))
desc->hitbox = OVERLAY_HITBOX_RADIAL;
@ -291,8 +365,25 @@ static bool input_overlay_load_desc(config_file_t *conf, struct overlay_desc *de
desc->analog_saturate_pct = 1.0f;
}
desc->range_x = strtod(list->elems[4].data, NULL) / width;
desc->range_y = strtod(list->elems[5].data, NULL) / height;
desc->range_x = (float)strtod(list->elems[4].data, NULL) * width_mod;
desc->range_y = (float)strtod(list->elems[5].data, NULL) * height_mod;
desc->mod_x = desc->x - desc->range_x;
desc->mod_w = 2.0f * desc->range_x;
desc->mod_y = desc->y - desc->range_y;
desc->mod_h = 2.0f * desc->range_y;
char conf_key[64];
snprintf(conf_key, sizeof(conf_key), "overlay%u_desc%u_alpha_mod", ol_index, desc_index);
desc->alpha_mod = alpha_mod;
config_get_float(conf, conf_key, &desc->alpha_mod);
snprintf(conf_key, sizeof(conf_key), "overlay%u_desc%u_range_mod", ol_index, desc_index);
desc->range_mod = range_mod;
config_get_float(conf, conf_key, &desc->range_mod);
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
end:
if (list)
@ -300,7 +391,7 @@ end:
return ret;
}
static bool input_overlay_load_overlay(config_file_t *conf, const char *config_path,
static bool input_overlay_load_overlay(input_overlay_t *ol, config_file_t *conf, const char *config_path,
struct overlay *overlay, unsigned index)
{
char overlay_path_key[64];
@ -309,29 +400,28 @@ static bool input_overlay_load_overlay(config_file_t *conf, const char *config_p
char overlay_resolved_path[PATH_MAX];
snprintf(overlay_path_key, sizeof(overlay_path_key), "overlay%u_overlay", index);
if (!config_get_path(conf, overlay_path_key, overlay_path, sizeof(overlay_path)))
if (config_get_path(conf, overlay_path_key, overlay_path, sizeof(overlay_path)))
{
RARCH_ERR("[Overlay]: Config key: %s is not set.\n", overlay_path_key);
return false;
fill_pathname_resolve_relative(overlay_resolved_path, config_path,
overlay_path, sizeof(overlay_resolved_path));
struct texture_image img = {0};
if (texture_image_load(overlay_resolved_path, &img))
{
overlay->image.image = img.pixels;
overlay->image.width = img.width;
overlay->image.height = img.height;
}
else
{
RARCH_ERR("[Overlay]: Failed to load image: %s.\n", overlay_resolved_path);
return false;
}
}
snprintf(overlay_name_key, sizeof(overlay_name_key), "overlay%u_name", index);
config_get_array(conf, overlay_name_key, overlay->name, sizeof(overlay->name));
fill_pathname_resolve_relative(overlay_resolved_path, config_path,
overlay_path, sizeof(overlay_resolved_path));
struct texture_image img = {0};
if (!texture_image_load(overlay_resolved_path, &img))
{
RARCH_ERR("Failed to load image: %s.\n", overlay_path);
return false;
}
overlay->image = img.pixels;
overlay->width = img.width;
overlay->height = img.height;
// By default, we stretch the overlay out in full.
overlay->x = overlay->y = 0.0f;
overlay->w = overlay->h = 1.0f;
@ -348,10 +438,10 @@ static bool input_overlay_load_overlay(config_file_t *conf, const char *config_p
return false;
}
overlay->x = strtod(list->elems[0].data, NULL);
overlay->y = strtod(list->elems[1].data, NULL);
overlay->w = strtod(list->elems[2].data, NULL);
overlay->h = strtod(list->elems[3].data, NULL);
overlay->x = (float)strtod(list->elems[0].data, NULL);
overlay->y = (float)strtod(list->elems[1].data, NULL);
overlay->w = (float)strtod(list->elems[2].data, NULL);
overlay->h = (float)strtod(list->elems[3].data, NULL);
string_list_free(list);
}
@ -380,15 +470,50 @@ static bool input_overlay_load_overlay(config_file_t *conf, const char *config_p
overlay->size = descs;
char conf_key[64];
bool normalized = false;
snprintf(conf_key, sizeof(conf_key),
"overlay%u_normalized", index);
config_get_bool(conf, conf_key, &normalized);
float alpha_mod = 1.0f;
snprintf(conf_key, sizeof(conf_key), "overlay%u_alpha_mod", index);
config_get_float(conf, conf_key, &alpha_mod);
float range_mod = 1.0f;
snprintf(conf_key, sizeof(conf_key), "overlay%u_range_mod", index);
config_get_float(conf, conf_key, &range_mod);
for (size_t i = 0; i < overlay->size; i++)
{
if (!input_overlay_load_desc(conf, &overlay->descs[i], index, i, img.width, img.height))
if (!input_overlay_load_desc(ol, conf, &overlay->descs[i], index, i,
overlay->image.width, overlay->image.height,
normalized, alpha_mod, range_mod))
{
RARCH_ERR("[Overlay]: Failed to load overlay descs for overlay #%u.\n", (unsigned)i);
return false;
}
}
// Precache load image array for simplicity.
overlay->load_images = (struct video_overlay_image*)calloc(1 + overlay->size, sizeof(struct overlay_desc));
if (!overlay->load_images)
{
RARCH_ERR("[Overlay]: Failed to allocate load_images.\n");
return false;
}
if (overlay->image.image)
overlay->load_images[overlay->load_images_size++] = overlay->image;
for (size_t i = 0; i < overlay->size; i++)
{
if (overlay->descs[i].image.image)
{
overlay->descs[i].image_index = overlay->load_images_size;
overlay->load_images[overlay->load_images_size++] = overlay->descs[i].image;
}
}
// Assume for now that scaling center is in the middle.
// TODO: Make this configurable.
@ -470,7 +595,7 @@ static bool input_overlay_load_overlays(input_overlay_t *ol, const char *path)
for (size_t i = 0; i < ol->size; i++)
{
if (!input_overlay_load_overlay(conf, path, &ol->overlays[i], i))
if (!input_overlay_load_overlay(ol, conf, path, &ol->overlays[i], i))
{
RARCH_ERR("[Overlay]: Failed to load overlay #%u.\n", (unsigned)i);
ret = false;
@ -493,6 +618,15 @@ end:
return ret;
}
static void input_overlay_load_active(input_overlay_t *ol)
{
ol->iface->load(ol->iface_data, ol->active->load_images, ol->active->load_images_size);
input_overlay_set_alpha_mod(ol, g_settings.input.overlay_opacity);
input_overlay_set_vertex_geom(ol);
ol->iface->full_screen(ol->iface_data, ol->active->full_screen);
}
input_overlay_t *input_overlay_new(const char *overlay)
{
input_overlay_t *ol = (input_overlay_t*)calloc(1, sizeof(*ol));
@ -500,6 +634,13 @@ input_overlay_t *input_overlay_new(const char *overlay)
if (!ol)
goto error;
ol->overlay_path = strdup(overlay);
if (!ol->overlay_path)
{
free(ol);
return NULL;
}
if (!driver.video->overlay_interface)
{
RARCH_ERR("Overlay interface is not present in video driver.\n");
@ -516,16 +657,13 @@ input_overlay_t *input_overlay_new(const char *overlay)
goto error;
ol->active = &ol->overlays[0];
ol->iface->load(ol->iface_data, ol->active->image, ol->active->width, ol->active->height);
ol->iface->vertex_geom(ol->iface_data,
ol->active->mod_x, ol->active->mod_y, ol->active->mod_w, ol->active->mod_h);
ol->iface->full_screen(ol->iface_data, ol->active->full_screen);
input_overlay_load_active(ol);
ol->iface->enable(ol->iface_data, true);
ol->enable = true;
input_overlay_set_alpha_mod(ol, g_settings.input.overlay_opacity);
input_overlay_set_scale_factor(ol, 1.0f);
input_overlay_set_scale_factor(ol, g_settings.input.overlay_scale);
ol->next_index = (ol->index + 1) % ol->size;
return ol;
@ -548,21 +686,31 @@ static bool inside_hitbox(const struct overlay_desc *desc, float x, float y)
case OVERLAY_HITBOX_RADIAL:
{
// Ellipsis.
float x_dist = (x - desc->x) / desc->range_x;
float y_dist = (y - desc->y) / desc->range_y;
float x_dist = (x - desc->x) / desc->range_x_mod;
float y_dist = (y - desc->y) / desc->range_y_mod;
float sq_dist = x_dist * x_dist + y_dist * y_dist;
return sq_dist <= 1.0f;
}
case OVERLAY_HITBOX_RECT:
return (fabs(x - desc->x) <= desc->range_x) &&
(fabs(y - desc->y) <= desc->range_y);
return (fabs(x - desc->x) <= desc->range_x_mod) &&
(fabs(y - desc->y) <= desc->range_y_mod);
default:
return false;
}
}
static inline float clamp(float val, float lower, float upper)
{
if (val < lower)
return lower;
else if (val > upper)
return upper;
else
return val;
}
void input_overlay_poll(input_overlay_t *ol, input_overlay_state_t *out, int16_t norm_x, int16_t norm_y)
{
memset(out, 0, sizeof(*out));
@ -582,33 +730,42 @@ void input_overlay_poll(input_overlay_t *ol, input_overlay_state_t *out, int16_t
x /= ol->active->mod_w;
y /= ol->active->mod_h;
input_overlay_set_alpha_mod(ol, g_settings.input.overlay_opacity);
for (size_t i = 0; i < ol->active->size; i++)
{
if (!inside_hitbox(&ol->active->descs[i], x, y))
continue;
if (ol->active->descs[i].type == OVERLAY_TYPE_BUTTONS)
struct overlay_desc *desc = &ol->active->descs[i];
if (!inside_hitbox(desc, x, y))
{
uint64_t mask = ol->active->descs[i].key_mask;
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
continue;
}
// If pressed, change the hitbox.
desc->range_x_mod = desc->range_x * desc->range_mod;
desc->range_y_mod = desc->range_y * desc->range_mod;
if (desc->image.image)
ol->iface->set_alpha(ol->iface_data, desc->image_index,
desc->alpha_mod * g_settings.input.overlay_opacity);
if (desc->type == OVERLAY_TYPE_BUTTONS)
{
uint64_t mask = desc->key_mask;
out->buttons |= mask;
if (mask & (UINT64_C(1) << RARCH_OVERLAY_NEXT))
ol->next_index = ol->active->descs[i].next_index;
ol->next_index = desc->next_index;
}
else
{
float x_val = (x - ol->active->descs[i].x) / ol->active->descs[i].range_x / ol->active->descs[i].analog_saturate_pct;
float y_val = (y - ol->active->descs[i].y) / ol->active->descs[i].range_y / ol->active->descs[i].analog_saturate_pct;
float x_val = (x - desc->x) / desc->range_x_mod / desc->analog_saturate_pct;
float y_val = (y - desc->y) / desc->range_y_mod / desc->analog_saturate_pct;
if (fabs(x_val) > 1.0f)
x_val = (x_val > 0.0f) ? 1.0f : -1.0f;
if (fabs(y_val) > 1.0f)
y_val = (y_val > 0.0f) ? 1.0f : -1.0f;
unsigned int base = (ol->active->descs[i].type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0;
out->analog[base + 0] = x_val * 32767.0f;
out->analog[base + 1] = y_val * 32767.0f;
unsigned int base = (desc->type == OVERLAY_TYPE_ANALOG_RIGHT) ? 2 : 0;
out->analog[base + 0] = clamp(x_val, -1.0f, 1.0f) * 32767.0f;
out->analog[base + 1] = clamp(y_val, -1.0f, 1.0f) * 32767.0f;
}
}
@ -621,6 +778,14 @@ void input_overlay_poll(input_overlay_t *ol, input_overlay_state_t *out, int16_t
void input_overlay_poll_clear(input_overlay_t *ol)
{
ol->blocked = false;
input_overlay_set_alpha_mod(ol, g_settings.input.overlay_opacity);
for (size_t i = 0; i < ol->active->size; i++)
{
struct overlay_desc *desc = &ol->active->descs[i];
desc->range_x_mod = desc->range_x;
desc->range_y_mod = desc->range_y;
}
}
void input_overlay_next(input_overlay_t *ol)
@ -628,10 +793,8 @@ void input_overlay_next(input_overlay_t *ol)
ol->index = ol->next_index;
ol->active = &ol->overlays[ol->index];
ol->iface->load(ol->iface_data, ol->active->image, ol->active->width, ol->active->height);
ol->iface->vertex_geom(ol->iface_data,
ol->active->mod_x, ol->active->mod_y, ol->active->mod_w, ol->active->mod_h);
ol->iface->full_screen(ol->iface_data, ol->active->full_screen);
input_overlay_load_active(ol);
ol->blocked = true;
ol->next_index = (ol->index + 1) % ol->size;
}
@ -651,12 +814,13 @@ void input_overlay_free(input_overlay_t *ol)
if (ol->iface)
ol->iface->enable(ol->iface_data, false);
free(ol->overlay_path);
free(ol);
}
void input_overlay_set_alpha_mod(input_overlay_t *ol, float mod)
{
ol->iface->set_alpha(ol->iface_data, mod);
for (unsigned i = 0; i < ol->active->load_images_size; i++)
ol->iface->set_alpha(ol->iface_data, i, g_settings.input.overlay_opacity);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,22 @@
overlays = 1
# overlay0_overlay omitted. We don't need to splat an overlay across the screen.
overlay0_rect = "0.35,0.35,0.65,0.65" # Place overlay somewhere.
overlay0_full_screen = true
# These set defaults across the entire overlay. They can be overridden per-desc with overlayN_descM_alpha_mod = foo, etc.
overlay0_alpha_mod = 2.0 # If we press a button desc, it will have twice the alpha.
overlay0_range_mod = 1.5 # If we press a button desc, the hitbox range will be 1.5x the size until it's released.
overlay0_normalized = true # Descriptor coordinates use normalized coordinates [0, 1] instead of pixels.
overlay0_descs = 5
overlay0_desc0_overlay = left.png
overlay0_desc1_overlay = right.png
overlay0_desc2_overlay = up.png
overlay0_desc3_overlay = down.png
overlay0_desc0 = "left,0.25,0.50,rect,0.125,0.125"
overlay0_desc1 = "right,0.75,0.50,rect,0.125,0.125"
overlay0_desc2 = "up,0.50,0.25,rect,0.125,0.125"
overlay0_desc3 = "down,0.50,0.75,rect,0.125,0.125"
overlay0_desc4 = "nul,0.30,0.90,rect,0.28,0.08" # Paste an arbitrary image. Input associated with this is nul.
overlay0_desc4_overlay = logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB