mirror of https://github.com/PCSX2/pcsx2.git
GS: Draw alpha pass when dual source blend is missing
This commit is contained in:
parent
a8b9df3952
commit
19d310475b
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -423,6 +423,7 @@ struct Pcsx2Config
|
|||
UseDebugDevice : 1,
|
||||
UseBlitSwapChain : 1,
|
||||
DisableShaderCache : 1,
|
||||
DisableDualSourceBlend : 1,
|
||||
DisableFramebufferFetch : 1,
|
||||
ThreadedPresentation : 1,
|
||||
OsdShowMessages : 1,
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace GLState
|
|||
u16 f_sRGB;
|
||||
u16 f_dRGB;
|
||||
u8 bf;
|
||||
u32 wrgba;
|
||||
u8 wrgba;
|
||||
|
||||
bool depth;
|
||||
GLenum depth_func;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue