yagl_glsl_parser: Fix support for uniform arrays 88/264988/1 accepted/tizen/unified/20211007.052005 submit/tizen/20211007.015901
authorLukasz Kostyra <l.kostyra@samsung.com>
Wed, 6 Oct 2021 12:08:08 +0000 (14:08 +0200)
committerLukasz Kostyra <l.kostyra@samsung.com>
Wed, 6 Oct 2021 12:32:38 +0000 (14:32 +0200)
Uniform arrays were incorrectly processed by GLSL ES parser, which in
some situations resulted in shader compilation errors. Uniform array
support has been added and sampler replacement code has been slightly
adjusted to read amount of sampler units from shader code.

Note that this only fixes shader parsing code to allow successful
compilation of shaders. This does NOT yet add full support for
samplerExternalOES uniform arrays for GLES 2 and above.

Change-Id: I24ead4473eb51e3e9d13b39fd98186763c6a5384

GLESv2/CMakeLists.txt
GLESv2/yagl_gles2_calls.c
GLESv2/yagl_glsl_lexer.l
GLESv2/yagl_glsl_parser.y
GLESv2/yagl_glsl_state.c
GLESv2/yagl_glsl_state.h

index 5a97d533f84468647a85c5f87868006355db2a5e..c22c91db4fa4018df086250cfecee57dfac8e581 100644 (file)
@@ -27,11 +27,12 @@ include_directories(.)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 link_directories(${CMAKE_ARCHIVE_OUTPUT_DIRECTORY})
 
-flex_target(GLESv2 ${CMAKE_CURRENT_SOURCE_DIR}/yagl_glsl_lexer.l ${CMAKE_CURRENT_BINARY_DIR}/yagl_glsl_lexer.c)
-bison_target(GLESv2 ${CMAKE_CURRENT_SOURCE_DIR}/yagl_glsl_parser.y ${CMAKE_CURRENT_BINARY_DIR}/yagl_glsl_parser.c
+flex_target(GLSL_lexer ${CMAKE_CURRENT_SOURCE_DIR}/yagl_glsl_lexer.l ${CMAKE_CURRENT_BINARY_DIR}/yagl_glsl_lexer.c)
+bison_target(GLSL_parser ${CMAKE_CURRENT_SOURCE_DIR}/yagl_glsl_parser.y ${CMAKE_CURRENT_BINARY_DIR}/yagl_glsl_parser.c
              VERBOSE)
+add_flex_bison_dependency(GLSL_lexer GLSL_parser)
 
-add_library(GLESv2 SHARED ${SOURCES} ${FLEX_GLESv2_OUTPUTS} ${BISON_GLESv2_OUTPUTS})
+add_library(GLESv2 SHARED ${SOURCES} ${FLEX_GLSL_lexer_OUTPUTS} ${BISON_GLSL_parser_OUTPUTS})
 set_target_properties(GLESv2 PROPERTIES VERSION 2.0 SOVERSION 2)
 
 target_link_libraries(GLESv2 "-Wl,-whole-archive" GLES_common "-Wl,-no-whole-archive")
index ba0468ccb0e6957a7cf56214ad2125ad5e80df40..3978473f612bf43f23d9b21820097e5114fa5469 100644 (file)
@@ -1482,14 +1482,16 @@ YAGL_API void glShaderSource(GLuint shader, GLsizei count, const GLchar * const
             samplers = yagl_vector_data(&shader_obj->state.samplers_ExternalOES);
             size = yagl_vector_size(&shader_obj->state.samplers_ExternalOES);
             for (i = 0; i < size; ++i) {
-                YAGL_LOG_TRACE("    -> %s: at %d unit %d", samplers[i].name, samplers[i].location, samplers[i].value);
+                YAGL_LOG_TRACE("    -> %s: at %d unit %d count %d",
+                    samplers[i].name, samplers[i].location, samplers[i].value, samplers[i].count);
             }
 
             YAGL_LOG_TRACE("  sampler2D uniforms:");
             samplers = yagl_vector_data(&shader_obj->state.samplers_2D);
             size = yagl_vector_size(&shader_obj->state.samplers_2D);
             for (i = 0; i < size; ++i) {
-                YAGL_LOG_TRACE("    -> %s: at %d unit %d", samplers[i].name, samplers[i].location, samplers[i].value);
+                YAGL_LOG_TRACE("    -> %s: at %d unit %d count %d",
+                    samplers[i].name, samplers[i].location, samplers[i].value, samplers[i].count);
             }
 
             yagl_gles2_shader_source(shader_obj,
index f71fb0338e8d2c3f97ccf774bf358fe8a10ef11b..ac7271c9a5c4b4175066b21613f3b6b700af58ac 100644 (file)
@@ -377,6 +377,14 @@ PP_FUNCTION {STRING}{WS}*\([^\n]+
     return TOK_PP_IF_EOL;
 }
 
+<PP_IF>\] {
+    // for handling expression resolution in-code
+    struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
+    BEGIN(UNIFORM);
+    yagl_glsl_state_new_character_token(state, yylval, *yytext);
+    return TOK_ARR_PAREN_CLOSE;
+}
+
 <PP_IF>\/\/[^\n]* {
     struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
     yagl_glsl_state_new_comment(state, strlen(yytext));
@@ -552,22 +560,11 @@ PP_FUNCTION {STRING}{WS}*\([^\n]+
 
 <UNIFORM>\[ {
     struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
+    BEGIN(PP_IF);
     yagl_glsl_state_new_character_token(state, yylval, *yytext);
     return TOK_ARR_PAREN_OPEN;
 }
 
-<UNIFORM>\] {
-    struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
-    yagl_glsl_state_new_character_token(state, yylval, *yytext);
-    return TOK_ARR_PAREN_CLOSE;
-}
-
-<UNIFORM>[0-9]* {
-    struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
-    yagl_glsl_state_new_integer_token(state, yylval, strtol(yytext, NULL, 10));
-    return TOK_INTEGER;
-}
-
 <UNIFORM>{STRING} {
     struct yagl_glsl_state *state = yagl_glsl_lexer_get_extra(yyscanner);
     yagl_glsl_state_new_str_token(state, yylval, yytext);
@@ -641,6 +638,7 @@ int yagl_glsl_state_init(struct yagl_glsl_state *state,
     state->es3_supported = es3_supported;
     state->scanner = scanner;
     state->token_index = 1;
+    state->last_declared_sampler_externaloes = -1;
     memset(&state->pp_conditions, 0, sizeof(int) * YAGL_GLSL_PP_CONDITION_STACK_SIZE);
     state->pp_current_condition = 0;
     state->pp_condition_parse_started = 0;
index 095bd71dc09d8190bde1bbceceec5ecb893ab8ba..74e5246411d443f4a57fb304e2214d01b3277a44 100644 (file)
@@ -329,7 +329,7 @@ expression
     yagl_glsl_state_append_output_char(state, $1.c);
 
     if (state->pp_condition_parse_started) {
-        yagl_glsl_pp_condition_status status = yagl_glsl_state_pp_condition_resolve(state);
+        yagl_glsl_pp_condition_status status = yagl_glsl_state_pp_condition_resolve(state, NULL);
         if (status == yagl_glsl_pp_condition_error) {
             yyerror(state, "GLSL preprocessor condition resolution failure");
         } else {
@@ -673,10 +673,8 @@ expression
         yagl_glsl_state_append_output(state, $1.value);
     }
 }
-| TOK_UNIFORM TOK_STRING TOK_STRING TOK_STRING TOK_ARR_PAREN_OPEN TOK_INTEGER TOK_ARR_PAREN_CLOSE TOK_EOI
+| TOK_UNIFORM TOK_STRING TOK_STRING TOK_STRING TOK_EOI
 {
-    char s[100];
-
     yagl_glsl_state_flush_pending(state, $1.index);
     yagl_glsl_state_append_output(state, $1.value);
     yagl_glsl_state_flush_pending(state, $2.index);
@@ -687,15 +685,7 @@ expression
     yagl_glsl_state_append_output(state, $4.value);
     yagl_glsl_state_flush_pending(state, $5.index);
     yagl_glsl_state_append_output_char(state, $5.c);
-    yagl_glsl_state_flush_pending(state, $6.index);
-    snprintf(s, 100, "%d", $6.value);
-    yagl_glsl_state_append_output(state, s);
-    yagl_glsl_state_flush_pending(state, $7.index);
-    yagl_glsl_state_append_output_char(state, $7.c);
-    yagl_glsl_state_flush_pending(state, $8.index);
-    yagl_glsl_state_append_output_char(state, $8.c);
 
-    // TODO this should also take into account TOK_INTEGER, because it could be an array of samplers
     if (yagl_glsl_state_pp_is_condition_met(state)) {
         // locally try to resolve the define based on current knowledge
         // it won't matter if our type is not a macro but an actual sampler type
@@ -715,14 +705,13 @@ expression
                 yagl_glsl_state_add_sampler_2D(state, $4.value);
             }
 
+            yagl_glsl_state_set_last_sampler_array_count(state, 1);
             free(type_resolved);
         }
     }
 }
-| TOK_UNIFORM TOK_STRING TOK_STRING TOK_ARR_PAREN_OPEN TOK_INTEGER TOK_ARR_PAREN_CLOSE TOK_EOI
+| TOK_UNIFORM TOK_STRING TOK_STRING TOK_EOI
 {
-    char s[100];
-
     yagl_glsl_state_flush_pending(state, $1.index);
     yagl_glsl_state_append_output(state, $1.value);
     yagl_glsl_state_flush_pending(state, $2.index);
@@ -731,15 +720,7 @@ expression
     yagl_glsl_state_append_output(state, $3.value);
     yagl_glsl_state_flush_pending(state, $4.index);
     yagl_glsl_state_append_output_char(state, $4.c);
-    yagl_glsl_state_flush_pending(state, $5.index);
-    snprintf(s, 100, "%d", $5.value);
-    yagl_glsl_state_append_output(state, s);
-    yagl_glsl_state_flush_pending(state, $6.index);
-    yagl_glsl_state_append_output_char(state, $6.c);
-    yagl_glsl_state_flush_pending(state, $7.index);
-    yagl_glsl_state_append_output_char(state, $7.c);
 
-    // TODO this should also take into account TOK_INTEGER, because it is an array of samplers (not a single sampler)
     if (yagl_glsl_state_pp_is_condition_met(state)) {
         // locally try to resolve the define based on current knowledge
         // it won't matter if our type is not a macro but an actual sampler type
@@ -759,11 +740,12 @@ expression
                 yagl_glsl_state_add_sampler_2D(state, $3.value);
             }
 
+            yagl_glsl_state_set_last_sampler_array_count(state, 1);
             free(type_resolved);
         }
     }
 }
-| TOK_UNIFORM TOK_STRING TOK_STRING TOK_STRING TOK_EOI
+| TOK_UNIFORM TOK_STRING TOK_STRING TOK_STRING TOK_ARR_PAREN_OPEN
 {
     yagl_glsl_state_flush_pending(state, $1.index);
     yagl_glsl_state_append_output(state, $1.value);
@@ -790,16 +772,19 @@ expression
                     state->have_samplerexternaloes = 1;
                 }
 
+                // TODO add how many samplers are added too - check resolution_value
                 yagl_glsl_state_add_sampler_ExternalOES(state, $4.value);
+                yagl_glsl_state_pp_condition_parse_start(state);
             } else if (strcmp(type_resolved, "sampler2D") == 0) {
                 yagl_glsl_state_add_sampler_2D(state, $4.value);
+                yagl_glsl_state_pp_condition_parse_start(state);
             }
 
             free(type_resolved);
         }
     }
 }
-| TOK_UNIFORM TOK_STRING TOK_STRING TOK_EOI
+| TOK_UNIFORM TOK_STRING TOK_STRING TOK_ARR_PAREN_OPEN
 {
     yagl_glsl_state_flush_pending(state, $1.index);
     yagl_glsl_state_append_output(state, $1.value);
@@ -825,14 +810,42 @@ expression
                 }
 
                 yagl_glsl_state_add_sampler_ExternalOES(state, $3.value);
+                yagl_glsl_state_pp_condition_parse_start(state);
             } else if (strcmp(type_resolved, "sampler2D") == 0) {
                 yagl_glsl_state_add_sampler_2D(state, $3.value);
+                yagl_glsl_state_pp_condition_parse_start(state);
             }
 
             free(type_resolved);
         }
     }
 }
+| TOK_ARR_PAREN_CLOSE TOK_EOI
+{
+    yagl_glsl_state_flush_pending(state, $1.index);
+    yagl_glsl_state_append_output_char(state, $1.c);
+    yagl_glsl_state_flush_pending(state, $2.index);
+    yagl_glsl_state_append_output_char(state, $2.c);
+
+    // check our expression resolution result to know how many array items we have
+    if (state->pp_condition_parse_started) {
+        int resolution_value = 0;
+        yagl_glsl_pp_condition_status status = yagl_glsl_state_pp_condition_resolve(state, &resolution_value);
+        if (yagl_glsl_state_pp_is_condition_met(state)) {
+            if (status == yagl_glsl_pp_condition_error) {
+                yyerror(state, "GLSL preprocessor expression resolution failure");
+            }
+
+            if (resolution_value <= 0) {
+                yyerror(state, "GLSL declared uniform array of size less than (or equal) to 0");
+            }
+
+            // TODO sampler array is rarely used, but it still has to be properly processed
+            //      at samplerExternalOES -> sampler2D substitution stage
+            yagl_glsl_state_set_last_sampler_array_count(state, resolution_value);
+        }
+    }
+}
 | TOK_TEXTURE1D
 {
     yagl_glsl_state_flush_pending(state, $1.index);
index c6e9f4897520e2a6e80c2a896ee2a5b404375152..86190ec892a96e4ae0d4bb69d6ed866598b90074 100644 (file)
@@ -408,8 +408,11 @@ 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 = 0; // GL spec predefines uniform value as 0
+    sampler.count = YAGL_GLSL_SAMPLER_DEFAULT_COUNT; // real count provided by yagl_glsl_state_set_last_sampler_array_count
     sampler.replaced_tex2d = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
     yagl_vector_push_back(&state->samplers_ExternalOES, &sampler);
+
+    state->last_declared_sampler_externaloes = 1;
 }
 
 void yagl_glsl_state_add_sampler_2D(struct yagl_glsl_state *state,
@@ -419,8 +422,31 @@ 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 = 0; // GL spec predefines uniform value as 0
+    sampler.count = YAGL_GLSL_SAMPLER_DEFAULT_COUNT; // real count provided by yagl_glsl_state_set_last_sampler_array_count
     sampler.replaced_tex2d = YAGL_GLSL_SAMPLER_VALUE_UNKNOWN;
     yagl_vector_push_back(&state->samplers_2D, &sampler);
+
+    state->last_declared_sampler_externaloes = 0;
+}
+
+void yagl_glsl_state_set_last_sampler_array_count(struct yagl_glsl_state *state, int count)
+{
+    assert(state->last_declared_sampler_externaloes != -1);
+
+    struct yagl_vector *samplers = NULL;
+    if (state->last_declared_sampler_externaloes) {
+        samplers = &state->samplers_ExternalOES;
+    } else {
+        samplers = &state->samplers_2D;
+    }
+
+    int samplers_size = yagl_vector_size(samplers);
+    assert(samplers_size > 0);
+    struct yagl_glsl_sampler *samplers_data = (struct yagl_glsl_sampler *)yagl_vector_data(samplers);
+    samplers_data += samplers_size - 1;
+    samplers_data->count = count;
+
+    state->last_declared_sampler_externaloes = -1;
 }
 
 void yagl_glsl_state_pp_add_define_string(struct yagl_glsl_state *state,
@@ -583,6 +609,8 @@ int yagl_glsl_state_pp_is_condition_completed(struct yagl_glsl_state *state)
 
 void yagl_glsl_state_pp_condition_parse_start(struct yagl_glsl_state *state)
 {
+    assert(!state->pp_condition_parse_started);
+
     // initialize expression stack and operation stack for RPN
     memset(state->pp_exprs, 0, sizeof(struct yagl_glsl_pp_expr) * YAGL_GLSL_PP_EXPRESSION_STACK_SIZE);
     state->pp_current_expr = 0;
@@ -785,7 +813,8 @@ static inline int yagl_glsl_pp_expr_op_is_unary(yagl_glsl_pp_expr_op op)
     }
 }
 
-yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_glsl_state *state)
+yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_glsl_state *state,
+                                                                   int *resolution_value)
 {
     struct yagl_glsl_pp_token res_stack[YAGL_GLSL_PP_EXPRESSION_STACK_SIZE];
     int res_cur = 0;
@@ -794,6 +823,8 @@ yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_g
     yagl_glsl_pp_condition_status result = yagl_glsl_pp_condition_not_met;
     YAGL_LOG_FUNC_SET(yagl_glsl_state_pp_condition_resolve);
 
+    assert(state->pp_condition_parse_started);
+
     // empty op stack into expression stack
     while (state->pp_current_op > 0) {
         assert(state->pp_current_expr < YAGL_GLSL_PP_EXPRESSION_STACK_SIZE);
@@ -837,7 +868,29 @@ yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_g
 
     // there should be only one resolution on stack remaining - the result
     assert(res_cur == 1);
-    result = (res_stack[res_cur - 1].value > 0 ? yagl_glsl_pp_condition_met : yagl_glsl_pp_condition_not_met);
+
+    // in some cases resolution can be just a preprocessor macro - try resolving it
+    int result_value = 0;
+    if (res_stack[res_cur - 1].macro != NULL) {
+        char* define_resolution = NULL; // should stay null, but if it doesn't it's syntax error
+        yagl_glsl_state_pp_resolve_define(state, res_stack[res_cur - 1].macro, &define_resolution, &result_value);
+        if (define_resolution != NULL) {
+            // error - macro did not become a constant, cannot evaluate result
+            YAGL_LOG_ERROR("Expression resolution error - macro %s did not evaluate to integer constant", res_stack[res_cur - 1].macro);
+            result = yagl_glsl_pp_condition_error;
+            yagl_free(define_resolution);
+            goto clean;
+        }
+    } else {
+        result_value = res_stack[res_cur - 1].value;
+    }
+
+    // determine final result (and pass the value if it's needed)
+    result = (result_value > 0 ? yagl_glsl_pp_condition_met : yagl_glsl_pp_condition_not_met);
+    if (resolution_value != NULL) {
+        *resolution_value = result_value;
+        YAGL_LOG_TRACE("Expression resolution value: %d", *resolution_value);
+    }
 
 clean:
     // cleanup
index 1d334da58ff195e7f39a9e94a06c4744ad0ad55b..fedd211935c66f35fbde6384b817ebaa32846eaa 100644 (file)
@@ -51,6 +51,7 @@ struct yagl_glsl_pending
 
 #define YAGL_GLSL_SAMPLER_LOCATION_UNKNOWN (-1)
 #define YAGL_GLSL_SAMPLER_VALUE_UNKNOWN (-1)
+#define YAGL_GLSL_SAMPLER_DEFAULT_COUNT (0)
 #define YAGL_GLSL_PP_CONDITION_STACK_SIZE 32
 #define YAGL_GLSL_PP_EXPRESSION_STACK_SIZE 128
 #define YAGL_GLSL_PP_OPERATION_STACK_SIZE 64
@@ -67,6 +68,7 @@ struct yagl_glsl_sampler
     char* name;
     int location;
     int value;
+    int count; // for uniform array support
     int replaced_tex2d; // only used in samplerExternalOES handling
 };
 
@@ -186,6 +188,9 @@ struct yagl_glsl_state
     int texturecube_declared;
     int texturecubelod_declared;
 
+    // used for determining which sampler group was used last
+    int last_declared_sampler_externaloes;
+
     // Each token is assigned an index.
     int token_index;
 
@@ -294,6 +299,10 @@ void yagl_glsl_state_add_sampler_ExternalOES(struct yagl_glsl_state *state,
 void yagl_glsl_state_add_sampler_2D(struct yagl_glsl_state *state,
                                     const char *str);
 
+// Reaches last added sampler (ExternalOES or 2D) and adds information on how many samplers there are
+// count can be 1 for singular samplers and more for sampler arrays
+void yagl_glsl_state_set_last_sampler_array_count(struct yagl_glsl_state *state, int count);
+
 /*
  * GLSL Preprocessor control functions
  * @{
@@ -335,7 +344,8 @@ void yagl_glsl_state_pp_condition_parse_add_expr(struct yagl_glsl_state *state,
 void yagl_glsl_state_pp_condition_parse_add_op(struct yagl_glsl_state *state, yagl_glsl_pp_expr_op op);
 
 // resolve preprocessor condition parser; 1 if met, 0 if not met, -1 on error
-yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_glsl_state *state);
+// detailed information on resolution value can be optionally acquired via resolution_value param
+yagl_glsl_pp_condition_status yagl_glsl_state_pp_condition_resolve(struct yagl_glsl_state *state, int *resolution_value);
 /*
  * @}
  */