| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  | // Copyright 2016 Dolphin Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2+
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "VideoCommon/RenderState.h"
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <array>
 | 
					
						
							|  |  |  | #include "VideoCommon/SamplerCommon.h"
 | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 18:07:57 +10:00
										 |  |  | void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   cullmode = bp.genMode.cullmode; | 
					
						
							|  |  |  |   primitive = primitive_type; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Back-face culling should be disabled for points/lines.
 | 
					
						
							|  |  |  |   if (primitive_type != PrimitiveType::Triangles && primitive_type != PrimitiveType::TriangleStrip) | 
					
						
							|  |  |  |     cullmode = GenMode::CULL_NONE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | RasterizationState& RasterizationState::operator=(const RasterizationState& rhs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   hex = rhs.hex; | 
					
						
							|  |  |  |   return *this; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 15:54:45 +10:00
										 |  |  | void DepthState::Generate(const BPMemory& bp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   testenable = bp.zmode.testenable.Value(); | 
					
						
							|  |  |  |   updateenable = bp.zmode.updateenable.Value(); | 
					
						
							|  |  |  |   func = bp.zmode.func.Value(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | DepthState& DepthState::operator=(const DepthState& rhs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   hex = rhs.hex; | 
					
						
							|  |  |  |   return *this; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  | // If the framebuffer format has no alpha channel, it is assumed to
 | 
					
						
							|  |  |  | // ONE on blending. As the backends may emulate this framebuffer
 | 
					
						
							|  |  |  | // configuration with an alpha channel, we just drop all references
 | 
					
						
							|  |  |  | // to the destination alpha channel.
 | 
					
						
							|  |  |  | static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   switch (factor) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case BlendMode::DSTALPHA: | 
					
						
							|  |  |  |     return BlendMode::ONE; | 
					
						
							|  |  |  |   case BlendMode::INVDSTALPHA: | 
					
						
							|  |  |  |     return BlendMode::ZERO; | 
					
						
							|  |  |  |   default: | 
					
						
							|  |  |  |     return factor; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // We separate the blending parameter for rgb and alpha. For blending
 | 
					
						
							|  |  |  | // the alpha component, CLR and ALPHA are indentical. So just always
 | 
					
						
							|  |  |  | // use ALPHA as this makes it easier for the backends to use the second
 | 
					
						
							|  |  |  | // alpha value of dual source blending.
 | 
					
						
							|  |  |  | static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   switch (factor) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case BlendMode::SRCCLR: | 
					
						
							|  |  |  |     return BlendMode::SRCALPHA; | 
					
						
							|  |  |  |   case BlendMode::INVSRCCLR: | 
					
						
							|  |  |  |     return BlendMode::INVSRCALPHA; | 
					
						
							|  |  |  |   default: | 
					
						
							|  |  |  |     return factor; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Same as RemoveSrcColorUsage, but because of the overlapping enum,
 | 
					
						
							|  |  |  | // this must be written as another function.
 | 
					
						
							|  |  |  | static BlendMode::BlendFactor RemoveDstColorUsage(BlendMode::BlendFactor factor) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   switch (factor) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |   case BlendMode::DSTCLR: | 
					
						
							|  |  |  |     return BlendMode::DSTALPHA; | 
					
						
							|  |  |  |   case BlendMode::INVDSTCLR: | 
					
						
							|  |  |  |     return BlendMode::INVDSTALPHA; | 
					
						
							|  |  |  |   default: | 
					
						
							|  |  |  |     return factor; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BlendingState::Generate(const BPMemory& bp) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   // Start with everything disabled.
 | 
					
						
							|  |  |  |   hex = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bool target_has_alpha = bp.zcontrol.pixel_format == PEControl::RGBA6_Z24; | 
					
						
							|  |  |  |   bool alpha_test_may_success = bp.alpha_test.TestResult() != AlphaTest::FAIL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   colorupdate = bp.blendmode.colorupdate && alpha_test_may_success; | 
					
						
							|  |  |  |   alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_success; | 
					
						
							|  |  |  |   dstalpha = bp.dstalpha.enable && alphaupdate; | 
					
						
							| 
									
										
										
										
											2017-04-17 23:11:58 +10:00
										 |  |  |   usedualsrc = true; | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // The subtract bit has the highest priority
 | 
					
						
							|  |  |  |   if (bp.blendmode.subtract) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     blendenable = true; | 
					
						
							|  |  |  |     subtractAlpha = subtract = true; | 
					
						
							|  |  |  |     srcfactoralpha = srcfactor = BlendMode::ONE; | 
					
						
							|  |  |  |     dstfactoralpha = dstfactor = BlendMode::ONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (dstalpha) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       subtractAlpha = false; | 
					
						
							|  |  |  |       srcfactoralpha = BlendMode::ONE; | 
					
						
							|  |  |  |       dstfactoralpha = BlendMode::ZERO; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The blendenable bit has the middle priority
 | 
					
						
							|  |  |  |   else if (bp.blendmode.blendenable) | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     blendenable = true; | 
					
						
							|  |  |  |     srcfactor = bp.blendmode.srcfactor; | 
					
						
							|  |  |  |     dstfactor = bp.blendmode.dstfactor; | 
					
						
							|  |  |  |     if (!target_has_alpha) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       // uses ONE instead of DSTALPHA
 | 
					
						
							|  |  |  |       srcfactor = RemoveDstAlphaUsage(srcfactor); | 
					
						
							|  |  |  |       dstfactor = RemoveDstAlphaUsage(dstfactor); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-08-21 16:08:32 +02:00
										 |  |  |     // replaces SRCCLR with SRCALPHA and DSTCLR with DSTALPHA, it is important to
 | 
					
						
							|  |  |  |     // use the dst function for the src factor and vice versa
 | 
					
						
							|  |  |  |     srcfactoralpha = RemoveDstColorUsage(srcfactor); | 
					
						
							|  |  |  |     dstfactoralpha = RemoveSrcColorUsage(dstfactor); | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (dstalpha) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       srcfactoralpha = BlendMode::ONE; | 
					
						
							|  |  |  |       dstfactoralpha = BlendMode::ZERO; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // The logicop bit has the lowest priority
 | 
					
						
							|  |  |  |   else if (bp.blendmode.logicopenable) | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2016-12-29 18:15:41 +01:00
										 |  |  |     if (bp.blendmode.logicmode == BlendMode::NOOP) | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2016-12-29 18:15:41 +01:00
										 |  |  |       // Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
 | 
					
						
							|  |  |  |       colorupdate = false; | 
					
						
							|  |  |  |       alphaupdate = alphaupdate && dstalpha; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       logicopenable = true; | 
					
						
							|  |  |  |       logicmode = bp.blendmode.logicmode; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (dstalpha) | 
					
						
							|  |  |  |       { | 
					
						
							|  |  |  |         // TODO: Not supported by backends.
 | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-12-29 17:31:51 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:10:40 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | BlendingState& BlendingState::operator=(const BlendingState& rhs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   hex = rhs.hex; | 
					
						
							|  |  |  |   return *this; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void SamplerState::Generate(const BPMemory& bp, u32 index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   const FourTexUnits& tex = bpmem.tex[index / 4]; | 
					
						
							|  |  |  |   const TexMode0& tm0 = tex.texMode0[index % 4]; | 
					
						
							|  |  |  |   const TexMode1& tm1 = tex.texMode1[index % 4]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // GX can configure the mip filter to none. However, D3D and Vulkan can't express this in their
 | 
					
						
							|  |  |  |   // sampler states. Therefore, we set the min/max LOD to zero if this option is used.
 | 
					
						
							|  |  |  |   min_filter = (tm0.min_filter & 4) != 0 ? Filter::Linear : Filter::Point; | 
					
						
							|  |  |  |   mipmap_filter = (tm0.min_filter & 3) == TexMode0::TEXF_LINEAR ? Filter::Linear : Filter::Point; | 
					
						
							|  |  |  |   mag_filter = tm0.mag_filter != 0 ? Filter::Linear : Filter::Point; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // If mipmaps are disabled, clamp min/max lod
 | 
					
						
							|  |  |  |   max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0; | 
					
						
							| 
									
										
										
										
											2017-10-18 02:02:56 -07:00
										 |  |  |   min_lod = std::min(max_lod.Value(), static_cast<u64>(tm1.min_lod)); | 
					
						
							|  |  |  |   lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias * (256 / 32) : 0; | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // Address modes
 | 
					
						
							|  |  |  |   static constexpr std::array<AddressMode, 4> address_modes = { | 
					
						
							|  |  |  |       {AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Repeat}}; | 
					
						
							|  |  |  |   wrap_u = address_modes[tm0.wrap_s]; | 
					
						
							|  |  |  |   wrap_v = address_modes[tm0.wrap_t]; | 
					
						
							|  |  |  |   anisotropic_filtering = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SamplerState& SamplerState::operator=(const SamplerState& rhs) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   hex = rhs.hex; | 
					
						
							|  |  |  |   return *this; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-09 00:10:40 +10:00
										 |  |  | namespace RenderState | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | RasterizationState GetNoCullRasterizationState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   RasterizationState state = {}; | 
					
						
							|  |  |  |   state.cullmode = GenMode::CULL_NONE; | 
					
						
							|  |  |  |   return state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DepthState GetNoDepthTestingDepthStencilState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   DepthState state = {}; | 
					
						
							|  |  |  |   state.testenable = false; | 
					
						
							|  |  |  |   state.updateenable = false; | 
					
						
							|  |  |  |   state.func = ZMode::ALWAYS; | 
					
						
							|  |  |  |   return state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BlendingState GetNoBlendingBlendState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   BlendingState state = {}; | 
					
						
							|  |  |  |   state.usedualsrc = false; | 
					
						
							|  |  |  |   state.blendenable = false; | 
					
						
							|  |  |  |   state.srcfactor = BlendMode::ONE; | 
					
						
							|  |  |  |   state.srcfactoralpha = BlendMode::ONE; | 
					
						
							|  |  |  |   state.dstfactor = BlendMode::ZERO; | 
					
						
							|  |  |  |   state.dstfactoralpha = BlendMode::ZERO; | 
					
						
							|  |  |  |   state.logicopenable = false; | 
					
						
							|  |  |  |   state.colorupdate = true; | 
					
						
							|  |  |  |   state.alphaupdate = true; | 
					
						
							|  |  |  |   return state; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-09 18:30:15 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | SamplerState GetPointSamplerState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SamplerState state = {}; | 
					
						
							|  |  |  |   state.min_filter = SamplerState::Filter::Point; | 
					
						
							|  |  |  |   state.mag_filter = SamplerState::Filter::Point; | 
					
						
							|  |  |  |   state.mipmap_filter = SamplerState::Filter::Point; | 
					
						
							|  |  |  |   state.wrap_u = SamplerState::AddressMode::Clamp; | 
					
						
							|  |  |  |   state.wrap_v = SamplerState::AddressMode::Clamp; | 
					
						
							|  |  |  |   state.min_lod = 0; | 
					
						
							|  |  |  |   state.max_lod = 255; | 
					
						
							|  |  |  |   state.lod_bias = 0; | 
					
						
							|  |  |  |   state.anisotropic_filtering = false; | 
					
						
							|  |  |  |   return state; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SamplerState GetLinearSamplerState() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |   SamplerState state = {}; | 
					
						
							|  |  |  |   state.min_filter = SamplerState::Filter::Linear; | 
					
						
							|  |  |  |   state.mag_filter = SamplerState::Filter::Linear; | 
					
						
							|  |  |  |   state.mipmap_filter = SamplerState::Filter::Linear; | 
					
						
							|  |  |  |   state.wrap_u = SamplerState::AddressMode::Clamp; | 
					
						
							|  |  |  |   state.wrap_v = SamplerState::AddressMode::Clamp; | 
					
						
							|  |  |  |   state.min_lod = 0; | 
					
						
							|  |  |  |   state.max_lod = 255; | 
					
						
							|  |  |  |   state.lod_bias = 0; | 
					
						
							|  |  |  |   state.anisotropic_filtering = false; | 
					
						
							|  |  |  |   return state; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-09-09 00:10:40 +10:00
										 |  |  | } |