From fb7025dbd37d549ae7a4b5193aa9cfc48c5032c6 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Tue, 21 Jan 2025 10:42:56 +0900 Subject: [PATCH] [qfcc] Handle execution modes for spir-v/glsl This means that `layout(triangles)` etc now work, though there are issues with frag shaders missing functions (since getting function calls working) and compute shaders silently failing. --- tools/qfcc/include/glsl-lang.h | 1 + tools/qfcc/include/spirv.h | 13 +- tools/qfcc/include/spirv_grammar.h | 1 + tools/qfcc/include/target.h | 2 + tools/qfcc/source/glsl-builtins.c | 19 +- tools/qfcc/source/glsl-layout.c | 325 +++++++++++++++++++---------- tools/qfcc/source/glsl-sub_comp.c | 1 + tools/qfcc/source/glsl-sub_frag.c | 1 + tools/qfcc/source/glsl-sub_geom.c | 1 + tools/qfcc/source/glsl-sub_tesc.c | 1 + tools/qfcc/source/glsl-sub_tese.c | 1 + tools/qfcc/source/glsl-sub_vert.c | 1 + tools/qfcc/source/qc-parse.y | 2 +- tools/qfcc/source/spirv_grammar.c | 78 ++++--- tools/qfcc/source/target_spirv.c | 106 ++++++++-- 15 files changed, 381 insertions(+), 172 deletions(-) diff --git a/tools/qfcc/include/glsl-lang.h b/tools/qfcc/include/glsl-lang.h index d62743bab..8f8b38142 100644 --- a/tools/qfcc/include/glsl-lang.h +++ b/tools/qfcc/include/glsl-lang.h @@ -115,6 +115,7 @@ extern type_t type_glsl_sampled_image; typedef struct glsl_sublang_s { const char *name; const char **interface_default_names; + const char *model_name; } glsl_sublang_t; extern glsl_sublang_t glsl_sublang; extern glsl_sublang_t glsl_comp_sublanguage; diff --git a/tools/qfcc/include/spirv.h b/tools/qfcc/include/spirv.h index ec2433006..df174b795 100644 --- a/tools/qfcc/include/spirv.h +++ b/tools/qfcc/include/spirv.h @@ -36,6 +36,7 @@ #include "tools/qfcc/include/expr.h" typedef struct symbol_s symbol_t; +typedef struct expr_s expr_t; typedef struct entrypoint_s { struct entrypoint_s *next; @@ -45,12 +46,22 @@ typedef struct entrypoint_s { ex_list_t interface; ///< list of symbols forming interface struct DARRAY_TYPE (symbol_t *) interface_syms; struct function_s *func; + + const expr_t *invocations; + const expr_t *local_size[3]; + const expr_t *primitive_in; + const expr_t *primitive_out; + const expr_t *max_vertices; + const expr_t *spacing; + const expr_t *order; + const expr_t *frag_depth; + bool point_mode; + bool early_fragment_tests; } entrypoint_t; typedef struct module_s { ex_list_t capabilities; ex_list_t extensions; - SpvExecutionModel default_model; symtab_t *extinst_imports; const expr_t *addressing_model; const expr_t *memory_model; diff --git a/tools/qfcc/include/spirv_grammar.h b/tools/qfcc/include/spirv_grammar.h index bf4a15371..d0018ad71 100644 --- a/tools/qfcc/include/spirv_grammar.h +++ b/tools/qfcc/include/spirv_grammar.h @@ -100,5 +100,6 @@ const plitem_t *spirv_operand_kind (const char *set, const char *kind); uint32_t spirv_instruction_opcode (const char *set, const expr_t *opcode); bool spirv_setup_intrinsic_symtab (symtab_t *symtab); +uint32_t spirv_execution_model (const char *model); #endif//__spirv_grammar_h diff --git a/tools/qfcc/include/target.h b/tools/qfcc/include/target.h index aa3f5e071..532d1bf3b 100644 --- a/tools/qfcc/include/target.h +++ b/tools/qfcc/include/target.h @@ -44,6 +44,8 @@ typedef struct { symtab_t *symtab, expr_t *block); void (*vararg_int) (const expr_t *e); + bool (*create_entry_point) (const char *name, const char *model_name); + const expr_t *(*initialized_temp) (const type_t *type, const expr_t *src); const expr_t *(*assign_vector) (const expr_t *dst, const expr_t *src); const expr_t *(*proc_switch) (const expr_t *expr, rua_ctx_t *ctx); diff --git a/tools/qfcc/source/glsl-builtins.c b/tools/qfcc/source/glsl-builtins.c index 9095b0d67..1f1c02b6d 100644 --- a/tools/qfcc/source/glsl-builtins.c +++ b/tools/qfcc/source/glsl-builtins.c @@ -42,6 +42,7 @@ #include "tools/qfcc/include/rua-lang.h" #include "tools/qfcc/include/spirv.h" #include "tools/qfcc/include/struct.h" +#include "tools/qfcc/include/target.h" #include "tools/qfcc/include/type.h" glsl_sublang_t glsl_sublang; @@ -1364,6 +1365,13 @@ glsl_init_common (rua_ctx_t *ctx) static module_t module; //FIXME probably not what I want pr.module = &module; + spirv_set_addressing_model (pr.module, SpvAddressingModelLogical); + spirv_set_memory_model (pr.module, SpvMemoryModelGLSL450); + + glsl_sublang = *(glsl_sublang_t *) ctx->language->sublanguage; + + current_target.create_entry_point ("main", glsl_sublang.model_name); + make_structure ("@image", 's', glsl_image_struct, &type_glsl_image); make_structure ("@sampled_image", 's', glsl_sampled_image_struct, &type_glsl_sampled_image); @@ -1374,10 +1382,6 @@ glsl_init_common (rua_ctx_t *ctx) DARRAY_RESIZE (&glsl_imageset, 0); - spirv_set_addressing_model (pr.module, SpvAddressingModelLogical); - spirv_set_memory_model (pr.module, SpvMemoryModelGLSL450); - - glsl_sublang = *(glsl_sublang_t *) ctx->language->sublanguage; ctx->language->initialized = true; glsl_block_clear (); rua_ctx_t rua_ctx = { .language = &lang_ruamoko }; @@ -1410,8 +1414,6 @@ glsl_init_vert (rua_ctx_t *ctx) glsl_parse_vars (glsl_Vulkan_vertex_vars, ctx); rua_ctx_t rua_ctx = { .language = &lang_ruamoko }; qc_parse_string (glsl_other_texture_functions, &rua_ctx); - - pr.module->default_model = SpvExecutionModelVertex; } void @@ -1423,7 +1425,6 @@ glsl_init_tesc (rua_ctx_t *ctx) qc_parse_string (glsl_other_texture_functions, &rua_ctx); spirv_add_capability (pr.module, SpvCapabilityTessellation); - pr.module->default_model = SpvExecutionModelTessellationControl; } void @@ -1435,7 +1436,6 @@ glsl_init_tese (rua_ctx_t *ctx) qc_parse_string (glsl_other_texture_functions, &rua_ctx); spirv_add_capability (pr.module, SpvCapabilityTessellation); - pr.module->default_model = SpvExecutionModelTessellationEvaluation; } void @@ -1448,7 +1448,6 @@ glsl_init_geom (rua_ctx_t *ctx) qc_parse_string (glsl_other_texture_functions, &rua_ctx); spirv_add_capability (pr.module, SpvCapabilityGeometry); - pr.module->default_model = SpvExecutionModelGeometry; } void @@ -1459,6 +1458,4 @@ glsl_init_frag (rua_ctx_t *ctx) rua_ctx_t rua_ctx = { .language = &lang_ruamoko }; qc_parse_string (glsl_fragment_functions, &rua_ctx); qc_parse_string (glsl_frag_texture_functions, &rua_ctx); - - pr.module->default_model = SpvExecutionModelFragment; } diff --git a/tools/qfcc/source/glsl-layout.c b/tools/qfcc/source/glsl-layout.c index 45fe97ed6..2233bb39d 100644 --- a/tools/qfcc/source/glsl-layout.c +++ b/tools/qfcc/source/glsl-layout.c @@ -39,21 +39,57 @@ #include "tools/qfcc/include/def.h" #include "tools/qfcc/include/diagnostic.h" #include "tools/qfcc/include/glsl-lang.h" +#include "tools/qfcc/include/qfcc.h" #include "tools/qfcc/include/shared.h" +#include "tools/qfcc/include/spirv.h" #include "tools/qfcc/include/strpool.h" #include "tools/qfcc/include/symtab.h" #include "tools/qfcc/include/type.h" #include "tools/qfcc/include/value.h" +typedef enum { + decl_var = 1 << 0, + decl_qual = 1 << 1, + decl_block = 1 << 2, + decl_member = 1 << 3, +} glsl_obj_t; + +typedef enum { + var_any, + var_opaque, + var_atomic, + var_subpass, + var_scalar, + var_image, + var_gl_FragCoord, + var_gl_FragDepth, +} glsl_var_t; + +typedef struct layout_qual_s layout_qual_t; +typedef struct layout_qual_s { + const char *name; + void (*apply) (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name); + void (*apply_expr) (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val); + unsigned obj_mask; + glsl_var_t var_type; + unsigned if_mask; + const char **stage_filter; + int val; + const char *accessor; +} layout_qual_t; + static void -glsl_layout_invalid_A (specifier_t spec, const expr_t *qual_name) +glsl_layout_invalid_A (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) { error (qual_name, "not allowed for vulkan"); } static void -glsl_layout_invalid_E (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_invalid_E (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { error (qual_name, "not allowed for vulkan"); } @@ -73,26 +109,22 @@ set_attribute (attribute_t **attributes, const char *name, const expr_t *val) } static void -glsl_layout_packing (specifier_t spec, const expr_t *qual_name) +glsl_layout_packing (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) { } static void -glsl_layout_location (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_location (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { const char *name = expr_string (qual_name); set_attribute (&spec.sym->attributes, name, val); } static void -glsl_layout_geom_in_primitive (specifier_t spec, const expr_t *qual_name) -{ -} - -static void -glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_constant_id (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { if (spec.sym->sy_type == sy_const) { auto expr = new_value_expr (spec.sym->value, false); @@ -115,96 +147,107 @@ glsl_layout_constant_id (specifier_t spec, const expr_t *qual_name, } static void -glsl_layout_binding (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_binding (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { const char *name = expr_string (qual_name); set_attribute (&spec.sym->attributes, name, val); } static void -glsl_layout_offset (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_offset (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { } static void -glsl_layout_set (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_set (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { const char *name = expr_string (qual_name); set_attribute (&spec.sym->attributes, name, val); } static void -glsl_layout_set_property (specifier_t spec, const expr_t *qual_name, - const expr_t *val) -{ - //auto interface = glsl_iftype_from_sc (spec.storage); - //notice (qual_name, "%s %s %s", glsl_interface_names[interface], - // expr_string (qual_name), get_value_string (val->value)); -} - -static void -glsl_layout_geom_out_primitive (specifier_t spec, const expr_t *qual_name) +glsl_layout_format (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) { } static void -glsl_layout_geom_out_max_vertices (specifier_t spec, const expr_t *qual_name, - const expr_t *val) +glsl_layout_push_constant (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) { } static void -glsl_layout_format (specifier_t spec, const expr_t *qual_name) +glsl_layout_input_attachment_index (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) { } -static void -glsl_layout_push_constant (specifier_t spec, const expr_t *qual_name) -{ -} - -static void -glsl_layout_input_attachment_index (specifier_t spec, const expr_t *qual_name, - const expr_t *val) -{ -} - -static void -glsl_layout_execution_mode (specifier_t spec, const expr_t *qual_name) -{ -} - -typedef enum { - decl_var = 1 << 0, - decl_qual = 1 << 1, - decl_block = 1 << 2, - decl_member = 1 << 3, -} glsl_obj_t; - -typedef enum { - var_any, - var_opaque, - var_atomic, - var_subpass, - var_scalar, - var_image, - var_gl_FragCoord, - var_gl_FragDepth, -} glsl_var_t; - -typedef struct layout_qual_s { +#define EP(n) {.name = #n, .offset = offsetof (entrypoint_t, n)} +#define EPF(n) {.name = #n, .offset = offsetof (entrypoint_t, n), .flag = true} +static struct { const char *name; - void (*apply) (specifier_t spec, const expr_t *qual_name); - void (*apply_expr) (specifier_t spec, const expr_t *qual_name, - const expr_t *val); - unsigned obj_mask; - glsl_var_t var_type; - unsigned if_mask; - const char **stage_filter; -} layout_qual_t; + int offset; + bool flag; +} entrypoint_fields[] = { + EP (invocations), + EP (local_size[0]), + EP (local_size[1]), + EP (local_size[2]), + EP (primitive_in), + EP (primitive_out), + EP (max_vertices), + EP (spacing), + EP (order), + EP (frag_depth), + EPF (point_mode), + EPF (early_fragment_tests), + {} +}; +#undef EP + +static void +glsl_layout_exec_mode_param (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name, const expr_t *val) +{ + auto entry_point = pr.module->entry_points; + if (!entry_point) { + internal_error (0, "no entry point"); + } + for (auto ep = entrypoint_fields; ep->name; ep++) { + if (strcmp (ep->name, qual->accessor) == 0) { + auto val_ptr = ((byte *) entry_point + ep->offset); + if (ep->flag) { + auto flag = (bool *) val_ptr; + *flag = true; + } else { + auto expr = (const expr_t **) val_ptr; + *expr = val; + } + return; + } + } + internal_error (0, "invalid accessor: %s", qual->accessor); +} + +static void +glsl_layout_exec_mode (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) +{ + auto val = new_int_expr (qual->val, false); + glsl_layout_exec_mode_param (qual, spec, qual_name, val); +} + +static void +glsl_layout_ignore (const layout_qual_t *qual, specifier_t spec, + const expr_t *qual_name) +{ + const char *name = expr_string (qual_name); + debug (qual_name, "ignoring %s", name); +} #define A(a) a, nullptr #define E(e) nullptr, e @@ -314,100 +357,130 @@ static layout_qual_t layout_qualifiers[] = { }, { .name = "triangles", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeTriangles, + .accessor = "primitive_out", }, { .name = "quads", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeQuads, + .accessor = "primitive_out", }, { .name = "isolines", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeIsolines, + .accessor = "primitive_out", }, { .name = "equal_spacing", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeSpacingEqual, + .accessor = "spacing", }, { .name = "fractional_even_spacing", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeSpacingFractionalEven, + .accessor = "spacing", }, { .name = "fractional_odd_spacing", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeSpacingFractionalOdd, + .accessor = "spacing", }, { .name = "cw", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeVertexOrderCw, + .accessor = "order", }, { .name = "ccw", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = SpvExecutionModeVertexOrderCcw, + .accessor = "order", }, { .name = "point_mode", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "tessellation evaluation", nullptr }, + .val = true, + .accessor = "point_mode", }, { .name = "points", - .apply = A(glsl_layout_geom_in_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), - .if_mask = I(in)|I(out), + .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeInputPoints, + .accessor = "primitive_in", }, { .name = "lines", - .apply = A(glsl_layout_geom_in_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeInputLines, + .accessor = "primitive_in", }, { .name = "lines_adjacency", - .apply = A(glsl_layout_geom_in_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeInputLinesAdjacency, + .accessor = "primitive_in", }, { .name = "triangles", - .apply = A(glsl_layout_geom_in_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeTriangles, + .accessor = "primitive_in", }, { .name = "triangles_adjacency", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeInputTrianglesAdjacency, + .accessor = "primitive_in", }, { .name = "invocations", - .apply = E(nullptr), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "geometry", nullptr }, + .accessor = "invocations", }, { .name = "origin_upper_left", - .apply = A(nullptr), + .apply = A(glsl_layout_ignore), .obj_mask = D(qual), + .var_type = V(gl_FragCoord), .if_mask = I(in), .stage_filter = C { "fragment", nullptr }, }, @@ -418,47 +491,55 @@ static layout_qual_t layout_qualifiers[] = { .stage_filter = C { "fragment", nullptr }, }, { .name = "early_fragment_tests", - .apply = A(glsl_layout_execution_mode), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "fragment", nullptr }, + .val = true, + .accessor = "early_fragment_tests", }, { .name = "local_size_x", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[0]", }, { .name = "local_size_y", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[1]", }, { .name = "local_size_z", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[2]", }, { .name = "local_size_x_id", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[0]", }, { .name = "local_size_y_id", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[1]", }, { .name = "local_size_z_id", - .apply = E(glsl_layout_set_property), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(in), .stage_filter = C { "compute", nullptr }, + .accessor = "local_size[2]", }, { .name = "xfb_buffer", @@ -502,35 +583,43 @@ static layout_qual_t layout_qualifiers[] = { }, { .name = "vertices", - .apply = E(nullptr), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(out), .stage_filter = C { "tessellation control", nullptr }, + .accessor = "max_vertices", }, { .name = "points", - .apply = A(glsl_layout_geom_out_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(out), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeOutputPoints, + .accessor = "primitive_out", }, { .name = "line_strip", - .apply = A(glsl_layout_geom_out_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(out), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeOutputLineStrip, + .accessor = "primitive_out", }, { .name = "triangle_strip", - .apply = A(glsl_layout_geom_out_primitive), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(qual), .if_mask = I(out), .stage_filter = C { "geometry", nullptr }, + .val = SpvExecutionModeOutputTriangleStrip, + .accessor = "primitive_out", }, { .name = "max_vertices", - .apply = E(glsl_layout_geom_out_max_vertices), + .apply = E(glsl_layout_exec_mode_param), .obj_mask = D(qual), .if_mask = I(out), .stage_filter = C { "geometry", nullptr }, + .accessor = "max_vertices", }, { .name = "stream", .apply = E(nullptr), @@ -541,32 +630,40 @@ static layout_qual_t layout_qualifiers[] = { }, { .name = "depth_any", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(var), - .var_type = V(any), + .var_type = V(gl_FragDepth), .if_mask = I(out), .stage_filter = C { "fragment", nullptr }, + .val = SpvExecutionModeDepthReplacing, + .accessor = "frag_depth", }, { .name = "depth_greater", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(var), - .var_type = V(any), + .var_type = V(gl_FragDepth), .if_mask = I(out), .stage_filter = C { "fragment", nullptr }, + .val = SpvExecutionModeDepthGreater, + .accessor = "frag_depth", }, { .name = "depth_less", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(var), - .var_type = V(any), + .var_type = V(gl_FragDepth), .if_mask = I(out), .stage_filter = C { "fragment", nullptr }, + .val = SpvExecutionModeDepthLess, + .accessor = "frag_depth", }, { .name = "depth_unchanged", - .apply = A(nullptr), + .apply = A(glsl_layout_exec_mode), .obj_mask = D(var), - .var_type = V(any), + .var_type = V(gl_FragDepth), .if_mask = I(out), .stage_filter = C { "fragment", nullptr }, + .val = SpvExecutionModeDepthUnchanged, + .accessor = "frag_depth", }, { .name = "constant_id", @@ -953,14 +1050,14 @@ layout_apply_qualifier (const expr_t *qualifier, specifier_t spec) if (!val) { error (qualifier, "%s requires a value", key.name); } else { - qual->apply_expr (spec, qualifier->expr.e1, val); + qual->apply_expr (qual, spec, qualifier->expr.e1, val); } return; } else if (qual->apply) { if (val) { error (qualifier, "%s does not take a value", key.name); } else { - qual->apply (spec, qualifier->expr.e1); + qual->apply (qual, spec, qualifier->expr.e1); } return; } else { diff --git a/tools/qfcc/source/glsl-sub_comp.c b/tools/qfcc/source/glsl-sub_comp.c index 56b9c7bbc..881652f1c 100644 --- a/tools/qfcc/source/glsl-sub_comp.c +++ b/tools/qfcc/source/glsl-sub_comp.c @@ -50,4 +50,5 @@ static const char *glsl_comp_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_comp_sublanguage = { .name = "compute", .interface_default_names = glsl_comp_interface_default_names, + .model_name = "GLCompute", }; diff --git a/tools/qfcc/source/glsl-sub_frag.c b/tools/qfcc/source/glsl-sub_frag.c index ace75964a..ff85345af 100644 --- a/tools/qfcc/source/glsl-sub_frag.c +++ b/tools/qfcc/source/glsl-sub_frag.c @@ -48,4 +48,5 @@ static const char *glsl_frag_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_frag_sublanguage = { .name = "fragment", .interface_default_names = glsl_frag_interface_default_names, + .model_name = "Fragment", }; diff --git a/tools/qfcc/source/glsl-sub_geom.c b/tools/qfcc/source/glsl-sub_geom.c index f94c315e0..3a3fc013c 100644 --- a/tools/qfcc/source/glsl-sub_geom.c +++ b/tools/qfcc/source/glsl-sub_geom.c @@ -52,4 +52,5 @@ static const char *glsl_geom_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_geom_sublanguage = { .name = "geometry", .interface_default_names = glsl_geom_interface_default_names, + .model_name = "Geometry", }; diff --git a/tools/qfcc/source/glsl-sub_tesc.c b/tools/qfcc/source/glsl-sub_tesc.c index 45e133bcd..2af7c289e 100644 --- a/tools/qfcc/source/glsl-sub_tesc.c +++ b/tools/qfcc/source/glsl-sub_tesc.c @@ -50,4 +50,5 @@ static const char *glsl_tesc_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_tesc_sublanguage = { .name = "tessellation control", .interface_default_names = glsl_tesc_interface_default_names, + .model_name = "TessellationControl", }; diff --git a/tools/qfcc/source/glsl-sub_tese.c b/tools/qfcc/source/glsl-sub_tese.c index 15a804277..3c9287c8f 100644 --- a/tools/qfcc/source/glsl-sub_tese.c +++ b/tools/qfcc/source/glsl-sub_tese.c @@ -50,4 +50,5 @@ static const char *glsl_tese_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_tese_sublanguage = { .name = "tessellation evaluation", .interface_default_names = glsl_tese_interface_default_names, + .model_name = "TessellationEvaluation", }; diff --git a/tools/qfcc/source/glsl-sub_vert.c b/tools/qfcc/source/glsl-sub_vert.c index 8eae31601..2fe2de5f1 100644 --- a/tools/qfcc/source/glsl-sub_vert.c +++ b/tools/qfcc/source/glsl-sub_vert.c @@ -50,4 +50,5 @@ static const char *glsl_vert_interface_default_names[glsl_num_interfaces] = { glsl_sublang_t glsl_vert_sublanguage = { .name = "vertex", .interface_default_names = glsl_vert_interface_default_names, + .model_name = "Vertex", }; diff --git a/tools/qfcc/source/qc-parse.y b/tools/qfcc/source/qc-parse.y index dc122d849..6590753e7 100644 --- a/tools/qfcc/source/qc-parse.y +++ b/tools/qfcc/source/qc-parse.y @@ -3274,7 +3274,7 @@ static void rua_init (rua_ctx_t *ctx) { ctx->language->initialized = true; - if (options.code.spirv) { + if (options.code.spirv && !pr.module) { static module_t module; //FIXME probably not what I want pr.module = &module; diff --git a/tools/qfcc/source/spirv_grammar.c b/tools/qfcc/source/spirv_grammar.c index fdeaad085..386682be3 100644 --- a/tools/qfcc/source/spirv_grammar.c +++ b/tools/qfcc/source/spirv_grammar.c @@ -424,31 +424,14 @@ build_grammars (void) } } -const plitem_t * -spirv_operand_kind (const char *set, const char *kind) -{ - if (!built) { - build_grammars (); - built = true; - } - return nullptr; -} - -const uint32_t -spirv_instruction_opcode (const char *set, const expr_t *opcode) +static spirv_grammar_t * +find_grammar (const char *set) { if (!built) { build_grammars (); built = true; } - if (is_integral_val (opcode)) { - return expr_integral (opcode); - } - if (opcode->type != ex_symbol) { - error (opcode, "not a an integer constant or symbol"); - return 0; - } spirv_grammar_t *grammar = nullptr; for (int i = 0; builtin_json[i].name; i++) { if (strcmp (builtin_json[i].name, set) == 0) { @@ -456,6 +439,28 @@ spirv_instruction_opcode (const char *set, const expr_t *opcode) break; } } + return grammar; +} + +const plitem_t * +spirv_operand_kind (const char *set, const char *kind) +{ + find_grammar (set); + return nullptr; +} + +uint32_t +spirv_instruction_opcode (const char *set, const expr_t *opcode) +{ + if (is_integral_val (opcode)) { + return expr_integral (opcode); + } + if (opcode->type != ex_symbol) { + error (opcode, "not a an integer constant or symbol"); + return 0; + } + + auto grammar = find_grammar (set); if (!grammar) { error (opcode, "unrecognized grammar set %s", set); return 0; @@ -518,18 +523,8 @@ spirv_intrinsic_symbol (const char *name, symtab_t *symtab) bool spirv_setup_intrinsic_symtab (symtab_t *symtab) { - if (!built) { - build_grammars (); - built = true; - } const char *set = "core"; - spirv_grammar_t *grammar = nullptr; - for (int i = 0; builtin_json[i].name; i++) { - if (strcmp (builtin_json[i].name, set) == 0) { - grammar = builtin_json[i].grammar; - break; - } - } + auto grammar = find_grammar (set); if (!grammar) { error (0, "unrecognized grammar set %s", set); return false; @@ -538,3 +533,26 @@ spirv_setup_intrinsic_symtab (symtab_t *symtab) symtab->procsymbol_data = grammar; return true; } + +uint32_t +spirv_execution_model (const char *model) +{ + const char *set = "core"; + auto grammar = find_grammar (set); + if (!grammar) { + error (0, "unrecognized grammar set %s", set); + return false; + } + symtab_t symtab = { .procsymbol_data = grammar }; + auto model_enum = spirv_intrinsic_symbol ("ExecutionModel", &symtab); + if (!model_enum) { + error (0, "ExecutionModel not found"); + return 0; + } + auto model_val = symtab_lookup (model_enum->namespace, model); + if (!model_val) { + error (0, "Execution model %s not found", model); + return 0; + } + return model_val->value->uint_val; +} diff --git a/tools/qfcc/source/target_spirv.c b/tools/qfcc/source/target_spirv.c index 577a2ec2f..d3830a781 100644 --- a/tools/qfcc/source/target_spirv.c +++ b/tools/qfcc/source/target_spirv.c @@ -837,6 +837,63 @@ spirv_EntryPoint (entrypoint_t *entrypoint, spirvctx_t *ctx) } auto exec_modes = ctx->module->exec_modes; + if (entrypoint->invocations) { + insn = spirv_new_insn (SpvOpExecutionMode, 4, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = SpvExecutionModeInvocations; + INSN (insn, 3) = expr_integral (entrypoint->invocations); + } + // assume that if 1 is set, all are set + if (entrypoint->local_size[0]) { + insn = spirv_new_insn (SpvOpExecutionMode, 6, exec_modes); + INSN (insn, 1) = func_id; + //FIXME LocalSizeId + INSN (insn, 2) = SpvExecutionModeLocalSize; + INSN (insn, 3) = expr_integral (entrypoint->local_size[0]); + INSN (insn, 4) = expr_integral (entrypoint->local_size[1]); + INSN (insn, 5) = expr_integral (entrypoint->local_size[2]); + } + if (entrypoint->primitive_in) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = expr_integral (entrypoint->primitive_in); + } + if (entrypoint->primitive_out) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = expr_integral (entrypoint->primitive_out); + } + if (entrypoint->max_vertices) { + insn = spirv_new_insn (SpvOpExecutionMode, 4, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = SpvExecutionModeOutputVertices; + INSN (insn, 3) = expr_integral (entrypoint->max_vertices); + } + if (entrypoint->spacing) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = expr_integral (entrypoint->spacing); + } + if (entrypoint->order) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = expr_integral (entrypoint->order); + } + if (entrypoint->frag_depth) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = expr_integral (entrypoint->frag_depth); + } + if (entrypoint->point_mode) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = SpvExecutionModePointMode; + } + if (entrypoint->early_fragment_tests) { + insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); + INSN (insn, 1) = func_id; + INSN (insn, 2) = SpvExecutionModeEarlyFragmentTests; + } for (auto m = entrypoint->modes; m; m = m->next) { insn = spirv_new_insn (SpvOpExecutionMode, 3, exec_modes); INSN (insn, 1) = func_id; @@ -2069,22 +2126,13 @@ static void spirv_build_code (function_t *func, const expr_t *statements) { func->exprs = statements; - if (strcmp ("main", func->o_name) == 0) { - attribute_t *mode = nullptr; - if (pr.module->default_model == SpvExecutionModelFragment) { - mode = new_attribute ("mode", - new_int_expr (SpvExecutionModeOriginUpperLeft, false)); + for (auto ep = pr.module->entry_points; ep; ep = ep->next) { + if (strcmp (ep->name, func->o_name) == 0) { + if (ep->func && ep->func != func) { + error (statements, "entry point %s redefined", ep->name); + } + ep->func = func; } - entrypoint_t *ep = malloc (sizeof (entrypoint_t)); - *(ep) = (entrypoint_t) { - .next = pr.module->entry_points, - .model = pr.module->default_model, - .name = "main", - .modes = mode, - .interface_syms = DARRAY_STATIC_INIT (16), - .func = func, - }; - pr.module->entry_points = ep; } } @@ -2123,6 +2171,33 @@ spirv_declare_sym (specifier_t spec, const expr_t *init, symtab_t *symtab, } } +static bool +spirv_create_entry_point (const char *name, const char *model_name) +{ + for (auto ep = pr.module->entry_points; ep; ep = ep->next) { + if (strcmp (ep->name, name) == 0) { + error (0, "entry point %s already exists", name); + return false; + } + } + attribute_t *mode = nullptr; + unsigned model = spirv_execution_model (model_name); + if (model == SpvExecutionModelFragment) { + mode = new_attribute ("mode", + new_int_expr (SpvExecutionModeOriginUpperLeft, false)); + } + entrypoint_t *ep = malloc (sizeof (entrypoint_t)); + *(ep) = (entrypoint_t) { + .next = pr.module->entry_points, + .model = model, + .name = save_string (name), + .modes = mode, + .interface_syms = DARRAY_STATIC_INIT (16), + }; + pr.module->entry_points = ep; + return true; +} + static const expr_t * spirv_build_element_chain (element_chain_t *element_chain, const type_t *type, const expr_t *eles) @@ -2274,6 +2349,7 @@ target_t spirv_target = { .build_scope = spirv_build_scope, .build_code = spirv_build_code, .declare_sym = spirv_declare_sym, + .create_entry_point = spirv_create_entry_point, .initialized_temp = spirv_initialized_temp, .assign_vector = spirv_assign_vector, .setup_intrinsic_symtab = spirv_setup_intrinsic_symtab,