1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef sw_ShaderCore_hpp
16 #define sw_ShaderCore_hpp
17
18 #include "Reactor/Print.hpp"
19 #include "Reactor/Reactor.hpp"
20 #include "System/Debug.hpp"
21
22 #include <array>
23 #include <atomic> // std::memory_order
24 #include <utility> // std::pair
25
26 namespace sw {
27
28 using namespace rr;
29
30 class Vector4s
31 {
32 public:
33 Vector4s();
34 Vector4s(unsigned short x, unsigned short y, unsigned short z, unsigned short w);
35 Vector4s(const Vector4s &rhs);
36
37 Short4 &operator[](int i);
38 Vector4s &operator=(const Vector4s &rhs);
39
40 Short4 x;
41 Short4 y;
42 Short4 z;
43 Short4 w;
44 };
45
46 class Vector4f
47 {
48 public:
49 Vector4f();
50 Vector4f(float x, float y, float z, float w);
51 Vector4f(const Vector4f &rhs);
52
53 Float4 &operator[](int i);
54 Vector4f &operator=(const Vector4f &rhs);
55
56 Float4 x;
57 Float4 y;
58 Float4 z;
59 Float4 w;
60 };
61
62 class Vector4i
63 {
64 public:
65 Vector4i();
66 Vector4i(int x, int y, int z, int w);
67 Vector4i(const Vector4i &rhs);
68
69 Int4 &operator[](int i);
70 Vector4i &operator=(const Vector4i &rhs);
71
72 Int4 x;
73 Int4 y;
74 Int4 z;
75 Int4 w;
76 };
77
78 enum class OutOfBoundsBehavior
79 {
80 Nullify, // Loads become zero, stores are elided.
81 RobustBufferAccess, // As defined by the Vulkan spec (in short: access anywhere within bounds, or zeroing).
82 UndefinedValue, // Only for load operations. Not secure. No program termination.
83 UndefinedBehavior, // Program may terminate.
84 };
85
86 // SIMD contains types that represent multiple scalars packed into a single
87 // vector data type. Types in the SIMD namespace provide a semantic hint
88 // that the data should be treated as a per-execution-lane scalar instead of
89 // a typical euclidean-style vector type.
90 namespace SIMD {
91
92 // Width is the number of per-lane scalars packed into each SIMD vector.
93 static constexpr int Width = 4;
94
95 using Float = rr::Float4;
96 using Int = rr::Int4;
97 using UInt = rr::UInt4;
98
99 struct Pointer
100 {
101 Pointer(rr::Pointer<Byte> base, rr::Int limit);
102 Pointer(rr::Pointer<Byte> base, unsigned int limit);
103 Pointer(rr::Pointer<Byte> base, rr::Int limit, SIMD::Int offset);
104 Pointer(rr::Pointer<Byte> base, unsigned int limit, SIMD::Int offset);
105
106 Pointer &operator+=(Int i);
107 Pointer &operator*=(Int i);
108
109 Pointer operator+(SIMD::Int i);
110 Pointer operator*(SIMD::Int i);
111
112 Pointer &operator+=(int i);
113 Pointer &operator*=(int i);
114
115 Pointer operator+(int i);
116 Pointer operator*(int i);
117
118 SIMD::Int offsets() const;
119
120 SIMD::Int isInBounds(unsigned int accessSize, OutOfBoundsBehavior robustness) const;
121
122 bool isStaticallyInBounds(unsigned int accessSize, OutOfBoundsBehavior robustness) const;
123
124 rr::Int limit() const;
125
126 // Returns true if all offsets are sequential
127 // (N+0*step, N+1*step, N+2*step, N+3*step)
128 rr::Bool hasSequentialOffsets(unsigned int step) const;
129
130 // Returns true if all offsets are are compile-time static and
131 // sequential (N+0*step, N+1*step, N+2*step, N+3*step)
132 bool hasStaticSequentialOffsets(unsigned int step) const;
133
134 // Returns true if all offsets are equal (N, N, N, N)
135 rr::Bool hasEqualOffsets() const;
136
137 // Returns true if all offsets are compile-time static and are equal
138 // (N, N, N, N)
139 bool hasStaticEqualOffsets() const;
140
141 template<typename T>
142 inline T Load(OutOfBoundsBehavior robustness, Int mask, bool atomic = false, std::memory_order order = std::memory_order_relaxed, int alignment = sizeof(float));
143
144 template<typename T>
145 inline void Store(T val, OutOfBoundsBehavior robustness, Int mask, bool atomic = false, std::memory_order order = std::memory_order_relaxed);
146
147 template<typename T>
148 inline void Store(RValue<T> val, OutOfBoundsBehavior robustness, Int mask, bool atomic = false, std::memory_order order = std::memory_order_relaxed);
149
150 // Base address for the pointer, common across all lanes.
151 rr::Pointer<rr::Byte> base;
152
153 // Upper (non-inclusive) limit for offsets from base.
154 rr::Int dynamicLimit; // If hasDynamicLimit is false, dynamicLimit is zero.
155 unsigned int staticLimit;
156
157 // Per lane offsets from base.
158 SIMD::Int dynamicOffsets; // If hasDynamicOffsets is false, all dynamicOffsets are zero.
159 std::array<int32_t, SIMD::Width> staticOffsets;
160
161 bool hasDynamicLimit; // True if dynamicLimit is non-zero.
162 bool hasDynamicOffsets; // True if any dynamicOffsets are non-zero.
163 };
164
165 template<typename T>
166 struct Element
167 {};
168 template<>
169 struct Element<Float>
170 {
171 using type = rr::Float;
172 };
173 template<>
174 struct Element<Int>
175 {
176 using type = rr::Int;
177 };
178 template<>
179 struct Element<UInt>
180 {
181 using type = rr::UInt;
182 };
183
184 } // namespace SIMD
185
186 Float4 exponential2(RValue<Float4> x, bool pp = false);
187 Float4 logarithm2(RValue<Float4> x, bool pp = false);
188 Float4 exponential(RValue<Float4> x, bool pp = false);
189 Float4 logarithm(RValue<Float4> x, bool pp = false);
190 Float4 power(RValue<Float4> x, RValue<Float4> y, bool pp = false);
191 Float4 reciprocal(RValue<Float4> x, bool pp = false, bool finite = false, bool exactAtPow2 = false);
192 Float4 reciprocalSquareRoot(RValue<Float4> x, bool abs, bool pp = false);
193 Float4 modulo(RValue<Float4> x, RValue<Float4> y);
194 Float4 sine_pi(RValue<Float4> x, bool pp = false); // limited to [-pi, pi] range
195 Float4 cosine_pi(RValue<Float4> x, bool pp = false); // limited to [-pi, pi] range
196 Float4 sine(RValue<Float4> x, bool pp = false);
197 Float4 cosine(RValue<Float4> x, bool pp = false);
198 Float4 tangent(RValue<Float4> x, bool pp = false);
199 Float4 arccos(RValue<Float4> x, bool pp = false);
200 Float4 arcsin(RValue<Float4> x, bool pp = false);
201 Float4 arctan(RValue<Float4> x, bool pp = false);
202 Float4 arctan(RValue<Float4> y, RValue<Float4> x, bool pp = false);
203 Float4 sineh(RValue<Float4> x, bool pp = false);
204 Float4 cosineh(RValue<Float4> x, bool pp = false);
205 Float4 tangenth(RValue<Float4> x, bool pp = false);
206 Float4 arccosh(RValue<Float4> x, bool pp = false); // Limited to x >= 1
207 Float4 arcsinh(RValue<Float4> x, bool pp = false);
208 Float4 arctanh(RValue<Float4> x, bool pp = false); // Limited to ]-1, 1[ range
209
210 Float4 dot2(const Vector4f &v0, const Vector4f &v1);
211 Float4 dot3(const Vector4f &v0, const Vector4f &v1);
212 Float4 dot4(const Vector4f &v0, const Vector4f &v1);
213
214 void transpose4x4(Short4 &row0, Short4 &row1, Short4 &row2, Short4 &row3);
215 void transpose4x3(Short4 &row0, Short4 &row1, Short4 &row2, Short4 &row3);
216 void transpose4x4(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3);
217 void transpose4x3(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3);
218 void transpose4x2(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3);
219 void transpose4x1(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3);
220 void transpose2x4(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3);
221 void transpose4xN(Float4 &row0, Float4 &row1, Float4 &row2, Float4 &row3, int N);
222
223 sw::SIMD::UInt halfToFloatBits(sw::SIMD::UInt halfBits);
224 sw::SIMD::UInt floatToHalfBits(sw::SIMD::UInt floatBits, bool storeInUpperBits);
225 Float4 r11g11b10Unpack(UInt r11g11b10bits);
226 UInt r11g11b10Pack(const Float4 &value);
227
228 rr::RValue<rr::Bool> AnyTrue(rr::RValue<sw::SIMD::Int> const &ints);
229
230 rr::RValue<rr::Bool> AnyFalse(rr::RValue<sw::SIMD::Int> const &ints);
231
232 template<typename T>
233 inline rr::RValue<T> AndAll(rr::RValue<T> const &mask);
234
235 template<typename T>
236 inline rr::RValue<T> OrAll(rr::RValue<T> const &mask);
237
238 rr::RValue<sw::SIMD::Float> Sign(rr::RValue<sw::SIMD::Float> const &val);
239
240 // Returns the <whole, frac> of val.
241 // Both whole and frac will have the same sign as val.
242 std::pair<rr::RValue<sw::SIMD::Float>, rr::RValue<sw::SIMD::Float>>
243 Modf(rr::RValue<sw::SIMD::Float> const &val);
244
245 // Returns the number of 1s in bits, per lane.
246 sw::SIMD::UInt CountBits(rr::RValue<sw::SIMD::UInt> const &bits);
247
248 // Returns 1 << bits.
249 // If the resulting bit overflows a 32 bit integer, 0 is returned.
250 rr::RValue<sw::SIMD::UInt> NthBit32(rr::RValue<sw::SIMD::UInt> const &bits);
251
252 // Returns bitCount number of of 1's starting from the LSB.
253 rr::RValue<sw::SIMD::UInt> Bitmask32(rr::RValue<sw::SIMD::UInt> const &bitCount);
254
255 // Performs a fused-multiply add, returning a * b + c.
256 rr::RValue<sw::SIMD::Float> FMA(
257 rr::RValue<sw::SIMD::Float> const &a,
258 rr::RValue<sw::SIMD::Float> const &b,
259 rr::RValue<sw::SIMD::Float> const &c);
260
261 // Returns the exponent of the floating point number f.
262 // Assumes IEEE 754
263 rr::RValue<sw::SIMD::Int> Exponent(rr::RValue<sw::SIMD::Float> f);
264
265 // Returns y if y < x; otherwise result is x.
266 // If one operand is a NaN, the other operand is the result.
267 // If both operands are NaN, the result is a NaN.
268 rr::RValue<sw::SIMD::Float> NMin(rr::RValue<sw::SIMD::Float> const &x, rr::RValue<sw::SIMD::Float> const &y);
269
270 // Returns y if y > x; otherwise result is x.
271 // If one operand is a NaN, the other operand is the result.
272 // If both operands are NaN, the result is a NaN.
273 rr::RValue<sw::SIMD::Float> NMax(rr::RValue<sw::SIMD::Float> const &x, rr::RValue<sw::SIMD::Float> const &y);
274
275 // Returns the determinant of a 2x2 matrix.
276 rr::RValue<sw::SIMD::Float> Determinant(
277 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b,
278 rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d);
279
280 // Returns the determinant of a 3x3 matrix.
281 rr::RValue<sw::SIMD::Float> Determinant(
282 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c,
283 rr::RValue<sw::SIMD::Float> const &d, rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f,
284 rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h, rr::RValue<sw::SIMD::Float> const &i);
285
286 // Returns the determinant of a 4x4 matrix.
287 rr::RValue<sw::SIMD::Float> Determinant(
288 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d,
289 rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f, rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h,
290 rr::RValue<sw::SIMD::Float> const &i, rr::RValue<sw::SIMD::Float> const &j, rr::RValue<sw::SIMD::Float> const &k, rr::RValue<sw::SIMD::Float> const &l,
291 rr::RValue<sw::SIMD::Float> const &m, rr::RValue<sw::SIMD::Float> const &n, rr::RValue<sw::SIMD::Float> const &o, rr::RValue<sw::SIMD::Float> const &p);
292
293 // Returns the inverse of a 2x2 matrix.
294 std::array<rr::RValue<sw::SIMD::Float>, 4> MatrixInverse(
295 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b,
296 rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d);
297
298 // Returns the inverse of a 3x3 matrix.
299 std::array<rr::RValue<sw::SIMD::Float>, 9> MatrixInverse(
300 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c,
301 rr::RValue<sw::SIMD::Float> const &d, rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f,
302 rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h, rr::RValue<sw::SIMD::Float> const &i);
303
304 // Returns the inverse of a 4x4 matrix.
305 std::array<rr::RValue<sw::SIMD::Float>, 16> MatrixInverse(
306 rr::RValue<sw::SIMD::Float> const &a, rr::RValue<sw::SIMD::Float> const &b, rr::RValue<sw::SIMD::Float> const &c, rr::RValue<sw::SIMD::Float> const &d,
307 rr::RValue<sw::SIMD::Float> const &e, rr::RValue<sw::SIMD::Float> const &f, rr::RValue<sw::SIMD::Float> const &g, rr::RValue<sw::SIMD::Float> const &h,
308 rr::RValue<sw::SIMD::Float> const &i, rr::RValue<sw::SIMD::Float> const &j, rr::RValue<sw::SIMD::Float> const &k, rr::RValue<sw::SIMD::Float> const &l,
309 rr::RValue<sw::SIMD::Float> const &m, rr::RValue<sw::SIMD::Float> const &n, rr::RValue<sw::SIMD::Float> const &o, rr::RValue<sw::SIMD::Float> const &p);
310
311 ////////////////////////////////////////////////////////////////////////////
312 // Inline functions
313 ////////////////////////////////////////////////////////////////////////////
314
315 template<typename T>
Load(OutOfBoundsBehavior robustness,Int mask,bool atomic,std::memory_order order,int alignment)316 inline T SIMD::Pointer::Load(OutOfBoundsBehavior robustness, Int mask, bool atomic /* = false */, std::memory_order order /* = std::memory_order_relaxed */, int alignment /* = sizeof(float) */)
317 {
318 using EL = typename Element<T>::type;
319
320 if(isStaticallyInBounds(sizeof(float), robustness))
321 {
322 // All elements are statically known to be in-bounds.
323 // We can avoid costly conditional on masks.
324
325 if(hasStaticSequentialOffsets(sizeof(float)))
326 {
327 // Offsets are sequential. Perform regular load.
328 return rr::Load(rr::Pointer<T>(base + staticOffsets[0]), alignment, atomic, order);
329 }
330
331 if(hasStaticEqualOffsets())
332 {
333 // Load one, replicate.
334 return T(*rr::Pointer<EL>(base + staticOffsets[0], alignment));
335 }
336 }
337 else
338 {
339 switch(robustness)
340 {
341 case OutOfBoundsBehavior::Nullify:
342 case OutOfBoundsBehavior::RobustBufferAccess:
343 case OutOfBoundsBehavior::UndefinedValue:
344 mask &= isInBounds(sizeof(float), robustness); // Disable out-of-bounds reads.
345 break;
346 case OutOfBoundsBehavior::UndefinedBehavior:
347 // Nothing to do. Application/compiler must guarantee no out-of-bounds accesses.
348 break;
349 }
350 }
351
352 auto offs = offsets();
353
354 if(!atomic && order == std::memory_order_relaxed)
355 {
356 if(hasStaticEqualOffsets())
357 {
358 // Load one, replicate.
359 // Be careful of the case where the post-bounds-check mask
360 // is 0, in which case we must not load.
361 T out = T(0);
362 If(AnyTrue(mask))
363 {
364 EL el = *rr::Pointer<EL>(base + staticOffsets[0], alignment);
365 out = T(el);
366 }
367 return out;
368 }
369
370 bool zeroMaskedLanes = true;
371 switch(robustness)
372 {
373 case OutOfBoundsBehavior::Nullify:
374 case OutOfBoundsBehavior::RobustBufferAccess: // Must either return an in-bounds value, or zero.
375 zeroMaskedLanes = true;
376 break;
377 case OutOfBoundsBehavior::UndefinedValue:
378 case OutOfBoundsBehavior::UndefinedBehavior:
379 zeroMaskedLanes = false;
380 break;
381 }
382
383 // TODO(b/195446858): Optimize static sequential offsets case by using masked load.
384
385 return rr::Gather(rr::Pointer<EL>(base), offs, mask, alignment, zeroMaskedLanes);
386 }
387 else
388 {
389 T out;
390 auto anyLanesDisabled = AnyFalse(mask);
391 If(hasEqualOffsets() && !anyLanesDisabled)
392 {
393 // Load one, replicate.
394 auto offset = Extract(offs, 0);
395 out = T(rr::Load(rr::Pointer<EL>(&base[offset]), alignment, atomic, order));
396 }
397 Else If(hasSequentialOffsets(sizeof(float)) && !anyLanesDisabled)
398 {
399 // Load all elements in a single SIMD instruction.
400 auto offset = Extract(offs, 0);
401 out = rr::Load(rr::Pointer<T>(&base[offset]), alignment, atomic, order);
402 }
403 Else
404 {
405 // Divergent offsets or masked lanes.
406 out = T(0);
407 for(int i = 0; i < SIMD::Width; i++)
408 {
409 If(Extract(mask, i) != 0)
410 {
411 auto offset = Extract(offs, i);
412 auto el = rr::Load(rr::Pointer<EL>(&base[offset]), alignment, atomic, order);
413 out = Insert(out, el, i);
414 }
415 }
416 }
417 return out;
418 }
419 }
420
421 template<typename T>
Store(T val,OutOfBoundsBehavior robustness,Int mask,bool atomic,std::memory_order order)422 inline void SIMD::Pointer::Store(T val, OutOfBoundsBehavior robustness, Int mask, bool atomic /* = false */, std::memory_order order /* = std::memory_order_relaxed */)
423 {
424 using EL = typename Element<T>::type;
425 constexpr size_t alignment = sizeof(float);
426 auto offs = offsets();
427
428 switch(robustness)
429 {
430 case OutOfBoundsBehavior::Nullify:
431 case OutOfBoundsBehavior::RobustBufferAccess: // TODO: Allows writing anywhere within bounds. Could be faster than masking.
432 case OutOfBoundsBehavior::UndefinedValue: // Should not be used for store operations. Treat as robust buffer access.
433 mask &= isInBounds(sizeof(float), robustness); // Disable out-of-bounds writes.
434 break;
435 case OutOfBoundsBehavior::UndefinedBehavior:
436 // Nothing to do. Application/compiler must guarantee no out-of-bounds accesses.
437 break;
438 }
439
440 if(!atomic && order == std::memory_order_relaxed)
441 {
442 if(hasStaticEqualOffsets())
443 {
444 If(AnyTrue(mask))
445 {
446 // All equal. One of these writes will win -- elect the winning lane.
447 auto v0111 = SIMD::Int(0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
448 auto elect = mask & ~(v0111 & (mask.xxyz | mask.xxxy | mask.xxxx));
449 auto maskedVal = As<SIMD::Int>(val) & elect;
450 auto scalarVal = Extract(maskedVal, 0) |
451 Extract(maskedVal, 1) |
452 Extract(maskedVal, 2) |
453 Extract(maskedVal, 3);
454 *rr::Pointer<EL>(base + staticOffsets[0], alignment) = As<EL>(scalarVal);
455 }
456 }
457 else if(hasStaticSequentialOffsets(sizeof(float)) &&
458 isStaticallyInBounds(sizeof(float), robustness))
459 {
460 // TODO(b/195446858): Optimize using masked store.
461 // Pointer has no elements OOB, and the store is not atomic.
462 // Perform a read-modify-write.
463 auto p = rr::Pointer<SIMD::Int>(base + staticOffsets[0], alignment);
464 auto prev = *p;
465 *p = (prev & ~mask) | (As<SIMD::Int>(val) & mask);
466 }
467 else
468 {
469 rr::Scatter(rr::Pointer<EL>(base), val, offs, mask, alignment);
470 }
471 }
472 else
473 {
474 auto anyLanesDisabled = AnyFalse(mask);
475 If(hasSequentialOffsets(sizeof(float)) && !anyLanesDisabled)
476 {
477 // Store all elements in a single SIMD instruction.
478 auto offset = Extract(offs, 0);
479 rr::Store(val, rr::Pointer<T>(&base[offset]), alignment, atomic, order);
480 }
481 Else
482 {
483 // Divergent offsets or masked lanes.
484 for(int i = 0; i < SIMD::Width; i++)
485 {
486 If(Extract(mask, i) != 0)
487 {
488 auto offset = Extract(offs, i);
489 rr::Store(Extract(val, i), rr::Pointer<EL>(&base[offset]), alignment, atomic, order);
490 }
491 }
492 }
493 }
494 }
495
496 template<typename T>
Store(RValue<T> val,OutOfBoundsBehavior robustness,Int mask,bool atomic,std::memory_order order)497 inline void SIMD::Pointer::Store(RValue<T> val, OutOfBoundsBehavior robustness, Int mask, bool atomic /* = false */, std::memory_order order /* = std::memory_order_relaxed */)
498 {
499 Store(T(val), robustness, mask, atomic, order);
500 }
501
502 template<typename T>
AndAll(rr::RValue<T> const & mask)503 inline rr::RValue<T> AndAll(rr::RValue<T> const &mask)
504 {
505 T v1 = mask; // [x] [y] [z] [w]
506 T v2 = v1.xzxz & v1.ywyw; // [xy] [zw] [xy] [zw]
507 return v2.xxxx & v2.yyyy; // [xyzw] [xyzw] [xyzw] [xyzw]
508 }
509
510 template<typename T>
OrAll(rr::RValue<T> const & mask)511 inline rr::RValue<T> OrAll(rr::RValue<T> const &mask)
512 {
513 T v1 = mask; // [x] [y] [z] [w]
514 T v2 = v1.xzxz | v1.ywyw; // [xy] [zw] [xy] [zw]
515 return v2.xxxx | v2.yyyy; // [xyzw] [xyzw] [xyzw] [xyzw]
516 }
517
518 } // namespace sw
519
520 #ifdef ENABLE_RR_PRINT
521 namespace rr {
522 template<>
523 struct PrintValue::Ty<sw::Vector4f>
524 {
fmtrr::PrintValue::Ty525 static std::string fmt(const sw::Vector4f &v)
526 {
527 return "[x: " + PrintValue::fmt(v.x) +
528 ", y: " + PrintValue::fmt(v.y) +
529 ", z: " + PrintValue::fmt(v.z) +
530 ", w: " + PrintValue::fmt(v.w) + "]";
531 }
532
valrr::PrintValue::Ty533 static std::vector<rr::Value *> val(const sw::Vector4f &v)
534 {
535 return PrintValue::vals(v.x, v.y, v.z, v.w);
536 }
537 };
538 template<>
539 struct PrintValue::Ty<sw::Vector4s>
540 {
fmtrr::PrintValue::Ty541 static std::string fmt(const sw::Vector4s &v)
542 {
543 return "[x: " + PrintValue::fmt(v.x) +
544 ", y: " + PrintValue::fmt(v.y) +
545 ", z: " + PrintValue::fmt(v.z) +
546 ", w: " + PrintValue::fmt(v.w) + "]";
547 }
548
valrr::PrintValue::Ty549 static std::vector<rr::Value *> val(const sw::Vector4s &v)
550 {
551 return PrintValue::vals(v.x, v.y, v.z, v.w);
552 }
553 };
554 template<>
555 struct PrintValue::Ty<sw::SIMD::Pointer>
556 {
fmtrr::PrintValue::Ty557 static std::string fmt(const sw::SIMD::Pointer &v)
558 {
559 return "{" + PrintValue::fmt(v.base) + " +" + PrintValue::fmt(v.offsets()) + "}";
560 }
561
valrr::PrintValue::Ty562 static std::vector<rr::Value *> val(const sw::SIMD::Pointer &v)
563 {
564 return PrintValue::vals(v.base, v.offsets());
565 }
566 };
567 } // namespace rr
568 #endif // ENABLE_RR_PRINT
569
570 #endif // sw_ShaderCore_hpp
571