mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Pica/Rasterizer: Add initial implementation of texture combiners.
This commit is contained in:
		
							parent
							
								
									c4691b784b
								
							
						
					
					
						commit
						27cab6477e
					
				
					 2 changed files with 225 additions and 2 deletions
				
			
		|  | @ -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); | ||||
|  |  | |||
|  | @ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue