123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- // Copyright 2016 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "VideoCommon/RenderState.h"
- #include <algorithm>
- #include <array>
- #include "VideoCommon/BPMemory.h"
- #include "VideoCommon/TextureConfig.h"
- 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 = CullMode::None;
- }
- void DepthState::Generate(const BPMemory& bp)
- {
- testenable = bp.zmode.testenable.Value();
- updateenable = bp.zmode.updateenable.Value();
- func = bp.zmode.func.Value();
- }
- static bool IsDualSrc(SrcBlendFactor factor)
- {
- return factor == SrcBlendFactor::SrcAlpha || factor == SrcBlendFactor::InvSrcAlpha;
- }
- static bool IsDualSrc(DstBlendFactor factor)
- {
- return factor == DstBlendFactor::SrcAlpha || factor == DstBlendFactor::InvSrcAlpha;
- }
- bool BlendingState::RequiresDualSrc() const
- {
- bool requires_dual_src = false;
- requires_dual_src |= IsDualSrc(srcfactor) || IsDualSrc(srcfactoralpha);
- requires_dual_src |= IsDualSrc(dstfactor) || IsDualSrc(dstfactoralpha);
- requires_dual_src &= blendenable && usedualsrc;
- return requires_dual_src;
- }
- // 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 SrcBlendFactor RemoveDstAlphaUsage(SrcBlendFactor factor)
- {
- switch (factor)
- {
- case SrcBlendFactor::DstAlpha:
- return SrcBlendFactor::One;
- case SrcBlendFactor::InvDstAlpha:
- return SrcBlendFactor::Zero;
- default:
- return factor;
- }
- }
- static DstBlendFactor RemoveDstAlphaUsage(DstBlendFactor factor)
- {
- switch (factor)
- {
- case DstBlendFactor::DstAlpha:
- return DstBlendFactor::One;
- case DstBlendFactor::InvDstAlpha:
- return DstBlendFactor::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 DstBlendFactor RemoveSrcColorUsage(DstBlendFactor factor)
- {
- switch (factor)
- {
- case DstBlendFactor::SrcClr:
- return DstBlendFactor::SrcAlpha;
- case DstBlendFactor::InvSrcClr:
- return DstBlendFactor::InvSrcAlpha;
- default:
- return factor;
- }
- }
- // Same as RemoveSrcColorUsage, but because of the overlapping enum,
- // this must be written as another function.
- static SrcBlendFactor RemoveDstColorUsage(SrcBlendFactor factor)
- {
- switch (factor)
- {
- case SrcBlendFactor::DstClr:
- return SrcBlendFactor::DstAlpha;
- case SrcBlendFactor::InvDstClr:
- return SrcBlendFactor::InvDstAlpha;
- default:
- return factor;
- }
- }
- void BlendingState::Generate(const BPMemory& bp)
- {
- // Start with everything disabled.
- hex = 0;
- const bool target_has_alpha = bp.zcontrol.pixel_format == PixelFormat::RGBA6_Z24;
- const bool alpha_test_may_succeed = bp.alpha_test.TestResult() != AlphaTestResult::Fail;
- colorupdate = bp.blendmode.colorupdate && alpha_test_may_succeed;
- alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_succeed;
- const bool dstalpha = bp.dstalpha.enable && alphaupdate;
- usedualsrc = true;
- if (bp.blendmode.blendenable)
- {
- if (bp.blendmode.subtract)
- {
- blendenable = true;
- subtractAlpha = subtract = true;
- srcfactoralpha = srcfactor = SrcBlendFactor::One;
- dstfactoralpha = dstfactor = DstBlendFactor::One;
- if (dstalpha)
- {
- subtractAlpha = false;
- srcfactoralpha = SrcBlendFactor::One;
- dstfactoralpha = DstBlendFactor::Zero;
- }
- }
- else
- {
- 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);
- }
- // 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);
- if (dstalpha)
- {
- srcfactoralpha = SrcBlendFactor::One;
- dstfactoralpha = DstBlendFactor::Zero;
- }
- }
- }
- else if (bp.blendmode.logicopenable)
- {
- if (bp.blendmode.logicmode == LogicOp::NoOp)
- {
- // 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.
- }
- }
- }
- // If we aren't writing color or alpha, don't blend it.
- // Intel GPUs on D3D12 seem to have issues with dual-source blend if the second source is used in
- // the blend state but not actually written (i.e. the alpha src or dst factor is src alpha, but
- // alpha update is disabled). So, change the blending configuration to not use a dual-source
- // factor. Note that in theory, disabling writing should render these irrelevant.
- if (!colorupdate)
- {
- srcfactor = SrcBlendFactor::Zero;
- dstfactor = DstBlendFactor::One;
- }
- if (!alphaupdate)
- {
- srcfactoralpha = SrcBlendFactor::Zero;
- dstfactoralpha = DstBlendFactor::One;
- }
- }
- void BlendingState::ApproximateLogicOpWithBlending()
- {
- struct LogicOpApproximation
- {
- bool blendEnable;
- bool subtract;
- SrcBlendFactor srcfactor;
- DstBlendFactor dstfactor;
- };
- // TODO: This previously had a warning about SRC and DST being aliased and not to mix them,
- // but INVSRCCLR and INVDSTCLR were also aliased and were mixed.
- // Thus, NOR, EQUIV, INVERT, COPY_INVERTED, and OR_INVERTED duplicate(d) other values.
- static constexpr std::array<LogicOpApproximation, 16> approximations = {{
- // clang-format off
- {false, false, SrcBlendFactor::One, DstBlendFactor::Zero}, // CLEAR (Shader outputs 0)
- {true, false, SrcBlendFactor::DstClr, DstBlendFactor::Zero}, // AND
- {true, true, SrcBlendFactor::One, DstBlendFactor::InvSrcClr}, // AND_REVERSE
- {false, false, SrcBlendFactor::One, DstBlendFactor::Zero}, // COPY
- {true, true, SrcBlendFactor::DstClr, DstBlendFactor::One}, // AND_INVERTED
- {true, false, SrcBlendFactor::Zero, DstBlendFactor::One}, // NOOP
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // XOR
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::One}, // OR
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // NOR
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::Zero}, // EQUIV
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::Zero}, // INVERT (Shader outputs 255)
- {true, false, SrcBlendFactor::One, DstBlendFactor::InvDstAlpha}, // OR_REVERSE
- {false, false, SrcBlendFactor::One, DstBlendFactor::Zero}, // COPY_INVERTED (Shader inverts)
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::One}, // OR_INVERTED
- {true, false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // NAND
- {false, false, SrcBlendFactor::One, DstBlendFactor::Zero}, // SET (Shader outputs 255)
- // clang-format on
- }};
- logicopenable = false;
- usedualsrc = false;
- const LogicOpApproximation& approximation = approximations[static_cast<u32>(logicmode.Value())];
- if (approximation.blendEnable)
- {
- blendenable = true;
- subtract = approximation.subtract;
- srcfactor = approximation.srcfactor;
- srcfactoralpha = approximation.srcfactor;
- dstfactor = approximation.dstfactor;
- dstfactoralpha = approximation.dstfactor;
- }
- }
- bool BlendingState::LogicOpApproximationIsExact()
- {
- switch (logicmode.Value())
- {
- case LogicOp::Clear:
- case LogicOp::Set:
- case LogicOp::NoOp:
- case LogicOp::Invert:
- case LogicOp::CopyInverted:
- case LogicOp::Copy:
- return true;
- default:
- return false;
- }
- }
- bool BlendingState::LogicOpApproximationWantsShaderHelp()
- {
- switch (logicmode.Value())
- {
- case LogicOp::Clear:
- case LogicOp::Set:
- case LogicOp::NoOp:
- case LogicOp::Invert:
- case LogicOp::CopyInverted:
- return true;
- default:
- return false;
- }
- }
- void SamplerState::Generate(const BPMemory& bp, u32 index)
- {
- auto tex = bp.tex.GetUnit(index);
- const TexMode0& bp_tm0 = tex.texMode0;
- const TexMode1& bp_tm1 = tex.texMode1;
- // 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.
- tm0.min_filter = bp_tm0.min_filter;
- tm0.mipmap_filter =
- bp_tm0.mipmap_filter == MipMode::Linear ? FilterMode::Linear : FilterMode::Near;
- tm0.mag_filter = bp_tm0.mag_filter;
- // If mipmaps are disabled, clamp min/max lod
- if (bp_tm0.mipmap_filter == MipMode::None)
- {
- tm1.max_lod = 0;
- tm1.min_lod = 0;
- tm0.lod_bias = 0;
- }
- else
- {
- // NOTE: When comparing, max is checked first, then min; if max is less than min, max wins
- tm1.max_lod = bp_tm1.max_lod.Value();
- tm1.min_lod = std::min(tm1.max_lod.Value(), bp_tm1.min_lod.Value());
- tm0.lod_bias = bp_tm0.lod_bias * (256 / 32);
- }
- // Wrap modes
- // Hardware testing indicates that wrap_mode set to 3 behaves the same as clamp.
- auto filter_invalid_wrap = [](WrapMode mode) {
- return (mode <= WrapMode::Mirror) ? mode : WrapMode::Clamp;
- };
- tm0.wrap_u = filter_invalid_wrap(bp_tm0.wrap_s);
- tm0.wrap_v = filter_invalid_wrap(bp_tm0.wrap_t);
- tm0.diag_lod = bp_tm0.diag_lod;
- tm0.anisotropic_filtering = false; // TODO: Respect BP anisotropic filtering mode
- tm0.lod_clamp = bp_tm0.lod_clamp; // TODO: What does this do?
- }
- namespace RenderState
- {
- RasterizationState GetInvalidRasterizationState()
- {
- RasterizationState state;
- state.hex = UINT32_C(0xFFFFFFFF);
- return state;
- }
- RasterizationState GetNoCullRasterizationState(PrimitiveType primitive)
- {
- RasterizationState state = {};
- state.cullmode = CullMode::None;
- state.primitive = primitive;
- return state;
- }
- RasterizationState GetCullBackFaceRasterizationState(PrimitiveType primitive)
- {
- RasterizationState state = {};
- state.cullmode = CullMode::Back;
- state.primitive = primitive;
- return state;
- }
- DepthState GetInvalidDepthState()
- {
- DepthState state;
- state.hex = UINT32_C(0xFFFFFFFF);
- return state;
- }
- DepthState GetNoDepthTestingDepthState()
- {
- DepthState state = {};
- state.testenable = false;
- state.updateenable = false;
- state.func = CompareMode::Always;
- return state;
- }
- DepthState GetAlwaysWriteDepthState()
- {
- DepthState state = {};
- state.testenable = true;
- state.updateenable = true;
- state.func = CompareMode::Always;
- return state;
- }
- BlendingState GetInvalidBlendingState()
- {
- BlendingState state;
- state.hex = UINT32_C(0xFFFFFFFF);
- return state;
- }
- BlendingState GetNoBlendingBlendState()
- {
- BlendingState state = {};
- state.usedualsrc = false;
- state.blendenable = false;
- state.srcfactor = SrcBlendFactor::One;
- state.srcfactoralpha = SrcBlendFactor::One;
- state.dstfactor = DstBlendFactor::Zero;
- state.dstfactoralpha = DstBlendFactor::Zero;
- state.logicopenable = false;
- state.colorupdate = true;
- state.alphaupdate = true;
- return state;
- }
- BlendingState GetNoColorWriteBlendState()
- {
- BlendingState state = {};
- state.usedualsrc = false;
- state.blendenable = false;
- state.srcfactor = SrcBlendFactor::One;
- state.srcfactoralpha = SrcBlendFactor::One;
- state.dstfactor = DstBlendFactor::Zero;
- state.dstfactoralpha = DstBlendFactor::Zero;
- state.logicopenable = false;
- state.colorupdate = false;
- state.alphaupdate = false;
- return state;
- }
- SamplerState GetInvalidSamplerState()
- {
- SamplerState state;
- state.tm0.hex = 0xFFFFFFFF;
- state.tm1.hex = 0xFFFFFFFF;
- return state;
- }
- SamplerState GetPointSamplerState()
- {
- SamplerState state = {};
- state.tm0.min_filter = FilterMode::Near;
- state.tm0.mag_filter = FilterMode::Near;
- state.tm0.mipmap_filter = FilterMode::Near;
- state.tm0.wrap_u = WrapMode::Clamp;
- state.tm0.wrap_v = WrapMode::Clamp;
- state.tm1.min_lod = 0;
- state.tm1.max_lod = 255;
- state.tm0.lod_bias = 0;
- state.tm0.anisotropic_filtering = false;
- state.tm0.diag_lod = LODType::Edge;
- state.tm0.lod_clamp = false;
- return state;
- }
- SamplerState GetLinearSamplerState()
- {
- SamplerState state = {};
- state.tm0.min_filter = FilterMode::Linear;
- state.tm0.mag_filter = FilterMode::Linear;
- state.tm0.mipmap_filter = FilterMode::Linear;
- state.tm0.wrap_u = WrapMode::Clamp;
- state.tm0.wrap_v = WrapMode::Clamp;
- state.tm1.min_lod = 0;
- state.tm1.max_lod = 255;
- state.tm0.lod_bias = 0;
- state.tm0.anisotropic_filtering = false;
- state.tm0.diag_lod = LODType::Edge;
- state.tm0.lod_clamp = false;
- return state;
- }
- FramebufferState GetColorFramebufferState(AbstractTextureFormat format)
- {
- FramebufferState state = {};
- state.color_texture_format = format;
- state.depth_texture_format = AbstractTextureFormat::Undefined;
- state.per_sample_shading = false;
- state.samples = 1;
- state.additional_color_attachment_count = 0;
- return state;
- }
- FramebufferState GetRGBA8FramebufferState()
- {
- return GetColorFramebufferState(AbstractTextureFormat::RGBA8);
- }
- } // namespace RenderState
|