Skip to content

Commit f7c57b3

Browse files
committed
GS:MTL: Port oneshot ROV to Metal.
Co-authored-by: TellowKrinkle
1 parent fb0ee89 commit f7c57b3

4 files changed

Lines changed: 197 additions & 28 deletions

File tree

pcsx2/GS/Renderers/Metal/GSDeviceMTL.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ class GSDeviceMTL final : public GSDevice
250250
// Functions and Pipeline States
251251
MRCOwned<id<MTLComputePipelineState>> m_cas_pipeline[2];
252252
std::vector<MRCOwned<id<MTLRenderPipelineState>>> m_convert_pipeline;
253+
MRCOwned<id<MTLRenderPipelineState>> m_rov_copy_one_pipeline[2]; // [color]
254+
MRCOwned<id<MTLRenderPipelineState>> m_rov_copy_both_pipeline;
253255
MRCOwned<id<MTLRenderPipelineState>> m_present_pipeline[static_cast<int>(PresentShader::Count)];
254256
MRCOwned<id<MTLRenderPipelineState>> m_merge_pipeline[4];
255257
MRCOwned<id<MTLRenderPipelineState>> m_interlace_pipeline[NUM_INTERLACE_SHADERS];
@@ -335,6 +337,8 @@ class GSDeviceMTL final : public GSDevice
335337
GSTexture* m_ds_as_rt_gstexture = nullptr;
336338
MRCOwned<id<MTLTexture>> m_rov_dummy_texture;
337339
struct { u32 w, h; } m_rov_dummy_texture_size = {};
340+
std::unique_ptr<GSTextureMTL> m_rov_rt;
341+
std::unique_ptr<GSTextureMTL> m_rov_ds;
338342

339343
struct DebugEntry
340344
{
@@ -383,6 +387,8 @@ class GSDeviceMTL final : public GSDevice
383387
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare, bool rt1 = false);
384388
/// Begin a new full-ROV render pass (may reuse existing)
385389
void BeginFullROV(NSString* name, uint32_t width, uint32_t height);
390+
/// Begin a render pass for combined color+depth ROV copy
391+
void BeginROVCopy(GSTextureMTL* color, GSTextureMTL* depth);
386392
/// Call at the end of each frame
387393
void FrameCompleted();
388394

@@ -464,6 +470,7 @@ class GSDeviceMTL final : public GSDevice
464470

465471
// MARK: Render HW
466472

473+
void SetupOneshotROV(const GSHWDrawConfig& config, GSTextureMTL* rt, GSTextureMTL* ds, const std::vector<GSVector4i>& rects, const GSVector2i& size);
467474
void SetupDestinationAlpha(GSTexture* rt, GSTexture* ds, const GSVector4i& r, SetDATM datm);
468475
void PrepareROVTexture(GSTexture** ptex);
469476
void RenderHW(GSHWDrawConfig& config) override;

pcsx2/GS/Renderers/Metal/GSDeviceMTL.mm

Lines changed: 152 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,8 @@ static GSVector4 GetRTLoadInfo(GSTextureMTL* tex, MTLLoadAction* load_action)
505505
color1.texture = GetRT1DepthTexture(md);
506506
if (m_features.framebuffer_fetch)
507507
color1.clearColor = MTLClearColorMake(depth_load == MTLLoadActionClear ? depth_clear.x : -1, 0, 0, 0);
508+
else
509+
color1.loadAction = MTLLoadActionLoad;
508510
}
509511

510512
PrepareBeginRenderPass();
@@ -576,6 +578,27 @@ static GSVector4 GetRTLoadInfo(GSTextureMTL* tex, MTLLoadAction* load_action)
576578
beforeStages:MTLRenderStageVertex];
577579
}
578580

581+
void GSDeviceMTL::BeginROVCopy(GSTextureMTL* color, GSTextureMTL* depth)
582+
{
583+
pxAssert(!m_features.framebuffer_fetch);
584+
color->SetState(GSTexture::State::Dirty);
585+
depth->SetState(GSTexture::State::Dirty);
586+
MTLRenderPassDescriptor* desc = m_render_pass_desc[1 | 8];
587+
MTLRenderPassColorAttachmentDescriptor* color0 = desc.colorAttachments[0];
588+
color0.loadAction = MTLLoadActionDontCare;
589+
color0.texture = color->GetTexture();
590+
MTLRenderPassColorAttachmentDescriptor* color1 = desc.colorAttachments[1];
591+
color1.loadAction = MTLLoadActionDontCare;
592+
color1.storeAction = MTLStoreActionStore;
593+
color1.texture = depth->GetTexture();
594+
PrepareBeginRenderPass();
595+
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
596+
[m_current_render.encoder setLabel:@"ROV Clear"];
597+
if (!m_dev.features.unified_memory)
598+
[m_current_render.encoder waitForFence:m_draw_sync_fence
599+
beforeStages:MTLRenderStageVertex];
600+
}
601+
579602
void GSDeviceMTL::FrameCompleted()
580603
{
581604
if (m_spin_timer)
@@ -1125,11 +1148,11 @@ static bool ConvertShaderNotNeeded(ShaderConvert shader)
11251148
auto desc = MRCTransfer([MTLRenderPassDescriptor new]);
11261149
[[desc depthAttachment] setStoreAction:MTLStoreActionStore];
11271150
[[desc stencilAttachment] setStoreAction:MTLStoreActionStore];
1128-
if (rt1)
1151+
if (rt1 && m_features.framebuffer_fetch)
11291152
{
11301153
MTLRenderPassColorAttachmentDescriptor* color1 = [[desc colorAttachments] objectAtIndexedSubscript:1];
11311154
[color1 setStoreAction:MTLStoreActionDontCare];
1132-
[color1 setLoadAction:m_features.framebuffer_fetch ? MTLLoadActionClear : MTLLoadActionLoad];
1155+
[color1 setLoadAction:MTLLoadActionClear];
11331156
}
11341157
m_render_pass_desc[i] = desc;
11351158
}
@@ -1304,8 +1327,22 @@ static bool ConvertShaderNotNeeded(ShaderConvert shader)
13041327
setFnConstantB(m_fn_constants, shader.DepthOutput(), GSMTLConstantIndex_DEPTH_OUT);
13051328
m_convert_pipeline[shader.Index()] = MakePipeline(pdesc, vs_convert, LoadShader(shader_name), name);
13061329
}
1330+
13071331
pdesc.colorAttachments[0].writeMask = MTLColorWriteMaskAll;
13081332
pdesc.depthAttachmentPixelFormat = MTLPixelFormatInvalid;
1333+
pdesc.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;
1334+
1335+
if (m_features.rov)
1336+
{
1337+
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatRGBA8Unorm;
1338+
pdesc.colorAttachments[1].pixelFormat = MTLPixelFormatR32Float;
1339+
m_rov_copy_both_pipeline = MakePipeline(pdesc, vs_convert, LoadShader(@"ps_rov_copy_both"), @"ROV Copy Both");
1340+
pdesc.colorAttachments[1].pixelFormat = MTLPixelFormatInvalid;
1341+
m_rov_copy_one_pipeline[1] = MakePipeline(pdesc, vs_convert, LoadShader(@"ps_rov_copy_color"), @"ROV Copy Color");
1342+
pdesc.colorAttachments[0].pixelFormat = MTLPixelFormatR32Float;
1343+
m_rov_copy_one_pipeline[0] = MakePipeline(pdesc, vs_convert, LoadShader(@"ps_rov_copy_depth"), @"ROV Copy Depth");
1344+
}
1345+
13091346
for (size_t i = 0; i < std::size(m_present_pipeline); i++)
13101347
{
13111348
PresentShader conv = static_cast<PresentShader>(i);
@@ -1845,6 +1882,102 @@ static bool ConvertShaderNotNeeded(ShaderConvert shader)
18451882
flush(num_rects);
18461883
}}
18471884

1885+
void GSDeviceMTL::SetupOneshotROV(const GSHWDrawConfig& config, GSTextureMTL* rt, GSTextureMTL* ds, const std::vector<GSVector4i>& rects, const GSVector2i& size_i)
1886+
{
1887+
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
1888+
g_perfmon.Put(GSPerfMon::TextureCopiesROV, 1);
1889+
g_perfmon.Put(GSPerfMon::DrawCallsROV, 1);
1890+
1891+
bool rt_copy = config.ps.HasOneshotColorROV();
1892+
bool ds_copy = config.ps.HasOneshotDepthROV();
1893+
1894+
// Create ROV textures
1895+
if (rt_copy && (!m_rov_rt || m_rov_rt->GetSize() != size_i))
1896+
{
1897+
if (m_rov_rt)
1898+
Recycle(m_rov_rt.release());
1899+
m_rov_rt.reset(static_cast<GSTextureMTL*>(CreateShaderWriteTarget(size_i, GSTexture::Format::Color, false)));
1900+
#if PCSX2_DEVBUILD
1901+
m_rov_rt->SetDebugName(fmt::format("RT for oneshot ROV {}x{}", size_i.x, size_i.y));
1902+
#endif
1903+
}
1904+
if (ds_copy && (!m_rov_ds || m_rov_ds->GetSize() != size_i))
1905+
{
1906+
if (m_rov_ds)
1907+
Recycle(m_rov_ds.release());
1908+
m_rov_ds.reset(static_cast<GSTextureMTL*>(CreateShaderWriteTarget(size_i, GSTexture::Format::DepthColor, false)));
1909+
#if PCSX2_DEVBUILD
1910+
m_rov_ds->SetDebugName(fmt::format("DS for oneshot ROV {}x{}", size_i.x, size_i.y));
1911+
#endif
1912+
}
1913+
1914+
// Avoid copies if the original is cleared.
1915+
if (rt_copy && rt->GetState() == GSTexture::State::Cleared)
1916+
{
1917+
m_rov_rt->SetClearColor(rt->GetClearColor());
1918+
m_rov_rt->FlushClears();
1919+
rt_copy = false;
1920+
}
1921+
if (ds_copy && ds->GetState() == GSTexture::State::Cleared)
1922+
{
1923+
m_rov_ds->SetClearDepth(ds->GetClearDepth());
1924+
m_rov_ds->FlushClears();
1925+
ds_copy = false;
1926+
}
1927+
1928+
if (!(rt_copy || ds_copy))
1929+
return; // Copy handled with clear.
1930+
1931+
// Vertices / IA
1932+
const u32 num_rects = static_cast<u32>(rects.size());
1933+
const Map allocation = Allocate(m_vertex_upload_buf, sizeof(ConvertShaderVertex) * 4 * num_rects);
1934+
std::array<GSVector4, 4>* write = static_cast<std::array<GSVector4, 4>*>(allocation.cpu_buffer);
1935+
1936+
const GSVector2 size(static_cast<float>(size_i.x), static_cast<float>(size_i.y));
1937+
for (const GSVector4i& rect_i : rects)
1938+
{
1939+
GSVector4 rect(rect_i);
1940+
*write++ = CalcStrechRectPoints(rect, rect, size);
1941+
}
1942+
1943+
// Render pass
1944+
if (rt_copy && ds_copy)
1945+
{
1946+
BeginROVCopy(m_rov_rt.get(), m_rov_ds.get());
1947+
MRESetTexture(rt, 0);
1948+
MRESetTexture(ds, 1);
1949+
MRESetPipeline(m_rov_copy_both_pipeline);
1950+
}
1951+
else
1952+
{
1953+
GSTextureMTL* src = rt_copy ? rt : ds;
1954+
GSTextureMTL* dst = rt_copy ? m_rov_rt.get() : m_rov_ds.get();
1955+
BeginRenderPass(@"ROV Copy", dst, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare, nullptr, MTLLoadActionDontCare, false);
1956+
MRESetTexture(src, GSMTLTextureIndexNonHW);
1957+
MRESetPipeline(m_rov_copy_one_pipeline[rt_copy]);
1958+
}
1959+
1960+
FlushDebugEntries(m_current_render.encoder);
1961+
MREClearScissor();
1962+
MRESetDSS(DepthStencilSelector::NoDepth());
1963+
1964+
const id<MTLRenderCommandEncoder> enc = m_current_render.encoder;
1965+
[enc setVertexBuffer:allocation.gpu_buffer
1966+
offset:allocation.gpu_offset
1967+
atIndex:GSMTLBufferIndexVertices];
1968+
1969+
[enc drawIndexedPrimitives:MTLPrimitiveTypeTriangle
1970+
indexCount:num_rects * 6
1971+
indexType:MTLIndexTypeUInt16
1972+
indexBuffer:m_expand_index_buffer
1973+
indexBufferOffset:0
1974+
instanceCount:1
1975+
baseVertex:0
1976+
baseInstance:0];
1977+
1978+
EndRenderPass();
1979+
}
1980+
18481981
void GSDeviceMTL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
18491982
{
18501983
GSMTLCLUTConvertPSUniform uniform = { sScale, {offsetX, offsetY}, dOffset };
@@ -2060,7 +2193,7 @@ static MTLBlendOperation ConvertBlendOp(GSDevice::BlendOp generic)
20602193
setFnConstantI(m_fn_constants, pssel.aa1, GSMTLConstantIndex_PS_AA1);
20612194
setFnConstantB(m_fn_constants, pssel.abe, GSMTLConstantIndex_PS_ABE);
20622195
setFnConstantI(m_fn_constants, pssel.sw_aniso, GSMTLConstantIndex_PS_SW_ANISO);
2063-
setFnConstantB(m_fn_constants, pssel.rov_color, GSMTLConstantIndex_PS_ROV_COLOR);
2196+
setFnConstantI(m_fn_constants, pssel.rov_color, GSMTLConstantIndex_PS_ROV_COLOR);
20642197
setFnConstantI(m_fn_constants, pssel.rov_depth, GSMTLConstantIndex_PS_ROV_DEPTH);
20652198
bool eft = pssel.HasColorROV() && !pssel.HasDepthROV() && !pssel.HasDepthOutput();
20662199
auto newps = LoadShader(eft ? @"ps_main_rov_eft" : @"ps_main");
@@ -2347,6 +2480,13 @@ static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
23472480

23482481
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
23492482
{ @autoreleasepool {
2483+
if (config.ps.HasOneshotROV())
2484+
{
2485+
// Do this first since it requires a different vertex upload from the main draw.
2486+
const GSVector2i rtsize = (config.rt ? config.rt : config.ds)->GetSize();
2487+
SetupOneshotROV(config, static_cast<GSTextureMTL*>(config.rt), static_cast<GSTextureMTL*>(config.ds), *config.draw_coarse_rasterize, rtsize);
2488+
}
2489+
23502490
if (config.tex && (config.ds == config.tex || config.rt == config.tex))
23512491
EndRenderPass(); // Barrier
23522492

@@ -2481,7 +2621,7 @@ static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
24812621
}
24822622

24832623
// Try to reduce render pass restarts
2484-
if (!config.ps.HasColorROV() && !config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
2624+
if (!config.HasContinuousColorROV() && !config.ds && m_current_render.color_target == rt && stencil == m_current_render.stencil_target && m_current_render.depth_target != config.tex)
24852625
config.ds = m_current_render.depth_target;
24862626
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
24872627
rt = m_current_render.color_target;
@@ -2496,12 +2636,12 @@ static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
24962636
}
24972637

24982638
const bool feedback_depth = config.ps.IsFeedbackLoopDepth();
2499-
const bool rt1 = feedback_depth && !m_features.depth_feedback && !config.ps.HasDepthROV();
2639+
const bool rt1 = feedback_depth && !m_features.depth_feedback && !config.HasContinuousDepthROV();
25002640
GSTexture* rt_bind = rt;
25012641
GSTexture* ds_bind = config.ds;
25022642
GSTexture* rt_size = rt_bind ? rt_bind : ds_bind;
2503-
if (config.ps.HasColorROV() && rt_bind) PrepareROVTexture(&rt_bind);
2504-
if (config.ps.HasDepthROV() && ds_bind) PrepareROVTexture(&ds_bind);
2643+
if (config.HasContinuousColorROV() && rt_bind) PrepareROVTexture(&rt_bind);
2644+
if (config.HasContinuousDepthROV() && ds_bind) PrepareROVTexture(&ds_bind);
25052645
if (!rt_bind && !ds_bind && !stencil)
25062646
BeginFullROV(@"RenderHWROV", rt_size->GetWidth(), rt_size->GetHeight());
25072647
else
@@ -2511,13 +2651,13 @@ static bool usesStencil(GSHWDrawConfig::DestinationAlphaMode dstalpha)
25112651
if (usesStencil(config.destination_alpha))
25122652
[mtlenc setStencilReferenceValue:1];
25132653
MREInitHWDraw(config, allocation);
2514-
if (config.ps.HasColorROV() && m_dev.features.rov_requires_r32)
2654+
if (config.HasContinuousColorROV() && m_dev.features.rov_requires_r32)
25152655
MRESetTexture(reinterpret_cast<GSTextureMTL*>(rt)->GetROVTexture(), GSMTLTextureIndexRenderTarget);
2516-
else if (config.require_one_barrier || config.require_full_barrier || config.ps.HasColorROV())
2517-
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
2518-
if (config.ps.HasDepthROV())
2656+
else if (config.require_one_barrier || config.require_full_barrier || config.HasColorROV())
2657+
MRESetTexture(config.HasOneshotColorROV() ? m_rov_rt.get() : rt, GSMTLTextureIndexRenderTarget);
2658+
if (config.HasDepthROV())
25192659
{
2520-
MRESetTexture(config.ds, GSMTLTextureIndexDepthTarget);
2660+
MRESetTexture(config.HasOneshotDepthROV() ? m_rov_ds.get() : config.ds, GSMTLTextureIndexDepthTarget);
25212661
}
25222662
else if (feedback_depth)
25232663
{

pcsx2/GS/Renderers/Metal/convert.metal

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,23 @@ fragment float4 ps_shadeboost(float4 p [[position]], DirectReadTextureIn<float>
564564

565565
return float4(csb, 1);
566566
}
567+
568+
struct ROVCopyBothOut
569+
{
570+
float4 color [[color(0)]];
571+
float depth [[color(1)]];
572+
};
573+
574+
fragment float4 ps_rov_copy_color(float4 p [[position]], texture2d<float> tex [[texture(GSMTLTextureIndexNonHW)]])
575+
{
576+
return tex.read(uint2(p.xy));
577+
}
578+
fragment float ps_rov_copy_depth(float4 p [[position]], depth2d<float> tex [[texture(GSMTLTextureIndexNonHW)]])
579+
{
580+
return tex.read(uint2(p.xy));
581+
}
582+
fragment ROVCopyBothOut ps_rov_copy_both(float4 p [[position]], texture2d<float> ctex [[texture(0)]], depth2d<float> dtex [[texture(1)]])
583+
{
584+
uint2 pos = uint2(p.xy);
585+
return { ctex.read(pos), dtex.read(pos) };
586+
}

0 commit comments

Comments
 (0)