From 5fae12ddaa60d3eae47c7ba074c136dc40a006c1 Mon Sep 17 00:00:00 2001 From: Anthony Pesch Date: Sun, 16 Apr 2017 19:41:47 -0400 Subject: [PATCH] support sharing render_backend / OpenGL contexts --- src/sys/thread.h | 9 ++ src/ui/microprofile.cc | 3 - src/ui/window.c | 21 ++-- src/ui/window.h | 2 +- src/video/gl_backend.c | 233 ++++++++++++++++++++++--------------- src/video/render_backend.h | 11 +- 6 files changed, 162 insertions(+), 117 deletions(-) diff --git a/src/sys/thread.h b/src/sys/thread.h index 87dcc66a..fa664dd6 100644 --- a/src/sys/thread.h +++ b/src/sys/thread.h @@ -29,6 +29,15 @@ int cond_timedwait(cond_t cond, mutex_t mutex, int ms); void cond_signal(cond_t cond); void cond_destroy(cond_t cond); +/* + * tls + */ +#if PLATFORM_WINDOWS +#define _Thread_local __declspec(thread) +#else +#define _Thread_local __thread +#endif + /* * sleeping */ diff --git a/src/ui/microprofile.cc b/src/ui/microprofile.cc index 29275be9..d67fd91f 100644 --- a/src/ui/microprofile.cc +++ b/src/ui/microprofile.cc @@ -106,7 +106,6 @@ static void mp_draw_text(struct microprofile *mp, int x, int y, uint32_t color, int text_len = static_cast(strlen(text)); struct vertex2 *vertex = mp_alloc_verts(mp, {PRIM_TRIANGLES, - 0, mp->font_texture, BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, @@ -155,7 +154,6 @@ static void mp_draw_text(struct microprofile *mp, int x, int y, uint32_t color, static void mp_draw_box(struct microprofile *mp, int x0, int y0, int x1, int y1, uint32_t color, enum box_type type) { struct vertex2 *vertex = mp_alloc_verts(mp, {PRIM_TRIANGLES, - 0, 0, BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, @@ -215,7 +213,6 @@ static void mp_draw_line(struct microprofile *mp, float *verts, int num_verts, CHECK(num_verts); struct vertex2 *vertex = mp_alloc_verts(mp, {PRIM_LINES, - 0, 0, BLEND_SRC_ALPHA, BLEND_ONE_MINUS_SRC_ALPHA, diff --git a/src/ui/window.c b/src/ui/window.c index 8dc034a9..fdc38d74 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -835,27 +835,24 @@ void win_gl_make_current(struct window *win, glcontext_t ctx) { SDL_GL_MakeCurrent(win->handle, ctx); } +glcontext_t win_gl_create_context_from(struct window *win, glcontext_t from) { + /* bind default context at this point to make sure a context is available to + be shared from */ + SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); + SDL_GL_MakeCurrent(win->handle, from); + + return win_gl_create_context(win); +} + glcontext_t win_gl_create_context(struct window *win) { /* need at least a 3.3 core context for our shaders */ SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - /* bind default context at this point to make sure a context is available to - be shared from */ - if (win->default_ctx) { - SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1); - SDL_GL_MakeCurrent(win->handle, win->default_ctx); - } - SDL_GLContext ctx = SDL_GL_CreateContext(win->handle); CHECK_NOTNULL(ctx, "OpenGL context creation failed: %s", SDL_GetError()); - /* track default context for future sharing */ - if (!win->default_ctx) { - win->default_ctx = ctx; - } - /* link in gl functions at runtime */ glewExperimental = GL_TRUE; GLenum err = glewInit(); diff --git a/src/ui/window.h b/src/ui/window.h index e4d9fa29..27f9b903 100644 --- a/src/ui/window.h +++ b/src/ui/window.h @@ -42,7 +42,6 @@ struct window_listener { struct window { /* public */ struct SDL_Window *handle; - glcontext_t default_ctx; /* read only */ int width; @@ -60,6 +59,7 @@ struct window *win_create(); void win_destroy(struct window *win); glcontext_t win_gl_create_context(struct window *win); +glcontext_t win_gl_create_context_from(struct window *win, glcontext_t other); void win_gl_make_current(struct window *win, glcontext_t ctx); void win_gl_destroy_context(struct window *win, glcontext_t ctx); diff --git a/src/video/gl_backend.c b/src/video/gl_backend.c index 8b5e595c..834b7b56 100644 --- a/src/video/gl_backend.c +++ b/src/video/gl_backend.c @@ -4,6 +4,7 @@ #include "core/assert.h" #include "core/profiler.h" #include "core/string.h" +#include "sys/thread.h" #include "ui/nuklear.h" #include "ui/window.h" #include "video/render_backend.h" @@ -61,18 +62,30 @@ struct texture { }; struct render_backend { - struct window *window; - + struct window *win; glcontext_t ctx; - /* resources */ - struct framebuffer framebuffers[MAX_FRAMEBUFFERS]; - struct texture textures[MAX_TEXTURES]; - struct texture white; - + /* default assets created during intitialization */ + GLuint white_texture; struct shader_program ta_programs[ATTR_COUNT]; struct shader_program ui_program; + /* note, in this backend framebuffer_handle_t and texture_handle_t are the + OpenGL object handles, not indexes into these arrays. this lets OpenGL + handle generating unique IDs across multiple contexts, with no additional + synchronization on our part. however, to delete an object a reverse lookup + must be performed to match the handle to an index in these arrays + + note note, due to this dumbed down design, textures can be shared across + multiple backends for rendering purposes, but can only be deleted on the + backend that created them + + TODO the textures / framebuffers arrays exist purely for cleanup purposes, + it'd be nice to replace with a hashtable to avoid O(n) reverse lookup */ + struct texture textures[MAX_TEXTURES]; + struct framebuffer framebuffers[MAX_FRAMEBUFFERS]; + + /* surface render state */ GLuint ta_vao; GLuint ta_vbo; GLuint ui_vao; @@ -80,10 +93,6 @@ struct render_backend { GLuint ui_ibo; int ui_use_ibo; - /* begin_surfaces / draw_surfaces / end_surfaces uniform state */ - uint64_t uniform_token; - const float *uniform_mvp; - /* current gl state */ int scissor_test; int depth_mask; @@ -92,8 +101,9 @@ struct render_backend { enum blend_func src_blend; enum blend_func dst_blend; GLuint current_vao; - int vertex_attribs; struct shader_program *current_program; + uint64_t uniform_token; + const float *uniform_mvp; }; #include "video/ta.glsl" @@ -360,42 +370,7 @@ static int r_compile_program(struct render_backend *r, return 1; } -static void r_destroy_textures(struct render_backend *r) { - if (!r->ctx) { - return; - } - - glDeleteTextures(1, &r->white.texture); - - for (int i = 1; i < MAX_TEXTURES; i++) { - struct texture *tex = &r->textures[i]; - - if (!tex->texture) { - continue; - } - - glDeleteTextures(1, &tex->texture); - } -} - -static void r_create_textures(struct render_backend *r) { - uint8_t pixels[64 * 64 * 4]; - - memset(pixels, 0xff, sizeof(pixels)); - glGenTextures(1, &r->white.texture); - glBindTexture(GL_TEXTURE_2D, r->white.texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, - pixels); - glBindTexture(GL_TEXTURE_2D, 0); -} - static void r_destroy_shaders(struct render_backend *r) { - if (!r->ctx) { - return; - } - for (int i = 0; i < ATTR_COUNT; i++) { r_destroy_program(&r->ta_programs[i]); } @@ -456,11 +431,35 @@ static void r_create_shaders(struct render_backend *r) { } } -static void r_destroy_vertex_buffers(struct render_backend *r) { - if (!r->ctx) { - return; - } +static void r_destroy_textures(struct render_backend *r) { + glDeleteTextures(1, &r->white_texture); + for (int i = 0; i < MAX_TEXTURES; i++) { + struct texture *tex = &r->textures[i]; + + if (!tex->texture) { + continue; + } + + glDeleteTextures(1, &tex->texture); + } +} + +static void r_create_textures(struct render_backend *r) { + /* create default all white texture */ + uint8_t pixels[64 * 64 * 4]; + memset(pixels, 0xff, sizeof(pixels)); + + glGenTextures(1, &r->white_texture); + glBindTexture(GL_TEXTURE_2D, r->white_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 64, 64, 0, GL_RGBA, GL_UNSIGNED_BYTE, + pixels); + glBindTexture(GL_TEXTURE_2D, 0); +} + +static void r_destroy_vertex_arrays(struct render_backend *r) { glDeleteBuffers(1, &r->ui_ibo); glDeleteBuffers(1, &r->ui_vbo); glDeleteVertexArrays(1, &r->ui_vao); @@ -469,7 +468,7 @@ static void r_destroy_vertex_buffers(struct render_backend *r) { glDeleteVertexArrays(1, &r->ta_vao); } -static void r_create_vertex_buffers(struct render_backend *r) { +static void r_create_vertex_arrays(struct render_backend *r) { /* ui vao */ { glGenVertexArrays(1, &r->ui_vao); @@ -587,8 +586,7 @@ void r_draw_surface(struct render_backend *r, const struct surface *surf) { } if (surf->texture) { - struct texture *tex = &r->textures[surf->texture]; - r_bind_texture(r, MAP_DIFFUSE, tex->texture); + r_bind_texture(r, MAP_DIFFUSE, surf->texture); } glDrawArrays(GL_TRIANGLE_STRIP, surf->first_vert, surf->num_verts); @@ -621,14 +619,10 @@ void r_draw_surface2(struct render_backend *r, const struct surface2 *surf) { r_set_blend_func(r, surf->src_blend, surf->dst_blend); - if (surf->framebuffer) { - struct framebuffer *fb = &r->framebuffers[surf->framebuffer]; - r_bind_texture(r, MAP_DIFFUSE, fb->color_component); - } else if (surf->texture) { - struct texture *tex = &r->textures[surf->texture]; - r_bind_texture(r, MAP_DIFFUSE, tex->texture); + if (surf->texture) { + r_bind_texture(r, MAP_DIFFUSE, surf->texture); } else { - r_bind_texture(r, MAP_DIFFUSE, r->white.texture); + r_bind_texture(r, MAP_DIFFUSE, r->white_texture); } if (r->ui_use_ibo) { @@ -665,13 +659,13 @@ void r_end_ortho(struct render_backend *r) { void r_begin_ortho(struct render_backend *r) { float ortho[16]; - ortho[0] = 2.0f / (float)r->window->width; + ortho[0] = 2.0f / (float)r->win->width; ortho[4] = 0.0f; ortho[8] = 0.0f; ortho[12] = -1.0f; ortho[1] = 0.0f; - ortho[5] = -2.0f / (float)r->window->height; + ortho[5] = -2.0f / (float)r->win->height; ortho[9] = 0.0f; ortho[13] = 1.0f; @@ -695,13 +689,13 @@ void r_begin_ortho(struct render_backend *r) { } void r_end_frame(struct render_backend *r) { - SDL_GL_SwapWindow(r->window->handle); + SDL_GL_SwapWindow(r->win->handle); } void r_begin_frame(struct render_backend *r) { r_set_depth_mask(r, 1); - glViewport(0, 0, r->window->width, r->window->height); + glViewport(0, 0, r->win->width, r->win->height); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } @@ -722,7 +716,17 @@ sync_handle_t r_sync(struct render_backend *r) { } void r_destroy_texture(struct render_backend *r, texture_handle_t handle) { - struct texture *tex = &r->textures[handle]; + /* lookup texture entry */ + int entry; + for (entry = 0; entry < MAX_TEXTURES; entry++) { + struct texture *tex = &r->textures[entry]; + if (tex->texture == handle) { + break; + } + } + CHECK_LT(entry, MAX_TEXTURES); + + struct texture *tex = &r->textures[entry]; glDeleteTextures(1, &tex->texture); tex->texture = 0; } @@ -733,15 +737,15 @@ texture_handle_t r_create_texture(struct render_backend *r, enum wrap_mode wrap_u, enum wrap_mode wrap_v, int mipmaps, int width, int height, const uint8_t *buffer) { - /* find next open texture handle */ - texture_handle_t handle; - for (handle = 1; handle < MAX_TEXTURES; handle++) { - struct texture *tex = &r->textures[handle]; + /* find next open texture entry */ + int entry; + for (entry = 0; entry < MAX_TEXTURES; entry++) { + struct texture *tex = &r->textures[entry]; if (!tex->texture) { break; } } - CHECK_LT(handle, MAX_TEXTURES); + CHECK_LT(entry, MAX_TEXTURES); GLuint internal_fmt; GLuint pixel_fmt; @@ -771,7 +775,7 @@ texture_handle_t r_create_texture(struct render_backend *r, break; } - struct texture *tex = &r->textures[handle]; + struct texture *tex = &r->textures[entry]; glGenTextures(1, &tex->texture); glBindTexture(GL_TEXTURE_2D, tex->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, @@ -788,12 +792,22 @@ texture_handle_t r_create_texture(struct render_backend *r, glBindTexture(GL_TEXTURE_2D, 0); - return handle; + return tex->texture; } void r_destroy_framebuffer(struct render_backend *r, framebuffer_handle_t handle) { - struct framebuffer *fb = &r->framebuffers[handle]; + /* lookup framebuffer entry */ + int entry; + for (entry = 0; entry < MAX_FRAMEBUFFERS; entry++) { + struct framebuffer *fb = &r->framebuffers[entry]; + if (fb->fbo == handle) { + break; + } + } + CHECK_LT(entry, MAX_FRAMEBUFFERS); + + struct framebuffer *fb = &r->framebuffers[entry]; glDeleteTextures(1, &fb->color_component); fb->color_component = 0; @@ -806,29 +820,28 @@ void r_destroy_framebuffer(struct render_backend *r, } void r_bind_framebuffer(struct render_backend *r, framebuffer_handle_t handle) { - struct framebuffer *fb = &r->framebuffers[handle]; - - glBindFramebuffer(GL_FRAMEBUFFER, fb->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, handle); } -framebuffer_handle_t r_create_framebuffer(struct render_backend *r) { +framebuffer_handle_t r_create_framebuffer(struct render_backend *r, + texture_handle_t *color_component) { /* find next open framebuffer handle */ - framebuffer_handle_t handle; - for (handle = 1; handle < MAX_FRAMEBUFFERS; handle++) { - struct framebuffer *fb = &r->framebuffers[handle]; + int entry; + for (entry = 0; entry < MAX_FRAMEBUFFERS; entry++) { + struct framebuffer *fb = &r->framebuffers[entry]; if (!fb->fbo) { break; } } - CHECK_LT(handle, MAX_FRAMEBUFFERS); + CHECK_LT(entry, MAX_FRAMEBUFFERS); - struct framebuffer *fb = &r->framebuffers[handle]; + struct framebuffer *fb = &r->framebuffers[entry]; /* create color component */ glGenTextures(1, &fb->color_component); glBindTexture(GL_TEXTURE_2D, fb->color_component); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r->window->width, r->window->height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r->win->width, r->win->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); @@ -836,8 +849,8 @@ framebuffer_handle_t r_create_framebuffer(struct render_backend *r) { /* create depth component */ glGenRenderbuffers(1, &fb->depth_component); glBindRenderbuffer(GL_RENDERBUFFER, fb->depth_component); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, r->window->width, - r->window->height); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, r->win->width, + r->win->height); glBindRenderbuffer(GL_RENDERBUFFER, 0); /* create fbo */ @@ -854,30 +867,58 @@ framebuffer_handle_t r_create_framebuffer(struct render_backend *r) { /* switch back to default framebuffer */ glBindFramebuffer(GL_FRAMEBUFFER, 0); - return handle; + *color_component = fb->color_component; + + return fb->fbo; +} + +static void r_check_one_per_thread() { + /* to keep things simple, don't allow more than one gl backend per thread. + this avoids providing interfaces to manage the current gl context */ + static _Thread_local int initialized; + CHECK_EQ(initialized, 0); + initialized = 1; } void r_destroy(struct render_backend *r) { - r_destroy_vertex_buffers(r); + r_destroy_vertex_arrays(r); r_destroy_shaders(r); r_destroy_textures(r); - win_gl_destroy_context(r->window, r->ctx); + win_gl_destroy_context(r->win, r->ctx); free(r); } -struct render_backend *r_create(struct window *window) { +struct render_backend *r_create_from(struct render_backend *from) { + r_check_one_per_thread(); + struct render_backend *r = calloc(1, sizeof(struct render_backend)); - r->window = window; - - /* setup gl context */ - r->ctx = win_gl_create_context(r->window); + r->win = from->win; + r->ctx = win_gl_create_context_from(r->win, from->ctx); r_create_textures(r); r_create_shaders(r); - r_create_vertex_buffers(r); + r_create_vertex_arrays(r); + + r_set_initial_state(r); + + return r; +} + +struct render_backend *r_create(struct window *win) { + r_check_one_per_thread(); + + struct render_backend *r = calloc(1, sizeof(struct render_backend)); + + r->win = win; + r->ctx = win_gl_create_context(win); + + r_create_textures(r); + r_create_shaders(r); + r_create_vertex_arrays(r); + r_set_initial_state(r); return r; diff --git a/src/video/render_backend.h b/src/video/render_backend.h index aa49d176..954741b3 100644 --- a/src/video/render_backend.h +++ b/src/video/render_backend.h @@ -5,8 +5,8 @@ struct window; -typedef int framebuffer_handle_t; -typedef int texture_handle_t; +typedef unsigned framebuffer_handle_t; +typedef unsigned texture_handle_t; typedef void *sync_handle_t; enum pxl_format { @@ -114,7 +114,6 @@ struct vertex2 { struct surface2 { enum prim_type prim_type; - framebuffer_handle_t framebuffer; texture_handle_t texture; enum blend_func src_blend; @@ -130,9 +129,11 @@ struct surface2 { struct render_backend; struct render_backend *r_create(struct window *window); -void r_destroy(struct render_backend *r); +struct render_backend *r_create_from(struct render_backend *other); +void r_destroy(struct render_backend *rc); -framebuffer_handle_t r_create_framebuffer(struct render_backend *r); +framebuffer_handle_t r_create_framebuffer(struct render_backend *r, + texture_handle_t *color_componet); void r_bind_framebuffer(struct render_backend *r, framebuffer_handle_t handle); void r_destroy_framebuffer(struct render_backend *r, framebuffer_handle_t handle);