diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/pica.h | 105 | ||||
| -rw-r--r-- | src/video_core/rasterizer.cpp | 122 |
2 files changed, 225 insertions, 2 deletions
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index f288615b8..7bd4388b5 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 7 | #include <cstddef> | 8 | #include <cstddef> |
| 8 | #include <initializer_list> | 9 | #include <initializer_list> |
| 9 | #include <map> | 10 | #include <map> |
| @@ -133,7 +134,97 @@ struct Regs { | |||
| 133 | INSERT_PADDING_WORDS(0x8); | 134 | INSERT_PADDING_WORDS(0x8); |
| 134 | BitField<0, 4, TextureFormat> texture0_format; | 135 | BitField<0, 4, TextureFormat> texture0_format; |
| 135 | 136 | ||
| 136 | INSERT_PADDING_WORDS(0x81); | 137 | INSERT_PADDING_WORDS(0x31); |
| 138 | |||
| 139 | // 0xc0-0xff: Texture Combiner (akin to glTexEnv) | ||
| 140 | struct TevStageConfig { | ||
| 141 | enum class Source : u32 { | ||
| 142 | PrimaryColor = 0x0, | ||
| 143 | Texture0 = 0x3, | ||
| 144 | Texture1 = 0x4, | ||
| 145 | Texture2 = 0x5, | ||
| 146 | Texture3 = 0x6, | ||
| 147 | // 0x7-0xc = primary color?? | ||
| 148 | Constant = 0xe, | ||
| 149 | Previous = 0xf, | ||
| 150 | }; | ||
| 151 | |||
| 152 | enum class ColorModifier : u32 { | ||
| 153 | SourceColor = 0, | ||
| 154 | OneMinusSourceColor = 1, | ||
| 155 | SourceAlpha = 2, | ||
| 156 | OneMinusSourceAlpha = 3, | ||
| 157 | |||
| 158 | // Other values seem to be non-standard extensions | ||
| 159 | }; | ||
| 160 | |||
| 161 | enum class AlphaModifier : u32 { | ||
| 162 | SourceAlpha = 0, | ||
| 163 | OneMinusSourceAlpha = 1, | ||
| 164 | |||
| 165 | // Other values seem to be non-standard extensions | ||
| 166 | }; | ||
| 167 | |||
| 168 | enum class Operation : u32 { | ||
| 169 | Replace = 0, | ||
| 170 | Modulate = 1, | ||
| 171 | Add = 2, | ||
| 172 | AddSigned = 3, | ||
| 173 | Lerp = 4, | ||
| 174 | Subtract = 5, | ||
| 175 | }; | ||
| 176 | |||
| 177 | union { | ||
| 178 | BitField< 0, 4, Source> color_source1; | ||
| 179 | BitField< 4, 4, Source> color_source2; | ||
| 180 | BitField< 8, 4, Source> color_source3; | ||
| 181 | BitField<16, 4, Source> alpha_source1; | ||
| 182 | BitField<20, 4, Source> alpha_source2; | ||
| 183 | BitField<24, 4, Source> alpha_source3; | ||
| 184 | }; | ||
| 185 | |||
| 186 | union { | ||
| 187 | BitField< 0, 4, ColorModifier> color_modifier1; | ||
| 188 | BitField< 4, 4, ColorModifier> color_modifier2; | ||
| 189 | BitField< 8, 4, ColorModifier> color_modifier3; | ||
| 190 | BitField<12, 3, AlphaModifier> alpha_modifier1; | ||
| 191 | BitField<16, 3, AlphaModifier> alpha_modifier2; | ||
| 192 | BitField<20, 3, AlphaModifier> alpha_modifier3; | ||
| 193 | }; | ||
| 194 | |||
| 195 | union { | ||
| 196 | BitField< 0, 4, Operation> color_op; | ||
| 197 | BitField<16, 4, Operation> alpha_op; | ||
| 198 | }; | ||
| 199 | |||
| 200 | union { | ||
| 201 | BitField< 0, 8, u32> const_r; | ||
| 202 | BitField< 8, 8, u32> const_g; | ||
| 203 | BitField<16, 8, u32> const_b; | ||
| 204 | BitField<24, 8, u32> const_a; | ||
| 205 | }; | ||
| 206 | |||
| 207 | INSERT_PADDING_WORDS(0x1); | ||
| 208 | }; | ||
| 209 | |||
| 210 | TevStageConfig tev_stage0; | ||
| 211 | INSERT_PADDING_WORDS(0x3); | ||
| 212 | TevStageConfig tev_stage1; | ||
| 213 | INSERT_PADDING_WORDS(0x3); | ||
| 214 | TevStageConfig tev_stage2; | ||
| 215 | INSERT_PADDING_WORDS(0x3); | ||
| 216 | TevStageConfig tev_stage3; | ||
| 217 | INSERT_PADDING_WORDS(0x13); | ||
| 218 | TevStageConfig tev_stage4; | ||
| 219 | INSERT_PADDING_WORDS(0x3); | ||
| 220 | TevStageConfig tev_stage5; | ||
| 221 | INSERT_PADDING_WORDS(0x13); | ||
| 222 | |||
| 223 | const std::array<Regs::TevStageConfig,6> GetTevStages() const { | ||
| 224 | return { tev_stage0, tev_stage1, | ||
| 225 | tev_stage2, tev_stage3, | ||
| 226 | tev_stage4, tev_stage5 }; | ||
| 227 | }; | ||
| 137 | 228 | ||
| 138 | struct { | 229 | struct { |
| 139 | enum ColorFormat : u32 { | 230 | enum ColorFormat : u32 { |
| @@ -444,6 +535,12 @@ struct Regs { | |||
| 444 | ADD_FIELD(viewport_corner); | 535 | ADD_FIELD(viewport_corner); |
| 445 | ADD_FIELD(texture0); | 536 | ADD_FIELD(texture0); |
| 446 | ADD_FIELD(texture0_format); | 537 | ADD_FIELD(texture0_format); |
| 538 | ADD_FIELD(tev_stage0); | ||
| 539 | ADD_FIELD(tev_stage1); | ||
| 540 | ADD_FIELD(tev_stage2); | ||
| 541 | ADD_FIELD(tev_stage3); | ||
| 542 | ADD_FIELD(tev_stage4); | ||
| 543 | ADD_FIELD(tev_stage5); | ||
| 447 | ADD_FIELD(framebuffer); | 544 | ADD_FIELD(framebuffer); |
| 448 | ADD_FIELD(vertex_attributes); | 545 | ADD_FIELD(vertex_attributes); |
| 449 | ADD_FIELD(index_array); | 546 | ADD_FIELD(index_array); |
| @@ -503,6 +600,12 @@ ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | |||
| 503 | ASSERT_REG_POSITION(viewport_corner, 0x68); | 600 | ASSERT_REG_POSITION(viewport_corner, 0x68); |
| 504 | ASSERT_REG_POSITION(texture0, 0x81); | 601 | ASSERT_REG_POSITION(texture0, 0x81); |
| 505 | ASSERT_REG_POSITION(texture0_format, 0x8e); | 602 | ASSERT_REG_POSITION(texture0_format, 0x8e); |
| 603 | ASSERT_REG_POSITION(tev_stage0, 0xc0); | ||
| 604 | ASSERT_REG_POSITION(tev_stage1, 0xc8); | ||
| 605 | ASSERT_REG_POSITION(tev_stage2, 0xd0); | ||
| 606 | ASSERT_REG_POSITION(tev_stage3, 0xd8); | ||
| 607 | ASSERT_REG_POSITION(tev_stage4, 0xf0); | ||
| 608 | ASSERT_REG_POSITION(tev_stage5, 0xf8); | ||
| 506 | ASSERT_REG_POSITION(framebuffer, 0x110); | 609 | ASSERT_REG_POSITION(framebuffer, 0x110); |
| 507 | ASSERT_REG_POSITION(vertex_attributes, 0x200); | 610 | ASSERT_REG_POSITION(vertex_attributes, 0x200); |
| 508 | ASSERT_REG_POSITION(index_array, 0x227); | 611 | ASSERT_REG_POSITION(index_array, 0x227); |
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index f418518a1..5a4155c84 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp | |||
| @@ -165,12 +165,132 @@ void ProcessTriangle(const VertexShader::OutputVertex& v0, | |||
| 165 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) | 165 | (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) |
| 166 | }; | 166 | }; |
| 167 | 167 | ||
| 168 | // Texture environment - consists of 6 stages of color and alpha combining. | ||
| 169 | // | ||
| 170 | // Color combiners take three input color values from some source (e.g. interpolated | ||
| 171 | // vertex color, texture color, previous stage, etc), perform some very simple | ||
| 172 | // operations on each of them (e.g. inversion) and then calculate the output color | ||
| 173 | // with some basic arithmetic. Alpha combiners can be configured separately but work | ||
| 174 | // analogously. | ||
| 175 | Math::Vec4<u8> combiner_output; | ||
| 176 | for (auto tev_stage : registers.GetTevStages()) { | ||
| 177 | using Source = Regs::TevStageConfig::Source; | ||
| 178 | using ColorModifier = Regs::TevStageConfig::ColorModifier; | ||
| 179 | using AlphaModifier = Regs::TevStageConfig::AlphaModifier; | ||
| 180 | using Operation = Regs::TevStageConfig::Operation; | ||
| 181 | |||
| 182 | auto GetColorSource = [&](Source source) -> Math::Vec3<u8> { | ||
| 183 | switch (source) { | ||
| 184 | case Source::PrimaryColor: | ||
| 185 | return primary_color.rgb(); | ||
| 186 | |||
| 187 | case Source::Constant: | ||
| 188 | return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; | ||
| 189 | |||
| 190 | case Source::Previous: | ||
| 191 | return combiner_output.rgb(); | ||
| 192 | |||
| 193 | default: | ||
| 194 | ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source); | ||
| 195 | return {}; | ||
| 196 | } | ||
| 197 | }; | ||
| 198 | |||
| 199 | auto GetAlphaSource = [&](Source source) -> u8 { | ||
| 200 | switch (source) { | ||
| 201 | case Source::PrimaryColor: | ||
| 202 | return primary_color.a(); | ||
| 203 | |||
| 204 | case Source::Constant: | ||
| 205 | return tev_stage.const_a; | ||
| 206 | |||
| 207 | case Source::Previous: | ||
| 208 | return combiner_output.a(); | ||
| 209 | |||
| 210 | default: | ||
| 211 | ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source); | ||
| 212 | return 0; | ||
| 213 | } | ||
| 214 | }; | ||
| 215 | |||
| 216 | auto GetColorModifier = [](ColorModifier factor, const Math::Vec3<u8>& values) -> Math::Vec3<u8> { | ||
| 217 | switch (factor) | ||
| 218 | { | ||
| 219 | case ColorModifier::SourceColor: | ||
| 220 | return values; | ||
| 221 | default: | ||
| 222 | ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); | ||
| 223 | return {}; | ||
| 224 | } | ||
| 225 | }; | ||
| 226 | |||
| 227 | auto GetAlphaModifier = [](AlphaModifier factor, u8 value) -> u8 { | ||
| 228 | switch (factor) { | ||
| 229 | case AlphaModifier::SourceAlpha: | ||
| 230 | return value; | ||
| 231 | default: | ||
| 232 | ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); | ||
| 233 | return 0; | ||
| 234 | } | ||
| 235 | }; | ||
| 236 | |||
| 237 | auto ColorCombine = [](Operation op, const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> { | ||
| 238 | switch (op) { | ||
| 239 | case Operation::Replace: | ||
| 240 | return input[0]; | ||
| 241 | |||
| 242 | case Operation::Modulate: | ||
| 243 | return ((input[0] * input[1]) / 255).Cast<u8>(); | ||
| 244 | |||
| 245 | default: | ||
| 246 | ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op); | ||
| 247 | return {}; | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | |||
| 251 | auto AlphaCombine = [](Operation op, const std::array<u8,3>& input) -> u8 { | ||
| 252 | switch (op) { | ||
| 253 | case Operation::Replace: | ||
| 254 | return input[0]; | ||
| 255 | |||
| 256 | case Operation::Modulate: | ||
| 257 | return input[0] * input[1] / 255; | ||
| 258 | |||
| 259 | default: | ||
| 260 | ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op); | ||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | |||
| 265 | // color combiner | ||
| 266 | // NOTE: Not sure if the alpha combiner might use the color output of the previous | ||
| 267 | // stage as input. Hence, we currently don't directly write the result to | ||
| 268 | // combiner_output.rgb(), but instead store it in a temporary variable until | ||
| 269 | // alpha combining has been done. | ||
| 270 | Math::Vec3<u8> color_result[3] = { | ||
| 271 | GetColorModifier(tev_stage.color_modifier1, GetColorSource(tev_stage.color_source1)), | ||
| 272 | GetColorModifier(tev_stage.color_modifier2, GetColorSource(tev_stage.color_source2)), | ||
| 273 | GetColorModifier(tev_stage.color_modifier3, GetColorSource(tev_stage.color_source3)) | ||
| 274 | }; | ||
| 275 | auto color_output = ColorCombine(tev_stage.color_op, color_result); | ||
| 276 | |||
| 277 | // alpha combiner | ||
| 278 | std::array<u8,3> alpha_result = { | ||
| 279 | GetAlphaModifier(tev_stage.alpha_modifier1, GetAlphaSource(tev_stage.alpha_source1)), | ||
| 280 | GetAlphaModifier(tev_stage.alpha_modifier2, GetAlphaSource(tev_stage.alpha_source2)), | ||
| 281 | GetAlphaModifier(tev_stage.alpha_modifier3, GetAlphaSource(tev_stage.alpha_source3)) | ||
| 282 | }; | ||
| 283 | auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result); | ||
| 284 | |||
| 285 | combiner_output = Math::MakeVec(color_output, alpha_output); | ||
| 286 | } | ||
| 287 | |||
| 168 | u16 z = (u16)(((float)v0.screenpos[2].ToFloat32() * w0 + | 288 | u16 z = (u16)(((float)v0.screenpos[2].ToFloat32() * w0 + |
| 169 | (float)v1.screenpos[2].ToFloat32() * w1 + | 289 | (float)v1.screenpos[2].ToFloat32() * w1 + |
| 170 | (float)v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); // TODO: Shouldn't need to multiply by 65536? | 290 | (float)v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); // TODO: Shouldn't need to multiply by 65536? |
| 171 | SetDepth(x >> 4, y >> 4, z); | 291 | SetDepth(x >> 4, y >> 4, z); |
| 172 | 292 | ||
| 173 | DrawPixel(x >> 4, y >> 4, primary_color); | 293 | DrawPixel(x >> 4, y >> 4, combiner_output); |
| 174 | } | 294 | } |
| 175 | } | 295 | } |
| 176 | } | 296 | } |