X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fstate_tracker%2Fst_manager.c;h=4ef5429c58baf752f75b9deb109a6ad8aa7d577b;hb=f986741a91b80091b510752b707a82f5b19440ee;hp=834bcc9f8c2f6860247b838543a9407f5d8f0430;hpb=bbc29393d3beaf6344c7188547b4ff61b63946ae;p=mesa.git diff --git a/src/mesa/state_tracker/st_manager.c b/src/mesa/state_tracker/st_manager.c index 834bcc9f8c2..4ef5429c58b 100644 --- a/src/mesa/state_tracker/st_manager.c +++ b/src/mesa/state_tracker/st_manager.c @@ -45,15 +45,18 @@ #include "st_debug.h" #include "st_extensions.h" #include "st_format.h" +#include "st_cb_bitmap.h" #include "st_cb_fbo.h" #include "st_cb_flush.h" #include "st_manager.h" +#include "st_sampler_view.h" #include "state_tracker/st_gl_api.h" #include "pipe/p_context.h" #include "pipe/p_screen.h" #include "util/u_format.h" +#include "util/u_helpers.h" #include "util/u_pointer.h" #include "util/u_inlines.h" #include "util/u_atomic.h" @@ -67,7 +70,6 @@ struct st_manager_private mtx_t st_mutex; }; -static void st_manager_destroy(struct st_manager *); /** * Map an attachment to a buffer index. @@ -105,6 +107,7 @@ attachment_to_buffer_index(enum st_attachment_type statt) return index; } + /** * Map a buffer index to an attachment. */ @@ -140,6 +143,7 @@ buffer_index_to_attachment(gl_buffer_index index) return statt; } + /** * Make sure a context picks up the latest cached state of the * drawables it binds to. @@ -168,6 +172,27 @@ st_context_validate(struct st_context *st, } } + +void +st_set_ws_renderbuffer_surface(struct st_renderbuffer *strb, + struct pipe_surface *surf) +{ + pipe_surface_reference(&strb->surface_srgb, NULL); + pipe_surface_reference(&strb->surface_linear, NULL); + + if (util_format_is_srgb(surf->format)) + pipe_surface_reference(&strb->surface_srgb, surf); + else + pipe_surface_reference(&strb->surface_linear, surf); + + strb->surface = surf; /* just assign, don't ref */ + pipe_resource_reference(&strb->texture, surf->texture); + + strb->Base.Width = surf->width; + strb->Base.Height = surf->height; +} + + /** * Validate a framebuffer to make sure up-to-date pipe_textures are used. * The context is only used for creating pipe surfaces and for calling @@ -183,13 +208,15 @@ st_framebuffer_validate(struct st_framebuffer *stfb, struct pipe_resource *textures[ST_ATTACHMENT_COUNT]; uint width, height; unsigned i; - boolean changed = FALSE; + bool changed = false; int32_t new_stamp; new_stamp = p_atomic_read(&stfb->iface->stamp); if (stfb->iface_stamp == new_stamp) return; + memset(textures, 0, stfb->num_statts * sizeof(textures[0])); + /* validate the fb */ do { if (!stfb->iface->validate(&st->iface, stfb->iface, stfb->statts, @@ -227,20 +254,10 @@ st_framebuffer_validate(struct st_framebuffer *stfb, u_surface_default_template(&surf_tmpl, textures[i]); ps = st->pipe->create_surface(st->pipe, textures[i], &surf_tmpl); if (ps) { - struct pipe_surface **psurf = - util_format_is_srgb(ps->format) ? &strb->surface_srgb : - &strb->surface_linear; - - pipe_surface_reference(psurf, ps); - strb->surface = *psurf; - pipe_resource_reference(&strb->texture, ps->texture); - /* ownership transfered */ + st_set_ws_renderbuffer_surface(strb, ps); pipe_surface_reference(&ps, NULL); - changed = TRUE; - - strb->Base.Width = strb->surface->width; - strb->Base.Height = strb->surface->height; + changed = true; width = strb->Base.Width; height = strb->Base.Height; @@ -255,6 +272,7 @@ st_framebuffer_validate(struct st_framebuffer *stfb, } } + /** * Update the attachments to validate by looping the existing renderbuffers. */ @@ -280,17 +298,18 @@ st_framebuffer_update_attachments(struct st_framebuffer *stfb) stfb->stamp++; } + /** * Add a renderbuffer to the framebuffer. The framebuffer is one that * corresponds to a window and is not a user-created FBO. */ -static boolean +static bool st_framebuffer_add_renderbuffer(struct st_framebuffer *stfb, - gl_buffer_index idx) + gl_buffer_index idx, bool prefer_srgb) { struct gl_renderbuffer *rb; enum pipe_format format; - boolean sw; + bool sw; assert(_mesa_is_winsys_fbo(&stfb->Base)); @@ -301,30 +320,30 @@ st_framebuffer_add_renderbuffer(struct st_framebuffer *stfb, switch (idx) { case BUFFER_DEPTH: format = stfb->iface->visual->depth_stencil_format; - sw = FALSE; + sw = false; break; case BUFFER_ACCUM: format = stfb->iface->visual->accum_format; - sw = TRUE; + sw = true; break; default: format = stfb->iface->visual->color_format; - if (stfb->Base.Visual.sRGBCapable) + if (prefer_srgb) format = util_format_srgb(format); - sw = FALSE; + sw = false; break; } if (format == PIPE_FORMAT_NONE) - return FALSE; + return false; rb = st_new_renderbuffer_fb(format, stfb->iface->visual->samples, sw); if (!rb) - return FALSE; + return false; if (idx != BUFFER_DEPTH) { _mesa_attach_and_own_rb(&stfb->Base, idx, rb); - return TRUE; + return true; } bool rb_ownership_taken = false; @@ -340,9 +359,10 @@ st_framebuffer_add_renderbuffer(struct st_framebuffer *stfb, _mesa_attach_and_own_rb(&stfb->Base, BUFFER_STENCIL, rb); } - return TRUE; + return true; } + /** * Intialize a struct gl_config from a visual. */ @@ -354,6 +374,7 @@ st_visual_to_context_mode(const struct st_visual *visual, if (st_visual_have_buffers(visual, ST_ATTACHMENT_BACK_LEFT_MASK)) mode->doubleBufferMode = GL_TRUE; + if (st_visual_have_buffers(visual, ST_ATTACHMENT_FRONT_RIGHT_MASK | ST_ATTACHMENT_BACK_RIGHT_MASK)) mode->stereoMode = GL_TRUE; @@ -414,6 +435,7 @@ st_visual_to_context_mode(const struct st_visual *visual, } } + /** * Create a framebuffer from a manager interface. */ @@ -424,6 +446,7 @@ st_framebuffer_create(struct st_context *st, struct st_framebuffer *stfb; struct gl_config mode; gl_buffer_index idx; + bool prefer_srgb = false; if (!stfbi) return NULL; @@ -445,14 +468,15 @@ st_framebuffer_create(struct st_context *st, * format such that util_format_srgb(visual->color_format) can be supported * by the pipe driver. We still need to advertise the capability here. * - * For GLES, however, sRGB framebuffer write is controlled only by the - * capability of the framebuffer. There is GL_EXT_sRGB_write_control to - * give applications the control back, but sRGB write is still enabled by - * default. To avoid unexpected results, we should not advertise the - * capability. This could change when we add support for - * EGL_KHR_gl_colorspace. + * For GLES, however, sRGB framebuffer write is initially only controlled + * by the capability of the framebuffer, with GL_EXT_sRGB_write_control + * control is given back to the applications, but GL_FRAMEBUFFER_SRGB is + * still enabled by default since this is the behaviour when + * EXT_sRGB_write_control is not available. Since GL_EXT_sRGB_write_control + * brings GLES on par with desktop GLs EXT_framebuffer_sRGB, in mesa this + * is also expressed by using the same extension flag */ - if (_mesa_is_desktop_gl(st->ctx)) { + if (_mesa_has_EXT_framebuffer_sRGB(st->ctx)) { struct pipe_screen *screen = st->pipe->screen; const enum pipe_format srgb_format = util_format_srgb(stfbi->visual->color_format); @@ -461,9 +485,16 @@ st_framebuffer_create(struct st_context *st, st_pipe_format_to_mesa_format(srgb_format) != MESA_FORMAT_NONE && screen->is_format_supported(screen, srgb_format, PIPE_TEXTURE_2D, stfbi->visual->samples, + stfbi->visual->samples, (PIPE_BIND_DISPLAY_TARGET | - PIPE_BIND_RENDER_TARGET))) + PIPE_BIND_RENDER_TARGET))) { mode.sRGBCapable = GL_TRUE; + /* Since GL_FRAMEBUFFER_SRGB is enabled by default on GLES we must not + * create renderbuffers with an sRGB format derived from the + * visual->color_format, but we still want sRGB for desktop GL. + */ + prefer_srgb = _mesa_is_desktop_gl(st->ctx); + } } _mesa_initialize_window_framebuffer(&stfb->Base, &mode); @@ -474,13 +505,13 @@ st_framebuffer_create(struct st_context *st, /* add the color buffer */ idx = stfb->Base._ColorDrawBufferIndexes[0]; - if (!st_framebuffer_add_renderbuffer(stfb, idx)) { + if (!st_framebuffer_add_renderbuffer(stfb, idx, prefer_srgb)) { free(stfb); return NULL; } - st_framebuffer_add_renderbuffer(stfb, BUFFER_DEPTH); - st_framebuffer_add_renderbuffer(stfb, BUFFER_ACCUM); + st_framebuffer_add_renderbuffer(stfb, BUFFER_DEPTH, false); + st_framebuffer_add_renderbuffer(stfb, BUFFER_ACCUM, false); stfb->stamp = 0; st_framebuffer_update_attachments(stfb); @@ -488,6 +519,7 @@ st_framebuffer_create(struct st_context *st, return stfb; } + /** * Reference a framebuffer. */ @@ -514,7 +546,7 @@ st_framebuffer_iface_equal(const void *a, const void *b) } -static boolean +static bool st_framebuffer_iface_lookup(struct st_manager *smapi, const struct st_framebuffer_iface *stfbi) { @@ -533,7 +565,7 @@ st_framebuffer_iface_lookup(struct st_manager *smapi, } -static boolean +static bool st_framebuffer_iface_insert(struct st_manager *smapi, struct st_framebuffer_iface *stfbi) { @@ -560,7 +592,7 @@ st_framebuffer_iface_remove(struct st_manager *smapi, (struct st_manager_private *)smapi->st_manager_private; struct hash_entry *entry; - if (!smPriv || !smPriv->stfbi_ht); + if (!smPriv || !smPriv->stfbi_ht) return; mtx_lock(&smPriv->st_mutex); @@ -583,7 +615,7 @@ static void st_api_destroy_drawable(struct st_api *stapi, struct st_framebuffer_iface *stfbi) { - if (stfbi) + if (!stfbi) return; st_framebuffer_iface_remove(stfbi->state_manager, stfbi); @@ -621,6 +653,7 @@ st_framebuffers_purge(struct st_context *st) } } + static void st_context_flush(struct st_context_iface *stctxi, unsigned flags, struct pipe_fence_handle **fence) @@ -628,13 +661,16 @@ st_context_flush(struct st_context_iface *stctxi, unsigned flags, struct st_context *st = (struct st_context *) stctxi; unsigned pipe_flags = 0; - if (flags & ST_FLUSH_END_OF_FRAME) { + if (flags & ST_FLUSH_END_OF_FRAME) pipe_flags |= PIPE_FLUSH_END_OF_FRAME; - } + if (flags & ST_FLUSH_FENCE_FD) + pipe_flags |= PIPE_FLUSH_FENCE_FD; + FLUSH_VERTICES(st->ctx, 0); + FLUSH_CURRENT(st->ctx, 0); st_flush(st, fence, pipe_flags); - if ((flags & ST_FLUSH_WAIT) && fence) { + if ((flags & ST_FLUSH_WAIT) && fence && *fence) { st->pipe->screen->fence_finish(st->pipe->screen, NULL, *fence, PIPE_TIMEOUT_INFINITE); st->pipe->screen->fence_reference(st->pipe->screen, fence, NULL); @@ -642,13 +678,23 @@ st_context_flush(struct st_context_iface *stctxi, unsigned flags, if (flags & ST_FLUSH_FRONT) st_manager_flush_frontbuffer(st); + + /* DRI3 changes the framebuffer after SwapBuffers, but we need to invoke + * st_manager_validate_framebuffers to notice that. + * + * Set gfx_shaders_may_be_dirty to invoke st_validate_state in the next + * draw call, which will invoke st_manager_validate_framebuffers, but it + * won't dirty states if there is no change. + */ + if (flags & ST_FLUSH_END_OF_FRAME) + st->gfx_shaders_may_be_dirty = true; } -static boolean +static bool st_context_teximage(struct st_context_iface *stctxi, enum st_texture_type tex_type, int level, enum pipe_format pipe_format, - struct pipe_resource *tex, boolean mipmap) + struct pipe_resource *tex, bool mipmap) { struct st_context *st = (struct st_context *) stctxi; struct gl_context *ctx = st->ctx; @@ -684,7 +730,7 @@ st_context_teximage(struct st_context_iface *stctxi, stObj = st_texture_object(texObj); /* switch to surface based */ if (!stObj->surface_based) { - _mesa_clear_texture_object(ctx, texObj); + _mesa_clear_texture_object(ctx, texObj, NULL); stObj->surface_based = GL_TRUE; } @@ -722,6 +768,8 @@ st_context_teximage(struct st_context_iface *stctxi, width = height = depth = 0; } + pipe_resource_reference(&stObj->pt, tex); + st_texture_release_all_sampler_views(st, stObj); pipe_resource_reference(&stImage->pt, tex); stObj->surface_format = pipe_format; @@ -730,9 +778,10 @@ st_context_teximage(struct st_context_iface *stctxi, _mesa_dirty_texobj(ctx, texObj); _mesa_unlock_texture(ctx, texObj); - return TRUE; + return true; } + static void st_context_copy(struct st_context_iface *stctxi, struct st_context_iface *stsrci, unsigned mask) @@ -743,7 +792,8 @@ st_context_copy(struct st_context_iface *stctxi, _mesa_copy_context(src->ctx, st->ctx, mask); } -static boolean + +static bool st_context_share(struct st_context_iface *stctxi, struct st_context_iface *stsrci) { @@ -753,6 +803,7 @@ st_context_share(struct st_context_iface *stctxi, return _mesa_share_state(st->ctx, src->ctx); } + static void st_context_destroy(struct st_context_iface *stctxi) { @@ -760,14 +811,27 @@ st_context_destroy(struct st_context_iface *stctxi) st_destroy_context(st); } + static void st_start_thread(struct st_context_iface *stctxi) { struct st_context *st = (struct st_context *) stctxi; _mesa_glthread_init(st->ctx); + + /* Pin all driver threads to one L3 cache for optimal performance + * on AMD Zen. This is only done if glthread is enabled. + * + * If glthread is disabled, st_draw.c re-pins driver threads regularly + * based on the location of the app thread. + */ + struct glthread_state *glthread = st->ctx->GLThread; + if (glthread && st->pipe->set_context_param) { + util_pin_driver_threads_to_random_L3(st->pipe, &glthread->queue.threads[0]); + } } + static void st_thread_finish(struct st_context_iface *stctxi) { @@ -776,6 +840,21 @@ st_thread_finish(struct st_context_iface *stctxi) _mesa_glthread_finish(st->ctx); } + +static void +st_manager_destroy(struct st_manager *smapi) +{ + struct st_manager_private *smPriv = smapi->st_manager_private; + + if (smPriv && smPriv->stfbi_ht) { + _mesa_hash_table_destroy(smPriv->stfbi_ht, NULL); + mtx_destroy(&smPriv->st_mutex); + free(smPriv); + smapi->st_manager_private = NULL; + } +} + + static struct st_context_iface * st_api_create_context(struct st_api *stapi, struct st_manager *smapi, const struct st_context_attribs *attribs, @@ -785,6 +864,7 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi, struct st_context *shared_ctx = (struct st_context *) shared_stctxi; struct st_context *st; struct pipe_context *pipe; + struct gl_config* mode_ptr; struct gl_config mode; gl_api api; bool no_error = false; @@ -832,6 +912,14 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi, if (attribs->flags & ST_CONTEXT_FLAG_NO_ERROR) no_error = true; + if (attribs->flags & ST_CONTEXT_FLAG_LOW_PRIORITY) + ctx_flags |= PIPE_CONTEXT_LOW_PRIORITY; + else if (attribs->flags & ST_CONTEXT_FLAG_HIGH_PRIORITY) + ctx_flags |= PIPE_CONTEXT_HIGH_PRIORITY; + + if (attribs->flags & ST_CONTEXT_FLAG_RESET_NOTIFICATION_ENABLED) + ctx_flags |= PIPE_CONTEXT_LOSE_CONTEXT_ON_RESET; + pipe = smapi->screen->context_create(smapi->screen, NULL, ctx_flags); if (!pipe) { *error = ST_CONTEXT_ERROR_NO_MEMORY; @@ -839,7 +927,14 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi, } st_visual_to_context_mode(&attribs->visual, &mode); - st = st_create_context(api, pipe, &mode, shared_ctx, &attribs->options, no_error); + + if (attribs->visual.no_config) + mode_ptr = NULL; + else + mode_ptr = &mode; + + st = st_create_context(api, pipe, mode_ptr, shared_ctx, + &attribs->options, no_error); if (!st) { *error = ST_CONTEXT_ERROR_NO_MEMORY; pipe->destroy(pipe); @@ -870,6 +965,9 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi, st_install_device_reset_callback(st); } + if (attribs->flags & ST_CONTEXT_FLAG_RELEASE_NONE) + st->ctx->Const.ContextReleaseBehavior = GL_NONE; + /* need to perform version check */ if (attribs->major > 1 || attribs->minor > 0) { /* Is the actual version less than the requested version? @@ -900,15 +998,17 @@ st_api_create_context(struct st_api *stapi, struct st_manager *smapi, return &st->iface; } + static struct st_context_iface * st_api_get_current(struct st_api *stapi) { GET_CURRENT_CONTEXT(ctx); - struct st_context *st = (ctx) ? ctx->st : NULL; + struct st_context *st = ctx ? ctx->st : NULL; - return (st) ? &st->iface : NULL; + return st ? &st->iface : NULL; } + static struct st_framebuffer * st_framebuffer_reuse_or_create(struct st_context *st, struct gl_framebuffer *fb, @@ -917,7 +1017,7 @@ st_framebuffer_reuse_or_create(struct st_context *st, struct st_framebuffer *cur = NULL, *stfb = NULL; if (!stfbi) - return NULL; + return NULL; /* Check if there is already a framebuffer object for the specified * framebuffer interface in this context. If there is one, use it. @@ -952,16 +1052,15 @@ st_framebuffer_reuse_or_create(struct st_context *st, return stfb; } -static boolean + +static bool st_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi, struct st_framebuffer_iface *stdrawi, struct st_framebuffer_iface *streadi) { struct st_context *st = (struct st_context *) stctxi; struct st_framebuffer *stdraw, *stread; - boolean ret; - - _glapi_check_multithread(); + bool ret; if (st) { /* reuse or create the draw fb */ @@ -1004,17 +1103,31 @@ st_api_make_current(struct st_api *stapi, struct st_context_iface *stctxi, st_framebuffers_purge(st); } else { + GET_CURRENT_CONTEXT(ctx); + + if (ctx) { + /* Before releasing the context, release its associated + * winsys buffers first. Then purge the context's winsys buffers list + * to free the resources of any winsys buffers that no longer have + * an existing drawable. + */ + ret = _mesa_make_current(ctx, NULL, NULL); + st_framebuffers_purge(ctx->st); + } + ret = _mesa_make_current(NULL, NULL, NULL); } return ret; } + static void st_api_destroy(struct st_api *stapi) { } + /** * Flush the front buffer if the current context renders to the front buffer. */ @@ -1024,15 +1137,34 @@ st_manager_flush_frontbuffer(struct st_context *st) struct st_framebuffer *stfb = st_ws_framebuffer(st->ctx->DrawBuffer); struct st_renderbuffer *strb = NULL; - if (stfb) - strb = st_renderbuffer(stfb->Base.Attachment[BUFFER_FRONT_LEFT].Renderbuffer); - if (!strb) + if (!stfb) + return; + + /* If the context uses a doublebuffered visual, but the buffer is + * single-buffered, guess that it's a pbuffer, which doesn't need + * flushing. + */ + if (st->ctx->Visual.doubleBufferMode && + !stfb->Base.Visual.doubleBufferMode) return; - /* never a dummy fb */ - stfb->iface->flush_front(&st->iface, stfb->iface, ST_ATTACHMENT_FRONT_LEFT); + strb = st_renderbuffer(stfb->Base.Attachment[BUFFER_FRONT_LEFT]. + Renderbuffer); + + /* Do we have a front color buffer and has it been drawn to since last + * frontbuffer flush? + */ + if (strb && strb->defined) { + stfb->iface->flush_front(&st->iface, stfb->iface, + ST_ATTACHMENT_FRONT_LEFT); + strb->defined = GL_FALSE; + + /* Trigger an update of strb->defined on next draw */ + st->dirty |= ST_NEW_FB_STATE; + } } + /** * Re-validate the framebuffers. */ @@ -1050,11 +1182,33 @@ st_manager_validate_framebuffers(struct st_context *st) st_context_validate(st, stdraw, stread); } + +/** + * Flush any outstanding swapbuffers on the current draw framebuffer. + */ +void +st_manager_flush_swapbuffers(void) +{ + GET_CURRENT_CONTEXT(ctx); + struct st_context *st = (ctx) ? ctx->st : NULL; + struct st_framebuffer *stfb; + + if (!st) + return; + + stfb = st_ws_framebuffer(ctx->DrawBuffer); + if (!stfb || !stfb->iface->flush_swapbuffers) + return; + + stfb->iface->flush_swapbuffers(&st->iface, stfb->iface); +} + + /** * Add a color renderbuffer on demand. The FBO must correspond to a window, * not a user-created FBO. */ -boolean +bool st_manager_add_color_renderbuffer(struct st_context *st, struct gl_framebuffer *fb, gl_buffer_index idx) @@ -1063,12 +1217,12 @@ st_manager_add_color_renderbuffer(struct st_context *st, /* FBO */ if (!stfb) - return FALSE; + return false; assert(_mesa_is_winsys_fbo(fb)); if (stfb->Base.Attachment[idx].Renderbuffer) - return TRUE; + return true; switch (idx) { case BUFFER_FRONT_LEFT: @@ -1077,11 +1231,12 @@ st_manager_add_color_renderbuffer(struct st_context *st, case BUFFER_BACK_RIGHT: break; default: - return FALSE; + return false; } - if (!st_framebuffer_add_renderbuffer(stfb, idx)) - return FALSE; + if (!st_framebuffer_add_renderbuffer(stfb, idx, + stfb->Base.Visual.sRGBCapable)) + return false; st_framebuffer_update_attachments(stfb); @@ -1095,21 +1250,9 @@ st_manager_add_color_renderbuffer(struct st_context *st, st_invalidate_buffers(st); - return TRUE; + return true; } -static void -st_manager_destroy(struct st_manager *smapi) -{ - struct st_manager_private *smPriv = smapi->st_manager_private; - - if (smPriv && smPriv->stfbi_ht) { - _mesa_hash_table_destroy(smPriv->stfbi_ht, NULL); - mtx_destroy(&smPriv->st_mutex); - free(smPriv); - smapi->st_manager_private = NULL; - } -} static unsigned get_version(struct pipe_screen *screen, @@ -1127,11 +1270,12 @@ get_version(struct pipe_screen *screen, _mesa_init_extensions(&extensions); st_init_limits(screen, &consts, &extensions); - st_init_extensions(screen, &consts, &extensions, options, GL_TRUE); + st_init_extensions(screen, &consts, &extensions, options, api); return _mesa_get_version(&extensions, &consts, api); } + static void st_api_query_versions(struct st_api *stapi, struct st_manager *sm, struct st_config_options *options, @@ -1146,6 +1290,7 @@ st_api_query_versions(struct st_api *stapi, struct st_manager *sm, *gl_es2_version = get_version(sm->screen, options, API_OPENGLES2); } + static const struct st_api st_gl_api = { .name = "Mesa " PACKAGE_VERSION, .api = ST_API_OPENGL, @@ -1163,6 +1308,7 @@ static const struct st_api st_gl_api = { .destroy_drawable = st_api_destroy_drawable, }; + struct st_api * st_gl_api_create(void) {