shaderMethods.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2017 to 2019 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_RENDER_SHADER_METHODS
  24. #define DFPSR_RENDER_SHADER_METHODS
  25. #include <stdint.h>
  26. #include "../../math/FVector.h"
  27. #include "../../math/scalar.h"
  28. #include "../../base/simd3D.h"
  29. #include "../../image/ImageRgbaU8.h"
  30. #include "shaderTypes.h"
  31. #include "../constants.h"
  32. namespace dsr {
  33. namespace shaderMethods {
  34. // Returns the linear interpolation of the values using corresponding weight ratios for A, B and C in 4 pixels at the same time.
  35. inline F32x4 interpolate(const FVector3D &vertexData, const F32x4x3 &vertexWeights) {
  36. ALIGN16 F32x4 vMA = vertexData.x * vertexWeights.v1;
  37. ALIGN16 F32x4 vMB = vertexData.y * vertexWeights.v2;
  38. ALIGN16 F32x4 vMC = vertexData.z * vertexWeights.v3;
  39. return vMA + vMB + vMC;
  40. }
  41. inline rgba_F32 interpolateVertexColor(const FVector3D &red, const FVector3D &green, const FVector3D &blue, const FVector3D &alpha, const F32x4x3 &vertexWeights) {
  42. return rgba_F32(
  43. interpolate(red, vertexWeights),
  44. interpolate(green, vertexWeights),
  45. interpolate(blue, vertexWeights),
  46. interpolate(alpha, vertexWeights)
  47. );
  48. }
  49. // Returns (colorA * weightA + colorB * weightB) / 256 as bytes
  50. // weightA and weightB should contain pairs of the same 16-bit weights for each of the 4 pixels in the corresponding A and B colors
  51. inline U32x4 weightColors(const U32x4 &colorA, const U16x8 &weightA, const U32x4 &colorB, const U16x8 &weightB) {
  52. ALIGN16 U32x4 lowMask(0x00FF00FFu);
  53. ALIGN16 U16x8 lowColorA = U16x8(colorA & lowMask);
  54. ALIGN16 U16x8 lowColorB = U16x8(colorB & lowMask);
  55. ALIGN16 U32x4 highMask(0xFF00FF00u);
  56. ALIGN16 U16x8 highColorA = U16x8((colorA & highMask) >> 8);
  57. ALIGN16 U16x8 highColorB = U16x8((colorB & highMask) >> 8);
  58. ALIGN16 U32x4 lowColor = (((lowColorA * weightA) + (lowColorB * weightB))).get_U32();
  59. ALIGN16 U32x4 highColor = (((highColorA * weightA) + (highColorB * weightB))).get_U32();
  60. return (((lowColor >> 8) & lowMask) | (highColor & highMask));
  61. }
  62. // The more significant bits must be zero so that the lower bits can fill the space.
  63. // lowBits[x] < 2^16
  64. inline U16x8 repeatAs16Bits(const U32x4 &lowBits) {
  65. return U16x8(lowBits | (lowBits << 16));
  66. }
  67. // Returns 256 - weight
  68. inline U16x8 invertWeight(const U16x8 &weight) {
  69. return U16x8(0x01000100u) - weight;
  70. }
  71. inline U32x4 mix_L(const U32x4 &colorA, const U32x4 &colorB, const U32x4 &weight) {
  72. // Get inverse weights
  73. ALIGN16 U16x8 weightB = repeatAs16Bits(weight);
  74. ALIGN16 U16x8 weightA = invertWeight(weightB);
  75. // Multiply
  76. return weightColors(colorA, weightA, colorB, weightB);
  77. }
  78. inline U32x4 mix_BL(const U32x4 &colorA, const U32x4 &colorB, const U32x4 &colorC, const U32x4 &colorD, const U32x4 &weightX, const U32x4 &weightY) {
  79. // Get inverse weights
  80. ALIGN16 U16x8 weightXR = repeatAs16Bits(weightX);
  81. ALIGN16 U16x8 weightYB = repeatAs16Bits(weightY);
  82. ALIGN16 U16x8 weightXL = invertWeight(weightXR);
  83. ALIGN16 U16x8 weightYT = invertWeight(weightYB);
  84. // Multiply
  85. return weightColors(weightColors(colorA, weightXL, colorB, weightXR), weightYT, weightColors(colorC, weightXL, colorD, weightXR), weightYB);
  86. }
  87. // Single layer sampling methods
  88. inline U32x4 sample_U32(const TextureRgbaLayer *source, const U32x4 &col, const U32x4 &row) {
  89. #ifdef USE_AVX2
  90. ALIGN16 U32x4 pixelOffset((col + (row << (source->strideShift - 2)))); // PixelOffset = Column + Row * PixelStride
  91. return U32x4(GATHER_U32_AVX2(source->data, pixelOffset.v, 4));
  92. #else
  93. UVector4D byteOffset = ((col << 2) + (row << source->strideShift)).get(); // ByteOffset = Column * 4 + Row * ByteStride
  94. return U32x4(
  95. *((uint32_t*)(source->data + byteOffset.x)),
  96. *((uint32_t*)(source->data + byteOffset.y)),
  97. *((uint32_t*)(source->data + byteOffset.z)),
  98. *((uint32_t*)(source->data + byteOffset.w))
  99. );
  100. #endif
  101. }
  102. // How many mip levels down from here should be sampled for the given texture coordinates
  103. template<int maxOffset>
  104. inline int getMipLevelOffset(const TextureRgbaLayer *source, const F32x4 &u, const F32x4 &v) {
  105. FVector4D ua = u.get();
  106. FVector4D va = v.get();
  107. float offsetUX = fabs(ua.x - ua.y);
  108. float offsetUY = fabs(ua.x - ua.z);
  109. float offsetVX = fabs(va.x - va.y);
  110. float offsetVY = fabs(va.x - va.z);
  111. float offsetU = max(offsetUX, offsetUY) * source->width;
  112. float offsetV = max(offsetVX, offsetVY) * source->height;
  113. float offset = max(offsetU, offsetV);
  114. // This log2 approximation has to be adapted if the number of mip levels changes.
  115. static_assert(MIP_BIN_COUNT == 5, "Changing MIP_BIN_COUNT must also adapt shaderMethods::getMipLevelOffset");
  116. int result = 0;
  117. if (offset > 2.0f) { result = 1; }
  118. if (offset > 4.0f) { result = 2; }
  119. if (offset > 8.0f) { result = 3; }
  120. if (offset > 16.0f) { result = 4; }
  121. return result;
  122. }
  123. inline int getMipLevel(const TextureRgba *source, const F32x4 &u, const F32x4 &v) {
  124. return getMipLevelOffset<MIP_BIN_COUNT - 1>(source->mips, u, v);
  125. }
  126. // Single layer sampling method
  127. // Precondition: u, v > -0.875f = 1 - (0.5 / minimumMipSize)
  128. template<Interpolation INTERPOLATION>
  129. inline U32x4 sample_U32(const TextureRgbaLayer *source, const F32x4 &u, const F32x4 &v) {
  130. if (INTERPOLATION == Interpolation::BL) {
  131. ALIGN16 F32x4 uLow(u + source->halfPixelOffsetU);
  132. ALIGN16 F32x4 vLow(v + source->halfPixelOffsetV);
  133. ALIGN16 U32x4 subPixLowX(truncateToU32(uLow * source->subWidth)); // SubPixelLowX = ULow * (Width * 256)
  134. ALIGN16 U32x4 subPixLowY(truncateToU32(vLow * source->subHeight)); // SubPixelLowY = VLow * (Height * 256)
  135. ALIGN16 U32x4 weightX = subPixLowX & 255; // WeightX = SubPixelLowX % 256
  136. ALIGN16 U32x4 weightY = subPixLowY & 255; // WeightY = SubPixelLowY % 256
  137. ALIGN16 U32x4 pixLowX(subPixLowX >> 8); // PixelLowX = SubPixelLowX / 256
  138. ALIGN16 U32x4 pixLowY(subPixLowY >> 8); // PixelLowY = SubPixelLowY / 256
  139. ALIGN16 U32x4 wMask(source->widthMask);
  140. ALIGN16 U32x4 hMask(source->heightMask);
  141. ALIGN16 U32x4 colLow(pixLowX & wMask); // ColumnLow = PixelLowX % Width
  142. ALIGN16 U32x4 rowLow(pixLowY & hMask); // RowLow = PixelLowY % Height
  143. ALIGN16 U32x4 colHigh(((colLow + 1) & wMask)); // ColumnHigh = (ColumnLow + 1) % Width
  144. ALIGN16 U32x4 rowHigh(((rowLow + 1) & hMask)); // RowHigh = (RowLow + 1) % Height
  145. // Sample colors in the 4 closest pixels
  146. ALIGN16 U32x4 colorA(sample_U32(source, colLow, rowLow));
  147. ALIGN16 U32x4 colorB(sample_U32(source, colHigh, rowLow));
  148. ALIGN16 U32x4 colorC(sample_U32(source, colLow, rowHigh));
  149. ALIGN16 U32x4 colorD(sample_U32(source, colHigh, rowHigh));
  150. // Take a weighted average
  151. return shaderMethods::mix_BL(colorA, colorB, colorC, colorD, weightX, weightY);
  152. } else { // Interpolation::NN or unhandled
  153. ALIGN16 U32x4 pixX(truncateToU32(u * source->width)); // PixelX = U * Width
  154. ALIGN16 U32x4 pixY(truncateToU32(v * source->height)); // PixelY = V * Height
  155. ALIGN16 U32x4 col(pixX & source->widthMask); // Column = PixelX % Width
  156. ALIGN16 U32x4 row(pixY & source->heightMask); // Row = PixelY % Height
  157. return sample_U32(source, col, row);
  158. }
  159. }
  160. // Precondition: u, v > -0.875f = 1 - (0.5 / minimumMipSize)
  161. template<Interpolation INTERPOLATION, bool HIGH_QUALITY>
  162. inline rgba_F32 sample_F32(const TextureRgbaLayer *source, const F32x4 &u, const F32x4 &v) {
  163. if (INTERPOLATION == Interpolation::BL) {
  164. if (HIGH_QUALITY) { // High quality interpolation
  165. ALIGN16 F32x4 uLow(u + source->halfPixelOffsetU);
  166. ALIGN16 F32x4 vLow(v + source->halfPixelOffsetV);
  167. ALIGN16 F32x4 pixX = uLow * source->width; // PixelX = ULow * Width
  168. ALIGN16 F32x4 pixY = vLow * source->height; // PixelY = VLow * Height
  169. // Truncation can be used as floor for positive input
  170. ALIGN16 U32x4 pixLowX(truncateToU32(pixX)); // PixelLowX = floor(PixelX)
  171. ALIGN16 U32x4 pixLowY(truncateToU32(pixY)); // PixelLowY = floor(PixelY)
  172. ALIGN16 U32x4 wMask(source->widthMask);
  173. ALIGN16 U32x4 hMask(source->heightMask);
  174. ALIGN16 U32x4 colLow(pixLowX & wMask); // ColumnLow = PixelLowX % Width
  175. ALIGN16 U32x4 rowLow(pixLowY & hMask); // RowLow = PixelLowY % Height
  176. ALIGN16 U32x4 colHigh(((colLow + 1) & wMask)); // ColumnHigh = (ColumnLow + 1) % Width
  177. ALIGN16 U32x4 rowHigh(((rowLow + 1) & hMask)); // RowHigh = (RowLow + 1) % Height
  178. // Sample colors in the 4 closest pixels
  179. ALIGN16 rgba_F32 colorA(rgba_F32(sample_U32(source, colLow, rowLow)));
  180. ALIGN16 rgba_F32 colorB(rgba_F32(sample_U32(source, colHigh, rowLow)));
  181. ALIGN16 rgba_F32 colorC(rgba_F32(sample_U32(source, colLow, rowHigh)));
  182. ALIGN16 rgba_F32 colorD(rgba_F32(sample_U32(source, colHigh, rowHigh)));
  183. ALIGN16 F32x4 weightX = pixX - floatFromU32(pixLowX);
  184. ALIGN16 F32x4 weightY = pixY - floatFromU32(pixLowY);
  185. ALIGN16 F32x4 invWeightX = 1.0f - weightX;
  186. ALIGN16 F32x4 invWeightY = 1.0f - weightY;
  187. return (colorA * invWeightX + colorB * weightX) * invWeightY + (colorC * invWeightX + colorD * weightX) * weightY;
  188. } else { // Fast interpolation
  189. return rgba_F32(sample_U32<Interpolation::BL>(source, u, v));
  190. }
  191. } else { // Interpolation::NN or unhandled
  192. return rgba_F32(sample_U32<Interpolation::NN>(source, u, v));
  193. }
  194. }
  195. // Multi layer sampling method
  196. // Precondition: u, v > -0.875f = 1 - (0.5 / minimumMipSize)
  197. template<Interpolation INTERPOLATION>
  198. inline U32x4 sample_U32(const TextureRgba *source, const F32x4 &u, const F32x4 &v) {
  199. int mipLevel = getMipLevel(source, u, v);
  200. return sample_U32<INTERPOLATION>(&(source->mips[mipLevel]), u, v);
  201. }
  202. // Precondition: u, v > -0.875f = 1 - (0.5 / minimumMipSize)
  203. template<Interpolation INTERPOLATION, bool HIGH_QUALITY>
  204. inline rgba_F32 sample_F32(const TextureRgba *source, const F32x4 &u, const F32x4 &v) {
  205. int mipLevel = getMipLevel(source, u, v);
  206. return sample_F32<INTERPOLATION, HIGH_QUALITY>(&(source->mips[mipLevel]), u, v);
  207. }
  208. }
  209. }
  210. #endif