Bind external textures via 2D texture targets 45/250345/2
authorLukasz Kostyra <l.kostyra@samsung.com>
Wed, 23 Dec 2020 22:01:01 +0000 (23:01 +0100)
committerLukasz Kostyra <l.kostyra@samsung.com>
Thu, 7 Jan 2021 16:40:54 +0000 (17:40 +0100)
Change-Id: I56a834f01c3fdc862ed06a2e616f83600b425490

GLES_common/yagl_gles_context.c
GLES_common/yagl_gles_context.h
GLES_common/yagl_gles_texture.c
GLES_common/yagl_gles_texture_unit.c
GLESv1_CM/yagl_gles1_context.c
GLESv2/yagl_gles2_calls.c
GLESv2/yagl_gles2_context.c
GLESv2/yagl_gles2_program.c
GLESv2/yagl_glsl_state.c
GLESv2/yagl_glsl_state.h

index b3f261540c000682c069246a22f357ae71c513d7..dac2270d8649154781476702a698908a42b2fd01 100644 (file)
@@ -286,6 +286,8 @@ void yagl_gles_context_init(struct yagl_gles_context *ctx,
     ctx->polygon_offset_units = 0.0f;
 
     ctx->dither_enabled = GL_TRUE;
+
+    ctx->have_externaloes_rebinds = 0;
 }
 
 void yagl_gles_context_prepare(struct yagl_gles_context *ctx,
@@ -971,9 +973,12 @@ void yagl_gles_context_bind_texture(struct yagl_gles_context *ctx,
         texture = texture_target_state->texture_zero;
     }
 
-    if (!yagl_gles_texture_bind(texture, target)) {
-        YAGL_SET_ERR(GL_INVALID_OPERATION);
-        return;
+    // TEXTURE_EXTERNAL_OES is late bound, before drawing
+    if (target != GL_TEXTURE_EXTERNAL_OES) {
+        if (!yagl_gles_texture_bind(texture, target)) {
+            YAGL_SET_ERR(GL_INVALID_OPERATION);
+            return;
+        }
     }
 
     yagl_gles_texture_acquire(texture);
index 5167e84140c98cd14ab1b04bc9f680b8f619ac4a..ca3f43fbddb44f5abeee6dd9277d5d01f9fe42e9 100644 (file)
@@ -300,6 +300,11 @@ struct yagl_gles_context
     GLboolean sample_coverage_enabled;
     GLboolean scissor_test_enabled;
     GLboolean stencil_test_enabled;
+
+    /*
+     * Texture information to restore post-draw. Updated only by pre/post-draw
+     */
+    int have_externaloes_rebinds;
 };
 
 void yagl_gles_context_init(struct yagl_gles_context *ctx,
index e9a0580730c4295795acc3de87660f71cb6d4c6d..ab3f4e656f06f3c9bc776b1429fba5ecbaf841c9 100644 (file)
@@ -158,8 +158,14 @@ int yagl_gles_texture_bind(struct yagl_gles_texture *texture,
         return 0;
     }
 
-    yagl_host_glBindTexture(target == GL_TEXTURE_EXTERNAL_OES ? GL_TEXTURE_2D : target,
-                            texture->global_name);
+    // To support TEXTURE_EXTERNAL_OES we need to use TEXTURE_2D target
+    // on host side. In order to not collide with what is bound there we'll
+    // store the texture for later on guest side and do the temporary bind
+    // on-demand, whenever app modifies TEXTURE_EXTERNAL_OES
+    // target via other GL APIs.
+    assert(target != GL_TEXTURE_EXTERNAL_OES);
+
+    yagl_host_glBindTexture(target, texture->global_name);
 
     texture->target = target;
 
@@ -248,16 +254,6 @@ void yagl_gles_texture_set_image(struct yagl_gles_texture *texture,
         texture->binding = NULL;
     }
 
-    /*
-     * FIXME We still need to update the host binding when using external
-     * textures even if that same image is already set. E.g. consider the bellow
-     * scenario:
-     *      glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex);
-     *      glBindTexture(GL_TEXTURE_2D, 0);
-     *      glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, img);
-     * External textures are handled as GL_TEXTURE_2D thus the corresponding
-     * host binding will be zero in this case.
-     */
     if (texture->image == image) {
         goto bind;
     }
@@ -273,8 +269,9 @@ void yagl_gles_texture_set_image(struct yagl_gles_texture *texture,
     texture->internalformat = GL_RGBA;
 
 bind:
-    yagl_host_glBindTexture(texture->target == GL_TEXTURE_EXTERNAL_OES ? GL_TEXTURE_2D : texture->target,
-                            texture->global_name);
+    if (texture->target != GL_TEXTURE_EXTERNAL_OES) {
+        yagl_host_glBindTexture(texture->target, texture->global_name);
+    }
 }
 
 void yagl_gles_texture_unset_image(struct yagl_gles_texture *texture)
@@ -291,8 +288,9 @@ void yagl_gles_texture_unset_image(struct yagl_gles_texture *texture)
         texture->global_name = yagl_get_global_name();
 
         yagl_host_glGenTextures(&texture->global_name, 1);
-        yagl_host_glBindTexture(texture->target == GL_TEXTURE_EXTERNAL_OES ? GL_TEXTURE_2D : texture->target,
-                                texture->global_name);
+        if (texture->target != GL_TEXTURE_EXTERNAL_OES) {
+            yagl_host_glBindTexture(texture->target, texture->global_name);
+        }
     }
 }
 
@@ -318,8 +316,9 @@ void yagl_gles_texture_bind_tex_image(struct yagl_gles_texture *texture,
     texture->image = image;
     texture->binding = binding;
 
-    yagl_host_glBindTexture(texture->target == GL_TEXTURE_EXTERNAL_OES ? GL_TEXTURE_2D : texture->target,
-                            texture->global_name);
+    if (texture->target != GL_TEXTURE_EXTERNAL_OES) {
+        yagl_host_glBindTexture(texture->target, texture->global_name);
+    }
 }
 
 /*
index cd9f638f87a83ebf1d59137275af5d1694a5e46b..1cb8d76776ba550485e46a706b42c858e2fba813 100644 (file)
@@ -66,8 +66,11 @@ void yagl_gles_texture_unit_init(struct yagl_gles_texture_unit *texture_unit,
             yagl_object_acquire(sg->texture_zero[i]);
         }
 
-        yagl_gles_texture_bind(texture_unit->target_states[i].texture_zero,
-                               target_map[i]);
+        // TEXTURE_EXTERNAL_OES is manually late-bound, before drawing
+        if (target_map[i] != GL_TEXTURE_EXTERNAL_OES) {
+            yagl_gles_texture_bind(texture_unit->target_states[i].texture_zero,
+                                   target_map[i]);
+        }
 
         yagl_gles_texture_acquire(texture_unit->target_states[i].texture_zero);
         texture_unit->target_states[i].texture =
index 1f641c68008492617b2e387c36a4c58dd69f6c08..f51200a957e59e48fc277193a71f29156b22c6e3 100644 (file)
@@ -1380,6 +1380,42 @@ static void yagl_gles1_draw_elem_psize(struct yagl_gles_context *ctx,
     }
 }
 
+// Below pre-draw and post-draw are GLESv1-exclusive
+// Fixed-funciton GLES needs a bit different handling than programmable GLES
+static void yagl_gles1_context_pre_draw(struct yagl_gles_context *ctx)
+{
+    // In GLESv1 there is a set priority of used texture targets.
+    // TEXTURE_EXTERNAL_OES takes over TEXTURE_2D - we must rebind if that's the case
+    // luckily switching textures
+    GLboolean is_externaloes_enabled = 0;
+    ctx->is_enabled(ctx, GL_TEXTURE_EXTERNAL_OES, &is_externaloes_enabled);
+    if (is_externaloes_enabled) {
+        struct yagl_gles_texture_target_state *texture_external_state;
+
+        texture_external_state =
+            &ctx->texture_units[ctx->active_texture_unit].target_states[yagl_gles_texture_target_external_oes];
+
+        if (texture_external_state->texture != texture_external_state->texture_zero) {
+            yagl_host_glBindTexture(GL_TEXTURE_2D, texture_external_state->texture->global_name);
+            ctx->have_externaloes_rebinds = 1;
+        }
+    }
+}
+
+static void yagl_gles1_context_post_draw(struct yagl_gles_context *ctx)
+{
+    // Return to previous binding to keep consistency for other functions
+    if (ctx->have_externaloes_rebinds) {
+        struct yagl_gles_texture_target_state *texture_2d_state;
+
+        texture_2d_state =
+            &ctx->texture_units[ctx->active_texture_unit].target_states[yagl_gles_texture_target_2d];
+
+        yagl_host_glBindTexture(GL_TEXTURE_2D, texture_2d_state->texture->global_name);
+        ctx->have_externaloes_rebinds = 0;
+    }
+}
+
 static void yagl_gles1_context_draw_arrays(struct yagl_gles_context *ctx,
                                            GLenum mode,
                                            GLint first,
@@ -1392,11 +1428,15 @@ static void yagl_gles1_context_draw_arrays(struct yagl_gles_context *ctx,
         return;
     }
 
+    yagl_gles1_context_pre_draw(ctx);
+
     if ((mode == GL_POINTS) && ctx->vao->arrays[yagl_gles1_array_pointsize].enabled) {
         yagl_gles1_draw_arrays_psize(ctx, first, count);
     } else {
         yagl_host_glDrawArrays(mode, first, count);
     }
+
+    yagl_gles1_context_post_draw(ctx);
 }
 
 static void yagl_gles1_context_draw_elements(struct yagl_gles_context *ctx,
@@ -1414,11 +1454,15 @@ static void yagl_gles1_context_draw_elements(struct yagl_gles_context *ctx,
         return;
     }
 
+    yagl_gles1_context_pre_draw(ctx);
+
     if ((mode == GL_POINTS) && ctx->vao->arrays[yagl_gles1_array_pointsize].enabled) {
         yagl_gles1_draw_elem_psize(ctx, count, type, indices, indices_count);
     } else {
         yagl_host_glDrawElements(mode, count, type, indices, indices_count);
     }
+
+    yagl_gles1_context_post_draw(ctx);
 }
 
 static int yagl_gles1_context_bind_buffer(struct yagl_gles_context *ctx,
index 1f3576cac67a864ac67b78befc897f4b89c2d999..fec51f506cdb081702e803586eb30961bba3b37f 100644 (file)
@@ -1404,6 +1404,8 @@ YAGL_API void glShaderSource(GLuint shader, GLsizei count, const GLchar * const
         }
     }
 
+    YAGL_LOG_INFO("have_strings is %d string is %p", have_strings, string);
+
     if (have_strings) {
         uint8_t *tmp_buff;
         int ret;
index 5b55a032da4d9900de8079e653ebc6b7f9137cb9..b3018e2c562d0e91c3fa708bbdbc39440fb8c0ea 100644 (file)
@@ -638,22 +638,48 @@ void yagl_gles2_context_pre_draw(struct yagl_gles2_context *ctx,
      */
     struct yagl_gles2_shader *fragment_shader = ctx->program->fragment_shader;
     if (fragment_shader->state.have_samplerexternaloes) {
-        struct yagl_glsl_sampler *data = NULL, *it = NULL;
-        int i = 0;
+        struct yagl_glsl_sampler *samplers = NULL;
+        int i = 0, samplers_size = 0;
+
+        ctx->base.have_externaloes_rebinds = 1;
+
+        // FIXME for now assume user is always right and there is no error cases.
+        // Errors to consider:
+        //   * samplerExternalOES and sampler2D attempting to access same unit
+        //   * samplerExternalOES points at unit having no texture bound
+        samplers = yagl_vector_data(&fragment_shader->state.samplers_ExternalOES);
+        samplers_size = yagl_vector_size(&fragment_shader->state.samplers_ExternalOES);
+        for (i = 0; i < samplers_size; ++i) {
+            struct yagl_gles_texture_target_state *texture_2d_state, *texture_external_state;
+
+            texture_2d_state =
+                &ctx->base.texture_units[samplers[i].value].target_states[yagl_gles_texture_target_2d];
+            texture_external_state =
+                &ctx->base.texture_units[samplers[i].value].target_states[yagl_gles_texture_target_external_oes];
+
+            if (texture_external_state->texture == texture_external_state->texture_zero) {
+                YAGL_LOG_WARN("Trying to rebind unbound external texture at unit %d", samplers[i].value);
+                continue; // TODO error GL_INVALID_OPERATION?
+            }
 
-        YAGL_LOG_INFO("LKDEBUG We have samplerExternalOES to handle");
+            if (samplers[i].location == YAGL_GLSL_SAMPLER_LOCATION_UNKNOWN ||
+                samplers[i].value == YAGL_GLSL_SAMPLER_VALUE_UNKNOWN) {
+                YAGL_LOG_WARN("Unknown location/value of uniform %s - skipping", samplers[i].name);
+                continue;
+            }
 
-        // LKDEBUG
-        data = yagl_vector_data(&fragment_shader->state.samplers_ExternalOES);
-        for (i = 0; i < yagl_vector_size(&fragment_shader->state.samplers_ExternalOES); ++i) {
-            it = data + i;
-            YAGL_LOG_INFO("LKDEBUG externaloes sampler %s location %d value %d", it->name, it->location, it->value);
-        }
+            // we could have currently bound either an actual texture 2d or a "zero texture" (for binding = 0)
+            // store whatever is bound from cached info, we'll restore it post-draw
+            if (texture_2d_state->texture != texture_2d_state->texture_zero) {
+                samplers[i].replaced_tex2d = texture_2d_state->texture->global_name;
+            } else {
+                samplers[i].replaced_tex2d = texture_2d_state->texture_zero->global_name;
+            }
+            YAGL_LOG_TRACE("  #%d: slot %d bound texture2D ID %d", i, samplers[i].value, samplers[i].replaced_tex2d);
 
-        data = yagl_vector_data(&fragment_shader->state.samplers_2D);
-        for (i = 0; i < yagl_vector_size(&fragment_shader->state.samplers_2D); ++i) {
-            it = data + i;
-            YAGL_LOG_INFO("LKDEBUG 2d sampler %s location %d value %d", it->name, it->location, it->value);
+            // samplers[i].value is unit requested by app from shader - replace it
+            yagl_host_glActiveTexture(GL_TEXTURE0 + samplers[i].value);
+            yagl_host_glBindTexture(GL_TEXTURE_2D, texture_external_state->texture->global_name);
         }
     }
 }
@@ -687,6 +713,31 @@ void yagl_gles2_context_post_draw(struct yagl_gles2_context *ctx,
          */
         yagl_host_glDisableVertexAttribArray(0);
     }
+
+    /*
+     * Restore original TEXTURE_2D bindings
+     */
+    if (ctx->base.have_externaloes_rebinds) {
+        struct yagl_gles2_shader *fragment_shader = ctx->program->fragment_shader;
+        struct yagl_glsl_sampler *samplers;
+        int i = 0, samplers_size = 0;
+
+        samplers = yagl_vector_data(&fragment_shader->state.samplers_ExternalOES);
+        samplers_size = yagl_vector_size(&fragment_shader->state.samplers_ExternalOES);
+        for (i = 0; i < samplers_size; ++i) {
+            if (samplers[i].location == YAGL_GLSL_SAMPLER_LOCATION_UNKNOWN ||
+                samplers[i].value == YAGL_GLSL_SAMPLER_VALUE_UNKNOWN) {
+                continue;
+            }
+
+            yagl_host_glActiveTexture(GL_TEXTURE0 + samplers[i].value);
+            yagl_host_glBindTexture(GL_TEXTURE_2D, samplers[i].replaced_tex2d);
+            samplers[i].replaced_tex2d = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
+        }
+
+        yagl_host_glActiveTexture(ctx->base.active_texture_unit);
+        ctx->base.have_externaloes_rebinds = 0;
+    }
 }
 
 void yagl_gles2_context_compressed_tex_image_2d(struct yagl_gles_context *gles_ctx,
index e5dd3354eb7ed9921bf79b242769bb3bbb3cd034..3e0f88878a2b1444e345be5ed5a14d65fb4fc57b 100644 (file)
@@ -410,6 +410,28 @@ int yagl_gles2_program_get_uniform_location(struct yagl_gles2_program *program,
         yagl_list_add_tail(&program->uniform_locations_l, &location->list);
     }
 
+
+    /*
+     * We need to perform an extra search and go through our samplers.
+     * This will come in handy on pre_draw/post_draw logic later on.
+     * Luckily, uniform location does not change so it has to be done
+     * only once.
+     */
+    if (program->fragment_shader->state.have_samplerexternaloes) {
+        struct yagl_glsl_sampler *data = NULL, *it = NULL;
+        int i = 0, data_size = 0;
+
+        data = yagl_vector_data(&program->fragment_shader->state.samplers_ExternalOES);
+        data_size = yagl_vector_size(&program->fragment_shader->state.samplers_ExternalOES);
+        for (i = 0; i < data_size; ++i) {
+            it = data + i;
+            if (strcmp(it->name, name) == 0) {
+                it->location = ret;
+                break;
+            }
+        }
+    }
+
     return ret;
 }
 
@@ -658,6 +680,24 @@ int yagl_gles2_program_uniform1i(struct yagl_gles2_program *program,
 
     yagl_host_glUniform1i(program->gen_locations, global_location, x);
 
+    /*
+     * Update fragment shader state for pre_draw/post_draw analysis
+     */
+    if (program->fragment_shader->state.have_samplerexternaloes) {
+        struct yagl_glsl_sampler *data = NULL, *it = NULL;
+        int i = 0, data_size = 0;
+
+        data = yagl_vector_data(&program->fragment_shader->state.samplers_ExternalOES);
+        data_size = yagl_vector_size(&program->fragment_shader->state.samplers_ExternalOES);
+        for (i = 0; i < data_size; ++i) {
+            it = data + i;
+            if (it->location == location) {
+                it->value = x;
+                break;
+            }
+        }
+    }
+
     return 1;
 }
 
index 097cb15f30680796ab549bba39c6a1799bbe964c..c1c3a5d5c96d2fe7ac739dcc015d3c190dfc3160 100644 (file)
@@ -310,6 +310,7 @@ void yagl_glsl_state_add_sampler_ExternalOES(struct yagl_glsl_state *state,
     sampler.name = strdup(str);
     sampler.location = YAGL_GLSL_SAMPLER_LOCATION_UNKNOWN;
     sampler.value = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
+    sampler.replaced_tex2d = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
     yagl_vector_push_back(&state->samplers_ExternalOES, &sampler);
 }
 
@@ -320,5 +321,6 @@ void yagl_glsl_state_add_sampler_2D(struct yagl_glsl_state *state,
     sampler.name = strdup(str);
     sampler.location = YAGL_GLSL_SAMPLER_LOCATION_UNKNOWN;
     sampler.value = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
+    sampler.replaced_tex2d = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
     yagl_vector_push_back(&state->samplers_2D, &sampler);
 }
index a2f158a3290f5eceb897ce0752c6c9dac59cec0a..2f9184f6783e375759ec631363715267d99c7115 100644 (file)
@@ -57,6 +57,7 @@ struct yagl_glsl_sampler
     char* name;
     int location;
     int value;
+    int replaced_tex2d; // only used in samplerExternalOES handling
 };
 
 typedef enum