mirror of https://github.com/PCSX2/pcsx2.git
GS:MTL: Implement DrawMultiStretchRects
This commit is contained in:
parent
8aad1c78af
commit
df847835ad
|
@ -415,13 +415,15 @@ public:
|
||||||
void ClearSamplerCache() override;
|
void ClearSamplerCache() override;
|
||||||
|
|
||||||
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
void CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r, u32 destX, u32 destY) override;
|
||||||
|
void BeginStretchRect(NSString* name, GSTexture* dTex, MTLLoadAction action);
|
||||||
void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len);
|
void DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len);
|
||||||
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds);
|
void DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2& ds);
|
||||||
/// Copy from a position in sTex to the same position in the currently active render encoder using the given fs pipeline and rect
|
/// Copy from a position in sTex to the same position in the currently active render encoder using the given fs pipeline and rect
|
||||||
void RenderCopy(GSTexture* sTex, id<MTLRenderPipelineState> pipeline, const GSVector4i& rect);
|
void RenderCopy(GSTexture* sTex, id<MTLRenderPipelineState> pipeline, const GSVector4i& rect);
|
||||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;
|
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader = ShaderConvert::COPY, bool linear = true) override;
|
||||||
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) override;
|
void StretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, bool red, bool green, bool blue, bool alpha) override;
|
||||||
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) override;
|
void PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, PresentShader shader, float shaderTime, bool linear) override;
|
||||||
|
void DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader) override;
|
||||||
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
void UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize) override;
|
||||||
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
void ConvertToIndexedTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, u32 SBW, u32 SPSM, GSTexture* dTex, u32 DBW, u32 DPSM) override;
|
||||||
|
|
||||||
|
|
|
@ -1496,14 +1496,25 @@ void GSDeviceMTL::CopyRect(GSTexture* sTex, GSTexture* dTex, const GSVector4i& r
|
||||||
[encoder endEncoding];
|
[encoder endEncoding];
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
void GSDeviceMTL::BeginStretchRect(NSString* name, GSTexture* dTex, MTLLoadAction action)
|
||||||
|
{
|
||||||
|
if (dTex->GetFormat() == GSTexture::Format::DepthStencil)
|
||||||
|
BeginRenderPass(name, nullptr, MTLLoadActionDontCare, dTex, action);
|
||||||
|
else
|
||||||
|
BeginRenderPass(name, dTex, action, nullptr, MTLLoadActionDontCare);
|
||||||
|
|
||||||
|
FlushDebugEntries(m_current_render.encoder);
|
||||||
|
MREClearScissor();
|
||||||
|
DepthStencilSelector dsel = DepthStencilSelector::NoDepth();
|
||||||
|
dsel.zwe = dTex->GetFormat() == GSTexture::Format::DepthStencil;
|
||||||
|
MRESetDSS(dsel);
|
||||||
|
}
|
||||||
|
|
||||||
void GSDeviceMTL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len)
|
void GSDeviceMTL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTexture* dTex, const GSVector4& dRect, id<MTLRenderPipelineState> pipeline, bool linear, LoadAction load_action, const void* frag_uniform, size_t frag_uniform_len)
|
||||||
{
|
{
|
||||||
FlushClears(sTex);
|
FlushClears(sTex);
|
||||||
|
|
||||||
GSTextureMTL* sT = static_cast<GSTextureMTL*>(sTex);
|
GSVector2i ds = dTex->GetSize();
|
||||||
GSTextureMTL* dT = static_cast<GSTextureMTL*>(dTex);
|
|
||||||
|
|
||||||
GSVector2i ds = dT->GetSize();
|
|
||||||
|
|
||||||
bool covers_target = static_cast<int>(dRect.x) <= 0
|
bool covers_target = static_cast<int>(dRect.x) <= 0
|
||||||
&& static_cast<int>(dRect.y) <= 0
|
&& static_cast<int>(dRect.y) <= 0
|
||||||
|
@ -1512,45 +1523,39 @@ void GSDeviceMTL::DoStretchRect(GSTexture* sTex, const GSVector4& sRect, GSTextu
|
||||||
bool dontcare = load_action == LoadAction::DontCare || (load_action == LoadAction::DontCareIfFull && covers_target);
|
bool dontcare = load_action == LoadAction::DontCare || (load_action == LoadAction::DontCareIfFull && covers_target);
|
||||||
MTLLoadAction action = dontcare ? MTLLoadActionDontCare : MTLLoadActionLoad;
|
MTLLoadAction action = dontcare ? MTLLoadActionDontCare : MTLLoadActionLoad;
|
||||||
|
|
||||||
if (dT->GetFormat() == GSTexture::Format::DepthStencil)
|
BeginStretchRect(@"StretchRect", dTex, action);
|
||||||
BeginRenderPass(@"StretchRect", nullptr, MTLLoadActionDontCare, dT, action);
|
|
||||||
else
|
|
||||||
BeginRenderPass(@"StretchRect", dT, action, nullptr, MTLLoadActionDontCare);
|
|
||||||
|
|
||||||
FlushDebugEntries(m_current_render.encoder);
|
|
||||||
MREClearScissor();
|
|
||||||
DepthStencilSelector dsel;
|
|
||||||
dsel.ztst = ZTST_ALWAYS;
|
|
||||||
dsel.zwe = dT->GetFormat() == GSTexture::Format::DepthStencil;
|
|
||||||
MRESetDSS(dsel);
|
|
||||||
|
|
||||||
MRESetPipeline(pipeline);
|
MRESetPipeline(pipeline);
|
||||||
MRESetTexture(sT, GSMTLTextureIndexNonHW);
|
MRESetTexture(sTex, GSMTLTextureIndexNonHW);
|
||||||
|
|
||||||
if (frag_uniform && frag_uniform_len)
|
if (frag_uniform && frag_uniform_len)
|
||||||
[m_current_render.encoder setFragmentBytes:frag_uniform length:frag_uniform_len atIndex:GSMTLBufferIndexUniforms];
|
[m_current_render.encoder setFragmentBytes:frag_uniform length:frag_uniform_len atIndex:GSMTLBufferIndexUniforms];
|
||||||
|
|
||||||
MRESetSampler(linear ? SamplerSelector::Linear() : SamplerSelector::Point());
|
MRESetSampler(linear ? SamplerSelector::Linear() : SamplerSelector::Point());
|
||||||
|
|
||||||
DrawStretchRect(sRect, dRect, ds);
|
DrawStretchRect(sRect, dRect, GSVector2(static_cast<float>(ds.x), static_cast<float>(ds.y)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GSDeviceMTL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2i& ds)
|
static std::array<GSVector4, 4> CalcStrechRectPoints(const GSVector4& sRect, const GSVector4& dRect, const GSVector2& ds)
|
||||||
{
|
{
|
||||||
float left = dRect.x * 2 / ds.x - 1.0f;
|
static_assert(sizeof(GSDeviceMTL::ConvertShaderVertex) == sizeof(GSVector4), "Using GSVector4 as a ConvertShaderVertex");
|
||||||
float right = dRect.z * 2 / ds.x - 1.0f;
|
GSVector4 dst = dRect;
|
||||||
float top = 1.0f - dRect.y * 2 / ds.y;
|
dst /= GSVector4(ds.x, ds.y, ds.x, ds.y);
|
||||||
float bottom = 1.0f - dRect.w * 2 / ds.y;
|
dst *= GSVector4(2, -2, 2, -2);
|
||||||
|
dst += GSVector4(-1, 1, -1, 1);
|
||||||
ConvertShaderVertex vertices[] =
|
return {
|
||||||
{
|
dst.xyxy(sRect),
|
||||||
{{left, top}, {sRect.x, sRect.y}},
|
dst.zyzy(sRect),
|
||||||
{{right, top}, {sRect.z, sRect.y}},
|
dst.xwxw(sRect),
|
||||||
{{left, bottom}, {sRect.x, sRect.w}},
|
dst.zwzw(sRect)
|
||||||
{{right, bottom}, {sRect.z, sRect.w}}
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
[m_current_render.encoder setVertexBytes:vertices length:sizeof(vertices) atIndex:GSMTLBufferIndexVertices];
|
void GSDeviceMTL::DrawStretchRect(const GSVector4& sRect, const GSVector4& dRect, const GSVector2& ds)
|
||||||
|
{
|
||||||
|
std::array<GSVector4, 4> vertices = CalcStrechRectPoints(sRect, dRect, ds);
|
||||||
|
|
||||||
|
[m_current_render.encoder setVertexBytes:&vertices length:sizeof(vertices) atIndex:GSMTLBufferIndexVertices];
|
||||||
|
|
||||||
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
|
[m_current_render.encoder drawPrimitives:MTLPrimitiveTypeTriangleStrip
|
||||||
vertexStart:0
|
vertexStart:0
|
||||||
|
@ -1622,10 +1627,69 @@ void GSDeviceMTL::PresentRect(GSTexture* sTex, const GSVector4& sRect, GSTexture
|
||||||
[m_current_render.encoder setFragmentSamplerState:m_sampler_hw[linear ? SamplerSelector::Linear().key : SamplerSelector::Point().key] atIndex:0];
|
[m_current_render.encoder setFragmentSamplerState:m_sampler_hw[linear ? SamplerSelector::Linear().key : SamplerSelector::Point().key] atIndex:0];
|
||||||
[m_current_render.encoder setFragmentTexture:static_cast<GSTextureMTL*>(sTex)->GetTexture() atIndex:0];
|
[m_current_render.encoder setFragmentTexture:static_cast<GSTextureMTL*>(sTex)->GetTexture() atIndex:0];
|
||||||
[m_current_render.encoder setFragmentBytes:&cb length:sizeof(cb) atIndex:GSMTLBufferIndexUniforms];
|
[m_current_render.encoder setFragmentBytes:&cb length:sizeof(cb) atIndex:GSMTLBufferIndexUniforms];
|
||||||
DrawStretchRect(sRect, dRect, ds);
|
DrawStretchRect(sRect, dRect, GSVector2(static_cast<float>(ds.x), static_cast<float>(ds.y)));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
void GSDeviceMTL::DrawMultiStretchRects(const MultiStretchRect* rects, u32 num_rects, GSTexture* dTex, ShaderConvert shader)
|
||||||
|
{ @autoreleasepool {
|
||||||
|
BeginStretchRect(@"MultiStretchRect", dTex, MTLLoadActionLoad);
|
||||||
|
|
||||||
|
id<MTLRenderPipelineState> pipeline = nullptr;
|
||||||
|
GSTexture* sTex = rects[0].src;
|
||||||
|
bool linear = rects[0].linear;
|
||||||
|
u8 wmask = rects[0].wmask.wrgba;
|
||||||
|
|
||||||
|
const GSVector2 ds(static_cast<float>(dTex->GetWidth()), static_cast<float>(dTex->GetHeight()));
|
||||||
|
const Map allocation = Allocate(m_vertex_upload_buf, sizeof(ConvertShaderVertex) * 4 * num_rects);
|
||||||
|
std::array<GSVector4, 4>* write = static_cast<std::array<GSVector4, 4>*>(allocation.cpu_buffer);
|
||||||
|
const id<MTLRenderCommandEncoder> enc = m_current_render.encoder;
|
||||||
|
[enc setVertexBuffer:allocation.gpu_buffer
|
||||||
|
offset:allocation.gpu_offset
|
||||||
|
atIndex:GSMTLBufferIndexVertices];
|
||||||
|
u32 start = 0;
|
||||||
|
|
||||||
|
auto flush = [&](u32 i) {
|
||||||
|
const u32 end = i * 4;
|
||||||
|
const u32 vertex_count = end - start;
|
||||||
|
const u32 index_count = vertex_count + (vertex_count >> 1); // 6 indices per 4 vertices
|
||||||
|
id<MTLRenderPipelineState> new_pipeline = wmask == 0xf ? m_convert_pipeline[static_cast<int>(shader)]
|
||||||
|
: m_convert_pipeline_copy_mask[wmask];
|
||||||
|
if (new_pipeline != pipeline)
|
||||||
|
{
|
||||||
|
pipeline = new_pipeline;
|
||||||
|
pxAssertRel(pipeline, fmt::format("No pipeline for {}", shaderName(shader)).c_str());
|
||||||
|
MRESetPipeline(pipeline);
|
||||||
|
}
|
||||||
|
MRESetSampler(linear ? SamplerSelector::Linear() : SamplerSelector::Point());
|
||||||
|
MRESetTexture(sTex, GSMTLTextureIndexNonHW);
|
||||||
|
[enc drawIndexedPrimitives:MTLPrimitiveTypeTriangle
|
||||||
|
indexCount:index_count
|
||||||
|
indexType:MTLIndexTypeUInt16
|
||||||
|
indexBuffer:m_expand_index_buffer
|
||||||
|
indexBufferOffset:0
|
||||||
|
instanceCount:1
|
||||||
|
baseVertex:start
|
||||||
|
baseInstance:0];
|
||||||
|
start = end;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (u32 i = 0; i < num_rects; i++)
|
||||||
|
{
|
||||||
|
const MultiStretchRect& rect = rects[i];
|
||||||
|
if (rect.src != sTex || rect.linear != linear || rect.wmask.wrgba != wmask)
|
||||||
|
{
|
||||||
|
flush(i);
|
||||||
|
sTex = rect.src;
|
||||||
|
linear = rect.linear;
|
||||||
|
wmask = rect.wmask.wrgba;
|
||||||
|
}
|
||||||
|
*write++ = CalcStrechRectPoints(rect.src_rect, rect.dst_rect, ds);
|
||||||
|
}
|
||||||
|
|
||||||
|
flush(num_rects);
|
||||||
|
}}
|
||||||
|
|
||||||
void GSDeviceMTL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
void GSDeviceMTL::UpdateCLUTTexture(GSTexture* sTex, float sScale, u32 offsetX, u32 offsetY, GSTexture* dTex, u32 dOffset, u32 dSize)
|
||||||
{
|
{
|
||||||
GSMTLCLUTConvertPSUniform uniform = { sScale, {offsetX, offsetY}, dOffset };
|
GSMTLCLUTConvertPSUniform uniform = { sScale, {offsetX, offsetY}, dOffset };
|
||||||
|
|
Loading…
Reference in New Issue