diff options
-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 @@ #pragma once +#include <array> #include <cstddef> #include <initializer_list> #include <map> @@ -133,7 +134,97 @@ struct Regs { INSERT_PADDING_WORDS(0x8); BitField<0, 4, TextureFormat> texture0_format; - INSERT_PADDING_WORDS(0x81); + INSERT_PADDING_WORDS(0x31); + + // 0xc0-0xff: Texture Combiner (akin to glTexEnv) + struct TevStageConfig { + enum class Source : u32 { + PrimaryColor = 0x0, + Texture0 = 0x3, + Texture1 = 0x4, + Texture2 = 0x5, + Texture3 = 0x6, + // 0x7-0xc = primary color?? + Constant = 0xe, + Previous = 0xf, + }; + + enum class ColorModifier : u32 { + SourceColor = 0, + OneMinusSourceColor = 1, + SourceAlpha = 2, + OneMinusSourceAlpha = 3, + + // Other values seem to be non-standard extensions + }; + + enum class AlphaModifier : u32 { + SourceAlpha = 0, + OneMinusSourceAlpha = 1, + + // Other values seem to be non-standard extensions + }; + + enum class Operation : u32 { + Replace = 0, + Modulate = 1, + Add = 2, + AddSigned = 3, + Lerp = 4, + Subtract = 5, + }; + + union { + BitField< 0, 4, Source> color_source1; + BitField< 4, 4, Source> color_source2; + BitField< 8, 4, Source> color_source3; + BitField<16, 4, Source> alpha_source1; + BitField<20, 4, Source> alpha_source2; + BitField<24, 4, Source> alpha_source3; + }; + + union { + BitField< 0, 4, ColorModifier> color_modifier1; + BitField< 4, 4, ColorModifier> color_modifier2; + BitField< 8, 4, ColorModifier> color_modifier3; + BitField<12, 3, AlphaModifier> alpha_modifier1; + BitField<16, 3, AlphaModifier> alpha_modifier2; + BitField<20, 3, AlphaModifier> alpha_modifier3; + }; + + union { + BitField< 0, 4, Operation> color_op; + BitField<16, 4, Operation> alpha_op; + }; + + union { + BitField< 0, 8, u32> const_r; + BitField< 8, 8, u32> const_g; + BitField<16, 8, u32> const_b; + BitField<24, 8, u32> const_a; + }; + + INSERT_PADDING_WORDS(0x1); + }; + + TevStageConfig tev_stage0; + INSERT_PADDING_WORDS(0x3); + TevStageConfig tev_stage1; + INSERT_PADDING_WORDS(0x3); + TevStageConfig tev_stage2; + INSERT_PADDING_WORDS(0x3); + TevStageConfig tev_stage3; + INSERT_PADDING_WORDS(0x13); + TevStageConfig tev_stage4; + INSERT_PADDING_WORDS(0x3); + TevStageConfig tev_stage5; + INSERT_PADDING_WORDS(0x13); + + const std::array<Regs::TevStageConfig,6> GetTevStages() const { + return { tev_stage0, tev_stage1, + tev_stage2, tev_stage3, + tev_stage4, tev_stage5 }; + }; struct { enum ColorFormat : u32 { @@ -444,6 +535,12 @@ struct Regs { ADD_FIELD(viewport_corner); ADD_FIELD(texture0); ADD_FIELD(texture0_format); + ADD_FIELD(tev_stage0); + ADD_FIELD(tev_stage1); + ADD_FIELD(tev_stage2); + ADD_FIELD(tev_stage3); + ADD_FIELD(tev_stage4); + ADD_FIELD(tev_stage5); ADD_FIELD(framebuffer); ADD_FIELD(vertex_attributes); ADD_FIELD(index_array); @@ -503,6 +600,12 @@ ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); ASSERT_REG_POSITION(viewport_corner, 0x68); ASSERT_REG_POSITION(texture0, 0x81); ASSERT_REG_POSITION(texture0_format, 0x8e); +ASSERT_REG_POSITION(tev_stage0, 0xc0); +ASSERT_REG_POSITION(tev_stage1, 0xc8); +ASSERT_REG_POSITION(tev_stage2, 0xd0); +ASSERT_REG_POSITION(tev_stage3, 0xd8); +ASSERT_REG_POSITION(tev_stage4, 0xf0); +ASSERT_REG_POSITION(tev_stage5, 0xf8); ASSERT_REG_POSITION(framebuffer, 0x110); ASSERT_REG_POSITION(vertex_attributes, 0x200); 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, (u8)(GetInterpolatedAttribute(v0.color.a(), v1.color.a(), v2.color.a()).ToFloat32() * 255) }; + // Texture environment - consists of 6 stages of color and alpha combining. + // + // Color combiners take three input color values from some source (e.g. interpolated + // vertex color, texture color, previous stage, etc), perform some very simple + // operations on each of them (e.g. inversion) and then calculate the output color + // with some basic arithmetic. Alpha combiners can be configured separately but work + // analogously. + Math::Vec4<u8> combiner_output; + for (auto tev_stage : registers.GetTevStages()) { + using Source = Regs::TevStageConfig::Source; + using ColorModifier = Regs::TevStageConfig::ColorModifier; + using AlphaModifier = Regs::TevStageConfig::AlphaModifier; + using Operation = Regs::TevStageConfig::Operation; + + auto GetColorSource = [&](Source source) -> Math::Vec3<u8> { + switch (source) { + case Source::PrimaryColor: + return primary_color.rgb(); + + case Source::Constant: + return {tev_stage.const_r, tev_stage.const_g, tev_stage.const_b}; + + case Source::Previous: + return combiner_output.rgb(); + + default: + ERROR_LOG(GPU, "Unknown color combiner source %d\n", (int)source); + return {}; + } + }; + + auto GetAlphaSource = [&](Source source) -> u8 { + switch (source) { + case Source::PrimaryColor: + return primary_color.a(); + + case Source::Constant: + return tev_stage.const_a; + + case Source::Previous: + return combiner_output.a(); + + default: + ERROR_LOG(GPU, "Unknown alpha combiner source %d\n", (int)source); + return 0; + } + }; + + auto GetColorModifier = [](ColorModifier factor, const Math::Vec3<u8>& values) -> Math::Vec3<u8> { + switch (factor) + { + case ColorModifier::SourceColor: + return values; + default: + ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); + return {}; + } + }; + + auto GetAlphaModifier = [](AlphaModifier factor, u8 value) -> u8 { + switch (factor) { + case AlphaModifier::SourceAlpha: + return value; + default: + ERROR_LOG(GPU, "Unknown color factor %d\n", (int)factor); + return 0; + } + }; + + auto ColorCombine = [](Operation op, const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> { + switch (op) { + case Operation::Replace: + return input[0]; + + case Operation::Modulate: + return ((input[0] * input[1]) / 255).Cast<u8>(); + + default: + ERROR_LOG(GPU, "Unknown color combiner operation %d\n", (int)op); + return {}; + } + }; + + auto AlphaCombine = [](Operation op, const std::array<u8,3>& input) -> u8 { + switch (op) { + case Operation::Replace: + return input[0]; + + case Operation::Modulate: + return input[0] * input[1] / 255; + + default: + ERROR_LOG(GPU, "Unknown alpha combiner operation %d\n", (int)op); + return 0; + } + }; + + // color combiner + // NOTE: Not sure if the alpha combiner might use the color output of the previous + // stage as input. Hence, we currently don't directly write the result to + // combiner_output.rgb(), but instead store it in a temporary variable until + // alpha combining has been done. + Math::Vec3<u8> color_result[3] = { + GetColorModifier(tev_stage.color_modifier1, GetColorSource(tev_stage.color_source1)), + GetColorModifier(tev_stage.color_modifier2, GetColorSource(tev_stage.color_source2)), + GetColorModifier(tev_stage.color_modifier3, GetColorSource(tev_stage.color_source3)) + }; + auto color_output = ColorCombine(tev_stage.color_op, color_result); + + // alpha combiner + std::array<u8,3> alpha_result = { + GetAlphaModifier(tev_stage.alpha_modifier1, GetAlphaSource(tev_stage.alpha_source1)), + GetAlphaModifier(tev_stage.alpha_modifier2, GetAlphaSource(tev_stage.alpha_source2)), + GetAlphaModifier(tev_stage.alpha_modifier3, GetAlphaSource(tev_stage.alpha_source3)) + }; + auto alpha_output = AlphaCombine(tev_stage.alpha_op, alpha_result); + + combiner_output = Math::MakeVec(color_output, alpha_output); + } + u16 z = (u16)(((float)v0.screenpos[2].ToFloat32() * w0 + (float)v1.screenpos[2].ToFloat32() * w1 + (float)v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); // TODO: Shouldn't need to multiply by 65536? SetDepth(x >> 4, y >> 4, z); - DrawPixel(x >> 4, y >> 4, primary_color); + DrawPixel(x >> 4, y >> 4, combiner_output); } } } |