mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40: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 | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <array> | ||||||
| #include <cstddef> | #include <cstddef> | ||||||
| #include <initializer_list> | #include <initializer_list> | ||||||
| #include <map> | #include <map> | ||||||
|  | @ -133,7 +134,97 @@ struct Regs { | ||||||
|     INSERT_PADDING_WORDS(0x8); |     INSERT_PADDING_WORDS(0x8); | ||||||
|     BitField<0, 4, TextureFormat> texture0_format; |     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 { |     struct { | ||||||
|         enum ColorFormat : u32 { |         enum ColorFormat : u32 { | ||||||
|  | @ -444,6 +535,12 @@ struct Regs { | ||||||
|         ADD_FIELD(viewport_corner); |         ADD_FIELD(viewport_corner); | ||||||
|         ADD_FIELD(texture0); |         ADD_FIELD(texture0); | ||||||
|         ADD_FIELD(texture0_format); |         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(framebuffer); | ||||||
|         ADD_FIELD(vertex_attributes); |         ADD_FIELD(vertex_attributes); | ||||||
|         ADD_FIELD(index_array); |         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(viewport_corner, 0x68); | ||||||
| ASSERT_REG_POSITION(texture0, 0x81); | ASSERT_REG_POSITION(texture0, 0x81); | ||||||
| ASSERT_REG_POSITION(texture0_format, 0x8e); | 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(framebuffer, 0x110); | ||||||
| ASSERT_REG_POSITION(vertex_attributes, 0x200); | ASSERT_REG_POSITION(vertex_attributes, 0x200); | ||||||
| ASSERT_REG_POSITION(index_array, 0x227); | 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) |                 (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 + |             u16 z = (u16)(((float)v0.screenpos[2].ToFloat32() * w0 + | ||||||
|                            (float)v1.screenpos[2].ToFloat32() * w1 + |                            (float)v1.screenpos[2].ToFloat32() * w1 + | ||||||
|                            (float)v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); // TODO: Shouldn't need to multiply by 65536?
 |                            (float)v2.screenpos[2].ToFloat32() * w2) * 65535.f / wsum); // TODO: Shouldn't need to multiply by 65536?
 | ||||||
|             SetDepth(x >> 4, y >> 4, z); |             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