GS: Draw alpha pass when dual source blend is missing

This commit is contained in:
Connor McLaughlin 2021-12-31 17:29:26 +10:00 committed by refractionpcsx2
parent a8b9df3952
commit 19d310475b
23 changed files with 364 additions and 123 deletions

View File

@ -57,6 +57,9 @@
#define PS_AUTOMATIC_LOD 0
#define PS_MANUAL_LOD 0
#define PS_TEX_IS_FB 0
#define PS_NO_ABLEND 0
#define PS_ONLY_ALPHA 0
#define PS_NO_COLOR1 0
#endif
#define SW_BLEND (PS_BLEND_A || PS_BLEND_B || PS_BLEND_D)
@ -100,7 +103,9 @@ struct PS_INPUT
struct PS_OUTPUT
{
float4 c0 : SV_Target0;
#if !PS_NO_COLOR1
float4 c1 : SV_Target1;
#endif
#if PS_ZCLAMP
float depth : SV_Depth;
#endif
@ -855,7 +860,18 @@ PS_OUTPUT ps_main(PS_INPUT input)
ps_fbmask(C, input.p.xy);
output.c0 = C / 255.0f;
#if !PS_NO_COLOR1
output.c1 = (float4)(alpha_blend);
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
output.c0.a = alpha_blend;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
output.c0.rgb = float3(0.0f, 0.0f, 0.0f);
#endif
#if PS_ZCLAMP
output.depth = min(input.p.z, MaxDepthPS);

View File

@ -50,7 +50,7 @@ in SHADER
#endif
#endif
#ifndef DISABLE_DUAL_SOURCE
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
// Same buffer but 2 colors for dual source blending
layout(location = 0, index = 0) TARGET_0_QUALIFIER vec4 SV_Target0;
layout(location = 0, index = 1) out vec4 SV_Target1;
@ -943,10 +943,19 @@ void ps_main()
ps_fbmask(C);
SV_Target0 = C / 255.0f;
#ifndef DISABLE_DUAL_SOURCE
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
SV_Target1 = vec4(alpha_blend);
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
SV_Target0.a = alpha_blend;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
SV_Target0.rgb = vec3(0.0f);
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#endif

View File

@ -378,7 +378,7 @@ layout(location = 0) in VSOutput
#endif
} vsIn;
#ifndef DISABLE_DUAL_SOURCE
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
layout(location = 0, index = 0) out vec4 o_col0;
layout(location = 0, index = 1) out vec4 o_col1;
#else
@ -1202,10 +1202,19 @@ void main()
ps_fbmask(C);
o_col0 = C / 255.0f;
#ifndef DISABLE_DUAL_SOURCE
#if !defined(DISABLE_DUAL_SOURCE) && !PS_NO_COLOR1
o_col1 = vec4(alpha_blend);
#endif
#if PS_NO_ABLEND
// write alpha blend factor into col0
o_col0.a = alpha_blend;
#endif
#if PS_ONLY_ALPHA
// rgb isn't used
o_col0.rgb = vec3(0.0f);
#endif
#if PS_ZCLAMP
gl_FragDepth = min(gl_FragCoord.z, MaxDepthPS);
#endif

View File

@ -182,6 +182,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideTextureBarriers, "EmuCore/GS", "OverrideTextureBarriers", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.overrideGeometryShader, "EmuCore/GS", "OverrideGeometryShaders", -1, -1);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableFramebufferFetch, "EmuCore/GS", "DisableFramebufferFetch", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDualSource, "EmuCore/GS", "DisableDualSourceBlend", false);
//////////////////////////////////////////////////////////////////////////
// SW Settings

View File

@ -1033,6 +1033,13 @@
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="disableDualSource">
<property name="text">
<string>Disable Dual Source Blending</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="disableFramebufferFetch">
<property name="text">
<string>Disable Framebuffer Fetch</string>

View File

@ -423,6 +423,7 @@ struct Pcsx2Config
UseDebugDevice : 1,
UseBlitSwapChain : 1,
DisableShaderCache : 1,
DisableDualSourceBlend : 1,
DisableFramebufferFetch : 1,
ThreadedPresentation : 1,
OsdShowMessages : 1,

View File

@ -14,7 +14,7 @@
#include "imgui_impl_vulkan.h"
#include <array>
static constexpr u32 SHADER_CACHE_VERSION = 3;
static constexpr u32 SHADER_CACHE_VERSION = 4;
class VulkanHostDisplayTexture : public HostDisplayTexture
{

View File

@ -1308,6 +1308,7 @@ void GSApp::Init()
m_default_configuration["CrcHacksExclusions"] = "";
m_default_configuration["disable_hw_gl_draw"] = "0";
m_default_configuration["disable_shader_cache"] = "0";
m_default_configuration["DisableDualSourceBlend"] = "0";
m_default_configuration["DisableFramebufferFetch"] = "0";
m_default_configuration["dithering_ps2"] = "2";
m_default_configuration["dump"] = "0";
@ -1642,4 +1643,4 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){
}},
END_HOTKEY_LIST()
#endif
#endif

View File

@ -498,18 +498,38 @@ GSAdapter::GSAdapter(const DXGI_ADAPTER_DESC1& desc_dxgi)
// TODO
#endif
HWBlend GSDevice::GetBlend(size_t index)
HWBlend GSDevice::GetBlend(size_t index, bool replace_dual_src)
{
HWBlend blend = m_blendMap[index];
blend.op = ConvertBlendEnum(blend.op);
blend.src = ConvertBlendEnum(blend.src);
blend.dst = ConvertBlendEnum(blend.dst);
blend.src = ConvertBlendEnum(replace_dual_src ? m_replaceDualSrcBlendMap[blend.src] : blend.src);
blend.dst = ConvertBlendEnum(replace_dual_src ? m_replaceDualSrcBlendMap[blend.dst] : blend.dst);
return blend;
}
// clang-format off
std::array<HWBlend, 3*3*3*3 + 1> GSDevice::m_blendMap =
const std::array<u16, 16> GSDevice::m_replaceDualSrcBlendMap =
{{
SRC_COLOR, // SRC_COLOR
INV_SRC_COLOR, // INV_SRC_COLOR
DST_COLOR, // DST_COLOR
INV_DST_COLOR, // INV_DST_COLOR
SRC_COLOR, // SRC1_COLOR
INV_SRC_COLOR, // INV_SRC1_COLOR
SRC_ALPHA, // SRC_ALPHA
INV_SRC_ALPHA, // INV_SRC_ALPHA
DST_ALPHA, // DST_ALPHA
INV_DST_ALPHA, // INV_DST_ALPHA
SRC_ALPHA, // SRC1_ALPHA
INV_SRC_ALPHA, // INV_SRC1_ALPHA
CONST_COLOR, // CONST_COLOR
INV_CONST_COLOR, // INV_CONST_COLOR
CONST_ONE, // CONST_ONE
CONST_ZERO // CONST_ZERO
}};
const std::array<HWBlend, 3*3*3*3 + 1> GSDevice::m_blendMap =
{{
{ BLEND_NO_REC , OP_ADD , CONST_ONE , CONST_ZERO} , // 0000: (Cs - Cs)*As + Cs ==> Cs
{ BLEND_CD , OP_ADD , CONST_ZERO , CONST_ONE} , // 0001: (Cs - Cs)*As + Cd ==> Cd

View File

@ -15,6 +15,7 @@
#pragma once
#include "common/HashCombine.h"
#include "common/WindowInfo.h"
#include "GSFastList.h"
#include "GSTexture.h"
@ -150,6 +151,7 @@ struct alignas(16) GSHWDrawConfig
Triangle,
Sprite,
};
#pragma pack(push, 1)
struct GSSelector
{
union
@ -182,6 +184,8 @@ struct alignas(16) GSHWDrawConfig
VSSelector(): key(0) {}
VSSelector(u8 k): key(k) {}
};
#pragma pack(pop)
#pragma pack(push, 4)
struct PSSelector
{
// Performance note: there are too many shader combinations
@ -233,6 +237,9 @@ struct alignas(16) GSHWDrawConfig
u32 colclip : 1;
u32 blend_mix : 1;
u32 pabe : 1;
u32 no_ablend : 1; // output alpha blend in col0 (for no-DSB)
u32 no_color1 : 1; // disables second color output (when unnecessary)
u32 only_alpha : 1; // don't bother computing RGB
// Others ways to fetch the texture
u32 channel : 3;
@ -255,19 +262,36 @@ struct alignas(16) GSHWDrawConfig
// Scan mask
u32 scanmsk : 2;
//u32 _free2 : 0;
};
u64 key;
struct
{
u64 key_lo;
u32 key_hi;
};
};
PSSelector(): key(0) {}
__fi PSSelector() : key_lo(0), key_hi(0) {}
__fi bool operator==(const PSSelector& rhs) const { return (key_lo == rhs.key_lo && key_hi == rhs.key_hi); }
__fi bool operator!=(const PSSelector& rhs) const { return (key_lo != rhs.key_lo || key_hi != rhs.key_hi); }
__fi bool operator<(const PSSelector& rhs) const { return (key_lo < rhs.key_lo || key_hi < rhs.key_hi); }
__fi bool IsFeedbackLoop() const
{
return tex_is_fb || fbmask || date > 0 || blend_a == 1 || blend_b == 1 || blend_c == 1 || blend_d == 1;
}
};
#pragma pack(pop)
struct PSSelectorHash
{
std::size_t operator()(const PSSelector& p) const
{
std::size_t h = 0;
HashCombine(h, p.key_lo, p.key_hi);
return h;
}
};
#pragma pack(push, 1)
struct SamplerSelector
{
union
@ -284,7 +308,7 @@ struct alignas(16) GSHWDrawConfig
u8 key;
};
SamplerSelector(): key(0) {}
SamplerSelector(u32 k): key(k) {}
SamplerSelector(u8 k): key(k) {}
static SamplerSelector Point() { return SamplerSelector(); }
static SamplerSelector Linear()
{
@ -338,7 +362,7 @@ struct alignas(16) GSHWDrawConfig
u8 key;
};
DepthStencilSelector(): key(0) {}
DepthStencilSelector(u32 k): key(k) {}
DepthStencilSelector(u8 k): key(k) {}
static DepthStencilSelector NoDepth()
{
DepthStencilSelector out;
@ -366,8 +390,9 @@ struct alignas(16) GSHWDrawConfig
u8 key;
};
ColorMaskSelector(): key(0xF) {}
ColorMaskSelector(u32 c): key(0) { wrgba = c; }
ColorMaskSelector(u8 c): key(0) { wrgba = c; }
};
#pragma pack(pop)
struct alignas(16) VSConstantBuffer
{
GSVector2 vertex_scale;
@ -463,11 +488,12 @@ struct alignas(16) GSHWDrawConfig
bool is_constant : 1;
bool is_accumulation : 1;
bool is_mixed_hw_sw : 1;
bool replace_dual_src: 1;
};
u32 key;
};
BlendState(): key(0) {}
BlendState(u8 index, u8 factor, bool is_constant, bool is_accumulation, bool is_mixed_hw_sw)
BlendState(u8 index, u8 factor, bool is_constant, bool is_accumulation, bool is_mixed_hw_sw, bool replace_dual_src)
: key(0)
{
this->index = index;
@ -475,6 +501,7 @@ struct alignas(16) GSHWDrawConfig
this->is_constant = is_constant;
this->is_accumulation = is_accumulation;
this->is_mixed_hw_sw = is_mixed_hw_sw;
this->replace_dual_src = replace_dual_src;
}
};
enum class DestinationAlphaMode : u8
@ -500,9 +527,9 @@ struct alignas(16) GSHWDrawConfig
GSVector4i drawarea; ///< Area in the framebuffer which will be modified.
Topology topology; ///< Draw topology
alignas(8) PSSelector ps;
GSSelector gs;
VSSelector vs;
PSSelector ps;
BlendState blend;
SamplerSelector sampler;
@ -516,14 +543,18 @@ struct alignas(16) GSHWDrawConfig
bool datm : 1;
bool line_expand : 1;
struct AlphaSecondPass
struct AlphaPass
{
alignas(8) PSSelector ps;
bool enable;
ColorMaskSelector colormask;
DepthStencilSelector depth;
PSSelector ps;
float ps_aref;
} alpha_second_pass;
};
static_assert(sizeof(AlphaPass) == 24, "alpha pass is 24 bytes");
AlphaPass alpha_second_pass;
AlphaPass alpha_third_pass;
VSConstantBuffer cb_vs;
PSConstantBuffer cb_ps;
@ -554,7 +585,8 @@ public:
private:
FastList<GSTexture*> m_pool;
static std::array<HWBlend, 3*3*3*3 + 1> m_blendMap;
static const std::array<HWBlend, 3*3*3*3 + 1> m_blendMap;
static const std::array<u16, 16> m_replaceDualSrcBlendMap;
protected:
enum : u16
@ -707,7 +739,7 @@ public:
// Convert the GS blend equations to HW specific blend factors/ops
// Index is computed as ((((A * 3 + B) * 3) + C) * 3) + D. A, B, C, D taken from ALPHA register.
HWBlend GetBlend(size_t index);
HWBlend GetBlend(size_t index, bool replace_dual_src);
__fi HWBlend GetUnconvertedBlend(size_t index) { return m_blendMap[index]; }
__fi u16 GetBlendFlags(size_t index) const { return m_blendMap[index].flags; }
};

View File

@ -213,7 +213,7 @@ private:
std::unordered_map<u32, GSVertexShader11> m_vs;
wil::com_ptr_nothrow<ID3D11Buffer> m_vs_cb;
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11GeometryShader>> m_gs;
std::unordered_map<u64, wil::com_ptr_nothrow<ID3D11PixelShader>> m_ps;
std::unordered_map<PSSelector, wil::com_ptr_nothrow<ID3D11PixelShader>, GSHWDrawConfig::PSSelectorHash> m_ps;
wil::com_ptr_nothrow<ID3D11Buffer> m_ps_cb;
std::unordered_map<u32, wil::com_ptr_nothrow<ID3D11SamplerState>> m_ps_ss;
wil::com_ptr_nothrow<ID3D11SamplerState> m_palette_ss;
@ -291,7 +291,7 @@ public:
bool CreateTextureFX();
void SetupVS(VSSelector sel, const GSHWDrawConfig::VSConstantBuffer* cb);
void SetupGS(GSSelector sel);
void SetupPS(PSSelector sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
void SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel);
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8 afix);
void RenderHW(GSHWDrawConfig& config) final;

View File

@ -147,9 +147,9 @@ void GSDevice11::SetupGS(GSSelector sel)
GSSetShader(gs.get(), m_vs_cb.get());
}
void GSDevice11::SetupPS(PSSelector sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel)
void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstantBuffer* cb, PSSamplerSelector ssel)
{
auto i = std::as_const(m_ps).find(sel.key);
auto i = std::as_const(m_ps).find(sel);
if (i == m_ps.end())
{
@ -195,9 +195,12 @@ void GSDevice11::SetupPS(PSSelector sel, const GSHWDrawConfig::PSConstantBuffer*
sm.AddMacro("PS_AUTOMATIC_LOD", sel.automatic_lod);
sm.AddMacro("PS_MANUAL_LOD", sel.manual_lod);
sm.AddMacro("PS_TEX_IS_FB", sel.tex_is_fb);
sm.AddMacro("PS_NO_ABLEND", sel.no_ablend);
sm.AddMacro("PS_ONLY_ALPHA", sel.only_alpha);
sm.AddMacro("PS_NO_COLOR1", sel.no_color1);
wil::com_ptr_nothrow<ID3D11PixelShader> ps = m_shader_cache.GetPixelShader(m_dev.get(), m_tfx_source, sm.GetPtr(), "ps_main");
i = m_ps.try_emplace(sel.key, std::move(ps)).first;
i = m_ps.try_emplace(sel, std::move(ps)).first;
}
if (cb && m_ps_cb_cache.Update(*cb))
@ -335,7 +338,7 @@ void GSDevice11::SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, u8
if (bsel.abe)
{
const HWBlend blend = GetBlend(bsel.blend_index);
const HWBlend blend = GetBlend(bsel.blend_index, false);
bd.RenderTarget[0].BlendEnable = TRUE;
bd.RenderTarget[0].BlendOp = (D3D11_BLEND_OP)blend.op;
bd.RenderTarget[0].SrcBlend = (D3D11_BLEND)blend.src;

View File

@ -521,7 +521,7 @@ void GSRendererNew::EmulateChannelShuffle(const GSTextureCache::Source* tex)
}
}
void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass)
{
// AA1: Don't enable blending on AA1, not yet implemented on hardware mode,
// it requires coverage sample so it's safer to turn it off instead.
@ -536,6 +536,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
if (FBMASK || PABE || !(PRIM->ABE || AA1))
{
m_conf.blend = {};
m_conf.ps.no_color1 = true;
return;
}
@ -629,19 +630,9 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
&& (m_env.COLCLAMP.CLAMP) // Let's add a colclamp check too, hw blend will clamp to 0-1.
&& !(m_conf.require_one_barrier || m_conf.require_full_barrier); // Also don't run if there are barriers present.
bool sw_blending = false;
if (!features.dual_source_blend)
{
const HWBlend unconverted_blend = g_gs_device->GetUnconvertedBlend(blend_index);
if (GSDevice::IsDualSourceBlendFactor(unconverted_blend.dst) ||
GSDevice::IsDualSourceBlendFactor(unconverted_blend.src))
{
sw_blending = true;
}
}
// Warning no break on purpose
// Note: the [[fallthrough]] attribute tell compilers not to complain about not having breaks.
bool sw_blending = false;
if (features.texture_barrier)
{
// Condition 1: Require full sw blend for full barrier.
@ -740,6 +731,50 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
}
}
bool replace_dual_src = false;
if (!features.dual_source_blend)
{
const HWBlend unconverted_blend = g_gs_device->GetUnconvertedBlend(blend_index);
if (GSDevice::IsDualSourceBlendFactor(unconverted_blend.dst) ||
GSDevice::IsDualSourceBlendFactor(unconverted_blend.src))
{
// if we don't have an alpha channel, we don't need a second pass, just output the alpha blend
// in the single colour's alpha chnanel, and blend with it
if (!m_conf.colormask.wa)
{
GL_INS("Outputting alpha blend in col0 because of no alpha write");
m_conf.ps.no_ablend = true;
replace_dual_src = true;
}
else if (features.framebuffer_fetch || m_conf.require_one_barrier || m_conf.require_full_barrier)
{
// prefer single pass sw blend (if barrier) or framebuffer fetch over dual pass alpha when supported
sw_blending = true;
color_dest_blend = false;
accumulation_blend &= !features.framebuffer_fetch;
blend_mix = false;
}
else
{
// split the draw into two
blending_alpha_pass = true;
replace_dual_src = true;
}
}
}
else if (features.framebuffer_fetch)
{
// If we have fbfetch, use software blending when we need the fb value for anything else.
// This saves outputting the second color when it's not needed.
if (m_conf.require_one_barrier || m_conf.require_full_barrier)
{
sw_blending = true;
color_dest_blend = false;
accumulation_blend = false;
blend_mix = false;
}
}
// Color clip
if (m_env.COLCLAMP.CLAMP == 0)
{
@ -849,7 +884,7 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
if (accumulation_blend)
{
// Keep HW blending to do the addition/subtraction
m_conf.blend = {blend_index, 0, false, true, false};
m_conf.blend = {blend_index, 0, false, true, false, replace_dual_src};
if (m_conf.ps.blend_a == 2)
{
// The blend unit does a reverse subtraction so it means
@ -861,12 +896,15 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
// Remove the addition/substraction from the SW blending
m_conf.ps.blend_d = 2;
// Dual source output not needed (accumulation blend replaces it with ONE).
m_conf.ps.no_color1 = true;
// Only Ad case will require one barrier
m_conf.require_one_barrier |= blend_ad_alpha_masked;
}
else if (blend_mix)
{
m_conf.blend = {blend_index, ALPHA.FIX, m_conf.ps.blend_c == 2, false, true};
m_conf.blend = {blend_index, ALPHA.FIX, m_conf.ps.blend_c == 2, false, true, replace_dual_src};
m_conf.ps.blend_mix = 1;
if (blend_mix1)
@ -900,6 +938,8 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
{
// Disable HW blending
m_conf.blend = {};
replace_dual_src = false;
blending_alpha_pass = false;
const bool blend_non_recursive_one_barrier = blend_non_recursive && blend_ad_alpha_masked;
if (blend_non_recursive_one_barrier)
@ -966,11 +1006,11 @@ void GSRendererNew::EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER)
{
// 24 bits doesn't have an alpha channel so use 1.0f fix factor as equivalent
const u8 hacked_blend_index = blend_index + 3; // +3 <=> +1 on C
m_conf.blend = {hacked_blend_index, 128, true, false, false};
m_conf.blend = {hacked_blend_index, 128, true, false, false, replace_dual_src};
}
else
{
m_conf.blend = {blend_index, ALPHA.FIX, m_conf.ps.blend_c == 2, false, false};
m_conf.blend = {blend_index, ALPHA.FIX, m_conf.ps.blend_c == 2, false, false, replace_dual_src};
}
}
@ -1461,13 +1501,15 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
// Blend
bool blending_alpha_pass = false;
if (!IsOpaque() && rt)
{
EmulateBlending(DATE_PRIMID, DATE_BARRIER);
EmulateBlending(DATE_PRIMID, DATE_BARRIER, blending_alpha_pass);
}
else
{
m_conf.blend = {}; // No blending please
m_conf.ps.no_color1 = true;
}
if (features.framebuffer_fetch)
@ -1477,6 +1519,10 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
m_conf.require_full_barrier = false;
}
// Don't emit the second color output from the pixel shader when it's
// not going to be used (no or sw blending).
m_conf.ps.no_color1 |= (m_conf.blend.index == 0);
if (m_conf.ps.scanmsk & 2)
DATE_PRIMID = false; // to have discard in the shader work correctly
@ -1756,6 +1802,46 @@ void GSRendererNew::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
m_conf.alpha_second_pass.enable = false;
}
if (blending_alpha_pass)
{
// ensure alpha blend output is disabled on both passes
m_conf.ps.no_ablend = true;
// if we're doing RGBA then Z alpha testing, we can't combine Z and A, and we need a third pass :(
if (ate_RGBA_then_Z)
{
// move second pass to third pass, since we want to write A first
std::memcpy(&m_conf.alpha_third_pass, &m_conf.alpha_second_pass, sizeof(m_conf.alpha_third_pass));
m_conf.alpha_third_pass.ps.no_ablend = true;
m_conf.alpha_second_pass.enable = false;
}
if (!m_conf.alpha_second_pass.enable)
{
m_conf.alpha_second_pass.enable = true;
memcpy(&m_conf.alpha_second_pass.ps, &m_conf.ps, sizeof(m_conf.ps));
memcpy(&m_conf.alpha_second_pass.colormask, &m_conf.colormask, sizeof(m_conf.colormask));
memcpy(&m_conf.alpha_second_pass.depth, &m_conf.depth, sizeof(m_conf.depth));
// disable alpha writes on first pass
m_conf.colormask.wa = false;
}
// only need to compute the alpha component (allow the shader to optimize better)
m_conf.alpha_second_pass.ps.no_ablend = false;
m_conf.alpha_second_pass.ps.only_alpha = true;
m_conf.alpha_second_pass.colormask.wr = m_conf.alpha_second_pass.colormask.wg = m_conf.alpha_second_pass.colormask.wb = false;
m_conf.alpha_second_pass.colormask.wa = true;
// if depth writes are on, we can optimize to an EQUAL test, otherwise we leave the tests alone
// since the alpha channel isn't blended, the last fragment wins and this'll be okay
if (m_conf.alpha_second_pass.depth.zwe)
{
m_conf.alpha_second_pass.depth.zwe = false;
m_conf.alpha_second_pass.depth.ztst = ZTST_GEQUAL;
}
}
if (m_conf.require_full_barrier && m_prim_overlap == PRIM_OVERLAP_NO)
{
m_conf.require_full_barrier = false;

View File

@ -28,7 +28,7 @@ private:
inline void SetupIA(const float& sx, const float& sy);
inline void EmulateTextureShuffleAndFbmask();
inline void EmulateChannelShuffle(const GSTextureCache::Source* tex);
inline void EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER);
inline void EmulateBlending(bool& DATE_PRIMID, bool& DATE_BARRIER, bool& blending_alpha_pass);
inline void EmulateTextureSampler(const GSTextureCache::Source* tex);
inline void EmulateZbuffer();
inline void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);

View File

@ -291,6 +291,7 @@ namespace GLLoader
// Rendering might be corrupted but it could be good enough for test/virtual machine.
optional("GL_ARB_texture_barrier");
has_dual_source_blend = GLAD_GL_VERSION_3_2 || GLAD_GL_ARB_blend_func_extended;
found_framebuffer_fetch = GLAD_GL_EXT_shader_framebuffer_fetch || GLAD_GL_ARM_shader_framebuffer_fetch;
if (found_framebuffer_fetch && GSConfig.DisableFramebufferFetch)
{

View File

@ -30,7 +30,7 @@ namespace GLState
u16 f_sRGB;
u16 f_dRGB;
u8 bf;
u32 wrgba;
u8 wrgba;
bool depth;
GLenum depth_func;

View File

@ -32,7 +32,7 @@ namespace GLState
extern u16 f_sRGB;
extern u16 f_dRGB;
extern u8 bf;
extern u32 wrgba;
extern u8 wrgba;
extern bool depth;
extern GLenum depth_func;

View File

@ -224,7 +224,7 @@ bool GSDeviceOGL::Create(HostDisplay* display)
m_features.bptc_textures = GL_VERSION_4_2 || GL_ARB_texture_compression_bptc || GL_EXT_texture_compression_bptc;
m_features.prefer_new_textures = false;
m_features.framebuffer_fetch = GLLoader::found_framebuffer_fetch;
m_features.dual_source_blend = GLLoader::has_dual_source_blend;
m_features.dual_source_blend = GLLoader::has_dual_source_blend && !GSConfig.DisableDualSourceBlend;
GLint point_range[2] = {};
GLint line_range[2] = {};
@ -556,8 +556,8 @@ bool GSDeviceOGL::Create(HostDisplay* display)
fprintf(stdout, "Available VRAM/RAM:%lldMB for textures\n", GLState::available_vram >> 20u);
// Basic to ensure structures are correctly packed
static_assert(sizeof(VSSelector) == 4, "Wrong VSSelector size");
static_assert(sizeof(PSSelector) == 8, "Wrong PSSelector size");
static_assert(sizeof(VSSelector) == 1, "Wrong VSSelector size");
static_assert(sizeof(PSSelector) == 12, "Wrong PSSelector size");
static_assert(sizeof(PSSamplerSelector) == 1, "Wrong PSSamplerSelector size");
static_assert(sizeof(OMDepthStencilSelector) == 1, "Wrong OMDepthStencilSelector size");
static_assert(sizeof(OMColorMaskSelector) == 1, "Wrong OMColorMaskSelector size");
@ -1073,10 +1073,10 @@ std::string GSDeviceOGL::GetGSSource(GSSelector sel)
return src;
}
std::string GSDeviceOGL::GetPSSource(PSSelector sel)
std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
{
#ifdef PCSX2_DEVBUILD
Console.WriteLn("Compiling new pixel shader with selector 0x%" PRIX64, sel.key);
Console.WriteLn("Compiling new pixel shader with selector 0x%" PRIX64 "%08X", sel.key_hi, sel.key_lo);
#endif
std::string macro = format("#define PS_FST %d\n", sel.fst)
@ -1121,6 +1121,9 @@ std::string GSDeviceOGL::GetPSSource(PSSelector sel)
+ format("#define PS_PABE %d\n", sel.pabe)
+ format("#define PS_SCANMSK %d\n", sel.scanmsk)
+ format("#define PS_SCALE_FACTOR %d\n", GSConfig.UpscaleMultiplier)
+ format("#define PS_NO_ABLEND %d\n", sel.no_ablend)
+ format("#define PS_NO_COLOR1 %d\n", sel.no_color1)
+ format("#define PS_ONLY_ALPHA %d\n", sel.only_alpha)
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@ -1684,7 +1687,7 @@ void GSDeviceOGL::OMSetColorMaskState(OMColorMaskSelector sel)
}
}
void GSDeviceOGL::OMSetBlendState(u8 blend_index, u8 blend_factor, bool is_blend_constant, bool accumulation_blend, bool blend_mix)
void GSDeviceOGL::OMSetBlendState(u8 blend_index, u8 blend_factor, bool is_blend_constant, bool accumulation_blend, bool blend_mix, bool replace_dual_src)
{
if (blend_index)
{
@ -1701,7 +1704,7 @@ void GSDeviceOGL::OMSetBlendState(u8 blend_index, u8 blend_factor, bool is_blend
glBlendColor(bf, bf, bf, bf);
}
HWBlend b = GetBlend(blend_index);
HWBlend b = GetBlend(blend_index, replace_dual_src);
if (accumulation_blend)
{
b.src = GL_ONE;
@ -1897,7 +1900,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
PSSetShaderResource(2, config.rt);
SetupSampler(config.sampler);
OMSetBlendState(config.blend.index, config.blend.factor, config.blend.is_constant, config.blend.is_accumulation, config.blend.is_mixed_hw_sw);
OMSetBlendState(config.blend.index, config.blend.factor, config.blend.is_constant, config.blend.is_accumulation, config.blend.is_mixed_hw_sw, config.blend.replace_dual_src);
OMSetColorMaskState(config.colormask);
SetupOM(config.depth);
@ -1914,8 +1917,10 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
ProgramSelector psel;
psel.vs = convertSel(config.vs);
psel.ps = config.ps;
psel.ps.key_hi = config.ps.key_hi;
psel.ps.key_lo = config.ps.key_lo;
psel.gs.key = 0;
psel.pad = 0;
if (config.gs.expand)
{
psel.gs.iip = config.gs.iip;
@ -1999,6 +2004,23 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
SendHWDraw(config);
}
if (config.alpha_third_pass.enable)
{
// cbuffer will definitely be dirty if aref changes, no need to check it
if (config.cb_ps.FogColor_AREF.a != config.alpha_third_pass.ps_aref)
{
config.cb_ps.FogColor_AREF.a = config.alpha_third_pass.ps_aref;
WriteToStreamBuffer(m_fragment_uniform_stream_buffer.get(), g_ps_cb_index,
m_uniform_buffer_alignment, &config.cb_ps, sizeof(config.cb_ps));
}
psel.ps = config.alpha_third_pass.ps;
SetupPipeline(psel);
OMSetColorMaskState(config.alpha_third_pass.colormask);
SetupOM(config.alpha_third_pass.depth);
SendHWDraw(config);
}
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
RecycleDateTexture();

View File

@ -132,22 +132,20 @@ public:
{
struct
{
u32 int_fst : 1;
u32 iip : 1;
u32 point_size : 1;
u32 _free : 29;
u8 int_fst : 1;
u8 iip : 1;
u8 point_size : 1;
u8 _free : 5;
};
u32 key;
u8 key;
};
operator u32() const { return key; }
VSSelector()
: key(0)
{
}
VSSelector(u32 k)
VSSelector(u8 k)
: key(k)
{
}
@ -159,15 +157,15 @@ public:
{
struct
{
u32 sprite : 1;
u32 point : 1;
u32 line : 1;
u32 iip : 1;
u8 sprite : 1;
u8 point : 1;
u8 line : 1;
u8 iip : 1;
u32 _free : 28;
u8 _free : 4;
};
u32 key;
u8 key;
};
operator u32() const { return key; }
@ -176,7 +174,7 @@ public:
: key(0)
{
}
GSSelector(u32 k)
GSSelector(u8 k)
: key(k)
{
}
@ -187,22 +185,24 @@ public:
using OMDepthStencilSelector = GSHWDrawConfig::DepthStencilSelector;
using OMColorMaskSelector = GSHWDrawConfig::ColorMaskSelector;
struct ProgramSelector
struct alignas(16) ProgramSelector
{
PSSelector ps;
VSSelector vs;
GSSelector gs;
PSSelector ps;
u16 pad;
__fi bool operator==(const ProgramSelector& p) const { return vs.key == p.vs.key && gs.key == p.gs.key && ps.key == p.ps.key; }
__fi bool operator!=(const ProgramSelector& p) const { return vs.key != p.vs.key || gs.key != p.gs.key || ps.key != p.ps.key; }
__fi bool operator==(const ProgramSelector& p) const { return (std::memcmp(this, &p, sizeof(*this)) == 0); }
__fi bool operator!=(const ProgramSelector& p) const { return (std::memcmp(this, &p, sizeof(*this)) != 0); }
};
static_assert(sizeof(ProgramSelector) == 16, "Program selector is 16 bytes");
struct ProgramSelectorHash
{
__fi std::size_t operator()(const ProgramSelector& p) const noexcept
{
std::size_t h = 0;
HashCombine(h, p.vs.key, p.gs.key, p.ps.key);
HashCombine(h, p.vs.key, p.gs.key, p.ps.key_hi, p.ps.key_lo);
return h;
}
};
@ -212,7 +212,7 @@ public:
private:
// Increment this constant whenever shaders change, to invalidate user's program binary cache.
static constexpr u32 SHADER_VERSION = 2;
static constexpr u32 SHADER_VERSION = 3;
static FILE* m_debug_gl_file;
@ -372,7 +372,7 @@ public:
void ClearSamplerCache() final;
void OMSetDepthStencilState(GSDepthStencilOGL* dss);
void OMSetBlendState(u8 blend_index = 0, u8 blend_factor = 0, bool is_blend_constant = false, bool accumulation_blend = false, bool blend_mix = false);
void OMSetBlendState(u8 blend_index = 0, u8 blend_factor = 0, bool is_blend_constant = false, bool accumulation_blend = false, bool blend_mix = false, bool replace_dual_src = false);
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, const GSVector4i* scissor = NULL);
void OMSetColorMaskState(OMColorMaskSelector sel = OMColorMaskSelector());
@ -384,7 +384,7 @@ public:
std::string GenGlslHeader(const std::string_view& entry, GLenum type, const std::string_view& macro);
std::string GetVSSource(VSSelector sel);
std::string GetGSSource(GSSelector sel);
std::string GetPSSource(PSSelector sel);
std::string GetPSSource(const PSSelector& sel);
GLuint CreateSampler(PSSamplerSelector sel);
GSDepthStencilOGL* CreateDepthStencil(OMDepthStencilSelector dssel);

View File

@ -244,9 +244,9 @@ bool GSDeviceVK::CheckFeatures()
m_features.prefer_new_textures = true;
m_features.provoking_vertex_last = g_vulkan_context->GetOptionalExtensions().vk_ext_provoking_vertex;
m_features.framebuffer_fetch = false;
m_features.dual_source_blend = features.dualSrcBlend;
m_features.dual_source_blend = features.dualSrcBlend && !GSConfig.DisableDualSourceBlend;
if (!features.dualSrcBlend)
if (!m_features.dual_source_blend)
Console.Warning("Vulkan driver is missing dual-source blending. This will have an impact on performance.");
if (!m_features.texture_barrier)
@ -1040,6 +1040,8 @@ static void AddShaderHeader(std::stringstream& ss)
const GSDevice::FeatureSupport features(g_gs_device->Features());
if (!features.texture_barrier)
ss << "#define DISABLE_TEXTURE_BARRIER 1\n";
if (!features.dual_source_blend)
ss << "#define DISABLE_DUAL_SOURCE 1\n";
}
static void AddShaderStageMacro(std::stringstream& ss, bool vs, bool gs, bool fs)
@ -1767,9 +1769,9 @@ VkShaderModule GSDeviceVK::GetTFXGeometryShader(GSHWDrawConfig::GSSelector sel)
return mod;
}
VkShaderModule GSDeviceVK::GetTFXFragmentShader(GSHWDrawConfig::PSSelector sel)
VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector& sel)
{
const auto it = m_tfx_fragment_shaders.find(sel.key);
const auto it = m_tfx_fragment_shaders.find(sel);
if (it != m_tfx_fragment_shaders.end())
return it->second;
@ -1819,13 +1821,16 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(GSHWDrawConfig::PSSelector sel)
AddMacro(ss, "PS_SCANMSK", sel.scanmsk);
AddMacro(ss, "PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
AddMacro(ss, "PS_TEX_IS_FB", sel.tex_is_fb);
AddMacro(ss, "PS_NO_ABLEND", sel.no_ablend);
AddMacro(ss, "PS_NO_COLOR1", sel.no_color1);
AddMacro(ss, "PS_ONLY_ALPHA", sel.only_alpha);
ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetFragmentShader(ss.str());
if (mod)
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), mod, "TFX Fragment %" PRIX64, sel.key);
Vulkan::Util::SetObjectName(g_vulkan_context->GetDevice(), mod, "TFX Fragment %" PRIX64 "%08X", sel.key_hi, sel.key_lo);
m_tfx_fragment_shaders.emplace(sel.key, mod);
m_tfx_fragment_shaders.emplace(sel, mod);
return mod;
}
@ -1837,9 +1842,20 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // Triangle
}};
GSHWDrawConfig::BlendState pbs{p.bs};
GSHWDrawConfig::PSSelector pps{p.ps};
if ((p.cms.wrgba & 0x7) == 0)
{
// disable blending when colours are masked
pbs.index = 0;
}
// don't output alpha blend when blending is disabled
pps.no_color1 = (pbs.index == 0);
VkShaderModule vs = GetTFXVertexShader(p.vs);
VkShaderModule gs = p.gs.expand ? GetTFXGeometryShader(p.gs) : VK_NULL_HANDLE;
VkShaderModule fs = GetTFXFragmentShader(p.ps);
VkShaderModule fs = GetTFXFragmentShader(pps);
if (vs == VK_NULL_HANDLE || (p.gs.expand && gs == VK_NULL_HANDLE) || fs == VK_NULL_HANDLE)
return VK_NULL_HANDLE;
@ -1901,12 +1917,19 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
gpb.SetBlendAttachment(0, true, VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_MIN, VK_BLEND_FACTOR_ONE,
VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, VK_COLOR_COMPONENT_R_BIT);
}
else if (p.bs.index > 0)
else if (pbs.index > 0)
{
const HWBlend blend = GetBlend(p.bs.index);
const HWBlend blend = GetBlend(pbs.index, pbs.replace_dual_src);
#ifdef PCSX2_DEVBUILD
pxAssertRel(m_features.dual_source_blend || pbs.is_accumulation ||
(blend.src != VK_BLEND_FACTOR_SRC1_ALPHA && blend.src != VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA &&
blend.dst != VK_BLEND_FACTOR_SRC1_ALPHA && blend.dst != VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA),
"Not using dual source factors");
#endif
gpb.SetBlendAttachment(0, true,
(p.bs.is_accumulation || p.bs.is_mixed_hw_sw) ? VK_BLEND_FACTOR_ONE : static_cast<VkBlendFactor>(blend.src),
p.bs.is_accumulation ? VK_BLEND_FACTOR_ONE : static_cast<VkBlendFactor>(blend.dst),
(pbs.is_accumulation || pbs.is_mixed_hw_sw) ? VK_BLEND_FACTOR_ONE : static_cast<VkBlendFactor>(blend.src),
pbs.is_accumulation ? VK_BLEND_FACTOR_ONE : static_cast<VkBlendFactor>(blend.dst),
static_cast<VkBlendOp>(blend.op), VK_BLEND_FACTOR_ONE, VK_BLEND_FACTOR_ZERO, VK_BLEND_OP_ADD, p.cms.wrgba);
}
else
@ -1922,7 +1945,7 @@ VkPipeline GSDeviceVK::CreateTFXPipeline(const PipelineSelector& p)
if (pipeline)
{
Vulkan::Util::SetObjectName(
g_vulkan_context->GetDevice(), pipeline, "TFX Pipeline %08X/%08X/%" PRIX64, p.vs.key, p.gs.key, p.ps.key);
g_vulkan_context->GetDevice(), pipeline, "TFX Pipeline %08X/%08X/%" PRIX64 "%08X", p.vs.key, p.gs.key, p.ps.key_hi, p.ps.key_lo);
}
return pipeline;
@ -2892,6 +2915,21 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
if (BindDrawPipeline(pipe))
SendHWDraw(config, draw_rt);
}
if (config.alpha_third_pass.enable)
{
// cbuffer will definitely be dirty if aref changes, no need to check it
if (config.cb_ps.FogColor_AREF.a != config.alpha_third_pass.ps_aref)
{
config.cb_ps.FogColor_AREF.a = config.alpha_third_pass.ps_aref;
SetPSConstantBuffer(config.cb_ps);
}
pipe.ps = config.alpha_third_pass.ps;
pipe.cms = config.alpha_third_pass.colormask;
pipe.dss = config.alpha_third_pass.depth;
if (BindDrawPipeline(pipe))
SendHWDraw(config, draw_rt);
}
if (copy_ds)
Recycle(copy_ds);
@ -2953,7 +2991,8 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config)
{
m_pipeline_selector.vs.key = config.vs.key;
m_pipeline_selector.gs.key = config.gs.key;
m_pipeline_selector.ps.key = config.ps.key;
m_pipeline_selector.ps.key_hi = config.ps.key_hi;
m_pipeline_selector.ps.key_lo = config.ps.key_lo;
m_pipeline_selector.dss.key = config.depth.key;
m_pipeline_selector.bs.key = config.blend.key;
m_pipeline_selector.bs.factor = 0; // don't dupe states with different alpha values

View File

@ -27,14 +27,9 @@
class GSDeviceVK final : public GSDevice
{
public:
struct PipelineSelector
struct alignas(8) PipelineSelector
{
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::GSSelector gs;
GSHWDrawConfig::PSSelector ps;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::BlendState bs;
GSHWDrawConfig::ColorMaskSelector cms;
union
{
@ -50,29 +45,25 @@ public:
u32 key;
};
__fi bool operator==(const PipelineSelector& p) const
{
return vs.key == p.vs.key && gs.key == p.gs.key && ps.key == p.ps.key && dss.key == p.dss.key &&
bs.key == p.bs.key && cms.key == p.cms.key && key == p.key;
}
__fi bool operator!=(const PipelineSelector& p) const
{
return vs.key != p.vs.key || gs.key != p.gs.key || ps.key != p.ps.key || dss.key != p.dss.key ||
bs.key != p.bs.key || cms.key != p.cms.key || key != p.key;
}
GSHWDrawConfig::VSSelector vs;
GSHWDrawConfig::GSSelector gs;
GSHWDrawConfig::DepthStencilSelector dss;
GSHWDrawConfig::ColorMaskSelector cms;
GSHWDrawConfig::BlendState bs;
PipelineSelector()
: key(0)
{
}
__fi bool operator==(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) == 0); }
__fi bool operator!=(const PipelineSelector& p) const { return (memcmp(this, &p, sizeof(p)) != 0); }
__fi PipelineSelector() { memset(this, 0, sizeof(*this)); }
};
static_assert(sizeof(PipelineSelector) == 24, "Pipeline selector is 24 bytes");
struct PipelineSelectorHash
{
std::size_t operator()(const PipelineSelector& e) const noexcept
{
std::size_t hash = 0;
HashCombine(hash, e.vs.key, e.gs.key, e.ps.key, e.dss.key, e.cms.key, e.bs.key, e.key);
HashCombine(hash, e.vs.key, e.gs.key, e.ps.key_hi, e.ps.key_lo, e.dss.key, e.cms.key, e.bs.key, e.key);
return hash;
}
};
@ -136,7 +127,7 @@ private:
std::unordered_map<u32, VkShaderModule> m_tfx_vertex_shaders;
std::unordered_map<u32, VkShaderModule> m_tfx_geometry_shaders;
std::unordered_map<u64, VkShaderModule> m_tfx_fragment_shaders;
std::unordered_map<GSHWDrawConfig::PSSelector, VkShaderModule, GSHWDrawConfig::PSSelectorHash> m_tfx_fragment_shaders;
std::unordered_map<PipelineSelector, VkPipeline, PipelineSelectorHash> m_tfx_pipelines;
VkRenderPass m_utility_color_render_pass_load = VK_NULL_HANDLE;
@ -168,7 +159,7 @@ private:
VkShaderModule GetTFXVertexShader(GSHWDrawConfig::VSSelector sel);
VkShaderModule GetTFXGeometryShader(GSHWDrawConfig::GSSelector sel);
VkShaderModule GetTFXFragmentShader(GSHWDrawConfig::PSSelector sel);
VkShaderModule GetTFXFragmentShader(const GSHWDrawConfig::PSSelector& sel);
VkPipeline CreateTFXPipeline(const PipelineSelector& p);
VkPipeline GetTFXPipeline(const PipelineSelector& p);

View File

@ -541,11 +541,12 @@ DebugTab::DebugTab(wxWindow* parent)
{
PaddedBoxSizer<wxStaticBoxSizer> debug_box(wxVERTICAL, this, "Debug");
auto* debug_check_box = new wxWrapSizer(wxHORIZONTAL);
m_ui.addCheckBox(debug_check_box, "Use Blit Swap Chain", "UseBlitSwapChain");
m_ui.addCheckBox(debug_check_box, "Disable Shader Cache", "disable_shader_cache");
m_ui.addCheckBox(debug_check_box, "Disable Framebuffer Fetch", "DisableFramebufferFetch");
m_ui.addCheckBox(debug_check_box, "Use Debug Device", "UseDebugDevice");
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
m_ui.addCheckBox(debug_check_box, "Use Blit Swap Chain", "UseBlitSwapChain");
m_ui.addCheckBox(debug_check_box, "Disable Shader Cache", "disable_shader_cache");
m_ui.addCheckBox(debug_check_box, "Disable Framebuffer Fetch", "DisableFramebufferFetch");
m_ui.addCheckBox(debug_check_box, "Disable Dual-Source Blending", "DisableDualSourceBlend");
m_ui.addCheckBox(debug_check_box, "Use Debug Device", "UseDebugDevice");
m_ui.addCheckBox(debug_check_box, "Dump GS data", "dump");
auto* debug_save_check_box = new wxWrapSizer(wxHORIZONTAL);
m_ui.addCheckBox(debug_save_check_box, "Save RT", "save");

View File

@ -422,6 +422,7 @@ bool Pcsx2Config::GSOptions::RestartOptionsAreEqual(const GSOptions& right) cons
OpEqu(UseDebugDevice) &&
OpEqu(UseBlitSwapChain) &&
OpEqu(DisableShaderCache) &&
OpEqu(DisableDualSourceBlend) &&
OpEqu(DisableFramebufferFetch) &&
OpEqu(ThreadedPresentation) &&
OpEqu(OverrideTextureBarriers) &&
@ -503,6 +504,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingBool(UseDebugDevice);
GSSettingBool(UseBlitSwapChain);
GSSettingBoolEx(DisableShaderCache, "disable_shader_cache");
GSSettingBool(DisableDualSourceBlend);
GSSettingBool(DisableFramebufferFetch);
GSSettingBool(ThreadedPresentation);
GSSettingBool(OsdShowMessages);