From a0712e25e188d16744df8e66de72d67ab7518cda Mon Sep 17 00:00:00 2001 From: Jean-Philippe Andre Date: Fri, 19 Sep 2014 17:32:26 +0900 Subject: [PATCH] Evas GL: Add support for pbuffer surfaces Supports only EGL for now :( These pbuffer surfaces can be used to create dummy surfaces for make_current and render threads. @feature --- src/lib/evas/Evas_GL.h | 40 ++++- src/lib/evas/canvas/evas_gl.c | 56 +++++++ src/lib/evas/include/evas_private.h | 1 + src/modules/evas/engines/gl_common/evas_gl_core.c | 175 ++++++++++++++++++++- src/modules/evas/engines/gl_common/evas_gl_core.h | 1 + .../evas/engines/gl_common/evas_gl_core_private.h | 13 ++ src/modules/evas/engines/gl_generic/evas_engine.c | 10 ++ src/modules/evas/engines/gl_x11/evas_engine.c | 122 +++++++++++++- .../evas/engines/software_generic/evas_engine.c | 1 + 9 files changed, 412 insertions(+), 7 deletions(-) diff --git a/src/lib/evas/Evas_GL.h b/src/lib/evas/Evas_GL.h index c061770..a2da465 100644 --- a/src/lib/evas/Evas_GL.h +++ b/src/lib/evas/Evas_GL.h @@ -391,8 +391,9 @@ typedef void *EvasGLImage; */ typedef enum _Evas_GL_Color_Format { - EVAS_GL_RGB_888 = 0, - EVAS_GL_RGBA_8888 = 1 + EVAS_GL_RGB_888 = 0, /**< Opaque RGB surface */ + EVAS_GL_RGBA_8888 = 1, /**< RGBA surface with alpha */ + EVAS_GL_NO_FBO = 2 /**< Special value for creating PBuffer surfaces without any attached buffer. @see evas_gl_pbuffer_surface_create. @since 1.12*/ } Evas_GL_Color_Format; /** @@ -529,10 +530,45 @@ EAPI void evas_gl_config_free (Evas_GL_Config *cfg) E EAPI Evas_GL_Surface *evas_gl_surface_create (Evas_GL *evas_gl, Evas_GL_Config *cfg, int w, int h) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1,2); /** + * @brief Create a pixel buffer surface + * + * @param[in] evas_gl The given Evas_GL object + * @param[in] cfg Pixel format and configuration of the pixel buffer surface + * @param[in] w Requested width of the buffer + * @param[in] h Requested height of the buffer + * @param[in] attrib_list An optional list of attribute-value pairs terminated by attribute 0, can be @c NULL. Currently, no attributes are supported. + * + * @return The created GL surface object, + * otherwise @c NULL on failure + * + * The surface must be released with @ref evas_gl_surface_destroy. + * + * If the color format in @a cfg is @ref EVAS_GL_RGB_888 or @ref EVAS_GL_RGBA_8888, + * then Evas will automatically generate a framebuffer attached to this PBuffer. + * Its properties can be queried using @ref evas_gl_native_surface_get. + * If you want to attach an FBO yourself, or create a PBuffer surface only, + * please use the color format @ref EVAS_GL_NO_FBO. + * + * Creating a 1x1 PBuffer surface can be useful in order to call + * @ref evas_gl_make_current() from another thread. + * + * @note The attribute list can be terminated by EVAS_GL_NONE or 0. + * As of now, no special attributes are supported yet. Also, only EGL + * is supported at the moment of writing. + * + * @see evas_gl_surface_destroy + * + * @since 1.12 + */ +EAPI Evas_GL_Surface *evas_gl_pbuffer_surface_create(Evas_GL *evas_gl, Evas_GL_Config *cfg, int w, int h, const int *attrib_list) EINA_WARN_UNUSED_RESULT EINA_ARG_NONNULL(1,2); + +/** * @brief Destroys an Evas GL Surface. * * @param[in] evas_gl The given Evas_GL object * @param[in] surf The given GL surface object + * + * @note This function can also destroy pbuffer surfaces. */ EAPI void evas_gl_surface_destroy (Evas_GL *evas_gl, Evas_GL_Surface *surf) EINA_ARG_NONNULL(1,2); diff --git a/src/lib/evas/canvas/evas_gl.c b/src/lib/evas/canvas/evas_gl.c index 3b7286c..456d1be 100644 --- a/src/lib/evas/canvas/evas_gl.c +++ b/src/lib/evas/canvas/evas_gl.c @@ -241,6 +241,62 @@ evas_gl_surface_create(Evas_GL *evas_gl, Evas_GL_Config *config, int width, int return surf; } +EAPI Evas_GL_Surface * +evas_gl_pbuffer_surface_create(Evas_GL *evas_gl, Evas_GL_Config *cfg, + int w, int h, const int *attrib_list) +{ + Evas_GL_Surface *surf; + + // Magic + MAGIC_CHECK(evas_gl, Evas_GL, MAGIC_EVAS_GL); + return NULL; + MAGIC_CHECK_END(); + + if (!cfg) + { + ERR("Invalid Config Pointer!"); + _evas_gl_internal_error_set(evas_gl, EVAS_GL_BAD_CONFIG); + return NULL; + } + + if ((w <= 0) || (h <= 0)) + { + ERR("Invalid surface dimensions: %d, %d", w, h); + _evas_gl_internal_error_set(evas_gl, EVAS_GL_BAD_PARAMETER); + return NULL; + } + + if (!evas_gl->evas->engine.func->gl_pbuffer_surface_create) + { + ERR("Engine does not support PBuffer!"); + _evas_gl_internal_error_set(evas_gl, EVAS_GL_NOT_INITIALIZED); + return NULL; + } + + surf = calloc(1, sizeof(Evas_GL_Surface)); + if (!surf) + { + _evas_gl_internal_error_set(evas_gl, EVAS_GL_BAD_ALLOC); + return NULL; + } + + surf->data = evas_gl->evas->engine.func->gl_pbuffer_surface_create + (evas_gl->evas->engine.data.output, cfg, w, h, attrib_list); + if (!surf->data) + { + ERR("Engine failed to create a PBuffer!"); + free(surf); + return NULL; + } + + // Keep track of the surface creations + LKL(evas_gl->lck); + evas_gl->surfaces = eina_list_prepend(evas_gl->surfaces, surf); + LKU(evas_gl->lck); + + return surf; +} + EAPI void evas_gl_surface_destroy(Evas_GL *evas_gl, Evas_GL_Surface *surf) { diff --git a/src/lib/evas/include/evas_private.h b/src/lib/evas/include/evas_private.h index 1243033..573cca9 100644 --- a/src/lib/evas/include/evas_private.h +++ b/src/lib/evas/include/evas_private.h @@ -1231,6 +1231,7 @@ struct _Evas_Func /* EFL-GL Glue Layer */ void *(*gl_surface_create) (void *data, void *config, int w, int h); + void *(*gl_pbuffer_surface_create) (void *data, void *config, int w, int h, int const *attrib_list); int (*gl_surface_destroy) (void *data, void *surface); void *(*gl_context_create) (void *data, void *share_context); int (*gl_context_destroy) (void *data, void *context); diff --git a/src/modules/evas/engines/gl_common/evas_gl_core.c b/src/modules/evas/engines/gl_common/evas_gl_core.c index 4fe2685..1b18ae1 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core.c +++ b/src/modules/evas/engines/gl_common/evas_gl_core.c @@ -1588,6 +1588,117 @@ error: return NULL; } +void * +evgl_pbuffer_surface_create(void *eng_data, Evas_GL_Config *cfg, + int w, int h, const int *attrib_list) +{ + EVGL_Surface *sfc = NULL; + void *pbuffer; + + // Check if engine is valid + if (!evgl_engine) + { + ERR("Invalid EVGL Engine!"); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_ACCESS); + return NULL; + } + + if (!cfg) + { + ERR("Invalid Config!"); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_CONFIG); + return NULL; + } + + if (!evgl_engine->funcs->pbuffer_surface_create) + { + ERR("Engine can not create PBuffers"); + evas_gl_common_error_set(eng_data, EVAS_GL_NOT_INITIALIZED); + return NULL; + } + + // Allocate surface structure + sfc = calloc(1, sizeof(EVGL_Surface)); + if (!sfc) + { + ERR("Surface allocation failed."); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_ALLOC); + goto error; + } + + sfc->w = w; + sfc->h = h; + sfc->pbuffer.color_fmt = cfg->color_format; + sfc->pbuffer.is_pbuffer = EINA_TRUE; + + // Set the context current with resource context/surface + if (!_internal_resource_make_current(eng_data, NULL)) + { + ERR("Error doing an internal resource make current"); + goto error; + } + + // If the surface is defined as RGB or RGBA, then create an FBO + if (sfc->pbuffer.color_fmt != EVAS_GL_NO_FBO) + { + // Set the internal config value + if (!_internal_config_set(sfc, cfg)) + { + ERR("Unsupported Format!"); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_CONFIG); + goto error; + } + + // Create internal buffers + if (!_surface_buffers_create(sfc)) + { + ERR("Unable Create Specificed Surfaces."); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_ALLOC); + goto error; + }; + + // Allocate resources for fallback unless the flag is on + if (!evgl_engine->direct_mem_opt) + { + if (!_surface_buffers_allocate(eng_data, sfc, sfc->w, sfc->h, 0)) + { + ERR("Unable Create Allocate Memory for Surface."); + evas_gl_common_error_set(eng_data, EVAS_GL_BAD_ALLOC); + goto error; + } + } + } + + // Not calling make_current + + pbuffer = evgl_engine->funcs->pbuffer_surface_create + (eng_data, sfc, attrib_list); + + if (!pbuffer) + { + ERR("Engine failed to create a PBuffer"); + goto error; + } + + sfc->pbuffer.native_surface = pbuffer; + + if (!evgl_engine->funcs->make_current(eng_data, NULL, NULL, 0)) + { + ERR("Error doing make_current(NULL, NULL)."); + goto error; + } + + // Keep track of all the created surfaces + LKL(evgl_engine->resource_lock); + evgl_engine->surfaces = eina_list_prepend(evgl_engine->surfaces, sfc); + LKU(evgl_engine->resource_lock); + + return sfc; + +error: + free(sfc); + return NULL; +} int evgl_surface_destroy(void *eng_data, EVGL_Surface *sfc) @@ -1624,11 +1735,14 @@ evgl_surface_destroy(void *eng_data, EVGL_Surface *sfc) evgl_make_current(eng_data, NULL, NULL); } - // Set the context current with resource context/surface - if (!_internal_resource_make_current(eng_data, NULL)) + if (!sfc->pbuffer.native_surface) { - ERR("Error doing an internal resource make current"); - return 0; + // Set the context current with resource context/surface + if (!_internal_resource_make_current(eng_data, NULL)) + { + ERR("Error doing an internal resource make current"); + return 0; + } } // Destroy created buffers @@ -1638,6 +1752,24 @@ evgl_surface_destroy(void *eng_data, EVGL_Surface *sfc) return 0; } + // Destroy PBuffer surfaces + if (sfc->pbuffer.native_surface) + { + int ret; + + if (sfc->pbuffer.fbo) + glDeleteFramebuffers(1, &sfc->pbuffer.fbo); + + ret = evgl_engine->funcs->surface_destroy(eng_data, sfc->pbuffer.native_surface); + LKL(evgl_engine->resource_lock); + evgl_engine->surfaces = eina_list_remove(evgl_engine->surfaces, sfc); + LKU(evgl_engine->resource_lock); + free(sfc); + + if (!ret) ERR("Engine failed to destroy a PBuffer."); + return ret; + } + if (!evgl_engine->funcs->make_current(eng_data, NULL, NULL, 0)) { ERR("Error doing make_current(NULL, NULL)."); @@ -1879,6 +2011,12 @@ evgl_make_current(void *eng_data, EVGL_Surface *sfc, EVGL_Context *ctx) glBindFramebuffer(GL_FRAMEBUFFER, 0); ctx->current_fbo = 0; } + else if (ctx->current_sfc && (ctx->current_sfc->pbuffer.is_pbuffer)) + { + // Using the same context, we were rendering on a pbuffer + glBindFramebuffer(GL_FRAMEBUFFER, 0); + ctx->current_fbo = 0; + } if (ctx->current_fbo == 0) { @@ -1895,6 +2033,35 @@ evgl_make_current(void *eng_data, EVGL_Surface *sfc, EVGL_Context *ctx) rsc->direct.rendered = 1; } + else if (sfc->pbuffer.native_surface) + { + // Call end tiling + if (rsc->direct.partial.enabled) + evgl_direct_partial_render_end(); + + if (sfc->color_buf) + { + if (!sfc->pbuffer.fbo) + { + glGenFramebuffers(1, &sfc->pbuffer.fbo); + GLERRLOG(); + } + if (!_surface_buffers_fbo_set(sfc, sfc->pbuffer.fbo)) + ERR("Could not detach current FBO"); + } + + evgl_engine->funcs->make_current(eng_data, sfc->pbuffer.native_surface, + ctx->context, EINA_TRUE); + + // Bind to the previously bound buffer (may be 0) + if (ctx->current_fbo) + { + glBindFramebuffer(GL_FRAMEBUFFER, ctx->current_fbo); + GLERRLOG(); + } + + rsc->direct.rendered = 0; + } else { // Attach fbo and the buffers diff --git a/src/modules/evas/engines/gl_common/evas_gl_core.h b/src/modules/evas/engines/gl_common/evas_gl_core.h index 24a2d5e..f011724 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core.h +++ b/src/modules/evas/engines/gl_common/evas_gl_core.h @@ -24,6 +24,7 @@ typedef void (*EVGL_Engine_Call)(void *eng_data); EVGL_Engine *evgl_engine_init(void *eng_data, const EVGL_Interface *efunc); void *evgl_surface_create(void *eng_data, Evas_GL_Config *cfg, int w, int h); +void *evgl_pbuffer_surface_create(void *eng_data, Evas_GL_Config *cfg, int w, int h, const int *attrib_list); int evgl_surface_destroy(void *eng_data, EVGL_Surface *sfc); void *evgl_context_create(void *eng_data, EVGL_Context *share_ctx); int evgl_context_destroy(void *eng_data, EVGL_Context *ctx); diff --git a/src/modules/evas/engines/gl_common/evas_gl_core_private.h b/src/modules/evas/engines/gl_common/evas_gl_core_private.h index fd951fd..7325bde 100644 --- a/src/modules/evas/engines/gl_common/evas_gl_core_private.h +++ b/src/modules/evas/engines/gl_common/evas_gl_core_private.h @@ -64,6 +64,9 @@ struct _EVGL_Interface // Returns the current rotation angle of evas int (*rotation_angle_get)(void *data); + + // Create a pbuffer surface + void *(*pbuffer_surface_create)(void *data, EVGL_Surface *evgl_sfc, const int *attrib_list); }; struct _EVGL_Surface @@ -111,6 +114,16 @@ struct _EVGL_Surface int buffer_mem[4]; //-------------------------// + // Related to PBuffer Surface + struct { + EVGLNative_Surface native_surface; + Evas_GL_Color_Format color_fmt; + GLuint fbo; + Eina_Bool is_pbuffer : 1; + } pbuffer; + + + //-------------------------// EVGL_Context *current_ctx; }; diff --git a/src/modules/evas/engines/gl_generic/evas_engine.c b/src/modules/evas/engines/gl_generic/evas_engine.c index b717984..caba606 100644 --- a/src/modules/evas/engines/gl_generic/evas_engine.c +++ b/src/modules/evas/engines/gl_generic/evas_engine.c @@ -1113,6 +1113,15 @@ eng_gl_surface_create(void *data, void *config, int w, int h) return evgl_surface_create(data, cfg, w, h); } +static void * +eng_gl_pbuffer_surface_create(void *data, void *config, int w, int h, const int *attrib_list) +{ + Evas_GL_Config *cfg = (Evas_GL_Config *)config; + + EVGLINIT(data, NULL); + return evgl_pbuffer_surface_create(data, cfg, w, h, attrib_list); +} + static int eng_gl_surface_destroy(void *data, void *surface) { @@ -1897,6 +1906,7 @@ module_open(Evas_Module *em) ORD(image_cache_get); ORD(gl_surface_create); + ORD(gl_pbuffer_surface_create); ORD(gl_surface_destroy); ORD(gl_context_create); ORD(gl_context_destroy); diff --git a/src/modules/evas/engines/gl_x11/evas_engine.c b/src/modules/evas/engines/gl_x11/evas_engine.c index 496a25e..2b0153a 100644 --- a/src/modules/evas/engines/gl_x11/evas_engine.c +++ b/src/modules/evas/engines/gl_x11/evas_engine.c @@ -607,6 +607,125 @@ evgl_eng_rotation_angle_get(void *data) } } +static void * +evgl_eng_pbuffer_surface_create(void *data, EVGL_Surface *sfc, + const int *attrib_list) +{ + Render_Engine *re = (Render_Engine *)data; + + // TODO: Add support for surfaceless pbuffers (EGL_NO_TEXTURE) + // TODO: Add support for EGL_MIPMAP_TEXTURE??? (GLX doesn't support them) + +#ifdef GL_GLES + int config_attrs[20]; + int surface_attrs[20]; + EGLSurface egl_sfc; + EGLConfig egl_cfg; + int num_config, i = 0; + + if (attrib_list) + WRN("This PBuffer implementation does not support extra attributes yet"); + +#if 0 + // Choose framebuffer configuration + // DISABLED FOR NOW + if (sfc->pbuffer.color_fmt != EVAS_GL_NO_FBO) + { + config_attrs[i++] = EGL_RED_SIZE; + config_attrs[i++] = 1; + config_attrs[i++] = EGL_GREEN_SIZE; + config_attrs[i++] = 1; + config_attrs[i++] = EGL_BLUE_SIZE; + config_attrs[i++] = 1; + + if (sfc->pbuffer.color_fmt == EVAS_GL_RGBA_8888) + { + config_attrs[i++] = EGL_ALPHA_SIZE; + config_attrs[i++] = 1; + //config_attrs[i++] = EGL_BIND_TO_TEXTURE_RGBA; + //config_attrs[i++] = EGL_TRUE; + } + else + { + //config_attrs[i++] = EGL_BIND_TO_TEXTURE_RGB; + //config_attrs[i++] = EGL_TRUE; + } + } + + if (sfc->depth_fmt || sfc->depth_stencil_fmt) + { + config_attrs[i++] = EGL_DEPTH_SIZE; + config_attrs[i++] = 1; + } + + if (sfc->stencil_fmt || sfc->depth_stencil_fmt) + { + config_attrs[i++] = EGL_STENCIL_SIZE; + config_attrs[i++] = 1; + } + + config_attrs[i++] = EGL_RENDERABLE_TYPE; + config_attrs[i++] = EGL_OPENGL_ES2_BIT; + config_attrs[i++] = EGL_SURFACE_TYPE; + config_attrs[i++] = EGL_PBUFFER_BIT; + config_attrs[i++] = EGL_NONE; +#else + // It looks like eglMakeCurrent might fail if we use a different config from + // the actual display surface. This is weird. + i = 0; + config_attrs[i++] = EGL_CONFIG_ID; + config_attrs[i++] = 0; + config_attrs[i++] = EGL_NONE; + eglQueryContext(re->win->egl_disp, re->win->egl_context[0], EGL_CONFIG_ID, &config_attrs[1]); +#endif + + if (!eglChooseConfig(re->win->egl_disp, config_attrs, &egl_cfg, 1, &num_config) + || (num_config < 1)) + { + int err = eglGetError(); + _evgl_error_set(err - EGL_SUCCESS); + ERR("eglChooseConfig failed with error %x", err); + return NULL; + } + + // Now, choose the config for the PBuffer + i = 0; + surface_attrs[i++] = EGL_WIDTH; + surface_attrs[i++] = sfc->w; + surface_attrs[i++] = EGL_HEIGHT; + surface_attrs[i++] = sfc->h; +#if 0 + // Adding these parameters will trigger EGL_BAD_ATTRIBUTE because + // the config also requires EGL_BIND_TO_TEXTURE_RGB[A]. But some drivers + // don't support those configs (eg. nvidia) + surface_attrs[i++] = EGL_TEXTURE_FORMAT; + if (sfc->pbuffer.color_fmt == EVAS_GL_RGB_888) + surface_attrs[i++] = EGL_TEXTURE_RGB; + else + surface_attrs[i++] = EGL_TEXTURE_RGBA; + surface_attrs[i++] = EGL_TEXTURE_TARGET; + surface_attrs[i++] = EGL_TEXTURE_2D; + surface_attrs[i++] = EGL_MIPMAP_TEXTURE; + surface_attrs[i++] = EINA_TRUE; +#endif + surface_attrs[i++] = EGL_NONE; + + egl_sfc = eglCreatePbufferSurface(re->win->egl_disp, egl_cfg, surface_attrs); + if (!egl_sfc) + { + int err = eglGetError(); + _evgl_error_set(err - EGL_SUCCESS); + ERR("eglCreatePbufferSurface failed with error %x", err); + return NULL; + } + + return egl_sfc; +#else + ERR("PBuffer support is not implemented yet!"); + return NULL; +#endif +} + static const EVGL_Interface evgl_funcs = { evgl_eng_display_get, @@ -620,7 +739,8 @@ static const EVGL_Interface evgl_funcs = evgl_eng_make_current, evgl_eng_proc_address_get, evgl_eng_string_get, - evgl_eng_rotation_angle_get + evgl_eng_rotation_angle_get, + evgl_eng_pbuffer_surface_create }; //----------------------------------------------------------// diff --git a/src/modules/evas/engines/software_generic/evas_engine.c b/src/modules/evas/engines/software_generic/evas_engine.c index 8ee3d90..3417751 100644 --- a/src/modules/evas/engines/software_generic/evas_engine.c +++ b/src/modules/evas/engines/software_generic/evas_engine.c @@ -3062,6 +3062,7 @@ static Evas_Func func = eng_font_text_props_info_create, eng_font_right_inset_get, NULL, // need software mesa for gl rendering <- gl_surface_create + NULL, // need software mesa for gl rendering <- gl_pbuffer_surface_create NULL, // need software mesa for gl rendering <- gl_surface_destroy NULL, // need software mesa for gl rendering <- gl_context_create NULL, // need software mesa for gl rendering <- gl_context_destroy -- 2.7.4