diff --git a/WickedEngine/shaders/ShaderInterop_Renderer.h b/WickedEngine/shaders/ShaderInterop_Renderer.h index 05ac9432e..b7cbaac43 100644 --- a/WickedEngine/shaders/ShaderInterop_Renderer.h +++ b/WickedEngine/shaders/ShaderInterop_Renderer.h @@ -1096,9 +1096,9 @@ struct VolumetricCloudCapturePushConstants struct TerrainVirtualTexturePush { uint2 offset; + float2 resolution_rcp; uint map_type; int region_weights_textureRO; - int output_textureRW; }; struct VirtualTextureResidencyUpdatePush { diff --git a/WickedEngine/shaders/compressonator/bcn_common_api.h b/WickedEngine/shaders/compressonator/bcn_common_api.h new file mode 100644 index 000000000..676428664 --- /dev/null +++ b/WickedEngine/shaders/compressonator/bcn_common_api.h @@ -0,0 +1,1408 @@ +//=============================================================================== +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//=============================================================================== + +#ifndef BCN_COMMON_API_H_ +#define BCN_COMMON_API_H_ + +//=================================================================== +// NOTE: Do not use these API in production code, subject to changes +//=================================================================== + +#ifndef ASPM_GPU +#pragma warning(disable : 4244) +#pragma warning(disable : 4201) +#endif + +#include "common_def.h" + +#define CMP_MAX_16BITFLOAT 65504.0f +#define CMP_FLT_MAX 3.402823466e+38F +#define BC1ConstructColour(r, g, b) (((r) << 11) | ((g) << 5) | (b)) + + +#ifdef ASPM_HLSL +#define fabs(x) abs(x) +#endif + +CMP_STATIC CGU_FLOAT cmp_fabs(CMP_IN CGU_FLOAT x) +{ + return fabs(x); +} + +CMP_STATIC CGU_FLOAT cmp_linearToSrgbf(CMP_IN CGU_FLOAT Color) +{ + if (Color <= 0.0f) + return (0.0f); + if (Color >= 1.0f) + return (1.0f); + // standard : 0.0031308f + if (Color <= 0.00313066844250063) + return (Color * 12.92f); + return (pow(fabs(Color), 1.0f / 2.4f) * 1.055f - 0.055f); +} + +CMP_STATIC CGU_Vec3f cmp_linearToSrgb(CMP_IN CGU_Vec3f Color) +{ + Color.x = cmp_linearToSrgbf(Color.x); + Color.y = cmp_linearToSrgbf(Color.y); + Color.z = cmp_linearToSrgbf(Color.z); + return Color; +} + +CMP_STATIC CGU_FLOAT cmp_srgbToLinearf(CMP_IN CGU_FLOAT Color) +{ + if (Color <= 0.0f) + return (0.0f); + if (Color >= 1.0f) + return (1.0f); + // standard 0.04045f + if (Color <= 0.0404482362771082) + return (Color / 12.92f); + return pow((Color + 0.055f) / 1.055f, 2.4f); +} + +CMP_STATIC CGU_Vec3f cmp_srgbToLinear(CMP_IN CGU_Vec3f Color) +{ + Color.x = cmp_srgbToLinearf(Color.x); + Color.y = cmp_srgbToLinearf(Color.y); + Color.z = cmp_srgbToLinearf(Color.z); + return Color; +} + +CMP_STATIC CGU_Vec3f cmp_565ToLinear(CMP_IN CGU_UINT32 n565) +{ + CGU_UINT32 r0; + CGU_UINT32 g0; + CGU_UINT32 b0; + + r0 = ((n565 & 0xf800) >> 8); + g0 = ((n565 & 0x07e0) >> 3); + b0 = ((n565 & 0x001f) << 3); + + // Apply the lower bit replication to give full dynamic range (5,6,5) + r0 += (r0 >> 5); + g0 += (g0 >> 6); + b0 += (b0 >> 5); + + CGU_Vec3f LinearColor; + LinearColor.x = (CGU_FLOAT)r0; + LinearColor.y = (CGU_FLOAT)g0; + LinearColor.z = (CGU_FLOAT)b0; + + return LinearColor; +} + +CMP_STATIC CGU_UINT32 cmp_get2Bit32(CMP_IN CGU_UINT32 value, CMP_IN CGU_UINT32 indexPos) +{ + return (value >> (indexPos * 2)) & 0x3; +} + +CMP_STATIC CGU_UINT32 cmp_set2Bit32(CMP_IN CGU_UINT32 value, CMP_IN CGU_UINT32 indexPos) +{ + return ((value & 0x3) << (indexPos * 2)); +} + +CMP_STATIC CGU_UINT32 cmp_constructColor(CMP_IN CGU_UINT32 R, CMP_IN CGU_UINT32 G, CMP_IN CGU_UINT32 B) +{ + return (((R & 0x000000F8) << 8) | ((G & 0x000000FC) << 3) | ((B & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_FLOAT cmp_clampf(CMP_IN CGU_FLOAT v, CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC CGU_Vec3f cmp_clampVec3f(CMP_IN CGU_Vec3f value, CMP_IN CGU_FLOAT minValue, CMP_IN CGU_FLOAT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, minValue, maxValue); + revalue.y = cmp_clampf(value.y, minValue, maxValue); + revalue.z = cmp_clampf(value.z, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_saturate(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return saturate(value); +#else + return cmp_clampVec3f(value, 0.0f, 1.0f); +#endif +} + +static CGU_Vec3f cmp_powVec3f(CGU_Vec3f color, CGU_FLOAT ex) +{ +#ifdef ASPM_GPU + return pow(color, ex); +#else + CGU_Vec3f ColorSrgbPower; + ColorSrgbPower.x = pow(color.x, ex); + ColorSrgbPower.y = pow(color.y, ex); + ColorSrgbPower.z = pow(color.z, ex); + return ColorSrgbPower; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_minVec3f(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ +#ifdef ASPM_HLSL + return min(a, b); +#endif + CGU_Vec3f res; + if (a.x < b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y < b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z < b.z) + res.z = a.z; + else + res.z = b.z; + return res; +} + +CMP_STATIC CGU_Vec3f cmp_maxVec3f(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ +#ifdef ASPM_HLSL + return max(a, b); +#endif + CGU_Vec3f res; + if (a.x > b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y > b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z > b.z) + res.z = a.z; + else + res.z = b.z; + return res; +} + +inline CGU_Vec3f cmp_min3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return min(value1, value2); +#else + CGU_Vec3f res; + res.x = CMP_MIN(value1.x, value2.x); + res.y = CMP_MIN(value1.y, value2.y); + res.z = CMP_MIN(value1.z, value2.z); + return res; +#endif +} + +inline CGU_Vec3f cmp_max3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return max(value1, value2); +#else + CGU_Vec3f res; + res.x = CMP_MAX(value1.x, value2.x); + res.y = CMP_MAX(value1.y, value2.y); + res.z = CMP_MAX(value1.z, value2.z); + return res; +#endif +} + +CMP_STATIC CGU_FLOAT cmp_minf(CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + return a < b ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_maxf(CMP_IN CGU_FLOAT a, CMP_IN CGU_FLOAT b) +{ + return a > b ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_floor(CMP_IN CGU_FLOAT value) +{ + return floor(value); +} + +CMP_STATIC CGU_Vec3f cmp_floorVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return floor(value); +#else + CGU_Vec3f revalue; + revalue.x = floor(value.x); + revalue.y = floor(value.y); + revalue.z = floor(value.z); + return revalue; +#endif +} + +#ifndef ASPM_OPENCL + +//======================================================= +// COMMON GPU & CPU API +//======================================================= + +//====================== +// implicit vector cast +//====================== +CMP_STATIC CGU_Vec4i cmp_castimp(CGU_Vec4ui v1) +{ +#ifdef ASPM_HLSL + return (v1); +#else + return (v1.x, v1.y, v1.z, v1.w); +#endif +} + +CMP_STATIC CGU_Vec3i cmp_castimp(CGU_Vec3ui v1) +{ +#ifdef ASPM_HLSL + return (v1); +#else + return (v1.x, v1.y, v1.z); +#endif +} + +//====================== +// Min / Max +//====================== + +CMP_STATIC CGU_UINT8 cmp_min8(CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + return a < b ? a : b; +} + +CMP_STATIC CGU_UINT8 cmp_max8(CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + return a > b ? a : b; +} + +CMP_STATIC CGU_UINT32 cmp_mini(CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + return (a < b) ? a : b; +} + +CMP_STATIC CGU_UINT32 cmp_maxi(CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + return (a > b) ? a : b; +} + +CMP_STATIC CGU_FLOAT cmp_max3(CMP_IN CGU_FLOAT i, CMP_IN CGU_FLOAT j, CMP_IN CGU_FLOAT k) +{ +#ifdef ASPM_GLSL + return max3(i, j, k); +#else + CGU_FLOAT max = i; + + if (max < j) + max = j; + + if (max < k) + max = k; + + return (max); +#endif +} + + +CMP_STATIC CGU_Vec4ui cmp_minVec4ui(CMP_IN CGU_Vec4ui a, CMP_IN CGU_Vec4ui b) +{ + //#ifdef ASPM_HLSL + // return min(a, b); + //#endif + //#ifndef ASPM_GPU + CGU_Vec4ui res; + if (a.x < b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y < b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z < b.z) + res.z = a.z; + else + res.z = b.z; + if (a.w < b.w) + res.w = a.w; + else + res.w = b.w; + return res; + //#endif +} + +CMP_STATIC CGU_Vec4ui cmp_maxVec4ui(CMP_IN CGU_Vec4ui a, CMP_IN CGU_Vec4ui b) +{ + //#ifdef ASPM_HLSL + // return max(a, b); + //#endif + //#ifndef ASPM_GPU + CGU_Vec4ui res; + if (a.x > b.x) + res.x = a.x; + else + res.x = b.x; + if (a.y > b.y) + res.y = a.y; + else + res.y = b.y; + if (a.z > b.z) + res.z = a.z; + else + res.z = b.z; + if (a.w > b.w) + res.w = a.w; + else + res.w = b.w; + return res; + //#endif +} + +//====================== +// Clamps +//====================== + +CMP_STATIC CGU_UINT32 cmp_clampui32(CMP_IN CGU_UINT32 v, CMP_IN CGU_UINT32 a, CMP_IN CGU_UINT32 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + + +// Test Ref:https://en.wikipedia.org/wiki/Half-precision_floating-point_format +// Half (in Hex) Float Comment +// --------------------------------------------------------------------------- +// 0001 (approx) = 0.000000059604645 smallest positive subnormal number +// 03ff (approx) = 0.000060975552 largest subnormal number +// 0400 (approx) = 0.00006103515625 smallest positive normal number +// 7bff (approx) = 65504 largest normal number +// 3bff (approx) = 0.99951172 largest number less than one +// 3c00 (approx) = 1.00097656 smallest number larger than one +// 3555 = 0.33325195 the rounding of 1/3 to nearest +// c000 = ?2 +// 8000 = -0 +// 0000 = 0 +// 7c00 = infinity +// fc00 = infinity +// Half Float Math + +CMP_STATIC CGU_FLOAT HalfToFloat(CGU_UINT32 h) +{ +#if defined(ASPM_GPU) + CGU_FLOAT f = min16float((float)(h)); + return f; +#else + union FP32 + { + CGU_UINT32 u; + CGU_FLOAT f; + }; + + const FP32 magic = {(254 - 15) << 23}; + const FP32 was_infnan = {(127 + 16) << 23}; + + FP32 o; + o.u = (h & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // check Inf/NaN + o.u |= 255 << 23; + o.u |= (h & 0x8000) << 16; // sign bit + return o.f; +#endif +} + +// From BC6HEcode.hlsl + +CMP_STATIC CGU_FLOAT cmp_half2float1(CGU_UINT32 Value) +{ + CGU_UINT32 Mantissa = (CGU_UINT32)(Value & 0x03FF); + + CGU_UINT32 Exponent; + if ((Value & 0x7C00) != 0) // The value is normalized + { + Exponent = (CGU_UINT32)((Value >> 10) & 0x1F); + } + else if (Mantissa != 0) // The value is denormalized + { + // Normalize the value in the resulting float + Exponent = 1; + + do + { + Exponent--; + Mantissa <<= 1; + } while ((Mantissa & 0x0400) == 0); + + Mantissa &= 0x03FF; + } + else // The value is zero + { + Exponent = (CGU_UINT32)(-112); + } + + CGU_UINT32 Result = ((Value & 0x8000) << 16) | // Sign + ((Exponent + 112) << 23) | // Exponent + (Mantissa << 13); // Mantissa + + return CGU_FLOAT(Result); +} + +CMP_STATIC CGU_Vec3f cmp_half2floatVec3(CGU_Vec3ui color_h) +{ + //uint3 sign = color_h & 0x8000; + //uint3 expo = color_h & 0x7C00; + //uint3 base = color_h & 0x03FF; + //return ( expo == 0 ) ? asfloat( ( sign << 16 ) | asuint( float3(base) / 16777216 ) ) //16777216 = 2^24 + // : asfloat( ( sign << 16 ) | ( ( ( expo + 0x1C000 ) | base ) << 13 ) ); //0x1C000 = 0x1FC00 - 0x3C00 + + return CGU_Vec3f(cmp_half2float1(color_h.x), cmp_half2float1(color_h.y), cmp_half2float1(color_h.z)); +} + +CMP_STATIC CGU_UINT16 FloatToHalf(CGU_FLOAT value) +{ +#if defined(ASPM_GPU) + return 0; +#else + union FP32 + { + CGU_UINT16 u; + float f; + struct + { + CGU_UINT32 Mantissa : 23; + CGU_UINT32 Exponent : 8; + CGU_UINT32 Sign : 1; + }; + }; + + union FP16 + { + CGU_UINT16 u; + struct + { + CGU_UINT32 Mantissa : 10; + CGU_UINT32 Exponent : 5; + CGU_UINT32 Sign : 1; + }; + }; + + FP16 o = {0}; + FP32 f; + f.f = value; + + // Based on ISPC reference code (with minor modifications) + if (f.Exponent == 0) // Signed zero/denormal (which will underflow) + o.Exponent = 0; + else if (f.Exponent == 255) // Inf or NaN (all exponent bits set) + { + o.Exponent = 31; + o.Mantissa = f.Mantissa ? 0x200 : 0; // NaN->qNaN and Inf->Inf + } + else // Normalized number + { + // Exponent unbias the single, then bias the halfp + int newexp = f.Exponent - 127 + 15; + if (newexp >= 31) // Overflow, return signed infinity + o.Exponent = 31; + else if (newexp <= 0) // Underflow + { + if ((14 - newexp) <= 24) // Mantissa might be non-zero + { + CGU_UINT32 mant = f.Mantissa | 0x800000; // Hidden 1 bit + o.Mantissa = mant >> (14 - newexp); + if ((mant >> (13 - newexp)) & 1) // Check for rounding + o.u++; // Round, might overflow into exp bit, but this is OK + } + } + else + { + o.Exponent = newexp; + o.Mantissa = f.Mantissa >> 13; + if (f.Mantissa & 0x1000) // Check for rounding + o.u++; // Round, might overflow to inf, this is OK + } + } + + o.Sign = f.Sign; + return o.u; +#endif +} + +CMP_STATIC CGU_UINT32 cmp_float2halfui(CGU_FLOAT f) +{ + CGU_UINT32 Result; + + CGU_UINT32 IValue = CGU_UINT32(f); + CGU_UINT32 Sign = (IValue & 0x80000000U) >> 16U; + IValue = IValue & 0x7FFFFFFFU; + + if (IValue > 0x47FFEFFFU) + { + // The number is too large to be represented as a half. Saturate to infinity. + Result = 0x7FFFU; + } + else + { + if (IValue < 0x38800000U) + { + // The number is too small to be represented as a normalized half. + // Convert it to a denormalized value. + CGU_UINT32 Shift = 113U - (IValue >> 23U); + IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized half. + IValue += 0xC8000000U; + } + + Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU; + } + return (Result | Sign); +} + +CMP_STATIC CGU_Vec3ui cmp_float2half(CGU_Vec3f endPoint_f) +{ + return CGU_Vec3ui(cmp_float2halfui(endPoint_f.x), cmp_float2halfui(endPoint_f.y), cmp_float2halfui(endPoint_f.z)); +} + +CMP_STATIC CGU_UINT32 cmp_float2half1(CGU_FLOAT f) +{ + CGU_UINT32 Result; + + CGU_UINT32 IValue = CGU_UINT32(f); //asuint(f); + CGU_UINT32 Sign = (IValue & 0x80000000U) >> 16U; + IValue = IValue & 0x7FFFFFFFU; + + if (IValue > 0x47FFEFFFU) + { + // The number is too large to be represented as a half. Saturate to infinity. + Result = 0x7FFFU; + } + else + { + if (IValue < 0x38800000U) + { + // The number is too small to be represented as a normalized half. + // Convert it to a denormalized value. + CGU_UINT32 Shift = 113U - (IValue >> 23U); + IValue = (0x800000U | (IValue & 0x7FFFFFU)) >> Shift; + } + else + { + // Rebias the exponent to represent the value as a normalized half. + IValue += 0xC8000000U; + } + + Result = ((IValue + 0x0FFFU + ((IValue >> 13U) & 1U)) >> 13U) & 0x7FFFU; + } + return (Result | Sign); +} + +CMP_STATIC CGU_Vec3ui cmp_float2halfVec3(CGU_Vec3f endPoint_f) +{ + return CGU_Vec3ui(cmp_float2half1(endPoint_f.x), cmp_float2half1(endPoint_f.y), cmp_float2half1(endPoint_f.z)); +} + +CMP_STATIC CGU_FLOAT cmp_f32tof16(CMP_IN CGU_FLOAT value) +{ +#ifdef ASPM_GLSL + return packHalf2x16(CGU_Vec2f(value.x, 0.0)); +#endif +#ifdef ASPM_HLSL + return f32tof16(value); +#endif +#ifndef ASPM_GPU + return FloatToHalf(value); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f32tof16(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(packHalf2x16(CGU_Vec2f(value.x, 0.0)), packHalf2x16(CGU_Vec2f(value.y, 0.0)), packHalf2x16(CGU_Vec2f(value.z, 0.0))); +#endif +#ifdef ASPM_HLSL + return f32tof16(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = FloatToHalf(value.x); + res.y = FloatToHalf(value.y); + res.z = FloatToHalf(value.z); + return res; +#endif +} + +CMP_STATIC CGU_FLOAT cmp_f16tof32(CGU_UINT32 value) +{ +#ifdef ASPM_GLSL + return unpackHalf2x16(value).x; +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + return HalfToFloat(value); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f16tof32(CGU_Vec3ui value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(unpackHalf2x16(value.x).x, unpackHalf2x16(value.y).x, unpackHalf2x16(value.z).x); +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = HalfToFloat(value.x); + res.y = HalfToFloat(value.y); + res.z = HalfToFloat(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_f16tof32(CGU_Vec3f value) +{ +#ifdef ASPM_GLSL + return CGU_Vec3f(unpackHalf2x16(value.x).x, unpackHalf2x16(value.y).x, unpackHalf2x16(value.z).x); +#endif +#ifdef ASPM_HLSL + return f16tof32(value); +#endif +#ifndef ASPM_GPU + CGU_Vec3f res; + res.x = HalfToFloat((CGU_UINT32)value.x); + res.y = HalfToFloat((CGU_UINT32)value.y); + res.z = HalfToFloat((CGU_UINT32)value.z); + return res; +#endif +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_Vec3f CMP_REFINOUT a, CMP_INOUT CGU_Vec3f CMP_REFINOUT b) +{ + CGU_Vec3f tmp = a; + a = b; + b = tmp; +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_FLOAT CMP_REFINOUT a, CMP_INOUT CGU_FLOAT CMP_REFINOUT b) +{ + CGU_FLOAT tmp = a; + a = b; + b = tmp; +} + +CMP_STATIC void cmp_swap(CMP_INOUT CGU_Vec3i CMP_REFINOUT lhs, CMP_INOUT CGU_Vec3i CMP_REFINOUT rhs) // valided with msc code +{ + CGU_Vec3i tmp = lhs; + lhs = rhs; + rhs = tmp; +} + +CMP_STATIC CGU_INT cmp_dotVec2i(CMP_IN CGU_Vec2i value1, CMP_IN CGU_Vec2i value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y); +#endif +} + +CMP_STATIC CGU_FLOAT cmp_dotVec3f(CMP_IN CGU_Vec3f value1, CMP_IN CGU_Vec3f value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec3ui(CMP_IN CGU_Vec3ui value1, CMP_IN CGU_Vec3ui value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec4i(CMP_IN CGU_Vec4i value1, CMP_IN CGU_Vec4i value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC CGU_UINT32 cmp_dotVec4ui(CMP_IN CGU_Vec4ui value1, CMP_IN CGU_Vec4ui value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC CGU_Vec3f cmp_clampVec3fi(CMP_IN CGU_Vec3f value, CMP_IN CGU_INT minValue, CMP_IN CGU_INT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + revalue.y = cmp_clampf(value.y, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + revalue.z = cmp_clampf(value.z, (CGU_FLOAT)minValue, (CGU_FLOAT)maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec4ui cmp_clampVec4ui(CMP_IN CGU_Vec4ui value, CMP_IN CGU_UINT32 minValue, CMP_IN CGU_UINT32 maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec4ui revalue; + revalue.x = cmp_clampui32(value.x, minValue, maxValue); + revalue.y = cmp_clampui32(value.y, minValue, maxValue); + revalue.z = cmp_clampui32(value.z, minValue, maxValue); + revalue.w = cmp_clampui32(value.w, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec4f cmp_clampVec4f(CMP_IN CGU_Vec4f value, CMP_IN CGU_FLOAT minValue, CMP_IN CGU_FLOAT maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec4f revalue; + revalue.x = cmp_clampf(value.x, minValue, maxValue); + revalue.y = cmp_clampf(value.y, minValue, maxValue); + revalue.z = cmp_clampf(value.z, minValue, maxValue); + revalue.w = cmp_clampf(value.w, minValue, maxValue); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_clamp3Vec3f(CMP_IN CGU_Vec3f value, CMP_IN CGU_Vec3f minValue, CMP_IN CGU_Vec3f maxValue) +{ +#ifdef ASPM_GPU + return clamp(value, minValue, maxValue); +#else + CGU_Vec3f revalue; + revalue.x = cmp_clampf(value.x, minValue.x, maxValue.x); + revalue.y = cmp_clampf(value.y, minValue.y, maxValue.y); + revalue.z = cmp_clampf(value.z, minValue.z, maxValue.z); + return revalue; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_exp2(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return exp2(value); +#else + CGU_Vec3f revalue; + revalue.x = exp2(value.x); + revalue.y = exp2(value.y); + revalue.z = exp2(value.z); + return revalue; +#endif +} + + +CMP_STATIC CGU_Vec3f cmp_roundVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return round(value); +#endif +#ifndef ASPM_HLSL + CGU_Vec3f res; + res.x = round(value.x); + res.y = round(value.y); + res.z = round(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_log2Vec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_GPU + return log2(value); +#else + CGU_Vec3f res; + res.x = log2(value.x); + res.y = log2(value.y); + res.z = log2(value.z); + return res; +#endif +} + +// used in BC1 LowQuality code +CMP_STATIC CGU_FLOAT cmp_saturate(CMP_IN CGU_FLOAT value) +{ +#ifdef ASPM_HLSL + return saturate(value); +#else + return cmp_clampf(value, 0.0f, 1.0f); +#endif +} + +CMP_STATIC CGU_FLOAT cmp_rcp(CMP_IN CGU_FLOAT det) +{ +#ifdef ASPM_HLSL + return rcp(det); +#else + if (det > 0.0f) + return (1 / det); + else + return 0.0f; +#endif +} + +CMP_STATIC CGU_UINT32 cmp_Get4BitIndexPos(CMP_IN CGU_FLOAT indexPos, CMP_IN CGU_FLOAT endPoint0Pos, CMP_IN CGU_FLOAT endPoint1Pos) +{ + CGU_FLOAT r = (indexPos - endPoint0Pos) / (endPoint1Pos - endPoint0Pos); + return cmp_clampui32(CGU_UINT32(r * 14.93333f + 0.03333f + 0.5f), 0, 15); +} + +// Calculate Mean Square Least Error (MSLE) for 2 Vectors +CMP_STATIC CGU_FLOAT cmp_CalcMSLE(CMP_IN CGU_Vec3f a, CMP_IN CGU_Vec3f b) +{ + CGU_Vec3f err = cmp_log2Vec3f((b + 1.0f) / (a + 1.0f)); + err = err * err; + return err.x + err.y + err.z; +} + + + +// Compute Endpoints (min/max) bounding box +CMP_STATIC void cmp_GetTexelMinMax(CMP_IN CGU_Vec3f texels[16], CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + blockMin = texels[0]; + blockMax = texels[0]; + for (CGU_UINT32 i = 1u; i < 16u; ++i) + { + blockMin = cmp_minVec3f(blockMin, texels[i]); + blockMax = cmp_maxVec3f(blockMax, texels[i]); + } +} + +// Refine Endpoints (min/max) by insetting bounding box in log2 RGB space +CMP_STATIC void cmp_RefineMinMaxAsLog2(CMP_IN CGU_Vec3f texels[16], CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + CGU_Vec3f refinedBlockMin = blockMax; + CGU_Vec3f refinedBlockMax = blockMin; + for (CGU_UINT32 i = 0u; i < 16u; ++i) + { + refinedBlockMin = cmp_minVec3f(refinedBlockMin, texels[i] == blockMin ? refinedBlockMin : texels[i]); + refinedBlockMax = cmp_maxVec3f(refinedBlockMax, texels[i] == blockMax ? refinedBlockMax : texels[i]); + } + + CGU_Vec3f logBlockMax = cmp_log2Vec3f(blockMax + 1.0f); + CGU_Vec3f logBlockMin = cmp_log2Vec3f(blockMin + 1.0f); + + CGU_Vec3f logRefinedBlockMax = cmp_log2Vec3f(refinedBlockMax + 1.0f); + CGU_Vec3f logRefinedBlockMin = cmp_log2Vec3f(refinedBlockMin + 1.0f); + CGU_Vec3f logBlockMaxExt = (logBlockMax - logBlockMin) * (1.0f / 32.0f); + + logBlockMin += cmp_minVec3f(logRefinedBlockMin - logBlockMin, logBlockMaxExt); + logBlockMax -= cmp_minVec3f(logBlockMax - logRefinedBlockMax, logBlockMaxExt); + + blockMin = cmp_exp2(logBlockMin) - 1.0f; + blockMax = cmp_exp2(logBlockMax) - 1.0f; +} + +// Refine Endpoints (min/max) by Least Squares Optimization +CMP_STATIC void cmp_RefineMinMaxAs16BitLeastSquares(CMP_IN CGU_Vec3f texels[16], + CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMin, + CMP_INOUT CGU_Vec3f CMP_REFINOUT blockMax) +{ + CGU_Vec3f blockDir = blockMax - blockMin; + blockDir = blockDir / (blockDir.x + blockDir.y + blockDir.z); + + CGU_FLOAT endPoint0Pos = cmp_f32tof16(cmp_dotVec3f(blockMin, blockDir)); + CGU_FLOAT endPoint1Pos = cmp_f32tof16(cmp_dotVec3f(blockMax, blockDir)); + + CGU_Vec3f alphaTexelSum = 0.0f; + CGU_Vec3f betaTexelSum = 0.0f; + CGU_FLOAT alphaBetaSum = 0.0f; + CGU_FLOAT alphaSqSum = 0.0f; + CGU_FLOAT betaSqSum = 0.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + CGU_FLOAT texelPos = cmp_f32tof16(cmp_dotVec3f(texels[i], blockDir)); + CGU_UINT32 texelIndex = cmp_Get4BitIndexPos(texelPos, endPoint0Pos, endPoint1Pos); + + CGU_FLOAT beta = cmp_saturate(texelIndex / 15.0f); + CGU_FLOAT alpha = 1.0f - beta; + + CGU_Vec3f texelF16; + texelF16.x = cmp_f32tof16(texels[i].x); + texelF16.y = cmp_f32tof16(texels[i].y); + texelF16.z = cmp_f32tof16(texels[i].z); + + alphaTexelSum += texelF16 * alpha; + betaTexelSum += texelF16 * beta; + + alphaBetaSum += alpha * beta; + + alphaSqSum += alpha * alpha; + betaSqSum += beta * beta; + } + + CGU_FLOAT det = alphaSqSum * betaSqSum - alphaBetaSum * alphaBetaSum; + + if (abs(det) > 0.00001f) + { + CGU_FLOAT detRcp = cmp_rcp(det); + blockMin = cmp_clampVec3f((alphaTexelSum * betaSqSum - betaTexelSum * alphaBetaSum) * detRcp, 0.0f, CMP_MAX_16BITFLOAT); + blockMax = cmp_clampVec3f((betaTexelSum * alphaSqSum - alphaTexelSum * alphaBetaSum) * detRcp, 0.0f, CMP_MAX_16BITFLOAT); + blockMin = cmp_f16tof32(blockMin); + blockMax = cmp_f16tof32(blockMax); + } +} + +//============================================================================================= + +CMP_STATIC CGU_Vec3f cmp_fabsVec3f(CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return abs(value); +#else + CGU_Vec3f res; + res.x = abs(value.x); + res.y = abs(value.y); + res.z = abs(value.z); + return res; +#endif +} + + +CMP_STATIC CGU_UINT32 cmp_constructColor(CMP_IN CGU_Vec3ui EndPoints) +{ + return (((EndPoints.r & 0x000000F8) << 8) | ((EndPoints.g & 0x000000FC) << 3) | ((EndPoints.b & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_UINT32 cmp_constructColorBGR(CMP_IN CGU_Vec3f EndPoints) +{ + return (((CGU_UINT32(EndPoints.b) & 0x000000F8) << 8) | ((CGU_UINT32(EndPoints.g) & 0x000000FC) << 3) | ((CGU_UINT32(EndPoints.r) & 0x000000F8) >> 3)); +} + +CMP_STATIC CGU_FLOAT cmp_mod(CMP_IN CGU_FLOAT value, CMP_IN CGU_FLOAT modval) +{ +#ifdef ASPM_GLSL + return mod(value, modval); +#endif + return fmod(value, modval); +} + +CMP_STATIC CGU_Vec3f cmp_truncVec3f(CMP_IN CGU_Vec3f value) +{ +#ifdef ASPM_HLSL + return trunc(value); +#else + CGU_Vec3f res; + res.x = trunc(value.x); + res.y = trunc(value.y); + res.z = trunc(value.z); + return res; +#endif +} + +CMP_STATIC CGU_Vec3f cmp_ceilVec3f(CMP_IN CGU_Vec3f value) +{ + CGU_Vec3f res; + res.x = ceil(value.x); + res.y = ceil(value.y); + res.z = ceil(value.z); + return res; +} + +CMP_STATIC CGU_FLOAT cmp_sqrt(CGU_FLOAT value) +{ + return sqrt(value); +} + +// Computes inverse square root over an implementation-defined range. The maximum error is implementation-defined. +CMP_STATIC CGV_FLOAT cmp_rsqrt(CGV_FLOAT f) +{ + CGV_FLOAT sf = sqrt(f); + if (sf != 0) + return 1 / sqrt(f); + else + return 0.0f; +} + +// Common to BC7 API ------------------------------------------------------------------------------------------------------------------------ + +// valid bit range is 0..8 for mode 1 +CMP_STATIC INLINE CGU_UINT32 cmp_shift_right_uint32(CMP_IN CGU_UINT32 v, CMP_IN CGU_INT bits) +{ + return v >> bits; // (perf warning expected) +} + +CMP_STATIC INLINE CGU_INT cmp_clampi(CMP_IN CGU_INT value, CMP_IN CGU_INT low, CMP_IN CGU_INT high) +{ + if (value < low) + return low; + else if (value > high) + return high; + return value; +} + +CMP_STATIC INLINE CGU_INT32 cmp_clampi32(CMP_IN CGU_INT32 value, CMP_IN CGU_INT32 low, CMP_IN CGU_INT32 high) +{ + if (value < low) + value = low; + else if (value > high) + value = high; + return value; +} + +CMP_STATIC CGV_FLOAT cmp_dot4f(CMP_IN CGV_Vec4f value1, CMP_IN CGV_Vec4f value2) +{ +#ifdef ASPM_GPU + return dot(value1, value2); +#else + return (value1.x * value2.x) + (value1.y * value2.y) + (value1.z * value2.z) + (value1.w * value2.w); +#endif +} + +CMP_STATIC INLINE void cmp_set_vec4f(CMP_INOUT CGU_Vec4f CMP_REFINOUT pV, CMP_IN CGU_FLOAT x, CMP_IN CGU_FLOAT y, CMP_IN CGU_FLOAT z, CMP_IN CGU_FLOAT w) +{ + pV[0] = x; + pV[1] = y; + pV[2] = z; + pV[3] = w; +} + +CMP_STATIC INLINE void cmp_set_vec4ui(CGU_Vec4ui CMP_REFINOUT pV, CMP_IN CGU_UINT8 x, CMP_IN CGU_UINT8 y, CMP_IN CGU_UINT8 z, CMP_IN CGU_UINT8 w) +{ + pV[0] = x; + pV[1] = y; + pV[2] = z; + pV[3] = w; +} + +CMP_STATIC inline void cmp_set_vec4ui_clamped(CGU_Vec4ui CMP_REFINOUT pRes, CMP_IN CGU_INT32 r, CMP_IN CGU_INT32 g, CMP_IN CGU_INT32 b, CMP_IN CGU_INT32 a) +{ + pRes[0] = (CGU_UINT8)cmp_clampi32(r, 0, 255); + pRes[1] = (CGU_UINT8)cmp_clampi32(g, 0, 255); + pRes[2] = (CGU_UINT8)cmp_clampi32(b, 0, 255); + pRes[3] = (CGU_UINT8)cmp_clampi32(a, 0, 255); +} + +CMP_STATIC inline CGU_Vec4f cmp_clampNorm4f(CMP_IN CGU_Vec4f pV) +{ + CGU_Vec4f res; + res[0] = cmp_clampf(pV[0], 0.0f, 1.0f); + res[1] = cmp_clampf(pV[1], 0.0f, 1.0f); + res[2] = cmp_clampf(pV[2], 0.0f, 1.0f); + res[3] = cmp_clampf(pV[3], 0.0f, 1.0f); + return res; +} + +CMP_STATIC INLINE CGU_Vec4f cmp_vec4ui_to_vec4f(CMP_IN CGU_Vec4ui pC) +{ + CGU_Vec4f res; + cmp_set_vec4f(res, (CGU_FLOAT)pC[0], (CGU_FLOAT)pC[1], (CGU_FLOAT)pC[2], (CGU_FLOAT)pC[3]); + return res; +} + +CMP_STATIC INLINE void cmp_normalize(CGU_Vec4f CMP_REFINOUT pV) +{ + CGU_FLOAT s = cmp_dot4f(pV, pV); + if (s != 0.0f) + { + s = 1.0f / cmp_sqrt(s); + pV *= s; + } +} + +CMP_STATIC INLINE CGV_FLOAT cmp_squaref(CMP_IN CGV_FLOAT v) +{ + return v * v; +} + +CMP_STATIC INLINE CGU_INT cmp_squarei(CMP_IN CGU_INT i) +{ + return i * i; +} + +CMP_STATIC CGU_UINT8 cmp_clampui8(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC CGU_INT32 cmp_abs32(CMP_IN CGU_INT32 v) +{ + CGU_UINT32 msk = v >> 31; + return (v ^ msk) - msk; +} + +CMP_STATIC void cmp_swap32(CMP_INOUT CGU_UINT32 CMP_REFINOUT a, CMP_INOUT CGU_UINT32 CMP_REFINOUT b) +{ + CGU_UINT32 t = a; + a = b; + b = t; +} + +// Computes inverse square root over an implementation-defined range. The maximum error is implementation-defined. +CMP_STATIC CGV_FLOAT cmp_Image_rsqrt(CMP_IN CGV_FLOAT f) +{ + CGV_FLOAT sf = sqrt(f); + if (sf != 0) + return 1 / sqrt(f); + else + return 0.0f; +} + +CMP_STATIC void cmp_pack4bitindex32(CMP_INOUT CGU_UINT32 packed_index[2], CMP_IN CGU_UINT32 src_index[16]) +{ + // Converts from unpacked index to packed index + packed_index[0] = 0x0000; + packed_index[1] = 0x0000; + CGU_UINT32 shift = 0; // was CGU_UINT8 + for (CGU_INT k = 0; k < 8; k++) + { + packed_index[0] |= (CGU_UINT32)(src_index[k] & 0x0F) << shift; + packed_index[1] |= (CGU_UINT32)(src_index[k + 8] & 0x0F) << shift; + shift += 4; + } +} + +CMP_STATIC void cmp_pack4bitindex(CMP_INOUT CGU_UINT32 packed_index[2], CMP_IN CGU_UINT8 src_index[16]) +{ + // Converts from unpacked index to packed index + packed_index[0] = 0x0000; + packed_index[1] = 0x0000; + CGU_UINT32 shift = 0; // was CGU_UINT8 + for (CGU_INT k = 0; k < 8; k++) + { + packed_index[0] |= (CGU_UINT32)(src_index[k] & 0x0F) << shift; + packed_index[1] |= (CGU_UINT32)(src_index[k + 8] & 0x0F) << shift; + shift += 4; + } +} + +CMP_STATIC INLINE CGU_INT cmp_expandbits(CMP_IN CGU_INT v, CMP_IN CGU_INT bits) +{ + CGU_INT vv = v << (8 - bits); + return vv + cmp_shift_right_uint32(vv, bits); +} + +// This code need further improvements and investigation +CMP_STATIC INLINE CGU_UINT8 cmp_ep_find_floor2(CMP_IN CGV_FLOAT v, CMP_IN CGU_UINT8 bits, CMP_IN CGU_UINT8 use_par, CMP_IN CGU_UINT8 odd) +{ + CGU_UINT8 i1 = 0; + CGU_UINT8 i2 = 1 << (bits - use_par); + odd = use_par ? odd : 0; + while (i2 - i1 > 1) + { + CGU_UINT8 j = (CGU_UINT8)((i1 + i2) * 0.5f); + CGV_FLOAT ep_d = (CGV_FLOAT)cmp_expandbits((j << use_par) + odd, bits); + if (v >= ep_d) + i1 = j; + else + i2 = j; + } + + return (i1 << use_par) + odd; +} + +CMP_STATIC CGV_FLOAT cmp_absf(CMP_IN CGV_FLOAT a) +{ + return a > 0.0F ? a : -a; +} + +CMP_STATIC INLINE CGU_UINT32 cmp_pow2Packed(CMP_IN CGU_INT x) +{ + return 1 << x; +} + +CMP_STATIC INLINE CGU_UINT8 cmp_clampIndex(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 a, CMP_IN CGU_UINT8 b) +{ + if (v < a) + return a; + else if (v > b) + return b; + return v; +} + +CMP_STATIC INLINE CGU_UINT8 shift_right_uint82(CMP_IN CGU_UINT8 v, CMP_IN CGU_UINT8 bits) +{ + return v >> bits; // (perf warning expected) +} + +#endif + +CMP_STATIC CGU_INT cmp_QuantizeToBitSize(CMP_IN CGU_INT value, CMP_IN CGU_INT prec, CMP_IN CGU_BOOL signedfloat16) +{ + if (prec <= 1) + return 0; + CGU_BOOL negvalue = false; + + // move data to use extra bits for processing + CGU_INT ivalue = value; + + if (signedfloat16) + { + if (value < 0) + { + negvalue = true; + value = -value; + } + prec--; + } + else + { + // clamp -ve + if (value < 0) + value = 0; + } + + CGU_INT iQuantized; + CGU_INT bias = (prec > 10 && prec != 16) ? ((1 << (prec - 11)) - 1) : 0; + bias = (prec == 16) ? 15 : bias; + + iQuantized = ((ivalue << prec) + bias) / (0x7bff + 1); // 16 bit Float Max 0x7bff + + return (negvalue ? -iQuantized : iQuantized); +} + +//======================================================= +// CPU GPU Macro API +//======================================================= + +#ifdef ASPM_GPU +#define cmp_min(a, b) min(a, b) +#define cmp_max(a, b) max(a, b) +#else +#ifndef cmp_min +#define cmp_min(a, b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef cmp_max +#define cmp_max(a, b) ((a) > (b) ? (a) : (b)) +#endif +#endif + +//======================================================= +// CPU Template API +//======================================================= + +#ifndef ASPM_GPU +#ifndef TEMPLATE_API_INTERFACED +#define TEMPLATE_API_INTERFACED + +template +T clamp(T& v, const T& lo, const T& hi) +{ + if (v < lo) + return lo; + else if (v > hi) + return hi; + return v; +} + +template +Vec4T clamp(Vec4T& v, const T& lo, const T& hi) +{ + Vec4T res = v; + if (v.x < lo) + res.x = lo; + else if (v.x > hi) + res.x = hi; + if (v.y < lo) + res.y = lo; + else if (v.y > hi) + res.y = hi; + if (v.z < lo) + res.z = lo; + else if (v.w > hi) + res.w = hi; + if (v.w < lo) + res.w = lo; + else if (v.z > hi) + res.z = hi; + return res; +} + + +template +T dot(T& v1, T2& v2) +{ + return (v1 * v2); +} + +template +T dot(Vec4T& v1, Vec4T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w); +} + +template +T dot(Vec4T& v1, Vec4T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w); +} +template +T dot(Vec3T& v1, Vec3T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); +} + +template +T dot(Vec3T& v1, Vec3T& v2) +{ + return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z); +} + +#endif // API_INTERFACED +#endif // ASPM_GPU + +#endif // diff --git a/WickedEngine/shaders/compressonator/bcn_common_kernel.h b/WickedEngine/shaders/compressonator/bcn_common_kernel.h new file mode 100644 index 000000000..e6ca1dea9 --- /dev/null +++ b/WickedEngine/shaders/compressonator/bcn_common_kernel.h @@ -0,0 +1,2956 @@ +//============================================================================= +// Copyright (c) 2018-2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//===================================================================== +//===================================================================== +// Block-compression (BC) functionality ref +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +//===================================================================== +//************************************************************************************ +// ** NOTE ** +// Content and data types may change, use CMP_Core.h for interface to your application +//************************************************************************************ + +#ifndef _BCN_COMMON_KERNEL_H +#define _BCN_COMMON_KERNEL_H + +#pragma warning(disable : 4505) // disable warnings on unreferenced local function has been removed + +#include "common_def.h" +#include "bcn_common_api.h" + +//----------------------------------------------------------------------- +// When build is for CPU, we have some missing API calls common to GPU +// Use CPU CMP_Core replacements +//----------------------------------------------------------------------- +// used in BC1 HiQuaity +#if defined(ASPM_GPU) || defined(ASPM_HLSL) || defined(ASPM_OPENCL) +#define ALIGN_16 +#else +#include INC_cmp_math_func +#if defined(WIN32) || defined(_WIN64) +#define ALIGN_16 __declspec(align(16)) +#else // !WIN32 && !_WIN64 +#define ALIGN_16 +#endif // !WIN32 && !_WIN64 +#endif + + + +#define DXTC_OFFSET_ALPHA 0 +#define DXTC_OFFSET_RGB 2 + +#define BC1CompBlockSize 8 + +#define RC 2 +#define GC 1 +#define BC 0 +#define AC 3 + +/* +Channel Bits +*/ +#define RGBA8888_CHANNEL_A 3 +#define RGBA8888_CHANNEL_R 2 +#define RGBA8888_CHANNEL_G 1 +#define RGBA8888_CHANNEL_B 0 +#define RGBA8888_OFFSET_A (RGBA8888_CHANNEL_A * 8) +#define RGBA8888_OFFSET_R (RGBA8888_CHANNEL_R * 8) +#define RGBA8888_OFFSET_G (RGBA8888_CHANNEL_G * 8) +#define RGBA8888_OFFSET_B (RGBA8888_CHANNEL_B * 8) + +#ifndef MAX_ERROR +#define MAX_ERROR 128000.f +#endif + +#define MAX_BLOCK 64 +#define MAX_POINTS 16 +#define BLOCK_SIZE MAX_BLOCK +#define NUM_CHANNELS 4 +#define NUM_ENDPOINTS 2 +#define BLOCK_SIZE_4X4 16 +#define CMP_ALPHA_RAMP 8 // Number of Ramp Points used for Alpha Channels in BC5 + +#define ConstructColour(r, g, b) (((r) << 11) | ((g) << 5) | (b)) // same as BC1ConstructColour in common_api + +#define BYTEPP 4 +#define CMP_QUALITY0 0.25f +#define CMP_QUALITY1 0.50f +#define CMP_QUALITY2 0.75f +#define POS(x, y) (pos_on_axis[(x) + (y)*4]) + +// Find the first approximation of the line +// Assume there is a linear relation +// Z = a * X_In +// Z = b * Y_In +// Find a,b to minimize MSE between Z and Z_In +#define EPS (2.f / 255.f) * (2.f / 255.f) +#define EPS2 3.f * (2.f / 255.f) * (2.f / 255.f) + +// Grid precision +#define PIX_GRID 8 + +#define BYTE_MASK 0x00ff + +#define SCH_STPS 3 // number of search steps to make at each end of interval +static CMP_CONSTANT CGU_FLOAT sMvF[] = {0.f, -1.f, 1.f, -2.f, 2.f, -3.f, 3.f, -4.f, 4.f, -5.f, 5.f, -6.f, 6.f, -7.f, 7.f, -8.f, 8.f}; + +#ifndef GBL_SCH_STEP +#define GBL_SCH_STEP_MXS 0.018f +#define GBL_SCH_EXT_MXS 0.1f +#define LCL_SCH_STEP_MXS 0.6f +#define GBL_SCH_STEP_MXQ 0.0175f +#define GBL_SCH_EXT_MXQ 0.154f +#define LCL_SCH_STEP_MXQ 0.45f + +#define GBL_SCH_STEP GBL_SCH_STEP_MXS +#define GBL_SCH_EXT GBL_SCH_EXT_MXS +#define LCL_SCH_STEP LCL_SCH_STEP_MXS +#endif + +#ifndef ASPM_GPU + +typedef union +{ + struct colorblock64U + { + CGU_UINT8 col0; + CGU_UINT8 col1; + CGU_UINT8 indices[6]; + }; + CGU_INT8 cmp_data8[8]; + CGU_INT32 cmp_data32[2]; + CGU_UINT64 cmp_data64; +} CMP_BLOCK64_UNORM; + +typedef union +{ + struct colorblock64S + { + CGU_UINT8 col0; + CGU_UINT8 col1; + CGU_UINT8 indices[6]; + }; + CGU_INT8 cmp_data8[8]; + CGU_INT32 cmp_data32[2]; + CGU_UINT64 cmp_data64[2]; +} CMP_BLOCK64_SNORM; + +typedef union +{ + CGU_INT8 cmp_data8[16]; + CGU_INT32 cmp_data32[4]; + CGU_UINT64 cmp_data64[2]; +} CMP_BLOCK128_UNORM; + +#endif + +typedef struct +{ + CGU_UINT32 data; + CGU_UINT32 index; +} CMP_di; + +typedef struct +{ + CGU_FLOAT data; + CGU_UINT32 index; +} CMP_df; + +typedef struct +{ + // user setable + CGU_FLOAT m_fquality; + CGU_FLOAT m_fChannelWeights[3]; + CGU_BOOL m_bUseChannelWeighting; + CGU_BOOL m_bUseAdaptiveWeighting; + CGU_BOOL m_bUseFloat; + CGU_BOOL m_b3DRefinement; + CGU_BOOL m_bUseAlpha; + CGU_BOOL m_bIsSRGB; // Use Linear to SRGB color conversion used in BC1, default is false + CGU_BOOL m_bIsSNORM; + CGU_BOOL m_sintsrc; // source data pointer is signed data + CGU_UINT32 m_nRefinementSteps; + CGU_UINT32 m_nAlphaThreshold; + CGU_BOOL m_mapDecodeRGBA; + CGU_UINT32 m_src_width; + CGU_UINT32 m_src_height; +} CMP_BC15Options; + +typedef struct +{ + CGU_Vec3i end_point0; + CGU_Vec3i end_point1; + CGU_UINT8 indices[16]; + CGU_BOOL m_3color; +} CMP_BC1_Encode_Results; + +// used in BC1 LowQuality code +typedef struct +{ + CGU_Vec3f Color0; + CGU_Vec3f Color1; +} CMP_EndPoints; + +// Common data info used between encoders +// Defines properties of current 4x4 pixel block +typedef struct +{ + CGU_UINT32 grayscale_flag; + CGU_UINT32 any_black_pixels; + CGU_BOOL all_colors_equal; + CGU_Vec3i min; + CGU_Vec3i max; + CGU_Vec3i total; + CGU_Vec3i avg; +} CMP_EncodeData; + +typedef struct CMP_BC1_Block_t +{ + // Union struct not supported on GPU + // 8 Bytes Total + #ifndef ASPM_GPU + union { + struct { // 2 x 32bit + CGU_UINT32 colors; + CGU_UINT32 indices; + }; + struct { // 8 x 8bit + CGU_UINT8 m_low_color[2]; + CGU_UINT8 m_high_color[2]; + CGU_UINT8 m_selectors[4]; + }; + }; + + inline void set_low_color(CGU_UINT16 c) + { + m_low_color[0] = static_cast(c & 0xFF); + m_low_color[1] = static_cast((c >> 8) & 0xFF); + } + inline void set_high_color(CGU_UINT16 c) + { + m_high_color[0] = static_cast(c & 0xFF); + m_high_color[1] = static_cast((c >> 8) & 0xFF); + } + #else + CGU_UINT32 colors; + CGU_UINT32 indices; + #endif +} CMP_BC1_Block; + +// Helper functions to cut precision of floats +// Prec is a power of 10 value from 1,10,100,...,10000... INT MAX power 10 +static CGU_BOOL cmp_compareprecision(CGU_FLOAT f1, CGU_FLOAT f2, CGU_INT Prec) +{ + CGU_INT scale1 = (CGU_INT)(f1 * Prec); + CGU_INT scale2 = (CGU_INT)(f2 * Prec); + return (scale1 == scale2); +} + +// Helper function to compare floats to a set precision +static CGU_FLOAT cmp_getfloatprecision(CGU_FLOAT f1, CGU_INT Prec) +{ + CGU_INT scale1 = (CGU_INT)(f1 * Prec); + return ((CGU_FLOAT)(scale1) / Prec); +} + +static CGU_FLOAT cmp_getIndicesRGB(CMP_INOUT CGU_UINT32 CMP_PTRINOUT cmpindex, + const CGU_Vec3f block[16], + CGU_Vec3f minColor, + CGU_Vec3f maxColor, + CGU_BOOL getErr) +{ + CGU_UINT32 PackedIndices = 0; + CGU_FLOAT err = 0.0f; + CGU_Vec3f cn[4]; + CGU_FLOAT minDistance; + + if (getErr) + { + // remap to BC1 spec for decoding offsets, + // where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1 + cn[0] = maxColor; + cn[1] = minColor; + cn[2] = cn[0] * 2.0f / 3.0f + cn[1] * 1.0f / 3.0f; + cn[3] = cn[0] * 1.0f / 3.0f + cn[1] * 2.0f / 3.0f; + } + + CGU_FLOAT Scale = 3.f / dot(minColor - maxColor, minColor - maxColor); + CGU_Vec3f ScaledRange = (minColor - maxColor) * Scale; + CGU_FLOAT Bias = (dot(maxColor, maxColor) - dot(maxColor, minColor)) * Scale; + CGU_INT indexMap[4] = {0, 2, 3, 1}; // mapping based on BC1 Spec for color0 > color1 + CGU_UINT32 index; + CGU_FLOAT diff; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + // Get offset from base scale + diff = dot(block[i], ScaledRange) + Bias; + index = ((CGU_UINT32)round(diff)) & 0x3; + + // remap linear offset to spec offset + index = indexMap[index]; + + // use err calc for use in higher quality code + if (getErr) + { + minDistance = dot(block[i] - cn[index], block[i] - cn[index]); + err += minDistance; + } + + // Map the 2 bit index into compress 32 bit block + if (index) + PackedIndices |= (index << (2 * i)); + } + + if (getErr) + err = err * 0.0208333f; + + CMP_PTRINOUT cmpindex = PackedIndices; + return err; +} + +//---------------------------------------- BCn Common Utility Code ------------------------------------------------------- + +#ifndef ASPM_GPU +static void SetDefaultBC15Options(CMP_BC15Options* BC15Options) +{ + if (BC15Options) + { + BC15Options->m_fquality = 1.0f; + BC15Options->m_bUseChannelWeighting = false; + BC15Options->m_bUseAdaptiveWeighting = false; + BC15Options->m_fChannelWeights[0] = 0.3086f; + BC15Options->m_fChannelWeights[1] = 0.6094f; + BC15Options->m_fChannelWeights[2] = 0.0820f; + BC15Options->m_nAlphaThreshold = 128; + BC15Options->m_bUseFloat = false; + BC15Options->m_b3DRefinement = false; + BC15Options->m_bUseAlpha = false; + BC15Options->m_bIsSNORM = false; + BC15Options->m_bIsSRGB = false; + BC15Options->m_nRefinementSteps = 0; + BC15Options->m_src_width = 4; + BC15Options->m_src_height = 4; +#ifdef CMP_SET_BC13_DECODER_RGBA + BC15Options->m_mapDecodeRGBA = true; +#else + BC15Options->m_mapDecodeRGBA = false; +#endif + } +} +#endif + +static CMP_BC15Options CalculateColourWeightings(CGU_Vec4f rgbaBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ + CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; + + if (!BC15options.m_bUseChannelWeighting) + { + BC15options.m_fChannelWeights[0] = 1.0F; + BC15options.m_fChannelWeights[1] = 1.0F; + BC15options.m_fChannelWeights[2] = 1.0F; + return BC15options; + } + + if (BC15options.m_bUseAdaptiveWeighting) + { + float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; + + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { + medianR += rgbaBlock[k].x; + medianG += rgbaBlock[k].y; + medianB += rgbaBlock[k].z; + } + + medianR /= BLOCK_SIZE_4X4; + medianG /= BLOCK_SIZE_4X4; + medianB /= BLOCK_SIZE_4X4; + + // Now skew the colour weightings based on the gravity center of the block + float largest = max(max(medianR, medianG), medianB); + + if (largest > 0) + { + medianR /= largest; + medianG /= largest; + medianB /= largest; + } + else + medianR = medianG = medianB = 1.0f; + + // Scale weightings back up to 1.0f + CGU_FLOAT fWeightScale = 1.0f / (fBaseChannelWeights[0] + fBaseChannelWeights[1] + fBaseChannelWeights[2]); + + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0] * fWeightScale; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1] * fWeightScale; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2] * fWeightScale; + + BC15options.m_fChannelWeights[0] = ((BC15options.m_fChannelWeights[0] * 3 * medianR) + BC15options.m_fChannelWeights[0]) * 0.25f; + BC15options.m_fChannelWeights[1] = ((BC15options.m_fChannelWeights[1] * 3 * medianG) + BC15options.m_fChannelWeights[1]) * 0.25f; + BC15options.m_fChannelWeights[2] = ((BC15options.m_fChannelWeights[2] * 3 * medianB) + BC15options.m_fChannelWeights[2]) * 0.25f; + + fWeightScale = 1.0f / (BC15options.m_fChannelWeights[0] + BC15options.m_fChannelWeights[1] + BC15options.m_fChannelWeights[2]); + + BC15options.m_fChannelWeights[0] *= fWeightScale; + BC15options.m_fChannelWeights[1] *= fWeightScale; + BC15options.m_fChannelWeights[2] *= fWeightScale; + } + else + { + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; + } + + return BC15options; +} + +static CMP_BC15Options CalculateColourWeightings3f(CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], CMP_BC15Options BC15options) +{ + CGU_FLOAT fBaseChannelWeights[3] = {0.3086f, 0.6094f, 0.0820f}; + + if (!BC15options.m_bUseChannelWeighting) + { + BC15options.m_fChannelWeights[0] = 1.0F; + BC15options.m_fChannelWeights[1] = 1.0F; + BC15options.m_fChannelWeights[2] = 1.0F; + return BC15options; + } + + if (BC15options.m_bUseAdaptiveWeighting) + { + float medianR = 0.0f, medianG = 0.0f, medianB = 0.0f; + + for (CGU_UINT32 k = 0; k < BLOCK_SIZE_4X4; k++) + { + medianR += rgbBlock[k].x; + medianG += rgbBlock[k].y; + medianB += rgbBlock[k].z; + } + + medianR /= BLOCK_SIZE_4X4; + medianG /= BLOCK_SIZE_4X4; + medianB /= BLOCK_SIZE_4X4; + + // Now skew the colour weightings based on the gravity center of the block + float largest = max(max(medianR, medianG), medianB); + + if (largest > 0) + { + medianR /= largest; + medianG /= largest; + medianB /= largest; + } + else + medianR = medianG = medianB = 1.0f; + + // Scale weightings back up to 1.0f + CGU_FLOAT fWeightScale = 1.0f / (fBaseChannelWeights[0] + fBaseChannelWeights[1] + fBaseChannelWeights[2]); + + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0] * fWeightScale; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1] * fWeightScale; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2] * fWeightScale; + + BC15options.m_fChannelWeights[0] = ((BC15options.m_fChannelWeights[0] * 3 * medianR) + BC15options.m_fChannelWeights[0]) * 0.25f; + BC15options.m_fChannelWeights[1] = ((BC15options.m_fChannelWeights[1] * 3 * medianG) + BC15options.m_fChannelWeights[1]) * 0.25f; + BC15options.m_fChannelWeights[2] = ((BC15options.m_fChannelWeights[2] * 3 * medianB) + BC15options.m_fChannelWeights[2]) * 0.25f; + + fWeightScale = 1.0f / (BC15options.m_fChannelWeights[0] + BC15options.m_fChannelWeights[1] + BC15options.m_fChannelWeights[2]); + + BC15options.m_fChannelWeights[0] *= fWeightScale; + BC15options.m_fChannelWeights[1] *= fWeightScale; + BC15options.m_fChannelWeights[2] *= fWeightScale; + } + else + { + BC15options.m_fChannelWeights[0] = fBaseChannelWeights[0]; + BC15options.m_fChannelWeights[1] = fBaseChannelWeights[1]; + BC15options.m_fChannelWeights[2] = fBaseChannelWeights[2]; + } + + return BC15options; +} + +static CGU_FLOAT cmp_getRampErr(CGU_FLOAT Prj[BLOCK_SIZE_4X4], + CGU_FLOAT PrjErr[BLOCK_SIZE_4X4], + CGU_FLOAT PreMRep[BLOCK_SIZE_4X4], + CGU_FLOAT StepErr, + CGU_FLOAT lowPosStep, + CGU_FLOAT highPosStep, + CGU_UINT32 dwUniqueColors) +{ + CGU_FLOAT error = 0; + CGU_FLOAT step = (highPosStep - lowPosStep) / 3; // using (dwNumChannels=4 - 1); + CGU_FLOAT step_h = step * (CGU_FLOAT)0.5; + CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step; + + for (CGU_UINT32 i = 0; i < dwUniqueColors; i++) + { + CGU_FLOAT v; + // Work out which value in the block this select + CGU_FLOAT del; + + if ((del = Prj[i] - lowPosStep) <= 0) + v = lowPosStep; + else if (Prj[i] - highPosStep >= 0) + v = highPosStep; + else + v = floor((del + step_h) * rstep) * step + lowPosStep; + + // And accumulate the error + CGU_FLOAT d = (Prj[i] - v); + d *= d; + CGU_FLOAT err = PreMRep[i] * d + PrjErr[i]; + error += err; + if (StepErr < error) + { + error = StepErr; + break; + } + } + return error; +} + +static CGU_Vec2ui cmp_compressExplicitAlphaBlock(const CGU_FLOAT AlphaBlockUV[16]) +{ + CGU_Vec2ui compBlock = {0, 0}; + CGU_UINT8 i; + for (i = 0; i < 16; i++) + { + CGU_UINT8 v = (CGU_UINT8)(AlphaBlockUV[i] * 255.0F); + v = (v + 7 - (v >> 4)); + v >>= 4; + + if (v < 0) + v = 0; + else if (v > 0xf) + v = 0xf; + + if (i < 8) + compBlock.x |= v << (4 * i); + else + compBlock.y |= v << (4 * (i - 8)); + } + return compBlock; +} + +static CGU_FLOAT cmp_getRampError(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], + CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], + CGU_FLOAT _maxerror, + CGU_FLOAT _min_ex, + CGU_FLOAT _max_ex, + CGU_INT _NmbrClrs) +{ // Max 16 + CGU_INT i; + CGU_FLOAT error = 0; + const CGU_FLOAT step = (_max_ex - _min_ex) / 7; // (CGU_FLOAT)(dwNumPoints - 1); + const CGU_FLOAT step_h = step * 0.5f; + const CGU_FLOAT rstep = 1.0f / step; + + for (i = 0; i < _NmbrClrs; i++) + { + CGU_FLOAT v; + // Work out which value in the block this select + CGU_FLOAT del; + + if ((del = _Blk[i] - _min_ex) <= 0) + v = _min_ex; + else if (_Blk[i] - _max_ex >= 0) + v = _max_ex; + else + v = (floor((del + step_h) * rstep) * step) + _min_ex; + + // And accumulate the error + CGU_FLOAT del2 = (_Blk[i] - v); + error += del2 * del2 * _Rpt[i]; + + // if we've already lost to the previous step bail out + if (_maxerror < error) + { + error = _maxerror; + break; + } + } + return error; +} + +static CGU_FLOAT cmp_linearBlockRefine(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], + CGU_FLOAT _Rpt[BLOCK_SIZE_4X4], + CGU_FLOAT _MaxError, + CMP_INOUT CGU_FLOAT CMP_PTRINOUT _min_ex, + CMP_INOUT CGU_FLOAT CMP_PTRINOUT _max_ex, + CGU_FLOAT _m_step, + CGU_FLOAT _min_bnd, + CGU_FLOAT _max_bnd, + CGU_INT _NmbrClrs) +{ + // Start out assuming our endpoints are the min and max values we've + // determined + + // Attempt a (simple) progressive refinement step to reduce noise in the + // output image by trying to find a better overall match for the endpoints. + + CGU_FLOAT maxerror = _MaxError; + CGU_FLOAT min_ex = CMP_PTRINOUT _min_ex; + CGU_FLOAT max_ex = CMP_PTRINOUT _max_ex; + + CGU_INT mode, bestmode; + + do + { + CGU_FLOAT cr_min0 = min_ex; + CGU_FLOAT cr_max0 = max_ex; + for (bestmode = -1, mode = 0; mode < SCH_STPS * SCH_STPS; mode++) + { + // check each move (see sStep for direction) + CGU_FLOAT cr_min = min_ex + _m_step * sMvF[mode / SCH_STPS]; + CGU_FLOAT cr_max = max_ex + _m_step * sMvF[mode % SCH_STPS]; + + cr_min = max(cr_min, _min_bnd); + cr_max = min(cr_max, _max_bnd); + + CGU_FLOAT error; + error = cmp_getRampError(_Blk, _Rpt, maxerror, cr_min, cr_max, _NmbrClrs); + + if (error < maxerror) + { + maxerror = error; + bestmode = mode; + cr_min0 = cr_min; + cr_max0 = cr_max; + } + } + + if (bestmode != -1) + { + // make move (see sStep for direction) + min_ex = cr_min0; + max_ex = cr_max0; + } + } while (bestmode != -1); + + CMP_PTRINOUT _min_ex = min_ex; + CMP_PTRINOUT _max_ex = max_ex; + + return maxerror; +} + +static CGU_Vec2f cmp_getLinearEndPoints(CGU_FLOAT _Blk[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ + CGU_UINT32 i; + CGU_Vec2f cmpMinMax; + + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + if (fquality < CMP_QUALITY2) + { + cmpMinMax.x = _Blk[0]; + cmpMinMax.y = _Blk[0]; + for (i = 1; i < BLOCK_SIZE_4X4; ++i) + { + cmpMinMax.x = min(cmpMinMax.x, _Blk[i]); + cmpMinMax.y = max(cmpMinMax.y, _Blk[i]); + } + return cmpMinMax; + } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + CGU_FLOAT Ramp[2]; + + // Result defaults for SNORM or UNORM + Ramp[0] = isSigned ? -1.0f : 0.0f; + Ramp[1] = 1.0f; + + ALIGN_16 CGU_FLOAT afUniqueValues[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT afValueRepeats[BLOCK_SIZE_4X4]; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + afUniqueValues[i] = afValueRepeats[i] = 0.f; + + // For each unique value we compute the number of it appearances. + CGU_FLOAT fBlk[BLOCK_SIZE_4X4]; + +// sort the input +#ifndef ASPM_GPU + memcpy(fBlk, _Blk, BLOCK_SIZE_4X4 * sizeof(CGU_FLOAT)); + qsort((void*)fBlk, (size_t)BLOCK_SIZE_4X4, sizeof(CGU_FLOAT), QSortFCmp); +#else + CGU_UINT32 j; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + fBlk[i] = _Blk[i]; + } + + CMP_df what[BLOCK_SIZE]; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + what[i].index = i; + what[i].data = fBlk[i]; + } + + CGU_UINT32 tmp_index; + CGU_FLOAT tmp_data; + + for (i = 1; i < BLOCK_SIZE_4X4; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { + tmp_index = what[j].index; + tmp_data = what[j].data; + what[j].index = what[j - 1].index; + what[j].data = what[j - 1].data; + what[j - 1].index = tmp_index; + what[j - 1].data = tmp_data; + } + } + } + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + fBlk[i] = what[i].data; +#endif + + CGU_FLOAT new_p = -2.0f; + + CGU_UINT32 dwUniqueValues = 0; + afUniqueValues[0] = 0.0f; + CGU_BOOL requiresCalculation = true; + + { + // Ramp not fixed + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + if (new_p != fBlk[i]) + { + afUniqueValues[dwUniqueValues] = new_p = fBlk[i]; + afValueRepeats[dwUniqueValues] = 1.f; + dwUniqueValues++; + } + else if (dwUniqueValues) + afValueRepeats[dwUniqueValues - 1] += 1.f; + } + + // if number of unique colors is less or eq 2, we've done + if (dwUniqueValues <= 2) + { + Ramp[0] = floor(afUniqueValues[0] * 255.0f + 0.5f); + if (dwUniqueValues == 1) + Ramp[1] = Ramp[0] + 1.f; + else + Ramp[1] = floor(afUniqueValues[1] * 255.0f + 0.5f); + requiresCalculation = false; + } + } // Ramp not fixed + + if (requiresCalculation) + { + CGU_FLOAT min_ex = afUniqueValues[0]; + CGU_FLOAT max_ex = afUniqueValues[dwUniqueValues - 1]; + CGU_FLOAT min_bnd = 0, max_bnd = 1.; + CGU_FLOAT min_r = min_ex, max_r = max_ex; + CGU_FLOAT gbl_l = 0, gbl_r = 0; + CGU_FLOAT cntr = (min_r + max_r) / 2; + + CGU_FLOAT gbl_err = MAX_ERROR; + // Trying to avoid unnecessary calculations. Heuristics: after some analisis + // it appears that in integer case, if the input interval not more then 48 + // we won't get much better + bool wantsSearch = !((max_ex - min_ex) <= (48.f / 256.0f)); + + if (wantsSearch) + { + // Search. + // 1. take the vicinities of both low and high bound of the input + // interval. + // 2. setup some search step + // 3. find the new low and high bound which provides an (sub) optimal + // (infinite precision) clusterization. + CGU_FLOAT gbl_llb = (min_bnd > min_r - GBL_SCH_EXT) ? min_bnd : min_r - GBL_SCH_EXT; + CGU_FLOAT gbl_rrb = (max_bnd < max_r + GBL_SCH_EXT) ? max_bnd : max_r + GBL_SCH_EXT; + CGU_FLOAT gbl_lrb = (cntr < min_r + GBL_SCH_EXT) ? cntr : min_r + GBL_SCH_EXT; + CGU_FLOAT gbl_rlb = (cntr > max_r - GBL_SCH_EXT) ? cntr : max_r - GBL_SCH_EXT; + + for (CGU_FLOAT step_l = gbl_llb; step_l < gbl_lrb; step_l += GBL_SCH_STEP) + { + for (CGU_FLOAT step_r = gbl_rrb; gbl_rlb <= step_r; step_r -= GBL_SCH_STEP) + { + CGU_FLOAT sch_err; + // an sse version is avaiable + sch_err = cmp_getRampError(afUniqueValues, afValueRepeats, gbl_err, step_l, step_r, dwUniqueValues); + if (sch_err < gbl_err) + { + gbl_err = sch_err; + gbl_l = step_l; + gbl_r = step_r; + } + } + } + + min_r = gbl_l; + max_r = gbl_r; + } // want search + + // This is a refinement call. The function tries to make several small + // stretches or squashes to minimize quantization error. + CGU_FLOAT m_step = LCL_SCH_STEP / 256.0f; + cmp_linearBlockRefine(afUniqueValues, afValueRepeats, gbl_err, CMP_REFINOUT min_r, CMP_REFINOUT max_r, m_step, min_bnd, max_bnd, dwUniqueValues); + + min_ex = min_r; + max_ex = max_r; + max_ex *= 255.0f; + min_ex *= 255.0f; + + Ramp[0] = floor(min_ex + 0.5f); + Ramp[1] = floor(max_ex + 0.5f); + } + + // Ensure that the two endpoints are not the same + // This is legal but serves no need & can break some optimizations in the compressor + if (Ramp[0] == Ramp[1]) + { + if (Ramp[1] < 255.f) + Ramp[1] = Ramp[1] + 1.0f; + else if (Ramp[1] > 0.0f) + Ramp[1] = Ramp[1] - 1.0f; + } + + cmpMinMax.x = Ramp[0]; + cmpMinMax.y = Ramp[1]; + + return cmpMinMax; +} + +static CGU_Vec2ui cmp_getBlockPackedIndices(CGU_Vec2f RampMinMax, CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality) +{ + CGU_UINT32 i; + CGU_UINT32 j; + CGU_Vec2ui cmpBlock = {0, 0}; + CGU_UINT32 MinRampU; + CGU_UINT32 MaxRampU; + CGU_INT32 pcIndices[BLOCK_SIZE_4X4]; + + if (fquality < CMP_QUALITY2) + { + CGU_FLOAT Range; + CGU_FLOAT RampSteps; // segments into 0..7 sections + CGU_FLOAT Bias; + + if (RampMinMax.x != RampMinMax.y) + Range = RampMinMax.x - RampMinMax.y; + else + Range = 1.0f; + + RampSteps = 7.f / Range; // segments into 0..7 sections + Bias = -RampSteps * RampMinMax.y; + + for (i = 0; i < 16; ++i) + { + pcIndices[i] = (CGU_UINT32)round(alphaBlock[i] * RampSteps + Bias); + if (i < 5) + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); + } + else if (i > 5) + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7 ? 1 : 0)); + } + else + { + pcIndices[i] += (pcIndices[i] > 0) - (7 * (pcIndices[i] == 7)); + } + } + + MinRampU = (CGU_UINT32)round(RampMinMax.x * 255.0f); + MaxRampU = (CGU_UINT32)round(RampMinMax.y * 255.0f); + + cmpBlock.x = (MinRampU << 8) | MaxRampU; + cmpBlock.y = 0; + for (i = 0; i < 5; ++i) + { + cmpBlock.x |= (pcIndices[i] << (16 + (i * 3))); + } + { + cmpBlock.x |= (pcIndices[5] << 31); + cmpBlock.y |= (pcIndices[5] >> 1); + } + for (i = 6; i < BLOCK_SIZE_4X4; ++i) + { + cmpBlock.y |= (pcIndices[i] << (i * 3 - 16)); + } + } + else + { + CGU_UINT32 epoint; + CGU_FLOAT alpha[BLOCK_SIZE_4X4]; + CGU_FLOAT OverIntFctr; + CGU_FLOAT shortest; + CGU_FLOAT adist; + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + pcIndices[i] = 0; + + for (i = 0; i < MAX_POINTS; i++) + alpha[i] = 0; + + // GetRmp1 + { + if (RampMinMax.x <= RampMinMax.y) + { + CGU_FLOAT t = RampMinMax.x; + RampMinMax.x = RampMinMax.y; + RampMinMax.y = t; + } + + //============================= + // final clusterization applied + //============================= + CGU_FLOAT ramp[NUM_ENDPOINTS]; + + ramp[0] = RampMinMax.x; + ramp[1] = RampMinMax.y; + + { + // BldRmp1 + alpha[0] = ramp[0]; + alpha[1] = ramp[1]; + for (epoint = 1; epoint < CMP_ALPHA_RAMP - 1; epoint++) + alpha[epoint + 1] = (alpha[0] * (CMP_ALPHA_RAMP - 1 - epoint) + alpha[1] * epoint) / (CGU_FLOAT)(CMP_ALPHA_RAMP - 1); + for (epoint = CMP_ALPHA_RAMP; epoint < BLOCK_SIZE_4X4; epoint++) + alpha[epoint] = 100000.f; + } // BldRmp1 + + // FixedRamp + for (i = 0; i < CMP_ALPHA_RAMP; i++) + { + alpha[i] = floor(alpha[i] + 0.5f); + } + } // GetRmp1 + + OverIntFctr = 1.f / 255.0f; + for (i = 0; i < CMP_ALPHA_RAMP; i++) + alpha[i] *= OverIntFctr; + + // For each colour in the original block, calculate its weighted + // distance from each point in the original and assign it + // to the closest cluster + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + shortest = 10000000.f; + for (j = 0; j < CMP_ALPHA_RAMP; j++) + { + adist = (alphaBlock[i] - alpha[j]); + adist *= adist; + if (adist < shortest) + { + shortest = adist; + pcIndices[i] = j; + } + } + } + + //================================================== + // EncodeAlphaBlock + //================================================== + MinRampU = (CGU_UINT32)RampMinMax.x; + MaxRampU = (CGU_UINT32)RampMinMax.y; + + cmpBlock.x = (MaxRampU << 8) | MinRampU; + cmpBlock.y = 0; + for (i = 0; i < 5; i++) + { + cmpBlock.x |= (pcIndices[i]) << (16 + (i * 3)); + } + { + cmpBlock.x |= (pcIndices[5] & 0x1) << 31; + cmpBlock.y |= (pcIndices[5] & 0x6) >> 1; + } + for (i = 6; i < BLOCK_SIZE_4X4; i++) + { + cmpBlock.y |= (pcIndices[i]) << (i * 3 - 16); + } + } + return cmpBlock; +} + +//======================= SNORM CODE ================================== + +static CGU_INT8 cmp_snormFloatToSInt(CGU_FLOAT fsnorm) +{ + if (isnan(fsnorm)) + fsnorm = 0; + else if (fsnorm > 1) + fsnorm = 1; // Clamp to 1 + else if (fsnorm < -1) + fsnorm = -1; // Clamp to -1 + + fsnorm = fsnorm * 127U; + + // shift round up or down + if (fsnorm >= 0) + fsnorm += .5f; + else + fsnorm -= .5f; + +#ifdef ASPM_GPU + CGU_INT8 res = (CGU_INT8)fsnorm; +#else + CGU_INT8 res = static_cast(fsnorm); +#endif + return (res); +} + +static CGU_Vec2f cmp_optimizeEndPoints(CGU_FLOAT pPoints[BLOCK_SIZE_4X4], CGU_INT8 cSteps, CGU_BOOL isSigned) +{ + CGU_Vec2f fendpoints; + CGU_FLOAT MAX_VALUE = 1.0f; + CGU_FLOAT MIN_VALUE = isSigned ? -1.0f : 0.0f; + + // Find Min and Max points, as starting point + CGU_FLOAT fX = MAX_VALUE; + CGU_FLOAT fY = MIN_VALUE; + + if (8 == cSteps) + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY) + fY = pPoints[iPoint]; + } + } + else + { + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + if (pPoints[iPoint] < fX && pPoints[iPoint] > MIN_VALUE) + fX = pPoints[iPoint]; + + if (pPoints[iPoint] > fY && pPoints[iPoint] < MAX_VALUE) + fY = pPoints[iPoint]; + } + + if (fX == fY) + { + fY = MAX_VALUE; + } + } + + //=================== + // Use Newton Method + //=================== +#ifdef ASPM_GPU + CGU_FLOAT cStepsDiv = (CGU_FLOAT)(cSteps - 1); +#else + CGU_FLOAT cStepsDiv = static_cast(cSteps - 1); +#endif + CGU_FLOAT pSteps[8]; + CGU_FLOAT fc; + CGU_FLOAT fd; + + for (CGU_INT8 iIteration = 0; iIteration < 8; iIteration++) + { + // reach minimum threashold break + if ((fY - fX) < (1.0f / 256.0f)) + break; + + CGU_FLOAT fScale = cStepsDiv / (fY - fX); + + // Calculate new steps + for (CGU_INT8 iStep = 0; iStep < cSteps; iStep++) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + pSteps[iStep] = fc * fX + fd * fY; + } + + if (6 == cSteps) + { + pSteps[6] = MIN_VALUE; + pSteps[7] = MAX_VALUE; + } + + // Evaluate function, and derivatives + CGU_FLOAT dX = 0.0f; + CGU_FLOAT dY = 0.0f; + CGU_FLOAT d2X = 0.0f; + CGU_FLOAT d2Y = 0.0f; + + for (CGU_INT8 iPoint = 0; iPoint < BLOCK_SIZE_4X4; iPoint++) + { + CGU_FLOAT fDot = (pPoints[iPoint] - fX) * fScale; + + CGU_INT8 iStep; + if (fDot <= 0.0f) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] <= (fX + MIN_VALUE) * 0.5f)) ? 6u : 0u; + } + else if (fDot >= cStepsDiv) + { + iStep = ((6 == cSteps) && (pPoints[iPoint] >= (fY + MAX_VALUE) * 0.5f)) ? 7u : (cSteps - 1); + } + else + { + iStep = (CGU_UINT32)(fDot + 0.5f); + } + + // steps to improve quality + if (iStep < cSteps) + { + fc = (cStepsDiv - (CGU_FLOAT)iStep) / cStepsDiv; + fd = (CGU_FLOAT)iStep / cStepsDiv; + CGU_FLOAT fDiff = pSteps[iStep] - pPoints[iPoint]; + dX += fc * fDiff; + d2X += fc * fc; + dY += fd * fDiff; + d2Y += fd * fd; + } + } + + // Move endpoints + if (d2X > 0.0f) + fX -= dX / d2X; + + if (d2Y > 0.0f) + fY -= dY / d2Y; + + if (fX > fY) + { + float f = fX; + fX = fY; + fY = f; + } + + if ((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) + break; + } + + fendpoints.x = (fX < MIN_VALUE) ? MIN_VALUE : (fX > MAX_VALUE) ? MAX_VALUE : fX; + fendpoints.y = (fY < MIN_VALUE) ? MIN_VALUE : (fY > MAX_VALUE) ? MAX_VALUE : fY; + + return fendpoints; +} + +static CGU_Vec2i cmp_findEndpointsAlphaBlockSnorm(CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4]) +{ + //================================================================ + // Bounding Box + // lowest quality calculation to get min and max value to use + //================================================================ + CGU_Vec2f cmpMinMax; + cmpMinMax.x = alphaBlockSnorm[0]; + cmpMinMax.y = alphaBlockSnorm[0]; + + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + if (alphaBlockSnorm[i] < cmpMinMax.x) + { + cmpMinMax.x = alphaBlockSnorm[i]; + } + else if (alphaBlockSnorm[i] > cmpMinMax.y) + { + cmpMinMax.y = alphaBlockSnorm[i]; + } + } + + CGU_Vec2i endpoints; + CGU_Vec2f fendpoints; + + // Are we done for lowest quality setting! + // CGU_FLOAT fquality = 1.0f; + // + // if (fquality < CMP_QUALITY2) { + // endpoints.x = (CGU_INT8)(cmpMinMax.x); + // endpoints.y = (CGU_INT8)(cmpMinMax.y); + // return endpoints; + // } + + //================================================================ + // Do more calculations to get the best min and max values to use + //================================================================ + if ((-1.0f == cmpMinMax.x || 1.0f == cmpMinMax.y)) + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 6, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.x); + endpoints.y = cmp_snormFloatToSInt(fendpoints.y); + } + else + { + fendpoints = cmp_optimizeEndPoints(alphaBlockSnorm, 8, true); + endpoints.x = cmp_snormFloatToSInt(fendpoints.y); + endpoints.y = cmp_snormFloatToSInt(fendpoints.x); + } + + return endpoints; +} + +#ifndef ASPM_HLSL +static CGU_UINT64 cmp_getBlockPackedIndicesSNorm(CGU_Vec2f alphaMinMax, CGU_FLOAT alphaBlockSnorm[BLOCK_SIZE_4X4], CGU_UINT64 data) +{ + CGU_FLOAT alpha[8]; + alpha[0] = alphaMinMax.x; + alpha[1] = alphaMinMax.y; + + if (alphaMinMax.x > alphaMinMax.y) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 6.0f + alpha[1]) / 7.0f; + alpha[3] = (alpha[0] * 5.0f + alpha[1] * 2.0f) / 7.0f; + alpha[4] = (alpha[0] * 4.0f + alpha[1] * 3.0f) / 7.0f; + alpha[5] = (alpha[0] * 3.0f + alpha[1] * 4.0f) / 7.0f; + alpha[6] = (alpha[0] * 2.0f + alpha[1] * 5.0f) / 7.0f; + alpha[7] = (alpha[0] + alpha[1] * 6.0f) / 7.0f; + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. + alpha[2] = (alpha[0] * 4.0f + alpha[1]) / 5.0f; + alpha[3] = (alpha[0] * 3.0f + alpha[1] * 2.0f) / 5.0f; + alpha[4] = (alpha[0] * 2.0f + alpha[1] * 3.0f) / 5.0f; + alpha[5] = (alpha[0] + alpha[1] * 4.0f) / 5.0f; + alpha[6] = -1.0f; + alpha[7] = 1.0f; + } + + // Index all colors using best alpha value + for (CGU_UINT8 i = 0; i < BLOCK_SIZE_4X4; ++i) + { + CGU_UINT8 uBestIndex = 0; + CGU_FLOAT fBestDelta = CMP_FLOAT_MAX; + for (CGU_INT32 uIndex = 0; uIndex < 8; uIndex++) + { + CGU_FLOAT fCurrentDelta = fabs(alpha[uIndex] - alphaBlockSnorm[i]); + if (fCurrentDelta < fBestDelta) + { + uBestIndex = uIndex; + fBestDelta = fCurrentDelta; + } + } + + data &= ~((CGU_UINT64)(0x07) << (3 * i + 16)); + data |= ((CGU_UINT64)(uBestIndex) << (3 * i + 16)); + } + + return data; +} +#endif +static void cmp_getCompressedAlphaRampS(CGU_INT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ + alpha[0] = (CGU_INT8)(compressedBlock[0] & 0xff); + alpha[1] = (CGU_INT8)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#else + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#endif + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#else + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#endif + alpha[6] = -128; // Bit code 110 + alpha[7] = 127; // Bit code 111 + } +} + +static void cmp_decompressAlphaBlockS(CGU_INT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ + CGU_UINT32 i; + CGU_INT8 alpha[8]; + cmp_getCompressedAlphaRampS(alpha, compressedBlock); + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + CGU_UINT32 index; + if (i < 5) + index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); + else if (i > 5) + index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); + else + { + index = (compressedBlock[0] & 0x80000000) >> 31; + index |= (compressedBlock[1] & 0x3) << 1; + } + + alphaBlock[i] = alpha[index]; + } +} + +//============================================================================= + +// Processes Alpha Channel either as Unsigned Norm (0..1) or (Signed Norm -1..1) +static CGU_Vec2ui cmp_compressAlphaBlock(CMP_IN CGU_FLOAT alphaBlock[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CMP_IN CGU_BOOL isSigned) +{ + CGU_Vec2ui CmpBlock; + + if (isSigned) + { +#ifndef ASPM_HLSL + union + { + CGU_INT32 compressedBlock[2]; + struct + { + CGU_INT8 red_0; + CGU_INT8 red_1; + CGU_UINT8 indices[6]; + }; + CGU_UINT64 data; + } BC4_Snorm_block; + +#ifndef ASPM_GPU + BC4_Snorm_block.data = 0LL; +#else + BC4_Snorm_block.data = 0; +#endif + + CGU_Vec2i reds; + reds = cmp_findEndpointsAlphaBlockSnorm(alphaBlock); + + BC4_Snorm_block.red_0 = reds.x & 0xFF; + BC4_Snorm_block.red_1 = reds.y & 0xFF; + + // check low end boundaries + if (BC4_Snorm_block.red_0 == -128) + BC4_Snorm_block.red_0 = -127; + if (BC4_Snorm_block.red_1 == -128) + BC4_Snorm_block.red_1 = -127; + + // Normalize signed int -128..127 to float -1..1 + CGU_Vec2f alphaMinMax; + alphaMinMax.x = (CGU_FLOAT)(BC4_Snorm_block.red_0) / 127.0f; + alphaMinMax.y = (CGU_FLOAT)(BC4_Snorm_block.red_1) / 127.0f; + + BC4_Snorm_block.data = cmp_getBlockPackedIndicesSNorm(alphaMinMax, alphaBlock, BC4_Snorm_block.data); + CmpBlock.x = BC4_Snorm_block.compressedBlock[0]; + CmpBlock.y = BC4_Snorm_block.compressedBlock[1]; +#else + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); +#endif + } + else + { + CGU_Vec2f RampMinMax; + RampMinMax = cmp_getLinearEndPoints(alphaBlock, fquality, false); // revert code to remove the false param + CmpBlock = cmp_getBlockPackedIndices(RampMinMax, alphaBlock, fquality); + } + + return CmpBlock; +} + +static void cmp_getCompressedAlphaRamp(CGU_UINT8 alpha[8], const CGU_UINT32 compressedBlock[2]) +{ + alpha[0] = (CGU_UINT8)(compressedBlock[0] & 0xff); + alpha[1] = (CGU_UINT8)((compressedBlock[0] >> 8) & 0xff); + + if (alpha[0] > alpha[1]) + { + // 8-alpha block: derive the other six alphas. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = (CGU_UINT8)((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = (CGU_UINT8)((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = (CGU_UINT8)((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = (CGU_UINT8)((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = (CGU_UINT8)((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#else + alpha[2] = static_cast((6 * alpha[0] + 1 * alpha[1] + 3) / 7); // bit code 010 + alpha[3] = static_cast((5 * alpha[0] + 2 * alpha[1] + 3) / 7); // bit code 011 + alpha[4] = static_cast((4 * alpha[0] + 3 * alpha[1] + 3) / 7); // bit code 100 + alpha[5] = static_cast((3 * alpha[0] + 4 * alpha[1] + 3) / 7); // bit code 101 + alpha[6] = static_cast((2 * alpha[0] + 5 * alpha[1] + 3) / 7); // bit code 110 + alpha[7] = static_cast((1 * alpha[0] + 6 * alpha[1] + 3) / 7); // bit code 111 +#endif + } + else + { + // 6-alpha block. + // Bit code 000 = alpha_0, 001 = alpha_1, others are interpolated. +#ifdef ASPM_GPU + alpha[2] = (CGU_UINT8)((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = (CGU_UINT8)((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = (CGU_UINT8)((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = (CGU_UINT8)((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#else + alpha[2] = static_cast((4 * alpha[0] + 1 * alpha[1] + 2) / 5); // Bit code 010 + alpha[3] = static_cast((3 * alpha[0] + 2 * alpha[1] + 2) / 5); // Bit code 011 + alpha[4] = static_cast((2 * alpha[0] + 3 * alpha[1] + 2) / 5); // Bit code 100 + alpha[5] = static_cast((1 * alpha[0] + 4 * alpha[1] + 2) / 5); // Bit code 101 +#endif + alpha[6] = 0; // Bit code 110 + alpha[7] = 255; // Bit code 111 + } +} + +static void cmp_decompressAlphaBlock(CGU_UINT8 alphaBlock[BLOCK_SIZE_4X4], const CGU_UINT32 compressedBlock[2]) +{ + CGU_UINT32 i; + CGU_UINT8 alpha[8]; + cmp_getCompressedAlphaRamp(alpha, compressedBlock); + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + CGU_UINT32 index; + if (i < 5) + index = (compressedBlock[0] & (0x7 << (16 + (i * 3)))) >> (16 + (i * 3)); + else if (i > 5) + index = (compressedBlock[1] & (0x7 << (2 + (i - 6) * 3))) >> (2 + (i - 6) * 3); + else + { + index = (compressedBlock[0] & 0x80000000) >> 31; + index |= (compressedBlock[1] & 0x3) << 1; + } + + alphaBlock[i] = alpha[index]; + } +} + +static void cmp_ProcessColors(CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMin, + CMP_INOUT CGU_Vec3f CMP_PTRINOUT colorMax, + CMP_INOUT CGU_UINT32 CMP_PTRINOUT c0, + CMP_INOUT CGU_UINT32 CMP_PTRINOUT c1, + CGU_INT setopt, + CGU_BOOL isSRGB) +{ + // CGU_UINT32 srbMap[32] = {0,5,8,11,12,13,14,15,16,17,18,19,20,21,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31}; + // CGU_UINT32 sgMap[64] = {0,10,14,16,19,20,22,24,25,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,42,43,43,44,45,45, + // 46,47,47,48,48,49,50,50,51,52,52,53,53,54,54,55,55,56,56,57,57,58,58,59,59,60,60,61,61,62,62,63,63}; + CGU_INT32 x, y, z; + CGU_Vec3f scale = {31.0f, 63.0f, 31.0f}; + CGU_Vec3f MinColorScaled; + CGU_Vec3f MaxColorScaled; + + // Clamp or Transform is needed, the transforms have built in clamps + if (isSRGB) + { + MinColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMin); + MaxColorScaled = cmp_linearToSrgb(CMP_PTRINOUT colorMax); + } + else + { + MinColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMin, 0.0f, 1.0f); + MaxColorScaled = cmp_clampVec3f(CMP_PTRINOUT colorMax, 0.0f, 1.0f); + } + + switch (setopt) + { + case 0: // Use Min Max processing + MinColorScaled = floor(MinColorScaled * scale); + MaxColorScaled = ceil(MaxColorScaled * scale); + CMP_PTRINOUT colorMin = MinColorScaled / scale; + CMP_PTRINOUT colorMax = MaxColorScaled / scale; + break; + default: // Use round processing + MinColorScaled = round(MinColorScaled * scale); + MaxColorScaled = round(MaxColorScaled * scale); + break; + } + + x = (CGU_UINT32)(MinColorScaled.x); + y = (CGU_UINT32)(MinColorScaled.y); + z = (CGU_UINT32)(MinColorScaled.z); + + //if (isSRGB) { + // // scale RB + // x = srbMap[x]; // &0x1F]; + // y = sgMap [y]; // &0x3F]; + // z = srbMap[z]; // &0x1F]; + // // scale G + //} + CMP_PTRINOUT c0 = (x << 11) | (y << 5) | z; + + x = (CGU_UINT32)(MaxColorScaled.x); + y = (CGU_UINT32)(MaxColorScaled.y); + z = (CGU_UINT32)(MaxColorScaled.z); + CMP_PTRINOUT c1 = (x << 11) | (y << 5) | z; +} + +#ifndef ASPM_GPU // Used by BC1, BC2 & BC3 +//---------------------------------------------------- +// This function decompresses a DXT colour block +// The block is decompressed to 8 bits per channel +// Result buffer is RGBA format, A is set to 255 +//---------------------------------------------------- +static void cmp_decompressDXTRGBA_Internal(CGU_UINT8 rgbBlock[BLOCK_SIZE_4X4X4], const CGU_Vec2ui compressedBlock, const CGU_BOOL mapDecodeRGBA) +{ + CGU_BOOL bDXT1 = TRUE; + CGU_UINT32 n0 = compressedBlock.x & 0xffff; + CGU_UINT32 n1 = compressedBlock.x >> 16; + CGU_UINT32 r0; + CGU_UINT32 g0; + CGU_UINT32 b0; + CGU_UINT32 r1; + CGU_UINT32 g1; + CGU_UINT32 b1; + + r0 = ((n0 & 0xf800) >> 8); + g0 = ((n0 & 0x07e0) >> 3); + b0 = ((n0 & 0x001f) << 3); + + r1 = ((n1 & 0xf800) >> 8); + g1 = ((n1 & 0x07e0) >> 3); + b1 = ((n1 & 0x001f) << 3); + + // Apply the lower bit replication to give full dynamic range + r0 += (r0 >> 5); + r1 += (r1 >> 5); + g0 += (g0 >> 6); + g1 += (g1 >> 6); + b0 += (b0 >> 5); + b1 += (b1 >> 5); + + if (!mapDecodeRGBA) + { + //-------------------------------------------------------------- + // Channel mapping output as BGRA + //-------------------------------------------------------------- + CGU_UINT32 c0 = 0xff000000 | (r0 << 16) | (g0 << 8) | b0; + CGU_UINT32 c1 = 0xff000000 | (r1 << 16) | (g1 << 8) | b1; + + if (!bDXT1 || n0 > n1) + { + CGU_UINT32 c2 = 0xff000000 | (((2 * r0 + r1) / 3) << 16) | (((2 * g0 + g1) / 3) << 8) | (((2 * b0 + b1) / 3)); + CGU_UINT32 c3 = 0xff000000 | (((2 * r1 + r0) / 3) << 16) | (((2 * g1 + g0) / 3) << 8) | (((2 * b1 + b0) / 3)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + + switch (index) + { + case 0: + ((CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CGU_UINT32*)rgbBlock)[i] = c3; + break; + } + } + } + else + { + // Transparent decode + CGU_UINT32 c2 = 0xff000000 | (((r0 + r1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((b0 + b1) / 2)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + + switch (index) + { + case 0: + ((CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CGU_UINT32*)rgbBlock)[i] = 0x00000000; + break; + } + } + } + } + else + { + // MAP_BC15_TO_ABGR + //-------------------------------------------------------------- + // Channel mapping output as RGBA + //-------------------------------------------------------------- + + CGU_UINT32 c0 = 0xff000000 | (b0 << 16) | (g0 << 8) | r0; + CGU_UINT32 c1 = 0xff000000 | (b1 << 16) | (g1 << 8) | r1; + + if (!bDXT1 || n0 > n1) + { + CGU_UINT32 c2 = 0xff000000 | (((2 * b0 + b1 + 1) / 3) << 16) | (((2 * g0 + g1 + 1) / 3) << 8) | (((2 * r0 + r1 + 1) / 3)); + CGU_UINT32 c3 = 0xff000000 | (((2 * b1 + b0 + 1) / 3) << 16) | (((2 * g1 + g0 + 1) / 3) << 8) | (((2 * r1 + r0 + 1) / 3)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c3; + break; + } + } + } + else + { + // Transparent decode + CGU_UINT32 c2 = 0xff000000 | (((b0 + b1) / 2) << 16) | (((g0 + g1) / 2) << 8) | (((r0 + r1) / 2)); + + for (int i = 0; i < 16; i++) + { + int index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c0; + break; + case 1: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c1; + break; + case 2: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = c2; + break; + case 3: + ((CMP_GLOBAL CGU_UINT32*)rgbBlock)[i] = 0x00000000; + break; + } + } + } + } //MAP_ABGR +} +#endif // !ASPM_GPU + +//-------------------------------------------------------------------------------------------------------- +// Decompress is RGB (0.0f..255.0f) +//-------------------------------------------------------------------------------------------------------- +static void cmp_decompressRGBBlock(CMP_INOUT CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock) +{ + CGU_UINT32 n0 = compressedBlock.x & 0xffff; + CGU_UINT32 n1 = compressedBlock.x >> 16; + CGU_UINT32 index; + + //------------------------------------------------------- + // Decode the compressed block 0..255 color range + //------------------------------------------------------- + CGU_Vec3f c0 = cmp_565ToLinear(n0); // max color + CGU_Vec3f c1 = cmp_565ToLinear(n1); // min color + CGU_Vec3f c2; + CGU_Vec3f c3; + + if (n0 > n1) + { + c2 = (c0 * 2.0f + c1) / 3.0f; + c3 = (c1 * 2.0f + c0) / 3.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + rgbBlock[i] = c0; + break; + case 1: + rgbBlock[i] = c1; + break; + case 2: + rgbBlock[i] = c2; + break; + case 3: + rgbBlock[i] = c3; + break; + } + } + } + else + { + // Transparent decode + c2 = (c0 + c1) / 2.0f; + + for (CGU_UINT32 i = 0; i < 16; i++) + { + index = (compressedBlock.y >> (2 * i)) & 3; + switch (index) + { + case 0: + rgbBlock[i] = c0; + break; + case 1: + rgbBlock[i] = c1; + break; + case 2: + rgbBlock[i] = c2; + break; + case 3: + rgbBlock[i] = 0.0f; + break; + } + } + } +} + +// The source is 0..1, decompressed data using cmp_decompressRGBBlock is 0..255 which is converted down to 0..1 +static float CMP_RGBBlockError(const CGU_Vec3f src_rgbBlock[BLOCK_SIZE_4X4], const CGU_Vec2ui compressedBlock, CGU_BOOL isSRGB) +{ + CGU_Vec3f rgbBlock[BLOCK_SIZE_4X4]; + + // Decompressed block channels are 0..255 + cmp_decompressRGBBlock(rgbBlock, compressedBlock); + + //------------------------------------------------------------------ + // Calculate MSE of the block + // Note : pow is used as Float type for the code to be usable on CPU + //------------------------------------------------------------------ + CGU_Vec3f serr; + serr = 0.0f; + + float sR, sG, sB, R, G, B; + + for (int j = 0; j < 16; j++) + { + if (isSRGB) + { + sR = round(cmp_linearToSrgbf(src_rgbBlock[j].x) * 255.0f); + sG = round(cmp_linearToSrgbf(src_rgbBlock[j].y) * 255.0f); + sB = round(cmp_linearToSrgbf(src_rgbBlock[j].z) * 255.0f); + } + else + { + sR = round(src_rgbBlock[j].x * 255.0f); + sG = round(src_rgbBlock[j].y * 255.0f); + sB = round(src_rgbBlock[j].z * 255.0f); + } + + rgbBlock[j] = rgbBlock[j]; + + R = rgbBlock[j].x; + G = rgbBlock[j].y; + B = rgbBlock[j].z; + + // Norm colors + serr.x += pow(sR - R, 2.0f); + serr.y += pow(sG - G, 2.0f); + serr.z += pow(sB - B, 2.0f); + } + + // MSE for 16 texels + return (serr.x + serr.y + serr.z) / 48.0f; +} + +// Processing input source 0..1.0f) +static CGU_Vec2ui CompressRGBBlock_FM(const CGU_Vec3f rgbBlockUVf[16], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB, CMP_INOUT CGU_FLOAT CMP_PTRINOUT errout) +{ + CGU_Vec3f axisVectorRGB = {0.0f, 0.0f, 0.0f}; // The axis vector for index projection + CGU_FLOAT pos_on_axis[16]; // The distance each unique falls along the compression axis + CGU_FLOAT axisleft = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_FLOAT axisright = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_FLOAT axiscentre = 0; // The extremities and centre (average of left/right) of srcRGB along the compression axis + CGU_INT32 swap = 0; // Indicator if the RGB values need swapping to generate an opaque result + CGU_Vec3f average_rgb; // The centrepoint of the axis + CGU_Vec3f srcRGB[16]; // The list of source colors with blue channel altered + CGU_Vec3f srcBlock[16]; // The list of source colors with any color space transforms and clipping + CGU_Vec3f rgb; + CGU_UINT32 c0 = 0; + CGU_UINT32 c1 = 0; + CGU_Vec2ui compressedBlock = {0, 0}; + CGU_FLOAT Q1CompErr = CMP_FLT_MAX; + CGU_Vec2ui Q1CompData = {0,0}; + + // ------------------------------------------------------------------------------------- + // (1) Find the array of unique pixel values and sum them to find their average position + // ------------------------------------------------------------------------------------- + { + CGU_FLOAT errLQ = 0.0f; + CGU_BOOL fastProcess = (fquality <= CMP_QUALITY1); + CGU_Vec3f srcMin = 1.0f; // Min source color + CGU_Vec3f srcMax = 0.0f; // Max source color + CGU_Vec2ui Q1compressedBlock = {0, 0}; + + average_rgb = 0.0f; + // Get average and modifed src + // find average position and save list of pixels as 0F..255F range for processing + // Note: z (blue) is average of blue+green channels + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + srcMin = cmp_minVec3f(srcMin, rgbBlockUVf[i]); + srcMax = cmp_maxVec3f(srcMax, rgbBlockUVf[i]); + if (!fastProcess) + { + rgb = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : cmp_saturate(rgbBlockUVf[i]); + rgb.z = (rgb.y + rgb.z) * 0.5F; // Z-axiz => (R+G)/2 + srcRGB[i] = rgb; + average_rgb = average_rgb + rgb; + } + } + + // Process two colors for saving in 565 format as C0 and C1 + cmp_ProcessColors(CMP_REFINOUT srcMin, CMP_REFINOUT srcMax, CMP_REFINOUT c0, CMP_REFINOUT c1, isSRGB ? 1 : 0, isSRGB); + + // Save simple min-max encoding + CGU_UINT32 index = 0; + if (c0 < c1) + { + Q1CompData.x = (c0 << 16) | c1; + errLQ = cmp_getIndicesRGB(CMP_REFINOUT index, rgbBlockUVf, srcMin, srcMax, false); + Q1CompData.y = index; + CMP_PTRINOUT errout = errLQ; + } + else + { + // Most simple case all colors are equal or 0.0f + Q1compressedBlock.x = (c1 << 16) | c0; + Q1compressedBlock.y = 0; + CMP_PTRINOUT errout = 0.0f; + return Q1compressedBlock; + } + + if (fastProcess) + return Q1CompData; + + // 0.0625F is (1/BLOCK_SIZE_4X4) + average_rgb = average_rgb * 0.0625F; + } + + // ------------------------------------------------------------------------------------- + // (4) For each component, reflect points about the average so all lie on the same side + // of the average, and compute the new average - this gives a second point that defines the axis + // To compute the sign of the axis sum the positive differences of G for each of R and B (the + // G axis is always positive in this implementation + // ------------------------------------------------------------------------------------- + // An interesting situation occurs if the G axis contains no information, in which case the RB + // axis is also compared. I am not entirely sure if this is the correct implementation - should + // the priority axis be determined by magnitude? + { + CGU_FLOAT rg_pos = 0.0f; + CGU_FLOAT bg_pos = 0.0f; + CGU_FLOAT rb_pos = 0.0f; + + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + rgb = srcRGB[i] - average_rgb; + axisVectorRGB = axisVectorRGB + fabs(rgb); + if (rgb.x > 0) + { + rg_pos += rgb.y; + rb_pos += rgb.z; + } + if (rgb.z > 0) + bg_pos += rgb.y; + } + + // Average over BLOCK_SIZE_4X4 + axisVectorRGB = axisVectorRGB * 0.0625F; + + // New average position + if (rg_pos < 0) + axisVectorRGB.x = -axisVectorRGB.x; + if (bg_pos < 0) + axisVectorRGB.z = -axisVectorRGB.z; + if ((rg_pos == bg_pos) && (rg_pos == 0)) + { + if (rb_pos < 0) + axisVectorRGB.z = -axisVectorRGB.z; + } + } + + // ------------------------------------------------------------------------------------- + // (5) Axis projection and remapping + // ------------------------------------------------------------------------------------- + { + CGU_FLOAT v2_recip; + // Normalize the axis for simplicity of future calculation + v2_recip = dot(axisVectorRGB, axisVectorRGB); + if (v2_recip > 0) + v2_recip = 1.0f / (CGU_FLOAT)sqrt(v2_recip); + else + v2_recip = 1.0f; + axisVectorRGB = axisVectorRGB * v2_recip; + } + + // ------------------------------------------------------------------------------------- + // (6) Map the axis + // ------------------------------------------------------------------------------------- + // the line joining (and extended on either side of) average and axis + // defines the axis onto which the points will be projected + // Project all the points onto the axis, calculate the distance along + // the axis from the centre of the axis (average) + // From Foley & Van Dam: Closest point of approach of a line (P + v) to a point (R) is + // P + ((R-P).v) / (v.v))v + // The distance along v is therefore (R-P).v / (v.v) where (v.v) is 1 if v is a unit vector. + // + // Calculate the extremities at the same time - these need to be reasonably accurately + // represented in all cases + { + axisleft = CMP_FLOAT_MAX; + axisright = -CMP_FLOAT_MAX; + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Compute the distance along the axis of the point of closest approach + CGU_Vec3f temp = (srcRGB[i] - average_rgb); + pos_on_axis[i] = dot(temp, axisVectorRGB); + + // Work out the extremities + if (pos_on_axis[i] < axisleft) + axisleft = pos_on_axis[i]; + if (pos_on_axis[i] > axisright) + axisright = pos_on_axis[i]; + } + } + + // --------------------------------------------------------------------------------------------- + // (7) Now we have a good axis and the basic information about how the points are mapped to it + // Our initial guess is to represent the endpoints accurately, by moving the average + // to the centre and recalculating the point positions along the line + // --------------------------------------------------------------------------------------------- + { + axiscentre = (axisleft + axisright) * 0.5F; + average_rgb = average_rgb + (axisVectorRGB * axiscentre); + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + pos_on_axis[i] -= axiscentre; + axisright -= axiscentre; + axisleft -= axiscentre; + } + + // ------------------------------------------------------------------------------------- + // (8) Calculate the high and low output colour values + // Involved in this is a rounding procedure which is undoubtedly slightly twitchy. A + // straight rounded average is not correct, as the decompressor 'unrounds' by replicating + // the top bits to the bottom. + // In order to take account of this process, we don't just apply a straight rounding correction, + // but base our rounding on the input value (a straight rounding is actually pretty good in terms of + // error measure, but creates a visual colour and/or brightness shift relative to the original image) + // The method used here is to apply a centre-biased rounding dependent on the input value, which was + // (mostly by experiment) found to give minimum MSE while preserving the visual characteristics of + // the image. + // rgb = (average_rgb + (left|right)*axisVectorRGB); + // ------------------------------------------------------------------------------------- + { + CGU_Vec3f MinColor, MaxColor; + + MinColor = average_rgb + (axisVectorRGB * axisleft); + MaxColor = average_rgb + (axisVectorRGB * axisright); + MinColor.z = (MinColor.z * 2) - MinColor.y; + MaxColor.z = (MaxColor.z * 2) - MaxColor.y; + + cmp_ProcessColors(CMP_REFINOUT MinColor, CMP_REFINOUT MaxColor, CMP_REFINOUT c0, CMP_REFINOUT c1, 1, false); + + // Force to be a 4-colour opaque block - in which case, c0 is greater than c1 + swap = 0; + if (c0 < c1) + { + CGU_UINT32 t; + t = c0; + c0 = c1; + c1 = t; + swap = 1; + } + else if (c0 == c1) + { + // This block will always be encoded in 3-colour mode + // Need to ensure that only one of the two points gets used, + // avoiding accidentally setting some transparent pixels into the block + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + pos_on_axis[i] = axisleft; + } + + compressedBlock.x = c0 | (c1 << 16); + + // ------------------------------------------------------------------------------------- + // (9) Final clustering, creating the 2-bit values that define the output + // ------------------------------------------------------------------------------------- + + CGU_UINT32 index; + CGU_FLOAT division; + { + compressedBlock.y = 0; + division = axisright * 2.0f / 3.0f; + axiscentre = (axisleft + axisright) / 2; // Actually, this code only works if centre is 0 or approximately so + + CGU_FLOAT CompMinErr; + + // This feature is work in progress + // remap to BC1 spec for decoding offsets, + // where cn[0] > cn[1] Max Color = index 0, 2/3 offset =index 2, 1/3 offset = index 3, Min Color = index 1 + // CGU_Vec3f cn[4]; + // cn[0] = MaxColor; + // cn[1] = MinColor; + // cn[2] = cn[0]*2.0f/3.0f + cn[1]*1.0f/3.0f; + // cn[3] = cn[0]*1.0f/3.0f + cn[1]*2.0f/3.0f; + + for (CGU_INT32 i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Endpoints (indicated by block > average) are 0 and 1, while + // interpolants are 2 and 3 + if (fabs(pos_on_axis[i]) >= division) + index = 0; + else + index = 2; + // Positive is in the latter half of the block + if (pos_on_axis[i] >= axiscentre) + index += 1; + + index = index ^ swap; + // Set the output, taking swapping into account + compressedBlock.y |= (index << (2 * i)); + + // use err calc for use in higher quality code + //CompMinErr += dot(srcRGBRef[i] - cn[index],srcRGBRef[i] - cn[index]); + } + + //CompMinErr = CompMinErr * 0.0208333f; + + CompMinErr = CMP_RGBBlockError(rgbBlockUVf, compressedBlock, isSRGB); + Q1CompErr = CMP_RGBBlockError(rgbBlockUVf, Q1CompData, isSRGB); + + if (CompMinErr > Q1CompErr) + { + compressedBlock = Q1CompData; + CMP_PTRINOUT errout = Q1CompErr; + } + else + CMP_PTRINOUT errout = CompMinErr; + } + } + // done + + return compressedBlock; +} + +#ifndef CMP_USE_LOWQUALITY +static CMP_EndPoints CompressRGBBlock_Slow(CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4], + CGU_FLOAT Rpt[BLOCK_SIZE_4X4], + CGU_UINT32 dwUniqueColors, + CGU_Vec3f channelWeightsBGR, + CGU_UINT32 m_nRefinementSteps) +{ + CMP_UNUSED(channelWeightsBGR); + CMP_UNUSED(m_nRefinementSteps); + ALIGN_16 CGU_FLOAT Prj0[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT Prj[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT PrjErr[BLOCK_SIZE_4X4]; + ALIGN_16 CGU_FLOAT RmpIndxs[BLOCK_SIZE_4X4]; + + CGU_Vec3f LineDirG; + CGU_Vec3f LineDir; + CGU_FLOAT LineDir0[NUM_CHANNELS]; + CGU_Vec3f BlkUV[BLOCK_SIZE_4X4]; + CGU_Vec3f BlkSh[BLOCK_SIZE_4X4]; + CGU_Vec3f Mdl; + + CGU_Vec3f rsltC0; + CGU_Vec3f rsltC1; + CGU_Vec3f PosG0 = {0.0f, 0.0f, 0.0f}; + CGU_Vec3f PosG1 = {0.0f, 0.0f, 0.0f}; + CGU_UINT32 i; + + for (i = 0; i < dwUniqueColors; i++) + { + BlkUV[i] = BlkInBGRf_UV[i]; + } + + // if not more then 2 different colors, we've done + if (dwUniqueColors <= 2) + { + rsltC0 = BlkInBGRf_UV[0] * 255.0f; + rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; + } + else + { + // This is our first attempt to find an axis we will go along. + // The cumulation is done to find a line minimizing the MSE from the + // input 3D points. + + // While trying to find the axis we found that the diameter of the input + // set is quite small. Do not bother. + + // FindAxisIsSmall(BlkSh, LineDir0, Mdl, Blk, Rpt,dwUniqueColors); + { + CGU_UINT32 ii; + CGU_UINT32 jj; + CGU_UINT32 kk; + + // These vars cannot be Vec3 as index to them are varying + CGU_FLOAT Crrl[NUM_CHANNELS]; + CGU_FLOAT RGB2[NUM_CHANNELS]; + + LineDir0[0] = LineDir0[1] = LineDir0[2] = RGB2[0] = RGB2[1] = RGB2[2] = Crrl[0] = Crrl[1] = Crrl[2] = Mdl.x = Mdl.y = Mdl.z = 0.f; + + // sum position of all points + CGU_FLOAT fNumPoints = 0.0f; + for (ii = 0; ii < dwUniqueColors; ii++) + { + Mdl.x += BlkUV[ii].x * Rpt[ii]; + Mdl.y += BlkUV[ii].y * Rpt[ii]; + Mdl.z += BlkUV[ii].z * Rpt[ii]; + fNumPoints += Rpt[ii]; + } + + // and then average to calculate center coordinate of block + Mdl /= fNumPoints; + + for (ii = 0; ii < dwUniqueColors; ii++) + { + // calculate output block as offsets around block center + BlkSh[ii] = BlkUV[ii] - Mdl; + + // compute correlation matrix + // RGB2 = sum of ((distance from point from center) squared) + RGB2[0] += BlkSh[ii].x * BlkSh[ii].x * Rpt[ii]; + RGB2[1] += BlkSh[ii].y * BlkSh[ii].y * Rpt[ii]; + RGB2[2] += BlkSh[ii].z * BlkSh[ii].z * Rpt[ii]; + + Crrl[0] += BlkSh[ii].x * BlkSh[ii].y * Rpt[ii]; + Crrl[1] += BlkSh[ii].y * BlkSh[ii].z * Rpt[ii]; + Crrl[2] += BlkSh[ii].z * BlkSh[ii].x * Rpt[ii]; + } + + // if set's diameter is small + CGU_UINT32 i0 = 0, i1 = 1; + CGU_FLOAT mxRGB2 = 0.0f; + + CGU_FLOAT fEPS = fNumPoints * EPS; + for (kk = 0, jj = 0; jj < 3; jj++) + { + if (RGB2[jj] >= fEPS) + kk++; + else + RGB2[jj] = 0.0f; + + if (mxRGB2 < RGB2[jj]) + { + mxRGB2 = RGB2[jj]; + i0 = jj; + } + } + + CGU_FLOAT fEPS2 = fNumPoints * EPS2; + CGU_BOOL AxisIsSmall; + + AxisIsSmall = (RGB2[0] < fEPS2); + AxisIsSmall = AxisIsSmall && (RGB2[1] < fEPS2); + AxisIsSmall = AxisIsSmall && (RGB2[2] < fEPS2); + + // all are very small to avoid division on the small determinant + if (AxisIsSmall) + { + rsltC0 = BlkInBGRf_UV[0] * 255.0f; + rsltC1 = BlkInBGRf_UV[dwUniqueColors - 1] * 255.0f; + } + else + { + // !AxisIsSmall + if (kk == 1) // really only 1 dimension + LineDir0[i0] = 1.; + else if (kk == 2) + { // really only 2 dimensions + i1 = (RGB2[(i0 + 1) % 3] > 0.f) ? (i0 + 1) % 3 : (i0 + 2) % 3; + CGU_FLOAT Crl = (i1 == (i0 + 1) % 3) ? Crrl[i0] : Crrl[(i0 + 2) % 3]; + LineDir0[i1] = Crl / RGB2[i0]; + LineDir0[i0] = 1.; + } + else + { + CGU_FLOAT maxDet = 100000.f; + CGU_FLOAT Cs[3]; + // select max det for precision + for (jj = 0; jj < 3; jj++) + { + // 3 = nDimensions + CGU_FLOAT Det = RGB2[jj] * RGB2[(jj + 1) % 3] - Crrl[jj] * Crrl[jj]; + Cs[jj] = fabs(Crrl[jj] / sqrt(RGB2[jj] * RGB2[(jj + 1) % 3])); + if (maxDet < Det) + { + maxDet = Det; + i0 = jj; + } + } + + // inverse correl matrix + // -- -- -- -- + // | A B | | C -B | + // | B C | => | -B A | + // -- -- -- -- + CGU_FLOAT mtrx1[2][2]; + CGU_FLOAT vc1[2]; + CGU_FLOAT vc[2]; + vc1[0] = Crrl[(i0 + 2) % 3]; + vc1[1] = Crrl[(i0 + 1) % 3]; + // C + mtrx1[0][0] = RGB2[(i0 + 1) % 3]; + // A + mtrx1[1][1] = RGB2[i0]; + // -B + mtrx1[1][0] = mtrx1[0][1] = -Crrl[i0]; + // find a solution + vc[0] = mtrx1[0][0] * vc1[0] + mtrx1[0][1] * vc1[1]; + vc[1] = mtrx1[1][0] * vc1[0] + mtrx1[1][1] * vc1[1]; + // normalize + vc[0] /= maxDet; + vc[1] /= maxDet; + // find a line direction vector + LineDir0[i0] = 1.; + LineDir0[(i0 + 1) % 3] = 1.; + LineDir0[(i0 + 2) % 3] = vc[0] + vc[1]; + } + + // normalize direction vector + CGU_FLOAT Len = LineDir0[0] * LineDir0[0] + LineDir0[1] * LineDir0[1] + LineDir0[2] * LineDir0[2]; + Len = sqrt(Len); + + LineDir0[0] = (Len > 0.f) ? LineDir0[0] / Len : 0.0f; + LineDir0[1] = (Len > 0.f) ? LineDir0[1] / Len : 0.0f; + LineDir0[2] = (Len > 0.f) ? LineDir0[2] / Len : 0.0f; + } + } // FindAxisIsSmall + + // GCC is being an awful being when it comes to goto-jumps. + // So please bear with this. + CGU_FLOAT ErrG = 10000000.f; + CGU_FLOAT PrjBnd0; + CGU_FLOAT PrjBnd1; + ALIGN_16 CGU_FLOAT PreMRep[BLOCK_SIZE_4X4]; + + LineDir.x = LineDir0[0]; + LineDir.y = LineDir0[1]; + LineDir.z = LineDir0[2]; + + // Here is the main loop. + // 1. Project input set on the axis in consideration. + // 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal pair of end points. + // 3. Compute the vector of indexes (or clusters) for the current approximate ramp. + // 4. Present our color channels as 3 16DIM vectors. + // 5. Find closest approximation of each of 16DIM color vector with the projection of the 16DIM index vector. + // 6. Plug the projections as a new directional vector for the axis. + // 7. Goto 1. + // D - is 16 dim "index" vector (or 16 DIM vector of indexes - {0, 1/3,2/3, 0, ...,}, but shifted and normalized). + // Ci - is a 16 dim vector of color i. for each Ci find a scalar Ai such that (Ai * D - Ci) (Ai * D - Ci) -> min , + // i.e distance between vector AiD and C is min. You can think of D as a unit interval(vector) "clusterizer", and Ai is a scale + // you need to apply to the clusterizer to approximate the Ci vector instead of the unit vector. + // Solution is + // Ai = (D . Ci) / (D . D); . - is a dot product. + // in 3 dim space Ai(s) represent a line direction, along which + // we again try to find (sub)optimal quantizer. + // That's what our for(;;) loop is about. + for (;;) + { + // 1. Project input set on the axis in consideration. + // From Foley & Van Dam: Closest point of approach of a line (P + v) to a + // point (R) is + // P + ((R-P).v) / (v.v))v + // The distance along v is therefore (R-P).v / (v.v) + // (v.v) is 1 if v is a unit vector. + // + PrjBnd0 = 1000.0f; + PrjBnd1 = -1000.0f; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + Prj0[i] = Prj[i] = PrjErr[i] = PreMRep[i] = 0.f; + + for (i = 0; i < dwUniqueColors; i++) + { + Prj0[i] = Prj[i] = dot(BlkSh[i], LineDir); + PrjErr[i] = dot(BlkSh[i] - LineDir * Prj[i], BlkSh[i] - LineDir * Prj[i]); + PrjBnd0 = min(PrjBnd0, Prj[i]); + PrjBnd1 = max(PrjBnd1, Prj[i]); + } + + // 2. Run 1 dimensional search (see scalar case) to find an (sub) optimal + // pair of end points. + + // min and max of the search interval + CGU_FLOAT Scl0; + CGU_FLOAT Scl1; + Scl0 = PrjBnd0 - (PrjBnd1 - PrjBnd0) * 0.125f; + Scl1 = PrjBnd1 + (PrjBnd1 - PrjBnd0) * 0.125f; + + // compute scaling factor to scale down the search interval to [0.,1] + const CGU_FLOAT Scl2 = (Scl1 - Scl0) * (Scl1 - Scl0); + const CGU_FLOAT overScl = 1.f / (Scl1 - Scl0); + + for (i = 0; i < dwUniqueColors; i++) + { + // scale them + Prj[i] = (Prj[i] - Scl0) * overScl; + // premultiply the scale square to plug into error computation later + PreMRep[i] = Rpt[i] * Scl2; + } + + // scale first approximation of end points + PrjBnd0 = (PrjBnd0 - Scl0) * overScl; + PrjBnd1 = (PrjBnd1 - Scl0) * overScl; + + CGU_FLOAT StepErr = MAX_ERROR; + + // search step + CGU_FLOAT searchStep = 0.025f; + + // low Start/End; high Start/End + const CGU_FLOAT lowStartEnd = (PrjBnd0 - 2.f * searchStep > 0.f) ? PrjBnd0 - 2.f * searchStep : 0.f; + const CGU_FLOAT highStartEnd = (PrjBnd1 + 2.f * searchStep < 1.f) ? PrjBnd1 + 2.f * searchStep : 1.f; + + // find the best endpoints + CGU_FLOAT Pos0 = 0; + CGU_FLOAT Pos1 = 0; + CGU_FLOAT lowPosStep, highPosStep; + CGU_FLOAT err; + + int l, h; + for (l = 0, lowPosStep = lowStartEnd; l < 8; l++, lowPosStep += searchStep) + { + for (h = 0, highPosStep = highStartEnd; h < 8; h++, highPosStep -= searchStep) + { + // compute an error for the current pair of end points. + err = cmp_getRampErr(Prj, PrjErr, PreMRep, StepErr, lowPosStep, highPosStep, dwUniqueColors); + + if (err < StepErr) + { + // save better result + StepErr = err; + Pos0 = lowPosStep; + Pos1 = highPosStep; + } + } + } + + // inverse the scaling + Pos0 = Pos0 * (Scl1 - Scl0) + Scl0; + Pos1 = Pos1 * (Scl1 - Scl0) + Scl0; + + // did we find somthing better from the previous run? + if (StepErr + 0.001 < ErrG) + { + // yes, remember it + ErrG = StepErr; + LineDirG = LineDir; + + PosG0.x = Pos0; + PosG0.y = Pos0; + PosG0.z = Pos0; + PosG1.x = Pos1; + PosG1.y = Pos1; + PosG1.z = Pos1; + + // 3. Compute the vector of indexes (or clusters) for the current + // approximate ramp. + // indexes + const CGU_FLOAT step = (Pos1 - Pos0) / 3.0f; // (dwNumChannels=4 - 1); + const CGU_FLOAT step_h = step * (CGU_FLOAT)0.5; + const CGU_FLOAT rstep = (CGU_FLOAT)1.0f / step; + const CGU_FLOAT overBlkTp = 1.f / 3.0f; // (dwNumChannels=4 - 1); + + // here the index vector is computed, + // shifted and normalized + CGU_FLOAT indxAvrg = 3.0f / 2.0f; // (dwNumChannels=4 - 1); + + for (i = 0; i < dwUniqueColors; i++) + { + CGU_FLOAT del; + // CGU_UINT32 n = (CGU_UINT32)((b - _min_ex + (step*0.5f)) * rstep); + if ((del = Prj0[i] - Pos0) <= 0) + RmpIndxs[i] = 0.f; + else if (Prj0[i] - Pos1 >= 0) + RmpIndxs[i] = 3.0f; // (dwNumChannels=4 - 1); + else + RmpIndxs[i] = floor((del + step_h) * rstep); + // shift and normalization + RmpIndxs[i] = (RmpIndxs[i] - indxAvrg) * overBlkTp; + } + + // 4. Present our color channels as 3 16 DIM vectors. + // 5. Find closest aproximation of each of 16DIM color vector with the + // pojection of the 16DIM index vector. + CGU_Vec3f Crs = {0.0f, 0.0f, 0.0f}; + CGU_FLOAT Len = 0.0f; + + for (i = 0; i < dwUniqueColors; i++) + { + const CGU_FLOAT PreMlt = RmpIndxs[i] * Rpt[i]; + Len += RmpIndxs[i] * PreMlt; + Crs.x += BlkSh[i].x * PreMlt; + Crs.y += BlkSh[i].y * PreMlt; + Crs.z += BlkSh[i].z * PreMlt; + } + + LineDir.x = LineDir.y = LineDir.z = 0.0f; + if (Len > 0.0f) + { + CGU_FLOAT Len2; + LineDir = Crs / Len; + // 6. Plug the projections as a new directional vector for the axis. + // 7. Goto 1. + Len2 = dot(LineDir, LineDir); // LineDir.x * LineDir.x + LineDir.y * LineDir.y + LineDir.z * LineDir.z; + Len2 = sqrt(Len2); + LineDir /= Len2; + } + } + else // We was not able to find anything better. Drop out. + break; + } + + // inverse transform to find end-points of 3-color ramp + rsltC0 = (PosG0 * LineDirG + Mdl) * 255.f; + rsltC1 = (PosG1 * LineDirG + Mdl) * 255.f; + } // !isDone + + // We've dealt with (almost) unrestricted full precision realm. + // Now back digital world. + + // round the end points to make them look like compressed ones + CGU_Vec3f inpRmpEndPts0 = {0.0f, 255.0f, 0.0f}; + CGU_Vec3f inpRmpEndPts1 = {0.0f, 255.0f, 0.0f}; + CGU_Vec3f Fctrs0 = {8.0f, 4.0f, 8.0f}; //(1 << (PIX_GRID - BG)); x (1 << (PIX_GRID - GG)); y (1 << (PIX_GRID - RG)); z + CGU_Vec3f Fctrs1 = {32.0f, 64.0f, 32.0f}; //(CGU_FLOAT)(1 << RG); z (CGU_FLOAT)(1 << GG); y (CGU_FLOAT)(1 << BG); x + CGU_FLOAT _Min = 0.0f; + CGU_FLOAT _Max = 255.0f; + + { + // MkRmpOnGrid(inpRmpEndPts, rsltC, _Min, _Max); + + inpRmpEndPts0 = floor(rsltC0); + + if (inpRmpEndPts0.x <= _Min) + inpRmpEndPts0.x = _Min; + else + { + inpRmpEndPts0.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts0.x / Fctrs1.x); + inpRmpEndPts0.x = min(inpRmpEndPts0.x, _Max); + } + if (inpRmpEndPts0.y <= _Min) + inpRmpEndPts0.y = _Min; + else + { + inpRmpEndPts0.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts0.y / Fctrs1.y); + inpRmpEndPts0.y = min(inpRmpEndPts0.y, _Max); + } + if (inpRmpEndPts0.z <= _Min) + inpRmpEndPts0.z = _Min; + else + { + inpRmpEndPts0.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts0.z / Fctrs1.z); + inpRmpEndPts0.z = min(inpRmpEndPts0.z, _Max); + } + + inpRmpEndPts0 = floor(inpRmpEndPts0 / Fctrs0) * Fctrs0; + + inpRmpEndPts1 = floor(rsltC1); + if (inpRmpEndPts1.x <= _Min) + inpRmpEndPts1.x = _Min; + else + { + inpRmpEndPts1.x += floor(128.f / Fctrs1.x) - floor(inpRmpEndPts1.x / Fctrs1.x); + inpRmpEndPts1.x = min(inpRmpEndPts1.x, _Max); + } + if (inpRmpEndPts1.y <= _Min) + inpRmpEndPts1.y = _Min; + else + { + inpRmpEndPts1.y += floor(128.f / Fctrs1.y) - floor(inpRmpEndPts1.y / Fctrs1.y); + inpRmpEndPts1.y = min(inpRmpEndPts1.y, _Max); + } + if (inpRmpEndPts1.z <= _Min) + inpRmpEndPts1.z = _Min; + else + { + inpRmpEndPts1.z += floor(128.f / Fctrs1.z) - floor(inpRmpEndPts1.z / Fctrs1.z); + inpRmpEndPts1.z = min(inpRmpEndPts1.z, _Max); + } + + inpRmpEndPts1 = floor(inpRmpEndPts1 / Fctrs0) * Fctrs0; + } // MkRmpOnGrid + + CMP_EndPoints EndPoints; + EndPoints.Color0 = inpRmpEndPts0; + EndPoints.Color1 = inpRmpEndPts1; + + return EndPoints; +} +#endif + +// Process a rgbBlock which is normalized (0.0f ... 1.0f), signed normal is not implemented +static CGU_Vec2ui CompressBlockBC1_RGBA_Internal(const CGU_Vec3f rgbBlockUVf[BLOCK_SIZE_4X4], + const CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_Vec3f channelWeights, + CGU_UINT32 dwAlphaThreshold, + CGU_UINT32 m_nRefinementSteps, + CMP_IN CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec2ui cmpBlock = {0, 0}; + CGU_FLOAT errLQ = 1e6f; + + cmpBlock = CompressRGBBlock_FM(rgbBlockUVf, fquality, isSRGB, CMP_REFINOUT errLQ); + +#ifndef CMP_USE_LOWQUALITY + //------------------------------------------------------------------ + // Processing is in 0..255 range, code needs to be normized to 0..1 + //------------------------------------------------------------------ + if ((errLQ > 0.0f) && (fquality > CMP_QUALITY2)) + { + CGU_Vec3f rgbBlock_normal[BLOCK_SIZE_4X4]; + CGU_UINT32 nCmpIndices = 0; + CGU_UINT32 c0, c1; + // High Quality + CMP_EndPoints EndPoints = {{0, 0, 0xFF}, {0, 0, 0xFF}}; + // Hold a err ref to lowest quality compression, to check if new compression is any better + CGU_Vec2ui Q1CompData = cmpBlock; + // High Quality + CGU_UINT32 i; + + ALIGN_16 CGU_FLOAT Rpt[BLOCK_SIZE_4X4]; + CGU_UINT32 pcIndices = 0; + + m_nRefinementSteps = 0; + + CGU_Vec3f BlkInBGRf_UV[BLOCK_SIZE_4X4]; // Normalized Block Input (0..1) in BGR channel format + // Default inidices & endpoints for Transparent Block + CGU_Vec3ui nEndpoints0 = {0, 0, 0}; // Endpoints are stored BGR as x,y,z + CGU_Vec3ui nEndpoints1 = {0xFF, 0xFF, 0xFF}; // Endpoints are stored BGR as x,y,z + + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + Rpt[i] = 0.0f; + } + + //=============================================================== + // Check if we have more then 2 colors and process Alpha block + CGU_UINT32 dwColors = 0; + CGU_UINT32 dwBlk[BLOCK_SIZE_4X4]; + CGU_UINT32 R, G, B, A; + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + // Do any color conversion prior to processing the block + rgbBlock_normal[i] = isSRGB ? cmp_linearToSrgb(rgbBlockUVf[i]) : rgbBlockUVf[i]; + + R = (CGU_UINT32)(rgbBlock_normal[i].x * 255.0f); + G = (CGU_UINT32)(rgbBlock_normal[i].y * 255.0f); + B = (CGU_UINT32)(rgbBlock_normal[i].z * 255.0f); + + if (dwAlphaThreshold > 0) + A = (CGU_UINT32)BlockA[i]; + else + A = 255; + + // Punch Through Alpha in BC1 Codec (1 bit alpha) + if ((dwAlphaThreshold == 0) || (A >= dwAlphaThreshold)) + { + // copy to local RGB data and have alpha set to 0xFF + dwBlk[dwColors++] = A << 24 | R << 16 | G << 8 | B; + } + } + + if (!dwColors) + { + // All are colors transparent + EndPoints.Color0.x = EndPoints.Color0.y = EndPoints.Color0.z = 0.0f; + EndPoints.Color1.x = EndPoints.Color1.y = EndPoints.Color0.z = 255.0f; + nCmpIndices = 0xFFFFFFFF; + } + else + { + // We have colors to process + nCmpIndices = 0; +// Punch Through Alpha Support ToDo +// CGU_BOOL bHasAlpha = (dwColors != BLOCK_SIZE_4X4); +// bHasAlpha = bHasAlpha && (dwAlphaThreshold > 0); // valid for (dwNumChannels=4); +// if (bHasAlpha) { +// CGU_Vec2ui compBlock = {0xf800f800,0}; +// return compBlock; +// } + +// Here we are computing an unique number of sorted colors. +// For each unique value we compute the number of it appearences. +// qsort((void *)dwBlk, (size_t)dwColors, sizeof(CGU_UINT32), QSortIntCmp); +#ifndef ASPM_GPU + std::sort(dwBlk, dwBlk + 15); +#else + { + CGU_UINT32 j; + CMP_di what[BLOCK_SIZE_4X4]; + + for (i = 0; i < dwColors; i++) + { + what[i].index = i; + what[i].data = dwBlk[i]; + } + + CGU_UINT32 tmp_index; + CGU_UINT32 tmp_data; + + for (i = 1; i < dwColors; i++) + { + for (j = i; j > 0; j--) + { + if (what[j - 1].data > what[j].data) + { + tmp_index = what[j].index; + tmp_data = what[j].data; + what[j].index = what[j - 1].index; + what[j].data = what[j - 1].data; + what[j - 1].index = tmp_index; + what[j - 1].data = tmp_data; + } + } + } + for (i = 0; i < dwColors; i++) + dwBlk[i] = what[i].data; + } +#endif + CGU_UINT32 new_p; + CGU_UINT32 dwBlkU[BLOCK_SIZE_4X4]; + CGU_UINT32 dwUniqueColors = 0; + new_p = dwBlkU[0] = dwBlk[0]; + Rpt[dwUniqueColors] = 1.f; + for (i = 1; i < dwColors; i++) + { + if (new_p != dwBlk[i]) + { + dwUniqueColors++; + new_p = dwBlkU[dwUniqueColors] = dwBlk[i]; + Rpt[dwUniqueColors] = 1.f; + } + else + Rpt[dwUniqueColors] += 1.f; + } + dwUniqueColors++; + + // Simple case of only 2 colors to process + // no need for futher processing as lowest quality methods work best for this case + if (dwUniqueColors <= 2) + { + return Q1CompData; + } + else + { + // switch from int range back to UV floats + for (i = 0; i < dwUniqueColors; i++) + { + R = (dwBlkU[i] >> 16) & 0xff; + G = (dwBlkU[i] >> 8) & 0xff; + B = (dwBlkU[i] >> 0) & 0xff; + BlkInBGRf_UV[i].z = (CGU_FLOAT)R / 255.0f; + BlkInBGRf_UV[i].y = (CGU_FLOAT)G / 255.0f; + BlkInBGRf_UV[i].x = (CGU_FLOAT)B / 255.0f; + } + + CGU_Vec3f channelWeightsBGR; + channelWeightsBGR.x = channelWeights.z; + channelWeightsBGR.y = channelWeights.y; + channelWeightsBGR.z = channelWeights.x; + + EndPoints = CompressRGBBlock_Slow(BlkInBGRf_UV, Rpt, dwUniqueColors, channelWeightsBGR, m_nRefinementSteps); + } + } // colors + + //=================================================================== + // Process Cluster INPUT is constant EndPointsf OUTPUT is pcIndices + //=================================================================== + if (nCmpIndices == 0) + { + R = (CGU_UINT32)(EndPoints.Color0.z); + G = (CGU_UINT32)(EndPoints.Color0.y); + B = (CGU_UINT32)(EndPoints.Color0.x); + CGU_INT32 cluster0 = cmp_constructColor(R, G, B); + + R = (CGU_UINT32)(EndPoints.Color1.z); + G = (CGU_UINT32)(EndPoints.Color1.y); + B = (CGU_UINT32)(EndPoints.Color1.x); + CGU_INT32 cluster1 = cmp_constructColor(R, G, B); + + CGU_Vec3f InpRmp[NUM_ENDPOINTS]; + if ((cluster0 <= cluster1) // valid for 4 channels + // || (cluster0 > cluster1) // valid for 3 channels + ) + { + // inverse endpoints + InpRmp[0] = EndPoints.Color1; + InpRmp[1] = EndPoints.Color0; + } + else + { + InpRmp[0] = EndPoints.Color0; + InpRmp[1] = EndPoints.Color1; + } + + CGU_Vec3f srcblockBGR[BLOCK_SIZE_4X4]; + CGU_FLOAT srcblockA[BLOCK_SIZE_4X4]; + + // Swizzle the source RGB to BGR for processing + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + srcblockBGR[i].z = rgbBlock_normal[i].x * 255.0f; + srcblockBGR[i].y = rgbBlock_normal[i].y * 255.0f; + srcblockBGR[i].x = rgbBlock_normal[i].z * 255.0f; + srcblockA[i] = 0.0f; + if (dwAlphaThreshold > 0) + { + CGU_UINT32 alpha = (CGU_UINT32)BlockA[i]; + if (alpha >= dwAlphaThreshold) + srcblockA[i] = BlockA[i]; + } + } + + // input ramp is on the coarse grid + // make ramp endpoints the way they'll going to be decompressed + CGU_Vec3f InpRmpL[NUM_ENDPOINTS]; + CGU_Vec3f Fctrs = {32.0F, 64.0F, 32.0F}; // 1 << RG,1 << GG,1 << BG + + { + // ConstantRamp = MkWkRmpPts(InpRmpL, InpRmp); + InpRmpL[0] = InpRmp[0] + floor(InpRmp[0] / Fctrs); + InpRmpL[0] = cmp_clampVec3f(InpRmpL[0], 0.0f, 255.0f); + InpRmpL[1] = InpRmp[1] + floor(InpRmp[1] / Fctrs); + InpRmpL[1] = cmp_clampVec3f(InpRmpL[1], 0.0f, 255.0f); + } // MkWkRmpPts + + // build ramp + CGU_Vec3f LerpRmp[4]; + CGU_Vec3f offset = {1.0f, 1.0f, 1.0f}; + { + //BldRmp(Rmp, InpRmpL, dwNumChannels); + // linear interpolate end points to get the ramp + LerpRmp[0] = InpRmpL[0]; + LerpRmp[3] = InpRmpL[1]; + LerpRmp[1] = floor((InpRmpL[0] * 2.0f + LerpRmp[3] + offset) / 3.0f); + LerpRmp[2] = floor((InpRmpL[0] + LerpRmp[3] * 2.0f + offset) / 3.0f); + } // BldRmp + + //========================================================================= + // Clusterize, Compute error and find DXTC indexes for the current cluster + //========================================================================= + { + // Clusterize + CGU_UINT32 alpha; + + // For each colour in the original block assign it + // to the closest cluster and compute the cumulative error + for (i = 0; i < BLOCK_SIZE_4X4; i++) + { + alpha = (CGU_UINT32)srcblockA[i]; + if ((dwAlphaThreshold > 0) && alpha == 0) + { //*((CGU_DWORD *)&_Blk[i][AC]) == 0) + pcIndices |= cmp_set2Bit32(4, i); // dwNumChannels 3 or 4 (default is 4) + } + else + { + CGU_FLOAT shortest = 99999999999.f; + CGU_UINT8 shortestIndex = 0; + + CGU_Vec3f channelWeightsBGR; + channelWeightsBGR.x = channelWeights.z; + channelWeightsBGR.y = channelWeights.y; + channelWeightsBGR.z = channelWeights.x; + + for (CGU_UINT8 rampindex = 0; rampindex < 4; rampindex++) + { + // r is either 1 or 4 + // calculate the distance for each component + CGU_FLOAT distance = + dot(((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR), ((srcblockBGR[i] - LerpRmp[rampindex]) * channelWeightsBGR)); + if (distance < shortest) + { + shortest = distance; + shortestIndex = rampindex; + } + } + + // The total is a sum of (error += shortest) + // We have the index of the best cluster, so assign this in the block + // Reorder indices to match correct DXTC ordering + if (shortestIndex == 3) // dwNumChannels - 1 + shortestIndex = 1; + else if (shortestIndex) + shortestIndex++; + pcIndices |= cmp_set2Bit32(shortestIndex, i); + } + } // BLOCK_SIZE_4X4 + } // Clusterize + } // Process Cluster + + //============================================================== + // Generate Compressed Result from nEndpoints & pcIndices + //============================================================== + R = (CGU_UINT32)(EndPoints.Color0.z); + G = (CGU_UINT32)(EndPoints.Color0.y); + B = (CGU_UINT32)(EndPoints.Color0.x); + c0 = cmp_constructColor(R, G, B); + + R = (CGU_UINT32)(EndPoints.Color1.z); + G = (CGU_UINT32)(EndPoints.Color1.y); + B = (CGU_UINT32)(EndPoints.Color1.x); + c1 = cmp_constructColor(R, G, B); + + // Get Processed indices if not set + if (nCmpIndices == 0) + nCmpIndices = pcIndices; + + if (c0 <= c1) + { + cmpBlock.x = c1 | (c0 << 16); + } + else + cmpBlock.x = c0 | (c1 << 16); + + cmpBlock.y = nCmpIndices; + + // Select best compression + CGU_FLOAT CompErr = CMP_RGBBlockError(rgbBlockUVf, cmpBlock, isSRGB); + if (CompErr > errLQ) + cmpBlock = Q1CompData; + } +#endif + return cmpBlock; +} + +//============================= Alpha: New single header interfaces: supports GPU shader interface ================================================== + +// Compress a BC1 block - Use new code in cmp_bc1.h +static CGU_Vec2ui CompressBlockBC1_UNORM(CGU_Vec3f rgbablockf[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT fquality, CGU_BOOL isSRGB) +{ + CGU_FLOAT BlockA[BLOCK_SIZE_4X4]; // Not used but required + CGU_Vec3f channelWeights = {1.0f, 1.0f, 1.0f}; + + return CompressBlockBC1_RGBA_Internal(rgbablockf, + BlockA, // ToDo support nullptr + channelWeights, + 0, + 1, + fquality, + isSRGB); +} + +// Compress a BC2 block +static CGU_Vec4ui CompressBlockBC2_UNORM(CMP_IN CGU_Vec3f BlockRGB[BLOCK_SIZE_4X4], + CMP_IN CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec2ui compressedBlocks; + CGU_Vec4ui compBlock; + compressedBlocks = cmp_compressExplicitAlphaBlock(BlockA); + compBlock.x = compressedBlocks.x; + compBlock.y = compressedBlocks.y; + + CGU_Vec3f channelWeights = {1.0f, 1.0f, 1.0f}; + compressedBlocks = CompressBlockBC1_RGBA_Internal(BlockRGB, BlockA, channelWeights, 0, 1, fquality, isSRGB); + compBlock.z = compressedBlocks.x; + compBlock.w = compressedBlocks.y; + return compBlock; +} + +// Compress a BC3 block +static CGU_Vec4ui CompressBlockBC3_UNORM(CMP_IN CGU_Vec3f BlockRGB[BLOCK_SIZE_4X4], + CMP_IN CGU_FLOAT BlockA[BLOCK_SIZE_4X4], + CGU_FLOAT fquality, + CGU_BOOL isSRGB) +{ + CGU_Vec4ui compBlock; + CGU_Vec2ui cmpBlock; + + cmpBlock = cmp_compressAlphaBlock(BlockA, fquality, FALSE); + compBlock.x = cmpBlock.x; + compBlock.y = cmpBlock.y; + + CGU_Vec2ui compressedBlocks; + compressedBlocks = CompressBlockBC1_UNORM(BlockRGB, fquality, isSRGB); + compBlock.z = compressedBlocks.x; + compBlock.w = compressedBlocks.y; + return compBlock; +} + +// Compress a BC4 block +static CGU_Vec2ui CompressBlockBC4_UNORM(CMP_IN CGU_FLOAT Block[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(Block, fquality, FALSE); + return cmpBlock; +} + +// Compress a BC4 block +static CGU_Vec2ui CompressBlockBC4_SNORM(CMP_IN CGU_FLOAT Block[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(Block, fquality, TRUE); + return cmpBlock; +} + +// Compress a BC5 block +static CGU_Vec4ui CompressBlockBC5_UNORM(CMP_IN CGU_FLOAT BlockU[BLOCK_SIZE_4X4], CMP_IN CGU_FLOAT BlockV[BLOCK_SIZE_4X4], CGU_FLOAT fquality) +{ + CGU_Vec4ui compressedBlock = {0, 0, 0, 0}; + CGU_Vec2ui cmpBlock; + cmpBlock = cmp_compressAlphaBlock(BlockU, fquality, FALSE); + compressedBlock.x = cmpBlock.x; + compressedBlock.y = cmpBlock.y; + + cmpBlock = cmp_compressAlphaBlock(BlockV, fquality, FALSE); + compressedBlock.z = cmpBlock.x; + compressedBlock.w = cmpBlock.y; + + return compressedBlock; +} + +// Compress a BC6 & BC7 UNORM block ToDo + +#endif diff --git a/WickedEngine/shaders/compressonator/common_def.h b/WickedEngine/shaders/compressonator/common_def.h new file mode 100644 index 000000000..53105955c --- /dev/null +++ b/WickedEngine/shaders/compressonator/common_def.h @@ -0,0 +1,2282 @@ +// ================================================================================================== +// Copyright (c) 2007-2021 Advanced Micro Devices, Inc. All rights reserved. +// Copyright (c) 2004-2006 ATI Technologies Inc. +// Copyright (c) <2014> +// ================================================================================================== +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// +// File Name: Common_Def +// Description: common definitions used for CPU/HPC/GPU +// +// Pull changes: +// Fixed build of cmp_core on Mac. (#164) +// + +#ifndef CMP_COMMON_DEFINITIONS_H +#define CMP_COMMON_DEFINITIONS_H + +//#define USE_CMP_FIDELITY_FX_H + +// Proxy ISPC compiler (Warning! Not all ASPM features will be available : expect build errors for specialized ASPM code! +#ifdef ISPC +#define ASPM +#endif + +// Using OpenCL Compiler +#ifdef __OPENCL_VERSION__ +#define ASPM_OPENCL +#endif + +#ifdef USE_CMP_FIDELITY_FX_H + +// Fidelity FX SDK mapping +#if defined(A_CPU) +#ifndef ASPM_CPU +#define ASPM_CPU +#endif +#endif + +#if defined(A_GPU) +#ifndef ASPM_GPU +#define ASPM_GPU +#endif +#endif + +#if defined(A_GLS) +#ifndef ASPM_GLS +#define ASPM_GLS +#endif +#endif + +#if defined(A_HLSL) +#ifndef ASPM_HLSL +#define ASPM_HLSL +#endif +#endif + +#if defined(A_GCC) +#ifndef ASPM_GCC +#define ASPM_GCC +#endif +#endif + +#endif + +#if (defined(ASPM_HLSL) || defined(ASPM_OPENCL)) +#ifndef ASPM_GPU +#define ASPM_GPU +#endif +#endif + + +//======================================= +// Skip CMP if only using FidelityFX Code +//======================================= +#if !(defined(A_GPU) || defined(A_GLS) || defined(A_HLSL) || defined(A_GCC)) + +// The shaders for UE4 require extension in the form of .ush in place of standard .h +// this directive is used to make the change without users requiring to modify all of the include extensions +// specific to UE4 + +#ifdef ASPM_HLSL_UE4 +#pragma once +#define INC_cmp_math_vec4 "cmp_math_vec4.ush" +#define INC_cmp_math_func "cmp_math_func.ush" +#else +#define INC_cmp_math_vec4 "cmp_math_vec4.h" +#define INC_cmp_math_func "cmp_math_func.h" +#endif + +#if defined(__unix__) || defined(__APPLE__) +#ifndef _LINUX +#define _LINUX +#endif +#ifdef ASPM_GPU +#undef ASPM_GPU +#endif +#include +#include +#include +#include INC_cmp_math_vec4 +#endif + +#ifdef _WIN32 +//#define USE_ASPM_CODE +#include +#endif + +#ifndef CMP_MAX +#define CMP_MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef CMP_MIN +#define CMP_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#ifndef cmp_isnan +#ifdef ASPM_GPU +#define cmp_isnan(x) isnan(x) +#else +#define cmp_isnan(x) std::isnan(x) +#endif +#endif + +#ifdef ASPM_GPU +#define CMP_STATIC_CAST(x, y) (x)(y) +#define CMP_TYPE_CAST(x) (x) +#else +#define CMP_STATIC_CAST(x, y) static_cast(y) +#define CMP_TYPE_CAST(x) (x&) +#endif + +// Sets mapping BC1, BC2 & BC3 to decode Red,Green,Blue and Alpha +// RGBA to channels [0,1,2,3] else BGRA maps to [0,1,2,3] +// BC4 alpha always maps as AAAA to channels [0,1,2,3] +// BC5 decoded (Red&Green) maps R,G,B=0,A=255 to [0,1,2,3] else maps [B=0,G,R,A=255] to [0,1,2,3] +#define CMP_SET_BC13_DECODER_RGBA +#define CMP_FLOAT_MAX 3.402823466e+38F // max value used to detect an Error in processing +#define CMP_FLOAT_MAX_EXP 38 +#define USE_PROCESS_SEPERATE_ALPHA // Enable this to use higher quality code using CompressDualIndexBlock +#define COMPRESSED_BLOCK_SIZE 16 // Size of a compressed block in bytes +#define MAX_DIMENSION_BIG 4 // Max number of channels (RGBA) +#define MAX_SUBSETS 3 // Maximum number of possible subsets +#define MAX_SUBSET_SIZE 16 // Largest possible size for an individual subset +#define BLOCK_SIZE_4X4X4 64 +#define BLOCK_SIZE_4X4 16 +#define BlockX 4 +#define BlockY 4 +//#define USE_BLOCK_LINEAR // Source Data is organized in linear form for each block : Experimental Code not fully developed +//#define USE_DOUBLE // Default is to use float, enable to use double data types only for float definitions + +//--------------------------------------------- +// Predefinitions for GPU and CPU compiled code +//--------------------------------------------- +#ifdef ASPM_HLSL +// ==== Vectors ==== +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; + +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; + +typedef int4 CGU_Vec4i; +typedef int4 CGV_Vec4i; + +typedef int4 CGU_Vec4uc; +typedef int4 CGV_Vec4uc; + +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; + +// ==== Scalar Types ==== to remove from code +typedef int CGU_INT8; +typedef int CGU_INT; +typedef int CGV_INT; +typedef uint CGU_UINT8; +typedef uint CGU_UINT; + +// ==== Scalar Types ==== +typedef int CGU_BOOL; +typedef int CGV_BOOL; +typedef int CGV_INT8; +typedef int CGV_UINT8; +typedef uint CGU_UINT16; +typedef int CGU_INT32; +typedef int CGV_INT32; +typedef uint CGU_UINT32; +typedef uint CGV_UINT32; +typedef float CGV_FLOAT; +typedef float CGU_FLOAT; +typedef min16float CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support + +#define TRUE 1 +#define FALSE 0 +#define CMP_CDECL + +#define BC7_ENCODECLASS +#define CMP_EXPORT +#define INLINE inline +#define uniform +#define varying +#define CMP_GLOBAL +#define CMP_KERNEL +#define CMP_CONSTANT const +#define CMP_STATIC static +#define CMP_REFINOUT +#define CMP_PTRINOUT +#define CMP_INOUT inout +#define CMP_OUT out +#define CMP_IN in +#define CMP_UNUSED(x) (x); +#define CMP_UNROLL [unroll] +#define CMP_SVGROUPINDEX :SV_GroupIndex +#define CMP_SVGROUPID :SV_GroupID +#define CMP_NUMTHREADS(x, y, z) [numthreads(x, y, z)] + +#else + +#define CMP_SVGROUPINDEX +#define CMP_SVGROUPID +#define CMP_NUMTHREADS(x, y, z) + +typedef enum +{ + CGU_CORE_OK = 0, // No errors, call was successfull + CGU_CORE_ERR_UNKOWN, // An unknown error occurred + CGU_CORE_ERR_NEWMEM, // New Memory Allocation Failed + CGU_CORE_ERR_INVALIDPTR, // The pointer value used is invalid or null + CGU_CORE_ERR_RANGERED, // values for Red Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEGREEN, // values for Green Channel is out of range (too high or too low) + CGU_CORE_ERR_RANGEBLUE, // values for Blue Channel is out of range (too high or too low) +} CGU_ERROR_CODES; + +#ifdef ASPM_OPENCL // GPU Based code using OpenCL +// ==== Vectors ==== +typedef float2 CGU_Vec2f; +typedef float2 CGV_Vec2f; +typedef float3 CMP_Vec3f; +typedef float3 CGU_Vec3f; +typedef float3 CGV_Vec3f; +typedef float4 CGU_Vec4f; +typedef float4 CGV_Vec4f; + +typedef uchar3 CGU_Vec3uc; +typedef uchar3 CGV_Vec3uc; + +typedef uchar4 CMP_Vec4uc; +typedef uchar4 CGU_Vec4uc; +typedef uchar4 CGV_Vec4uc; + +typedef int2 CGU_Vec2i; +typedef int2 CGV_Vec2i; +typedef int3 CGU_Vec3i; +typedef int3 CGV_Vec3i; +typedef int4 CGU_Vec4i; +typedef int4 CGV_Vec4i; + + +typedef uint2 CGU_Vec2ui; +typedef uint2 CGV_Vec2ui; +typedef uint3 CGU_Vec3ui; +typedef uint3 CGV_Vec3ui; +typedef uint4 CGU_Vec4ui; +typedef uint4 CGV_Vec4ui; + +#define USE_BC7_SP_ERR_IDX +#define BC7_ENCODECLASS +#define ASPM_PRINT(args) printf args + +#define CMP_EXPORT +#define INLINE +#define uniform +#define varying +#define CMP_GLOBAL __global +#define CMP_KERNEL __kernel +#define CMP_CONSTANT __constant +#define CMP_STATIC +#define CMP_REFINOUT & +#define CMP_PTRINOUT * +#define CMP_INOUT +#define CMP_OUT +#define CMP_IN +#define CMP_UNUSED(x) +#define CMP_UNROLL + +typedef unsigned int CGU_DWORD; //32bits +typedef int CGU_INT; //32bits +typedef bool CGU_BOOL; +typedef unsigned short CGU_SHORT; //16bits +typedef float CGU_FLOAT; +typedef half CGU_MIN16_FLOAT; // FP16 GPU support defaults to 32 bit if no HW support +typedef unsigned int uint32; // need to remove this def + +typedef int CGV_INT; +typedef unsigned int CGU_UINT; +typedef int CGUV_INT; +typedef int CGV_BOOL; + +typedef char CGU_INT8; +typedef unsigned char CGU_UINT8; +typedef short CGU_INT16; +typedef unsigned short CGU_UINT16; +typedef int CGU_INT32; +typedef unsigned int CGU_UINT32; +typedef unsigned long long CGU_UINT64; + +typedef char CGV_INT8; +typedef unsigned char CGV_UINT8; +typedef short CGV_INT16; +typedef unsigned short CGV_UINT16; +typedef int CGV_INT32; +typedef unsigned int CGV_UINT32; +typedef unsigned long CGV_UINT64; + +typedef float CGV_FLOAT; + +#define TRUE 1 +#define FALSE 0 +#define CMP_CDECL + +#else +// CPU & ASPM definitions + +#define CMP_REFINOUT & +#define CMP_PTRINOUT * +#define CMP_INOUT +#define CMP_OUT +#define CMP_IN +#define CMP_UNUSED(x) (void)(x); +#define CMP_UNROLL + +#ifdef ASPM // SPMD ,SIMD CPU code +// using hybrid (CPU/GPU) aspm compiler +#define ASPM_PRINT(args) print args +#define CMP_USE_FOREACH_ASPM +#define __ASPM__ +#define BC7_ENCODECLASS + +#define USE_BC7_SP_ERR_IDX +//#define USE_BC7_RAMP + +#define CMP_EXPORT export +#define TRUE true +#define FALSE false +typedef uniform bool CGU_BOOL; +typedef bool CGV_BOOL; + +typedef unsigned int8 uint8; +typedef unsigned int16 uint16; +typedef unsigned int32 uint32; +typedef unsigned int64 uint64; +typedef uniform float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef uniform float CGU_MIN16_FLOAT; + +typedef uniform uint16 CGU_UINT16; + +typedef uniform uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; + +typedef uniform uint64 CGU_UINT64; + +typedef CGV_UINT8<4> CGV_Vec4uc; +typedef CGU_UINT8<4> CGU_Vec4uc; + +typedef CGU_FLOAT<2> CGU_Vec2f; +typedef CGV_FLOAT<2> CGV_Vec2f; +typedef CGU_FLOAT<3> CGU_Vec3f; +typedef CGV_FLOAT<3> CGV_Vec3f; +typedef CGU_FLOAT<4> CGU_Vec4f; +typedef CGV_FLOAT<4> CGV_Vec4f; + +typedef CGU_UINT32<3> CGU_Vec3ui; +typedef CGV_UINT32<3> CGV_Vec3ui; + +typedef CGU_UINT32<4> CGU_Vec4ui; +typedef CGV_UINT32<4> CGV_Vec4ui; + +#define CMP_CDECL +#else // standard CPU code +#include +#include +#include INC_cmp_math_vec4 + +// using CPU compiler +#define ASPM_PRINT(args) printf args +#define USE_BC7_RAMP +#define USE_BC7_SP_ERR_IDX + +#define CMP_EXPORT +#define BC7_ENCODECLASS BC7_EncodeClass:: +#define TRUE 1 +#define FALSE 0 +#define uniform +#define varying + +typedef char int8; +typedef short int16; +typedef int int32; +typedef long long int64; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef int8 CGV_BOOL; +typedef bool CGU_BOOL; +typedef int16 CGU_WORD; +typedef uint8 CGU_SHORT; +typedef long CGU_LONG; +typedef unsigned long CGU_ULONG; + +typedef uniform float CGU_FLOAT; +typedef varying float CGV_FLOAT; +typedef uniform float CGU_MIN16_FLOAT; + +typedef uniform uint8 CGU_UINT8; +typedef varying uint8 CGV_UINT8; + +typedef uniform uint16 CGU_UINT16; + +typedef CMP_Vec3ui CGU_Vec3ui; +typedef CMP_Vec3ui CGV_Vec3ui; + +typedef CMP_Vec2f CGU_Vec2f; +typedef CMP_Vec3f CGU_Vec3f; + +typedef CMP_Vec4uc CGU_Vec4uc; +typedef CMP_Vec4uc CGV_Vec4uc; +typedef CMP_Vec4i CGU_Vec4i; +typedef CMP_Vec4ui CGU_Vec4ui; +typedef CMP_Vec4ui CGV_Vec4ui; +typedef CMP_Vec4f CGU_Vec4f; +typedef CMP_Vec4f CGV_Vec4f; + +#if defined(WIN32) || defined(_WIN64) +#define CMP_CDECL __cdecl +#else +#define CMP_CDECL +#endif +#endif + +// Common CPU & ASPM definitions +#define CMP_ASSERT(arg) + +#define CMP_GLOBAL + +#define CMP_KERNEL +#define __local const +#define __constant const +#define CMP_CONSTANT const +#define INLINE inline +#define CMP_STATIC static + +typedef uniform int32 CGU_DWORD; +typedef uniform uint8 CGU_UBYTE; +typedef uniform int CGU_INT; +typedef uniform int8 CGU_INT8; + +typedef uniform int16 CGU_INT16; +typedef uniform uint16 CGU_UINT16; +typedef uniform int32 CGU_INT32; +typedef uniform uint32 CGU_UINT32; +typedef uniform uint64 CGU_UINT64; + +typedef int CGV_INT; +typedef int8 CGV_INT8; +typedef int16 CGV_INT16; +typedef int32 CGV_INT32; +typedef uint16 CGV_UINT16; +typedef uint32 CGV_UINT32; +typedef uint64 CGV_UINT64; + +#endif // else ASPM_GPU + +#define CMP_UNIFORM uniform +#define CMP_VARYING varying + +typedef struct +{ + CGU_UINT32 m_src_width; + CGU_UINT32 m_src_height; + CGU_UINT32 m_width_in_blocks; + CGU_UINT32 m_height_in_blocks; + CGU_FLOAT m_fquality; +} Source_Info; + +typedef unsigned char* CGU_PTR; + +// Ref Compute_CPU_HPC +struct texture_surface +{ + CGU_PTR ptr; + CGU_INT width, height, stride; + CGU_INT channels; +}; +#endif // else ASPM_HLSL + +#endif // USE CMP defines + +//======================================= +// using FidelityFX Code +//======================================= +#ifdef USE_CMP_FIDELITY_FX_H +// ================================================================================================== +// [A] SHADER PORTABILITY 1.20190530 +// ================================================================================================== +// ABOUT +// ===== +// Common central point for high-level shading language and C portability for various shader headers. +//--------------------------------------------------------------------------------------------------- +// DEFINES TYPES FOR CMP AND FIDELITY FX +// Both ASPM_ and A_ types are interchanable in code, the table below show compatibility between types +// ================================================================================================== +// Compressonator Fidelity FX Comments +// ASPM_CPU A_CPU Include the CPU related code. +// ASPM_GPU A_GPU Include the GPU related code. +// ASPM_GLSL A_GLS Using GLSL. +// ASMP_HLSL A_HLS Using HLSL. +// - A_GCC Using a GCC compatible compiler (else assume MSVC compatible compiler by default). +// ======= +// CGU_UINT8 A_BYTE Support 8-bit integer. +// CGU_HALF A_HALF Support 16-bit integer and floating point. +// A_LONG Support 64-bit integer. (GLSL & CPU only) +// A_DUBL Support 64-bit floating point. +// ======= +// A_WAVE Support wave-wide operations. +//-------------------------------------------------------------------------------------------------- +// To get #include "common_def.h" working in GLSL use '#extension GL_GOOGLE_include_directive:require' +//--------------------------------------------------------------------------------------------------- +// SIMPLIFIED TYPE SYSTEM FOR FX CODE +// ================================================================================================== +// - All ints will be unsigned with exception of when signed is required. +// - Type naming simplified and shortened "A<#components>", +// - H = 16-bit float (half) +// - F = 32-bit float (float) +// - D = 64-bit float (double) +// - P = 1-bit integer (predicate, not using bool because 'B' is used for byte) +// - B = 8-bit integer (byte) +// - W = 16-bit integer (word) +// - U = 32-bit integer (unsigned) +// - L = 64-bit integer (long) +// - Using "AS<#components>" for signed when required. +//--------------------------------------------------------------------------------------------------- +// TODO +// ==== +// - Make sure 'ALerp*(a,b,m)' does 'b*m+(-a*m+a)' (2 ops). +// - Add subgroup ops. +//------------------------------------------------------------------------------------------------------------------------------ +// CHANGE LOG +// ========== +// 20210518 - Merged CMP Common_def.h with ffx_a.h +//============================================================================================================================== + + +#define A_2PI 6.28318530718 // 2xPI + +//============================================================================================================================== +// CPU +//============================================================================================================================== +// This provides a minimum subset of functionality compared to the GPU parts. +//============================================================================================================================== +#if defined(A_CPU) || !(defined(ASPM_GPU) || defined(ASPM_HLSL) || defined(ASPM_OPENCL)) + +#include "stdint.h" + +// Supporting user defined overrides. +#ifndef A_RESTRICT +#define A_RESTRICT __restrict // CMP_RESTRICT +#endif +//------------------------------------------------------------------------------------------------------------------------------ +#ifndef A_STATIC +#define A_STATIC static // CMP_STATIC +#endif + //------------------------------------------------------------------------------------------------------------------------------ + // Same types across CPU and GPU. + // Predicate uses 32-bit integer (C friendly bool). + typedef uint32_t AP1; // CGU_UINT32 + typedef float AF1; // CGU_FLOAT + typedef double AD1; // CGU_DOUBLE + typedef uint8_t AB1; // CGU_UINT8 + typedef uint16_t AW1; // CGU_UINT16 + typedef uint32_t AU1; // CGU_UINT32 + typedef uint64_t AL1; // CGU_UINT64 + typedef int8_t ASB1; // CGU_INT8 + typedef int16_t ASW1; // CGU_INT16 + typedef int32_t ASU1; // CGU_INT32 + typedef int64_t ASL1; // CGU_INT64 + +//------------------------------------------------------------------------------------------------------------------------------ + #define AD1_(a) ((AD1)(a)) + #define AF1_(a) ((AF1)(a)) + #define AL1_(a) ((AL1)(a)) + #define AU1_(a) ((AU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1_(a) ((ASL1)(a)) + #define ASU1_(a) ((ASU1)(a)) +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AU1 AU1_AF1(AF1 a){union{AF1 f;AU1 u;}bits;bits.f=a;return bits.u;} +//------------------------------------------------------------------------------------------------------------------------------ + #define A_TRUE 1 + #define A_FALSE 0 +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// +// CPU/GPU PORTING +// +//------------------------------------------------------------------------------------------------------------------------------ +// Hackary to get CPU and GPU to share all setup code, without duplicate code paths. +// Unfortunately this is the level of "ugly" that is required since the languages are very different. +// This uses a lower-case prefix for special vector constructs. +// - In C restrict pointers are used. +// - In the shading language, in/inout/out arguments are used. +// This depends on the ability to access a vector value in both languages via array syntax (aka color[2]). +//============================================================================================================================== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD1 *A_RESTRICT + #define retAD3 AD1 *A_RESTRICT + #define retAD4 AD1 *A_RESTRICT + #define retAF2 AF1 *A_RESTRICT + #define retAF3 AF1 *A_RESTRICT + #define retAF4 AF1 *A_RESTRICT + #define retAL2 AL1 *A_RESTRICT + #define retAL3 AL1 *A_RESTRICT + #define retAL4 AL1 *A_RESTRICT + #define retAU2 AU1 *A_RESTRICT + #define retAU3 AU1 *A_RESTRICT + #define retAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 AD1 *A_RESTRICT + #define inAD3 AD1 *A_RESTRICT + #define inAD4 AD1 *A_RESTRICT + #define inAF2 AF1 *A_RESTRICT + #define inAF3 AF1 *A_RESTRICT + #define inAF4 AF1 *A_RESTRICT + #define inAL2 AL1 *A_RESTRICT + #define inAL3 AL1 *A_RESTRICT + #define inAL4 AL1 *A_RESTRICT + #define inAU2 AU1 *A_RESTRICT + #define inAU3 AU1 *A_RESTRICT + #define inAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 AD1 *A_RESTRICT + #define inoutAD3 AD1 *A_RESTRICT + #define inoutAD4 AD1 *A_RESTRICT + #define inoutAF2 AF1 *A_RESTRICT + #define inoutAF3 AF1 *A_RESTRICT + #define inoutAF4 AF1 *A_RESTRICT + #define inoutAL2 AL1 *A_RESTRICT + #define inoutAL3 AL1 *A_RESTRICT + #define inoutAL4 AL1 *A_RESTRICT + #define inoutAU2 AU1 *A_RESTRICT + #define inoutAU3 AU1 *A_RESTRICT + #define inoutAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 AD1 *A_RESTRICT + #define outAD3 AD1 *A_RESTRICT + #define outAD4 AD1 *A_RESTRICT + #define outAF2 AF1 *A_RESTRICT + #define outAF3 AF1 *A_RESTRICT + #define outAF4 AF1 *A_RESTRICT + #define outAL2 AL1 *A_RESTRICT + #define outAL3 AL1 *A_RESTRICT + #define outAL4 AL1 *A_RESTRICT + #define outAU2 AU1 *A_RESTRICT + #define outAU3 AU1 *A_RESTRICT + #define outAU4 AU1 *A_RESTRICT +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD1 x[2] + #define varAD3(x) AD1 x[3] + #define varAD4(x) AD1 x[4] + #define varAF2(x) AF1 x[2] + #define varAF3(x) AF1 x[3] + #define varAF4(x) AF1 x[4] + #define varAL2(x) AL1 x[2] + #define varAL3(x) AL1 x[3] + #define varAL4(x) AL1 x[4] + #define varAU2(x) AU1 x[2] + #define varAU3(x) AU1 x[3] + #define varAU4(x) AU1 x[4] +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) {x,y} + #define initAD3(x,y,z) {x,y,z} + #define initAD4(x,y,z,w) {x,y,z,w} + #define initAF2(x,y) {x,y} + #define initAF3(x,y,z) {x,y,z} + #define initAF4(x,y,z,w) {x,y,z,w} + #define initAL2(x,y) {x,y} + #define initAL3(x,y,z) {x,y,z} + #define initAL4(x,y,z,w) {x,y,z,w} + #define initAU2(x,y) {x,y} + #define initAU3(x,y,z) {x,y,z} + #define initAU4(x,y,z,w) {x,y,z,w} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Replace transcendentals with manual versions. +//============================================================================================================================== + #ifdef A_GCC + A_STATIC AD1 AAbsD1(AD1 a){return __builtin_fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return __builtin_fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(__builtin_abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(__builtin_labs(ASL1_(a)));} + #else + A_STATIC AD1 AAbsD1(AD1 a){return fabs(a);} + A_STATIC AF1 AAbsF1(AF1 a){return fabsf(a);} + A_STATIC AU1 AAbsSU1(AU1 a){return AU1_(abs(ASU1_(a)));} + A_STATIC AL1 AAbsSL1(AL1 a){return AL1_(llabs(ASL1_(a)));} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ACosD1(AD1 a){return __builtin_cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return __builtin_cosf(a);} + #else + A_STATIC AD1 ACosD1(AD1 a){return cos(a);} + A_STATIC AF1 ACosF1(AF1 a){return cosf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ADotD2(inAD2 a,inAD2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AD1 ADotD3(inAD3 a,inAD3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AD1 ADotD4(inAD4 a,inAD4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} + A_STATIC AF1 ADotF2(inAF2 a,inAF2 b){return a[0]*b[0]+a[1]*b[1];} + A_STATIC AF1 ADotF3(inAF3 a,inAF3 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + A_STATIC AF1 ADotF4(inAF4 a,inAF4 b){return a[0]*b[0]+a[1]*b[1]+a[2]*b[2]+a[3]*b[3];} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AExp2D1(AD1 a){return __builtin_exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return __builtin_exp2f(a);} + #else + A_STATIC AD1 AExp2D1(AD1 a){return exp2(a);} + A_STATIC AF1 AExp2F1(AF1 a){return exp2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 AFloorD1(AD1 a){return __builtin_floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return __builtin_floorf(a);} + #else + A_STATIC AD1 AFloorD1(AD1 a){return floor(a);} + A_STATIC AF1 AFloorF1(AF1 a){return floorf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ALerpD1(AD1 a,AD1 b,AD1 c){return b*c+(-a*c+a);} + A_STATIC AF1 ALerpF1(AF1 a,AF1 b,AF1 c){return b*c+(-a*c+a);} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ALog2D1(AD1 a){return __builtin_log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return __builtin_log2f(a);} + #else + A_STATIC AD1 ALog2D1(AD1 a){return log2(a);} + A_STATIC AF1 ALog2F1(AF1 a){return log2f(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMaxD1(AD1 a,AD1 b){return a>b?a:b;} + A_STATIC AF1 AMaxF1(AF1 a,AF1 b){return a>b?a:b;} + A_STATIC AL1 AMaxL1(AL1 a,AL1 b){return a>b?a:b;} + A_STATIC AU1 AMaxU1(AU1 a,AU1 b){return a>b?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + // These follow the convention that A integer types don't have signage, until they are operated on. + A_STATIC AL1 AMaxSL1(AL1 a,AL1 b){return (ASL1_(a)>ASL1_(b))?a:b;} + A_STATIC AU1 AMaxSU1(AU1 a,AU1 b){return (ASU1_(a)>ASU1_(b))?a:b;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 AMinD1(AD1 a,AD1 b){return a>ASL1_(b));} + A_STATIC AU1 AShrSU1(AU1 a,AU1 b){return AU1_(ASU1_(a)>>ASU1_(b));} +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASinD1(AD1 a){return __builtin_sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return __builtin_sinf(a);} + #else + A_STATIC AD1 ASinD1(AD1 a){return sin(a);} + A_STATIC AF1 ASinF1(AF1 a){return sinf(a);} + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_GCC + A_STATIC AD1 ASqrtD1(AD1 a){return __builtin_sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return __builtin_sqrtf(a);} + #else + A_STATIC AD1 ASqrtD1(AD1 a){return sqrt(a);} + A_STATIC AF1 ASqrtF1(AF1 a){return sqrtf(a);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + A_STATIC AD1 AFractD1(AD1 a){return a-AFloorD1(a);} + A_STATIC AF1 AFractF1(AF1 a){return a-AFloorF1(a);} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 APowD1(AD1 a,AD1 b){return AExp2D1(b*ALog2D1(a));} + A_STATIC AF1 APowF1(AF1 a,AF1 b){return AExp2F1(b*ALog2F1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ARsqD1(AD1 a){return ARcpD1(ASqrtD1(a));} + A_STATIC AF1 ARsqF1(AF1 a){return ARcpF1(ASqrtF1(a));} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC AD1 ASatD1(AD1 a){return AMinD1(1.0,AMaxD1(0.0,a));} + A_STATIC AF1 ASatF1(AF1 a){return AMinF1(1.0f,AMaxF1(0.0f,a));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + A_STATIC retAD2 opAAbsD2(outAD2 d,inAD2 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);return d;} + A_STATIC retAD3 opAAbsD3(outAD3 d,inAD3 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);return d;} + A_STATIC retAD4 opAAbsD4(outAD4 d,inAD4 a){d[0]=AAbsD1(a[0]);d[1]=AAbsD1(a[1]);d[2]=AAbsD1(a[2]);d[3]=AAbsD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAbsF2(outAF2 d,inAF2 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);return d;} + A_STATIC retAF3 opAAbsF3(outAF3 d,inAF3 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);return d;} + A_STATIC retAF4 opAAbsF4(outAF4 d,inAF4 a){d[0]=AAbsF1(a[0]);d[1]=AAbsF1(a[1]);d[2]=AAbsF1(a[2]);d[3]=AAbsF1(a[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];return d;} + A_STATIC retAF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];return d;} + A_STATIC retAF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]+b[0];d[1]=a[1]+b[1];d[2]=a[2]+b[2];d[3]=a[3]+b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opACpyD2(outAD2 d,inAD2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAD3 opACpyD3(outAD3 d,inAD3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAD4 opACpyD4(outAD4 d,inAD4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opACpyF2(outAF2 d,inAF2 a){d[0]=a[0];d[1]=a[1];return d;} + A_STATIC retAF3 opACpyF3(outAF3 d,inAF3 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];return d;} + A_STATIC retAF4 opACpyF4(outAF4 d,inAF4 a){d[0]=a[0];d[1]=a[1];d[2]=a[2];d[3]=a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);return d;} + A_STATIC retAD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);return d;} + A_STATIC retAD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d[0]=ALerpD1(a[0],b[0],c[0]);d[1]=ALerpD1(a[1],b[1],c[1]);d[2]=ALerpD1(a[2],b[2],c[2]);d[3]=ALerpD1(a[3],b[3],c[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);return d;} + A_STATIC retAF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);return d;} + A_STATIC retAF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d[0]=ALerpF1(a[0],b[0],c[0]);d[1]=ALerpF1(a[1],b[1],c[1]);d[2]=ALerpF1(a[2],b[2],c[2]);d[3]=ALerpF1(a[3],b[3],c[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);return d;} + A_STATIC retAD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);return d;} + A_STATIC retAD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d[0]=ALerpD1(a[0],b[0],c);d[1]=ALerpD1(a[1],b[1],c);d[2]=ALerpD1(a[2],b[2],c);d[3]=ALerpD1(a[3],b[3],c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);return d;} + A_STATIC retAF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);return d;} + A_STATIC retAF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d[0]=ALerpF1(a[0],b[0],c);d[1]=ALerpF1(a[1],b[1],c);d[2]=ALerpF1(a[2],b[2],c);d[3]=ALerpF1(a[3],b[3],c);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMaxD1(a[0],b[0]);d[1]=AMaxD1(a[1],b[1]);d[2]=AMaxD1(a[2],b[2]);d[3]=AMaxD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMaxF1(a[0],b[0]);d[1]=AMaxF1(a[1],b[1]);d[2]=AMaxF1(a[2],b[2]);d[3]=AMaxF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);return d;} + A_STATIC retAD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);return d;} + A_STATIC retAD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d[0]=AMinD1(a[0],b[0]);d[1]=AMinD1(a[1],b[1]);d[2]=AMinD1(a[2],b[2]);d[3]=AMinD1(a[3],b[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);return d;} + A_STATIC retAF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);return d;} + A_STATIC retAF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d[0]=AMinF1(a[0],b[0]);d[1]=AMinF1(a[1],b[1]);d[2]=AMinF1(a[2],b[2]);d[3]=AMinF1(a[3],b[3]);return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];return d;} + A_STATIC retAF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];return d;} + A_STATIC retAF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d[0]=a[0]*b[0];d[1]=a[1]*b[1];d[2]=a[2]*b[2];d[3]=a[3]*b[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;return d;} + A_STATIC retAF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;return d;} + A_STATIC retAF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d[0]=a[0]*b;d[1]=a[1]*b;d[2]=a[2]*b;d[3]=a[3]*b;return d;} +//============================================================================================================================== + A_STATIC retAD2 opANegD2(outAD2 d,inAD2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAD3 opANegD3(outAD3 d,inAD3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAD4 opANegD4(outAD4 d,inAD4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opANegF2(outAF2 d,inAF2 a){d[0]=-a[0];d[1]=-a[1];return d;} + A_STATIC retAF3 opANegF3(outAF3 d,inAF3 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];return d;} + A_STATIC retAF4 opANegF4(outAF4 d,inAF4 a){d[0]=-a[0];d[1]=-a[1];d[2]=-a[2];d[3]=-a[3];return d;} +//============================================================================================================================== + A_STATIC retAD2 opARcpD2(outAD2 d,inAD2 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);return d;} + A_STATIC retAD3 opARcpD3(outAD3 d,inAD3 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);return d;} + A_STATIC retAD4 opARcpD4(outAD4 d,inAD4 a){d[0]=ARcpD1(a[0]);d[1]=ARcpD1(a[1]);d[2]=ARcpD1(a[2]);d[3]=ARcpD1(a[3]);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + A_STATIC retAF2 opARcpF2(outAF2 d,inAF2 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);return d;} + A_STATIC retAF3 opARcpF3(outAF3 d,inAF3 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);return d;} + A_STATIC retAF4 opARcpF4(outAF4 d,inAF4 a){d[0]=ARcpF1(a[0]);d[1]=ARcpF1(a[1]);d[2]=ARcpF1(a[2]);d[3]=ARcpF1(a[3]);return d;} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF FLOAT PACKING +//============================================================================================================================== + // Convert float to half (in lower 16-bits of output). + // Same fast technique as documented here: ftp://ftp.fox-toolkit.org/pub/fasthalffloatconversion.pdf + // Supports denormals. + // Conversion rules are to make computations possibly "safer" on the GPU, + // -INF & -NaN -> -65504 + // +INF & +NaN -> +65504 + A_STATIC AU1 AU1_AH1_AF1(AF1 f){ + static AW1 base[512]={ + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, + 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0001,0x0002,0x0004,0x0008,0x0010,0x0020,0x0040,0x0080,0x0100, + 0x0200,0x0400,0x0800,0x0c00,0x1000,0x1400,0x1800,0x1c00,0x2000,0x2400,0x2800,0x2c00,0x3000,0x3400,0x3800,0x3c00, + 0x4000,0x4400,0x4800,0x4c00,0x5000,0x5400,0x5800,0x5c00,0x6000,0x6400,0x6800,0x6c00,0x7000,0x7400,0x7800,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff,0x7bff, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000, + 0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8000,0x8001,0x8002,0x8004,0x8008,0x8010,0x8020,0x8040,0x8080,0x8100, + 0x8200,0x8400,0x8800,0x8c00,0x9000,0x9400,0x9800,0x9c00,0xa000,0xa400,0xa800,0xac00,0xb000,0xb400,0xb800,0xbc00, + 0xc000,0xc400,0xc800,0xcc00,0xd000,0xd400,0xd800,0xdc00,0xe000,0xe400,0xe800,0xec00,0xf000,0xf400,0xf800,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff, + 0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff,0xfbff}; + static AB1 shift[512]={ + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x17,0x16,0x15,0x14,0x13,0x12,0x11,0x10,0x0f, + 0x0e,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d, + 0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x0d,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18, + 0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18}; + union{AF1 f;AU1 u;}bits;bits.f=f;AU1 u=bits.u;AU1 i=u>>23;return (AU1)(base[i])+((u&0x7fffff)>>shift[i]);} +//------------------------------------------------------------------------------------------------------------------------------ + // Used to output packed constant. + A_STATIC AU1 AU1_AH2_AF2(inAF2 a){return AU1_AH1_AF1(a[0])+(AU1_AH1_AF1(a[1])<<16);} +#endif + +//============================================================================================================================== +// GLSL +//============================================================================================================================== +#if (defined(A_GLSL) && defined(A_GPU)) || (defined(ASPM_GLSL) && defined(ASPM_GPU)) + #ifndef A_SKIP_EXT + #ifdef A_HALF + #extension GL_EXT_shader_16bit_storage:require + #extension GL_EXT_shader_explicit_arithmetic_types:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_LONG + #extension GL_ARB_gpu_shader_int64:require + // TODO: Fixme to more portable extension!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #extension GL_NV_shader_atomic_int64:require + #endif +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_WAVE + #extension GL_KHR_shader_subgroup_arithmetic:require + #extension GL_KHR_shader_subgroup_ballot:require + #extension GL_KHR_shader_subgroup_quad:require + #extension GL_KHR_shader_subgroup_shuffle:require + #endif + #endif +//============================================================================================================================== + #define AP1 bool + #define AP2 bvec2 + #define AP3 bvec3 + #define AP4 bvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 vec2 + #define AF3 vec3 + #define AF4 vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uvec2 + #define AU3 uvec3 + #define AU4 uvec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 ivec2 + #define ASU3 ivec3 + #define ASU4 ivec4 +//============================================================================================================================== + #define AF1_AU1(x) uintBitsToFloat(AU1(x)) + #define AF2_AU2(x) uintBitsToFloat(AU2(x)) + #define AF3_AU3(x) uintBitsToFloat(AU3(x)) + #define AF4_AU4(x) uintBitsToFloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) floatBitsToUint(AF1(x)) + #define AU2_AF2(x) floatBitsToUint(AF2(x)) + #define AU3_AF3(x) floatBitsToUint(AF3(x)) + #define AU4_AF4(x) floatBitsToUint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2_AF2 packHalf2x16 + #define AU1_AW2Unorm_AF2 packUnorm2x16 + #define AU1_AB4Unorm_AF4 packUnorm4x8 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF2_AH2_AU1 unpackHalf2x16 + #define AF2_AW2Unorm_AU1 unpackUnorm2x16 + #define AF4_AB4Unorm_AU1 unpackUnorm4x8 +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){return bitfieldExtract(src,ASU1(off),ASU1(bits));} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + // Proxy for V_BFI_B32 where the 'mask' is set as 'bits', 'mask=(1<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #define AB1 uint8_t + #define AB2 u8vec2 + #define AB3 u8vec3 + #define AB4 u8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASB1 int8_t + #define ASB2 i8vec2 + #define ASB3 i8vec3 + #define ASB4 i8vec4 +//------------------------------------------------------------------------------------------------------------------------------ + AB1 AB1_x(AB1 a){return AB1(a);} + AB2 AB2_x(AB1 a){return AB2(a,a);} + AB3 AB3_x(AB1 a){return AB3(a,a,a);} + AB4 AB4_x(AB1 a){return AB4(a,a,a,a);} + #define AB1_(a) AB1_x(AB1(a)) + #define AB2_(a) AB2_x(AB1(a)) + #define AB3_(a) AB3_x(AB1(a)) + #define AB4_(a) AB4_x(AB1(a)) + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 float16_t + #define AH2 f16vec2 + #define AH3 f16vec3 + #define AH4 f16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 uint16_t + #define AW2 u16vec2 + #define AW3 u16vec3 + #define AW4 u16vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 int16_t + #define ASW2 i16vec2 + #define ASW3 i16vec3 + #define ASW4 i16vec4 +//============================================================================================================================== + #define AH2_AU1(x) unpackFloat2x16(AU1(x)) + AH4 AH4_AU2_x(AU2 x){return AH4(unpackFloat2x16(x.x),unpackFloat2x16(x.y));} + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) unpackUint2x16(AU1(x)) + #define AW4_AU2(x) unpackUint4x16(pack64(AU2(x))) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AH2(x) packFloat2x16(AH2(x)) + AU2 AU2_AH4_x(AH4 x){return AU2(packFloat2x16(x.xy),packFloat2x16(x.zw));} + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) packUint2x16(AW2(x)) + #define AU2_AW4(x) unpack32(packUint4x16(AW4(x))) +//============================================================================================================================== + #define AW1_AH1(x) halfBitsToUint16(AH1(x)) + #define AW2_AH2(x) halfBitsToUint16(AH2(x)) + #define AW3_AH3(x) halfBitsToUint16(AH3(x)) + #define AW4_AH4(x) halfBitsToUint16(AH4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AH1_AW1(x) uint16BitsToHalf(AW1(x)) + #define AH2_AW2(x) uint16BitsToHalf(AW2(x)) + #define AH3_AW3(x) uint16BitsToHalf(AW3(x)) + #define AH4_AW4(x) uint16BitsToHalf(AW4(x)) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AFractH1(AH1 x){return fract(x);} + AH2 AFractH2(AH2 x){return fract(x);} + AH3 AFractH3(AH3 x){return fract(x);} + AH4 AFractH4(AH4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return mix(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return mix(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return mix(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of max3. + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + // No packed version of min3. + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return AH1_(1.0)/x;} + AH2 ARcpH2(AH2 x){return AH2_(1.0)/x;} + AH3 ARcpH3(AH3 x){return AH3_(1.0)/x;} + AH4 ARcpH4(AH4 x){return AH4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return AH1_(1.0)/sqrt(x);} + AH2 ARsqH2(AH2 x){return AH2_(1.0)/sqrt(x);} + AH3 ARsqH3(AH3 x){return AH3_(1.0)/sqrt(x);} + AH4 ARsqH4(AH4 x){return AH4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return clamp(x,AH1_(0.0),AH1_(1.0));} + AH2 ASatH2(AH2 x){return clamp(x,AH2_(0.0),AH2_(1.0));} + AH3 ASatH3(AH3 x){return clamp(x,AH3_(0.0),AH3_(1.0));} + AH4 ASatH4(AH4 x){return clamp(x,AH4_(0.0),AH4_(1.0));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 dvec2 + #define AD3 dvec3 + #define AD4 dvec4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 x){return fract(x);} + AD2 AFractD2(AD2 x){return fract(x);} + AD3 AFractD3(AD3 x){return fract(x);} + AD4 AFractD4(AD4 x){return fract(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return mix(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return mix(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return mix(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return mix(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return AD1_(1.0)/x;} + AD2 ARcpD2(AD2 x){return AD2_(1.0)/x;} + AD3 ARcpD3(AD3 x){return AD3_(1.0)/x;} + AD4 ARcpD4(AD4 x){return AD4_(1.0)/x;} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return AD1_(1.0)/sqrt(x);} + AD2 ARsqD2(AD2 x){return AD2_(1.0)/sqrt(x);} + AD3 ARsqD3(AD3 x){return AD3_(1.0)/sqrt(x);} + AD4 ARsqD4(AD4 x){return AD4_(1.0)/sqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return clamp(x,AD1_(0.0),AD1_(1.0));} + AD2 ASatD2(AD2 x){return clamp(x,AD2_(0.0),AD2_(1.0));} + AD3 ASatD3(AD3 x){return clamp(x,AD3_(0.0),AD3_(1.0));} + AD4 ASatD4(AD4 x){return clamp(x,AD4_(0.0),AD4_(1.0));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// GLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #define AL1 uint64_t + #define AL2 u64vec2 + #define AL3 u64vec3 + #define AL4 u64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASL1 int64_t + #define ASL2 i64vec2 + #define ASL3 i64vec3 + #define ASL4 i64vec4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AL1_AU2(x) packUint2x32(AU2(x)) + #define AU2_AL1(x) unpackUint2x32(AL1(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AL1_x(AL1 a){return AL1(a);} + AL2 AL2_x(AL1 a){return AL2(a,a);} + AL3 AL3_x(AL1 a){return AL3(a,a,a);} + AL4 AL4_x(AL1 a){return AL4(a,a,a,a);} + #define AL1_(a) AL1_x(AL1(a)) + #define AL2_(a) AL2_x(AL1(a)) + #define AL3_(a) AL3_x(AL1(a)) + #define AL4_(a) AL4_x(AL1(a)) +//============================================================================================================================== + AL1 AAbsSL1(AL1 a){return AL1(abs(ASL1(a)));} + AL2 AAbsSL2(AL2 a){return AL2(abs(ASL2(a)));} + AL3 AAbsSL3(AL3 a){return AL3(abs(ASL3(a)));} + AL4 AAbsSL4(AL4 a){return AL4(abs(ASL4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMaxSL1(AL1 a,AL1 b){return AL1(max(ASU1(a),ASU1(b)));} + AL2 AMaxSL2(AL2 a,AL2 b){return AL2(max(ASU2(a),ASU2(b)));} + AL3 AMaxSL3(AL3 a,AL3 b){return AL3(max(ASU3(a),ASU3(b)));} + AL4 AMaxSL4(AL4 a,AL4 b){return AL4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AL1 AMinSL1(AL1 a,AL1 b){return AL1(min(ASU1(a),ASU1(b)));} + AL2 AMinSL2(AL2 a,AL2 b){return AL2(min(ASU2(a),ASU2(b)));} + AL3 AMinSL3(AL3 a,AL3 b){return AL3(min(ASU3(a),ASU3(b)));} + AL4 AMinSL4(AL4 a,AL4 b){return AL4(min(ASU4(a),ASU4(b)));} + #endif +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// WAVE OPERATIONS +//============================================================================================================================== + #ifdef A_WAVE + AF1 AWaveAdd(AF1 v){return subgroupAdd(v);} + AF2 AWaveAdd(AF2 v){return subgroupAdd(v);} + AF3 AWaveAdd(AF3 v){return subgroupAdd(v);} + AF4 AWaveAdd(AF4 v){return subgroupAdd(v);} + #endif +//============================================================================================================================== +#endif + +//============================================================================================================================== +// HLSL +//============================================================================================================================== +#if (defined(A_HLSL) && defined(A_GPU)) || (defined(ASPM_HLSL) && defined(ASPM_GPU)) + #define AP1 bool + #define AP2 bool2 + #define AP3 bool3 + #define AP4 bool4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AF1 float + #define AF2 float2 + #define AF3 float3 + #define AF4 float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1 uint + #define AU2 uint2 + #define AU3 uint3 + #define AU4 uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASU1 int + #define ASU2 int2 + #define ASU3 int3 + #define ASU4 int4 +//============================================================================================================================== + #define AF1_AU1(x) asfloat(AU1(x)) + #define AF2_AU2(x) asfloat(AU2(x)) + #define AF3_AU3(x) asfloat(AU3(x)) + #define AF4_AU4(x) asfloat(AU4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AU1_AF1(x) asuint(AF1(x)) + #define AU2_AF2(x) asuint(AF2(x)) + #define AU3_AF3(x) asuint(AF3(x)) + #define AU4_AF4(x) asuint(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_AF2_x(AF2 a){return f32tof16(a.x)|(f32tof16(a.y)<<16);} + #define AU1_AH2_AF2(a) AU1_AH2_AF2_x(AF2(a)) + #define AU1_AB4Unorm_AF4(x) D3DCOLORtoUBYTE4(AF4(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AF2 AF2_AH2_AU1_x(AU1 x){return AF2(f16tof32(x&0xFFFF),f16tof32(x>>16));} + #define AF2_AH2_AU1(x) AF2_AH2_AU1_x(AU1(x)) +//============================================================================================================================== + AF1 AF1_x(AF1 a){return AF1(a);} + AF2 AF2_x(AF1 a){return AF2(a,a);} + AF3 AF3_x(AF1 a){return AF3(a,a,a);} + AF4 AF4_x(AF1 a){return AF4(a,a,a,a);} + #define AF1_(a) AF1_x(AF1(a)) + #define AF2_(a) AF2_x(AF1(a)) + #define AF3_(a) AF3_x(AF1(a)) + #define AF4_(a) AF4_x(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_x(AU1 a){return AU1(a);} + AU2 AU2_x(AU1 a){return AU2(a,a);} + AU3 AU3_x(AU1 a){return AU3(a,a,a);} + AU4 AU4_x(AU1 a){return AU4(a,a,a,a);} + #define AU1_(a) AU1_x(AU1(a)) + #define AU2_(a) AU2_x(AU1(a)) + #define AU3_(a) AU3_x(AU1(a)) + #define AU4_(a) AU4_x(AU1(a)) +//============================================================================================================================== + AU1 AAbsSU1(AU1 a){return AU1(abs(ASU1(a)));} + AU2 AAbsSU2(AU2 a){return AU2(abs(ASU2(a)));} + AU3 AAbsSU3(AU3 a){return AU3(abs(ASU3(a)));} + AU4 AAbsSU4(AU4 a){return AU4(abs(ASU4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + AU1 ABfe(AU1 src,AU1 off,AU1 bits){AU1 mask=(1<>off)&mask;} + AU1 ABfi(AU1 src,AU1 ins,AU1 mask){return (ins&mask)|(src&(~mask));} + AU1 ABfiM(AU1 src,AU1 ins,AU1 bits){AU1 mask=(1<>ASU1(b));} + AU2 AShrSU2(AU2 a,AU2 b){return AU2(ASU2(a)>>ASU2(b));} + AU3 AShrSU3(AU3 a,AU3 b){return AU3(ASU3(a)>>ASU3(b));} + AU4 AShrSU4(AU4 a,AU4 b){return AU4(ASU4(a)>>ASU4(b));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL BYTE +//============================================================================================================================== + #ifdef A_BYTE + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL HALF +//============================================================================================================================== + #ifdef A_HALF + #define AH1 min16float + #define AH2 min16float2 + #define AH3 min16float3 + #define AH4 min16float4 +//------------------------------------------------------------------------------------------------------------------------------ + #define AW1 min16uint + #define AW2 min16uint2 + #define AW3 min16uint3 + #define AW4 min16uint4 +//------------------------------------------------------------------------------------------------------------------------------ + #define ASW1 min16int + #define ASW2 min16int2 + #define ASW3 min16int3 + #define ASW4 min16int4 +//============================================================================================================================== + // Need to use manual unpack to get optimal execution (don't use packed types in buffers directly). + // Unpack requires this pattern: https://gpuopen.com/first-steps-implementing-fp16/ + AH2 AH2_AU1_x(AU1 x){AF2 t=f16tof32(AU2(x&0xFFFF,x>>16));return AH2(t);} + AH4 AH4_AU2_x(AU2 x){return AH4(AH2_AU1_x(x.x),AH2_AU1_x(x.y));} + AW2 AW2_AU1_x(AU1 x){AU2 t=AU2(x&0xFFFF,x>>16);return AW2(t);} + AW4 AW4_AU2_x(AU2 x){return AW4(AW2_AU1_x(x.x),AW2_AU1_x(x.y));} + #define AH2_AU1(x) AH2_AU1_x(AU1(x)) + #define AH4_AU2(x) AH4_AU2_x(AU2(x)) + #define AW2_AU1(x) AW2_AU1_x(AU1(x)) + #define AW4_AU2(x) AW4_AU2_x(AU2(x)) +//------------------------------------------------------------------------------------------------------------------------------ + AU1 AU1_AH2_x(AH2 x){return f32tof16(x.x)+(f32tof16(x.y)<<16);} + AU2 AU2_AH4_x(AH4 x){return AU2(AU1_AH2_x(x.xy),AU1_AH2_x(x.zw));} + AU1 AU1_AW2_x(AW2 x){return AU1(x.x)+(AU1(x.y)<<16);} + AU2 AU2_AW4_x(AW4 x){return AU2(AU1_AW2_x(x.xy),AU1_AW2_x(x.zw));} + #define AU1_AH2(x) AU1_AH2_x(AH2(x)) + #define AU2_AH4(x) AU2_AH4_x(AH4(x)) + #define AU1_AW2(x) AU1_AW2_x(AW2(x)) + #define AU2_AW4(x) AU2_AW4_x(AW4(x)) +//============================================================================================================================== + // TODO: These are broken!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #define AW1_AH1(x) AW1(asuint(AF1(x))) + #define AW2_AH2(x) AW2(asuint(AF2(x))) + #define AW3_AH3(x) AW3(asuint(AF3(x))) + #define AW4_AH4(x) AW4(asuint(AF4(x))) +//------------------------------------------------------------------------------------------------------------------------------ + // TODO: These are broken!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + #define AH1_AW1(x) AH1(asfloat(AU1(x))) + #define AH2_AW2(x) AH2(asfloat(AU2(x))) + #define AH3_AW3(x) AH3(asfloat(AU3(x))) + #define AH4_AW4(x) AH4(asfloat(AU4(x))) +//============================================================================================================================== + AH1 AH1_x(AH1 a){return AH1(a);} + AH2 AH2_x(AH1 a){return AH2(a,a);} + AH3 AH3_x(AH1 a){return AH3(a,a,a);} + AH4 AH4_x(AH1 a){return AH4(a,a,a,a);} + #define AH1_(a) AH1_x(AH1(a)) + #define AH2_(a) AH2_x(AH1(a)) + #define AH3_(a) AH3_x(AH1(a)) + #define AH4_(a) AH4_x(AH1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AW1_x(AW1 a){return AW1(a);} + AW2 AW2_x(AW1 a){return AW2(a,a);} + AW3 AW3_x(AW1 a){return AW3(a,a,a);} + AW4 AW4_x(AW1 a){return AW4(a,a,a,a);} + #define AW1_(a) AW1_x(AW1(a)) + #define AW2_(a) AW2_x(AW1(a)) + #define AW3_(a) AW3_x(AW1(a)) + #define AW4_(a) AW4_x(AW1(a)) +//============================================================================================================================== + AW1 AAbsSW1(AW1 a){return AW1(abs(ASW1(a)));} + AW2 AAbsSW2(AW2 a){return AW2(abs(ASW2(a)));} + AW3 AAbsSW3(AW3 a){return AW3(abs(ASW3(a)));} + AW4 AAbsSW4(AW4 a){return AW4(abs(ASW4(a)));} +//------------------------------------------------------------------------------------------------------------------------------ + // V_FRACT_F16 (note DX frac() is different). + AH1 AFractH1(AH1 x){return x-floor(x);} + AH2 AFractH2(AH2 x){return x-floor(x);} + AH3 AFractH3(AH3 x){return x-floor(x);} + AH4 AFractH4(AH4 x){return x-floor(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ALerpH1(AH1 x,AH1 y,AH1 a){return lerp(x,y,a);} + AH2 ALerpH2(AH2 x,AH2 y,AH2 a){return lerp(x,y,a);} + AH3 ALerpH3(AH3 x,AH3 y,AH3 a){return lerp(x,y,a);} + AH4 ALerpH4(AH4 x,AH4 y,AH4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMax3H1(AH1 x,AH1 y,AH1 z){return max(x,max(y,z));} + AH2 AMax3H2(AH2 x,AH2 y,AH2 z){return max(x,max(y,z));} + AH3 AMax3H3(AH3 x,AH3 y,AH3 z){return max(x,max(y,z));} + AH4 AMax3H4(AH4 x,AH4 y,AH4 z){return max(x,max(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMaxSW1(AW1 a,AW1 b){return AW1(max(ASU1(a),ASU1(b)));} + AW2 AMaxSW2(AW2 a,AW2 b){return AW2(max(ASU2(a),ASU2(b)));} + AW3 AMaxSW3(AW3 a,AW3 b){return AW3(max(ASU3(a),ASU3(b)));} + AW4 AMaxSW4(AW4 a,AW4 b){return AW4(max(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 AMin3H1(AH1 x,AH1 y,AH1 z){return min(x,min(y,z));} + AH2 AMin3H2(AH2 x,AH2 y,AH2 z){return min(x,min(y,z));} + AH3 AMin3H3(AH3 x,AH3 y,AH3 z){return min(x,min(y,z));} + AH4 AMin3H4(AH4 x,AH4 y,AH4 z){return min(x,min(y,z));} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AMinSW1(AW1 a,AW1 b){return AW1(min(ASU1(a),ASU1(b)));} + AW2 AMinSW2(AW2 a,AW2 b){return AW2(min(ASU2(a),ASU2(b)));} + AW3 AMinSW3(AW3 a,AW3 b){return AW3(min(ASU3(a),ASU3(b)));} + AW4 AMinSW4(AW4 a,AW4 b){return AW4(min(ASU4(a),ASU4(b)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARcpH1(AH1 x){return rcp(x);} + AH2 ARcpH2(AH2 x){return rcp(x);} + AH3 ARcpH3(AH3 x){return rcp(x);} + AH4 ARcpH4(AH4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ARsqH1(AH1 x){return rsqrt(x);} + AH2 ARsqH2(AH2 x){return rsqrt(x);} + AH3 ARsqH3(AH3 x){return rsqrt(x);} + AH4 ARsqH4(AH4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASatH1(AH1 x){return saturate(x);} + AH2 ASatH2(AH2 x){return saturate(x);} + AH3 ASatH3(AH3 x){return saturate(x);} + AH4 ASatH4(AH4 x){return saturate(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AW1 AShrSW1(AW1 a,AW1 b){return AW1(ASW1(a)>>ASW1(b));} + AW2 AShrSW2(AW2 a,AW2 b){return AW2(ASW2(a)>>ASW2(b));} + AW3 AShrSW3(AW3 a,AW3 b){return AW3(ASW3(a)>>ASW3(b));} + AW4 AShrSW4(AW4 a,AW4 b){return AW4(ASW4(a)>>ASW4(b));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL DOUBLE +//============================================================================================================================== + #ifdef A_DUBL + #define AD1 double + #define AD2 double2 + #define AD3 double3 + #define AD4 double4 +//------------------------------------------------------------------------------------------------------------------------------ + AD1 AD1_x(AD1 a){return AD1(a);} + AD2 AD2_x(AD1 a){return AD2(a,a);} + AD3 AD3_x(AD1 a){return AD3(a,a,a);} + AD4 AD4_x(AD1 a){return AD4(a,a,a,a);} + #define AD1_(a) AD1_x(AD1(a)) + #define AD2_(a) AD2_x(AD1(a)) + #define AD3_(a) AD3_x(AD1(a)) + #define AD4_(a) AD4_x(AD1(a)) +//============================================================================================================================== + AD1 AFractD1(AD1 a){return a-floor(a);} + AD2 AFractD2(AD2 a){return a-floor(a);} + AD3 AFractD3(AD3 a){return a-floor(a);} + AD4 AFractD4(AD4 a){return a-floor(a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ALerpD1(AD1 x,AD1 y,AD1 a){return lerp(x,y,a);} + AD2 ALerpD2(AD2 x,AD2 y,AD2 a){return lerp(x,y,a);} + AD3 ALerpD3(AD3 x,AD3 y,AD3 a){return lerp(x,y,a);} + AD4 ALerpD4(AD4 x,AD4 y,AD4 a){return lerp(x,y,a);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARcpD1(AD1 x){return rcp(x);} + AD2 ARcpD2(AD2 x){return rcp(x);} + AD3 ARcpD3(AD3 x){return rcp(x);} + AD4 ARcpD4(AD4 x){return rcp(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ARsqD1(AD1 x){return rsqrt(x);} + AD2 ARsqD2(AD2 x){return rsqrt(x);} + AD3 ARsqD3(AD3 x){return rsqrt(x);} + AD4 ARsqD4(AD4 x){return rsqrt(x);} +//------------------------------------------------------------------------------------------------------------------------------ + AD1 ASatD1(AD1 x){return saturate(x);} + AD2 ASatD2(AD2 x){return saturate(x);} + AD3 ASatD3(AD3 x){return saturate(x);} + AD4 ASatD4(AD4 x){return saturate(x);} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HLSL LONG +//============================================================================================================================== + #ifdef A_LONG + #endif +//============================================================================================================================== +#endif + +//============================================================================================================================== +// GPU COMMON +//============================================================================================================================== +#if defined(A_GPU) || (defined(ASPM_GPU) && !(defined(ASPM_OPENCL))) + // Negative and positive infinity. + #define A_INFN_F AF1_AU1(0x7f800000u) + #define A_INFP_F AF1_AU1(0xff800000u) +//------------------------------------------------------------------------------------------------------------------------------ + // Copy sign from 's' to positive 'd'. + AF1 ACpySgnF1(AF1 d,AF1 s){return AF1_AU1(AU1_AF1(d)|(AU1_AF1(s)&AU1_(0x80000000u)));} + AF2 ACpySgnF2(AF2 d,AF2 s){return AF2_AU2(AU2_AF2(d)|(AU2_AF2(s)&AU2_(0x80000000u)));} + AF3 ACpySgnF3(AF3 d,AF3 s){return AF3_AU3(AU3_AF3(d)|(AU3_AF3(s)&AU3_(0x80000000u)));} + AF4 ACpySgnF4(AF4 d,AF4 s){return AF4_AU4(AU4_AF4(d)|(AU4_AF4(s)&AU4_(0x80000000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + // Single operation to return (useful to create a mask to use in lerp for branch free logic), + // m=NaN := 0 + // m>=0 := 0 + // m<0 := 1 + // Uses the following useful floating point logic, + // saturate(+a*(-INF)==-INF) := 0 + // saturate( 0*(-INF)== NaN) := 0 + // saturate(-a*(-INF)==+INF) := 1 + AF1 ASignedF1(AF1 m){return ASatF1(m*AF1_(A_INFN_F));} + AF2 ASignedF2(AF2 m){return ASatF2(m*AF2_(A_INFN_F));} + AF3 ASignedF3(AF3 m){return ASatF3(m*AF3_(A_INFN_F));} + AF4 ASignedF4(AF4 m){return ASatF4(m*AF4_(A_INFN_F));} +//============================================================================================================================== + #ifdef A_HALF + #define A_INFN_H AH1_AW1(0x7c00u) + #define A_INFP_H AH1_AW1(0xfc00u) +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ACpySgnH1(AH1 d,AH1 s){return AH1_AW1(AW1_AH1(d)|(AW1_AH1(s)&AW1_(0x8000u)));} + AH2 ACpySgnH2(AH2 d,AH2 s){return AH2_AW2(AW2_AH2(d)|(AW2_AH2(s)&AW2_(0x8000u)));} + AH3 ACpySgnH3(AH3 d,AH3 s){return AH3_AW3(AW3_AH3(d)|(AW3_AH3(s)&AW3_(0x8000u)));} + AH4 ACpySgnH4(AH4 d,AH4 s){return AH4_AW4(AW4_AH4(d)|(AW4_AH4(s)&AW4_(0x8000u)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH1 ASignedH1(AH1 m){return ASatH1(m*AH1_(A_INFN_H));} + AH2 ASignedH2(AH2 m){return ASatH2(m*AH2_(A_INFN_H));} + AH3 ASignedH3(AH3 m){return ASatH3(m*AH3_(A_INFN_H));} + AH4 ASignedH4(AH4 m){return ASatH4(m*AH4_(A_INFN_H));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// HALF APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These support only positive inputs. +// Did not see value yet in specialization for range. +// Using quick testing, ended up mostly getting the same "best" approximation for various ranges. +// With hardware that can co-execute transcendentals, the value in approximations could be less than expected. +// However from a latency perspective, if execution of a transcendental is 4 clk, with no packed support, -> 8 clk total. +// And co-execution would require a compiler interleaving a lot of independent work for packed usage. +//------------------------------------------------------------------------------------------------------------------------------ +// The one Newton Raphson iteration form of rsq() was skipped (requires 6 ops total). +// Same with sqrt(), as this could be x*rsq() (7 ops). +//------------------------------------------------------------------------------------------------------------------------------ +// IDEAS +// ===== +// - Polaris hardware has 16-bit support, but non-double rate. +// Could be possible still get part double rate for some of this logic, +// by clearing out the lower half's sign when necessary and using 32-bit ops... +//============================================================================================================================== + #ifdef A_HALF + // Minimize squared error across full positive range, 2 ops. + // The 0x1de2 based approximation maps {0 to 1} input maps to < 1 output. + AH1 APrxLoSqrtH1(AH1 a){return AH1_AW1((AW1_AH1(a)>>AW1_(1))+AW1_(0x1de2));} + AH2 APrxLoSqrtH2(AH2 a){return AH2_AW2((AW2_AH2(a)>>AW2_(1))+AW2_(0x1de2));} +//------------------------------------------------------------------------------------------------------------------------------ + // Lower precision estimation, 1 op. + // Minimize squared error across {smallest normal to 16384.0}. + AH1 APrxLoRcpH1(AH1 a){return AH1_AW1(AW1_(0x7784)-AW1_AH1(a));} + AH2 APrxLoRcpH2(AH2 a){return AH2_AW2(AW2_(0x7784)-AW2_AH2(a));} +//------------------------------------------------------------------------------------------------------------------------------ + // Medium precision estimation, one Newton Raphson iteration, 3 ops. + AH1 APrxMedRcpH1(AH1 a){AH1 b=AH1_AW1(AW1_(0x778d)-AW1_AH1(a));return b*(-b*a+AH1_(2.0));} + AH2 APrxMedRcpH2(AH2 a){AH2 b=AH2_AW2(AW2_(0x778d)-AW2_AH2(a));return b*(-b*a+AH2_(2.0));} +//------------------------------------------------------------------------------------------------------------------------------ + // Minimize squared error across {smallest normal to 16384.0}, 2 ops. + AH1 APrxLoRsqH1(AH1 a){return AH1_AW1(AW1_(0x59a3)-(AW1_AH1(a)>>AW1_(1)));} + AH2 APrxLoRsqH2(AH2 a){return AH2_AW2(AW2_(0x59a3)-(AW2_AH2(a)>>AW2_(1)));} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// FLOAT APPROXIMATIONS +//------------------------------------------------------------------------------------------------------------------------------ +// Michal Drobot has an excellent presentation on these: "Low Level Optimizations For GCN", +// - Idea dates back to SGI, then to Quake 3, etc. +// - https://michaldrobot.files.wordpress.com/2014/05/gcn_alu_opt_digitaldragons2014.pdf +// - sqrt(x)=rsqrt(x)*x +// - rcp(x)=rsqrt(x)*rsqrt(x) for positive x +// - https://github.com/michaldrobot/ShaderFastLibs/blob/master/ShaderFastMathLib.h +//------------------------------------------------------------------------------------------------------------------------------ +// These below are from perhaps less complete searching for optimal. +// Used FP16 normal range for testing with +4096 32-bit step size for sampling error. +// So these match up well with the half approximations. +//============================================================================================================================== + AF1 APrxLoSqrtF1(AF1 a){return AF1_AU1((AU1_AF1(a)>>AU1_(1))+AU1_(0x1fbc4639));} + AF1 APrxLoRcpF1(AF1 a){return AF1_AU1(AU1_(0x7ef07ebb)-AU1_AF1(a));} + AF1 APrxMedRcpF1(AF1 a){AF1 b=AF1_AU1(AU1_(0x7ef19fff)-AU1_AF1(a));return b*(-b*a+AF1_(2.0));} + AF1 APrxLoRsqF1(AF1 a){return AF1_AU1(AU1_(0x5f347d74)-(AU1_AF1(a)>>AU1_(1)));} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// PARABOLIC SIN & COS +//------------------------------------------------------------------------------------------------------------------------------ +// Approximate answers to transcendental questions. +//------------------------------------------------------------------------------------------------------------------------------ +// TODO +// ==== +// - Verify packed math ABS is correctly doing an AND. +//============================================================================================================================== + // Valid input range is {-1 to 1} representing {0 to 2 pi}. + // Output range is {-1/4 to -1/4} representing {-1 to 1}. + AF1 APSinF1(AF1 x){return x*abs(x)-x;} // MAD. + AF1 APCosF1(AF1 x){x=AFractF1(x*AF1_(0.5)+AF1_(0.75));x=x*AF1_(2.0)-AF1_(1.0);return APSinF1(x);} // 3x MAD, FRACT +//------------------------------------------------------------------------------------------------------------------------------ + #ifdef A_HALF + // For a packed {sin,cos} pair, + // - Native takes 16 clocks and 4 issue slots (no packed transcendentals). + // - Parabolic takes 8 clocks and 8 issue slots (only fract is non-packed). + AH2 APSinH2(AH2 x){return x*abs(x)-x;} // AND,FMA + AH2 APCosH2(AH2 x){x=AFractH2(x*AH2_(0.5)+AH2_(0.75));x=x*AH2_(2.0)-AH2_(1.0);return APSinH2(x);} // 3x FMA, 2xFRACT, AND + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// COLOR CONVERSIONS +//------------------------------------------------------------------------------------------------------------------------------ +// These are all linear to/from some other space (where 'linear' has been shortened out of the function name). +// So 'ToGamma' is 'LinearToGamma', and 'FromGamma' is 'LinearFromGamma'. +// These are branch free implementations. +// The AToSrgbF1() function is useful for stores for compute shaders for GPUs without hardware linear->sRGB store conversion. +//------------------------------------------------------------------------------------------------------------------------------ +// TRANSFER FUNCTIONS +// ================== +// 709 ..... Rec709 used for some HDTVs +// Gamma ... Typically 2.2 for some PC displays, or 2.4-2.5 for CRTs, or 2.2 FreeSync2 native +// Pq ...... PQ native for HDR10 +// Srgb .... The sRGB output, typical of PC displays, useful for 10-bit output, or storing to 8-bit UNORM without SRGB type +// Two ..... Gamma 2.0, fastest conversion (useful for intermediate pass approximations) +//------------------------------------------------------------------------------------------------------------------------------ +// FOR PQ +// ====== +// Both input and output is {0.0-1.0}, and where output 1.0 represents 10000.0 cd/m^2. +// All constants are only specified to FP32 precision. +// External PQ source reference, +// - https://github.com/ampas/aces-dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1.0.1.ctl +//------------------------------------------------------------------------------------------------------------------------------ +// PACKED VERSIONS +// =============== +// These are the A*H2() functions. +// There is no PQ functions as FP16 seemed to not have enough precision for the conversion. +// The remaining functions are "good enough" for 8-bit, and maybe 10-bit if not concerned about a few 1-bit errors. +// Precision is lowest in the 709 conversion, higher in sRGB, higher still in Two and Gamma (when using 2.2 at least). +//------------------------------------------------------------------------------------------------------------------------------ +// NOTES +// ===== +// Could be faster for PQ conversions to be in ALU or a texture lookup depending on usage case. +//============================================================================================================================== + AF1 ATo709F1(AF1 c){return max(min(c*AF1_(4.5),AF1_(0.018)),AF1_(1.099)*pow(c,AF1_(0.45))-AF1_(0.099));} +//------------------------------------------------------------------------------------------------------------------------------ + // Note 'rcpX' is '1/x', where the 'x' is what would be used in AFromGamma(). + AF1 AToGammaF1(AF1 c,AF1 rcpX){return pow(c,rcpX);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToPqF1(AF1 x){AF1 p=pow(x,AF1_(0.159302)); + return pow((AF1_(0.835938)+AF1_(18.8516)*p)/(AF1_(1.0)+AF1_(18.6875)*p),AF1_(78.8438));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToSrgbF1(AF1 c){return max(min(c*AF1_(12.92),AF1_(0.0031308)),AF1_(1.055)*pow(c,AF1_(0.41666))-AF1_(0.055));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AToTwoF1(AF1 c){return sqrt(c);} +//============================================================================================================================== + AF1 AFrom709F1(AF1 c){return max(min(c*AF1_(1.0/4.5),AF1_(0.081)), + pow((c+AF1_(0.099))*(AF1_(1.0)/(AF1_(1.099))),AF1_(1.0/0.45)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromGammaF1(AF1 c,AF1 x){return pow(c,x);} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromPqF1(AF1 x){AF1 p=pow(x,AF1_(0.0126833)); + return pow(ASatF1(p-AF1_(0.835938))/(AF1_(18.8516)-AF1_(18.6875)*p),AF1_(6.27739));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromSrgbF1(AF1 c){return max(min(c*AF1_(1.0/12.92),AF1_(0.04045)), + pow((c+AF1_(0.055))*(AF1_(1.0)/AF1_(1.055)),AF1_(2.4)));} +//------------------------------------------------------------------------------------------------------------------------------ + AF1 AFromTwoF1(AF1 c){return c*c;} +//============================================================================================================================== + #ifdef A_HALF + AH2 ATo709H2(AH2 c){return max(min(c*AH2_(4.5),AH2_(0.018)),AH2_(1.099)*pow(c,AH2_(0.45))-AH2_(0.099));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToGammaH2(AH2 c,AH1 rcpX){return pow(c,AH2_(rcpX));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToSrgbH2(AH2 c){return max(min(c*AH2_(12.92),AH2_(0.0031308)),AH2_(1.055)*pow(c,AH2_(0.41666))-AH2_(0.055));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AToTwoH2(AH2 c){return sqrt(c);} + #endif +//============================================================================================================================== + #ifdef A_HALF + AH2 AFrom709H2(AH2 c){return max(min(c*AH2_(1.0/4.5),AH2_(0.081)), + pow((c+AH2_(0.099))*(AH2_(1.0)/(AH2_(1.099))),AH2_(1.0/0.45)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromGammaH2(AH2 c,AH1 x){return pow(c,AH2_(x));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromSrgbH2(AH2 c){return max(min(c*AH2_(1.0/12.92),AH2_(0.04045)), + pow((c+AH2_(0.055))*(AH2_(1.0)/AH2_(1.055)),AH2_(2.4)));} +//------------------------------------------------------------------------------------------------------------------------------ + AH2 AFromTwoH2(AH2 c){return c*c;} + #endif +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// CS REMAP +//============================================================================================================================== + // Simple remap 64x1 to 8x8 with rotated 2x2 pixel quads in quad linear. + // 543210 + // ====== + // ..xxx. + // yy...y + AU2 ARmp8x8(AU1 a){return AU2(ABfe(a,1u,3u),ABfiM(ABfe(a,3u,3u),a,1u));} +//============================================================================================================================== + // More complex remap 64x1 to 8x8 which is necessary for 2D wave reductions. + // 543210 + // ====== + // .xx..x + // y..yy. + // Details, + // LANE TO 8x8 MAPPING + // =================== + // 00 01 08 09 10 11 18 19 + // 02 03 0a 0b 12 13 1a 1b + // 04 05 0c 0d 14 15 1c 1d + // 06 07 0e 0f 16 17 1e 1f + // 20 21 28 29 30 31 38 39 + // 22 23 2a 2b 32 33 3a 3b + // 24 25 2c 2d 34 35 3c 3d + // 26 27 2e 2f 36 37 3e 3f + AU2 ARmpRed8x8(AU1 a){return AU2(ABfiM(ABfe(a,2u,3u),a,1u),ABfiM(ABfe(a,3u,3u),ABfe(a,1u,2u),2u));} +#endif + +//============================================================================================================================== +// GPU/CPU PORTABILITY +//------------------------------------------------------------------------------------------------------------------------------ +// This is the GPU implementation. +// See the CPU implementation for docs. +//============================================================================================================================== +#if defined(A_GPU) || (defined(ASPM_GPU) && !(defined(ASPM_OPENCL))) + #define A_TRUE true + #define A_FALSE false + #define A_STATIC +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR ARGUMENT/RETURN/INITIALIZATION PORTABILITY +//============================================================================================================================== + #define retAD2 AD2 + #define retAD3 AD3 + #define retAD4 AD4 + #define retAF2 AF2 + #define retAF3 AF3 + #define retAF4 AF4 + #define retAL2 AL2 + #define retAL3 AL3 + #define retAL4 AL4 + #define retAU2 AU2 + #define retAU3 AU3 + #define retAU4 AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inAD2 in AD2 + #define inAD3 in AD3 + #define inAD4 in AD4 + #define inAF2 in AF2 + #define inAF3 in AF3 + #define inAF4 in AF4 + #define inAL2 in AL2 + #define inAL3 in AL3 + #define inAL4 in AL4 + #define inAU2 in AU2 + #define inAU3 in AU3 + #define inAU4 in AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define inoutAD2 inout AD2 + #define inoutAD3 inout AD3 + #define inoutAD4 inout AD4 + #define inoutAF2 inout AF2 + #define inoutAF3 inout AF3 + #define inoutAF4 inout AF4 + #define inoutAL2 inout AL2 + #define inoutAL3 inout AL3 + #define inoutAL4 inout AL4 + #define inoutAU2 inout AU2 + #define inoutAU3 inout AU3 + #define inoutAU4 inout AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define outAD2 out AD2 + #define outAD3 out AD3 + #define outAD4 out AD4 + #define outAF2 out AF2 + #define outAF3 out AF3 + #define outAF4 out AF4 + #define outAL2 out AL2 + #define outAL3 out AL3 + #define outAL4 out AL4 + #define outAU2 out AU2 + #define outAU3 out AU3 + #define outAU4 out AU4 +//------------------------------------------------------------------------------------------------------------------------------ + #define varAD2(x) AD2 x + #define varAD3(x) AD3 x + #define varAD4(x) AD4 x + #define varAF2(x) AF2 x + #define varAF3(x) AF3 x + #define varAF4(x) AF4 x + #define varAL2(x) AL2 x + #define varAL3(x) AL3 x + #define varAL4(x) AL4 x + #define varAU2(x) AU2 x + #define varAU3(x) AU3 x + #define varAU4(x) AU4 x +//------------------------------------------------------------------------------------------------------------------------------ + #define initAD2(x,y) AD2(x,y) + #define initAD3(x,y,z) AD3(x,y,z) + #define initAD4(x,y,z,w) AD4(x,y,z,w) + #define initAF2(x,y) AF2(x,y) + #define initAF3(x,y,z) AF3(x,y,z) + #define initAF4(x,y,z,w) AF4(x,y,z,w) + #define initAL2(x,y) AL2(x,y) + #define initAL3(x,y,z) AL3(x,y,z) + #define initAL4(x,y,z,w) AL4(x,y,z,w) + #define initAU2(x,y) AU2(x,y) + #define initAU3(x,y,z) AU3(x,y,z) + #define initAU4(x,y,z,w) AU4(x,y,z,w) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS +//============================================================================================================================== + #define AAbsD1(a) abs(AD1(a)) + #define AAbsF1(a) abs(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ACosD1(a) cos(AD1(a)) + #define ACosF1(a) cos(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ADotD2(a,b) dot(AD2(a),AD2(b)) + #define ADotD3(a,b) dot(AD3(a),AD3(b)) + #define ADotD4(a,b) dot(AD4(a),AD4(b)) + #define ADotF2(a,b) dot(AF2(a),AF2(b)) + #define ADotF3(a,b) dot(AF3(a),AF3(b)) + #define ADotF4(a,b) dot(AF4(a),AF4(b)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AExp2D1(a) exp2(AD1(a)) + #define AExp2F1(a) exp2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AFloorD1(a) floor(AD1(a)) + #define AFloorF1(a) floor(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ALog2D1(a) log2(AD1(a)) + #define ALog2F1(a) log2(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMaxD1(a,b) min(a,b) + #define AMaxF1(a,b) min(a,b) + #define AMaxL1(a,b) min(a,b) + #define AMaxU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define AMinD1(a,b) min(a,b) + #define AMinF1(a,b) min(a,b) + #define AMinL1(a,b) min(a,b) + #define AMinU1(a,b) min(a,b) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASinD1(a) sin(AD1(a)) + #define ASinF1(a) sin(AF1(a)) +//------------------------------------------------------------------------------------------------------------------------------ + #define ASqrtD1(a) sqrt(AD1(a)) + #define ASqrtF1(a) sqrt(AF1(a)) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// SCALAR RETURN OPS - DEPENDENT +//============================================================================================================================== + #define APowD1(a,b) pow(AD1(a),AF1(b)) + #define APowF1(a,b) pow(AF1(a),AF1(b)) +//_____________________________________________________________/\_______________________________________________________________ +//============================================================================================================================== +// VECTOR OPS +//------------------------------------------------------------------------------------------------------------------------------ +// These are added as needed for production or prototyping, so not necessarily a complete set. +// They follow a convention of taking in a destination and also returning the destination value to increase utility. +//============================================================================================================================== + #ifdef A_DUBL + AD2 opAAbsD2(outAD2 d,inAD2 a){d=abs(a);return d;} + AD3 opAAbsD3(outAD3 d,inAD3 a){d=abs(a);return d;} + AD4 opAAbsD4(outAD4 d,inAD4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAAddD2(outAD2 d,inAD2 a,inAD2 b){d=a+b;return d;} + AD3 opAAddD3(outAD3 d,inAD3 a,inAD3 b){d=a+b;return d;} + AD4 opAAddD4(outAD4 d,inAD4 a,inAD4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opACpyD2(outAD2 d,inAD2 a){d=a;return d;} + AD3 opACpyD3(outAD3 d,inAD3 a){d=a;return d;} + AD4 opACpyD4(outAD4 d,inAD4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpD2(outAD2 d,inAD2 a,inAD2 b,inAD2 c){d=ALerpD2(a,b,c);return d;} + AD3 opALerpD3(outAD3 d,inAD3 a,inAD3 b,inAD3 c){d=ALerpD3(a,b,c);return d;} + AD4 opALerpD4(outAD4 d,inAD4 a,inAD4 b,inAD4 c){d=ALerpD4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opALerpOneD2(outAD2 d,inAD2 a,inAD2 b,AD1 c){d=ALerpD2(a,b,AD2_(c));return d;} + AD3 opALerpOneD3(outAD3 d,inAD3 a,inAD3 b,AD1 c){d=ALerpD3(a,b,AD3_(c));return d;} + AD4 opALerpOneD4(outAD4 d,inAD4 a,inAD4 b,AD1 c){d=ALerpD4(a,b,AD4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMaxD2(outAD2 d,inAD2 a,inAD2 b){d=max(a,b);return d;} + AD3 opAMaxD3(outAD3 d,inAD3 a,inAD3 b){d=max(a,b);return d;} + AD4 opAMaxD4(outAD4 d,inAD4 a,inAD4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMinD2(outAD2 d,inAD2 a,inAD2 b){d=min(a,b);return d;} + AD3 opAMinD3(outAD3 d,inAD3 a,inAD3 b){d=min(a,b);return d;} + AD4 opAMinD4(outAD4 d,inAD4 a,inAD4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulD2(outAD2 d,inAD2 a,inAD2 b){d=a*b;return d;} + AD3 opAMulD3(outAD3 d,inAD3 a,inAD3 b){d=a*b;return d;} + AD4 opAMulD4(outAD4 d,inAD4 a,inAD4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opAMulOneD2(outAD2 d,inAD2 a,AD1 b){d=a*AD2_(b);return d;} + AD3 opAMulOneD3(outAD3 d,inAD3 a,AD1 b){d=a*AD3_(b);return d;} + AD4 opAMulOneD4(outAD4 d,inAD4 a,AD1 b){d=a*AD4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opANegD2(outAD2 d,inAD2 a){d=-a;return d;} + AD3 opANegD3(outAD3 d,inAD3 a){d=-a;return d;} + AD4 opANegD4(outAD4 d,inAD4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AD2 opARcpD2(outAD2 d,inAD2 a){d=ARcpD2(a);return d;} + AD3 opARcpD3(outAD3 d,inAD3 a){d=ARcpD3(a);return d;} + AD4 opARcpD4(outAD4 d,inAD4 a){d=ARcpD4(a);return d;} + #endif +//============================================================================================================================== + AF2 opAAbsF2(outAF2 d,inAF2 a){d=abs(a);return d;} + AF3 opAAbsF3(outAF3 d,inAF3 a){d=abs(a);return d;} + AF4 opAAbsF4(outAF4 d,inAF4 a){d=abs(a);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAAddF2(outAF2 d,inAF2 a,inAF2 b){d=a+b;return d;} + AF3 opAAddF3(outAF3 d,inAF3 a,inAF3 b){d=a+b;return d;} + AF4 opAAddF4(outAF4 d,inAF4 a,inAF4 b){d=a+b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opACpyF2(outAF2 d,inAF2 a){d=a;return d;} + AF3 opACpyF3(outAF3 d,inAF3 a){d=a;return d;} + AF4 opACpyF4(outAF4 d,inAF4 a){d=a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpF2(outAF2 d,inAF2 a,inAF2 b,inAF2 c){d=ALerpF2(a,b,c);return d;} + AF3 opALerpF3(outAF3 d,inAF3 a,inAF3 b,inAF3 c){d=ALerpF3(a,b,c);return d;} + AF4 opALerpF4(outAF4 d,inAF4 a,inAF4 b,inAF4 c){d=ALerpF4(a,b,c);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opALerpOneF2(outAF2 d,inAF2 a,inAF2 b,AF1 c){d=ALerpF2(a,b,AF2_(c));return d;} + AF3 opALerpOneF3(outAF3 d,inAF3 a,inAF3 b,AF1 c){d=ALerpF3(a,b,AF3_(c));return d;} + AF4 opALerpOneF4(outAF4 d,inAF4 a,inAF4 b,AF1 c){d=ALerpF4(a,b,AF4_(c));return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMaxF2(outAF2 d,inAF2 a,inAF2 b){d=max(a,b);return d;} + AF3 opAMaxF3(outAF3 d,inAF3 a,inAF3 b){d=max(a,b);return d;} + AF4 opAMaxF4(outAF4 d,inAF4 a,inAF4 b){d=max(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMinF2(outAF2 d,inAF2 a,inAF2 b){d=min(a,b);return d;} + AF3 opAMinF3(outAF3 d,inAF3 a,inAF3 b){d=min(a,b);return d;} + AF4 opAMinF4(outAF4 d,inAF4 a,inAF4 b){d=min(a,b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulF2(outAF2 d,inAF2 a,inAF2 b){d=a*b;return d;} + AF3 opAMulF3(outAF3 d,inAF3 a,inAF3 b){d=a*b;return d;} + AF4 opAMulF4(outAF4 d,inAF4 a,inAF4 b){d=a*b;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opAMulOneF2(outAF2 d,inAF2 a,AF1 b){d=a*AF2_(b);return d;} + AF3 opAMulOneF3(outAF3 d,inAF3 a,AF1 b){d=a*AF3_(b);return d;} + AF4 opAMulOneF4(outAF4 d,inAF4 a,AF1 b){d=a*AF4_(b);return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opANegF2(outAF2 d,inAF2 a){d=-a;return d;} + AF3 opANegF3(outAF3 d,inAF3 a){d=-a;return d;} + AF4 opANegF4(outAF4 d,inAF4 a){d=-a;return d;} +//------------------------------------------------------------------------------------------------------------------------------ + AF2 opARcpF2(outAF2 d,inAF2 a){d=ARcpF2(a);return d;} + AF3 opARcpF3(outAF3 d,inAF3 a){d=ARcpF3(a);return d;} + AF4 opARcpF4(outAF4 d,inAF4 a){d=ARcpF4(a);return d;} +#endif +#endif // USE_CMP_FIDELITY_FX_H + +#endif // Common_Def.h diff --git a/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl b/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl index 83f56dd0e..5ca47ed97 100644 --- a/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl +++ b/WickedEngine/shaders/terrainVirtualTextureUpdateCS.hlsl @@ -1,5 +1,8 @@ #include "globals.hlsli" +#define ASPM_HLSL +#include "compressonator/bcn_common_kernel.h" + static const uint region_count = 4; PUSHCONSTANT(push, TerrainVirtualTexturePush); @@ -10,94 +13,131 @@ struct Terrain }; ConstantBuffer terrain : register(b0); +RWTexture2D output_bc1_unorm : register(u0); +RWTexture2D output_bc3_unorm : register(u1); +RWTexture2D output_bc5_unorm : register(u2); + [numthreads(8, 8, 1)] void main(uint3 DTid : SV_DispatchThreadID, uint2 GTid : SV_GroupThreadID) { Texture2D region_weights_texture = bindless_textures[push.region_weights_textureRO]; - RWTexture2D output = bindless_rwtextures[push.output_textureRW]; - const uint2 pixel = DTid.xy + push.offset; + float3 block_rgb[BLOCK_SIZE_4X4]; + float block_x[BLOCK_SIZE_4X4]; + float block_y[BLOCK_SIZE_4X4]; - float2 output_dim; - output.GetDimensions(output_dim.x, output_dim.y); - const float2 uv = (pixel.xy + 0.5f) / output_dim; - - float4 region_weights = region_weights_texture.SampleLevel(sampler_linear_clamp, uv, 0); - - float weight_sum = 0; - float4 total_color = 0; - for (uint i = 0; i < region_count; ++i) + for (uint y = 0; y < 4; ++y) { - float weight = region_weights[i]; - - ShaderMaterial material = terrain.materials[i]; - - switch (push.map_type) + for (uint x = 0; x < 4; ++x) { + const uint2 pixel = push.offset + DTid.xy * 4 + uint2(x, y); + const float2 uv = (pixel.xy + 0.5f) * push.resolution_rcp; - default: - case 0: - { - float4 baseColor = material.baseColor; - [branch] - if (material.textures[BASECOLORMAP].IsValid()) + float4 region_weights = region_weights_texture.SampleLevel(sampler_linear_clamp, uv, 0); + + float weight_sum = 0; + float4 total_color = 0; + for (uint i = 0; i < region_count; ++i) { - Texture2D tex = bindless_textures[material.textures[BASECOLORMAP].texture_descriptor]; - float2 dim = 0; - tex.GetDimensions(dim.x, dim.y); - float2 diff = dim / output_dim; - float lod = log2(max(diff.x, diff.y)); - float2 overscale = lod < 0 ? diff : 1; - float4 baseColorMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod); - baseColor *= baseColorMap; - } - total_color += baseColor * weight; - } - break; + float weight = region_weights[i]; - case 1: - { - float2 normal = float2(0.5, 0.5); - [branch] - if (material.textures[NORMALMAP].IsValid()) + ShaderMaterial material = terrain.materials[i]; + + switch (push.map_type) + { + + default: + case 0: + { + float4 baseColor = material.baseColor; + [branch] + if (material.textures[BASECOLORMAP].IsValid()) + { + Texture2D tex = bindless_textures[material.textures[BASECOLORMAP].texture_descriptor]; + float2 dim = 0; + tex.GetDimensions(dim.x, dim.y); + float2 diff = dim * push.resolution_rcp; + float lod = log2(max(diff.x, diff.y)); + float2 overscale = lod < 0 ? diff : 1; + float4 baseColorMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod); + baseColor *= baseColorMap; + } + total_color += baseColor * weight; + } + break; + + case 1: + { + float2 normal = float2(0.5, 0.5); + [branch] + if (material.textures[NORMALMAP].IsValid()) + { + Texture2D tex = bindless_textures[material.textures[NORMALMAP].texture_descriptor]; + float2 dim = 0; + tex.GetDimensions(dim.x, dim.y); + float2 diff = dim * push.resolution_rcp; + float lod = log2(max(diff.x, diff.y)); + float2 overscale = lod < 0 ? diff : 1; + float2 normalMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod).rg; + normal = normalMap; + } + total_color += float4(normal.rg, 1, 1) * weight; + } + break; + + case 2: + { + float4 surface = float4(1, material.roughness, material.metalness, material.reflectance); + [branch] + if (material.textures[SURFACEMAP].IsValid()) + { + Texture2D tex = bindless_textures[material.textures[SURFACEMAP].texture_descriptor]; + float2 dim = 0; + tex.GetDimensions(dim.x, dim.y); + float2 diff = dim * push.resolution_rcp; + float lod = log2(max(diff.x, diff.y)); + float2 overscale = lod < 0 ? diff : 1; + float4 surfaceMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod); + surface *= surfaceMap; + } + total_color += surface * weight; + } + break; + + } + + weight_sum += weight; + } + total_color /= weight_sum; + + const uint idx = x + y * 4; + if (push.map_type == 1) { - Texture2D tex = bindless_textures[material.textures[NORMALMAP].texture_descriptor]; - float2 dim = 0; - tex.GetDimensions(dim.x, dim.y); - float2 diff = dim / output_dim; - float lod = log2(max(diff.x, diff.y)); - float2 overscale = lod < 0 ? diff : 1; - float2 normalMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod).rg; - normal = normalMap; + block_x[idx] = total_color.r; + block_y[idx] = total_color.g; } - total_color += float4(normal.rg, 1, 1) * weight; - } - break; - - case 2: - { - float4 surface = float4(1, material.roughness, material.metalness, material.reflectance); - [branch] - if (material.textures[SURFACEMAP].IsValid()) + if (push.map_type == 2) { - Texture2D tex = bindless_textures[material.textures[SURFACEMAP].texture_descriptor]; - float2 dim = 0; - tex.GetDimensions(dim.x, dim.y); - float2 diff = dim / output_dim; - float lod = log2(max(diff.x, diff.y)); - float2 overscale = lod < 0 ? diff : 1; - float4 surfaceMap = tex.SampleLevel(sampler_linear_wrap, uv / overscale, lod); - surface *= surfaceMap; + block_rgb[idx] = total_color.rgb; + block_x[idx] = total_color.a; + } + else + { + block_rgb[idx] = total_color.rgb; } - total_color += surface * weight; } - break; - - } - - weight_sum += weight; } - total_color /= weight_sum; - output[pixel] = total_color; + if (push.map_type == 1) + { + output_bc5_unorm[DTid.xy] = CompressBlockBC5_UNORM(block_x, block_y, CMP_QUALITY0); + } + if (push.map_type == 2) + { + output_bc3_unorm[DTid.xy] = CompressBlockBC3_UNORM(block_rgb, block_x, CMP_QUALITY2, /*isSRGB =*/ false); + } + else + { + output_bc1_unorm[DTid.xy] = CompressBlockBC1_UNORM(block_rgb, CMP_QUALITY0, /*isSRGB =*/ false); + } } diff --git a/WickedEngine/wiGraphics.h b/WickedEngine/wiGraphics.h index 796b72d07..3476c1489 100644 --- a/WickedEngine/wiGraphics.h +++ b/WickedEngine/wiGraphics.h @@ -697,6 +697,16 @@ namespace wi::graphics int32_t bottom = 0; }; + struct Box + { + uint32_t left = 0; + uint32_t top = 0; + uint32_t front = 0; + uint32_t right = 0; + uint32_t bottom = 0; + uint32_t back = 0; + }; + struct SparseTextureProperties { uint32_t tile_width = 0; // width of 1 tile in texels diff --git a/WickedEngine/wiGraphicsDevice.h b/WickedEngine/wiGraphicsDevice.h index a4883191a..d9b127146 100644 --- a/WickedEngine/wiGraphicsDevice.h +++ b/WickedEngine/wiGraphicsDevice.h @@ -203,6 +203,7 @@ namespace wi::graphics virtual void DispatchMeshIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) {} virtual void CopyResource(const GPUResource* pDst, const GPUResource* pSrc, CommandList cmd) = 0; virtual void CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd) = 0; + virtual void CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox = nullptr) = 0; virtual void QueryBegin(const GPUQueryHeap *heap, uint32_t index, CommandList cmd) = 0; virtual void QueryEnd(const GPUQueryHeap *heap, uint32_t index, CommandList cmd) = 0; virtual void QueryResolve(const GPUQueryHeap* heap, uint32_t index, uint32_t count, const GPUBuffer* dest, uint64_t dest_offset, CommandList cmd) = 0; diff --git a/WickedEngine/wiGraphicsDevice_DX12.cpp b/WickedEngine/wiGraphicsDevice_DX12.cpp index 5725c435d..3375b8cbe 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.cpp +++ b/WickedEngine/wiGraphicsDevice_DX12.cpp @@ -6135,6 +6135,44 @@ using namespace dx12_internal; commandlist.GetGraphicsCommandList()->CopyBufferRegion(dst_internal->resource.Get(), dst_offset, src_internal->resource.Get(), src_offset, size); } + void GraphicsDevice_DX12::CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox) + { + CommandList_DX12& commandlist = GetCommandList(cmd); + auto src_internal = to_internal((const GPUBuffer*)src); + auto dst_internal = to_internal((const GPUBuffer*)dst); + CD3DX12_TEXTURE_COPY_LOCATION src_location(src_internal->resource.Get(), D3D12CalcSubresource(srcMip, srcSlice, 0, src->desc.mip_levels, src->desc.array_size)); + CD3DX12_TEXTURE_COPY_LOCATION dst_location(dst_internal->resource.Get(), D3D12CalcSubresource(dstMip, dstSlice, 0, dst->desc.mip_levels, dst->desc.array_size)); + if (srcbox == nullptr) + { + commandlist.GetGraphicsCommandList()->CopyTextureRegion( + &dst_location, + dstX, + dstY, + dstZ, + &src_location, + nullptr + ); + } + else + { + D3D12_BOX d3dbox = {}; + d3dbox.left = srcbox->left; + d3dbox.top = srcbox->top; + d3dbox.front = srcbox->front; + d3dbox.right = srcbox->right; + d3dbox.bottom = srcbox->bottom; + d3dbox.back = srcbox->back; + commandlist.GetGraphicsCommandList()->CopyTextureRegion( + &dst_location, + dstX, + dstY, + dstZ, + &src_location, + &d3dbox + ); + } + + } void GraphicsDevice_DX12::QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) { CommandList_DX12& commandlist = GetCommandList(cmd); diff --git a/WickedEngine/wiGraphicsDevice_DX12.h b/WickedEngine/wiGraphicsDevice_DX12.h index ff6c874ca..b4c29db07 100644 --- a/WickedEngine/wiGraphicsDevice_DX12.h +++ b/WickedEngine/wiGraphicsDevice_DX12.h @@ -307,6 +307,7 @@ namespace wi::graphics void DispatchMeshIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override; void CopyResource(const GPUResource* pDst, const GPUResource* pSrc, CommandList cmd) override; void CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd) override; + void CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox) override; void QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override; void QueryEnd(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override; void QueryResolve(const GPUQueryHeap* heap, uint32_t index, uint32_t count, const GPUBuffer* dest, uint64_t dest_offset, CommandList cmd) override; diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.cpp b/WickedEngine/wiGraphicsDevice_Vulkan.cpp index 271e31d44..0f2477092 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.cpp +++ b/WickedEngine/wiGraphicsDevice_Vulkan.cpp @@ -7706,8 +7706,8 @@ using namespace vulkan_internal; void GraphicsDevice_Vulkan::CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd) { CommandList_Vulkan& commandlist = GetCommandList(cmd); - auto internal_state_src = to_internal((const GPUBuffer*)pSrc); - auto internal_state_dst = to_internal((const GPUBuffer*)pDst); + auto internal_state_src = to_internal(pSrc); + auto internal_state_dst = to_internal(pDst); VkBufferCopy copy = {}; copy.srcOffset = src_offset; @@ -7720,6 +7720,55 @@ using namespace vulkan_internal; 1, © ); } + void GraphicsDevice_Vulkan::CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox) + { + CommandList_Vulkan& commandlist = GetCommandList(cmd); + auto src_internal = to_internal(src); + auto dst_internal = to_internal(dst); + + VkImageCopy copy = {}; + copy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.dstSubresource.baseArrayLayer = dstSlice; + copy.dstSubresource.layerCount = 1; + copy.dstSubresource.mipLevel = dstMip; + copy.dstOffset.x = dstX; + copy.dstOffset.y = dstY; + copy.dstOffset.z = dstZ; + + copy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy.srcSubresource.baseArrayLayer = srcSlice; + copy.srcSubresource.layerCount = 1; + copy.srcSubresource.mipLevel = srcMip; + + if (srcbox == nullptr) + { + copy.srcOffset.x = 0; + copy.srcOffset.y = 0; + copy.srcOffset.z = 0; + copy.extent.width = std::min(dst->desc.width, src->desc.width); + copy.extent.height = std::min(dst->desc.height, src->desc.height); + copy.extent.depth = std::min(dst->desc.depth, src->desc.depth); + } + else + { + copy.srcOffset.x = srcbox->left; + copy.srcOffset.y = srcbox->top; + copy.srcOffset.z = srcbox->front; + copy.extent.width = srcbox->right - srcbox->left; + copy.extent.height = srcbox->bottom - srcbox->top; + copy.extent.depth = srcbox->back - srcbox->front; + } + + vkCmdCopyImage( + commandlist.GetCommandBuffer(), + src_internal->resource, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + dst_internal->resource, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 1, + © + ); + } void GraphicsDevice_Vulkan::QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) { CommandList_Vulkan& commandlist = GetCommandList(cmd); diff --git a/WickedEngine/wiGraphicsDevice_Vulkan.h b/WickedEngine/wiGraphicsDevice_Vulkan.h index 19d5d6650..0c1f0f393 100644 --- a/WickedEngine/wiGraphicsDevice_Vulkan.h +++ b/WickedEngine/wiGraphicsDevice_Vulkan.h @@ -402,6 +402,7 @@ namespace wi::graphics void DispatchMeshIndirect(const GPUBuffer* args, uint64_t args_offset, CommandList cmd) override; void CopyResource(const GPUResource* pDst, const GPUResource* pSrc, CommandList cmd) override; void CopyBuffer(const GPUBuffer* pDst, uint64_t dst_offset, const GPUBuffer* pSrc, uint64_t src_offset, uint64_t size, CommandList cmd) override; + void CopyTexture(const Texture* dst, uint32_t dstX, uint32_t dstY, uint32_t dstZ, uint32_t dstMip, uint32_t dstSlice, const Texture* src, uint32_t srcMip, uint32_t srcSlice, CommandList cmd, const Box* srcbox) override; void QueryBegin(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override; void QueryEnd(const GPUQueryHeap* heap, uint32_t index, CommandList cmd) override; void QueryResolve(const GPUQueryHeap* heap, uint32_t index, uint32_t count, const GPUBuffer* dest, uint64_t dest_offset, CommandList cmd) override; diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 066293d5a..086a0193a 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -233,13 +233,6 @@ namespace wi::terrain bool success = device->CreateTexture(&desc, nullptr, &texture); assert(success); - for (uint32_t i = 0; i < texture.desc.mip_levels; ++i) - { - int subresource = 0; - subresource = device->CreateSubresource(&texture, SubresourceType::UAV, 0, 1, i, 1); - assert(subresource == i); - } - residencyMap = {}; feedbackMap = {}; requestBuffer = {}; @@ -1459,13 +1452,17 @@ namespace wi::terrain // This should have been created on generation thread, but if not (serialized), create it last minute: CreateChunkRegionTexture(chunk_data); - const uint32_t min_resolution = 128u; + const uint32_t min_resolution = 256u; + const uint32_t min_mips = 7u; #ifdef TERRAIN_VIRTUAL_TEXTURE_DEBUG const uint32_t max_resolution = 2048; + const uint32_t max_mips = 10u; #else const uint32_t max_resolution = 16384u; + const uint32_t max_mips = 13u; #endif // TERRAIN_VIRTUAL_TEXTURE_DEBUG const uint32_t required_resolution = dist < 2 ? max_resolution : min_resolution; + const uint32_t required_mips = dist < 2 ? max_mips : min_mips; chunk_data.vt.resize(3); // base, normal, surface for (uint32_t map_type = 0; map_type < chunk_data.vt.size(); ++map_type) @@ -1480,17 +1477,21 @@ namespace wi::terrain desc.width = required_resolution; desc.height = required_resolution; desc.misc_flags = ResourceMiscFlag::SPARSE; - desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS; - desc.mip_levels = 0; + desc.bind_flags = BindFlag::SHADER_RESOURCE; + desc.mip_levels = required_mips; desc.layout = ResourceState::SHADER_RESOURCE_COMPUTE; - if (map_type == MaterialComponent::NORMALMAP) + switch (map_type) { - desc.format = Format::R8G8_UNORM; - } - else - { - desc.format = Format::R8G8B8A8_UNORM; + default: + desc.format = Format::BC1_UNORM; + break; + case MaterialComponent::NORMALMAP: + desc.format = Format::BC5_UNORM; + break; + case MaterialComponent::SURFACEMAP: + desc.format = Format::BC3_UNORM; + break; } vt.init(page_allocator, desc); @@ -1504,8 +1505,8 @@ namespace wi::terrain material->textures[map_type].lod_clamp = (float)vt.lod_count - 1; virtual_textures_in_use.push_back(&vt); - virtual_texture_barriers_before_update.push_back(GPUBarrier::Image(&vt.texture, vt.texture.desc.layout, ResourceState::UNORDERED_ACCESS)); - virtual_texture_barriers_after_update.push_back(GPUBarrier::Image(&vt.texture, ResourceState::UNORDERED_ACCESS, vt.texture.desc.layout)); + virtual_texture_barriers_before_update.push_back(GPUBarrier::Image(&vt.texture, vt.texture.desc.layout, ResourceState::COPY_DST)); + virtual_texture_barriers_after_update.push_back(GPUBarrier::Image(&vt.texture, ResourceState::COPY_DST, vt.texture.desc.layout)); if (!vt.residencyMap.IsValid()) continue; @@ -1668,6 +1669,46 @@ namespace wi::terrain material_HighAltitude.WriteShaderMaterial(&materials[3]); device->BindDynamicConstantBuffer(materials, 0, cmd); + static Texture bc1_raw; + if (!bc1_raw.IsValid()) + { + TextureDesc td; + td.width = 512 / 4; + td.height = 256 / 4; + td.format = Format::R32G32_UINT; + td.bind_flags = BindFlag::UNORDERED_ACCESS; + td.layout = ResourceState::UNORDERED_ACCESS; + bool success = device->CreateTexture(&td, nullptr, &bc1_raw); + assert(success); + } + static Texture bc3_raw; + if (!bc3_raw.IsValid()) + { + TextureDesc td; + td.width = 256 / 4; + td.height = 256 / 4; + td.format = Format::R32G32B32A32_UINT; + td.bind_flags = BindFlag::UNORDERED_ACCESS; + td.layout = ResourceState::UNORDERED_ACCESS; + bool success = device->CreateTexture(&td, nullptr, &bc3_raw); + assert(success); + } + static Texture bc5_raw; + if (!bc5_raw.IsValid()) + { + TextureDesc td; + td.width = 256 / 4; + td.height = 256 / 4; + td.format = Format::R32G32B32A32_UINT; + td.bind_flags = BindFlag::UNORDERED_ACCESS; + td.layout = ResourceState::UNORDERED_ACCESS; + bool success = device->CreateTexture(&td, nullptr, &bc5_raw); + assert(success); + } + device->BindUAV(&bc1_raw, 0, cmd); + device->BindUAV(&bc3_raw, 1, cmd); + device->BindUAV(&bc5_raw, 2, cmd); + for (const VirtualTexture* vt : virtual_textures_in_use) { for (auto& request : vt->update_requests) @@ -1692,18 +1733,53 @@ namespace wi::terrain std::min(request_lod_resolution.x, vt->texture.sparse_properties->tile_width), std::min(request_lod_resolution.y, vt->texture.sparse_properties->tile_height) ); + const uint2 bc_size = uint2( + (size.x + 3u) / 4u, + (size.y + 3u) / 4u + ); TerrainVirtualTexturePush push; push.offset = uint2( request.tile_x * size.x, request.tile_y * size.y ); + push.resolution_rcp.x = 1.0f / request_lod_resolution.x; + push.resolution_rcp.y = 1.0f / request_lod_resolution.y; push.map_type = vt->map_type; push.region_weights_textureRO = device->GetDescriptorIndex(&vt->region_weights_texture, SubresourceType::SRV); - push.output_textureRW = device->GetDescriptorIndex(&vt->texture, SubresourceType::UAV, mip); device->PushConstants(&push, sizeof(push), cmd); - device->Dispatch((size.x + 7u) / 8u, (size.y + 7u) / 8u, 1, cmd); + device->Dispatch((bc_size.x + 7u) / 8u, (bc_size.y + 7u) / 8u, 1, cmd); + + // Sadly we can't write directly into block compressed texture, so we must copy from raw block format to real BC format: + const Texture* bc_raw = nullptr; + switch (vt->map_type) + { + default: + bc_raw = &bc1_raw; + break; + case MaterialComponent::NORMALMAP: + bc_raw = &bc5_raw; + break; + case MaterialComponent::SURFACEMAP: + bc_raw = &bc3_raw; + break; + } + GPUBarrier barrier = GPUBarrier::Image(bc_raw, bc_raw->desc.layout, ResourceState::COPY_SRC); + device->Barrier(&barrier, 1, cmd); + Box srcbox; + srcbox.left = 0; + srcbox.right = bc_size.x; + srcbox.top = 0; + srcbox.bottom = bc_size.y; + srcbox.front = 0; + srcbox.back = 1; + device->CopyTexture( + &vt->texture, request.tile_x * size.x, request.tile_y * size.y, 0, mip, 0, + bc_raw, 0, 0, cmd, &srcbox + ); + std::swap(barrier.image.layout_before, barrier.image.layout_after); + device->Barrier(&barrier, 1, cmd); } } vt->update_requests.clear(); diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 77dc05a50..236eaf949 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 77; + const int revision = 78; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision); diff --git a/third_party_software.txt b/third_party_software.txt index 06774724f..41d54d484 100644 --- a/third_party_software.txt +++ b/third_party_software.txt @@ -508,6 +508,36 @@ ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. + +############################################################################################################################### + +Compressonator: https://github.com/GPUOpen-Tools/compressonator + +//=============================================================================== +// Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//=============================================================================== + + + ############################################################################################################################### ###############################################################################################################################