GS/HW: Reduce number of copies for HDR

This commit is contained in:
refractionpcsx2 2025-01-30 04:46:54 +00:00 committed by Ty
parent cd98f72f10
commit 2d6a42ac06
12 changed files with 633 additions and 265 deletions

View File

@ -799,7 +799,8 @@ void ps_fbmask(inout float4 C, float2 pos_xy)
{
if (PS_FBMASK)
{
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * 255.0f + 0.1f);
float multi = PS_HDR ? 65535.0f : 255.0f;
float4 RT = trunc(RtTexture.Load(int3(pos_xy, 0)) * multi + 0.1f);
C = (float4)(((uint4)C & ~FbMask) | ((uint4)RT & FbMask));
}
}
@ -895,7 +896,8 @@ void ps_blend(inout float4 Color, inout float4 As_rgba, float2 pos_xy)
}
float Ad = PS_RTA_CORRECTION ? trunc(RT.a * 128.0f + 0.1f) / 128.0f : trunc(RT.a * 255.0f + 0.1f) / 128.0f;
float3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
float color_multi = PS_HDR ? 65535.0f : 255.0f;
float3 Cd = trunc(RT.rgb * color_multi + 0.1f);
float3 Cs = Color.rgb;
float3 A = (PS_BLEND_A == 0) ? Cs : ((PS_BLEND_A == 1) ? Cd : (float3)0.0f);

View File

@ -708,7 +708,11 @@ void ps_fbmask(inout vec4 C)
{
// FIXME do I need special case for 16 bits
#if PS_FBMASK
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
#if PS_HDR == 1
vec4 RT = trunc(fetch_rt() * 65535.0f);
#else
vec4 RT = trunc(fetch_rt() * 255.0f + 0.1f);
#endif
C = vec4((uvec4(C) & ~FbMask) | (uvec4(RT) & FbMask));
#endif
}
@ -823,7 +827,11 @@ float As = As_rgba.a;
#endif
// Let the compiler do its jobs !
vec3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
#if PS_HDR == 1
vec3 Cd = trunc(RT.rgb * 65535.0f);
#else
vec3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
#endif
vec3 Cs = Color.rgb;
#if PS_BLEND_A == 0

View File

@ -972,7 +972,12 @@ vec4 ps_color()
void ps_fbmask(inout vec4 C)
{
#if PS_FBMASK
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
#if PS_HDR == 1
vec4 RT = trunc(sample_from_rt() * 65535.0f);
#else
vec4 RT = trunc(sample_from_rt() * 255.0f + 0.1f);
#endif
C = vec4((uvec4(C) & ~FbMask) | (uvec4(RT) & FbMask));
#endif
}
@ -1090,7 +1095,11 @@ void ps_blend(inout vec4 Color, inout vec4 As_rgba)
#endif
// Let the compiler do its jobs !
#if PS_HDR == 1
vec3 Cd = trunc(RT.rgb * 65535.0f);
#else
vec3 Cd = trunc(RT.rgb * 255.0f + 0.1f);
#endif
vec3 Cs = Color.rgb;
#if PS_BLEND_A == 0

View File

@ -8,6 +8,7 @@
#include "GS/GS.h"
#include "GS/Renderers/Common/GSFastList.h"
#include "GS/Renderers/Common/GSTexture.h"
#include "GS/Renderers/Vulkan/GSTextureVK.h"
#include "GS/Renderers/Common/GSVertex.h"
#include "GS/GSAlignedClass.h"
#include "GS/GSExtra.h"
@ -674,6 +675,15 @@ struct alignas(16) GSHWDrawConfig
Full, ///< Full emulation (using barriers / ROV)
};
enum class HDRMode : u8
{
NoModify = 0,
ConvertOnly = 1,
ResolveOnly = 2,
ConvertAndResolve = 3,
EarlyResolve = 4
};
GSTexture* rt; ///< Render target
GSTexture* ds; ///< Depth stencil
GSTexture* tex; ///< Source texture
@ -730,6 +740,11 @@ struct alignas(16) GSHWDrawConfig
VSConstantBuffer cb_vs;
PSConstantBuffer cb_ps;
// These are here as they need to be preserved between draws, and the state clear only does up to the constant buffers.
HDRMode hdr_mode;
GIFRegFRAME hdr_frame;
GSVector4i hdr_update_area; ///< Area in the framebuffer which HDR will modify;
};
class GSDevice : public GSAlignedClass<32>
@ -850,6 +865,7 @@ protected:
GSTexture* m_target_tmp = nullptr;
GSTexture* m_current = nullptr;
GSTexture* m_cas = nullptr;
GSTexture* m_hdr_rt = nullptr; ///< Temp HDR texture
bool AcquireWindow(bool recreate_window);
@ -874,6 +890,10 @@ public:
/// Returns a string containing current adapter in use.
const std::string& GetName() const { return m_name; }
GSTexture* GetHDRTexture() const { return m_hdr_rt; }
void SetHDRTexture(GSTexture* tex) { m_hdr_rt = tex; }
/// Returns a string representing the specified API.
static const char* RenderAPIToString(RenderAPI api);

View File

@ -2521,6 +2521,47 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
if (hdr_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
hdr_rt = nullptr;
}
else
config.ps.hdr = 1;
}
if (config.ps.hdr)
{
if (!hdr_rt)
{
config.hdr_update_area = config.drawarea;
const GSVector4 dRect = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor);
if (!hdr_rt)
return;
g_gs_device->SetHDRTexture(hdr_rt);
// Warning: StretchRect must be called before BeginScene otherwise
// vertices will be overwritten. Trust me you don't want to do that.
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
}
}
GSTexture* primid_tex = nullptr;
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
@ -2528,7 +2569,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (!primid_tex)
return;
StretchRect(config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
StretchRect(hdr_rt ? hdr_rt : config.rt, GSVector4(config.drawarea) / GSVector4(rtsize).xyxy(),
primid_tex, GSVector4(config.drawarea), m_date.primid_init_ps[static_cast<u8>(config.datm)].get(), nullptr, false);
}
else if (config.destination_alpha != GSHWDrawConfig::DestinationAlphaMode::Off)
@ -2544,22 +2585,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
{GSVector4(dst.z, -dst.w, 0.5f, 1.0f), GSVector2(src.z, src.w)},
};
SetupDATE(config.rt, config.ds, vertices, config.datm);
}
GSTexture* hdr_rt = nullptr;
if (config.ps.hdr)
{
const GSVector4 dRect(config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor);
if (!hdr_rt)
return;
// Warning: StretchRect must be called before BeginScene otherwise
// vertices will be overwritten. Trust me you don't want to do that.
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
SetupDATE(hdr_rt ? hdr_rt : config.rt, config.ds, vertices, config.datm);
}
if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
@ -2623,7 +2649,7 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
// Do not always bind the rt when it's not needed,
// only bind it when effects use it such as fbmask emulation currently
// because we copy the frame buffer and it is quite slow.
CloneTexture(config.rt, &rt_copy, config.drawarea);
CloneTexture(hdr_rt ? hdr_rt : config.rt, &rt_copy, config.drawarea);
if (rt_copy)
{
if (config.require_one_barrier)
@ -2690,11 +2716,18 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
if (hdr_rt)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.drawarea);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
if (config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
}
}

View File

@ -3816,18 +3816,64 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// Destination Alpha Setup
const bool stencil_DATE = (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::Stencil ||
config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::StencilOne);
GSTexture12* hdr_rt = static_cast<GSTexture12*>(g_gs_device->GetHDRTexture());
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_clone = nullptr;
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
PipelineSelector& pipe = m_pipeline_selector;
// figure out the pipeline
UpdateHWPipelineSelector(config);
// now blit the hdr texture back to the original target
if (hdr_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
{
GL_PUSH("Blit HDR back to RT");
EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(draw_ds),
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetUNormClearColor(), 0.0f, 0);
const GSVector4 sRect(GSVector4(config.hdr_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds].get());
SetUtilityTexture(hdr_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.hdr_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
else
{
draw_rt = hdr_rt;
pipe.ps.hdr = 1;
}
}
if (stencil_DATE)
SetupDATE(config.rt, config.ds, config.datm, config.drawarea);
SetupDATE(draw_rt, config.ds, config.datm, config.drawarea);
// stream buffer in first, in case we need to exec
SetVSConstantBuffer(config.cb_vs);
SetPSConstantBuffer(config.cb_ps);
// figure out the pipeline
UpdateHWPipelineSelector(config);
// bind textures before checking the render pass, in case we need to transition them
PipelineSelector& pipe = m_pipeline_selector;
if (config.tex)
{
PSSetShaderResource(0, config.tex, config.tex != config.rt);
@ -3843,7 +3889,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
GSTexture12* date_image = nullptr;
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
GSTexture* backup_rt = config.rt;
config.rt = draw_rt;
date_image = SetupPrimitiveTrackingDATE(config, pipe);
config.rt = backup_rt;
if (!date_image)
{
Console.WriteLn("D3D12: Failed to allocate DATE image, aborting draw.");
@ -3851,57 +3900,10 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
// Align the render area to 128x128, hopefully avoiding render pass restarts for small render area changes (e.g. Ratchet and Clank).
const int render_area_alignment = 128 * GSConfig.UpscaleMultiplier;
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
const GSVector4i render_area(
config.ps.hdr ? config.drawarea :
GSVector4i(Common::AlignDownPow2(config.scissor.left, render_area_alignment),
Common::AlignDownPow2(config.scissor.top, render_area_alignment),
std::min(Common::AlignUpPow2(config.scissor.right, render_area_alignment), rtsize.x),
std::min(Common::AlignUpPow2(config.scissor.bottom, render_area_alignment), rtsize.y)));
GSTexture12* draw_rt = static_cast<GSTexture12*>(config.rt);
GSTexture12* draw_ds = static_cast<GSTexture12*>(config.ds);
GSTexture12* draw_rt_clone = nullptr;
GSTexture12* hdr_rt = nullptr;
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
{
EndRenderPass();
hdr_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
{
Console.WriteLn("D3D12: Failed to allocate HDR render target, aborting draw.");
if (date_image)
Recycle(date_image);
return;
}
// propagate clear value through if the hdr render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
draw_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
// we're not drawing to the RT, so we can use it as a source
if (config.require_one_barrier)
PSSetShaderResource(2, draw_rt, true);
draw_rt = hdr_rt;
}
else if (config.require_one_barrier)
if (config.require_one_barrier)
{
// requires a copy of the RT
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, true));
draw_rt_clone = static_cast<GSTexture12*>(CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true));
if (draw_rt_clone)
{
EndRenderPass();
@ -3915,8 +3917,50 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
}
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
{
if (!hdr_rt)
{
config.hdr_update_area = config.drawarea;
EndRenderPass();
hdr_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
{
Console.WriteLn("D3D12: Failed to allocate HDR render target, aborting draw.");
if (date_image)
Recycle(date_image);
return;
}
g_gs_device->SetHDRTexture(static_cast<GSTexture*>(hdr_rt));
// propagate clear value through if the hdr render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
draw_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}
// we're not drawing to the RT, so we can use it as a source
if (config.require_one_barrier)
PSSetShaderResource(2, draw_rt, true);
}
draw_rt = hdr_rt;
}
// clear texture binding when it's bound to RT or DS
if (((config.rt && static_cast<GSTexture12*>(config.rt)->GetSRVDescriptor() == m_tfx_textures[0]) ||
if (((draw_rt && static_cast<GSTexture12*>(draw_rt)->GetSRVDescriptor() == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTexture12*>(config.ds)->GetSRVDescriptor() == m_tfx_textures[0])))
{
PSSetShaderResource(0, nullptr, false);
@ -3964,13 +4008,14 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
}
// rt -> hdr blit if enabled
if (hdr_rt && config.rt->GetState() == GSTexture::State::Dirty)
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
{
SetUtilityTexture(static_cast<GSTexture12*>(config.rt), m_point_sampler_cpu);
SetPipeline(m_hdr_setup_pipelines[pipe.ds].get());
const GSVector4 sRect(GSVector4(render_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
DrawStretchRect(sRect, GSVector4(render_area), rtsize);
const GSVector4 drawareaf = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize.x, rtsize.y).xyxy());
DrawStretchRect(sRect, GSVector4(drawareaf), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_POP();
@ -4025,28 +4070,34 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
// now blit the hdr texture back to the original target
if (hdr_rt)
{
GL_PUSH("Blit HDR back to RT");
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
{
GL_PUSH("Blit HDR back to RT");
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(draw_ds),
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetUNormClearColor(), 0.0f, 0);
draw_rt = static_cast<GSTexture12*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor);
const GSVector4 sRect(GSVector4(render_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds].get());
SetUtilityTexture(hdr_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(render_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
BeginRenderPass(GetLoadOpForTexture(draw_rt), D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE,
GetLoadOpForTexture(draw_ds),
draw_ds ? D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE : D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS, D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS,
draw_rt->GetUNormClearColor(), 0.0f, 0);
Recycle(hdr_rt);
const GSVector4 sRect(GSVector4(config.hdr_update_area) / GSVector4(rtsize.x, rtsize.y).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds].get());
SetUtilityTexture(hdr_rt, m_point_sampler_cpu);
DrawStretchRect(sRect, GSVector4(config.hdr_update_area), rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
}
}

View File

@ -970,6 +970,21 @@ GSVector2i GSRendererHW::GetTargetSize(const GSTextureCache::Source* tex, const
return g_texture_cache->GetTargetSize(m_cached_ctx.FRAME.Block(), m_cached_ctx.FRAME.FBW, m_cached_ctx.FRAME.PSM, valid_size.x, valid_size.y, can_expand);
}
bool GSRendererHW::NextDrawHDR() const
{
const int get_next_ctx = (m_state_flush_reason == CONTEXTCHANGE) ? m_env.PRIM.CTXT : m_backed_up_ctx;
const GSDrawingContext& next_ctx = m_env.CTXT[get_next_ctx];
// If it wasn't a context change we can't guarantee the next draw is going to be set up
if (m_state_flush_reason != GSFlushReason::CONTEXTCHANGE || m_env.COLCLAMP.CLAMP != 0 || m_env.PRIM.ABE == 0 ||
(m_context->FRAME.U64 ^ next_ctx.FRAME.U64) != 0 || (m_env.PRIM.TME && next_ctx.TEX0.TBP0 == m_context->FRAME.Block()))
{
return false;
}
return true;
}
bool GSRendererHW::IsPossibleChannelShuffle() const
{
if (!PRIM->TME || m_cached_ctx.TEX0.PSM != PSMT8 || // 8-bit texture draw
@ -2150,6 +2165,35 @@ void GSRendererHW::Draw()
return;
}
// I hate that I have to do this, but some games (like Pac-Man World Rally) troll us by causing a flush with degenerate triangles, so we don't have all available information about the next draw.
// So we have to check when the next draw happens if our frame has changed or if it's become recursive.
const bool has_HDR_texture = g_gs_device->GetHDRTexture() != nullptr;
if (!no_rt && has_HDR_texture && (m_conf.hdr_frame.FBP != m_cached_ctx.FRAME.FBP || m_conf.hdr_frame.Block() == m_cached_ctx.TEX0.TBP0))
{
GIFRegTEX0 FRAME;
FRAME.TBP0 = m_conf.hdr_frame.Block();
FRAME.TBW = m_conf.hdr_frame.FBW;
FRAME.PSM = m_conf.hdr_frame.PSM;
GSTextureCache::Target* old_rt = g_texture_cache->LookupTarget(FRAME, GSVector2i(1, 1), GetTextureScaleFactor(), GSTextureCache::RenderTarget, true,
fm, false, false, true, true, GSVector4i(0, 0, 1, 1), true, false, false);
if (old_rt)
{
GL_CACHE("Pre-draw resolve of HDR! Address: %x", FRAME.TBP0);
GSTexture* hdr_texture = g_gs_device->GetHDRTexture();
g_gs_device->StretchRect(hdr_texture, GSVector4(m_conf.hdr_update_area) / GSVector4(GSVector4i(hdr_texture->GetSize()).xyxy()), old_rt->m_texture, GSVector4(m_conf.hdr_update_area),
ShaderConvert::HDR_RESOLVE, false);
g_gs_device->Recycle(hdr_texture);
g_gs_device->SetHDRTexture(nullptr);
}
else
DevCon.Warning("Error resolving HDR texture for pre-draw resolve");
}
const bool draw_sprite_tex = PRIM->TME && (m_vt.m_primclass == GS_SPRITE_CLASS);
// We trigger the sw prim render here super early, to avoid creating superfluous render targets.
@ -3413,10 +3457,19 @@ void GSRendererHW::Draw()
GSTextureCache::RenderTarget, m_cached_ctx.ZBUF.Block(), m_cached_ctx.ZBUF.PSM, zm);
}
//
if (GSConfig.DumpGSData)
{
const bool writeback_HDR_texture = g_gs_device->GetHDRTexture() != nullptr;
if (writeback_HDR_texture)
{
GSTexture* hdr_texture = g_gs_device->GetHDRTexture();
g_gs_device->StretchRect(hdr_texture, GSVector4(m_conf.hdr_update_area) / GSVector4(GSVector4i(hdr_texture->GetSize()).xyxy()), rt->m_texture, GSVector4(m_conf.hdr_update_area),
ShaderConvert::HDR_RESOLVE, false);
}
const u64 frame = g_perfmon.GetFrame();
std::string s;
@ -4404,14 +4457,36 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// Color clip
if (COLCLAMP.CLAMP == 0)
{
const bool free_colclip = features.framebuffer_fetch || no_prim_overlap || blend_non_recursive;
bool has_HDR_texture = g_gs_device->GetHDRTexture() != nullptr;
// Don't know any game that resizes the RT mid HDR, but gotta be careful.
if (has_HDR_texture)
{
GSTexture* hdr_texture = g_gs_device->GetHDRTexture();
if (hdr_texture->GetSize() != rt->m_texture->GetSize())
{
GL_CACHE("Pre-Blend resolve of HDR due to size change! Address: %x", rt->m_TEX0.TBP0);
g_gs_device->StretchRect(hdr_texture, GSVector4(m_conf.hdr_update_area) / GSVector4(GSVector4i(hdr_texture->GetSize()).xyxy()), rt->m_texture, GSVector4(m_conf.hdr_update_area),
ShaderConvert::HDR_RESOLVE, false);
g_gs_device->Recycle(hdr_texture);
g_gs_device->SetHDRTexture(nullptr);
has_HDR_texture = false;
}
}
const bool free_colclip = !has_HDR_texture && (features.framebuffer_fetch || no_prim_overlap || blend_non_recursive);
GL_DBG("COLCLIP Info (Blending: %u/%u/%u/%u, OVERLAP: %d)", m_conf.ps.blend_a, m_conf.ps.blend_b, m_conf.ps.blend_c, m_conf.ps.blend_d, m_prim_overlap);
if (color_dest_blend || color_dest_blend2 || blend_zero_to_one_range)
{
// No overflow, disable colclip.
GL_INS("COLCLIP mode DISABLED");
sw_blending = false;
m_conf.hdr_mode = (has_HDR_texture && !NextDrawHDR()) ? GSHWDrawConfig::HDRMode::ResolveOnly : GSHWDrawConfig::HDRMode::NoModify;
}
else if (free_colclip)
{
@ -4422,6 +4497,7 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// Disable the HDR algo
accumulation_blend = false;
blend_mix = false;
m_conf.hdr_mode = (has_HDR_texture && !NextDrawHDR()) ? GSHWDrawConfig::HDRMode::ResolveOnly : GSHWDrawConfig::HDRMode::NoModify;
}
else if (accumulation_blend)
{
@ -4429,18 +4505,24 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
GL_INS("COLCLIP Fast HDR mode ENABLED");
m_conf.ps.hdr = 1;
sw_blending = true; // Enable sw blending for the HDR algo
m_conf.hdr_mode = has_HDR_texture ? (NextDrawHDR() ? GSHWDrawConfig::HDRMode::NoModify : GSHWDrawConfig::HDRMode::ResolveOnly) : (NextDrawHDR() ? GSHWDrawConfig::HDRMode::ConvertOnly : GSHWDrawConfig::HDRMode::ConvertAndResolve);
}
else if (sw_blending)
{
// A slow algo that could requires several passes (barely used)
GL_INS("COLCLIP SW mode ENABLED");
m_conf.ps.colclip = 1;
m_conf.hdr_mode = (has_HDR_texture && !NextDrawHDR()) ? GSHWDrawConfig::HDRMode::ResolveOnly : GSHWDrawConfig::HDRMode::NoModify;
}
else
{
GL_INS("COLCLIP HDR mode ENABLED");
m_conf.ps.hdr = 1;
m_conf.hdr_mode = has_HDR_texture ? (NextDrawHDR() ? GSHWDrawConfig::HDRMode::NoModify : GSHWDrawConfig::HDRMode::ResolveOnly) : (NextDrawHDR() ? GSHWDrawConfig::HDRMode::ConvertOnly : GSHWDrawConfig::HDRMode::ConvertAndResolve);
}
m_conf.hdr_frame = m_cached_ctx.FRAME;
}
// Per pixel alpha blending
@ -4474,8 +4556,10 @@ void GSRendererHW::EmulateBlending(int rt_alpha_min, int rt_alpha_max, const boo
// HDR mode should be disabled when doing sw blend, swap with sw colclip.
if (m_conf.ps.hdr)
{
bool has_HDR_texture = g_gs_device->GetHDRTexture() != nullptr;
m_conf.ps.hdr = 0;
m_conf.ps.colclip = 1;
m_conf.hdr_mode = has_HDR_texture ? GSHWDrawConfig::HDRMode::EarlyResolve : GSHWDrawConfig::HDRMode::NoModify;
}
}
else

View File

@ -111,7 +111,7 @@ private:
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
void SetTCOffset();
bool NextDrawHDR() const;
bool IsPossibleChannelShuffle() const;
bool NextDrawMatchesShuffle() const;
bool IsSplitTextureShuffle(GSTextureCache::Target* rt);

View File

@ -2135,6 +2135,64 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
GSTexture* stencil = nullptr;
GSTexture* primid_tex = nullptr;
GSTexture* rt = config.rt;
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
if (hdr_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
{
BeginRenderPass(@"HDR Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(hdr_rt, m_hdr_resolve_pipeline, config.hdr_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
hdr_rt = nullptr;
}
else
config.ps.hdr = 1;
}
if (config.ps.hdr)
{
if (!hdr_rt)
{
config.hdr_update_area = config.drawarea;
GSVector2i size = config.rt->GetSize();
rt = hdr_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::HDRColor, false);
g_gs_device->SetHDRTexture(hdr_rt);
const GSVector4i copy_rect = (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(size) : config.drawarea;
switch (config.rt->GetState())
{
case GSTexture::State::Dirty:
BeginRenderPass(@"HDR Init", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
RenderCopy(config.rt, m_hdr_init_pipeline, copy_rect);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
break;
case GSTexture::State::Cleared:
{
BeginRenderPass(@"HDR Clear", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
GSVector4 color = GSVector4::rgba32(config.rt->GetClearColor()) / GSVector4::cxpr(65535, 65535, 65535, 255);
[m_current_render.encoder setFragmentBytes:&color length:sizeof(color) atIndex:GSMTLBufferIndexUniforms];
RenderCopy(nullptr, m_hdr_clear_pipeline, copy_rect);
break;
}
case GSTexture::State::Invalidated:
break;
}
}
rt = hdr_rt;
}
switch (config.destination_alpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Off:
@ -2142,18 +2200,18 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
break; // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking:
{
FlushClears(config.rt);
GSVector2i size = config.rt->GetSize();
FlushClears(rt);
GSVector2i size = rt->GetSize();
primid_tex = CreateRenderTarget(size.x, size.y, GSTexture::Format::PrimID);
DepthStencilSelector dsel = config.depth;
dsel.zwe = 0;
GSTexture* depth = dsel.key == DepthStencilSelector::NoDepth().key ? nullptr : config.ds;
BeginRenderPass(@"PrimID Destination Alpha Init", primid_tex, MTLLoadActionDontCare, depth, MTLLoadActionLoad);
RenderCopy(config.rt, m_primid_init_pipeline[static_cast<bool>(depth)][static_cast<u8>(config.datm)], config.drawarea);
RenderCopy(rt, m_primid_init_pipeline[static_cast<bool>(depth)][static_cast<u8>(config.datm)], config.drawarea);
MRESetDSS(dsel);
pxAssert(config.ps.date == 1 || config.ps.date == 2);
if (config.ps.tex_is_fb)
MRESetTexture(config.rt, GSMTLTextureIndexRenderTarget);
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
config.require_one_barrier = false; // Ending render pass is our barrier
pxAssert(config.require_full_barrier == false && config.drawlist == nullptr);
MRESetHWPipelineState(config.vs, config.ps, {}, {});
@ -2170,38 +2228,11 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
stencil = config.ds;
break;
case GSHWDrawConfig::DestinationAlphaMode::Stencil:
SetupDestinationAlpha(config.rt, config.ds, config.drawarea, config.datm);
SetupDestinationAlpha(rt, config.ds, config.drawarea, config.datm);
stencil = config.ds;
break;
}
GSTexture* hdr_rt = nullptr;
if (config.ps.hdr)
{
GSVector2i size = config.rt->GetSize();
rt = hdr_rt = CreateRenderTarget(size.x, size.y, GSTexture::Format::HDRColor, false);
switch (config.rt->GetState())
{
case GSTexture::State::Dirty:
BeginRenderPass(@"HDR Init", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
RenderCopy(config.rt, m_hdr_init_pipeline, config.drawarea);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
break;
case GSTexture::State::Cleared:
{
BeginRenderPass(@"HDR Clear", hdr_rt, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare);
GSVector4 color = GSVector4::rgba32(config.rt->GetClearColor()) / GSVector4::cxpr(65535, 65535, 65535, 255);
[m_current_render.encoder setFragmentBytes:&color length:sizeof(color) atIndex:GSMTLBufferIndexUniforms];
RenderCopy(nullptr, m_hdr_clear_pipeline, config.drawarea);
break;
}
case GSTexture::State::Invalidated:
break;
}
}
// Try to reduce render pass restarts
if (!config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
config.ds = m_current_render.depth_target;
@ -2224,7 +2255,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
[mtlenc setStencilReferenceValue:1];
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier)
MRESetTexture(config.rt, GSMTLTextureIndexRenderTarget);
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
if (primid_tex)
MRESetTexture(primid_tex, GSMTLTextureIndexPrimIDs);
if (config.blend.constant_enable)
@ -2248,11 +2279,18 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
if (hdr_rt)
{
BeginRenderPass(@"HDR Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(hdr_rt, m_hdr_resolve_pipeline, config.drawarea);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
Recycle(hdr_rt);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
{
BeginRenderPass(@"HDR Resolve", config.rt, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
RenderCopy(hdr_rt, m_hdr_resolve_pipeline, config.hdr_update_area);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
}
if (primid_tex)

View File

@ -856,7 +856,10 @@ struct PSMain
void ps_fbmask(thread float4& C)
{
if (PS_FBMASK)
C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(current_color * 255.5) & cb.fbmask));
{
float multi = PS_HDR ? 65535.0 : 255.5;
C = float4((uint4(int4(C)) & (cb.fbmask ^ 0xff)) | (uint4(current_color * float4(multi, multi, multi, 255)) & cb.fbmask));
}
}
void ps_dither(thread float4& C, float As)
@ -956,8 +959,8 @@ struct PSMain
current_color.a = float(denorm_rt.g & 0x80);
}
}
float3 Cd = trunc(current_color.rgb * 255.5f);
float multi = PS_HDR ? 65535.0 : 255.5;
float3 Cd = trunc(current_color.rgb * multi);
float3 Cs = Color.rgb;
float3 A = pick(PS_BLEND_A, Cs, Cd, float3(0.f));

View File

@ -2414,6 +2414,45 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
GSTexture* primid_texture = nullptr;
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();
if (hdr_rt)
{
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
hdr_rt = nullptr;
}
else
{
config.ps.hdr = 1;
}
}
if (config.ps.hdr)
{
if (!hdr_rt)
{
config.hdr_update_area = config.drawarea;
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false);
OMSetRenderTargets(hdr_rt, config.ds, nullptr);
g_gs_device->SetHDRTexture(hdr_rt);
const GSVector4 dRect = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
}
}
// Destination Alpha Setup
switch (config.destination_alpha)
@ -2422,7 +2461,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
case GSHWDrawConfig::DestinationAlphaMode::Full:
break; // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking:
primid_texture = InitPrimDateTexture(config.rt, config.drawarea, config.datm);
primid_texture = InitPrimDateTexture(hdr_rt ? hdr_rt : config.rt, config.drawarea, config.datm);
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne:
if (m_features.texture_barrier)
@ -2442,29 +2481,20 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
{GSVector4(dst.x, dst.w, 0.0f, 0.0f), GSVector2(src.x, src.w)},
{GSVector4(dst.z, dst.w, 0.0f, 0.0f), GSVector2(src.z, src.w)},
};
SetupDATE(config.rt, config.ds, vertices, config.datm);
SetupDATE(hdr_rt ? hdr_rt : config.rt, config.ds, vertices, config.datm);
}
}
GSTexture* hdr_rt = nullptr;
GSTexture* draw_rt_clone = nullptr;
if (config.ps.hdr)
{
hdr_rt = CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false);
OMSetRenderTargets(hdr_rt, config.ds, &config.scissor);
GSVector4 dRect(config.drawarea);
const GSVector4 sRect = dRect / GSVector4(rtsize.x, rtsize.y).xyxy();
StretchRect(config.rt, sRect, hdr_rt, dRect, ShaderConvert::HDR_INIT, false);
}
else if (config.require_one_barrier && !m_features.texture_barrier)
if (config.require_one_barrier && !m_features.texture_barrier)
{
// Requires a copy of the RT
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, true);
draw_rt_clone = CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true);
GL_PUSH("Copy RT to temp texture for fbmask {%d,%d %dx%d}",
config.drawarea.left, config.drawarea.top,
config.drawarea.width(), config.drawarea.height());
CopyRect(config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
CopyRect(hdr_rt ? hdr_rt : config.rt, draw_rt_clone, config.drawarea, config.drawarea.left, config.drawarea.top);
}
else if (config.tex && config.tex == config.ds)
{
@ -2510,7 +2540,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (draw_rt_clone)
PSSetShaderResource(2, draw_rt_clone);
else if (config.require_one_barrier || config.require_full_barrier)
PSSetShaderResource(2, config.rt);
PSSetShaderResource(2, hdr_rt ? hdr_rt : config.rt);
SetupSampler(config.sampler);
@ -2669,12 +2699,19 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config)
if (hdr_rt)
{
GSVector2i size = config.rt->GetSize();
GSVector4 dRect(config.drawarea);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
Recycle(hdr_rt);
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
{
const GSVector2i size = config.rt->GetSize();
const GSVector4 dRect(config.hdr_update_area);
const GSVector4 sRect = dRect / GSVector4(size.x, size.y).xyxy();
StretchRect(hdr_rt, sRect, config.rt, dRect, ShaderConvert::HDR_RESOLVE, false);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
}
}

View File

@ -9,6 +9,7 @@
#include "GS/Renderers/Vulkan/VKBuilders.h"
#include "GS/Renderers/Vulkan/VKShaderCache.h"
#include "GS/Renderers/Vulkan/VKSwapChain.h"
#include "GS/Renderers/Common/GSDevice.h"
#include "BuildVersion.h"
#include "Host.h"
@ -5589,28 +5590,12 @@ GSTextureVK* GSDeviceVK::SetupPrimitiveTrackingDATE(GSHWDrawConfig& config)
void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
{
// Destination Alpha Setup
switch (config.destination_alpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Off: // No setup
case GSHWDrawConfig::DestinationAlphaMode::Full: // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking: // Setup is done below
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne: // setup is done below
{
// we only need to do the setup here if we don't have barriers, in which case do full DATE.
if (!m_features.texture_barrier)
{
SetupDATE(config.rt, config.ds, config.datm, config.drawarea);
config.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Stencil;
}
}
break;
case GSHWDrawConfig::DestinationAlphaMode::Stencil:
SetupDATE(config.rt, config.ds, config.datm, config.drawarea);
break;
}
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* hdr_rt = static_cast<GSTextureVK*>(g_gs_device->GetHDRTexture());
// stream buffer in first, in case we need to exec
SetVSConstantBuffer(config.cb_vs);
@ -5632,68 +5617,112 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
SetLineWidth(config.line_expand ? config.cb_ps.ScaleFactor.z : 1.0f);
// Primitive ID tracking DATE setup.
// Needs to be done before
GSTextureVK* date_image = nullptr;
if (config.destination_alpha == GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking)
{
// If we have a HDR in progress, we need to use the HDR texture, but we can't check this later as there's a chicken/egg problem with the pipe setup.
GSTexture* backup_rt = config.rt;
if(hdr_rt)
config.rt = hdr_rt;
date_image = SetupPrimitiveTrackingDATE(config);
if (!date_image)
{
Console.WriteLn("Failed to allocate DATE image, aborting draw.");
return;
}
config.rt = backup_rt;
}
// figure out the pipeline
PipelineSelector& pipe = m_pipeline_selector;
UpdateHWPipelineSelector(config, pipe);
const GSVector2i rtsize(config.rt ? config.rt->GetSize() : config.ds->GetSize());
GSTextureVK* draw_rt = static_cast<GSTextureVK*>(config.rt);
GSTextureVK* draw_ds = static_cast<GSTextureVK*>(config.ds);
GSTextureVK* draw_rt_clone = nullptr;
GSTextureVK* hdr_rt = nullptr;
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
// now blit the hdr texture back to the original target
if (hdr_rt)
{
EndRenderPass();
hdr_rt = static_cast<GSTextureVK*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
if (config.hdr_mode == GSHWDrawConfig::HDRMode::EarlyResolve)
{
Console.WriteLn("Failed to allocate HDR render target, aborting draw.");
if (date_image)
Recycle(date_image);
GL_POP();
return;
}
GL_PUSH("Blit HDR back to RT");
// propagate clear value through if the hdr render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
EndRenderPass();
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, GSVector4i::loadh(rtsize), static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
alignas(16) VkClearValue cvs[2];
u32 cv_count = 0;
GSVector4::store<true>(&cvs[cv_count++].color, draw_rt->GetUNormClearColor());
if (draw_ds)
cvs[cv_count++].depthStencil = {draw_ds->GetClearDepth(), 1};
BeginClearRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_CLEAR,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect(), cvs, cv_count);
draw_rt->SetState(GSTexture::State::Dirty);
}
else
{
BeginRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_LOAD,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect());
}
const GSVector4 drawareaf = GSVector4(config.hdr_update_area);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(hdr_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
hdr_rt = nullptr;
}
else
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
// If depth is cleared, we need to commit it, because we're only going to draw to the active part of the FB.
if (draw_ds && draw_ds->GetState() == GSTexture::State::Cleared && !config.drawarea.eq(GSVector4i::loadh(rtsize)))
draw_ds->CommitClear(m_current_command_buffer);
pipe.ps.hdr = 1;
draw_rt = hdr_rt;
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
draw_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
// 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);
draw_rt = hdr_rt;
}
else if (config.require_one_barrier && !m_features.texture_barrier)
// Destination Alpha Setup
switch (config.destination_alpha)
{
case GSHWDrawConfig::DestinationAlphaMode::Off: // No setup
case GSHWDrawConfig::DestinationAlphaMode::Full: // No setup
case GSHWDrawConfig::DestinationAlphaMode::PrimIDTracking: // Setup is done below
break;
case GSHWDrawConfig::DestinationAlphaMode::StencilOne: // setup is done below
{
// we only need to do the setup here if we don't have barriers, in which case do full DATE.
if (!m_features.texture_barrier)
{
SetupDATE(draw_rt, config.ds, config.datm, config.drawarea);
config.destination_alpha = GSHWDrawConfig::DestinationAlphaMode::Stencil;
}
}
break;
case GSHWDrawConfig::DestinationAlphaMode::Stencil:
SetupDATE(draw_rt, config.ds, config.datm, config.drawarea);
break;
}
if (config.require_one_barrier && !m_features.texture_barrier)
{
// requires a copy of the RT
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, GSTexture::Format::Color, true));
draw_rt_clone = static_cast<GSTextureVK*>(CreateTexture(rtsize.x, rtsize.y, 1, hdr_rt ? GSTexture::Format::HDRColor : GSTexture::Format::Color, true));
if (draw_rt_clone)
{
EndRenderPass();
@ -5706,6 +5735,49 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
}
// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
{
if (!hdr_rt)
{
config.hdr_update_area = config.drawarea;
EndRenderPass();
hdr_rt = static_cast<GSTextureVK*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
if (!hdr_rt)
{
Console.WriteLn("Failed to allocate HDR render target, aborting draw.");
if (date_image)
Recycle(date_image);
GL_POP();
return;
}
g_gs_device->SetHDRTexture(static_cast<GSTexture*>(hdr_rt));
// propagate clear value through if the hdr render is the first
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
hdr_rt->SetState(GSTexture::State::Cleared);
hdr_rt->SetClearColor(draw_rt->GetClearColor());
// If depth is cleared, we need to commit it, because we're only going to draw to the active part of the FB.
if (draw_ds && draw_ds->GetState() == GSTexture::State::Cleared && !config.drawarea.eq(GSVector4i::loadh(rtsize)))
draw_ds->CommitClear(m_current_command_buffer);
}
else if (draw_rt->GetState() == GSTexture::State::Dirty)
{
GL_PUSH_("HDR Render Target Setup");
draw_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
}
// 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);
}
draw_rt = hdr_rt;
}
// clear texture binding when it's bound to RT or DS.
if (!config.tex && ((config.rt && static_cast<GSTextureVK*>(config.rt) == m_tfx_textures[0]) ||
(config.ds && static_cast<GSTextureVK*>(config.ds) == m_tfx_textures[0])))
@ -5714,7 +5786,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// render pass restart optimizations
if (hdr_rt)
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly))
{
// HDR requires blitting.
EndRenderPass();
@ -5774,7 +5846,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// Only draw to the active area of the HDR target. Except when depth is cleared, we need to use the full
// buffer size, otherwise it'll only clear the draw part of the depth buffer.
const GSVector4i render_area = (pipe.ps.hdr && ds_op != VK_ATTACHMENT_LOAD_OP_CLEAR) ? config.drawarea :
const GSVector4i render_area = (pipe.ps.hdr && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && ds_op != VK_ATTACHMENT_LOAD_OP_CLEAR) ? config.drawarea :
GSVector4i::loadh(rtsize);
if (is_clearing_rt)
@ -5813,17 +5885,19 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
}
// rt -> hdr blit if enabled
if (hdr_rt && config.rt->GetState() == GSTexture::State::Dirty)
if (hdr_rt && (config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) && config.rt->GetState() == GSTexture::State::Dirty)
{
OMSetRenderTargets(draw_rt, draw_ds, GSVector4i::loadh(rtsize), static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
SetUtilityTexture(static_cast<GSTextureVK*>(config.rt), m_point_sampler);
SetPipeline(m_hdr_setup_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
const GSVector4 drawareaf = GSVector4(config.drawarea);
const GSVector4 drawareaf = GSVector4((config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertOnly) ? GSVector4i::loadh(rtsize) : config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
GL_POP();
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
}
// VB/IB upload, if we did DATE setup and it's not HDR this has already been done
@ -5880,46 +5954,55 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config)
// now blit the hdr texture back to the original target
if (hdr_rt)
{
GL_PUSH("Blit HDR back to RT");
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);
EndRenderPass();
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
if (draw_rt->GetState() == GSTexture::State::Cleared)
if ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
{
alignas(16) VkClearValue cvs[2];
u32 cv_count = 0;
GSVector4::store<true>(&cvs[cv_count++].color, draw_rt->GetUNormClearColor());
if (draw_ds)
cvs[cv_count++].depthStencil = {draw_ds->GetClearDepth(), 1};
GL_PUSH("Blit HDR back to RT");
BeginClearRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_CLEAR,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect(), cvs, cv_count);
draw_rt->SetState(GSTexture::State::Dirty);
EndRenderPass();
hdr_rt->TransitionToLayout(GSTextureVK::Layout::ShaderReadOnly);
draw_rt = static_cast<GSTextureVK*>(config.rt);
OMSetRenderTargets(draw_rt, draw_ds, (config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly) ? GSVector4i::loadh(rtsize) : config.scissor, static_cast<FeedbackLoopFlag>(pipe.feedback_loop_flags));
// if this target was cleared and never drawn to, perform the clear as part of the resolve here.
if (draw_rt->GetState() == GSTexture::State::Cleared)
{
alignas(16) VkClearValue cvs[2];
u32 cv_count = 0;
GSVector4::store<true>(&cvs[cv_count++].color, draw_rt->GetUNormClearColor());
if (draw_ds)
cvs[cv_count++].depthStencil = {draw_ds->GetClearDepth(), 1};
BeginClearRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_CLEAR,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect(), cvs, cv_count);
draw_rt->SetState(GSTexture::State::Dirty);
}
else
{
BeginRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_LOAD,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect());
}
const GSVector4 drawareaf = GSVector4(config.hdr_update_area);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(hdr_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
g_gs_device->SetHDRTexture(nullptr);
}
else
{
BeginRenderPass(GetTFXRenderPass(true, pipe.ds, false, false, pipe.IsRTFeedbackLoop(),
pipe.IsTestingAndSamplingDepth(), VK_ATTACHMENT_LOAD_OP_LOAD,
pipe.ds ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_DONT_CARE),
draw_rt->GetRect());
}
const GSVector4 drawareaf = GSVector4(config.drawarea);
const GSVector4 sRect(drawareaf / GSVector4(rtsize).xyxy());
SetPipeline(m_hdr_finish_pipelines[pipe.ds][pipe.IsRTFeedbackLoop()]);
SetUtilityTexture(hdr_rt, m_point_sampler);
DrawStretchRect(sRect, drawareaf, rtsize);
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
Recycle(hdr_rt);
}
config.hdr_mode = GSHWDrawConfig::HDRMode::NoModify;
}
void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelector& pipe)