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