Reland "Metal: add loadMSAAFromResolve support."

This is a reland of 9f82158f17

Original change's description:
> Metal: add loadMSAAFromResolve support.
>
> Also fixes some minor warnings.
>
> Bug: skia:12086
> Change-Id: Ia476a7a196b490022978761e92f935a6d668136a
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/441797
> Reviewed-by: Greg Daniel <egdaniel@google.com>
> Commit-Queue: Jim Van Verth <jvanverth@google.com>

Bug: skia:12086
Change-Id: I1d9dd36b3f8f19a0ab4a98ccaab18146566c1885
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/446077
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Greg Daniel <egdaniel@google.com>
This commit is contained in:
Jim Van Verth 2021-09-07 11:42:09 -04:00
parent be0e255c3e
commit 7efdc8fd5d
7 changed files with 187 additions and 6 deletions

View File

@ -51,6 +51,10 @@ public:
id<MTLTexture> mtlTexture() const { return fTexture; }
unsigned int sampleCount() const { return fTexture.sampleCount; }
bool framebufferOnly() const { return fTexture.framebufferOnly; }
protected:
void onRelease() override;
void onAbandon() override;

View File

@ -107,6 +107,10 @@ public:
void finishOutstandingGpuWork() override;
std::unique_ptr<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override;
bool loadMSAAFromResolve(GrAttachment* dst,
GrMtlAttachment* src,
const SkIRect& srcRect);
// When the Metal backend actually uses indirect command buffers, this function will actually do
// what it says. For now, every command is encoded directly into the primary command buffer, so
// this function is pretty useless, except for indicating that a render target has been drawn

View File

@ -147,7 +147,7 @@ GrOpsRenderPass* GrMtlGpu::onGetOpsRenderPass(
// TODO: Make use of discardable MSAA
bool withResolve = false;
// Figure out if we can use a Resolve store action for this render pass.
// Figure out if we can use a Resolve store for this render pass
if (useMSAASurface && mtlRT->resolveAttachment() &&
this->mtlCaps().storeAndMultisampleResolveSupport()) {
withResolve = true;
@ -356,7 +356,7 @@ bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex,
int currentWidth = rect.width();
int currentHeight = rect.height();
SkDEBUGCODE(int layerHeight = tex->height();)
SkDEBUGCODE(int layerHeight = tex->height());
MTLOrigin origin = MTLOriginMake(rect.left(), rect.top(), 0);
auto cmdBuffer = this->commandBuffer();
@ -387,7 +387,7 @@ bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex,
}
currentWidth = std::max(1, currentWidth/2);
currentHeight = std::max(1, currentHeight/2);
SkDEBUGCODE(layerHeight = currentHeight;)
SkDEBUGCODE(layerHeight = currentHeight);
}
#ifdef SK_BUILD_FOR_MAC
[mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)];
@ -1530,6 +1530,80 @@ void GrMtlGpu::resolve(GrMtlAttachment* resolveAttachment,
this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(msaaAttachment));
}
bool GrMtlGpu::loadMSAAFromResolve(GrAttachment* dst,
GrMtlAttachment* src,
const SkIRect& srcRect) {
if (!dst) {
return false;
}
if (!src || src->framebufferOnly()) {
return false;
}
GrMtlAttachment* mtlDst = static_cast<GrMtlAttachment*>(dst);
auto renderPipeline = this->resourceProvider().findOrCreateMSAALoadPipeline(mtlDst->mtlFormat(),
dst->numSamples());
// Set up rendercommandencoder
auto renderPassDesc = [MTLRenderPassDescriptor new];
auto colorAttachment = renderPassDesc.colorAttachments[0];
colorAttachment.texture = mtlDst->mtlTexture();
colorAttachment.loadAction = MTLLoadActionDontCare;
colorAttachment.storeAction = MTLStoreActionMultisampleResolve;
colorAttachment.resolveTexture = src->mtlTexture();
auto renderCmdEncoder =
this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr, nullptr);
// Bind pipeline
renderCmdEncoder->setRenderPipelineState(renderPipeline->mtlPipelineState());
this->commandBuffer()->addResource(sk_ref_sp(renderPipeline));
// Bind src as input texture
renderCmdEncoder->setFragmentTexture(src->mtlTexture(), 0);
// No sampler needed
this->commandBuffer()->addGrSurface(sk_ref_sp<GrSurface>(src));
// Scissor and viewport should default to size of color attachment
// Update and bind uniform data
int w = srcRect.width();
int h = srcRect.height();
// dst rect edges in NDC (-1 to 1)
int dw = dst->width();
int dh = dst->height();
float dx0 = 2.f * srcRect.fLeft / dw - 1.f;
float dx1 = 2.f * (srcRect.fLeft + w) / dw - 1.f;
float dy0 = 2.f * srcRect.fTop / dh - 1.f;
float dy1 = 2.f * (srcRect.fTop + h) / dh - 1.f;
struct {
float posXform[4];
int textureSize[2];
int pad[2];
} uniData = {{dx1 - dx0, dy1 - dy0, dx0, dy0}, {dw, dh}, {0, 0}};
constexpr size_t uniformSize = 32;
if (@available(macOS 10.11, iOS 8.3, *)) {
SkASSERT(uniformSize <= this->caps()->maxPushConstantsSize());
renderCmdEncoder->setVertexBytes(&uniData, uniformSize, 0);
} else {
// upload the data
GrRingBuffer::Slice slice = this->uniformsRingBuffer()->suballocate(uniformSize);
GrMtlBuffer* buffer = (GrMtlBuffer*) slice.fBuffer;
char* destPtr = static_cast<char*>(slice.fBuffer->map()) + slice.fOffset;
memcpy(destPtr, &uniData, uniformSize);
renderCmdEncoder->setVertexBuffer(buffer->mtlBuffer(), slice.fOffset, 0);
}
renderCmdEncoder->drawPrimitives(MTLPrimitiveTypeTriangleStrip, (NSUInteger)0, (NSUInteger)4);
return true;
}
#if GR_TEST_UTILS
void GrMtlGpu::testingOnly_startCapture() {
if (@available(macOS 10.13, iOS 11.0, *)) {

View File

@ -33,7 +33,7 @@ public:
fPipelineState = nil;
}
id<MTLRenderPipelineState> mtlPipelineState() { return fPipelineState; }
id<MTLRenderPipelineState> mtlPipelineState() const { return fPipelineState; }
private:
GrMtlRenderPipeline(id<MTLRenderPipelineState> pso)

View File

@ -15,6 +15,7 @@
#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/GrThreadSafePipelineBuilder.h"
#include "src/gpu/mtl/GrMtlDepthStencil.h"
#include "src/gpu/mtl/GrMtlPipeline.h"
#include "src/gpu/mtl/GrMtlPipelineStateBuilder.h"
#include "src/gpu/mtl/GrMtlSampler.h"
@ -39,6 +40,8 @@ public:
// Finds or creates a compatible MTLSamplerState based on the GrSamplerState.
GrMtlSampler* findOrCreateCompatibleSampler(GrSamplerState);
const GrMtlRenderPipeline* findOrCreateMSAALoadPipeline(MTLPixelFormat, int sampleCount);
// Destroy any cached resources. To be called before releasing the MtlDevice.
void destroyResources();
@ -85,6 +88,14 @@ private:
SkTDynamicHash<GrMtlSampler, GrMtlSampler::Key> fSamplers;
SkTDynamicHash<GrMtlDepthStencil, GrMtlDepthStencil::Key> fDepthStencilStates;
struct MSAALoadPipelineEntry {
sk_sp<const GrMtlRenderPipeline> fPipeline;
MTLPixelFormat fPixelFormat;
int fSampleCount;
};
id<MTLLibrary> fMSAALoadLibrary;
SkTArray<MSAALoadPipelineEntry> fMSAALoadPipelines;
};
#endif

View File

@ -9,6 +9,7 @@
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "src/core/SkTraceEvent.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/mtl/GrMtlCommandBuffer.h"
@ -66,7 +67,93 @@ GrMtlSampler* GrMtlResourceProvider::findOrCreateCompatibleSampler(GrSamplerStat
return sampler;
}
const GrMtlRenderPipeline* GrMtlResourceProvider::findOrCreateMSAALoadPipeline(
MTLPixelFormat pixelFormat, int sampleCount) {
if (!fMSAALoadLibrary) {
TRACE_EVENT0("skia", TRACE_FUNC);
SkSL::String shaderText;
shaderText.append(
"#include <metal_stdlib>\n"
"#include <simd/simd.h>\n"
"using namespace metal;\n"
"\n"
"typedef struct {\n"
" float4 position [[position]];\n"
"} VertexOutput;\n"
"\n"
"typedef struct {\n"
" float4 uPosXform;\n"
" uint2 uTextureSize;\n"
"} VertexUniforms;\n"
"\n"
"vertex VertexOutput vertexMain(constant VertexUniforms& uniforms [[buffer(0)]],\n"
" uint vertexID [[vertex_id]]) {\n"
" VertexOutput out;\n"
" float2 position = float2(float(vertexID >> 1), float(vertexID & 1));\n"
" out.position.xy = position * uniforms.uPosXform.xy + uniforms.uPosXform.zw;\n"
" out.position.zw = float2(0.0, 1.0);\n"
" return out;\n"
"}\n"
"\n"
"fragment float4 fragmentMain(VertexOutput in [[stage_in]],\n"
" texture2d<half> colorMap [[texture(0)]]) {\n"
" uint2 coords = uint2(in.position.x, in.position.y);"
" half4 colorSample = colorMap.read(coords);\n"
" return float4(colorSample);\n"
"}"
);
auto errorHandler = fGpu->getContext()->priv().getShaderErrorHandler();
fMSAALoadLibrary = GrCompileMtlShaderLibrary(fGpu, shaderText, errorHandler);
if (!fMSAALoadLibrary) {
return nullptr;
}
}
for (int i = 0; i < fMSAALoadPipelines.count(); ++i) {
if (fMSAALoadPipelines[i].fPixelFormat == pixelFormat &&
fMSAALoadPipelines[i].fSampleCount == sampleCount) {
return fMSAALoadPipelines[i].fPipeline.get();
}
}
auto pipelineDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
pipelineDescriptor.vertexFunction =
[fMSAALoadLibrary newFunctionWithName: @"vertexMain"];
pipelineDescriptor.fragmentFunction =
[fMSAALoadLibrary newFunctionWithName: @"fragmentMain"];
auto mtlColorAttachment = [[MTLRenderPipelineColorAttachmentDescriptor alloc] init];
mtlColorAttachment.pixelFormat = pixelFormat;
mtlColorAttachment.blendingEnabled = FALSE;
mtlColorAttachment.writeMask = MTLColorWriteMaskAll;
pipelineDescriptor.colorAttachments[0] = mtlColorAttachment;
pipelineDescriptor.sampleCount = sampleCount;
NSError* error;
auto pso =
[fGpu->device() newRenderPipelineStateWithDescriptor: pipelineDescriptor
error: &error];
if (!pso) {
SkDebugf("Error creating pipeline: %s\n",
[[error localizedDescription] cStringUsingEncoding: NSASCIIStringEncoding]);
}
auto renderPipeline = GrMtlRenderPipeline::Make(pso);
fMSAALoadPipelines.push_back({renderPipeline, pixelFormat, sampleCount});
return fMSAALoadPipelines[fMSAALoadPipelines.count()-1].fPipeline.get();
}
void GrMtlResourceProvider::destroyResources() {
fMSAALoadLibrary = nil;
fMSAALoadPipelines.reset();
fSamplers.foreach([&](GrMtlSampler* sampler) { sampler->unref(); });
fSamplers.reset();

View File

@ -17,11 +17,12 @@ GR_NORETAIN_BEGIN
static void finalize_helper(GrMtlVaryingHandler::VarArray& vars) {
int locationIndex = 0;
int componentCount = 0;
SkDEBUGCODE(int componentCount = 0);
for (GrShaderVar& var : vars.items()) {
// Metal only allows scalars (including bool and char) and vectors as varyings
SkASSERT(GrSLTypeVecLength(var.getType()) != -1);
componentCount += GrSLTypeVecLength(var.getType());
SkDEBUGCODE(componentCount += GrSLTypeVecLength(var.getType()));
SkString location;
location.appendf("location = %d", locationIndex);