From: Enlightenment S. <no-...@en...> - 2011-06-20 10:55:10
|
Log: Upon reviewing the elm_glview, I've realized a few issues and mistakes that i've made originally so I've made some changes/ updates to elm_glview 1.) ?\194?\160GL Resource Deletion in ELM_GLView In order to delete GL resources, the current approach simply registered a delete callback to the GLView object and handled resource deletion there. Unfortunately, using the delete callback did not guarantee the?\194?\160glview context to be current. ?\194?\160In order to guarantee that the current context was the glview context, the make_current call needs to be called explicitly. ?\194?\160Since we were hiding all the make current details in?\194?\160elm_glview, i've decided to add an API that registers a delete callback function.?\194?\160I know that this may seem redundant since there is already a delete callback that you can register with glview objects. Unfortunately, this is the only option that we have apart?\194?\160from exposing make_current, which is something that went again what we are trying to do with elm_glview. Since adding delete callback alone seemed a little out of place, i've taken the liberty to add other callback functions to make it seem consistent. void elm_glview_init_func_set(Evas_Object *obj, Elm_GLView_Func func); void elm_glview_del_func_set(Evas_Object *obj, Elm_GLView_Func func); void elm_glview_resize_func_set(Evas_Object *obj, Elm_GLView_Func func); resize callback can be controversial as well but I want to argue that adding this callback makes the render function a lot cleaner. To handle resize differently, the user in render function needs to manually compare and see if the size has changed, and then handle the cases. Doing all of this internally once makes the developers life a lot easier in my opinion. these callback functions do make the render function a lot cleaner. You can check out the updated test_glview.c or newly added test_glview_simple. 2.) Minor bug fixes/changes elm_glview_scale_policy_set() was supposed to be elm_glview_resize_policy_set() but it somehow evaded our reviews. That has been fixed. Also, in _glview_resize, after updating the surface, it was explicitly calling the render function. It is actually unnecessary here and calling it here will cause problems if resize gets called before everything else is setup properly. So that has been commented out. 3.) test_glview & test_glview_simple elementary_test case for glview has been updated to reflect the api changes. when you run the elmentary_test, you need to make sure that you set ELM_ENGINE=gl as glview currently only runs on gl backend. test_glview runs the gears example. For testing purposes I've included a simple glview test case that renders a triangle and changing background color. Author: raster Date: 2011-06-20 03:55:02 -0700 (Mon, 20 Jun 2011) New Revision: 60517 Trac: http://trac.enlightenment.org/e/changeset/60517 Added: trunk/elementary/src/bin/test_glview_simple.c Modified: trunk/elementary/src/bin/Makefile.am trunk/elementary/src/bin/test.c trunk/elementary/src/bin/test_glview.c trunk/elementary/src/lib/Elementary.h.in trunk/elementary/src/lib/elm_glview.c Modified: trunk/elementary/src/bin/Makefile.am =================================================================== --- trunk/elementary/src/bin/Makefile.am 2011-06-20 10:14:35 UTC (rev 60516) +++ trunk/elementary/src/bin/Makefile.am 2011-06-20 10:55:02 UTC (rev 60517) @@ -66,6 +66,7 @@ test_focus.c \ test_gengrid.c \ test_genlist.c \ +test_glview_simple.c \ test_glview.c \ test_grid.c \ test_hover.c \ Modified: trunk/elementary/src/bin/test.c =================================================================== --- trunk/elementary/src/bin/test.c 2011-06-20 10:14:35 UTC (rev 60516) +++ trunk/elementary/src/bin/test.c 2011-06-20 10:55:02 UTC (rev 60517) @@ -134,6 +134,7 @@ void test_store(void *data, Evas_Object *obj, void *event_info); void test_win_inline(void *data, Evas_Object *obj, void *event_info); void test_grid(void *data, Evas_Object *obj, void *event_info); +void test_glview_simple(void *data, Evas_Object *obj, void *event_info); void test_glview(void *data, Evas_Object *obj, void *event_info); void test_3d(void *data, Evas_Object *obj, void *event_info); #ifdef HAVE_EIO @@ -408,6 +409,7 @@ ADD_TEST("Store", test_store); ADD_TEST("Window Inline", test_win_inline); ADD_TEST("Grid", test_grid); + ADD_TEST("GLViewSimple", test_glview_simple); ADD_TEST("GLView", test_glview); ADD_TEST("3D", test_3d); #undef ADD_TEST Modified: trunk/elementary/src/bin/test_glview.c =================================================================== --- trunk/elementary/src/bin/test_glview.c 2011-06-20 10:14:35 UTC (rev 60516) +++ trunk/elementary/src/bin/test_glview.c 2011-06-20 10:55:02 UTC (rev 60517) @@ -47,6 +47,7 @@ }; static void gears_init(GLData *gld); +static void free_gear(Gear *gear); static void gears_reshape(GLData *gld, int width, int height); static void render_gears(GLData *gld); @@ -172,10 +173,19 @@ gl->glBufferData(GL_ARRAY_BUFFER, gear->count * 6 * 4, gear->vertices, GL_STATIC_DRAW); + return gear; } static void +free_gear(Gear *gear) +{ + free(gear->vertices); + free(gear); + gear = NULL; +} + +static void multiply(GLfloat *m, const GLfloat *n) { GLfloat tmp[16]; @@ -315,20 +325,21 @@ " rotated_normal = tmp.xyz;\n" "}\n"; -static const char fragment_shader[] = - //"precision mediump float;\n" + static const char fragment_shader[] = + "precision mediump float;\n" "uniform vec4 color;\n" "uniform vec3 light;\n" "varying vec3 rotated_normal;\n" "varying vec3 rotated_position;\n" "vec3 light_direction;\n" - "vec4 white = vec4(0.3, 0.3, 0.5, 1.0);\n" + "vec4 white = vec4(0.5, 0.5, 0.5, 1.0);\n" "void main()\n" "{\n" " light_direction = normalize(light - rotated_position);\n" " gl_FragColor = color + white * dot(light_direction, rotated_normal);\n" "}\n"; + static void gears_init(GLData *gld) { @@ -345,14 +356,14 @@ gl->glShaderSource(gld->vtx_shader, 1, &p, NULL); gl->glCompileShader(gld->vtx_shader); gl->glGetShaderInfoLog(gld->vtx_shader, sizeof msg, NULL, msg); - //printf("vertex shader info: %s\n", msg); + printf("vertex shader info: %s\n", msg); p = fragment_shader; gld->fgmt_shader = gl->glCreateShader(GL_FRAGMENT_SHADER); gl->glShaderSource(gld->fgmt_shader, 1, &p, NULL); gl->glCompileShader(gld->fgmt_shader); gl->glGetShaderInfoLog(gld->fgmt_shader, sizeof msg, NULL, msg); - //printf("fragment shader info: %s\n", msg); + printf("fragment shader info: %s\n", msg); gld->program = gl->glCreateProgram(); gl->glAttachShader(gld->program, gld->vtx_shader); @@ -362,7 +373,7 @@ gl->glLinkProgram(gld->program); gl->glGetProgramInfoLog(gld->program, sizeof msg, NULL, msg); - //printf("info: %s\n", msg); + printf("info: %s\n", msg); gl->glUseProgram(gld->program); gld->proj_location = gl->glGetUniformLocation(gld->program, "proj"); @@ -396,25 +407,62 @@ //-------------------------// static void -_draw_gl(Evas_Object *obj) +_init_gl(Evas_Object *obj) { - int w, h; - Evas_GL_API *gl = elm_glview_gl_api_get(obj); GLData *gld = evas_object_data_get(obj, "gld"); - if (!gld) return; - elm_glview_size_get(obj, &w, &h); + gears_init(gld); +} - if (!gld->initialized) +static void +_del_gl(Evas_Object *obj) +{ + GLData *gld = evas_object_data_get(obj, "gld"); + if (!gld) { - gears_init(gld); - gld->initialized = 1; + printf("Unable to get GLData. \n"); + return; } + Evas_GL_API *gl = gld->glapi; + gl->glDeleteShader(gld->vtx_shader); + gl->glDeleteShader(gld->fgmt_shader); + gl->glDeleteProgram(gld->program); + gl->glDeleteBuffers(1, &gld->gear1->vbo); + gl->glDeleteBuffers(1, &gld->gear2->vbo); + gl->glDeleteBuffers(1, &gld->gear3->vbo); + + free_gear(gld->gear1); + free_gear(gld->gear2); + free_gear(gld->gear3); + + evas_object_data_del((Evas_Object*)obj, "..gld"); + free(gld); +} + + +static void +_resize_gl(Evas_Object *obj) +{ + int w, h; + GLData *gld = evas_object_data_get(obj, "gld"); + + elm_glview_size_get(obj, &w, &h); + // GL Viewport stuff. you can avoid doing this if viewport is all the // same as last frame if you want gears_reshape(gld, w,h); +} + + +static void +_draw_gl(Evas_Object *obj) +{ + Evas_GL_API *gl = elm_glview_gl_api_get(obj); + GLData *gld = evas_object_data_get(obj, "gld"); + if (!gld) return; + render_gears(gld); gl->glFinish(); } @@ -434,29 +482,15 @@ static void -_del(void *data, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) +_del(void *data __UNUSED__, Evas *evas __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__) { - GLData *gld = evas_object_data_get(data, "gld"); - if (!gld) - { - printf("Unable to get GLData. \n"); - return; - } - Evas_GL_API *gl = gld->glapi; - - gl->glDeleteShader(gld->vtx_shader); - gl->glDeleteShader(gld->fgmt_shader); - gl->glDeleteProgram(gld->program); - evas_object_data_del((Evas_Object*)data, "..gld"); - free(gld); - Ecore_Animator *ani = evas_object_data_get(obj, "ani"); ecore_animator_del(ani); } static void -_key_down(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info) +_key_down(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info) { Evas_Event_Key_Down *ev; ev = (Evas_Event_Key_Down *)event_info; @@ -488,7 +522,7 @@ if ((strcmp(ev->keyname, "Escape") == 0) || (strcmp(ev->keyname, "Return") == 0)) { - _on_done(data, obj, event_info); + //_on_done(data, obj, event_info); return; } } @@ -533,16 +567,20 @@ Ecore_Animator *ani; GLData *gld = NULL; + // alloc a data struct to hold our relevant gl info in if (!(gld = calloc(1, sizeof(GLData)))) return; gldata_init(gld); + // new window - do the usual and give it a name, title and delete handler win = elm_win_add(NULL, "glview", ELM_WIN_BASIC); elm_win_title_set(win, "GLView"); elm_win_autodel_set(win, 1); + + // add a standard bg bg = elm_bg_add(win); + evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); elm_win_resize_object_add(win, bg); - evas_object_size_hint_weight_set(bg, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); evas_object_show(bg); bx = elm_box_add(win); @@ -550,38 +588,45 @@ elm_win_resize_object_add(win, bx); evas_object_show(bx); + // Add a GLView gl = elm_glview_add(win); evas_object_size_hint_align_set(gl, EVAS_HINT_FILL, EVAS_HINT_FILL); evas_object_size_hint_weight_set(gl, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); - elm_glview_mode_set(gl, ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH); -// elm_glview_scale_policy_set(gl, ELM_GLVIEW_RESIZE_POLICY_SCALE); -// elm_glview_size_set(gl, 256, 256); - elm_glview_scale_policy_set(gl, ELM_GLVIEW_RESIZE_POLICY_RECREATE); + elm_glview_mode_set(gl, ELM_GLVIEW_ALPHA|ELM_GLVIEW_DEPTH); + elm_glview_resize_policy_set(gl, ELM_GLVIEW_RESIZE_POLICY_RECREATE); elm_glview_render_policy_set(gl, ELM_GLVIEW_RENDER_POLICY_ALWAYS); - elm_glview_render_func_set(gl, _draw_gl); + elm_glview_init_func_set(gl, _init_gl); + elm_glview_del_func_set(gl, _del_gl); + elm_glview_resize_func_set(gl, _resize_gl); + elm_glview_render_func_set(gl, (Elm_GLView_Func)_draw_gl); elm_box_pack_end(bx, gl); evas_object_show(gl); + // Add Mouse/Key Event Callbacks elm_object_focus(gl); evas_object_event_callback_add(gl, EVAS_CALLBACK_KEY_DOWN, _key_down, gl); evas_object_event_callback_add(gl, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, gl); evas_object_event_callback_add(gl, EVAS_CALLBACK_MOUSE_UP, _mouse_up, gl); evas_object_event_callback_add(gl, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, gl); + + // Animator and other vars ani = ecore_animator_add(_anim, gl); gld->glapi = elm_glview_gl_api_get(gl); evas_object_data_set(gl, "ani", ani); evas_object_data_set(gl, "gld", gld); evas_object_event_callback_add(gl, EVAS_CALLBACK_DEL, _del, gl); + + /* add an ok button */ bt = elm_button_add(win); elm_button_label_set(bt, "OK"); evas_object_size_hint_align_set(bt, EVAS_HINT_FILL, EVAS_HINT_FILL); - evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); + evas_object_size_hint_weight_set(bt, EVAS_HINT_EXPAND, 0.0); elm_box_pack_end(bx, bt); evas_object_show(bt); evas_object_smart_callback_add(bt, "clicked", _on_done, win); - + evas_object_resize(win, 320, 480); evas_object_show(win); } Modified: trunk/elementary/src/lib/Elementary.h.in =================================================================== --- trunk/elementary/src/lib/Elementary.h.in 2011-06-20 10:14:35 UTC (rev 60516) +++ trunk/elementary/src/lib/Elementary.h.in 2011-06-20 10:55:02 UTC (rev 60517) @@ -1218,9 +1218,12 @@ EAPI void elm_glview_size_get(const Evas_Object *obj, Evas_Coord *width, Evas_Coord *height) EINA_ARG_NONNULL(1); EAPI Evas_GL_API *elm_glview_gl_api_get(const Evas_Object *obj) EINA_ARG_NONNULL(1); EAPI Eina_Bool elm_glview_mode_set(Evas_Object *obj, Elm_GLView_Mode mode) EINA_ARG_NONNULL(1); - EAPI Eina_Bool elm_glview_scale_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy) EINA_ARG_NONNULL(1); + EAPI Eina_Bool elm_glview_resize_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy) EINA_ARG_NONNULL(1); EAPI Eina_Bool elm_glview_render_policy_set(Evas_Object *obj, Elm_GLView_Render_Policy policy) EINA_ARG_NONNULL(1); - EAPI void elm_glview_render_func_set(Evas_Object *obj, Elm_GLView_Func func); + EAPI void elm_glview_init_func_set(Evas_Object *obj, Elm_GLView_Func func) EINA_ARG_NONNULL(1); + EAPI void elm_glview_del_func_set(Evas_Object *obj, Elm_GLView_Func func) EINA_ARG_NONNULL(1); + EAPI void elm_glview_resize_func_set(Evas_Object *obj, Elm_GLView_Func func) EINA_ARG_NONNULL(1); + EAPI void elm_glview_render_func_set(Evas_Object *obj, Elm_GLView_Func func) EINA_ARG_NONNULL(1); EAPI void elm_glview_changed_set(Evas_Object *obj) EINA_ARG_NONNULL(1); /* box */ Modified: trunk/elementary/src/lib/elm_glview.c =================================================================== --- trunk/elementary/src/lib/elm_glview.c 2011-06-20 10:14:35 UTC (rev 60516) +++ trunk/elementary/src/lib/elm_glview.c 2011-06-20 10:55:02 UTC (rev 60517) @@ -8,29 +8,33 @@ * * Signals that you can add callbacks for are: * - * "clicked" - This is called when a user has clicked the image */ typedef struct _Widget_Data Widget_Data; struct _Widget_Data { - Evas_Object *glview_image; + Evas_Object *glview_image; - Elm_GLView_Mode mode; + Elm_GLView_Mode mode; Elm_GLView_Resize_Policy scale_policy; - Elm_GLView_Render_Policy render_policy; + Elm_GLView_Render_Policy render_policy; - Evas_GL *evasgl; - Evas_GL_Config config; - Evas_GL_Surface *surface; - Evas_GL_Context *context; + Evas_GL *evasgl; + Evas_GL_Config config; + Evas_GL_Surface *surface; + Evas_GL_Context *context; - Evas_Coord w, h; + Evas_Coord w, h; - Elm_GLView_Func render_func; - Ecore_Idle_Enterer *render_idle_enterer; + Elm_GLView_Func init_func; + Elm_GLView_Func del_func; + Elm_GLView_Func resize_func; + Elm_GLView_Func render_func; - Eina_Bool initialized; + Ecore_Idle_Enterer *render_idle_enterer; + + Eina_Bool initialized; + Eina_Bool resized; }; static const char *widtype = NULL; @@ -45,6 +49,13 @@ { Widget_Data *wd = elm_widget_data_get(obj); if (!wd) return; + + // Call delete func if it's registered + if (wd->del_func) + { + evas_gl_make_current(wd->evasgl, wd->surface, wd->context); + wd->del_func(obj); + } if (wd->render_idle_enterer) ecore_idle_enterer_del(wd->render_idle_enterer); @@ -107,6 +118,9 @@ Evas_Coord w, h; if (!wd) return; + + wd->resized = EINA_TRUE; + if (wd->scale_policy == ELM_GLVIEW_RESIZE_POLICY_RECREATE) { evas_object_geometry_get(wd->glview_image, NULL, NULL, &w, &h); @@ -119,11 +133,13 @@ wd->w = w; wd->h = h; _glview_update_surface(data); + /* if (wd->render_func) { evas_gl_make_current(wd->evasgl, wd->surface, wd->context); wd->render_func(data); } + */ } } @@ -137,9 +153,23 @@ if (!evas_gl_make_current(wd->evasgl, wd->surface, wd->context)) { wd->render_idle_enterer = NULL; + ERR("Failed doing make current.\n"); return EINA_FALSE; } + // Call the init function if it hasn't been called already + if (!wd->initialized) + { + if (wd->init_func) wd->init_func(obj); + wd->initialized = EINA_TRUE; + } + + if (wd->resized) + { + if (wd->resize_func) wd->resize_func(obj); + wd->resized = EINA_FALSE; + } + // Call the render function if (wd->render_func) wd->render_func(obj); @@ -183,6 +213,7 @@ case ELM_GLVIEW_RENDER_POLICY_ALWAYS: // Unset the pixel getter callback if set already evas_object_image_pixels_get_callback_set(wd->glview_image, NULL, NULL); + break; default: ERR("Invalid Render Policy.\n"); @@ -234,16 +265,23 @@ evas_object_show(wd->glview_image); // Initialize variables - wd->mode = 0; - wd->scale_policy = ELM_GLVIEW_RESIZE_POLICY_RECREATE; - wd->render_policy = ELM_GLVIEW_RENDER_POLICY_ON_DEMAND; - wd->config = cfg; - wd->surface = NULL; + wd->mode = 0; + wd->scale_policy = ELM_GLVIEW_RESIZE_POLICY_RECREATE; + wd->render_policy = ELM_GLVIEW_RENDER_POLICY_ON_DEMAND; + wd->config = cfg; + wd->surface = NULL; - wd->w = 64; - wd->h = 64; + // Initialize it to (64,64) (It's an arbitrary value) + wd->w = 64; + wd->h = 64; + // Initialize the rest of the values + wd->init_func = NULL; + wd->del_func = NULL; + wd->render_func = NULL; wd->render_idle_enterer = NULL; + wd->initialized = EINA_FALSE; + wd->resized = EINA_FALSE; // Create Context if (!wd->context) @@ -321,12 +359,12 @@ } /** - * Set the scaling policy for the glview object. + * Set the resize policy for the glview object. * * @param obj The glview object. * @param policy The scaling policy. * - * By default, the scaling policy is set to ELM_GLVIEW_RESIZE_POLICY_RECREATE. + * By default, the resize policy is set to ELM_GLVIEW_RESIZE_POLICY_RECREATE. * When resize is called it destroys the previous surface and recreates the newly * specified size. If the policy is set to ELM_GLVIEW_RESIZE_POLICY_SCALE, however, * glview only scales the image object and not the underlying GL Surface. @@ -334,7 +372,7 @@ * @ingroup GLView */ EAPI Eina_Bool -elm_glview_scale_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy) +elm_glview_resize_policy_set(Evas_Object *obj, Elm_GLView_Resize_Policy policy) { ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE; Widget_Data *wd = elm_widget_data_get(obj); @@ -437,9 +475,73 @@ } /** + * Set the init function that runs once in the main loop. + * + * @param obj The glview object. + * @param func The init function to be registered. + * + * The registered init function gets called once during the render loop. + * + * @ingroup GLView + */ +EAPI void +elm_glview_init_func_set(Evas_Object *obj, Elm_GLView_Func func) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + + wd->initialized = EINA_FALSE; + wd->init_func = func; +} + +/** * Set the render function that runs in the main loop. * * @param obj The glview object. + * @param func The delete function to be registered. + * + * The registered del function gets called when GLView object is deleted. + * + * @ingroup GLView + */ +EAPI void +elm_glview_del_func_set(Evas_Object *obj, Elm_GLView_Func func) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) return; + + wd->del_func = func; +} + +/** + * Set the resize function that gets called when resize happens. + * + * @param obj The glview object. + * @param func The resize function to be registered. + * + * @ingroup GLView + */ +EAPI void +elm_glview_resize_func_set(Evas_Object *obj, Elm_GLView_Func func) +{ + ELM_CHECK_WIDTYPE(obj, widtype); + Widget_Data *wd = elm_widget_data_get(obj); + if (!wd) + { + ERR("Invalid Widget Object.\n"); + return; + } + + wd->resize_func = func; +} + + +/** + * Set the render function that runs in the main loop. + * + * @param obj The glview object. * @param func The render function to be registered. * * @ingroup GLView |