Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/reshadefx/src/effect_codegen_glsl.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 <cmath> // std::isinf, std::isnan, std::signbit
9
#include <cassert>
10
#include <cstring> // std::memcmp
11
#include <charconv> // std::from_chars, std::to_chars
12
#include <algorithm> // std::find, std::find_if, std::max
13
#include <locale>
14
#include <sstream>
15
#include <iomanip>
16
#include <unordered_set>
17
18
using namespace reshadefx;
19
20
namespace {
21
22
inline char to_digit(unsigned int value)
23
{
24
assert(value < 10);
25
return '0' + static_cast<char>(value);
26
}
27
28
inline uint32_t align_up(uint32_t size, uint32_t alignment)
29
{
30
alignment -= 1;
31
return ((size + alignment) & ~alignment);
32
}
33
34
class codegen_glsl final : public codegen
35
{
36
public:
37
codegen_glsl(unsigned version, bool gles, bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y) :
38
_glsl_version(version),
39
_gles(gles),
40
_debug_info(debug_info),
41
_vulkan_semantics(vulkan_semantics),
42
_uniforms_to_spec_constants(uniforms_to_spec_constants),
43
_enable_16bit_types(enable_16bit_types),
44
_flip_vert_y(flip_vert_y)
45
{
46
// Create default block and reserve a memory block to avoid frequent reallocations
47
std::string &block = _blocks.emplace(0, std::string()).first->second;
48
block.reserve(8192);
49
}
50
51
private:
52
enum class naming
53
{
54
// After escaping, name should already be unique, so no additional steps are taken
55
unique,
56
// After escaping, will be numbered when clashing with another name
57
general,
58
// This is a special name that is not modified and should be unique
59
reserved,
60
// Replace name with a code snippet
61
expression,
62
};
63
64
unsigned _glsl_version = 0;
65
bool _gles = false;
66
bool _debug_info = false;
67
bool _vulkan_semantics = false;
68
bool _uniforms_to_spec_constants = false;
69
bool _enable_16bit_types = false;
70
bool _flip_vert_y = false;
71
72
std::unordered_map<id, std::string> _names;
73
std::unordered_map<id, std::string> _blocks;
74
std::string _ubo_block;
75
std::string _compute_block;
76
std::string _current_function_declaration;
77
78
std::unordered_map<id, id> _remapped_sampler_variables;
79
std::unordered_map<std::string, uint32_t> _semantic_to_location;
80
std::vector<std::tuple<type, constant, id>> _constant_lookup;
81
82
// Only write compatibility intrinsics to result if they are actually in use
83
bool _uses_fmod = false;
84
bool _uses_componentwise_or = false;
85
bool _uses_componentwise_and = false;
86
bool _uses_componentwise_cond = false;
87
bool _uses_control_flow_attributes = false;
88
89
std::string finalize_preamble() const
90
{
91
std::string preamble = "#version " + std::to_string(_glsl_version) + (_gles ? " es\n" : ((_glsl_version >= 330) ? " core\n" : "\n"));
92
93
if (_gles)
94
preamble += "precision highp float;\nprecision highp int;\nprecision highp sampler2D;\n";
95
96
if (_enable_16bit_types)
97
// GL_NV_gpu_shader5, GL_AMD_gpu_shader_half_float or GL_EXT_shader_16bit_storage
98
preamble += "#extension GL_NV_gpu_shader5 : require\n";
99
if (_uses_control_flow_attributes)
100
preamble += "#extension GL_EXT_control_flow_attributes : enable\n";
101
102
if (_uses_fmod)
103
preamble += "float fmodHLSL(float x, float y) { return x - y * trunc(x / y); }\n"
104
"vec2 fmodHLSL(vec2 x, vec2 y) { return x - y * trunc(x / y); }\n"
105
"vec3 fmodHLSL(vec3 x, vec3 y) { return x - y * trunc(x / y); }\n"
106
"vec4 fmodHLSL(vec4 x, vec4 y) { return x - y * trunc(x / y); }\n"
107
"mat2 fmodHLSL(mat2 x, mat2 y) { return x - matrixCompMult(y, mat2(trunc(x[0] / y[0]), trunc(x[1] / y[1]))); }\n"
108
"mat3 fmodHLSL(mat3 x, mat3 y) { return x - matrixCompMult(y, mat3(trunc(x[0] / y[0]), trunc(x[1] / y[1]), trunc(x[2] / y[2]))); }\n"
109
"mat4 fmodHLSL(mat4 x, mat4 y) { return x - matrixCompMult(y, mat4(trunc(x[0] / y[0]), trunc(x[1] / y[1]), trunc(x[2] / y[2]), trunc(x[3] / y[3]))); }\n";
110
if (_uses_componentwise_or)
111
preamble +=
112
"bvec2 compOr(bvec2 a, bvec2 b) { return bvec2(a.x || b.x, a.y || b.y); }\n"
113
"bvec3 compOr(bvec3 a, bvec3 b) { return bvec3(a.x || b.x, a.y || b.y, a.z || b.z); }\n"
114
"bvec4 compOr(bvec4 a, bvec4 b) { return bvec4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); }\n";
115
if (_uses_componentwise_and)
116
preamble +=
117
"bvec2 compAnd(bvec2 a, bvec2 b) { return bvec2(a.x && b.x, a.y && b.y); }\n"
118
"bvec3 compAnd(bvec3 a, bvec3 b) { return bvec3(a.x && b.x, a.y && b.y, a.z && b.z); }\n"
119
"bvec4 compAnd(bvec4 a, bvec4 b) { return bvec4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); }\n";
120
if (_uses_componentwise_cond)
121
preamble +=
122
"vec2 compCond(bvec2 cond, vec2 a, vec2 b) { return vec2(cond.x ? a.x : b.x, cond.y ? a.y : b.y); }\n"
123
"vec3 compCond(bvec3 cond, vec3 a, vec3 b) { return vec3(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z); }\n"
124
"vec4 compCond(bvec4 cond, vec4 a, vec4 b) { return vec4(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z, cond.w ? a.w : b.w); }\n"
125
"bvec2 compCond(bvec2 cond, bvec2 a, bvec2 b) { return bvec2(cond.x ? a.x : b.x, cond.y ? a.y : b.y); }\n"
126
"bvec3 compCond(bvec3 cond, bvec3 a, bvec3 b) { return bvec3(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z); }\n"
127
"bvec4 compCond(bvec4 cond, bvec4 a, bvec4 b) { return bvec4(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z, cond.w ? a.w : b.w); }\n"
128
"ivec2 compCond(bvec2 cond, ivec2 a, ivec2 b) { return ivec2(cond.x ? a.x : b.x, cond.y ? a.y : b.y); }\n"
129
"ivec3 compCond(bvec3 cond, ivec3 a, ivec3 b) { return ivec3(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z); }\n"
130
"ivec4 compCond(bvec4 cond, ivec4 a, ivec4 b) { return ivec4(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z, cond.w ? a.w : b.w); }\n"
131
"uvec2 compCond(bvec2 cond, uvec2 a, uvec2 b) { return uvec2(cond.x ? a.x : b.x, cond.y ? a.y : b.y); }\n"
132
"uvec3 compCond(bvec3 cond, uvec3 a, uvec3 b) { return uvec3(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z); }\n"
133
"uvec4 compCond(bvec4 cond, uvec4 a, uvec4 b) { return uvec4(cond.x ? a.x : b.x, cond.y ? a.y : b.y, cond.z ? a.z : b.z, cond.w ? a.w : b.w); }\n";
134
135
if (!_ubo_block.empty())
136
{
137
if (_vulkan_semantics)
138
{
139
preamble += "layout(std140, set = 0, binding = 0) uniform _Globals {\n" + _ubo_block + "};\n";
140
}
141
else
142
{
143
preamble += "layout(std140, binding = 0) uniform _Globals {\n" + _ubo_block + "};\n";
144
}
145
}
146
147
return preamble;
148
}
149
150
std::string finalize_code() const override
151
{
152
std::string code = finalize_preamble();
153
154
// Add sampler definitions
155
for (const sampler &info : _module.samplers)
156
code += _blocks.at(info.id);
157
158
// Add storage definitions
159
for (const storage &info : _module.storages)
160
code += _blocks.at(info.id);
161
162
// Add global definitions (struct types, global variables, ...)
163
code += _blocks.at(0);
164
165
// Add function definitions
166
for (const std::unique_ptr<function> &func : _functions)
167
{
168
const bool is_entry_point = func->unique_name[0] == 'E';
169
if (is_entry_point)
170
code += "#ifdef " + func->unique_name + '\n';
171
172
code += _blocks.at(func->id);
173
174
if (is_entry_point)
175
code += "#endif\n";
176
}
177
178
return code;
179
}
180
std::string finalize_code_for_entry_point(const std::string &entry_point_name) const override
181
{
182
const auto entry_point_it = std::find_if(_functions.begin(), _functions.end(),
183
[&entry_point_name](const std::unique_ptr<function> &func) {
184
return func->unique_name == entry_point_name;
185
});
186
if (entry_point_it == _functions.end())
187
return {};
188
const function &entry_point = *entry_point_it->get();
189
190
std::string code = finalize_preamble();
191
192
if (entry_point.type != shader_type::pixel)
193
code +=
194
// OpenGL does not allow using 'discard' in the vertex shader profile
195
"#define discard\n"
196
// 'dFdx', 'dFdx' and 'fwidth' too are only available in fragment shaders
197
"#define dFdx(x) x\n"
198
"#define dFdy(y) y\n"
199
"#define fwidth(p) p\n";
200
201
if (entry_point.type != shader_type::compute)
202
code +=
203
// OpenGL does not allow using 'shared' in vertex/fragment shader profile
204
"#define shared\n"
205
"#define atomicAdd(a, b) a\n"
206
"#define atomicAnd(a, b) a\n"
207
"#define atomicOr(a, b) a\n"
208
"#define atomicXor(a, b) a\n"
209
"#define atomicMin(a, b) a\n"
210
"#define atomicMax(a, b) a\n"
211
"#define atomicExchange(a, b) a\n"
212
"#define atomicCompSwap(a, b, c) a\n"
213
// Barrier intrinsics are only available in compute shaders
214
"#define barrier()\n"
215
"#define memoryBarrier()\n"
216
"#define groupMemoryBarrier()\n";
217
218
const auto replace_binding =
219
[](std::string &code, uint32_t binding) {
220
const size_t beg = code.find("layout(binding = ") + 17;
221
const size_t end = code.find_first_of("),", beg);
222
code.replace(beg, end - beg, std::to_string(binding));
223
};
224
225
// Add referenced sampler definitions
226
for (uint32_t binding = 0; binding < entry_point.referenced_samplers.size(); ++binding)
227
{
228
if (entry_point.referenced_samplers[binding] == 0)
229
continue;
230
231
std::string block_code = _blocks.at(entry_point.referenced_samplers[binding]);
232
replace_binding(block_code, binding);
233
code += block_code;
234
}
235
236
// Add referenced storage definitions
237
for (uint32_t binding = 0; binding < entry_point.referenced_storages.size(); ++binding)
238
{
239
if (entry_point.referenced_storages[binding] == 0)
240
continue;
241
242
std::string block_code = _blocks.at(entry_point.referenced_storages[binding]);
243
replace_binding(block_code, binding);
244
code += block_code;
245
}
246
247
// Add global definitions (struct types, global variables, ...)
248
code += _blocks.at(0);
249
250
// Add referenced function definitions
251
for (const std::unique_ptr<function> &func : _functions)
252
{
253
if (func->id != entry_point.id &&
254
std::find(entry_point.referenced_functions.begin(), entry_point.referenced_functions.end(), func->id) == entry_point.referenced_functions.end())
255
continue;
256
257
code += _blocks.at(func->id);
258
}
259
260
return code;
261
}
262
263
template <bool is_param = false, bool is_decl = true, bool is_interface = false>
264
void write_type(std::string &s, const type &type) const
265
{
266
if constexpr (is_decl)
267
{
268
// Global variables are implicitly 'static' in GLSL, so the keyword does not exist
269
if (type.has(type::q_precise))
270
s += "precise ";
271
if (type.has(type::q_groupshared))
272
s += "shared ";
273
}
274
275
if constexpr (is_interface)
276
{
277
if (type.has(type::q_linear))
278
s += "smooth ";
279
if (type.has(type::q_noperspective))
280
s += "noperspective ";
281
if (type.has(type::q_centroid))
282
s += "centroid ";
283
if (type.has(type::q_nointerpolation))
284
s += "flat ";
285
}
286
287
if constexpr (is_interface || is_param)
288
{
289
if (type.has(type::q_inout))
290
s += "inout ";
291
else if (type.has(type::q_in))
292
s += "in ";
293
else if (type.has(type::q_out))
294
s += "out ";
295
}
296
297
switch (type.base)
298
{
299
case type::t_void:
300
s += "void";
301
break;
302
case type::t_bool:
303
if (type.cols > 1)
304
s += "mat", s += to_digit(type.rows), s += 'x', s += to_digit(type.cols);
305
else if (type.rows > 1)
306
s += "bvec", s += to_digit(type.rows);
307
else
308
s += "bool";
309
break;
310
case type::t_min16int:
311
if (_enable_16bit_types)
312
{
313
assert(type.cols == 1);
314
if (type.rows > 1)
315
s += "i16vec", s += to_digit(type.rows);
316
else
317
s += "int16_t";
318
break;
319
}
320
else if constexpr (is_decl)
321
s += "mediump ";
322
[[fallthrough]];
323
case type::t_int:
324
if (type.cols > 1)
325
s += "mat", s += to_digit(type.rows), s += 'x', s += to_digit(type.cols);
326
else if (type.rows > 1)
327
s += "ivec", s += to_digit(type.rows);
328
else
329
s += "int";
330
break;
331
case type::t_min16uint:
332
if (_enable_16bit_types)
333
{
334
assert(type.cols == 1);
335
if (type.rows > 1)
336
s += "u16vec", s += to_digit(type.rows);
337
else
338
s += "uint16_t";
339
break;
340
}
341
else if constexpr (is_decl)
342
s += "mediump ";
343
[[fallthrough]];
344
case type::t_uint:
345
if (type.cols > 1)
346
s += "mat", s += to_digit(type.rows), s += 'x', s += to_digit(type.cols);
347
else if (type.rows > 1)
348
s += "uvec", s += to_digit(type.rows);
349
else
350
s += "uint";
351
break;
352
case type::t_min16float:
353
if (_enable_16bit_types)
354
{
355
assert(type.cols == 1);
356
if (type.rows > 1)
357
s += "f16vec", s += to_digit(type.rows);
358
else
359
s += "float16_t";
360
break;
361
}
362
else if constexpr (is_decl)
363
s += "mediump ";
364
[[fallthrough]];
365
case type::t_float:
366
if (type.cols > 1)
367
s += "mat", s += to_digit(type.rows), s += 'x', s += to_digit(type.cols);
368
else if (type.rows > 1)
369
s += "vec", s += to_digit(type.rows);
370
else
371
s += "float";
372
break;
373
case type::t_struct:
374
s += id_to_name(type.struct_definition);
375
break;
376
case type::t_sampler1d_int:
377
s += "isampler1D";
378
break;
379
case type::t_sampler2d_int:
380
s += "isampler2D";
381
break;
382
case type::t_sampler3d_int:
383
s += "isampler3D";
384
break;
385
case type::t_sampler1d_uint:
386
s += "usampler1D";
387
break;
388
case type::t_sampler3d_uint:
389
s += "usampler3D";
390
break;
391
case type::t_sampler2d_uint:
392
s += "usampler2D";
393
break;
394
case type::t_sampler1d_float:
395
s += "sampler1D";
396
break;
397
case type::t_sampler2d_float:
398
s += "sampler2D";
399
break;
400
case type::t_sampler3d_float:
401
s += "sampler3D";
402
break;
403
case type::t_storage1d_int:
404
if constexpr (is_param)
405
s += "writeonly ";
406
s += "iimage1D";
407
break;
408
case type::t_storage2d_int:
409
if constexpr (is_param)
410
s += "writeonly ";
411
s += "iimage2D";
412
break;
413
case type::t_storage3d_int:
414
if constexpr (is_param)
415
s += "writeonly ";
416
s += "iimage3D";
417
break;
418
case type::t_storage1d_uint:
419
if constexpr (is_param)
420
s += "writeonly ";
421
s += "uimage1D";
422
break;
423
case type::t_storage2d_uint:
424
if constexpr (is_param)
425
s += "writeonly ";
426
s += "uimage2D";
427
break;
428
case type::t_storage3d_uint:
429
if constexpr (is_param)
430
s += "writeonly ";
431
s += "uimage3D";
432
break;
433
case type::t_storage1d_float:
434
if constexpr (is_param)
435
s += "writeonly ";
436
s += "image1D";
437
break;
438
case type::t_storage2d_float:
439
if constexpr (is_param) // Images need a format to be readable, but declaring that on function parameters is not well supported, so can only support write-only images there
440
s += "writeonly ";
441
s += "image2D";
442
break;
443
case type::t_storage3d_float:
444
if constexpr (is_param)
445
s += "writeonly ";
446
s += "image3D";
447
break;
448
default:
449
assert(false);
450
}
451
}
452
void write_constant(std::string &s, const type &data_type, const constant &data) const
453
{
454
if (data_type.is_array())
455
{
456
assert(data_type.is_bounded_array());
457
458
type elem_type = data_type;
459
elem_type.array_length = 0;
460
461
write_type<false, false>(s, elem_type);
462
s += '[' + std::to_string(data_type.array_length) + "](";
463
464
for (unsigned int a = 0; a < data_type.array_length; ++a)
465
{
466
write_constant(s, elem_type, a < static_cast<unsigned int>(data.array_data.size()) ? data.array_data[a] : constant {});
467
s += ", ";
468
}
469
470
// Remove trailing ", "
471
s.erase(s.size() - 2);
472
473
s += ')';
474
return;
475
}
476
477
// There can only be numeric constants
478
assert(data_type.is_numeric());
479
480
if (!data_type.is_scalar())
481
write_type<false, false>(s, data_type), s += '(';
482
483
for (unsigned int i = 0; i < data_type.components(); ++i)
484
{
485
switch (data_type.base)
486
{
487
case type::t_bool:
488
s += data.as_uint[i] ? "true" : "false";
489
break;
490
case type::t_min16int:
491
case type::t_int:
492
s += std::to_string(data.as_int[i]);
493
break;
494
case type::t_min16uint:
495
case type::t_uint:
496
s += std::to_string(data.as_uint[i]) + 'u';
497
break;
498
case type::t_min16float:
499
case type::t_float:
500
if (std::isnan(data.as_float[i])) {
501
s += "0.0/0.0/*nan*/";
502
break;
503
}
504
if (std::isinf(data.as_float[i])) {
505
s += std::signbit(data.as_float[i]) ? "1.0/0.0/*inf*/" : "-1.0/0.0/*-inf*/";
506
break;
507
}
508
509
{
510
#ifdef _MSC_VER
511
char temp[64];
512
const std::to_chars_result res = std::to_chars(temp, temp + sizeof(temp), data.as_float[i], std::chars_format::scientific, 8);
513
if (res.ec == std::errc())
514
s.append(temp, res.ptr);
515
else
516
assert(false);
517
#else
518
std::ostringstream ss;
519
ss.imbue(std::locale::classic());
520
ss << std::scientific << data.as_float[i];
521
s += ss.str();
522
#endif
523
}
524
break;
525
default:
526
assert(false);
527
}
528
529
s += ", ";
530
}
531
532
// Remove trailing ", "
533
s.erase(s.size() - 2);
534
535
if (!data_type.is_scalar())
536
s += ')';
537
}
538
void write_location(std::string &s, const location &loc) const
539
{
540
if (loc.source.empty() || !_debug_info)
541
return;
542
543
s += "#line " + std::to_string(loc.line) + '\n';
544
}
545
void write_texture_format(std::string &s, texture_format format)
546
{
547
switch (format)
548
{
549
case texture_format::r8:
550
s += "r8";
551
break;
552
case texture_format::r16:
553
s += "r16";
554
break;
555
case texture_format::r16f:
556
s += "r16f";
557
break;
558
case texture_format::r32i:
559
s += "r32i";
560
break;
561
case texture_format::r32u:
562
s += "r32ui";
563
break;
564
case texture_format::r32f:
565
s += "r32f";
566
break;
567
case texture_format::rg8:
568
s += "rg8";
569
break;
570
case texture_format::rg16:
571
s += "rg16";
572
break;
573
case texture_format::rg16f:
574
s += "rg16f";
575
break;
576
case texture_format::rg32f:
577
s += "rg32f";
578
break;
579
case texture_format::rgba8:
580
s += "rgba8";
581
break;
582
case texture_format::rgba16:
583
s += "rgba16";
584
break;
585
case texture_format::rgba16f:
586
s += "rgba16f";
587
break;
588
case texture_format::rgba32f:
589
s += "rgba32f";
590
break;
591
case texture_format::rgb10a2:
592
s += "rgb10_a2";
593
break;
594
default:
595
assert(false);
596
}
597
}
598
599
std::string id_to_name(id id) const
600
{
601
if (const auto it = _remapped_sampler_variables.find(id);
602
it != _remapped_sampler_variables.end())
603
id = it->second;
604
605
assert(id != 0);
606
if (const auto names_it = _names.find(id);
607
names_it != _names.end())
608
return names_it->second;
609
return '_' + std::to_string(id);
610
}
611
612
template <naming naming_type = naming::general>
613
void define_name(const id id, std::string name)
614
{
615
assert(!name.empty());
616
if constexpr (naming_type != naming::expression)
617
if (name[0] == '_')
618
return; // Filter out names that may clash with automatic ones
619
if constexpr (naming_type != naming::reserved)
620
name = escape_name(std::move(name));
621
if constexpr (naming_type == naming::general)
622
if (std::find_if(_names.begin(), _names.end(),
623
[&name](const auto &names_it) { return names_it.second == name; }) != _names.end())
624
name += '_' + std::to_string(id); // Append a numbered suffix if the name already exists
625
_names[id] = std::move(name);
626
}
627
628
uint32_t semantic_to_location(const std::string &semantic, uint32_t max_attributes = 1)
629
{
630
if (const auto location_it = _semantic_to_location.find(semantic);
631
location_it != _semantic_to_location.end())
632
return location_it->second;
633
634
// Extract the semantic index from the semantic name (e.g. 2 for "TEXCOORD2")
635
size_t digit_index = semantic.size() - 1;
636
while (digit_index != 0 && semantic[digit_index] >= '0' && semantic[digit_index] <= '9')
637
digit_index--;
638
digit_index++;
639
640
const std::string semantic_base = semantic.substr(0, digit_index);
641
642
uint32_t semantic_digit = 0;
643
std::from_chars(semantic.c_str() + digit_index, semantic.c_str() + semantic.size(), semantic_digit);
644
645
if (semantic_base == "COLOR" || semantic_base == "SV_TARGET")
646
return semantic_digit;
647
648
uint32_t location = static_cast<uint32_t>(_semantic_to_location.size());
649
650
// Now create adjoining location indices for all possible semantic indices belonging to this semantic name
651
for (uint32_t a = 0; a < semantic_digit + max_attributes; ++a)
652
{
653
const auto insert = _semantic_to_location.emplace(semantic_base + std::to_string(a), location + a);
654
if (!insert.second)
655
{
656
assert(a == 0 || (insert.first->second - a) == location);
657
658
// Semantic was already created with a different location index, so need to remap to that
659
location = insert.first->second - a;
660
}
661
}
662
663
return location + semantic_digit;
664
}
665
666
std::string escape_name(std::string name) const
667
{
668
static const std::unordered_set<std::string> s_reserverd_names = {
669
"common", "partition", "input", "output", "active", "filter", "superp", "invariant",
670
"attribute", "varying", "buffer", "resource", "coherent", "readonly", "writeonly",
671
"layout", "flat", "smooth", "lowp", "mediump", "highp", "precision", "patch", "subroutine",
672
"atomic_uint", "fixed",
673
"vec2", "vec3", "vec4", "ivec2", "dvec2", "dvec3", "dvec4", "ivec3", "ivec4", "uvec2", "uvec3", "uvec4", "bvec2", "bvec3", "bvec4", "fvec2", "fvec3", "fvec4", "hvec2", "hvec3", "hvec4",
674
"mat2", "mat3", "mat4", "dmat2", "dmat3", "dmat4", "mat2x2", "mat2x3", "mat2x4", "dmat2x2", "dmat2x3", "dmat2x4", "mat3x2", "mat3x3", "mat3x4", "dmat3x2", "dmat3x3", "dmat3x4", "mat4x2", "mat4x3", "mat4x4", "dmat4x2", "dmat4x3", "dmat4x4",
675
"sampler1DShadow", "sampler1DArrayShadow", "isampler1D", "isampler1DArray", "usampler1D", "usampler1DArray",
676
"sampler2DShadow", "sampler2DArrayShadow", "isampler2D", "isampler2DArray", "usampler2D", "usampler2DArray", "sampler2DRect", "sampler2DRectShadow", "isampler2DRect", "usampler2DRect", "isampler2DMS", "usampler2DMS", "isampler2DMSArray", "usampler2DMSArray",
677
"isampler3D", "usampler3D", "sampler3DRect",
678
"samplerCubeShadow", "samplerCubeArrayShadow", "isamplerCube", "isamplerCubeArray", "usamplerCube", "usamplerCubeArray",
679
"samplerBuffer", "isamplerBuffer", "usamplerBuffer",
680
"image1D", "iimage1D", "uimage1D", "image1DArray", "iimage1DArray", "uimage1DArray",
681
"image2D", "iimage2D", "uimage2D", "image2DArray", "iimage2DArray", "uimage2DArray", "image2DRect", "iimage2DRect", "uimage2DRect", "image2DMS", "iimage2DMS", "uimage2DMS", "image2DMSArray", "iimage2DMSArray", "uimage2DMSArray",
682
"image3D", "iimage3D", "uimage3D",
683
"imageCube", "iimageCube", "uimageCube", "imageCubeArray", "iimageCubeArray", "uimageCubeArray",
684
"imageBuffer", "iimageBuffer", "uimageBuffer",
685
"abs", "sign", "all", "any", "sin", "sinh", "cos", "cosh", "tan", "tanh", "asin", "acos", "atan",
686
"exp", "exp2", "log", "log2", "sqrt", "inversesqrt", "ceil", "floor", "fract", "trunc", "round",
687
"radians", "degrees", "length", "normalize", "transpose", "determinant", "intBitsToFloat", "uintBitsToFloat",
688
"floatBitsToInt", "floatBitsToUint", "matrixCompMult", "not", "lessThan", "greaterThan", "lessThanEqual",
689
"greaterThanEqual", "equal", "notEqual", "dot", "cross", "distance", "pow", "modf", "frexp", "ldexp",
690
"min", "max", "step", "reflect", "texture", "textureOffset", "fma", "mix", "clamp", "smoothstep", "refract",
691
"faceforward", "textureLod", "textureLodOffset", "texelFetch", "main"
692
};
693
694
// Escape reserved names so that they do not fail to compile
695
if (name.compare(0, 3, "gl_") == 0 || s_reserverd_names.count(name))
696
// Append an underscore at start instead of the end, since another one may get added in 'define_name' when there is a suffix
697
// This is guaranteed to not clash with user defined names, since those starting with an underscore are filtered out in 'define_name'
698
name = '_' + name;
699
700
// Remove duplicated underscore symbols from name which can occur due to namespaces but are not allowed in GLSL
701
for (size_t pos = 0; (pos = name.find("__", pos)) != std::string::npos;)
702
name.replace(pos, 2, "_x");
703
704
return name;
705
}
706
std::string semantic_to_builtin(std::string name, const std::string &semantic, shader_type stype) const
707
{
708
if (semantic == "SV_POSITION")
709
return stype == shader_type::pixel ? "gl_FragCoord" : "gl_Position";
710
if (semantic == "SV_POINTSIZE")
711
return "gl_PointSize";
712
if (semantic == "SV_DEPTH")
713
return "gl_FragDepth";
714
if (semantic == "SV_VERTEXID")
715
return _vulkan_semantics ? "gl_VertexIndex" : "gl_VertexID";
716
if (semantic == "SV_ISFRONTFACE")
717
return "gl_FrontFacing";
718
if (semantic == "SV_GROUPID")
719
return "gl_WorkGroupID";
720
if (semantic == "SV_GROUPINDEX")
721
return "gl_LocalInvocationIndex";
722
if (semantic == "SV_GROUPTHREADID")
723
return "gl_LocalInvocationID";
724
if (semantic == "SV_DISPATCHTHREADID")
725
return "gl_GlobalInvocationID";
726
727
return escape_name(std::move(name));
728
}
729
730
static void increase_indentation_level(std::string &block)
731
{
732
if (block.empty())
733
return;
734
735
for (size_t pos = 0; (pos = block.find("\n\t", pos)) != std::string::npos; pos += 3)
736
block.replace(pos, 2, "\n\t\t");
737
738
block.insert(block.begin(), '\t');
739
}
740
741
id define_struct(const location &loc, struct_type &info) override
742
{
743
const id res = info.id = make_id();
744
define_name<naming::unique>(res, info.unique_name);
745
746
_structs.push_back(info);
747
748
std::string &code = _blocks.at(_current_block);
749
750
write_location(code, loc);
751
752
code += "struct " + id_to_name(res) + "\n{\n";
753
754
for (const member_type &member : info.member_list)
755
{
756
code += '\t';
757
write_type(code, member.type); // GLSL does not allow interpolation attributes on struct members
758
code += ' ';
759
code += escape_name(member.name);
760
if (member.type.is_array())
761
code += '[' + std::to_string(member.type.array_length) + ']';
762
code += ";\n";
763
}
764
765
if (info.member_list.empty())
766
code += "float _dummy;\n";
767
768
code += "};\n";
769
770
return res;
771
}
772
id define_texture(const location &, texture &info) override
773
{
774
const id res = info.id = make_id();
775
776
_module.textures.push_back(info);
777
778
return res;
779
}
780
id define_sampler(const location &loc, const texture &, sampler &info) override
781
{
782
const id res = info.id = create_block();
783
define_name<naming::unique>(res, info.unique_name);
784
785
std::string &code = _blocks.at(res);
786
787
write_location(code, loc);
788
789
// 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)
790
const uint32_t default_binding = static_cast<uint32_t>(_module.samplers.size());
791
792
code += "layout(binding = " + std::to_string(default_binding);
793
if (_vulkan_semantics)
794
code += ", set = 1";
795
code += ") uniform ";
796
write_type(code, info.type);
797
code += ' ' + id_to_name(res) + ";\n";
798
799
_module.samplers.push_back(info);
800
801
return res;
802
}
803
id define_storage(const location &loc, const texture &tex_info, storage &info) override
804
{
805
const id res = info.id = create_block();
806
define_name<naming::unique>(res, info.unique_name);
807
808
std::string &code = _blocks.at(res);
809
810
write_location(code, loc);
811
812
// 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)
813
const uint32_t default_binding = static_cast<uint32_t>(_module.storages.size());
814
815
code += "layout(binding = " + std::to_string(default_binding) + ", ";
816
write_texture_format(code, tex_info.format);
817
code += ") uniform ";
818
write_type(code, info.type);
819
code += ' ' + id_to_name(res) + ";\n";
820
821
_module.storages.push_back(info);
822
823
return res;
824
}
825
id define_uniform(const location &loc, uniform &info) override
826
{
827
const id res = make_id();
828
define_name<naming::unique>(res, info.name);
829
830
if (_uniforms_to_spec_constants && info.has_initializer_value)
831
{
832
info.size = info.type.components() * 4;
833
if (info.type.is_array())
834
info.size *= info.type.array_length;
835
836
std::string &code = _blocks.at(_current_block);
837
838
write_location(code, loc);
839
840
assert(!info.type.has(type::q_static) && !info.type.has(type::q_const));
841
842
if (!_gles)
843
code += "const ";
844
write_type(code, info.type);
845
code += ' ' + id_to_name(res) + " = ";
846
if (!info.type.is_scalar())
847
write_type<false, false>(code, info.type);
848
code += "(SPEC_CONSTANT_" + info.name + ");\n";
849
850
_module.spec_constants.push_back(info);
851
}
852
else
853
{
854
// GLSL specification on std140 layout:
855
// 1. If the member is a scalar consuming N basic machine units, the base alignment is N.
856
// 2. If the member is a two- or four-component vector with components consuming N basic machine units, the base alignment is 2N or 4N, respectively.
857
// 3. If the member is a three-component vector with components consuming N basic machine units, the base alignment is 4N.
858
// 4. If the member is an array of scalars or vectors, the base alignment and array stride are set to match the base alignment of a single array element,
859
// according to rules (1), (2), and (3), and rounded up to the base alignment of a four-component vector.
860
// 7. If the member is a row-major matrix with C columns and R rows, the matrix is stored identically to an array of R row vectors with C components each, according to rule (4).
861
// 8. If the member is an array of S row-major matrices with C columns and R rows, the matrix is stored identically to a row of S*R row vectors with C components each, according to rule (4).
862
uint32_t alignment = (info.type.rows == 3 ? 4 /* (3) */ : info.type.rows /* (2) */) * 4 /* (1) */;
863
info.size = info.type.rows * 4;
864
865
if (info.type.is_matrix())
866
{
867
alignment = 16 /* (4) */;
868
info.size = info.type.rows * alignment /* (7), (8) */;
869
}
870
if (info.type.is_array())
871
{
872
alignment = 16 /* (4) */;
873
info.size = align_up(info.size, alignment) * info.type.array_length;
874
}
875
876
// Adjust offset according to alignment rules from above
877
info.offset = _module.total_uniform_size;
878
info.offset = align_up(info.offset, alignment);
879
_module.total_uniform_size = info.offset + info.size;
880
881
write_location(_ubo_block, loc);
882
883
_ubo_block += '\t';
884
// Note: All matrices are floating-point, even if the uniform type says different!!
885
write_type(_ubo_block, info.type);
886
_ubo_block += ' ' + id_to_name(res);
887
888
if (info.type.is_array())
889
_ubo_block += '[' + std::to_string(info.type.array_length) + ']';
890
891
_ubo_block += ";\n";
892
893
_module.uniforms.push_back(info);
894
}
895
896
return res;
897
}
898
id define_variable(const location &loc, const type &type, std::string name, bool global, id initializer_value) override
899
{
900
// Constant variables with a constant initializer can just point to the initializer SSA variable, since they cannot be modified anyway, thus saving an unnecessary assignment
901
if (initializer_value != 0 && type.has(type::q_const) &&
902
std::find_if(_constant_lookup.begin(), _constant_lookup.end(),
903
[initializer_value](const auto &x) {
904
return initializer_value == std::get<2>(x);
905
}) != _constant_lookup.end())
906
return initializer_value;
907
908
const id res = make_id();
909
910
// GLSL does not allow local sampler variables, so try to remap those
911
if (!global && type.is_sampler())
912
{
913
_remapped_sampler_variables[res] = 0;
914
return res;
915
}
916
917
if (!name.empty())
918
define_name<naming::general>(res, name);
919
920
std::string &code = _blocks.at(_current_block);
921
922
write_location(code, loc);
923
924
if (!global)
925
code += '\t';
926
927
write_type(code, type);
928
code += ' ' + id_to_name(res);
929
930
if (type.is_array())
931
code += '[' + std::to_string(type.array_length) + ']';
932
933
if (initializer_value != 0)
934
code += " = " + id_to_name(initializer_value);
935
936
code += ";\n";
937
938
return res;
939
}
940
id define_function(const location &loc, function &info) override
941
{
942
const id res = info.id = make_id();
943
944
// Name is used in other places like the entry point defines, so escape it here
945
info.unique_name = escape_name(info.unique_name);
946
947
assert(!info.unique_name.empty() && (info.unique_name[0] == 'F' || info.unique_name[0] == 'E'));
948
const bool is_entry_point = info.unique_name[0] == 'E';
949
if (!is_entry_point)
950
define_name<naming::unique>(res, info.unique_name);
951
else
952
define_name<naming::reserved>(res, "main");
953
954
assert(_current_block == 0 && (_current_function_declaration.empty() || is_entry_point));
955
std::string &code = _current_function_declaration;
956
957
write_location(code, loc);
958
959
write_type(code, info.return_type);
960
code += ' ' + id_to_name(res) + '(';
961
962
assert(info.parameter_list.empty() || !is_entry_point);
963
964
for (member_type &param : info.parameter_list)
965
{
966
param.id = make_id();
967
define_name<naming::unique>(param.id, param.name);
968
969
code += '\n';
970
write_location(code, param.location);
971
code += '\t';
972
write_type<true>(code, param.type); // GLSL does not allow interpolation attributes on function parameters
973
code += ' ' + id_to_name(param.id);
974
975
if (param.type.is_array())
976
code += '[' + std::to_string(param.type.array_length) + ']';
977
978
code += ',';
979
}
980
981
// Remove trailing comma
982
if (!info.parameter_list.empty())
983
code.pop_back();
984
985
code += ")\n";
986
987
_functions.push_back(std::make_unique<function>(info));
988
_current_function = _functions.back().get();
989
990
return res;
991
}
992
993
void define_entry_point(function &func) override
994
{
995
assert(!func.unique_name.empty() && func.unique_name[0] == 'F');
996
func.unique_name[0] = 'E';
997
998
// Modify entry point name so each thread configuration is made separate
999
if (func.type == shader_type::compute)
1000
func.unique_name +=
1001
'_' + std::to_string(func.num_threads[0]) +
1002
'_' + std::to_string(func.num_threads[1]) +
1003
'_' + std::to_string(func.num_threads[2]);
1004
1005
if (std::find_if(_module.entry_points.begin(), _module.entry_points.end(),
1006
[&func](const std::pair<std::string, shader_type> &entry_point) {
1007
return entry_point.first == func.unique_name;
1008
}) != _module.entry_points.end())
1009
return;
1010
1011
_module.entry_points.emplace_back(func.unique_name, func.type);
1012
1013
assert(_current_function_declaration.empty());
1014
if (func.type == shader_type::compute)
1015
_current_function_declaration +=
1016
"layout(local_size_x = " + std::to_string(func.num_threads[0]) +
1017
", local_size_y = " + std::to_string(func.num_threads[1]) +
1018
", local_size_z = " + std::to_string(func.num_threads[2]) + ") in;\n";
1019
1020
// Generate the glue entry point function
1021
function entry_point = func;
1022
entry_point.referenced_functions.push_back(func.id);
1023
1024
// Change function signature to 'void main()'
1025
entry_point.return_type = { type::t_void };
1026
entry_point.return_semantic.clear();
1027
entry_point.parameter_list.clear();
1028
1029
std::unordered_map<std::string, std::string> semantic_to_varying_variable;
1030
1031
const auto create_varying_variable = [this, stype = func.type, &semantic_to_varying_variable](type type, unsigned int extra_qualifiers, const std::string &name, const std::string &semantic) {
1032
// Skip built in variables
1033
if (!semantic_to_builtin(std::string(), semantic, stype).empty())
1034
return;
1035
1036
// Do not create multiple input/output variables for duplicate semantic usage (since every input/output location may only be defined once in GLSL)
1037
if ((extra_qualifiers & type::q_in) != 0 &&
1038
!semantic_to_varying_variable.emplace(semantic, name).second)
1039
return;
1040
1041
type.qualifiers |= extra_qualifiers;
1042
assert((type.has(type::q_in) || type.has(type::q_out)) && !type.has(type::q_inout));
1043
1044
// OpenGL does not allow varying of type boolean
1045
if (type.is_boolean())
1046
type.base = type::t_float;
1047
1048
const uint32_t location = semantic_to_location(semantic, std::max(1u, type.array_length));
1049
1050
for (unsigned int a = 0; a < std::max(1u, type.array_length); ++a)
1051
{
1052
_current_function_declaration += "layout(location = " + std::to_string(location + a) + ") ";
1053
write_type<false, false, true>(_current_function_declaration, type);
1054
_current_function_declaration += ' ';
1055
_current_function_declaration += escape_name(type.is_array() ? name + '_' + std::to_string(a) : name);
1056
_current_function_declaration += ";\n";
1057
}
1058
};
1059
1060
// Translate return value to output variable
1061
if (func.return_type.is_struct())
1062
{
1063
const struct_type &definition = get_struct(func.return_type.struct_definition);
1064
1065
for (const member_type &member : definition.member_list)
1066
create_varying_variable(member.type, type::q_out, "_return_" + member.name, member.semantic);
1067
}
1068
else if (!func.return_type.is_void())
1069
{
1070
create_varying_variable(func.return_type, type::q_out, "_return", func.return_semantic);
1071
}
1072
1073
// Translate function parameters to input/output variables
1074
for (const member_type &param : func.parameter_list)
1075
{
1076
type param_type = param.type;
1077
param_type.qualifiers &= ~type::q_inout;
1078
1079
// Create separate input/output variables for "inout" parameters (since "inout" is not valid on those in GLSL)
1080
if (param.type.has(type::q_in))
1081
{
1082
// Flatten structure parameters
1083
if (param_type.is_struct())
1084
{
1085
const struct_type &definition = get_struct(param_type.struct_definition);
1086
1087
for (unsigned int a = 0, array_length = std::max(1u, param_type.array_length); a < array_length; a++)
1088
{
1089
for (const member_type &member : definition.member_list)
1090
create_varying_variable(member.type, param_type.qualifiers | type::q_in, "_in_" + id_to_name(param.id) + '_' + std::to_string(a) + '_' + member.name, member.semantic);
1091
}
1092
}
1093
else
1094
{
1095
create_varying_variable(param_type, type::q_in, "_in_" + id_to_name(param.id), param.semantic);
1096
}
1097
}
1098
1099
if (param.type.has(type::q_out))
1100
{
1101
if (param_type.is_struct())
1102
{
1103
const struct_type &definition = get_struct(param_type.struct_definition);
1104
1105
for (unsigned int a = 0, array_length = std::max(1u, param_type.array_length); a < array_length; a++)
1106
{
1107
for (const member_type &member : definition.member_list)
1108
create_varying_variable(member.type, param_type.qualifiers | type::q_out, "_out_" + id_to_name(param.id) + '_' + std::to_string(a) + '_' + member.name, member.semantic);
1109
}
1110
}
1111
else
1112
{
1113
create_varying_variable(param_type, type::q_out, "_out_" + id_to_name(param.id), param.semantic);
1114
}
1115
}
1116
}
1117
1118
define_function({}, entry_point);
1119
enter_block(create_block());
1120
1121
std::string &code = _blocks.at(_current_block);
1122
1123
// Handle input parameters
1124
for (const member_type &param : func.parameter_list)
1125
{
1126
if (param.type.has(type::q_in))
1127
{
1128
const std::string param_name = id_to_name(param.id);
1129
1130
// Create local array element variables
1131
for (unsigned int a = 0, array_length = std::max(1u, param.type.array_length); a < array_length; a++)
1132
{
1133
if (param.type.is_struct())
1134
{
1135
// Build struct from separate member input variables
1136
code += '\t';
1137
write_type<false, true>(code, param.type);
1138
code += ' ';
1139
code += escape_name(param.type.is_array() ? "_in_" + param_name + '_' + std::to_string(a) : "_in_" + param_name);
1140
code += " = ";
1141
write_type<false, false>(code, param.type);
1142
code += '(';
1143
1144
const struct_type &struct_definition = get_struct(param.type.struct_definition);
1145
1146
for (const member_type &member : struct_definition.member_list)
1147
{
1148
std::string in_param_name;
1149
{
1150
if (const auto it = semantic_to_varying_variable.find(member.semantic);
1151
it != semantic_to_varying_variable.end())
1152
in_param_name = it->second;
1153
else
1154
in_param_name = "_in_" + param_name + '_' + std::to_string(a) + '_' + member.name;
1155
}
1156
1157
if (member.type.is_array())
1158
{
1159
write_type<false, false>(code, member.type);
1160
code += "[](";
1161
1162
for (unsigned int b = 0; b < member.type.array_length; b++)
1163
{
1164
// OpenGL does not allow varying of type boolean, so need to cast here
1165
if (member.type.is_boolean())
1166
{
1167
write_type<false, false>(code, member.type);
1168
code += '(';
1169
}
1170
1171
code += escape_name(in_param_name + '_' + std::to_string(b));
1172
1173
if (member.type.is_boolean())
1174
code += ')';
1175
1176
code += ", ";
1177
}
1178
1179
// Remove trailing ", "
1180
code.erase(code.size() - 2);
1181
1182
code += ')';
1183
}
1184
else
1185
{
1186
if (member.type.is_boolean() || (_gles && member.type.is_integral()))
1187
{
1188
write_type<false, false>(code, member.type);
1189
code += '(';
1190
}
1191
1192
code += semantic_to_builtin(std::move(in_param_name), member.semantic, func.type);
1193
1194
if (member.type.is_boolean() || (_gles && member.type.is_integral()))
1195
code += ')';
1196
}
1197
1198
code += ", ";
1199
}
1200
1201
// There can be no empty structs, so can assume that the last two characters are always ", "
1202
code.erase(code.size() - 2);
1203
1204
code += ");\n";
1205
}
1206
else
1207
if (const auto it = semantic_to_varying_variable.find(param.semantic);
1208
it != semantic_to_varying_variable.end() &&
1209
it->second != "_in_" + id_to_name(param.id))
1210
{
1211
// Create local variables for duplicated semantics (since no input/output variable is created for those, see 'create_varying_variable')
1212
code += '\t';
1213
write_type<false, true>(code, param.type);
1214
code += ' ';
1215
code += escape_name(param.type.is_array() ? "_in_" + id_to_name(param.id) + '_' + std::to_string(a) : "_in_" + id_to_name(param.id));
1216
code += " = ";
1217
1218
if (param.type.is_boolean())
1219
{
1220
write_type<false, false>(code, param.type);
1221
code += '(';
1222
}
1223
1224
code += escape_name(param.type.is_array() ? it->second + '_' + std::to_string(a) : it->second);
1225
1226
if (param.type.is_boolean())
1227
code += ')';
1228
1229
code += ";\n";
1230
}
1231
}
1232
}
1233
1234
// Create local parameter variables which are used as arguments in the entry point function call below
1235
code += '\t';
1236
write_type<false, true>(code, param.type);
1237
code += ' ';
1238
code += id_to_name(param.id);
1239
if (param.type.is_array())
1240
code += '[' + std::to_string(param.type.array_length) + ']';
1241
1242
// Initialize those local variables with the input value if existing
1243
// Parameters with only an "out" qualifier are written to by the entry point function, so do not need to be initialized
1244
if (param.type.has(type::q_in))
1245
{
1246
code += " = ";
1247
1248
// Build array from separate array element variables
1249
if (param.type.is_array())
1250
{
1251
write_type<false, false>(code, param.type);
1252
code += "[](";
1253
1254
for (unsigned int a = 0; a < param.type.array_length; ++a)
1255
{
1256
// OpenGL does not allow varying of type boolean, so need to cast here
1257
if (param.type.is_boolean())
1258
{
1259
write_type<false, false>(code, param.type);
1260
code += '(';
1261
}
1262
1263
code += escape_name("_in_" + id_to_name(param.id) + '_' + std::to_string(a));
1264
1265
if (param.type.is_boolean())
1266
code += ')';
1267
1268
code += ", ";
1269
}
1270
1271
// Remove trailing ", "
1272
code.erase(code.size() - 2);
1273
1274
code += ')';
1275
}
1276
else
1277
{
1278
if (param.type.is_boolean() || (_gles && param.type.is_integral()))
1279
{
1280
write_type<false, false>(code, param.type);
1281
code += '(';
1282
}
1283
1284
code += semantic_to_builtin("_in_" + id_to_name(param.id), param.semantic, func.type);
1285
1286
if (param.type.is_boolean() || (_gles && param.type.is_integral()))
1287
code += ')';
1288
}
1289
}
1290
1291
code += ";\n";
1292
}
1293
1294
code += '\t';
1295
// Structs cannot be output variables, so have to write to a temporary first and then output each member separately
1296
if (func.return_type.is_struct())
1297
{
1298
write_type(code, func.return_type);
1299
code += " _return = ";
1300
}
1301
// All other output types can write to the output variable directly
1302
else if (!func.return_type.is_void())
1303
{
1304
code += semantic_to_builtin("_return", func.return_semantic, func.type);
1305
code += " = ";
1306
}
1307
1308
// Call the function this entry point refers to
1309
code += id_to_name(func.id) + '(';
1310
1311
for (const member_type &param : func.parameter_list)
1312
{
1313
code += id_to_name(param.id);
1314
code += ", ";
1315
}
1316
1317
// Remove trailing ", "
1318
if (!func.parameter_list.empty())
1319
code.erase(code.size() - 2);
1320
1321
code += ");\n";
1322
1323
// Handle output parameters
1324
for (const member_type &param : func.parameter_list)
1325
{
1326
if (param.type.has(type::q_out))
1327
{
1328
const std::string param_name = id_to_name(param.id);
1329
1330
if (param.type.is_struct())
1331
{
1332
const struct_type &definition = get_struct(param.type.struct_definition);
1333
1334
// Split out struct fields into separate output variables again
1335
for (unsigned int a = 0, array_length = std::max(1u, param.type.array_length); a < array_length; a++)
1336
{
1337
for (const member_type &member : definition.member_list)
1338
{
1339
if (member.type.is_array())
1340
{
1341
for (unsigned int b = 0; b < member.type.array_length; b++)
1342
{
1343
code += '\t';
1344
code += escape_name("_out_" + param_name + '_' + std::to_string(a) + '_' + member.name + '_' + std::to_string(b));
1345
code += " = ";
1346
1347
// OpenGL does not allow varying of type boolean, so need to cast here
1348
if (member.type.is_boolean())
1349
{
1350
type varying_type = member.type;
1351
varying_type.base = type::t_float;
1352
write_type<false, false>(code, varying_type);
1353
code += '(';
1354
}
1355
1356
code += param_name;
1357
if (param.type.is_array())
1358
code += '[' + std::to_string(a) + ']';
1359
code += '.';
1360
code += member.name;
1361
code += '[' + std::to_string(b) + ']';
1362
1363
if (member.type.is_boolean())
1364
code += ')';
1365
1366
code += ";\n";
1367
}
1368
}
1369
else
1370
{
1371
code += '\t';
1372
code += semantic_to_builtin("_out_" + param_name + '_' + std::to_string(a) + '_' + member.name, member.semantic, func.type);
1373
code += " = ";
1374
1375
if (member.type.is_boolean())
1376
{
1377
type varying_type = member.type;
1378
varying_type.base = type::t_float;
1379
write_type<false, false>(code, varying_type);
1380
code += '(';
1381
}
1382
1383
code += param_name;
1384
if (param.type.is_array())
1385
code += '[' + std::to_string(a) + ']';
1386
code += '.';
1387
code += member.name;
1388
1389
if (member.type.is_boolean())
1390
code += ')';
1391
1392
code += ";\n";
1393
}
1394
}
1395
}
1396
}
1397
else if (param.type.is_array())
1398
{
1399
// Split up array output into individual array elements again
1400
for (unsigned int a = 0; a < param.type.array_length; a++)
1401
{
1402
code += '\t';
1403
code += escape_name("_out_" + param_name + '_' + std::to_string(a));
1404
code += " = ";
1405
1406
// OpenGL does not allow varying of type boolean, so need to cast here
1407
if (param.type.is_boolean())
1408
{
1409
type varying_type = param.type;
1410
varying_type.base = type::t_float;
1411
write_type<false, false>(code, varying_type);
1412
code += '(';
1413
}
1414
1415
code += param_name;
1416
code += '[' + std::to_string(a) + ']';
1417
1418
if (param.type.is_boolean())
1419
code += ')';
1420
1421
code += ";\n";
1422
}
1423
}
1424
else
1425
{
1426
code += '\t';
1427
code += semantic_to_builtin("_out_" + param_name, param.semantic, func.type);
1428
code += " = ";
1429
1430
if (param.type.is_boolean())
1431
{
1432
type varying_type = param.type;
1433
varying_type.base = type::t_float;
1434
write_type<false, false>(code, varying_type);
1435
code += '(';
1436
}
1437
1438
code += param_name;
1439
1440
if (param.type.is_boolean())
1441
code += ')';
1442
1443
code += ";\n";
1444
}
1445
}
1446
}
1447
1448
// Handle return struct output variables
1449
if (func.return_type.is_struct())
1450
{
1451
const struct_type &struct_definition = get_struct(func.return_type.struct_definition);
1452
1453
for (const member_type &member : struct_definition.member_list)
1454
{
1455
code += '\t';
1456
code += semantic_to_builtin("_return_" + member.name, member.semantic, func.type);
1457
code += " = _return." + escape_name(member.name) + ";\n";
1458
}
1459
}
1460
1461
// Add code to flip the output vertically
1462
if (_flip_vert_y && func.type == shader_type::vertex)
1463
code += "\tgl_Position.y = -gl_Position.y;\n";
1464
1465
leave_block_and_return(0);
1466
leave_function();
1467
}
1468
1469
id emit_load(const expression &exp, bool force_new_id) override
1470
{
1471
if (exp.is_constant)
1472
return emit_constant(exp.type, exp.constant);
1473
else if (exp.chain.empty() && !force_new_id) // Can refer to values without access chain directly
1474
return exp.base;
1475
1476
const id res = make_id();
1477
1478
std::string type, expr_code = id_to_name(exp.base);
1479
1480
for (const expression::operation &op : exp.chain)
1481
{
1482
switch (op.op)
1483
{
1484
case expression::operation::op_cast:
1485
type.clear();
1486
write_type<false, false>(type, op.to);
1487
expr_code = type + '(' + expr_code + ')';
1488
break;
1489
case expression::operation::op_member:
1490
expr_code += '.';
1491
expr_code += escape_name(get_struct(op.from.struct_definition).member_list[op.index].name);
1492
break;
1493
case expression::operation::op_dynamic_index:
1494
// For matrices this will extract a column, but that is fine, since they are initialized column-wise too
1495
// Also cast to an integer, since it could be a boolean too, but GLSL does not allow those in index expressions
1496
expr_code += "[int(" + id_to_name(op.index) + ")]";
1497
break;
1498
case expression::operation::op_constant_index:
1499
if (op.from.is_vector() && !op.from.is_array())
1500
expr_code += '.',
1501
expr_code += "xyzw"[op.index];
1502
else
1503
expr_code += '[' + std::to_string(op.index) + ']';
1504
break;
1505
case expression::operation::op_swizzle:
1506
if (op.from.is_matrix())
1507
{
1508
if (op.swizzle[1] < 0)
1509
{
1510
const char row = (op.swizzle[0] % 4);
1511
const char col = (op.swizzle[0] - row) / 4;
1512
1513
expr_code += '[';
1514
expr_code += to_digit(row);
1515
expr_code += "][";
1516
expr_code += to_digit(col);
1517
expr_code += ']';
1518
}
1519
else
1520
{
1521
// TODO: Implement matrix to vector swizzles
1522
assert(false);
1523
expr_code += "_NOT_IMPLEMENTED_"; // Make sure compilation fails
1524
}
1525
}
1526
else
1527
{
1528
// can't swizzle scalars
1529
if (_gles && op.from.is_scalar())
1530
{
1531
// => e.g. vec3(expr, expr, expr).xyz
1532
type.clear();
1533
write_type<false, false>(type, op.to);
1534
std::string new_code = type;
1535
new_code += '(';
1536
1537
const unsigned int components = op.to.components();
1538
for (unsigned int i = 0; i < components; ++i)
1539
{
1540
if (i > 0)
1541
new_code += ',';
1542
new_code += '(' + expr_code + ')';
1543
}
1544
new_code += ')';
1545
expr_code = std::move(new_code);
1546
}
1547
else
1548
{
1549
expr_code += '.';
1550
for (int i = 0; i < 4 && op.swizzle[i] >= 0; ++i)
1551
expr_code += "xyzw"[op.swizzle[i]];
1552
}
1553
}
1554
break;
1555
}
1556
}
1557
1558
// GLSL matrices are always floating point, so need to cast result to the target type
1559
if (!exp.chain.empty() && exp.chain[0].from.is_matrix() && !exp.chain[0].from.is_floating_point())
1560
{
1561
type.clear();
1562
write_type<false, false>(type, exp.type);
1563
expr_code = type + '(' + expr_code + ')';
1564
}
1565
1566
if (force_new_id)
1567
{
1568
// Need to store value in a new variable to comply with request for a new ID
1569
std::string &code = _blocks.at(_current_block);
1570
1571
code += '\t';
1572
write_type(code, exp.type);
1573
code += ' ' + id_to_name(res) + " = " + expr_code + ";\n";
1574
}
1575
else
1576
{
1577
// Avoid excessive variable definitions by instancing simple load operations in code every time
1578
define_name<naming::expression>(res, std::move(expr_code));
1579
}
1580
1581
return res;
1582
}
1583
void emit_store(const expression &exp, id value) override
1584
{
1585
if (const auto it = _remapped_sampler_variables.find(exp.base);
1586
it != _remapped_sampler_variables.end())
1587
{
1588
assert(it->second == 0);
1589
it->second = value;
1590
return;
1591
}
1592
1593
std::string &code = _blocks.at(_current_block);
1594
1595
write_location(code, exp.location);
1596
1597
code += '\t' + id_to_name(exp.base);
1598
1599
for (const expression::operation &op : exp.chain)
1600
{
1601
switch (op.op)
1602
{
1603
case expression::operation::op_member:
1604
code += '.';
1605
code += escape_name(get_struct(op.from.struct_definition).member_list[op.index].name);
1606
break;
1607
case expression::operation::op_dynamic_index:
1608
code += "[int(" + id_to_name(op.index) + ")]";
1609
break;
1610
case expression::operation::op_constant_index:
1611
code += '[' + std::to_string(op.index) + ']';
1612
break;
1613
case expression::operation::op_swizzle:
1614
if (op.from.is_matrix())
1615
{
1616
if (op.swizzle[1] < 0)
1617
{
1618
const char row = (op.swizzle[0] % 4);
1619
const char col = (op.swizzle[0] - row) / 4;
1620
1621
code += '[';
1622
code += '1' + row - 1;
1623
code += "][";
1624
code += '1' + col - 1;
1625
code += ']';
1626
}
1627
else
1628
{
1629
// TODO: Implement matrix to vector swizzles
1630
assert(false);
1631
code += "_NOT_IMPLEMENTED_"; // Make sure compilation fails
1632
}
1633
}
1634
else
1635
{
1636
code += '.';
1637
for (int i = 0; i < 4 && op.swizzle[i] >= 0; ++i)
1638
code += "xyzw"[op.swizzle[i]];
1639
}
1640
break;
1641
}
1642
}
1643
1644
code += " = ";
1645
1646
// GLSL matrices are always floating point, so need to cast type
1647
if (!exp.chain.empty() && exp.chain[0].from.is_matrix() && !exp.chain[0].from.is_floating_point())
1648
// Only supporting scalar assignments to matrices currently, so can assume to always cast to float
1649
code += "float(" + id_to_name(value) + ");\n";
1650
else
1651
code += id_to_name(value) + ";\n";
1652
}
1653
1654
id emit_constant(const type &data_type, const constant &data) override
1655
{
1656
const id res = make_id();
1657
1658
if (data_type.is_array() || data_type.is_struct())
1659
{
1660
assert(data_type.has(type::q_const));
1661
1662
if (const auto it = std::find_if(_constant_lookup.begin(), _constant_lookup.end(),
1663
[&data_type, &data](const std::tuple<type, constant, id> &x) {
1664
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()))
1665
return false;
1666
for (size_t i = 0; i < data.array_data.size(); ++i)
1667
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)
1668
return false;
1669
return true;
1670
});
1671
it != _constant_lookup.end())
1672
return std::get<2>(*it); // Reuse existing constant instead of duplicating the definition
1673
else if (data_type.is_array())
1674
_constant_lookup.push_back({ data_type, data, res });
1675
1676
// Put constant variable into global scope, so that it can be reused in different blocks
1677
std::string &code = _blocks.at(0);
1678
1679
// GLSL requires constants to be initialized, but struct initialization is not supported right now
1680
if (!data_type.is_struct())
1681
code += "const ";
1682
1683
write_type<false, false>(code, data_type);
1684
code += ' ' + id_to_name(res);
1685
1686
// Array constants need to be stored in a constant variable as they cannot be used in-place
1687
if (data_type.is_array())
1688
code += '[' + std::to_string(data_type.array_length) + ']';
1689
1690
// Struct initialization is not supported right now
1691
if (!data_type.is_struct()) {
1692
code += " = ";
1693
write_constant(code, data_type, data);
1694
}
1695
1696
code += ";\n";
1697
return res;
1698
}
1699
1700
std::string code;
1701
write_constant(code, data_type, data);
1702
define_name<naming::expression>(res, std::move(code));
1703
1704
return res;
1705
}
1706
1707
id emit_unary_op(const location &loc, tokenid op, const type &res_type, id val) override
1708
{
1709
const id res = make_id();
1710
1711
std::string &code = _blocks.at(_current_block);
1712
1713
write_location(code, loc);
1714
1715
code += '\t';
1716
write_type(code, res_type);
1717
code += ' ' + id_to_name(res) + " = ";
1718
1719
switch (op)
1720
{
1721
case tokenid::minus:
1722
code += '-';
1723
break;
1724
case tokenid::tilde:
1725
code += '~';
1726
break;
1727
case tokenid::exclaim:
1728
if (res_type.is_vector())
1729
code += "not";
1730
else
1731
code += "!bool";
1732
break;
1733
default:
1734
assert(false);
1735
}
1736
1737
code += '(' + id_to_name(val) + ");\n";
1738
1739
return res;
1740
}
1741
id emit_binary_op(const location &loc, tokenid op, const type &res_type, const type &exp_type, id lhs, id rhs) override
1742
{
1743
const id res = make_id();
1744
1745
std::string &code = _blocks.at(_current_block);
1746
1747
write_location(code, loc);
1748
1749
code += '\t';
1750
write_type(code, res_type);
1751
code += ' ' + id_to_name(res) + " = ";
1752
1753
std::string intrinsic, operator_code;
1754
1755
switch (op)
1756
{
1757
case tokenid::plus:
1758
case tokenid::plus_plus:
1759
case tokenid::plus_equal:
1760
operator_code = '+';
1761
break;
1762
case tokenid::minus:
1763
case tokenid::minus_minus:
1764
case tokenid::minus_equal:
1765
operator_code = '-';
1766
break;
1767
case tokenid::star:
1768
case tokenid::star_equal:
1769
if (exp_type.is_matrix())
1770
intrinsic = "matrixCompMult";
1771
else
1772
operator_code = '*';
1773
break;
1774
case tokenid::slash:
1775
case tokenid::slash_equal:
1776
operator_code = '/';
1777
break;
1778
case tokenid::percent:
1779
case tokenid::percent_equal:
1780
if (exp_type.is_floating_point())
1781
intrinsic = "fmodHLSL",
1782
_uses_fmod = true;
1783
else
1784
operator_code = '%';
1785
break;
1786
case tokenid::caret:
1787
case tokenid::caret_equal:
1788
operator_code = '^';
1789
break;
1790
case tokenid::pipe:
1791
case tokenid::pipe_equal:
1792
operator_code = '|';
1793
break;
1794
case tokenid::ampersand:
1795
case tokenid::ampersand_equal:
1796
operator_code = '&';
1797
break;
1798
case tokenid::less_less:
1799
case tokenid::less_less_equal:
1800
operator_code = "<<";
1801
break;
1802
case tokenid::greater_greater:
1803
case tokenid::greater_greater_equal:
1804
operator_code = ">>";
1805
break;
1806
case tokenid::pipe_pipe:
1807
if (exp_type.is_vector())
1808
intrinsic = "compOr",
1809
_uses_componentwise_or = true;
1810
else
1811
operator_code = "||";
1812
break;
1813
case tokenid::ampersand_ampersand:
1814
if (exp_type.is_vector())
1815
intrinsic = "compAnd",
1816
_uses_componentwise_and = true;
1817
else
1818
operator_code = "&&";
1819
break;
1820
case tokenid::less:
1821
if (exp_type.is_vector())
1822
intrinsic = "lessThan";
1823
else
1824
operator_code = '<';
1825
break;
1826
case tokenid::less_equal:
1827
if (exp_type.is_vector())
1828
intrinsic = "lessThanEqual";
1829
else
1830
operator_code = "<=";
1831
break;
1832
case tokenid::greater:
1833
if (exp_type.is_vector())
1834
intrinsic = "greaterThan";
1835
else
1836
operator_code = '>';
1837
break;
1838
case tokenid::greater_equal:
1839
if (exp_type.is_vector())
1840
intrinsic = "greaterThanEqual";
1841
else
1842
operator_code = ">=";
1843
break;
1844
case tokenid::equal_equal:
1845
if (exp_type.is_vector())
1846
intrinsic = "equal";
1847
else
1848
operator_code = "==";
1849
break;
1850
case tokenid::exclaim_equal:
1851
if (exp_type.is_vector())
1852
intrinsic = "notEqual";
1853
else
1854
operator_code = "!=";
1855
break;
1856
default:
1857
assert(false);
1858
}
1859
1860
if (!intrinsic.empty())
1861
code += intrinsic + '(' + id_to_name(lhs) + ", " + id_to_name(rhs) + ')';
1862
else
1863
code += id_to_name(lhs) + ' ' + operator_code + ' ' + id_to_name(rhs);
1864
1865
code += ";\n";
1866
1867
return res;
1868
}
1869
id emit_ternary_op(const location &loc, tokenid op, const type &res_type, id condition, id true_value, id false_value) override
1870
{
1871
if (op != tokenid::question)
1872
return assert(false), 0; // Should never happen, since this is the only ternary operator currently supported
1873
1874
const id res = make_id();
1875
1876
std::string &code = _blocks.at(_current_block);
1877
1878
write_location(code, loc);
1879
1880
code += '\t';
1881
write_type(code, res_type);
1882
code += ' ' + id_to_name(res);
1883
1884
if (res_type.is_array())
1885
code += '[' + std::to_string(res_type.array_length) + ']';
1886
1887
code += " = ";
1888
1889
if (res_type.is_vector())
1890
code += "compCond(" + id_to_name(condition) + ", " + id_to_name(true_value) + ", " + id_to_name(false_value) + ");\n",
1891
_uses_componentwise_cond = true;
1892
else // GLSL requires the conditional expression to be a scalar boolean
1893
code += id_to_name(condition) + " ? " + id_to_name(true_value) + " : " + id_to_name(false_value) + ";\n";
1894
1895
return res;
1896
}
1897
id emit_call(const location &loc, id function, const type &res_type, const std::vector<expression> &args) override
1898
{
1899
#ifndef NDEBUG
1900
for (const expression &arg : args)
1901
assert(arg.chain.empty() && arg.base != 0);
1902
#endif
1903
1904
const id res = make_id();
1905
1906
std::string &code = _blocks.at(_current_block);
1907
1908
write_location(code, loc);
1909
1910
code += '\t';
1911
1912
if (!res_type.is_void())
1913
{
1914
write_type(code, res_type);
1915
code += ' ' + id_to_name(res);
1916
1917
if (res_type.is_array())
1918
code += '[' + std::to_string(res_type.array_length) + ']';
1919
1920
code += " = ";
1921
}
1922
1923
code += id_to_name(function) + '(';
1924
1925
for (const expression &arg : args)
1926
{
1927
code += id_to_name(arg.base);
1928
code += ", ";
1929
}
1930
1931
// Remove trailing ", "
1932
if (!args.empty())
1933
code.erase(code.size() - 2);
1934
1935
code += ");\n";
1936
1937
return res;
1938
}
1939
id emit_call_intrinsic(const location &loc, id intrinsic, const type &res_type, const std::vector<expression> &args) override
1940
{
1941
#ifndef NDEBUG
1942
for (const expression &arg : args)
1943
assert(arg.chain.empty() && arg.base != 0);
1944
#endif
1945
1946
const id res = make_id();
1947
1948
std::string &code = _blocks.at(_current_block);
1949
1950
write_location(code, loc);
1951
1952
code += '\t';
1953
1954
if (!res_type.is_void())
1955
{
1956
write_type(code, res_type);
1957
code += ' ' + id_to_name(res) + " = ";
1958
}
1959
1960
enum
1961
{
1962
#define IMPLEMENT_INTRINSIC_GLSL(name, i, code) name##i,
1963
#include "effect_symbol_table_intrinsics.inl"
1964
};
1965
1966
switch (intrinsic)
1967
{
1968
#define IMPLEMENT_INTRINSIC_GLSL(name, i, code) case name##i: code break;
1969
#include "effect_symbol_table_intrinsics.inl"
1970
default:
1971
assert(false);
1972
}
1973
1974
code += ";\n";
1975
1976
return res;
1977
}
1978
id emit_construct(const location &loc, const type &res_type, const std::vector<expression> &args) override
1979
{
1980
#ifndef NDEBUG
1981
for (const expression &arg : args)
1982
assert((arg.type.is_scalar() || res_type.is_array()) && arg.chain.empty() && arg.base != 0);
1983
#endif
1984
1985
const id res = make_id();
1986
1987
std::string &code = _blocks.at(_current_block);
1988
1989
write_location(code, loc);
1990
1991
code += '\t';
1992
write_type(code, res_type);
1993
code += ' ' + id_to_name(res);
1994
1995
if (res_type.is_array())
1996
code += '[' + std::to_string(res_type.array_length) + ']';
1997
1998
code += " = ";
1999
2000
write_type<false, false>(code, res_type);
2001
2002
if (res_type.is_array())
2003
code += '[' + std::to_string(res_type.array_length) + ']';
2004
2005
code += '(';
2006
2007
for (const expression &arg : args)
2008
{
2009
code += id_to_name(arg.base);
2010
code += ", ";
2011
}
2012
2013
// Remove trailing ", "
2014
if (!args.empty())
2015
code.erase(code.size() - 2);
2016
2017
code += ");\n";
2018
2019
return res;
2020
}
2021
2022
void emit_if(const location &loc, id condition_value, id condition_block, id true_statement_block, id false_statement_block, unsigned int flags) override
2023
{
2024
assert(condition_value != 0 && condition_block != 0 && true_statement_block != 0 && false_statement_block != 0);
2025
2026
std::string &code = _blocks.at(_current_block);
2027
2028
std::string &true_statement_data = _blocks.at(true_statement_block);
2029
std::string &false_statement_data = _blocks.at(false_statement_block);
2030
2031
increase_indentation_level(true_statement_data);
2032
increase_indentation_level(false_statement_data);
2033
2034
code += _blocks.at(condition_block);
2035
2036
write_location(code, loc);
2037
2038
if (flags != 0 && !_gles)
2039
{
2040
_uses_control_flow_attributes = true;
2041
2042
code += "#if GL_EXT_control_flow_attributes\n\t[[";
2043
if ((flags & 0x1) == 0x1)
2044
code += "flatten";
2045
if ((flags & 0x3) == 0x3)
2046
code += ", ";
2047
if ((flags & 0x2) == 0x2)
2048
code += "dont_flatten";
2049
code += "]]\n#endif\n";
2050
}
2051
2052
code += '\t';
2053
code += "if (" + id_to_name(condition_value) + ")\n\t{\n";
2054
code += true_statement_data;
2055
code += "\t}\n";
2056
2057
if (!false_statement_data.empty())
2058
{
2059
code += "\telse\n\t{\n";
2060
code += false_statement_data;
2061
code += "\t}\n";
2062
}
2063
2064
// Remove consumed blocks to save memory
2065
_blocks.erase(condition_block);
2066
_blocks.erase(true_statement_block);
2067
_blocks.erase(false_statement_block);
2068
}
2069
id emit_phi(const location &loc, id condition_value, id condition_block, id true_value, id true_statement_block, id false_value, id false_statement_block, const type &res_type) override
2070
{
2071
assert(condition_value != 0 && condition_block != 0 && true_value != 0 && true_statement_block != 0 && false_value != 0 && false_statement_block != 0);
2072
2073
std::string &code = _blocks.at(_current_block);
2074
2075
std::string &true_statement_data = _blocks.at(true_statement_block);
2076
std::string &false_statement_data = _blocks.at(false_statement_block);
2077
2078
increase_indentation_level(true_statement_data);
2079
increase_indentation_level(false_statement_data);
2080
2081
const id res = make_id();
2082
2083
code += _blocks.at(condition_block);
2084
2085
code += '\t';
2086
write_type(code, res_type);
2087
code += ' ' + id_to_name(res) + ";\n";
2088
2089
write_location(code, loc);
2090
2091
code += "\tif (" + id_to_name(condition_value) + ")\n\t{\n";
2092
code += (true_statement_block != condition_block ? true_statement_data : std::string());
2093
code += "\t\t" + id_to_name(res) + " = " + id_to_name(true_value) + ";\n";
2094
code += "\t}\n\telse\n\t{\n";
2095
code += (false_statement_block != condition_block ? false_statement_data : std::string());
2096
code += "\t\t" + id_to_name(res) + " = " + id_to_name(false_value) + ";\n";
2097
code += "\t}\n";
2098
2099
// Remove consumed blocks to save memory
2100
_blocks.erase(condition_block);
2101
_blocks.erase(true_statement_block);
2102
_blocks.erase(false_statement_block);
2103
2104
return res;
2105
}
2106
void emit_loop(const location &loc, id condition_value, id prev_block, id header_block, id condition_block, id loop_block, id continue_block, unsigned int flags) override
2107
{
2108
assert(prev_block != 0 && header_block != 0 && loop_block != 0 && continue_block != 0);
2109
2110
std::string &code = _blocks.at(_current_block);
2111
2112
std::string &loop_data = _blocks.at(loop_block);
2113
std::string &continue_data = _blocks.at(continue_block);
2114
2115
increase_indentation_level(loop_data);
2116
increase_indentation_level(loop_data);
2117
increase_indentation_level(continue_data);
2118
2119
code += _blocks.at(prev_block);
2120
2121
std::string attributes;
2122
if (flags != 0 && !_gles)
2123
{
2124
_uses_control_flow_attributes = true;
2125
2126
attributes += "#if GL_EXT_control_flow_attributes\n\t[[";
2127
if ((flags & 0x1) == 0x1)
2128
attributes += "unroll";
2129
if ((flags & 0x3) == 0x3)
2130
attributes += ", ";
2131
if ((flags & 0x2) == 0x2)
2132
attributes += "dont_unroll";
2133
attributes += "]]\n#endif\n";
2134
}
2135
2136
// Condition value can be missing in infinite loop constructs like "for (;;)"
2137
std::string condition_name = condition_value != 0 ? id_to_name(condition_value) : "true";
2138
2139
if (condition_block == 0)
2140
{
2141
// Convert the last SSA variable initializer to an assignment statement
2142
const size_t pos_assign = continue_data.rfind(condition_name);
2143
const size_t pos_prev_assign = continue_data.rfind('\t', pos_assign);
2144
continue_data.erase(pos_prev_assign + 1, pos_assign - pos_prev_assign - 1);
2145
2146
// We need to add the continue block to all "continue" statements as well
2147
const std::string continue_id = "__CONTINUE__" + std::to_string(continue_block);
2148
for (size_t offset = 0; (offset = loop_data.find(continue_id, offset)) != std::string::npos; offset += continue_data.size())
2149
loop_data.replace(offset, continue_id.size(), continue_data);
2150
2151
code += "\tbool " + condition_name + ";\n";
2152
2153
write_location(code, loc);
2154
2155
code += attributes;
2156
code += '\t';
2157
code += "do\n\t{\n\t\t{\n";
2158
code += loop_data; // Encapsulate loop body into another scope, so not to confuse any local variables with the current iteration variable accessed in the continue block below
2159
code += "\t\t}\n";
2160
code += continue_data;
2161
code += "\t}\n\twhile (" + condition_name + ");\n";
2162
}
2163
else
2164
{
2165
std::string &condition_data = _blocks.at(condition_block);
2166
2167
// If the condition data is just a single line, then it is a simple expression, which we can just put into the loop condition as-is
2168
if (std::count(condition_data.begin(), condition_data.end(), '\n') == 1)
2169
{
2170
// Convert SSA variable initializer back to a condition expression
2171
const size_t pos_assign = condition_data.find('=');
2172
condition_data.erase(0, pos_assign + 2);
2173
const size_t pos_semicolon = condition_data.rfind(';');
2174
condition_data.erase(pos_semicolon);
2175
2176
condition_name = std::move(condition_data);
2177
assert(condition_data.empty());
2178
}
2179
else
2180
{
2181
code += condition_data;
2182
2183
increase_indentation_level(condition_data);
2184
2185
// Convert the last SSA variable initializer to an assignment statement
2186
const size_t pos_assign = condition_data.rfind(condition_name);
2187
const size_t pos_prev_assign = condition_data.rfind('\t', pos_assign);
2188
condition_data.erase(pos_prev_assign + 1, pos_assign - pos_prev_assign - 1);
2189
}
2190
2191
const std::string continue_id = "__CONTINUE__" + std::to_string(continue_block);
2192
for (size_t offset = 0; (offset = loop_data.find(continue_id, offset)) != std::string::npos; offset += continue_data.size())
2193
loop_data.replace(offset, continue_id.size(), continue_data + condition_data);
2194
2195
code += attributes;
2196
code += '\t';
2197
code += "while (" + condition_name + ")\n\t{\n\t\t{\n";
2198
code += loop_data;
2199
code += "\t\t}\n";
2200
code += continue_data;
2201
code += condition_data;
2202
code += "\t}\n";
2203
2204
_blocks.erase(condition_block);
2205
}
2206
2207
// Remove consumed blocks to save memory
2208
_blocks.erase(prev_block);
2209
_blocks.erase(header_block);
2210
_blocks.erase(loop_block);
2211
_blocks.erase(continue_block);
2212
}
2213
void emit_switch(const location &loc, id selector_value, 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) override
2214
{
2215
assert(selector_value != 0 && selector_block != 0 && default_label != 0 && default_block != 0);
2216
assert(case_blocks.size() == case_literal_and_labels.size() / 2);
2217
2218
std::string &code = _blocks.at(_current_block);
2219
2220
code += _blocks.at(selector_block);
2221
2222
write_location(code, loc);
2223
2224
code += "\tswitch (" + id_to_name(selector_value) + ")\n\t{\n";
2225
2226
std::vector<id> labels = case_literal_and_labels;
2227
for (size_t i = 0; i < labels.size(); i += 2)
2228
{
2229
if (labels[i + 1] == 0)
2230
continue; // Happens if a case was already handled, see below
2231
2232
code += "\tcase " + std::to_string(labels[i]) + ": ";
2233
2234
if (labels[i + 1] == default_label)
2235
{
2236
code += "default: ";
2237
default_label = 0;
2238
}
2239
else
2240
{
2241
for (size_t k = i + 2; k < labels.size(); k += 2)
2242
{
2243
if (labels[k + 1] == 0 || labels[k + 1] != labels[i + 1])
2244
continue;
2245
2246
code += "case " + std::to_string(labels[k]) + ": ";
2247
labels[k + 1] = 0;
2248
}
2249
}
2250
2251
assert(case_blocks[i / 2] != 0);
2252
std::string &case_data = _blocks.at(case_blocks[i / 2]);
2253
2254
increase_indentation_level(case_data);
2255
2256
code += "{\n";
2257
code += case_data;
2258
code += "\t}\n";
2259
}
2260
2261
2262
if (default_label != 0 && default_block != _current_block)
2263
{
2264
std::string &default_data = _blocks.at(default_block);
2265
2266
increase_indentation_level(default_data);
2267
2268
code += "\tdefault: {\n";
2269
code += default_data;
2270
code += "\t}\n";
2271
2272
_blocks.erase(default_block);
2273
}
2274
2275
code += "\t}\n";
2276
2277
// Remove consumed blocks to save memory
2278
_blocks.erase(selector_block);
2279
for (const id case_block : case_blocks)
2280
_blocks.erase(case_block);
2281
}
2282
2283
id create_block() override
2284
{
2285
const id res = make_id();
2286
2287
std::string &block = _blocks.emplace(res, std::string()).first->second;
2288
// Reserve a decently big enough memory block to avoid frequent reallocations
2289
block.reserve(4096);
2290
2291
return res;
2292
}
2293
id set_block(id id) override
2294
{
2295
_last_block = _current_block;
2296
_current_block = id;
2297
2298
return _last_block;
2299
}
2300
void enter_block(id id) override
2301
{
2302
_current_block = id;
2303
}
2304
id leave_block_and_kill() override
2305
{
2306
if (!is_in_block())
2307
return 0;
2308
2309
std::string &code = _blocks.at(_current_block);
2310
2311
code += "\tdiscard;\n";
2312
2313
const type &return_type = _current_function->return_type;
2314
if (!return_type.is_void())
2315
{
2316
// Add a return statement to exit functions in case discard is the last control flow statement
2317
code += "\treturn ";
2318
write_constant(code, return_type, constant());
2319
code += ";\n";
2320
}
2321
2322
return set_block(0);
2323
}
2324
id leave_block_and_return(id value) override
2325
{
2326
if (!is_in_block())
2327
return 0;
2328
2329
// Skip implicit return statement
2330
if (!_current_function->return_type.is_void() && value == 0)
2331
return set_block(0);
2332
2333
std::string &code = _blocks.at(_current_block);
2334
2335
code += "\treturn";
2336
2337
if (value != 0)
2338
code += ' ' + id_to_name(value);
2339
2340
code += ";\n";
2341
2342
return set_block(0);
2343
}
2344
id leave_block_and_switch(id, id) override
2345
{
2346
if (!is_in_block())
2347
return _last_block;
2348
2349
return set_block(0);
2350
}
2351
id leave_block_and_branch(id target, unsigned int loop_flow) override
2352
{
2353
if (!is_in_block())
2354
return _last_block;
2355
2356
std::string &code = _blocks.at(_current_block);
2357
2358
switch (loop_flow)
2359
{
2360
case 1:
2361
code += "\tbreak;\n";
2362
break;
2363
case 2: // Keep track of continue target block, so we can insert its code here later
2364
code += "__CONTINUE__" + std::to_string(target) + "\tcontinue;\n";
2365
break;
2366
}
2367
2368
return set_block(0);
2369
}
2370
id leave_block_and_branch_conditional(id, id, id) override
2371
{
2372
if (!is_in_block())
2373
return _last_block;
2374
2375
return set_block(0);
2376
}
2377
void leave_function() override
2378
{
2379
assert(_current_function != nullptr && _last_block != 0);
2380
2381
_blocks.emplace(_current_function->id, _current_function_declaration + "{\n" + _blocks.at(_last_block) + "}\n");
2382
2383
_current_function = nullptr;
2384
_current_function_declaration.clear();
2385
}
2386
};
2387
} // namespace
2388
2389
codegen *reshadefx::create_codegen_glsl(unsigned version, bool gles, bool vulkan_semantics, bool debug_info, bool uniforms_to_spec_constants, bool enable_16bit_types, bool flip_vert_y)
2390
{
2391
return new codegen_glsl(version, gles, vulkan_semantics, debug_info, uniforms_to_spec_constants, enable_16bit_types, flip_vert_y);
2392
}
2393
2394