Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/reshadefx/src/effect_codegen_spirv.cpp
4246 views
1
/*
2
* Copyright (C) 2014 Patrick Mours
3
* SPDX-License-Identifier: BSD-3-Clause
4
*/
5
6
#include "effect_parser.hpp"
7
#include "effect_codegen.hpp"
8
#include <cassert>
9
#include <cstring> // std::memcmp
10
#include <charconv> // std::from_chars
11
#include <algorithm> // std::find_if, std::max, std::sort
12
#include <unordered_set>
13
14
// Use the C++ variant of the SPIR-V headers
15
#include <spirv.hpp>
16
namespace spv {
17
#include <GLSL.std.450.h>
18
}
19
20
using namespace reshadefx;
21
22
inline uint32_t align_up(uint32_t size, uint32_t alignment)
23
{
24
alignment -= 1;
25
return ((size + alignment) & ~alignment);
26
}
27
28
/// <summary>
29
/// A single instruction in a SPIR-V module
30
/// </summary>
31
struct spirv_instruction
32
{
33
spv::Op op;
34
spv::Id type;
35
spv::Id result;
36
std::vector<spv::Id> operands;
37
38
explicit spirv_instruction(spv::Op op = spv::OpNop) : op(op), type(0), result(0) {}
39
spirv_instruction(spv::Op op, spv::Id result) : op(op), type(result), result(0) {}
40
spirv_instruction(spv::Op op, spv::Id type, spv::Id result) : op(op), type(type), result(result) {}
41
42
/// <summary>
43
/// Add a single operand to the instruction.
44
/// </summary>
45
spirv_instruction &add(spv::Id operand)
46
{
47
operands.push_back(operand);
48
return *this;
49
}
50
51
/// <summary>
52
/// Add a range of operands to the instruction.
53
/// </summary>
54
template <typename It>
55
spirv_instruction &add(It begin, It end)
56
{
57
operands.insert(operands.end(), begin, end);
58
return *this;
59
}
60
61
/// <summary>
62
/// Add a null-terminated literal UTF-8 string to the instruction.
63
/// </summary>
64
spirv_instruction &add_string(const char *string)
65
{
66
uint32_t word;
67
do {
68
word = 0;
69
for (uint32_t i = 0; i < 4 && *string; ++i)
70
reinterpret_cast<uint8_t *>(&word)[i] = *string++;
71
add(word);
72
} while (*string || (word & 0xFF000000));
73
return *this;
74
}
75
76
/// <summary>
77
/// Write this instruction to a SPIR-V module.
78
/// </summary>
79
/// <param name="output">The output stream to append this instruction to.</param>
80
void write(std::basic_string<char> &output) const
81
{
82
// See https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html
83
// 0 | Opcode: The 16 high-order bits are the WordCount of the instruction. The 16 low-order bits are the opcode enumerant.
84
// 1 | Optional instruction type <id>
85
// . | Optional instruction Result <id>
86
// . | Operand 1 (if needed)
87
// . | Operand 2 (if needed)
88
// ... | ...
89
// WordCount - 1 | Operand N (N is determined by WordCount minus the 1 to 3 words used for the opcode, instruction type <id>, and instruction Result <id>).
90
91
const uint32_t word_count = 1 + (type != 0) + (result != 0) + static_cast<uint32_t>(operands.size());
92
write_word(output, (word_count << spv::WordCountShift) | op);
93
94
// Optional instruction type ID
95
if (type != 0)
96
write_word(output, type);
97
98
// Optional instruction result ID
99
if (result != 0)
100
write_word(output, result);
101
102
// Write out the operands
103
for (const uint32_t operand : operands)
104
write_word(output, operand);
105
}
106
107
static void write_word(std::basic_string<char> &output, uint32_t word)
108
{
109
output.insert(output.end(), reinterpret_cast<const char *>(&word), reinterpret_cast<const char *>(&word + 1));
110
}
111
112
operator uint32_t() const
113
{
114
assert(result != 0);
115
116
return result;
117
}
118
};
119
120
/// <summary>
121
/// A list of instructions forming a basic block in the SPIR-V module
122
/// </summary>
123
struct spirv_basic_block
124
{
125
std::vector<spirv_instruction> instructions;
126
127
/// <summary>
128
/// Append another basic block the end of this one.
129
/// </summary>
130
void append(const spirv_basic_block &block)
131
{
132
instructions.insert(instructions.end(), block.instructions.begin(), block.instructions.end());
133
}
134
};
135
136
class codegen_spirv final : public codegen
137
{
138
static_assert(sizeof(id) == sizeof(spv::Id), "unexpected SPIR-V id type size");
139
140
public:
141
codegen_spirv(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y) :
142
_debug_info(debug_info),
143
_vulkan_semantics(vulkan_semantics),
144
_uniforms_to_spec_constants(uniforms_to_spec_constants),
145
_enable_16bit_types(enable_16bit_types),
146
_flip_vert_y(flip_vert_y)
147
{
148
_glsl_ext = make_id();
149
}
150
151
private:
152
struct type_lookup
153
{
154
reshadefx::type type;
155
bool is_ptr;
156
uint32_t array_stride;
157
std::pair<spv::StorageClass, spv::ImageFormat> storage;
158
159
friend bool operator==(const type_lookup &lhs, const type_lookup &rhs)
160
{
161
return lhs.type == rhs.type && lhs.is_ptr == rhs.is_ptr && lhs.array_stride == rhs.array_stride && lhs.storage == rhs.storage;
162
}
163
};
164
struct function_blocks
165
{
166
spirv_basic_block declaration;
167
spirv_basic_block variables;
168
spirv_basic_block definition;
169
reshadefx::type return_type;
170
std::vector<reshadefx::type> param_types;
171
172
friend bool operator==(const function_blocks &lhs, const function_blocks &rhs)
173
{
174
if (lhs.param_types.size() != rhs.param_types.size())
175
return false;
176
for (size_t i = 0; i < lhs.param_types.size(); ++i)
177
if (!(lhs.param_types[i] == rhs.param_types[i]))
178
return false;
179
return lhs.return_type == rhs.return_type;
180
}
181
};
182
183
bool _debug_info = false;
184
bool _vulkan_semantics = false;
185
bool _uniforms_to_spec_constants = false;
186
bool _enable_16bit_types = false;
187
bool _flip_vert_y = false;
188
189
spirv_basic_block _entries;
190
spirv_basic_block _execution_modes;
191
spirv_basic_block _debug_a;
192
spirv_basic_block _debug_b;
193
spirv_basic_block _annotations;
194
spirv_basic_block _types_and_constants;
195
spirv_basic_block _variables;
196
197
std::vector<function_blocks> _functions_blocks;
198
std::unordered_map<id, spirv_basic_block> _block_data;
199
spirv_basic_block *_current_block_data = nullptr;
200
201
spv::Id _glsl_ext = 0;
202
spv::Id _global_ubo_type = 0;
203
spv::Id _global_ubo_variable = 0;
204
std::vector<spv::Id> _global_ubo_types;
205
function_blocks *_current_function_blocks = nullptr;
206
207
std::vector<std::pair<type_lookup, spv::Id>> _type_lookup;
208
std::vector<std::tuple<type, constant, spv::Id>> _constant_lookup;
209
std::vector<std::pair<function_blocks, spv::Id>> _function_type_lookup;
210
std::unordered_map<std::string, spv::Id> _string_lookup;
211
std::unordered_map<spv::Id, std::pair<spv::StorageClass, spv::ImageFormat>> _storage_lookup;
212
std::unordered_map<std::string, uint32_t> _semantic_to_location;
213
214
std::unordered_set<spv::Id> _spec_constants;
215
std::unordered_set<spv::Capability> _capabilities;
216
217
void add_location(const location &loc, spirv_basic_block &block)
218
{
219
if (loc.source.empty() || !_debug_info)
220
return;
221
222
spv::Id file;
223
224
if (const auto it = _string_lookup.find(loc.source);
225
it != _string_lookup.end())
226
{
227
file = it->second;
228
}
229
else
230
{
231
file =
232
add_instruction(spv::OpString, 0, _debug_a)
233
.add_string(loc.source.c_str());
234
_string_lookup.emplace(loc.source, file);
235
}
236
237
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpLine
238
add_instruction_without_result(spv::OpLine, block)
239
.add(file)
240
.add(loc.line)
241
.add(loc.column);
242
}
243
spirv_instruction &add_instruction(spv::Op op, spv::Id type = 0)
244
{
245
assert(is_in_function() && is_in_block());
246
247
return add_instruction(op, type, *_current_block_data);
248
}
249
spirv_instruction &add_instruction(spv::Op op, spv::Id type, spirv_basic_block &block)
250
{
251
spirv_instruction &instruction = add_instruction_without_result(op, block);
252
instruction.type = type;
253
instruction.result = make_id();
254
return instruction;
255
}
256
spirv_instruction &add_instruction_without_result(spv::Op op)
257
{
258
assert(is_in_function() && is_in_block());
259
260
return add_instruction_without_result(op, *_current_block_data);
261
}
262
spirv_instruction &add_instruction_without_result(spv::Op op, spirv_basic_block &block)
263
{
264
return block.instructions.emplace_back(op);
265
}
266
267
void finalize_header_section(std::basic_string<char> &spirv) const
268
{
269
// Write SPIRV header info
270
spirv_instruction::write_word(spirv, spv::MagicNumber);
271
spirv_instruction::write_word(spirv, 0x10300); // Force SPIR-V 1.3
272
spirv_instruction::write_word(spirv, 0u); // Generator magic number, see https://www.khronos.org/registry/spir-v/api/spir-v.xml
273
spirv_instruction::write_word(spirv, _next_id); // Maximum ID
274
spirv_instruction::write_word(spirv, 0u); // Reserved for instruction schema
275
276
// All capabilities
277
spirv_instruction(spv::OpCapability)
278
.add(spv::CapabilityShader) // Implicitly declares the Matrix capability too
279
.write(spirv);
280
281
for (const spv::Capability capability : _capabilities)
282
spirv_instruction(spv::OpCapability)
283
.add(capability)
284
.write(spirv);
285
286
// Optional extension instructions
287
spirv_instruction(spv::OpExtInstImport, _glsl_ext)
288
.add_string("GLSL.std.450") // Import GLSL extension
289
.write(spirv);
290
291
// Single required memory model instruction
292
spirv_instruction(spv::OpMemoryModel)
293
.add(spv::AddressingModelLogical)
294
.add(spv::MemoryModelGLSL450)
295
.write(spirv);
296
}
297
void finalize_debug_info_section(std::basic_string<char> &spirv) const
298
{
299
spirv_instruction(spv::OpSource)
300
.add(spv::SourceLanguageUnknown) // ReShade FX is not a reserved token at the moment
301
.add(0) // Language version, TODO: Maybe fill in ReShade version here?
302
.write(spirv);
303
304
if (_debug_info)
305
{
306
// All debug instructions
307
for (const spirv_instruction &inst : _debug_a.instructions)
308
inst.write(spirv);
309
for (const spirv_instruction &inst : _debug_b.instructions)
310
inst.write(spirv);
311
}
312
}
313
void finalize_type_and_constants_section(std::basic_string<char> &spirv) const
314
{
315
// All type declarations
316
for (const spirv_instruction &inst : _types_and_constants.instructions)
317
inst.write(spirv);
318
319
// Initialize the UBO type now that all member types are known
320
if (_global_ubo_type == 0 || _global_ubo_variable == 0)
321
return;
322
323
const id global_ubo_type_ptr = _global_ubo_type + 1;
324
325
spirv_instruction(spv::OpTypeStruct, _global_ubo_type)
326
.add(_global_ubo_types.begin(), _global_ubo_types.end())
327
.write(spirv);
328
spirv_instruction(spv::OpTypePointer, global_ubo_type_ptr)
329
.add(spv::StorageClassUniform)
330
.add(_global_ubo_type)
331
.write(spirv);
332
333
spirv_instruction(spv::OpVariable, global_ubo_type_ptr, _global_ubo_variable)
334
.add(spv::StorageClassUniform)
335
.write(spirv);
336
}
337
338
std::basic_string<char> finalize_code() const override
339
{
340
std::basic_string<char> spirv;
341
finalize_header_section(spirv);
342
343
// All entry point declarations
344
for (const spirv_instruction &inst : _entries.instructions)
345
inst.write(spirv);
346
347
// All execution mode declarations
348
for (const spirv_instruction &inst : _execution_modes.instructions)
349
inst.write(spirv);
350
351
finalize_debug_info_section(spirv);
352
353
// All annotation instructions
354
for (const spirv_instruction &inst : _annotations.instructions)
355
inst.write(spirv);
356
357
finalize_type_and_constants_section(spirv);
358
359
for (const spirv_instruction &inst : _variables.instructions)
360
inst.write(spirv);
361
362
// All function definitions
363
for (const function_blocks &func : _functions_blocks)
364
{
365
if (func.definition.instructions.empty())
366
continue;
367
368
for (const spirv_instruction &inst : func.declaration.instructions)
369
inst.write(spirv);
370
371
// Grab first label and move it in front of variable declarations
372
func.definition.instructions.front().write(spirv);
373
assert(func.definition.instructions.front().op == spv::OpLabel);
374
375
for (const spirv_instruction &inst : func.variables.instructions)
376
inst.write(spirv);
377
for (auto inst_it = func.definition.instructions.begin() + 1; inst_it != func.definition.instructions.end(); ++inst_it)
378
inst_it->write(spirv);
379
}
380
381
return spirv;
382
}
383
std::basic_string<char> finalize_code_for_entry_point(const std::string &entry_point_name) const override
384
{
385
const auto entry_point_it = std::find_if(_functions.begin(), _functions.end(),
386
[&entry_point_name](const std::unique_ptr<function> &func) {
387
return func->unique_name == entry_point_name;
388
});
389
if (entry_point_it == _functions.end())
390
return {};
391
const function &entry_point = *entry_point_it->get();
392
393
const auto write_entry_point = [this](const spirv_instruction& oins, std::basic_string<char>& spirv) {
394
assert(oins.operands.size() > 2);
395
spirv_instruction nins(oins.op, oins.type, oins.result);
396
nins.add(oins.operands[0]);
397
nins.add(oins.operands[1]);
398
nins.add_string("main");
399
400
size_t param_start_index = 2;
401
while (param_start_index < oins.operands.size() && (oins.operands[param_start_index] & 0xFF000000) != 0)
402
param_start_index++;
403
404
// skip zero
405
param_start_index++;
406
407
for (size_t i = param_start_index; i < oins.operands.size(); i++)
408
nins.add(oins.operands[i]);
409
nins.write(spirv);
410
};
411
412
// Build list of IDs to remove
413
std::vector<spv::Id> variables_to_remove;
414
#if 1
415
std::vector<spv::Id> functions_to_remove;
416
#else
417
for (const sampler &info : _module.samplers)
418
if (std::find(entry_point.referenced_samplers.begin(), entry_point.referenced_samplers.end(), info.id) == entry_point.referenced_samplers.end())
419
variables_to_remove.push_back(info.id);
420
for (const storage &info : _module.storages)
421
if (std::find(entry_point.referenced_storages.begin(), entry_point.referenced_storages.end(), info.id) == entry_point.referenced_storages.end())
422
variables_to_remove.push_back(info.id);
423
#endif
424
425
std::basic_string<char> spirv;
426
finalize_header_section(spirv);
427
428
// The entry point and execution mode declaration
429
for (const spirv_instruction &inst : _entries.instructions)
430
{
431
assert(inst.op == spv::OpEntryPoint);
432
433
// Only add the matching entry point
434
if (inst.operands[1] == entry_point.id)
435
{
436
write_entry_point(inst, spirv);
437
}
438
else
439
{
440
#if 1
441
functions_to_remove.push_back(inst.operands[1]);
442
#endif
443
// Add interface variables to list of variables to remove
444
for (uint32_t k = 2 + static_cast<uint32_t>((std::strlen(reinterpret_cast<const char *>(&inst.operands[2])) + 4) / 4); k < inst.operands.size(); ++k)
445
variables_to_remove.push_back(inst.operands[k]);
446
}
447
}
448
449
for (const spirv_instruction &inst : _execution_modes.instructions)
450
{
451
assert(inst.op == spv::OpExecutionMode);
452
453
// Only add execution mode for the matching entry point
454
if (inst.operands[0] == entry_point.id)
455
{
456
inst.write(spirv);
457
}
458
}
459
460
finalize_debug_info_section(spirv);
461
462
// All annotation instructions
463
for (spirv_instruction inst : _annotations.instructions)
464
{
465
if (inst.op == spv::OpDecorate)
466
{
467
// Remove all decorations targeting any of the interface variables for non-matching entry points
468
if (std::find(variables_to_remove.begin(), variables_to_remove.end(), inst.operands[0]) != variables_to_remove.end())
469
continue;
470
471
// Replace bindings
472
if (inst.operands[1] == spv::DecorationBinding)
473
{
474
if (const auto referenced_sampler_it = std::find(entry_point.referenced_samplers.begin(), entry_point.referenced_samplers.end(), inst.operands[0]);
475
referenced_sampler_it != entry_point.referenced_samplers.end())
476
inst.operands[2] = static_cast<uint32_t>(std::distance(entry_point.referenced_samplers.begin(), referenced_sampler_it));
477
else
478
if (const auto referenced_storage_it = std::find(entry_point.referenced_storages.begin(), entry_point.referenced_storages.end(), inst.operands[0]);
479
referenced_storage_it != entry_point.referenced_storages.end())
480
inst.operands[2] = static_cast<uint32_t>(std::distance(entry_point.referenced_storages.begin(), referenced_storage_it));
481
}
482
}
483
484
inst.write(spirv);
485
}
486
487
finalize_type_and_constants_section(spirv);
488
489
for (const spirv_instruction &inst : _variables.instructions)
490
{
491
// Remove all declarations of the interface variables for non-matching entry points
492
if (inst.op == spv::OpVariable && std::find(variables_to_remove.begin(), variables_to_remove.end(), inst.result) != variables_to_remove.end())
493
continue;
494
495
inst.write(spirv);
496
}
497
498
// All referenced function definitions
499
for (const function_blocks &func : _functions_blocks)
500
{
501
if (func.definition.instructions.empty())
502
continue;
503
504
const bool has_line = (_debug_info && func.declaration.instructions[0].op == spv::OpLine);
505
assert(func.declaration.instructions[has_line ? 1 : 0].op == spv::OpFunction);
506
const spv::Id definition = func.declaration.instructions[has_line ? 1 : 0].result;
507
508
#if 1
509
if (std::find(functions_to_remove.begin(), functions_to_remove.end(), definition) != functions_to_remove.end())
510
#else
511
if (struct_definition != entry_point.struct_definition &&
512
entry_point.referenced_functions.find(struct_definition) == entry_point.referenced_functions.end())
513
#endif
514
continue;
515
516
for (const spirv_instruction &inst : func.declaration.instructions)
517
inst.write(spirv);
518
519
// Grab first label and move it in front of variable declarations
520
func.definition.instructions.front().write(spirv);
521
assert(func.definition.instructions.front().op == spv::OpLabel);
522
523
for (const spirv_instruction &inst : func.variables.instructions)
524
inst.write(spirv);
525
for (auto inst_it = func.definition.instructions.begin() + 1; inst_it != func.definition.instructions.end(); ++inst_it)
526
inst_it->write(spirv);
527
}
528
529
return spirv;
530
}
531
532
spv::Id convert_type(type info, bool is_ptr = false, spv::StorageClass storage = spv::StorageClassFunction, spv::ImageFormat format = spv::ImageFormatUnknown, uint32_t array_stride = 0)
533
{
534
assert(array_stride == 0 || info.is_array());
535
536
// The storage class is only relevant for pointers, so ignore it for other types during lookup
537
if (is_ptr == false)
538
storage = spv::StorageClassFunction;
539
// There cannot be sampler variables that are local to a function, so always assume uniform storage for them
540
if (info.is_object())
541
storage = spv::StorageClassUniformConstant;
542
else
543
assert(format == spv::ImageFormatUnknown);
544
545
if (info.is_sampler() || info.is_storage())
546
info.rows = info.cols = 1;
547
548
// Fall back to 32-bit types and use relaxed precision decoration instead if 16-bit types are not enabled
549
if (!_enable_16bit_types && info.is_numeric() && info.precision() < 32)
550
info.base = static_cast<type::datatype>(info.base + 1); // min16int -> int, min16uint -> uint, min16float -> float
551
552
const type_lookup lookup { info, is_ptr, array_stride, { storage, format } };
553
554
if (const auto lookup_it = std::find_if(_type_lookup.begin(), _type_lookup.end(),
555
[&lookup](const std::pair<type_lookup, spv::Id> &lookup_entry) { return lookup_entry.first == lookup; });
556
lookup_it != _type_lookup.end())
557
return lookup_it->second;
558
559
spv::Id type_id, elem_type_id;
560
if (is_ptr)
561
{
562
elem_type_id = convert_type(info, false, storage, format, array_stride);
563
type_id =
564
add_instruction(spv::OpTypePointer, 0, _types_and_constants)
565
.add(storage)
566
.add(elem_type_id);
567
}
568
else if (info.is_array())
569
{
570
type elem_info = info;
571
elem_info.array_length = 0;
572
573
elem_type_id = convert_type(elem_info, false, storage, format);
574
575
// Make sure we don't get any dynamic arrays here
576
assert(info.is_bounded_array());
577
578
const spv::Id array_length_id = emit_constant(info.array_length);
579
580
type_id =
581
add_instruction(spv::OpTypeArray, 0, _types_and_constants)
582
.add(elem_type_id)
583
.add(array_length_id);
584
585
if (array_stride != 0)
586
add_decoration(type_id, spv::DecorationArrayStride, { array_stride });
587
}
588
else if (info.is_matrix())
589
{
590
// Convert MxN matrix to a SPIR-V matrix with M vectors with N elements
591
type elem_info = info;
592
elem_info.rows = info.cols;
593
elem_info.cols = 1;
594
595
elem_type_id = convert_type(elem_info, false, storage, format);
596
597
// Matrix types with just one row are interpreted as if they were a vector type
598
if (info.rows == 1)
599
return elem_type_id;
600
601
type_id =
602
add_instruction(spv::OpTypeMatrix, 0, _types_and_constants)
603
.add(elem_type_id)
604
.add(info.rows);
605
}
606
else if (info.is_vector())
607
{
608
type elem_info = info;
609
elem_info.rows = 1;
610
elem_info.cols = 1;
611
612
elem_type_id = convert_type(elem_info, false, storage, format);
613
type_id =
614
add_instruction(spv::OpTypeVector, 0, _types_and_constants)
615
.add(elem_type_id)
616
.add(info.rows);
617
}
618
else
619
{
620
switch (info.base)
621
{
622
case type::t_void:
623
assert(info.rows == 0 && info.cols == 0);
624
type_id = add_instruction(spv::OpTypeVoid, 0, _types_and_constants);
625
break;
626
case type::t_bool:
627
assert(info.rows == 1 && info.cols == 1);
628
type_id = add_instruction(spv::OpTypeBool, 0, _types_and_constants);
629
break;
630
case type::t_min16int:
631
assert(_enable_16bit_types && info.rows == 1 && info.cols == 1);
632
add_capability(spv::CapabilityInt16);
633
if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput)
634
add_capability(spv::CapabilityStorageInputOutput16);
635
type_id =
636
add_instruction(spv::OpTypeInt, 0, _types_and_constants)
637
.add(16) // Width
638
.add(1); // Signedness
639
break;
640
case type::t_int:
641
assert(info.rows == 1 && info.cols == 1);
642
type_id =
643
add_instruction(spv::OpTypeInt, 0, _types_and_constants)
644
.add(32) // Width
645
.add(1); // Signedness
646
break;
647
case type::t_min16uint:
648
assert(_enable_16bit_types && info.rows == 1 && info.cols == 1);
649
add_capability(spv::CapabilityInt16);
650
if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput)
651
add_capability(spv::CapabilityStorageInputOutput16);
652
type_id =
653
add_instruction(spv::OpTypeInt, 0, _types_and_constants)
654
.add(16) // Width
655
.add(0); // Signedness
656
break;
657
case type::t_uint:
658
assert(info.rows == 1 && info.cols == 1);
659
type_id =
660
add_instruction(spv::OpTypeInt, 0, _types_and_constants)
661
.add(32) // Width
662
.add(0); // Signedness
663
break;
664
case type::t_min16float:
665
assert(_enable_16bit_types && info.rows == 1 && info.cols == 1);
666
add_capability(spv::CapabilityFloat16);
667
if (storage == spv::StorageClassInput || storage == spv::StorageClassOutput)
668
add_capability(spv::CapabilityStorageInputOutput16);
669
type_id =
670
add_instruction(spv::OpTypeFloat, 0, _types_and_constants)
671
.add(16); // Width
672
break;
673
case type::t_float:
674
assert(info.rows == 1 && info.cols == 1);
675
type_id =
676
add_instruction(spv::OpTypeFloat, 0, _types_and_constants)
677
.add(32); // Width
678
break;
679
case type::t_struct:
680
assert(info.rows == 0 && info.cols == 0 && info.struct_definition != 0);
681
type_id = info.struct_definition;
682
break;
683
case type::t_sampler1d_int:
684
case type::t_sampler1d_uint:
685
case type::t_sampler1d_float:
686
add_capability(spv::CapabilitySampled1D);
687
[[fallthrough]];
688
case type::t_sampler2d_int:
689
case type::t_sampler2d_uint:
690
case type::t_sampler2d_float:
691
case type::t_sampler3d_int:
692
case type::t_sampler3d_uint:
693
case type::t_sampler3d_float:
694
elem_type_id = convert_image_type(info, format);
695
type_id =
696
add_instruction(spv::OpTypeSampledImage, 0, _types_and_constants)
697
.add(elem_type_id);
698
break;
699
case type::t_storage1d_int:
700
case type::t_storage1d_uint:
701
case type::t_storage1d_float:
702
add_capability(spv::CapabilityImage1D);
703
[[fallthrough]];
704
case type::t_storage2d_int:
705
case type::t_storage2d_uint:
706
case type::t_storage2d_float:
707
case type::t_storage3d_int:
708
case type::t_storage3d_uint:
709
case type::t_storage3d_float:
710
// No format specified for the storage image
711
if (format == spv::ImageFormatUnknown)
712
add_capability(spv::CapabilityStorageImageWriteWithoutFormat);
713
return convert_image_type(info, format);
714
default:
715
assert(false);
716
return 0;
717
}
718
}
719
720
_type_lookup.push_back({ lookup, type_id });
721
722
return type_id;
723
}
724
spv::Id convert_type(const function_blocks &info)
725
{
726
if (const auto lookup_it = std::find_if(_function_type_lookup.begin(), _function_type_lookup.end(),
727
[&lookup = info](const std::pair<function_blocks, spv::Id> &lookup_entry) { return lookup_entry.first == lookup; });
728
lookup_it != _function_type_lookup.end())
729
return lookup_it->second;
730
731
const spv::Id return_type_id = convert_type(info.return_type);
732
assert(return_type_id != 0);
733
734
std::vector<spv::Id> param_type_ids;
735
param_type_ids.reserve(info.param_types.size());
736
for (const type &param_type : info.param_types)
737
param_type_ids.push_back(convert_type(param_type, true));
738
739
spirv_instruction &inst = add_instruction(spv::OpTypeFunction, 0, _types_and_constants)
740
.add(return_type_id)
741
.add(param_type_ids.begin(), param_type_ids.end());
742
743
_function_type_lookup.push_back({ info, inst });
744
745
return inst;
746
}
747
spv::Id convert_image_type(type info, spv::ImageFormat format = spv::ImageFormatUnknown)
748
{
749
type elem_info = info;
750
elem_info.rows = 1;
751
elem_info.cols = 1;
752
753
if (!info.is_numeric())
754
{
755
if ((info.is_integral() && info.is_signed()) || (format >= spv::ImageFormatRgba32i && format <= spv::ImageFormatR8i))
756
elem_info.base = type::t_int;
757
else if ((info.is_integral() && info.is_unsigned()) || (format >= spv::ImageFormatRgba32ui && format <= spv::ImageFormatR8ui))
758
elem_info.base = type::t_uint;
759
else
760
elem_info.base = type::t_float;
761
}
762
763
type_lookup lookup { info, false, 0u, { spv::StorageClassUniformConstant, format } };
764
if (!info.is_storage())
765
{
766
lookup.type = elem_info;
767
lookup.type.base = static_cast<type::datatype>(type::t_texture1d + info.texture_dimension() - 1);
768
lookup.type.struct_definition = static_cast<uint32_t>(elem_info.base);
769
}
770
771
if (const auto lookup_it = std::find_if(_type_lookup.begin(), _type_lookup.end(),
772
[&lookup](const std::pair<type_lookup, spv::Id> &lookup_entry) { return lookup_entry.first == lookup; });
773
lookup_it != _type_lookup.end())
774
return lookup_it->second;
775
776
spv::Id type_id, elem_type_id = convert_type(elem_info, false, spv::StorageClassUniformConstant);
777
type_id =
778
add_instruction(spv::OpTypeImage, 0, _types_and_constants)
779
.add(elem_type_id) // Sampled Type (always a scalar type)
780
.add(spv::Dim1D + info.texture_dimension() - 1)
781
.add(0) // Not a depth image
782
.add(0) // Not an array
783
.add(0) // Not multi-sampled
784
.add(info.is_storage() ? 2 : 1) // Used with a sampler or as storage
785
.add(format);
786
787
_type_lookup.push_back({ lookup, type_id });
788
789
return type_id;
790
}
791
792
uint32_t semantic_to_location(const std::string &semantic, uint32_t max_attributes = 1)
793
{
794
if (const auto it = _semantic_to_location.find(semantic);
795
it != _semantic_to_location.end())
796
return it->second;
797
798
// Extract the semantic index from the semantic name (e.g. 2 for "TEXCOORD2")
799
size_t digit_index = semantic.size() - 1;
800
while (digit_index != 0 && semantic[digit_index] >= '0' && semantic[digit_index] <= '9')
801
digit_index--;
802
digit_index++;
803
804
const std::string semantic_base = semantic.substr(0, digit_index);
805
806
uint32_t semantic_digit = 0;
807
std::from_chars(semantic.c_str() + digit_index, semantic.c_str() + semantic.size(), semantic_digit);
808
809
if (semantic_base == "COLOR" || semantic_base == "SV_TARGET")
810
return semantic_digit;
811
812
uint32_t location = static_cast<uint32_t>(_semantic_to_location.size());
813
814
// Now create adjoining location indices for all possible semantic indices belonging to this semantic name
815
for (uint32_t a = 0; a < semantic_digit + max_attributes; ++a)
816
{
817
const auto insert = _semantic_to_location.emplace(semantic_base + std::to_string(a), location + a);
818
if (!insert.second)
819
{
820
assert(a == 0 || (insert.first->second - a) == location);
821
822
// Semantic was already created with a different location index, so need to remap to that
823
location = insert.first->second - a;
824
}
825
}
826
827
return location + semantic_digit;
828
}
829
830
spv::BuiltIn semantic_to_builtin(const std::string &semantic, shader_type stype) const
831
{
832
if (semantic == "SV_POSITION")
833
return stype == shader_type::pixel ? spv::BuiltInFragCoord : spv::BuiltInPosition;
834
if (semantic == "SV_POINTSIZE")
835
return spv::BuiltInPointSize;
836
if (semantic == "SV_DEPTH")
837
return spv::BuiltInFragDepth;
838
if (semantic == "SV_VERTEXID")
839
return _vulkan_semantics ? spv::BuiltInVertexIndex : spv::BuiltInVertexId;
840
if (semantic == "SV_ISFRONTFACE")
841
return spv::BuiltInFrontFacing;
842
if (semantic == "SV_GROUPID")
843
return spv::BuiltInWorkgroupId;
844
if (semantic == "SV_GROUPINDEX")
845
return spv::BuiltInLocalInvocationIndex;
846
if (semantic == "SV_GROUPTHREADID")
847
return spv::BuiltInLocalInvocationId;
848
if (semantic == "SV_DISPATCHTHREADID")
849
return spv::BuiltInGlobalInvocationId;
850
return spv::BuiltInMax;
851
}
852
spv::ImageFormat format_to_image_format(texture_format format)
853
{
854
switch (format)
855
{
856
default:
857
assert(false);
858
[[fallthrough]];
859
case texture_format::unknown:
860
return spv::ImageFormatUnknown;
861
case texture_format::r8:
862
add_capability(spv::CapabilityStorageImageExtendedFormats);
863
return spv::ImageFormatR8;
864
case texture_format::r16:
865
add_capability(spv::CapabilityStorageImageExtendedFormats);
866
return spv::ImageFormatR16;
867
case texture_format::r16f:
868
add_capability(spv::CapabilityStorageImageExtendedFormats);
869
return spv::ImageFormatR16f;
870
case texture_format::r32i:
871
return spv::ImageFormatR32i;
872
case texture_format::r32u:
873
return spv::ImageFormatR32ui;
874
case texture_format::r32f:
875
return spv::ImageFormatR32f;
876
case texture_format::rg8:
877
add_capability(spv::CapabilityStorageImageExtendedFormats);
878
return spv::ImageFormatRg8;
879
case texture_format::rg16:
880
add_capability(spv::CapabilityStorageImageExtendedFormats);
881
return spv::ImageFormatRg16;
882
case texture_format::rg16f:
883
add_capability(spv::CapabilityStorageImageExtendedFormats);
884
return spv::ImageFormatRg16f;
885
case texture_format::rg32f:
886
add_capability(spv::CapabilityStorageImageExtendedFormats);
887
return spv::ImageFormatRg32f;
888
case texture_format::rgba8:
889
return spv::ImageFormatRgba8;
890
case texture_format::rgba16:
891
add_capability(spv::CapabilityStorageImageExtendedFormats);
892
return spv::ImageFormatRgba16;
893
case texture_format::rgba16f:
894
return spv::ImageFormatRgba16f;
895
case texture_format::rgba32f:
896
return spv::ImageFormatRgba32f;
897
case texture_format::rgb10a2:
898
add_capability(spv::CapabilityStorageImageExtendedFormats);
899
return spv::ImageFormatRgb10A2;
900
}
901
}
902
903
void add_name(id id, const char *name)
904
{
905
if (!_debug_info)
906
return;
907
908
assert(name != nullptr);
909
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpName
910
add_instruction_without_result(spv::OpName, _debug_b)
911
.add(id)
912
.add_string(name);
913
}
914
void add_builtin(id id, spv::BuiltIn builtin)
915
{
916
add_instruction_without_result(spv::OpDecorate, _annotations)
917
.add(id)
918
.add(spv::DecorationBuiltIn)
919
.add(builtin);
920
}
921
void add_decoration(id id, spv::Decoration decoration, std::initializer_list<uint32_t> values = {})
922
{
923
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpDecorate
924
add_instruction_without_result(spv::OpDecorate, _annotations)
925
.add(id)
926
.add(decoration)
927
.add(values.begin(), values.end());
928
}
929
void add_member_name(id id, uint32_t member_index, const char *name)
930
{
931
if (!_debug_info)
932
return;
933
934
assert(name != nullptr);
935
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpMemberName
936
add_instruction_without_result(spv::OpMemberName, _debug_b)
937
.add(id)
938
.add(member_index)
939
.add_string(name);
940
}
941
void add_member_builtin(id id, uint32_t member_index, spv::BuiltIn builtin)
942
{
943
add_instruction_without_result(spv::OpMemberDecorate, _annotations)
944
.add(id)
945
.add(member_index)
946
.add(spv::DecorationBuiltIn)
947
.add(builtin);
948
}
949
void add_member_decoration(id id, uint32_t member_index, spv::Decoration decoration, std::initializer_list<uint32_t> values = {})
950
{
951
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpMemberDecorate
952
add_instruction_without_result(spv::OpMemberDecorate, _annotations)
953
.add(id)
954
.add(member_index)
955
.add(decoration)
956
.add(values.begin(), values.end());
957
}
958
void add_capability(spv::Capability capability)
959
{
960
_capabilities.insert(capability);
961
}
962
963
id define_struct(const location &loc, struct_type &info) override
964
{
965
// First define all member types to make sure they are declared before the struct type references them
966
std::vector<spv::Id> member_types;
967
member_types.reserve(info.member_list.size());
968
for (const member_type &member : info.member_list)
969
member_types.push_back(convert_type(member.type));
970
971
// Afterwards define the actual struct type
972
add_location(loc, _types_and_constants);
973
974
const id res = info.id =
975
add_instruction(spv::OpTypeStruct, 0, _types_and_constants)
976
.add(member_types.begin(), member_types.end());
977
978
if (!info.unique_name.empty())
979
add_name(res, info.unique_name.c_str());
980
981
for (uint32_t index = 0; index < info.member_list.size(); ++index)
982
{
983
const member_type &member = info.member_list[index];
984
985
add_member_name(res, index, member.name.c_str());
986
987
if (!_enable_16bit_types && member.type.is_numeric() && member.type.precision() < 32)
988
add_member_decoration(res, index, spv::DecorationRelaxedPrecision);
989
}
990
991
_structs.push_back(info);
992
993
return res;
994
}
995
id define_texture(const location &, texture &info) override
996
{
997
const id res = info.id = make_id(); // Need to create an unique ID here too, so that the symbol lookup for textures works
998
999
_module.textures.push_back(info);
1000
1001
return res;
1002
}
1003
id define_sampler(const location &loc, const texture &, sampler &info) override
1004
{
1005
const id res = info.id = define_variable(loc, info.type, info.unique_name.c_str(), spv::StorageClassUniformConstant);
1006
1007
// Default to a binding index equivalent to the entry in the sampler list (this is later overwritten in 'finalize_code_for_entry_point' to a more optimal placement)
1008
const uint32_t default_binding = static_cast<uint32_t>(_module.samplers.size());
1009
add_decoration(res, spv::DecorationBinding, { default_binding });
1010
add_decoration(res, spv::DecorationDescriptorSet, { 1 });
1011
1012
_module.samplers.push_back(info);
1013
1014
return res;
1015
}
1016
id define_storage(const location &loc, const texture &tex_info, storage &info) override
1017
{
1018
const id res = info.id = define_variable(loc, info.type, info.unique_name.c_str(), spv::StorageClassUniformConstant, format_to_image_format(tex_info.format));
1019
1020
// Default to a binding index equivalent to the entry in the storage list (this is later overwritten in 'finalize_code_for_entry_point' to a more optimal placement)
1021
const uint32_t default_binding = static_cast<uint32_t>(_module.storages.size());
1022
add_decoration(res, spv::DecorationBinding, { default_binding });
1023
add_decoration(res, spv::DecorationDescriptorSet, { 2 });
1024
1025
_module.storages.push_back(info);
1026
1027
return res;
1028
}
1029
id define_uniform(const location &, uniform &info) override
1030
{
1031
if (_uniforms_to_spec_constants && info.has_initializer_value)
1032
{
1033
const id res = emit_constant(info.type, info.initializer_value, true);
1034
1035
add_name(res, info.name.c_str());
1036
1037
const auto add_spec_constant = [this](const spirv_instruction &inst, const uniform &info, const constant &initializer_value, size_t initializer_offset) {
1038
assert(inst.op == spv::OpSpecConstant || inst.op == spv::OpSpecConstantTrue || inst.op == spv::OpSpecConstantFalse);
1039
1040
const uint32_t spec_id = static_cast<uint32_t>(_module.spec_constants.size());
1041
add_decoration(inst, spv::DecorationSpecId, { spec_id });
1042
1043
uniform scalar_info = info;
1044
scalar_info.type.rows = 1;
1045
scalar_info.type.cols = 1;
1046
scalar_info.size = 4;
1047
scalar_info.offset = static_cast<uint32_t>(initializer_offset);
1048
scalar_info.initializer_value = {};
1049
scalar_info.initializer_value.as_uint[0] = initializer_value.as_uint[initializer_offset];
1050
1051
_module.spec_constants.push_back(std::move(scalar_info));
1052
};
1053
1054
const spirv_instruction &base_inst = _types_and_constants.instructions.back();
1055
assert(base_inst == res);
1056
1057
// External specialization constants need to be scalars
1058
if (info.type.is_scalar())
1059
{
1060
add_spec_constant(base_inst, info, info.initializer_value, 0);
1061
}
1062
else
1063
{
1064
assert(base_inst.op == spv::OpSpecConstantComposite);
1065
1066
// Add each individual scalar component of the constant as a separate external specialization constant
1067
for (size_t i = 0; i < (info.type.is_array() ? base_inst.operands.size() : 1); ++i)
1068
{
1069
constant initializer_value = info.initializer_value;
1070
spirv_instruction elem_inst = base_inst;
1071
1072
if (info.type.is_array())
1073
{
1074
elem_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(),
1075
[operand_id = base_inst.operands[i]](const spirv_instruction &inst) { return inst == operand_id; });
1076
1077
assert(initializer_value.array_data.size() == base_inst.operands.size());
1078
initializer_value = initializer_value.array_data[i];
1079
}
1080
1081
for (size_t row = 0; row < elem_inst.operands.size(); ++row)
1082
{
1083
const spirv_instruction &row_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(),
1084
[operand_id = elem_inst.operands[row]](const spirv_instruction &inst) { return inst == operand_id; });
1085
1086
if (row_inst.op != spv::OpSpecConstantComposite)
1087
{
1088
add_spec_constant(row_inst, info, initializer_value, row);
1089
continue;
1090
}
1091
1092
for (size_t col = 0; col < row_inst.operands.size(); ++col)
1093
{
1094
const spirv_instruction &col_inst = *std::find_if(_types_and_constants.instructions.rbegin(), _types_and_constants.instructions.rend(),
1095
[operand_id = row_inst.operands[col]](const spirv_instruction &inst) { return inst == operand_id; });
1096
1097
add_spec_constant(col_inst, info, initializer_value, row * info.type.cols + col);
1098
}
1099
}
1100
}
1101
}
1102
1103
return res;
1104
}
1105
else
1106
{
1107
// Create global uniform buffer variable on demand
1108
if (_global_ubo_type == 0)
1109
{
1110
_global_ubo_type = make_id();
1111
make_id(); // Pointer type for '_global_ubo_type'
1112
1113
add_decoration(_global_ubo_type, spv::DecorationBlock);
1114
}
1115
if (_global_ubo_variable == 0)
1116
{
1117
_global_ubo_variable = make_id();
1118
1119
add_decoration(_global_ubo_variable, spv::DecorationDescriptorSet, { 0 });
1120
add_decoration(_global_ubo_variable, spv::DecorationBinding, { 0 });
1121
}
1122
1123
uint32_t alignment = (info.type.rows == 3 ? 4 : info.type.rows) * 4;
1124
info.size = info.type.rows * 4;
1125
1126
uint32_t array_stride = 16;
1127
const uint32_t matrix_stride = 16;
1128
1129
if (info.type.is_matrix())
1130
{
1131
alignment = matrix_stride;
1132
info.size = info.type.rows * matrix_stride;
1133
}
1134
if (info.type.is_array())
1135
{
1136
alignment = array_stride;
1137
array_stride = align_up(info.size, array_stride);
1138
// Uniform block rules do not permit anything in the padding of an array
1139
info.size = array_stride * info.type.array_length;
1140
}
1141
1142
info.offset = _module.total_uniform_size;
1143
info.offset = align_up(info.offset, alignment);
1144
_module.total_uniform_size = info.offset + info.size;
1145
1146
type ubo_type = info.type;
1147
// Convert boolean uniform variables to integer type so that they have a defined size
1148
if (info.type.is_boolean())
1149
ubo_type.base = type::t_uint;
1150
1151
const uint32_t member_index = static_cast<uint32_t>(_global_ubo_types.size());
1152
1153
// Composite objects in the uniform storage class must be explicitly laid out, which includes array types requiring a stride decoration
1154
_global_ubo_types.push_back(
1155
convert_type(ubo_type, false, spv::StorageClassUniform, spv::ImageFormatUnknown, info.type.is_array() ? array_stride : 0u));
1156
1157
add_member_name(_global_ubo_type, member_index, info.name.c_str());
1158
1159
add_member_decoration(_global_ubo_type, member_index, spv::DecorationOffset, { info.offset });
1160
1161
if (info.type.is_matrix())
1162
{
1163
// Read matrices in column major layout, even though they are actually row major, to avoid transposing them on every access (since SPIR-V uses column matrices)
1164
// TODO: This technically only works with square matrices
1165
add_member_decoration(_global_ubo_type, member_index, spv::DecorationColMajor);
1166
add_member_decoration(_global_ubo_type, member_index, spv::DecorationMatrixStride, { matrix_stride });
1167
}
1168
1169
_module.uniforms.push_back(info);
1170
1171
return 0xF0000000 | member_index;
1172
}
1173
}
1174
id define_variable(const location &loc, const type &type, std::string name, bool global, id initializer_value) override
1175
{
1176
spv::StorageClass storage = spv::StorageClassFunction;
1177
if (type.has(type::q_groupshared))
1178
storage = spv::StorageClassWorkgroup;
1179
else if (global)
1180
storage = spv::StorageClassPrivate;
1181
1182
return define_variable(loc, type, name.c_str(), storage, spv::ImageFormatUnknown, initializer_value);
1183
}
1184
id define_variable(const location &loc, const type &type, const char *name, spv::StorageClass storage, spv::ImageFormat format = spv::ImageFormatUnknown, id initializer_value = 0)
1185
{
1186
assert(storage != spv::StorageClassFunction || (_current_function_blocks != nullptr && _current_function != nullptr && !_current_function->unique_name.empty() && (_current_function->unique_name[0] == 'F' || _current_function->unique_name[0] == 'E')));
1187
1188
spirv_basic_block &block = (storage != spv::StorageClassFunction) ?
1189
_variables : _current_function_blocks->variables;
1190
1191
add_location(loc, block);
1192
1193
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpVariable
1194
spirv_instruction &inst = add_instruction(spv::OpVariable, convert_type(type, true, storage, format), block);
1195
inst.add(storage);
1196
1197
const id res = inst.result;
1198
1199
if (initializer_value != 0)
1200
{
1201
if (storage != spv::StorageClassFunction || /* is_entry_point = */ _current_function->unique_name[0] == 'E')
1202
{
1203
// The initializer for variables must be a constant
1204
inst.add(initializer_value);
1205
}
1206
else
1207
{
1208
// Only use the variable initializer on global variables, since local variables for e.g. "for" statements need to be assigned in their respective scope and not their declaration
1209
expression variable;
1210
variable.reset_to_lvalue(loc, res, type);
1211
emit_store(variable, initializer_value);
1212
}
1213
}
1214
1215
if (name != nullptr && *name != '\0')
1216
add_name(res, name);
1217
1218
if (!_enable_16bit_types && type.is_numeric() && type.precision() < 32)
1219
add_decoration(res, spv::DecorationRelaxedPrecision);
1220
1221
_storage_lookup[res] = { storage, format };
1222
1223
return res;
1224
}
1225
id define_function(const location &loc, function &info) override
1226
{
1227
assert(!is_in_function());
1228
1229
function_blocks &func = _functions_blocks.emplace_back();
1230
func.return_type = info.return_type;
1231
1232
for (const member_type &param : info.parameter_list)
1233
func.param_types.push_back(param.type);
1234
1235
add_location(loc, func.declaration);
1236
1237
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpFunction
1238
const id res = info.id =
1239
add_instruction(spv::OpFunction, convert_type(info.return_type), func.declaration)
1240
.add(spv::FunctionControlMaskNone)
1241
.add(convert_type(func));
1242
1243
if (!info.name.empty())
1244
add_name(res, info.name.c_str());
1245
1246
for (member_type &param : info.parameter_list)
1247
{
1248
add_location(param.location, func.declaration);
1249
1250
param.id = add_instruction(spv::OpFunctionParameter, convert_type(param.type, true), func.declaration);
1251
1252
add_name(param.id, param.name.c_str());
1253
}
1254
1255
_functions.push_back(std::make_unique<function>(info));
1256
_current_function = _functions.back().get();
1257
_current_function_blocks = &func;
1258
1259
return res;
1260
}
1261
1262
void define_entry_point(function &func) override
1263
{
1264
assert(!func.unique_name.empty() && func.unique_name[0] == 'F');
1265
func.unique_name[0] = 'E';
1266
1267
// Modify entry point name so each thread configuration is made separate
1268
if (func.type == shader_type::compute)
1269
func.unique_name +=
1270
'_' + std::to_string(func.num_threads[0]) +
1271
'_' + std::to_string(func.num_threads[1]) +
1272
'_' + std::to_string(func.num_threads[2]);
1273
1274
if (std::find_if(_module.entry_points.begin(), _module.entry_points.end(),
1275
[&func](const std::pair<std::string, shader_type> &entry_point) {
1276
return entry_point.first == func.unique_name;
1277
}) != _module.entry_points.end())
1278
return;
1279
1280
_module.entry_points.emplace_back(func.unique_name, func.type);
1281
1282
spv::Id position_variable = 0;
1283
spv::Id point_size_variable = 0;
1284
std::vector<spv::Id> inputs_and_outputs;
1285
std::vector<expression> call_params;
1286
1287
// Generate the glue entry point function
1288
function entry_point = func;
1289
entry_point.referenced_functions.push_back(func.id);
1290
1291
// Change function signature to 'void main()'
1292
entry_point.return_type = { type::t_void };
1293
entry_point.return_semantic.clear();
1294
entry_point.parameter_list.clear();
1295
1296
const id entry_point_definition = define_function({}, entry_point);
1297
enter_block(create_block());
1298
1299
const auto create_varying_param = [this, &call_params](const member_type &param) {
1300
// Initialize all output variables with zero
1301
const spv::Id variable = define_variable({}, param.type, nullptr, spv::StorageClassFunction, spv::ImageFormatUnknown, emit_constant(param.type, 0u));
1302
1303
expression &call_param = call_params.emplace_back();
1304
call_param.reset_to_lvalue({}, variable, param.type);
1305
1306
return variable;
1307
};
1308
1309
const auto create_varying_variable = [this, &inputs_and_outputs, &position_variable, &point_size_variable, stype = func.type](const type &param_type, const std::string &semantic, spv::StorageClass storage, int a = 0) {
1310
const spv::Id variable = define_variable({}, param_type, nullptr, storage);
1311
1312
if (const spv::BuiltIn builtin = semantic_to_builtin(semantic, stype);
1313
builtin != spv::BuiltInMax)
1314
{
1315
assert(a == 0); // Built-in variables cannot be arrays
1316
1317
add_builtin(variable, builtin);
1318
1319
if (builtin == spv::BuiltInPosition && storage == spv::StorageClassOutput)
1320
position_variable = variable;
1321
if (builtin == spv::BuiltInPointSize && storage == spv::StorageClassOutput)
1322
point_size_variable = variable;
1323
}
1324
else
1325
{
1326
assert(stype != shader_type::compute); // Compute shaders cannot have custom inputs or outputs
1327
1328
const uint32_t location = semantic_to_location(semantic, std::max(1u, param_type.array_length));
1329
add_decoration(variable, spv::DecorationLocation, { location + a });
1330
}
1331
1332
if (param_type.has(type::q_noperspective))
1333
add_decoration(variable, spv::DecorationNoPerspective);
1334
if (param_type.has(type::q_centroid))
1335
add_decoration(variable, spv::DecorationCentroid);
1336
if (param_type.has(type::q_nointerpolation))
1337
add_decoration(variable, spv::DecorationFlat);
1338
1339
inputs_and_outputs.push_back(variable);
1340
return variable;
1341
};
1342
1343
// Translate function parameters to input/output variables
1344
for (const member_type &param : func.parameter_list)
1345
{
1346
spv::Id param_var = create_varying_param(param);
1347
1348
// Create separate input/output variables for "inout" parameters
1349
if (param.type.has(type::q_in))
1350
{
1351
spv::Id param_value = 0;
1352
1353
// Flatten structure parameters
1354
if (param.type.is_struct())
1355
{
1356
const struct_type &struct_definition = get_struct(param.type.struct_definition);
1357
1358
type struct_type = param.type;
1359
const auto array_length = std::max(1u, param.type.array_length);
1360
struct_type.array_length = 0;
1361
1362
// Struct arrays need to be flattened into individual elements as well
1363
std::vector<spv::Id> array_element_ids;
1364
array_element_ids.reserve(array_length);
1365
for (unsigned int a = 0; a < array_length; a++)
1366
{
1367
std::vector<spv::Id> struct_element_ids;
1368
struct_element_ids.reserve(struct_definition.member_list.size());
1369
for (const member_type &member : struct_definition.member_list)
1370
{
1371
const spv::Id input_var = create_varying_variable(member.type, member.semantic, spv::StorageClassInput, a);
1372
1373
param_value =
1374
add_instruction(spv::OpLoad, convert_type(member.type))
1375
.add(input_var);
1376
struct_element_ids.push_back(param_value);
1377
}
1378
1379
param_value =
1380
add_instruction(spv::OpCompositeConstruct, convert_type(struct_type))
1381
.add(struct_element_ids.begin(), struct_element_ids.end());
1382
array_element_ids.push_back(param_value);
1383
}
1384
1385
if (param.type.is_array())
1386
{
1387
// Build the array from all constructed struct elements
1388
param_value =
1389
add_instruction(spv::OpCompositeConstruct, convert_type(param.type))
1390
.add(array_element_ids.begin(), array_element_ids.end());
1391
}
1392
}
1393
else
1394
{
1395
const spv::Id input_var = create_varying_variable(param.type, param.semantic, spv::StorageClassInput);
1396
1397
param_value =
1398
add_instruction(spv::OpLoad, convert_type(param.type))
1399
.add(input_var);
1400
}
1401
1402
add_instruction_without_result(spv::OpStore)
1403
.add(param_var)
1404
.add(param_value);
1405
}
1406
1407
if (param.type.has(type::q_out))
1408
{
1409
if (param.type.is_struct())
1410
{
1411
const struct_type &struct_definition = get_struct(param.type.struct_definition);
1412
1413
for (unsigned int a = 0, array_length = std::max(1u, param.type.array_length); a < array_length; a++)
1414
{
1415
for (const member_type &member : struct_definition.member_list)
1416
{
1417
create_varying_variable(member.type, member.semantic, spv::StorageClassOutput, a);
1418
}
1419
}
1420
}
1421
else
1422
{
1423
create_varying_variable(param.type, param.semantic, spv::StorageClassOutput);
1424
}
1425
}
1426
}
1427
1428
const id call_result = emit_call({}, func.id, func.return_type, call_params);
1429
1430
for (size_t i = 0, inputs_and_outputs_index = 0; i < func.parameter_list.size(); ++i)
1431
{
1432
const member_type &param = func.parameter_list[i];
1433
1434
if (param.type.has(type::q_out))
1435
{
1436
const spv::Id value =
1437
add_instruction(spv::OpLoad, convert_type(param.type))
1438
.add(call_params[i].base);
1439
1440
if (param.type.is_struct())
1441
{
1442
const struct_type &struct_definition = get_struct(param.type.struct_definition);
1443
1444
type struct_type = param.type;
1445
const auto array_length = std::max(1u, param.type.array_length);
1446
struct_type.array_length = 0;
1447
1448
// Skip input variables if this is an "inout" parameter
1449
if (param.type.has(type::q_in))
1450
inputs_and_outputs_index += struct_definition.member_list.size() * array_length;
1451
1452
// Split up struct array into individual struct elements again
1453
for (unsigned int a = 0; a < array_length; a++)
1454
{
1455
spv::Id element_value = value;
1456
if (param.type.is_array())
1457
{
1458
element_value =
1459
add_instruction(spv::OpCompositeExtract, convert_type(struct_type))
1460
.add(value)
1461
.add(a);
1462
}
1463
1464
// Split out struct fields into separate output variables again
1465
for (uint32_t member_index = 0; member_index < struct_definition.member_list.size(); ++member_index)
1466
{
1467
const spv::Id member_value =
1468
add_instruction(spv::OpCompositeExtract, convert_type(struct_definition.member_list[member_index].type))
1469
.add(element_value)
1470
.add(member_index);
1471
1472
add_instruction_without_result(spv::OpStore)
1473
.add(inputs_and_outputs[inputs_and_outputs_index++])
1474
.add(member_value);
1475
}
1476
}
1477
}
1478
else
1479
{
1480
// Skip input variable if this is an "inout" parameter (see loop above)
1481
if (param.type.has(type::q_in))
1482
inputs_and_outputs_index += 1;
1483
1484
add_instruction_without_result(spv::OpStore)
1485
.add(inputs_and_outputs[inputs_and_outputs_index++])
1486
.add(value);
1487
}
1488
}
1489
else
1490
{
1491
// Input parameters do not need to store anything, but increase the input/output variable index
1492
if (param.type.is_struct())
1493
{
1494
const struct_type &struct_definition = get_struct(param.type.struct_definition);
1495
inputs_and_outputs_index += struct_definition.member_list.size() * std::max(1u, param.type.array_length);
1496
}
1497
else
1498
{
1499
inputs_and_outputs_index += 1;
1500
}
1501
}
1502
}
1503
1504
if (func.return_type.is_struct())
1505
{
1506
const struct_type &struct_definition = get_struct(func.return_type.struct_definition);
1507
1508
for (uint32_t member_index = 0; member_index < struct_definition.member_list.size(); ++member_index)
1509
{
1510
const member_type &member = struct_definition.member_list[member_index];
1511
1512
const spv::Id result_var = create_varying_variable(member.type, member.semantic, spv::StorageClassOutput);
1513
1514
const spv::Id member_result =
1515
add_instruction(spv::OpCompositeExtract, convert_type(member.type))
1516
.add(call_result)
1517
.add(member_index);
1518
1519
add_instruction_without_result(spv::OpStore)
1520
.add(result_var)
1521
.add(member_result);
1522
}
1523
}
1524
else if (!func.return_type.is_void())
1525
{
1526
const spv::Id result_var = create_varying_variable(func.return_type, func.return_semantic, spv::StorageClassOutput);
1527
1528
add_instruction_without_result(spv::OpStore)
1529
.add(result_var)
1530
.add(call_result);
1531
}
1532
1533
// Add code to flip the output vertically
1534
if (_flip_vert_y && position_variable != 0 && func.type == shader_type::vertex)
1535
{
1536
expression position;
1537
position.reset_to_lvalue({}, position_variable, { type::t_float, 4, 1 });
1538
position.add_constant_index_access(1); // Y component
1539
1540
// gl_Position.y = -gl_Position.y
1541
emit_store(position,
1542
emit_unary_op({}, tokenid::minus, { type::t_float, 1, 1 },
1543
emit_load(position, false)));
1544
}
1545
1546
#if 0
1547
// Disabled because it breaks on MacOS/Metal - point size should not be defined for a non-point primitive.
1548
// Add code that sets the point size to a default value (in case this vertex shader is used with point primitives)
1549
if (point_size_variable == 0 && func.type == shader_type::vertex)
1550
{
1551
create_varying_variable({ type::t_float, 1, 1 }, "SV_POINTSIZE", spv::StorageClassOutput);
1552
1553
expression point_size;
1554
point_size.reset_to_lvalue({}, point_size_variable, { type::t_float, 1, 1 });
1555
1556
// gl_PointSize = 1.0
1557
emit_store(point_size, emit_constant({ type::t_float, 1, 1 }, 1));
1558
}
1559
#endif
1560
1561
leave_block_and_return(0);
1562
leave_function();
1563
1564
spv::ExecutionModel model;
1565
switch (func.type)
1566
{
1567
case shader_type::vertex:
1568
model = spv::ExecutionModelVertex;
1569
break;
1570
case shader_type::pixel:
1571
model = spv::ExecutionModelFragment;
1572
add_instruction_without_result(spv::OpExecutionMode, _execution_modes)
1573
.add(entry_point_definition)
1574
.add(_vulkan_semantics ? spv::ExecutionModeOriginUpperLeft : spv::ExecutionModeOriginLowerLeft);
1575
break;
1576
case shader_type::compute:
1577
model = spv::ExecutionModelGLCompute;
1578
add_instruction_without_result(spv::OpExecutionMode, _execution_modes)
1579
.add(entry_point_definition)
1580
.add(spv::ExecutionModeLocalSize)
1581
.add(func.num_threads[0])
1582
.add(func.num_threads[1])
1583
.add(func.num_threads[2]);
1584
break;
1585
default:
1586
assert(false);
1587
return;
1588
}
1589
1590
add_instruction_without_result(spv::OpEntryPoint, _entries)
1591
.add(model)
1592
.add(entry_point_definition)
1593
.add_string(func.unique_name.c_str())
1594
.add(inputs_and_outputs.begin(), inputs_and_outputs.end());
1595
}
1596
1597
id emit_load(const expression &exp, bool) override
1598
{
1599
if (exp.is_constant) // Constant expressions do not have a complex access chain
1600
return emit_constant(exp.type, exp.constant);
1601
1602
size_t i = 0;
1603
spv::Id result = exp.base;
1604
type base_type = exp.type;
1605
bool is_uniform_bool = false;
1606
1607
if (exp.is_lvalue || !exp.chain.empty())
1608
add_location(exp.location, *_current_block_data);
1609
1610
// If a variable is referenced, load the value first
1611
if (exp.is_lvalue && _spec_constants.find(exp.base) == _spec_constants.end())
1612
{
1613
if (!exp.chain.empty())
1614
base_type = exp.chain[0].from;
1615
1616
std::pair<spv::StorageClass, spv::ImageFormat> storage = { spv::StorageClassFunction, spv::ImageFormatUnknown };
1617
if (const auto it = _storage_lookup.find(exp.base);
1618
it != _storage_lookup.end())
1619
storage = it->second;
1620
1621
spirv_instruction *access_chain = nullptr;
1622
1623
// Check if this is a uniform variable (see 'define_uniform' function above) and dereference it
1624
if (result & 0xF0000000)
1625
{
1626
const uint32_t member_index = result ^ 0xF0000000;
1627
1628
storage.first = spv::StorageClassUniform;
1629
is_uniform_bool = base_type.is_boolean();
1630
1631
if (is_uniform_bool)
1632
base_type.base = type::t_uint;
1633
1634
access_chain = &add_instruction(spv::OpAccessChain)
1635
.add(_global_ubo_variable)
1636
.add(emit_constant(member_index));
1637
}
1638
1639
// Any indexing expressions can be resolved during load with an 'OpAccessChain' already
1640
if (!exp.chain.empty() && (
1641
exp.chain[0].op == expression::operation::op_member ||
1642
exp.chain[0].op == expression::operation::op_dynamic_index ||
1643
exp.chain[0].op == expression::operation::op_constant_index))
1644
{
1645
// Ensure that 'access_chain' cannot get invalidated by calls to 'emit_constant' or 'convert_type'
1646
assert(_current_block_data != &_types_and_constants);
1647
1648
// Use access chain from uniform if possible, otherwise create new one
1649
if (access_chain == nullptr) access_chain =
1650
&add_instruction(spv::OpAccessChain).add(result); // Base
1651
1652
// Ignore first index into 1xN matrices, since they were translated to a vector type in SPIR-V
1653
if (exp.chain[0].from.rows == 1 && exp.chain[0].from.cols > 1)
1654
i = 1;
1655
1656
for (; i < exp.chain.size() && (
1657
exp.chain[i].op == expression::operation::op_member ||
1658
exp.chain[i].op == expression::operation::op_dynamic_index ||
1659
exp.chain[i].op == expression::operation::op_constant_index); ++i)
1660
access_chain->add(exp.chain[i].op == expression::operation::op_dynamic_index ?
1661
exp.chain[i].index :
1662
emit_constant(exp.chain[i].index)); // Indexes
1663
1664
base_type = exp.chain[i - 1].to;
1665
access_chain->type = convert_type(base_type, true, storage.first, storage.second); // Last type is the result
1666
result = access_chain->result;
1667
}
1668
else if (access_chain != nullptr)
1669
{
1670
access_chain->type = convert_type(base_type, true, storage.first, storage.second, base_type.is_array() ? 16u : 0u);
1671
result = access_chain->result;
1672
}
1673
1674
result =
1675
add_instruction(spv::OpLoad, convert_type(base_type, false, spv::StorageClassFunction, storage.second))
1676
.add(result); // Pointer
1677
}
1678
1679
// Need to convert boolean uniforms which are actually integers in SPIR-V
1680
if (is_uniform_bool)
1681
{
1682
base_type.base = type::t_bool;
1683
1684
result =
1685
add_instruction(spv::OpINotEqual, convert_type(base_type))
1686
.add(result)
1687
.add(emit_constant(0));
1688
}
1689
1690
// Work through all remaining operations in the access chain and apply them to the value
1691
for (; i < exp.chain.size(); ++i)
1692
{
1693
assert(result != 0);
1694
const expression::operation &op = exp.chain[i];
1695
1696
switch (op.op)
1697
{
1698
case expression::operation::op_cast:
1699
if (op.from.is_scalar() && !op.to.is_scalar())
1700
{
1701
type cast_type = op.to;
1702
cast_type.base = op.from.base;
1703
1704
std::vector<expression> args;
1705
args.reserve(op.to.components());
1706
for (unsigned int c = 0; c < op.to.components(); ++c)
1707
args.emplace_back().reset_to_rvalue(exp.location, result, op.from);
1708
1709
result = emit_construct(exp.location, cast_type, args);
1710
}
1711
1712
if (op.from.is_boolean())
1713
{
1714
const spv::Id true_constant = emit_constant(op.to, 1);
1715
const spv::Id false_constant = emit_constant(op.to, 0);
1716
1717
result =
1718
add_instruction(spv::OpSelect, convert_type(op.to))
1719
.add(result) // Condition
1720
.add(true_constant)
1721
.add(false_constant);
1722
}
1723
else
1724
{
1725
spv::Op spv_op = spv::OpNop;
1726
switch (op.to.base)
1727
{
1728
case type::t_bool:
1729
if (op.from.is_floating_point())
1730
spv_op = spv::OpFOrdNotEqual;
1731
else
1732
spv_op = spv::OpINotEqual;
1733
// Add instruction to compare value against zero instead of casting
1734
result =
1735
add_instruction(spv_op, convert_type(op.to))
1736
.add(result)
1737
.add(emit_constant(op.from, 0));
1738
continue;
1739
case type::t_min16int:
1740
case type::t_int:
1741
if (op.from.is_floating_point())
1742
spv_op = spv::OpConvertFToS;
1743
else if (op.from.precision() == op.to.precision())
1744
spv_op = spv::OpBitcast;
1745
else if (_enable_16bit_types)
1746
spv_op = spv::OpSConvert;
1747
else
1748
continue; // Do not have to add conversion instruction between min16int/int if 16-bit types are not enabled
1749
break;
1750
case type::t_min16uint:
1751
case type::t_uint:
1752
if (op.from.is_floating_point())
1753
spv_op = spv::OpConvertFToU;
1754
else if (op.from.precision() == op.to.precision())
1755
spv_op = spv::OpBitcast;
1756
else if (_enable_16bit_types)
1757
spv_op = spv::OpUConvert;
1758
else
1759
continue;
1760
break;
1761
case type::t_min16float:
1762
case type::t_float:
1763
if (op.from.is_floating_point() && !_enable_16bit_types)
1764
continue; // Do not have to add conversion instruction between min16float/float if 16-bit types are not enabled
1765
else if (op.from.is_floating_point())
1766
spv_op = spv::OpFConvert;
1767
else if (op.from.is_signed())
1768
spv_op = spv::OpConvertSToF;
1769
else
1770
spv_op = spv::OpConvertUToF;
1771
break;
1772
default:
1773
assert(false);
1774
}
1775
1776
result =
1777
add_instruction(spv_op, convert_type(op.to))
1778
.add(result);
1779
}
1780
break;
1781
case expression::operation::op_dynamic_index:
1782
assert(op.from.is_vector() && op.to.is_scalar());
1783
result =
1784
add_instruction(spv::OpVectorExtractDynamic, convert_type(op.to))
1785
.add(result) // Vector
1786
.add(op.index); // Index
1787
break;
1788
case expression::operation::op_member: // In case of struct return values, which are r-values
1789
case expression::operation::op_constant_index:
1790
assert(op.from.is_vector() || op.from.is_matrix() || op.from.is_struct());
1791
result =
1792
add_instruction(spv::OpCompositeExtract, convert_type(op.to))
1793
.add(result)
1794
.add(op.index); // Literal Index
1795
break;
1796
case expression::operation::op_swizzle:
1797
if (op.to.is_vector())
1798
{
1799
if (op.from.is_matrix())
1800
{
1801
spv::Id components[4];
1802
for (int c = 0; c < 4 && op.swizzle[c] >= 0; ++c)
1803
{
1804
const unsigned int row = op.swizzle[c] / 4;
1805
const unsigned int column = op.swizzle[c] - row * 4;
1806
1807
type scalar_type = op.to;
1808
scalar_type.rows = 1;
1809
scalar_type.cols = 1;
1810
1811
spirv_instruction &inst = add_instruction(spv::OpCompositeExtract, convert_type(scalar_type));
1812
inst.add(result);
1813
if (op.from.rows > 1) // Matrix types with a single row are actually vectors, so they don't need the extra index
1814
inst.add(row);
1815
inst.add(column);
1816
1817
components[c] = inst;
1818
}
1819
1820
spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(op.to));
1821
for (int c = 0; c < 4 && op.swizzle[c] >= 0; ++c)
1822
inst.add(components[c]);
1823
result = inst;
1824
}
1825
else if (op.from.is_vector())
1826
{
1827
spirv_instruction &inst = add_instruction(spv::OpVectorShuffle, convert_type(op.to));
1828
inst.add(result); // Vector 1
1829
inst.add(result); // Vector 2
1830
for (int c = 0; c < 4 && op.swizzle[c] >= 0; ++c)
1831
inst.add(op.swizzle[c]);
1832
result = inst;
1833
}
1834
else
1835
{
1836
spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(op.to));
1837
for (unsigned int c = 0; c < op.to.rows; ++c)
1838
inst.add(result);
1839
result = inst;
1840
}
1841
break;
1842
}
1843
else if (op.from.is_matrix() && op.to.is_scalar())
1844
{
1845
assert(op.swizzle[1] < 0);
1846
1847
spirv_instruction &inst = add_instruction(spv::OpCompositeExtract, convert_type(op.to));
1848
inst.add(result); // Composite
1849
if (op.from.rows > 1)
1850
{
1851
const unsigned int row = op.swizzle[0] / 4;
1852
const unsigned int column = op.swizzle[0] - row * 4;
1853
inst.add(row);
1854
inst.add(column);
1855
}
1856
else
1857
{
1858
inst.add(op.swizzle[0]);
1859
}
1860
result = inst;
1861
break;
1862
}
1863
else
1864
{
1865
assert(false);
1866
break;
1867
}
1868
}
1869
}
1870
1871
return result;
1872
}
1873
void emit_store(const expression &exp, id value) override
1874
{
1875
assert(value != 0 && exp.is_lvalue && !exp.is_constant && !exp.type.is_sampler());
1876
1877
add_location(exp.location, *_current_block_data);
1878
1879
size_t i = 0;
1880
// Any indexing expressions can be resolved with an 'OpAccessChain' already
1881
spv::Id target = emit_access_chain(exp, i);
1882
type base_type = exp.chain.empty() ? exp.type : i == 0 ? exp.chain[0].from : exp.chain[i - 1].to;
1883
1884
// TODO: Complex access chains like float4x4[0].m00m10[0] = 0;
1885
// Work through all remaining operations in the access chain and apply them to the value
1886
for (; i < exp.chain.size(); ++i)
1887
{
1888
const expression::operation &op = exp.chain[i];
1889
switch (op.op)
1890
{
1891
case expression::operation::op_cast:
1892
case expression::operation::op_member:
1893
// These should have been handled above already (and casting does not make sense for a store operation)
1894
break;
1895
case expression::operation::op_dynamic_index:
1896
case expression::operation::op_constant_index:
1897
assert(false);
1898
break;
1899
case expression::operation::op_swizzle:
1900
{
1901
spv::Id result =
1902
add_instruction(spv::OpLoad, convert_type(base_type))
1903
.add(target); // Pointer
1904
1905
if (base_type.is_vector())
1906
{
1907
spirv_instruction &inst = add_instruction(spv::OpVectorShuffle, convert_type(base_type));
1908
inst.add(result); // Vector 1
1909
inst.add(value); // Vector 2
1910
1911
unsigned int shuffle[4] = { 0, 1, 2, 3 };
1912
for (unsigned int c = 0; c < base_type.rows; ++c)
1913
if (op.swizzle[c] >= 0)
1914
shuffle[op.swizzle[c]] = base_type.rows + c;
1915
for (unsigned int c = 0; c < base_type.rows; ++c)
1916
inst.add(shuffle[c]);
1917
1918
value = inst;
1919
}
1920
else if (op.to.is_scalar())
1921
{
1922
assert(op.swizzle[1] < 0);
1923
1924
spirv_instruction &inst = add_instruction(spv::OpCompositeInsert, convert_type(base_type));
1925
inst.add(value); // Object
1926
inst.add(result); // Composite
1927
1928
if (op.from.is_matrix() && op.from.rows > 1)
1929
{
1930
const unsigned int row = op.swizzle[0] / 4;
1931
const unsigned int column = op.swizzle[0] - row * 4;
1932
inst.add(row);
1933
inst.add(column);
1934
}
1935
else
1936
{
1937
inst.add(op.swizzle[0]);
1938
}
1939
1940
value = inst;
1941
}
1942
else
1943
{
1944
// TODO: Implement matrix to vector swizzles
1945
assert(false);
1946
}
1947
break;
1948
}
1949
}
1950
}
1951
1952
add_instruction_without_result(spv::OpStore)
1953
.add(target)
1954
.add(value);
1955
}
1956
id emit_access_chain(const expression &exp, size_t &i) override
1957
{
1958
// This function cannot create access chains for uniform variables
1959
assert((exp.base & 0xF0000000) == 0);
1960
1961
i = 0;
1962
if (exp.chain.empty() || (
1963
exp.chain[0].op != expression::operation::op_member &&
1964
exp.chain[0].op != expression::operation::op_dynamic_index &&
1965
exp.chain[0].op != expression::operation::op_constant_index))
1966
return exp.base;
1967
1968
std::pair<spv::StorageClass, spv::ImageFormat> storage = { spv::StorageClassFunction, spv::ImageFormatUnknown };
1969
if (const auto it = _storage_lookup.find(exp.base);
1970
it != _storage_lookup.end())
1971
storage = it->second;
1972
1973
// Ensure that 'access_chain' cannot get invalidated by calls to 'emit_constant' or 'convert_type'
1974
assert(_current_block_data != &_types_and_constants);
1975
1976
spirv_instruction *access_chain =
1977
&add_instruction(spv::OpAccessChain).add(exp.base); // Base
1978
1979
// Ignore first index into 1xN matrices, since they were translated to a vector type in SPIR-V
1980
if (exp.chain[0].from.rows == 1 && exp.chain[0].from.cols > 1)
1981
i = 1;
1982
1983
for (; i < exp.chain.size() && (
1984
exp.chain[i].op == expression::operation::op_member ||
1985
exp.chain[i].op == expression::operation::op_dynamic_index ||
1986
exp.chain[i].op == expression::operation::op_constant_index); ++i)
1987
access_chain->add(exp.chain[i].op == expression::operation::op_dynamic_index ?
1988
exp.chain[i].index :
1989
emit_constant(exp.chain[i].index)); // Indexes
1990
1991
access_chain->type = convert_type(exp.chain[i - 1].to, true, storage.first, storage.second); // Last type is the result
1992
return access_chain->result;
1993
}
1994
1995
using codegen::emit_constant;
1996
id emit_constant(uint32_t value)
1997
{
1998
return emit_constant({ type::t_uint, 1, 1 }, value);
1999
}
2000
id emit_constant(const type &data_type, const constant &data) override
2001
{
2002
return emit_constant(data_type, data, false);
2003
}
2004
id emit_constant(const type &data_type, const constant &data, bool spec_constant)
2005
{
2006
if (!spec_constant) // Specialization constants cannot reuse other constants
2007
{
2008
if (const auto it = std::find_if(_constant_lookup.begin(), _constant_lookup.end(),
2009
[&data_type, &data](std::tuple<type, constant, spv::Id> &x) {
2010
if (!(std::get<0>(x) == data_type && std::memcmp(&std::get<1>(x).as_uint[0], &data.as_uint[0], sizeof(uint32_t) * 16) == 0 && std::get<1>(x).array_data.size() == data.array_data.size()))
2011
return false;
2012
for (size_t i = 0; i < data.array_data.size(); ++i)
2013
if (std::memcmp(&std::get<1>(x).array_data[i].as_uint[0], &data.array_data[i].as_uint[0], sizeof(uint32_t) * 16) != 0)
2014
return false;
2015
return true;
2016
});
2017
it != _constant_lookup.end())
2018
return std::get<2>(*it); // Reuse existing constant instead of duplicating the definition
2019
}
2020
2021
spv::Id result;
2022
if (data_type.is_array())
2023
{
2024
assert(data_type.is_bounded_array()); // Unbounded arrays cannot be constants
2025
2026
type elem_type = data_type;
2027
elem_type.array_length = 0;
2028
2029
std::vector<spv::Id> elements;
2030
elements.reserve(data_type.array_length);
2031
2032
// Fill up elements with constant array data
2033
for (const constant &elem : data.array_data)
2034
elements.push_back(emit_constant(elem_type, elem, spec_constant));
2035
// Fill up any remaining elements with a default value (when the array data did not specify them)
2036
for (size_t i = elements.size(); i < static_cast<size_t>(data_type.array_length); ++i)
2037
elements.push_back(emit_constant(elem_type, {}, spec_constant));
2038
2039
result =
2040
add_instruction(spec_constant ? spv::OpSpecConstantComposite : spv::OpConstantComposite, convert_type(data_type), _types_and_constants)
2041
.add(elements.begin(), elements.end());
2042
}
2043
else if (data_type.is_struct())
2044
{
2045
assert(!spec_constant); // Structures cannot be specialization constants
2046
2047
result = add_instruction(spv::OpConstantNull, convert_type(data_type), _types_and_constants);
2048
}
2049
else if (data_type.is_vector() || data_type.is_matrix())
2050
{
2051
type elem_type = data_type;
2052
elem_type.rows = data_type.cols;
2053
elem_type.cols = 1;
2054
2055
spv::Id rows[4] = {};
2056
2057
// Construct matrix constant out of row vector constants
2058
// Construct vector constant out of scalar constants for each element
2059
for (unsigned int i = 0; i < data_type.rows; ++i)
2060
{
2061
constant row_data = {};
2062
for (unsigned int k = 0; k < data_type.cols; ++k)
2063
row_data.as_uint[k] = data.as_uint[i * data_type.cols + k];
2064
2065
rows[i] = emit_constant(elem_type, row_data, spec_constant);
2066
}
2067
2068
if (data_type.rows == 1)
2069
{
2070
result = rows[0];
2071
}
2072
else
2073
{
2074
spirv_instruction &inst = add_instruction(spec_constant ? spv::OpSpecConstantComposite : spv::OpConstantComposite, convert_type(data_type), _types_and_constants);
2075
for (unsigned int i = 0; i < data_type.rows; ++i)
2076
inst.add(rows[i]);
2077
result = inst;
2078
}
2079
}
2080
else if (data_type.is_boolean())
2081
{
2082
result = add_instruction(data.as_uint[0] ?
2083
(spec_constant ? spv::OpSpecConstantTrue : spv::OpConstantTrue) :
2084
(spec_constant ? spv::OpSpecConstantFalse : spv::OpConstantFalse), convert_type(data_type), _types_and_constants);
2085
}
2086
else
2087
{
2088
assert(data_type.is_scalar());
2089
2090
result =
2091
add_instruction(spec_constant ? spv::OpSpecConstant : spv::OpConstant, convert_type(data_type), _types_and_constants)
2092
.add(data.as_uint[0]);
2093
}
2094
2095
if (spec_constant) // Keep track of all specialization constants
2096
_spec_constants.insert(result);
2097
else
2098
_constant_lookup.push_back({ data_type, data, result });
2099
2100
return result;
2101
}
2102
2103
id emit_unary_op(const location &loc, tokenid op, const type &res_type, id val) override
2104
{
2105
spv::Op spv_op = spv::OpNop;
2106
2107
switch (op)
2108
{
2109
case tokenid::minus:
2110
spv_op = res_type.is_floating_point() ? spv::OpFNegate : spv::OpSNegate;
2111
break;
2112
case tokenid::tilde:
2113
spv_op = spv::OpNot;
2114
break;
2115
case tokenid::exclaim:
2116
spv_op = spv::OpLogicalNot;
2117
break;
2118
default:
2119
return assert(false), 0;
2120
}
2121
2122
add_location(loc, *_current_block_data);
2123
2124
spirv_instruction &inst = add_instruction(spv_op, convert_type(res_type));
2125
inst.add(val); // Operand
2126
2127
return inst;
2128
}
2129
id emit_binary_op(const location &loc, tokenid op, const type &res_type, const type &exp_type, id lhs, id rhs) override
2130
{
2131
spv::Op spv_op = spv::OpNop;
2132
2133
switch (op)
2134
{
2135
case tokenid::plus:
2136
case tokenid::plus_plus:
2137
case tokenid::plus_equal:
2138
spv_op = exp_type.is_floating_point() ? spv::OpFAdd : spv::OpIAdd;
2139
break;
2140
case tokenid::minus:
2141
case tokenid::minus_minus:
2142
case tokenid::minus_equal:
2143
spv_op = exp_type.is_floating_point() ? spv::OpFSub : spv::OpISub;
2144
break;
2145
case tokenid::star:
2146
case tokenid::star_equal:
2147
spv_op = exp_type.is_floating_point() ? spv::OpFMul : spv::OpIMul;
2148
break;
2149
case tokenid::slash:
2150
case tokenid::slash_equal:
2151
spv_op = exp_type.is_floating_point() ? spv::OpFDiv : exp_type.is_signed() ? spv::OpSDiv : spv::OpUDiv;
2152
break;
2153
case tokenid::percent:
2154
case tokenid::percent_equal:
2155
spv_op = exp_type.is_floating_point() ? spv::OpFRem : exp_type.is_signed() ? spv::OpSRem : spv::OpUMod;
2156
break;
2157
case tokenid::caret:
2158
case tokenid::caret_equal:
2159
spv_op = spv::OpBitwiseXor;
2160
break;
2161
case tokenid::pipe:
2162
case tokenid::pipe_equal:
2163
spv_op = spv::OpBitwiseOr;
2164
break;
2165
case tokenid::ampersand:
2166
case tokenid::ampersand_equal:
2167
spv_op = spv::OpBitwiseAnd;
2168
break;
2169
case tokenid::less_less:
2170
case tokenid::less_less_equal:
2171
spv_op = spv::OpShiftLeftLogical;
2172
break;
2173
case tokenid::greater_greater:
2174
case tokenid::greater_greater_equal:
2175
spv_op = exp_type.is_signed() ? spv::OpShiftRightArithmetic : spv::OpShiftRightLogical;
2176
break;
2177
case tokenid::pipe_pipe:
2178
spv_op = spv::OpLogicalOr;
2179
break;
2180
case tokenid::ampersand_ampersand:
2181
spv_op = spv::OpLogicalAnd;
2182
break;
2183
case tokenid::less:
2184
spv_op = exp_type.is_floating_point() ? spv::OpFOrdLessThan :
2185
exp_type.is_signed() ? spv::OpSLessThan : spv::OpULessThan;
2186
break;
2187
case tokenid::less_equal:
2188
spv_op = exp_type.is_floating_point() ? spv::OpFOrdLessThanEqual :
2189
exp_type.is_signed() ? spv::OpSLessThanEqual : spv::OpULessThanEqual;
2190
break;
2191
case tokenid::greater:
2192
spv_op = exp_type.is_floating_point() ? spv::OpFOrdGreaterThan :
2193
exp_type.is_signed() ? spv::OpSGreaterThan : spv::OpUGreaterThan;
2194
break;
2195
case tokenid::greater_equal:
2196
spv_op = exp_type.is_floating_point() ? spv::OpFOrdGreaterThanEqual :
2197
exp_type.is_signed() ? spv::OpSGreaterThanEqual : spv::OpUGreaterThanEqual;
2198
break;
2199
case tokenid::equal_equal:
2200
spv_op = exp_type.is_floating_point() ? spv::OpFOrdEqual :
2201
exp_type.is_boolean() ? spv::OpLogicalEqual : spv::OpIEqual;
2202
break;
2203
case tokenid::exclaim_equal:
2204
spv_op = exp_type.is_floating_point() ? spv::OpFOrdNotEqual :
2205
exp_type.is_boolean() ? spv::OpLogicalNotEqual : spv::OpINotEqual;
2206
break;
2207
default:
2208
return assert(false), 0;
2209
}
2210
2211
add_location(loc, *_current_block_data);
2212
2213
// Binary operators generally only work on scalars and vectors in SPIR-V, so need to apply them to matrices component-wise
2214
if (exp_type.is_matrix() && exp_type.rows != 1)
2215
{
2216
std::vector<spv::Id> ids;
2217
ids.reserve(exp_type.cols);
2218
2219
type vector_type = exp_type;
2220
vector_type.rows = exp_type.cols;
2221
vector_type.cols = 1;
2222
2223
for (unsigned int row = 0; row < exp_type.rows; ++row)
2224
{
2225
const spv::Id lhs_elem = add_instruction(spv::OpCompositeExtract, convert_type(vector_type))
2226
.add(lhs)
2227
.add(row);
2228
const spv::Id rhs_elem = add_instruction(spv::OpCompositeExtract, convert_type(vector_type))
2229
.add(rhs)
2230
.add(row);
2231
2232
spirv_instruction &inst = add_instruction(spv_op, convert_type(vector_type));
2233
inst.add(lhs_elem); // Operand 1
2234
inst.add(rhs_elem); // Operand 2
2235
2236
if (res_type.has(type::q_precise))
2237
add_decoration(inst, spv::DecorationNoContraction);
2238
if (!_enable_16bit_types && res_type.precision() < 32)
2239
add_decoration(inst, spv::DecorationRelaxedPrecision);
2240
2241
ids.push_back(inst);
2242
}
2243
2244
spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(res_type));
2245
inst.add(ids.begin(), ids.end());
2246
2247
return inst;
2248
}
2249
else
2250
{
2251
spirv_instruction &inst = add_instruction(spv_op, convert_type(res_type));
2252
inst.add(lhs); // Operand 1
2253
inst.add(rhs); // Operand 2
2254
2255
if (res_type.has(type::q_precise))
2256
add_decoration(inst, spv::DecorationNoContraction);
2257
if (!_enable_16bit_types && res_type.precision() < 32)
2258
add_decoration(inst, spv::DecorationRelaxedPrecision);
2259
2260
return inst;
2261
}
2262
}
2263
id emit_ternary_op(const location &loc, tokenid op, const type &res_type, id condition, id true_value, id false_value) override
2264
{
2265
if (op != tokenid::question)
2266
return assert(false), 0;
2267
2268
add_location(loc, *_current_block_data);
2269
2270
spirv_instruction &inst = add_instruction(spv::OpSelect, convert_type(res_type));
2271
inst.add(condition); // Condition
2272
inst.add(true_value); // Object 1
2273
inst.add(false_value); // Object 2
2274
2275
return inst;
2276
}
2277
id emit_call(const location &loc, id function, const type &res_type, const std::vector<expression> &args) override
2278
{
2279
#ifndef NDEBUG
2280
for (const expression &arg : args)
2281
assert(arg.chain.empty() && arg.base != 0);
2282
#endif
2283
add_location(loc, *_current_block_data);
2284
2285
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpFunctionCall
2286
spirv_instruction &inst = add_instruction(spv::OpFunctionCall, convert_type(res_type));
2287
inst.add(function); // Function
2288
for (const expression &arg : args)
2289
inst.add(arg.base); // Arguments
2290
2291
return inst;
2292
}
2293
id emit_call_intrinsic(const location &loc, id intrinsic, const type &res_type, const std::vector<expression> &args) override
2294
{
2295
#ifndef NDEBUG
2296
for (const expression &arg : args)
2297
assert(arg.chain.empty() && arg.base != 0);
2298
#endif
2299
add_location(loc, *_current_block_data);
2300
2301
enum
2302
{
2303
#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) name##i,
2304
#include "effect_symbol_table_intrinsics.inl"
2305
};
2306
2307
switch (intrinsic)
2308
{
2309
#define IMPLEMENT_INTRINSIC_SPIRV(name, i, code) case name##i: code
2310
#include "effect_symbol_table_intrinsics.inl"
2311
default:
2312
return assert(false), 0;
2313
}
2314
}
2315
id emit_construct(const location &loc, const type &res_type, const std::vector<expression> &args) override
2316
{
2317
#ifndef NDEBUG
2318
for (const expression &arg : args)
2319
assert((arg.type.is_scalar() || res_type.is_array()) && arg.chain.empty() && arg.base != 0);
2320
#endif
2321
add_location(loc, *_current_block_data);
2322
2323
std::vector<spv::Id> ids;
2324
ids.reserve(args.size());
2325
2326
// There must be exactly one constituent for each top-level component of the result
2327
if (res_type.is_matrix())
2328
{
2329
type vector_type = res_type;
2330
vector_type.rows = res_type.cols;
2331
vector_type.cols = 1;
2332
2333
// Turn the list of scalar arguments into a list of column vectors
2334
for (size_t arg = 0; arg < args.size(); arg += vector_type.rows)
2335
{
2336
spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(vector_type));
2337
for (unsigned row = 0; row < vector_type.rows; ++row)
2338
inst.add(args[arg + row].base);
2339
2340
ids.push_back(inst);
2341
}
2342
}
2343
else
2344
{
2345
assert(res_type.is_vector() || res_type.is_array());
2346
2347
// The exception is that for constructing a vector, a contiguous subset of the scalars consumed can be represented by a vector operand instead
2348
for (const expression &arg : args)
2349
ids.push_back(arg.base);
2350
}
2351
2352
spirv_instruction &inst = add_instruction(spv::OpCompositeConstruct, convert_type(res_type));
2353
inst.add(ids.begin(), ids.end());
2354
2355
return inst;
2356
}
2357
2358
void emit_if(const location &loc, id, id condition_block, id true_statement_block, id false_statement_block, unsigned int selection_control) override
2359
{
2360
spirv_instruction merge_label = _current_block_data->instructions.back();
2361
assert(merge_label.op == spv::OpLabel);
2362
_current_block_data->instructions.pop_back();
2363
2364
// Add previous block containing the condition value first
2365
_current_block_data->append(_block_data[condition_block]);
2366
2367
spirv_instruction branch_inst = _current_block_data->instructions.back();
2368
assert(branch_inst.op == spv::OpBranchConditional);
2369
_current_block_data->instructions.pop_back();
2370
2371
// Add structured control flow instruction
2372
add_location(loc, *_current_block_data);
2373
add_instruction_without_result(spv::OpSelectionMerge)
2374
.add(merge_label)
2375
.add(selection_control & 0x3); // 'SelectionControl' happens to match the flags produced by the parser
2376
2377
// Append all blocks belonging to the branch
2378
_current_block_data->instructions.push_back(branch_inst);
2379
_current_block_data->append(_block_data[true_statement_block]);
2380
_current_block_data->append(_block_data[false_statement_block]);
2381
2382
_current_block_data->instructions.push_back(merge_label);
2383
}
2384
id emit_phi(const location &loc, id, id condition_block, id true_value, id true_statement_block, id false_value, id false_statement_block, const type &res_type) override
2385
{
2386
spirv_instruction merge_label = _current_block_data->instructions.back();
2387
assert(merge_label.op == spv::OpLabel);
2388
_current_block_data->instructions.pop_back();
2389
2390
// Add previous block containing the condition value first
2391
_current_block_data->append(_block_data[condition_block]);
2392
2393
if (true_statement_block != condition_block)
2394
_current_block_data->append(_block_data[true_statement_block]);
2395
if (false_statement_block != condition_block)
2396
_current_block_data->append(_block_data[false_statement_block]);
2397
2398
_current_block_data->instructions.push_back(merge_label);
2399
2400
add_location(loc, *_current_block_data);
2401
2402
// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpPhi
2403
spirv_instruction &inst = add_instruction(spv::OpPhi, convert_type(res_type))
2404
.add(true_value) // Variable 0
2405
.add(true_statement_block) // Parent 0
2406
.add(false_value) // Variable 1
2407
.add(false_statement_block); // Parent 1
2408
2409
return inst;
2410
}
2411
void emit_loop(const location &loc, id, id prev_block, id header_block, id condition_block, id loop_block, id continue_block, unsigned int loop_control) override
2412
{
2413
spirv_instruction merge_label = _current_block_data->instructions.back();
2414
assert(merge_label.op == spv::OpLabel);
2415
_current_block_data->instructions.pop_back();
2416
2417
// Add previous block first
2418
_current_block_data->append(_block_data[prev_block]);
2419
2420
// Fill header block
2421
assert(_block_data[header_block].instructions.size() == 2);
2422
_current_block_data->instructions.push_back(_block_data[header_block].instructions[0]);
2423
assert(_current_block_data->instructions.back().op == spv::OpLabel);
2424
2425
// Add structured control flow instruction
2426
add_location(loc, *_current_block_data);
2427
add_instruction_without_result(spv::OpLoopMerge)
2428
.add(merge_label)
2429
.add(continue_block)
2430
.add(loop_control & 0x3); // 'LoopControl' happens to match the flags produced by the parser
2431
2432
_current_block_data->instructions.push_back(_block_data[header_block].instructions[1]);
2433
assert(_current_block_data->instructions.back().op == spv::OpBranch);
2434
2435
// Add condition block if it exists
2436
if (condition_block != 0)
2437
_current_block_data->append(_block_data[condition_block]);
2438
2439
// Append loop body block before continue block
2440
_current_block_data->append(_block_data[loop_block]);
2441
_current_block_data->append(_block_data[continue_block]);
2442
2443
_current_block_data->instructions.push_back(merge_label);
2444
}
2445
void emit_switch(const location &loc, id, id selector_block, id default_label, id default_block, const std::vector<id> &case_literal_and_labels, const std::vector<id> &case_blocks, unsigned int selection_control) override
2446
{
2447
assert(case_blocks.size() == case_literal_and_labels.size() / 2);
2448
2449
spirv_instruction merge_label = _current_block_data->instructions.back();
2450
assert(merge_label.op == spv::OpLabel);
2451
_current_block_data->instructions.pop_back();
2452
2453
// Add previous block containing the selector value first
2454
_current_block_data->append(_block_data[selector_block]);
2455
2456
spirv_instruction switch_inst = _current_block_data->instructions.back();
2457
assert(switch_inst.op == spv::OpSwitch);
2458
_current_block_data->instructions.pop_back();
2459
2460
// Add structured control flow instruction
2461
add_location(loc, *_current_block_data);
2462
add_instruction_without_result(spv::OpSelectionMerge)
2463
.add(merge_label)
2464
.add(selection_control & 0x3); // 'SelectionControl' happens to match the flags produced by the parser
2465
2466
// Update switch instruction to contain all case labels
2467
switch_inst.operands[1] = default_label;
2468
switch_inst.add(case_literal_and_labels.begin(), case_literal_and_labels.end());
2469
2470
// Append all blocks belonging to the switch
2471
_current_block_data->instructions.push_back(switch_inst);
2472
2473
std::vector<id> blocks = case_blocks;
2474
if (default_label != merge_label)
2475
blocks.push_back(default_block);
2476
// Eliminate duplicates (because of multiple case labels pointing to the same block)
2477
std::sort(blocks.begin(), blocks.end());
2478
blocks.erase(std::unique(blocks.begin(), blocks.end()), blocks.end());
2479
for (const id case_block : blocks)
2480
_current_block_data->append(_block_data[case_block]);
2481
2482
_current_block_data->instructions.push_back(merge_label);
2483
}
2484
2485
bool is_in_function() const { return _current_function_blocks != nullptr; }
2486
2487
id set_block(id id) override
2488
{
2489
_last_block = _current_block;
2490
_current_block = id;
2491
_current_block_data = &_block_data[id];
2492
2493
return _last_block;
2494
}
2495
void enter_block(id id) override
2496
{
2497
assert(id != 0);
2498
// Can only use labels inside functions and should never be in another basic block if creating a new one
2499
assert(is_in_function() && !is_in_block());
2500
2501
set_block(id);
2502
2503
add_instruction_without_result(spv::OpLabel).result = id;
2504
}
2505
id leave_block_and_kill() override
2506
{
2507
assert(is_in_function()); // Can only discard inside functions
2508
2509
if (!is_in_block())
2510
return 0;
2511
2512
add_instruction_without_result(spv::OpKill);
2513
2514
return set_block(0);
2515
}
2516
id leave_block_and_return(id value) override
2517
{
2518
assert(is_in_function()); // Can only return from inside functions
2519
2520
if (!is_in_block()) // Might already have left the last block in which case this has to be ignored
2521
return 0;
2522
2523
if (_current_function_blocks->return_type.is_void())
2524
{
2525
add_instruction_without_result(spv::OpReturn);
2526
}
2527
else
2528
{
2529
if (0 == value) // The implicit return statement needs this
2530
value = add_instruction(spv::OpUndef, convert_type(_current_function_blocks->return_type), _types_and_constants);
2531
2532
add_instruction_without_result(spv::OpReturnValue)
2533
.add(value);
2534
}
2535
2536
return set_block(0);
2537
}
2538
id leave_block_and_switch(id value, id default_target) override
2539
{
2540
assert(value != 0 && default_target != 0);
2541
assert(is_in_function()); // Can only switch inside functions
2542
2543
if (!is_in_block())
2544
return _last_block;
2545
2546
add_instruction_without_result(spv::OpSwitch)
2547
.add(value)
2548
.add(default_target);
2549
2550
return set_block(0);
2551
}
2552
id leave_block_and_branch(id target, unsigned int) override
2553
{
2554
assert(target != 0);
2555
assert(is_in_function()); // Can only branch inside functions
2556
2557
if (!is_in_block())
2558
return _last_block;
2559
2560
add_instruction_without_result(spv::OpBranch)
2561
.add(target);
2562
2563
return set_block(0);
2564
}
2565
id leave_block_and_branch_conditional(id condition, id true_target, id false_target) override
2566
{
2567
assert(condition != 0 && true_target != 0 && false_target != 0);
2568
assert(is_in_function()); // Can only branch inside functions
2569
2570
if (!is_in_block())
2571
return _last_block;
2572
2573
add_instruction_without_result(spv::OpBranchConditional)
2574
.add(condition)
2575
.add(true_target)
2576
.add(false_target);
2577
2578
return set_block(0);
2579
}
2580
void leave_function() override
2581
{
2582
assert(is_in_function()); // Can only leave if there was a function to begin with
2583
2584
_current_function_blocks->definition = _block_data[_last_block];
2585
2586
// Append function end instruction
2587
add_instruction_without_result(spv::OpFunctionEnd, _current_function_blocks->definition);
2588
2589
_current_function = nullptr;
2590
_current_function_blocks = nullptr;
2591
}
2592
};
2593
2594
codegen *reshadefx::create_codegen_spirv(bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y)
2595
{
2596
return new codegen_spirv(vulkan_semantics, debug_info, uniforms_to_spec_constants, enable_16bit_types, flip_vert_y);
2597
}
2598
2599