123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 |
- // Copyright 2008 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "VideoCommon/PixelShaderManager.h"
- #include <iterator>
- #include "Common/ChunkFile.h"
- #include "Common/CommonTypes.h"
- #include "VideoCommon/FramebufferManager.h"
- #include "VideoCommon/VideoCommon.h"
- #include "VideoCommon/VideoConfig.h"
- #include "VideoCommon/XFMemory.h"
- void PixelShaderManager::Init()
- {
- constants = {};
- // Init any intial constants which aren't zero when bpmem is zero.
- m_fog_range_adjusted_changed = true;
- m_viewport_changed = false;
- SetIndMatrixChanged(0);
- SetIndMatrixChanged(1);
- SetIndMatrixChanged(2);
- SetZTextureTypeChanged();
- SetTexCoordChanged(0);
- SetTexCoordChanged(1);
- SetTexCoordChanged(2);
- SetTexCoordChanged(3);
- SetTexCoordChanged(4);
- SetTexCoordChanged(5);
- SetTexCoordChanged(6);
- SetTexCoordChanged(7);
- // fixed Konstants
- for (int component = 0; component < 4; component++)
- {
- constants.konst[0][component] = 255; // 1
- constants.konst[1][component] = 223; // 7/8
- constants.konst[2][component] = 191; // 3/4
- constants.konst[3][component] = 159; // 5/8
- constants.konst[4][component] = 128; // 1/2
- constants.konst[5][component] = 96; // 3/8
- constants.konst[6][component] = 64; // 1/4
- constants.konst[7][component] = 32; // 1/8
- // Invalid Konstants (reads as zero on hardware)
- constants.konst[8][component] = 0;
- constants.konst[9][component] = 0;
- constants.konst[10][component] = 0;
- constants.konst[11][component] = 0;
- // Annoyingly, alpha reads zero values for the .rgb colors (offically
- // defined as invalid)
- // If it wasn't for this, we could just use one of the first 3 colunms
- // instead of
- // wasting an entire 4th column just for alpha.
- if (component == 3)
- {
- constants.konst[12][component] = 0;
- constants.konst[13][component] = 0;
- constants.konst[14][component] = 0;
- constants.konst[15][component] = 0;
- }
- }
- Dirty();
- }
- void PixelShaderManager::Dirty()
- {
- // This function is called after a savestate is loaded.
- // Any constants that can changed based on settings should be re-calculated
- m_fog_range_adjusted_changed = true;
- SetEfbScaleChanged(g_framebuffer_manager->EFBToScaledXf(1),
- g_framebuffer_manager->EFBToScaledYf(1));
- SetFogParamChanged();
- dirty = true;
- }
- void PixelShaderManager::SetConstants()
- {
- if (m_fog_range_adjusted_changed)
- {
- // set by two components, so keep changed flag here
- // TODO: try to split both registers and move this logic to the shader
- if (!g_ActiveConfig.bDisableFog && bpmem.fogRange.Base.Enabled == 1)
- {
- // bpmem.fogRange.Base.Center : center of the viewport in x axis. observation:
- // bpmem.fogRange.Base.Center = realcenter + 342;
- int center = ((u32)bpmem.fogRange.Base.Center) - 342;
- // normalize center to make calculations easy
- float ScreenSpaceCenter = center / (2.0f * xfmem.viewport.wd);
- ScreenSpaceCenter = (ScreenSpaceCenter * 2.0f) - 1.0f;
- // bpmem.fogRange.K seems to be a table of precalculated coefficients for the adjust factor
- // observations: bpmem.fogRange.K[0].LO appears to be the lowest value and
- // bpmem.fogRange.K[4].HI the largest
- // they always seems to be larger than 256 so my theory is :
- // they are the coefficients from the center to the border of the screen
- // so to simplify I use the hi coefficient as K in the shader taking 256 as the scale
- // TODO: Shouldn't this be EFBToScaledXf?
- constants.fogf[2] = ScreenSpaceCenter;
- constants.fogf[3] = static_cast<float>(
- g_framebuffer_manager->EFBToScaledX(static_cast<int>(2.0f * xfmem.viewport.wd)));
- for (size_t i = 0, vec_index = 0; i < std::size(bpmem.fogRange.K); i++)
- {
- constexpr float scale = 4.0f;
- constants.fogrange[vec_index / 4][vec_index % 4] = bpmem.fogRange.K[i].GetValue(0) * scale;
- vec_index++;
- constants.fogrange[vec_index / 4][vec_index % 4] = bpmem.fogRange.K[i].GetValue(1) * scale;
- vec_index++;
- }
- }
- else
- {
- constants.fogf[2] = 0;
- constants.fogf[3] = 1;
- }
- dirty = true;
- m_fog_range_adjusted_changed = false;
- }
- if (m_viewport_changed)
- {
- constants.zbias[1][0] = (s32)xfmem.viewport.farZ;
- constants.zbias[1][1] = (s32)xfmem.viewport.zRange;
- dirty = true;
- m_viewport_changed = false;
- }
- if (m_indirect_dirty)
- {
- for (int i = 0; i < 4; i++)
- constants.pack1[i][3] = 0;
- for (u32 i = 0; i < (bpmem.genMode.numtevstages + 1); ++i)
- {
- // Note: a tevind of zero just happens to be a passthrough, so no need
- // to set an extra bit. Furthermore, wrap and add to previous apply even if there is no
- // indirect stage.
- constants.pack1[i][2] = bpmem.tevind[i].hex;
- u32 stage = bpmem.tevind[i].bt;
- // We use an extra bit (1 << 16) to provide a fast way of testing if this feature is in use.
- // Note also that this is indexed by indirect stage, not by TEV stage.
- if (bpmem.tevind[i].IsActive() && stage < bpmem.genMode.numindstages)
- constants.pack1[stage][3] =
- bpmem.tevindref.getTexCoord(stage) | bpmem.tevindref.getTexMap(stage) << 8 | 1 << 16;
- }
- dirty = true;
- m_indirect_dirty = false;
- }
- if (m_dest_alpha_dirty)
- {
- // Destination alpha is only enabled if alpha writes are enabled. Force entire uniform to zero
- // when disabled.
- u32 dstalpha = bpmem.blendmode.alphaupdate && bpmem.dstalpha.enable &&
- bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 ?
- bpmem.dstalpha.hex :
- 0;
- if (constants.dstalpha != dstalpha)
- {
- constants.dstalpha = dstalpha;
- dirty = true;
- }
- }
- }
- void PixelShaderManager::SetTevColor(int index, int component, s32 value)
- {
- auto& c = constants.colors[index];
- c[component] = value;
- dirty = true;
- PRIM_LOG("tev color{}: {} {} {} {}", index, c[0], c[1], c[2], c[3]);
- }
- void PixelShaderManager::SetTevKonstColor(int index, int component, s32 value)
- {
- auto& c = constants.kcolors[index];
- c[component] = value;
- dirty = true;
- // Konst for ubershaders. We build the whole array on cpu so the gpu can do a single indirect
- // access.
- if (component != 3) // Alpha doesn't included in the .rgb konsts
- constants.konst[index + 12][component] = value;
- // .rrrr .gggg .bbbb .aaaa konsts
- constants.konst[index + 16 + component * 4][0] = value;
- constants.konst[index + 16 + component * 4][1] = value;
- constants.konst[index + 16 + component * 4][2] = value;
- constants.konst[index + 16 + component * 4][3] = value;
- PRIM_LOG("tev konst color{}: {} {} {} {}", index, c[0], c[1], c[2], c[3]);
- }
- void PixelShaderManager::SetTevOrder(int index, u32 order)
- {
- if (constants.pack2[index][0] != order)
- {
- constants.pack2[index][0] = order;
- dirty = true;
- }
- }
- void PixelShaderManager::SetTevKSel(int index, u32 ksel)
- {
- if (constants.pack2[index][1] != ksel)
- {
- constants.pack2[index][1] = ksel;
- dirty = true;
- }
- }
- void PixelShaderManager::SetTevCombiner(int index, int alpha, u32 combiner)
- {
- if (constants.pack1[index][alpha] != combiner)
- {
- constants.pack1[index][alpha] = combiner;
- dirty = true;
- }
- }
- void PixelShaderManager::SetTevIndirectChanged()
- {
- m_indirect_dirty = true;
- }
- void PixelShaderManager::SetAlpha()
- {
- constants.alpha[0] = bpmem.alpha_test.ref0;
- constants.alpha[1] = bpmem.alpha_test.ref1;
- constants.alpha[3] = static_cast<s32>(bpmem.dstalpha.alpha);
- dirty = true;
- }
- void PixelShaderManager::SetAlphaTestChanged()
- {
- // Force alphaTest Uniform to zero if it will always pass.
- // (set an extra bit to distinguish from "never && never")
- // TODO: we could optimize this further and check the actual constants,
- // i.e. "a <= 0" and "a >= 255" will always pass.
- u32 alpha_test =
- bpmem.alpha_test.TestResult() != AlphaTestResult::Pass ? bpmem.alpha_test.hex | 1 << 31 : 0;
- if (constants.alphaTest != alpha_test)
- {
- constants.alphaTest = alpha_test;
- dirty = true;
- }
- }
- void PixelShaderManager::SetDestAlphaChanged()
- {
- m_dest_alpha_dirty = true;
- }
- void PixelShaderManager::SetTexDims(int texmapid, u32 width, u32 height)
- {
- // TODO: move this check out to callee. There we could just call this function on texture changes
- // or better, use textureSize() in glsl
- if (constants.texdims[texmapid][0] != width || constants.texdims[texmapid][1] != height)
- dirty = true;
- constants.texdims[texmapid][0] = width;
- constants.texdims[texmapid][1] = height;
- }
- void PixelShaderManager::SetSamplerState(int texmapid, u32 tm0, u32 tm1)
- {
- if (constants.pack2[texmapid][2] != tm0 || constants.pack2[texmapid][3] != tm1)
- dirty = true;
- constants.pack2[texmapid][2] = tm0;
- constants.pack2[texmapid][3] = tm1;
- }
- void PixelShaderManager::SetZTextureBias()
- {
- constants.zbias[1][3] = bpmem.ztex1.bias;
- dirty = true;
- }
- void PixelShaderManager::SetViewportChanged()
- {
- m_viewport_changed = true;
- m_fog_range_adjusted_changed =
- true; // TODO: Shouldn't be necessary with an accurate fog range adjust implementation
- }
- void PixelShaderManager::SetEfbScaleChanged(float scalex, float scaley)
- {
- constants.efbscale[0] = 1.0f / scalex;
- constants.efbscale[1] = 1.0f / scaley;
- dirty = true;
- }
- void PixelShaderManager::SetZSlope(float dfdx, float dfdy, float f0)
- {
- constants.zslope[0] = dfdx;
- constants.zslope[1] = dfdy;
- constants.zslope[2] = f0;
- dirty = true;
- }
- void PixelShaderManager::SetIndTexScaleChanged(bool high)
- {
- constants.indtexscale[high][0] = bpmem.texscale[high].ss0;
- constants.indtexscale[high][1] = bpmem.texscale[high].ts0;
- constants.indtexscale[high][2] = bpmem.texscale[high].ss1;
- constants.indtexscale[high][3] = bpmem.texscale[high].ts1;
- dirty = true;
- }
- void PixelShaderManager::SetIndMatrixChanged(int matrixidx)
- {
- const u8 scale = bpmem.indmtx[matrixidx].GetScale();
- // xyz - static matrix
- // w - dynamic matrix scale / 128
- constants.indtexmtx[2 * matrixidx][0] = bpmem.indmtx[matrixidx].col0.ma;
- constants.indtexmtx[2 * matrixidx][1] = bpmem.indmtx[matrixidx].col1.mc;
- constants.indtexmtx[2 * matrixidx][2] = bpmem.indmtx[matrixidx].col2.me;
- constants.indtexmtx[2 * matrixidx][3] = 17 - scale;
- constants.indtexmtx[2 * matrixidx + 1][0] = bpmem.indmtx[matrixidx].col0.mb;
- constants.indtexmtx[2 * matrixidx + 1][1] = bpmem.indmtx[matrixidx].col1.md;
- constants.indtexmtx[2 * matrixidx + 1][2] = bpmem.indmtx[matrixidx].col2.mf;
- constants.indtexmtx[2 * matrixidx + 1][3] = 17 - scale;
- dirty = true;
- PRIM_LOG("indmtx{}: scale={}, mat=({} {} {}; {} {} {})", matrixidx, scale,
- bpmem.indmtx[matrixidx].col0.ma, bpmem.indmtx[matrixidx].col1.mc,
- bpmem.indmtx[matrixidx].col2.me, bpmem.indmtx[matrixidx].col0.mb,
- bpmem.indmtx[matrixidx].col1.md, bpmem.indmtx[matrixidx].col2.mf);
- }
- void PixelShaderManager::SetZTextureTypeChanged()
- {
- switch (bpmem.ztex2.type)
- {
- case ZTexFormat::U8:
- constants.zbias[0][0] = 0;
- constants.zbias[0][1] = 0;
- constants.zbias[0][2] = 0;
- constants.zbias[0][3] = 1;
- break;
- case ZTexFormat::U16:
- constants.zbias[0][0] = 1;
- constants.zbias[0][1] = 0;
- constants.zbias[0][2] = 0;
- constants.zbias[0][3] = 256;
- break;
- case ZTexFormat::U24:
- constants.zbias[0][0] = 65536;
- constants.zbias[0][1] = 256;
- constants.zbias[0][2] = 1;
- constants.zbias[0][3] = 0;
- break;
- default:
- PanicAlertFmt("Invalid ztex format {}", bpmem.ztex2.type);
- break;
- }
- dirty = true;
- }
- void PixelShaderManager::SetZTextureOpChanged()
- {
- constants.ztex_op = bpmem.ztex2.op;
- dirty = true;
- }
- void PixelShaderManager::SetTexCoordChanged(u8 texmapid)
- {
- TCoordInfo& tc = bpmem.texcoords[texmapid];
- constants.texdims[texmapid][2] = tc.s.scale_minus_1 + 1;
- constants.texdims[texmapid][3] = tc.t.scale_minus_1 + 1;
- dirty = true;
- }
- void PixelShaderManager::SetFogColorChanged()
- {
- if (g_ActiveConfig.bDisableFog)
- return;
- constants.fogcolor[0] = bpmem.fog.color.r;
- constants.fogcolor[1] = bpmem.fog.color.g;
- constants.fogcolor[2] = bpmem.fog.color.b;
- dirty = true;
- }
- void PixelShaderManager::SetFogParamChanged()
- {
- if (!g_ActiveConfig.bDisableFog)
- {
- constants.fogf[0] = bpmem.fog.GetA();
- constants.fogf[1] = bpmem.fog.GetC();
- constants.fogi[1] = bpmem.fog.b_magnitude;
- constants.fogi[3] = bpmem.fog.b_shift;
- constants.fogParam3 = bpmem.fog.c_proj_fsel.hex;
- }
- else
- {
- constants.fogf[0] = 0.f;
- constants.fogf[1] = 0.f;
- constants.fogi[1] = 1;
- constants.fogi[3] = 1;
- constants.fogParam3 = 0;
- }
- dirty = true;
- }
- void PixelShaderManager::SetFogRangeAdjustChanged()
- {
- if (g_ActiveConfig.bDisableFog)
- return;
- m_fog_range_adjusted_changed = true;
- if (constants.fogRangeBase != bpmem.fogRange.Base.hex)
- {
- constants.fogRangeBase = bpmem.fogRange.Base.hex;
- dirty = true;
- }
- }
- void PixelShaderManager::SetGenModeChanged()
- {
- constants.genmode = bpmem.genMode.hex;
- m_indirect_dirty = true;
- dirty = true;
- }
- void PixelShaderManager::SetZModeControl()
- {
- u32 late_ztest = bpmem.GetEmulatedZ() == EmulatedZ::Late;
- u32 rgba6_format =
- (bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor) ?
- 1 :
- 0;
- u32 dither = rgba6_format && bpmem.blendmode.dither;
- if (constants.late_ztest != late_ztest || constants.rgba6_format != rgba6_format ||
- constants.dither != dither)
- {
- constants.late_ztest = late_ztest;
- constants.rgba6_format = rgba6_format;
- constants.dither = dither;
- dirty = true;
- }
- m_dest_alpha_dirty = true;
- }
- void PixelShaderManager::SetBlendModeChanged()
- {
- u32 dither = constants.rgba6_format && bpmem.blendmode.dither;
- if (constants.dither != dither)
- {
- constants.dither = dither;
- dirty = true;
- }
- BlendingState state = {};
- state.Generate(bpmem);
- if (constants.blend_enable != state.blendenable)
- {
- constants.blend_enable = state.blendenable;
- dirty = true;
- }
- if (constants.blend_src_factor != state.srcfactor)
- {
- constants.blend_src_factor = state.srcfactor;
- dirty = true;
- }
- if (constants.blend_src_factor_alpha != state.srcfactoralpha)
- {
- constants.blend_src_factor_alpha = state.srcfactoralpha;
- dirty = true;
- }
- if (constants.blend_dst_factor != state.dstfactor)
- {
- constants.blend_dst_factor = state.dstfactor;
- dirty = true;
- }
- if (constants.blend_dst_factor_alpha != state.dstfactoralpha)
- {
- constants.blend_dst_factor_alpha = state.dstfactoralpha;
- dirty = true;
- }
- if (constants.blend_subtract != state.subtract)
- {
- constants.blend_subtract = state.subtract;
- dirty = true;
- }
- if (constants.blend_subtract_alpha != state.subtractAlpha)
- {
- constants.blend_subtract_alpha = state.subtractAlpha;
- dirty = true;
- }
- if (constants.logic_op_enable != state.logicopenable)
- {
- constants.logic_op_enable = state.logicopenable;
- dirty = true;
- }
- if (constants.logic_op_mode != state.logicmode)
- {
- constants.logic_op_mode = state.logicmode;
- dirty = true;
- }
- m_dest_alpha_dirty = true;
- }
- void PixelShaderManager::SetBoundingBoxActive(bool active)
- {
- const bool enable = active && g_ActiveConfig.bBBoxEnable;
- if (enable == (constants.bounding_box != 0))
- return;
- constants.bounding_box = active;
- dirty = true;
- }
- void PixelShaderManager::DoState(PointerWrap& p)
- {
- p.Do(m_fog_range_adjusted_changed);
- p.Do(m_viewport_changed);
- p.Do(m_indirect_dirty);
- p.Do(m_dest_alpha_dirty);
- p.Do(constants);
- if (p.IsReadMode())
- {
- // Fixup the current state from global GPU state
- // NOTE: This requires that all GPU memory has been loaded already.
- Dirty();
- }
- }
|