[Mesa-dev] [PATCH 1/2] Allow for GLSL shaders to have #extension directive anywhere in source file. (reworked)
Kevin Rogovin
kevin.rogovin at intel.com
Tue Dec 3 04:47:04 PST 2013
Allow for GLSL shaders to have #extension directive
anywhere in source file; during preprocessor, #extension
directives are saved to a list rather than being added
to the pre-processed string. That list is played back
before parsing of pre-processed text takes place.
---
src/glsl/Makefile.sources | 3 +-
src/glsl/glcpp/glcpp-lex.l | 20 +++++++-
src/glsl/glcpp/glcpp-parse.y | 30 ++++++++++-
src/glsl/glcpp/glcpp.c | 2 +-
src/glsl/glcpp/glcpp.h | 17 +++++-
src/glsl/glcpp/glcpp_extras.c | 111 ++++++++++++++++++++++++++++++++++++++++
src/glsl/glcpp/glcpp_extras.h | 97 +++++++++++++++++++++++++++++++++++
src/glsl/glcpp/pp.c | 5 +-
src/glsl/glsl_parser_extras.cpp | 56 +++++++++++++++++++-
src/glsl/glsl_parser_extras.h | 14 ++++-
10 files changed, 344 insertions(+), 11 deletions(-)
create mode 100644 src/glsl/glcpp/glcpp_extras.c
create mode 100644 src/glsl/glcpp/glcpp_extras.h
diff --git a/src/glsl/Makefile.sources b/src/glsl/Makefile.sources
index 2e81ded..90f57ad 100644
--- a/src/glsl/Makefile.sources
+++ b/src/glsl/Makefile.sources
@@ -7,7 +7,8 @@ GLSL_BUILDDIR = $(top_builddir)/src/glsl
LIBGLCPP_FILES = \
$(GLSL_SRCDIR)/ralloc.c \
- $(GLSL_SRCDIR)/glcpp/pp.c
+ $(GLSL_SRCDIR)/glcpp/pp.c \
+ $(GLSL_SRCDIR)/glcpp/glcpp_extras.c
LIBGLCPP_GENERATED_FILES = \
$(GLSL_BUILDDIR)/glcpp/glcpp-lex.c \
diff --git a/src/glsl/glcpp/glcpp-lex.l b/src/glsl/glcpp/glcpp-lex.l
index a029f62..1998fce 100644
--- a/src/glsl/glcpp/glcpp-lex.l
+++ b/src/glsl/glcpp/glcpp-lex.l
@@ -126,15 +126,31 @@ HEXADECIMAL_INTEGER 0[xX][0-9a-fA-F]+[uU]?
return HASH_VERSION;
}
- /* glcpp doesn't handle #extension, #version, or #pragma directives.
+ /* glcpp doesn't handle #pragma directives.
* Simply pass them through to the main compiler's lexer/parser. */
-{HASH}(extension|pragma)[^\n]+ {
+{HASH}(pragma)[^\n]+ {
yylval->str = ralloc_strdup (yyextra, yytext);
yylineno++;
yycolumn = 0;
return OTHER;
}
+ /* glcpp will handle the #extension directive if
+ * and only if parser->directive_ext_list is non-NULL
+ * otherwise it will just send that off to the
+ * main compiler's lexer/parser. */
+{HASH}(extension)[^\n]+ {
+ yylval->str = ralloc_strdup (yyextra, yytext);
+ yylineno++;
+ yycolumn = 0;
+ if(parser->directive_ext_list) {
+ return EXTENSION_DIRECTIVE;
+ } else {
+ return OTHER;
+ }
+}
+
+
{HASH}line{HSPACE}+ {
return HASH_LINE;
}
diff --git a/src/glsl/glcpp/glcpp-parse.y b/src/glsl/glcpp/glcpp-parse.y
index 7edc274..b5a5f05 100644
--- a/src/glsl/glcpp/glcpp-parse.y
+++ b/src/glsl/glcpp/glcpp-parse.y
@@ -166,6 +166,7 @@ add_builtin_define(glcpp_parser_t *parser, const char *name, int value);
%expect 0
%token COMMA_FINAL DEFINED ELIF_EXPANDED HASH HASH_DEFINE FUNC_IDENTIFIER OBJ_IDENTIFIER HASH_ELIF HASH_ELSE HASH_ENDIF HASH_IF HASH_IFDEF HASH_IFNDEF HASH_LINE HASH_UNDEF HASH_VERSION IDENTIFIER IF_EXPANDED INTEGER INTEGER_STRING LINE_EXPANDED NEWLINE OTHER PLACEHOLDER SPACE
%token PASTE
+%token <str> EXTENSION_DIRECTIVE
%type <ival> expression INTEGER operator SPACE integer_constant
%type <str> IDENTIFIER FUNC_IDENTIFIER OBJ_IDENTIFIER INTEGER_STRING OTHER
%type <string_list> identifier_list
@@ -208,9 +209,33 @@ line:
ralloc_free ($1);
}
| expanded_line
+| extension_directive
| HASH non_directive
;
+extension_directive:
+ EXTENSION_DIRECTIVE {
+ int cnt;
+
+ assert(parser->directive_ext_list);
+ /* the lexer increments yylineno when it encounters
+ * #extension. If it did not, then the line used by
+ * the glsl parser when then be wrong. We get around
+ * the issue by decrementing by number of #extension
+ * processed so far.
+ */
+ cnt=parser->directive_ext_list->count;
+ add_extension_from_preprocessor($1,
+ @1.first_line - cnt,
+ @1.first_column,
+ @1.last_line - cnt,
+ @1.last_column,
+ @1.source,
+ parser->directive_ext_list);
+ }
+;
+
+
expanded_line:
IF_EXPANDED expression NEWLINE {
_glcpp_parser_skip_stack_push_if (parser, & @1, $2);
@@ -1148,7 +1173,8 @@ static void add_builtin_define(glcpp_parser_t *parser,
}
glcpp_parser_t *
-glcpp_parser_create (const struct gl_extensions *extensions, int api)
+glcpp_parser_create (const struct gl_extensions *extensions, int api,
+ struct glcpp_extension_directive_list *ext_list)
{
glcpp_parser_t *parser;
int language_version;
@@ -1182,6 +1208,7 @@ glcpp_parser_create (const struct gl_extensions *extensions, int api)
parser->new_source_number = 0;
parser->is_gles = false;
+ parser->directive_ext_list = ext_list;
/* Add pre-defined macros. */
if (api == API_OPENGLES2) {
@@ -2098,3 +2125,4 @@ _glcpp_parser_handle_version_declaration(glcpp_parser_t *parser, intmax_t versio
es_identifier ? " " : "",
es_identifier ? es_identifier : "");
}
+
diff --git a/src/glsl/glcpp/glcpp.c b/src/glsl/glcpp/glcpp.c
index 6994d7b..bc2ac3c 100644
--- a/src/glsl/glcpp/glcpp.c
+++ b/src/glsl/glcpp/glcpp.c
@@ -164,7 +164,7 @@ main (int argc, char *argv[])
if (shader == NULL)
return 1;
- ret = glcpp_preprocess(ctx, &shader, &info_log, NULL, &gl_ctx);
+ ret = glcpp_preprocess(ctx, &shader, &info_log, NULL, &gl_ctx, NULL);
printf("%s", shader);
fprintf(stderr, "%s", info_log);
diff --git a/src/glsl/glcpp/glcpp.h b/src/glsl/glcpp/glcpp.h
index 8aaa551..1e5341a 100644
--- a/src/glsl/glcpp/glcpp.h
+++ b/src/glsl/glcpp/glcpp.h
@@ -28,6 +28,7 @@
#include <stdbool.h>
#include "main/mtypes.h"
+#include "glcpp_extras.h"
#include "../ralloc.h"
@@ -185,12 +186,14 @@ struct glcpp_parser {
bool has_new_source_number;
int new_source_number;
bool is_gles;
+ struct glcpp_extension_directive_list *directive_ext_list;
};
struct gl_extensions;
glcpp_parser_t *
-glcpp_parser_create (const struct gl_extensions *extensions, int api);
+glcpp_parser_create (const struct gl_extensions *extensions, int api,
+ struct glcpp_extension_directive_list *ext_list);
int
glcpp_parser_parse (glcpp_parser_t *parser);
@@ -198,9 +201,19 @@ glcpp_parser_parse (glcpp_parser_t *parser);
void
glcpp_parser_destroy (glcpp_parser_t *parser);
+/**
+ * Execute pre-processor on source code provided by *shader
+ * and writing output to *shader on success
+ * \param ext_list if NON-null indicates that the pre-processor
+ * should intercept #extension directives
+ * and build a list of the directives. The
+ * outputted source code will have those
+ * directive missing.
+ */
int
glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
- const struct gl_extensions *extensions, struct gl_context *g_ctx);
+ const struct gl_extensions *extensions, struct gl_context *g_ctx,
+ struct glcpp_extension_directive_list *ext_list);
/* Functions for writing to the info log */
diff --git a/src/glsl/glcpp/glcpp_extras.c b/src/glsl/glcpp/glcpp_extras.c
new file mode 100644
index 0000000..932ee1d
--- /dev/null
+++ b/src/glsl/glcpp/glcpp_extras.c
@@ -0,0 +1,111 @@
+#include <string.h>
+#include <ctype.h>
+#include "glcpp_extras.h"
+#include "main/imports.h"
+#include "../ralloc.h"
+
+static const char*
+remove_surrounding_white_space(char *value)
+{
+ size_t len;
+ char *back;
+
+ /*
+ remark:
+ -since the return value of remove_surrounding_white_space
+ is to never be freed, we can safely return a non-NULL
+ "empty" string if value was NULL.
+ */
+
+ if(value == NULL || *value == 0) {
+ return "";
+ }
+
+ while(*value != 0 && isspace(*value)) {
+ ++value;
+ }
+
+ len = strlen(value);
+ if(len == 0) {
+ return value;
+ }
+
+ for(back = value + len - 1; back != value && isspace(*back); --back) {
+ *back=0;
+ }
+
+ return value;
+}
+
+static const char*
+strtoke_and_remove_white_spaces(const char *delim, char **stroke_state)
+{
+ char *value;
+ value = strtok_r(NULL, delim, stroke_state);
+ return remove_surrounding_white_space(value);
+}
+
+
+void
+init_glcpp_extension_directive_list(struct glcpp_extension_directive_list *p)
+{
+ assert(p != NULL);
+ p->head = p->tail = NULL;
+ p->count = 0;
+}
+
+void
+delete_glcpp_extension_directive_list_contents(struct glcpp_extension_directive_list *p)
+{
+ assert(p != NULL);
+ if(p->head) {
+ ralloc_free(p->head);
+ }
+
+ p->head = p->tail = NULL;
+ p->count = 0;
+}
+
+void
+add_extension_from_preprocessor(const char *l,
+ int first_line,
+ int first_column,
+ int last_line,
+ int last_column,
+ unsigned source,
+ struct glcpp_extension_directive_list *directive_list)
+{
+ struct glcpp_extension_directive_list_element *current;
+ char *save_strok_state;
+
+ assert(directive_list != NULL);
+ current=ralloc(directive_list->tail,
+ struct glcpp_extension_directive_list_element);
+
+ /*should we limit the string size for paranoi's sake?*/
+ current->line_string=ralloc_strdup(current, l);
+ current->strtoked_string=ralloc_strdup(current, l);
+
+ current->location.first_line = first_line;
+ current->location.first_column = first_column;
+ current->location.last_line = last_line;
+ current->location.last_column = last_column;
+ current->location.source = source;
+
+ /*first strtok_r is to advance past #extension*/
+ strtok_r(current->strtoked_string, " ", &save_strok_state);
+
+ current->name = strtoke_and_remove_white_spaces(":", &save_strok_state);
+ current->behavior_string = strtoke_and_remove_white_spaces(" ", &save_strok_state);
+ current->next = NULL;
+
+ if(directive_list->tail == NULL) {
+ assert(directive_list->head == NULL);
+ directive_list->head = current;
+ } else {
+ directive_list->tail->next = current;
+ }
+ directive_list->tail = current;
+ directive_list->count++;
+}
+
diff --git a/src/glsl/glcpp/glcpp_extras.h b/src/glsl/glcpp/glcpp_extras.h
new file mode 100644
index 0000000..8cb21cb
--- /dev/null
+++ b/src/glsl/glcpp/glcpp_extras.h
@@ -0,0 +1,97 @@
+#ifndef GLCPP_EXTRAS_H
+#define GLCPP_EXTRAS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Specifies a single #extension directive picked up
+ * by the pre-processor.
+ */
+struct glcpp_extension_directive_list_element {
+
+ char *line_string; /*!< string from source line of GLSL */
+
+ struct {
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ unsigned source;
+ } location; /*!< location of directive in GLSL source */
+
+ /**
+ * name of extension, is a substring of
+ * \ref strtoked_string or a substring of
+ * a const (compiled) string
+ */
+ const char *name;
+
+ /**
+ * behavior of extension, is a substring of
+ * \ref strtoked_string or a substring of
+ * a const (compiled) string
+ */
+ const char *behavior_string;
+
+ /**
+ * next element in linked list
+ */
+ struct glcpp_extension_directive_list_element *next;
+
+ char *strtoked_string; /*!< original string modified by strtok_r */
+};
+
+/**
+ * Convenience wrapper for linked list of elements.
+ */
+struct glcpp_extension_directive_list {
+ struct glcpp_extension_directive_list_element *head;
+ struct glcpp_extension_directive_list_element *tail;
+ int count;
+};
+
+/**
+ * Initialize the contents of a \ref glcpp_extension_directive_list
+ * \param l struct to initialize, must not be NULL
+ */
+extern void
+init_glcpp_extension_directive_list(struct glcpp_extension_directive_list *l);
+
+/**
+ * Delete the <i>contents</i> of a \ref glcpp_extension_directive_list.
+ * Does NOT delete the containing struct.
+ * \param l struct to clear, must not be NULL.
+ */
+extern void
+delete_glcpp_extension_directive_list_contents(struct glcpp_extension_directive_list *l);
+
+/**
+ * Means of allowing #extension directives to come
+ * -after- source code by processing the directives
+ * in the pre-processor.
+ *
+ * \param line_string line of source of the extension directive, string is copied
+ * \param first_line value of first_line field of location of directive
+ * \param first_column value of first_column field of location of directive
+ * \param last_line value of last_line field of location of directive
+ * \param last_column value of field last_column of location of directive
+ * \param source value of source field of location of directive
+ * \param l list to which to append the directive
+ */
+extern void
+add_extension_from_preprocessor(const char *line_string,
+ int first_line,
+ int first_column,
+ int last_line,
+ int last_column,
+ unsigned source,
+ struct glcpp_extension_directive_list *l);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/src/glsl/glcpp/pp.c b/src/glsl/glcpp/pp.c
index 7e1b6c6..48176a6 100644
--- a/src/glsl/glcpp/pp.c
+++ b/src/glsl/glcpp/pp.c
@@ -136,10 +136,11 @@ remove_line_continuations(glcpp_parser_t *ctx, const char *shader)
int
glcpp_preprocess(void *ralloc_ctx, const char **shader, char **info_log,
- const struct gl_extensions *extensions, struct gl_context *gl_ctx)
+ const struct gl_extensions *extensions, struct gl_context *gl_ctx,
+ struct glcpp_extension_directive_list *ext_list)
{
int errors;
- glcpp_parser_t *parser = glcpp_parser_create (extensions, gl_ctx->API);
+ glcpp_parser_t *parser = glcpp_parser_create (extensions, gl_ctx->API, ext_list);
if (! gl_ctx->Const.DisableGLSLLineContinuations)
*shader = remove_line_continuations(parser, *shader);
diff --git a/src/glsl/glsl_parser_extras.cpp b/src/glsl/glsl_parser_extras.cpp
index d76d94b..7ac6798 100644
--- a/src/glsl/glsl_parser_extras.cpp
+++ b/src/glsl/glsl_parser_extras.cpp
@@ -603,6 +603,43 @@ static const _mesa_glsl_extension *find_extension(const char *name)
return NULL;
}
+/**
+ * Walks the directive list built by the preprocessor and
+ * sets the parser state to enable them, in addition frees
+ * the elements of the linked list.
+ */
+static
+void
+_mesa_glsl_enable_extensions_from_cpp(struct _mesa_glsl_parse_state *state)
+{
+ if(state->ext_directive_list) {
+ YYLTYPE location;
+ struct glcpp_extension_directive_list_element *current;
+ bool extension_ok;
+
+ for (current = state->ext_directive_list->head;
+ current != NULL; current = current->next) {
+ location.first_line = current->location.first_line;
+ location.first_column = current->location.first_column;
+ location.last_line = current->location.last_line;
+ location.last_column = current->location.last_column;
+ location.source = current->location.source;
+ extension_ok = _mesa_glsl_process_extension(current->name, &location,
+ current->behavior_string, &location,
+ state);
+
+ if (!extension_ok) {
+ _mesa_glsl_error(&location, state, "%s", current->line_string);
+ }
+
+ }
+
+ delete_glcpp_extension_directive_list_contents(state->ext_directive_list);
+ state->ext_directive_list = NULL;
+ }
+}
+
+
bool
_mesa_glsl_process_extension(const char *name, YYLTYPE *name_locp,
@@ -1475,15 +1512,32 @@ void
_mesa_glsl_compile_shader(struct gl_context *ctx, struct gl_shader *shader,
bool dump_ast, bool dump_hir)
{
+ struct glcpp_extension_directive_list extension_directive_list;
struct _mesa_glsl_parse_state *state =
new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
const char *source = shader->Source;
+ if (1) {
+ state->ext_directive_list = &extension_directive_list;
+ init_glcpp_extension_directive_list(state->ext_directive_list);
+ } else {
+ state->ext_directive_list = NULL;
+ }
+
state->error = glcpp_preprocess(state, &source, &state->info_log,
- &ctx->Extensions, ctx);
+ &ctx->Extensions, ctx,
+ state->ext_directive_list);
+
if (!state->error) {
_mesa_glsl_lexer_ctor(state, source);
+
+ /* before parsing, walk the directive list
+ * and enable the extensions.
+ */
+ _mesa_glsl_enable_extensions_from_cpp(state);
+
+
_mesa_glsl_parse(state);
_mesa_glsl_lexer_dtor(state);
}
diff --git a/src/glsl/glsl_parser_extras.h b/src/glsl/glsl_parser_extras.h
index d232bb3..666e97e 100644
--- a/src/glsl/glsl_parser_extras.h
+++ b/src/glsl/glsl_parser_extras.h
@@ -25,6 +25,9 @@
#ifndef GLSL_PARSER_EXTRAS_H
#define GLSL_PARSER_EXTRAS_H
+
+#include "glcpp/glcpp_extras.h"
+
/*
* Most of the definitions here only apply to C++
*/
@@ -376,6 +379,14 @@ struct _mesa_glsl_parse_state {
/** Atomic counter offsets by binding */
unsigned atomic_counter_offsets[MAX_COMBINED_ATOMIC_BUFFERS];
+
+ /**
+ * If non-NULL gives a list of directives picked up
+ * by the preprocessor.
+ */
+ struct glcpp_extension_directive_list *ext_directive_list;
+
+
};
# define YYLLOC_DEFAULT(Current, Rhs, N) \
@@ -450,7 +461,8 @@ extern const char *
_mesa_glsl_shader_target_name(GLenum type);
extern int glcpp_preprocess(void *ctx, const char **shader, char **info_log,
- const struct gl_extensions *extensions, struct gl_context *gl_ctx);
+ const struct gl_extensions *extensions, struct gl_context *gl_ctx,
+ struct glcpp_extension_directive_list *ext_list);
extern void _mesa_destroy_shader_compiler(void);
extern void _mesa_destroy_shader_compiler_caches(void);
--
1.8.1.2
More information about the mesa-dev
mailing list