mirror of
https://github.com/libretro/RetroArch
synced 2025-01-30 03:32:46 +00:00
Merge pull request #169 from Themaister/gl-render
HW GL render for libretro.
This commit is contained in:
commit
ebd7dcf721
28
driver.c
28
driver.c
@ -270,6 +270,24 @@ void driver_set_monitor_refresh_rate(float hz)
|
||||
|
||||
}
|
||||
|
||||
uintptr_t driver_get_current_framebuffer(void)
|
||||
{
|
||||
#ifdef HAVE_FBO
|
||||
if (driver.video_poke && driver.video_poke->get_current_framebuffer)
|
||||
return driver.video_poke->get_current_framebuffer(driver.video_data);
|
||||
else
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
retro_proc_address_t driver_get_proc_address(const char *sym)
|
||||
{
|
||||
if (driver.video_poke && driver.video_poke->get_proc_address)
|
||||
return driver.video_poke->get_proc_address(driver.video_data, sym);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Only called once on init and deinit.
|
||||
// Video and input drivers need to be active (owned)
|
||||
// before retroarch core starts.
|
||||
@ -322,6 +340,10 @@ void init_drivers(void)
|
||||
adjust_system_rates();
|
||||
|
||||
init_video_input();
|
||||
|
||||
if (g_extern.system.hw_render_callback.context_reset)
|
||||
g_extern.system.hw_render_callback.context_reset();
|
||||
|
||||
init_audio();
|
||||
}
|
||||
|
||||
@ -645,6 +667,12 @@ static void init_filter(bool rgb32)
|
||||
if (!*g_settings.video.filter_path)
|
||||
return;
|
||||
|
||||
if (g_extern.system.hw_render_callback.context_type)
|
||||
{
|
||||
RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
RARCH_LOG("Loading bSNES filter from \"%s\"\n", g_settings.video.filter_path);
|
||||
g_extern.filter.lib = dylib_load(g_settings.video.filter_path);
|
||||
if (!g_extern.filter.lib)
|
||||
|
6
driver.h
6
driver.h
@ -326,6 +326,8 @@ typedef struct video_poke_interface
|
||||
#ifdef HAVE_FBO
|
||||
void (*set_fbo_state)(void *data, unsigned state);
|
||||
unsigned (*get_fbo_state)(void *data);
|
||||
uintptr_t (*get_current_framebuffer)(void *data);
|
||||
retro_proc_address_t (*get_proc_address)(void *data, const char *sym);
|
||||
#endif
|
||||
void (*set_aspect_ratio)(void *data, unsigned aspectratio_index);
|
||||
void (*apply_state_changes)(void *data);
|
||||
@ -452,6 +454,10 @@ void uninit_audio(void);
|
||||
|
||||
void driver_set_monitor_refresh_rate(float hz);
|
||||
|
||||
// Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
|
||||
uintptr_t driver_get_current_framebuffer(void);
|
||||
retro_proc_address_t driver_get_proc_address(const char *sym);
|
||||
|
||||
extern driver_t driver;
|
||||
|
||||
//////////////////////////////////////////////// Backends
|
||||
|
40
dynamic.c
40
dynamic.c
@ -556,6 +556,46 @@ static bool environment_cb(unsigned cmd, void *data)
|
||||
g_extern.system.disk_control = *(const struct retro_disk_control_callback*)data;
|
||||
break;
|
||||
|
||||
case RETRO_ENVIRONMENT_SET_HW_RENDER:
|
||||
{
|
||||
RARCH_LOG("Environ SET_HW_RENDER.\n");
|
||||
struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data;
|
||||
switch (cb->context_type)
|
||||
{
|
||||
case RETRO_HW_CONTEXT_NONE:
|
||||
RARCH_LOG("Requesting no HW context.\n");
|
||||
break;
|
||||
|
||||
#if defined(HAVE_OPENGLES2)
|
||||
case RETRO_HW_CONTEXT_OPENGLES2:
|
||||
RARCH_LOG("Requesting OpenGLES2 context.\n");
|
||||
driver.video = &video_gl;
|
||||
break;
|
||||
|
||||
case RETRO_HW_CONTEXT_OPENGL:
|
||||
RARCH_ERR("Requesting OpenGL context, but RetroArch is compiled against OpenGLES2. Cannot use HW context.\n");
|
||||
return false;
|
||||
#elif defined(HAVE_OPENGL)
|
||||
case RETRO_HW_CONTEXT_OPENGLES2:
|
||||
RARCH_ERR("Requesting OpenGLES2 context, but RetroArch is compiled against OpenGL. Cannot use HW context.\n");
|
||||
return false;
|
||||
|
||||
case RETRO_HW_CONTEXT_OPENGL:
|
||||
RARCH_LOG("Requesting OpenGL context.\n");
|
||||
driver.video = &video_gl;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
RARCH_LOG("Requesting unknown context.\n");
|
||||
return false;
|
||||
}
|
||||
cb->get_current_framebuffer = driver_get_current_framebuffer;
|
||||
cb->get_proc_address = driver_get_proc_address;
|
||||
memcpy(&g_extern.system.hw_render_callback, cb, sizeof(*cb));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
RARCH_LOG("Environ UNSUPPORTED (#%u).\n", cmd);
|
||||
return false;
|
||||
|
@ -392,6 +392,7 @@ struct global
|
||||
retro_keyboard_event_t key_event;
|
||||
|
||||
struct retro_disk_control_callback disk_control;
|
||||
struct retro_hw_render_callback hw_render_callback;
|
||||
} system;
|
||||
|
||||
struct
|
||||
|
221
gfx/gl.c
221
gfx/gl.c
@ -135,6 +135,11 @@ static PFNGLBINDFRAMEBUFFERPROC pglBindFramebuffer;
|
||||
static PFNGLFRAMEBUFFERTEXTURE2DPROC pglFramebufferTexture2D;
|
||||
static PFNGLCHECKFRAMEBUFFERSTATUSPROC pglCheckFramebufferStatus;
|
||||
static PFNGLDELETEFRAMEBUFFERSPROC pglDeleteFramebuffers;
|
||||
static PFNGLGENRENDERBUFFERSPROC pglGenRenderbuffers;
|
||||
static PFNGLBINDRENDERBUFFERPROC pglBindRenderbuffer;
|
||||
static PFNGLFRAMEBUFFERRENDERBUFFERPROC pglFramebufferRenderbuffer;
|
||||
static PFNGLRENDERBUFFERSTORAGEPROC pglRenderbufferStorage;
|
||||
static PFNGLDELETERENDERBUFFERSPROC pglDeleteRenderbuffers;
|
||||
|
||||
static bool load_fbo_proc(gl_t *gl)
|
||||
{
|
||||
@ -143,9 +148,17 @@ static bool load_fbo_proc(gl_t *gl)
|
||||
LOAD_GL_SYM(FramebufferTexture2D);
|
||||
LOAD_GL_SYM(CheckFramebufferStatus);
|
||||
LOAD_GL_SYM(DeleteFramebuffers);
|
||||
LOAD_GL_SYM(GenRenderbuffers);
|
||||
LOAD_GL_SYM(BindRenderbuffer);
|
||||
LOAD_GL_SYM(FramebufferRenderbuffer);
|
||||
LOAD_GL_SYM(RenderbufferStorage);
|
||||
LOAD_GL_SYM(DeleteRenderbuffers);
|
||||
|
||||
return pglGenFramebuffers && pglBindFramebuffer && pglFramebufferTexture2D &&
|
||||
pglCheckFramebufferStatus && pglDeleteFramebuffers;
|
||||
pglCheckFramebufferStatus && pglDeleteFramebuffers &&
|
||||
pglGenRenderbuffers && pglBindRenderbuffer &&
|
||||
pglFramebufferRenderbuffer && pglRenderbufferStorage &&
|
||||
pglDeleteRenderbuffers;
|
||||
}
|
||||
#elif defined(HAVE_OPENGLES2)
|
||||
#define pglGenFramebuffers glGenFramebuffers
|
||||
@ -153,6 +166,11 @@ static bool load_fbo_proc(gl_t *gl)
|
||||
#define pglFramebufferTexture2D glFramebufferTexture2D
|
||||
#define pglCheckFramebufferStatus glCheckFramebufferStatus
|
||||
#define pglDeleteFramebuffers glDeleteFramebuffers
|
||||
#define pglGenRenderbuffers glGenRenderbuffers
|
||||
#define pglBindRenderbuffer glBindRenderbuffer
|
||||
#define pglFramebufferRenderbuffer glFramebufferRenderbuffer
|
||||
#define pglRenderbufferStorage glRenderbufferStorage
|
||||
#define pglDeleteRenderbuffers glDeleteRenderbuffers
|
||||
#define load_fbo_proc(gl) (true)
|
||||
#elif defined(HAVE_OPENGLES)
|
||||
#define pglGenFramebuffers glGenFramebuffersOES
|
||||
@ -160,6 +178,11 @@ static bool load_fbo_proc(gl_t *gl)
|
||||
#define pglFramebufferTexture2D glFramebufferTexture2DOES
|
||||
#define pglCheckFramebufferStatus glCheckFramebufferStatusOES
|
||||
#define pglDeleteFramebuffers glDeleteFramebuffersOES
|
||||
#define pglGenRenderbuffers glGenRenderbuffersOES
|
||||
#define pglBindRenderbuffer glBindRenderbufferOES
|
||||
#define pglFramebufferRenderbuffer glFramebufferRenderbufferOES
|
||||
#define pglRenderbufferStorage glRenderbufferStorageOES
|
||||
#define pglDeleteRenderbuffers glDeleteRenderbuffersOES
|
||||
#define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES
|
||||
#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_EXT
|
||||
#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES
|
||||
@ -170,6 +193,11 @@ static bool load_fbo_proc(gl_t *gl)
|
||||
#define pglFramebufferTexture2D glFramebufferTexture2D
|
||||
#define pglCheckFramebufferStatus glCheckFramebufferStatus
|
||||
#define pglDeleteFramebuffers glDeleteFramebuffers
|
||||
#define pglGenRenderbuffers glGenRenderbuffers
|
||||
#define pglBindRenderbuffer glBindRenderbuffer
|
||||
#define pglFramebufferRenderbuffer glFramebufferRenderbuffer
|
||||
#define pglRenderbufferStorage glRenderbufferStorage
|
||||
#define pglDeleteRenderbuffers glDeleteRenderbuffers
|
||||
#define load_fbo_proc(gl) (true)
|
||||
#endif
|
||||
#endif
|
||||
@ -703,6 +731,68 @@ void gl_init_fbo(void *data, unsigned width, unsigned height)
|
||||
|
||||
gl->fbo_inited = true;
|
||||
}
|
||||
|
||||
bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
|
||||
{
|
||||
RARCH_LOG("[GL]: Initializing HW render (%u x %u).\n", width, height);
|
||||
|
||||
if (!load_fbo_proc(gl))
|
||||
return false;
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
pglGenFramebuffers(TEXTURES, gl->hw_render_fbo);
|
||||
|
||||
bool depth = g_extern.system.hw_render_callback.depth;
|
||||
bool stencil = g_extern.system.hw_render_callback.stencil;
|
||||
|
||||
if (depth)
|
||||
{
|
||||
pglGenRenderbuffers(TEXTURES, gl->hw_render_depth);
|
||||
gl->hw_render_depth_init = true;
|
||||
}
|
||||
|
||||
if (stencil)
|
||||
{
|
||||
pglGenRenderbuffers(TEXTURES, gl->hw_render_stencil);
|
||||
gl->hw_render_stencil_init = true;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < TEXTURES; i++)
|
||||
{
|
||||
pglBindFramebuffer(GL_FRAMEBUFFER, gl->hw_render_fbo[i]);
|
||||
pglFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl->texture[i], 0);
|
||||
|
||||
if (depth)
|
||||
{
|
||||
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_depth[i]);
|
||||
pglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
|
||||
width, height);
|
||||
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl->hw_render_depth[i]);
|
||||
}
|
||||
|
||||
if (stencil)
|
||||
{
|
||||
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_stencil[i]);
|
||||
pglRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
||||
width, height);
|
||||
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
|
||||
GL_RENDERBUFFER, gl->hw_render_stencil[i]);
|
||||
}
|
||||
|
||||
GLenum status = pglCheckFramebufferStatus(GL_FRAMEBUFFER);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE)
|
||||
{
|
||||
RARCH_ERR("[GL]: Failed to create HW render FBO.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
pglBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
gl->hw_render_fbo_init = true;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void gl_set_projection(void *data, struct gl_ortho *ortho, bool allow_rotate)
|
||||
@ -981,7 +1071,7 @@ static void gl_update_resize(void *data)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void gl_update_input_size(void *data, unsigned width, unsigned height, unsigned pitch)
|
||||
static void gl_update_input_size(void *data, unsigned width, unsigned height, unsigned pitch, bool clear)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
// Res change. Need to clear out texture.
|
||||
@ -990,18 +1080,21 @@ static void gl_update_input_size(void *data, unsigned width, unsigned height, un
|
||||
gl->last_width[gl->tex_index] = width;
|
||||
gl->last_height[gl->tex_index] = height;
|
||||
|
||||
if (clear)
|
||||
{
|
||||
#if defined(HAVE_PSGL)
|
||||
glBufferSubData(GL_TEXTURE_REFERENCE_BUFFER_SCE,
|
||||
gl->tex_w * gl->tex_h * gl->tex_index * gl->base_size,
|
||||
gl->tex_w * gl->tex_h * gl->base_size,
|
||||
gl->empty_buf);
|
||||
glBufferSubData(GL_TEXTURE_REFERENCE_BUFFER_SCE,
|
||||
gl->tex_w * gl->tex_h * gl->tex_index * gl->base_size,
|
||||
gl->tex_w * gl->tex_h * gl->base_size,
|
||||
gl->empty_buf);
|
||||
#else
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t)));
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(width * sizeof(uint32_t)));
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
|
||||
gl->texture_fmt, gl->empty_buf);
|
||||
glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0, 0, 0, gl->tex_w, gl->tex_h, gl->texture_type,
|
||||
gl->texture_fmt, gl->empty_buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
GLfloat xamt = (GLfloat)width / gl->tex_w;
|
||||
GLfloat yamt = (GLfloat)height / gl->tex_h;
|
||||
@ -1364,20 +1457,50 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
gl->tex_index = (gl->tex_index + 1) & TEXTURES_MASK;
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
gl_update_input_size(gl, width, height, pitch);
|
||||
#ifdef HAVE_FBO
|
||||
// Data is already on GPU :) Have to reset some state however incase core changed it.
|
||||
if (gl->hw_render_fbo_init)
|
||||
{
|
||||
gl_update_input_size(gl, width, height, pitch, false);
|
||||
|
||||
RARCH_PERFORMANCE_INIT(copy_frame);
|
||||
RARCH_PERFORMANCE_START(copy_frame);
|
||||
gl_copy_frame(gl, frame, width, height, pitch);
|
||||
RARCH_PERFORMANCE_STOP(copy_frame);
|
||||
if (!gl->fbo_inited)
|
||||
{
|
||||
pglBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
gl_update_input_size(gl, width, height, pitch, true);
|
||||
RARCH_PERFORMANCE_INIT(copy_frame);
|
||||
RARCH_PERFORMANCE_START(copy_frame);
|
||||
gl_copy_frame(gl, frame, width, height, pitch);
|
||||
RARCH_PERFORMANCE_STOP(copy_frame);
|
||||
|
||||
#ifdef IOS // Apparently the viewport is lost each frame, thanks apple.
|
||||
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
|
||||
gl_set_viewport(gl, gl->win_width, gl->win_height, false, true);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
glBindTexture(GL_TEXTURE_2D, gl->texture[gl->tex_index]);
|
||||
|
||||
// Have to reset rendering state which libretro core could easily have overridden.
|
||||
#ifdef HAVE_FBO
|
||||
if (gl->hw_render_fbo_init)
|
||||
{
|
||||
#ifndef HAVE_OPENGLES
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
#endif
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DITHER);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct gl_tex_info tex_info = {0};
|
||||
tex_info.tex = gl->texture[gl->tex_index];
|
||||
tex_info.input_size[0] = width;
|
||||
@ -1442,6 +1565,23 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
|
||||
RARCH_PERFORMANCE_STOP(frame_run);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
// Reset state which could easily mess up libretro core.
|
||||
if (gl->hw_render_fbo_init)
|
||||
{
|
||||
gl_shader_use_func(gl, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
#ifndef NO_GL_FF_VERTEX
|
||||
pglClientActiveTexture(GL_TEXTURE1);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
pglClientActiveTexture(GL_TEXTURE0);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RMENU)
|
||||
if (lifecycle_mode_state & (1ULL << MODE_MENU_DRAW))
|
||||
context_rmenu_frame_func(gl);
|
||||
@ -1453,7 +1593,7 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
|
||||
if (gl->pbo_readback_enable)
|
||||
gl_pbo_async_readback(gl);
|
||||
#endif
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1516,6 +1656,14 @@ static void gl_free(void *data)
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
gl_deinit_fbo(gl);
|
||||
|
||||
if (gl->hw_render_fbo_init)
|
||||
pglDeleteFramebuffers(TEXTURES, gl->hw_render_fbo);
|
||||
if (gl->hw_render_depth)
|
||||
pglDeleteRenderbuffers(TEXTURES, gl->hw_render_depth);
|
||||
if (gl->hw_render_stencil)
|
||||
pglDeleteRenderbuffers(TEXTURES, gl->hw_render_stencil);
|
||||
gl->hw_render_fbo_init = false;
|
||||
#endif
|
||||
|
||||
context_destroy_func();
|
||||
@ -1816,11 +1964,6 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
gl->tex_w = RARCH_SCALE_BASE * video->input_scale;
|
||||
gl->tex_h = RARCH_SCALE_BASE * video->input_scale;
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
// Set up render to texture.
|
||||
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
|
||||
#endif
|
||||
|
||||
gl->keep_aspect = video->force_aspect;
|
||||
|
||||
// Apparently need to set viewport for passes when we aren't using FBOs.
|
||||
@ -1840,6 +1983,7 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
#endif
|
||||
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_DITHER);
|
||||
|
||||
memcpy(gl->tex_coords, tex_coords, sizeof(tex_coords));
|
||||
@ -1865,6 +2009,25 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
|
||||
gl_init_textures(gl, video);
|
||||
gl_init_textures_data(gl);
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
// Set up render to texture.
|
||||
gl_init_fbo(gl, gl->tex_w, gl->tex_h);
|
||||
|
||||
#ifdef HAVE_OPENGLES2
|
||||
enum retro_hw_context_type desired = RETRO_HW_CONTEXT_OPENGLES2;
|
||||
#else
|
||||
enum retro_hw_context_type desired = RETRO_HW_CONTEXT_OPENGL;
|
||||
#endif
|
||||
|
||||
if (g_extern.system.hw_render_callback.context_type == desired
|
||||
&& !gl_init_hw_render(gl, gl->tex_w, gl->tex_h))
|
||||
{
|
||||
context_destroy_func();
|
||||
free(gl);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (input && input_data)
|
||||
context_input_driver_func(input, input_data);
|
||||
|
||||
@ -2317,6 +2480,18 @@ static unsigned gl_get_fbo_state(void *data)
|
||||
gl_t *gl = (gl_t*)data;
|
||||
return gl->fbo_inited ? FBO_INIT : FBO_DEINIT;
|
||||
}
|
||||
|
||||
static uintptr_t gl_get_current_framebuffer(void *data)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
return gl->hw_render_fbo[(gl->tex_index + 1) & TEXTURES_MASK];
|
||||
}
|
||||
|
||||
static retro_proc_address_t gl_get_proc_address(void *data, const char *sym)
|
||||
{
|
||||
gl_t *gl = (gl_t*)data;
|
||||
return gl->ctx_driver->get_proc_address(sym);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void gl_set_aspect_ratio(void *data, unsigned aspectratio_index)
|
||||
@ -2382,6 +2557,8 @@ static const video_poke_interface_t gl_poke_interface = {
|
||||
#ifdef HAVE_FBO
|
||||
gl_set_fbo_state,
|
||||
gl_get_fbo_state,
|
||||
gl_get_current_framebuffer,
|
||||
gl_get_proc_address,
|
||||
#endif
|
||||
gl_set_aspect_ratio,
|
||||
gl_apply_state_changes,
|
||||
|
@ -242,6 +242,13 @@ typedef struct gl
|
||||
struct gl_fbo_scale fbo_scale[MAX_SHADERS];
|
||||
int fbo_pass;
|
||||
bool fbo_inited;
|
||||
|
||||
GLuint hw_render_fbo[TEXTURES];
|
||||
GLuint hw_render_depth[TEXTURES];
|
||||
GLuint hw_render_stencil[TEXTURES];
|
||||
bool hw_render_fbo_init;
|
||||
bool hw_render_depth_init;
|
||||
bool hw_render_stencil_init;
|
||||
#endif
|
||||
|
||||
bool should_resize;
|
||||
|
@ -678,6 +678,8 @@ static const video_poke_interface_t thread_poke = {
|
||||
#ifdef HAVE_FBO
|
||||
thread_set_fbo_state,
|
||||
thread_get_fbo_state,
|
||||
NULL,
|
||||
NULL,
|
||||
#endif
|
||||
thread_set_aspect_ratio,
|
||||
thread_apply_state_changes,
|
||||
|
61
libretro-test-gl/Makefile
Normal file
61
libretro-test-gl/Makefile
Normal file
@ -0,0 +1,61 @@
|
||||
|
||||
ifeq ($(platform),)
|
||||
platform = unix
|
||||
ifeq ($(shell uname -a),)
|
||||
platform = win
|
||||
else ifneq ($(findstring MINGW,$(shell uname -a)),)
|
||||
platform = win
|
||||
else ifneq ($(findstring Darwin,$(shell uname -a)),)
|
||||
platform = osx
|
||||
else ifneq ($(findstring win,$(shell uname -a)),)
|
||||
platform = win
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(platform), unix)
|
||||
TARGET := libretro.so
|
||||
fpic := -fPIC
|
||||
SHARED := -shared -Wl,--version-script=link.T -Wl,--no-undefined
|
||||
GL_LIB := -lGL
|
||||
else ifeq ($(platform), osx)
|
||||
TARGET := libretro.dylib
|
||||
fpic := -fPIC
|
||||
SHARED := -dynamiclib
|
||||
GL_LIB := -framework OpenGL
|
||||
else
|
||||
CC = gcc
|
||||
TARGET := retro.dll
|
||||
SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=link.T -Wl,--no-undefined
|
||||
GL_LIB := -lopengl32
|
||||
CFLAGS += -I..
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -O0 -g
|
||||
else
|
||||
CFLAGS += -O3
|
||||
endif
|
||||
|
||||
OBJECTS := libretro-test.o
|
||||
CFLAGS += -std=gnu99 -Wall -pedantic $(fpic)
|
||||
|
||||
ifeq ($(GLES), 1)
|
||||
CFLAGS += -DGLES
|
||||
LIBS += -lGLESv2
|
||||
else
|
||||
LIBS += $(GL_LIB)
|
||||
endif
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJECTS)
|
||||
$(CC) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) $(LIBS) -lm
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(OBJECTS) $(TARGET)
|
||||
|
||||
.PHONY: clean
|
||||
|
373
libretro-test-gl/libretro-test.c
Normal file
373
libretro-test-gl/libretro-test.c
Normal file
@ -0,0 +1,373 @@
|
||||
#include "../libretro.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
static struct retro_hw_render_callback hw_render;
|
||||
|
||||
#define GL_GLEXT_PROTOTYPES
|
||||
#if defined(GLES) && 0
|
||||
#include <GLES2/gl2.h>
|
||||
#else
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#endif
|
||||
|
||||
static PFNGLCREATEPROGRAMPROC pglCreateProgram;
|
||||
static PFNGLCREATESHADERPROC pglCreateShader;
|
||||
static PFNGLCREATESHADERPROC pglCompileShader;
|
||||
static PFNGLCREATESHADERPROC pglUseProgram;
|
||||
static PFNGLSHADERSOURCEPROC pglShaderSource;
|
||||
static PFNGLATTACHSHADERPROC pglAttachShader;
|
||||
static PFNGLLINKPROGRAMPROC pglLinkProgram;
|
||||
static PFNGLBINDFRAMEBUFFERPROC pglBindFramebuffer;
|
||||
static PFNGLGETUNIFORMLOCATIONPROC pglGetUniformLocation;
|
||||
static PFNGLUNIFORMMATRIX4FVPROC pglUniformMatrix4fv;
|
||||
static PFNGLGETATTRIBLOCATIONPROC pglGetAttribLocation;
|
||||
static PFNGLVERTEXATTRIBPOINTERPROC pglVertexAttribPointer;
|
||||
static PFNGLENABLEVERTEXATTRIBARRAYPROC pglEnableVertexAttribArray;
|
||||
static PFNGLDISABLEVERTEXATTRIBARRAYPROC pglDisableVertexAttribArray;
|
||||
static PFNGLGENVERTEXARRAYSPROC pglGenVertexArrays;
|
||||
static PFNGLBINDVERTEXARRAYPROC pglBindVertexArray;
|
||||
static PFNGLDELETEVERTEXARRAYSPROC pglDeleteVertexArray;
|
||||
static PFNGLGENBUFFERSPROC pglGenBuffers;
|
||||
static PFNGLBUFFERDATAPROC pglBufferData;
|
||||
static PFNGLBINDBUFFERPROC pglBindBuffer;
|
||||
|
||||
struct gl_proc_map
|
||||
{
|
||||
void *proc;
|
||||
const char *sym;
|
||||
};
|
||||
|
||||
#define PROC_BIND(name) { &(pgl##name), "gl" #name }
|
||||
static const struct gl_proc_map proc_map[] = {
|
||||
PROC_BIND(CreateProgram),
|
||||
PROC_BIND(CreateShader),
|
||||
PROC_BIND(CompileShader),
|
||||
PROC_BIND(UseProgram),
|
||||
PROC_BIND(ShaderSource),
|
||||
PROC_BIND(AttachShader),
|
||||
PROC_BIND(LinkProgram),
|
||||
PROC_BIND(BindFramebuffer),
|
||||
PROC_BIND(GetUniformLocation),
|
||||
PROC_BIND(GetAttribLocation),
|
||||
PROC_BIND(UniformMatrix4fv),
|
||||
PROC_BIND(VertexAttribPointer),
|
||||
PROC_BIND(EnableVertexAttribArray),
|
||||
PROC_BIND(DisableVertexAttribArray),
|
||||
PROC_BIND(GenVertexArrays),
|
||||
PROC_BIND(BindVertexArray),
|
||||
PROC_BIND(DeleteVertexArray),
|
||||
PROC_BIND(GenBuffers),
|
||||
PROC_BIND(BufferData),
|
||||
PROC_BIND(BindBuffer),
|
||||
};
|
||||
|
||||
static void init_gl_proc(void)
|
||||
{
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(proc_map); i++)
|
||||
{
|
||||
retro_proc_address_t proc = hw_render.get_proc_address(proc_map[i].sym);
|
||||
if (!proc)
|
||||
fprintf(stderr, "Symbol %s not found!\n", proc_map[i].sym);
|
||||
memcpy(proc_map[i].proc, &proc, sizeof(proc));
|
||||
}
|
||||
}
|
||||
|
||||
static GLuint prog;
|
||||
static GLuint vao;
|
||||
static GLuint vbo;
|
||||
|
||||
static const GLfloat vertex_data[] = {
|
||||
-0.5, -0.5,
|
||||
0.5, -0.5,
|
||||
-0.5, 0.5,
|
||||
0.5, 0.5,
|
||||
1.0, 1.0, 1.0, 1.0,
|
||||
1.0, 1.0, 0.0, 1.0,
|
||||
0.0, 1.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
|
||||
static const char *vertex_shader[] = {
|
||||
"uniform mat4 uMVP;",
|
||||
"attribute vec2 aVertex;",
|
||||
"attribute vec4 aColor;",
|
||||
"varying vec4 color;",
|
||||
"void main() {",
|
||||
" gl_Position = uMVP * vec4(aVertex, 0.0, 1.0);",
|
||||
" color = aColor;",
|
||||
"}",
|
||||
};
|
||||
|
||||
static const char *fragment_shader[] = {
|
||||
"varying vec4 color;",
|
||||
"void main() {",
|
||||
" gl_FragColor = color;",
|
||||
"}",
|
||||
};
|
||||
|
||||
static void compile_program(void)
|
||||
{
|
||||
prog = pglCreateProgram();
|
||||
GLuint vert = pglCreateShader(GL_VERTEX_SHADER);
|
||||
GLuint frag = pglCreateShader(GL_FRAGMENT_SHADER);
|
||||
|
||||
pglShaderSource(vert, ARRAY_SIZE(vertex_shader), vertex_shader, 0);
|
||||
pglShaderSource(frag, ARRAY_SIZE(fragment_shader), fragment_shader, 0);
|
||||
pglCompileShader(vert);
|
||||
pglCompileShader(frag);
|
||||
|
||||
pglAttachShader(prog, vert);
|
||||
pglAttachShader(prog, frag);
|
||||
pglLinkProgram(prog);
|
||||
}
|
||||
|
||||
static void setup_vao(void)
|
||||
{
|
||||
pglUseProgram(prog);
|
||||
|
||||
pglGenVertexArrays(1, &vao);
|
||||
pglBindVertexArray(vao);
|
||||
|
||||
pglGenBuffers(1, &vbo);
|
||||
pglBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||
pglBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
|
||||
|
||||
int vloc = pglGetAttribLocation(prog, "aVertex");
|
||||
pglVertexAttribPointer(vloc, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
|
||||
pglEnableVertexAttribArray(vloc);
|
||||
int cloc = pglGetAttribLocation(prog, "aColor");
|
||||
pglVertexAttribPointer(cloc, 4, GL_FLOAT, GL_FALSE, 0, (void*)(8 * sizeof(GLfloat)));
|
||||
pglEnableVertexAttribArray(cloc);
|
||||
|
||||
pglBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
pglBindVertexArray(0);
|
||||
pglUseProgram(0);
|
||||
}
|
||||
|
||||
void retro_init(void)
|
||||
{}
|
||||
|
||||
void retro_deinit(void)
|
||||
{}
|
||||
|
||||
unsigned retro_api_version(void)
|
||||
{
|
||||
return RETRO_API_VERSION;
|
||||
}
|
||||
|
||||
void retro_set_controller_port_device(unsigned port, unsigned device)
|
||||
{
|
||||
(void)port;
|
||||
(void)device;
|
||||
}
|
||||
|
||||
void retro_get_system_info(struct retro_system_info *info)
|
||||
{
|
||||
memset(info, 0, sizeof(*info));
|
||||
info->library_name = "TestCore GL";
|
||||
info->library_version = "v1";
|
||||
info->need_fullpath = false;
|
||||
info->valid_extensions = NULL; // Anything is fine, we don't care.
|
||||
}
|
||||
|
||||
void retro_get_system_av_info(struct retro_system_av_info *info)
|
||||
{
|
||||
info->timing = (struct retro_system_timing) {
|
||||
.fps = 60.0,
|
||||
.sample_rate = 30000.0,
|
||||
};
|
||||
|
||||
info->geometry = (struct retro_game_geometry) {
|
||||
.base_width = 512,
|
||||
.base_height = 512,
|
||||
.max_width = 512,
|
||||
.max_height = 512,
|
||||
.aspect_ratio = 4.0 / 3.0,
|
||||
};
|
||||
}
|
||||
|
||||
static retro_video_refresh_t video_cb;
|
||||
static retro_audio_sample_t audio_cb;
|
||||
static retro_audio_sample_batch_t audio_batch_cb;
|
||||
static retro_environment_t environ_cb;
|
||||
static retro_input_poll_t input_poll_cb;
|
||||
static retro_input_state_t input_state_cb;
|
||||
|
||||
void retro_set_environment(retro_environment_t cb)
|
||||
{
|
||||
environ_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
{
|
||||
audio_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
|
||||
{
|
||||
audio_batch_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_input_poll(retro_input_poll_t cb)
|
||||
{
|
||||
input_poll_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_input_state(retro_input_state_t cb)
|
||||
{
|
||||
input_state_cb = cb;
|
||||
}
|
||||
|
||||
void retro_set_video_refresh(retro_video_refresh_t cb)
|
||||
{
|
||||
video_cb = cb;
|
||||
}
|
||||
|
||||
void retro_run(void)
|
||||
{
|
||||
input_poll_cb();
|
||||
|
||||
pglBindFramebuffer(GL_FRAMEBUFFER, hw_render.get_current_framebuffer());
|
||||
glClearColor(0.3, 0.4, 0.5, 1.0);
|
||||
glViewport(0, 0, 512, 512);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
pglUseProgram(prog);
|
||||
pglBindVertexArray(vao);
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
int loc = pglGetUniformLocation(prog, "uMVP");
|
||||
|
||||
static unsigned frame_count;
|
||||
frame_count++;
|
||||
float angle = frame_count / 100.0;
|
||||
float cos_angle = cos(angle);
|
||||
float sin_angle = sin(angle);
|
||||
|
||||
const GLfloat mvp[] = {
|
||||
cos_angle, -sin_angle, 0, 0,
|
||||
sin_angle, cos_angle, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
};
|
||||
pglUniformMatrix4fv(loc, 1, GL_FALSE, mvp);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
cos_angle *= 0.5;
|
||||
sin_angle *= 0.5;
|
||||
const GLfloat mvp2[] = {
|
||||
cos_angle, -sin_angle, 0, 0.0,
|
||||
sin_angle, cos_angle, 0, 0.0,
|
||||
0, 0, 1, 0,
|
||||
0.4, 0.4, 0.2, 1,
|
||||
};
|
||||
|
||||
pglUniformMatrix4fv(loc, 1, GL_FALSE, mvp2);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
pglUseProgram(0);
|
||||
pglBindVertexArray(0);
|
||||
|
||||
video_cb(RETRO_HW_FRAME_BUFFER_VALID, 512, 512, 0);
|
||||
}
|
||||
|
||||
static void context_reset(void)
|
||||
{
|
||||
fprintf(stderr, "Context reset!\n");
|
||||
init_gl_proc();
|
||||
compile_program();
|
||||
setup_vao();
|
||||
}
|
||||
|
||||
bool retro_load_game(const struct retro_game_info *info)
|
||||
{
|
||||
enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||
if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt))
|
||||
{
|
||||
fprintf(stderr, "XRGB8888 is not supported.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef GLES
|
||||
hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES2;
|
||||
#else
|
||||
hw_render.context_type = RETRO_HW_CONTEXT_OPENGL;
|
||||
#endif
|
||||
hw_render.context_reset = context_reset;
|
||||
hw_render.depth = true;
|
||||
hw_render.stencil = true;
|
||||
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
||||
return false;
|
||||
|
||||
fprintf(stderr, "Loaded game!\n");
|
||||
(void)info;
|
||||
return true;
|
||||
}
|
||||
|
||||
void retro_unload_game(void)
|
||||
{}
|
||||
|
||||
unsigned retro_get_region(void)
|
||||
{
|
||||
return RETRO_REGION_NTSC;
|
||||
}
|
||||
|
||||
bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num)
|
||||
{
|
||||
(void)type;
|
||||
(void)info;
|
||||
(void)num;
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t retro_serialize_size(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool retro_serialize(void *data, size_t size)
|
||||
{
|
||||
(void)data;
|
||||
(void)size;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retro_unserialize(const void *data, size_t size)
|
||||
{
|
||||
(void)data;
|
||||
(void)size;
|
||||
return false;
|
||||
}
|
||||
|
||||
void *retro_get_memory_data(unsigned id)
|
||||
{
|
||||
(void)id;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t retro_get_memory_size(unsigned id)
|
||||
{
|
||||
(void)id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void retro_reset(void)
|
||||
{}
|
||||
|
||||
void retro_cheat_reset(void)
|
||||
{}
|
||||
|
||||
void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
{
|
||||
(void)index;
|
||||
(void)enabled;
|
||||
(void)code;
|
||||
}
|
||||
|
5
libretro-test-gl/link.T
Normal file
5
libretro-test-gl/link.T
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
global: retro_*;
|
||||
local: *;
|
||||
};
|
||||
|
46
libretro.h
46
libretro.h
@ -333,6 +333,8 @@ enum retro_mod
|
||||
RETROKMOD_DUMMY = INT_MAX // Ensure sizeof(enum) == sizeof(int)
|
||||
};
|
||||
|
||||
// If set, this call is not part of the public libretro API yet. It can change or be removed at any time.
|
||||
#define RETRO_ENVIRONMENT_EXPERIMENTAL 0x10000
|
||||
|
||||
// Environment commands.
|
||||
#define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * --
|
||||
@ -421,8 +423,52 @@ enum retro_mod
|
||||
// Sets an interface which frontend can use to eject and insert disk images.
|
||||
// This is used for games which consist of multiple images and must be manually
|
||||
// swapped out by the user (e.g. PSX).
|
||||
#define RETRO_ENVIRONMENT_SET_HW_RENDER (14 | RETRO_ENVIRONMENT_EXPERIMENTAL)
|
||||
// struct retro_hw_render_callback * --
|
||||
// NOTE: This call is currently very experimental, and should not be considered part of the public API.
|
||||
// The interface could be changed or removed at any time.
|
||||
// Sets an interface to let a libretro core render with hardware acceleration.
|
||||
// Should be called in retro_load_game().
|
||||
// If successful, libretro cores will be able to render to a frontend-provided framebuffer.
|
||||
// The size of this framebuffer will be at least as large as max_width/max_height provided in get_av_info().
|
||||
// If HW rendering is used, pass only RETRO_HW_FRAME_BUFFER_VALID or NULL to retro_video_refresh_t.
|
||||
|
||||
|
||||
// Pass this to retro_video_refresh_t if rendering to hardware.
|
||||
// Passing NULL to retro_video_refresh_t is still a frame dupe as normal.
|
||||
#define RETRO_HW_FRAME_BUFFER_VALID ((void*)-1)
|
||||
|
||||
// Invalidates the current HW context.
|
||||
// If called, all GPU resources must be reinitialized.
|
||||
// Usually called when frontend reinits video driver.
|
||||
// Also called first time video driver is initialized, allowing libretro core to init resources.
|
||||
typedef void (*retro_hw_context_reset_t)(void);
|
||||
// Gets current framebuffer which is to be rendered to. Could change every frame potentially.
|
||||
typedef uintptr_t (*retro_hw_get_current_framebuffer_t)(void);
|
||||
|
||||
// Get a symbol from HW context.
|
||||
typedef void (*retro_proc_address_t)(void);
|
||||
typedef retro_proc_address_t (*retro_hw_get_proc_address_t)(const char *sym);
|
||||
|
||||
enum retro_hw_context_type
|
||||
{
|
||||
RETRO_HW_CONTEXT_NONE = 0,
|
||||
RETRO_HW_CONTEXT_OPENGL, // OpenGL 2.x. Latest version available before 3.x+.
|
||||
RETRO_HW_CONTEXT_OPENGLES2, // GLES 2.0
|
||||
|
||||
RETRO_HW_CONTEXT_DUMMY = INT_MAX
|
||||
};
|
||||
|
||||
struct retro_hw_render_callback
|
||||
{
|
||||
enum retro_hw_context_type context_type; // Which API to use. Set by libretro core.
|
||||
retro_hw_context_reset_t context_reset; // Set by libretro core.
|
||||
retro_hw_get_current_framebuffer_t get_current_framebuffer; // Set by frontend.
|
||||
retro_hw_get_proc_address_t get_proc_address; // Set by frontend.
|
||||
bool depth; // Set if render buffers should have depth component attached.
|
||||
bool stencil; // Set if render buffers should have stencil component attached.
|
||||
};
|
||||
|
||||
// Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. Called by the frontend in response to keyboard events.
|
||||
// down is set if the key is being pressed, or false if it is being released.
|
||||
// keycode is the RETROK value of the char.
|
||||
|
28
retroarch.c
28
retroarch.c
@ -141,10 +141,18 @@ static void take_screenshot(void)
|
||||
|
||||
bool ret = false;
|
||||
|
||||
if (g_settings.video.gpu_screenshot && driver.video->read_viewport && driver.video->viewport_info)
|
||||
ret = take_screenshot_viewport();
|
||||
else if (g_extern.frame_cache.data)
|
||||
ret = take_screenshot_raw();
|
||||
if (g_extern.frame_cache.data)
|
||||
{
|
||||
if ((g_settings.video.gpu_screenshot ||
|
||||
(g_extern.frame_cache.data == RETRO_HW_FRAME_BUFFER_VALID)) &&
|
||||
driver.video->read_viewport &&
|
||||
driver.video->viewport_info)
|
||||
ret = take_screenshot_viewport();
|
||||
else if (g_extern.frame_cache.data && (g_extern.frame_cache.data != RETRO_HW_FRAME_BUFFER_VALID))
|
||||
ret = take_screenshot_raw();
|
||||
else
|
||||
RARCH_ERR("Cannot take screenshot. GPU rendering is used and read_viewport is not supported.\n");
|
||||
}
|
||||
|
||||
const char *msg = NULL;
|
||||
if (ret)
|
||||
@ -329,10 +337,14 @@ void rarch_render_cached_frame(void)
|
||||
g_extern.recording = false;
|
||||
#endif
|
||||
|
||||
const void *frame = g_extern.frame_cache.data;
|
||||
if (frame == RETRO_HW_FRAME_BUFFER_VALID)
|
||||
frame = NULL; // Dupe
|
||||
|
||||
// Not 100% safe, since the library might have
|
||||
// freed the memory, but no known implementations do this :D
|
||||
// It would be really stupid at any rate ...
|
||||
video_frame(g_extern.frame_cache.data,
|
||||
video_frame(frame,
|
||||
g_extern.frame_cache.width,
|
||||
g_extern.frame_cache.height,
|
||||
g_extern.frame_cache.pitch);
|
||||
@ -1291,6 +1303,12 @@ static void init_recording(void)
|
||||
if (!g_extern.recording)
|
||||
return;
|
||||
|
||||
if (!g_settings.video.gpu_record && g_extern.system.hw_render_callback.context_type)
|
||||
{
|
||||
RARCH_WARN("Libretro core is hardware rendered. Must use post-shaded FFmpeg recording as well.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
double fps = g_extern.system.av_info.timing.fps;
|
||||
double samplerate = g_extern.system.av_info.timing.sample_rate;
|
||||
RARCH_LOG("Custom timing given: FPS: %.4f, Sample rate: %.4f\n", (float)fps, (float)samplerate);
|
||||
|
Loading…
x
Reference in New Issue
Block a user