| 1 | // |
| 2 | // Copyright (c) 2002-2011 The ANGLE Project Authors. 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 | |
| 7 | #include "compiler/translator/BuiltInFunctionEmulatorGLSL.h" |
| 8 | |
| 9 | #include "angle_gl.h" |
| 10 | #include "compiler/translator/BuiltInFunctionEmulator.h" |
| 11 | #include "compiler/translator/VersionGLSL.h" |
| 12 | #include "compiler/translator/tree_util/BuiltIn_autogen.h" |
| 13 | |
| 14 | namespace sh |
| 15 | { |
| 16 | |
| 17 | void InitBuiltInAbsFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, |
| 18 | sh::GLenum shaderType) |
| 19 | { |
| 20 | if (shaderType == GL_VERTEX_SHADER) |
| 21 | { |
| 22 | emu->addEmulatedFunction(BuiltInId::abs_Int1, "int abs_emu(int x) { return x * sign(x); }" ); |
| 23 | } |
| 24 | } |
| 25 | |
| 26 | void InitBuiltInIsnanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu, |
| 27 | int targetGLSLVersion) |
| 28 | { |
| 29 | // isnan() is supported since GLSL 1.3. |
| 30 | if (targetGLSLVersion < GLSL_VERSION_130) |
| 31 | return; |
| 32 | |
| 33 | // !(x > 0.0 || x < 0.0 || x == 0.0) will be optimized and always equal to false. |
| 34 | emu->addEmulatedFunction( |
| 35 | BuiltInId::isnan_Float1, |
| 36 | "bool isnan_emu(float x) { return (x > 0.0 || x < 0.0) ? false : x != 0.0; }" ); |
| 37 | emu->addEmulatedFunction( |
| 38 | BuiltInId::isnan_Float2, |
| 39 | "bvec2 isnan_emu(vec2 x)\n" |
| 40 | "{\n" |
| 41 | " bvec2 isnan;\n" |
| 42 | " for (int i = 0; i < 2; i++)\n" |
| 43 | " {\n" |
| 44 | " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n" |
| 45 | " }\n" |
| 46 | " return isnan;\n" |
| 47 | "}\n" ); |
| 48 | emu->addEmulatedFunction( |
| 49 | BuiltInId::isnan_Float3, |
| 50 | "bvec3 isnan_emu(vec3 x)\n" |
| 51 | "{\n" |
| 52 | " bvec3 isnan;\n" |
| 53 | " for (int i = 0; i < 3; i++)\n" |
| 54 | " {\n" |
| 55 | " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n" |
| 56 | " }\n" |
| 57 | " return isnan;\n" |
| 58 | "}\n" ); |
| 59 | emu->addEmulatedFunction( |
| 60 | BuiltInId::isnan_Float4, |
| 61 | "bvec4 isnan_emu(vec4 x)\n" |
| 62 | "{\n" |
| 63 | " bvec4 isnan;\n" |
| 64 | " for (int i = 0; i < 4; i++)\n" |
| 65 | " {\n" |
| 66 | " isnan[i] = (x[i] > 0.0 || x[i] < 0.0) ? false : x[i] != 0.0;\n" |
| 67 | " }\n" |
| 68 | " return isnan;\n" |
| 69 | "}\n" ); |
| 70 | } |
| 71 | |
| 72 | void InitBuiltInAtanFunctionEmulatorForGLSLWorkarounds(BuiltInFunctionEmulator *emu) |
| 73 | { |
| 74 | emu->addEmulatedFunction(BuiltInId::atan_Float1_Float1, |
| 75 | "emu_precision float atan_emu(emu_precision float y, emu_precision " |
| 76 | "float x)\n" |
| 77 | "{\n" |
| 78 | " if (x > 0.0) return atan(y / x);\n" |
| 79 | " else if (x < 0.0 && y >= 0.0) return atan(y / x) + 3.14159265;\n" |
| 80 | " else if (x < 0.0 && y < 0.0) return atan(y / x) - 3.14159265;\n" |
| 81 | " else return 1.57079632 * sign(y);\n" |
| 82 | "}\n" ); |
| 83 | static const std::array<TSymbolUniqueId, 4> ids = { |
| 84 | BuiltInId::atan_Float1_Float1, |
| 85 | BuiltInId::atan_Float2_Float2, |
| 86 | BuiltInId::atan_Float3_Float3, |
| 87 | BuiltInId::atan_Float4_Float4, |
| 88 | }; |
| 89 | for (int dim = 2; dim <= 4; ++dim) |
| 90 | { |
| 91 | std::stringstream ss = sh::InitializeStream<std::stringstream>(); |
| 92 | ss << "emu_precision vec" << dim << " atan_emu(emu_precision vec" << dim |
| 93 | << " y, emu_precision vec" << dim << " x)\n" |
| 94 | << "{\n" |
| 95 | " return vec" |
| 96 | << dim << "(" ; |
| 97 | for (int i = 0; i < dim; ++i) |
| 98 | { |
| 99 | ss << "atan_emu(y[" << i << "], x[" << i << "])" ; |
| 100 | if (i < dim - 1) |
| 101 | { |
| 102 | ss << ", " ; |
| 103 | } |
| 104 | } |
| 105 | ss << ");\n" |
| 106 | "}\n" ; |
| 107 | emu->addEmulatedFunctionWithDependency(BuiltInId::atan_Float1_Float1, ids[dim - 1], |
| 108 | ss.str().c_str()); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | // Emulate built-in functions missing from GLSL 1.30 and higher |
| 113 | void InitBuiltInFunctionEmulatorForGLSLMissingFunctions(BuiltInFunctionEmulator *emu, |
| 114 | sh::GLenum shaderType, |
| 115 | int targetGLSLVersion) |
| 116 | { |
| 117 | // Emulate packUnorm2x16 and unpackUnorm2x16 (GLSL 4.10) |
| 118 | if (targetGLSLVersion < GLSL_VERSION_410) |
| 119 | { |
| 120 | // clang-format off |
| 121 | emu->addEmulatedFunction(BuiltInId::packUnorm2x16_Float2, |
| 122 | "uint packUnorm2x16_emu(vec2 v)\n" |
| 123 | "{\n" |
| 124 | " int x = int(round(clamp(v.x, 0.0, 1.0) * 65535.0));\n" |
| 125 | " int y = int(round(clamp(v.y, 0.0, 1.0) * 65535.0));\n" |
| 126 | " return uint((y << 16) | (x & 0xFFFF));\n" |
| 127 | "}\n" ); |
| 128 | |
| 129 | emu->addEmulatedFunction(BuiltInId::unpackUnorm2x16_UInt1, |
| 130 | "vec2 unpackUnorm2x16_emu(uint u)\n" |
| 131 | "{\n" |
| 132 | " float x = float(u & 0xFFFFu) / 65535.0;\n" |
| 133 | " float y = float(u >> 16) / 65535.0;\n" |
| 134 | " return vec2(x, y);\n" |
| 135 | "}\n" ); |
| 136 | // clang-format on |
| 137 | } |
| 138 | |
| 139 | // Emulate packSnorm2x16, packHalf2x16, unpackSnorm2x16, and unpackHalf2x16 (GLSL 4.20) |
| 140 | // by using floatBitsToInt, floatBitsToUint, intBitsToFloat, and uintBitsToFloat (GLSL 3.30). |
| 141 | if (targetGLSLVersion >= GLSL_VERSION_330 && targetGLSLVersion < GLSL_VERSION_420) |
| 142 | { |
| 143 | // clang-format off |
| 144 | emu->addEmulatedFunction(BuiltInId::packSnorm2x16_Float2, |
| 145 | "uint packSnorm2x16_emu(vec2 v)\n" |
| 146 | "{\n" |
| 147 | " #if defined(GL_ARB_shading_language_packing)\n" |
| 148 | " return packSnorm2x16(v);\n" |
| 149 | " #else\n" |
| 150 | " int x = int(round(clamp(v.x, -1.0, 1.0) * 32767.0));\n" |
| 151 | " int y = int(round(clamp(v.y, -1.0, 1.0) * 32767.0));\n" |
| 152 | " return uint((y << 16) | (x & 0xFFFF));\n" |
| 153 | " #endif\n" |
| 154 | "}\n" ); |
| 155 | emu->addEmulatedFunction(BuiltInId::unpackSnorm2x16_UInt1, |
| 156 | "#if !defined(GL_ARB_shading_language_packing)\n" |
| 157 | " float fromSnorm(uint x)\n" |
| 158 | " {\n" |
| 159 | " int xi = (int(x) & 0x7FFF) - (int(x) & 0x8000);\n" |
| 160 | " return clamp(float(xi) / 32767.0, -1.0, 1.0);\n" |
| 161 | " }\n" |
| 162 | "#endif\n" |
| 163 | "\n" |
| 164 | "vec2 unpackSnorm2x16_emu(uint u)\n" |
| 165 | "{\n" |
| 166 | " #if defined(GL_ARB_shading_language_packing)\n" |
| 167 | " return unpackSnorm2x16(u);\n" |
| 168 | " #else\n" |
| 169 | " uint y = (u >> 16);\n" |
| 170 | " uint x = u;\n" |
| 171 | " return vec2(fromSnorm(x), fromSnorm(y));\n" |
| 172 | " #endif\n" |
| 173 | "}\n" ); |
| 174 | // Functions uint f32tof16(float val) and float f16tof32(uint val) are |
| 175 | // based on the OpenGL redbook Appendix Session "Floating-Point Formats Used in OpenGL". |
| 176 | emu->addEmulatedFunction(BuiltInId::packHalf2x16_Float2, |
| 177 | "#if !defined(GL_ARB_shading_language_packing)\n" |
| 178 | " uint f32tof16(float val)\n" |
| 179 | " {\n" |
| 180 | " uint f32 = floatBitsToUint(val);\n" |
| 181 | " uint f16 = 0u;\n" |
| 182 | " uint sign = (f32 >> 16) & 0x8000u;\n" |
| 183 | " int exponent = int((f32 >> 23) & 0xFFu) - 127;\n" |
| 184 | " uint mantissa = f32 & 0x007FFFFFu;\n" |
| 185 | " if (exponent == 128)\n" |
| 186 | " {\n" |
| 187 | " // Infinity or NaN\n" |
| 188 | " // NaN bits that are masked out by 0x3FF get discarded.\n" |
| 189 | " // This can turn some NaNs to infinity, but this is allowed by the spec.\n" |
| 190 | " f16 = sign | (0x1Fu << 10);\n" |
| 191 | " f16 |= (mantissa & 0x3FFu);\n" |
| 192 | " }\n" |
| 193 | " else if (exponent > 15)\n" |
| 194 | " {\n" |
| 195 | " // Overflow - flush to Infinity\n" |
| 196 | " f16 = sign | (0x1Fu << 10);\n" |
| 197 | " }\n" |
| 198 | " else if (exponent > -15)\n" |
| 199 | " {\n" |
| 200 | " // Representable value\n" |
| 201 | " exponent += 15;\n" |
| 202 | " mantissa >>= 13;\n" |
| 203 | " f16 = sign | uint(exponent << 10) | mantissa;\n" |
| 204 | " }\n" |
| 205 | " else\n" |
| 206 | " {\n" |
| 207 | " f16 = sign;\n" |
| 208 | " }\n" |
| 209 | " return f16;\n" |
| 210 | " }\n" |
| 211 | "#endif\n" |
| 212 | "\n" |
| 213 | "uint packHalf2x16_emu(vec2 v)\n" |
| 214 | "{\n" |
| 215 | " #if defined(GL_ARB_shading_language_packing)\n" |
| 216 | " return packHalf2x16(v);\n" |
| 217 | " #else\n" |
| 218 | " uint x = f32tof16(v.x);\n" |
| 219 | " uint y = f32tof16(v.y);\n" |
| 220 | " return (y << 16) | x;\n" |
| 221 | " #endif\n" |
| 222 | "}\n" ); |
| 223 | emu->addEmulatedFunction(BuiltInId::unpackHalf2x16_UInt1, |
| 224 | "#if !defined(GL_ARB_shading_language_packing)\n" |
| 225 | " float f16tof32(uint val)\n" |
| 226 | " {\n" |
| 227 | " uint sign = (val & 0x8000u) << 16;\n" |
| 228 | " int exponent = int((val & 0x7C00u) >> 10);\n" |
| 229 | " uint mantissa = val & 0x03FFu;\n" |
| 230 | " float f32 = 0.0;\n" |
| 231 | " if(exponent == 0)\n" |
| 232 | " {\n" |
| 233 | " if (mantissa != 0u)\n" |
| 234 | " {\n" |
| 235 | " const float scale = 1.0 / (1 << 24);\n" |
| 236 | " f32 = scale * mantissa;\n" |
| 237 | " }\n" |
| 238 | " }\n" |
| 239 | " else if (exponent == 31)\n" |
| 240 | " {\n" |
| 241 | " return uintBitsToFloat(sign | 0x7F800000u | mantissa);\n" |
| 242 | " }\n" |
| 243 | " else\n" |
| 244 | " {\n" |
| 245 | " exponent -= 15;\n" |
| 246 | " float scale;\n" |
| 247 | " if(exponent < 0)\n" |
| 248 | " {\n" |
| 249 | " // The negative unary operator is buggy on OSX.\n" |
| 250 | " // Work around this by using abs instead.\n" |
| 251 | " scale = 1.0 / (1 << abs(exponent));\n" |
| 252 | " }\n" |
| 253 | " else\n" |
| 254 | " {\n" |
| 255 | " scale = 1 << exponent;\n" |
| 256 | " }\n" |
| 257 | " float decimal = 1.0 + float(mantissa) / float(1 << 10);\n" |
| 258 | " f32 = scale * decimal;\n" |
| 259 | " }\n" |
| 260 | "\n" |
| 261 | " if (sign != 0u)\n" |
| 262 | " {\n" |
| 263 | " f32 = -f32;\n" |
| 264 | " }\n" |
| 265 | "\n" |
| 266 | " return f32;\n" |
| 267 | " }\n" |
| 268 | "#endif\n" |
| 269 | "\n" |
| 270 | "vec2 unpackHalf2x16_emu(uint u)\n" |
| 271 | "{\n" |
| 272 | " #if defined(GL_ARB_shading_language_packing)\n" |
| 273 | " return unpackHalf2x16(u);\n" |
| 274 | " #else\n" |
| 275 | " uint y = (u >> 16);\n" |
| 276 | " uint x = u & 0xFFFFu;\n" |
| 277 | " return vec2(f16tof32(x), f16tof32(y));\n" |
| 278 | " #endif\n" |
| 279 | "}\n" ); |
| 280 | // clang-format on |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | } // namespace sh |
| 285 | |