From f335540e9982bf229d4e8b6a8b4b998828f064e9 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sun, 1 Sep 2024 16:56:35 +0900 Subject: [PATCH] [qfcc] Handle glsl version and extension directives The version directive really does only some error checking, and only GL_EXT_multiview and GL_GOOGLE_include_directive are supported for extensions, but enable/disable work (but not yet warn for multiview). --- tools/qfcc/include/glsl-lang.h | 4 ++ tools/qfcc/include/rua-lang.h | 8 +++ tools/qfcc/source/glsl-builtins.c | 49 +++++++++++++ tools/qfcc/source/glsl-parse.y | 111 ++++++++++++++++++++++++++++++ tools/qfcc/source/pre-parse.y | 25 +++++-- tools/qfcc/source/qc-lex.l | 10 ++- tools/qfcc/source/qfcc.c | 7 +- tools/qfcc/source/shared.c | 1 + 8 files changed, 206 insertions(+), 9 deletions(-) diff --git a/tools/qfcc/include/glsl-lang.h b/tools/qfcc/include/glsl-lang.h index a51cdb7ab..df67b166e 100644 --- a/tools/qfcc/include/glsl-lang.h +++ b/tools/qfcc/include/glsl-lang.h @@ -58,4 +58,8 @@ void glsl_block_clear (void); void glsl_declare_block (struct specifier_s spec, struct symbol_s *block_sym, struct symbol_s *instance_name); +bool glsl_on_include (const char *name); +void glsl_include (int behavior, void *scanner); +void glsl_multiview (int behavior, void *scanner); + #endif//__glsl_lang_h diff --git a/tools/qfcc/include/rua-lang.h b/tools/qfcc/include/rua-lang.h index d528d73d8..7bcc7139f 100644 --- a/tools/qfcc/include/rua-lang.h +++ b/tools/qfcc/include/rua-lang.h @@ -28,6 +28,8 @@ #ifndef __rua_lang_h #define __rua_lang_h +#include + #include "QF/dstring.h" #include "specifier.h" @@ -189,11 +191,17 @@ const char *rua_directive_get_key (const void *dir, void *unused) __attribute__( const char *rua_keyword_get_key (const void *dir, void *unused) __attribute__((pure)); typedef struct language_s { + bool initialized; void (*init) (void); int (*parse) (FILE *in); int (*finish) (const char *file); + void (*extension) (const char *name, const char *value, void *scanner); + void (*version) (int version, const char *profile); + bool (*on_include) (const char *name); } language_t; +extern language_t current_language; + extern language_t lang_ruamoko; extern language_t lang_pascal; diff --git a/tools/qfcc/source/glsl-builtins.c b/tools/qfcc/source/glsl-builtins.c index 611d19837..1e6c48147 100644 --- a/tools/qfcc/source/glsl-builtins.c +++ b/tools/qfcc/source/glsl-builtins.c @@ -28,7 +28,9 @@ # include "config.h" #endif +#include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/glsl-lang.h" +#include "tools/qfcc/include/rua-lang.h" #define SRC_LINE_EXP2(l,f) "#line " #l " " #f "\n" #define SRC_LINE_EXP(l,f) SRC_LINE_EXP2(l,f) @@ -41,6 +43,9 @@ SRC_LINE "in int gl_DrawID;" "\n" "in int gl_BaseVertex;" "\n" "in int gl_BaseInstance;" "\n" +"#ifdef GL_EXT_multiview" "\n" +"highp int gl_ViewIndex;" "\n" +"#endif" "\n" "out gl_PerVertex {" "\n" " vec4 gl_Position;" "\n" " float gl_PointSize;" "\n" @@ -60,6 +65,9 @@ SRC_LINE "in int gl_PatchVerticesIn;" "\n" "in int gl_PrimitiveID;" "\n" "in int gl_InvocationID;" "\n" +"#ifdef GL_EXT_multiview" "\n" +"highp int gl_ViewIndex;" "\n" +"#endif" "\n" "out gl_PerVertex {" "\n" " vec4 gl_Position;" "\n" " float gl_PointSize;" "\n" @@ -82,6 +90,9 @@ SRC_LINE "in vec3 gl_TessCoord;" "\n" "patch in float gl_TessLevelOuter[4];" "\n" "patch in float gl_TessLevelInner[2];" "\n" +"#ifdef GL_EXT_multiview" "\n" +"highp int gl_ViewIndex;" "\n" +"#endif" "\n" "out gl_PerVertex {" "\n" " vec4 gl_Position;" "\n" " float gl_PointSize;" "\n" @@ -99,6 +110,9 @@ SRC_LINE "} gl_in[];" "\n" "in int gl_PrimitiveIDIn;" "\n" "in int gl_InvocationID;" "\n" +"#ifdef GL_EXT_multiview" "\n" +"highp int gl_ViewIndex;" "\n" +"#endif" "\n" "out gl_PerVertex {" "\n" " vec4 gl_Position;" "\n" " float gl_PointSize;" "\n" @@ -123,6 +137,9 @@ SRC_LINE "in int gl_Layer;" "\n" "in int gl_ViewportIndex;" "\n" "in bool gl_HelperInvocation;" "\n" +"#ifdef GL_EXT_multiview" "\n" +"highp int gl_ViewIndex;" "\n" +"#endif" "\n" "out float gl_FragDepth;" "\n" "out int gl_SampleMask[];" "\n"; @@ -817,6 +834,37 @@ bool allInvocations(bool value) bool allInvocationsEqual(bool value) #endif +void +glsl_multiview (int behavior, void *scanner) +{ + if (behavior) { + rua_parse_define ("GL_EXT_multiview 1\n"); + } else { + rua_undefine ("GL_EXT_multiview", scanner); + } +} + +static int glsl_include_state = 0; + +bool +glsl_on_include (const char *name) +{ + if (!glsl_include_state) { + error (0, "'#include' : required extension not requested"); + return false; + } + if (glsl_include_state > 1) { + warning (0, "'#include' : required extension not requested"); + } + return true; +} + +void +glsl_include (int behavior, void *scanner) +{ + glsl_include_state = behavior; +} + static void glsl_parse_vars (const char *var_src) { @@ -826,6 +874,7 @@ glsl_parse_vars (const char *var_src) static void glsl_init_common (void) { + current_language.initialized = true; glsl_block_clear (); glsl_parse_vars (glsl_system_constants); } diff --git a/tools/qfcc/source/glsl-parse.y b/tools/qfcc/source/glsl-parse.y index bdd95b658..95851cd00 100644 --- a/tools/qfcc/source/glsl-parse.y +++ b/tools/qfcc/source/glsl-parse.y @@ -1492,32 +1492,143 @@ glsl_parse_string (const char *str) return ret; } +static const char *glsl_behaviors[] = { + "disable", + "enable", + "require", + "warn", +}; +#define num_behaviors (sizeof (glsl_behaviors) / sizeof (glsl_behaviors[0])) + +typedef struct { + const char *name; + void (*set_behavior) (int behavior, void *scanner); +} glsl_ext_t; + +static glsl_ext_t glsl_extensions[] = { + {"GL_EXT_multiview", glsl_multiview }, + {"GL_GOOGLE_include_directive", glsl_include }, +}; +#define num_extensions (sizeof (glsl_extensions) / sizeof (glsl_extensions[0])) + +static int +extension_cmp (const void *_a, const void *_b) +{ + auto a = (const glsl_ext_t *) _a; + auto b = (const glsl_ext_t *) _b; + return strcmp (a->name, b->name); +} + +static int +behavior_cmp (const void *_a, const void *_b) +{ + auto a = (const char **) _a; + auto b = (const char **) _b; + return strcmp (*a, *b); +} + +static void +glsl_extension (const char *name, const char *value, void *scanner) +{ + if (current_language.initialized) { + error (0, "extensions directives must occur before any " + "non-preprocessor tokens"); + return; + } + const char **res; + res = bsearch (&value, glsl_behaviors, num_behaviors, sizeof (char *), + behavior_cmp); + if (!res) { + error (0, "invalid behavior: %s", value); + return; + } + int behavior = res - glsl_behaviors; + if (strcmp (name, "all") == 0) { + if (behavior != 0 && behavior != 3) { + error (0, "behavior must be 'warn' or 'disable' for 'all'"); + return; + } + for (size_t i = 0; i < num_extensions; i++) { + glsl_extensions[i].set_behavior (behavior, scanner); + } + } else { + glsl_ext_t key = { .name = name }; + glsl_ext_t *ext; + ext = bsearch (&key, glsl_extensions, num_extensions, + sizeof (glsl_ext_t), extension_cmp); + if (ext) { + ext->set_behavior (behavior, scanner); + } else { + if (behavior == 2) { + error (0, "unknown extension '%s'", name); + } else { + warning (0, "unknown extension '%s'", name); + } + } + } +} + +static void +glsl_version (int version, const char *profile) +{ + if (!profile || strcmp (profile, "core") == 0) { + // ok + } else if (strcmp (profile, "es") == 0 + || strcmp (profile, "compatibility") == 0) { + error (0, "profile '%s' not supported", profile); + } else { + error (0, "bad profile name '%s': use es, core or compatibility", + profile); + } + if (version != 450 && version != 460) { + error (0, "version not supported"); + } +} + language_t lang_glsl_comp = { .init = glsl_init_comp, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; language_t lang_glsl_vert = { .init = glsl_init_vert, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; language_t lang_glsl_tesc = { .init = glsl_init_tesc, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; language_t lang_glsl_tese = { .init = glsl_init_tese, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; language_t lang_glsl_geom = { .init = glsl_init_geom, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; language_t lang_glsl_frag = { .init = glsl_init_frag, .parse = glsl_yyparse, + .extension = glsl_extension, + .version = glsl_version, + .on_include = glsl_on_include, }; diff --git a/tools/qfcc/source/pre-parse.y b/tools/qfcc/source/pre-parse.y index edcc32fe2..1293a8353 100644 --- a/tools/qfcc/source/pre-parse.y +++ b/tools/qfcc/source/pre-parse.y @@ -138,7 +138,7 @@ parse_error (void *scanner) %token CONCAT ARGS %token EXTENSION VERSION -%type string +%type string opt_profile %type params opt_params body arg arg_list arg_clist %type text text_text %type unary_expr expr id defined defined_id line_expr @@ -247,12 +247,29 @@ directive extra_warn | ENDIF { rua_endif (scanner); } extra_warn - | EXTENSION ID ':' ID eod - | VERSION VALUE opt_profile eod + | EXTENSION ID ':' ID + { + if (current_language.extension) { + current_language.extension ($2, $4, scanner); + } else { + internal_error (0, "invalid directive"); + } + } + extra_warn + | VERSION VALUE opt_profile + { + if (current_language.version) { + auto version = get_long ($2, $2, 460); + current_language.version (expr_long (version), $3); + } else { + internal_error (0, "invalid directive"); + } + } + extra_warn ; opt_profile - : /* empty */ + : /* empty */ { $$ = nullptr; } | ID ; diff --git a/tools/qfcc/source/qc-lex.l b/tools/qfcc/source/qc-lex.l index d8fa12941..b8804fb08 100644 --- a/tools/qfcc/source/qc-lex.l +++ b/tools/qfcc/source/qc-lex.l @@ -427,8 +427,6 @@ directive (const char *token, yyscan_t scanner) extra->preprocessor = false; return -1; } - //auto lex = qc_yyget_extra (scanner); - //lex->current_lang = &lex->pre_lang; return directive->value; } @@ -1167,6 +1165,9 @@ qc_process (rua_extra_t *extra, int token, rua_tok_t *tok, yyscan_t scanner) } return token ? YYPUSH_MORE : 0; } + if (!current_language.initialized && current_language.init) { + current_language.init (); + } QC_YYSTYPE lval = {}; token = qc_token (extra, &lval, tok, scanner); if (token >= 0) { @@ -2141,6 +2142,11 @@ rua_include_file (const char *name, void *scanner) if (extra->suppressed) { return; } + if (current_language.on_include) { + if (!current_language.on_include (name)) { + return; + } + } struct yyguts_t * yyg = (struct yyguts_t*)scanner;//FIXME int quote = *name; name = make_string (name, 0); diff --git a/tools/qfcc/source/qfcc.c b/tools/qfcc/source/qfcc.c index 66c0cd930..b8fecd139 100644 --- a/tools/qfcc/source/qfcc.c +++ b/tools/qfcc/source/qfcc.c @@ -385,14 +385,14 @@ compile_to_obj (const char *file, const char *obj, language_t *lang) return !options.preprocess_only; } + current_language = *lang; + InitData (); chain_initial_types (); begin_compilation (); pr.comp_dir = save_cwd (); add_source_file (file); - if (lang->init) { - lang->init (); - } + lang->initialized = false; err = lang->parse (yyin) || pr.error_count; fclose (yyin); if (cpp_name && !options.save_temps) { @@ -672,6 +672,7 @@ compile_file (const char *filename) FILE *yyin; int (*yyparse) (FILE *in) = lang_ruamoko.parse; + current_language = lang_ruamoko; yyin = preprocess_file (filename, 0); if (options.preprocess_only || !yyin) return !options.preprocess_only; diff --git a/tools/qfcc/source/shared.c b/tools/qfcc/source/shared.c index 439abdd75..c3934156e 100644 --- a/tools/qfcc/source/shared.c +++ b/tools/qfcc/source/shared.c @@ -40,6 +40,7 @@ #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" +language_t current_language; function_t *current_func; class_type_t *current_class; expr_t *local_expr;