1 //
2 // Copyright 2019 The ANGLE Project. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // common.h: Common header for other metal source code.
7
8 #ifndef LIBANGLE_RENDERER_METAL_SHADERS_COMMON_H_
9 #define LIBANGLE_RENDERER_METAL_SHADERS_COMMON_H_
10
11 // clang-format off
12 #ifndef SKIP_STD_HEADERS
13 @@# include <simd/simd.h>
14 @@# include <metal_stdlib>
15 #endif
16
17 #include "constants.h"
18
19 #define ANGLE_KERNEL_GUARD(IDX, MAX_COUNT) \
20 if (IDX >= MAX_COUNT) \
21 { \
22 return; \
23 }
24
25 using namespace metal;
26 // clang-format on
27
28 // Common constant defined number of color outputs
29 constant uint32_t kNumColorOutputs [[function_constant(0)]];
30 constant bool kColorOutputAvailable0 = kNumColorOutputs > 0;
31 constant bool kColorOutputAvailable1 = kNumColorOutputs > 1;
32 constant bool kColorOutputAvailable2 = kNumColorOutputs > 2;
33 constant bool kColorOutputAvailable3 = kNumColorOutputs > 3;
34 constant bool kColorOutputAvailable4 = kNumColorOutputs > 4;
35 constant bool kColorOutputAvailable5 = kNumColorOutputs > 5;
36 constant bool kColorOutputAvailable6 = kNumColorOutputs > 6;
37 constant bool kColorOutputAvailable7 = kNumColorOutputs > 7;
38
39 namespace rx
40 {
41 namespace mtl_shader
42 {
43
44 // Full screen triangle's vertices
45 constant float2 gCorners[3] = {float2(-1.0f, -1.0f), float2(3.0f, -1.0f), float2(-1.0f, 3.0f)};
46
47 template <typename T>
48 struct MultipleColorOutputs
49 {
50 vec<T, 4> color0 [[color(0), function_constant(kColorOutputAvailable0)]];
51 vec<T, 4> color1 [[color(1), function_constant(kColorOutputAvailable1)]];
52 vec<T, 4> color2 [[color(2), function_constant(kColorOutputAvailable2)]];
53 vec<T, 4> color3 [[color(3), function_constant(kColorOutputAvailable3)]];
54 vec<T, 4> color4 [[color(4), function_constant(kColorOutputAvailable4)]];
55 vec<T, 4> color5 [[color(5), function_constant(kColorOutputAvailable5)]];
56 vec<T, 4> color6 [[color(6), function_constant(kColorOutputAvailable6)]];
57 vec<T, 4> color7 [[color(7), function_constant(kColorOutputAvailable7)]];
58 };
59
60 #define ANGLE_ASSIGN_COLOR_OUPUT(STRUCT_VARIABLE, COLOR_INDEX, VALUE) \
61 do \
62 { \
63 if (kColorOutputAvailable##COLOR_INDEX) \
64 { \
65 STRUCT_VARIABLE.color##COLOR_INDEX = VALUE; \
66 } \
67 } while (0)
68
69 template <typename T>
toMultipleColorOutputs(vec<T,4> color)70 static inline MultipleColorOutputs<T> toMultipleColorOutputs(vec<T, 4> color)
71 {
72 MultipleColorOutputs<T> re;
73
74 ANGLE_ASSIGN_COLOR_OUPUT(re, 0, color);
75 ANGLE_ASSIGN_COLOR_OUPUT(re, 1, color);
76 ANGLE_ASSIGN_COLOR_OUPUT(re, 2, color);
77 ANGLE_ASSIGN_COLOR_OUPUT(re, 3, color);
78 ANGLE_ASSIGN_COLOR_OUPUT(re, 4, color);
79 ANGLE_ASSIGN_COLOR_OUPUT(re, 5, color);
80 ANGLE_ASSIGN_COLOR_OUPUT(re, 6, color);
81 ANGLE_ASSIGN_COLOR_OUPUT(re, 7, color);
82
83 return re;
84 }
85
cubeTexcoords(float2 texcoords,int face)86 static inline float3 cubeTexcoords(float2 texcoords, int face)
87 {
88 texcoords = 2.0 * texcoords - 1.0;
89 switch (face)
90 {
91 case 0:
92 return float3(1.0, -texcoords.y, -texcoords.x);
93 case 1:
94 return float3(-1.0, -texcoords.y, texcoords.x);
95 case 2:
96 return float3(texcoords.x, 1.0, texcoords.y);
97 case 3:
98 return float3(texcoords.x, -1.0, -texcoords.y);
99 case 4:
100 return float3(texcoords.x, -texcoords.y, 1.0);
101 case 5:
102 return float3(-texcoords.x, -texcoords.y, -1.0);
103 }
104 return float3(texcoords, 0);
105 }
106
107 template <typename T>
resolveTextureMS(texture2d_ms<T> srcTexture,uint2 coords)108 static inline vec<T, 4> resolveTextureMS(texture2d_ms<T> srcTexture, uint2 coords)
109 {
110 uint samples = srcTexture.get_num_samples();
111
112 vec<T, 4> output(0);
113
114 for (uint sample = 0; sample < samples; ++sample)
115 {
116 output += srcTexture.read(coords, sample);
117 }
118
119 output = output / samples;
120
121 return output;
122 }
123
sRGBtoLinear(float4 color)124 static inline float4 sRGBtoLinear(float4 color)
125 {
126 float3 linear1 = color.rgb / 12.92;
127 float3 linear2 = powr((color.rgb + float3(0.055)) / 1.055, 2.4);
128 float3 factor = float3(color.rgb <= float3(0.04045));
129 float4 linear = float4(factor * linear1 + float3(1.0 - factor) * linear2, color.a);
130
131 return linear;
132 }
133
linearToSRGB(float color)134 static inline float linearToSRGB(float color)
135 {
136 if (color <= 0.0f)
137 return 0.0f;
138 if (color < 0.0031308f)
139 return 12.92f * color;
140 if (color < 1.0f)
141 return 1.055f * powr(color, 0.41666f) - 0.055f;
142 return 1.0f;
143 }
144
linearToSRGB(float4 color)145 static inline float4 linearToSRGB(float4 color)
146 {
147 return float4(linearToSRGB(color.r), linearToSRGB(color.g), linearToSRGB(color.b), color.a);
148 }
149
150 template <typename Short>
bytesToShort(constant uchar * input,uint offset)151 static inline Short bytesToShort(constant uchar *input, uint offset)
152 {
153 Short inputLo = input[offset];
154 Short inputHi = input[offset + 1];
155 // Little endian conversion:
156 return inputLo | (inputHi << 8);
157 }
158
159 template <typename Int>
bytesToInt(constant uchar * input,uint offset)160 static inline Int bytesToInt(constant uchar *input, uint offset)
161 {
162 Int input0 = input[offset];
163 Int input1 = input[offset + 1];
164 Int input2 = input[offset + 2];
165 Int input3 = input[offset + 3];
166 // Little endian conversion:
167 return input0 | (input1 << 8) | (input2 << 16) | (input3 << 24);
168 }
169
170 template <typename Short>
shortToBytes(Short val,uint offset,device uchar * output)171 static inline void shortToBytes(Short val, uint offset, device uchar *output)
172 {
173 ushort valUnsigned = as_type<ushort>(val);
174 output[offset] = valUnsigned & 0xff;
175 output[offset + 1] = (valUnsigned >> 8) & 0xff;
176 }
177
178 template <typename Int>
intToBytes(Int val,uint offset,device uchar * output)179 static inline void intToBytes(Int val, uint offset, device uchar *output)
180 {
181 uint valUnsigned = as_type<uint>(val);
182 output[offset] = valUnsigned & 0xff;
183 output[offset + 1] = (valUnsigned >> 8) & 0xff;
184 output[offset + 2] = (valUnsigned >> 16) & 0xff;
185 output[offset + 3] = (valUnsigned >> 24) & 0xff;
186 }
187
floatToBytes(float val,uint offset,device uchar * output)188 static inline void floatToBytes(float val, uint offset, device uchar *output)
189 {
190 intToBytes(as_type<uint>(val), offset, output);
191 }
192
int24bitToBytes(uint val,uint offset,device uchar * output)193 static inline void int24bitToBytes(uint val, uint offset, device uchar *output)
194 {
195 output[offset] = val & 0xff;
196 output[offset + 1] = (val >> 8) & 0xff;
197 output[offset + 2] = (val >> 16) & 0xff;
198 }
199
200 template <unsigned int inputBitCount, unsigned int inputBitStart, typename T>
getShiftedData(T input)201 static inline T getShiftedData(T input)
202 {
203 static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8),
204 "T must have at least as many bits as inputBitCount + inputBitStart.");
205 const T mask = (1 << inputBitCount) - 1;
206 return (input >> inputBitStart) & mask;
207 }
208
209 template <unsigned int inputBitCount, unsigned int inputBitStart, typename T>
shiftData(T input)210 static inline T shiftData(T input)
211 {
212 static_assert(inputBitCount + inputBitStart <= (sizeof(T) * 8),
213 "T must have at least as many bits as inputBitCount + inputBitStart.");
214 const T mask = (1 << inputBitCount) - 1;
215 return (input & mask) << inputBitStart;
216 }
217
218 template <unsigned int inputBitCount, typename T>
normalizedToFloat(T input)219 static inline float normalizedToFloat(T input)
220 {
221 static_assert(inputBitCount <= (sizeof(T) * 8),
222 "T must have more bits than or same bits as inputBitCount.");
223 static_assert(inputBitCount <= 23, "Only single precision is supported");
224
225 constexpr float inverseMax = 1.0f / ((1 << inputBitCount) - 1);
226 return input * inverseMax;
227 }
228
229 template <typename T>
normalizedToFloat(T input)230 static inline float normalizedToFloat(T input)
231 {
232 return normalizedToFloat<sizeof(T) * 8, T>(input);
233 }
234
235 template <>
normalizedToFloat(short input)236 inline float normalizedToFloat(short input)
237 {
238 constexpr float inverseMax = 1.0f / 0x7fff;
239 return static_cast<float>(input) * inverseMax;
240 }
241
242 template <>
normalizedToFloat(int input)243 inline float normalizedToFloat(int input)
244 {
245 constexpr float inverseMax = 1.0f / 0x7fffffff;
246 return static_cast<float>(input) * inverseMax;
247 }
248
249 template <>
normalizedToFloat(uint input)250 inline float normalizedToFloat(uint input)
251 {
252 constexpr float inverseMax = 1.0f / 0xffffffff;
253 return static_cast<float>(input) * inverseMax;
254 }
255
256 template <unsigned int outputBitCount, typename T>
floatToNormalized(float input)257 static inline T floatToNormalized(float input)
258 {
259 static_assert(outputBitCount <= (sizeof(T) * 8),
260 "T must have more bits than or same bits as inputBitCount.");
261 static_assert(outputBitCount > (metal::is_unsigned<T>::value ? 0 : 1),
262 "outputBitCount must be at least 1 not counting the sign bit.");
263 constexpr unsigned int bits =
264 metal::is_unsigned<T>::value ? outputBitCount : outputBitCount - 1;
265 static_assert(bits <= 23, "Only single precision is supported");
266
267 return static_cast<T>(metal::round(((1 << bits) - 1) * input));
268 }
269
270 template <typename T>
floatToNormalized(float input)271 static inline T floatToNormalized(float input)
272 {
273 return floatToNormalized<sizeof(T) * 8, T>(input);
274 }
275
276 } // namespace mtl_shader
277 } // namespace rx
278
279 #endif /* LIBANGLE_RENDERER_METAL_SHADERS_COMMON_H_ */
280