GS:MTL: Use GSTexture base class's clear tracking

This commit is contained in:
TellowKrinkle 2023-05-16 22:23:07 -05:00 committed by lightningterror
parent d88921bb58
commit ff9a910c1a
3 changed files with 36 additions and 123 deletions

View File

@ -395,23 +395,36 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
|| stencil != m_current_render.stencil_target; || stencil != m_current_render.stencil_target;
GSVector4 color_clear; GSVector4 color_clear;
float depth_clear; float depth_clear;
int stencil_clear;
bool needs_color_clear = false; bool needs_color_clear = false;
bool needs_depth_clear = false; bool needs_depth_clear = false;
bool needs_stencil_clear = false;
// Depth and stencil might be the same, so do all invalidation checks before resetting invalidation // Depth and stencil might be the same, so do all invalidation checks before resetting invalidation
if (mc && mc->IsInvalidated()) color_load = MTLLoadActionDontCare; #define CHECK_CLEAR(tex, load_action, clear, ClearGetter) \
if (md && md->IsInvalidated()) depth_load = MTLLoadActionDontCare; if (tex) \
if (ms && ms->IsInvalidated()) stencil_load = MTLLoadActionDontCare; { \
if (mc) { mc->ResetInvalidation(); needs_color_clear = mc->GetResetNeedsColorClear(color_clear); } if (tex->GetState() == GSTexture::State::Invalidated) \
if (md) { md->ResetInvalidation(); needs_depth_clear = md->GetResetNeedsDepthClear(depth_clear); } { \
if (ms) { ms->ResetInvalidation(); needs_stencil_clear = ms->GetResetNeedsStencilClear(stencil_clear); } load_action = MTLLoadActionDontCare; \
if (needs_color_clear && color_load != MTLLoadActionDontCare) color_load = MTLLoadActionClear; } \
if (needs_depth_clear && depth_load != MTLLoadActionDontCare) depth_load = MTLLoadActionClear; else if (tex->GetState() == GSTexture::State::Cleared && load_action != MTLLoadActionDontCare) \
if (needs_stencil_clear && stencil_load != MTLLoadActionDontCare) stencil_load = MTLLoadActionClear; { \
clear = tex->ClearGetter(); \
load_action = MTLLoadActionClear; \
} \
}
CHECK_CLEAR(mc, color_load, color_clear, GetClearColor)
CHECK_CLEAR(md, depth_load, depth_clear, GetClearDepth)
#undef CHECK_CLEAR
// Stencil and depth are one texture, stencil clears aren't supported
if (ms && ms->GetState() == GSTexture::State::Invalidated)
stencil_load = MTLLoadActionDontCare;
needs_new |= mc && color_load == MTLLoadActionClear; needs_new |= mc && color_load == MTLLoadActionClear;
needs_new |= md && depth_load == MTLLoadActionClear; needs_new |= md && depth_load == MTLLoadActionClear;
needs_new |= ms && stencil_load == MTLLoadActionClear;
// Reset texture state
if (mc) mc->SetState(GSTexture::State::Dirty);
if (md) md->SetState(GSTexture::State::Dirty);
if (ms) ms->SetState(GSTexture::State::Dirty);
if (!needs_new) if (!needs_new)
{ {
@ -457,8 +470,7 @@ void GSDeviceMTL::BeginRenderPass(NSString* name, GSTexture* color, MTLLoadActio
{ {
ms->m_last_write = m_current_draw; ms->m_last_write = m_current_draw;
desc.stencilAttachment.texture = ms->GetTexture(); desc.stencilAttachment.texture = ms->GetTexture();
if (stencil_load == MTLLoadActionClear) assert(stencil_load != MTLLoadActionClear);
desc.stencilAttachment.clearStencil = stencil_clear;
desc.stencilAttachment.loadAction = stencil_load; desc.stencilAttachment.loadAction = stencil_load;
} }
@ -1423,7 +1435,7 @@ void GSDeviceMTL::AccumulateCommandBufferTime(id<MTLCommandBuffer> buffer)
void GSDeviceMTL::ClearRenderTarget(GSTexture* t, const GSVector4& c) void GSDeviceMTL::ClearRenderTarget(GSTexture* t, const GSVector4& c)
{ {
if (!t) return; if (!t) return;
static_cast<GSTextureMTL*>(t)->RequestColorClear(c); t->SetClearColor(c);
} }
void GSDeviceMTL::ClearRenderTarget(GSTexture* t, uint32 c) void GSDeviceMTL::ClearRenderTarget(GSTexture* t, uint32 c)
@ -1435,13 +1447,13 @@ void GSDeviceMTL::ClearRenderTarget(GSTexture* t, uint32 c)
void GSDeviceMTL::ClearDepth(GSTexture* t) void GSDeviceMTL::ClearDepth(GSTexture* t)
{ {
if (!t) return; if (!t) return;
static_cast<GSTextureMTL*>(t)->RequestDepthClear(0); t->SetClearDepth(0);
} }
void GSDeviceMTL::InvalidateRenderTarget(GSTexture* t) void GSDeviceMTL::InvalidateRenderTarget(GSTexture* t)
{ {
if (!t) return; if (!t) return;
static_cast<GSTextureMTL*>(t)->Invalidate(); t->SetState(GSTexture::State::Invalidated);
} }
std::unique_ptr<GSDownloadTexture> GSDeviceMTL::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) std::unique_ptr<GSDownloadTexture> GSDeviceMTL::CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format)
@ -1468,7 +1480,7 @@ void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
if (r.width() < dsize.x || r.height() < dsize.y) if (r.width() < dsize.x || r.height() < dsize.y)
dT->FlushClears(); dT->FlushClears();
else else
dT->InvalidateClears(); dT->SetState(GSTexture::State::Dirty);
EndRenderPass(); EndRenderPass();

View File

@ -33,16 +33,6 @@ class GSTextureMTL : public GSTexture
MRCOwned<id<MTLTexture>> m_texture; MRCOwned<id<MTLTexture>> m_texture;
bool m_has_mipmaps = false; bool m_has_mipmaps = false;
// In Metal clears happen as a part of render passes instead of as separate steps, but the GSDevice API has it as a separate step
// To deal with that, store the fact that a clear was requested here and it'll be applied on the next render pass
bool m_needs_color_clear = false;
bool m_needs_depth_clear = false;
bool m_needs_stencil_clear = false;
bool m_invalidated = false;
GSVector4 m_clear_color;
float m_clear_depth;
int m_clear_stencil;
public: public:
u64 m_last_read = 0; ///< Last time this texture was read by a draw 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 u64 m_last_write = 0; ///< Last time this texture was written by a draw
@ -52,28 +42,8 @@ public:
/// For making fake backbuffers /// For making fake backbuffers
void SetSize(GSVector2i size) { m_size = size; } void SetSize(GSVector2i size) { m_size = size; }
/// Requests the texture be cleared the next time a color render is done
void RequestColorClear(GSVector4 color);
/// Requests the texture be cleared the next time a depth render is done
void RequestDepthClear(float depth);
/// Requests the texture be cleared the next time a stencil render is done
void RequestStencilClear(int stencil);
/// Reads whether a color clear was requested, then clears the request
bool GetResetNeedsColorClear(GSVector4& colorOut);
/// Reads whether a depth clear was requested, then clears the request
bool GetResetNeedsDepthClear(float& depthOut);
/// Reads whether a stencil clear was requested, then clears the request
bool GetResetNeedsStencilClear(int& stencilOut);
/// Flushes requested clears to the texture /// Flushes requested clears to the texture
void FlushClears(); void FlushClears();
/// Marks pending clears as done (e.g. if the whole texture is about to be overwritten)
void InvalidateClears();
/// Marks the texture as invalid (will load with DontCare)
void Invalidate();
/// Reads whether the texture has been invalidated, then clears the invalidation
bool IsInvalidated() const { return m_invalidated; };
/// Clears any invalidation requests
void ResetInvalidation() { m_invalidated = false; }
void* GetNativeHandle() const override; void* GetNativeHandle() const override;
bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override; bool Update(const GSVector4i& r, const void* data, int pitch, int layer = 0) override;

View File

@ -39,64 +39,14 @@ GSTextureMTL::~GSTextureMTL()
{ {
} }
void GSTextureMTL::RequestColorClear(GSVector4 color)
{
m_needs_color_clear = true;
m_invalidated = false;
m_clear_color = color;
}
void GSTextureMTL::RequestDepthClear(float depth)
{
m_needs_depth_clear = true;
m_invalidated = false;
m_clear_depth = depth;
}
void GSTextureMTL::RequestStencilClear(int stencil)
{
m_needs_stencil_clear = true;
m_invalidated = false;
m_clear_stencil = stencil;
}
bool GSTextureMTL::GetResetNeedsColorClear(GSVector4& colorOut)
{
if (m_needs_color_clear)
{
m_needs_color_clear = false;
colorOut = m_clear_color;
return true;
}
return false;
}
bool GSTextureMTL::GetResetNeedsDepthClear(float& depthOut)
{
if (m_needs_depth_clear)
{
m_needs_depth_clear = false;
depthOut = m_clear_depth;
return true;
}
return false;
}
bool GSTextureMTL::GetResetNeedsStencilClear(int& stencilOut)
{
if (m_needs_stencil_clear)
{
m_needs_stencil_clear = false;
stencilOut = m_clear_stencil;
return true;
}
return false;
}
void GSTextureMTL::FlushClears() void GSTextureMTL::FlushClears()
{ {
if (!m_needs_color_clear && !m_needs_depth_clear && !m_needs_stencil_clear) if (m_state != GSTexture::State::Cleared)
return; return;
m_dev->BeginRenderPass(@"Clear", m_dev->BeginRenderPass(@"Clear",
m_needs_color_clear ? this : nullptr, MTLLoadActionLoad, !IsDepthStencil() ? this : nullptr, MTLLoadActionLoad,
m_needs_depth_clear ? this : nullptr, MTLLoadActionLoad, IsDepthStencil() ? this : nullptr, MTLLoadActionLoad);
m_needs_stencil_clear ? this : nullptr, MTLLoadActionLoad);
} }
void* GSTextureMTL::GetNativeHandle() const void* GSTextureMTL::GetNativeHandle() const
@ -104,19 +54,6 @@ void* GSTextureMTL::GetNativeHandle() const
return (__bridge void*)m_texture; return (__bridge void*)m_texture;
} }
void GSTextureMTL::InvalidateClears()
{
m_needs_color_clear = false;
m_needs_depth_clear = false;
m_needs_stencil_clear = false;
}
void GSTextureMTL::Invalidate()
{
InvalidateClears();
m_invalidated = true;
}
bool GSTextureMTL::Update(const GSVector4i& r, const void* data, int pitch, int layer) bool GSTextureMTL::Update(const GSVector4i& r, const void* data, int pitch, int layer)
{ {
if (void* buffer = MapWithPitch(r, pitch, layer)) if (void* buffer = MapWithPitch(r, pitch, layer))
@ -151,9 +88,9 @@ void* GSTextureMTL::MapWithPitch(const GSVector4i& r, int pitch, int layer)
GSDeviceMTL::Map map; GSDeviceMTL::Map map;
bool needs_clear = false; bool needs_clear = false;
if (m_needs_color_clear) if (m_state == GSTexture::State::Cleared)
{ {
m_needs_color_clear = false; m_state = GSTexture::State::Dirty;
// Not uploading to full texture // Not uploading to full texture
needs_clear = r.left > 0 || r.top > 0 || r.right < m_size.x || r.bottom < m_size.y; needs_clear = r.left > 0 || r.top > 0 || r.right < m_size.x || r.bottom < m_size.y;
} }
@ -163,7 +100,7 @@ void* GSTextureMTL::MapWithPitch(const GSVector4i& r, int pitch, int layer)
{ {
if (needs_clear) if (needs_clear)
{ {
m_needs_color_clear = true; m_state = GSTexture::State::Cleared;
m_dev->BeginRenderPass(@"Pre-Upload Clear", this, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare); m_dev->BeginRenderPass(@"Pre-Upload Clear", this, MTLLoadActionLoad, nullptr, MTLLoadActionDontCare);
} }
enc = m_dev->GetLateTextureUploadEncoder(); enc = m_dev->GetLateTextureUploadEncoder();
@ -212,12 +149,6 @@ void GSTextureMTL::Swap(GSTexture* other)
#define SWAP(x) std::swap(x, mtex->x) #define SWAP(x) std::swap(x, mtex->x)
SWAP(m_texture); SWAP(m_texture);
SWAP(m_has_mipmaps); SWAP(m_has_mipmaps);
SWAP(m_needs_color_clear);
SWAP(m_needs_depth_clear);
SWAP(m_needs_stencil_clear);
SWAP(m_clear_color);
SWAP(m_clear_depth);
SWAP(m_clear_stencil);
#undef SWAP #undef SWAP
} }