Hi all, I followed the Tizen sdk 2.3 help contents https://developer.tizen.org/dev-guide/2.3.0/org.tizen.mobile.native.appprogramming/html/tutorials/ui_tutorial/multithread_opengl_tutorial.htm to write a application which use multi thread for rendering.
It can compile ,but when run it to "the glBindTexture(GL_TEXTURE_2D, tex)" the emulator disappear. And I run it in real phone , it also just show backgroud.
Anyone who can help me ? Thank you.
The codes are:
#include <app.h>
#include <Elementary.h>
#include <Elementary_GL_Helpers.h>
#include <efl_extension.h>
#include <tizen.h>
#include <math.h>
#include <sys/time.h>
#include <tizen_type.h>
#include <dlog.h>
#ifdef LOG_TAG
#undef LOG_TAG
#endif
#define LOG_TAG "glview"
ELEMENTARY_GLVIEW_GLOBAL_DEFINE();
//EVAS_GL_GLOBAL_GLES2_DEFINE();
#define BUFFER_COUNT 5
//#define GL() gl->
typedef enum message_Type
{
MSG_NEWFRAME = 0,
MSG_READY ,
MSG_HELLO,
MSG_GOODBYE,
}Message_Type;
typedef struct
{
EINA_INLIST;
GLuint fbo, tex;
EvasGLSync sync;
int id;
}
Target_Buffer;
typedef struct
{
Message_Type type;
Target_Buffer *target;
}Message_Data;
typedef struct appdata {
const char *name;
Evas_Object *win;
Evas_Object *conform;
/* GL related data here... */
unsigned int program;
unsigned int vtx_shader;
unsigned int fgmt_shader;
float xangle;
float yangle;
unsigned int idx_position;
unsigned int idx_color;
int idx_mvp;
float mvp[16];
Eina_Bool mouse_down : 1;
Eina_Bool initialized :1;
Evas_Object *glview;
Ecore_Pipe * pipe;
Eina_Lock lck;
Evas_GL_Context *main_ctx;
GLuint vbo[4];
Ecore_Thread * thread;
Evas_GL_Context* ctx;
Evas_GL_Surface* sfc;
Eina_Inlist* buffers_empty;
Eina_Inlist* buffers_ready;
} appdata_s;
static void win_back_cb(void *data, Evas_Object *obj, void *event_info) {
appdata_s *ad = data;
/* Let window go to hidden state. */
elm_win_lower(ad->win);
}
static void del_gl(Evas_Object *obj) {
appdata_s *ad = evas_object_data_get(obj, "ad");
glDeleteShader(ad->vtx_shader);
glDeleteShader(ad->fgmt_shader);
glDeleteProgram(ad->program);
evas_object_data_del((Evas_Object*) obj, "ad");
}
static void del_anim(void *data, Evas *evas, Evas_Object *obj, void *event_info)
{
Ecore_Animator *ani = evas_object_data_get(obj, "ani");
ecore_animator_del(ani);
}
static Eina_Bool anim(void *data) {
elm_glview_changed_set(data);
return EINA_TRUE;
}
static void resize_gl(Evas_Object *obj) {
int w, h;
elm_glview_size_get(obj, &w, &h);
glViewport(0, 0, w, h);
}
static void create_indicator(appdata_s *ad) {
elm_win_conformant_set(ad->win, EINA_TRUE);
elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_TRANSPARENT);
ad->conform = elm_conformant_add(ad->win);
evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND,
EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, ad->conform);
evas_object_show(ad->conform);
}
void
draw_rectangle(appdata_s *ad, int w, int h, int tex)
{
//ELEMENTARY_GLVIEW_GLOBAL_USE(ad->glview);
GLuint u;
glViewport(0, 0, w, h);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glUseProgram(ad->program);
glBindBuffer(GL_ARRAY_BUFFER, ad->vbo[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, ad->vbo[1]);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
u = glGetUniformLocation(ad->program, "tex");
glUniform1i(u, 0);
glActiveTexture(GL_TEXTURE0);
printf("tex %d\n",tex);
//return ;
glBindTexture(GL_TEXTURE_2D, tex); //crash!!!!!!!!!
glDrawArrays(GL_TRIANGLES, 0, 6);
}
static void render(Evas_Object *obj)
{
appdata_s *ad = evas_object_data_get(obj,"ad");
int w,h;
Target_Buffer *target;
eina_lock_take(&ad->lck);
if (!ad->buffers_ready)
{
// Wait
eina_lock_release(&ad->lck);
usleep(1000);
return;
}
target = EINA_INLIST_CONTAINER_GET(ad->buffers_ready, Target_Buffer);
ad->buffers_ready = eina_inlist_remove(ad->buffers_ready, ad->buffers_ready);
eina_lock_release(&ad->lck);
evas_object_geometry_get(ad->glview, 0, 0, &w, &h);
//printf("tex %d\n",target->tex);
draw_rectangle(ad, w, h, target->tex);
}
// Render thread to main loop
static void
message_send(appdata_s *ad, Message_Type type, ...)
{
Message_Data msg = {0};
msg.type = type;
if (type == MSG_NEWFRAME)
{
va_list args;
va_start(args, type);
msg.target = va_arg(args, Target_Buffer *);
va_end(args);
}
// The pipe copies the passed data
if(ad->pipe == NULL)
{
printf("pipe is null \n");
return;
}
ecore_pipe_write(ad->pipe, &msg, sizeof(msg));
return ;
}
static void
pipe_handler(void *data, void *buf, unsigned int len EINA_UNUSED)
{
Message_Data *msg = buf;
appdata_s *ad = data;
switch (msg->type)
{
case MSG_HELLO:
// Render thread has started
printf("The render thread is saying hello.\n");
break;
case MSG_GOODBYE:
// It is now safe to request exit from the main loop
printf("Thread has cleanly terminated.\n");
elm_exit();
break;
case MSG_READY:
printf("The RenderThread has ready.\n");
break;
case MSG_NEWFRAME:
// Queue a new frame description
printf("Got a new frame with buffer %d\n", msg->target->id);
eina_lock_take(&ad->lck);
ad->buffers_ready = eina_inlist_append(ad->buffers_ready, EINA_INLIST_GET(msg->target));
eina_lock_release(&ad->lck);
elm_glview_changed_set(ad->glview);
//render(ad->glview);
break;
}
}
static Target_Buffer *
target_create(appdata_s *ad EINA_UNUSED, Evas_GL_API *gl)
{
Target_Buffer *target = calloc(1, sizeof(Target_Buffer));
GLenum err;
if (!target)
return NULL;
// glGenFramebuffers(1, &target->fbo);
// glBindFramebuffer(GL_FRAMEBUFFER, target->fbo);
glGenTextures(1, &target->tex);
glBindTexture(GL_TEXTURE_2D, target->tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 720, 1280, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target->tex, 0);
//err = glCheckFramebufferStatus(GL_FRAMEBUFFER);
//printf("tex create :%d \n",target->tex);
//if (err != GL_FRAMEBUFFER_COMPLETE)
//
//{
// printf("FBO could not be set: 0x%x\n", (int) err);
// glDeleteTextures(1, &target->tex);
//glDeleteFramebuffers(1, &target->fbo);
// free(target);
// return NULL;
// }
return target;
}
static void
thread_draw(appdata_s *ad, Evas_GL_API *gl)
{
// Draw function
glClearColor(0, 0,0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
void thread_run(void *data, Ecore_Thread *th EINA_UNUSED)
{
printf("The Render Thread run .\n");
appdata_s *ad = (appdata_s *)data;
Evas_GL *evgl = elm_glview_evas_gl_get(ad->glview);
Evas_GL_Config *cfg;
cfg = evas_gl_config_new();
cfg->color_format = EVAS_GL_NO_FBO;
cfg->depth_bits = EVAS_GL_DEPTH_NONE;
cfg->stencil_bits = EVAS_GL_STENCIL_NONE;
cfg->options_bits = EVAS_GL_OPTIONS_NONE;
ad->sfc = evas_gl_pbuffer_surface_create(evgl, cfg, 720, 1280, NULL);
evas_gl_config_free(cfg);
ad->ctx = evas_gl_context_create(evgl, ad->main_ctx);
evas_gl_make_current(evgl, ad->sfc, ad->ctx);
Evas_GL_API* gl= evas_gl_api_get(evgl);
// Create targets (needs at least 2)
eina_lock_take(&ad->lck);
int i;
Target_Buffer *target;
for (i = 0; i < BUFFER_COUNT; i++)
{
target = target_create(ad, gl);
if (!target) break;
target->id = i;
ad->buffers_empty = eina_inlist_append(ad->buffers_empty,
EINA_INLIST_GET(target));
}
eina_lock_release(&ad->lck);
message_send(ad, MSG_READY);
evas_gl_make_current(evgl, ad->sfc, ad->ctx);
while (!ecore_thread_check(ad->thread))
{
// Get an empty buffer
eina_lock_take(&ad->lck);
if (!ad->buffers_empty)
{
// Wait
eina_lock_release(&ad->lck);
usleep(1000);
continue;
}
target = EINA_INLIST_CONTAINER_GET(ad->buffers_empty, Target_Buffer);
ad->buffers_empty = eina_inlist_remove(ad->buffers_empty, ad->buffers_empty);
eina_lock_release(&ad->lck);
// Prepare new frame
glViewport(0, 0, 720, 1280);
// glBindFramebuffer(GL_FRAMEBUFFER, target->fbo);
// Draw a new frame
thread_draw(ad, gl);
// Release FBO; some drivers complain if it is bound by a different thread
// glBindFramebuffer(GL_FRAMEBUFFER, 0); //remove by ds
message_send(ad, MSG_NEWFRAME, target); //add by ds
}
}
static void
init(Evas_Object *o)
{
//APPDATA(o);
//GLUSE(o);
printf("The main thread init.\n");
appdata_s *ad;
ad = evas_object_data_get(o,"ad");
const char *p;
static const char vertex_texture[] =
"attribute vec4 vPosition;\n"
"attribute vec2 vTexCoord;\n"
"varying vec2 texcoord;\n"
"void main()\n"
"{\n"
" gl_Position = vPosition;\n"
" texcoord = vTexCoord;\n"
"}\n";
static const char fragment_texture[] =
"#ifdef GL_ES\n"
"precision mediump float;\n"
"#endif\n"
"uniform sampler2D tex;\n"
"varying vec2 texcoord;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texcoord);\n"
"}\n";
const float rectangle_fullscreen_vertices[] =
{
1.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0,
1.0, 1.0, 0.0,
-1.0, 1.0, 0.0,
-1.0, -1.0, 0.0
};
const float texture_vertices[] =
{
1.0, 1.0,
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
0.0, 0.0,
};
// Init main data
ad->main_ctx = evas_gl_current_context_get(elm_glview_evas_gl_get(o));
// Create vertex data
glGenBuffers(2, ad->vbo);
glBindBuffer(GL_ARRAY_BUFFER, ad->vbo[0]);
glBufferData(GL_ARRAY_BUFFER, 3 * 6 * 4, rectangle_fullscreen_vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, ad->vbo[1]);
glBufferData(GL_ARRAY_BUFFER, 2 * 6 * 4, texture_vertices, GL_STATIC_DRAW);
// Texture draw
p = vertex_texture;
ad->vtx_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(ad->vtx_shader, 1, &p, NULL);
glCompileShader(ad->vtx_shader);
p = fragment_texture;
ad->fgmt_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(ad->fgmt_shader, 1, &p, NULL);
glCompileShader(ad->fgmt_shader);
ad->program = glCreateProgram();
glAttachShader(ad->program, ad->vtx_shader);
glAttachShader(ad->program, ad->fgmt_shader);
glBindAttribLocation(ad->program, 0, "vPosition");
glBindAttribLocation(ad->program, 1, "vTexCoord");
glLinkProgram(ad->program);
printf("The main thread end.\n");
ad->thread = ecore_thread_run(thread_run, NULL, NULL, ad);
}
static Evas_Object* add_win(const char *name) {
Evas_Object *win;
//elm_config_accel_preference_set("opengl");
elm_config_accel_preference_set("gl");
elm_config_accel_preference_override_set(EINA_TRUE);
win = elm_win_util_standard_add(name, "OpenGL example: Cube");
if (!win)
return NULL;
// if (elm_win_wm_rotation_supported_get(win)) {
// int rots[4] = { 0, 90, 180, 270 };
// elm_win_wm_rotation_available_rotations_set(win, rots, 4);
// }
evas_object_show(win);
return win;
}
static bool app_create(void *data) {
/* Hook to take necessary actions before main event loop starts
* Initialize UI resources and application's data
* If this function returns true, the main loop of application starts
* If this function returns false, the application is terminated. */
printf("The app create.\n");
Evas_Object *win, *bx, *o;
Ecore_Animator *ani;
appdata_s *ad = data;
if (!data)
return false;
/* Create the window */
win = add_win("glview");
if (!win)
return false;
eext_object_event_callback_add(win, EEXT_CALLBACK_BACK, win_back_cb, ad);
ad->win = win;
/* Add a box to contain our GLView */
bx = elm_box_add(win);
evas_object_size_hint_weight_set(bx, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, bx);
evas_object_show(bx);
/* Create and initialize GLView */
o = elm_glview_add(win);
//ELEMENTARY_GLVIEW_GLOBAL_USE(o);
evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
//o = elm_glview_version_add(win, EVAS_GL_GLES_2_X);
//evas_object_size_hint_min_set(o, 240, 240);
//elm_win_resize_object_add(win, o);
/* Request a surface with alpha and a depth buffer */
elm_glview_mode_set(o, ELM_GLVIEW_ALPHA | ELM_GLVIEW_DEPTH);
elm_glview_render_policy_set(o, ELM_GLVIEW_RENDER_POLICY_ON_DEMAND);
elm_glview_resize_policy_set(o, ELM_GLVIEW_RESIZE_POLICY_RECREATE);
ELEMENTARY_GLVIEW_GLOBAL_USE(o); //add by ds
/* The initialize callback function gets registered here */
printf("ready to into init function.\n");
ad->pipe = ecore_pipe_add(pipe_handler, ad);
eina_lock_new(&ad->lck);
elm_glview_init_func_set(o, init);
/* The delete callback function gets registered here */
elm_glview_del_func_set(o, del_gl);
/* The resize callback function gets registered here */
elm_glview_resize_func_set(o, resize_gl);
/* The render callback function gets registered here */
elm_glview_render_func_set(o, render);
/* Add the GLView to the box and show it */
elm_box_pack_end(bx, o);
evas_object_show(o);
elm_object_focus_set(o, EINA_TRUE);
ani = ecore_animator_add(anim, o);
evas_object_data_set(o, "ani", ani);
evas_object_show(win);
evas_object_data_set(o, "ad", ad);
ad->glview = o ;
create_indicator(ad);
/* Return true: the main loop will now start running */
return true;
}
static void app_control(app_control_h app_control, void *data) {
/* Handle the launch request. */
}
static void app_pause(void *data) {
/* Take necessary actions when application becomes invisible. */
}
static void app_resume(void *data) {
/* Take necessary actions when application becomes visible. */
}
static void app_terminate(void *data) {
/* Release all resources. */
}
int main(int argc, char *argv[]) {
appdata_s ad = { NULL, };
int ret = 0;
ui_app_lifecycle_callback_s event_callback = {NULL,};
ad.name = "glview";
event_callback.create = app_create;
event_callback.terminate = app_terminate;
event_callback.pause = app_pause;
event_callback.resume = app_resume;
event_callback.app_control = app_control;
ret = ui_app_main(argc, argv, &event_callback, &ad);
if (ret != APP_ERROR_NONE) {
printf( "The application failed to start, and returned %d", ret);
}
return ret;
}