From: Stanislav Vorobiov Date: Mon, 24 Mar 2014 07:52:52 +0000 (+0400) Subject: YaGL: Workaround broken UBO on some platforms X-Git-Tag: Tizen_Studio_1.3_Release_p2.3.1~450^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=53cece76363bebd6ffacd354693c7cead55410de;p=sdk%2Femulator%2Fqemu.git YaGL: Workaround broken UBO on some platforms Some platforms (like Mac OS X 10.8) have broken UBO support. The problem is that if an UB is unnamed it still requires a name, i.e.: uniform myUB { mat4 myMatrix; }; API should be able to reference myMatrix as "myMatrix", but broken UBO platform expects "myUB.myMatrix". For named UBs there's no error however: uniform myUB { mat4 myMatrix; } myUBName; Here myMatrix is referenced as "myUB.myMatrix" (yes, myUB, not myUBName) and this is correct. To work around the problem we must patch index lookups by UB name when needed Change-Id: I0e73f32b116469d2bffbf8fee2a2129f7cff8cb7 Signed-off-by: Stanislav Vorobiov --- diff --git a/hw/yagl/yagl_apis/gles/yagl_gles_api.h b/hw/yagl/yagl_apis/gles/yagl_gles_api.h index 5f196a8e84..ef6946ff5d 100644 --- a/hw/yagl/yagl_apis/gles/yagl_gles_api.h +++ b/hw/yagl/yagl_apis/gles/yagl_gles_api.h @@ -39,6 +39,11 @@ struct yagl_gles_api struct yagl_api base; struct yagl_gles_driver *driver; + + bool checked; + + bool use_map_buffer_range; + bool broken_ubo; }; /* diff --git a/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.c b/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.c index 01e0e3d14b..7285176dd8 100644 --- a/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.c +++ b/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.c @@ -29,17 +29,243 @@ #include #include "yagl_gles_api_ts.h" +#include "yagl_gles_api_ps.h" +#include "yagl_gles_api.h" #include "yagl_gles_driver.h" #include "yagl_process.h" #include "yagl_thread.h" +#include "yagl_log.h" + +static GLuint yagl_gles_api_ts_create_shader(struct yagl_gles_driver *driver, + const char *source, + GLenum type) +{ + GLuint shader = driver->CreateShader(type); + GLint tmp = 0; + + YAGL_LOG_FUNC_SET(yagl_gles_api_ts_create_shader); + + if (!shader) { + YAGL_LOG_ERROR("Unable to create shader type %d", type); + return 0; + } + + driver->ShaderSource(shader, 1, &source, NULL); + driver->CompileShader(shader); + driver->GetShaderiv(shader, GL_COMPILE_STATUS, &tmp); + + if (!tmp) { + char *buff; + + tmp = 0; + + driver->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &tmp); + + buff = g_malloc0(tmp); + + driver->GetShaderInfoLog(shader, tmp, NULL, buff); + + YAGL_LOG_ERROR("Unable to compile shader (type = %d) - %s", + type, buff); + + driver->DeleteShader(shader); + + g_free(buff); + + return 0; + } + + return shader; +} + +/* + * Some platforms (like Mac OS X 10.8) have broken UBO support. + * The problem is that if an UB is unnamed it still requires a name, i.e.: + * + * uniform myUB + * { + * mat4 myMatrix; + * }; + * + * API should be able to reference myMatrix as "myMatrix", but broken + * UBO platform expects "myUB.myMatrix". + * For named UBs there's no error however: + * + * uniform myUB + * { + * mat4 myMatrix; + * } myUBName; + * + * Here myMatrix is referenced as "myUB.myMatrix" (yes, myUB, not myUBName) + * and this is correct. + * + * To (partially) work around the problem we must patch + * index lookups by UB name. + */ +static bool yagl_gles_api_ts_broken_ubo_test(struct yagl_gles_driver *driver) +{ + static const char *vs_source_es3 = + "#version 300 es\n\n" + "uniform myUB\n" + "{\n" + " mediump mat4 myMatrix;\n" + "};\n" + "in mediump vec4 vertCoord;\n" + "void main()\n" + "{\n" + " gl_Position = myMatrix * vertCoord;\n" + "}\n"; + + static const char *fs_source_es3 = + "#version 300 es\n\n" + "out mediump vec4 FragColor;\n" + "void main()\n" + "{\n" + " FragColor = vec4(0, 0, 0, 0);\n" + "}\n"; + + static const char *vs_source_3_2 = + "#version 150\n\n" + "uniform myUB\n" + "{\n" + " mat4 myMatrix;\n" + "};\n" + "in vec4 vertCoord;\n" + "void main()\n" + "{\n" + " gl_Position = myMatrix * vertCoord;\n" + "}\n"; + + static const char *fs_source_3_2 = + "#version 150\n\n" + "out vec4 FragColor;\n" + "void main()\n" + "{\n" + " FragColor = vec4(0, 0, 0, 0);\n" + "}\n"; + + static const GLchar *good_name = "myMatrix"; + static const GLchar *bad_name = "myUB.myMatrix"; + + bool res = false; + GLuint vs, fs; + GLuint program; + GLint tmp = 0; + GLuint index = GL_INVALID_INDEX; + + YAGL_LOG_FUNC_SET(yagl_gles_api_ts_broken_ubo_test); + + vs = yagl_gles_api_ts_create_shader(driver, + ((driver->gl_version == yagl_gl_3_1_es3) ? vs_source_es3 : vs_source_3_2), + GL_VERTEX_SHADER); + + if (!vs) { + goto out1; + } + + fs = yagl_gles_api_ts_create_shader(driver, + ((driver->gl_version == yagl_gl_3_1_es3) ? fs_source_es3 : fs_source_3_2), + GL_FRAGMENT_SHADER); + + if (!fs) { + goto out2; + } + + program = driver->CreateProgram(); + + if (!program) { + YAGL_LOG_ERROR("Unable to create program"); + goto out3; + } + + driver->AttachShader(program, vs); + driver->AttachShader(program, fs); + driver->LinkProgram(program); + driver->GetProgramiv(program, GL_LINK_STATUS, &tmp); + + if (!tmp) { + char *buff; + + tmp = 0; + + driver->GetProgramiv(program, GL_INFO_LOG_LENGTH, &tmp); + + buff = g_malloc0(tmp); + + driver->GetProgramInfoLog(program, tmp, NULL, buff); + + YAGL_LOG_ERROR("Unable to link program - %s", buff); + + driver->DeleteProgram(program); + + g_free(buff); + + goto out4; + } + + driver->GetUniformIndices(program, 1, &good_name, &index); + + if (index == GL_INVALID_INDEX) { + driver->GetUniformIndices(program, 1, &bad_name, &index); + + if (index == GL_INVALID_INDEX) { + YAGL_LOG_ERROR("UBO support is broken in unusual way, unable to workaround. Using UBO may cause undefined behavior"); + } else { + YAGL_LOG_WARN("UBO support is broken, applying workaround"); + res = true; + } + } + +out4: + driver->DetachShader(program, fs); + driver->DetachShader(program, vs); + driver->DeleteProgram(program); +out3: + driver->DeleteShader(fs); +out2: + driver->DeleteShader(vs); +out1: + return res; +} void yagl_gles_api_ts_init(struct yagl_gles_api_ts *gles_api_ts, struct yagl_gles_driver *driver, struct yagl_gles_api_ps *ps) { + YAGL_LOG_FUNC_SET(yagl_gles_api_ts_init); + gles_api_ts->driver = driver; gles_api_ts->ps = ps; - gles_api_ts->use_map_buffer_range = -1; + gles_api_ts->api = (struct yagl_gles_api*)ps->base.api; + + if (gles_api_ts->api->checked) { + return; + } + + yagl_ensure_ctx(0); + + if (driver->gl_version > yagl_gl_2) { + gles_api_ts->api->use_map_buffer_range = true; + } else { + const char *tmp = (const char*)driver->GetString(GL_EXTENSIONS); + + gles_api_ts->api->use_map_buffer_range = + (tmp && (strstr(tmp, "GL_ARB_map_buffer_range ") != NULL)); + + if (!gles_api_ts->api->use_map_buffer_range) { + YAGL_LOG_WARN("glMapBufferRange not supported, using glBufferSubData"); + } + } + + if (driver->gl_version >= yagl_gl_3_1_es3) { + gles_api_ts->api->broken_ubo = yagl_gles_api_ts_broken_ubo_test(driver); + } else { + gles_api_ts->api->broken_ubo = false; + } + + yagl_unensure_ctx(0); + + gles_api_ts->api->checked = true; } void yagl_gles_api_ts_cleanup(struct yagl_gles_api_ts *gles_api_ts) diff --git a/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.h b/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.h index f24ad7cbac..0a881e7c3b 100644 --- a/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.h +++ b/hw/yagl/yagl_apis/gles/yagl_gles_api_ts.h @@ -34,6 +34,7 @@ struct yagl_gles_driver; struct yagl_gles_api_ps; +struct yagl_gles_api; /* * OpenGL 3.1+ core profile doesn't allow one to @@ -52,6 +53,11 @@ struct yagl_gles_api_ts struct yagl_gles_api_ps *ps; + /* + * From 'ps->base.api' for speed. + */ + struct yagl_gles_api *api; + struct yagl_gles_array *arrays; uint32_t num_arrays; @@ -60,11 +66,6 @@ struct yagl_gles_api_ts */ GLuint ebo; uint32_t ebo_size; - - /* - * -1 when undecided, 0/1 when decided. - */ - int use_map_buffer_range; }; void yagl_gles_api_ts_init(struct yagl_gles_api_ts *gles_api_ts, diff --git a/hw/yagl/yagl_apis/gles/yagl_host_gles_calls.c b/hw/yagl/yagl_apis/gles/yagl_host_gles_calls.c index 034f2d3108..11bb2d4ffb 100644 --- a/hw/yagl/yagl_apis/gles/yagl_host_gles_calls.c +++ b/hw/yagl/yagl_apis/gles/yagl_host_gles_calls.c @@ -61,28 +61,6 @@ typedef enum yagl_gles1_array_texcoord, } yagl_gles1_array_type; -static bool yagl_gles_use_map_buffer_range(void) -{ - YAGL_LOG_FUNC_SET(yagl_gles_use_map_buffer_range); - - if (gles_api_ts->use_map_buffer_range == -1) { - if (gles_api_ts->driver->gl_version > yagl_gl_2) { - gles_api_ts->use_map_buffer_range = 1; - } else { - const char *tmp = (const char*)gles_api_ts->driver->GetString(GL_EXTENSIONS); - - gles_api_ts->use_map_buffer_range = - (tmp && (strstr(tmp, "GL_ARB_map_buffer_range ") != NULL)); - - if (!gles_api_ts->use_map_buffer_range) { - YAGL_LOG_WARN("glMapBufferRange not supported, using glBufferSubData"); - } - } - } - - return gles_api_ts->use_map_buffer_range; -} - static GLuint yagl_gles_bind_array(uint32_t indx, GLint first, GLsizei stride, @@ -130,7 +108,7 @@ static GLuint yagl_gles_bind_array(uint32_t indx, gles_api_ts->arrays[indx].size = size; } - if (yagl_gles_use_map_buffer_range()) { + if (gles_api_ts->api->use_map_buffer_range) { ptr = gles_api_ts->driver->MapBufferRange(GL_ARRAY_BUFFER, first * stride, data_count, @@ -176,7 +154,7 @@ static GLuint yagl_gles_bind_ebo(const GLvoid *data, int32_t size) gles_api_ts->ebo_size = size; } - if (yagl_gles_use_map_buffer_range()) { + if (gles_api_ts->api->use_map_buffer_range) { ptr = gles_api_ts->driver->MapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, size, @@ -921,7 +899,7 @@ void yagl_host_glBufferSubData(GLenum target, YAGL_LOG_FUNC_SET(glBufferSubData); - if (yagl_gles_use_map_buffer_range()) { + if (gles_api_ts->api->use_map_buffer_range) { ptr = gles_api_ts->driver->MapBufferRange(target, offset, data_count, @@ -2221,9 +2199,15 @@ void yagl_host_glGetUniformIndices(GLuint program, const GLchar *uniformNames, int32_t uniformNames_count, GLuint *uniformIndices, int32_t uniformIndices_maxcount, int32_t *uniformIndices_count) { - GLuint obj = yagl_gles_object_get(program); - int max_active_uniform_bufsize = 1, i; + GLuint obj; + int max_active_uniform_bufsize = 1, i, j; const GLchar **name_pointers; + int num_active_uniforms = 0; + GLchar *uniform_name; + + YAGL_LOG_FUNC_SET(glGetUniformIndices); + + obj = yagl_gles_object_get(program); gles_api_ts->driver->GetProgramiv(obj, GL_ACTIVE_UNIFORM_MAX_LENGTH, @@ -2243,6 +2227,99 @@ void yagl_host_glGetUniformIndices(GLuint program, g_free(name_pointers); *uniformIndices_count = uniformIndices_maxcount; + + if (!gles_api_ts->api->broken_ubo) { + return; + } + + gles_api_ts->driver->GetProgramiv(obj, + GL_ACTIVE_UNIFORMS, + &num_active_uniforms); + + uniform_name = g_malloc(max_active_uniform_bufsize + 1); + + for (i = 0; i < num_active_uniforms; ++i) { + GLsizei length = 0; + GLint size = 0; + GLenum type = 0; + const GLchar *tmp; + size_t tmp_len; + + gles_api_ts->driver->GetActiveUniform(obj, + i, + max_active_uniform_bufsize, + &length, + &size, + &type, + uniform_name); + + if (length == 0) { + YAGL_LOG_ERROR("Cannot get active uniform %d for program %u", i, obj); + continue; + } + + tmp = strchr(uniform_name, '.'); + + if (!tmp) { + continue; + } + + tmp += 1; + + tmp_len = strlen(tmp); + + for (j = 0; j < uniformIndices_maxcount; ++j) { + const GLchar *test, *dot; + size_t test_len; + + if (uniformIndices[j] != GL_INVALID_INDEX) { + continue; + } + + /* + * This solution is not perfect, but it's better than nothing. + * It may yield incorrect index in cases like this: + * + * uniform myUB1 + * { + * mat4 myMatrix; + * } myName; + * + * uniform myUB2 + * { + * mat4 myMatrix; + * }; + * + * A query for "myMatrix" may return index of + * "myUB1.myMatrix" instead of intended "myUB2.myMatrix". + * The problem is that we can't find out if UB is named or + * not from API, so if the uniform with same name is + * present in several UBs we might get the wrong one... + */ + + test = &uniformNames[max_active_uniform_bufsize * j]; + test_len = strlen(test); + + dot = strchr(test, '.'); + + if (dot && + (strncmp(test, + uniform_name, + dot - test) == 0) && + (uniform_name[dot - test] == '[')) { + test = dot + 1; + test_len = strlen(test); + } + + if ((strncmp(test, tmp, test_len) == 0) && + ((test_len == tmp_len) || + ((test_len < tmp_len) && (tmp[test_len] == '[')))) { + uniformIndices[j] = i; + } + } + } + + g_free(uniform_name); } GLuint yagl_host_glGetUniformBlockIndex(GLuint program,