Initial work on core GL context support.

This commit is contained in:
Themaister 2013-06-22 15:06:56 +02:00
parent bd4f7133a4
commit b9fce188ea
16 changed files with 201 additions and 60 deletions

View File

@ -653,6 +653,7 @@ static bool environment_cb(unsigned cmd, void *data)
break;
case RETRO_ENVIRONMENT_SET_HW_RENDER:
case RETRO_ENVIRONMENT_SET_HW_RENDER | RETRO_ENVIRONMENT_EXPERIMENTAL: // ABI compat
{
RARCH_LOG("Environ SET_HW_RENDER.\n");
struct retro_hw_render_callback *cb = (struct retro_hw_render_callback*)data;
@ -669,6 +670,7 @@ static bool environment_cb(unsigned cmd, void *data)
break;
case RETRO_HW_CONTEXT_OPENGL:
case RETRO_HW_CONTEXT_OPENGL_CORE:
RARCH_ERR("Requesting OpenGL context, but RetroArch is compiled against OpenGLES2. Cannot use HW context.\n");
return false;
#elif defined(HAVE_OPENGL)
@ -680,6 +682,11 @@ static bool environment_cb(unsigned cmd, void *data)
RARCH_LOG("Requesting OpenGL context.\n");
driver.video = &video_gl;
break;
case RETRO_HW_CONTEXT_OPENGL_CORE:
RARCH_LOG("Requesting core OpenGL context (%u.%u).\n", cb->version_major, cb->version_minor);
driver.video = &video_gl;
break;
#endif
default:
@ -688,7 +695,11 @@ static bool environment_cb(unsigned cmd, void *data)
}
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));
if (cmd & RETRO_ENVIRONMENT_EXPERIMENTAL) // Old ABI. Don't copy garbage.
memcpy(&g_extern.system.hw_render_callback, cb, offsetof(struct retro_hw_render_callback, stencil));
else
memcpy(&g_extern.system.hw_render_callback, cb, sizeof(*cb));
break;
}

View File

@ -236,8 +236,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return ret;
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
g_api = api;
return api == GFX_CTX_OPENGL_ES_API;
}

View File

@ -27,8 +27,10 @@
#include "../../apple/RetroArch/rarch_wrapper.h"
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
#ifdef IOS
return api == GFX_CTX_OPENGL_ES_API;
#else

View File

@ -362,8 +362,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return ret;
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
g_api = api;
return api == GFX_CTX_OPENGL_ES_API;
}

View File

@ -553,8 +553,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return eglGetProcAddress(symbol);
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
g_api = api;
switch (api)
{

View File

@ -39,6 +39,13 @@ static unsigned g_screen;
static GLXContext g_ctx;
static GLXFBConfig g_fbc;
static unsigned g_major;
static unsigned g_minor;
static bool g_core;
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*,
GLXFBConfig, GLXContext, Bool, const int*);
static glXCreateContextAttribsARBProc glx_create_context_attribs;
static XF86VidModeModeInfo g_desktop_mode;
static bool g_should_reset_mode;
@ -207,11 +214,26 @@ static bool gfx_ctx_init(void)
if (!g_dpy)
goto error;
// GLX 1.3+ required.
int major, minor;
glXQueryVersion(g_dpy, &major, &minor);
if (major < 1 || (major == 1 && minor < 3))
goto error;
if (g_major * 1000 + g_minor >= 3001) // Core context
{
g_core = true;
// GLX 1.4+ required.
if ((major * 1000 + minor) < 1004)
goto error;
glx_create_context_attribs = (glXCreateContextAttribsARBProc)glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB");
if (!glx_create_context_attribs)
goto error;
}
else
{
g_core = false;
// GLX 1.3+ required.
if ((major * 1000 + minor) < 1003)
goto error;
}
int nelements;
fbcs = glXChooseFBConfig(g_dpy, DefaultScreen(g_dpy),
@ -339,7 +361,20 @@ static bool gfx_ctx_set_video_mode(
XEvent event;
XIfEvent(g_dpy, &event, glx_wait_notify, NULL);
g_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, 0, True);
if (g_core)
{
const int attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, g_major,
GLX_CONTEXT_MINOR_VERSION_ARB, g_minor,
GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB,
None,
};
g_ctx = glx_create_context_attribs(g_dpy, g_fbc, NULL, True, attribs);
}
else
g_ctx = glXCreateNewContext(g_dpy, g_fbc, GLX_RGBA_TYPE, 0, True);
if (!g_ctx)
{
RARCH_ERR("[GLX]: Failed to create new context.\n");
@ -460,6 +495,8 @@ static void gfx_ctx_destroy(void)
g_inited = false;
g_pglSwapInterval = NULL;
g_major = g_minor = 0;
g_core = false;
}
static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data)
@ -486,8 +523,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return glXGetProcAddress((const GLubyte*)symbol);
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
g_major = major;
g_minor = minor;
return api == GFX_CTX_OPENGL_API;
}

View File

@ -300,8 +300,10 @@ static void gfx_ctx_destroy(void)
static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) { }
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
return api == GFX_CTX_OPENGL_API || GFX_CTX_OPENGL_ES_API;
}

View File

@ -243,8 +243,10 @@ static bool gfx_ctx_set_video_mode(
return true;
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
g_api = api;
switch (api)
{

View File

@ -484,8 +484,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return (gfx_ctx_proc_t)wglGetProcAddress(symbol);
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
return api == GFX_CTX_OPENGL_API;
}

View File

@ -260,8 +260,10 @@ static void gfx_ctx_xdk_destroy(void)
static void gfx_ctx_xdk_input_driver(const input_driver_t **input, void **input_data) { }
static bool gfx_ctx_xdk_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_xdk_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
#if defined(_XBOX1)
return api == GFX_CTX_DIRECT3D8_API;
#elif defined(_XBOX360)

View File

@ -555,8 +555,10 @@ static gfx_ctx_proc_t gfx_ctx_get_proc_address(const char *symbol)
return eglGetProcAddress(symbol);
}
static bool gfx_ctx_bind_api(enum gfx_ctx_api api)
static bool gfx_ctx_bind_api(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
(void)major;
(void)minor;
g_api = api;
switch (api)
{

View File

@ -14,6 +14,7 @@
*/
#include "gfx_context.h"
#include "general.h"
#include <string.h>
#ifdef HAVE_CONFIG_H
@ -55,7 +56,7 @@ static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident)
{
for (unsigned i = 0; i < sizeof(gfx_ctx_drivers) / sizeof(gfx_ctx_drivers[0]); i++)
for (unsigned i = 0; i < ARRAY_SIZE(gfx_ctx_drivers); i++)
{
if (strcmp(gfx_ctx_drivers[i]->ident, ident) == 0)
return gfx_ctx_drivers[i];
@ -64,11 +65,11 @@ const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident)
return NULL;
}
const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api)
const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api, unsigned major, unsigned minor)
{
for (unsigned i = 0; i < sizeof(gfx_ctx_drivers) / sizeof(gfx_ctx_drivers[0]); i++)
for (unsigned i = 0; i < ARRAY_SIZE(gfx_ctx_drivers); i++)
{
if (gfx_ctx_drivers[i]->bind_api(api))
if (gfx_ctx_drivers[i]->bind_api(api, major, minor))
{
if (gfx_ctx_drivers[i]->init())
return gfx_ctx_drivers[i];

View File

@ -41,7 +41,7 @@ typedef struct gfx_ctx_driver
bool (*init)(void);
void (*destroy)(void);
bool (*bind_api)(enum gfx_ctx_api); // Which API to bind to.
bool (*bind_api)(enum gfx_ctx_api, unsigned major, unsigned minor); // Which API to bind to.
// Sets the swap interval.
void (*swap_interval)(unsigned);
@ -111,7 +111,7 @@ extern const gfx_ctx_driver_t gfx_ctx_apple;
extern const gfx_ctx_driver_t gfx_ctx_null;
const gfx_ctx_driver_t *gfx_ctx_find_driver(const char *ident); // Finds driver with ident. Does not initialize.
const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api); // Finds first suitable driver and initializes.
const gfx_ctx_driver_t *gfx_ctx_init_first(enum gfx_ctx_api api, unsigned major, unsigned minor); // Finds first suitable driver and initializes.
#endif

138
gfx/gl.c
View File

@ -138,6 +138,29 @@ static bool load_sync_proc(gl_t *gl)
}
#endif
#ifndef HAVE_OPENGLES
static PFNGLGENVERTEXARRAYSPROC pglGenVertexArrays;
static PFNGLBINDVERTEXARRAYPROC pglBindVertexArray;
static PFNGLDELETEVERTEXARRAYSPROC pglDeleteVertexArrays;
static bool load_vao_proc(gl_t *gl)
{
if (!gl_query_extension("ARB_vertex_array_object"))
return false;
LOAD_GL_SYM(GenVertexArrays);
LOAD_GL_SYM(BindVertexArray);
LOAD_GL_SYM(DeleteVertexArrays);
bool present = pglGenVertexArrays && pglBindVertexArray && pglDeleteVertexArrays;
if (!present)
return false;
pglGenVertexArrays(1, &gl->vao);
return true;
}
#endif
#ifdef HAVE_FBO
#if defined(_WIN32) && !defined(RARCH_CONSOLE)
static PFNGLGENFRAMEBUFFERSPROC pglGenFramebuffers;
@ -667,20 +690,6 @@ void gl_init_fbo(void *data, unsigned width, unsigned height)
#ifndef HAVE_RGL
// GLES and GL inconsistency.
#ifndef GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT
#endif
#ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
#endif
#ifndef GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT
#endif
#ifndef GL_FRAMEBUFFER_UNSUPPORTED
#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_EXT
#endif
bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
{
RARCH_LOG("[GL]: Initializing HW render (%u x %u).\n", width, height);
@ -697,6 +706,12 @@ bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
pglGenFramebuffers(TEXTURES, gl->hw_render_fbo);
bool depth = g_extern.system.hw_render_callback.depth;
bool stencil = g_extern.system.hw_render_callback.stencil;
#ifdef HAVE_OPENGLES2
if (stencil && !gl_query_extension("OES_packed_depth_stencil"))
return false;
#endif
if (depth)
{
@ -711,29 +726,47 @@ bool gl_init_hw_render(gl_t *gl, unsigned width, unsigned height)
if (depth)
{
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_depth[i]);
pglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
width, height);
pglBindRenderbuffer(GL_RENDERBUFFER, 0);
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, gl->hw_render_depth[i]);
GLenum component = GL_DEPTH_COMPONENT16;
GLenum attachment = GL_DEPTH_ATTACHMENT;
if (stencil)
{
#ifdef HAVE_OPENGLES2
// GLES2 is a bit weird, as always. :P
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_depth[i]);
pglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
width, height);
pglBindRenderbuffer(GL_RENDERBUFFER, 0);
// There's no GL_DEPTH_STENCIL_ATTACHMENT like in desktop GL.
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, gl->hw_render_depth[i]);
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, gl->hw_render_depth[i]);
#else
// We use ARB FBO extensions, no need to check.
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_depth[i]);
pglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
width, height);
pglBindRenderbuffer(GL_RENDERBUFFER, 0);
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, gl->hw_render_depth[i]);
#endif
}
else
{
pglBindRenderbuffer(GL_RENDERBUFFER, gl->hw_render_depth[i]);
pglRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16,
width, height);
pglBindRenderbuffer(GL_RENDERBUFFER, 0);
pglFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, gl->hw_render_depth[i]);
}
}
GLenum status = pglCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
RARCH_ERR("[GL]: Failed to create HW render FBO #%u.\n", i);
const char *err = NULL;
switch (status)
{
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: err = "Incomplete Attachment"; break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: err = "Incomplete Dimensions"; break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: err = "Missing Attachment"; break;
case GL_FRAMEBUFFER_UNSUPPORTED: err = "Unsupported"; break;
default: err = "Unknown"; break;
}
RARCH_ERR("[GL]: Error: %s.\n", err);
RARCH_ERR("[GL]: Failed to create HW render FBO #%u, error: 0x%u.\n", i, (unsigned)status);
return false;
}
}
@ -1380,8 +1413,11 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
RARCH_PERFORMANCE_START(frame_run);
gl_t *gl = (gl_t*)data;
uint64_t lifecycle_mode_state = g_extern.lifecycle_mode_state;
(void)lifecycle_mode_state;
#ifndef HAVE_OPENGLES
if (gl->core_context)
pglBindVertexArray(gl->vao);
#endif
if (gl->shader)
gl->shader->use(1);
@ -1543,6 +1579,11 @@ static bool gl_frame(void *data, const void *frame, unsigned width, unsigned hei
}
#endif
#ifndef HAVE_OPENGLES
if (gl->core_context)
pglBindVertexArray(0);
#endif
return true;
}
@ -1614,11 +1655,18 @@ static void gl_free(void *data)
#endif
#endif
#ifndef HAVE_OPENGLES
if (gl->core_context)
{
pglBindVertexArray(0);
pglDeleteVertexArrays(1, &gl->vao);
}
#endif
context_destroy_func();
free(gl->empty_buf);
free(gl->conv_buffer);
free(gl);
}
@ -1640,6 +1688,17 @@ static bool resolve_extensions(gl_t *gl)
return false;
#endif
#ifndef HAVE_OPENGLES
gl->core_context = g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL_CORE;
RARCH_LOG("[GL]: Using Core GL context.\n");
if (gl->core_context &&
!load_vao_proc(gl))
{
RARCH_ERR("[GL]: Failed to init VAOs.\n");
return false;
}
#endif
#ifdef HAVE_GL_SYNC
gl->have_sync = load_sync_proc(gl);
if (gl->have_sync && g_settings.video.hard_sync)
@ -1779,6 +1838,8 @@ static void gl_init_pbo_readback(void *data)
static const gfx_ctx_driver_t *gl_get_context(void)
{
unsigned major = g_extern.system.hw_render_callback.version_major;
unsigned minor = g_extern.system.hw_render_callback.version_minor;
#ifdef HAVE_OPENGLES
enum gfx_ctx_api api = GFX_CTX_OPENGL_ES_API;
const char *api_name = "OpenGL ES";
@ -1792,7 +1853,7 @@ static const gfx_ctx_driver_t *gl_get_context(void)
const gfx_ctx_driver_t *ctx = gfx_ctx_find_driver(g_settings.video.gl_context);
if (ctx)
{
if (!ctx->bind_api(api))
if (!ctx->bind_api(api, major, minor))
{
RARCH_ERR("Failed to bind API %s to context %s.\n", api_name, g_settings.video.gl_context);
return NULL;
@ -1813,7 +1874,7 @@ static const gfx_ctx_driver_t *gl_get_context(void)
return ctx;
}
else
return gfx_ctx_init_first(api);
return gfx_ctx_init_first(api, major, minor);
}
static void *gl_init(const video_info_t *video, const input_driver_t **input, void **input_data)
@ -1923,7 +1984,8 @@ static void *gl_init(const video_info_t *video, const input_driver_t **input, vo
#ifdef HAVE_OPENGLES2
gl->hw_render_use = g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGLES2;
#else
gl->hw_render_use = g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL;
gl->hw_render_use = g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL ||
g_extern.system.hw_render_callback.context_type == RETRO_HW_CONTEXT_OPENGL_CORE;
#endif
#endif

View File

@ -284,6 +284,11 @@ typedef struct gl
GLsync fences[MAX_FENCES];
unsigned fence_count;
#endif
#ifndef HAVE_OPENGLES
bool core_context;
GLuint vao;
#endif
} gl_t;
// Windows ... <_<

View File

@ -434,7 +434,7 @@ 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)
#define RETRO_ENVIRONMENT_SET_HW_RENDER 14
// 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.
@ -509,6 +509,7 @@ 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_OPENGL_CORE, // Modern desktop core GL context. Use major/minor fields to set GL version.
RETRO_HW_CONTEXT_DUMMY = INT_MAX
};
@ -520,6 +521,10 @@ struct retro_hw_render_callback
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 stencil buffers should be attached.
// If depth and stencil are true, a packed 24/8 buffer will be added. Only attaching stencil is invalid and will be ignored.
unsigned version_major; // Major version number for core GL context.
unsigned version_minor; // Minor version number for core GL context.
};
// Callback type passed in RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK. Called by the frontend in response to keyboard events.