Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/reshadefx/src/effect_parser_stmt.cpp
4246 views
1
/*
2
* Copyright (C) 2014 Patrick Mours
3
* SPDX-License-Identifier: BSD-3-Clause
4
*/
5
6
#include "effect_lexer.hpp"
7
#include "effect_parser.hpp"
8
#include "effect_codegen.hpp"
9
#include <cctype> // std::toupper
10
#include <cassert>
11
#include <climits>
12
#include <algorithm> // std::max, std::replace, std::transform
13
#include <limits>
14
#include <string_view>
15
16
template <typename ENTER_TYPE, typename LEAVE_TYPE>
17
struct scope_guard
18
{
19
explicit scope_guard(ENTER_TYPE &&enter_lambda, LEAVE_TYPE &&leave_lambda) :
20
leave_lambda(std::forward<LEAVE_TYPE>(leave_lambda)) { enter_lambda(); }
21
~scope_guard() { leave_lambda(); }
22
23
private:
24
LEAVE_TYPE leave_lambda;
25
};
26
27
bool reshadefx::parser::parse(std::string input, codegen *backend)
28
{
29
_lexer = std::make_unique<lexer>(std::move(input));
30
31
// Set backend for subsequent code-generation
32
_codegen = backend;
33
assert(backend != nullptr);
34
35
consume();
36
37
bool parse_success = true;
38
bool current_success = true;
39
40
while (!peek(tokenid::end_of_file))
41
{
42
if (!parse_top(current_success))
43
return false;
44
if (!current_success)
45
parse_success = false;
46
}
47
48
if (parse_success)
49
backend->optimize_bindings();
50
51
return parse_success;
52
}
53
54
bool reshadefx::parser::parse_top(bool &parse_success)
55
{
56
if (accept(tokenid::namespace_))
57
{
58
// Anonymous namespaces are not supported right now, so an identifier is a must
59
if (!expect(tokenid::identifier))
60
return false;
61
62
const std::string name = std::move(_token.literal_as_string);
63
64
if (!expect('{'))
65
return false;
66
67
enter_namespace(name);
68
69
bool current_success = true;
70
bool parse_success_namespace = true;
71
72
// Recursively parse top level statements until the namespace is closed again
73
while (!peek('}')) // Empty namespaces are valid
74
{
75
if (!parse_top(current_success))
76
return false;
77
if (!current_success)
78
parse_success_namespace = false;
79
}
80
81
leave_namespace();
82
83
parse_success = expect('}') && parse_success_namespace;
84
}
85
else if (accept(tokenid::struct_)) // Structure keyword found, parse the structure definition
86
{
87
// Structure definitions are terminated with a semicolon
88
parse_success = parse_struct() && expect(';');
89
}
90
else if (accept(tokenid::technique)) // Technique keyword found, parse the technique definition
91
{
92
parse_success = parse_technique();
93
}
94
else
95
{
96
location attribute_location;
97
shader_type stype = shader_type::unknown;
98
int num_threads[3] = { 0, 0, 0 };
99
100
// Read any function attributes first
101
while (accept('['))
102
{
103
if (!expect(tokenid::identifier))
104
return false;
105
106
const std::string attribute = std::move(_token.literal_as_string);
107
108
if (attribute == "shader")
109
{
110
attribute_location = _token_next.location;
111
112
if (!expect('(') || !expect(tokenid::string_literal))
113
return false;
114
115
if (_token.literal_as_string == "vertex")
116
stype = shader_type::vertex;
117
else if (_token.literal_as_string == "pixel")
118
stype = shader_type::pixel;
119
else if (_token.literal_as_string == "compute")
120
stype = shader_type::compute;
121
122
if (!expect(')'))
123
return false;
124
}
125
else if (attribute == "numthreads")
126
{
127
attribute_location = _token_next.location;
128
129
expression x, y, z;
130
if (!expect('(') || !parse_expression_multary(x, 8) || !expect(',') || !parse_expression_multary(y, 8) || !expect(',') || !parse_expression_multary(z, 8) || !expect(')'))
131
return false;
132
133
if (!x.is_constant)
134
{
135
error(x.location, 3011, "value must be a literal expression");
136
parse_success = false;
137
}
138
if (!y.is_constant)
139
{
140
error(y.location, 3011, "value must be a literal expression");
141
parse_success = false;
142
}
143
if (!z.is_constant)
144
{
145
error(z.location, 3011, "value must be a literal expression");
146
parse_success = false;
147
}
148
x.add_cast_operation({ type::t_int, 1, 1 });
149
y.add_cast_operation({ type::t_int, 1, 1 });
150
z.add_cast_operation({ type::t_int, 1, 1 });
151
num_threads[0] = x.constant.as_int[0];
152
num_threads[1] = y.constant.as_int[0];
153
num_threads[2] = z.constant.as_int[0];
154
}
155
else
156
{
157
warning(_token.location, 0, "unknown attribute '" + attribute + "'");
158
}
159
160
if (!expect(']'))
161
return false;
162
}
163
164
if (type type = {}; parse_type(type)) // Type found, this can be either a variable or a function declaration
165
{
166
parse_success = expect(tokenid::identifier);
167
if (!parse_success)
168
return true;
169
170
if (peek('('))
171
{
172
const std::string name = std::move(_token.literal_as_string);
173
174
// This is definitely a function declaration, so parse it
175
if (!parse_function(type, name, stype, num_threads))
176
{
177
// Insert dummy function into symbol table, so later references can be resolved despite the error
178
insert_symbol(name, { symbol_type::function, UINT32_MAX, { type::t_function } }, true);
179
parse_success = false;
180
return true;
181
}
182
}
183
else
184
{
185
if (!attribute_location.source.empty())
186
{
187
error(attribute_location, 0, "attribute is valid only on functions");
188
parse_success = false;
189
}
190
191
// There may be multiple variable names after the type, handle them all
192
unsigned int count = 0;
193
do
194
{
195
if (count++ > 0 && !(expect(',') && expect(tokenid::identifier)))
196
{
197
parse_success = false;
198
return false;
199
}
200
201
const std::string name = std::move(_token.literal_as_string);
202
203
if (!parse_variable(type, name, true))
204
{
205
// Insert dummy variable into symbol table, so later references can be resolved despite the error
206
insert_symbol(name, { symbol_type::variable, UINT32_MAX, type }, true);
207
// Skip the rest of the statement
208
consume_until(';');
209
parse_success = false;
210
return true;
211
}
212
}
213
while (!peek(';'));
214
215
// Variable declarations are terminated with a semicolon
216
parse_success = expect(';');
217
}
218
}
219
else if (accept(';')) // Ignore single semicolons in the source
220
{
221
parse_success = true;
222
}
223
else
224
{
225
// Unexpected token in source stream, consume and report an error about it
226
consume();
227
// Only add another error message if succeeded parsing previously
228
// This is done to avoid walls of error messages because of consequential errors following a top-level syntax mistake
229
if (parse_success)
230
error(_token.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token.id) + '\'');
231
parse_success = false;
232
}
233
}
234
235
return true;
236
}
237
238
bool reshadefx::parser::parse_statement(bool scoped)
239
{
240
if (!_codegen->is_in_block())
241
{
242
error(_token_next.location, 0, "unreachable code");
243
return false;
244
}
245
246
unsigned int loop_control = 0;
247
unsigned int selection_control = 0;
248
249
// Read any loop and branch control attributes first
250
while (accept('['))
251
{
252
enum control_mask
253
{
254
unroll = 0x1,
255
dont_unroll = 0x2,
256
flatten = (0x1 << 4),
257
dont_flatten = (0x2 << 4),
258
switch_force_case = (0x4 << 4),
259
switch_call = (0x8 << 4)
260
};
261
262
const std::string attribute = std::move(_token_next.literal_as_string);
263
264
if (!expect(tokenid::identifier) || !expect(']'))
265
return false;
266
267
if (attribute == "unroll")
268
loop_control |= unroll;
269
else if (attribute == "loop" || attribute == "fastopt")
270
loop_control |= dont_unroll;
271
else if (attribute == "flatten")
272
selection_control |= flatten;
273
else if (attribute == "branch")
274
selection_control |= dont_flatten;
275
else if (attribute == "forcecase")
276
selection_control |= switch_force_case;
277
else if (attribute == "call")
278
selection_control |= switch_call;
279
else
280
warning(_token.location, 0, "unknown attribute '" + attribute + "'");
281
282
if ((loop_control & (unroll | dont_unroll)) == (unroll | dont_unroll))
283
{
284
error(_token.location, 3524, "can't use loop and unroll attributes together");
285
return false;
286
}
287
if ((selection_control & (flatten | dont_flatten)) == (flatten | dont_flatten))
288
{
289
error(_token.location, 3524, "can't use branch and flatten attributes together");
290
return false;
291
}
292
}
293
294
// Shift by two so that the possible values are 0x01 for 'flatten' and 0x02 for 'dont_flatten', equivalent to 'unroll' and 'dont_unroll'
295
selection_control >>= 4;
296
297
if (peek('{')) // Parse statement block
298
return parse_statement_block(scoped);
299
300
if (accept(';')) // Ignore empty statements
301
return true;
302
303
// Most statements with the exception of declarations are only valid inside functions
304
if (_codegen->is_in_function())
305
{
306
const location statement_location = _token_next.location;
307
308
if (accept(tokenid::if_))
309
{
310
codegen::id true_block = _codegen->create_block(); // Block which contains the statements executed when the condition is true
311
codegen::id false_block = _codegen->create_block(); // Block which contains the statements executed when the condition is false
312
const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the branch re-merged with the current control flow
313
314
expression condition_exp;
315
if (!expect('(') || !parse_expression(condition_exp) || !expect(')'))
316
return false;
317
318
if (!condition_exp.type.is_scalar())
319
{
320
error(condition_exp.location, 3019, "if statement conditional expressions must evaluate to a scalar");
321
return false;
322
}
323
324
// Load condition and convert to boolean value as required by 'OpBranchConditional' in SPIR-V
325
condition_exp.add_cast_operation({ type::t_bool, 1, 1 });
326
327
const codegen::id condition_value = _codegen->emit_load(condition_exp);
328
const codegen::id condition_block = _codegen->leave_block_and_branch_conditional(condition_value, true_block, false_block);
329
330
{ // Then block of the if statement
331
_codegen->enter_block(true_block);
332
333
if (!parse_statement(true))
334
return false;
335
336
true_block = _codegen->leave_block_and_branch(merge_block);
337
}
338
{ // Else block of the if statement
339
_codegen->enter_block(false_block);
340
341
if (accept(tokenid::else_) && !parse_statement(true))
342
return false;
343
344
false_block = _codegen->leave_block_and_branch(merge_block);
345
}
346
347
_codegen->enter_block(merge_block);
348
349
// Emit structured control flow for an if statement and connect all basic blocks
350
_codegen->emit_if(statement_location, condition_value, condition_block, true_block, false_block, selection_control);
351
352
return true;
353
}
354
355
if (accept(tokenid::switch_))
356
{
357
const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the switch re-merged with the current control flow
358
359
expression selector_exp;
360
if (!expect('(') || !parse_expression(selector_exp) || !expect(')'))
361
return false;
362
363
if (!selector_exp.type.is_scalar())
364
{
365
error(selector_exp.location, 3019, "switch statement expression must evaluate to a scalar");
366
return false;
367
}
368
369
// Load selector and convert to integral value as required by switch instruction
370
selector_exp.add_cast_operation({ type::t_int, 1, 1 });
371
372
const codegen::id selector_value = _codegen->emit_load(selector_exp);
373
const codegen::id selector_block = _codegen->leave_block_and_switch(selector_value, merge_block);
374
375
if (!expect('{'))
376
return false;
377
378
scope_guard _(
379
[this, merge_block]() {
380
_loop_break_target_stack.push_back(merge_block);
381
},
382
[this]() {
383
_loop_break_target_stack.pop_back();
384
});
385
386
bool parse_success = true;
387
// The default case jumps to the end of the switch statement if not overwritten
388
codegen::id default_label = merge_block, default_block = merge_block;
389
codegen::id current_label = _codegen->create_block();
390
std::vector<codegen::id> case_literal_and_labels, case_blocks;
391
size_t last_case_label_index = 0;
392
393
// Enter first switch statement body block
394
_codegen->enter_block(current_label);
395
396
while (!peek(tokenid::end_of_file))
397
{
398
while (accept(tokenid::case_) || accept(tokenid::default_))
399
{
400
if (_token.id == tokenid::case_)
401
{
402
expression case_label;
403
if (!parse_expression(case_label))
404
{
405
consume_until('}');
406
return false;
407
}
408
409
if (!case_label.type.is_scalar() || !case_label.type.is_integral() || !case_label.is_constant)
410
{
411
error(case_label.location, 3020, "invalid type for case expression - value must be an integer scalar");
412
consume_until('}');
413
return false;
414
}
415
416
// Check for duplicate case values
417
for (size_t i = 0; i < case_literal_and_labels.size(); i += 2)
418
{
419
if (case_literal_and_labels[i] == case_label.constant.as_uint[0])
420
{
421
parse_success = false;
422
error(case_label.location, 3532, "duplicate case " + std::to_string(case_label.constant.as_uint[0]));
423
break;
424
}
425
}
426
427
case_blocks.emplace_back(); // This is set to the actual block below
428
case_literal_and_labels.push_back(case_label.constant.as_uint[0]);
429
case_literal_and_labels.push_back(current_label);
430
}
431
else
432
{
433
// Check if the default label was already changed by a previous 'default' statement
434
if (default_label != merge_block)
435
{
436
parse_success = false;
437
error(_token.location, 3532, "duplicate default in switch statement");
438
}
439
440
default_label = current_label;
441
default_block = 0; // This is set to the actual block below
442
}
443
444
if (!expect(':'))
445
{
446
consume_until('}');
447
return false;
448
}
449
}
450
451
// It is valid for no statement to follow if this is the last label in the switch body
452
const bool end_of_switch = peek('}');
453
454
if (!end_of_switch && !parse_statement(true))
455
{
456
consume_until('}');
457
return false;
458
}
459
460
// Handle fall-through case and end of switch statement
461
if (peek(tokenid::case_) || peek(tokenid::default_) || end_of_switch)
462
{
463
if (_codegen->is_in_block()) // Disallow fall-through for now
464
{
465
parse_success = false;
466
error(_token_next.location, 3533, "non-empty case statements must have break or return");
467
}
468
469
const codegen::id next_label = end_of_switch ? merge_block : _codegen->create_block();
470
// This is different from 'current_label', since there may have been branching logic inside the case, which would have changed the active block
471
const codegen::id current_block = _codegen->leave_block_and_branch(next_label);
472
473
if (0 == default_block)
474
default_block = current_block;
475
for (size_t i = last_case_label_index; i < case_blocks.size(); ++i)
476
// Need to use the initial label for the switch table, but the current block to merge all the block data
477
case_blocks[i] = current_block;
478
479
current_label = next_label;
480
_codegen->enter_block(current_label);
481
482
if (end_of_switch) // We reached the end, nothing more to do
483
break;
484
485
last_case_label_index = case_blocks.size();
486
}
487
}
488
489
if (case_literal_and_labels.empty() && default_label == merge_block)
490
warning(statement_location, 5002, "switch statement contains no 'case' or 'default' labels");
491
492
// Emit structured control flow for a switch statement and connect all basic blocks
493
_codegen->emit_switch(statement_location, selector_value, selector_block, default_label, default_block, case_literal_and_labels, case_blocks, selection_control);
494
495
return expect('}') && parse_success;
496
}
497
498
if (accept(tokenid::for_))
499
{
500
if (!expect('('))
501
return false;
502
503
scope_guard _(
504
[this]() { enter_scope(); },
505
[this]() { leave_scope(); });
506
507
// Parse initializer first
508
if (type type = {}; parse_type(type))
509
{
510
unsigned int count = 0;
511
do
512
{
513
// There may be multiple declarations behind a type, so loop through them
514
if (count++ > 0 && !expect(','))
515
return false;
516
517
if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string)))
518
return false;
519
}
520
while (!peek(';'));
521
}
522
else
523
{
524
// Initializer can also contain an expression if not a variable declaration list and not empty
525
if (!peek(';'))
526
{
527
expression initializer_exp;
528
if (!parse_expression(initializer_exp))
529
return false;
530
}
531
}
532
533
if (!expect(';'))
534
return false;
535
536
const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the loop
537
const codegen::id header_label = _codegen->create_block(); // Pointer to the loop merge instruction
538
const codegen::id continue_label = _codegen->create_block(); // Pointer to the continue block
539
codegen::id loop_block = _codegen->create_block(); // Pointer to the main loop body block
540
codegen::id condition_block = _codegen->create_block(); // Pointer to the condition check
541
codegen::id condition_value = 0;
542
543
// End current block by branching to the next label
544
const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
545
546
{ // Begin loop block (this header is used for explicit structured control flow)
547
_codegen->enter_block(header_label);
548
549
_codegen->leave_block_and_branch(condition_block);
550
}
551
552
{ // Parse condition block
553
_codegen->enter_block(condition_block);
554
555
if (!peek(';'))
556
{
557
expression condition_exp;
558
if (!parse_expression(condition_exp))
559
return false;
560
561
if (!condition_exp.type.is_scalar())
562
{
563
error(condition_exp.location, 3019, "scalar value expected");
564
return false;
565
}
566
567
// Evaluate condition and branch to the right target
568
condition_exp.add_cast_operation({ type::t_bool, 1, 1 });
569
570
condition_value = _codegen->emit_load(condition_exp);
571
condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block);
572
}
573
else // It is valid for there to be no condition expression
574
{
575
condition_block = _codegen->leave_block_and_branch(loop_block);
576
}
577
578
if (!expect(';'))
579
return false;
580
}
581
582
{ // Parse loop continue block into separate block so it can be appended to the end down the line
583
_codegen->enter_block(continue_label);
584
585
if (!peek(')'))
586
{
587
expression continue_exp;
588
if (!parse_expression(continue_exp))
589
return false;
590
}
591
592
if (!expect(')'))
593
return false;
594
595
// Branch back to the loop header at the end of the continue block
596
_codegen->leave_block_and_branch(header_label);
597
}
598
599
{ // Parse loop body block
600
_codegen->enter_block(loop_block);
601
602
_loop_break_target_stack.push_back(merge_block);
603
_loop_continue_target_stack.push_back(continue_label);
604
605
const bool parse_success = parse_statement(false);
606
607
_loop_break_target_stack.pop_back();
608
_loop_continue_target_stack.pop_back();
609
610
if (!parse_success)
611
return false;
612
613
loop_block = _codegen->leave_block_and_branch(continue_label);
614
}
615
616
// Add merge block label to the end of the loop
617
_codegen->enter_block(merge_block);
618
619
// Emit structured control flow for a loop statement and connect all basic blocks
620
_codegen->emit_loop(statement_location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control);
621
622
return true;
623
}
624
625
if (accept(tokenid::while_))
626
{
627
scope_guard _(
628
[this]() { enter_scope(); },
629
[this]() { leave_scope(); });
630
631
const codegen::id merge_block = _codegen->create_block();
632
const codegen::id header_label = _codegen->create_block();
633
const codegen::id continue_label = _codegen->create_block();
634
codegen::id loop_block = _codegen->create_block();
635
codegen::id condition_block = _codegen->create_block();
636
codegen::id condition_value = 0;
637
638
// End current block by branching to the next label
639
const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
640
641
{ // Begin loop block
642
_codegen->enter_block(header_label);
643
644
_codegen->leave_block_and_branch(condition_block);
645
}
646
647
{ // Parse condition block
648
_codegen->enter_block(condition_block);
649
650
expression condition_exp;
651
if (!expect('(') || !parse_expression(condition_exp) || !expect(')'))
652
return false;
653
654
if (!condition_exp.type.is_scalar())
655
{
656
error(condition_exp.location, 3019, "scalar value expected");
657
return false;
658
}
659
660
// Evaluate condition and branch to the right target
661
condition_exp.add_cast_operation({ type::t_bool, 1, 1 });
662
663
condition_value = _codegen->emit_load(condition_exp);
664
condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block);
665
}
666
667
{ // Parse loop body block
668
_codegen->enter_block(loop_block);
669
670
_loop_break_target_stack.push_back(merge_block);
671
_loop_continue_target_stack.push_back(continue_label);
672
673
const bool parse_success = parse_statement(false);
674
675
_loop_break_target_stack.pop_back();
676
_loop_continue_target_stack.pop_back();
677
678
if (!parse_success)
679
return false;
680
681
loop_block = _codegen->leave_block_and_branch(continue_label);
682
}
683
684
{ // Branch back to the loop header in empty continue block
685
_codegen->enter_block(continue_label);
686
687
_codegen->leave_block_and_branch(header_label);
688
}
689
690
// Add merge block label to the end of the loop
691
_codegen->enter_block(merge_block);
692
693
// Emit structured control flow for a loop statement and connect all basic blocks
694
_codegen->emit_loop(statement_location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control);
695
696
return true;
697
}
698
699
if (accept(tokenid::do_))
700
{
701
const codegen::id merge_block = _codegen->create_block();
702
const codegen::id header_label = _codegen->create_block();
703
const codegen::id continue_label = _codegen->create_block();
704
codegen::id loop_block = _codegen->create_block();
705
codegen::id condition_value = 0;
706
707
// End current block by branching to the next label
708
const codegen::id prev_block = _codegen->leave_block_and_branch(header_label);
709
710
{ // Begin loop block
711
_codegen->enter_block(header_label);
712
713
_codegen->leave_block_and_branch(loop_block);
714
}
715
716
{ // Parse loop body block
717
_codegen->enter_block(loop_block);
718
719
_loop_break_target_stack.push_back(merge_block);
720
_loop_continue_target_stack.push_back(continue_label);
721
722
const bool parse_success = parse_statement(true);
723
724
_loop_break_target_stack.pop_back();
725
_loop_continue_target_stack.pop_back();
726
727
if (!parse_success)
728
return false;
729
730
loop_block = _codegen->leave_block_and_branch(continue_label);
731
}
732
733
{ // Continue block does the condition evaluation
734
_codegen->enter_block(continue_label);
735
736
expression condition_exp;
737
if (!expect(tokenid::while_) || !expect('(') || !parse_expression(condition_exp) || !expect(')') || !expect(';'))
738
return false;
739
740
if (!condition_exp.type.is_scalar())
741
{
742
error(condition_exp.location, 3019, "scalar value expected");
743
return false;
744
}
745
746
// Evaluate condition and branch to the right target
747
condition_exp.add_cast_operation({ type::t_bool, 1, 1 });
748
749
condition_value = _codegen->emit_load(condition_exp);
750
751
_codegen->leave_block_and_branch_conditional(condition_value, header_label, merge_block);
752
}
753
754
// Add merge block label to the end of the loop
755
_codegen->enter_block(merge_block);
756
757
// Emit structured control flow for a loop statement and connect all basic blocks
758
_codegen->emit_loop(statement_location, condition_value, prev_block, header_label, 0, loop_block, continue_label, loop_control);
759
760
return true;
761
}
762
763
if (accept(tokenid::break_))
764
{
765
if (_loop_break_target_stack.empty())
766
{
767
error(statement_location, 3518, "break must be inside loop");
768
return false;
769
}
770
771
// Branch to the break target of the inner most loop on the stack
772
_codegen->leave_block_and_branch(_loop_break_target_stack.back(), 1);
773
774
return expect(';');
775
}
776
777
if (accept(tokenid::continue_))
778
{
779
if (_loop_continue_target_stack.empty())
780
{
781
error(statement_location, 3519, "continue must be inside loop");
782
return false;
783
}
784
785
// Branch to the continue target of the inner most loop on the stack
786
_codegen->leave_block_and_branch(_loop_continue_target_stack.back(), 2);
787
788
return expect(';');
789
}
790
791
if (accept(tokenid::return_))
792
{
793
const type &return_type = _codegen->_current_function->return_type;
794
795
if (!peek(';'))
796
{
797
expression return_exp;
798
if (!parse_expression(return_exp))
799
{
800
consume_until(';');
801
return false;
802
}
803
804
// Cannot return to void
805
if (return_type.is_void())
806
{
807
error(statement_location, 3079, "void functions cannot return a value");
808
// Consume the semicolon that follows the return expression so that parsing may continue
809
accept(';');
810
return false;
811
}
812
813
// Cannot return arrays from a function
814
if (return_exp.type.is_array() || !type::rank(return_exp.type, return_type))
815
{
816
error(statement_location, 3017, "expression (" + return_exp.type.description() + ") does not match function return type (" + return_type.description() + ')');
817
accept(';');
818
return false;
819
}
820
821
// Load return value and perform implicit cast to function return type
822
if (return_exp.type.components() > return_type.components())
823
warning(return_exp.location, 3206, "implicit truncation of vector type");
824
825
return_exp.add_cast_operation(return_type);
826
827
const codegen::id return_value = _codegen->emit_load(return_exp);
828
829
_codegen->leave_block_and_return(return_value);
830
}
831
else if (!return_type.is_void())
832
{
833
// No return value was found, but the function expects one
834
error(statement_location, 3080, "function must return a value");
835
836
// Consume the semicolon that follows the return expression so that parsing may continue
837
accept(';');
838
839
return false;
840
}
841
else
842
{
843
_codegen->leave_block_and_return();
844
}
845
846
return expect(';');
847
}
848
849
if (accept(tokenid::discard_))
850
{
851
// Leave the current function block
852
_codegen->leave_block_and_kill();
853
854
return expect(';');
855
}
856
}
857
858
// Handle variable declarations
859
if (type type = {}; parse_type(type))
860
{
861
unsigned int count = 0;
862
do
863
{
864
// There may be multiple declarations behind a type, so loop through them
865
if (count++ > 0 && !expect(','))
866
{
867
// Try to consume the rest of the declaration so that parsing may continue despite the error
868
consume_until(';');
869
return false;
870
}
871
872
if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string)))
873
{
874
consume_until(';');
875
return false;
876
}
877
}
878
while (!peek(';'));
879
880
return expect(';');
881
}
882
883
// Handle expression statements
884
expression statement_exp;
885
if (parse_expression(statement_exp))
886
return expect(';'); // A statement has to be terminated with a semicolon
887
888
// Gracefully consume any remaining characters until the statement would usually end, so that parsing may continue despite the error
889
consume_until(';');
890
891
return false;
892
}
893
bool reshadefx::parser::parse_statement_block(bool scoped)
894
{
895
if (!expect('{'))
896
return false;
897
898
if (scoped)
899
enter_scope();
900
901
// Parse statements until the end of the block is reached
902
while (!peek('}') && !peek(tokenid::end_of_file))
903
{
904
if (!parse_statement(true))
905
{
906
if (scoped)
907
leave_scope();
908
909
// Ignore the rest of this block
910
unsigned int level = 0;
911
912
while (!peek(tokenid::end_of_file))
913
{
914
if (accept('{'))
915
{
916
++level;
917
}
918
else if (accept('}'))
919
{
920
if (level-- == 0)
921
break;
922
} // These braces are necessary to match the 'else' to the correct 'if' statement
923
else
924
{
925
consume();
926
}
927
}
928
929
return false;
930
}
931
}
932
933
if (scoped)
934
leave_scope();
935
936
return expect('}');
937
}
938
939
bool reshadefx::parser::parse_type(type &type)
940
{
941
type.qualifiers = 0;
942
accept_type_qualifiers(type);
943
944
if (!accept_type_class(type))
945
return false;
946
947
if (type.is_integral() && (type.has(type::q_centroid) || type.has(type::q_noperspective)))
948
{
949
error(_token.location, 4576, "signature specifies invalid interpolation mode for integer component type");
950
return false;
951
}
952
953
if (type.has(type::q_centroid) && !type.has(type::q_noperspective))
954
type.qualifiers |= type::q_linear;
955
956
return true;
957
}
958
bool reshadefx::parser::parse_array_length(type &type)
959
{
960
// Reset array length to zero before checking if one exists
961
type.array_length = 0;
962
963
if (accept('['))
964
{
965
if (accept(']'))
966
{
967
// No length expression, so this is an unbounded array
968
type.array_length = 0xFFFFFFFF;
969
}
970
else if (expression length_exp; parse_expression(length_exp) && expect(']'))
971
{
972
if (!length_exp.is_constant || !(length_exp.type.is_scalar() && length_exp.type.is_integral()))
973
{
974
error(length_exp.location, 3058, "array dimensions must be literal scalar expressions");
975
return false;
976
}
977
978
type.array_length = length_exp.constant.as_uint[0];
979
980
if (type.array_length < 1 || type.array_length > 65536)
981
{
982
error(length_exp.location, 3059, "array dimension must be between 1 and 65536");
983
return false;
984
}
985
}
986
else
987
{
988
return false;
989
}
990
}
991
992
// Multi-dimensional arrays are not supported
993
if (peek('['))
994
{
995
error(_token_next.location, 3119, "arrays cannot be multi-dimensional");
996
return false;
997
}
998
999
return true;
1000
}
1001
1002
bool reshadefx::parser::parse_annotations(std::vector<annotation> &annotations)
1003
{
1004
// Check if annotations exist and return early if none do
1005
if (!accept('<'))
1006
return true;
1007
1008
bool parse_success = true;
1009
1010
while (!peek('>'))
1011
{
1012
if (type type /* = {} */; accept_type_class(type))
1013
warning(_token.location, 4717, "type prefixes for annotations are deprecated and ignored");
1014
1015
if (!expect(tokenid::identifier))
1016
{
1017
consume_until('>');
1018
return false;
1019
}
1020
1021
std::string name = std::move(_token.literal_as_string);
1022
1023
expression annotation_exp;
1024
if (!expect('=') || !parse_expression_multary(annotation_exp) || !expect(';'))
1025
{
1026
consume_until('>');
1027
return false;
1028
}
1029
1030
if (annotation_exp.is_constant)
1031
{
1032
annotations.push_back({ annotation_exp.type, std::move(name), std::move(annotation_exp.constant) });
1033
}
1034
else // Continue parsing annotations despite this not being a constant, since the syntax is still correct
1035
{
1036
parse_success = false;
1037
error(annotation_exp.location, 3011, "value must be a literal expression");
1038
}
1039
}
1040
1041
return expect('>') && parse_success;
1042
}
1043
1044
bool reshadefx::parser::parse_struct()
1045
{
1046
const location struct_location = std::move(_token.location);
1047
1048
struct_type info;
1049
// The structure name is optional
1050
if (accept(tokenid::identifier))
1051
info.name = std::move(_token.literal_as_string);
1052
else
1053
info.name = "_anonymous_struct_" + std::to_string(struct_location.line) + '_' + std::to_string(struct_location.column);
1054
1055
info.unique_name = 'S' + current_scope().name + info.name;
1056
std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_');
1057
1058
if (!expect('{'))
1059
return false;
1060
1061
bool parse_success = true;
1062
1063
while (!peek('}')) // Empty structures are possible
1064
{
1065
member_type member;
1066
1067
if (!parse_type(member.type))
1068
{
1069
error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected struct member type");
1070
consume_until('}');
1071
accept(';');
1072
return false;
1073
}
1074
1075
unsigned int count = 0;
1076
do
1077
{
1078
if ((count++ > 0 && !expect(',')) || !expect(tokenid::identifier))
1079
{
1080
consume_until('}');
1081
accept(';');
1082
return false;
1083
}
1084
1085
member.name = std::move(_token.literal_as_string);
1086
member.location = std::move(_token.location);
1087
1088
if (member.type.is_void())
1089
{
1090
parse_success = false;
1091
error(member.location, 3038, '\'' + member.name + "': struct members cannot be void");
1092
}
1093
if (member.type.is_struct()) // Nesting structures would make input/output argument flattening more complicated, so prevent it for now
1094
{
1095
parse_success = false;
1096
error(member.location, 3090, '\'' + member.name + "': nested struct members are not supported");
1097
}
1098
1099
if (member.type.has(type::q_in) || member.type.has(type::q_out))
1100
{
1101
parse_success = false;
1102
error(member.location, 3055, '\'' + member.name + "': struct members cannot be declared 'in' or 'out'");
1103
}
1104
if (member.type.has(type::q_const))
1105
{
1106
parse_success = false;
1107
error(member.location, 3035, '\'' + member.name + "': struct members cannot be declared 'const'");
1108
}
1109
if (member.type.has(type::q_extern))
1110
{
1111
parse_success = false;
1112
error(member.location, 3006, '\'' + member.name + "': struct members cannot be declared 'extern'");
1113
}
1114
if (member.type.has(type::q_static))
1115
{
1116
parse_success = false;
1117
error(member.location, 3007, '\'' + member.name + "': struct members cannot be declared 'static'");
1118
}
1119
if (member.type.has(type::q_uniform))
1120
{
1121
parse_success = false;
1122
error(member.location, 3047, '\'' + member.name + "': struct members cannot be declared 'uniform'");
1123
}
1124
if (member.type.has(type::q_groupshared))
1125
{
1126
parse_success = false;
1127
error(member.location, 3010, '\'' + member.name + "': struct members cannot be declared 'groupshared'");
1128
}
1129
1130
// Modify member specific type, so that following members in the declaration list are not affected by this
1131
if (!parse_array_length(member.type))
1132
{
1133
consume_until('}');
1134
accept(';');
1135
return false;
1136
}
1137
1138
if (member.type.is_unbounded_array())
1139
{
1140
parse_success = false;
1141
error(member.location, 3072, '\'' + member.name + "': array dimensions of struct members must be explicit");
1142
}
1143
1144
// Structure members may have semantics to use them as input/output types
1145
if (accept(':'))
1146
{
1147
if (!expect(tokenid::identifier))
1148
{
1149
consume_until('}');
1150
accept(';');
1151
return false;
1152
}
1153
1154
member.semantic = std::move(_token.literal_as_string);
1155
// Make semantic upper case to simplify comparison later on
1156
std::transform(member.semantic.begin(), member.semantic.end(), member.semantic.begin(),
1157
[](std::string::value_type c) {
1158
return static_cast<std::string::value_type>(std::toupper(c));
1159
});
1160
1161
if (member.semantic.compare(0, 3, "SV_") != 0)
1162
{
1163
// Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location
1164
if (const char c = member.semantic.back(); c < '0' || c > '9')
1165
member.semantic += '0';
1166
1167
if (member.type.is_integral() && !member.type.has(type::q_nointerpolation))
1168
{
1169
member.type.qualifiers |= type::q_nointerpolation; // Integer fields do not interpolate, so make this explicit (to avoid issues with GLSL)
1170
warning(member.location, 4568, '\'' + member.name + "': integer fields have the 'nointerpolation' qualifier by default");
1171
}
1172
}
1173
else
1174
{
1175
// Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing
1176
if (member.semantic.back() == '0' && (member.semantic[member.semantic.size() - 2] < '0' || member.semantic[member.semantic.size() - 2] > '9'))
1177
member.semantic.pop_back();
1178
}
1179
}
1180
1181
// Save member name and type for bookkeeping
1182
info.member_list.push_back(member);
1183
}
1184
while (!peek(';'));
1185
1186
if (!expect(';'))
1187
{
1188
consume_until('}');
1189
accept(';');
1190
return false;
1191
}
1192
}
1193
1194
// Empty structures are valid, but not usually intended, so emit a warning
1195
if (info.member_list.empty())
1196
warning(struct_location, 5001, "struct has no members");
1197
1198
// Define the structure now that information about all the member types was gathered
1199
const codegen::id id = _codegen->define_struct(struct_location, info);
1200
1201
// Insert the symbol into the symbol table
1202
symbol symbol = { symbol_type::structure, id };
1203
1204
if (!insert_symbol(info.name, symbol, true))
1205
{
1206
error(struct_location, 3003, "redefinition of '" + info.name + '\'');
1207
return false;
1208
}
1209
1210
return expect('}') && parse_success;
1211
}
1212
1213
bool reshadefx::parser::parse_function(type type, std::string name, shader_type stype, int num_threads[3])
1214
{
1215
const location function_location = std::move(_token.location);
1216
1217
if (!expect('(')) // Functions always have a parameter list
1218
return false;
1219
1220
if (type.qualifiers != 0)
1221
{
1222
error(function_location, 3047, '\'' + name + "': function return type cannot have any qualifiers");
1223
return false;
1224
}
1225
1226
function info;
1227
info.name = name;
1228
info.unique_name = 'F' + current_scope().name + name;
1229
std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_');
1230
1231
info.return_type = type;
1232
info.type = stype;
1233
info.num_threads[0] = num_threads[0];
1234
info.num_threads[1] = num_threads[1];
1235
info.num_threads[2] = num_threads[2];
1236
1237
_codegen->_current_function = &info;
1238
1239
bool parse_success = true;
1240
bool expect_parenthesis = true;
1241
1242
// Enter function scope (and leave it again when parsing this function finished)
1243
scope_guard _(
1244
[this]() {
1245
enter_scope();
1246
},
1247
[this]() {
1248
leave_scope();
1249
_codegen->leave_function();
1250
});
1251
1252
while (!peek(')'))
1253
{
1254
if (!info.parameter_list.empty() && !expect(','))
1255
{
1256
parse_success = false;
1257
expect_parenthesis = false;
1258
consume_until(')');
1259
break;
1260
}
1261
1262
member_type param;
1263
1264
if (!parse_type(param.type))
1265
{
1266
error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected parameter type");
1267
parse_success = false;
1268
expect_parenthesis = false;
1269
consume_until(')');
1270
break;
1271
}
1272
1273
if (!expect(tokenid::identifier))
1274
{
1275
parse_success = false;
1276
expect_parenthesis = false;
1277
consume_until(')');
1278
break;
1279
}
1280
1281
param.name = std::move(_token.literal_as_string);
1282
param.location = std::move(_token.location);
1283
1284
if (param.type.is_void())
1285
{
1286
parse_success = false;
1287
error(param.location, 3038, '\'' + param.name + "': function parameters cannot be void");
1288
}
1289
1290
if (param.type.has(type::q_extern))
1291
{
1292
parse_success = false;
1293
error(param.location, 3006, '\'' + param.name + "': function parameters cannot be declared 'extern'");
1294
}
1295
if (param.type.has(type::q_static))
1296
{
1297
parse_success = false;
1298
error(param.location, 3007, '\'' + param.name + "': function parameters cannot be declared 'static'");
1299
}
1300
if (param.type.has(type::q_uniform))
1301
{
1302
parse_success = false;
1303
error(param.location, 3047, '\'' + param.name + "': function parameters cannot be declared 'uniform', consider placing in global scope instead");
1304
}
1305
if (param.type.has(type::q_groupshared))
1306
{
1307
parse_success = false;
1308
error(param.location, 3010, '\'' + param.name + "': function parameters cannot be declared 'groupshared'");
1309
}
1310
1311
if (param.type.has(type::q_out) && param.type.has(type::q_const))
1312
{
1313
parse_success = false;
1314
error(param.location, 3046, '\'' + param.name + "': output parameters cannot be declared 'const'");
1315
}
1316
else if (!param.type.has(type::q_out))
1317
{
1318
// Function parameters are implicitly 'in' if not explicitly defined as 'out'
1319
param.type.qualifiers |= type::q_in;
1320
}
1321
1322
if (!parse_array_length(param.type))
1323
{
1324
parse_success = false;
1325
expect_parenthesis = false;
1326
consume_until(')');
1327
break;
1328
}
1329
1330
if (param.type.is_unbounded_array())
1331
{
1332
parse_success = false;
1333
error(param.location, 3072, '\'' + param.name + "': array dimensions of function parameters must be explicit");
1334
param.type.array_length = 0;
1335
}
1336
1337
// Handle parameter type semantic
1338
if (accept(':'))
1339
{
1340
if (!expect(tokenid::identifier))
1341
{
1342
parse_success = false;
1343
expect_parenthesis = false;
1344
consume_until(')');
1345
break;
1346
}
1347
1348
param.semantic = std::move(_token.literal_as_string);
1349
// Make semantic upper case to simplify comparison later on
1350
std::transform(param.semantic.begin(), param.semantic.end(), param.semantic.begin(),
1351
[](std::string::value_type c) {
1352
return static_cast<std::string::value_type>(std::toupper(c));
1353
});
1354
1355
if (param.semantic.compare(0, 3, "SV_") != 0)
1356
{
1357
// Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location
1358
if (const char c = param.semantic.back(); c < '0' || c > '9')
1359
param.semantic += '0';
1360
1361
if (param.type.is_integral() && !param.type.has(type::q_nointerpolation))
1362
{
1363
param.type.qualifiers |= type::q_nointerpolation; // Integer parameters do not interpolate, so make this explicit (to avoid issues with GLSL)
1364
warning(param.location, 4568, '\'' + param.name + "': integer parameters have the 'nointerpolation' qualifier by default");
1365
}
1366
}
1367
else
1368
{
1369
// Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing
1370
if (param.semantic.back() == '0' && (param.semantic[param.semantic.size() - 2] < '0' || param.semantic[param.semantic.size() - 2] > '9'))
1371
param.semantic.pop_back();
1372
}
1373
}
1374
1375
// Handle default argument
1376
if (accept('='))
1377
{
1378
expression default_value_exp;
1379
if (!parse_expression_multary(default_value_exp))
1380
{
1381
parse_success = false;
1382
expect_parenthesis = false;
1383
consume_until(')');
1384
break;
1385
}
1386
1387
default_value_exp.add_cast_operation(param.type);
1388
1389
if (!default_value_exp.is_constant)
1390
{
1391
parse_success = false;
1392
error(default_value_exp.location, 3011, '\'' + param.name + "': value must be a literal expression");
1393
}
1394
1395
param.default_value = std::move(default_value_exp.constant);
1396
param.has_default_value = true;
1397
}
1398
else
1399
{
1400
if (!info.parameter_list.empty() && info.parameter_list.back().has_default_value)
1401
{
1402
parse_success = false;
1403
error(param.location, 3044, '\'' + name + "': missing default value for parameter '" + param.name + '\'');
1404
}
1405
}
1406
1407
info.parameter_list.push_back(std::move(param));
1408
}
1409
1410
if (expect_parenthesis && !expect(')'))
1411
return false;
1412
1413
// Handle return type semantic
1414
if (accept(':'))
1415
{
1416
if (!expect(tokenid::identifier))
1417
return false;
1418
1419
if (type.is_void())
1420
{
1421
error(_token.location, 3076, '\'' + name + "': void function cannot have a semantic");
1422
return false;
1423
}
1424
1425
info.return_semantic = std::move(_token.literal_as_string);
1426
// Make semantic upper case to simplify comparison later on
1427
std::transform(info.return_semantic.begin(), info.return_semantic.end(), info.return_semantic.begin(),
1428
[](std::string::value_type c) {
1429
return static_cast<std::string::value_type>(std::toupper(c));
1430
});
1431
}
1432
1433
// Check if this is a function declaration without a body
1434
if (accept(';'))
1435
{
1436
error(function_location, 3510, '\'' + name + "': function is missing an implementation");
1437
return false;
1438
}
1439
1440
// Define the function now that information about the declaration was gathered
1441
const codegen::id id = _codegen->define_function(function_location, info);
1442
1443
// Insert the function and parameter symbols into the symbol table and update current function pointer to the permanent one
1444
symbol symbol = { symbol_type::function, id, { type::t_function } };
1445
symbol.function = &_codegen->get_function(id);
1446
1447
if (!insert_symbol(name, symbol, true))
1448
{
1449
error(function_location, 3003, "redefinition of '" + name + '\'');
1450
return false;
1451
}
1452
1453
for (const member_type &param : info.parameter_list)
1454
{
1455
if (!insert_symbol(param.name, { symbol_type::variable, param.id, param.type }))
1456
{
1457
error(param.location, 3003, "redefinition of '" + param.name + '\'');
1458
return false;
1459
}
1460
}
1461
1462
// A function has to start with a new block
1463
_codegen->enter_block(_codegen->create_block());
1464
1465
if (!parse_statement_block(false))
1466
parse_success = false;
1467
1468
// Add implicit return statement to the end of functions
1469
if (_codegen->is_in_block())
1470
_codegen->leave_block_and_return();
1471
1472
return parse_success;
1473
}
1474
1475
bool reshadefx::parser::parse_variable(type type, std::string name, bool global)
1476
{
1477
const location variable_location = std::move(_token.location);
1478
1479
if (type.is_void())
1480
{
1481
error(variable_location, 3038, '\'' + name + "': variables cannot be void");
1482
return false;
1483
}
1484
if (type.has(type::q_in) || type.has(type::q_out))
1485
{
1486
error(variable_location, 3055, '\'' + name + "': variables cannot be declared 'in' or 'out'");
1487
return false;
1488
}
1489
1490
// Local and global variables have different requirements
1491
if (global)
1492
{
1493
// Check that type qualifier combinations are valid
1494
if (type.has(type::q_static))
1495
{
1496
// Global variables that are 'static' cannot be of another storage class
1497
if (type.has(type::q_uniform))
1498
{
1499
error(variable_location, 3007, '\'' + name + "': uniform global variables cannot be declared 'static'");
1500
return false;
1501
}
1502
// The 'volatile' qualifier is only valid memory object declarations that are storage images or uniform blocks
1503
if (type.has(type::q_volatile))
1504
{
1505
error(variable_location, 3008, '\'' + name + "': global variables cannot be declared 'volatile'");
1506
return false;
1507
}
1508
}
1509
else if (!type.has(type::q_groupshared))
1510
{
1511
// Make all global variables 'uniform' by default, since they should be externally visible without the 'static' keyword
1512
if (!type.has(type::q_uniform) && !type.is_object())
1513
warning(variable_location, 5000, '\'' + name + "': global variables are considered 'uniform' by default");
1514
1515
// Global variables that are not 'static' are always 'extern' and 'uniform'
1516
type.qualifiers |= type::q_extern | type::q_uniform;
1517
1518
// It is invalid to make 'uniform' variables constant, since they can be modified externally
1519
if (type.has(type::q_const))
1520
{
1521
error(variable_location, 3035, '\'' + name + "': variables which are 'uniform' cannot be declared 'const'");
1522
return false;
1523
}
1524
}
1525
}
1526
else
1527
{
1528
// Static does not really have meaning on local variables
1529
if (type.has(type::q_static))
1530
type.qualifiers &= ~type::q_static;
1531
1532
if (type.has(type::q_extern))
1533
{
1534
error(variable_location, 3006, '\'' + name + "': local variables cannot be declared 'extern'");
1535
return false;
1536
}
1537
if (type.has(type::q_uniform))
1538
{
1539
error(variable_location, 3047, '\'' + name + "': local variables cannot be declared 'uniform'");
1540
return false;
1541
}
1542
if (type.has(type::q_groupshared))
1543
{
1544
error(variable_location, 3010, '\'' + name + "': local variables cannot be declared 'groupshared'");
1545
return false;
1546
}
1547
1548
if (type.is_object())
1549
{
1550
error(variable_location, 3038, '\'' + name + "': local variables cannot be texture, sampler or storage objects");
1551
return false;
1552
}
1553
}
1554
1555
// The variable name may be followed by an optional array size expression
1556
if (!parse_array_length(type))
1557
return false;
1558
1559
bool parse_success = true;
1560
expression initializer;
1561
texture texture_info;
1562
sampler sampler_info;
1563
storage storage_info;
1564
1565
if (accept(':'))
1566
{
1567
if (!expect(tokenid::identifier))
1568
return false;
1569
1570
if (!global) // Only global variables can have a semantic
1571
{
1572
error(_token.location, 3043, '\'' + name + "': local variables cannot have semantics");
1573
return false;
1574
}
1575
1576
std::string &semantic = texture_info.semantic;
1577
semantic = std::move(_token.literal_as_string);
1578
1579
// Make semantic upper case to simplify comparison later on
1580
std::transform(semantic.begin(), semantic.end(), semantic.begin(),
1581
[](std::string::value_type c) {
1582
return static_cast<std::string::value_type>(std::toupper(c));
1583
});
1584
}
1585
else
1586
{
1587
// Global variables can have optional annotations
1588
if (global && !parse_annotations(sampler_info.annotations))
1589
parse_success = false;
1590
1591
// Variables without a semantic may have an optional initializer
1592
if (accept('='))
1593
{
1594
if (!parse_expression_assignment(initializer))
1595
return false;
1596
1597
if (type.has(type::q_groupshared))
1598
{
1599
error(initializer.location, 3009, '\'' + name + "': variables declared 'groupshared' cannot have an initializer");
1600
return false;
1601
}
1602
1603
// TODO: This could be resolved by initializing these at the beginning of the entry point
1604
if (global && !initializer.is_constant)
1605
{
1606
error(initializer.location, 3011, '\'' + name + "': initial value must be a literal expression");
1607
return false;
1608
}
1609
1610
// Check type compatibility
1611
if ((!type.is_unbounded_array() && initializer.type.array_length != type.array_length) || !type::rank(initializer.type, type))
1612
{
1613
error(initializer.location, 3017, '\'' + name + "': initial value (" + initializer.type.description() + ") does not match variable type (" + type.description() + ')');
1614
return false;
1615
}
1616
if ((initializer.type.rows < type.rows || initializer.type.cols < type.cols) && !initializer.type.is_scalar())
1617
{
1618
error(initializer.location, 3017, '\'' + name + "': cannot implicitly convert these vector types (from " + initializer.type.description() + " to " + type.description() + ')');
1619
return false;
1620
}
1621
1622
// Deduce array size from the initializer expression
1623
if (initializer.type.is_array())
1624
type.array_length = initializer.type.array_length;
1625
1626
// Perform implicit cast from initializer expression to variable type
1627
if (initializer.type.components() > type.components())
1628
warning(initializer.location, 3206, "implicit truncation of vector type");
1629
1630
initializer.add_cast_operation(type);
1631
1632
if (type.has(type::q_static))
1633
initializer.type.qualifiers |= type::q_static;
1634
}
1635
else if (type.is_numeric() || type.is_struct()) // Numeric variables without an initializer need special handling
1636
{
1637
if (type.has(type::q_const)) // Constants have to have an initial value
1638
{
1639
error(variable_location, 3012, '\'' + name + "': missing initial value");
1640
return false;
1641
}
1642
1643
if (!type.has(type::q_uniform)) // Zero initialize all global variables
1644
initializer.reset_to_rvalue_constant(variable_location, {}, type);
1645
}
1646
else if (global && accept('{')) // Textures and samplers can have a property block attached to their declaration
1647
{
1648
// Non-numeric variables cannot be constants
1649
if (type.has(type::q_const))
1650
{
1651
error(variable_location, 3035, '\'' + name + "': this variable type cannot be declared 'const'");
1652
return false;
1653
}
1654
1655
while (!peek('}'))
1656
{
1657
if (!expect(tokenid::identifier))
1658
{
1659
consume_until('}');
1660
return false;
1661
}
1662
1663
location property_location = std::move(_token.location);
1664
const std::string property_name = std::move(_token.literal_as_string);
1665
1666
if (!expect('='))
1667
{
1668
consume_until('}');
1669
return false;
1670
}
1671
1672
backup();
1673
1674
expression property_exp;
1675
1676
if (accept(tokenid::identifier)) // Handle special enumeration names for property values
1677
{
1678
// Transform identifier to uppercase to do case-insensitive comparison
1679
std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(),
1680
[](std::string::value_type c) {
1681
return static_cast<std::string::value_type>(std::toupper(c));
1682
});
1683
1684
static const std::unordered_map<std::string_view, uint32_t> s_enum_values = {
1685
{ "NONE", 0 }, { "POINT", 0 },
1686
{ "LINEAR", 1 },
1687
{ "ANISOTROPIC", 0x55 },
1688
{ "WRAP", uint32_t(texture_address_mode::wrap) }, { "REPEAT", uint32_t(texture_address_mode::wrap) },
1689
{ "MIRROR", uint32_t(texture_address_mode::mirror) },
1690
{ "CLAMP", uint32_t(texture_address_mode::clamp) },
1691
{ "BORDER", uint32_t(texture_address_mode::border) },
1692
{ "R8", uint32_t(texture_format::r8) },
1693
{ "R16", uint32_t(texture_format::r16) },
1694
{ "R16F", uint32_t(texture_format::r16f) },
1695
{ "R32I", uint32_t(texture_format::r32i) },
1696
{ "R32U", uint32_t(texture_format::r32u) },
1697
{ "R32F", uint32_t(texture_format::r32f) },
1698
{ "RG8", uint32_t(texture_format::rg8) }, { "R8G8", uint32_t(texture_format::rg8) },
1699
{ "RG16", uint32_t(texture_format::rg16) }, { "R16G16", uint32_t(texture_format::rg16) },
1700
{ "RG16F", uint32_t(texture_format::rg16f) }, { "R16G16F", uint32_t(texture_format::rg16f) },
1701
{ "RG32F", uint32_t(texture_format::rg32f) }, { "R32G32F", uint32_t(texture_format::rg32f) },
1702
{ "RGBA8", uint32_t(texture_format::rgba8) }, { "R8G8B8A8", uint32_t(texture_format::rgba8) },
1703
{ "RGBA16", uint32_t(texture_format::rgba16) }, { "R16G16B16A16", uint32_t(texture_format::rgba16) },
1704
{ "RGBA16F", uint32_t(texture_format::rgba16f) }, { "R16G16B16A16F", uint32_t(texture_format::rgba16f) },
1705
{ "RGBA32F", uint32_t(texture_format::rgba32f) }, { "R32G32B32A32F", uint32_t(texture_format::rgba32f) },
1706
{ "RGB10A2", uint32_t(texture_format::rgb10a2) }, { "R10G10B10A2", uint32_t(texture_format::rgb10a2) },
1707
};
1708
1709
// Look up identifier in list of possible enumeration names
1710
if (const auto it = s_enum_values.find(_token.literal_as_string);
1711
it != s_enum_values.end())
1712
property_exp.reset_to_rvalue_constant(_token.location, it->second);
1713
else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression
1714
restore();
1715
}
1716
1717
// Parse right hand side as normal expression if no special enumeration name was matched already
1718
if (!property_exp.is_constant && !parse_expression_multary(property_exp))
1719
{
1720
consume_until('}');
1721
return false;
1722
}
1723
1724
if (property_name == "Texture")
1725
{
1726
// Ignore invalid symbols that were added during error recovery
1727
if (property_exp.base == UINT32_MAX)
1728
{
1729
consume_until('}');
1730
return false;
1731
}
1732
1733
if (!property_exp.type.is_texture())
1734
{
1735
error(property_exp.location, 3020, "type mismatch, expected texture name");
1736
consume_until('}');
1737
return false;
1738
}
1739
1740
if (type.is_sampler() || type.is_storage())
1741
{
1742
texture &target_info = _codegen->get_texture(property_exp.base);
1743
if (type.is_storage())
1744
// Texture is used as storage
1745
target_info.storage_access = true;
1746
1747
texture_info = target_info;
1748
sampler_info.texture_name = target_info.unique_name;
1749
storage_info.texture_name = target_info.unique_name;
1750
}
1751
}
1752
else
1753
{
1754
if (!property_exp.is_constant || !property_exp.type.is_scalar())
1755
{
1756
error(property_exp.location, 3538, "value must be a literal scalar expression");
1757
consume_until('}');
1758
return false;
1759
}
1760
1761
// All states below expect the value to be of an integer type
1762
property_exp.add_cast_operation({ type::t_int, 1, 1 });
1763
const int value = property_exp.constant.as_int[0];
1764
1765
if (value < 0) // There is little use for negative values, so warn in those cases
1766
warning(property_exp.location, 3571, "negative value specified for property '" + property_name + '\'');
1767
1768
if (type.is_texture())
1769
{
1770
if (property_name == "Width")
1771
texture_info.width = value > 0 ? value : 1;
1772
else if (type.texture_dimension() >= 2 && property_name == "Height")
1773
texture_info.height = value > 0 ? value : 1;
1774
else if (type.texture_dimension() >= 3 && property_name == "Depth")
1775
texture_info.depth = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1;
1776
else if (property_name == "MipLevels")
1777
// Also ensures negative values do not cause problems
1778
texture_info.levels = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1;
1779
else if (property_name == "Format")
1780
texture_info.format = static_cast<texture_format>(value);
1781
else
1782
error(property_location, 3004, "unrecognized property '" + property_name + '\'');
1783
}
1784
else if (type.is_sampler())
1785
{
1786
if (property_name == "SRGBTexture" || property_name == "SRGBReadEnable")
1787
sampler_info.srgb = value != 0;
1788
else if (property_name == "AddressU")
1789
sampler_info.address_u = static_cast<texture_address_mode>(value);
1790
else if (property_name == "AddressV")
1791
sampler_info.address_v = static_cast<texture_address_mode>(value);
1792
else if (property_name == "AddressW")
1793
sampler_info.address_w = static_cast<texture_address_mode>(value);
1794
else if (property_name == "MinFilter")
1795
// Combine sampler filter components into a single filter enumeration value
1796
sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x4F) | ((value & 0x03) << 4) | (value & 0x40));
1797
else if (property_name == "MagFilter")
1798
sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x73) | ((value & 0x03) << 2) | (value & 0x40));
1799
else if (property_name == "MipFilter")
1800
sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x7C) | ((value & 0x03) ) | (value & 0x40));
1801
else if (property_name == "MinLOD" || property_name == "MaxMipLevel")
1802
sampler_info.min_lod = static_cast<float>(value);
1803
else if (property_name == "MaxLOD")
1804
sampler_info.max_lod = static_cast<float>(value);
1805
else if (property_name == "MipLODBias" || property_name == "MipMapLodBias")
1806
sampler_info.lod_bias = static_cast<float>(value);
1807
else
1808
error(property_location, 3004, "unrecognized property '" + property_name + '\'');
1809
}
1810
else if (type.is_storage())
1811
{
1812
if (property_name == "MipLOD" || property_name == "MipLevel")
1813
storage_info.level = value > 0 && value < std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 0;
1814
else
1815
error(property_location, 3004, "unrecognized property '" + property_name + '\'');
1816
}
1817
}
1818
1819
if (!expect(';'))
1820
{
1821
consume_until('}');
1822
return false;
1823
}
1824
}
1825
1826
if (!expect('}'))
1827
return false;
1828
}
1829
}
1830
1831
// At this point the array size should be known (either from the declaration or the initializer)
1832
if (type.is_unbounded_array())
1833
{
1834
error(variable_location, 3074, '\'' + name + "': implicit array missing initial value");
1835
return false;
1836
}
1837
1838
symbol symbol;
1839
1840
// Variables with a constant initializer and constant type are named constants
1841
// Skip this for very large arrays though, to avoid large amounts of duplicated values when that array constant is accessed with a dynamic index
1842
if (type.is_numeric() && type.has(type::q_const) && initializer.is_constant && type.array_length < 100)
1843
{
1844
// Named constants are special symbols
1845
symbol = { symbol_type::constant, 0, type, initializer.constant };
1846
}
1847
else if (type.is_texture())
1848
{
1849
assert(global);
1850
1851
texture_info.name = name;
1852
texture_info.type = static_cast<texture_type>(type.texture_dimension());
1853
1854
// Add namespace scope to avoid name clashes
1855
texture_info.unique_name = 'V' + current_scope().name + name;
1856
std::replace(texture_info.unique_name.begin(), texture_info.unique_name.end(), ':', '_');
1857
1858
texture_info.annotations = std::move(sampler_info.annotations);
1859
1860
const codegen::id id = _codegen->define_texture(variable_location, texture_info);
1861
symbol = { symbol_type::variable, id, type };
1862
}
1863
// Samplers are actually combined image samplers
1864
else if (type.is_sampler())
1865
{
1866
assert(global);
1867
1868
if (sampler_info.texture_name.empty())
1869
{
1870
error(variable_location, 3012, '\'' + name + "': missing 'Texture' property");
1871
return false;
1872
}
1873
if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type))
1874
{
1875
error(variable_location, 3521, '\'' + name + "': type mismatch between texture and sampler type");
1876
return false;
1877
}
1878
if (sampler_info.srgb && texture_info.format != texture_format::rgba8)
1879
{
1880
error(variable_location, 4582, '\'' + name + "': texture does not support sRGB sampling (only textures with RGBA8 format do)");
1881
return false;
1882
}
1883
1884
if (texture_info.format == texture_format::r32i ?
1885
!type.is_integral() || !type.is_signed() :
1886
texture_info.format == texture_format::r32u ?
1887
!type.is_integral() || !type.is_unsigned() :
1888
!type.is_floating_point())
1889
{
1890
error(variable_location, 4582, '\'' + name + "': type mismatch between texture format and sampler element type");
1891
return false;
1892
}
1893
1894
sampler_info.name = name;
1895
sampler_info.type = type;
1896
1897
// Add namespace scope to avoid name clashes
1898
sampler_info.unique_name = 'V' + current_scope().name + name;
1899
std::replace(sampler_info.unique_name.begin(), sampler_info.unique_name.end(), ':', '_');
1900
1901
const codegen::id id = _codegen->define_sampler(variable_location, texture_info, sampler_info);
1902
symbol = { symbol_type::variable, id, type };
1903
}
1904
else if (type.is_storage())
1905
{
1906
assert(global);
1907
1908
if (storage_info.texture_name.empty())
1909
{
1910
error(variable_location, 3012, '\'' + name + "': missing 'Texture' property");
1911
return false;
1912
}
1913
if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type))
1914
{
1915
error(variable_location, 3521, '\'' + name + "': type mismatch between texture and storage type");
1916
return false;
1917
}
1918
1919
if (texture_info.format == texture_format::r32i ?
1920
!type.is_integral() || !type.is_signed() :
1921
texture_info.format == texture_format::r32u ?
1922
!type.is_integral() || !type.is_unsigned() :
1923
!type.is_floating_point())
1924
{
1925
error(variable_location, 4582, '\'' + name + "': type mismatch between texture format and storage element type");
1926
return false;
1927
}
1928
1929
storage_info.name = name;
1930
storage_info.type = type;
1931
1932
// Add namespace scope to avoid name clashes
1933
storage_info.unique_name = 'V' + current_scope().name + name;
1934
std::replace(storage_info.unique_name.begin(), storage_info.unique_name.end(), ':', '_');
1935
1936
if (storage_info.level > texture_info.levels - 1)
1937
storage_info.level = texture_info.levels - 1;
1938
1939
const codegen::id id = _codegen->define_storage(variable_location, texture_info, storage_info);
1940
symbol = { symbol_type::variable, id, type };
1941
}
1942
// Uniform variables are put into a global uniform buffer structure
1943
else if (type.has(type::q_uniform))
1944
{
1945
assert(global);
1946
1947
uniform uniform_info;
1948
uniform_info.name = name;
1949
uniform_info.type = type;
1950
1951
uniform_info.annotations = std::move(sampler_info.annotations);
1952
1953
uniform_info.initializer_value = std::move(initializer.constant);
1954
uniform_info.has_initializer_value = initializer.is_constant;
1955
1956
const codegen::id id = _codegen->define_uniform(variable_location, uniform_info);
1957
symbol = { symbol_type::variable, id, type };
1958
}
1959
// All other variables are separate entities
1960
else
1961
{
1962
// Update global variable names to contain the namespace scope to avoid name clashes
1963
std::string unique_name = global ? 'V' + current_scope().name + name : name;
1964
std::replace(unique_name.begin(), unique_name.end(), ':', '_');
1965
1966
symbol = { symbol_type::variable, 0, type };
1967
symbol.id = _codegen->define_variable(variable_location, type, std::move(unique_name), global,
1968
// Shared variables cannot have an initializer
1969
type.has(type::q_groupshared) ? 0 : _codegen->emit_load(initializer));
1970
}
1971
1972
// Insert the symbol into the symbol table
1973
if (!insert_symbol(name, symbol, global))
1974
{
1975
error(variable_location, 3003, "redefinition of '" + name + '\'');
1976
return false;
1977
}
1978
1979
return parse_success;
1980
}
1981
1982
bool reshadefx::parser::parse_technique()
1983
{
1984
if (!expect(tokenid::identifier))
1985
return false;
1986
1987
technique info;
1988
info.name = std::move(_token.literal_as_string);
1989
1990
bool parse_success = parse_annotations(info.annotations);
1991
1992
if (!expect('{'))
1993
return false;
1994
1995
while (!peek('}'))
1996
{
1997
pass pass;
1998
if (parse_technique_pass(pass))
1999
{
2000
info.passes.push_back(std::move(pass));
2001
}
2002
else
2003
{
2004
parse_success = false;
2005
if (!peek(tokenid::pass) && !peek('}')) // If there is another pass definition following, try to parse that despite the error
2006
{
2007
consume_until('}');
2008
return false;
2009
}
2010
}
2011
}
2012
2013
_codegen->define_technique(std::move(info));
2014
2015
return expect('}') && parse_success;
2016
}
2017
bool reshadefx::parser::parse_technique_pass(pass &info)
2018
{
2019
if (!expect(tokenid::pass))
2020
return false;
2021
2022
const location pass_location = std::move(_token.location);
2023
2024
// Passes can have an optional name
2025
if (accept(tokenid::identifier))
2026
info.name = std::move(_token.literal_as_string);
2027
2028
bool parse_success = true;
2029
bool targets_support_srgb = true;
2030
function vs_info = {}, ps_info = {}, cs_info = {};
2031
2032
if (!expect('{'))
2033
return false;
2034
2035
while (!peek('}'))
2036
{
2037
// Parse pass states
2038
if (!expect(tokenid::identifier))
2039
{
2040
consume_until('}');
2041
return false;
2042
}
2043
2044
location state_location = std::move(_token.location);
2045
const std::string state_name = std::move(_token.literal_as_string);
2046
2047
if (!expect('='))
2048
{
2049
consume_until('}');
2050
return false;
2051
}
2052
2053
const bool is_shader_state = state_name.size() > 6 && state_name.compare(state_name.size() - 6, 6, "Shader") == 0; // VertexShader, PixelShader, ComputeShader, ...
2054
const bool is_texture_state = state_name.compare(0, 12, "RenderTarget") == 0 && (state_name.size() == 12 || (state_name[12] >= '0' && state_name[12] < '8'));
2055
2056
// Shader and render target assignment looks up values in the symbol table, so handle those separately from the other states
2057
if (is_shader_state || is_texture_state)
2058
{
2059
std::string identifier;
2060
scoped_symbol symbol;
2061
if (!accept_symbol(identifier, symbol))
2062
{
2063
consume_until('}');
2064
return false;
2065
}
2066
2067
state_location = std::move(_token.location);
2068
2069
int num_threads[3] = { 0, 0, 0 };
2070
if (accept('<'))
2071
{
2072
expression x, y, z;
2073
if (!parse_expression_multary(x, 8) || !expect(',') || !parse_expression_multary(y, 8))
2074
{
2075
consume_until('}');
2076
return false;
2077
}
2078
2079
// Parse optional third dimension (defaults to 1)
2080
z.reset_to_rvalue_constant({}, 1);
2081
if (accept(',') && !parse_expression_multary(z, 8))
2082
{
2083
consume_until('}');
2084
return false;
2085
}
2086
2087
if (!x.is_constant)
2088
{
2089
error(x.location, 3011, "value must be a literal expression");
2090
consume_until('}');
2091
return false;
2092
}
2093
if (!y.is_constant)
2094
{
2095
error(y.location, 3011, "value must be a literal expression");
2096
consume_until('}');
2097
return false;
2098
}
2099
if (!z.is_constant)
2100
{
2101
error(z.location, 3011, "value must be a literal expression");
2102
consume_until('}');
2103
return false;
2104
}
2105
x.add_cast_operation({ type::t_int, 1, 1 });
2106
y.add_cast_operation({ type::t_int, 1, 1 });
2107
z.add_cast_operation({ type::t_int, 1, 1 });
2108
num_threads[0] = x.constant.as_int[0];
2109
num_threads[1] = y.constant.as_int[0];
2110
num_threads[2] = z.constant.as_int[0];
2111
2112
if (!expect('>'))
2113
{
2114
consume_until('}');
2115
return false;
2116
}
2117
}
2118
2119
// Ignore invalid symbols that were added during error recovery
2120
if (symbol.id != UINT32_MAX)
2121
{
2122
if (is_shader_state)
2123
{
2124
if (!symbol.id)
2125
{
2126
parse_success = false;
2127
error(state_location, 3501, "undeclared identifier '" + identifier + "', expected function name");
2128
}
2129
else if (!symbol.type.is_function())
2130
{
2131
parse_success = false;
2132
error(state_location, 3020, "type mismatch, expected function name");
2133
}
2134
else
2135
{
2136
// Look up the matching function info for this function definition
2137
const function &function_info = _codegen->get_function(symbol.id);
2138
2139
// We potentially need to generate a special entry point function which translates between function parameters and input/output variables
2140
switch (state_name[0])
2141
{
2142
case 'V':
2143
vs_info = function_info;
2144
if (vs_info.type != shader_type::unknown && vs_info.type != shader_type::vertex)
2145
{
2146
parse_success = false;
2147
error(state_location, 3020, "type mismatch, expected vertex shader function");
2148
break;
2149
}
2150
vs_info.type = shader_type::vertex;
2151
_codegen->define_entry_point(vs_info);
2152
info.vs_entry_point = vs_info.unique_name;
2153
break;
2154
case 'P':
2155
ps_info = function_info;
2156
if (ps_info.type != shader_type::unknown && ps_info.type != shader_type::pixel)
2157
{
2158
parse_success = false;
2159
error(state_location, 3020, "type mismatch, expected pixel shader function");
2160
break;
2161
}
2162
ps_info.type = shader_type::pixel;
2163
_codegen->define_entry_point(ps_info);
2164
info.ps_entry_point = ps_info.unique_name;
2165
break;
2166
case 'C':
2167
cs_info = function_info;
2168
if (cs_info.type != shader_type::unknown && cs_info.type != shader_type::compute)
2169
{
2170
parse_success = false;
2171
error(state_location, 3020, "type mismatch, expected compute shader function");
2172
break;
2173
}
2174
cs_info.type = shader_type::compute;
2175
// Only use number of threads from pass when specified, otherwise fall back to number specified on the function definition with an attribute
2176
if (num_threads[0] != 0)
2177
{
2178
cs_info.num_threads[0] = num_threads[0];
2179
cs_info.num_threads[1] = num_threads[1];
2180
cs_info.num_threads[2] = num_threads[2];
2181
}
2182
else
2183
{
2184
cs_info.num_threads[0] = std::max(cs_info.num_threads[0], 1);
2185
cs_info.num_threads[1] = std::max(cs_info.num_threads[1], 1);
2186
cs_info.num_threads[2] = std::max(cs_info.num_threads[2], 1);
2187
}
2188
_codegen->define_entry_point(cs_info);
2189
info.cs_entry_point = cs_info.unique_name;
2190
break;
2191
}
2192
}
2193
}
2194
else
2195
{
2196
assert(is_texture_state);
2197
2198
if (!symbol.id)
2199
{
2200
parse_success = false;
2201
error(state_location, 3004, "undeclared identifier '" + identifier + "', expected texture name");
2202
}
2203
else if (!symbol.type.is_texture())
2204
{
2205
parse_success = false;
2206
error(state_location, 3020, "type mismatch, expected texture name");
2207
}
2208
else if (symbol.type.texture_dimension() != 2)
2209
{
2210
parse_success = false;
2211
error(state_location, 3020, "cannot use texture" + std::to_string(symbol.type.texture_dimension()) + "D as render target");
2212
}
2213
else
2214
{
2215
texture &target_info = _codegen->get_texture(symbol.id);
2216
2217
if (target_info.semantic.empty())
2218
{
2219
// Texture is used as a render target
2220
target_info.render_target = true;
2221
2222
// Verify that all render targets in this pass have the same dimensions
2223
if (info.viewport_width != 0 && info.viewport_height != 0 && (target_info.width != info.viewport_width || target_info.height != info.viewport_height))
2224
{
2225
parse_success = false;
2226
error(state_location, 4545, "cannot use multiple render targets with different texture dimensions (is " + std::to_string(target_info.width) + 'x' + std::to_string(target_info.height) + ", but expected " + std::to_string(info.viewport_width) + 'x' + std::to_string(info.viewport_height) + ')');
2227
}
2228
2229
info.viewport_width = target_info.width;
2230
info.viewport_height = target_info.height;
2231
2232
const int target_index = state_name.size() > 12 ? (state_name[12] - '0') : 0;
2233
info.render_target_names[target_index] = target_info.unique_name;
2234
2235
// Only RGBA8 format supports sRGB writes across all APIs
2236
if (target_info.format != texture_format::rgba8)
2237
targets_support_srgb = false;
2238
}
2239
else
2240
{
2241
parse_success = false;
2242
error(state_location, 3020, "cannot use texture with semantic as render target");
2243
}
2244
}
2245
}
2246
}
2247
else
2248
{
2249
parse_success = false;
2250
}
2251
}
2252
else // Handle the rest of the pass states
2253
{
2254
backup();
2255
2256
expression state_exp;
2257
2258
if (accept(tokenid::identifier)) // Handle special enumeration names for pass states
2259
{
2260
// Transform identifier to uppercase to do case-insensitive comparison
2261
std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(),
2262
[](std::string::value_type c) {
2263
return static_cast<std::string::value_type>(std::toupper(c));
2264
});
2265
2266
static const std::unordered_map<std::string_view, uint32_t> s_enum_values = {
2267
{ "NONE", 0 }, { "ZERO", 0 }, { "ONE", 1 },
2268
{ "ADD", uint32_t(blend_op::add) },
2269
{ "SUBTRACT", uint32_t(blend_op::subtract) },
2270
{ "REVSUBTRACT", uint32_t(blend_op::reverse_subtract) },
2271
{ "MIN", uint32_t(blend_op::min) },
2272
{ "MAX", uint32_t(blend_op::max) },
2273
{ "SRCCOLOR", uint32_t(blend_factor::source_color) },
2274
{ "INVSRCCOLOR", uint32_t(blend_factor::one_minus_source_color) },
2275
{ "DESTCOLOR", uint32_t(blend_factor::dest_color) },
2276
{ "INVDESTCOLOR", uint32_t(blend_factor::one_minus_dest_color) },
2277
{ "SRCALPHA", uint32_t(blend_factor::source_alpha) },
2278
{ "INVSRCALPHA", uint32_t(blend_factor::one_minus_source_alpha) },
2279
{ "DESTALPHA", uint32_t(blend_factor::dest_alpha) },
2280
{ "INVDESTALPHA", uint32_t(blend_factor::one_minus_dest_alpha) },
2281
{ "KEEP", uint32_t(stencil_op::keep) },
2282
{ "REPLACE", uint32_t(stencil_op::replace) },
2283
{ "INVERT", uint32_t(stencil_op::invert) },
2284
{ "INCR", uint32_t(stencil_op::increment) },
2285
{ "INCRSAT", uint32_t(stencil_op::increment_saturate) },
2286
{ "DECR", uint32_t(stencil_op::decrement) },
2287
{ "DECRSAT", uint32_t(stencil_op::decrement_saturate) },
2288
{ "NEVER", uint32_t(stencil_func::never) },
2289
{ "EQUAL", uint32_t(stencil_func::equal) },
2290
{ "NEQUAL", uint32_t(stencil_func::not_equal) }, { "NOTEQUAL", uint32_t(stencil_func::not_equal) },
2291
{ "LESS", uint32_t(stencil_func::less) },
2292
{ "GREATER", uint32_t(stencil_func::greater) },
2293
{ "LEQUAL", uint32_t(stencil_func::less_equal) }, { "LESSEQUAL", uint32_t(stencil_func::less_equal) },
2294
{ "GEQUAL", uint32_t(stencil_func::greater_equal) }, { "GREATEREQUAL", uint32_t(stencil_func::greater_equal) },
2295
{ "ALWAYS", uint32_t(stencil_func::always) },
2296
{ "POINTS", uint32_t(primitive_topology::point_list) },
2297
{ "POINTLIST", uint32_t(primitive_topology::point_list) },
2298
{ "LINES", uint32_t(primitive_topology::line_list) },
2299
{ "LINELIST", uint32_t(primitive_topology::line_list) },
2300
{ "LINESTRIP", uint32_t(primitive_topology::line_strip) },
2301
{ "TRIANGLES", uint32_t(primitive_topology::triangle_list) },
2302
{ "TRIANGLELIST", uint32_t(primitive_topology::triangle_list) },
2303
{ "TRIANGLESTRIP", uint32_t(primitive_topology::triangle_strip) },
2304
};
2305
2306
// Look up identifier in list of possible enumeration names
2307
if (const auto it = s_enum_values.find(_token.literal_as_string);
2308
it != s_enum_values.end())
2309
state_exp.reset_to_rvalue_constant(_token.location, it->second);
2310
else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression
2311
restore();
2312
}
2313
2314
// Parse right hand side as normal expression if no special enumeration name was matched already
2315
if (!state_exp.is_constant && !parse_expression_multary(state_exp))
2316
{
2317
consume_until('}');
2318
return false;
2319
}
2320
2321
if (!state_exp.is_constant || !state_exp.type.is_scalar())
2322
{
2323
parse_success = false;
2324
error(state_exp.location, 3011, "pass state value must be a literal scalar expression");
2325
}
2326
2327
// All states below expect the value to be of an unsigned integer type
2328
state_exp.add_cast_operation({ type::t_uint, 1, 1 });
2329
const unsigned int value = state_exp.constant.as_uint[0];
2330
2331
#define SET_STATE_VALUE_INDEXED(name, info_name, value) \
2332
else if (constexpr size_t name##_len = sizeof(#name) - 1; state_name.compare(0, name##_len, #name) == 0 && \
2333
(state_name.size() == name##_len || (state_name[name##_len] >= '0' && state_name[name##_len] < ('0' + static_cast<char>(std::size(info.info_name)))))) \
2334
{ \
2335
if (state_name.size() != name##_len) \
2336
info.info_name[state_name[name##_len] - '0'] = (value); \
2337
else \
2338
for (int i = 0; i < static_cast<int>(std::size(info.info_name)); ++i) \
2339
info.info_name[i] = (value); \
2340
}
2341
2342
if (state_name == "SRGBWriteEnable")
2343
info.srgb_write_enable = (value != 0);
2344
SET_STATE_VALUE_INDEXED(BlendEnable, blend_enable, value != 0)
2345
else if (state_name == "StencilEnable")
2346
info.stencil_enable = (value != 0);
2347
else if (state_name == "ClearRenderTargets")
2348
info.clear_render_targets = (value != 0);
2349
SET_STATE_VALUE_INDEXED(ColorWriteMask, render_target_write_mask, value & 0xFF)
2350
SET_STATE_VALUE_INDEXED(RenderTargetWriteMask, render_target_write_mask, value & 0xFF)
2351
else if (state_name == "StencilReadMask" || state_name == "StencilMask")
2352
info.stencil_read_mask = value & 0xFF;
2353
else if (state_name == "StencilWriteMask")
2354
info.stencil_write_mask = value & 0xFF;
2355
SET_STATE_VALUE_INDEXED(BlendOp, color_blend_op, static_cast<blend_op>(value))
2356
SET_STATE_VALUE_INDEXED(BlendOpAlpha, alpha_blend_op, static_cast<blend_op>(value))
2357
SET_STATE_VALUE_INDEXED(SrcBlend, source_color_blend_factor, static_cast<blend_factor>(value))
2358
SET_STATE_VALUE_INDEXED(SrcBlendAlpha, source_alpha_blend_factor, static_cast<blend_factor>(value))
2359
SET_STATE_VALUE_INDEXED(DestBlend, dest_color_blend_factor, static_cast<blend_factor>(value))
2360
SET_STATE_VALUE_INDEXED(DestBlendAlpha, dest_alpha_blend_factor, static_cast<blend_factor>(value))
2361
else if (state_name == "StencilFunc")
2362
info.stencil_comparison_func = static_cast<stencil_func>(value);
2363
else if (state_name == "StencilRef")
2364
info.stencil_reference_value = value;
2365
else if (state_name == "StencilPass" || state_name == "StencilPassOp")
2366
info.stencil_pass_op = static_cast<stencil_op>(value);
2367
else if (state_name == "StencilFail" || state_name == "StencilFailOp")
2368
info.stencil_fail_op = static_cast<stencil_op>(value);
2369
else if (state_name == "StencilZFail" || state_name == "StencilDepthFail" || state_name == "StencilDepthFailOp")
2370
info.stencil_depth_fail_op = static_cast<stencil_op>(value);
2371
else if (state_name == "VertexCount")
2372
info.num_vertices = value;
2373
else if (state_name == "PrimitiveType" || state_name == "PrimitiveTopology")
2374
info.topology = static_cast<primitive_topology>(value);
2375
else if (state_name == "DispatchSizeX")
2376
info.viewport_width = value;
2377
else if (state_name == "DispatchSizeY")
2378
info.viewport_height = value;
2379
else if (state_name == "DispatchSizeZ")
2380
info.viewport_dispatch_z = value;
2381
else if (state_name == "GenerateMipmaps" || state_name == "GenerateMipMaps")
2382
info.generate_mipmaps = (value != 0);
2383
else
2384
error(state_location, 3004, "unrecognized pass state '" + state_name + '\'');
2385
2386
#undef SET_STATE_VALUE_INDEXED
2387
}
2388
2389
if (!expect(';'))
2390
{
2391
consume_until('}');
2392
return false;
2393
}
2394
}
2395
2396
if (parse_success)
2397
{
2398
if (!info.cs_entry_point.empty())
2399
{
2400
if (info.viewport_width == 0 || info.viewport_height == 0)
2401
{
2402
parse_success = false;
2403
error(pass_location, 3012, "pass is missing 'DispatchSizeX' or 'DispatchSizeY' property");
2404
}
2405
2406
if (!info.vs_entry_point.empty())
2407
warning(pass_location, 3089, "pass is specifying both 'VertexShader' and 'ComputeShader' which cannot be used together");
2408
if (!info.ps_entry_point.empty())
2409
warning(pass_location, 3089, "pass is specifying both 'PixelShader' and 'ComputeShader' which cannot be used together");
2410
}
2411
else
2412
{
2413
if (info.vs_entry_point.empty())
2414
{
2415
parse_success = false;
2416
error(pass_location, 3012, "pass is missing 'VertexShader' property");
2417
}
2418
2419
// Verify that shader signatures between VS and PS match (both semantics and interpolation qualifiers)
2420
std::unordered_map<std::string_view, type> vs_semantic_mapping;
2421
if (vs_info.return_semantic.empty())
2422
{
2423
if (!vs_info.return_type.is_void() && !vs_info.return_type.is_struct())
2424
{
2425
parse_success = false;
2426
error(pass_location, 3503, '\'' + vs_info.name + "': function return value is missing semantics");
2427
}
2428
}
2429
else
2430
{
2431
vs_semantic_mapping[vs_info.return_semantic] = vs_info.return_type;
2432
}
2433
2434
for (const member_type &param : vs_info.parameter_list)
2435
{
2436
if (param.semantic.empty())
2437
{
2438
if (!param.type.is_struct())
2439
{
2440
parse_success = false;
2441
if (param.type.has(type::q_in))
2442
error(pass_location, 3502, '\'' + vs_info.name + "': input parameter '" + param.name + "' is missing semantics");
2443
else
2444
error(pass_location, 3503, '\'' + vs_info.name + "': output parameter '" + param.name + "' is missing semantics");
2445
}
2446
}
2447
else if (param.type.has(type::q_out))
2448
{
2449
vs_semantic_mapping[param.semantic] = param.type;
2450
}
2451
}
2452
2453
if (ps_info.return_semantic.empty())
2454
{
2455
if (!ps_info.return_type.is_void() && !ps_info.return_type.is_struct())
2456
{
2457
parse_success = false;
2458
error(pass_location, 3503, '\'' + ps_info.name + "': function return value is missing semantics");
2459
}
2460
}
2461
2462
for (const member_type &param : ps_info.parameter_list)
2463
{
2464
if (param.semantic.empty())
2465
{
2466
if (!param.type.is_struct())
2467
{
2468
parse_success = false;
2469
if (param.type.has(type::q_in))
2470
error(pass_location, 3502, '\'' + ps_info.name + "': input parameter '" + param.name + "' is missing semantics");
2471
else
2472
error(pass_location, 3503, '\'' + ps_info.name + "': output parameter '" + param.name + "' is missing semantics");
2473
}
2474
}
2475
else if (param.type.has(type::q_in))
2476
{
2477
if (const auto it = vs_semantic_mapping.find(param.semantic);
2478
it == vs_semantic_mapping.end() || it->second != param.type)
2479
{
2480
warning(pass_location, 4576, '\'' + ps_info.name + "': input parameter '" + param.name + "' semantic does not match vertex shader one");
2481
}
2482
else if (((it->second.qualifiers ^ param.type.qualifiers) & (type::q_linear | type::q_noperspective | type::q_centroid | type::q_nointerpolation)) != 0)
2483
{
2484
parse_success = false;
2485
error( pass_location, 4568, '\'' + ps_info.name + "': input parameter '" + param.name + "' interpolation qualifiers do not match vertex shader ones");
2486
}
2487
}
2488
}
2489
2490
for (codegen::id id : vs_info.referenced_samplers)
2491
{
2492
const sampler &sampler = _codegen->get_sampler(id);
2493
if (std::find(std::begin(info.render_target_names), std::end(info.render_target_names), sampler.texture_name) != std::end(info.render_target_names))
2494
error(pass_location, 3020, '\'' + sampler.texture_name + "': cannot sample from texture that is also used as render target in the same pass");
2495
}
2496
for (codegen::id id : ps_info.referenced_samplers)
2497
{
2498
const sampler &sampler = _codegen->get_sampler(id);
2499
if (std::find(std::begin(info.render_target_names), std::end(info.render_target_names), sampler.texture_name) != std::end(info.render_target_names))
2500
error(pass_location, 3020, '\'' + sampler.texture_name + "': cannot sample from texture that is also used as render target in the same pass");
2501
}
2502
2503
if (!vs_info.referenced_storages.empty() || !ps_info.referenced_storages.empty())
2504
{
2505
parse_success = false;
2506
error(pass_location, 3667, "storage writes are only valid in compute shaders");
2507
}
2508
2509
// Verify render target format supports sRGB writes if enabled
2510
if (info.srgb_write_enable && !targets_support_srgb)
2511
{
2512
parse_success = false;
2513
error(pass_location, 4582, "one or more render target(s) do not support sRGB writes (only textures with RGBA8 format do)");
2514
}
2515
}
2516
}
2517
2518
return expect('}') && parse_success;
2519
}
2520
2521
void reshadefx::codegen::optimize_bindings()
2522
{
2523
struct sampler_group
2524
{
2525
std::vector<id> bindings;
2526
function *grouped_entry_point = nullptr;
2527
};
2528
struct entry_point_info
2529
{
2530
std::vector<sampler_group> sampler_groups;
2531
2532
static void compare_and_update_bindings(std::unordered_map<function *, entry_point_info> &per_entry_point, sampler_group &a, sampler_group &b, size_t binding)
2533
{
2534
for (; binding < std::min(a.bindings.size(), b.bindings.size()); ++binding)
2535
{
2536
if (a.bindings[binding] != b.bindings[binding])
2537
{
2538
if (a.bindings[binding] == 0)
2539
{
2540
b.bindings.insert(b.bindings.begin() + binding, 0);
2541
2542
if (b.grouped_entry_point != nullptr)
2543
for (sampler_group &c : per_entry_point.at(b.grouped_entry_point).sampler_groups)
2544
compare_and_update_bindings(per_entry_point, b, c, binding);
2545
continue;
2546
}
2547
2548
if (b.bindings[binding] == 0)
2549
{
2550
a.bindings.insert(a.bindings.begin() + binding, 0);
2551
2552
if (a.grouped_entry_point != nullptr)
2553
for (sampler_group &c : per_entry_point.at(a.grouped_entry_point).sampler_groups)
2554
compare_and_update_bindings(per_entry_point, a, c, binding);
2555
continue;
2556
}
2557
}
2558
}
2559
}
2560
};
2561
2562
std::unordered_map<function *, entry_point_info> per_entry_point;
2563
for (const auto &[name, type] : _module.entry_points)
2564
{
2565
per_entry_point.emplace(&get_function(name), entry_point_info {});
2566
}
2567
2568
std::unordered_map<id, int> usage_count;
2569
for (const auto &[entry_point, entry_point_info] : per_entry_point)
2570
{
2571
for (const id sampler_id : entry_point->referenced_samplers)
2572
usage_count[sampler_id]++;
2573
for (const id storage_id : entry_point->referenced_storages)
2574
usage_count[storage_id]++;
2575
}
2576
2577
// First sort bindings by usage and for each pass arrange them so that VS and PS use matching bindings for the objects they use (so that the same bindings can be used for both entry points).
2578
// If the entry points VS1 and PS1 use the following objects A, B and C:
2579
// - VS1: A B
2580
// - PS1: B C
2581
// Then this generates the following bindings:
2582
// - VS1: C A
2583
// - PS1: C 0 B
2584
2585
const auto usage_pred =
2586
[&](const id lhs, const id rhs) {
2587
return usage_count.at(lhs) > usage_count.at(rhs) || (usage_count.at(lhs) == usage_count.at(rhs) && lhs < rhs);
2588
};
2589
2590
for (const auto &[entry_point, entry_point_info] : per_entry_point)
2591
{
2592
std::sort(entry_point->referenced_samplers.begin(), entry_point->referenced_samplers.end(), usage_pred);
2593
std::sort(entry_point->referenced_storages.begin(), entry_point->referenced_storages.end(), usage_pred);
2594
}
2595
2596
for (const technique &tech : _module.techniques)
2597
{
2598
for (const pass &pass : tech.passes)
2599
{
2600
if (!pass.cs_entry_point.empty())
2601
{
2602
function &cs = get_function(pass.cs_entry_point);
2603
2604
sampler_group cs_sampler_info;
2605
cs_sampler_info.bindings = cs.referenced_samplers;
2606
per_entry_point.at(&cs).sampler_groups.push_back(std::move(cs_sampler_info));
2607
}
2608
else
2609
{
2610
function &vs = get_function(pass.vs_entry_point);
2611
2612
sampler_group vs_sampler_info;
2613
vs_sampler_info.bindings = vs.referenced_samplers;
2614
2615
if (!pass.ps_entry_point.empty())
2616
{
2617
function &ps = get_function(pass.ps_entry_point);
2618
2619
vs_sampler_info.grouped_entry_point = &ps;
2620
2621
sampler_group ps_sampler_info;
2622
ps_sampler_info.bindings = ps.referenced_samplers;
2623
ps_sampler_info.grouped_entry_point = &vs;
2624
2625
for (size_t binding = 0; binding < std::min(vs_sampler_info.bindings.size(), ps_sampler_info.bindings.size()); ++binding)
2626
{
2627
if (vs_sampler_info.bindings[binding] != ps_sampler_info.bindings[binding])
2628
{
2629
if (usage_pred(vs_sampler_info.bindings[binding], ps_sampler_info.bindings[binding]))
2630
ps_sampler_info.bindings.insert(ps_sampler_info.bindings.begin() + binding, 0);
2631
else
2632
vs_sampler_info.bindings.insert(vs_sampler_info.bindings.begin() + binding, 0);
2633
}
2634
}
2635
2636
per_entry_point.at(&ps).sampler_groups.push_back(std::move(ps_sampler_info));
2637
}
2638
2639
per_entry_point.at(&vs).sampler_groups.push_back(std::move(vs_sampler_info));
2640
}
2641
}
2642
}
2643
2644
// Next walk through all entry point groups and shift bindings as needed so that there are no mismatches across passes.
2645
// If the entry points VS1, PS1 and PS2 use the following bindings (notice the mismatches of VS1 between pass 0 and pass 1, as well as PS2 between pass 1 and pass 2):
2646
// - pass 0
2647
// - VS1: C A
2648
// - PS1: C 0 B
2649
// - pass 1
2650
// - VS1: C 0 A
2651
// - PS2: 0 D A
2652
// - pass 2
2653
// - VS2: D
2654
// - PS2: D A
2655
// Then this generates the following final bindings:
2656
// - pass 0
2657
// - VS1: C 0 A
2658
// - PS1: C 0 B
2659
// - pass 1
2660
// - VS1: C 0 A
2661
// - PS2: 0 D A
2662
// - pass 2
2663
// - VS2: 0 D
2664
// - PS2: 0 D A
2665
2666
for (auto &[entry_point, entry_point_info] : per_entry_point)
2667
{
2668
while (entry_point_info.sampler_groups.size() > 1)
2669
{
2670
entry_point_info::compare_and_update_bindings(per_entry_point, entry_point_info.sampler_groups[0], entry_point_info.sampler_groups[1], 0);
2671
entry_point_info.sampler_groups.erase(entry_point_info.sampler_groups.begin() + 1);
2672
}
2673
}
2674
2675
for (auto &[entry_point, entry_point_info] : per_entry_point)
2676
{
2677
if (entry_point_info.sampler_groups.empty())
2678
continue;
2679
2680
entry_point->referenced_samplers = std::move(entry_point_info.sampler_groups[0].bindings);
2681
}
2682
2683
// Finally apply the generated bindings to all passes
2684
2685
for (technique &tech : _module.techniques)
2686
{
2687
for (pass &pass : tech.passes)
2688
{
2689
std::vector<id> referenced_samplers;
2690
std::vector<id> referenced_storages;
2691
2692
if (!pass.cs_entry_point.empty())
2693
{
2694
const function &cs = get_function(pass.cs_entry_point);
2695
2696
referenced_samplers = cs.referenced_samplers;
2697
referenced_storages = cs.referenced_storages;
2698
}
2699
else
2700
{
2701
const function &vs = get_function(pass.vs_entry_point);
2702
2703
referenced_samplers = vs.referenced_samplers;
2704
2705
if (!pass.ps_entry_point.empty())
2706
{
2707
const function &ps = get_function(pass.ps_entry_point);
2708
2709
if (ps.referenced_samplers.size() > referenced_samplers.size())
2710
referenced_samplers.resize(ps.referenced_samplers.size());
2711
2712
for (uint32_t binding = 0; binding < ps.referenced_samplers.size(); ++binding)
2713
if (ps.referenced_samplers[binding] != 0)
2714
referenced_samplers[binding] = ps.referenced_samplers[binding];
2715
}
2716
}
2717
2718
for (uint32_t binding = 0; binding < referenced_samplers.size(); ++binding)
2719
{
2720
if (referenced_samplers[binding] == 0)
2721
continue;
2722
2723
const sampler &sampler = get_sampler(referenced_samplers[binding]);
2724
2725
texture_binding t;
2726
t.texture_name = sampler.texture_name;
2727
t.binding = binding;
2728
t.srgb = sampler.srgb;
2729
pass.texture_bindings.push_back(std::move(t));
2730
2731
if (binding >= _module.num_texture_bindings)
2732
_module.num_texture_bindings = binding + 1;
2733
2734
sampler_binding s;
2735
s.binding = binding;
2736
s.filter = sampler.filter;
2737
s.address_u = sampler.address_u;
2738
s.address_v = sampler.address_v;
2739
s.address_w = sampler.address_w;
2740
s.min_lod = sampler.min_lod;
2741
s.max_lod = sampler.max_lod;
2742
s.lod_bias = sampler.lod_bias;
2743
pass.sampler_bindings.push_back(std::move(s));
2744
2745
if (binding >= _module.num_sampler_bindings)
2746
_module.num_sampler_bindings = binding + 1;
2747
}
2748
2749
for (uint32_t binding = 0; binding < referenced_storages.size(); ++binding)
2750
{
2751
if (referenced_storages[binding] == 0)
2752
continue;
2753
2754
const storage &storage = get_storage(referenced_storages[binding]);
2755
2756
storage_binding u;
2757
u.texture_name = storage.texture_name;
2758
u.binding = binding;
2759
u.level = storage.level;
2760
pass.storage_bindings.push_back(std::move(u));
2761
2762
if (binding >= _module.num_storage_bindings)
2763
_module.num_storage_bindings = binding + 1;
2764
}
2765
}
2766
}
2767
}
2768
2769