mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-11 01:34:17 +02:00
GS/HW: ROV revisions.
Use GSTexture::FeedbackTarget RTs by default (not GSTexture::ShaderWriteTarget). Do ROV type conversion for both color and depth (if needed). Make sure the formats for ROV actually support UAV/storage image. Change 'PSSetUnorderedAccess' to 'PSSetROVs' for clarity. DX12: Use enum names instead of raw constants in PSSetShaderResource(). Some string format cleanup to GL_*() macros. VK: Do not enable ROV if FB fetch is available. Other misc cosmetic changes such as whitespace, etc. Co-authored-by: TellowKrinkle
This commit is contained in:
@@ -101,7 +101,7 @@ static u64 s_total_barriers = 0;
|
||||
static u64 s_total_copies = 0;
|
||||
static u64 s_total_uploads = 0;
|
||||
static u64 s_total_readbacks = 0;
|
||||
static u64 s_total_depth_copies_rov = 0;
|
||||
static u64 s_total_copies_rov = 0;
|
||||
static u64 s_total_draws_rov = 0;
|
||||
static u64 s_total_barriers_rov = 0;
|
||||
static u32 s_total_frames = 0;
|
||||
@@ -298,7 +298,7 @@ void Host::BeginPresentFrame()
|
||||
update_stat(GSPerfMon::TextureCopies, s_total_copies, s_last_copies);
|
||||
update_stat(GSPerfMon::TextureUploads, s_total_uploads, s_last_uploads);
|
||||
update_stat(GSPerfMon::Readbacks, s_total_readbacks, s_last_readbacks);
|
||||
update_stat(GSPerfMon::DepthCopiesROV, s_total_depth_copies_rov, s_last_depth_copies_rov);
|
||||
update_stat(GSPerfMon::TextureCopiesROV, s_total_copies_rov, s_last_depth_copies_rov);
|
||||
update_stat(GSPerfMon::DrawCallsROV, s_total_draws_rov, s_last_draws_rov);
|
||||
update_stat(GSPerfMon::BarriersROV, s_total_barriers_rov, s_last_barriers_rov);
|
||||
|
||||
@@ -917,7 +917,7 @@ void GSRunner::DumpStats()
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Copies: {} (avg {})", s_total_copies, static_cast<u64>(std::ceil(s_total_copies / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Uploads: {} (avg {})", s_total_uploads, static_cast<u64>(std::ceil(s_total_uploads / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Readbacks: {} (avg {})", s_total_readbacks, static_cast<u64>(std::ceil(s_total_readbacks / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Depth Copies (ROV): {} (avg {})", s_total_depth_copies_rov, static_cast<u64>(std::ceil(s_total_depth_copies_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Copies (ROV): {} (avg {})", s_total_copies_rov, static_cast<u64>(std::ceil(s_total_copies_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Draws Calls (ROV): {} (avg {})", s_total_draws_rov, static_cast<u64>(std::ceil(s_total_draws_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
Console.WriteLn(fmt::format("@HWSTAT@ Barriers (ROV): {} (avg {})", s_total_barriers_rov, static_cast<u64>(std::ceil(s_total_barriers_rov / static_cast<double>(s_total_drawn_frames)))));
|
||||
if (s_perf_enable)
|
||||
|
||||
+1
-1
@@ -729,7 +729,7 @@ void GSgetStats(SmallStringBase& info)
|
||||
(int)std::ceil(pm.Get(GSPerfMon::RenderPasses)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::Readbacks)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopies)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::DepthCopiesROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureCopiesROV)),
|
||||
(int)std::ceil(pm.Get(GSPerfMon::TextureUploads)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
SyncPoint,
|
||||
Barriers,
|
||||
RenderPasses,
|
||||
DepthCopiesROV, // Overlaps with regular texture copies.
|
||||
TextureCopiesROV, // Overlaps with regular texture copies.
|
||||
DrawCallsROV, // Overlaps with regular draw calls.
|
||||
BarriersROV, // Overlaps with regular barriers.
|
||||
CounterLast,
|
||||
|
||||
@@ -696,8 +696,6 @@ void GSDevice::Recycle(GSTexture* t)
|
||||
return;
|
||||
|
||||
t->SetLastFrameUsed(m_frame);
|
||||
|
||||
t->ClearUnorderedAccess();
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
t->SetDebugName("");
|
||||
|
||||
@@ -1492,7 +1492,7 @@ protected:
|
||||
/// Resolves CAS shader includes for the specified source.
|
||||
static bool GetCASShaderSource(std::string* source);
|
||||
|
||||
/// Applies CAS and writes to the destination texture, which should be a RWTexture.
|
||||
/// Applies CAS and writes to the destination texture, which should be a shader writeable texture.
|
||||
virtual bool DoCAS(GSTexture* sTex, GSTexture* dTex, bool sharpen_only, const std::array<u32, NUM_CAS_CONSTANTS>& constants) = 0;
|
||||
|
||||
/// Perform texture operations for ImGui
|
||||
|
||||
@@ -40,7 +40,7 @@ public:
|
||||
static constexpr Usage Texture = Usage::Texture;
|
||||
static constexpr Usage RenderTarget = Usage::RenderTarget;
|
||||
static constexpr Usage DepthStencil = Usage::DepthStencil;
|
||||
static constexpr Usage FeedbackTarget = Usage::RenderTarget | Usage::Feedback | Usage::ShaderWrite;
|
||||
static constexpr Usage FeedbackTarget = Usage::RenderTarget | Usage::Feedback;
|
||||
static constexpr Usage FeedbackDepth = Usage::DepthStencil | Usage::Feedback;
|
||||
static constexpr Usage ShaderWriteTarget = Usage::RenderTarget | Usage::Feedback | Usage::ShaderWrite;
|
||||
static constexpr Usage ShaderWriteTexture = Usage::Texture | Usage::ShaderWrite;
|
||||
@@ -94,10 +94,6 @@ protected:
|
||||
bool m_needs_mipmaps_generated = true;
|
||||
ClearValue m_clear_value = {};
|
||||
|
||||
// For GL/DX11 since they don't track layouts.
|
||||
// Used heuristically to decide whether to use ROV for a draw.
|
||||
bool m_unordered_access = false;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
std::string m_debug_name;
|
||||
#endif
|
||||
@@ -223,6 +219,12 @@ public:
|
||||
return IsFeedbackOrShaderWrite(m_usage);
|
||||
}
|
||||
|
||||
virtual bool IsShaderWriteMode() const
|
||||
{
|
||||
// Backends that track state explicitly can specialize this to use the actual state.
|
||||
return IsShaderWrite();
|
||||
}
|
||||
|
||||
__fi State GetState() const { return m_state; }
|
||||
__fi void SetState(State state) { m_state = state; }
|
||||
|
||||
@@ -254,12 +256,6 @@ public:
|
||||
// Typical size of a RGBA texture
|
||||
u32 GetMemUsage() const { return m_size.x * m_size.y * (m_format == Format::UNorm8 ? 1 : 4); }
|
||||
|
||||
// The unordered access flag is sticky, so once it's set we keep using ROV for the target
|
||||
// until it's recycled/destroyed. Only used for DX11/GL, which don't track actual resource layout/state.
|
||||
virtual bool IsUnorderedAccess() const { return m_unordered_access; }
|
||||
void SetUnorderedAccess() { m_unordered_access = true; }
|
||||
void ClearUnorderedAccess() { m_unordered_access = false; }
|
||||
|
||||
// Helper routines for formats/types
|
||||
static bool IsCompressedFormat(Format format) { return (format >= Format::BC1 && format <= Format::BC7); }
|
||||
};
|
||||
|
||||
@@ -48,6 +48,15 @@ static bool IsTextureFormatHWBlendable(ID3D11Device* dev, DXGI_FORMAT format)
|
||||
return (support & D3D11_FORMAT_SUPPORT_RENDER_TARGET) && (support & D3D11_FORMAT_SUPPORT_BLENDABLE);
|
||||
}
|
||||
|
||||
static bool IsTextureFormatUAVCapable(ID3D11Device* dev, DXGI_FORMAT format)
|
||||
{
|
||||
UINT support;
|
||||
if (FAILED(dev->CheckFormatSupport(format, &support)))
|
||||
return false;
|
||||
|
||||
return (support & D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW) != 0;
|
||||
}
|
||||
|
||||
GSDevice11::GSDevice11()
|
||||
{
|
||||
memset(&m_state, 0, sizeof(m_state));
|
||||
@@ -594,7 +603,8 @@ bool GSDevice11::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
}
|
||||
|
||||
// 1x1 dummy texture.
|
||||
m_null_texture = CreateSurface(GSTexture::ShaderWriteTarget, 1, 1, 1, GSTexture::Format::Color);
|
||||
const GSTexture::Usage null_usage = m_features.rov ? GSTexture::ShaderWriteTarget : GSTexture::Feedback;
|
||||
m_null_texture = CreateSurface(null_usage, 1, 1, 1, GSTexture::Format::Color);
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
@@ -717,6 +727,11 @@ void GSDevice11::SetFeatures(IDXGIAdapter1* adapter)
|
||||
D3D11_FEATURE_DATA_D3D11_OPTIONS2 options2{};
|
||||
m_dev->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &options2, sizeof(options2));
|
||||
m_features.rov = m_uav_texture && options2.ROVsSupported;
|
||||
for (u32 fmt = static_cast<u32>(GSTexture::Format::Color); fmt <= static_cast<u32>(GSTexture::Format::PrimID); fmt++)
|
||||
{
|
||||
if (GSTexture::IsShaderWriteFormat(static_cast<GSTexture::Format>(fmt)))
|
||||
m_features.rov &= IsTextureFormatUAVCapable(m_dev.get(), GSTexture11::GetDXGIFormat(static_cast<GSTexture::Format>(fmt)));
|
||||
}
|
||||
|
||||
// Let the user know if said features are available.
|
||||
Console.WriteLnFmt("D3D11: DXTn Texture Compression: {}", m_features.dxt_textures ? "Supported" : "Not Supported");
|
||||
|
||||
@@ -102,6 +102,8 @@ void GSTexture11::SetDebugName(std::string_view name)
|
||||
GSDevice11::SetD3DDebugObjectName(m_srv.get(), fmt::format("{} SRV", name));
|
||||
if (m_rtv)
|
||||
GSDevice11::SetD3DDebugObjectName(m_rtv.get(), fmt::format("{} RTV", name));
|
||||
if (m_uav)
|
||||
GSDevice11::SetD3DDebugObjectName(m_uav.get(), fmt::format("{} UAV", name));
|
||||
|
||||
m_debug_name = name;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,15 @@ bool GSDevice12::SupportsTextureFormat(DXGI_FORMAT format)
|
||||
(support.Support1 & required) == required;
|
||||
}
|
||||
|
||||
bool GSDevice12::IsTextureFormatUAVCapable(DXGI_FORMAT format)
|
||||
{
|
||||
constexpr u32 required = D3D12_FORMAT_SUPPORT1_TYPED_UNORDERED_ACCESS_VIEW;
|
||||
|
||||
D3D12_FEATURE_DATA_FORMAT_SUPPORT support = { format };
|
||||
return SUCCEEDED(m_device->CheckFeatureSupport(D3D12_FEATURE_FORMAT_SUPPORT, &support, sizeof(support))) &&
|
||||
(support.Support1 & required) == required;
|
||||
}
|
||||
|
||||
bool GSDevice12::SupportsProgrammableSamplePositions()
|
||||
{
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS2 options = {};
|
||||
@@ -1441,6 +1450,15 @@ bool GSDevice12::CheckFeatures(const u32& vendor_id)
|
||||
D3D12_FEATURE_DATA_D3D12_OPTIONS options{};
|
||||
m_device->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(D3D12_FEATURE_DATA_D3D12_OPTIONS));
|
||||
m_features.rov = options.ROVsSupported;
|
||||
for (u32 fmt = static_cast<u32>(GSTexture::Format::Color); fmt <= static_cast<u32>(GSTexture::Format::PrimID); fmt++)
|
||||
{
|
||||
if (GSTexture::IsShaderWriteFormat(static_cast<GSTexture::Format>(fmt)))
|
||||
{
|
||||
DXGI_FORMAT dxgi_fmt;
|
||||
LookupNativeFormat(static_cast<GSTexture::Format>(fmt), &dxgi_fmt, nullptr, nullptr, nullptr, nullptr);
|
||||
m_features.rov &= IsTextureFormatUAVCapable(dxgi_fmt);
|
||||
}
|
||||
}
|
||||
Console.WriteLnFmt("D3D12: Rasterizer Ordered Views: {}", m_features.rov ? "Supported" : "Not Supported");
|
||||
|
||||
Console.WriteLnFmt("D3D12: Tight Alignment: {}", m_allocator->IsTightAlignmentSupported() ? "Supported" : "Not Supported");
|
||||
@@ -1635,7 +1653,7 @@ void GSDevice12::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r,
|
||||
sTex12->TransitionToState(GSTexture12::ResourceState::CopySrc);
|
||||
sTex12->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
if (m_tfx_textures[0] && sTex12->GetSRVDescriptor() == m_tfx_textures[0])
|
||||
PSSetShaderResource(0, nullptr, false);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
|
||||
|
||||
dTex12->TransitionToState(GSTexture12::ResourceState::CopyDst);
|
||||
dTex12->SetUseFenceCounter(GetCurrentFenceValue());
|
||||
@@ -2605,9 +2623,11 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string
|
||||
|
||||
bool GSDevice12::CreateNullTexture()
|
||||
{
|
||||
const GSTexture::Usage null_access = m_features.rov ? GSTexture::ShaderWriteTarget : GSTexture::FeedbackTarget;
|
||||
const DXGI_FORMAT null_uav_format = m_features.rov ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_UNKNOWN;
|
||||
m_null_texture =
|
||||
GSTexture12::Create(GSTexture::ShaderWriteTarget, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
GSTexture12::Create(null_access, GSTexture::Format::Color, 1, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_UNKNOWN, null_uav_format);
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
@@ -3528,7 +3548,7 @@ void GSDevice12::PSSetSampler(GSHWDrawConfig::SamplerSelector sel)
|
||||
m_dirty_flags |= DIRTY_FLAG_TFX_SAMPLERS;
|
||||
}
|
||||
|
||||
void GSDevice12::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
void GSDevice12::PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
{
|
||||
GSTexture12* d12Rt = static_cast<GSTexture12*>(rt);
|
||||
GSTexture12* d12Ds = static_cast<GSTexture12*>(ds);
|
||||
@@ -4222,7 +4242,7 @@ GSTexture12* GSDevice12::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config, Pipe
|
||||
|
||||
// and bind the image to the primitive sampler
|
||||
image->TransitionToState(GSTexture12::ResourceState::PixelShaderResource);
|
||||
PSSetShaderResource(3, image, false);
|
||||
PSSetShaderResource(TEXTURE_PRIMID, image, false);
|
||||
return image;
|
||||
}
|
||||
|
||||
@@ -4340,11 +4360,11 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
// bind textures before checking the render pass, in case we need to transition them
|
||||
if (config.tex)
|
||||
{
|
||||
PSSetShaderResource(0, config.tex, config.tex != config.rt && config.tex != config.ds);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, config.tex, config.tex != config.rt && config.tex != config.ds);
|
||||
PSSetSampler(config.sampler);
|
||||
}
|
||||
if (config.pal)
|
||||
PSSetShaderResource(1, config.pal, true);
|
||||
PSSetShaderResource(TEXTURE_PALETTE, config.pal, true);
|
||||
|
||||
if (config.blend.constant_enable)
|
||||
SetBlendConstants(config.blend.constant);
|
||||
@@ -4400,7 +4420,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
|
||||
// we're not drawing to the RT, so we can use it as a source
|
||||
if (config.require_one_barrier && !m_features.texture_barrier)
|
||||
PSSetShaderResource(2, draw_rt, true);
|
||||
PSSetShaderResource(TEXTURE_RT, draw_rt, true);
|
||||
}
|
||||
|
||||
draw_rt = colclip_rt;
|
||||
@@ -4409,7 +4429,7 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
// Clear texture binding when it's bound to RT or DS.
|
||||
if (!config.tex && ((draw_rt && static_cast<GSTexture12*>(draw_rt)->GetSRVDescriptor() == m_tfx_textures[0]) ||
|
||||
(draw_ds && static_cast<GSTexture12*>(draw_ds)->GetSRVDescriptor() == m_tfx_textures[0])))
|
||||
PSSetShaderResource(0, nullptr, false);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, nullptr, false);
|
||||
|
||||
if (InRenderPass() && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds))
|
||||
{
|
||||
@@ -4471,15 +4491,15 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
if (config.require_one_barrier)
|
||||
PSSetShaderResource(2, draw_rt_clone, true);
|
||||
PSSetShaderResource(TEXTURE_RT, draw_rt_clone, true);
|
||||
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
|
||||
PSSetShaderResource(0, draw_rt_clone, true);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, draw_rt_clone, true);
|
||||
}
|
||||
else
|
||||
Console.Warning("D3D12: Failed to allocate temp texture for RT copy.");
|
||||
}
|
||||
|
||||
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
PSSetROVs(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
|
||||
// For depth testing and sampling, use a read only dsv, otherwise use a write dsv
|
||||
OMSetRenderTargets(draw_rt, draw_ds_as_rt, draw_ds, config.scissor,
|
||||
@@ -4645,11 +4665,11 @@ void GSDevice12::SendHWDraw(const PipelineSelector& pipe, const GSHWDrawConfig&
|
||||
Console.Warning("D3D12: Possible unnecessary barrier detected.");
|
||||
#endif
|
||||
if ((one_barrier || full_barrier) && feedback_rt)
|
||||
PSSetShaderResource(2, draw_rt, false, ResourceType::FBL);
|
||||
PSSetShaderResource(TEXTURE_RT, draw_rt, false, ResourceType::FBL);
|
||||
if (config.tex_hazard == GSHWDrawConfig::TEX_HAZARD_RT)
|
||||
PSSetShaderResource(0, draw_rt, false, ResourceType::FBL);
|
||||
PSSetShaderResource(TEXTURE_TEXTURE, draw_rt, false, ResourceType::FBL);
|
||||
if ((one_barrier || full_barrier) && feedback_depth)
|
||||
PSSetShaderResource(4, draw_ds, false, ResourceType::FBL);
|
||||
PSSetShaderResource(TEXTURE_DEPTH, draw_ds, false, ResourceType::FBL);
|
||||
|
||||
if (full_barrier)
|
||||
{
|
||||
|
||||
@@ -152,6 +152,9 @@ public:
|
||||
/// Test for support for the specified texture format.
|
||||
bool SupportsTextureFormat(DXGI_FORMAT format);
|
||||
|
||||
/// Test for UAV support for the specified texture format.
|
||||
bool IsTextureFormatUAVCapable(DXGI_FORMAT format);
|
||||
|
||||
// Partial depth copies require ProgrammableSamplePositions tier 1.
|
||||
bool SupportsProgrammableSamplePositions();
|
||||
|
||||
@@ -552,7 +555,7 @@ public:
|
||||
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
void PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
|
||||
void OMSetRenderTargets(GSTexture* rt, GSTexture* ds, GSTexture* ds_as_rt, const GSVector4i& scissor,
|
||||
bool depth_read = false, const GSVector2i& viewport_size = {});
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
__fi ID3D12Resource* GetResource() const { return m_resource.get(); }
|
||||
__fi ID3D12Resource* GetFBLResource() const { return m_resource_fbl.get(); }
|
||||
|
||||
virtual bool IsUnorderedAccess() const override { return GetResourceState() == ResourceState::PixelShaderUAV; }
|
||||
bool IsShaderWriteMode() const override { return GetResourceState() == ResourceState::PixelShaderUAV; }
|
||||
|
||||
void* GetNativeHandle() const override;
|
||||
|
||||
|
||||
@@ -6241,7 +6241,7 @@ void GSRendererHW::EmulateDither()
|
||||
if (m_conf.ps.dither || m_conf.blend_multi_pass.dither)
|
||||
{
|
||||
const GIFRegDIMX& DIMX = m_draw_env->DIMX;
|
||||
GL_DBG("DITHERING mode %d (%d)", (GSConfig.Dithering == 3) ? "Force 32bit" : ((GSConfig.Dithering == 0) ? "Disabled" : "Enabled"), GSConfig.Dithering);
|
||||
GL_DBG("DITHERING mode %s (%d)", (GSConfig.Dithering == 3) ? "Force 32bit" : ((GSConfig.Dithering == 0) ? "Disabled" : "Enabled"), GSConfig.Dithering);
|
||||
|
||||
if (m_conf.ps.dither || GSConfig.Dithering == 3)
|
||||
m_conf.ps.dither = GSConfig.Dithering;
|
||||
@@ -7601,7 +7601,7 @@ void GSRendererHW::DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache:
|
||||
const bool multipass_depth = (full_barrier && m_conf.ps.IsFeedbackLoopDepth()) || two_pass_alpha;
|
||||
|
||||
// If already ROV, just continue the usage.
|
||||
const bool color_is_rov = rt && rt->m_texture->IsUnorderedAccess();
|
||||
const bool color_is_rov = rt && rt->m_texture->IsShaderWriteMode();
|
||||
const bool depth_is_rov = ds && ds->m_texture->IsDepthColor();
|
||||
|
||||
bool use_rov_color = (color_write && multipass_color) || color_is_rov;
|
||||
@@ -7688,7 +7688,7 @@ void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
|
||||
{
|
||||
m_conf.cb_ps.FbMask |= fbmask;
|
||||
}
|
||||
GL_INS("ROV: FbMask={ R=%x, G=%x, B=%x, A=%x }",
|
||||
GL_INS("ROV: FbMask={R=%x, G=%x, B=%x, A=%x}",
|
||||
m_conf.cb_ps.FbMask.r, m_conf.cb_ps.FbMask.g, m_conf.cb_ps.FbMask.b, m_conf.cb_ps.FbMask.a);
|
||||
}
|
||||
else
|
||||
@@ -7807,89 +7807,92 @@ void GSRendererHW::ConfigureROV(bool color_rov, bool depth_rov)
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::SetUnorderedAccessFlag(GSTextureCache::Target* rt)
|
||||
void GSRendererHW::ConvertTextureTypeROVSingle(GSTextureCache::Target* tgt, bool shader_write)
|
||||
{
|
||||
// Set flag for ROV activation heuristic. Only used by DX11.
|
||||
// Only needed for RT, as we use a different method for depth.
|
||||
if (rt)
|
||||
{
|
||||
if (m_conf.ps.HasColorROV())
|
||||
rt->m_texture->SetUnorderedAccess();
|
||||
else
|
||||
rt->m_texture->ClearUnorderedAccess();
|
||||
}
|
||||
}
|
||||
const bool depth = (tgt->m_type == GSTextureCache::DepthStencil);
|
||||
|
||||
static void CopyDepthTextureROV(GSTexture* src, GSTexture* dst)
|
||||
{
|
||||
switch (src->GetState())
|
||||
GSTexture* old_tex = depth ? m_conf.ds : m_conf.rt;
|
||||
|
||||
GSTexture::Usage usage = shader_write ? GSTexture::ShaderWriteTarget : GSTexture::FeedbackTarget;
|
||||
GSTexture* new_tex = depth ?
|
||||
(shader_write ?
|
||||
g_gs_device->FetchSurface(usage, old_tex->GetSize(), 1, GSTexture::Format::DepthColor, false, false) :
|
||||
g_gs_device->CreateDepthStencil(old_tex->GetSize(), false, true)) :
|
||||
g_gs_device->FetchSurface(usage, old_tex->GetSize(), 1, GSTexture::Format::Color, false, false);
|
||||
|
||||
switch (old_tex->GetState())
|
||||
{
|
||||
case GSTexture::State::Cleared:
|
||||
g_gs_device->ClearDepth(dst, src->GetClearDepth());
|
||||
if (depth)
|
||||
g_gs_device->ClearDepth(new_tex, old_tex->GetClearDepth());
|
||||
else
|
||||
g_gs_device->ClearRenderTarget(new_tex, old_tex->GetClearColor());
|
||||
break;
|
||||
case GSTexture::State::Invalidated:
|
||||
g_gs_device->InvalidateRenderTarget(dst);
|
||||
g_gs_device->InvalidateRenderTarget(new_tex);
|
||||
break;
|
||||
case GSTexture::State::Dirty:
|
||||
g_gs_device->StretchRectAuto(src, dst, Nearest);
|
||||
g_gs_device->StretchRectAuto(old_tex, new_tex, Nearest);
|
||||
|
||||
// Count stats as part of both standard and ROV.
|
||||
g_perfmon.Put(GSPerfMon::TextureCopiesROV, 1.0);
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1.0);
|
||||
break;
|
||||
default:
|
||||
pxAssert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
// These stats are counted both as part of ROV and non-ROV stats.
|
||||
g_perfmon.Put(GSPerfMon::DepthCopiesROV, 1.0);
|
||||
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1.0);
|
||||
};
|
||||
|
||||
void GSRendererHW::ConvertDepthFormatROV(GSTextureCache::Target* ds)
|
||||
{
|
||||
if (!ds)
|
||||
return;
|
||||
|
||||
GSTexture* ds_tex_old = m_conf.ds;
|
||||
GSTexture* ds_tex_new = nullptr;
|
||||
|
||||
// Convert depth to depth color or vice versa if needed.
|
||||
bool depth_to_color;
|
||||
if (m_conf.ps.HasDepthROV() && !ds_tex_old->IsDepthColor())
|
||||
{
|
||||
depth_to_color = true;
|
||||
ds_tex_new = g_gs_device->CreateShaderWriteTarget(ds_tex_old->GetSize(), GSTexture::Format::DepthColor, false, true);
|
||||
}
|
||||
else if (!m_conf.ps.HasDepthROV() && ds_tex_old->IsDepthColor())
|
||||
{
|
||||
depth_to_color = false;
|
||||
ds_tex_new = g_gs_device->CreateDepthStencil(ds->m_texture->GetSize(), false, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GL_PUSH("HW: Convert %s for ROV.", depth_to_color ? "DepthStencil -> DepthColor" : "DepthColor -> DepthStencil");
|
||||
|
||||
CopyDepthTextureROV(ds_tex_old, ds_tex_new);
|
||||
|
||||
#if PCSX2_DEVBUILD
|
||||
ds_tex_new->SetDebugName(ds->m_texture->GetDebugName());
|
||||
new_tex->SetDebugName(tgt->m_texture->GetDebugName());
|
||||
#endif
|
||||
|
||||
// Fix up the texture cache.
|
||||
if (ds->m_texture == ds_tex_old)
|
||||
if (tgt->m_texture == old_tex)
|
||||
{
|
||||
GL_CACHE("HW: Replaced texture for DS @ 0x%04x", ds->m_TEX0.TBP0);
|
||||
ds->m_texture = ds_tex_new;
|
||||
GL_CACHE("HW: Replaced texture for %s @ 0x%04x", depth ? "DS" : "RT", tgt->m_TEX0.TBP0);
|
||||
tgt->m_texture = new_tex;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Must be the temporary Z.
|
||||
pxAssert(g_texture_cache->GetTemporaryZ() == ds_tex_old);
|
||||
pxAssert(depth && g_texture_cache->GetTemporaryZ() == old_tex);
|
||||
GL_CACHE("HW: Replaced texture for temporary Z @ 0x%04x", g_texture_cache->GetTemporaryZInfo().ZBP);
|
||||
g_texture_cache->SetTemporaryZ(ds_tex_new);
|
||||
g_texture_cache->SetTemporaryZ(new_tex);
|
||||
}
|
||||
|
||||
g_gs_device->Recycle(ds_tex_old);
|
||||
// Fixup the backend config.
|
||||
if (depth)
|
||||
m_conf.ds = new_tex;
|
||||
else
|
||||
m_conf.rt = new_tex;
|
||||
|
||||
m_conf.ds = ds_tex_new;
|
||||
g_gs_device->Recycle(old_tex);
|
||||
}
|
||||
|
||||
void GSRendererHW::ConvertTextureTypeROV(GSTextureCache::Target* rt, GSTextureCache::Target* ds)
|
||||
{
|
||||
// Convert depth to the proper type/format.
|
||||
if (ds)
|
||||
{
|
||||
if (m_conf.ps.HasDepthROV() && !ds->m_texture->IsShaderWrite())
|
||||
{
|
||||
GL_PUSH("HW: Convert DepthStencil -> DepthColor for ROV.");
|
||||
ConvertTextureTypeROVSingle(ds, true);
|
||||
}
|
||||
else if (!m_conf.ps.HasDepthROV() && !ds->m_texture->IsDepthStencil())
|
||||
{
|
||||
GL_PUSH("HW: Convert DepthColor -> DepthStencil for non-ROV.");
|
||||
ConvertTextureTypeROVSingle(ds, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert color to the proper type. This only adds the shader read/write flag and doesn't remove it,
|
||||
// since adding the read/write flag doesn't lose any pipeline capabilities.
|
||||
if (rt && m_conf.ps.HasColorROV() && !rt->m_texture->IsShaderWrite())
|
||||
{
|
||||
GL_PUSH("HW: Convert RenderTarget -> RenderTarget (shader write) for ROV.");
|
||||
ConvertTextureTypeROVSingle(rt, true);
|
||||
}
|
||||
}
|
||||
|
||||
__ri static constexpr bool IsRedundantClamp(u8 clamp, u32 clamp_min, u32 clamp_max, u32 tsize)
|
||||
@@ -9455,8 +9458,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
|
||||
// Call before computing the full drawlist in case ROV is used and we don't need it.
|
||||
DetermineROVUsage(rt, ds);
|
||||
ConvertDepthFormatROV(ds);
|
||||
SetUnorderedAccessFlag(rt);
|
||||
ConvertTextureTypeROV(rt, ds);
|
||||
|
||||
// Barriers must be determined before indices are modified via HandleProvokingVertexFirst/SetupIA.
|
||||
// This also computes the drawlist if needed.
|
||||
|
||||
@@ -256,8 +256,8 @@ private:
|
||||
void GetForcedROVUsage(bool& color_cov, bool& depth_rov); // Whether having color or depth with the current config forces the other.
|
||||
void DetermineROVUsage(GSTextureCache::Target* rt, GSTextureCache::Target* ds); // Heuristics to determine whether to enable/disable ROV
|
||||
void ConfigureROV(bool color_rov, bool depth_rov); // Actual config for ROV
|
||||
void SetUnorderedAccessFlag(GSTextureCache::Target* rt); // Set flag for DX11 ROV heuristic to work.
|
||||
void ConvertDepthFormatROV(GSTextureCache::Target* ds); // Convert between depth and depth color if needed.
|
||||
void ConvertTextureTypeROV(GSTextureCache::Target* rt, GSTextureCache::Target* ds); // Convert to RW capable textures if needed.
|
||||
void ConvertTextureTypeROVSingle(GSTextureCache::Target* tgt, bool shader_write); // Helper to do the above.
|
||||
|
||||
void SetTCOffset();
|
||||
bool NextDrawColClip() const;
|
||||
|
||||
@@ -2007,7 +2007,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
|
||||
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
|
||||
[color1 setPixelFormat:MTLPixelFormatR32Float];
|
||||
}
|
||||
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%llx", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
|
||||
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%llx.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
|
||||
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
|
||||
|
||||
[m_current_render.encoder setRenderPipelineState:pipeline];
|
||||
|
||||
@@ -908,11 +908,6 @@ bool GSDeviceOGL::CheckFeatures()
|
||||
}
|
||||
|
||||
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand && m_features.feedback_loops();
|
||||
|
||||
if (GSConfig.HWROV)
|
||||
{
|
||||
Console.Warning("GL: ROV is not implemented for GL and will be disabled.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2735,14 +2735,18 @@ bool GSDeviceVK::CheckFeatures()
|
||||
m_features.point_expand ? "hardware" : "vertex expanding",
|
||||
m_features.line_expand ? "hardware" : "vertex expanding");
|
||||
|
||||
bool has_rov_storage_flags = true;
|
||||
|
||||
// Check texture format support before we try to create them.
|
||||
for (u32 fmt = static_cast<u32>(GSTexture::Format::Color); fmt < static_cast<u32>(GSTexture::Format::PrimID); fmt++)
|
||||
{
|
||||
const VkFormat vkfmt = LookupNativeFormat(static_cast<GSTexture::Format>(fmt));
|
||||
const VkFormatFeatureFlags bits =
|
||||
(static_cast<GSTexture::Format>(fmt) == GSTexture::Format::DepthStencil) ?
|
||||
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) :
|
||||
(VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT);
|
||||
VkFormatFeatureFlags bits = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
||||
|
||||
if (static_cast<GSTexture::Format>(fmt) == GSTexture::Format::DepthStencil)
|
||||
bits |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
|
||||
else
|
||||
bits |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
|
||||
|
||||
VkFormatProperties props = {};
|
||||
vkGetPhysicalDeviceFormatProperties(m_physical_device, vkfmt, &props);
|
||||
@@ -2753,6 +2757,9 @@ bool GSDeviceVK::CheckFeatures()
|
||||
fmt, static_cast<unsigned>(vkfmt), props.optimalTilingFeatures, bits);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GSTexture::IsShaderWriteFormat(static_cast<GSTexture::Format>(fmt)))
|
||||
has_rov_storage_flags &= ((props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0);
|
||||
}
|
||||
|
||||
m_features.dxt_textures = m_device_features.textureCompressionBC;
|
||||
@@ -2769,7 +2776,9 @@ bool GSDeviceVK::CheckFeatures()
|
||||
m_max_texture_size = m_device_properties.limits.maxImageDimension2D;
|
||||
|
||||
m_features.rov = m_optional_extensions.vk_ext_fragment_shader_interlock &&
|
||||
m_device_features.fragmentStoresAndAtomics;
|
||||
m_device_features.fragmentStoresAndAtomics &&
|
||||
has_rov_storage_flags &&
|
||||
!m_features.framebuffer_fetch;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3822,7 +3831,8 @@ VkShaderModule GSDeviceVK::GetUtilityFragmentShader(const std::string& source, c
|
||||
|
||||
bool GSDeviceVK::CreateNullTexture()
|
||||
{
|
||||
m_null_texture = GSTextureVK::Create(GSTexture::ShaderWriteTarget, GSTexture::Format::Color, 1, 1, 1);
|
||||
GSTexture::Usage null_usage = m_features.rov ? GSTexture::ShaderWriteTarget : GSTexture::FeedbackTarget;
|
||||
m_null_texture = GSTextureVK::Create(null_usage, GSTexture::Format::Color, 1, 1, 1);
|
||||
if (!m_null_texture)
|
||||
return false;
|
||||
|
||||
@@ -5266,7 +5276,7 @@ void GSDeviceVK::SetLineWidth(float width)
|
||||
m_dirty_flags |= DIRTY_FLAG_LINE_WIDTH;
|
||||
}
|
||||
|
||||
void GSDeviceVK::PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
void GSDeviceVK::PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds)
|
||||
{
|
||||
GSTextureVK* vkRt = static_cast<GSTextureVK*>(rt);
|
||||
GSTextureVK* vkDs = static_cast<GSTextureVK*>(ds);
|
||||
@@ -6183,7 +6193,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
|
||||
PSSetShaderResource(TFX_TEXTURE_DEPTH, nullptr, false);
|
||||
}
|
||||
|
||||
PSSetUnorderedAccess(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
PSSetROVs(draw_rt_rov, draw_ds_rov, config.ps.HasColorOutput(), config.ps.HasDepthROVWrite());
|
||||
|
||||
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags), rtsize);
|
||||
|
||||
|
||||
@@ -607,7 +607,7 @@ public:
|
||||
void IASetIndexBuffer(const void* index, size_t count);
|
||||
void VSSetIndexBuffer(const void* index, size_t count);
|
||||
|
||||
void PSSetUnorderedAccess(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
void PSSetROVs(GSTexture* rt, GSTexture* ds, bool write_rt, bool write_ds);
|
||||
void PSSetShaderResource(int i, GSTexture* sr, bool check_state, ResourceType type = ResourceType::SRV);
|
||||
void PSSetSampler(GSHWDrawConfig::SamplerSelector sel);
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
__fi VkImage GetImage() const { return m_image; }
|
||||
__fi VkImageView GetView() const { return m_view; }
|
||||
__fi Layout GetLayout() const { return m_layout; }
|
||||
virtual bool IsUnorderedAccess() const override { return GetLayout() == Layout::ReadWriteImage; }
|
||||
bool IsShaderWriteMode() const override { return GetLayout() == Layout::ReadWriteImage; }
|
||||
|
||||
__fi VkFormat GetVkFormat() const { return m_vk_format; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user