Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/dep/reshadefx/src/effect_expression.cpp
4246 views
1
/*
2
* Copyright (C) 2014 Patrick Mours
3
* SPDX-License-Identifier: BSD-3-Clause
4
*/
5
6
#include "effect_expression.hpp"
7
#include <cmath> // std::fmod
8
#include <cassert>
9
#include <cstring> // std::memcpy, std::memset
10
#include <algorithm> // std::max, std::min
11
12
reshadefx::type reshadefx::type::merge(const type &lhs, const type &rhs)
13
{
14
type result;
15
result.base = std::max(lhs.base, rhs.base);
16
17
// Non-numeric types cannot be vectors or matrices
18
if (!result.is_numeric())
19
{
20
result.rows = 0;
21
result.cols = 0;
22
}
23
// If one side of the expression is scalar, it needs to be promoted to the same dimension as the other side
24
else if ((lhs.rows == 1 && lhs.cols == 1) || (rhs.rows == 1 && rhs.cols == 1))
25
{
26
result.rows = std::max(lhs.rows, rhs.rows);
27
result.cols = std::max(lhs.cols, rhs.cols);
28
}
29
else // Otherwise dimensions match or one side is truncated to match the other one
30
{
31
result.rows = std::min(lhs.rows, rhs.rows);
32
result.cols = std::min(lhs.cols, rhs.cols);
33
}
34
35
// Some qualifiers propagate to the result
36
result.qualifiers = (lhs.qualifiers & type::q_precise) | (rhs.qualifiers & type::q_precise);
37
38
// Cannot merge array types, assume no arrays
39
result.array_length = 0;
40
assert(lhs.array_length == 0 && rhs.array_length == 0);
41
42
// In case this is a structure, assume they are the same
43
result.struct_definition = rhs.struct_definition;
44
assert(lhs.struct_definition == rhs.struct_definition || lhs.struct_definition == 0);
45
46
return result;
47
}
48
49
std::string reshadefx::type::description() const
50
{
51
std::string result;
52
switch (base)
53
{
54
case t_void:
55
result = "void";
56
break;
57
case t_bool:
58
result = "bool";
59
break;
60
case t_min16int:
61
result = "min16int";
62
break;
63
case t_int:
64
result = "int";
65
break;
66
case t_min16uint:
67
result = "min16uint";
68
break;
69
case t_uint:
70
result = "uint";
71
break;
72
case t_min16float:
73
result = "min16float";
74
break;
75
case t_float:
76
result = "float";
77
break;
78
case t_string:
79
result = "string";
80
break;
81
case t_struct:
82
result = "struct";
83
break;
84
case t_texture1d:
85
result = "texture1D";
86
break;
87
case t_texture2d:
88
result = "texture2D";
89
break;
90
case t_texture3d:
91
result = "texture3D";
92
break;
93
case t_sampler1d_int:
94
result = "sampler1D<int" + std::to_string(rows) + '>';
95
break;
96
case t_sampler2d_int:
97
result = "sampler2D<int" + std::to_string(rows) + '>';
98
break;
99
case t_sampler3d_int:
100
result = "sampler3D<int" + std::to_string(rows) + '>';
101
break;
102
case t_sampler1d_uint:
103
result = "sampler1D<uint" + std::to_string(rows) + '>';
104
break;
105
case t_sampler2d_uint:
106
result = "sampler2D<uint" + std::to_string(rows) + '>';
107
break;
108
case t_sampler3d_uint:
109
result = "sampler3D<uint" + std::to_string(rows) + '>';
110
break;
111
case t_sampler1d_float:
112
result = "sampler1D<float" + std::to_string(rows) + '>';
113
break;
114
case t_sampler2d_float:
115
result = "sampler2D<float" + std::to_string(rows) + '>';
116
break;
117
case t_sampler3d_float:
118
result = "sampler3D<float" + std::to_string(rows) + '>';
119
break;
120
case t_storage1d_int:
121
result = "storage1D<int" + std::to_string(rows) + '>';
122
break;
123
case t_storage2d_int:
124
result = "storage2D<int" + std::to_string(rows) + '>';
125
break;
126
case t_storage3d_int:
127
result = "storage3D<int" + std::to_string(rows) + '>';
128
break;
129
case t_storage1d_uint:
130
result = "storage1D<uint" + std::to_string(rows) + '>';
131
break;
132
case t_storage2d_uint:
133
result = "storage2D<uint" + std::to_string(rows) + '>';
134
break;
135
case t_storage3d_uint:
136
result = "storage3D<uint" + std::to_string(rows) + '>';
137
break;
138
case t_storage1d_float:
139
result = "storage1D<float" + std::to_string(rows) + '>';
140
break;
141
case t_storage2d_float:
142
result = "storage2D<float" + std::to_string(rows) + '>';
143
break;
144
case t_storage3d_float:
145
result = "storage3D<float" + std::to_string(rows) + '>';
146
break;
147
case t_function:
148
assert(false);
149
break;
150
}
151
152
if (is_numeric())
153
{
154
if (rows > 1 || cols > 1)
155
result += std::to_string(rows);
156
if (cols > 1)
157
result += 'x' + std::to_string(cols);
158
}
159
160
if (is_array())
161
{
162
result += '[';
163
if (is_bounded_array())
164
result += std::to_string(array_length);
165
result += ']';
166
}
167
168
return result;
169
}
170
171
void reshadefx::expression::reset_to_lvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
172
{
173
type = in_type;
174
base = in_base;
175
location = loc;
176
is_lvalue = true;
177
is_constant = false;
178
chain.clear();
179
180
// Make sure uniform l-values cannot be assigned to by making them constant
181
if (in_type.has(type::q_uniform))
182
type.qualifiers |= type::q_const;
183
184
// Strip away global variable qualifiers
185
type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);
186
}
187
void reshadefx::expression::reset_to_rvalue(const reshadefx::location &loc, uint32_t in_base, const reshadefx::type &in_type)
188
{
189
type = in_type;
190
type.qualifiers |= type::q_const;
191
base = in_base;
192
location = loc;
193
is_lvalue = false;
194
is_constant = false;
195
chain.clear();
196
197
// Strip away global variable qualifiers
198
type.qualifiers &= ~(type::q_extern | type::q_static | type::q_uniform | type::q_groupshared);
199
}
200
201
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, bool data)
202
{
203
type = { type::t_bool, 1, 1, type::q_const };
204
base = 0; constant = {}; constant.as_uint[0] = data;
205
location = loc;
206
is_lvalue = false;
207
is_constant = true;
208
chain.clear();
209
}
210
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, float data)
211
{
212
type = { type::t_float, 1, 1, type::q_const };
213
base = 0; constant = {}; constant.as_float[0] = data;
214
location = loc;
215
is_lvalue = false;
216
is_constant = true;
217
chain.clear();
218
}
219
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, int32_t data)
220
{
221
type = { type::t_int, 1, 1, type::q_const };
222
base = 0; constant = {}; constant.as_int[0] = data;
223
location = loc;
224
is_lvalue = false;
225
is_constant = true;
226
chain.clear();
227
}
228
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, uint32_t data)
229
{
230
type = { type::t_uint, 1, 1, type::q_const };
231
base = 0; constant = {}; constant.as_uint[0] = data;
232
location = loc;
233
is_lvalue = false;
234
is_constant = true;
235
chain.clear();
236
}
237
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, std::string data)
238
{
239
type = { type::t_string, 0, 0, type::q_const };
240
base = 0; constant = {}; constant.string_data = std::move(data);
241
location = loc;
242
is_lvalue = false;
243
is_constant = true;
244
chain.clear();
245
}
246
void reshadefx::expression::reset_to_rvalue_constant(const reshadefx::location &loc, reshadefx::constant data, const reshadefx::type &in_type)
247
{
248
type = in_type;
249
type.qualifiers |= type::q_const;
250
base = 0; constant = std::move(data);
251
location = loc;
252
is_lvalue = false;
253
is_constant = true;
254
chain.clear();
255
}
256
257
void reshadefx::expression::add_cast_operation(const reshadefx::type &cast_type)
258
{
259
// First try to simplify the cast with a swizzle operation (only works with scalars and vectors)
260
if (type.cols == 1 && cast_type.cols == 1 && type.rows != cast_type.rows)
261
{
262
signed char swizzle[] = { 0, 1, 2, 3 };
263
// Ignore components in a demotion cast
264
for (unsigned int i = cast_type.rows; i < 4; ++i)
265
swizzle[i] = -1;
266
// Use the last component to fill in a promotion cast
267
for (unsigned int i = type.rows; i < cast_type.rows; ++i)
268
swizzle[i] = swizzle[type.rows - 1];
269
270
add_swizzle_access(swizzle, cast_type.rows);
271
}
272
273
if (type == cast_type)
274
return; // There is nothing more to do if the expression is already of the target type at this point
275
276
if (is_constant)
277
{
278
const auto cast_constant = [](reshadefx::constant &constant, const reshadefx::type &from, const reshadefx::type &to) {
279
// Handle scalar to vector promotion first
280
if (from.is_scalar() && !to.is_scalar())
281
for (unsigned int i = 1; i < to.components(); ++i)
282
constant.as_uint[i] = constant.as_uint[0];
283
284
// Next check whether the type needs casting as well (and don't convert between signed/unsigned, since that is handled by the union)
285
if (from.base == to.base || from.is_floating_point() == to.is_floating_point())
286
return;
287
288
if (!to.is_floating_point())
289
for (unsigned int i = 0; i < to.components(); ++i)
290
constant.as_uint[i] = static_cast<int>(constant.as_float[i]);
291
else
292
for (unsigned int i = 0; i < to.components(); ++i)
293
constant.as_float[i] = static_cast<float>(constant.as_int[i]);
294
};
295
296
for (struct constant &element : constant.array_data)
297
cast_constant(element, type, cast_type);
298
299
cast_constant(constant, type, cast_type);
300
}
301
else
302
{
303
assert(!type.is_array() && !cast_type.is_array());
304
305
chain.push_back({ operation::op_cast, type, cast_type });
306
}
307
308
type = cast_type;
309
type.qualifiers |= type::q_const; // Casting always makes expression not modifiable
310
}
311
void reshadefx::expression::add_member_access(unsigned int index, const reshadefx::type &in_type)
312
{
313
assert(type.is_struct());
314
315
chain.push_back({ operation::op_member, type, in_type, index });
316
317
// The type is now the type of the member that was accessed
318
type = in_type;
319
is_constant = false;
320
}
321
void reshadefx::expression::add_dynamic_index_access(uint32_t index_expression)
322
{
323
assert(!is_constant); // Cannot have dynamic indexing into constant in SPIR-V
324
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
325
326
struct type prev_type = type;
327
328
if (type.is_array())
329
{
330
type.array_length = 0;
331
}
332
else if (type.is_matrix())
333
{
334
type.rows = type.cols;
335
type.cols = 1;
336
}
337
else if (type.is_vector())
338
{
339
type.rows = 1;
340
}
341
342
chain.push_back({ operation::op_dynamic_index, prev_type, type, index_expression });
343
}
344
void reshadefx::expression::add_constant_index_access(unsigned int index)
345
{
346
assert(type.is_array() || (type.is_numeric() && !type.is_scalar()));
347
348
struct type prev_type = type;
349
350
if (type.is_array())
351
{
352
assert(index < type.array_length);
353
354
type.array_length = 0;
355
}
356
else if (type.is_matrix())
357
{
358
assert(index < type.components());
359
360
type.rows = type.cols;
361
type.cols = 1;
362
}
363
else if (type.is_vector())
364
{
365
assert(index < type.components());
366
367
type.rows = 1;
368
}
369
370
if (is_constant)
371
{
372
if (prev_type.is_array())
373
{
374
constant = constant.array_data[index];
375
}
376
else if (prev_type.is_matrix()) // Indexing into a matrix returns a row of it as a vector
377
{
378
for (unsigned int i = 0; i < prev_type.cols; ++i)
379
constant.as_uint[i] = constant.as_uint[index * prev_type.cols + i];
380
}
381
else // Indexing into a vector returns the element as a scalar
382
{
383
constant.as_uint[0] = constant.as_uint[index];
384
}
385
}
386
else
387
{
388
chain.push_back({ operation::op_constant_index, prev_type, type, index });
389
}
390
}
391
void reshadefx::expression::add_swizzle_access(const signed char swizzle[4], unsigned int length)
392
{
393
assert(type.is_numeric() && !type.is_array());
394
395
const struct type prev_type = type;
396
397
type.rows = length;
398
type.cols = 1;
399
400
if (is_constant)
401
{
402
assert(constant.array_data.empty());
403
404
uint32_t data[16];
405
std::memcpy(data, &constant.as_uint[0], sizeof(data));
406
for (unsigned int i = 0; i < length; ++i)
407
constant.as_uint[i] = data[swizzle[i]];
408
std::memset(&constant.as_uint[length], 0, sizeof(uint32_t) * (16 - length)); // Clear the rest of the constant
409
}
410
else if (length == 1 && prev_type.is_vector()) // Use indexing when possible since the code generation logic is simpler in SPIR-V
411
{
412
chain.push_back({ operation::op_constant_index, prev_type, type, static_cast<uint32_t>(swizzle[0]) });
413
}
414
else
415
{
416
chain.push_back({ operation::op_swizzle, prev_type, type, 0, { swizzle[0], swizzle[1], swizzle[2], swizzle[3] } });
417
}
418
}
419
420
bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op)
421
{
422
if (!is_constant)
423
return false;
424
425
switch (op)
426
{
427
case tokenid::exclaim:
428
for (unsigned int i = 0; i < type.components(); ++i)
429
constant.as_uint[i] = !constant.as_uint[i];
430
break;
431
case tokenid::minus:
432
if (type.is_floating_point())
433
for (unsigned int i = 0; i < type.components(); ++i)
434
constant.as_float[i] = -constant.as_float[i];
435
else
436
for (unsigned int i = 0; i < type.components(); ++i)
437
constant.as_int[i] = -constant.as_int[i];
438
break;
439
case tokenid::tilde:
440
for (unsigned int i = 0; i < type.components(); ++i)
441
constant.as_uint[i] = ~constant.as_uint[i];
442
break;
443
default:
444
// Unknown operator token, so nothing to do
445
break;
446
}
447
448
return true;
449
}
450
bool reshadefx::expression::evaluate_constant_expression(reshadefx::tokenid op, const reshadefx::constant &rhs)
451
{
452
if (!is_constant)
453
return false;
454
455
switch (op)
456
{
457
case tokenid::percent:
458
if (type.is_floating_point()) {
459
for (unsigned int i = 0; i < type.components(); ++i)
460
// Floating point modulo with zero is defined and results in NaN
461
if (rhs.as_float[i] == 0)
462
constant.as_float[i] = std::numeric_limits<float>::quiet_NaN();
463
else
464
constant.as_float[i] = std::fmod(constant.as_float[i], rhs.as_float[i]);
465
}
466
else if (type.is_signed()) {
467
for (unsigned int i = 0; i < type.components(); ++i)
468
// Integer modulo with zero on the other hand is not defined, so do not fold this expression in that case
469
if (rhs.as_int[i] == 0)
470
return false;
471
else
472
constant.as_int[i] %= rhs.as_int[i];
473
}
474
else {
475
for (unsigned int i = 0; i < type.components(); ++i)
476
if (rhs.as_uint[i] == 0)
477
return false;
478
else
479
constant.as_uint[i] %= rhs.as_uint[i];
480
}
481
break;
482
case tokenid::star:
483
if (type.is_floating_point())
484
for (unsigned int i = 0; i < type.components(); ++i)
485
constant.as_float[i] *= rhs.as_float[i];
486
else
487
for (unsigned int i = 0; i < type.components(); ++i)
488
constant.as_uint[i] *= rhs.as_uint[i];
489
break;
490
case tokenid::plus:
491
if (type.is_floating_point())
492
for (unsigned int i = 0; i < type.components(); ++i)
493
constant.as_float[i] += rhs.as_float[i];
494
else
495
for (unsigned int i = 0; i < type.components(); ++i)
496
constant.as_uint[i] += rhs.as_uint[i];
497
break;
498
case tokenid::minus:
499
if (type.is_floating_point())
500
for (unsigned int i = 0; i < type.components(); ++i)
501
constant.as_float[i] -= rhs.as_float[i];
502
else
503
for (unsigned int i = 0; i < type.components(); ++i)
504
constant.as_uint[i] -= rhs.as_uint[i];
505
break;
506
case tokenid::slash:
507
if (type.is_floating_point()) {
508
for (unsigned int i = 0; i < type.components(); ++i)
509
// Floating point division by zero is well defined and results in infinity or NaN
510
constant.as_float[i] /= rhs.as_float[i];
511
}
512
else if (type.is_signed()) {
513
for (unsigned int i = 0; i < type.components(); ++i)
514
// Integer division by zero on the other hand is not defined, so do not fold this expression in that case
515
if (rhs.as_int[i] == 0)
516
return false;
517
else
518
constant.as_int[i] /= rhs.as_int[i];
519
}
520
else {
521
for (unsigned int i = 0; i < type.components(); ++i)
522
if (rhs.as_uint[i] == 0)
523
return false;
524
else
525
constant.as_uint[i] /= rhs.as_uint[i];
526
}
527
break;
528
case tokenid::ampersand:
529
case tokenid::ampersand_ampersand:
530
for (unsigned int i = 0; i < type.components(); ++i)
531
constant.as_uint[i] &= rhs.as_uint[i];
532
break;
533
case tokenid::pipe:
534
case tokenid::pipe_pipe:
535
for (unsigned int i = 0; i < type.components(); ++i)
536
constant.as_uint[i] |= rhs.as_uint[i];
537
break;
538
case tokenid::caret:
539
for (unsigned int i = 0; i < type.components(); ++i)
540
constant.as_uint[i] ^= rhs.as_uint[i];
541
break;
542
case tokenid::less:
543
if (type.is_floating_point())
544
for (unsigned int i = 0; i < type.components(); ++i)
545
constant.as_uint[i] = constant.as_float[i] < rhs.as_float[i];
546
else if (type.is_signed())
547
for (unsigned int i = 0; i < type.components(); ++i)
548
constant.as_uint[i] = constant.as_int[i] < rhs.as_int[i];
549
else
550
for (unsigned int i = 0; i < type.components(); ++i)
551
constant.as_uint[i] = constant.as_uint[i] < rhs.as_uint[i];
552
type.base = type::t_bool; // Logic operations change the type to boolean
553
break;
554
case tokenid::less_equal:
555
if (type.is_floating_point())
556
for (unsigned int i = 0; i < type.components(); ++i)
557
constant.as_uint[i] = constant.as_float[i] <= rhs.as_float[i];
558
else if (type.is_signed())
559
for (unsigned int i = 0; i < type.components(); ++i)
560
constant.as_uint[i] = constant.as_int[i] <= rhs.as_int[i];
561
else
562
for (unsigned int i = 0; i < type.components(); ++i)
563
constant.as_uint[i] = constant.as_uint[i] <= rhs.as_uint[i];
564
type.base = type::t_bool;
565
break;
566
case tokenid::greater:
567
if (type.is_floating_point())
568
for (unsigned int i = 0; i < type.components(); ++i)
569
constant.as_uint[i] = constant.as_float[i] > rhs.as_float[i];
570
else if (type.is_signed())
571
for (unsigned int i = 0; i < type.components(); ++i)
572
constant.as_uint[i] = constant.as_int[i] > rhs.as_int[i];
573
else
574
for (unsigned int i = 0; i < type.components(); ++i)
575
constant.as_uint[i] = constant.as_uint[i] > rhs.as_uint[i];
576
type.base = type::t_bool;
577
break;
578
case tokenid::greater_equal:
579
if (type.is_floating_point())
580
for (unsigned int i = 0; i < type.components(); ++i)
581
constant.as_uint[i] = constant.as_float[i] >= rhs.as_float[i];
582
else if (type.is_signed())
583
for (unsigned int i = 0; i < type.components(); ++i)
584
constant.as_uint[i] = constant.as_int[i] >= rhs.as_int[i];
585
else
586
for (unsigned int i = 0; i < type.components(); ++i)
587
constant.as_uint[i] = constant.as_uint[i] >= rhs.as_uint[i];
588
type.base = type::t_bool;
589
break;
590
case tokenid::equal_equal:
591
if (type.is_floating_point())
592
for (unsigned int i = 0; i < type.components(); ++i)
593
constant.as_uint[i] = constant.as_float[i] == rhs.as_float[i];
594
else
595
for (unsigned int i = 0; i < type.components(); ++i)
596
constant.as_uint[i] = constant.as_uint[i] == rhs.as_uint[i];
597
type.base = type::t_bool;
598
break;
599
case tokenid::exclaim_equal:
600
if (type.is_floating_point())
601
for (unsigned int i = 0; i < type.components(); ++i)
602
constant.as_uint[i] = constant.as_float[i] != rhs.as_float[i];
603
else
604
for (unsigned int i = 0; i < type.components(); ++i)
605
constant.as_uint[i] = constant.as_uint[i] != rhs.as_uint[i];
606
type.base = type::t_bool;
607
break;
608
case tokenid::less_less:
609
for (unsigned int i = 0; i < type.components(); ++i)
610
constant.as_uint[i] <<= rhs.as_uint[i];
611
break;
612
case tokenid::greater_greater:
613
if (type.is_signed())
614
for (unsigned int i = 0; i < type.components(); ++i)
615
constant.as_int[i] >>= rhs.as_int[i];
616
else
617
for (unsigned int i = 0; i < type.components(); ++i)
618
constant.as_uint[i] >>= rhs.as_uint[i];
619
break;
620
default:
621
// Unknown operator token, so nothing to do
622
break;
623
}
624
625
return true;
626
}
627
628