effect_parser_stmt.cpp (74727B)
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 <limits> 12 #include <functional> 13 #include <string_view> 14 15 struct on_scope_exit 16 { 17 template <typename F> 18 explicit on_scope_exit(F lambda) : leave(lambda) { } 19 ~on_scope_exit() { leave(); } 20 21 std::function<void()> leave; 22 }; 23 24 bool reshadefx::parser::parse(std::string input, codegen *backend) 25 { 26 _lexer.reset(new lexer(std::move(input))); 27 28 // Set backend for subsequent code-generation 29 _codegen = backend; 30 assert(backend != nullptr); 31 32 consume(); 33 34 bool parse_success = true; 35 bool current_success = true; 36 37 while (!peek(tokenid::end_of_file)) 38 { 39 parse_top(current_success); 40 if (!current_success) 41 parse_success = false; 42 } 43 44 return parse_success; 45 } 46 47 void reshadefx::parser::parse_top(bool &parse_success) 48 { 49 if (accept(tokenid::namespace_)) 50 { 51 // Anonymous namespaces are not supported right now, so an identifier is a must 52 if (!expect(tokenid::identifier)) 53 { 54 parse_success = false; 55 return; 56 } 57 58 const auto name = std::move(_token.literal_as_string); 59 60 if (!expect('{')) 61 { 62 parse_success = false; 63 return; 64 } 65 66 enter_namespace(name); 67 68 bool current_success = true; 69 bool parse_success_namespace = true; 70 71 // Recursively parse top level statements until the namespace is closed again 72 while (!peek('}')) // Empty namespaces are valid 73 { 74 parse_top(current_success); 75 if (!current_success) 76 parse_success_namespace = false; 77 } 78 79 leave_namespace(); 80 81 parse_success = expect('}') && parse_success_namespace; 82 } 83 else if (accept(tokenid::struct_)) // Structure keyword found, parse the structure definition 84 { 85 // Structure definitions are terminated with a semicolon 86 parse_success = parse_struct() && expect(';'); 87 } 88 else if (accept(tokenid::technique)) // Technique keyword found, parse the technique definition 89 { 90 parse_success = parse_technique(); 91 } 92 else 93 { 94 if (type type; parse_type(type)) // Type found, this can be either a variable or a function declaration 95 { 96 parse_success = expect(tokenid::identifier); 97 if (!parse_success) 98 return; 99 100 if (peek('(')) 101 { 102 const auto name = std::move(_token.literal_as_string); 103 // This is definitely a function declaration, so parse it 104 if (!parse_function(type, name)) 105 { 106 // Insert dummy function into symbol table, so later references can be resolved despite the error 107 insert_symbol(name, { symbol_type::function, ~0u, { type::t_function } }, true); 108 parse_success = false; 109 return; 110 } 111 } 112 else 113 { 114 // There may be multiple variable names after the type, handle them all 115 unsigned int count = 0; 116 do { 117 if (count++ > 0 && !(expect(',') && expect(tokenid::identifier))) 118 { 119 parse_success = false; 120 return; 121 } 122 const auto name = std::move(_token.literal_as_string); 123 if (!parse_variable(type, name, true)) 124 { 125 // Insert dummy variable into symbol table, so later references can be resolved despite the error 126 insert_symbol(name, { symbol_type::variable, ~0u, type }, true); 127 // Skip the rest of the statement 128 consume_until(';'); 129 parse_success = false; 130 return; 131 } 132 } while (!peek(';')); 133 134 // Variable declarations are terminated with a semicolon 135 parse_success = expect(';'); 136 } 137 } 138 else if (accept(';')) // Ignore single semicolons in the source 139 { 140 parse_success = true; 141 } 142 else 143 { 144 // Unexpected token in source stream, consume and report an error about it 145 consume(); 146 // Only add another error message if succeeded parsing previously 147 // This is done to avoid walls of error messages because of consequential errors following a top-level syntax mistake 148 if (parse_success) 149 error(_token.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token.id) + '\''); 150 parse_success = false; 151 } 152 } 153 } 154 155 bool reshadefx::parser::parse_statement(bool scoped) 156 { 157 if (!_codegen->is_in_block()) 158 return error(_token_next.location, 0, "unreachable code"), false; 159 160 unsigned int loop_control = 0; 161 unsigned int selection_control = 0; 162 163 // Read any loop and branch control attributes first 164 while (accept('[')) 165 { 166 enum control_mask 167 { 168 unroll = 0x1, 169 dont_unroll = 0x2, 170 flatten = (0x1 << 4), 171 dont_flatten = (0x2 << 4), 172 switch_force_case = (0x4 << 4), 173 switch_call = (0x8 << 4) 174 }; 175 176 const auto attribute = std::move(_token_next.literal_as_string); 177 178 if (!expect(tokenid::identifier) || !expect(']')) 179 return false; 180 181 if (attribute == "unroll") 182 loop_control |= unroll; 183 else if (attribute == "loop" || attribute == "fastopt") 184 loop_control |= dont_unroll; 185 else if (attribute == "flatten") 186 selection_control |= flatten; 187 else if (attribute == "branch") 188 selection_control |= dont_flatten; 189 else if (attribute == "forcecase") 190 selection_control |= switch_force_case; 191 else if (attribute == "call") 192 selection_control |= switch_call; 193 else 194 warning(_token.location, 0, "unknown attribute"); 195 196 if ((loop_control & (unroll | dont_unroll)) == (unroll | dont_unroll)) 197 return error(_token.location, 3524, "can't use loop and unroll attributes together"), false; 198 if ((selection_control & (flatten | dont_flatten)) == (flatten | dont_flatten)) 199 return error(_token.location, 3524, "can't use branch and flatten attributes together"), false; 200 } 201 202 // Shift by two so that the possible values are 0x01 for 'flatten' and 0x02 for 'dont_flatten', equivalent to 'unroll' and 'dont_unroll' 203 selection_control >>= 4; 204 205 if (peek('{')) // Parse statement block 206 return parse_statement_block(scoped); 207 else if (accept(';')) // Ignore empty statements 208 return true; 209 210 // Most statements with the exception of declarations are only valid inside functions 211 if (_codegen->is_in_function()) 212 { 213 assert(_current_function != nullptr); 214 215 const auto location = _token_next.location; 216 217 if (accept(tokenid::if_)) 218 { 219 codegen::id true_block = _codegen->create_block(); // Block which contains the statements executed when the condition is true 220 codegen::id false_block = _codegen->create_block(); // Block which contains the statements executed when the condition is false 221 const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the branch re-merged with the current control flow 222 223 expression condition; 224 if (!expect('(') || !parse_expression(condition) || !expect(')')) 225 return false; 226 else if (!condition.type.is_scalar()) 227 return error(condition.location, 3019, "if statement conditional expressions must evaluate to a scalar"), false; 228 229 // Load condition and convert to boolean value as required by 'OpBranchConditional' 230 condition.add_cast_operation({ type::t_bool, 1, 1 }); 231 232 const codegen::id condition_value = _codegen->emit_load(condition); 233 const codegen::id condition_block = _codegen->leave_block_and_branch_conditional(condition_value, true_block, false_block); 234 235 { // Then block of the if statement 236 _codegen->enter_block(true_block); 237 238 if (!parse_statement(true)) 239 return false; 240 241 true_block = _codegen->leave_block_and_branch(merge_block); 242 } 243 { // Else block of the if statement 244 _codegen->enter_block(false_block); 245 246 if (accept(tokenid::else_) && !parse_statement(true)) 247 return false; 248 249 false_block = _codegen->leave_block_and_branch(merge_block); 250 } 251 252 _codegen->enter_block(merge_block); 253 254 // Emit structured control flow for an if statement and connect all basic blocks 255 _codegen->emit_if(location, condition_value, condition_block, true_block, false_block, selection_control); 256 257 return true; 258 } 259 260 if (accept(tokenid::switch_)) 261 { 262 const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the switch re-merged with the current control flow 263 264 expression selector_exp; 265 if (!expect('(') || !parse_expression(selector_exp) || !expect(')')) 266 return false; 267 else if (!selector_exp.type.is_scalar()) 268 return error(selector_exp.location, 3019, "switch statement expression must evaluate to a scalar"), false; 269 270 // Load selector and convert to integral value as required by switch instruction 271 selector_exp.add_cast_operation({ type::t_int, 1, 1 }); 272 273 const auto selector_value = _codegen->emit_load(selector_exp); 274 const auto selector_block = _codegen->leave_block_and_switch(selector_value, merge_block); 275 276 if (!expect('{')) 277 return false; 278 279 _loop_break_target_stack.push_back(merge_block); 280 on_scope_exit _([this]() { _loop_break_target_stack.pop_back(); }); 281 282 bool parse_success = true; 283 // The default case jumps to the end of the switch statement if not overwritten 284 codegen::id default_label = merge_block, default_block = merge_block; 285 codegen::id current_label = _codegen->create_block(); 286 std::vector<codegen::id> case_literal_and_labels, case_blocks; 287 size_t last_case_label_index = 0; 288 289 // Enter first switch statement body block 290 _codegen->enter_block(current_label); 291 292 while (!peek(tokenid::end_of_file)) 293 { 294 while (accept(tokenid::case_) || accept(tokenid::default_)) 295 { 296 if (_token.id == tokenid::case_) 297 { 298 expression case_label; 299 if (!parse_expression(case_label)) 300 return consume_until('}'), false; 301 else if (!case_label.type.is_scalar() || !case_label.type.is_integral() || !case_label.is_constant) 302 return error(case_label.location, 3020, "invalid type for case expression - value must be an integer scalar"), consume_until('}'), false; 303 304 // Check for duplicate case values 305 for (size_t i = 0; i < case_literal_and_labels.size(); i += 2) 306 { 307 if (case_literal_and_labels[i] == case_label.constant.as_uint[0]) 308 { 309 parse_success = false; 310 error(case_label.location, 3532, "duplicate case " + std::to_string(case_label.constant.as_uint[0])); 311 break; 312 } 313 } 314 315 case_blocks.emplace_back(); // This is set to the actual block below 316 case_literal_and_labels.push_back(case_label.constant.as_uint[0]); 317 case_literal_and_labels.push_back(current_label); 318 } 319 else 320 { 321 // Check if the default label was already changed by a previous 'default' statement 322 if (default_label != merge_block) 323 { 324 parse_success = false; 325 error(_token.location, 3532, "duplicate default in switch statement"); 326 } 327 328 default_label = current_label; 329 default_block = 0; // This is set to the actual block below 330 } 331 332 if (!expect(':')) 333 return consume_until('}'), false; 334 } 335 336 // It is valid for no statement to follow if this is the last label in the switch body 337 const bool end_of_switch = peek('}'); 338 339 if (!end_of_switch && !parse_statement(true)) 340 return consume_until('}'), false; 341 342 // Handle fall-through case and end of switch statement 343 if (peek(tokenid::case_) || peek(tokenid::default_) || end_of_switch) 344 { 345 if (_codegen->is_in_block()) // Disallow fall-through for now 346 { 347 parse_success = false; 348 error(_token_next.location, 3533, "non-empty case statements must have break or return"); 349 } 350 351 const codegen::id next_label = end_of_switch ? merge_block : _codegen->create_block(); 352 // This is different from 'current_label', since there may have been branching logic inside the case, which would have changed the active block 353 const codegen::id current_block = _codegen->leave_block_and_branch(next_label); 354 355 if (0 == default_block) 356 default_block = current_block; 357 for (size_t i = last_case_label_index; i < case_blocks.size(); ++i) 358 // Need to use the initial label for the switch table, but the current block to merge all the block data 359 case_blocks[i] = current_block; 360 361 current_label = next_label; 362 _codegen->enter_block(current_label); 363 364 if (end_of_switch) // We reached the end, nothing more to do 365 break; 366 367 last_case_label_index = case_blocks.size(); 368 } 369 } 370 371 if (case_literal_and_labels.empty() && default_label == merge_block) 372 warning(location, 5002, "switch statement contains no 'case' or 'default' labels"); 373 374 // Emit structured control flow for a switch statement and connect all basic blocks 375 _codegen->emit_switch(location, selector_value, selector_block, default_label, default_block, case_literal_and_labels, case_blocks, selection_control); 376 377 return expect('}') && parse_success; 378 } 379 380 if (accept(tokenid::for_)) 381 { 382 if (!expect('(')) 383 return false; 384 385 enter_scope(); 386 on_scope_exit _([this]() { leave_scope(); }); 387 388 // Parse initializer first 389 if (type type; parse_type(type)) 390 { 391 unsigned int count = 0; 392 do { // There may be multiple declarations behind a type, so loop through them 393 if (count++ > 0 && !expect(',')) 394 return false; 395 if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string))) 396 return false; 397 } while (!peek(';')); 398 } 399 else 400 { 401 // Initializer can also contain an expression if not a variable declaration list and not empty 402 if (!peek(';')) 403 { 404 expression expression; 405 if (!parse_expression(expression)) 406 return false; 407 } 408 } 409 410 if (!expect(';')) 411 return false; 412 413 const codegen::id merge_block = _codegen->create_block(); // Block that is executed after the loop 414 const codegen::id header_label = _codegen->create_block(); // Pointer to the loop merge instruction 415 const codegen::id continue_label = _codegen->create_block(); // Pointer to the continue block 416 codegen::id loop_block = _codegen->create_block(); // Pointer to the main loop body block 417 codegen::id condition_block = _codegen->create_block(); // Pointer to the condition check 418 codegen::id condition_value = 0; 419 420 // End current block by branching to the next label 421 const codegen::id prev_block = _codegen->leave_block_and_branch(header_label); 422 423 { // Begin loop block (this header is used for explicit structured control flow) 424 _codegen->enter_block(header_label); 425 426 _codegen->leave_block_and_branch(condition_block); 427 } 428 429 { // Parse condition block 430 _codegen->enter_block(condition_block); 431 432 if (!peek(';')) 433 { 434 expression condition; 435 if (!parse_expression(condition)) 436 return false; 437 438 if (!condition.type.is_scalar()) 439 return error(condition.location, 3019, "scalar value expected"), false; 440 441 // Evaluate condition and branch to the right target 442 condition.add_cast_operation({ type::t_bool, 1, 1 }); 443 444 condition_value = _codegen->emit_load(condition); 445 446 condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block); 447 } 448 else // It is valid for there to be no condition expression 449 { 450 condition_block = _codegen->leave_block_and_branch(loop_block); 451 } 452 453 if (!expect(';')) 454 return false; 455 } 456 457 { // Parse loop continue block into separate block so it can be appended to the end down the line 458 _codegen->enter_block(continue_label); 459 460 if (!peek(')')) 461 { 462 expression continue_exp; 463 if (!parse_expression(continue_exp)) 464 return false; 465 } 466 467 if (!expect(')')) 468 return false; 469 470 // Branch back to the loop header at the end of the continue block 471 _codegen->leave_block_and_branch(header_label); 472 } 473 474 { // Parse loop body block 475 _codegen->enter_block(loop_block); 476 477 _loop_break_target_stack.push_back(merge_block); 478 _loop_continue_target_stack.push_back(continue_label); 479 480 const bool parse_success = parse_statement(false); 481 482 _loop_break_target_stack.pop_back(); 483 _loop_continue_target_stack.pop_back(); 484 485 if (!parse_success) 486 return false; 487 488 loop_block = _codegen->leave_block_and_branch(continue_label); 489 } 490 491 // Add merge block label to the end of the loop 492 _codegen->enter_block(merge_block); 493 494 // Emit structured control flow for a loop statement and connect all basic blocks 495 _codegen->emit_loop(location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control); 496 497 return true; 498 } 499 500 if (accept(tokenid::while_)) 501 { 502 enter_scope(); 503 on_scope_exit _([this]() { leave_scope(); }); 504 505 const codegen::id merge_block = _codegen->create_block(); 506 const codegen::id header_label = _codegen->create_block(); 507 const codegen::id continue_label = _codegen->create_block(); 508 codegen::id loop_block = _codegen->create_block(); 509 codegen::id condition_block = _codegen->create_block(); 510 codegen::id condition_value = 0; 511 512 // End current block by branching to the next label 513 const codegen::id prev_block = _codegen->leave_block_and_branch(header_label); 514 515 { // Begin loop block 516 _codegen->enter_block(header_label); 517 518 _codegen->leave_block_and_branch(condition_block); 519 } 520 521 { // Parse condition block 522 _codegen->enter_block(condition_block); 523 524 expression condition; 525 if (!expect('(') || !parse_expression(condition) || !expect(')')) 526 return false; 527 else if (!condition.type.is_scalar()) 528 return error(condition.location, 3019, "scalar value expected"), false; 529 530 // Evaluate condition and branch to the right target 531 condition.add_cast_operation({ type::t_bool, 1, 1 }); 532 533 condition_value = _codegen->emit_load(condition); 534 535 condition_block = _codegen->leave_block_and_branch_conditional(condition_value, loop_block, merge_block); 536 } 537 538 { // Parse loop body block 539 _codegen->enter_block(loop_block); 540 541 _loop_break_target_stack.push_back(merge_block); 542 _loop_continue_target_stack.push_back(continue_label); 543 544 const bool parse_success = parse_statement(false); 545 546 _loop_break_target_stack.pop_back(); 547 _loop_continue_target_stack.pop_back(); 548 549 if (!parse_success) 550 return false; 551 552 loop_block = _codegen->leave_block_and_branch(continue_label); 553 } 554 555 { // Branch back to the loop header in empty continue block 556 _codegen->enter_block(continue_label); 557 558 _codegen->leave_block_and_branch(header_label); 559 } 560 561 // Add merge block label to the end of the loop 562 _codegen->enter_block(merge_block); 563 564 // Emit structured control flow for a loop statement and connect all basic blocks 565 _codegen->emit_loop(location, condition_value, prev_block, header_label, condition_block, loop_block, continue_label, loop_control); 566 567 return true; 568 } 569 570 if (accept(tokenid::do_)) 571 { 572 const codegen::id merge_block = _codegen->create_block(); 573 const codegen::id header_label = _codegen->create_block(); 574 const codegen::id continue_label = _codegen->create_block(); 575 codegen::id loop_block = _codegen->create_block(); 576 codegen::id condition_value = 0; 577 578 // End current block by branching to the next label 579 const codegen::id prev_block = _codegen->leave_block_and_branch(header_label); 580 581 { // Begin loop block 582 _codegen->enter_block(header_label); 583 584 _codegen->leave_block_and_branch(loop_block); 585 } 586 587 { // Parse loop body block 588 _codegen->enter_block(loop_block); 589 590 _loop_break_target_stack.push_back(merge_block); 591 _loop_continue_target_stack.push_back(continue_label); 592 593 const bool parse_success = parse_statement(true); 594 595 _loop_break_target_stack.pop_back(); 596 _loop_continue_target_stack.pop_back(); 597 598 if (!parse_success) 599 return false; 600 601 loop_block = _codegen->leave_block_and_branch(continue_label); 602 } 603 604 { // Continue block does the condition evaluation 605 _codegen->enter_block(continue_label); 606 607 expression condition; 608 if (!expect(tokenid::while_) || !expect('(') || !parse_expression(condition) || !expect(')') || !expect(';')) 609 return false; 610 else if (!condition.type.is_scalar()) 611 return error(condition.location, 3019, "scalar value expected"), false; 612 613 // Evaluate condition and branch to the right target 614 condition.add_cast_operation({ type::t_bool, 1, 1 }); 615 616 condition_value = _codegen->emit_load(condition); 617 618 _codegen->leave_block_and_branch_conditional(condition_value, header_label, merge_block); 619 } 620 621 // Add merge block label to the end of the loop 622 _codegen->enter_block(merge_block); 623 624 // Emit structured control flow for a loop statement and connect all basic blocks 625 _codegen->emit_loop(location, condition_value, prev_block, header_label, 0, loop_block, continue_label, loop_control); 626 627 return true; 628 } 629 630 if (accept(tokenid::break_)) 631 { 632 if (_loop_break_target_stack.empty()) 633 return error(location, 3518, "break must be inside loop"), false; 634 635 // Branch to the break target of the inner most loop on the stack 636 _codegen->leave_block_and_branch(_loop_break_target_stack.back(), 1); 637 638 return expect(';'); 639 } 640 641 if (accept(tokenid::continue_)) 642 { 643 if (_loop_continue_target_stack.empty()) 644 return error(location, 3519, "continue must be inside loop"), false; 645 646 // Branch to the continue target of the inner most loop on the stack 647 _codegen->leave_block_and_branch(_loop_continue_target_stack.back(), 2); 648 649 return expect(';'); 650 } 651 652 if (accept(tokenid::return_)) 653 { 654 const type &ret_type = _current_function->return_type; 655 656 if (!peek(';')) 657 { 658 expression expression; 659 if (!parse_expression(expression)) 660 return consume_until(';'), false; 661 662 // Cannot return to void 663 if (ret_type.is_void()) 664 // Consume the semicolon that follows the return expression so that parsing may continue 665 return error(location, 3079, "void functions cannot return a value"), accept(';'), false; 666 667 // Cannot return arrays from a function 668 if (expression.type.is_array() || !type::rank(expression.type, ret_type)) 669 return error(location, 3017, "expression (" + expression.type.description() + ") does not match function return type (" + ret_type.description() + ')'), accept(';'), false; 670 671 // Load return value and perform implicit cast to function return type 672 if (expression.type.components() > ret_type.components()) 673 warning(expression.location, 3206, "implicit truncation of vector type"); 674 675 expression.add_cast_operation(ret_type); 676 677 const auto return_value = _codegen->emit_load(expression); 678 679 _codegen->leave_block_and_return(return_value); 680 } 681 else if (!ret_type.is_void()) 682 { 683 // No return value was found, but the function expects one 684 error(location, 3080, "function must return a value"); 685 686 // Consume the semicolon that follows the return expression so that parsing may continue 687 accept(';'); 688 689 return false; 690 } 691 else 692 { 693 _codegen->leave_block_and_return(); 694 } 695 696 return expect(';'); 697 } 698 699 if (accept(tokenid::discard_)) 700 { 701 // Leave the current function block 702 _codegen->leave_block_and_kill(); 703 704 return expect(';'); 705 } 706 } 707 708 // Handle variable declarations 709 if (type type; parse_type(type)) 710 { 711 unsigned int count = 0; 712 do { // There may be multiple declarations behind a type, so loop through them 713 if (count++ > 0 && !expect(',')) 714 // Try to consume the rest of the declaration so that parsing may continue despite the error 715 return consume_until(';'), false; 716 if (!expect(tokenid::identifier) || !parse_variable(type, std::move(_token.literal_as_string))) 717 return consume_until(';'), false; 718 } while (!peek(';')); 719 720 return expect(';'); 721 } 722 723 // Handle expression statements 724 if (expression expression; parse_expression(expression)) 725 return expect(';'); // A statement has to be terminated with a semicolon 726 727 // Gracefully consume any remaining characters until the statement would usually end, so that parsing may continue despite the error 728 consume_until(';'); 729 730 return false; 731 } 732 bool reshadefx::parser::parse_statement_block(bool scoped) 733 { 734 if (!expect('{')) 735 return false; 736 737 if (scoped) 738 enter_scope(); 739 740 // Parse statements until the end of the block is reached 741 while (!peek('}') && !peek(tokenid::end_of_file)) 742 { 743 if (!parse_statement(true)) 744 { 745 if (scoped) 746 leave_scope(); 747 748 // Ignore the rest of this block 749 unsigned int level = 0; 750 751 while (!peek(tokenid::end_of_file)) 752 { 753 if (accept('{')) 754 { 755 ++level; 756 } 757 else if (accept('}')) 758 { 759 if (level-- == 0) 760 break; 761 } // These braces are necessary to match the 'else' to the correct 'if' statement 762 else 763 { 764 consume(); 765 } 766 } 767 768 return false; 769 } 770 } 771 772 if (scoped) 773 leave_scope(); 774 775 return expect('}'); 776 } 777 778 bool reshadefx::parser::parse_type(type &type) 779 { 780 type.qualifiers = 0; 781 782 accept_type_qualifiers(type); 783 784 if (!accept_type_class(type)) 785 return false; 786 787 if (type.is_integral() && (type.has(type::q_centroid) || type.has(type::q_noperspective))) 788 return error(_token.location, 4576, "signature specifies invalid interpolation mode for integer component type"), false; 789 else if (type.has(type::q_centroid) && !type.has(type::q_noperspective)) 790 type.qualifiers |= type::q_linear; 791 792 return true; 793 } 794 bool reshadefx::parser::parse_array_size(type &type) 795 { 796 // Reset array length to zero before checking if one exists 797 type.array_length = 0; 798 799 if (accept('[')) 800 { 801 if (accept(']')) 802 { 803 // No length expression, so this is an unsized array 804 type.array_length = -1; 805 } 806 else if (expression expression; parse_expression(expression) && expect(']')) 807 { 808 if (!expression.is_constant || !(expression.type.is_scalar() && expression.type.is_integral())) 809 return error(expression.location, 3058, "array dimensions must be literal scalar expressions"), false; 810 811 type.array_length = expression.constant.as_uint[0]; 812 813 if (type.array_length < 1 || type.array_length > 65536) 814 return error(expression.location, 3059, "array dimension must be between 1 and 65536"), false; 815 } 816 else 817 { 818 return false; 819 } 820 } 821 822 // Multi-dimensional arrays are not supported 823 if (peek('[')) 824 return error(_token_next.location, 3119, "arrays cannot be multi-dimensional"), false; 825 826 return true; 827 } 828 829 bool reshadefx::parser::parse_annotations(std::vector<annotation> &annotations) 830 { 831 // Check if annotations exist and return early if none do 832 if (!accept('<')) 833 return true; 834 835 bool parse_success = true; 836 837 while (!peek('>')) 838 { 839 if (type type; accept_type_class(type)) 840 warning(_token.location, 4717, "type prefixes for annotations are deprecated and ignored"); 841 842 if (!expect(tokenid::identifier)) 843 return consume_until('>'), false; 844 845 auto name = std::move(_token.literal_as_string); 846 847 if (expression expression; !expect('=') || !parse_expression_multary(expression) || !expect(';')) 848 return consume_until('>'), false; 849 else if (expression.is_constant) 850 annotations.push_back({ expression.type, std::move(name), std::move(expression.constant) }); 851 else // Continue parsing annotations despite this not being a constant, since the syntax is still correct 852 parse_success = false, 853 error(expression.location, 3011, "value must be a literal expression"); 854 } 855 856 return expect('>') && parse_success; 857 } 858 859 bool reshadefx::parser::parse_struct() 860 { 861 const auto location = std::move(_token.location); 862 863 struct_info info; 864 // The structure name is optional 865 if (accept(tokenid::identifier)) 866 info.name = std::move(_token.literal_as_string); 867 else 868 info.name = "_anonymous_struct_" + std::to_string(location.line) + '_' + std::to_string(location.column); 869 870 info.unique_name = 'S' + current_scope().name + info.name; 871 std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_'); 872 873 if (!expect('{')) 874 return false; 875 876 bool parse_success = true; 877 878 while (!peek('}')) // Empty structures are possible 879 { 880 struct_member_info member; 881 882 if (!parse_type(member.type)) 883 return error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected struct member type"), consume_until('}'), accept(';'), false; 884 885 unsigned int count = 0; 886 do { 887 if (count++ > 0 && !expect(',')) 888 return consume_until('}'), accept(';'), false; 889 890 if (!expect(tokenid::identifier)) 891 return consume_until('}'), accept(';'), false; 892 893 member.name = std::move(_token.literal_as_string); 894 member.location = std::move(_token.location); 895 896 if (member.type.is_void()) 897 parse_success = false, 898 error(member.location, 3038, '\'' + member.name + "': struct members cannot be void"); 899 if (member.type.is_struct()) // Nesting structures would make input/output argument flattening more complicated, so prevent it for now 900 parse_success = false, 901 error(member.location, 3090, '\'' + member.name + "': nested struct members are not supported"); 902 903 if (member.type.has(type::q_in) || member.type.has(type::q_out)) 904 parse_success = false, 905 error(member.location, 3055, '\'' + member.name + "': struct members cannot be declared 'in' or 'out'"); 906 if (member.type.has(type::q_const)) 907 parse_success = false, 908 error(member.location, 3035, '\'' + member.name + "': struct members cannot be declared 'const'"); 909 if (member.type.has(type::q_extern)) 910 parse_success = false, 911 error(member.location, 3006, '\'' + member.name + "': struct members cannot be declared 'extern'"); 912 if (member.type.has(type::q_static)) 913 parse_success = false, 914 error(member.location, 3007, '\'' + member.name + "': struct members cannot be declared 'static'"); 915 if (member.type.has(type::q_uniform)) 916 parse_success = false, 917 error(member.location, 3047, '\'' + member.name + "': struct members cannot be declared 'uniform'"); 918 if (member.type.has(type::q_groupshared)) 919 parse_success = false, 920 error(member.location, 3010, '\'' + member.name + "': struct members cannot be declared 'groupshared'"); 921 922 // Modify member specific type, so that following members in the declaration list are not affected by this 923 if (!parse_array_size(member.type)) 924 return consume_until('}'), accept(';'), false; 925 else if (member.type.array_length < 0) 926 parse_success = false, 927 error(member.location, 3072, '\'' + member.name + "': array dimensions of struct members must be explicit"); 928 929 // Structure members may have semantics to use them as input/output types 930 if (accept(':')) 931 { 932 if (!expect(tokenid::identifier)) 933 return consume_until('}'), accept(';'), false; 934 935 member.semantic = std::move(_token.literal_as_string); 936 // Make semantic upper case to simplify comparison later on 937 std::transform(member.semantic.begin(), member.semantic.end(), member.semantic.begin(), 938 [](std::string::value_type c) { 939 return static_cast<std::string::value_type>(std::toupper(c)); 940 }); 941 942 if (member.semantic.compare(0, 3, "SV_") != 0) 943 { 944 // Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location 945 if (const char c = member.semantic.back(); c < '0' || c > '9') 946 member.semantic += '0'; 947 948 if (member.type.is_integral() && !member.type.has(type::q_nointerpolation)) 949 { 950 member.type.qualifiers |= type::q_nointerpolation; // Integer fields do not interpolate, so make this explicit (to avoid issues with GLSL) 951 warning(member.location, 4568, '\'' + member.name + "': integer fields have the 'nointerpolation' qualifier by default"); 952 } 953 } 954 else 955 { 956 // Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing 957 if (member.semantic.back() == '0' && (member.semantic[member.semantic.size() - 2] < '0' || member.semantic[member.semantic.size() - 2] > '9')) 958 member.semantic.pop_back(); 959 } 960 } 961 962 // Save member name and type for book keeping 963 info.member_list.push_back(member); 964 } while (!peek(';')); 965 966 if (!expect(';')) 967 return consume_until('}'), accept(';'), false; 968 } 969 970 // Empty structures are valid, but not usually intended, so emit a warning 971 if (info.member_list.empty()) 972 warning(location, 5001, "struct has no members"); 973 974 // Define the structure now that information about all the member types was gathered 975 const auto id = _codegen->define_struct(location, info); 976 977 // Insert the symbol into the symbol table 978 const symbol symbol = { symbol_type::structure, id }; 979 980 if (!insert_symbol(info.name, symbol, true)) 981 return error(location, 3003, "redefinition of '" + info.name + '\''), false; 982 983 return expect('}') && parse_success; 984 } 985 986 bool reshadefx::parser::parse_function(type type, std::string name) 987 { 988 const auto location = std::move(_token.location); 989 990 if (!expect('(')) // Functions always have a parameter list 991 return false; 992 if (type.qualifiers != 0) 993 return error(location, 3047, '\'' + name + "': function return type cannot have any qualifiers"), false; 994 995 function_info info; 996 info.name = name; 997 info.unique_name = 'F' + current_scope().name + name; 998 std::replace(info.unique_name.begin(), info.unique_name.end(), ':', '_'); 999 1000 info.return_type = type; 1001 _current_function = &info; 1002 1003 bool parse_success = true; 1004 bool expect_parenthesis = true; 1005 1006 // Enter function scope (and leave it again when finished parsing this function) 1007 enter_scope(); 1008 on_scope_exit _([this]() { 1009 leave_scope(); 1010 _codegen->leave_function(); 1011 _current_function = nullptr; 1012 }); 1013 1014 while (!peek(')')) 1015 { 1016 if (!info.parameter_list.empty() && !expect(',')) 1017 { 1018 parse_success = false; 1019 expect_parenthesis = false; 1020 consume_until(')'); 1021 break; 1022 } 1023 1024 struct_member_info param; 1025 1026 if (!parse_type(param.type)) 1027 { 1028 error(_token_next.location, 3000, "syntax error: unexpected '" + token::id_to_name(_token_next.id) + "', expected parameter type"); 1029 parse_success = false; 1030 expect_parenthesis = false; 1031 consume_until(')'); 1032 break; 1033 } 1034 1035 if (!expect(tokenid::identifier)) 1036 { 1037 parse_success = false; 1038 expect_parenthesis = false; 1039 consume_until(')'); 1040 break; 1041 } 1042 1043 param.name = std::move(_token.literal_as_string); 1044 param.location = std::move(_token.location); 1045 1046 if (param.type.is_void()) 1047 parse_success = false, 1048 error(param.location, 3038, '\'' + param.name + "': function parameters cannot be void"); 1049 1050 if (param.type.has(type::q_extern)) 1051 parse_success = false, 1052 error(param.location, 3006, '\'' + param.name + "': function parameters cannot be declared 'extern'"); 1053 if (param.type.has(type::q_static)) 1054 parse_success = false, 1055 error(param.location, 3007, '\'' + param.name + "': function parameters cannot be declared 'static'"); 1056 if (param.type.has(type::q_uniform)) 1057 parse_success = false, 1058 error(param.location, 3047, '\'' + param.name + "': function parameters cannot be declared 'uniform', consider placing in global scope instead"); 1059 if (param.type.has(type::q_groupshared)) 1060 parse_success = false, 1061 error(param.location, 3010, '\'' + param.name + "': function parameters cannot be declared 'groupshared'"); 1062 1063 if (param.type.has(type::q_out) && param.type.has(type::q_const)) 1064 parse_success = false, 1065 error(param.location, 3046, '\'' + param.name + "': output parameters cannot be declared 'const'"); 1066 else if (!param.type.has(type::q_out)) 1067 param.type.qualifiers |= type::q_in; // Function parameters are implicitly 'in' if not explicitly defined as 'out' 1068 1069 if (!parse_array_size(param.type)) 1070 { 1071 parse_success = false; 1072 expect_parenthesis = false; 1073 consume_until(')'); 1074 break; 1075 } 1076 else if (param.type.array_length < 0) 1077 { 1078 parse_success = false; 1079 error(param.location, 3072, '\'' + param.name + "': array dimensions of function parameters must be explicit"); 1080 } 1081 1082 // Handle parameter type semantic 1083 if (accept(':')) 1084 { 1085 if (!expect(tokenid::identifier)) 1086 { 1087 parse_success = false; 1088 expect_parenthesis = false; 1089 consume_until(')'); 1090 break; 1091 } 1092 1093 param.semantic = std::move(_token.literal_as_string); 1094 // Make semantic upper case to simplify comparison later on 1095 std::transform(param.semantic.begin(), param.semantic.end(), param.semantic.begin(), 1096 [](std::string::value_type c) { 1097 return static_cast<std::string::value_type>(std::toupper(c)); 1098 }); 1099 1100 if (param.semantic.compare(0, 3, "SV_") != 0) 1101 { 1102 // Always numerate semantics, so that e.g. TEXCOORD and TEXCOORD0 point to the same location 1103 if (const char c = param.semantic.back(); c < '0' || c > '9') 1104 param.semantic += '0'; 1105 1106 if (param.type.is_integral() && !param.type.has(type::q_nointerpolation)) 1107 { 1108 param.type.qualifiers |= type::q_nointerpolation; // Integer parameters do not interpolate, so make this explicit (to avoid issues with GLSL) 1109 warning(param.location, 4568, '\'' + param.name + "': integer parameters have the 'nointerpolation' qualifier by default"); 1110 } 1111 } 1112 else 1113 { 1114 // Remove optional trailing zero from system value semantics, so that e.g. SV_POSITION and SV_POSITION0 mean the same thing 1115 if (param.semantic.back() == '0' && (param.semantic[param.semantic.size() - 2] < '0' || param.semantic[param.semantic.size() - 2] > '9')) 1116 param.semantic.pop_back(); 1117 } 1118 } 1119 1120 info.parameter_list.push_back(std::move(param)); 1121 } 1122 1123 if (expect_parenthesis && !expect(')')) 1124 return false; 1125 1126 // Handle return type semantic 1127 if (accept(':')) 1128 { 1129 if (!expect(tokenid::identifier)) 1130 return false; 1131 if (type.is_void()) 1132 return error(_token.location, 3076, '\'' + name + "': void function cannot have a semantic"), false; 1133 1134 info.return_semantic = std::move(_token.literal_as_string); 1135 // Make semantic upper case to simplify comparison later on 1136 std::transform(info.return_semantic.begin(), info.return_semantic.end(), info.return_semantic.begin(), 1137 [](std::string::value_type c) { 1138 return static_cast<std::string::value_type>(std::toupper(c)); 1139 }); 1140 } 1141 1142 // Check if this is a function declaration without a body 1143 if (accept(';')) 1144 return error(location, 3510, '\'' + name + "': function is missing an implementation"), false; 1145 1146 // Define the function now that information about the declaration was gathered 1147 const auto id = _codegen->define_function(location, info); 1148 1149 // Insert the function and parameter symbols into the symbol table and update current function pointer to the permanent one 1150 symbol symbol = { symbol_type::function, id, { type::t_function } }; 1151 symbol.function = _current_function = &_codegen->get_function(id); 1152 1153 if (!insert_symbol(name, symbol, true)) 1154 return error(location, 3003, "redefinition of '" + name + '\''), false; 1155 1156 for (const struct_member_info ¶m : info.parameter_list) 1157 if (!insert_symbol(param.name, { symbol_type::variable, param.definition, param.type })) 1158 return error(param.location, 3003, "redefinition of '" + param.name + '\''), false; 1159 1160 // A function has to start with a new block 1161 _codegen->enter_block(_codegen->create_block()); 1162 1163 if (!parse_statement_block(false)) 1164 parse_success = false; 1165 1166 // Add implicit return statement to the end of functions 1167 if (_codegen->is_in_block()) 1168 _codegen->leave_block_and_return(); 1169 1170 return parse_success; 1171 } 1172 1173 bool reshadefx::parser::parse_variable(type type, std::string name, bool global) 1174 { 1175 const auto location = std::move(_token.location); 1176 1177 if (type.is_void()) 1178 return error(location, 3038, '\'' + name + "': variables cannot be void"), false; 1179 if (type.has(type::q_in) || type.has(type::q_out)) 1180 return error(location, 3055, '\'' + name + "': variables cannot be declared 'in' or 'out'"), false; 1181 1182 // Local and global variables have different requirements 1183 if (global) 1184 { 1185 // Check that type qualifier combinations are valid 1186 if (type.has(type::q_static)) 1187 { 1188 // Global variables that are 'static' cannot be of another storage class 1189 if (type.has(type::q_uniform)) 1190 return error(location, 3007, '\'' + name + "': uniform global variables cannot be declared 'static'"), false; 1191 // The 'volatile' qualifier is only valid memory object declarations that are storage images or uniform blocks 1192 if (type.has(type::q_volatile)) 1193 return error(location, 3008, '\'' + name + "': global variables cannot be declared 'volatile'"), false; 1194 } 1195 else if (!type.has(type::q_groupshared)) 1196 { 1197 // Make all global variables 'uniform' by default, since they should be externally visible without the 'static' keyword 1198 if (!type.has(type::q_uniform) && !type.is_object()) 1199 warning(location, 5000, '\'' + name + "': global variables are considered 'uniform' by default"); 1200 1201 // Global variables that are not 'static' are always 'extern' and 'uniform' 1202 type.qualifiers |= type::q_extern | type::q_uniform; 1203 1204 // It is invalid to make 'uniform' variables constant, since they can be modified externally 1205 if (type.has(type::q_const)) 1206 return error(location, 3035, '\'' + name + "': variables which are 'uniform' cannot be declared 'const'"), false; 1207 } 1208 } 1209 else 1210 { 1211 // Static does not really have meaning on local variables 1212 if (type.has(type::q_static)) 1213 type.qualifiers &= ~type::q_static; 1214 1215 if (type.has(type::q_extern)) 1216 return error(location, 3006, '\'' + name + "': local variables cannot be declared 'extern'"), false; 1217 if (type.has(type::q_uniform)) 1218 return error(location, 3047, '\'' + name + "': local variables cannot be declared 'uniform'"), false; 1219 if (type.has(type::q_groupshared)) 1220 return error(location, 3010, '\'' + name + "': local variables cannot be declared 'groupshared'"), false; 1221 1222 if (type.is_object()) 1223 return error(location, 3038, '\'' + name + "': local variables cannot be texture, sampler or storage objects"), false; 1224 } 1225 1226 // The variable name may be followed by an optional array size expression 1227 if (!parse_array_size(type)) 1228 return false; 1229 1230 bool parse_success = true; 1231 expression initializer; 1232 texture_info texture_info; 1233 sampler_info sampler_info; 1234 storage_info storage_info; 1235 1236 if (accept(':')) 1237 { 1238 if (!expect(tokenid::identifier)) 1239 return false; 1240 else if (!global) // Only global variables can have a semantic 1241 return error(_token.location, 3043, '\'' + name + "': local variables cannot have semantics"), false; 1242 1243 std::string &semantic = texture_info.semantic; 1244 semantic = std::move(_token.literal_as_string); 1245 1246 // Make semantic upper case to simplify comparison later on 1247 std::transform(semantic.begin(), semantic.end(), semantic.begin(), 1248 [](std::string::value_type c) { 1249 return static_cast<std::string::value_type>(std::toupper(c)); 1250 }); 1251 } 1252 else 1253 { 1254 // Global variables can have optional annotations 1255 if (global && !parse_annotations(sampler_info.annotations)) 1256 parse_success = false; 1257 1258 // Variables without a semantic may have an optional initializer 1259 if (accept('=')) 1260 { 1261 if (!parse_expression_assignment(initializer)) 1262 return false; 1263 1264 if (type.has(type::q_groupshared)) 1265 return error(initializer.location, 3009, '\'' + name + "': variables declared 'groupshared' cannot have an initializer"), false; 1266 // TODO: This could be resolved by initializing these at the beginning of the entry point 1267 if (global && !initializer.is_constant) 1268 return error(initializer.location, 3011, '\'' + name + "': initial value must be a literal expression"), false; 1269 1270 // Check type compatibility 1271 if ((type.array_length >= 0 && initializer.type.array_length != type.array_length) || !type::rank(initializer.type, type)) 1272 return error(initializer.location, 3017, '\'' + name + "': initial value (" + initializer.type.description() + ") does not match variable type (" + type.description() + ')'), false; 1273 if ((initializer.type.rows < type.rows || initializer.type.cols < type.cols) && !initializer.type.is_scalar()) 1274 return error(initializer.location, 3017, '\'' + name + "': cannot implicitly convert these vector types (from " + initializer.type.description() + " to " + type.description() + ')'), false; 1275 1276 // Deduce array size from the initializer expression 1277 if (initializer.type.is_array()) 1278 type.array_length = initializer.type.array_length; 1279 1280 // Perform implicit cast from initializer expression to variable type 1281 if (initializer.type.components() > type.components()) 1282 warning(initializer.location, 3206, "implicit truncation of vector type"); 1283 1284 initializer.add_cast_operation(type); 1285 1286 if (type.has(type::q_static)) 1287 initializer.type.qualifiers |= type::q_static; 1288 } 1289 else if (type.is_numeric() || type.is_struct()) // Numeric variables without an initializer need special handling 1290 { 1291 if (type.has(type::q_const)) // Constants have to have an initial value 1292 return error(location, 3012, '\'' + name + "': missing initial value"), false; 1293 else if (!type.has(type::q_uniform)) // Zero initialize all global variables 1294 initializer.reset_to_rvalue_constant(location, {}, type); 1295 } 1296 else if (global && accept('{')) // Textures and samplers can have a property block attached to their declaration 1297 { 1298 // Non-numeric variables cannot be constants 1299 if (type.has(type::q_const)) 1300 return error(location, 3035, '\'' + name + "': this variable type cannot be declared 'const'"), false; 1301 1302 while (!peek('}')) 1303 { 1304 if (!expect(tokenid::identifier)) 1305 return consume_until('}'), false; 1306 1307 const auto property_name = std::move(_token.literal_as_string); 1308 const auto property_location = std::move(_token.location); 1309 1310 if (!expect('=')) 1311 return consume_until('}'), false; 1312 1313 backup(); 1314 1315 expression expression; 1316 1317 if (accept(tokenid::identifier)) // Handle special enumeration names for property values 1318 { 1319 // Transform identifier to uppercase to do case-insensitive comparison 1320 std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(), 1321 [](std::string::value_type c) { 1322 return static_cast<std::string::value_type>(std::toupper(c)); 1323 }); 1324 1325 static const std::unordered_map<std::string_view, uint32_t> s_enum_values = { 1326 { "NONE", 0 }, { "POINT", 0 }, 1327 { "LINEAR", 1 }, 1328 { "WRAP", uint32_t(texture_address_mode::wrap) }, { "REPEAT", uint32_t(texture_address_mode::wrap) }, 1329 { "MIRROR", uint32_t(texture_address_mode::mirror) }, 1330 { "CLAMP", uint32_t(texture_address_mode::clamp) }, 1331 { "BORDER", uint32_t(texture_address_mode::border) }, 1332 { "R8", uint32_t(texture_format::r8) }, 1333 { "R16", uint32_t(texture_format::r16) }, 1334 { "R16F", uint32_t(texture_format::r16f) }, 1335 { "R32I", uint32_t(texture_format::r32i) }, 1336 { "R32U", uint32_t(texture_format::r32u) }, 1337 { "R32F", uint32_t(texture_format::r32f) }, 1338 { "RG8", uint32_t(texture_format::rg8) }, { "R8G8", uint32_t(texture_format::rg8) }, 1339 { "RG16", uint32_t(texture_format::rg16) }, { "R16G16", uint32_t(texture_format::rg16) }, 1340 { "RG16F", uint32_t(texture_format::rg16f) }, { "R16G16F", uint32_t(texture_format::rg16f) }, 1341 { "RG32F", uint32_t(texture_format::rg32f) }, { "R32G32F", uint32_t(texture_format::rg32f) }, 1342 { "RGBA8", uint32_t(texture_format::rgba8) }, { "R8G8B8A8", uint32_t(texture_format::rgba8) }, 1343 { "RGBA16", uint32_t(texture_format::rgba16) }, { "R16G16B16A16", uint32_t(texture_format::rgba16) }, 1344 { "RGBA16F", uint32_t(texture_format::rgba16f) }, { "R16G16B16A16F", uint32_t(texture_format::rgba16f) }, 1345 { "RGBA32F", uint32_t(texture_format::rgba32f) }, { "R32G32B32A32F", uint32_t(texture_format::rgba32f) }, 1346 { "RGB10A2", uint32_t(texture_format::rgb10a2) }, { "R10G10B10A2", uint32_t(texture_format::rgb10a2) }, 1347 }; 1348 1349 // Look up identifier in list of possible enumeration names 1350 if (const auto it = s_enum_values.find(_token.literal_as_string); 1351 it != s_enum_values.end()) 1352 expression.reset_to_rvalue_constant(_token.location, it->second); 1353 else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression 1354 restore(); 1355 } 1356 1357 // Parse right hand side as normal expression if no special enumeration name was matched already 1358 if (!expression.is_constant && !parse_expression_multary(expression)) 1359 return consume_until('}'), false; 1360 1361 if (property_name == "Texture") 1362 { 1363 // Ignore invalid symbols that were added during error recovery 1364 if (expression.base == 0xFFFFFFFF) 1365 return consume_until('}'), false; 1366 1367 if (!expression.type.is_texture()) 1368 return error(expression.location, 3020, "type mismatch, expected texture name"), consume_until('}'), false; 1369 1370 if (type.is_sampler() || type.is_storage()) 1371 { 1372 reshadefx::texture_info &target_info = _codegen->get_texture(expression.base); 1373 if (type.is_storage()) 1374 // Texture is used as storage 1375 target_info.storage_access = true; 1376 1377 texture_info = target_info; 1378 sampler_info.texture_name = target_info.unique_name; 1379 storage_info.texture_name = target_info.unique_name; 1380 } 1381 } 1382 else 1383 { 1384 if (!expression.is_constant || !expression.type.is_scalar()) 1385 return error(expression.location, 3538, "value must be a literal scalar expression"), consume_until('}'), false; 1386 1387 // All states below expect the value to be of an integer type 1388 expression.add_cast_operation({ type::t_int, 1, 1 }); 1389 const int value = expression.constant.as_int[0]; 1390 1391 if (value < 0) // There is little use for negative values, so warn in those cases 1392 warning(expression.location, 3571, "negative value specified for property '" + property_name + '\''); 1393 1394 if (type.is_texture()) 1395 { 1396 if (property_name == "Width") 1397 texture_info.width = value > 0 ? value : 1; 1398 else if (type.texture_dimension() >= 2 && property_name == "Height") 1399 texture_info.height = value > 0 ? value : 1; 1400 else if (type.texture_dimension() >= 3 && property_name == "Depth") 1401 texture_info.depth = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1; 1402 else if (property_name == "MipLevels") 1403 // Also ensures negative values do not cause problems 1404 texture_info.levels = value > 0 && value <= std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 1; 1405 else if (property_name == "Format") 1406 texture_info.format = static_cast<texture_format>(value); 1407 else 1408 return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false; 1409 } 1410 else if (type.is_sampler()) 1411 { 1412 if (property_name == "SRGBTexture" || property_name == "SRGBReadEnable") 1413 sampler_info.srgb = value != 0; 1414 else if (property_name == "AddressU") 1415 sampler_info.address_u = static_cast<texture_address_mode>(value); 1416 else if (property_name == "AddressV") 1417 sampler_info.address_v = static_cast<texture_address_mode>(value); 1418 else if (property_name == "AddressW") 1419 sampler_info.address_w = static_cast<texture_address_mode>(value); 1420 else if (property_name == "MinFilter") 1421 sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x0F) | ((value << 4) & 0x30)); // Combine sampler filter components into a single filter enumeration value 1422 else if (property_name == "MagFilter") 1423 sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x33) | ((value << 2) & 0x0C)); 1424 else if (property_name == "MipFilter") 1425 sampler_info.filter = static_cast<filter_mode>((uint32_t(sampler_info.filter) & 0x3C) | (value & 0x03)); 1426 else if (property_name == "MinLOD" || property_name == "MaxMipLevel") 1427 sampler_info.min_lod = static_cast<float>(value); 1428 else if (property_name == "MaxLOD") 1429 sampler_info.max_lod = static_cast<float>(value); 1430 else if (property_name == "MipLODBias" || property_name == "MipMapLodBias") 1431 sampler_info.lod_bias = static_cast<float>(value); 1432 else 1433 return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false; 1434 } 1435 else if (type.is_storage()) 1436 { 1437 if (property_name == "MipLOD" || property_name == "MipLevel") 1438 storage_info.level = value > 0 && value < std::numeric_limits<uint16_t>::max() ? static_cast<uint16_t>(value) : 0; 1439 else 1440 return error(property_location, 3004, "unrecognized property '" + property_name + '\''), consume_until('}'), false; 1441 } 1442 } 1443 1444 if (!expect(';')) 1445 return consume_until('}'), false; 1446 } 1447 1448 if (!expect('}')) 1449 return false; 1450 } 1451 } 1452 1453 // At this point the array size should be known (either from the declaration or the initializer) 1454 if (type.array_length < 0) 1455 return error(location, 3074, '\'' + name + "': implicit array missing initial value"), false; 1456 1457 symbol symbol; 1458 1459 // Variables with a constant initializer and constant type are named constants 1460 // Skip this for very large arrays though, to avoid large amounts of duplicated values when that array constant is accessed with a dynamic index 1461 if (type.is_numeric() && type.has(type::q_const) && initializer.is_constant && type.array_length < 100) 1462 { 1463 // Named constants are special symbols 1464 symbol = { symbol_type::constant, 0, type, initializer.constant }; 1465 } 1466 else if (type.is_texture()) 1467 { 1468 assert(global); 1469 1470 texture_info.name = name; 1471 texture_info.type = static_cast<texture_type>(type.texture_dimension()); 1472 1473 // Add namespace scope to avoid name clashes 1474 texture_info.unique_name = 'V' + current_scope().name + name; 1475 std::replace(texture_info.unique_name.begin(), texture_info.unique_name.end(), ':', '_'); 1476 1477 texture_info.annotations = std::move(sampler_info.annotations); 1478 1479 symbol = { symbol_type::variable, 0, type }; 1480 symbol.id = _codegen->define_texture(location, texture_info); 1481 } 1482 // Samplers are actually combined image samplers 1483 else if (type.is_sampler()) 1484 { 1485 assert(global); 1486 1487 if (sampler_info.texture_name.empty()) 1488 return error(location, 3012, '\'' + name + "': missing 'Texture' property"), false; 1489 if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type)) 1490 return error(location, 3521, '\'' + name + "': type mismatch between texture and sampler type"), false; 1491 if (sampler_info.srgb && texture_info.format != texture_format::rgba8) 1492 return error(location, 4582, '\'' + name + "': texture does not support sRGB sampling (only textures with RGBA8 format do)"), false; 1493 1494 if (texture_info.format == texture_format::r32i ? 1495 !type.is_integral() || !type.is_signed() : 1496 texture_info.format == texture_format::r32u ? 1497 !type.is_integral() || !type.is_unsigned() : 1498 !type.is_floating_point()) 1499 return error(location, 4582, '\'' + name + "': type mismatch between texture format and sampler element type"), false; 1500 1501 sampler_info.name = name; 1502 sampler_info.type = type; 1503 1504 // Add namespace scope to avoid name clashes 1505 sampler_info.unique_name = 'V' + current_scope().name + name; 1506 std::replace(sampler_info.unique_name.begin(), sampler_info.unique_name.end(), ':', '_'); 1507 1508 symbol = { symbol_type::variable, 0, type }; 1509 symbol.id = _codegen->define_sampler(location, texture_info, sampler_info); 1510 } 1511 else if (type.is_storage()) 1512 { 1513 assert(global); 1514 1515 if (storage_info.texture_name.empty()) 1516 return error(location, 3012, '\'' + name + "': missing 'Texture' property"), false; 1517 if (type.texture_dimension() != static_cast<unsigned int>(texture_info.type)) 1518 return error(location, 3521, '\'' + name + "': type mismatch between texture and storage type"), false; 1519 1520 if (texture_info.format == texture_format::r32i ? 1521 !type.is_integral() || !type.is_signed() : 1522 texture_info.format == texture_format::r32u ? 1523 !type.is_integral() || !type.is_unsigned() : 1524 !type.is_floating_point()) 1525 return error(location, 4582, '\'' + name + "': type mismatch between texture format and storage element type"), false; 1526 1527 storage_info.name = name; 1528 storage_info.type = type; 1529 1530 // Add namespace scope to avoid name clashes 1531 storage_info.unique_name = 'V' + current_scope().name + name; 1532 std::replace(storage_info.unique_name.begin(), storage_info.unique_name.end(), ':', '_'); 1533 1534 if (storage_info.level > texture_info.levels - 1) 1535 storage_info.level = texture_info.levels - 1; 1536 1537 symbol = { symbol_type::variable, 0, type }; 1538 symbol.id = _codegen->define_storage(location, texture_info, storage_info); 1539 } 1540 // Uniform variables are put into a global uniform buffer structure 1541 else if (type.has(type::q_uniform)) 1542 { 1543 assert(global); 1544 1545 uniform_info uniform_info; 1546 uniform_info.name = name; 1547 uniform_info.type = type; 1548 1549 uniform_info.annotations = std::move(sampler_info.annotations); 1550 1551 uniform_info.initializer_value = std::move(initializer.constant); 1552 uniform_info.has_initializer_value = initializer.is_constant; 1553 1554 symbol = { symbol_type::variable, 0, type }; 1555 symbol.id = _codegen->define_uniform(location, uniform_info); 1556 } 1557 // All other variables are separate entities 1558 else 1559 { 1560 // Update global variable names to contain the namespace scope to avoid name clashes 1561 std::string unique_name = global ? 'V' + current_scope().name + name : name; 1562 std::replace(unique_name.begin(), unique_name.end(), ':', '_'); 1563 1564 symbol = { symbol_type::variable, 0, type }; 1565 symbol.id = _codegen->define_variable(location, type, std::move(unique_name), global, 1566 // Shared variables cannot have an initializer 1567 type.has(type::q_groupshared) ? 0 : _codegen->emit_load(initializer)); 1568 } 1569 1570 // Insert the symbol into the symbol table 1571 if (!insert_symbol(name, symbol, global)) 1572 return error(location, 3003, "redefinition of '" + name + '\''), false; 1573 1574 return parse_success; 1575 } 1576 1577 bool reshadefx::parser::parse_technique() 1578 { 1579 if (!expect(tokenid::identifier)) 1580 return false; 1581 1582 technique_info info; 1583 info.name = std::move(_token.literal_as_string); 1584 1585 bool parse_success = parse_annotations(info.annotations); 1586 1587 if (!expect('{')) 1588 return false; 1589 1590 while (!peek('}')) 1591 { 1592 if (pass_info pass; parse_technique_pass(pass)) 1593 info.passes.push_back(std::move(pass)); 1594 else { 1595 parse_success = false; 1596 if (!peek(tokenid::pass) && !peek('}')) // If there is another pass definition following, try to parse that despite the error 1597 return consume_until('}'), false; 1598 } 1599 } 1600 1601 _codegen->define_technique(std::move(info)); 1602 1603 return expect('}') && parse_success; 1604 } 1605 bool reshadefx::parser::parse_technique_pass(pass_info &info) 1606 { 1607 if (!expect(tokenid::pass)) 1608 return false; 1609 1610 const auto pass_location = std::move(_token.location); 1611 1612 // Passes can have an optional name 1613 if (accept(tokenid::identifier)) 1614 info.name = std::move(_token.literal_as_string); 1615 1616 bool parse_success = true; 1617 bool targets_support_srgb = true; 1618 function_info vs_info, ps_info, cs_info; 1619 1620 if (!expect('{')) 1621 return false; 1622 1623 while (!peek('}')) 1624 { 1625 // Parse pass states 1626 if (!expect(tokenid::identifier)) 1627 return consume_until('}'), false; 1628 1629 auto location = std::move(_token.location); 1630 const auto state = std::move(_token.literal_as_string); 1631 1632 if (!expect('=')) 1633 return consume_until('}'), false; 1634 1635 const bool is_shader_state = state == "VertexShader" || state == "PixelShader" || state == "ComputeShader"; 1636 const bool is_texture_state = state.compare(0, 12, "RenderTarget") == 0 && (state.size() == 12 || (state[12] >= '0' && state[12] < '8')); 1637 1638 // Shader and render target assignment looks up values in the symbol table, so handle those separately from the other states 1639 if (is_shader_state || is_texture_state) 1640 { 1641 std::string identifier; 1642 scoped_symbol symbol; 1643 if (!accept_symbol(identifier, symbol)) 1644 return consume_until('}'), false; 1645 1646 location = std::move(_token.location); 1647 1648 int num_threads[3] = { 1, 1, 1 }; 1649 if (accept('<')) 1650 { 1651 expression x, y, z; 1652 if (!parse_expression_multary(x, 8) || !expect(',') || !parse_expression_multary(y, 8)) 1653 return consume_until('}'), false; 1654 1655 // Parse optional third dimension (defaults to 1) 1656 z.reset_to_rvalue_constant({}, 1); 1657 if (accept(',') && !parse_expression_multary(z, 8)) 1658 return consume_until('}'), false; 1659 1660 if (!x.is_constant) 1661 return error(x.location, 3011, "value must be a literal expression"), consume_until('}'), false; 1662 if (!y.is_constant) 1663 return error(y.location, 3011, "value must be a literal expression"), consume_until('}'), false; 1664 if (!z.is_constant) 1665 return error(z.location, 3011, "value must be a literal expression"), consume_until('}'), false; 1666 x.add_cast_operation({ type::t_int, 1, 1 }); 1667 y.add_cast_operation({ type::t_int, 1, 1 }); 1668 z.add_cast_operation({ type::t_int, 1, 1 }); 1669 num_threads[0] = x.constant.as_int[0]; 1670 num_threads[1] = y.constant.as_int[0]; 1671 num_threads[2] = z.constant.as_int[0]; 1672 1673 if (!expect('>')) 1674 return consume_until('}'), false; 1675 } 1676 1677 // Ignore invalid symbols that were added during error recovery 1678 if (symbol.id != 0xFFFFFFFF) 1679 { 1680 if (is_shader_state) 1681 { 1682 if (!symbol.id) 1683 parse_success = false, 1684 error(location, 3501, "undeclared identifier '" + identifier + "', expected function name"); 1685 else if (!symbol.type.is_function()) 1686 parse_success = false, 1687 error(location, 3020, "type mismatch, expected function name"); 1688 else { 1689 // Look up the matching function info for this function definition 1690 function_info &function_info = _codegen->get_function(symbol.id); 1691 1692 // We potentially need to generate a special entry point function which translates between function parameters and input/output variables 1693 switch (state[0]) 1694 { 1695 case 'V': 1696 vs_info = function_info; 1697 _codegen->define_entry_point(vs_info, shader_type::vs); 1698 info.vs_entry_point = vs_info.unique_name; 1699 break; 1700 case 'P': 1701 ps_info = function_info; 1702 _codegen->define_entry_point(ps_info, shader_type::ps); 1703 info.ps_entry_point = ps_info.unique_name; 1704 break; 1705 case 'C': 1706 cs_info = function_info; 1707 _codegen->define_entry_point(cs_info, shader_type::cs, num_threads); 1708 info.cs_entry_point = cs_info.unique_name; 1709 break; 1710 } 1711 } 1712 } 1713 else 1714 { 1715 assert(is_texture_state); 1716 1717 if (!symbol.id) 1718 parse_success = false, 1719 error(location, 3004, "undeclared identifier '" + identifier + "', expected texture name"); 1720 else if (!symbol.type.is_texture()) 1721 parse_success = false, 1722 error(location, 3020, "type mismatch, expected texture name"); 1723 else if (symbol.type.texture_dimension() != 2) 1724 parse_success = false, 1725 error(location, 3020, "cannot use texture" + std::to_string(symbol.type.texture_dimension()) + "D as render target"); 1726 else { 1727 reshadefx::texture_info &target_info = _codegen->get_texture(symbol.id); 1728 // Texture is used as a render target 1729 target_info.render_target = true; 1730 1731 // Verify that all render targets in this pass have the same dimensions 1732 if (info.viewport_width != 0 && info.viewport_height != 0 && (target_info.width != info.viewport_width || target_info.height != info.viewport_height)) 1733 parse_success = false, 1734 error(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) + ')'); 1735 1736 info.viewport_width = target_info.width; 1737 info.viewport_height = target_info.height; 1738 1739 const auto target_index = state.size() > 12 ? (state[12] - '0') : 0; 1740 info.render_target_names[target_index] = target_info.unique_name; 1741 1742 // Only RGBA8 format supports sRGB writes across all APIs 1743 if (target_info.format != texture_format::rgba8) 1744 targets_support_srgb = false; 1745 } 1746 } 1747 } 1748 else 1749 { 1750 parse_success = false; 1751 } 1752 } 1753 else // Handle the rest of the pass states 1754 { 1755 backup(); 1756 1757 expression expression; 1758 1759 if (accept(tokenid::identifier)) // Handle special enumeration names for pass states 1760 { 1761 // Transform identifier to uppercase to do case-insensitive comparison 1762 std::transform(_token.literal_as_string.begin(), _token.literal_as_string.end(), _token.literal_as_string.begin(), 1763 [](std::string::value_type c) { 1764 return static_cast<std::string::value_type>(std::toupper(c)); 1765 }); 1766 1767 static const std::unordered_map<std::string_view, uint32_t> s_enum_values = { 1768 { "NONE", 0 }, { "ZERO", 0 }, { "ONE", 1 }, 1769 { "ADD", uint32_t(pass_blend_op::add) }, 1770 { "SUBTRACT", uint32_t(pass_blend_op::subtract) }, 1771 { "REVSUBTRACT", uint32_t(pass_blend_op::reverse_subtract) }, 1772 { "MIN", uint32_t(pass_blend_op::min) }, 1773 { "MAX", uint32_t(pass_blend_op::max) }, 1774 { "SRCCOLOR", uint32_t(pass_blend_factor::source_color) }, 1775 { "INVSRCCOLOR", uint32_t(pass_blend_factor::one_minus_source_color) }, 1776 { "DESTCOLOR", uint32_t(pass_blend_factor::dest_color) }, 1777 { "INVDESTCOLOR", uint32_t(pass_blend_factor::one_minus_dest_color) }, 1778 { "SRCALPHA", uint32_t(pass_blend_factor::source_alpha) }, 1779 { "INVSRCALPHA", uint32_t(pass_blend_factor::one_minus_source_alpha) }, 1780 { "DESTALPHA", uint32_t(pass_blend_factor::dest_alpha) }, 1781 { "INVDESTALPHA", uint32_t(pass_blend_factor::one_minus_dest_alpha) }, 1782 { "KEEP", uint32_t(pass_stencil_op::keep) }, 1783 { "REPLACE", uint32_t(pass_stencil_op::replace) }, 1784 { "INVERT", uint32_t(pass_stencil_op::invert) }, 1785 { "INCR", uint32_t(pass_stencil_op::increment) }, 1786 { "INCRSAT", uint32_t(pass_stencil_op::increment_saturate) }, 1787 { "DECR", uint32_t(pass_stencil_op::decrement) }, 1788 { "DECRSAT", uint32_t(pass_stencil_op::decrement_saturate) }, 1789 { "NEVER", uint32_t(pass_stencil_func::never) }, 1790 { "EQUAL", uint32_t(pass_stencil_func::equal) }, 1791 { "NEQUAL", uint32_t(pass_stencil_func::not_equal) }, { "NOTEQUAL", uint32_t(pass_stencil_func::not_equal) }, 1792 { "LESS", uint32_t(pass_stencil_func::less) }, 1793 { "GREATER", uint32_t(pass_stencil_func::greater) }, 1794 { "LEQUAL", uint32_t(pass_stencil_func::less_equal) }, { "LESSEQUAL", uint32_t(pass_stencil_func::less_equal) }, 1795 { "GEQUAL", uint32_t(pass_stencil_func::greater_equal) }, { "GREATEREQUAL", uint32_t(pass_stencil_func::greater_equal) }, 1796 { "ALWAYS", uint32_t(pass_stencil_func::always) }, 1797 { "POINTS", uint32_t(primitive_topology::point_list) }, 1798 { "POINTLIST", uint32_t(primitive_topology::point_list) }, 1799 { "LINES", uint32_t(primitive_topology::line_list) }, 1800 { "LINELIST", uint32_t(primitive_topology::line_list) }, 1801 { "LINESTRIP", uint32_t(primitive_topology::line_strip) }, 1802 { "TRIANGLES", uint32_t(primitive_topology::triangle_list) }, 1803 { "TRIANGLELIST", uint32_t(primitive_topology::triangle_list) }, 1804 { "TRIANGLESTRIP", uint32_t(primitive_topology::triangle_strip) }, 1805 }; 1806 1807 // Look up identifier in list of possible enumeration names 1808 if (const auto it = s_enum_values.find(_token.literal_as_string); 1809 it != s_enum_values.end()) 1810 expression.reset_to_rvalue_constant(_token.location, it->second); 1811 else // No match found, so rewind to parser state before the identifier was consumed and try parsing it as a normal expression 1812 restore(); 1813 } 1814 1815 // Parse right hand side as normal expression if no special enumeration name was matched already 1816 if (!expression.is_constant && !parse_expression_multary(expression)) 1817 return consume_until('}'), false; 1818 else if (!expression.is_constant || !expression.type.is_scalar()) 1819 parse_success = false, 1820 error(expression.location, 3011, "pass state value must be a literal scalar expression"); 1821 1822 // All states below expect the value to be of an unsigned integer type 1823 expression.add_cast_operation({ type::t_uint, 1, 1 }); 1824 const unsigned int value = expression.constant.as_uint[0]; 1825 1826 #define SET_STATE_VALUE_INDEXED(name, info_name, value) \ 1827 else if (constexpr size_t name##_len = sizeof(#name) - 1; state.compare(0, name##_len, #name) == 0 && (state.size() == name##_len || (state[name##_len] >= '0' && state[name##_len] < ('0' + static_cast<char>(std::size(info.info_name)))))) \ 1828 { \ 1829 if (state.size() != name##_len) \ 1830 info.info_name[state[name##_len] - '0'] = (value); \ 1831 else \ 1832 for (int i = 0; i < static_cast<int>(std::size(info.info_name)); ++i) \ 1833 info.info_name[i] = (value); \ 1834 } 1835 1836 if (state == "SRGBWriteEnable") 1837 info.srgb_write_enable = (value != 0); 1838 SET_STATE_VALUE_INDEXED(BlendEnable, blend_enable, value != 0) 1839 else if (state == "StencilEnable") 1840 info.stencil_enable = (value != 0); 1841 else if (state == "ClearRenderTargets") 1842 info.clear_render_targets = (value != 0); 1843 SET_STATE_VALUE_INDEXED(ColorWriteMask, color_write_mask, value & 0xFF) 1844 SET_STATE_VALUE_INDEXED(RenderTargetWriteMask, color_write_mask, value & 0xFF) 1845 else if (state == "StencilReadMask" || state == "StencilMask") 1846 info.stencil_read_mask = value & 0xFF; 1847 else if (state == "StencilWriteMask") 1848 info.stencil_write_mask = value & 0xFF; 1849 SET_STATE_VALUE_INDEXED(BlendOp, blend_op, static_cast<pass_blend_op>(value)) 1850 SET_STATE_VALUE_INDEXED(BlendOpAlpha, blend_op_alpha, static_cast<pass_blend_op>(value)) 1851 SET_STATE_VALUE_INDEXED(SrcBlend, src_blend, static_cast<pass_blend_factor>(value)) 1852 SET_STATE_VALUE_INDEXED(SrcBlendAlpha, src_blend_alpha, static_cast<pass_blend_factor>(value)) 1853 SET_STATE_VALUE_INDEXED(DestBlend, dest_blend, static_cast<pass_blend_factor>(value)) 1854 SET_STATE_VALUE_INDEXED(DestBlendAlpha, dest_blend_alpha, static_cast<pass_blend_factor>(value)) 1855 else if (state == "StencilFunc") 1856 info.stencil_comparison_func = static_cast<pass_stencil_func>(value); 1857 else if (state == "StencilRef") 1858 info.stencil_reference_value = value; 1859 else if (state == "StencilPass" || state == "StencilPassOp") 1860 info.stencil_op_pass = static_cast<pass_stencil_op>(value); 1861 else if (state == "StencilFail" || state == "StencilFailOp") 1862 info.stencil_op_fail = static_cast<pass_stencil_op>(value); 1863 else if (state == "StencilZFail" || state == "StencilDepthFail" || state == "StencilDepthFailOp") 1864 info.stencil_op_depth_fail = static_cast<pass_stencil_op>(value); 1865 else if (state == "VertexCount") 1866 info.num_vertices = value; 1867 else if (state == "PrimitiveType" || state == "PrimitiveTopology") 1868 info.topology = static_cast<primitive_topology>(value); 1869 else if (state == "DispatchSizeX") 1870 info.viewport_width = value; 1871 else if (state == "DispatchSizeY") 1872 info.viewport_height = value; 1873 else if (state == "DispatchSizeZ") 1874 info.viewport_dispatch_z = value; 1875 else if (state == "GenerateMipmaps" || state == "GenerateMipMaps") 1876 info.generate_mipmaps = (value != 0); 1877 else 1878 parse_success = false, 1879 error(location, 3004, "unrecognized pass state '" + state + '\''); 1880 1881 #undef SET_STATE_VALUE_INDEXED 1882 } 1883 1884 if (!expect(';')) 1885 return consume_until('}'), false; 1886 } 1887 1888 if (parse_success) 1889 { 1890 if (!info.cs_entry_point.empty()) 1891 { 1892 if (info.viewport_width == 0 || info.viewport_height == 0) 1893 { 1894 parse_success = false; 1895 error(pass_location, 3012, "pass is missing 'DispatchSizeX' or 'DispatchSizeY' property"); 1896 } 1897 1898 if (!info.vs_entry_point.empty()) 1899 warning(pass_location, 3089, "pass is specifying both 'VertexShader' and 'ComputeShader' which cannot be used together"); 1900 if (!info.ps_entry_point.empty()) 1901 warning(pass_location, 3089, "pass is specifying both 'PixelShader' and 'ComputeShader' which cannot be used together"); 1902 1903 for (codegen::id id : cs_info.referenced_samplers) 1904 info.samplers.push_back(_codegen->get_sampler(id)); 1905 for (codegen::id id : cs_info.referenced_storages) 1906 info.storages.push_back(_codegen->get_storage(id)); 1907 } 1908 else if (info.vs_entry_point.empty() || info.ps_entry_point.empty()) 1909 { 1910 parse_success = false; 1911 1912 if (info.vs_entry_point.empty()) 1913 error(pass_location, 3012, "pass is missing 'VertexShader' property"); 1914 if (info.ps_entry_point.empty()) 1915 error(pass_location, 3012, "pass is missing 'PixelShader' property"); 1916 } 1917 else 1918 { 1919 // Verify that shader signatures between VS and PS match (both semantics and interpolation qualifiers) 1920 std::unordered_map<std::string_view, type> vs_semantic_mapping; 1921 if (vs_info.return_semantic.empty()) 1922 { 1923 if (!vs_info.return_type.is_void() && !vs_info.return_type.is_struct()) 1924 { 1925 parse_success = false; 1926 error(pass_location, 3503, '\'' + vs_info.name + "': function return value is missing semantics"); 1927 } 1928 } 1929 else 1930 { 1931 vs_semantic_mapping[vs_info.return_semantic] = vs_info.return_type; 1932 } 1933 1934 for (const struct_member_info ¶m : vs_info.parameter_list) 1935 { 1936 if (param.semantic.empty()) 1937 { 1938 if (!param.type.is_struct()) 1939 { 1940 parse_success = false; 1941 if (param.type.has(type::q_in)) 1942 error(pass_location, 3502, '\'' + vs_info.name + "': input parameter '" + param.name + "' is missing semantics"); 1943 else 1944 error(pass_location, 3503, '\'' + vs_info.name + "': output parameter '" + param.name + "' is missing semantics"); 1945 } 1946 } 1947 else if (param.type.has(type::q_out)) 1948 { 1949 vs_semantic_mapping[param.semantic] = param.type; 1950 } 1951 } 1952 1953 if (ps_info.return_semantic.empty()) 1954 { 1955 if (!ps_info.return_type.is_void() && !ps_info.return_type.is_struct()) 1956 { 1957 parse_success = false; 1958 error(pass_location, 3503, '\'' + ps_info.name + "': function return value is missing semantics"); 1959 } 1960 } 1961 1962 for (const struct_member_info ¶m : ps_info.parameter_list) 1963 { 1964 if (param.semantic.empty()) 1965 { 1966 if (!param.type.is_struct()) 1967 { 1968 parse_success = false; 1969 if (param.type.has(type::q_in)) 1970 error(pass_location, 3502, '\'' + ps_info.name + "': input parameter '" + param.name + "' is missing semantics"); 1971 else 1972 error(pass_location, 3503, '\'' + ps_info.name + "': output parameter '" + param.name + "' is missing semantics"); 1973 } 1974 } 1975 else if (param.type.has(type::q_in)) 1976 { 1977 if (const auto it = vs_semantic_mapping.find(param.semantic); 1978 it == vs_semantic_mapping.end() || it->second != param.type) 1979 warning(pass_location, 4576, '\'' + ps_info.name + "': input parameter '" + param.name + "' semantic does not match vertex shader one"); 1980 else if (((it->second.qualifiers ^ param.type.qualifiers) & (type::q_linear | type::q_noperspective | type::q_centroid | type::q_nointerpolation)) != 0) 1981 parse_success = false, 1982 error( pass_location, 4568, '\'' + ps_info.name + "': input parameter '" + param.name + "' interpolation qualifiers do not match vertex shader ones"); 1983 } 1984 } 1985 1986 for (codegen::id id : vs_info.referenced_samplers) 1987 info.samplers.push_back(_codegen->get_sampler(id)); 1988 for (codegen::id id : ps_info.referenced_samplers) 1989 info.samplers.push_back(_codegen->get_sampler(id)); 1990 if (!vs_info.referenced_storages.empty() || !ps_info.referenced_storages.empty()) 1991 { 1992 parse_success = false; 1993 error(pass_location, 3667, "storage writes are only valid in compute shaders"); 1994 } 1995 } 1996 1997 // Verify render target format supports sRGB writes if enabled 1998 if (info.srgb_write_enable && !targets_support_srgb) 1999 parse_success = false, 2000 error(pass_location, 4582, "one or more render target(s) do not support sRGB writes (only textures with RGBA8 format do)"); 2001 } 2002 2003 return expect('}') && parse_success; 2004 }