GS:MTL: Implement depth feedback

This commit is contained in:
TellowKrinkle
2026-04-24 01:19:27 +02:00
committed by lightningterror
parent 3737b1e06a
commit 8f23e31ae4
8 changed files with 231 additions and 51 deletions
+1 -6
View File
@@ -930,12 +930,7 @@ public:
Performance
};
enum class DepthFeedbackSupport : u8
{
None, // No support for depth feedback loops.
Depth, // Implement depth feedback loops directly on the depth buffer.
DepthAsRT, // Implement depth feedback loops by first converting depth to a color RT.
};
using DepthFeedbackSupport = GSShader::DepthFeedbackSupport;
// clang-format off
struct FeatureSupport
+16
View File
@@ -19,6 +19,13 @@ enum class VSExpand : uint8_t
TriangleAA1 = 5,
};
enum class DepthFeedbackSupport : uint32_t
{
None = 0, ///< No support for depth feedback loops.
Depth = 1, ///< Implement depth feedback loops directly on the depth buffer.
DepthAsRT = 2, ///< Implement depth feedback loops by first converting depth to a color RT.
};
enum class PS_ATST : uint32_t
{
NONE = 0,
@@ -39,6 +46,15 @@ enum class PS_AFAIL : uint32_t
RGB_ONLY_SW_Z = 5, ///< RGB only with software Z discard
};
// Identical to GS_ZTST
enum class ZTST : uint32_t
{
NEVER = 0,
ALWAYS = 1,
GEQUAL = 2,
GREATER = 3,
};
enum class PS_AA1 : uint32_t
{
NONE = 0, ///< No AA1
+11 -6
View File
@@ -40,14 +40,15 @@ struct PipelineSelectorExtrasMTL
GSDevice::BlendFactor dst_factor_alpha : 4;
GSDevice::BlendOp blend_op : 2;
bool blend_enable : 1;
bool has_depth : 1;
bool has_stencil : 1;
bool has_depth : 1;
bool has_stencil : 1;
bool has_rt1 : 1;
};
u32 fullkey;
};
PipelineSelectorExtrasMTL(): fullkey(0) {}
PipelineSelectorExtrasMTL(GSHWDrawConfig::BlendState blend, GSTexture* rt, GSHWDrawConfig::ColorMaskSelector cms, bool has_depth, bool has_stencil)
PipelineSelectorExtrasMTL(GSHWDrawConfig::BlendState blend, GSTexture* rt, GSHWDrawConfig::ColorMaskSelector cms, bool has_depth, bool has_stencil, bool has_rt1)
: fullkey(0)
{
this->rt = rt ? rt->GetFormat() : GSTexture::Format::Invalid;
@@ -65,6 +66,7 @@ struct PipelineSelectorExtrasMTL
this->blend_enable = blend.enable;
this->has_depth = has_depth;
this->has_stencil = has_stencil;
this->has_rt1 = has_rt1;
}
};
struct PipelineSelectorMTL
@@ -267,7 +269,7 @@ public:
std::unordered_map<PSSelector, MRCOwned<id<MTLFunction>>> m_hw_ps;
std::unordered_map<PipelineSelectorMTL, MRCOwned<id<MTLRenderPipelineState>>> m_hw_pipeline;
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[8];
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[16];
MRCOwned<id<MTLSamplerState>> m_sampler_hw[1 << 8];
@@ -287,7 +289,7 @@ public:
GSTexture* color_target = nullptr;
GSTexture* depth_target = nullptr;
GSTexture* stencil_target = nullptr;
GSTexture* tex[8] = {};
GSTexture* tex[GSMTLTextureIndexCount] = {};
void* vertex_buffer = nullptr;
void* name = nullptr;
struct Has
@@ -298,6 +300,7 @@ public:
bool blend_color : 1;
bool pipeline_sel : 1;
bool sampler : 1;
bool rt1_depth : 1;
} has = {};
DepthStencilSelector depth_sel = DepthStencilSelector::NoDepth();
// Clear line (Things below here are tracked by `has` and don't need to be cleared to reset)
@@ -347,6 +350,8 @@ public:
id<MTLCommandBuffer> GetRenderCmdBufWithoutCreate();
/// Get the spin fence if spinning is enabled.
id<MTLFence> GetSpinFence();
/// Get the texture to use as RT1 depth for the given depth texture
id<MTLTexture> GetRT1DepthTexture(GSTextureMTL* depth);
/// Called by command buffers when they finish
void DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffer);
/// Flush pending operations from all encoders to the GPU
@@ -356,7 +361,7 @@ public:
/// End current render pass without flushing
void EndRenderPass();
/// Begin a new render pass (may reuse existing)
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare);
void BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil = nullptr, MTLLoadAction stencil_load = MTLLoadActionDontCare, bool rt1 = false);
/// Call at the end of each frame
void FrameCompleted();
+67 -8
View File
@@ -245,6 +245,11 @@ id<MTLFence> GSDeviceMTL::GetSpinFence()
return m_spin_timer ? m_spin_fence : nil;
}
id<MTLTexture> GSDeviceMTL::GetRT1DepthTexture(GSTextureMTL* depth)
{
return static_cast<GSTextureMTL*>(m_ds_as_rt)->GetTexture();
}
void GSDeviceMTL::DrawCommandBufferFinished(u64 draw, id<MTLCommandBuffer> buffer)
{
// We can do the update non-atomically because we only ever update under the lock
@@ -388,14 +393,15 @@ void GSDeviceMTL::EndRenderPass()
}
}
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load)
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load, GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load, bool rt1)
{
GSTextureMTL* mc = static_cast<GSTextureMTL*>(color);
GSTextureMTL* md = static_cast<GSTextureMTL*>(depth);
GSTextureMTL* ms = static_cast<GSTextureMTL*>(stencil);
bool needs_new = color != m_current_render.color_target
|| depth != m_current_render.depth_target
|| stencil != m_current_render.stencil_target;
|| stencil != m_current_render.stencil_target
|| rt1 != m_current_render.has.rt1_depth;
GSVector4 color_clear;
float depth_clear;
// Depth and stencil might be the same, so do all invalidation checks before resetting invalidation
@@ -449,6 +455,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
if (mc) idx |= 1;
if (md) idx |= 2;
if (ms) idx |= 4;
if (rt1) idx |= 8;
MTLRenderPassDescriptor* desc = m_render_pass_desc[idx];
if (mc)
@@ -474,6 +481,11 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
pxAssert(stencil_load != MTLLoadActionClear);
desc.stencilAttachment.loadAction = stencil_load;
}
if (rt1)
{
pxAssert(md);
desc.colorAttachments[1].texture = GetRT1DepthTexture(md);
}
EndRenderPass();
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
@@ -485,6 +497,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
m_current_render.color_target = color;
m_current_render.depth_target = depth;
m_current_render.stencil_target = stencil;
m_current_render.has.rt1_depth |= rt1;
pxAssertRel(m_current_render.encoder, "Failed to create render encoder!");
}
@@ -849,6 +862,21 @@ static MRCOwned<id<MTLSamplerState>> CreateSampler(id<MTLDevice> dev, GSHWDrawCo
return ret;
}
static GSDevice::DepthFeedbackSupport getDepthFeedback(const GSMTLDevice& dev, bool fbfetch)
{
switch (GSConfig.DepthFeedbackMode)
{
case GSDepthFeedbackMode::None: return GSDevice::DepthFeedbackSupport::None;
case GSDepthFeedbackMode::Auto: return dev.features.preferred_depth_feedback;
case GSDepthFeedbackMode::DepthAsRT: return GSDevice::DepthFeedbackSupport::DepthAsRT;
case GSDepthFeedbackMode::Depth:
if (!fbfetch)
return GSDevice::DepthFeedbackSupport::Depth;
else
return GSDevice::DepthFeedbackSupport::DepthAsRT; // Depth + FBFetch not supported
}
}
bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
{ @autoreleasepool {
if (!GSDevice::Create(vsync_mode, allow_present_throttle))
@@ -935,12 +963,13 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
m_features.stencil_buffer = true;
m_features.cas_sharpening = true;
m_features.test_and_sample_depth = true;
m_features.depth_feedback = GSDevice::DepthFeedbackSupport::None;
m_features.depth_feedback = getDepthFeedback(m_dev, 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_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantB(m_fn_constants, m_features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
setFnConstantI(m_fn_constants, m_features.depth_feedback, GSMTLConstantIndex_DEPTH_FEEDBACK);
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
[m_draw_sync_fence setLabel:@"Draw Sync Fence"];
@@ -976,11 +1005,27 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
applyAttribute(m_hw_vertex, GSMTLAttributeIndexUV, MTLVertexFormatUShort2, offsetof(GSVertex, UV), GSMTLBufferIndexHWVertices);
applyAttribute(m_hw_vertex, GSMTLAttributeIndexF, MTLVertexFormatUChar4Normalized, offsetof(GSVertex, FOG), GSMTLBufferIndexHWVertices);
for (auto& desc : m_render_pass_desc)
for (size_t i = 0; i < std::size(m_render_pass_desc); i++)
{
desc = MRCTransfer([MTLRenderPassDescriptor new]);
const bool depth = i & 2;
const bool stencil = i & 4;
const bool rt1 = i & 8;
if (rt1 && m_features.depth_feedback != GSDevice::DepthFeedbackSupport::DepthAsRT)
continue;
if (rt1 && !depth)
continue;
if (stencil && m_features.framebuffer_fetch)
continue;
auto desc = MRCTransfer([MTLRenderPassDescriptor new]);
[[desc depthAttachment] setStoreAction:MTLStoreActionStore];
[[desc stencilAttachment] setStoreAction:MTLStoreActionStore];
if (rt1)
{
MTLRenderPassColorAttachmentDescriptor* color1 = [[desc colorAttachments] objectAtIndexedSubscript:1];
[color1 setStoreAction:MTLStoreActionDontCare];
[color1 setLoadAction:MTLLoadActionLoad];
}
m_render_pass_desc[i] = desc;
}
// Init samplers
@@ -1800,7 +1845,7 @@ static MTLBlendOperation ConvertBlendOp(GSDevice::BlendOp generic)
void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDrawConfig::PSSelector pssel, GSHWDrawConfig::BlendState blend, GSHWDrawConfig::ColorMaskSelector cms)
{
PipelineSelectorExtrasMTL extras(blend, m_current_render.color_target, cms, m_current_render.depth_target, m_current_render.stencil_target);
PipelineSelectorExtrasMTL extras(blend, m_current_render.color_target, cms, m_current_render.depth_target, m_current_render.stencil_target, m_current_render.has.rt1_depth);
PipelineSelectorMTL fullsel(vssel, pssel, extras);
if (m_current_render.has.pipeline_sel && fullsel == m_current_render.pipeline_sel)
return;
@@ -1842,6 +1887,7 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
setFnConstantI(m_fn_constants, pssel.date, GSMTLConstantIndex_PS_DATE);
setFnConstantI(m_fn_constants, pssel.atst, GSMTLConstantIndex_PS_ATST);
setFnConstantI(m_fn_constants, pssel.afail, GSMTLConstantIndex_PS_AFAIL);
setFnConstantI(m_fn_constants, pssel.ztst, GSMTLConstantIndex_PS_ZTST);
setFnConstantI(m_fn_constants, pssel.tfx, GSMTLConstantIndex_PS_TFX);
setFnConstantB(m_fn_constants, pssel.tcc, GSMTLConstantIndex_PS_TCC);
setFnConstantI(m_fn_constants, pssel.wms, GSMTLConstantIndex_PS_WMS);
@@ -1921,6 +1967,11 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
color.sourceAlphaBlendFactor = ConvertBlendFactor(extras.src_factor_alpha);
color.destinationAlphaBlendFactor = ConvertBlendFactor(extras.dst_factor_alpha);
}
if (extras.has_rt1)
{
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
[color1 setPixelFormat:MTLPixelFormatR32Float];
}
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%x.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
@@ -2267,7 +2318,9 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
return;
}
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad);
const bool feedback_depth = config.ps.IsFeedbackLoopDepth();
const bool rt1 = feedback_depth && m_features.depth_feedback == GSDevice::DepthFeedbackSupport::DepthAsRT;
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
FlushDebugEntries(mtlenc);
if (usesStencil(config.destination_alpha))
@@ -2275,6 +2328,12 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
MREInitHWDraw(config, allocation);
if (config.require_one_barrier || config.require_full_barrier)
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
if (feedback_depth && !m_features.framebuffer_fetch)
{
bool depth_as_rt = m_features.depth_feedback == GSDevice::DepthFeedbackSupport::DepthAsRT;
GSTexture* tex = depth_as_rt ? m_ds_as_rt : config.ds;
MRESetTexture(tex, GSMTLTextureIndexDepthTarget);
}
if (primid_tex)
MRESetTexture(primid_tex, GSMTLTextureIndexPrimIDs);
if (config.blend.constant_enable)
+10 -6
View File
@@ -11,6 +11,7 @@
#include "common/MRCHelpers.h"
#include "common/Pcsx2Types.h"
#include "GS/Renderers/Common/GSShaderEnums.h"
#include <Metal/Metal.h>
struct GSMTLDevice
@@ -23,15 +24,18 @@ struct GSMTLDevice
Metal23, ///< Metal 2.3 (macOS 11, iOS 14)
};
using DepthFeedbackSupport = GSShader::DepthFeedbackSupport;
struct Features
{
bool unified_memory;
bool texture_swizzle;
bool framebuffer_fetch;
bool primid;
bool slow_color_compression; ///< Color compression seems to slow down rt read on AMD
bool has_fast_half;
bool unified_memory : 1;
bool texture_swizzle : 1;
bool framebuffer_fetch : 1;
bool primid : 1;
bool slow_color_compression : 1; ///< Color compression seems to slow down rt read on AMD
bool has_fast_half : 1;
MetalVersion shader_version;
DepthFeedbackSupport preferred_depth_feedback : 8;
int max_texsize;
};
+39 -11
View File
@@ -164,21 +164,49 @@ GSMTLDevice::GSMTLDevice(MRCOwned<id<MTLDevice>> dev)
if (features.primid && !detectPrimIDSupport(dev, shaders))
features.primid = false;
if (!features.framebuffer_fetch && features.shader_version >= MetalVersion::Metal23)
features.preferred_depth_feedback = DepthFeedbackSupport::DepthAsRT;
NSString* name = [dev name];
if ([name containsString:@"Intel"])
{
switch (detectIntelGPU(dev, shaders))
if (!features.framebuffer_fetch && features.shader_version >= MetalVersion::Metal23)
{
case DetectionResult::HaswellOrNotIntel:
break;
case DetectionResult::Broadwell:
features.primid = false; // Broken
break;
case DetectionResult::Skylake:
features.primid = false; // Broken
features.framebuffer_fetch = true;
break;
switch (detectIntelGPU(dev, shaders))
{
case DetectionResult::HaswellOrNotIntel:
// Older Intel GPUs seem to be fine with depth feedback
features.preferred_depth_feedback = DepthFeedbackSupport::Depth;
break;
case DetectionResult::Broadwell:
features.primid = false; // Broken
break;
case DetectionResult::Skylake:
features.primid = false; // Broken
features.framebuffer_fetch = true;
break;
}
}
}
else if ([name containsString:@"AMD"])
{
// RDNA+ seems to work fine with depth feedback
if (@available(macOS 13, iOS 16, *))
if ([dev supportsFamily:MTLGPUFamilyMetal3])
features.preferred_depth_feedback = DepthFeedbackSupport::Depth;
}
else if ([name containsString:@"NVIDIA"])
{
// macOS only supports Kepler, which seems to work fine with depth feedback
features.preferred_depth_feedback = DepthFeedbackSupport::Depth;
}
else if ([name containsString:@"Apple"])
{
// No special settings
}
else
{
Console.Warning("Unrecognized GPU vendor %s", [name UTF8String]);
}
if (features.framebuffer_fetch && GSConfig.DisableFramebufferFetch)
{
@@ -20,6 +20,8 @@ enum GSMTLTextureIndex
GSMTLTextureIndexPalette,
GSMTLTextureIndexRenderTarget,
GSMTLTextureIndexPrimIDs,
GSMTLTextureIndexDepthTarget,
GSMTLTextureIndexCount,
};
struct GSMTLConvertPSUniform
@@ -154,6 +156,7 @@ enum GSMTLFnConstants
{
GSMTLConstantIndex_CAS_SHARPEN_ONLY,
GSMTLConstantIndex_FRAMEBUFFER_FETCH,
GSMTLConstantIndex_DEPTH_FEEDBACK,
GSMTLConstantIndex_FST,
GSMTLConstantIndex_IIP,
GSMTLConstantIndex_VS_POINT_SIZE,
@@ -168,6 +171,7 @@ enum GSMTLFnConstants
GSMTLConstantIndex_PS_DATE,
GSMTLConstantIndex_PS_ATST,
GSMTLConstantIndex_PS_AFAIL,
GSMTLConstantIndex_PS_ZTST,
GSMTLConstantIndex_PS_TFX,
GSMTLConstantIndex_PS_TCC,
GSMTLConstantIndex_PS_WMS,
+83 -14
View File
@@ -12,6 +12,7 @@ constant uint SHUFFLE_READ = 1;
constant uint SHUFFLE_READWRITE = 3;
constant bool HAS_FBFETCH [[function_constant(GSMTLConstantIndex_FRAMEBUFFER_FETCH)]];
constant uint DEPTH_FEEDBACK_RAW [[function_constant(GSMTLConstantIndex_DEPTH_FEEDBACK)]];
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)]];
@@ -26,6 +27,7 @@ constant bool PS_FOG [[function_constant(GSMTLConstantIndex_PS_FO
constant uint PS_DATE [[function_constant(GSMTLConstantIndex_PS_DATE)]];
constant uint PS_ATST_RAW [[function_constant(GSMTLConstantIndex_PS_ATST)]];
constant uint PS_AFAIL_RAW [[function_constant(GSMTLConstantIndex_PS_AFAIL)]];
constant uint PS_ZTST_RAW [[function_constant(GSMTLConstantIndex_PS_ZTST)]];
constant uint PS_TFX [[function_constant(GSMTLConstantIndex_PS_TFX)]];
constant bool PS_TCC [[function_constant(GSMTLConstantIndex_PS_TCC)]];
constant uint PS_WMS [[function_constant(GSMTLConstantIndex_PS_WMS)]];
@@ -72,12 +74,16 @@ constant bool PS_REGION_RECT [[function_constant(GSMTLConstantIndex_PS_RE
constant uint PS_SCANMSK [[function_constant(GSMTLConstantIndex_PS_SCANMSK)]];
constant uint PS_SW_ANISO [[function_constant(GSMTLConstantIndex_PS_SW_ANISO)]];
using GSShader::DepthFeedbackSupport;
using GSShader::VSExpand;
using AFAIL = GSShader::PS_AFAIL;
using ATST = GSShader::PS_ATST;
using GSShader::ZTST;
constant DepthFeedbackSupport DEPTH_FEEDBACK = static_cast<DepthFeedbackSupport>(DEPTH_FEEDBACK_RAW);
constant VSExpand VS_EXPAND_TYPE = static_cast<VSExpand>(VS_EXPAND_TYPE_RAW);
constant AFAIL PS_AFAIL = static_cast<AFAIL>(PS_AFAIL_RAW);
constant ATST PS_ATST = static_cast<ATST>(PS_ATST_RAW);
constant ZTST PS_ZTST = static_cast<ZTST>(PS_ZTST_RAW);
#if defined(__METAL_MACOS__) && __METAL_VERSION__ >= 220
#define PRIMID_SUPPORT 1
@@ -104,12 +110,18 @@ constant bool SW_BLEND = (PS_BLEND_A != PS_BLEND_B) || PS_BLEND_D;
constant bool SW_AD_TO_HW = (PS_BLEND_C == 1 && PS_A_MASKED);
constant bool NEEDS_RT_FOR_BLEND = (((PS_BLEND_A != PS_BLEND_B) && (PS_BLEND_A == 1 || PS_BLEND_B == 1 || PS_BLEND_C == 1)) || PS_BLEND_D == 1 || SW_AD_TO_HW);
constant bool NEEDS_RT_EARLY = PS_TEX_IS_FB || PS_DATE >= 5;
constant bool NEEDS_RT_FOR_AFAIL = PS_AFAIL == AFAIL::ZB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY;
constant bool NEEDS_RT_FOR_AFAIL = PS_AFAIL == AFAIL::ZB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY_SW_Z;
constant bool NEEDS_RT = NEEDS_RT_FOR_AFAIL || NEEDS_RT_EARLY || (!PS_PRIM_CHECKING_INIT && (PS_FBMASK || NEEDS_RT_FOR_BLEND));
constant bool NEEDS_DEPTH_FOR_AFAIL = PS_AFAIL == AFAIL::FB_ONLY || PS_AFAIL == AFAIL::RGB_ONLY_SW_Z;
constant bool NEEDS_DEPTH_FOR_ZTST = PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER;
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST;
constant bool PS_COLOR0 = !PS_NO_COLOR;
constant bool PS_COLOR1 = !PS_NO_COLOR1;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR;
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH;
constant bool PS_ZOUTPUT_LESS = PS_ZOUTPUT && !SW_DEPTH;
constant bool PS_ZOUTPUT_ANY = PS_ZOUTPUT && SW_DEPTH;
constant bool PS_ZOUTPUT_COLOR = PS_ZOUTPUT_ANY && DEPTH_FEEDBACK == DepthFeedbackSupport::DepthAsRT;
struct MainVSIn
{
@@ -145,7 +157,18 @@ struct MainPSOut
{
float4 c0 [[color(0), index(0), function_constant(PS_COLOR0)]];
float4 c1 [[color(0), index(1), function_constant(PS_COLOR1)]];
float depth [[depth(less), function_constant(PS_ZOUTPUT)]];
float depthColor [[color(1), function_constant(PS_ZOUTPUT_COLOR)]];
float depthLess [[depth(less), function_constant(PS_ZOUTPUT_LESS)]];
float depthAny [[depth(any), function_constant(PS_ZOUTPUT_ANY)]];
void setDepth(float depth)
{
if (PS_ZOUTPUT_LESS)
depthLess = depth;
if (PS_ZOUTPUT_ANY)
depthAny = depth;
if (PS_ZOUTPUT_COLOR)
depthColor = depth;
}
};
// MARK: - Vertex functions
@@ -300,6 +323,7 @@ struct PSMain
texture2d<float> prim_id_tex;
sampler tex_sampler;
float4 current_color;
float current_depth;
uint prim_id;
const thread MainPSIn& in;
constant GSMTLMainPSUniform& cb;
@@ -1189,6 +1213,17 @@ struct PSMain
MainPSOut ps_main()
{
MainPSOut out = {};
float input_z = in.p.z;
if (PS_ZFLOOR)
input_z = floor(input_z * 0x1p32) * 0x1p-32;
if (PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER)
{
if (PS_ZTST == ZTST::GEQUAL && input_z < current_depth)
discard_fragment();
if (PS_ZTST == ZTST::GREATER && input_z <= current_depth)
discard_fragment();
}
if (PS_SCANMSK & 2)
{
@@ -1339,18 +1374,25 @@ struct PSMain
{
out.c0.a = PS_RTA_CORRECTION ? C.a / 128.f : C.a / 255.f;
out.c0.rgb = PS_COLCLIP_HW ? float3(C.rgb / 65535.f) : C.rgb / 255.f;
if (PS_AFAIL == AFAIL::RGB_ONLY && !atst_pass)
out.c0.a = current_color.a;
}
if (PS_COLOR1)
out.c1 = alpha_blend;
float depth_value = PS_ZFLOOR ? (floor(in.p.z * exp2(32.0f)) * exp2(-32.0f)) : in.p.z;
if (PS_ZCLAMP)
out.depth = min(depth_value, cb.max_depth);
else if (PS_ZFLOOR)
out.depth = depth_value;
input_z = min(input_z, cb.max_depth);
if (!atst_pass)
{
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::RGB_ONLY)
out.c0.a = current_color.a;
else if (PS_AFAIL == AFAIL::ZB_ONLY)
out.c0 = current_color;
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::FB_ONLY)
input_z = current_depth;
}
out.setDepth(input_z);
return out;
}
@@ -1364,9 +1406,14 @@ fragment float4 fbfetch_test(float4 in [[color(0), raster_order_group(0)]])
constant bool NEEDS_RT_TEX = NEEDS_RT && !HAS_FBFETCH;
constant bool NEEDS_RT_FBF = NEEDS_RT && HAS_FBFETCH;
constant bool NEEDS_DS_FBF = SW_DEPTH && HAS_FBFETCH && DEPTH_FEEDBACK == DepthFeedbackSupport::DepthAsRT;
#else
constant bool NEEDS_RT_TEX = NEEDS_RT;
constant bool NEEDS_DS_FBF = false;
constant float ds_fbf = 0;
#endif
constant bool NEEDS_DS_TEX = SW_DEPTH && DEPTH_FEEDBACK == DepthFeedbackSupport::DepthAsRT && !NEEDS_DS_FBF;
constant bool NEEDS_DS_DEPTH = SW_DEPTH && DEPTH_FEEDBACK == DepthFeedbackSupport::Depth;
fragment MainPSOut ps_main(
MainPSIn in [[stage_in]],
@@ -1377,12 +1424,15 @@ fragment MainPSOut ps_main(
#endif
#if FBFETCH_SUPPORT
float4 rt_fbf [[color(0), raster_order_group(0), function_constant(NEEDS_RT_FBF)]],
float ds_fbf [[color(1), raster_order_group(1), function_constant(NEEDS_DS_FBF)]],
#endif
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> rt [[texture(GSMTLTextureIndexRenderTarget), function_constant(NEEDS_RT_TEX)]],
texture2d<float> primidtex [[texture(GSMTLTextureIndexPrimIDs), function_constant(PS_PRIM_CHECKING_READ)]])
texture2d<float> primidtex [[texture(GSMTLTextureIndexPrimIDs), function_constant(PS_PRIM_CHECKING_READ)]],
texture2d<float> ds_tex [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_TEX)]],
depth2d<float> ds_depth [[texture(GSMTLTextureIndexDepthTarget), function_constant(NEEDS_DS_DEPTH)]])
{
PSMain main(in, cb);
main.tex_sampler = s;
@@ -1399,12 +1449,31 @@ fragment MainPSOut ps_main(
main.prim_id = primid;
#endif
uint2 coord = uint2(in.p.xy);
if (SW_DEPTH)
{
switch (DEPTH_FEEDBACK)
{
case DepthFeedbackSupport::Depth:
main.current_depth = ds_depth.read(coord);
break;
case DepthFeedbackSupport::DepthAsRT:
main.current_depth = HAS_FBFETCH ? ds_fbf : ds_tex.read(coord).x;
break;
case DepthFeedbackSupport::None:
// Should never happen
main.current_depth = 0;
break;
}
}
if (NEEDS_RT)
{
#if FBFETCH_SUPPORT
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(uint2(in.p.xy));
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
#else
main.current_color = rt.read(uint2(in.p.xy));
main.current_color = rt.read(coord);
#endif
}
else