#include <GL/gl.h>
#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)
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,
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,
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,
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,
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,
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,