mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-11 01:34:17 +02:00
GS:MTL: Implement ROV
This commit is contained in:
committed by
lightningterror
parent
1a13f9707b
commit
2821986fb2
@@ -899,7 +899,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
|
||||
__fi bool HasDepthROV() const
|
||||
{
|
||||
return rov_depth == PS_ROV_DEPTH::READ_ONLY || rov_depth == PS_ROV_DEPTH::READ_WRITE;
|
||||
return rov_depth != PS_ROV_DEPTH::NONE;
|
||||
}
|
||||
|
||||
__fi bool HasDepthROVWrite() const
|
||||
|
||||
@@ -279,6 +279,7 @@ public:
|
||||
std::unordered_map<PipelineSelectorMTL, MRCOwned<id<MTLRenderPipelineState>>> m_hw_pipeline;
|
||||
|
||||
MRCOwned<MTLRenderPassDescriptor*> m_render_pass_desc[16];
|
||||
MRCOwned<MTLRenderPassDescriptor*> m_full_rov_render_pass_desc;
|
||||
|
||||
MRCOwned<id<MTLSamplerState>> m_sampler_hw[1 << 8];
|
||||
|
||||
@@ -316,12 +317,14 @@ public:
|
||||
// Clear line (Things below here are tracked by `has` and don't need to be cleared to reset)
|
||||
SamplerSelector sampler_sel;
|
||||
u8 blend_color;
|
||||
struct { u32 w, h; } full_rov_size;
|
||||
GSVector4i scissor;
|
||||
PipelineSelectorMTL pipeline_sel;
|
||||
GSHWDrawConfig::VSConstantBuffer cb_vs;
|
||||
GSHWDrawConfig::PSConstantBuffer cb_ps;
|
||||
MainRenderEncoder(const MainRenderEncoder&) = delete;
|
||||
MainRenderEncoder() = default;
|
||||
bool is_full_rov() const { return encoder && !color_target && !depth_target && !stencil_target; }
|
||||
} m_current_render;
|
||||
MRCOwned<id<MTLCommandBuffer>> m_texture_upload_cmdbuf;
|
||||
MRCOwned<id<MTLBlitCommandEncoder>> m_texture_upload_encoder;
|
||||
@@ -330,6 +333,8 @@ public:
|
||||
MRCOwned<id<MTLBlitCommandEncoder>> m_vertex_upload_encoder;
|
||||
id<MTLTexture> m_ds_as_rt_texture = nil;
|
||||
GSTexture* m_ds_as_rt_gstexture = nullptr;
|
||||
MRCOwned<id<MTLTexture>> m_rov_dummy_texture;
|
||||
struct { u32 w, h; } m_rov_dummy_texture_size = {};
|
||||
|
||||
struct DebugEntry
|
||||
{
|
||||
@@ -372,8 +377,12 @@ public:
|
||||
void FlushEncodersForReadback();
|
||||
/// End current render pass without flushing
|
||||
void EndRenderPass();
|
||||
/// Prepare to begin a new render pass
|
||||
void PrepareBeginRenderPass();
|
||||
/// 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, bool rt1 = false);
|
||||
/// Begin a new full-ROV render pass (may reuse existing)
|
||||
void BeginFullROV(NSString* name, uint32_t width, uint32_t height);
|
||||
/// Call at the end of each frame
|
||||
void FrameCompleted();
|
||||
|
||||
@@ -455,6 +464,7 @@ public:
|
||||
// MARK: Render HW
|
||||
|
||||
void SetupDestinationAlpha(GSTexture* rt, GSTexture* ds, const GSVector4i& r, SetDATM datm);
|
||||
void PrepareROVTexture(GSTexture** ptex);
|
||||
void RenderHW(GSHWDrawConfig& config) override;
|
||||
void SendHWDraw(GSHWDrawConfig& config, id<MTLRenderCommandEncoder> enc, id<MTLBuffer> buffer, size_t off,
|
||||
bool one_barrier, bool full_barrier);
|
||||
|
||||
@@ -419,6 +419,19 @@ static GSVector4 GetRTLoadInfo(GSTextureMTL* tex, MTLLoadAction* load_action)
|
||||
return {};
|
||||
};
|
||||
|
||||
void GSDeviceMTL::PrepareBeginRenderPass()
|
||||
{
|
||||
m_encoders_in_current_cmdbuf++;
|
||||
|
||||
if (m_late_texture_upload_encoder)
|
||||
{
|
||||
[m_late_texture_upload_encoder endEncoding];
|
||||
m_late_texture_upload_encoder = nullptr;
|
||||
}
|
||||
|
||||
EndRenderPass();
|
||||
}
|
||||
|
||||
void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadAction color_load,
|
||||
GSTexture* depth, MTLLoadAction depth_load, GSTexture* stencil, MTLLoadAction stencil_load, bool rt1)
|
||||
{
|
||||
@@ -455,14 +468,6 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
|
||||
return;
|
||||
}
|
||||
|
||||
m_encoders_in_current_cmdbuf++;
|
||||
|
||||
if (m_late_texture_upload_encoder)
|
||||
{
|
||||
[m_late_texture_upload_encoder endEncoding];
|
||||
m_late_texture_upload_encoder = nullptr;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
if (mc) idx |= 1;
|
||||
if (md) idx |= 2;
|
||||
@@ -502,7 +507,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
|
||||
color1.clearColor = MTLClearColorMake(depth_load == MTLLoadActionClear ? depth_clear.x : -1, 0, 0, 0);
|
||||
}
|
||||
|
||||
EndRenderPass();
|
||||
PrepareBeginRenderPass();
|
||||
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
|
||||
m_current_render.name = (__bridge void*)name;
|
||||
[m_current_render.encoder setLabel:name];
|
||||
@@ -516,6 +521,61 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
|
||||
pxAssertRel(m_current_render.encoder, "Failed to create render encoder!");
|
||||
}
|
||||
|
||||
void GSDeviceMTL::BeginFullROV(NSString* name, uint32_t width, uint32_t height)
|
||||
{
|
||||
bool needs_new = !m_current_render.is_full_rov()
|
||||
|| width > m_current_render.full_rov_size.w
|
||||
|| height > m_current_render.full_rov_size.h;
|
||||
|
||||
if (!needs_new)
|
||||
{
|
||||
if (m_current_render.name != (__bridge void*)name)
|
||||
{
|
||||
m_current_render.name = (__bridge void*)name;
|
||||
[m_current_render.encoder setLabel:name];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_current_render.is_full_rov())
|
||||
{
|
||||
// Render was too small. Don't let it shrink in one dimension while growing in the other.
|
||||
width = std::max(width, m_current_render.full_rov_size.w);
|
||||
height = std::max(height, m_current_render.full_rov_size.h);
|
||||
}
|
||||
|
||||
m_current_render.full_rov_size.w = width;
|
||||
m_current_render.full_rov_size.h = height;
|
||||
|
||||
MTLRenderPassDescriptor* desc = m_full_rov_render_pass_desc;
|
||||
[desc setRenderTargetWidth:width];
|
||||
[desc setRenderTargetHeight:height];
|
||||
|
||||
if (m_dev.features.rov_requires_rt && (width > m_rov_dummy_texture_size.w || height > m_rov_dummy_texture_size.h))
|
||||
{
|
||||
m_rov_dummy_texture_size.w = std::max(m_rov_dummy_texture_size.w, width);
|
||||
m_rov_dummy_texture_size.h = std::max(m_rov_dummy_texture_size.h, height);
|
||||
MTLTextureDescriptor* tdesc = [MTLTextureDescriptor
|
||||
texture2DDescriptorWithPixelFormat:MTLPixelFormatR8Unorm
|
||||
width:m_rov_dummy_texture_size.w
|
||||
height:m_rov_dummy_texture_size.h
|
||||
mipmapped:NO];
|
||||
[tdesc setUsage:MTLTextureUsageRenderTarget];
|
||||
[tdesc setStorageMode:MTLStorageModePrivate];
|
||||
id<MTLTexture> tex = m_rov_dummy_texture = MRCTransfer([m_dev.dev newTextureWithDescriptor:tdesc]);
|
||||
[tex setLabel:@"ROV Dummy Texture"];
|
||||
[[[desc colorAttachments] objectAtIndexedSubscript:0] setTexture:tex];
|
||||
}
|
||||
|
||||
PrepareBeginRenderPass();
|
||||
m_current_render.encoder = MRCRetain([GetRenderCmdBuf() renderCommandEncoderWithDescriptor:desc]);
|
||||
m_current_render.name = (__bridge void*)name;
|
||||
[m_current_render.encoder setLabel:name];
|
||||
if (!m_dev.features.unified_memory)
|
||||
[m_current_render.encoder waitForFence:m_draw_sync_fence
|
||||
beforeStages:MTLRenderStageVertex];
|
||||
}
|
||||
|
||||
void GSDeviceMTL::FrameCompleted()
|
||||
{
|
||||
if (m_spin_timer)
|
||||
@@ -999,6 +1059,7 @@ 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_max_texture_size = m_dev.features.max_texsize;
|
||||
|
||||
// Init metal stuff
|
||||
@@ -1062,6 +1123,17 @@ bool GSDeviceMTL::Create(GSVSyncMode vsync_mode, bool allow_present_throttle)
|
||||
}
|
||||
m_render_pass_desc[i] = desc;
|
||||
}
|
||||
if (m_features.rov)
|
||||
{
|
||||
m_full_rov_render_pass_desc = MRCTransfer([MTLRenderPassDescriptor new]);
|
||||
[m_full_rov_render_pass_desc setDefaultRasterSampleCount:1];
|
||||
if (m_dev.features.rov_requires_rt)
|
||||
{
|
||||
MTLRenderPassColorAttachmentDescriptor* color0 = [[m_full_rov_render_pass_desc colorAttachments] objectAtIndexedSubscript:0];
|
||||
[color0 setLoadAction:MTLLoadActionDontCare];
|
||||
[color0 setStoreAction:MTLStoreActionDontCare];
|
||||
}
|
||||
}
|
||||
|
||||
// Init samplers
|
||||
m_sampler_hw[SamplerSelector::Linear().key] = CreateSampler(m_dev.dev, SamplerSelector::Linear());
|
||||
@@ -1978,7 +2050,10 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
|
||||
setFnConstantI(m_fn_constants, pssel.aa1, GSMTLConstantIndex_PS_AA1);
|
||||
setFnConstantB(m_fn_constants, pssel.abe, GSMTLConstantIndex_PS_ABE);
|
||||
setFnConstantI(m_fn_constants, pssel.sw_aniso, GSMTLConstantIndex_PS_SW_ANISO);
|
||||
auto newps = LoadShader(@"ps_main");
|
||||
setFnConstantB(m_fn_constants, pssel.rov_color, GSMTLConstantIndex_PS_ROV_COLOR);
|
||||
setFnConstantI(m_fn_constants, pssel.rov_depth, GSMTLConstantIndex_PS_ROV_DEPTH);
|
||||
bool eft = pssel.HasColorROV() && !pssel.HasDepthROV() && !pssel.HasDepthOutput();
|
||||
auto newps = LoadShader(eft ? @"ps_main_rov_eft" : @"ps_main");
|
||||
ps = newps;
|
||||
m_hw_ps.insert(std::make_pair(pssel, std::move(newps)));
|
||||
}
|
||||
@@ -2017,6 +2092,12 @@ void GSDeviceMTL::MRESetHWPipelineState(GSHWDrawConfig::VSSelector vssel, GSHWDr
|
||||
MTLRenderPipelineColorAttachmentDescriptor* color1 = [[pdesc colorAttachments] objectAtIndexedSubscript:1];
|
||||
[color1 setPixelFormat:MTLPixelFormatR32Float];
|
||||
}
|
||||
if (m_dev.features.rov_requires_rt && extras.rt == GSTexture::Format::Invalid && !extras.has_depth && !extras.has_stencil)
|
||||
{
|
||||
// Dummy texture
|
||||
[color setPixelFormat:MTLPixelFormatR8Unorm];
|
||||
[color setWriteMask:MTLColorWriteMaskNone];
|
||||
}
|
||||
NSString* pname = [NSString stringWithFormat:@"HW Render %x.%llx.%llx.%x", vssel_mtl.key, pssel.key_hi, pssel.key_lo, extras.fullkey];
|
||||
auto pipeline = MakePipeline(pdesc, vs, ps, pname);
|
||||
|
||||
@@ -2108,6 +2189,7 @@ void GSDeviceMTL::MRESetScissor(const GSVector4i& scissor)
|
||||
|
||||
void GSDeviceMTL::MREClearScissor()
|
||||
{
|
||||
assert(!m_current_render.is_full_rov());
|
||||
if (!m_current_render.has.scissor)
|
||||
return;
|
||||
m_current_render.has.scissor = false;
|
||||
@@ -2235,6 +2317,15 @@ void GSDeviceMTL::MREInitHWDraw(GSHWDrawConfig& config, const Map& verts)
|
||||
MRESetVSIndices(verts.gpu_buffer, verts.gpu_offset + config.nverts * sizeof(*config.verts));
|
||||
}
|
||||
|
||||
__fi void GSDeviceMTL::PrepareROVTexture(GSTexture** ptex)
|
||||
{
|
||||
GSTextureMTL* tex = static_cast<GSTextureMTL*>(*ptex);
|
||||
FlushClears(tex);
|
||||
tex->m_last_read = m_current_draw;
|
||||
tex->m_last_write = m_current_draw;
|
||||
*ptex = nullptr;
|
||||
}
|
||||
|
||||
void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
{ @autoreleasepool {
|
||||
if (config.tex && (config.ds == config.tex || config.rt == config.tex))
|
||||
@@ -2371,7 +2462,7 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
// 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)
|
||||
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)
|
||||
config.ds = m_current_render.depth_target;
|
||||
if (!rt && config.ds == m_current_render.depth_target && m_current_render.color_target != config.tex)
|
||||
rt = m_current_render.color_target;
|
||||
@@ -2386,16 +2477,28 @@ void GSDeviceMTL::RenderHW(GSHWDrawConfig& config)
|
||||
}
|
||||
|
||||
const bool feedback_depth = config.ps.IsFeedbackLoopDepth();
|
||||
const bool rt1 = feedback_depth && !m_features.depth_feedback;
|
||||
BeginRenderPass(@"RenderHW", rt, MTLLoadActionLoad, config.ds, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
|
||||
const bool rt1 = feedback_depth && !m_features.depth_feedback && !config.ps.HasDepthROV();
|
||||
GSTexture* rt_bind = rt;
|
||||
GSTexture* ds_bind = config.ds;
|
||||
GSTexture* rt_size = rt_bind ? rt_bind : ds_bind;
|
||||
if (config.ps.HasColorROV() && rt_bind) PrepareROVTexture(&rt_bind);
|
||||
if (config.ps.HasDepthROV() && ds_bind) PrepareROVTexture(&ds_bind);
|
||||
if (!rt_bind && !ds_bind && !stencil)
|
||||
BeginFullROV(@"RenderHWROV", rt_size->GetWidth(), rt_size->GetHeight());
|
||||
else
|
||||
BeginRenderPass(@"RenderHW", rt_bind, MTLLoadActionLoad, ds_bind, MTLLoadActionLoad, stencil, MTLLoadActionLoad, rt1);
|
||||
id<MTLRenderCommandEncoder> mtlenc = m_current_render.encoder;
|
||||
FlushDebugEntries(mtlenc);
|
||||
if (usesStencil(config.destination_alpha))
|
||||
[mtlenc setStencilReferenceValue:1];
|
||||
MREInitHWDraw(config, allocation);
|
||||
if (config.require_one_barrier || config.require_full_barrier)
|
||||
if (config.require_one_barrier || config.require_full_barrier || config.ps.HasColorROV())
|
||||
MRESetTexture(rt, GSMTLTextureIndexRenderTarget);
|
||||
if (feedback_depth)
|
||||
if (config.ps.HasDepthROV())
|
||||
{
|
||||
MRESetTexture(config.ds, GSMTLTextureIndexDepthTarget);
|
||||
}
|
||||
else if (feedback_depth)
|
||||
{
|
||||
GSTexture* tex = !m_features.depth_feedback && !m_features.framebuffer_fetch ? m_ds_as_rt : config.ds;
|
||||
MRESetTexture(tex, GSMTLTextureIndexDepthTarget);
|
||||
|
||||
@@ -227,4 +227,6 @@ enum GSMTLFnConstants
|
||||
GSMTLConstantIndex_PS_AA1,
|
||||
GSMTLConstantIndex_PS_ABE,
|
||||
GSMTLConstantIndex_PS_SW_ANISO,
|
||||
GSMTLConstantIndex_PS_ROV_COLOR,
|
||||
GSMTLConstantIndex_PS_ROV_DEPTH,
|
||||
};
|
||||
|
||||
@@ -75,17 +75,21 @@ constant uint PS_SCANMSK [[function_constant(GSMTLConstantIndex_PS_SC
|
||||
constant uint PS_AA1_RAW [[function_constant(GSMTLConstantIndex_PS_AA1)]];
|
||||
constant bool PS_ABE [[function_constant(GSMTLConstantIndex_PS_ABE)]];
|
||||
constant uint PS_SW_ANISO [[function_constant(GSMTLConstantIndex_PS_SW_ANISO)]];
|
||||
constant bool PS_ROV_COLOR [[function_constant(GSMTLConstantIndex_PS_ROV_COLOR)]];
|
||||
constant uint PS_ROV_DEPTH_RAW [[function_constant(GSMTLConstantIndex_PS_ROV_DEPTH)]];
|
||||
|
||||
using GSShader::VSExpand;
|
||||
using AFAIL = GSShader::PS_AFAIL;
|
||||
using ATST = GSShader::PS_ATST;
|
||||
using GSShader::ZTST;
|
||||
using AA1 = GSShader::PS_AA1;
|
||||
using ROV_DEPTH = GSShader::PS_ROV_DEPTH;
|
||||
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);
|
||||
constant AA1 PS_AA1 = static_cast<AA1>(PS_AA1_RAW);
|
||||
constant ROV_DEPTH PS_ROV_DEPTH = static_cast<ROV_DEPTH>(PS_ROV_DEPTH_RAW);
|
||||
|
||||
#if defined(__METAL_MACOS__) && __METAL_VERSION__ >= 220
|
||||
#define PRIMID_SUPPORT 1
|
||||
@@ -119,9 +123,9 @@ constant bool NEEDS_DEPTH_FOR_ZTST = PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST
|
||||
constant bool NEEDS_DEPTH_FOR_AA1 = PS_AA1 == AA1::TRIANGLE_SW_Z;
|
||||
constant bool SW_DEPTH = NEEDS_DEPTH_FOR_AFAIL || NEEDS_DEPTH_FOR_ZTST || NEEDS_DEPTH_FOR_AA1;
|
||||
|
||||
constant bool PS_COLOR0 = !PS_NO_COLOR;
|
||||
constant bool PS_COLOR1 = !PS_NO_COLOR1;
|
||||
constant bool PS_ZOUTPUT = PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH;
|
||||
constant bool PS_OUTPUT_COLOR0 = !PS_NO_COLOR && !PS_ROV_COLOR;
|
||||
constant bool PS_OUTPUT_COLOR1 = !PS_NO_COLOR1 && !PS_ROV_COLOR;
|
||||
constant bool PS_ZOUTPUT = (PS_ZCLAMP || PS_ZFLOOR || SW_DEPTH) && PS_ROV_DEPTH == ROV_DEPTH::NONE;
|
||||
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;
|
||||
@@ -165,21 +169,32 @@ struct MainPSIn
|
||||
uint interior [[function_constant(PS_INTERIOR)]];
|
||||
};
|
||||
|
||||
struct MainResult
|
||||
{
|
||||
float4 c0;
|
||||
float4 c1;
|
||||
float depth;
|
||||
};
|
||||
|
||||
struct MainPSOut
|
||||
{
|
||||
float4 c0 [[color(0), index(0), function_constant(PS_COLOR0)]];
|
||||
float4 c1 [[color(0), index(1), function_constant(PS_COLOR1)]];
|
||||
float4 c0 [[color(0), index(0), function_constant(PS_OUTPUT_COLOR0)]];
|
||||
float4 c1 [[color(0), index(1), function_constant(PS_OUTPUT_COLOR1)]];
|
||||
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)
|
||||
MainPSOut(MainResult res)
|
||||
{
|
||||
if (PS_OUTPUT_COLOR0)
|
||||
c0 = res.c0;
|
||||
if (PS_OUTPUT_COLOR1)
|
||||
c1 = res.c1;
|
||||
if (PS_ZOUTPUT_LESS)
|
||||
depthLess = depth;
|
||||
depthLess = res.depth;
|
||||
if (PS_ZOUTPUT_ANY)
|
||||
depthAny = depth;
|
||||
depthAny = res.depth;
|
||||
if (PS_ZOUTPUT_COLOR)
|
||||
depthColor = depth;
|
||||
depthColor = res.depth;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -564,11 +579,37 @@ struct PSMain
|
||||
float4 current_color;
|
||||
float current_depth;
|
||||
uint prim_id;
|
||||
bool color_discarded = false;
|
||||
bool depth_discarded = false;
|
||||
const thread MainPSIn& in;
|
||||
constant GSMTLMainPSUniform& cb;
|
||||
|
||||
PSMain(const thread MainPSIn& in, constant GSMTLMainPSUniform& cb): in(in), cb(cb) {}
|
||||
|
||||
void discard()
|
||||
{
|
||||
if (PS_ROV_COLOR || PS_ROV_DEPTH != ROV_DEPTH::NONE)
|
||||
color_discarded = depth_discarded = true;
|
||||
else
|
||||
discard_fragment();
|
||||
}
|
||||
|
||||
void discard_color(thread float4& output)
|
||||
{
|
||||
if (PS_ROV_COLOR)
|
||||
color_discarded = true;
|
||||
else
|
||||
output = current_color;
|
||||
}
|
||||
|
||||
void discard_depth(thread float& output)
|
||||
{
|
||||
if (PS_ROV_DEPTH == ROV_DEPTH::READ_WRITE)
|
||||
depth_discarded = true;
|
||||
else
|
||||
output = current_depth;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
float4 sample_tex(Args... args)
|
||||
{
|
||||
@@ -1454,9 +1495,9 @@ struct PSMain
|
||||
}
|
||||
}
|
||||
|
||||
MainPSOut ps_main()
|
||||
MainResult ps_main()
|
||||
{
|
||||
MainPSOut out = {};
|
||||
MainResult out = {};
|
||||
float input_z = in.p.z;
|
||||
if (PS_ZFLOOR)
|
||||
input_z = floor(input_z * 0x1p32) * 0x1p-32;
|
||||
@@ -1464,15 +1505,15 @@ struct PSMain
|
||||
if (PS_ZTST == ZTST::GEQUAL || PS_ZTST == ZTST::GREATER)
|
||||
{
|
||||
if (PS_ZTST == ZTST::GEQUAL && input_z < current_depth)
|
||||
discard_fragment();
|
||||
discard();
|
||||
if (PS_ZTST == ZTST::GREATER && input_z <= current_depth)
|
||||
discard_fragment();
|
||||
discard();
|
||||
}
|
||||
|
||||
if (PS_SCANMSK & 2)
|
||||
{
|
||||
if ((uint(in.p.y) & 1) == (PS_SCANMSK & 1))
|
||||
discard_fragment();
|
||||
discard();
|
||||
}
|
||||
|
||||
if (PS_DATE >= 5)
|
||||
@@ -1482,7 +1523,7 @@ struct PSMain
|
||||
bool bad = PS_RTA_CORRECTION ? ((PS_DATE & 3) == 1 ? (rt_a > (254.5f / 255.f)) : (rt_a < (254.5f / 255.f))) : ((PS_DATE & 3) == 1 ? (rt_a > 0.5) : (rt_a < 0.5));
|
||||
|
||||
if (bad)
|
||||
discard_fragment();
|
||||
discard();
|
||||
}
|
||||
|
||||
if (PS_DATE == 3)
|
||||
@@ -1491,7 +1532,7 @@ struct PSMain
|
||||
// Note prim_id == stencil_ceil will be the primitive that will update
|
||||
// the bad alpha value so we must keep it.
|
||||
if (float(prim_id) > stencil_ceil)
|
||||
discard_fragment();
|
||||
discard();
|
||||
}
|
||||
|
||||
float4 C = ps_color();
|
||||
@@ -1500,9 +1541,9 @@ struct PSMain
|
||||
|
||||
if (PS_AA1 != AA1::NONE)
|
||||
{
|
||||
float cov = PS_AA1 == AA1::LINE ?
|
||||
saturate(cb.line_cov_scale * (1.f - abs(in.inv_cov))) : // Blur only outer part of the line by scaling coverage.
|
||||
saturate(1.f - abs(in.inv_cov));
|
||||
float cov = PS_AA1 == AA1::LINE
|
||||
? saturate(cb.line_cov_scale * (1.f - abs(in.inv_cov))) // Blur only outer part of the line by scaling coverage.
|
||||
: saturate(1.f - abs(in.inv_cov));
|
||||
if (!PS_ABE || floor(C.a) == 128.f) // The coverage is only used if the fragment alpha is 128.
|
||||
C.a = 128.f * cov;
|
||||
}
|
||||
@@ -1514,7 +1555,7 @@ struct PSMain
|
||||
|
||||
bool atst_pass = atst(C);
|
||||
if (PS_AFAIL == AFAIL::KEEP && !atst_pass)
|
||||
discard_fragment();
|
||||
discard();
|
||||
|
||||
float4 alpha_blend = float4(0.f);
|
||||
if (SW_AD_TO_HW)
|
||||
@@ -1620,32 +1661,32 @@ struct PSMain
|
||||
if (PS_AFAIL == AFAIL::RGB_ONLY_DSB)
|
||||
alpha_blend.a = float(atst_pass);
|
||||
|
||||
if (PS_COLOR0)
|
||||
if (!PS_NO_COLOR)
|
||||
{
|
||||
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_COLOR1)
|
||||
if (!PS_NO_COLOR1)
|
||||
out.c1 = alpha_blend;
|
||||
|
||||
if (PS_ZCLAMP)
|
||||
input_z = min(input_z, cb.max_depth);
|
||||
|
||||
if (PS_AA1 == AA1::TRIANGLE_SW_Z && !in.interior)
|
||||
input_z = current_depth; // No depth update for triangle edges.
|
||||
discard_depth(input_z); // No depth update for triangle edges.
|
||||
|
||||
if (!atst_pass)
|
||||
{
|
||||
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::RGB_ONLY)
|
||||
out.c0.a = current_color.a;
|
||||
out.c0.a = current_color.a; // discard alpha
|
||||
else if (PS_AFAIL == AFAIL::ZB_ONLY)
|
||||
out.c0 = current_color;
|
||||
discard_color(out.c0);
|
||||
|
||||
if (PS_AFAIL == AFAIL::RGB_ONLY_SW_Z || PS_AFAIL == AFAIL::FB_ONLY)
|
||||
input_z = current_depth;
|
||||
discard_depth(input_z);
|
||||
}
|
||||
|
||||
out.setDepth(input_z);
|
||||
out.depth = input_z;
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -1657,16 +1698,18 @@ fragment float4 fbfetch_test(float4 in [[color(0), raster_order_group(0)]])
|
||||
return in * 2;
|
||||
}
|
||||
|
||||
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;
|
||||
constant bool NEEDS_RT_TEX = NEEDS_RT && !HAS_FBFETCH && !PS_ROV_COLOR;
|
||||
constant bool NEEDS_RT_FBF = NEEDS_RT && HAS_FBFETCH && !PS_ROV_COLOR;
|
||||
constant bool NEEDS_DS_FBF = SW_DEPTH && HAS_FBFETCH && !DEPTH_FEEDBACK && PS_ROV_DEPTH == ROV_DEPTH::NONE;
|
||||
#else
|
||||
constant bool NEEDS_RT_TEX = NEEDS_RT;
|
||||
constant bool NEEDS_RT_TEX = NEEDS_RT && !PS_ROV_COLOR;
|
||||
constant bool NEEDS_DS_FBF = false;
|
||||
constant float ds_fbf = 0;
|
||||
#endif
|
||||
constant bool NEEDS_DS_TEX = SW_DEPTH && !DEPTH_FEEDBACK && !NEEDS_DS_FBF;
|
||||
constant bool NEEDS_DS_DEPTH = SW_DEPTH && DEPTH_FEEDBACK || NEEDS_DS_FBF;
|
||||
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_DS_ROV = PS_ROV_DEPTH != ROV_DEPTH::NONE;
|
||||
|
||||
fragment MainPSOut ps_main(
|
||||
MainPSIn in [[stage_in]],
|
||||
@@ -1685,7 +1728,9 @@ fragment MainPSOut ps_main(
|
||||
texture2d<float> rt [[texture(GSMTLTextureIndexRenderTarget), function_constant(NEEDS_RT_TEX)]],
|
||||
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)]])
|
||||
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<float, access::read_write> ds_rov [[texture(GSMTLTextureIndexDepthTarget), raster_order_group(1), function_constant(NEEDS_DS_ROV)]])
|
||||
{
|
||||
PSMain main(in, cb);
|
||||
main.tex_sampler = s;
|
||||
@@ -1706,7 +1751,9 @@ fragment MainPSOut ps_main(
|
||||
|
||||
if (SW_DEPTH)
|
||||
{
|
||||
if (DEPTH_FEEDBACK)
|
||||
if (PS_ROV_DEPTH != ROV_DEPTH::NONE)
|
||||
main.current_depth = ds_rov.read(coord).x;
|
||||
else if (DEPTH_FEEDBACK)
|
||||
main.current_depth = ds_depth.read(coord);
|
||||
else if (NEEDS_DS_FBF)
|
||||
main.current_depth = ds_fbf < 0 ? ds_depth.read(coord) : ds_fbf;
|
||||
@@ -1714,20 +1761,67 @@ fragment MainPSOut ps_main(
|
||||
main.current_depth = ds_tex.read(coord).x;
|
||||
}
|
||||
|
||||
if (NEEDS_RT)
|
||||
if (NEEDS_RT || (PS_ROV_COLOR && any(cb.fbmask == 0xff)))
|
||||
{
|
||||
if (PS_ROV_COLOR)
|
||||
{
|
||||
main.current_color = rt_rov.read(coord);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if FBFETCH_SUPPORT
|
||||
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
|
||||
main.current_color = HAS_FBFETCH ? rt_fbf : rt.read(coord);
|
||||
#else
|
||||
main.current_color = rt.read(coord);
|
||||
main.current_color = rt.read(coord);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
main.current_color = 0;
|
||||
}
|
||||
|
||||
return main.ps_main();
|
||||
MainResult out = main.ps_main();
|
||||
if (PS_ROV_DEPTH == ROV_DEPTH::READ_WRITE && !main.depth_discarded)
|
||||
ds_rov.write(out.depth, coord);
|
||||
if (PS_ROV_COLOR && !main.color_discarded)
|
||||
{
|
||||
if (!PS_FBMASK)
|
||||
out.c0 = select(out.c0, main.current_color, cb.fbmask == 0xff);
|
||||
rt_rov.write(out.c0, coord);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Metal doesn't let you toggle eft with function constants so we need a separate function for it
|
||||
[[early_fragment_tests]]
|
||||
fragment void ps_main_rov_eft(
|
||||
MainPSIn in [[stage_in]],
|
||||
constant GSMTLMainPSUniform& cb [[buffer(GSMTLBufferIndexHWUniforms)]],
|
||||
sampler s [[sampler(0)]],
|
||||
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)]])
|
||||
{
|
||||
PSMain main(in, cb);
|
||||
main.tex_sampler = s;
|
||||
if (PS_TEX_IS_COLOR)
|
||||
main.tex = tex;
|
||||
else
|
||||
main.tex_depth = depth;
|
||||
if (PS_HAS_PALETTE)
|
||||
main.palette = palette;
|
||||
|
||||
uint2 coord = uint2(in.p.xy);
|
||||
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 PRIMID_SUPPORT
|
||||
|
||||
Reference in New Issue
Block a user