Skip to content

Commit

Permalink
GS/HW: Reduce number of copies for HDR
Browse files Browse the repository at this point in the history
  • Loading branch information
refractionpcsx2 committed Jan 30, 2025
1 parent df7646f commit ede16bb
Show file tree
Hide file tree
Showing 8 changed files with 454 additions and 163 deletions.
20 changes: 20 additions & 0 deletions pcsx2/GS/Renderers/Common/GSDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -850,6 +865,7 @@ class GSDevice : public GSAlignedClass<32>
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);

Expand All @@ -874,6 +890,10 @@ class GSDevice : public GSAlignedClass<32>
/// 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);

Expand Down
60 changes: 44 additions & 16 deletions pcsx2/GS/Renderers/DX11/GSDevice11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2547,19 +2547,40 @@ void GSDevice11::RenderHW(GSHWDrawConfig& config)
SetupDATE(config.rt, config.ds, vertices, config.datm);
}

GSTexture* hdr_rt = nullptr;
GSTexture* hdr_rt = g_gs_device->GetHDRTexture();

if (hdr_rt && 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;
}

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);
{
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);
}
}

if (config.vs.expand != GSHWDrawConfig::VSExpand::None)
Expand Down Expand Up @@ -2690,11 +2711,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);
}
}
}
133 changes: 86 additions & 47 deletions pcsx2/GS/Renderers/DX12/GSDevice12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3855,41 +3855,73 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config)
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)));
(config.ps.hdr && config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve) ? config.drawarea :
GSVector4i::loadh(rtsize));

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;
GSTexture12* hdr_rt = static_cast<GSTexture12*>(g_gs_device->GetHDRTexture());

// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
// now blit the hdr texture back to the original target
if (hdr_rt && 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);

hdr_rt = static_cast<GSTexture12*>(CreateRenderTarget(rtsize.x, rtsize.y, GSTexture::Format::HDRColor, false));
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);
}

// Switch to hdr target for colclip rendering
if (pipe.ps.hdr)
{
if (!hdr_rt)
{
Console.WriteLn("D3D12: Failed to allocate HDR render target, aborting draw.");
if (date_image)
Recycle(date_image);
return;
}
config.hdr_update_area = config.drawarea;

// 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);
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
Expand Down Expand Up @@ -3964,13 +3996,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();
Expand Down Expand Up @@ -4025,28 +4058,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");

EndRenderPass();
hdr_rt->TransitionToState(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
config.hdr_update_area = config.hdr_update_area.runion(config.drawarea);

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(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 ((config.hdr_mode == GSHWDrawConfig::HDRMode::ResolveOnly || config.hdr_mode == GSHWDrawConfig::HDRMode::ConvertAndResolve))
{
GL_PUSH("Blit HDR back to RT");

Recycle(hdr_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);
}
}
}

Expand Down
Loading

0 comments on commit ede16bb

Please sign in to comment.