GS:MTL: Add ROV support for RW Textures Tier 1 hardware

This commit is contained in:
TellowKrinkle
2026-06-30 01:03:30 -05:00
committed by lightningterror
parent 2821986fb2
commit 2a348dcff9
6 changed files with 57 additions and 13 deletions
+1
View File
@@ -451,6 +451,7 @@ public:
void MRESetDSS(id<MTLDepthStencilState> dss);
void MRESetSampler(SamplerSelector sel);
void MRESetTexture(GSTexture* tex, int pos);
void MRESetTexture(id<MTLTexture> tex, int pos);
void MRESetVertices(id<MTLBuffer> buffer, size_t offset);
void MRESetVSIndices(id<MTLBuffer> buffer, size_t offset);
void MRESetScissor(const GSVector4i& scissor);
+26 -5
View File
@@ -624,6 +624,7 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Usage usage, int width, int hei
[desc setStorageMode:MTLStorageModePrivate];
MTLTextureUsage mtl_usage = MTLTextureUsageShaderRead;
bool needs_rov_tex = false;
if (GSTexture::IsRenderTargetOrDepthStencil(usage))
mtl_usage |= MTLTextureUsageRenderTarget;
@@ -634,6 +635,13 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Usage usage, int width, int hei
mtl_usage |= MTLTextureUsagePixelFormatView; // Force color compression off by including PixelFormatView
}
if (usage == GSTexture::ShaderWriteTarget && format == GSTexture::Format::Color && m_dev.features.rov_requires_r32)
{
// Need to make an R32 view of the texture for ROV
mtl_usage |= MTLTextureUsagePixelFormatView;
needs_rov_tex = true;
}
if (GSTexture::IsShaderWrite(usage))
mtl_usage |= MTLTextureUsageShaderWrite;
@@ -642,7 +650,8 @@ GSTexture* GSDeviceMTL::CreateSurface(GSTexture::Usage usage, int width, int hei
MRCOwned<id<MTLTexture>> tex = MRCTransfer([m_dev.dev newTextureWithDescriptor:desc]);
if (tex)
{
GSTextureMTL* t = new GSTextureMTL(this, tex, usage, format);
MRCOwned<id<MTLTexture>> rov_tex = needs_rov_tex ? MRCTransfer([tex newTextureViewWithPixelFormat:MTLPixelFormatR32Uint]) : nil;
GSTextureMTL* t = new GSTextureMTL(this, tex, rov_tex, usage, format);
if (GSTexture::IsRenderTarget(usage))
{
ClearRenderTarget(t, 0);
@@ -1059,13 +1068,14 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.test_and_sample_depth = true;
m_features.depth_feedback = getDepthFeedback(m_dev, m_features.framebuffer_fetch);
m_features.aa1 = GSConfig.HWAA1 && m_features.vs_expand;
m_features.rov = m_dev.features.rov && !m_dev.features.rov_requires_r32 && !m_features.framebuffer_fetch;
m_features.rov = m_dev.features.rov && !m_features.framebuffer_fetch;
m_max_texture_size = m_dev.features.max_texsize;
// Init metal stuff
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
setFnConstantB(m_fn_constants, m_dev.features.rov_requires_r32, GSMTLConstantIndex_ROV_NEEDS_R32);
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
@@ -2147,6 +2157,15 @@ void GSDeviceMTL::MRESetTexture(GSTexture* tex, int pos)
mtex->m_last_read = m_current_draw;
}
void GSDeviceMTL::MRESetTexture(id<MTLTexture> tex, int pos)
{
GSTexture* gstex = reinterpret_cast<GSTexture*>(tex); // No one actually dereferences this
if (!tex || gstex == m_current_render.tex[pos])
return;
m_current_render.tex[pos] = gstex;
[m_current_render.encoder setFragmentTexture:tex atIndex:pos];
}
void GSDeviceMTL::MRESetVertices(id<MTLBuffer> buffer, size_t offset)
{
if (m_current_render.vertex_buffer != buffer)
@@ -2492,7 +2511,9 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
if (usesStencil(config.destination_alpha))
[mtlenc setStencilReferenceValue:1];
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier || config.ps.HasColorROV())
if (config.ps.HasColorROV() && m_dev.features.rov_requires_r32)
MRESetTexture(reinterpret_cast<GSTextureMTL*>(rt)->GetROVTexture(), GSMTLTextureIndexRenderTarget);
else if (config.require_one_barrier || config.require_full_barrier || config.ps.HasColorROV())
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
if (config.ps.HasDepthROV())
{
@@ -165,6 +165,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_CAS_SHARPEN_ONLY,
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
GSMTLConstantIndex_DEPTH_FEEDBACK,
GSMTLConstantIndex_ROV_NEEDS_R32,
GSMTLConstantIndex_FST,
GSMTLConstantIndex_IIP,
GSMTLConstantIndex_VS_POINT_SIZE,
+3 -1
View File
@@ -21,12 +21,13 @@ class GSTextureMTL : public GSTexture
{
GSDeviceMTL* m_dev;
MRCOwned<id<MTLTexture>> m_texture;
MRCOwned<id<MTLTexture>> m_rov_texture;
bool m_has_mipmaps = false;
public:
u64 m_last_read = 0; ///< Last time this texture was read by a draw
u64 m_last_write = 0; ///< Last time this texture was written by a draw
GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, Usage usage, Format format);
GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, MRCOwned<id<MTLTexture>> rov_texture, Usage usage, Format format);
~GSTextureMTL();
/// For making fake backbuffers
@@ -42,6 +43,7 @@ public:
void Unmap() override;
void GenerateMipmap() override;
id<MTLTexture> GetTexture() { return m_texture; }
id<MTLTexture> GetROVTexture() { return m_rov_texture; }
#ifdef PCSX2_DEVBUILD
void SetDebugName(std::string_view name) override;
+4 -1
View File
@@ -13,9 +13,10 @@
// Uploads/downloads need 32-byte alignment for AVX2.
static constexpr u32 PITCH_ALIGNMENT = 32;
GSTextureMTL::GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, Usage usage, Format format)
GSTextureMTL::GSTextureMTL(GSDeviceMTL* dev, MRCOwned<id<MTLTexture>> texture, MRCOwned<id<MTLTexture>> rov_texture, Usage usage, Format format)
: m_dev(dev)
, m_texture(std::move(texture))
, m_rov_texture(std::move(rov_texture))
{
m_usage = usage;
m_format = format;
@@ -141,6 +142,8 @@ void GSTextureMTL::SetDebugName(std::string_view name)
length:static_cast<NSUInteger>(name.length())
encoding:NSUTF8StringEncoding];
[m_texture setLabel:label];
if (m_rov_texture)
[m_rov_texture setLabel:[label stringByAppendingString:@".ROV"]];
}
m_debug_name = std::move(name);
+22 -6
View File
@@ -13,6 +13,7 @@ constant uint SHUFFLE_READWRITE = 3;
constant bool HAS_FBFETCH [[function_constant(GSMTLConstantIndex_FRAMEBUFFER_FETCH)]];
constant bool DEPTH_FEEDBACK [[function_constant(GSMTLConstantIndex_DEPTH_FEEDBACK)]];
constant bool ROV_NEEDS_R32 [[function_constant(GSMTLConstantIndex_ROV_NEEDS_R32)]];
constant bool FST [[function_constant(GSMTLConstantIndex_FST)]];
constant bool IIP [[function_constant(GSMTLConstantIndex_IIP)]];
constant bool VS_POINT_SIZE [[function_constant(GSMTLConstantIndex_VS_POINT_SIZE)]];
@@ -1708,7 +1709,8 @@ constant float ds_fbf = 0;
#endif
constant bool NEEDS_DS_TEX = SW_DEPTH && !DEPTH_FEEDBACK && !NEEDS_DS_FBF && PS_ROV_DEPTH == ROV_DEPTH::NONE;
constant bool NEEDS_DS_DEPTH = (SW_DEPTH && DEPTH_FEEDBACK || NEEDS_DS_FBF) && PS_ROV_DEPTH == ROV_DEPTH::NONE;
constant bool NEEDS_RT_ROV = PS_ROV_COLOR;
constant bool NEEDS_RT_ROV = PS_ROV_COLOR && !ROV_NEEDS_R32;
constant bool NEEDS_RT_U32 = PS_ROV_COLOR && ROV_NEEDS_R32;
constant bool NEEDS_DS_ROV = PS_ROV_DEPTH != ROV_DEPTH::NONE;
fragment MainPSOut ps_main(
@@ -1730,6 +1732,7 @@ fragment MainPSOut ps_main(
texture2d<float> ds_tex [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_TEX)]],
depth2d<float> ds_depth [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_DEPTH)]],
texture2d<float, access::read_write> rt_rov [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_ROV)]],
texture2d<uint, access::read_write> rt_u32 [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_U32)]],
texture2d<float, access::read_write> ds_rov [[texture(GSMTLTextureIndexDepthTarget), raster_order_group(1), function_constant(NEEDS_DS_ROV)]])
{
PSMain main(in, cb);
@@ -1765,7 +1768,10 @@ fragment MainPSOut ps_main(
{
if (PS_ROV_COLOR)
{
main.current_color = rt_rov.read(coord);
if (ROV_NEEDS_R32)
main.current_color = unpack_unorm4x8_to_float(rt_u32.read(coord).x);
else
main.current_color = rt_rov.read(coord);
}
else
{
@@ -1788,7 +1794,10 @@ fragment MainPSOut ps_main(
{
if (!PS_FBMASK)
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
rt_rov.write(out.c0, coord);
if (ROV_NEEDS_R32)
rt_u32.write(pack_float_to_unorm4x8(out.c0), coord);
else
rt_rov.write(out.c0, coord);
}
return out;
}
@@ -1802,7 +1811,8 @@ fragment void ps_main_rov_eft(
texture2d<float> tex [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_COLOR)]],
depth2d<float> depth [[texture(GSMTLTextureIndexTex), function_constant(PS_TEX_IS_DEPTH)]],
texture2d<float> palette [[texture(GSMTLTextureIndexPalette), function_constant(PS_HAS_PALETTE)]],
texture2d<float, access::read_write> rt_rov [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0)]])
texture2d<float, access::read_write> rt_rov [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_ROV)]],
texture2d<uint, access::read_write> rt_u32 [[texture(GSMTLTextureIndexRenderTarget), raster_order_group(0), function_constant(NEEDS_RT_U32)]])
{
PSMain main(in, cb);
main.tex_sampler = s;
@@ -1814,13 +1824,19 @@ fragment void ps_main_rov_eft(
main.palette = palette;
uint2 coord = uint2(in.p.xy);
main.current_color = rt_rov.read(coord);
if (ROV_NEEDS_R32)
main.current_color = unpack_unorm4x8_to_float(rt_u32.read(coord).x);
else
main.current_color = rt_rov.read(coord);
MainPSOut out = main.ps_main();
if (!main.color_discarded)
{
if (!PS_FBMASK)
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
rt_rov.write(out.c0, coord);
if (ROV_NEEDS_R32)
rt_u32.write(pack_float_to_unorm4x8(out.c0), coord);
else
rt_rov.write(out.c0, coord);
}
}