[qfcc] Parse @image() in Ruamoko

I'll probably tweak the syntax a little (make placement of the type more
flexible and not generate an error if either type or other arguments are
missing), but I think I like it result:

    typedef @image(int, 2D, Array, R8) bimage;
    typedef @image(float, 3D, Rgba8) fimage;
    typedef @image(float, Cube, Rgba8) cube;
    typedef @image(float, Array, Cube) cube_array;
This commit is contained in:
Bill Currie 2025-02-11 18:05:15 +09:00
parent 28604add29
commit 6af42e979e
3 changed files with 149 additions and 2 deletions

View file

@ -70,4 +70,6 @@ void image_init_types (void);
symbol_t *named_image_type (image_t *image, const type_t *htype,
const char *name);
const type_t *image_type (const type_t *type, const expr_t *params);
#endif//__image_h

View file

@ -42,6 +42,8 @@
#include "tools/qfcc/include/expr.h"
#include "tools/qfcc/include/diagnostic.h"
#include "tools/qfcc/include/image.h"
#include "tools/qfcc/include/spirv_grammar.h"
#include "tools/qfcc/include/spirv.h"
#include "tools/qfcc/include/struct.h"
#include "tools/qfcc/include/symtab.h"
#include "tools/qfcc/include/type.h"
@ -279,3 +281,120 @@ named_image_type (image_t *image, const type_t *htype, const char *name)
return sym;
}
const type_t *
image_type (const type_t *type, const expr_t *params)
{
if (!type || !(is_float (type) || is_int (type) || is_uint (type))
|| !is_scalar (type)) {
error (params, "invalid type for @image");
return &type_int;
}
if (is_error (params)) {
return &type_int;
}
if (params->type != ex_list) {
internal_error (params, "not a list");
}
image_t image = {
.sample_type = type,
.dim = ~0u,
.format = ~0u,
};
int count = list_count (&params->list);
const expr_t *args[count + 1] = {};
list_scatter (&params->list, args);
bool err = false;
for (int i = 0; i < count; i++) {
uint32_t val;
if (args[i]->type != ex_symbol) {
error (args[i], "invalid argument for @image");
err = true;
continue;
}
const char *name = args[i]->symbol->name;
if (spirv_enum_val_silent ("Dim", name, &val)) {
if (image.dim != ~0u) {
error (args[i], "multiple spec for image dimension");
err = true;
}
image.dim = val;
continue;
}
if (spirv_enum_val_silent ("ImageFormat", name, &val)) {
if (image.format != ~0u) {
error (args[i], "multiple spec for image format");
err = true;
}
image.format = val;
continue;
}
if (strcasecmp ("Depth", name) == 0) {
if (image.depth) {
error (args[i], "multiple spec for image depth");
err = true;
}
image.depth = 1;
continue;
}
if (strcasecmp ("Array", name) == 0
|| strcasecmp ("Arrayed", name) == 0) {
if (image.arrayed) {
error (args[i], "multiple spec for image array");
err = true;
}
image.arrayed = true;
continue;
}
if (strcasecmp ("MS", name) == 0) {
if (image.multisample) {
error (args[i], "multiple spec for image multisample");
err = true;
}
image.multisample = true;
continue;
}
if (strcasecmp ("Sampled", name) == 0) {
if (image.sampled) {
error (args[i], "multiple spec for image sampling");
err = true;
}
image.sampled = 1;
continue;
}
if (strcasecmp ("Storage", name) == 0) {
if (image.sampled) {
error (args[i], "multiple spec for image sampling");
err = true;
}
image.sampled = 2;
continue;
}
error (args[i], "invalid argument for @image: %s", name);
err = true;
}
if (image.dim == img_subpassdata) {
if (image.sampled) {
error (0, "multiple spec for image sampling");
err = true;
}
if (image.format && image.format != ~0u) {
error (0, "multiple spec for image format");
err = true;
}
image.format = SpvImageFormatUnknown;
image.sampled = 2;
}
if (image.dim == ~0u) {
error (0, "image dimenion not specified");
err = true;
}
if (image.format == ~0u) {
image.format = SpvImageFormatUnknown;
}
if (err) {
return &type_int;
}
auto sym = named_image_type (&image, &type_image, nullptr);
return sym->type;
}

View file

@ -67,6 +67,7 @@
#include "tools/qfcc/include/expr.h"
#include "tools/qfcc/include/function.h"
#include "tools/qfcc/include/grab.h"
#include "tools/qfcc/include/image.h"
#include "tools/qfcc/include/method.h"
#include "tools/qfcc/include/options.h"
#include "tools/qfcc/include/qfcc.h"
@ -155,7 +156,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
%token LOCAL WHILE DO IF ELSE FOR BREAK CONTINUE
%token RETURN AT_RETURN
%token NIL GOTO SWITCH CASE DEFAULT ENUM ALGEBRA
%token NIL GOTO SWITCH CASE DEFAULT ENUM ALGEBRA IMAGE
%token ARGS TYPEDEF EXTERN STATIC SYSTEM OVERLOAD NOT ATTRIBUTE
%token <op> STRUCT
%token HANDLE INTRINSIC
@ -197,7 +198,7 @@ int yylex (YYSTYPE *yylval, YYLTYPE *yylloc);
%type <symbol> tag
%type <spec> struct_specifier struct_list
%type <spec> enum_specifier algebra_specifier
%type <spec> enum_specifier algebra_specifier image_specifier
%type <symbol> optional_enum_list enum_list enumerator_list enumerator
%type <symbol> enum_init
%type <expr> array_decl
@ -724,6 +725,12 @@ pop_scope (symtab_t *current)
return parent;
}
static const expr_t *
number_as_symbol (const rua_tok_t *tok, rua_ctx_t *ctx)
{
return new_name_expr (tok->text);
}
%}
%expect 2
@ -1151,6 +1158,7 @@ typespec_reserved
{
$$ = type_spec (algebra_subtype ($1.type, $3));
}
| image_specifier
| enum_specifier
| struct_specifier
// NOTE: fields don't parse the way they should. This is not a problem
@ -1424,6 +1432,22 @@ algebra_specifier
}
;
image_specifier
: IMAGE '(' TYPE_SPEC[spec] ','
{
// allow 2D to be parsed as a symbol instead of 2.0 (double/decimal)
$<pointer>$ = ctx->language->parse_number;
ctx->language->parse_number = number_as_symbol;
}[parse_number]
expr_list ')'
{
auto type = $spec.type;
auto spec = type_spec (image_type (type, $expr_list));
$$ = spec;
ctx->language->parse_number = $<pointer>parse_number;
}
;
enum_specifier
: ENUM tag optional_enum_list
{
@ -3068,6 +3092,8 @@ static keyword_t qf_keywords[] = {
{"@dual", QC_DUAL, },
{"@undual", QC_UNDUAL, },
{"@image", QC_IMAGE, },
{"@construct", QC_CONSTRUCT, },
{"@generic", QC_GENERIC, },
{"@function", QC_AT_FUNCTION, },