/ src / video_core / renderer_software / sw_texturing.cpp
sw_texturing.cpp
  1  // Copyright 2017 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #include <algorithm>
  6  #include "common/assert.h"
  7  #include "common/common_types.h"
  8  #include "common/vector_math.h"
  9  #include "video_core/pica/regs_texturing.h"
 10  #include "video_core/renderer_software/sw_texturing.h"
 11  
 12  namespace SwRenderer {
 13  
 14  using TevStageConfig = Pica::TexturingRegs::TevStageConfig;
 15  
 16  int GetWrappedTexCoord(Pica::TexturingRegs::TextureConfig::WrapMode mode, s32 val, u32 size) {
 17      using TextureConfig = Pica::TexturingRegs::TextureConfig;
 18  
 19      switch (mode) {
 20      case TextureConfig::ClampToEdge2:
 21          // For negative coordinate, ClampToEdge2 behaves the same as Repeat
 22          if (val < 0) {
 23              return static_cast<s32>(static_cast<u32>(val) % size);
 24          }
 25          [[fallthrough]];
 26      case TextureConfig::ClampToEdge:
 27          val = std::max(val, 0);
 28          val = std::min(val, static_cast<s32>(size) - 1);
 29          return val;
 30      case TextureConfig::ClampToBorder:
 31          return val;
 32      case TextureConfig::ClampToBorder2:
 33      // For ClampToBorder2, the case of positive coordinate beyond the texture size is already
 34      // handled outside. Here we only handle the negative coordinate in the same way as Repeat.
 35      case TextureConfig::Repeat2:
 36      case TextureConfig::Repeat3:
 37      case TextureConfig::Repeat:
 38          return static_cast<s32>(static_cast<u32>(val) % size);
 39      case TextureConfig::MirroredRepeat: {
 40          u32 coord = (static_cast<u32>(val) % (2 * size));
 41          if (coord >= size) {
 42              coord = 2 * size - 1 - coord;
 43          }
 44          return static_cast<s32>(coord);
 45      }
 46      default:
 47          LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode {:x}", (int)mode);
 48          UNIMPLEMENTED();
 49          return 0;
 50      }
 51  };
 52  
 53  Common::Vec3<u8> GetColorModifier(TevStageConfig::ColorModifier factor,
 54                                    const Common::Vec4<u8>& values) {
 55      using ColorModifier = TevStageConfig::ColorModifier;
 56  
 57      switch (factor) {
 58      case ColorModifier::SourceColor:
 59          return values.rgb();
 60      case ColorModifier::OneMinusSourceColor:
 61          return (Common::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
 62      case ColorModifier::SourceAlpha:
 63          return values.aaa();
 64      case ColorModifier::OneMinusSourceAlpha:
 65          return (Common::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
 66      case ColorModifier::SourceRed:
 67          return values.rrr();
 68      case ColorModifier::OneMinusSourceRed:
 69          return (Common::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
 70      case ColorModifier::SourceGreen:
 71          return values.ggg();
 72      case ColorModifier::OneMinusSourceGreen:
 73          return (Common::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
 74      case ColorModifier::SourceBlue:
 75          return values.bbb();
 76      case ColorModifier::OneMinusSourceBlue:
 77          return (Common::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
 78      }
 79      UNREACHABLE();
 80  };
 81  
 82  u8 GetAlphaModifier(TevStageConfig::AlphaModifier factor, const Common::Vec4<u8>& values) {
 83      using AlphaModifier = TevStageConfig::AlphaModifier;
 84  
 85      switch (factor) {
 86      case AlphaModifier::SourceAlpha:
 87          return values.a();
 88      case AlphaModifier::OneMinusSourceAlpha:
 89          return 255 - values.a();
 90      case AlphaModifier::SourceRed:
 91          return values.r();
 92      case AlphaModifier::OneMinusSourceRed:
 93          return 255 - values.r();
 94      case AlphaModifier::SourceGreen:
 95          return values.g();
 96      case AlphaModifier::OneMinusSourceGreen:
 97          return 255 - values.g();
 98      case AlphaModifier::SourceBlue:
 99          return values.b();
100      case AlphaModifier::OneMinusSourceBlue:
101          return 255 - values.b();
102      }
103      UNREACHABLE();
104  };
105  
106  Common::Vec3<u8> ColorCombine(TevStageConfig::Operation op,
107                                std::span<const Common::Vec3<u8>, 3> input) {
108      using Operation = TevStageConfig::Operation;
109  
110      switch (op) {
111      case Operation::Replace:
112          return input[0];
113      case Operation::Modulate:
114          return ((input[0] * input[1]) / 255).Cast<u8>();
115      case Operation::Add: {
116          auto result = input[0] + input[1];
117          result.r() = std::min(255, result.r());
118          result.g() = std::min(255, result.g());
119          result.b() = std::min(255, result.b());
120          return result.Cast<u8>();
121      }
122      case Operation::AddSigned: {
123          // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
124          // (byte) 128 is correct
125          Common::Vec3i result =
126              input[0].Cast<s32>() + input[1].Cast<s32>() - Common::MakeVec<s32>(128, 128, 128);
127          result.r() = std::clamp<s32>(result.r(), 0, 255);
128          result.g() = std::clamp<s32>(result.g(), 0, 255);
129          result.b() = std::clamp<s32>(result.b(), 0, 255);
130          return result.Cast<u8>();
131      }
132      case Operation::Lerp:
133          return ((input[0] * input[2] +
134                   input[1] * (Common::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) /
135                  255)
136              .Cast<u8>();
137      case Operation::Subtract: {
138          auto result = input[0].Cast<s32>() - input[1].Cast<s32>();
139          result.r() = std::max(0, result.r());
140          result.g() = std::max(0, result.g());
141          result.b() = std::max(0, result.b());
142          return result.Cast<u8>();
143      }
144      case Operation::MultiplyThenAdd: {
145          auto result = (input[0] * input[1] + 255 * input[2].Cast<s32>()) / 255;
146          result.r() = std::min(255, result.r());
147          result.g() = std::min(255, result.g());
148          result.b() = std::min(255, result.b());
149          return result.Cast<u8>();
150      }
151      case Operation::AddThenMultiply: {
152          auto result = input[0] + input[1];
153          result.r() = std::min(255, result.r());
154          result.g() = std::min(255, result.g());
155          result.b() = std::min(255, result.b());
156          result = (result * input[2].Cast<s32>()) / 255;
157          return result.Cast<u8>();
158      }
159      case Operation::Dot3_RGB:
160      case Operation::Dot3_RGBA: {
161          // Not fully accurate.  Worst case scenario seems to yield a +/-3 error.  Some HW results
162          // indicate that the per-component computation can't have a higher precision than 1/256,
163          // while dot3_rgb((0x80,g0,b0), (0x7F,g1,b1)) and dot3_rgb((0x80,g0,b0), (0x80,g1,b1)) give
164          // different results.
165          s32 result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
166                       ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
167                       ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
168          result = std::clamp(result, 0, 255);
169          return Common::Vec3{result, result, result}.Cast<u8>();
170      }
171      default:
172          LOG_ERROR(HW_GPU, "Unknown color combiner operation {}", (int)op);
173          UNIMPLEMENTED();
174          return {0, 0, 0};
175      }
176  };
177  
178  u8 AlphaCombine(TevStageConfig::Operation op, const std::array<u8, 3>& input) {
179      switch (op) {
180          using Operation = TevStageConfig::Operation;
181      case Operation::Replace:
182          return input[0];
183      case Operation::Modulate:
184          return input[0] * input[1] / 255;
185      case Operation::Add:
186          return std::min(255, input[0] + input[1]);
187      case Operation::AddSigned: {
188          // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
189          auto result = static_cast<s32>(input[0]) + static_cast<s32>(input[1]) - 128;
190          return static_cast<u8>(std::clamp<s32>(result, 0, 255));
191      }
192      case Operation::Lerp:
193          return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
194      case Operation::Subtract:
195          return std::max(0, static_cast<s32>(input[0]) - static_cast<s32>(input[1]));
196      case Operation::MultiplyThenAdd:
197          return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
198      case Operation::AddThenMultiply:
199          return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
200      default:
201          LOG_ERROR(HW_GPU, "Unknown alpha combiner operation {}", (int)op);
202          UNIMPLEMENTED();
203          return 0;
204      }
205  };
206  
207  } // namespace SwRenderer