mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #3516 from wwylele/shadow-sw
SwRasterizer: Implement shadow mapping
This commit is contained in:
		
						commit
						384849232b
					
				
					 7 changed files with 172 additions and 10 deletions
				
			
		|  | @ -15,6 +15,12 @@ | ||||||
| namespace Pica { | namespace Pica { | ||||||
| 
 | 
 | ||||||
| struct FramebufferRegs { | struct FramebufferRegs { | ||||||
|  |     enum class FragmentOperationMode : u32 { | ||||||
|  |         Default = 0, | ||||||
|  |         Gas = 1, | ||||||
|  |         Shadow = 3, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     enum class LogicOp : u32 { |     enum class LogicOp : u32 { | ||||||
|         Clear = 0, |         Clear = 0, | ||||||
|         And = 1, |         And = 1, | ||||||
|  | @ -84,6 +90,7 @@ struct FramebufferRegs { | ||||||
| 
 | 
 | ||||||
|     struct { |     struct { | ||||||
|         union { |         union { | ||||||
|  |             BitField<0, 2, FragmentOperationMode> fragment_operation_mode; | ||||||
|             // If false, logic blending is used
 |             // If false, logic blending is used
 | ||||||
|             BitField<8, 1, u32> alphablend_enable; |             BitField<8, 1, u32> alphablend_enable; | ||||||
|         }; |         }; | ||||||
|  | @ -274,7 +281,14 @@ struct FramebufferRegs { | ||||||
|         ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format)); |         ASSERT_MSG(false, "Unknown depth format %u", static_cast<u32>(format)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     INSERT_PADDING_WORDS(0x20); |     INSERT_PADDING_WORDS(0x10); // Gas related registers
 | ||||||
|  | 
 | ||||||
|  |     union { | ||||||
|  |         BitField<0, 16, u32> constant; // float1.5.10
 | ||||||
|  |         BitField<16, 16, u32> linear;  // float1.5.10
 | ||||||
|  |     } shadow; | ||||||
|  | 
 | ||||||
|  |     INSERT_PADDING_WORDS(0xF); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static_assert(sizeof(FramebufferRegs) == 0x40 * sizeof(u32), | static_assert(sizeof(FramebufferRegs) == 0x40 * sizeof(u32), | ||||||
|  |  | ||||||
|  | @ -187,9 +187,15 @@ struct LightingRegs { | ||||||
|     BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
 |     BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
 | ||||||
| 
 | 
 | ||||||
|     union { |     union { | ||||||
|  |         BitField<0, 1, u32> enable_shadow; | ||||||
|         BitField<2, 2, LightingFresnelSelector> fresnel_selector; |         BitField<2, 2, LightingFresnelSelector> fresnel_selector; | ||||||
|         BitField<4, 4, LightingConfig> config; |         BitField<4, 4, LightingConfig> config; | ||||||
|  |         BitField<16, 1, u32> shadow_primary; | ||||||
|  |         BitField<17, 1, u32> shadow_secondary; | ||||||
|  |         BitField<18, 1, u32> shadow_invert; | ||||||
|  |         BitField<19, 1, u32> shadow_alpha; | ||||||
|         BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
 |         BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
 | ||||||
|  |         BitField<24, 2, u32> shadow_selector; | ||||||
|         BitField<27, 1, u32> clamp_highlights; |         BitField<27, 1, u32> clamp_highlights; | ||||||
|         BitField<28, 2, LightingBumpMode> bump_mode; |         BitField<28, 2, LightingBumpMode> bump_mode; | ||||||
|         BitField<30, 1, u32> disable_bump_renorm; |         BitField<30, 1, u32> disable_bump_renorm; | ||||||
|  | @ -198,6 +204,9 @@ struct LightingRegs { | ||||||
|     union { |     union { | ||||||
|         u32 raw; |         u32 raw; | ||||||
| 
 | 
 | ||||||
|  |         // Each bit specifies whether shadow should be applied for the corresponding light.
 | ||||||
|  |         BitField<0, 8, u32> disable_shadow; | ||||||
|  | 
 | ||||||
|         // Each bit specifies whether spot light attenuation should be applied for the corresponding
 |         // Each bit specifies whether spot light attenuation should be applied for the corresponding
 | ||||||
|         // light.
 |         // light.
 | ||||||
|         BitField<8, 8, u32> disable_spot_atten; |         BitField<8, 8, u32> disable_spot_atten; | ||||||
|  | @ -224,6 +233,10 @@ struct LightingRegs { | ||||||
|         return (config1.disable_spot_atten & (1 << index)) != 0; |         return (config1.disable_spot_atten & (1 << index)) != 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     bool IsShadowDisabled(unsigned index) const { | ||||||
|  |         return (config1.disable_shadow & (1 << index)) != 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     union { |     union { | ||||||
|         BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
 |         BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
 | ||||||
|         BitField<8, 5, u32> type;  ///< Type of LUT for which to set data
 |         BitField<8, 5, u32> type;  ///< Type of LUT for which to set data
 | ||||||
|  |  | ||||||
|  | @ -158,7 +158,12 @@ struct TexturingRegs { | ||||||
|         return address * 8; |         return address * 8; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     INSERT_PADDING_WORDS(0x3); |     union { | ||||||
|  |         BitField<0, 1, u32> orthographic; // 0: enable perspective divide
 | ||||||
|  |         BitField<1, 23, u32> bias;        // 23-bit fraction
 | ||||||
|  |     } shadow; | ||||||
|  | 
 | ||||||
|  |     INSERT_PADDING_WORDS(0x2); | ||||||
|     BitField<0, 4, TextureFormat> texture0_format; |     BitField<0, 4, TextureFormat> texture0_format; | ||||||
|     BitField<0, 1, u32> fragment_lighting_enable; |     BitField<0, 1, u32> fragment_lighting_enable; | ||||||
|     INSERT_PADDING_WORDS(0x1); |     INSERT_PADDING_WORDS(0x1); | ||||||
|  |  | ||||||
|  | @ -359,5 +359,54 @@ u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op) { | ||||||
|     UNREACHABLE(); |     UNREACHABLE(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | // Decode/Encode for shadow map format. It is similar to D24S8 format, but the depth field is in
 | ||||||
|  | // big-endian
 | ||||||
|  | static const Math::Vec2<u32> DecodeD24S8Shadow(const u8* bytes) { | ||||||
|  |     return {static_cast<u32>((bytes[0] << 16) | (bytes[1] << 8) | bytes[2]), bytes[3]}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void EncodeD24X8Shadow(u32 depth, u8* bytes) { | ||||||
|  |     bytes[2] = depth & 0xFF; | ||||||
|  |     bytes[1] = (depth >> 8) & 0xFF; | ||||||
|  |     bytes[0] = (depth >> 16) & 0xFF; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void EncodeX24S8Shadow(u8 stencil, u8* bytes) { | ||||||
|  |     bytes[3] = stencil; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DrawShadowMapPixel(int x, int y, u32 depth, u8 stencil) { | ||||||
|  |     const auto& framebuffer = g_state.regs.framebuffer.framebuffer; | ||||||
|  |     const auto& shadow = g_state.regs.framebuffer.shadow; | ||||||
|  |     const PAddr addr = framebuffer.GetColorBufferPhysicalAddress(); | ||||||
|  | 
 | ||||||
|  |     y = framebuffer.height - y; | ||||||
|  | 
 | ||||||
|  |     const u32 coarse_y = y & ~7; | ||||||
|  |     u32 bytes_per_pixel = 4; | ||||||
|  |     u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + | ||||||
|  |                      coarse_y * framebuffer.width * bytes_per_pixel; | ||||||
|  |     u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset; | ||||||
|  | 
 | ||||||
|  |     auto ref = DecodeD24S8Shadow(dst_pixel); | ||||||
|  |     u32 ref_z = ref.x; | ||||||
|  |     u32 ref_s = ref.y; | ||||||
|  | 
 | ||||||
|  |     if (depth < ref_z) { | ||||||
|  |         if (stencil == 0) { | ||||||
|  |             EncodeD24X8Shadow(depth, dst_pixel); | ||||||
|  |         } else { | ||||||
|  |             float16 constant = float16::FromRaw(shadow.constant); | ||||||
|  |             float16 linear = float16::FromRaw(shadow.linear); | ||||||
|  |             float16 x = float16::FromFloat32(static_cast<float>(depth) / ref_z); | ||||||
|  |             float16 stencil_new = float16::FromFloat32(stencil) / (constant + linear * x); | ||||||
|  |             stencil = static_cast<u8>(MathUtil::Clamp(stencil_new.ToFloat32(), 0.0f, 255.0f)); | ||||||
|  | 
 | ||||||
|  |             if (stencil < ref_s) | ||||||
|  |                 EncodeX24S8Shadow(stencil, dst_pixel); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Rasterizer
 | } // namespace Rasterizer
 | ||||||
| } // namespace Pica
 | } // namespace Pica
 | ||||||
|  |  | ||||||
|  | @ -25,5 +25,7 @@ Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4 | ||||||
| 
 | 
 | ||||||
| u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op); | u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op); | ||||||
| 
 | 
 | ||||||
|  | void DrawShadowMapPixel(int x, int y, u32 depth, u8 stencil); | ||||||
|  | 
 | ||||||
| } // namespace Rasterizer
 | } // namespace Rasterizer
 | ||||||
| } // namespace Pica
 | } // namespace Pica
 | ||||||
|  |  | ||||||
|  | @ -25,6 +25,16 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( | ||||||
|     const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view, |     const Math::Quaternion<float>& normquat, const Math::Vec3<float>& view, | ||||||
|     const Math::Vec4<u8> (&texture_color)[4]) { |     const Math::Vec4<u8> (&texture_color)[4]) { | ||||||
| 
 | 
 | ||||||
|  |     Math::Vec4<float> shadow; | ||||||
|  |     if (lighting.config0.enable_shadow) { | ||||||
|  |         shadow = texture_color[lighting.config0.shadow_selector].Cast<float>() / 255.0f; | ||||||
|  |         if (lighting.config0.shadow_invert) { | ||||||
|  |             shadow = Math::MakeVec(1.0f, 1.0f, 1.0f, 1.0f) - shadow; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         shadow = Math::MakeVec(1.0f, 1.0f, 1.0f, 1.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     Math::Vec3<float> surface_normal; |     Math::Vec3<float> surface_normal; | ||||||
|     Math::Vec3<float> surface_tangent; |     Math::Vec3<float> surface_tangent; | ||||||
| 
 | 
 | ||||||
|  | @ -278,11 +288,38 @@ std::tuple<Math::Vec4<u8>, Math::Vec4<u8>> ComputeFragmentsColors( | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         auto diffuse = |         auto diffuse = | ||||||
|             light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f(); |             (light_config.diffuse.ToVec3f() * dot_product + light_config.ambient.ToVec3f()) * | ||||||
|         diffuse_sum += Math::MakeVec(diffuse * dist_atten * spot_atten, 0.0f); |             dist_atten * spot_atten; | ||||||
|  |         auto specular = (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten; | ||||||
| 
 | 
 | ||||||
|         specular_sum += Math::MakeVec( |         if (!lighting.IsShadowDisabled(num)) { | ||||||
|             (specular_0 + specular_1) * clamp_highlights * dist_atten * spot_atten, 0.0f); |             if (lighting.config0.shadow_primary) { | ||||||
|  |                 diffuse = diffuse * shadow.xyz(); | ||||||
|  |             } | ||||||
|  |             if (lighting.config0.shadow_secondary) { | ||||||
|  |                 specular = specular * shadow.xyz(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         diffuse_sum += Math::MakeVec(diffuse, 0.0f); | ||||||
|  |         specular_sum += Math::MakeVec(specular, 0.0f); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (lighting.config0.shadow_alpha) { | ||||||
|  |         // Alpha shadow also uses the Fresnel selecotr to determine which alpha to apply
 | ||||||
|  |         // Enabled for diffuse lighting alpha component
 | ||||||
|  |         if (lighting.config0.fresnel_selector == | ||||||
|  |                 LightingRegs::LightingFresnelSelector::PrimaryAlpha || | ||||||
|  |             lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { | ||||||
|  |             diffuse_sum.a() *= shadow.w; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Enabled for the specular lighting alpha component
 | ||||||
|  |         if (lighting.config0.fresnel_selector == | ||||||
|  |                 LightingRegs::LightingFresnelSelector::SecondaryAlpha || | ||||||
|  |             lighting.config0.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { | ||||||
|  |             specular_sum.a() *= shadow.w; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); |     diffuse_sum += Math::MakeVec(lighting.global_ambient.ToVec3f(), 0.0f); | ||||||
|  |  | ||||||
|  | @ -74,8 +74,9 @@ static int SignedArea(const Math::Vec2<Fix12P4>& vtx1, const Math::Vec2<Fix12P4> | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// Convert a 3D vector for cube map coordinates to 2D texture coordinates along with the face name
 | /// Convert a 3D vector for cube map coordinates to 2D texture coordinates along with the face name
 | ||||||
| static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, float24 w, | static std::tuple<float24, float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v, | ||||||
|                                                             const TexturingRegs& regs) { |                                                                      float24 w, | ||||||
|  |                                                                      const TexturingRegs& regs) { | ||||||
|     const float abs_u = std::abs(u.ToFloat32()); |     const float abs_u = std::abs(u.ToFloat32()); | ||||||
|     const float abs_v = std::abs(v.ToFloat32()); |     const float abs_v = std::abs(v.ToFloat32()); | ||||||
|     const float abs_w = std::abs(w.ToFloat32()); |     const float abs_w = std::abs(w.ToFloat32()); | ||||||
|  | @ -112,8 +113,9 @@ static std::tuple<float24, float24, PAddr> ConvertCubeCoord(float24 u, float24 v | ||||||
|         x = u; |         x = u; | ||||||
|         z = w; |         z = w; | ||||||
|     } |     } | ||||||
|  |     float24 z_abs = float24::FromFloat32(std::abs(z.ToFloat32())); | ||||||
|     const float24 half = float24::FromFloat32(0.5f); |     const float24 half = float24::FromFloat32(0.5f); | ||||||
|     return std::make_tuple(x / z * half + half, y / z * half + half, addr); |     return std::make_tuple(x / z * half + half, y / z * half + half, z_abs, addr); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); | MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 240)); | ||||||
|  | @ -331,13 +333,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | ||||||
|                 // Only unit 0 respects the texturing type (according to 3DBrew)
 |                 // Only unit 0 respects the texturing type (according to 3DBrew)
 | ||||||
|                 // TODO: Refactor so cubemaps and shadowmaps can be handled
 |                 // TODO: Refactor so cubemaps and shadowmaps can be handled
 | ||||||
|                 PAddr texture_address = texture.config.GetPhysicalAddress(); |                 PAddr texture_address = texture.config.GetPhysicalAddress(); | ||||||
|  |                 float24 shadow_z; | ||||||
|                 if (i == 0) { |                 if (i == 0) { | ||||||
|                     switch (texture.config.type) { |                     switch (texture.config.type) { | ||||||
|                     case TexturingRegs::TextureConfig::Texture2D: |                     case TexturingRegs::TextureConfig::Texture2D: | ||||||
|                         break; |                         break; | ||||||
|  |                     case TexturingRegs::TextureConfig::ShadowCube: | ||||||
|                     case TexturingRegs::TextureConfig::TextureCube: { |                     case TexturingRegs::TextureConfig::TextureCube: { | ||||||
|                         auto w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); |                         auto w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); | ||||||
|                         std::tie(u, v, texture_address) = ConvertCubeCoord(u, v, w, regs.texturing); |                         std::tie(u, v, shadow_z, texture_address) = | ||||||
|  |                             ConvertCubeCoord(u, v, w, regs.texturing); | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                     case TexturingRegs::TextureConfig::Projection2D: { |                     case TexturingRegs::TextureConfig::Projection2D: { | ||||||
|  | @ -346,6 +351,16 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | ||||||
|                         v /= tc0_w; |                         v /= tc0_w; | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|  |                     case TexturingRegs::TextureConfig::Shadow2D: { | ||||||
|  |                         auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w); | ||||||
|  |                         if (!regs.texturing.shadow.orthographic) { | ||||||
|  |                             u /= tc0_w; | ||||||
|  |                             v /= tc0_w; | ||||||
|  |                         } | ||||||
|  | 
 | ||||||
|  |                         shadow_z = float24::FromFloat32(std::abs(tc0_w.ToFloat32())); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|                     default: |                     default: | ||||||
|                         // TODO: Change to LOG_ERROR when more types are handled.
 |                         // TODO: Change to LOG_ERROR when more types are handled.
 | ||||||
|                         LOG_DEBUG(HW_GPU, "Unhandled texture type %x", (int)texture.config.type); |                         LOG_DEBUG(HW_GPU, "Unhandled texture type %x", (int)texture.config.type); | ||||||
|  | @ -394,6 +409,22 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | ||||||
|                     // TODO: Apply the min and mag filters to the texture
 |                     // TODO: Apply the min and mag filters to the texture
 | ||||||
|                     texture_color[i] = Texture::LookupTexture(texture_data, s, t, info); |                     texture_color[i] = Texture::LookupTexture(texture_data, s, t, info); | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 if (i == 0 && (texture.config.type == TexturingRegs::TextureConfig::Shadow2D || | ||||||
|  |                                texture.config.type == TexturingRegs::TextureConfig::ShadowCube)) { | ||||||
|  | 
 | ||||||
|  |                     s32 z_int = static_cast<s32>(std::min(shadow_z.ToFloat32(), 1.0f) * 0xFFFFFF); | ||||||
|  |                     z_int -= regs.texturing.shadow.bias << 1; | ||||||
|  |                     auto& color = texture_color[i]; | ||||||
|  |                     s32 z_ref = (color.w << 16) | (color.z << 8) | color.y; | ||||||
|  |                     u8 density; | ||||||
|  |                     if (z_ref >= z_int) { | ||||||
|  |                         density = color.x; | ||||||
|  |                     } else { | ||||||
|  |                         density = 0; | ||||||
|  |                     } | ||||||
|  |                     texture_color[i] = {density, density, density, density}; | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // sample procedural texture
 |             // sample procedural texture
 | ||||||
|  | @ -541,6 +572,17 @@ static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Ve | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             const auto& output_merger = regs.framebuffer.output_merger; |             const auto& output_merger = regs.framebuffer.output_merger; | ||||||
|  | 
 | ||||||
|  |             if (output_merger.fragment_operation_mode == | ||||||
|  |                 FramebufferRegs::FragmentOperationMode::Shadow) { | ||||||
|  |                 u32 depth_int = static_cast<u32>(depth * 0xFFFFFF); | ||||||
|  |                 // use green color as the shadow intensity
 | ||||||
|  |                 u8 stencil = combiner_output.y; | ||||||
|  |                 DrawShadowMapPixel(x >> 4, y >> 4, depth_int, stencil); | ||||||
|  |                 // skip the normal output merger pipeline if it is in shadow mode
 | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // TODO: Does alpha testing happen before or after stencil?
 |             // TODO: Does alpha testing happen before or after stencil?
 | ||||||
|             if (output_merger.alpha_test.enable) { |             if (output_merger.alpha_test.enable) { | ||||||
|                 bool pass = false; |                 bool pass = false; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue