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
This commit is contained in:
Jean-Philippe Andre 2014-09-19 17:32:26 +09:00
parent 6cd0aa17da
commit a0712e25e1
9 changed files with 412 additions and 7 deletions

View File

@ -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;
/**
@ -528,11 +529,46 @@ 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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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
@ -110,6 +113,16 @@ struct _EVGL_Surface
// 0. color 1. depth 2. stencil 3. depth_stencil
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;

View File

@ -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);

View File

@ -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
};
//----------------------------------------------------------//

View File

@ -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