xref: /aosp_15_r20/external/skia/src/gpu/ganesh/mtl/GrMtlGpu.mm (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker/*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlGpu.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker#include "include/core/SkColorSpace.h"
11*c8dee2aaSAndroid Build Coastguard Worker#include "include/core/SkTextureCompressionType.h"
12*c8dee2aaSAndroid Build Coastguard Worker#include "include/gpu/GpuTypes.h"
13*c8dee2aaSAndroid Build Coastguard Worker#include "include/gpu/ganesh/mtl/GrMtlBackendSemaphore.h"
14*c8dee2aaSAndroid Build Coastguard Worker#include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h"
15*c8dee2aaSAndroid Build Coastguard Worker#include "include/private/gpu/ganesh/GrTypesPriv.h"
16*c8dee2aaSAndroid Build Coastguard Worker#include "src/base/SkMathPriv.h"
17*c8dee2aaSAndroid Build Coastguard Worker#include "src/base/SkRectMemcpy.h"
18*c8dee2aaSAndroid Build Coastguard Worker#include "src/core/SkCompressedDataUtils.h"
19*c8dee2aaSAndroid Build Coastguard Worker#include "src/core/SkMipmap.h"
20*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/DataUtils.h"
21*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrBackendUtils.h"
22*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrDataUtils.h"
23*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrDirectContextPriv.h"
24*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrImageInfo.h"
25*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrPixmap.h"
26*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrRenderTarget.h"
27*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrResourceProvider.h"
28*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrTexture.h"
29*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/GrThreadSafePipelineBuilder.h"
30*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlBuffer.h"
31*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlCommandBuffer.h"
32*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlOpsRenderPass.h"
33*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlPipelineStateBuilder.h"
34*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlRenderCommandEncoder.h"
35*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlSemaphore.h"
36*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlTexture.h"
37*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlTextureRenderTarget.h"
38*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/ganesh/mtl/GrMtlUtil.h"
39*c8dee2aaSAndroid Build Coastguard Worker#include "src/gpu/mtl/MtlUtilsPriv.h"
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker#import <simd/simd.h>
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Workerusing namespace skia_private;
44*c8dee2aaSAndroid Build Coastguard Worker
45*c8dee2aaSAndroid Build Coastguard Worker#if !__has_feature(objc_arc)
46*c8dee2aaSAndroid Build Coastguard Worker#error This file must be compiled with Arc. Use -fobjc-arc flag
47*c8dee2aaSAndroid Build Coastguard Worker#endif
48*c8dee2aaSAndroid Build Coastguard Worker
49*c8dee2aaSAndroid Build Coastguard WorkerGR_NORETAIN_BEGIN
50*c8dee2aaSAndroid Build Coastguard Worker
51*c8dee2aaSAndroid Build Coastguard Worker#if defined(GPU_TEST_UTILS)
52*c8dee2aaSAndroid Build Coastguard Worker// set to 1 if you want to do GPU capture of each commandBuffer
53*c8dee2aaSAndroid Build Coastguard Worker#define GR_METAL_CAPTURE_COMMANDBUFFER 0
54*c8dee2aaSAndroid Build Coastguard Worker#endif
55*c8dee2aaSAndroid Build Coastguard Worker
56*c8dee2aaSAndroid Build Coastguard Workerstd::unique_ptr<GrGpu> GrMtlGpu::Make(const GrMtlBackendContext& context,
57*c8dee2aaSAndroid Build Coastguard Worker                                      const GrContextOptions& options,
58*c8dee2aaSAndroid Build Coastguard Worker                                      GrDirectContext* direct) {
59*c8dee2aaSAndroid Build Coastguard Worker    if (!context.fDevice || !context.fQueue) {
60*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
61*c8dee2aaSAndroid Build Coastguard Worker    }
62*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.14, iOS 10.0, tvOS 10.0, *)) {
63*c8dee2aaSAndroid Build Coastguard Worker        // no warning needed
64*c8dee2aaSAndroid Build Coastguard Worker    } else {
65*c8dee2aaSAndroid Build Coastguard Worker        SkDebugf("*** Error ***: Skia's Metal backend no longer supports this OS version.\n");
66*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_IOS
67*c8dee2aaSAndroid Build Coastguard Worker        SkDebugf("Minimum supported version is iOS 10.0.\n");
68*c8dee2aaSAndroid Build Coastguard Worker#else
69*c8dee2aaSAndroid Build Coastguard Worker        SkDebugf("Minimum supported version is MacOS 10.14.\n");
70*c8dee2aaSAndroid Build Coastguard Worker#endif
71*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
72*c8dee2aaSAndroid Build Coastguard Worker    }
73*c8dee2aaSAndroid Build Coastguard Worker
74*c8dee2aaSAndroid Build Coastguard Worker    id<MTLDevice> GR_NORETAIN device = (__bridge id<MTLDevice>)(context.fDevice.get());
75*c8dee2aaSAndroid Build Coastguard Worker    id<MTLCommandQueue> GR_NORETAIN queue = (__bridge id<MTLCommandQueue>)(context.fQueue.get());
76*c8dee2aaSAndroid Build Coastguard Worker
77*c8dee2aaSAndroid Build Coastguard Worker    return std::unique_ptr<GrGpu>(new GrMtlGpu(direct,
78*c8dee2aaSAndroid Build Coastguard Worker                                               options,
79*c8dee2aaSAndroid Build Coastguard Worker                                               device,
80*c8dee2aaSAndroid Build Coastguard Worker                                               queue));
81*c8dee2aaSAndroid Build Coastguard Worker}
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker// This constant determines how many OutstandingCommandBuffers are allocated together as a block in
84*c8dee2aaSAndroid Build Coastguard Worker// the deque. As such it needs to balance allocating too much memory vs. incurring
85*c8dee2aaSAndroid Build Coastguard Worker// allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding
86*c8dee2aaSAndroid Build Coastguard Worker// command buffers we expect to see.
87*c8dee2aaSAndroid Build Coastguard Workerstatic const int kDefaultOutstandingAllocCnt = 8;
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard WorkerGrMtlGpu::GrMtlGpu(GrDirectContext* direct, const GrContextOptions& options,
90*c8dee2aaSAndroid Build Coastguard Worker                   id<MTLDevice> device, id<MTLCommandQueue> queue)
91*c8dee2aaSAndroid Build Coastguard Worker        : INHERITED(direct)
92*c8dee2aaSAndroid Build Coastguard Worker        , fDevice(device)
93*c8dee2aaSAndroid Build Coastguard Worker        , fQueue(queue)
94*c8dee2aaSAndroid Build Coastguard Worker        , fOutstandingCommandBuffers(sizeof(OutstandingCommandBuffer), kDefaultOutstandingAllocCnt)
95*c8dee2aaSAndroid Build Coastguard Worker        , fResourceProvider(this)
96*c8dee2aaSAndroid Build Coastguard Worker        , fStagingBufferManager(this)
97*c8dee2aaSAndroid Build Coastguard Worker        , fUniformsRingBuffer(this, 128 * 1024, 256, GrGpuBufferType::kUniform)
98*c8dee2aaSAndroid Build Coastguard Worker        , fDisconnected(false) {
99*c8dee2aaSAndroid Build Coastguard Worker    fMtlCaps.reset(new GrMtlCaps(options, fDevice));
100*c8dee2aaSAndroid Build Coastguard Worker    this->initCaps(fMtlCaps);
101*c8dee2aaSAndroid Build Coastguard Worker#if GR_METAL_CAPTURE_COMMANDBUFFER
102*c8dee2aaSAndroid Build Coastguard Worker    this->testingOnly_startCapture();
103*c8dee2aaSAndroid Build Coastguard Worker#endif
104*c8dee2aaSAndroid Build Coastguard Worker    fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue);
105*c8dee2aaSAndroid Build Coastguard Worker}
106*c8dee2aaSAndroid Build Coastguard Worker
107*c8dee2aaSAndroid Build Coastguard WorkerGrMtlGpu::~GrMtlGpu() {
108*c8dee2aaSAndroid Build Coastguard Worker    if (!fDisconnected) {
109*c8dee2aaSAndroid Build Coastguard Worker        this->destroyResources();
110*c8dee2aaSAndroid Build Coastguard Worker    }
111*c8dee2aaSAndroid Build Coastguard Worker}
112*c8dee2aaSAndroid Build Coastguard Worker
113*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::disconnect(DisconnectType type) {
114*c8dee2aaSAndroid Build Coastguard Worker    INHERITED::disconnect(type);
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker    if (!fDisconnected) {
117*c8dee2aaSAndroid Build Coastguard Worker        this->destroyResources();
118*c8dee2aaSAndroid Build Coastguard Worker        fDisconnected = true;
119*c8dee2aaSAndroid Build Coastguard Worker    }
120*c8dee2aaSAndroid Build Coastguard Worker}
121*c8dee2aaSAndroid Build Coastguard Worker
122*c8dee2aaSAndroid Build Coastguard WorkerGrThreadSafePipelineBuilder* GrMtlGpu::pipelineBuilder() {
123*c8dee2aaSAndroid Build Coastguard Worker    return nullptr;
124*c8dee2aaSAndroid Build Coastguard Worker}
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrThreadSafePipelineBuilder> GrMtlGpu::refPipelineBuilder() {
127*c8dee2aaSAndroid Build Coastguard Worker    return nullptr;
128*c8dee2aaSAndroid Build Coastguard Worker}
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::destroyResources() {
131*c8dee2aaSAndroid Build Coastguard Worker    this->submitCommandBuffer(SyncQueue::kForce_SyncQueue);
132*c8dee2aaSAndroid Build Coastguard Worker    // if there's no work we won't release the command buffer, so we do it here
133*c8dee2aaSAndroid Build Coastguard Worker    fCurrentCmdBuffer = nil;
134*c8dee2aaSAndroid Build Coastguard Worker
135*c8dee2aaSAndroid Build Coastguard Worker    // We used a placement new for each object in fOutstandingCommandBuffers, so we're responsible
136*c8dee2aaSAndroid Build Coastguard Worker    // for calling the destructor on each of them as well.
137*c8dee2aaSAndroid Build Coastguard Worker    while (!fOutstandingCommandBuffers.empty()) {
138*c8dee2aaSAndroid Build Coastguard Worker        OutstandingCommandBuffer* buffer =
139*c8dee2aaSAndroid Build Coastguard Worker                (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front();
140*c8dee2aaSAndroid Build Coastguard Worker        // make sure we remove before deleting as deletion might try to kick off another submit
141*c8dee2aaSAndroid Build Coastguard Worker        fOutstandingCommandBuffers.pop_front();
142*c8dee2aaSAndroid Build Coastguard Worker        buffer->~OutstandingCommandBuffer();
143*c8dee2aaSAndroid Build Coastguard Worker    }
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker    fStagingBufferManager.reset();
146*c8dee2aaSAndroid Build Coastguard Worker
147*c8dee2aaSAndroid Build Coastguard Worker    fResourceProvider.destroyResources();
148*c8dee2aaSAndroid Build Coastguard Worker
149*c8dee2aaSAndroid Build Coastguard Worker    fQueue = nil;
150*c8dee2aaSAndroid Build Coastguard Worker    fDevice = nil;
151*c8dee2aaSAndroid Build Coastguard Worker}
152*c8dee2aaSAndroid Build Coastguard Worker
153*c8dee2aaSAndroid Build Coastguard WorkerGrOpsRenderPass* GrMtlGpu::onGetOpsRenderPass(
154*c8dee2aaSAndroid Build Coastguard Worker            GrRenderTarget* renderTarget, bool useMSAASurface, GrAttachment* stencil,
155*c8dee2aaSAndroid Build Coastguard Worker            GrSurfaceOrigin origin, const SkIRect& bounds,
156*c8dee2aaSAndroid Build Coastguard Worker            const GrOpsRenderPass::LoadAndStoreInfo& colorInfo,
157*c8dee2aaSAndroid Build Coastguard Worker            const GrOpsRenderPass::StencilLoadAndStoreInfo& stencilInfo,
158*c8dee2aaSAndroid Build Coastguard Worker            const TArray<GrSurfaceProxy*, true>& sampledProxies,
159*c8dee2aaSAndroid Build Coastguard Worker            GrXferBarrierFlags renderPassXferBarriers) {
160*c8dee2aaSAndroid Build Coastguard Worker    // For the given render target and requested render pass features we need to find a compatible
161*c8dee2aaSAndroid Build Coastguard Worker    // framebuffer to use.
162*c8dee2aaSAndroid Build Coastguard Worker    GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(renderTarget);
163*c8dee2aaSAndroid Build Coastguard Worker
164*c8dee2aaSAndroid Build Coastguard Worker    // TODO: support DMSAA
165*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(!useMSAASurface ||
166*c8dee2aaSAndroid Build Coastguard Worker             (renderTarget->numSamples() > 1));
167*c8dee2aaSAndroid Build Coastguard Worker
168*c8dee2aaSAndroid Build Coastguard Worker    bool withResolve = false;
169*c8dee2aaSAndroid Build Coastguard Worker
170*c8dee2aaSAndroid Build Coastguard Worker    // Figure out if we can use a Resolve store action for this render pass. When we set up
171*c8dee2aaSAndroid Build Coastguard Worker    // the render pass we'll update the color load/store ops since we don't want to ever load
172*c8dee2aaSAndroid Build Coastguard Worker    // or store the msaa color attachment, but may need to for the resolve attachment.
173*c8dee2aaSAndroid Build Coastguard Worker    if (useMSAASurface && this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) {
174*c8dee2aaSAndroid Build Coastguard Worker        withResolve = true;
175*c8dee2aaSAndroid Build Coastguard Worker    }
176*c8dee2aaSAndroid Build Coastguard Worker
177*c8dee2aaSAndroid Build Coastguard Worker    sk_sp<GrMtlFramebuffer> framebuffer =
178*c8dee2aaSAndroid Build Coastguard Worker            sk_ref_sp(mtlRT->getFramebuffer(withResolve, SkToBool(stencil)));
179*c8dee2aaSAndroid Build Coastguard Worker    if (!framebuffer) {
180*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
181*c8dee2aaSAndroid Build Coastguard Worker    }
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker    return new GrMtlOpsRenderPass(this, renderTarget, std::move(framebuffer), origin, colorInfo,
184*c8dee2aaSAndroid Build Coastguard Worker                                  stencilInfo);
185*c8dee2aaSAndroid Build Coastguard Worker}
186*c8dee2aaSAndroid Build Coastguard Worker
187*c8dee2aaSAndroid Build Coastguard WorkerGrMtlCommandBuffer* GrMtlGpu::commandBuffer() {
188*c8dee2aaSAndroid Build Coastguard Worker    if (!fCurrentCmdBuffer) {
189*c8dee2aaSAndroid Build Coastguard Worker#if GR_METAL_CAPTURE_COMMANDBUFFER
190*c8dee2aaSAndroid Build Coastguard Worker        this->testingOnly_startCapture();
191*c8dee2aaSAndroid Build Coastguard Worker#endif
192*c8dee2aaSAndroid Build Coastguard Worker        // Create a new command buffer for the next submit
193*c8dee2aaSAndroid Build Coastguard Worker        fCurrentCmdBuffer = GrMtlCommandBuffer::Make(fQueue);
194*c8dee2aaSAndroid Build Coastguard Worker    }
195*c8dee2aaSAndroid Build Coastguard Worker
196*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(fCurrentCmdBuffer);
197*c8dee2aaSAndroid Build Coastguard Worker    return fCurrentCmdBuffer.get();
198*c8dee2aaSAndroid Build Coastguard Worker}
199*c8dee2aaSAndroid Build Coastguard Worker
200*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::takeOwnershipOfBuffer(sk_sp<GrGpuBuffer> buffer) {
201*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(buffer);
202*c8dee2aaSAndroid Build Coastguard Worker    this->commandBuffer()->addGrBuffer(std::move(buffer));
203*c8dee2aaSAndroid Build Coastguard Worker}
204*c8dee2aaSAndroid Build Coastguard Worker
205*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::submit(GrOpsRenderPass* renderPass) {
206*c8dee2aaSAndroid Build Coastguard Worker    GrMtlOpsRenderPass* mtlRenderPass = reinterpret_cast<GrMtlOpsRenderPass*>(renderPass);
207*c8dee2aaSAndroid Build Coastguard Worker    mtlRenderPass->submit();
208*c8dee2aaSAndroid Build Coastguard Worker    delete renderPass;
209*c8dee2aaSAndroid Build Coastguard Worker}
210*c8dee2aaSAndroid Build Coastguard Worker
211*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
212*c8dee2aaSAndroid Build Coastguard Worker    if (!fCurrentCmdBuffer || !fCurrentCmdBuffer->hasWork()) {
213*c8dee2aaSAndroid Build Coastguard Worker        if (sync == SyncQueue::kForce_SyncQueue) {
214*c8dee2aaSAndroid Build Coastguard Worker            this->finishOutstandingGpuWork();
215*c8dee2aaSAndroid Build Coastguard Worker            this->checkForFinishedCommandBuffers();
216*c8dee2aaSAndroid Build Coastguard Worker        }
217*c8dee2aaSAndroid Build Coastguard Worker        // We need to manually call the finishedCallbacks since we don't add this
218*c8dee2aaSAndroid Build Coastguard Worker        // to the OutstandingCommandBuffer list
219*c8dee2aaSAndroid Build Coastguard Worker        if (fCurrentCmdBuffer) {
220*c8dee2aaSAndroid Build Coastguard Worker            fCurrentCmdBuffer->callFinishedCallbacks();
221*c8dee2aaSAndroid Build Coastguard Worker        }
222*c8dee2aaSAndroid Build Coastguard Worker        return true;
223*c8dee2aaSAndroid Build Coastguard Worker    }
224*c8dee2aaSAndroid Build Coastguard Worker
225*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(fCurrentCmdBuffer);
226*c8dee2aaSAndroid Build Coastguard Worker    bool didCommit = fCurrentCmdBuffer->commit(sync == SyncQueue::kForce_SyncQueue);
227*c8dee2aaSAndroid Build Coastguard Worker    if (didCommit) {
228*c8dee2aaSAndroid Build Coastguard Worker        new (fOutstandingCommandBuffers.push_back()) OutstandingCommandBuffer(fCurrentCmdBuffer);
229*c8dee2aaSAndroid Build Coastguard Worker    }
230*c8dee2aaSAndroid Build Coastguard Worker
231*c8dee2aaSAndroid Build Coastguard Worker    // We don't create a new command buffer here because we may end up using it
232*c8dee2aaSAndroid Build Coastguard Worker    // in the next frame, and that confuses the GPU debugger. Instead we
233*c8dee2aaSAndroid Build Coastguard Worker    // create when we next need one.
234*c8dee2aaSAndroid Build Coastguard Worker    fCurrentCmdBuffer.reset();
235*c8dee2aaSAndroid Build Coastguard Worker
236*c8dee2aaSAndroid Build Coastguard Worker    // If the freeing of any resources held by a finished command buffer causes us to send
237*c8dee2aaSAndroid Build Coastguard Worker    // a new command to the gpu we'll create the new command buffer in commandBuffer(), above.
238*c8dee2aaSAndroid Build Coastguard Worker    this->checkForFinishedCommandBuffers();
239*c8dee2aaSAndroid Build Coastguard Worker
240*c8dee2aaSAndroid Build Coastguard Worker#if GR_METAL_CAPTURE_COMMANDBUFFER
241*c8dee2aaSAndroid Build Coastguard Worker    this->testingOnly_stopCapture();
242*c8dee2aaSAndroid Build Coastguard Worker#endif
243*c8dee2aaSAndroid Build Coastguard Worker    return didCommit;
244*c8dee2aaSAndroid Build Coastguard Worker}
245*c8dee2aaSAndroid Build Coastguard Worker
246*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::checkForFinishedCommandBuffers() {
247*c8dee2aaSAndroid Build Coastguard Worker    // Iterate over all the outstanding command buffers to see if any have finished. The command
248*c8dee2aaSAndroid Build Coastguard Worker    // buffers are in order from oldest to newest, so we start at the front to check if their fence
249*c8dee2aaSAndroid Build Coastguard Worker    // has signaled. If so we pop it off and move onto the next.
250*c8dee2aaSAndroid Build Coastguard Worker    // Repeat till we find a command list that has not finished yet (and all others afterwards are
251*c8dee2aaSAndroid Build Coastguard Worker    // also guaranteed to not have finished).
252*c8dee2aaSAndroid Build Coastguard Worker    OutstandingCommandBuffer* front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front();
253*c8dee2aaSAndroid Build Coastguard Worker    while (front && (*front)->isCompleted()) {
254*c8dee2aaSAndroid Build Coastguard Worker        // Make sure we remove before deleting as deletion might try to kick off another submit
255*c8dee2aaSAndroid Build Coastguard Worker        fOutstandingCommandBuffers.pop_front();
256*c8dee2aaSAndroid Build Coastguard Worker        // Since we used placement new we are responsible for calling the destructor manually.
257*c8dee2aaSAndroid Build Coastguard Worker        front->~OutstandingCommandBuffer();
258*c8dee2aaSAndroid Build Coastguard Worker        front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.front();
259*c8dee2aaSAndroid Build Coastguard Worker    }
260*c8dee2aaSAndroid Build Coastguard Worker}
261*c8dee2aaSAndroid Build Coastguard Worker
262*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::finishOutstandingGpuWork() {
263*c8dee2aaSAndroid Build Coastguard Worker    // wait for the last command buffer we've submitted to finish
264*c8dee2aaSAndroid Build Coastguard Worker    OutstandingCommandBuffer* back =
265*c8dee2aaSAndroid Build Coastguard Worker            (OutstandingCommandBuffer*)fOutstandingCommandBuffers.back();
266*c8dee2aaSAndroid Build Coastguard Worker    if (back) {
267*c8dee2aaSAndroid Build Coastguard Worker        (*back)->waitUntilCompleted();
268*c8dee2aaSAndroid Build Coastguard Worker    }
269*c8dee2aaSAndroid Build Coastguard Worker}
270*c8dee2aaSAndroid Build Coastguard Worker
271*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::addFinishedCallback(sk_sp<skgpu::RefCntedCallback> finishedCallback) {
272*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(finishedCallback);
273*c8dee2aaSAndroid Build Coastguard Worker    // Besides the current commandbuffer, we also add the finishedCallback to the newest outstanding
274*c8dee2aaSAndroid Build Coastguard Worker    // commandbuffer. Our contract for calling the proc is that all previous submitted cmdbuffers
275*c8dee2aaSAndroid Build Coastguard Worker    // have finished when we call it. However, if our current command buffer has no work when it is
276*c8dee2aaSAndroid Build Coastguard Worker    // flushed it will drop its ref to the callback immediately. But the previous work may not have
277*c8dee2aaSAndroid Build Coastguard Worker    // finished. It is safe to only add the proc to the newest outstanding commandbuffer cause that
278*c8dee2aaSAndroid Build Coastguard Worker    // must finish after all previously submitted command buffers.
279*c8dee2aaSAndroid Build Coastguard Worker    OutstandingCommandBuffer* back = (OutstandingCommandBuffer*)fOutstandingCommandBuffers.back();
280*c8dee2aaSAndroid Build Coastguard Worker    if (back) {
281*c8dee2aaSAndroid Build Coastguard Worker        (*back)->addFinishedCallback(finishedCallback);
282*c8dee2aaSAndroid Build Coastguard Worker    }
283*c8dee2aaSAndroid Build Coastguard Worker    commandBuffer()->addFinishedCallback(std::move(finishedCallback));
284*c8dee2aaSAndroid Build Coastguard Worker}
285*c8dee2aaSAndroid Build Coastguard Worker
286*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onSubmitToGpu(const GrSubmitInfo& info) {
287*c8dee2aaSAndroid Build Coastguard Worker    if (info.fSync == GrSyncCpu::kYes) {
288*c8dee2aaSAndroid Build Coastguard Worker        return this->submitCommandBuffer(kForce_SyncQueue);
289*c8dee2aaSAndroid Build Coastguard Worker    } else {
290*c8dee2aaSAndroid Build Coastguard Worker        return this->submitCommandBuffer(kSkip_SyncQueue);
291*c8dee2aaSAndroid Build Coastguard Worker    }
292*c8dee2aaSAndroid Build Coastguard Worker}
293*c8dee2aaSAndroid Build Coastguard Worker
294*c8dee2aaSAndroid Build Coastguard Workerstd::unique_ptr<GrSemaphore> GrMtlGpu::prepareTextureForCrossContextUsage(GrTexture*) {
295*c8dee2aaSAndroid Build Coastguard Worker    this->submitToGpu();
296*c8dee2aaSAndroid Build Coastguard Worker    return nullptr;
297*c8dee2aaSAndroid Build Coastguard Worker}
298*c8dee2aaSAndroid Build Coastguard Worker
299*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrGpuBuffer> GrMtlGpu::onCreateBuffer(size_t size,
300*c8dee2aaSAndroid Build Coastguard Worker                                            GrGpuBufferType type,
301*c8dee2aaSAndroid Build Coastguard Worker                                            GrAccessPattern accessPattern) {
302*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlBuffer::Make(this, size, type, accessPattern);
303*c8dee2aaSAndroid Build Coastguard Worker}
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Workerstatic bool check_max_blit_width(int widthInPixels) {
306*c8dee2aaSAndroid Build Coastguard Worker    if (widthInPixels > 32767) {
307*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(false); // surfaces should not be this wide anyway
308*c8dee2aaSAndroid Build Coastguard Worker        return false;
309*c8dee2aaSAndroid Build Coastguard Worker    }
310*c8dee2aaSAndroid Build Coastguard Worker    return true;
311*c8dee2aaSAndroid Build Coastguard Worker}
312*c8dee2aaSAndroid Build Coastguard Worker
313*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::uploadToTexture(GrMtlTexture* tex,
314*c8dee2aaSAndroid Build Coastguard Worker                               SkIRect rect,
315*c8dee2aaSAndroid Build Coastguard Worker                               GrColorType dataColorType,
316*c8dee2aaSAndroid Build Coastguard Worker                               const GrMipLevel texels[],
317*c8dee2aaSAndroid Build Coastguard Worker                               int mipLevelCount) {
318*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().isFormatTexturable(tex->mtlTexture().pixelFormat));
319*c8dee2aaSAndroid Build Coastguard Worker    // The assumption is either that we have no mipmaps, or that our rect is the entire texture
320*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mipLevelCount == 1 || rect == SkIRect::MakeSize(tex->dimensions()));
321*c8dee2aaSAndroid Build Coastguard Worker
322*c8dee2aaSAndroid Build Coastguard Worker    // We assume that if the texture has mip levels, we either upload to all the levels or just the
323*c8dee2aaSAndroid Build Coastguard Worker    // first.
324*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mipLevelCount == 1 || mipLevelCount == (tex->maxMipmapLevel() + 1));
325*c8dee2aaSAndroid Build Coastguard Worker
326*c8dee2aaSAndroid Build Coastguard Worker    if (!check_max_blit_width(rect.width())) {
327*c8dee2aaSAndroid Build Coastguard Worker        return false;
328*c8dee2aaSAndroid Build Coastguard Worker    }
329*c8dee2aaSAndroid Build Coastguard Worker    if (rect.isEmpty()) {
330*c8dee2aaSAndroid Build Coastguard Worker        return false;
331*c8dee2aaSAndroid Build Coastguard Worker    }
332*c8dee2aaSAndroid Build Coastguard Worker
333*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().surfaceSupportsWritePixels(tex));
334*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().areColorTypeAndFormatCompatible(dataColorType, tex->backendFormat()));
335*c8dee2aaSAndroid Build Coastguard Worker
336*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture();
337*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlTexture);
338*c8dee2aaSAndroid Build Coastguard Worker    // Either upload only the first miplevel or all miplevels
339*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
340*c8dee2aaSAndroid Build Coastguard Worker
341*c8dee2aaSAndroid Build Coastguard Worker    if (mipLevelCount == 1 && !texels[0].fPixels) {
342*c8dee2aaSAndroid Build Coastguard Worker        return true;   // no data to upload
343*c8dee2aaSAndroid Build Coastguard Worker    }
344*c8dee2aaSAndroid Build Coastguard Worker
345*c8dee2aaSAndroid Build Coastguard Worker    for (int i = 0; i < mipLevelCount; ++i) {
346*c8dee2aaSAndroid Build Coastguard Worker        // We do not allow any gaps in the mip data
347*c8dee2aaSAndroid Build Coastguard Worker        if (!texels[i].fPixels) {
348*c8dee2aaSAndroid Build Coastguard Worker            return false;
349*c8dee2aaSAndroid Build Coastguard Worker        }
350*c8dee2aaSAndroid Build Coastguard Worker    }
351*c8dee2aaSAndroid Build Coastguard Worker
352*c8dee2aaSAndroid Build Coastguard Worker    size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
353*c8dee2aaSAndroid Build Coastguard Worker
354*c8dee2aaSAndroid Build Coastguard Worker    TArray<size_t> individualMipOffsets(mipLevelCount);
355*c8dee2aaSAndroid Build Coastguard Worker    size_t combinedBufferSize = GrComputeTightCombinedBufferSize(bpp,
356*c8dee2aaSAndroid Build Coastguard Worker                                                                 rect.size(),
357*c8dee2aaSAndroid Build Coastguard Worker                                                                 &individualMipOffsets,
358*c8dee2aaSAndroid Build Coastguard Worker                                                                 mipLevelCount);
359*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(combinedBufferSize);
360*c8dee2aaSAndroid Build Coastguard Worker
361*c8dee2aaSAndroid Build Coastguard Worker
362*c8dee2aaSAndroid Build Coastguard Worker    // offset value must be a multiple of the destination texture's pixel size in bytes
363*c8dee2aaSAndroid Build Coastguard Worker    size_t alignment = std::max(bpp, this->mtlCaps().getMinBufferAlignment());
364*c8dee2aaSAndroid Build Coastguard Worker    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
365*c8dee2aaSAndroid Build Coastguard Worker            combinedBufferSize, alignment);
366*c8dee2aaSAndroid Build Coastguard Worker    if (!slice.fBuffer) {
367*c8dee2aaSAndroid Build Coastguard Worker        return false;
368*c8dee2aaSAndroid Build Coastguard Worker    }
369*c8dee2aaSAndroid Build Coastguard Worker    char* bufferData = (char*)slice.fOffsetMapPtr;
370*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
371*c8dee2aaSAndroid Build Coastguard Worker
372*c8dee2aaSAndroid Build Coastguard Worker    int currentWidth = rect.width();
373*c8dee2aaSAndroid Build Coastguard Worker    int currentHeight = rect.height();
374*c8dee2aaSAndroid Build Coastguard Worker    SkDEBUGCODE(int layerHeight = tex->height());
375*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(rect.left(), rect.top(), 0);
376*c8dee2aaSAndroid Build Coastguard Worker
377*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
378*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
379*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
380*c8dee2aaSAndroid Build Coastguard Worker        return false;
381*c8dee2aaSAndroid Build Coastguard Worker    }
382*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
383*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"uploadToTexture"];
384*c8dee2aaSAndroid Build Coastguard Worker#endif
385*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
386*c8dee2aaSAndroid Build Coastguard Worker        if (texels[currentMipLevel].fPixels) {
387*c8dee2aaSAndroid Build Coastguard Worker            SkASSERT(1 == mipLevelCount || currentHeight == layerHeight);
388*c8dee2aaSAndroid Build Coastguard Worker            const size_t trimRowBytes = currentWidth * bpp;
389*c8dee2aaSAndroid Build Coastguard Worker            const size_t rowBytes = texels[currentMipLevel].fRowBytes;
390*c8dee2aaSAndroid Build Coastguard Worker
391*c8dee2aaSAndroid Build Coastguard Worker            // copy data into the buffer, skipping any trailing bytes
392*c8dee2aaSAndroid Build Coastguard Worker            char* dst = bufferData + individualMipOffsets[currentMipLevel];
393*c8dee2aaSAndroid Build Coastguard Worker            const char* src = (const char*)texels[currentMipLevel].fPixels;
394*c8dee2aaSAndroid Build Coastguard Worker            SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight);
395*c8dee2aaSAndroid Build Coastguard Worker
396*c8dee2aaSAndroid Build Coastguard Worker            [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
397*c8dee2aaSAndroid Build Coastguard Worker                              sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel]
398*c8dee2aaSAndroid Build Coastguard Worker                         sourceBytesPerRow: trimRowBytes
399*c8dee2aaSAndroid Build Coastguard Worker                       sourceBytesPerImage: trimRowBytes*currentHeight
400*c8dee2aaSAndroid Build Coastguard Worker                                sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
401*c8dee2aaSAndroid Build Coastguard Worker                                 toTexture: mtlTexture
402*c8dee2aaSAndroid Build Coastguard Worker                          destinationSlice: 0
403*c8dee2aaSAndroid Build Coastguard Worker                          destinationLevel: currentMipLevel
404*c8dee2aaSAndroid Build Coastguard Worker                         destinationOrigin: origin];
405*c8dee2aaSAndroid Build Coastguard Worker        }
406*c8dee2aaSAndroid Build Coastguard Worker        currentWidth = std::max(1, currentWidth/2);
407*c8dee2aaSAndroid Build Coastguard Worker        currentHeight = std::max(1, currentHeight/2);
408*c8dee2aaSAndroid Build Coastguard Worker        SkDEBUGCODE(layerHeight = currentHeight);
409*c8dee2aaSAndroid Build Coastguard Worker    }
410*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
411*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().isMac()) {
412*c8dee2aaSAndroid Build Coastguard Worker        [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)];
413*c8dee2aaSAndroid Build Coastguard Worker    }
414*c8dee2aaSAndroid Build Coastguard Worker#endif
415*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
416*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
417*c8dee2aaSAndroid Build Coastguard Worker#endif
418*c8dee2aaSAndroid Build Coastguard Worker
419*c8dee2aaSAndroid Build Coastguard Worker    if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
420*c8dee2aaSAndroid Build Coastguard Worker        tex->markMipmapsDirty();
421*c8dee2aaSAndroid Build Coastguard Worker    }
422*c8dee2aaSAndroid Build Coastguard Worker
423*c8dee2aaSAndroid Build Coastguard Worker    return true;
424*c8dee2aaSAndroid Build Coastguard Worker}
425*c8dee2aaSAndroid Build Coastguard Worker
426*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::clearTexture(GrMtlTexture* tex, size_t bpp, uint32_t levelMask) {
427*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().isFormatTexturable(tex->mtlTexture().pixelFormat));
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker    if (!levelMask) {
430*c8dee2aaSAndroid Build Coastguard Worker        return true;
431*c8dee2aaSAndroid Build Coastguard Worker    }
432*c8dee2aaSAndroid Build Coastguard Worker
433*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture();
434*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlTexture);
435*c8dee2aaSAndroid Build Coastguard Worker    // Either upload only the first miplevel or all miplevels
436*c8dee2aaSAndroid Build Coastguard Worker    int mipLevelCount = (int)mtlTexture.mipmapLevelCount;
437*c8dee2aaSAndroid Build Coastguard Worker
438*c8dee2aaSAndroid Build Coastguard Worker    TArray<size_t> individualMipOffsets(mipLevelCount);
439*c8dee2aaSAndroid Build Coastguard Worker    size_t combinedBufferSize = 0;
440*c8dee2aaSAndroid Build Coastguard Worker    int currentWidth = tex->width();
441*c8dee2aaSAndroid Build Coastguard Worker    int currentHeight = tex->height();
442*c8dee2aaSAndroid Build Coastguard Worker
443*c8dee2aaSAndroid Build Coastguard Worker    // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
444*c8dee2aaSAndroid Build Coastguard Worker    // config. This works with the assumption that the bytes in pixel config is always a power of 2.
445*c8dee2aaSAndroid Build Coastguard Worker    // TODO: can we just copy from a single buffer the size of the largest cleared level w/o a perf
446*c8dee2aaSAndroid Build Coastguard Worker    // penalty?
447*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT((bpp & (bpp - 1)) == 0);
448*c8dee2aaSAndroid Build Coastguard Worker    const size_t alignmentMask = 0x3 | (bpp - 1);
449*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
450*c8dee2aaSAndroid Build Coastguard Worker        if (levelMask & (1 << currentMipLevel)) {
451*c8dee2aaSAndroid Build Coastguard Worker            const size_t trimmedSize = currentWidth * bpp * currentHeight;
452*c8dee2aaSAndroid Build Coastguard Worker            const size_t alignmentDiff = combinedBufferSize & alignmentMask;
453*c8dee2aaSAndroid Build Coastguard Worker            if (alignmentDiff != 0) {
454*c8dee2aaSAndroid Build Coastguard Worker                combinedBufferSize += alignmentMask - alignmentDiff + 1;
455*c8dee2aaSAndroid Build Coastguard Worker            }
456*c8dee2aaSAndroid Build Coastguard Worker            individualMipOffsets.push_back(combinedBufferSize);
457*c8dee2aaSAndroid Build Coastguard Worker            combinedBufferSize += trimmedSize;
458*c8dee2aaSAndroid Build Coastguard Worker        }
459*c8dee2aaSAndroid Build Coastguard Worker        currentWidth = std::max(1, currentWidth/2);
460*c8dee2aaSAndroid Build Coastguard Worker        currentHeight = std::max(1, currentHeight/2);
461*c8dee2aaSAndroid Build Coastguard Worker    }
462*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(combinedBufferSize > 0 && !individualMipOffsets.empty());
463*c8dee2aaSAndroid Build Coastguard Worker
464*c8dee2aaSAndroid Build Coastguard Worker    size_t alignment = std::max(bpp, this->mtlCaps().getMinBufferAlignment());
465*c8dee2aaSAndroid Build Coastguard Worker    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
466*c8dee2aaSAndroid Build Coastguard Worker            combinedBufferSize, alignment);
467*c8dee2aaSAndroid Build Coastguard Worker    if (!slice.fBuffer) {
468*c8dee2aaSAndroid Build Coastguard Worker        return false;
469*c8dee2aaSAndroid Build Coastguard Worker    }
470*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
471*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBuffer> transferBuffer = mtlBuffer->mtlBuffer();
472*c8dee2aaSAndroid Build Coastguard Worker
473*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
474*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
475*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
476*c8dee2aaSAndroid Build Coastguard Worker        return false;
477*c8dee2aaSAndroid Build Coastguard Worker    }
478*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
479*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"clearTexture"];
480*c8dee2aaSAndroid Build Coastguard Worker#endif
481*c8dee2aaSAndroid Build Coastguard Worker    // clear the buffer to transparent black
482*c8dee2aaSAndroid Build Coastguard Worker    NSRange clearRange;
483*c8dee2aaSAndroid Build Coastguard Worker    clearRange.location = 0;
484*c8dee2aaSAndroid Build Coastguard Worker    clearRange.length = combinedBufferSize;
485*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder fillBuffer: transferBuffer
486*c8dee2aaSAndroid Build Coastguard Worker                         range: clearRange
487*c8dee2aaSAndroid Build Coastguard Worker                         value: 0];
488*c8dee2aaSAndroid Build Coastguard Worker
489*c8dee2aaSAndroid Build Coastguard Worker    // now copy buffer to texture
490*c8dee2aaSAndroid Build Coastguard Worker    currentWidth = tex->width();
491*c8dee2aaSAndroid Build Coastguard Worker    currentHeight = tex->height();
492*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(0, 0, 0);
493*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
494*c8dee2aaSAndroid Build Coastguard Worker        if (levelMask & (1 << currentMipLevel)) {
495*c8dee2aaSAndroid Build Coastguard Worker            const size_t rowBytes = currentWidth * bpp;
496*c8dee2aaSAndroid Build Coastguard Worker
497*c8dee2aaSAndroid Build Coastguard Worker            [blitCmdEncoder copyFromBuffer: transferBuffer
498*c8dee2aaSAndroid Build Coastguard Worker                              sourceOffset: individualMipOffsets[currentMipLevel]
499*c8dee2aaSAndroid Build Coastguard Worker                         sourceBytesPerRow: rowBytes
500*c8dee2aaSAndroid Build Coastguard Worker                       sourceBytesPerImage: rowBytes * currentHeight
501*c8dee2aaSAndroid Build Coastguard Worker                                sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
502*c8dee2aaSAndroid Build Coastguard Worker                                 toTexture: mtlTexture
503*c8dee2aaSAndroid Build Coastguard Worker                          destinationSlice: 0
504*c8dee2aaSAndroid Build Coastguard Worker                          destinationLevel: currentMipLevel
505*c8dee2aaSAndroid Build Coastguard Worker                         destinationOrigin: origin];
506*c8dee2aaSAndroid Build Coastguard Worker        }
507*c8dee2aaSAndroid Build Coastguard Worker        currentWidth = std::max(1, currentWidth/2);
508*c8dee2aaSAndroid Build Coastguard Worker        currentHeight = std::max(1, currentHeight/2);
509*c8dee2aaSAndroid Build Coastguard Worker    }
510*c8dee2aaSAndroid Build Coastguard Worker    // Don't need didModifyRange: here because fillBuffer: happens on the GPU
511*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
512*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
513*c8dee2aaSAndroid Build Coastguard Worker#endif
514*c8dee2aaSAndroid Build Coastguard Worker
515*c8dee2aaSAndroid Build Coastguard Worker    if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
516*c8dee2aaSAndroid Build Coastguard Worker        tex->markMipmapsDirty();
517*c8dee2aaSAndroid Build Coastguard Worker    }
518*c8dee2aaSAndroid Build Coastguard Worker
519*c8dee2aaSAndroid Build Coastguard Worker    return true;
520*c8dee2aaSAndroid Build Coastguard Worker}
521*c8dee2aaSAndroid Build Coastguard Worker
522*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrAttachment> GrMtlGpu::makeStencilAttachment(const GrBackendFormat& /*colorFormat*/,
523*c8dee2aaSAndroid Build Coastguard Worker                                                    SkISize dimensions, int numStencilSamples) {
524*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat sFmt = this->mtlCaps().preferredStencilFormat();
525*c8dee2aaSAndroid Build Coastguard Worker
526*c8dee2aaSAndroid Build Coastguard Worker    fStats.incStencilAttachmentCreates();
527*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlAttachment::GrMtlAttachment::MakeStencil(this, dimensions, numStencilSamples, sFmt);
528*c8dee2aaSAndroid Build Coastguard Worker}
529*c8dee2aaSAndroid Build Coastguard Worker
530*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrAttachment> GrMtlGpu::makeMSAAAttachment(SkISize dimensions,
531*c8dee2aaSAndroid Build Coastguard Worker                                                 const GrBackendFormat& format,
532*c8dee2aaSAndroid Build Coastguard Worker                                                 int numSamples,
533*c8dee2aaSAndroid Build Coastguard Worker                                                 GrProtected isProtected,
534*c8dee2aaSAndroid Build Coastguard Worker                                                 GrMemoryless isMemoryless) {
535*c8dee2aaSAndroid Build Coastguard Worker    // Metal doesn't support protected textures
536*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(isProtected == GrProtected::kNo);
537*c8dee2aaSAndroid Build Coastguard Worker    // TODO: add memoryless support
538*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(isMemoryless == GrMemoryless::kNo);
539*c8dee2aaSAndroid Build Coastguard Worker
540*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat pixelFormat = (MTLPixelFormat)GrBackendFormats::AsMtlFormat(format);
541*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(pixelFormat != MTLPixelFormatInvalid);
542*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(!skgpu::MtlFormatIsCompressed(pixelFormat));
543*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().isFormatRenderable(pixelFormat, numSamples));
544*c8dee2aaSAndroid Build Coastguard Worker
545*c8dee2aaSAndroid Build Coastguard Worker    fStats.incMSAAAttachmentCreates();
546*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlAttachment::MakeMSAA(this, dimensions, numSamples, pixelFormat);
547*c8dee2aaSAndroid Build Coastguard Worker}
548*c8dee2aaSAndroid Build Coastguard Worker
549*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrTexture> GrMtlGpu::onCreateTexture(SkISize dimensions,
550*c8dee2aaSAndroid Build Coastguard Worker                                           const GrBackendFormat& format,
551*c8dee2aaSAndroid Build Coastguard Worker                                           GrRenderable renderable,
552*c8dee2aaSAndroid Build Coastguard Worker                                           int renderTargetSampleCnt,
553*c8dee2aaSAndroid Build Coastguard Worker                                           skgpu::Budgeted budgeted,
554*c8dee2aaSAndroid Build Coastguard Worker                                           GrProtected isProtected,
555*c8dee2aaSAndroid Build Coastguard Worker                                           int mipLevelCount,
556*c8dee2aaSAndroid Build Coastguard Worker                                           uint32_t levelClearMask,
557*c8dee2aaSAndroid Build Coastguard Worker                                           std::string_view label) {
558*c8dee2aaSAndroid Build Coastguard Worker    // We don't support protected textures in Metal.
559*c8dee2aaSAndroid Build Coastguard Worker    if (isProtected == GrProtected::kYes) {
560*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
561*c8dee2aaSAndroid Build Coastguard Worker    }
562*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mipLevelCount > 0);
563*c8dee2aaSAndroid Build Coastguard Worker
564*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format);
565*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlPixelFormat != MTLPixelFormatInvalid);
566*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(!this->caps()->isFormatCompressed(format));
567*c8dee2aaSAndroid Build Coastguard Worker
568*c8dee2aaSAndroid Build Coastguard Worker    sk_sp<GrMtlTexture> tex;
569*c8dee2aaSAndroid Build Coastguard Worker    GrMipmapStatus mipmapStatus =
570*c8dee2aaSAndroid Build Coastguard Worker            mipLevelCount > 1 ? GrMipmapStatus::kDirty : GrMipmapStatus::kNotAllocated;
571*c8dee2aaSAndroid Build Coastguard Worker    if (renderable == GrRenderable::kYes) {
572*c8dee2aaSAndroid Build Coastguard Worker        tex = GrMtlTextureRenderTarget::MakeNewTextureRenderTarget(
573*c8dee2aaSAndroid Build Coastguard Worker                this, budgeted, dimensions, renderTargetSampleCnt, mtlPixelFormat, mipLevelCount,
574*c8dee2aaSAndroid Build Coastguard Worker                mipmapStatus, label);
575*c8dee2aaSAndroid Build Coastguard Worker    } else {
576*c8dee2aaSAndroid Build Coastguard Worker        tex = GrMtlTexture::MakeNewTexture(this, budgeted, dimensions, mtlPixelFormat,
577*c8dee2aaSAndroid Build Coastguard Worker                                           mipLevelCount, mipmapStatus, label);
578*c8dee2aaSAndroid Build Coastguard Worker    }
579*c8dee2aaSAndroid Build Coastguard Worker
580*c8dee2aaSAndroid Build Coastguard Worker    if (!tex) {
581*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
582*c8dee2aaSAndroid Build Coastguard Worker    }
583*c8dee2aaSAndroid Build Coastguard Worker
584*c8dee2aaSAndroid Build Coastguard Worker    if (levelClearMask) {
585*c8dee2aaSAndroid Build Coastguard Worker        this->clearTexture(tex.get(),
586*c8dee2aaSAndroid Build Coastguard Worker                           skgpu::MtlFormatBytesPerBlock(mtlPixelFormat),
587*c8dee2aaSAndroid Build Coastguard Worker                           levelClearMask);
588*c8dee2aaSAndroid Build Coastguard Worker    }
589*c8dee2aaSAndroid Build Coastguard Worker
590*c8dee2aaSAndroid Build Coastguard Worker    return std::move(tex);
591*c8dee2aaSAndroid Build Coastguard Worker}
592*c8dee2aaSAndroid Build Coastguard Worker
593*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrTexture> GrMtlGpu::onCreateCompressedTexture(SkISize dimensions,
594*c8dee2aaSAndroid Build Coastguard Worker                                                     const GrBackendFormat& format,
595*c8dee2aaSAndroid Build Coastguard Worker                                                     skgpu::Budgeted budgeted,
596*c8dee2aaSAndroid Build Coastguard Worker                                                     skgpu::Mipmapped mipmapped,
597*c8dee2aaSAndroid Build Coastguard Worker                                                     GrProtected isProtected,
598*c8dee2aaSAndroid Build Coastguard Worker                                                     const void* data,
599*c8dee2aaSAndroid Build Coastguard Worker                                                     size_t dataSize) {
600*c8dee2aaSAndroid Build Coastguard Worker    // We don't support protected textures in Metal.
601*c8dee2aaSAndroid Build Coastguard Worker    if (isProtected == GrProtected::kYes) {
602*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
603*c8dee2aaSAndroid Build Coastguard Worker    }
604*c8dee2aaSAndroid Build Coastguard Worker
605*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->caps()->isFormatTexturable(format, GrTextureType::k2D));
606*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(data);
607*c8dee2aaSAndroid Build Coastguard Worker
608*c8dee2aaSAndroid Build Coastguard Worker    if (!check_max_blit_width(dimensions.width())) {
609*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
610*c8dee2aaSAndroid Build Coastguard Worker    }
611*c8dee2aaSAndroid Build Coastguard Worker
612*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat mtlPixelFormat = GrBackendFormatAsMTLPixelFormat(format);
613*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->caps()->isFormatCompressed(format));
614*c8dee2aaSAndroid Build Coastguard Worker
615*c8dee2aaSAndroid Build Coastguard Worker    int numMipLevels = 1;
616*c8dee2aaSAndroid Build Coastguard Worker    if (mipmapped == skgpu::Mipmapped::kYes) {
617*c8dee2aaSAndroid Build Coastguard Worker        numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
618*c8dee2aaSAndroid Build Coastguard Worker    }
619*c8dee2aaSAndroid Build Coastguard Worker
620*c8dee2aaSAndroid Build Coastguard Worker    GrMipmapStatus mipmapStatus = (mipmapped == skgpu::Mipmapped::kYes)
621*c8dee2aaSAndroid Build Coastguard Worker                                          ? GrMipmapStatus::kValid
622*c8dee2aaSAndroid Build Coastguard Worker                                          : GrMipmapStatus::kNotAllocated;
623*c8dee2aaSAndroid Build Coastguard Worker
624*c8dee2aaSAndroid Build Coastguard Worker    auto tex = GrMtlTexture::MakeNewTexture(this, budgeted, dimensions, mtlPixelFormat,
625*c8dee2aaSAndroid Build Coastguard Worker                                            numMipLevels, mipmapStatus,
626*c8dee2aaSAndroid Build Coastguard Worker                                            /*label=*/"MtlGpu_CreateCompressedTexture");
627*c8dee2aaSAndroid Build Coastguard Worker    if (!tex) {
628*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
629*c8dee2aaSAndroid Build Coastguard Worker    }
630*c8dee2aaSAndroid Build Coastguard Worker
631*c8dee2aaSAndroid Build Coastguard Worker    // Upload to texture
632*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = tex->mtlTexture();
633*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlTexture);
634*c8dee2aaSAndroid Build Coastguard Worker
635*c8dee2aaSAndroid Build Coastguard Worker    auto compressionType = GrBackendFormatToCompressionType(format);
636*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(compressionType != SkTextureCompressionType::kNone);
637*c8dee2aaSAndroid Build Coastguard Worker
638*c8dee2aaSAndroid Build Coastguard Worker    TArray<size_t> individualMipOffsets(numMipLevels);
639*c8dee2aaSAndroid Build Coastguard Worker    SkDEBUGCODE(size_t combinedBufferSize =)
640*c8dee2aaSAndroid Build Coastguard Worker            SkCompressedDataSize(compressionType,
641*c8dee2aaSAndroid Build Coastguard Worker                                 dimensions,
642*c8dee2aaSAndroid Build Coastguard Worker                                 &individualMipOffsets,
643*c8dee2aaSAndroid Build Coastguard Worker                                 mipmapped == skgpu::Mipmapped::kYes);
644*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(individualMipOffsets.size() == numMipLevels);
645*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(dataSize == combinedBufferSize);
646*c8dee2aaSAndroid Build Coastguard Worker
647*c8dee2aaSAndroid Build Coastguard Worker    // offset value must be a multiple of the destination texture's pixel size in bytes
648*c8dee2aaSAndroid Build Coastguard Worker    // for compressed textures, this is the block size
649*c8dee2aaSAndroid Build Coastguard Worker    size_t alignment = SkCompressedBlockSize(compressionType);
650*c8dee2aaSAndroid Build Coastguard Worker    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
651*c8dee2aaSAndroid Build Coastguard Worker            dataSize, alignment);
652*c8dee2aaSAndroid Build Coastguard Worker    if (!slice.fBuffer) {
653*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
654*c8dee2aaSAndroid Build Coastguard Worker    }
655*c8dee2aaSAndroid Build Coastguard Worker    char* bufferData = (char*)slice.fOffsetMapPtr;
656*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
657*c8dee2aaSAndroid Build Coastguard Worker
658*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(0, 0, 0);
659*c8dee2aaSAndroid Build Coastguard Worker
660*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
661*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
662*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
663*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
664*c8dee2aaSAndroid Build Coastguard Worker    }
665*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
666*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"onCreateCompressedTexture"];
667*c8dee2aaSAndroid Build Coastguard Worker#endif
668*c8dee2aaSAndroid Build Coastguard Worker
669*c8dee2aaSAndroid Build Coastguard Worker    // copy data into the buffer, skipping any trailing bytes
670*c8dee2aaSAndroid Build Coastguard Worker    memcpy(bufferData, data, dataSize);
671*c8dee2aaSAndroid Build Coastguard Worker
672*c8dee2aaSAndroid Build Coastguard Worker    SkISize levelDimensions = dimensions;
673*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
674*c8dee2aaSAndroid Build Coastguard Worker        const size_t levelRowBytes = skgpu::CompressedRowBytes(compressionType,
675*c8dee2aaSAndroid Build Coastguard Worker                                                               levelDimensions.width());
676*c8dee2aaSAndroid Build Coastguard Worker        size_t levelSize = SkCompressedDataSize(compressionType, levelDimensions, nullptr, false);
677*c8dee2aaSAndroid Build Coastguard Worker
678*c8dee2aaSAndroid Build Coastguard Worker        // TODO: can this all be done in one go?
679*c8dee2aaSAndroid Build Coastguard Worker        [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
680*c8dee2aaSAndroid Build Coastguard Worker                          sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel]
681*c8dee2aaSAndroid Build Coastguard Worker                     sourceBytesPerRow: levelRowBytes
682*c8dee2aaSAndroid Build Coastguard Worker                   sourceBytesPerImage: levelSize
683*c8dee2aaSAndroid Build Coastguard Worker                            sourceSize: MTLSizeMake(levelDimensions.width(),
684*c8dee2aaSAndroid Build Coastguard Worker                                                    levelDimensions.height(), 1)
685*c8dee2aaSAndroid Build Coastguard Worker                             toTexture: mtlTexture
686*c8dee2aaSAndroid Build Coastguard Worker                      destinationSlice: 0
687*c8dee2aaSAndroid Build Coastguard Worker                      destinationLevel: currentMipLevel
688*c8dee2aaSAndroid Build Coastguard Worker                     destinationOrigin: origin];
689*c8dee2aaSAndroid Build Coastguard Worker
690*c8dee2aaSAndroid Build Coastguard Worker        levelDimensions = {std::max(1, levelDimensions.width() /2),
691*c8dee2aaSAndroid Build Coastguard Worker                           std::max(1, levelDimensions.height()/2)};
692*c8dee2aaSAndroid Build Coastguard Worker    }
693*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
694*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().isMac()) {
695*c8dee2aaSAndroid Build Coastguard Worker        [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, dataSize)];
696*c8dee2aaSAndroid Build Coastguard Worker    }
697*c8dee2aaSAndroid Build Coastguard Worker#endif
698*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
699*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
700*c8dee2aaSAndroid Build Coastguard Worker#endif
701*c8dee2aaSAndroid Build Coastguard Worker
702*c8dee2aaSAndroid Build Coastguard Worker    return std::move(tex);
703*c8dee2aaSAndroid Build Coastguard Worker}
704*c8dee2aaSAndroid Build Coastguard Worker
705*c8dee2aaSAndroid Build Coastguard Worker// TODO: Extra retain/release can't be avoided here because of GetMtlTextureInfo copying the
706*c8dee2aaSAndroid Build Coastguard Worker// sk_cfp. It would be useful to have a (possibly-internal-only?) API to get the raw pointer.
707*c8dee2aaSAndroid Build Coastguard Workerstatic id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex) {
708*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo textureInfo;
709*c8dee2aaSAndroid Build Coastguard Worker    if (!GrBackendTextures::GetMtlTextureInfo(backendTex, &textureInfo)) {
710*c8dee2aaSAndroid Build Coastguard Worker        return nil;
711*c8dee2aaSAndroid Build Coastguard Worker    }
712*c8dee2aaSAndroid Build Coastguard Worker    return GrGetMTLTexture(textureInfo.fTexture.get());
713*c8dee2aaSAndroid Build Coastguard Worker}
714*c8dee2aaSAndroid Build Coastguard Worker
715*c8dee2aaSAndroid Build Coastguard Workerstatic id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
716*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo textureInfo;
717*c8dee2aaSAndroid Build Coastguard Worker    if (!GrBackendRenderTargets::GetMtlTextureInfo(backendRT, &textureInfo)) {
718*c8dee2aaSAndroid Build Coastguard Worker        return nil;
719*c8dee2aaSAndroid Build Coastguard Worker    }
720*c8dee2aaSAndroid Build Coastguard Worker    return GrGetMTLTexture(textureInfo.fTexture.get());
721*c8dee2aaSAndroid Build Coastguard Worker}
722*c8dee2aaSAndroid Build Coastguard Worker
723*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
724*c8dee2aaSAndroid Build Coastguard Worker                                                GrWrapOwnership,
725*c8dee2aaSAndroid Build Coastguard Worker                                                GrWrapCacheable cacheable,
726*c8dee2aaSAndroid Build Coastguard Worker                                                GrIOType ioType) {
727*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex);
728*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
729*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
730*c8dee2aaSAndroid Build Coastguard Worker    }
731*c8dee2aaSAndroid Build Coastguard Worker    // We don't currently support sampling from a MSAA texture in shaders.
732*c8dee2aaSAndroid Build Coastguard Worker    if (mtlTexture.sampleCount != 1) {
733*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
734*c8dee2aaSAndroid Build Coastguard Worker    }
735*c8dee2aaSAndroid Build Coastguard Worker
736*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlTexture::MakeWrappedTexture(this, backendTex.dimensions(), mtlTexture, cacheable,
737*c8dee2aaSAndroid Build Coastguard Worker                                            ioType);
738*c8dee2aaSAndroid Build Coastguard Worker}
739*c8dee2aaSAndroid Build Coastguard Worker
740*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrTexture> GrMtlGpu::onWrapCompressedBackendTexture(const GrBackendTexture& backendTex,
741*c8dee2aaSAndroid Build Coastguard Worker                                                          GrWrapOwnership,
742*c8dee2aaSAndroid Build Coastguard Worker                                                          GrWrapCacheable cacheable) {
743*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex);
744*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
745*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
746*c8dee2aaSAndroid Build Coastguard Worker    }
747*c8dee2aaSAndroid Build Coastguard Worker    // We don't currently support sampling from a MSAA texture in shaders.
748*c8dee2aaSAndroid Build Coastguard Worker    if (mtlTexture.sampleCount != 1) {
749*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
750*c8dee2aaSAndroid Build Coastguard Worker    }
751*c8dee2aaSAndroid Build Coastguard Worker
752*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlTexture::MakeWrappedTexture(this, backendTex.dimensions(), mtlTexture, cacheable,
753*c8dee2aaSAndroid Build Coastguard Worker                                            kRead_GrIOType);
754*c8dee2aaSAndroid Build Coastguard Worker}
755*c8dee2aaSAndroid Build Coastguard Worker
756*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
757*c8dee2aaSAndroid Build Coastguard Worker                                                          int sampleCnt,
758*c8dee2aaSAndroid Build Coastguard Worker                                                          GrWrapOwnership,
759*c8dee2aaSAndroid Build Coastguard Worker                                                          GrWrapCacheable cacheable) {
760*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex);
761*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
762*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
763*c8dee2aaSAndroid Build Coastguard Worker    }
764*c8dee2aaSAndroid Build Coastguard Worker    // We don't currently support sampling from a MSAA texture in shaders.
765*c8dee2aaSAndroid Build Coastguard Worker    if (mtlTexture.sampleCount != 1) {
766*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
767*c8dee2aaSAndroid Build Coastguard Worker    }
768*c8dee2aaSAndroid Build Coastguard Worker
769*c8dee2aaSAndroid Build Coastguard Worker    const GrMtlCaps& caps = this->mtlCaps();
770*c8dee2aaSAndroid Build Coastguard Worker
771*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat format = mtlTexture.pixelFormat;
772*c8dee2aaSAndroid Build Coastguard Worker    if (!caps.isFormatRenderable(format, sampleCnt)) {
773*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
774*c8dee2aaSAndroid Build Coastguard Worker    }
775*c8dee2aaSAndroid Build Coastguard Worker
776*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
777*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
778*c8dee2aaSAndroid Build Coastguard Worker    }
779*c8dee2aaSAndroid Build Coastguard Worker
780*c8dee2aaSAndroid Build Coastguard Worker    sampleCnt = caps.getRenderTargetSampleCount(sampleCnt, format);
781*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(sampleCnt);
782*c8dee2aaSAndroid Build Coastguard Worker
783*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(
784*c8dee2aaSAndroid Build Coastguard Worker            this, backendTex.dimensions(), sampleCnt, mtlTexture, cacheable);
785*c8dee2aaSAndroid Build Coastguard Worker}
786*c8dee2aaSAndroid Build Coastguard Worker
787*c8dee2aaSAndroid Build Coastguard Workersk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
788*c8dee2aaSAndroid Build Coastguard Worker    if (!this->caps()->isFormatRenderable(backendRT.getBackendFormat(), backendRT.sampleCnt())) {
789*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
790*c8dee2aaSAndroid Build Coastguard Worker    }
791*c8dee2aaSAndroid Build Coastguard Worker
792*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
793*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
794*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
795*c8dee2aaSAndroid Build Coastguard Worker    }
796*c8dee2aaSAndroid Build Coastguard Worker
797*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
798*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
799*c8dee2aaSAndroid Build Coastguard Worker    }
800*c8dee2aaSAndroid Build Coastguard Worker
801*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlRenderTarget::MakeWrappedRenderTarget(this, backendRT.dimensions(),
802*c8dee2aaSAndroid Build Coastguard Worker                                                      backendRT.sampleCnt(), mtlTexture);
803*c8dee2aaSAndroid Build Coastguard Worker}
804*c8dee2aaSAndroid Build Coastguard Worker
805*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onRegenerateMipMapLevels(GrTexture* texture) {
806*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture);
807*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = grMtlTexture->mtlTexture();
808*c8dee2aaSAndroid Build Coastguard Worker
809*c8dee2aaSAndroid Build Coastguard Worker    // Automatic mipmap generation is only supported by color-renderable formats
810*c8dee2aaSAndroid Build Coastguard Worker    if (!fMtlCaps->isFormatRenderable(mtlTexture.pixelFormat, 1) &&
811*c8dee2aaSAndroid Build Coastguard Worker        // We have pixel configs marked as textureable-only that use RGBA8 as the internal format
812*c8dee2aaSAndroid Build Coastguard Worker        MTLPixelFormatRGBA8Unorm != mtlTexture.pixelFormat) {
813*c8dee2aaSAndroid Build Coastguard Worker        return false;
814*c8dee2aaSAndroid Build Coastguard Worker    }
815*c8dee2aaSAndroid Build Coastguard Worker
816*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
817*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
818*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
819*c8dee2aaSAndroid Build Coastguard Worker        return false;
820*c8dee2aaSAndroid Build Coastguard Worker    }
821*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder generateMipmapsForTexture: mtlTexture];
822*c8dee2aaSAndroid Build Coastguard Worker    this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(grMtlTexture->attachment()));
823*c8dee2aaSAndroid Build Coastguard Worker
824*c8dee2aaSAndroid Build Coastguard Worker    return true;
825*c8dee2aaSAndroid Build Coastguard Worker}
826*c8dee2aaSAndroid Build Coastguard Worker
827*c8dee2aaSAndroid Build Coastguard Worker// Used to "clear" a backend texture to a constant color by transferring.
828*c8dee2aaSAndroid Build Coastguard Workerstatic GrColorType mtl_format_to_backend_tex_clear_colortype(MTLPixelFormat format) {
829*c8dee2aaSAndroid Build Coastguard Worker    switch(format) {
830*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatA8Unorm:         return GrColorType::kAlpha_8;
831*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatR8Unorm:         return GrColorType::kR_8;
832*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatB5G6R5Unorm:     return GrColorType::kBGR_565;
833*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatABGR4Unorm:      return GrColorType::kABGR_4444;
834*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRGBA8Unorm:      return GrColorType::kRGBA_8888;
835*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRGBA8Unorm_sRGB: return GrColorType::kRGBA_8888_SRGB;
836*c8dee2aaSAndroid Build Coastguard Worker
837*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRG8Unorm:        return GrColorType::kRG_88;
838*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatBGRA8Unorm:      return GrColorType::kBGRA_8888;
839*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRGB10A2Unorm:    return GrColorType::kRGBA_1010102;
840*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatBGR10A2Unorm:    return GrColorType::kBGRA_1010102;
841*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatR16Float:        return GrColorType::kR_F16;
842*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRGBA16Float:     return GrColorType::kRGBA_F16;
843*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatR16Unorm:        return GrColorType::kR_16;
844*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRG16Unorm:       return GrColorType::kRG_1616;
845*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRGBA16Unorm:     return GrColorType::kRGBA_16161616;
846*c8dee2aaSAndroid Build Coastguard Worker        case MTLPixelFormatRG16Float:       return GrColorType::kRG_F16;
847*c8dee2aaSAndroid Build Coastguard Worker        default:                            return GrColorType::kUnknown;
848*c8dee2aaSAndroid Build Coastguard Worker    }
849*c8dee2aaSAndroid Build Coastguard Worker
850*c8dee2aaSAndroid Build Coastguard Worker    SkUNREACHABLE;
851*c8dee2aaSAndroid Build Coastguard Worker}
852*c8dee2aaSAndroid Build Coastguard Worker
853*c8dee2aaSAndroid Build Coastguard Workervoid copy_src_data(char* dst,
854*c8dee2aaSAndroid Build Coastguard Worker                   size_t bytesPerPixel,
855*c8dee2aaSAndroid Build Coastguard Worker                   const TArray<size_t>& individualMipOffsets,
856*c8dee2aaSAndroid Build Coastguard Worker                   const GrPixmap srcData[],
857*c8dee2aaSAndroid Build Coastguard Worker                   int numMipLevels,
858*c8dee2aaSAndroid Build Coastguard Worker                   size_t bufferSize) {
859*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(srcData && numMipLevels);
860*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(individualMipOffsets.size() == numMipLevels);
861*c8dee2aaSAndroid Build Coastguard Worker
862*c8dee2aaSAndroid Build Coastguard Worker    for (int level = 0; level < numMipLevels; ++level) {
863*c8dee2aaSAndroid Build Coastguard Worker        const size_t trimRB = srcData[level].width() * bytesPerPixel;
864*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(individualMipOffsets[level] + trimRB * srcData[level].height() <= bufferSize);
865*c8dee2aaSAndroid Build Coastguard Worker        SkRectMemcpy(dst + individualMipOffsets[level], trimRB,
866*c8dee2aaSAndroid Build Coastguard Worker                     srcData[level].addr(), srcData[level].rowBytes(),
867*c8dee2aaSAndroid Build Coastguard Worker                     trimRB, srcData[level].height());
868*c8dee2aaSAndroid Build Coastguard Worker    }
869*c8dee2aaSAndroid Build Coastguard Worker}
870*c8dee2aaSAndroid Build Coastguard Worker
871*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat mtlFormat,
872*c8dee2aaSAndroid Build Coastguard Worker                                                 SkISize dimensions,
873*c8dee2aaSAndroid Build Coastguard Worker                                                 int sampleCnt,
874*c8dee2aaSAndroid Build Coastguard Worker                                                 GrTexturable texturable,
875*c8dee2aaSAndroid Build Coastguard Worker                                                 GrRenderable renderable,
876*c8dee2aaSAndroid Build Coastguard Worker                                                 skgpu::Mipmapped mipmapped,
877*c8dee2aaSAndroid Build Coastguard Worker                                                 GrMtlTextureInfo* info) {
878*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(texturable == GrTexturable::kYes || renderable == GrRenderable::kYes);
879*c8dee2aaSAndroid Build Coastguard Worker
880*c8dee2aaSAndroid Build Coastguard Worker    if (texturable == GrTexturable::kYes && !fMtlCaps->isFormatTexturable(mtlFormat)) {
881*c8dee2aaSAndroid Build Coastguard Worker        return false;
882*c8dee2aaSAndroid Build Coastguard Worker    }
883*c8dee2aaSAndroid Build Coastguard Worker    if (renderable == GrRenderable::kYes && !fMtlCaps->isFormatRenderable(mtlFormat, 1)) {
884*c8dee2aaSAndroid Build Coastguard Worker        return false;
885*c8dee2aaSAndroid Build Coastguard Worker    }
886*c8dee2aaSAndroid Build Coastguard Worker
887*c8dee2aaSAndroid Build Coastguard Worker    if (!check_max_blit_width(dimensions.width())) {
888*c8dee2aaSAndroid Build Coastguard Worker        return false;
889*c8dee2aaSAndroid Build Coastguard Worker    }
890*c8dee2aaSAndroid Build Coastguard Worker
891*c8dee2aaSAndroid Build Coastguard Worker    auto desc = [[MTLTextureDescriptor alloc] init];
892*c8dee2aaSAndroid Build Coastguard Worker    desc.pixelFormat = mtlFormat;
893*c8dee2aaSAndroid Build Coastguard Worker    desc.width = dimensions.width();
894*c8dee2aaSAndroid Build Coastguard Worker    desc.height = dimensions.height();
895*c8dee2aaSAndroid Build Coastguard Worker    if (mipmapped == skgpu::Mipmapped::kYes) {
896*c8dee2aaSAndroid Build Coastguard Worker        desc.mipmapLevelCount = 1 + SkPrevLog2(std::max(dimensions.width(), dimensions.height()));
897*c8dee2aaSAndroid Build Coastguard Worker    }
898*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
899*c8dee2aaSAndroid Build Coastguard Worker        desc.storageMode = MTLStorageModePrivate;
900*c8dee2aaSAndroid Build Coastguard Worker        MTLTextureUsage usage = texturable == GrTexturable::kYes ? MTLTextureUsageShaderRead : 0;
901*c8dee2aaSAndroid Build Coastguard Worker        usage |= renderable == GrRenderable::kYes ? MTLTextureUsageRenderTarget : 0;
902*c8dee2aaSAndroid Build Coastguard Worker        desc.usage = usage;
903*c8dee2aaSAndroid Build Coastguard Worker    }
904*c8dee2aaSAndroid Build Coastguard Worker    if (sampleCnt != 1) {
905*c8dee2aaSAndroid Build Coastguard Worker        desc.sampleCount = sampleCnt;
906*c8dee2aaSAndroid Build Coastguard Worker        desc.textureType = MTLTextureType2DMultisample;
907*c8dee2aaSAndroid Build Coastguard Worker    }
908*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
909*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
910*c8dee2aaSAndroid Build Coastguard Worker    testTexture.label = @"testTexture";
911*c8dee2aaSAndroid Build Coastguard Worker#endif
912*c8dee2aaSAndroid Build Coastguard Worker    info->fTexture.reset(GrRetainPtrFromId(testTexture));
913*c8dee2aaSAndroid Build Coastguard Worker    return true;
914*c8dee2aaSAndroid Build Coastguard Worker}
915*c8dee2aaSAndroid Build Coastguard Worker
916*c8dee2aaSAndroid Build Coastguard WorkerGrBackendTexture GrMtlGpu::onCreateBackendTexture(SkISize dimensions,
917*c8dee2aaSAndroid Build Coastguard Worker                                                  const GrBackendFormat& format,
918*c8dee2aaSAndroid Build Coastguard Worker                                                  GrRenderable renderable,
919*c8dee2aaSAndroid Build Coastguard Worker                                                  skgpu::Mipmapped mipmapped,
920*c8dee2aaSAndroid Build Coastguard Worker                                                  GrProtected isProtected,
921*c8dee2aaSAndroid Build Coastguard Worker                                                  std::string_view label) {
922*c8dee2aaSAndroid Build Coastguard Worker    const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
923*c8dee2aaSAndroid Build Coastguard Worker
924*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
925*c8dee2aaSAndroid Build Coastguard Worker    if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, 1, GrTexturable::kYes,
926*c8dee2aaSAndroid Build Coastguard Worker                                                 renderable, mipmapped, &info)) {
927*c8dee2aaSAndroid Build Coastguard Worker        return {};
928*c8dee2aaSAndroid Build Coastguard Worker    }
929*c8dee2aaSAndroid Build Coastguard Worker
930*c8dee2aaSAndroid Build Coastguard Worker    return GrBackendTextures::MakeMtl(dimensions.width(), dimensions.height(), mipmapped, info);
931*c8dee2aaSAndroid Build Coastguard Worker}
932*c8dee2aaSAndroid Build Coastguard Worker
933*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onClearBackendTexture(const GrBackendTexture& backendTexture,
934*c8dee2aaSAndroid Build Coastguard Worker                                     sk_sp<skgpu::RefCntedCallback> finishedCallback,
935*c8dee2aaSAndroid Build Coastguard Worker                                     std::array<float, 4> color) {
936*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
937*c8dee2aaSAndroid Build Coastguard Worker    SkAssertResult(GrBackendTextures::GetMtlTextureInfo(backendTexture, &info));
938*c8dee2aaSAndroid Build Coastguard Worker
939*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = GrGetMTLTexture(info.fTexture.get());
940*c8dee2aaSAndroid Build Coastguard Worker
941*c8dee2aaSAndroid Build Coastguard Worker    const MTLPixelFormat mtlFormat = mtlTexture.pixelFormat;
942*c8dee2aaSAndroid Build Coastguard Worker
943*c8dee2aaSAndroid Build Coastguard Worker    // Create a transfer buffer and fill with data.
944*c8dee2aaSAndroid Build Coastguard Worker    size_t bytesPerPixel = skgpu::MtlFormatBytesPerBlock(mtlFormat);
945*c8dee2aaSAndroid Build Coastguard Worker    size_t combinedBufferSize;
946*c8dee2aaSAndroid Build Coastguard Worker
947*c8dee2aaSAndroid Build Coastguard Worker    // Reuse the same buffer for all levels. Should be ok since we made the row bytes tight.
948*c8dee2aaSAndroid Build Coastguard Worker    combinedBufferSize = bytesPerPixel*backendTexture.width()*backendTexture.height();
949*c8dee2aaSAndroid Build Coastguard Worker
950*c8dee2aaSAndroid Build Coastguard Worker    size_t alignment = std::max(bytesPerPixel, this->mtlCaps().getMinBufferAlignment());
951*c8dee2aaSAndroid Build Coastguard Worker    GrStagingBufferManager::Slice slice = fStagingBufferManager.allocateStagingBufferSlice(
952*c8dee2aaSAndroid Build Coastguard Worker            combinedBufferSize, alignment);
953*c8dee2aaSAndroid Build Coastguard Worker    if (!slice.fBuffer) {
954*c8dee2aaSAndroid Build Coastguard Worker        return false;
955*c8dee2aaSAndroid Build Coastguard Worker    }
956*c8dee2aaSAndroid Build Coastguard Worker    char* buffer = (char*)slice.fOffsetMapPtr;
957*c8dee2aaSAndroid Build Coastguard Worker
958*c8dee2aaSAndroid Build Coastguard Worker    auto colorType = mtl_format_to_backend_tex_clear_colortype(mtlFormat);
959*c8dee2aaSAndroid Build Coastguard Worker    if (colorType == GrColorType::kUnknown) {
960*c8dee2aaSAndroid Build Coastguard Worker        return false;
961*c8dee2aaSAndroid Build Coastguard Worker    }
962*c8dee2aaSAndroid Build Coastguard Worker    GrImageInfo ii(colorType, kUnpremul_SkAlphaType, nullptr, backendTexture.dimensions());
963*c8dee2aaSAndroid Build Coastguard Worker    auto rb = ii.minRowBytes();
964*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(rb == bytesPerPixel*backendTexture.width());
965*c8dee2aaSAndroid Build Coastguard Worker    if (!GrClearImage(ii, buffer, rb, color)) {
966*c8dee2aaSAndroid Build Coastguard Worker        return false;
967*c8dee2aaSAndroid Build Coastguard Worker    }
968*c8dee2aaSAndroid Build Coastguard Worker
969*c8dee2aaSAndroid Build Coastguard Worker    // Transfer buffer contents to texture
970*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(0, 0, 0);
971*c8dee2aaSAndroid Build Coastguard Worker
972*c8dee2aaSAndroid Build Coastguard Worker    GrMtlCommandBuffer* cmdBuffer = this->commandBuffer();
973*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
974*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
975*c8dee2aaSAndroid Build Coastguard Worker        return false;
976*c8dee2aaSAndroid Build Coastguard Worker    }
977*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
978*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"onClearBackendTexture"];
979*c8dee2aaSAndroid Build Coastguard Worker#endif
980*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
981*c8dee2aaSAndroid Build Coastguard Worker
982*c8dee2aaSAndroid Build Coastguard Worker    SkISize levelDimensions(backendTexture.dimensions());
983*c8dee2aaSAndroid Build Coastguard Worker    int numMipLevels = mtlTexture.mipmapLevelCount;
984*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
985*c8dee2aaSAndroid Build Coastguard Worker        size_t levelRowBytes;
986*c8dee2aaSAndroid Build Coastguard Worker        size_t levelSize;
987*c8dee2aaSAndroid Build Coastguard Worker
988*c8dee2aaSAndroid Build Coastguard Worker        levelRowBytes = levelDimensions.width() * bytesPerPixel;
989*c8dee2aaSAndroid Build Coastguard Worker        levelSize = levelRowBytes * levelDimensions.height();
990*c8dee2aaSAndroid Build Coastguard Worker
991*c8dee2aaSAndroid Build Coastguard Worker        // TODO: can this all be done in one go?
992*c8dee2aaSAndroid Build Coastguard Worker        [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
993*c8dee2aaSAndroid Build Coastguard Worker                          sourceOffset: slice.fOffset
994*c8dee2aaSAndroid Build Coastguard Worker                     sourceBytesPerRow: levelRowBytes
995*c8dee2aaSAndroid Build Coastguard Worker                   sourceBytesPerImage: levelSize
996*c8dee2aaSAndroid Build Coastguard Worker                            sourceSize: MTLSizeMake(levelDimensions.width(),
997*c8dee2aaSAndroid Build Coastguard Worker                                                    levelDimensions.height(),
998*c8dee2aaSAndroid Build Coastguard Worker                                                    1)
999*c8dee2aaSAndroid Build Coastguard Worker                             toTexture: mtlTexture
1000*c8dee2aaSAndroid Build Coastguard Worker                      destinationSlice: 0
1001*c8dee2aaSAndroid Build Coastguard Worker                      destinationLevel: currentMipLevel
1002*c8dee2aaSAndroid Build Coastguard Worker                     destinationOrigin: origin];
1003*c8dee2aaSAndroid Build Coastguard Worker
1004*c8dee2aaSAndroid Build Coastguard Worker        levelDimensions = {std::max(1, levelDimensions.width() / 2),
1005*c8dee2aaSAndroid Build Coastguard Worker                           std::max(1, levelDimensions.height() / 2)};
1006*c8dee2aaSAndroid Build Coastguard Worker    }
1007*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1008*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().isMac()) {
1009*c8dee2aaSAndroid Build Coastguard Worker        [mtlBuffer->mtlBuffer() didModifyRange: NSMakeRange(slice.fOffset, combinedBufferSize)];
1010*c8dee2aaSAndroid Build Coastguard Worker    }
1011*c8dee2aaSAndroid Build Coastguard Worker#endif
1012*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1013*c8dee2aaSAndroid Build Coastguard Worker
1014*c8dee2aaSAndroid Build Coastguard Worker    if (finishedCallback) {
1015*c8dee2aaSAndroid Build Coastguard Worker        this->addFinishedCallback(std::move(finishedCallback));
1016*c8dee2aaSAndroid Build Coastguard Worker    }
1017*c8dee2aaSAndroid Build Coastguard Worker
1018*c8dee2aaSAndroid Build Coastguard Worker    return true;
1019*c8dee2aaSAndroid Build Coastguard Worker}
1020*c8dee2aaSAndroid Build Coastguard Worker
1021*c8dee2aaSAndroid Build Coastguard WorkerGrBackendTexture GrMtlGpu::onCreateCompressedBackendTexture(SkISize dimensions,
1022*c8dee2aaSAndroid Build Coastguard Worker                                                            const GrBackendFormat& format,
1023*c8dee2aaSAndroid Build Coastguard Worker                                                            skgpu::Mipmapped mipmapped,
1024*c8dee2aaSAndroid Build Coastguard Worker                                                            GrProtected isProtected) {
1025*c8dee2aaSAndroid Build Coastguard Worker    const MTLPixelFormat mtlFormat = GrBackendFormatAsMTLPixelFormat(format);
1026*c8dee2aaSAndroid Build Coastguard Worker
1027*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
1028*c8dee2aaSAndroid Build Coastguard Worker    if (!this->createMtlTextureForBackendSurface(mtlFormat, dimensions, 1, GrTexturable::kYes,
1029*c8dee2aaSAndroid Build Coastguard Worker                                                 GrRenderable::kNo, mipmapped, &info)) {
1030*c8dee2aaSAndroid Build Coastguard Worker        return {};
1031*c8dee2aaSAndroid Build Coastguard Worker    }
1032*c8dee2aaSAndroid Build Coastguard Worker
1033*c8dee2aaSAndroid Build Coastguard Worker    return GrBackendTextures::MakeMtl(dimensions.width(), dimensions.height(), mipmapped, info);
1034*c8dee2aaSAndroid Build Coastguard Worker}
1035*c8dee2aaSAndroid Build Coastguard Worker
1036*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onUpdateCompressedBackendTexture(const GrBackendTexture& backendTexture,
1037*c8dee2aaSAndroid Build Coastguard Worker                                                sk_sp<skgpu::RefCntedCallback> finishedCallback,
1038*c8dee2aaSAndroid Build Coastguard Worker                                                const void* data,
1039*c8dee2aaSAndroid Build Coastguard Worker                                                size_t size) {
1040*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
1041*c8dee2aaSAndroid Build Coastguard Worker    SkAssertResult(GrBackendTextures::GetMtlTextureInfo(backendTexture, &info));
1042*c8dee2aaSAndroid Build Coastguard Worker
1043*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get());
1044*c8dee2aaSAndroid Build Coastguard Worker
1045*c8dee2aaSAndroid Build Coastguard Worker    int numMipLevels = mtlTexture.mipmapLevelCount;
1046*c8dee2aaSAndroid Build Coastguard Worker    skgpu::Mipmapped mipmapped = numMipLevels > 1 ? skgpu::Mipmapped::kYes : skgpu::Mipmapped::kNo;
1047*c8dee2aaSAndroid Build Coastguard Worker
1048*c8dee2aaSAndroid Build Coastguard Worker    SkTextureCompressionType compression =
1049*c8dee2aaSAndroid Build Coastguard Worker            GrBackendFormatToCompressionType(backendTexture.getBackendFormat());
1050*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(compression != SkTextureCompressionType::kNone);
1051*c8dee2aaSAndroid Build Coastguard Worker
1052*c8dee2aaSAndroid Build Coastguard Worker    // Create a transfer buffer and fill with data.
1053*c8dee2aaSAndroid Build Coastguard Worker    STArray<16, size_t> individualMipOffsets;
1054*c8dee2aaSAndroid Build Coastguard Worker    size_t combinedBufferSize;
1055*c8dee2aaSAndroid Build Coastguard Worker    combinedBufferSize = SkCompressedDataSize(compression,
1056*c8dee2aaSAndroid Build Coastguard Worker                                              backendTexture.dimensions(),
1057*c8dee2aaSAndroid Build Coastguard Worker                                              &individualMipOffsets,
1058*c8dee2aaSAndroid Build Coastguard Worker                                              mipmapped == skgpu::Mipmapped::kYes);
1059*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(individualMipOffsets.size() == numMipLevels);
1060*c8dee2aaSAndroid Build Coastguard Worker
1061*c8dee2aaSAndroid Build Coastguard Worker    size_t alignment = std::max(SkCompressedBlockSize(compression),
1062*c8dee2aaSAndroid Build Coastguard Worker                                this->mtlCaps().getMinBufferAlignment());
1063*c8dee2aaSAndroid Build Coastguard Worker    GrStagingBufferManager::Slice slice =
1064*c8dee2aaSAndroid Build Coastguard Worker            fStagingBufferManager.allocateStagingBufferSlice(combinedBufferSize, alignment);
1065*c8dee2aaSAndroid Build Coastguard Worker    if (!slice.fBuffer) {
1066*c8dee2aaSAndroid Build Coastguard Worker        return false;
1067*c8dee2aaSAndroid Build Coastguard Worker    }
1068*c8dee2aaSAndroid Build Coastguard Worker    char* buffer = (char*)slice.fOffsetMapPtr;
1069*c8dee2aaSAndroid Build Coastguard Worker
1070*c8dee2aaSAndroid Build Coastguard Worker    memcpy(buffer, data, size);
1071*c8dee2aaSAndroid Build Coastguard Worker
1072*c8dee2aaSAndroid Build Coastguard Worker    // Transfer buffer contents to texture
1073*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(0, 0, 0);
1074*c8dee2aaSAndroid Build Coastguard Worker
1075*c8dee2aaSAndroid Build Coastguard Worker    GrMtlCommandBuffer* cmdBuffer = this->commandBuffer();
1076*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
1077*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
1078*c8dee2aaSAndroid Build Coastguard Worker        return false;
1079*c8dee2aaSAndroid Build Coastguard Worker    }
1080*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1081*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"onUpdateCompressedBackendTexture"];
1082*c8dee2aaSAndroid Build Coastguard Worker#endif
1083*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* mtlBuffer = static_cast<GrMtlBuffer*>(slice.fBuffer);
1084*c8dee2aaSAndroid Build Coastguard Worker
1085*c8dee2aaSAndroid Build Coastguard Worker    SkISize levelDimensions(backendTexture.dimensions());
1086*c8dee2aaSAndroid Build Coastguard Worker    for (int currentMipLevel = 0; currentMipLevel < numMipLevels; currentMipLevel++) {
1087*c8dee2aaSAndroid Build Coastguard Worker        size_t levelRowBytes;
1088*c8dee2aaSAndroid Build Coastguard Worker        size_t levelSize;
1089*c8dee2aaSAndroid Build Coastguard Worker
1090*c8dee2aaSAndroid Build Coastguard Worker        levelRowBytes = skgpu::CompressedRowBytes(compression, levelDimensions.width());
1091*c8dee2aaSAndroid Build Coastguard Worker        levelSize = SkCompressedDataSize(compression, levelDimensions, nullptr, false);
1092*c8dee2aaSAndroid Build Coastguard Worker
1093*c8dee2aaSAndroid Build Coastguard Worker        // TODO: can this all be done in one go?
1094*c8dee2aaSAndroid Build Coastguard Worker        [blitCmdEncoder copyFromBuffer: mtlBuffer->mtlBuffer()
1095*c8dee2aaSAndroid Build Coastguard Worker                          sourceOffset: slice.fOffset + individualMipOffsets[currentMipLevel]
1096*c8dee2aaSAndroid Build Coastguard Worker                     sourceBytesPerRow: levelRowBytes
1097*c8dee2aaSAndroid Build Coastguard Worker                   sourceBytesPerImage: levelSize
1098*c8dee2aaSAndroid Build Coastguard Worker                            sourceSize: MTLSizeMake(levelDimensions.width(),
1099*c8dee2aaSAndroid Build Coastguard Worker                                                    levelDimensions.height(),
1100*c8dee2aaSAndroid Build Coastguard Worker                                                    1)
1101*c8dee2aaSAndroid Build Coastguard Worker                             toTexture: mtlTexture
1102*c8dee2aaSAndroid Build Coastguard Worker                      destinationSlice: 0
1103*c8dee2aaSAndroid Build Coastguard Worker                      destinationLevel: currentMipLevel
1104*c8dee2aaSAndroid Build Coastguard Worker                     destinationOrigin: origin];
1105*c8dee2aaSAndroid Build Coastguard Worker
1106*c8dee2aaSAndroid Build Coastguard Worker        levelDimensions = {std::max(1, levelDimensions.width() / 2),
1107*c8dee2aaSAndroid Build Coastguard Worker                           std::max(1, levelDimensions.height() / 2)};
1108*c8dee2aaSAndroid Build Coastguard Worker    }
1109*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1110*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().isMac()) {
1111*c8dee2aaSAndroid Build Coastguard Worker        [mtlBuffer->mtlBuffer() didModifyRange:NSMakeRange(slice.fOffset, combinedBufferSize)];
1112*c8dee2aaSAndroid Build Coastguard Worker    }
1113*c8dee2aaSAndroid Build Coastguard Worker#endif
1114*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1115*c8dee2aaSAndroid Build Coastguard Worker
1116*c8dee2aaSAndroid Build Coastguard Worker    if (finishedCallback) {
1117*c8dee2aaSAndroid Build Coastguard Worker        this->addFinishedCallback(std::move(finishedCallback));
1118*c8dee2aaSAndroid Build Coastguard Worker    }
1119*c8dee2aaSAndroid Build Coastguard Worker
1120*c8dee2aaSAndroid Build Coastguard Worker    return true;
1121*c8dee2aaSAndroid Build Coastguard Worker}
1122*c8dee2aaSAndroid Build Coastguard Worker
1123*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::deleteBackendTexture(const GrBackendTexture& tex) {
1124*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(GrBackendApi::kMetal == tex.backend());
1125*c8dee2aaSAndroid Build Coastguard Worker    // Nothing to do here, will get cleaned up when the GrBackendTexture object goes away
1126*c8dee2aaSAndroid Build Coastguard Worker}
1127*c8dee2aaSAndroid Build Coastguard Worker
1128*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::compile(const GrProgramDesc& desc, const GrProgramInfo& programInfo) {
1129*c8dee2aaSAndroid Build Coastguard Worker
1130*c8dee2aaSAndroid Build Coastguard Worker    GrThreadSafePipelineBuilder::Stats::ProgramCacheResult stat;
1131*c8dee2aaSAndroid Build Coastguard Worker
1132*c8dee2aaSAndroid Build Coastguard Worker    auto pipelineState = this->resourceProvider().findOrCreateCompatiblePipelineState(
1133*c8dee2aaSAndroid Build Coastguard Worker                                 desc, programInfo, &stat);
1134*c8dee2aaSAndroid Build Coastguard Worker    if (!pipelineState) {
1135*c8dee2aaSAndroid Build Coastguard Worker        return false;
1136*c8dee2aaSAndroid Build Coastguard Worker    }
1137*c8dee2aaSAndroid Build Coastguard Worker
1138*c8dee2aaSAndroid Build Coastguard Worker    return stat != GrThreadSafePipelineBuilder::Stats::ProgramCacheResult::kHit;
1139*c8dee2aaSAndroid Build Coastguard Worker}
1140*c8dee2aaSAndroid Build Coastguard Worker
1141*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::precompileShader(const SkData& key, const SkData& data) {
1142*c8dee2aaSAndroid Build Coastguard Worker    return this->resourceProvider().precompileShader(key, data);
1143*c8dee2aaSAndroid Build Coastguard Worker}
1144*c8dee2aaSAndroid Build Coastguard Worker
1145*c8dee2aaSAndroid Build Coastguard Worker#if defined(GPU_TEST_UTILS)
1146*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
1147*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(GrBackendApi::kMetal == tex.backend());
1148*c8dee2aaSAndroid Build Coastguard Worker
1149*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
1150*c8dee2aaSAndroid Build Coastguard Worker    if (!GrBackendTextures::GetMtlTextureInfo(tex, &info)) {
1151*c8dee2aaSAndroid Build Coastguard Worker        return false;
1152*c8dee2aaSAndroid Build Coastguard Worker    }
1153*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture.get());
1154*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
1155*c8dee2aaSAndroid Build Coastguard Worker        return false;
1156*c8dee2aaSAndroid Build Coastguard Worker    }
1157*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
1158*c8dee2aaSAndroid Build Coastguard Worker        return mtlTexture.usage & MTLTextureUsageShaderRead;
1159*c8dee2aaSAndroid Build Coastguard Worker    } else {
1160*c8dee2aaSAndroid Build Coastguard Worker        return true; // best we can do
1161*c8dee2aaSAndroid Build Coastguard Worker    }
1162*c8dee2aaSAndroid Build Coastguard Worker}
1163*c8dee2aaSAndroid Build Coastguard Worker
1164*c8dee2aaSAndroid Build Coastguard WorkerGrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(SkISize dimensions,
1165*c8dee2aaSAndroid Build Coastguard Worker                                                                     GrColorType ct,
1166*c8dee2aaSAndroid Build Coastguard Worker                                                                     int sampleCnt,
1167*c8dee2aaSAndroid Build Coastguard Worker                                                                     GrProtected isProtected) {
1168*c8dee2aaSAndroid Build Coastguard Worker    if (dimensions.width()  > this->caps()->maxRenderTargetSize() ||
1169*c8dee2aaSAndroid Build Coastguard Worker        dimensions.height() > this->caps()->maxRenderTargetSize()) {
1170*c8dee2aaSAndroid Build Coastguard Worker        return {};
1171*c8dee2aaSAndroid Build Coastguard Worker    }
1172*c8dee2aaSAndroid Build Coastguard Worker    if (isProtected == GrProtected::kYes) {
1173*c8dee2aaSAndroid Build Coastguard Worker        return {};
1174*c8dee2aaSAndroid Build Coastguard Worker    }
1175*c8dee2aaSAndroid Build Coastguard Worker
1176*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat format = this->mtlCaps().getFormatFromColorType(ct);
1177*c8dee2aaSAndroid Build Coastguard Worker    sampleCnt = this->mtlCaps().getRenderTargetSampleCount(sampleCnt, format);
1178*c8dee2aaSAndroid Build Coastguard Worker    if (sampleCnt == 0) {
1179*c8dee2aaSAndroid Build Coastguard Worker        return {};
1180*c8dee2aaSAndroid Build Coastguard Worker    }
1181*c8dee2aaSAndroid Build Coastguard Worker
1182*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
1183*c8dee2aaSAndroid Build Coastguard Worker    if (!this->createMtlTextureForBackendSurface(format,
1184*c8dee2aaSAndroid Build Coastguard Worker                                                 dimensions,
1185*c8dee2aaSAndroid Build Coastguard Worker                                                 sampleCnt,
1186*c8dee2aaSAndroid Build Coastguard Worker                                                 GrTexturable::kNo,
1187*c8dee2aaSAndroid Build Coastguard Worker                                                 GrRenderable::kYes,
1188*c8dee2aaSAndroid Build Coastguard Worker                                                 skgpu::Mipmapped::kNo,
1189*c8dee2aaSAndroid Build Coastguard Worker                                                 &info)) {
1190*c8dee2aaSAndroid Build Coastguard Worker        return {};
1191*c8dee2aaSAndroid Build Coastguard Worker    }
1192*c8dee2aaSAndroid Build Coastguard Worker
1193*c8dee2aaSAndroid Build Coastguard Worker    return GrBackendRenderTargets::MakeMtl(dimensions.width(), dimensions.height(), info);
1194*c8dee2aaSAndroid Build Coastguard Worker}
1195*c8dee2aaSAndroid Build Coastguard Worker
1196*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
1197*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(GrBackendApi::kMetal == rt.backend());
1198*c8dee2aaSAndroid Build Coastguard Worker
1199*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTextureInfo info;
1200*c8dee2aaSAndroid Build Coastguard Worker    if (GrBackendRenderTargets::GetMtlTextureInfo(rt, &info)) {
1201*c8dee2aaSAndroid Build Coastguard Worker        GrSubmitInfo submitInfo;
1202*c8dee2aaSAndroid Build Coastguard Worker        submitInfo.fSync = GrSyncCpu::kYes;
1203*c8dee2aaSAndroid Build Coastguard Worker        this->submitToGpu(submitInfo);
1204*c8dee2aaSAndroid Build Coastguard Worker        // Nothing else to do here, will get cleaned up when the GrBackendRenderTarget
1205*c8dee2aaSAndroid Build Coastguard Worker        // is deleted.
1206*c8dee2aaSAndroid Build Coastguard Worker    }
1207*c8dee2aaSAndroid Build Coastguard Worker}
1208*c8dee2aaSAndroid Build Coastguard Worker#endif // defined(GPU_TEST_UTILS)
1209*c8dee2aaSAndroid Build Coastguard Worker
1210*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::copySurfaceAsResolve(GrSurface* dst, GrSurface* src) {
1211*c8dee2aaSAndroid Build Coastguard Worker    // TODO: Add support for subrectangles
1212*c8dee2aaSAndroid Build Coastguard Worker    GrMtlRenderTarget* srcRT = static_cast<GrMtlRenderTarget*>(src->asRenderTarget());
1213*c8dee2aaSAndroid Build Coastguard Worker    GrRenderTarget* dstRT = dst->asRenderTarget();
1214*c8dee2aaSAndroid Build Coastguard Worker    GrMtlAttachment* dstAttachment;
1215*c8dee2aaSAndroid Build Coastguard Worker    if (dstRT) {
1216*c8dee2aaSAndroid Build Coastguard Worker        GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(dstRT);
1217*c8dee2aaSAndroid Build Coastguard Worker        dstAttachment = mtlRT->colorAttachment();
1218*c8dee2aaSAndroid Build Coastguard Worker    } else {
1219*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(dst->asTexture());
1220*c8dee2aaSAndroid Build Coastguard Worker        dstAttachment = static_cast<GrMtlTexture*>(dst->asTexture())->attachment();
1221*c8dee2aaSAndroid Build Coastguard Worker    }
1222*c8dee2aaSAndroid Build Coastguard Worker
1223*c8dee2aaSAndroid Build Coastguard Worker    this->resolve(dstAttachment, srcRT->colorAttachment());
1224*c8dee2aaSAndroid Build Coastguard Worker}
1225*c8dee2aaSAndroid Build Coastguard Worker
1226*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurface* src,
1227*c8dee2aaSAndroid Build Coastguard Worker                                 GrMtlAttachment* dstAttachment, GrMtlAttachment* srcAttachment,
1228*c8dee2aaSAndroid Build Coastguard Worker                                 const SkIRect& srcRect, const SkIPoint& dstPoint) {
1229*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_DEBUG
1230*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->mtlCaps().canCopyAsBlit(dstAttachment->mtlFormat(), dstAttachment->numSamples(),
1231*c8dee2aaSAndroid Build Coastguard Worker                                           srcAttachment->mtlFormat(), dstAttachment->numSamples(),
1232*c8dee2aaSAndroid Build Coastguard Worker                                           srcRect, dstPoint, dst == src));
1233*c8dee2aaSAndroid Build Coastguard Worker#endif
1234*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN dstTex = dstAttachment->mtlTexture();
1235*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN srcTex = srcAttachment->mtlTexture();
1236*c8dee2aaSAndroid Build Coastguard Worker
1237*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
1238*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
1239*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
1240*c8dee2aaSAndroid Build Coastguard Worker        return;
1241*c8dee2aaSAndroid Build Coastguard Worker    }
1242*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1243*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"copySurfaceAsBlit"];
1244*c8dee2aaSAndroid Build Coastguard Worker#endif
1245*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder copyFromTexture: srcTex
1246*c8dee2aaSAndroid Build Coastguard Worker                        sourceSlice: 0
1247*c8dee2aaSAndroid Build Coastguard Worker                        sourceLevel: 0
1248*c8dee2aaSAndroid Build Coastguard Worker                       sourceOrigin: MTLOriginMake(srcRect.x(), srcRect.y(), 0)
1249*c8dee2aaSAndroid Build Coastguard Worker                         sourceSize: MTLSizeMake(srcRect.width(), srcRect.height(), 1)
1250*c8dee2aaSAndroid Build Coastguard Worker                          toTexture: dstTex
1251*c8dee2aaSAndroid Build Coastguard Worker                   destinationSlice: 0
1252*c8dee2aaSAndroid Build Coastguard Worker                   destinationLevel: 0
1253*c8dee2aaSAndroid Build Coastguard Worker                  destinationOrigin: MTLOriginMake(dstPoint.fX, dstPoint.fY, 0)];
1254*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1255*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1256*c8dee2aaSAndroid Build Coastguard Worker#endif
1257*c8dee2aaSAndroid Build Coastguard Worker    cmdBuffer->addGrSurface(sk_ref_sp<const GrSurface>(dst));
1258*c8dee2aaSAndroid Build Coastguard Worker    cmdBuffer->addGrSurface(sk_ref_sp<const GrSurface>(src));
1259*c8dee2aaSAndroid Build Coastguard Worker}
1260*c8dee2aaSAndroid Build Coastguard Worker
1261*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onCopySurface(GrSurface* dst, const SkIRect& dstRect,
1262*c8dee2aaSAndroid Build Coastguard Worker                             GrSurface* src, const SkIRect& srcRect,
1263*c8dee2aaSAndroid Build Coastguard Worker                             GrSamplerState::Filter) {
1264*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(!src->isProtected() && !dst->isProtected());
1265*c8dee2aaSAndroid Build Coastguard Worker
1266*c8dee2aaSAndroid Build Coastguard Worker    if (srcRect.size() != dstRect.size()) {
1267*c8dee2aaSAndroid Build Coastguard Worker        return false;
1268*c8dee2aaSAndroid Build Coastguard Worker    }
1269*c8dee2aaSAndroid Build Coastguard Worker
1270*c8dee2aaSAndroid Build Coastguard Worker    GrMtlAttachment* dstAttachment;
1271*c8dee2aaSAndroid Build Coastguard Worker    GrMtlAttachment* srcAttachment;
1272*c8dee2aaSAndroid Build Coastguard Worker    GrRenderTarget* dstRT = dst->asRenderTarget();
1273*c8dee2aaSAndroid Build Coastguard Worker    if (dstRT) {
1274*c8dee2aaSAndroid Build Coastguard Worker        GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(dstRT);
1275*c8dee2aaSAndroid Build Coastguard Worker        // This will technically return true for single sample rts that used DMSAA in which case we
1276*c8dee2aaSAndroid Build Coastguard Worker        // don't have to pick the resolve attachment. But in that case the resolve and color
1277*c8dee2aaSAndroid Build Coastguard Worker        // attachments will be the same anyways.
1278*c8dee2aaSAndroid Build Coastguard Worker        if (this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) {
1279*c8dee2aaSAndroid Build Coastguard Worker            dstAttachment = mtlRT->resolveAttachment();
1280*c8dee2aaSAndroid Build Coastguard Worker        } else {
1281*c8dee2aaSAndroid Build Coastguard Worker            dstAttachment = mtlRT->colorAttachment();
1282*c8dee2aaSAndroid Build Coastguard Worker        }
1283*c8dee2aaSAndroid Build Coastguard Worker    } else if (dst->asTexture()) {
1284*c8dee2aaSAndroid Build Coastguard Worker        dstAttachment = static_cast<GrMtlTexture*>(dst->asTexture())->attachment();
1285*c8dee2aaSAndroid Build Coastguard Worker    } else {
1286*c8dee2aaSAndroid Build Coastguard Worker        // The surface in a GrAttachment already
1287*c8dee2aaSAndroid Build Coastguard Worker        dstAttachment = static_cast<GrMtlAttachment*>(dst);
1288*c8dee2aaSAndroid Build Coastguard Worker    }
1289*c8dee2aaSAndroid Build Coastguard Worker    GrRenderTarget* srcRT = src->asRenderTarget();
1290*c8dee2aaSAndroid Build Coastguard Worker    if (srcRT) {
1291*c8dee2aaSAndroid Build Coastguard Worker        GrMtlRenderTarget* mtlRT = static_cast<GrMtlRenderTarget*>(srcRT);
1292*c8dee2aaSAndroid Build Coastguard Worker        // This will technically return true for single sample rts that used DMSAA in which case we
1293*c8dee2aaSAndroid Build Coastguard Worker        // don't have to pick the resolve attachment. But in that case the resolve and color
1294*c8dee2aaSAndroid Build Coastguard Worker        // attachments will be the same anyways.
1295*c8dee2aaSAndroid Build Coastguard Worker        if (this->mtlCaps().renderTargetSupportsDiscardableMSAA(mtlRT)) {
1296*c8dee2aaSAndroid Build Coastguard Worker            srcAttachment = mtlRT->resolveAttachment();
1297*c8dee2aaSAndroid Build Coastguard Worker        } else {
1298*c8dee2aaSAndroid Build Coastguard Worker            srcAttachment = mtlRT->colorAttachment();
1299*c8dee2aaSAndroid Build Coastguard Worker        }
1300*c8dee2aaSAndroid Build Coastguard Worker    } else if (src->asTexture()) {
1301*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(src->asTexture());
1302*c8dee2aaSAndroid Build Coastguard Worker        srcAttachment = static_cast<GrMtlTexture*>(src->asTexture())->attachment();
1303*c8dee2aaSAndroid Build Coastguard Worker    } else {
1304*c8dee2aaSAndroid Build Coastguard Worker        // The surface in a GrAttachment already
1305*c8dee2aaSAndroid Build Coastguard Worker        srcAttachment = static_cast<GrMtlAttachment*>(src);
1306*c8dee2aaSAndroid Build Coastguard Worker    }
1307*c8dee2aaSAndroid Build Coastguard Worker
1308*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat dstFormat = dstAttachment->mtlFormat();
1309*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat srcFormat = srcAttachment->mtlFormat();
1310*c8dee2aaSAndroid Build Coastguard Worker
1311*c8dee2aaSAndroid Build Coastguard Worker    int dstSampleCnt = dstAttachment->sampleCount();
1312*c8dee2aaSAndroid Build Coastguard Worker    int srcSampleCnt = srcAttachment->sampleCount();
1313*c8dee2aaSAndroid Build Coastguard Worker
1314*c8dee2aaSAndroid Build Coastguard Worker    const SkIPoint dstPoint = dstRect.topLeft();
1315*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().canCopyAsResolve(dstFormat, dstSampleCnt,
1316*c8dee2aaSAndroid Build Coastguard Worker                                         srcFormat, srcSampleCnt,
1317*c8dee2aaSAndroid Build Coastguard Worker                                         SkToBool(srcRT), src->dimensions(),
1318*c8dee2aaSAndroid Build Coastguard Worker                                         srcRect, dstPoint,
1319*c8dee2aaSAndroid Build Coastguard Worker                                         dstAttachment == srcAttachment)) {
1320*c8dee2aaSAndroid Build Coastguard Worker        this->copySurfaceAsResolve(dst, src);
1321*c8dee2aaSAndroid Build Coastguard Worker        return true;
1322*c8dee2aaSAndroid Build Coastguard Worker    }
1323*c8dee2aaSAndroid Build Coastguard Worker
1324*c8dee2aaSAndroid Build Coastguard Worker    if (srcAttachment->framebufferOnly() || dstAttachment->framebufferOnly()) {
1325*c8dee2aaSAndroid Build Coastguard Worker        return false;
1326*c8dee2aaSAndroid Build Coastguard Worker    }
1327*c8dee2aaSAndroid Build Coastguard Worker
1328*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().canCopyAsBlit(dstFormat, dstSampleCnt, srcFormat, srcSampleCnt,
1329*c8dee2aaSAndroid Build Coastguard Worker                                      srcRect, dstPoint, dstAttachment == srcAttachment)) {
1330*c8dee2aaSAndroid Build Coastguard Worker        this->copySurfaceAsBlit(dst, src, dstAttachment, srcAttachment, srcRect, dstPoint);
1331*c8dee2aaSAndroid Build Coastguard Worker        return true;
1332*c8dee2aaSAndroid Build Coastguard Worker    }
1333*c8dee2aaSAndroid Build Coastguard Worker
1334*c8dee2aaSAndroid Build Coastguard Worker    return false;
1335*c8dee2aaSAndroid Build Coastguard Worker}
1336*c8dee2aaSAndroid Build Coastguard Worker
1337*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onWritePixels(GrSurface* surface,
1338*c8dee2aaSAndroid Build Coastguard Worker                             SkIRect rect,
1339*c8dee2aaSAndroid Build Coastguard Worker                             GrColorType surfaceColorType,
1340*c8dee2aaSAndroid Build Coastguard Worker                             GrColorType srcColorType,
1341*c8dee2aaSAndroid Build Coastguard Worker                             const GrMipLevel texels[],
1342*c8dee2aaSAndroid Build Coastguard Worker                             int mipLevelCount,
1343*c8dee2aaSAndroid Build Coastguard Worker                             bool prepForTexSampling) {
1344*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
1345*c8dee2aaSAndroid Build Coastguard Worker    // TODO: In principle we should be able to support pure rendertargets as well, but
1346*c8dee2aaSAndroid Build Coastguard Worker    // until we find a use case we'll only support texture rendertargets.
1347*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
1348*c8dee2aaSAndroid Build Coastguard Worker        return false;
1349*c8dee2aaSAndroid Build Coastguard Worker    }
1350*c8dee2aaSAndroid Build Coastguard Worker    if (!mipLevelCount) {
1351*c8dee2aaSAndroid Build Coastguard Worker        return false;
1352*c8dee2aaSAndroid Build Coastguard Worker    }
1353*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_DEBUG
1354*c8dee2aaSAndroid Build Coastguard Worker    for (int i = 0; i < mipLevelCount; i++) {
1355*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(texels[i].fPixels);
1356*c8dee2aaSAndroid Build Coastguard Worker    }
1357*c8dee2aaSAndroid Build Coastguard Worker#endif
1358*c8dee2aaSAndroid Build Coastguard Worker    return this->uploadToTexture(mtlTexture, rect, srcColorType, texels, mipLevelCount);
1359*c8dee2aaSAndroid Build Coastguard Worker}
1360*c8dee2aaSAndroid Build Coastguard Worker
1361*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onReadPixels(GrSurface* surface,
1362*c8dee2aaSAndroid Build Coastguard Worker                            SkIRect rect,
1363*c8dee2aaSAndroid Build Coastguard Worker                            GrColorType surfaceColorType,
1364*c8dee2aaSAndroid Build Coastguard Worker                            GrColorType dstColorType,
1365*c8dee2aaSAndroid Build Coastguard Worker                            void* buffer,
1366*c8dee2aaSAndroid Build Coastguard Worker                            size_t rowBytes) {
1367*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(surface);
1368*c8dee2aaSAndroid Build Coastguard Worker
1369*c8dee2aaSAndroid Build Coastguard Worker    if (surfaceColorType != dstColorType) {
1370*c8dee2aaSAndroid Build Coastguard Worker        return false;
1371*c8dee2aaSAndroid Build Coastguard Worker    }
1372*c8dee2aaSAndroid Build Coastguard Worker
1373*c8dee2aaSAndroid Build Coastguard Worker    int bpp = GrColorTypeBytesPerPixel(dstColorType);
1374*c8dee2aaSAndroid Build Coastguard Worker    size_t transBufferRowBytes = bpp*rect.width();
1375*c8dee2aaSAndroid Build Coastguard Worker    size_t transBufferImageBytes = transBufferRowBytes*rect.height();
1376*c8dee2aaSAndroid Build Coastguard Worker
1377*c8dee2aaSAndroid Build Coastguard Worker    GrResourceProvider* resourceProvider = this->getContext()->priv().resourceProvider();
1378*c8dee2aaSAndroid Build Coastguard Worker    sk_sp<GrGpuBuffer> transferBuffer = resourceProvider->createBuffer(
1379*c8dee2aaSAndroid Build Coastguard Worker            transBufferImageBytes,
1380*c8dee2aaSAndroid Build Coastguard Worker            GrGpuBufferType::kXferGpuToCpu,
1381*c8dee2aaSAndroid Build Coastguard Worker            kDynamic_GrAccessPattern,
1382*c8dee2aaSAndroid Build Coastguard Worker            GrResourceProvider::ZeroInit::kNo);
1383*c8dee2aaSAndroid Build Coastguard Worker
1384*c8dee2aaSAndroid Build Coastguard Worker    if (!transferBuffer) {
1385*c8dee2aaSAndroid Build Coastguard Worker        return false;
1386*c8dee2aaSAndroid Build Coastguard Worker    }
1387*c8dee2aaSAndroid Build Coastguard Worker
1388*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get());
1389*c8dee2aaSAndroid Build Coastguard Worker    if (!this->readOrTransferPixels(surface,
1390*c8dee2aaSAndroid Build Coastguard Worker                                    rect,
1391*c8dee2aaSAndroid Build Coastguard Worker                                    dstColorType,
1392*c8dee2aaSAndroid Build Coastguard Worker                                    grMtlBuffer->mtlBuffer(),
1393*c8dee2aaSAndroid Build Coastguard Worker                                    0,
1394*c8dee2aaSAndroid Build Coastguard Worker                                    transBufferImageBytes,
1395*c8dee2aaSAndroid Build Coastguard Worker                                    transBufferRowBytes)) {
1396*c8dee2aaSAndroid Build Coastguard Worker        return false;
1397*c8dee2aaSAndroid Build Coastguard Worker    }
1398*c8dee2aaSAndroid Build Coastguard Worker    this->submitCommandBuffer(kForce_SyncQueue);
1399*c8dee2aaSAndroid Build Coastguard Worker
1400*c8dee2aaSAndroid Build Coastguard Worker    const void* mappedMemory = grMtlBuffer->mtlBuffer().contents;
1401*c8dee2aaSAndroid Build Coastguard Worker
1402*c8dee2aaSAndroid Build Coastguard Worker    SkRectMemcpy(buffer,
1403*c8dee2aaSAndroid Build Coastguard Worker                 rowBytes,
1404*c8dee2aaSAndroid Build Coastguard Worker                 mappedMemory,
1405*c8dee2aaSAndroid Build Coastguard Worker                 transBufferRowBytes,
1406*c8dee2aaSAndroid Build Coastguard Worker                 transBufferRowBytes,
1407*c8dee2aaSAndroid Build Coastguard Worker                 rect.height());
1408*c8dee2aaSAndroid Build Coastguard Worker
1409*c8dee2aaSAndroid Build Coastguard Worker    return true;
1410*c8dee2aaSAndroid Build Coastguard Worker}
1411*c8dee2aaSAndroid Build Coastguard Worker
1412*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onTransferFromBufferToBuffer(sk_sp<GrGpuBuffer> src,
1413*c8dee2aaSAndroid Build Coastguard Worker                                            size_t srcOffset,
1414*c8dee2aaSAndroid Build Coastguard Worker                                            sk_sp<GrGpuBuffer> dst,
1415*c8dee2aaSAndroid Build Coastguard Worker                                            size_t dstOffset,
1416*c8dee2aaSAndroid Build Coastguard Worker                                            size_t size) {
1417*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBuffer> GR_NORETAIN mtlSrc =  static_cast<GrMtlBuffer*>(src.get())->mtlBuffer();
1418*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBuffer> GR_NORETAIN mtlDst =  static_cast<GrMtlBuffer*>(dst.get())->mtlBuffer();
1419*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlSrc);
1420*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlDst);
1421*c8dee2aaSAndroid Build Coastguard Worker
1422*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
1423*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
1424*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
1425*c8dee2aaSAndroid Build Coastguard Worker        return false;
1426*c8dee2aaSAndroid Build Coastguard Worker    }
1427*c8dee2aaSAndroid Build Coastguard Worker
1428*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1429*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"onTransferFromBufferToBuffer"];
1430*c8dee2aaSAndroid Build Coastguard Worker#endif
1431*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder copyFromBuffer: mtlSrc
1432*c8dee2aaSAndroid Build Coastguard Worker                      sourceOffset: srcOffset
1433*c8dee2aaSAndroid Build Coastguard Worker                          toBuffer: mtlDst
1434*c8dee2aaSAndroid Build Coastguard Worker                 destinationOffset: dstOffset
1435*c8dee2aaSAndroid Build Coastguard Worker                              size: size];
1436*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1437*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1438*c8dee2aaSAndroid Build Coastguard Worker#endif
1439*c8dee2aaSAndroid Build Coastguard Worker
1440*c8dee2aaSAndroid Build Coastguard Worker    cmdBuffer->addGrBuffer(std::move(src));
1441*c8dee2aaSAndroid Build Coastguard Worker    cmdBuffer->addGrBuffer(std::move(dst));
1442*c8dee2aaSAndroid Build Coastguard Worker
1443*c8dee2aaSAndroid Build Coastguard Worker    return true;
1444*c8dee2aaSAndroid Build Coastguard Worker}
1445*c8dee2aaSAndroid Build Coastguard Worker
1446*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onTransferPixelsTo(GrTexture* texture,
1447*c8dee2aaSAndroid Build Coastguard Worker                                  SkIRect rect,
1448*c8dee2aaSAndroid Build Coastguard Worker                                  GrColorType textureColorType,
1449*c8dee2aaSAndroid Build Coastguard Worker                                  GrColorType bufferColorType,
1450*c8dee2aaSAndroid Build Coastguard Worker                                  sk_sp<GrGpuBuffer> transferBuffer,
1451*c8dee2aaSAndroid Build Coastguard Worker                                  size_t offset,
1452*c8dee2aaSAndroid Build Coastguard Worker                                  size_t rowBytes) {
1453*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(texture);
1454*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(transferBuffer);
1455*c8dee2aaSAndroid Build Coastguard Worker    if (textureColorType != bufferColorType) {
1456*c8dee2aaSAndroid Build Coastguard Worker        return false;
1457*c8dee2aaSAndroid Build Coastguard Worker    }
1458*c8dee2aaSAndroid Build Coastguard Worker
1459*c8dee2aaSAndroid Build Coastguard Worker    GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture);
1460*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> GR_NORETAIN mtlTexture = grMtlTexture->mtlTexture();
1461*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlTexture);
1462*c8dee2aaSAndroid Build Coastguard Worker
1463*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get());
1464*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBuffer> GR_NORETAIN mtlBuffer = grMtlBuffer->mtlBuffer();
1465*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(mtlBuffer);
1466*c8dee2aaSAndroid Build Coastguard Worker
1467*c8dee2aaSAndroid Build Coastguard Worker    size_t bpp = GrColorTypeBytesPerPixel(bufferColorType);
1468*c8dee2aaSAndroid Build Coastguard Worker    if (offset % bpp) {
1469*c8dee2aaSAndroid Build Coastguard Worker        return false;
1470*c8dee2aaSAndroid Build Coastguard Worker    }
1471*c8dee2aaSAndroid Build Coastguard Worker    if (GrBackendFormatBytesPerPixel(texture->backendFormat()) != bpp) {
1472*c8dee2aaSAndroid Build Coastguard Worker        return false;
1473*c8dee2aaSAndroid Build Coastguard Worker    }
1474*c8dee2aaSAndroid Build Coastguard Worker
1475*c8dee2aaSAndroid Build Coastguard Worker    MTLOrigin origin = MTLOriginMake(rect.left(), rect.top(), 0);
1476*c8dee2aaSAndroid Build Coastguard Worker
1477*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
1478*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
1479*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
1480*c8dee2aaSAndroid Build Coastguard Worker        return false;
1481*c8dee2aaSAndroid Build Coastguard Worker    }
1482*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1483*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"onTransferPixelsTo"];
1484*c8dee2aaSAndroid Build Coastguard Worker#endif
1485*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder copyFromBuffer: mtlBuffer
1486*c8dee2aaSAndroid Build Coastguard Worker                      sourceOffset: offset
1487*c8dee2aaSAndroid Build Coastguard Worker                 sourceBytesPerRow: rowBytes
1488*c8dee2aaSAndroid Build Coastguard Worker               sourceBytesPerImage: rowBytes*rect.height()
1489*c8dee2aaSAndroid Build Coastguard Worker                        sourceSize: MTLSizeMake(rect.width(), rect.height(), 1)
1490*c8dee2aaSAndroid Build Coastguard Worker                         toTexture: mtlTexture
1491*c8dee2aaSAndroid Build Coastguard Worker                  destinationSlice: 0
1492*c8dee2aaSAndroid Build Coastguard Worker                  destinationLevel: 0
1493*c8dee2aaSAndroid Build Coastguard Worker                 destinationOrigin: origin];
1494*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1495*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1496*c8dee2aaSAndroid Build Coastguard Worker#endif
1497*c8dee2aaSAndroid Build Coastguard Worker
1498*c8dee2aaSAndroid Build Coastguard Worker    return true;
1499*c8dee2aaSAndroid Build Coastguard Worker}
1500*c8dee2aaSAndroid Build Coastguard Worker
1501*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::onTransferPixelsFrom(GrSurface* surface,
1502*c8dee2aaSAndroid Build Coastguard Worker                                    SkIRect rect,
1503*c8dee2aaSAndroid Build Coastguard Worker                                    GrColorType surfaceColorType,
1504*c8dee2aaSAndroid Build Coastguard Worker                                    GrColorType bufferColorType,
1505*c8dee2aaSAndroid Build Coastguard Worker                                    sk_sp<GrGpuBuffer> transferBuffer,
1506*c8dee2aaSAndroid Build Coastguard Worker                                    size_t offset) {
1507*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(surface);
1508*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(transferBuffer);
1509*c8dee2aaSAndroid Build Coastguard Worker
1510*c8dee2aaSAndroid Build Coastguard Worker    if (surfaceColorType != bufferColorType) {
1511*c8dee2aaSAndroid Build Coastguard Worker        return false;
1512*c8dee2aaSAndroid Build Coastguard Worker    }
1513*c8dee2aaSAndroid Build Coastguard Worker
1514*c8dee2aaSAndroid Build Coastguard Worker    // Metal only supports offsets that are aligned to a pixel.
1515*c8dee2aaSAndroid Build Coastguard Worker    size_t bpp = GrColorTypeBytesPerPixel(bufferColorType);
1516*c8dee2aaSAndroid Build Coastguard Worker    if (offset % bpp) {
1517*c8dee2aaSAndroid Build Coastguard Worker        return false;
1518*c8dee2aaSAndroid Build Coastguard Worker    }
1519*c8dee2aaSAndroid Build Coastguard Worker    if (GrBackendFormatBytesPerPixel(surface->backendFormat()) != bpp) {
1520*c8dee2aaSAndroid Build Coastguard Worker        return false;
1521*c8dee2aaSAndroid Build Coastguard Worker    }
1522*c8dee2aaSAndroid Build Coastguard Worker
1523*c8dee2aaSAndroid Build Coastguard Worker    GrMtlBuffer* grMtlBuffer = static_cast<GrMtlBuffer*>(transferBuffer.get());
1524*c8dee2aaSAndroid Build Coastguard Worker
1525*c8dee2aaSAndroid Build Coastguard Worker    size_t transBufferRowBytes = bpp*rect.width();
1526*c8dee2aaSAndroid Build Coastguard Worker    size_t transBufferImageBytes = transBufferRowBytes*rect.height();
1527*c8dee2aaSAndroid Build Coastguard Worker
1528*c8dee2aaSAndroid Build Coastguard Worker    return this->readOrTransferPixels(surface,
1529*c8dee2aaSAndroid Build Coastguard Worker                                      rect,
1530*c8dee2aaSAndroid Build Coastguard Worker                                      bufferColorType,
1531*c8dee2aaSAndroid Build Coastguard Worker                                      grMtlBuffer->mtlBuffer(),
1532*c8dee2aaSAndroid Build Coastguard Worker                                      offset,
1533*c8dee2aaSAndroid Build Coastguard Worker                                      transBufferImageBytes,
1534*c8dee2aaSAndroid Build Coastguard Worker                                      transBufferRowBytes);
1535*c8dee2aaSAndroid Build Coastguard Worker}
1536*c8dee2aaSAndroid Build Coastguard Worker
1537*c8dee2aaSAndroid Build Coastguard Workerbool GrMtlGpu::readOrTransferPixels(GrSurface* surface,
1538*c8dee2aaSAndroid Build Coastguard Worker                                    SkIRect rect,
1539*c8dee2aaSAndroid Build Coastguard Worker                                    GrColorType dstColorType,
1540*c8dee2aaSAndroid Build Coastguard Worker                                    id<MTLBuffer> transferBuffer,
1541*c8dee2aaSAndroid Build Coastguard Worker                                    size_t offset,
1542*c8dee2aaSAndroid Build Coastguard Worker                                    size_t imageBytes,
1543*c8dee2aaSAndroid Build Coastguard Worker                                    size_t rowBytes) {
1544*c8dee2aaSAndroid Build Coastguard Worker    if (!check_max_blit_width(rect.width())) {
1545*c8dee2aaSAndroid Build Coastguard Worker        return false;
1546*c8dee2aaSAndroid Build Coastguard Worker    }
1547*c8dee2aaSAndroid Build Coastguard Worker
1548*c8dee2aaSAndroid Build Coastguard Worker    id<MTLTexture> mtlTexture;
1549*c8dee2aaSAndroid Build Coastguard Worker    if (GrMtlRenderTarget* rt = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget())) {
1550*c8dee2aaSAndroid Build Coastguard Worker        if (rt->numSamples() > 1) {
1551*c8dee2aaSAndroid Build Coastguard Worker            SkASSERT(rt->requiresManualMSAAResolve());  // msaa-render-to-texture not yet supported.
1552*c8dee2aaSAndroid Build Coastguard Worker            mtlTexture = rt->resolveMTLTexture();
1553*c8dee2aaSAndroid Build Coastguard Worker        } else {
1554*c8dee2aaSAndroid Build Coastguard Worker            SkASSERT(!rt->requiresManualMSAAResolve());
1555*c8dee2aaSAndroid Build Coastguard Worker            mtlTexture = rt->colorMTLTexture();
1556*c8dee2aaSAndroid Build Coastguard Worker        }
1557*c8dee2aaSAndroid Build Coastguard Worker    } else if (GrMtlTexture* texture = static_cast<GrMtlTexture*>(surface->asTexture())) {
1558*c8dee2aaSAndroid Build Coastguard Worker        mtlTexture = texture->mtlTexture();
1559*c8dee2aaSAndroid Build Coastguard Worker    }
1560*c8dee2aaSAndroid Build Coastguard Worker    if (!mtlTexture) {
1561*c8dee2aaSAndroid Build Coastguard Worker        return false;
1562*c8dee2aaSAndroid Build Coastguard Worker    }
1563*c8dee2aaSAndroid Build Coastguard Worker
1564*c8dee2aaSAndroid Build Coastguard Worker    auto cmdBuffer = this->commandBuffer();
1565*c8dee2aaSAndroid Build Coastguard Worker    id<MTLBlitCommandEncoder> GR_NORETAIN blitCmdEncoder = cmdBuffer->getBlitCommandEncoder();
1566*c8dee2aaSAndroid Build Coastguard Worker    if (!blitCmdEncoder) {
1567*c8dee2aaSAndroid Build Coastguard Worker        return false;
1568*c8dee2aaSAndroid Build Coastguard Worker    }
1569*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1570*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder pushDebugGroup:@"readOrTransferPixels"];
1571*c8dee2aaSAndroid Build Coastguard Worker#endif
1572*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder copyFromTexture: mtlTexture
1573*c8dee2aaSAndroid Build Coastguard Worker                        sourceSlice: 0
1574*c8dee2aaSAndroid Build Coastguard Worker                        sourceLevel: 0
1575*c8dee2aaSAndroid Build Coastguard Worker                       sourceOrigin: MTLOriginMake(rect.left(), rect.top(), 0)
1576*c8dee2aaSAndroid Build Coastguard Worker                         sourceSize: MTLSizeMake(rect.width(), rect.height(), 1)
1577*c8dee2aaSAndroid Build Coastguard Worker                           toBuffer: transferBuffer
1578*c8dee2aaSAndroid Build Coastguard Worker                  destinationOffset: offset
1579*c8dee2aaSAndroid Build Coastguard Worker             destinationBytesPerRow: rowBytes
1580*c8dee2aaSAndroid Build Coastguard Worker           destinationBytesPerImage: imageBytes];
1581*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1582*c8dee2aaSAndroid Build Coastguard Worker    if (this->mtlCaps().isMac()) {
1583*c8dee2aaSAndroid Build Coastguard Worker        // Sync GPU data back to the CPU
1584*c8dee2aaSAndroid Build Coastguard Worker        [blitCmdEncoder synchronizeResource: transferBuffer];
1585*c8dee2aaSAndroid Build Coastguard Worker    }
1586*c8dee2aaSAndroid Build Coastguard Worker#endif
1587*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_MTL_DEBUG_INFO
1588*c8dee2aaSAndroid Build Coastguard Worker    [blitCmdEncoder popDebugGroup];
1589*c8dee2aaSAndroid Build Coastguard Worker#endif
1590*c8dee2aaSAndroid Build Coastguard Worker
1591*c8dee2aaSAndroid Build Coastguard Worker    return true;
1592*c8dee2aaSAndroid Build Coastguard Worker}
1593*c8dee2aaSAndroid Build Coastguard Worker
1594*c8dee2aaSAndroid Build Coastguard Worker[[nodiscard]] std::unique_ptr<GrSemaphore> GrMtlGpu::makeSemaphore(bool /*isOwned*/) {
1595*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->caps()->semaphoreSupport());
1596*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlSemaphore::Make(this);
1597*c8dee2aaSAndroid Build Coastguard Worker}
1598*c8dee2aaSAndroid Build Coastguard Worker
1599*c8dee2aaSAndroid Build Coastguard Workerstd::unique_ptr<GrSemaphore> GrMtlGpu::wrapBackendSemaphore(const GrBackendSemaphore& semaphore,
1600*c8dee2aaSAndroid Build Coastguard Worker                                                            GrSemaphoreWrapType /* wrapType */,
1601*c8dee2aaSAndroid Build Coastguard Worker                                                            GrWrapOwnership /*ownership*/) {
1602*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(this->caps()->backendSemaphoreSupport());
1603*c8dee2aaSAndroid Build Coastguard Worker    return GrMtlSemaphore::MakeWrapped(GrBackendSemaphores::GetMtlHandle(semaphore),
1604*c8dee2aaSAndroid Build Coastguard Worker                                       GrBackendSemaphores::GetMtlValue(semaphore));
1605*c8dee2aaSAndroid Build Coastguard Worker}
1606*c8dee2aaSAndroid Build Coastguard Worker
1607*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::insertSemaphore(GrSemaphore* semaphore) {
1608*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) {
1609*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(semaphore);
1610*c8dee2aaSAndroid Build Coastguard Worker        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore);
1611*c8dee2aaSAndroid Build Coastguard Worker
1612*c8dee2aaSAndroid Build Coastguard Worker        this->commandBuffer()->encodeSignalEvent(mtlSem->event(), mtlSem->value());
1613*c8dee2aaSAndroid Build Coastguard Worker    }
1614*c8dee2aaSAndroid Build Coastguard Worker}
1615*c8dee2aaSAndroid Build Coastguard Worker
1616*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::waitSemaphore(GrSemaphore* semaphore) {
1617*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) {
1618*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(semaphore);
1619*c8dee2aaSAndroid Build Coastguard Worker        GrMtlSemaphore* mtlSem = static_cast<GrMtlSemaphore*>(semaphore);
1620*c8dee2aaSAndroid Build Coastguard Worker
1621*c8dee2aaSAndroid Build Coastguard Worker        this->commandBuffer()->encodeWaitForEvent(mtlSem->event(), mtlSem->value());
1622*c8dee2aaSAndroid Build Coastguard Worker    }
1623*c8dee2aaSAndroid Build Coastguard Worker}
1624*c8dee2aaSAndroid Build Coastguard Worker
1625*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::onResolveRenderTarget(GrRenderTarget* target, const SkIRect&) {
1626*c8dee2aaSAndroid Build Coastguard Worker    SkASSERT(target->numSamples() > 1);
1627*c8dee2aaSAndroid Build Coastguard Worker    GrMtlRenderTarget* rt = static_cast<GrMtlRenderTarget*>(target);
1628*c8dee2aaSAndroid Build Coastguard Worker
1629*c8dee2aaSAndroid Build Coastguard Worker    if (rt->resolveAttachment() && this->mtlCaps().renderTargetSupportsDiscardableMSAA(rt)) {
1630*c8dee2aaSAndroid Build Coastguard Worker        // We would have resolved the RT during the render pass.
1631*c8dee2aaSAndroid Build Coastguard Worker        return;
1632*c8dee2aaSAndroid Build Coastguard Worker    }
1633*c8dee2aaSAndroid Build Coastguard Worker
1634*c8dee2aaSAndroid Build Coastguard Worker    this->resolve(static_cast<GrMtlRenderTarget*>(target)->resolveAttachment(),
1635*c8dee2aaSAndroid Build Coastguard Worker                  static_cast<GrMtlRenderTarget*>(target)->colorAttachment());
1636*c8dee2aaSAndroid Build Coastguard Worker}
1637*c8dee2aaSAndroid Build Coastguard Worker
1638*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::resolve(GrMtlAttachment* resolveAttachment,
1639*c8dee2aaSAndroid Build Coastguard Worker                       GrMtlAttachment* msaaAttachment) {
1640*c8dee2aaSAndroid Build Coastguard Worker    auto renderPassDesc = [[MTLRenderPassDescriptor alloc] init];
1641*c8dee2aaSAndroid Build Coastguard Worker    auto colorAttachment = renderPassDesc.colorAttachments[0];
1642*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.texture = msaaAttachment->mtlTexture();
1643*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.resolveTexture = resolveAttachment->mtlTexture();
1644*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.loadAction = MTLLoadActionLoad;
1645*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.storeAction = MTLStoreActionMultisampleResolve;
1646*c8dee2aaSAndroid Build Coastguard Worker
1647*c8dee2aaSAndroid Build Coastguard Worker    GrMtlRenderCommandEncoder* cmdEncoder =
1648*c8dee2aaSAndroid Build Coastguard Worker            this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr, nullptr);
1649*c8dee2aaSAndroid Build Coastguard Worker    if (cmdEncoder) {
1650*c8dee2aaSAndroid Build Coastguard Worker        cmdEncoder->setLabel(@"resolveTexture");
1651*c8dee2aaSAndroid Build Coastguard Worker        this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(resolveAttachment));
1652*c8dee2aaSAndroid Build Coastguard Worker        this->commandBuffer()->addGrSurface(sk_ref_sp<const GrSurface>(msaaAttachment));
1653*c8dee2aaSAndroid Build Coastguard Worker    }
1654*c8dee2aaSAndroid Build Coastguard Worker}
1655*c8dee2aaSAndroid Build Coastguard Worker
1656*c8dee2aaSAndroid Build Coastguard WorkerGrMtlRenderCommandEncoder* GrMtlGpu::loadMSAAFromResolve(
1657*c8dee2aaSAndroid Build Coastguard Worker        GrAttachment* dst, GrMtlAttachment* src, const SkIRect& srcRect,
1658*c8dee2aaSAndroid Build Coastguard Worker        MTLRenderPassStencilAttachmentDescriptor* stencil) {
1659*c8dee2aaSAndroid Build Coastguard Worker    if (!dst) {
1660*c8dee2aaSAndroid Build Coastguard Worker        return nil;
1661*c8dee2aaSAndroid Build Coastguard Worker    }
1662*c8dee2aaSAndroid Build Coastguard Worker    if (!src || src->framebufferOnly()) {
1663*c8dee2aaSAndroid Build Coastguard Worker        return nil;
1664*c8dee2aaSAndroid Build Coastguard Worker    }
1665*c8dee2aaSAndroid Build Coastguard Worker
1666*c8dee2aaSAndroid Build Coastguard Worker    GrMtlAttachment* mtlDst = static_cast<GrMtlAttachment*>(dst);
1667*c8dee2aaSAndroid Build Coastguard Worker
1668*c8dee2aaSAndroid Build Coastguard Worker    MTLPixelFormat stencilFormat = stencil.texture.pixelFormat;
1669*c8dee2aaSAndroid Build Coastguard Worker    auto renderPipeline = this->resourceProvider().findOrCreateMSAALoadPipeline(mtlDst->mtlFormat(),
1670*c8dee2aaSAndroid Build Coastguard Worker                                                                                dst->numSamples(),
1671*c8dee2aaSAndroid Build Coastguard Worker                                                                                stencilFormat);
1672*c8dee2aaSAndroid Build Coastguard Worker
1673*c8dee2aaSAndroid Build Coastguard Worker    // Set up rendercommandencoder
1674*c8dee2aaSAndroid Build Coastguard Worker    auto renderPassDesc = [MTLRenderPassDescriptor new];
1675*c8dee2aaSAndroid Build Coastguard Worker    auto colorAttachment = renderPassDesc.colorAttachments[0];
1676*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.texture = mtlDst->mtlTexture();
1677*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.loadAction = MTLLoadActionDontCare;
1678*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.storeAction = MTLStoreActionMultisampleResolve;
1679*c8dee2aaSAndroid Build Coastguard Worker    colorAttachment.resolveTexture = src->mtlTexture();
1680*c8dee2aaSAndroid Build Coastguard Worker
1681*c8dee2aaSAndroid Build Coastguard Worker    renderPassDesc.stencilAttachment = stencil;
1682*c8dee2aaSAndroid Build Coastguard Worker
1683*c8dee2aaSAndroid Build Coastguard Worker    // We know in this case that the preceding renderCommandEncoder will not be compatible.
1684*c8dee2aaSAndroid Build Coastguard Worker    // Either it's using a different rendertarget, or we are reading from the resolve and
1685*c8dee2aaSAndroid Build Coastguard Worker    // hence we need to let the previous resolve finish. So we create a new one without checking.
1686*c8dee2aaSAndroid Build Coastguard Worker    auto renderCmdEncoder =
1687*c8dee2aaSAndroid Build Coastguard Worker                this->commandBuffer()->getRenderCommandEncoder(renderPassDesc, nullptr);
1688*c8dee2aaSAndroid Build Coastguard Worker    if (!renderCmdEncoder) {
1689*c8dee2aaSAndroid Build Coastguard Worker        return nullptr;
1690*c8dee2aaSAndroid Build Coastguard Worker    }
1691*c8dee2aaSAndroid Build Coastguard Worker
1692*c8dee2aaSAndroid Build Coastguard Worker    // Bind pipeline
1693*c8dee2aaSAndroid Build Coastguard Worker    renderCmdEncoder->setRenderPipelineState(renderPipeline->mtlPipelineState());
1694*c8dee2aaSAndroid Build Coastguard Worker    this->commandBuffer()->addResource(sk_ref_sp(renderPipeline));
1695*c8dee2aaSAndroid Build Coastguard Worker
1696*c8dee2aaSAndroid Build Coastguard Worker    // Bind src as input texture
1697*c8dee2aaSAndroid Build Coastguard Worker    renderCmdEncoder->setFragmentTexture(src->mtlTexture(), 0);
1698*c8dee2aaSAndroid Build Coastguard Worker    // No sampler needed
1699*c8dee2aaSAndroid Build Coastguard Worker    this->commandBuffer()->addGrSurface(sk_ref_sp<GrSurface>(src));
1700*c8dee2aaSAndroid Build Coastguard Worker
1701*c8dee2aaSAndroid Build Coastguard Worker    // Scissor and viewport should default to size of color attachment
1702*c8dee2aaSAndroid Build Coastguard Worker
1703*c8dee2aaSAndroid Build Coastguard Worker    // Update and bind uniform data
1704*c8dee2aaSAndroid Build Coastguard Worker    int w = srcRect.width();
1705*c8dee2aaSAndroid Build Coastguard Worker    int h = srcRect.height();
1706*c8dee2aaSAndroid Build Coastguard Worker
1707*c8dee2aaSAndroid Build Coastguard Worker    // dst rect edges in NDC (-1 to 1)
1708*c8dee2aaSAndroid Build Coastguard Worker    int dw = dst->width();
1709*c8dee2aaSAndroid Build Coastguard Worker    int dh = dst->height();
1710*c8dee2aaSAndroid Build Coastguard Worker    float dx0 = 2.f * srcRect.fLeft / dw - 1.f;
1711*c8dee2aaSAndroid Build Coastguard Worker    float dx1 = 2.f * (srcRect.fLeft + w) / dw - 1.f;
1712*c8dee2aaSAndroid Build Coastguard Worker    float dy0 = 2.f * srcRect.fTop / dh - 1.f;
1713*c8dee2aaSAndroid Build Coastguard Worker    float dy1 = 2.f * (srcRect.fTop + h) / dh - 1.f;
1714*c8dee2aaSAndroid Build Coastguard Worker
1715*c8dee2aaSAndroid Build Coastguard Worker    struct {
1716*c8dee2aaSAndroid Build Coastguard Worker        float posXform[4];
1717*c8dee2aaSAndroid Build Coastguard Worker        int textureSize[2];
1718*c8dee2aaSAndroid Build Coastguard Worker        int pad[2];
1719*c8dee2aaSAndroid Build Coastguard Worker    } uniData = {{dx1 - dx0, dy1 - dy0, dx0, dy0}, {dw, dh}, {0, 0}};
1720*c8dee2aaSAndroid Build Coastguard Worker
1721*c8dee2aaSAndroid Build Coastguard Worker    constexpr size_t uniformSize = 32;
1722*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 8.3, tvOS 9.0, *)) {
1723*c8dee2aaSAndroid Build Coastguard Worker        SkASSERT(uniformSize <= this->caps()->maxPushConstantsSize());
1724*c8dee2aaSAndroid Build Coastguard Worker        renderCmdEncoder->setVertexBytes(&uniData, uniformSize, 0);
1725*c8dee2aaSAndroid Build Coastguard Worker    } else {
1726*c8dee2aaSAndroid Build Coastguard Worker        // upload the data
1727*c8dee2aaSAndroid Build Coastguard Worker        GrRingBuffer::Slice slice = this->uniformsRingBuffer()->suballocate(uniformSize);
1728*c8dee2aaSAndroid Build Coastguard Worker        GrMtlBuffer* buffer = (GrMtlBuffer*) slice.fBuffer;
1729*c8dee2aaSAndroid Build Coastguard Worker        char* destPtr = static_cast<char*>(slice.fBuffer->map()) + slice.fOffset;
1730*c8dee2aaSAndroid Build Coastguard Worker        memcpy(destPtr, &uniData, uniformSize);
1731*c8dee2aaSAndroid Build Coastguard Worker
1732*c8dee2aaSAndroid Build Coastguard Worker        renderCmdEncoder->setVertexBuffer(buffer->mtlBuffer(), slice.fOffset, 0);
1733*c8dee2aaSAndroid Build Coastguard Worker    }
1734*c8dee2aaSAndroid Build Coastguard Worker
1735*c8dee2aaSAndroid Build Coastguard Worker    renderCmdEncoder->drawPrimitives(MTLPrimitiveTypeTriangleStrip, (NSUInteger)0, (NSUInteger)4);
1736*c8dee2aaSAndroid Build Coastguard Worker
1737*c8dee2aaSAndroid Build Coastguard Worker    return renderCmdEncoder;
1738*c8dee2aaSAndroid Build Coastguard Worker}
1739*c8dee2aaSAndroid Build Coastguard Worker
1740*c8dee2aaSAndroid Build Coastguard Worker#if defined(GPU_TEST_UTILS)
1741*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::testingOnly_startCapture() {
1742*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1743*c8dee2aaSAndroid Build Coastguard Worker        // TODO: add Metal 3 interface as well
1744*c8dee2aaSAndroid Build Coastguard Worker        MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
1745*c8dee2aaSAndroid Build Coastguard Worker        if (captureManager.isCapturing) {
1746*c8dee2aaSAndroid Build Coastguard Worker            return;
1747*c8dee2aaSAndroid Build Coastguard Worker        }
1748*c8dee2aaSAndroid Build Coastguard Worker        if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
1749*c8dee2aaSAndroid Build Coastguard Worker            MTLCaptureDescriptor* captureDescriptor = [[MTLCaptureDescriptor alloc] init];
1750*c8dee2aaSAndroid Build Coastguard Worker            captureDescriptor.captureObject = fQueue;
1751*c8dee2aaSAndroid Build Coastguard Worker
1752*c8dee2aaSAndroid Build Coastguard Worker            NSError *error;
1753*c8dee2aaSAndroid Build Coastguard Worker            if (![captureManager startCaptureWithDescriptor: captureDescriptor error:&error])
1754*c8dee2aaSAndroid Build Coastguard Worker            {
1755*c8dee2aaSAndroid Build Coastguard Worker                NSLog(@"Failed to start capture, error %@", error);
1756*c8dee2aaSAndroid Build Coastguard Worker            }
1757*c8dee2aaSAndroid Build Coastguard Worker        } else {
1758*c8dee2aaSAndroid Build Coastguard Worker            [captureManager startCaptureWithCommandQueue: fQueue];
1759*c8dee2aaSAndroid Build Coastguard Worker        }
1760*c8dee2aaSAndroid Build Coastguard Worker     }
1761*c8dee2aaSAndroid Build Coastguard Worker}
1762*c8dee2aaSAndroid Build Coastguard Worker
1763*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::testingOnly_stopCapture() {
1764*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1765*c8dee2aaSAndroid Build Coastguard Worker        MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager];
1766*c8dee2aaSAndroid Build Coastguard Worker        if (captureManager.isCapturing) {
1767*c8dee2aaSAndroid Build Coastguard Worker            [captureManager stopCapture];
1768*c8dee2aaSAndroid Build Coastguard Worker        }
1769*c8dee2aaSAndroid Build Coastguard Worker    }
1770*c8dee2aaSAndroid Build Coastguard Worker}
1771*c8dee2aaSAndroid Build Coastguard Worker#endif
1772*c8dee2aaSAndroid Build Coastguard Worker
1773*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_ENABLE_DUMP_GPU
1774*c8dee2aaSAndroid Build Coastguard Worker#include "src/utils/SkJSONWriter.h"
1775*c8dee2aaSAndroid Build Coastguard Workervoid GrMtlGpu::onDumpJSON(SkJSONWriter* writer) const {
1776*c8dee2aaSAndroid Build Coastguard Worker    // We are called by the base class, which has already called beginObject(). We choose to nest
1777*c8dee2aaSAndroid Build Coastguard Worker    // all of our caps information in a named sub-object.
1778*c8dee2aaSAndroid Build Coastguard Worker    writer->beginObject("Metal GPU");
1779*c8dee2aaSAndroid Build Coastguard Worker
1780*c8dee2aaSAndroid Build Coastguard Worker    writer->beginObject("Device");
1781*c8dee2aaSAndroid Build Coastguard Worker    writer->appendCString("name", fDevice.name.UTF8String);
1782*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1783*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, *)) {
1784*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("isHeadless", fDevice.isHeadless);
1785*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("isLowPower", fDevice.isLowPower);
1786*c8dee2aaSAndroid Build Coastguard Worker    }
1787*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, *)) {
1788*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("isRemovable", fDevice.isRemovable);
1789*c8dee2aaSAndroid Build Coastguard Worker    }
1790*c8dee2aaSAndroid Build Coastguard Worker#endif
1791*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1792*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("registryID", fDevice.registryID);
1793*c8dee2aaSAndroid Build Coastguard Worker    }
1794*c8dee2aaSAndroid Build Coastguard Worker#if defined(SK_BUILD_FOR_MAC) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
1795*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.15, *)) {
1796*c8dee2aaSAndroid Build Coastguard Worker        switch (fDevice.location) {
1797*c8dee2aaSAndroid Build Coastguard Worker            case MTLDeviceLocationBuiltIn:
1798*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("location", "builtIn");
1799*c8dee2aaSAndroid Build Coastguard Worker                break;
1800*c8dee2aaSAndroid Build Coastguard Worker            case MTLDeviceLocationSlot:
1801*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("location", "slot");
1802*c8dee2aaSAndroid Build Coastguard Worker                break;
1803*c8dee2aaSAndroid Build Coastguard Worker            case MTLDeviceLocationExternal:
1804*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("location", "external");
1805*c8dee2aaSAndroid Build Coastguard Worker                break;
1806*c8dee2aaSAndroid Build Coastguard Worker            case MTLDeviceLocationUnspecified:
1807*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("location", "unspecified");
1808*c8dee2aaSAndroid Build Coastguard Worker                break;
1809*c8dee2aaSAndroid Build Coastguard Worker            default:
1810*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("location", "unknown");
1811*c8dee2aaSAndroid Build Coastguard Worker                break;
1812*c8dee2aaSAndroid Build Coastguard Worker        }
1813*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("locationNumber", fDevice.locationNumber);
1814*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("maxTransferRate", fDevice.maxTransferRate);
1815*c8dee2aaSAndroid Build Coastguard Worker    }
1816*c8dee2aaSAndroid Build Coastguard Worker#endif  // SK_BUILD_FOR_MAC
1817*c8dee2aaSAndroid Build Coastguard Worker#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 || __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
1818*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
1819*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("hasUnifiedMemory", fDevice.hasUnifiedMemory);
1820*c8dee2aaSAndroid Build Coastguard Worker    }
1821*c8dee2aaSAndroid Build Coastguard Worker#endif
1822*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1823*c8dee2aaSAndroid Build Coastguard Worker#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
1824*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.15, *)) {
1825*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("peerGroupID", fDevice.peerGroupID);
1826*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU32("peerCount", fDevice.peerCount);
1827*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU32("peerIndex", fDevice.peerIndex);
1828*c8dee2aaSAndroid Build Coastguard Worker    }
1829*c8dee2aaSAndroid Build Coastguard Worker#endif
1830*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.12, *)) {
1831*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("recommendedMaxWorkingSetSize", fDevice.recommendedMaxWorkingSetSize);
1832*c8dee2aaSAndroid Build Coastguard Worker    }
1833*c8dee2aaSAndroid Build Coastguard Worker#endif  // SK_BUILD_FOR_MAC
1834*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1835*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("currentAllocatedSize", fDevice.currentAllocatedSize);
1836*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("maxThreadgroupMemoryLength", fDevice.maxThreadgroupMemoryLength);
1837*c8dee2aaSAndroid Build Coastguard Worker    }
1838*c8dee2aaSAndroid Build Coastguard Worker
1839*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, iOS 9.0, tvOS 9.0, *)) {
1840*c8dee2aaSAndroid Build Coastguard Worker        writer->beginObject("maxThreadsPerThreadgroup");
1841*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("width", fDevice.maxThreadsPerThreadgroup.width);
1842*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("height", fDevice.maxThreadsPerThreadgroup.height);
1843*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("depth", fDevice.maxThreadsPerThreadgroup.depth);
1844*c8dee2aaSAndroid Build Coastguard Worker        writer->endObject();
1845*c8dee2aaSAndroid Build Coastguard Worker    }
1846*c8dee2aaSAndroid Build Coastguard Worker
1847*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1848*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("areProgrammableSamplePositionsSupported",
1849*c8dee2aaSAndroid Build Coastguard Worker                           fDevice.areProgrammableSamplePositionsSupported);
1850*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("areRasterOrderGroupsSupported",
1851*c8dee2aaSAndroid Build Coastguard Worker                           fDevice.areRasterOrderGroupsSupported);
1852*c8dee2aaSAndroid Build Coastguard Worker    }
1853*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_MAC
1854*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.11, *)) {
1855*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("isDepth24Stencil8PixelFormatSupported",
1856*c8dee2aaSAndroid Build Coastguard Worker                           fDevice.isDepth24Stencil8PixelFormatSupported);
1857*c8dee2aaSAndroid Build Coastguard Worker
1858*c8dee2aaSAndroid Build Coastguard Worker    }
1859*c8dee2aaSAndroid Build Coastguard Worker#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101500
1860*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.15, *)) {
1861*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("areBarycentricCoordsSupported",
1862*c8dee2aaSAndroid Build Coastguard Worker                           fDevice.areBarycentricCoordsSupported);
1863*c8dee2aaSAndroid Build Coastguard Worker        writer->appendBool("supportsShaderBarycentricCoordinates",
1864*c8dee2aaSAndroid Build Coastguard Worker                           fDevice.supportsShaderBarycentricCoordinates);
1865*c8dee2aaSAndroid Build Coastguard Worker    }
1866*c8dee2aaSAndroid Build Coastguard Worker#endif
1867*c8dee2aaSAndroid Build Coastguard Worker#endif  // SK_BUILD_FOR_MAC
1868*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) {
1869*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("maxBufferLength", fDevice.maxBufferLength);
1870*c8dee2aaSAndroid Build Coastguard Worker    }
1871*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
1872*c8dee2aaSAndroid Build Coastguard Worker        switch (fDevice.readWriteTextureSupport) {
1873*c8dee2aaSAndroid Build Coastguard Worker            case MTLReadWriteTextureTier1:
1874*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("readWriteTextureSupport", "tier1");
1875*c8dee2aaSAndroid Build Coastguard Worker                break;
1876*c8dee2aaSAndroid Build Coastguard Worker            case MTLReadWriteTextureTier2:
1877*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("readWriteTextureSupport", "tier2");
1878*c8dee2aaSAndroid Build Coastguard Worker                break;
1879*c8dee2aaSAndroid Build Coastguard Worker            case MTLReadWriteTextureTierNone:
1880*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("readWriteTextureSupport", "tierNone");
1881*c8dee2aaSAndroid Build Coastguard Worker                break;
1882*c8dee2aaSAndroid Build Coastguard Worker            default:
1883*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("readWriteTextureSupport", "unknown");
1884*c8dee2aaSAndroid Build Coastguard Worker                break;
1885*c8dee2aaSAndroid Build Coastguard Worker        }
1886*c8dee2aaSAndroid Build Coastguard Worker        switch (fDevice.argumentBuffersSupport) {
1887*c8dee2aaSAndroid Build Coastguard Worker            case MTLArgumentBuffersTier1:
1888*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("argumentBuffersSupport", "tier1");
1889*c8dee2aaSAndroid Build Coastguard Worker                break;
1890*c8dee2aaSAndroid Build Coastguard Worker            case MTLArgumentBuffersTier2:
1891*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("argumentBuffersSupport", "tier2");
1892*c8dee2aaSAndroid Build Coastguard Worker                break;
1893*c8dee2aaSAndroid Build Coastguard Worker            default:
1894*c8dee2aaSAndroid Build Coastguard Worker                writer->appendNString("argumentBuffersSupport", "unknown");
1895*c8dee2aaSAndroid Build Coastguard Worker                break;
1896*c8dee2aaSAndroid Build Coastguard Worker        }
1897*c8dee2aaSAndroid Build Coastguard Worker    }
1898*c8dee2aaSAndroid Build Coastguard Worker    if (@available(macOS 10.14, iOS 12.0, tvOS 12.0, *)) {
1899*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("maxArgumentBufferSamplerCount", fDevice.maxArgumentBufferSamplerCount);
1900*c8dee2aaSAndroid Build Coastguard Worker    }
1901*c8dee2aaSAndroid Build Coastguard Worker#ifdef SK_BUILD_FOR_IOS
1902*c8dee2aaSAndroid Build Coastguard Worker    if (@available(iOS 13.0, tvOS 13.0, *)) {
1903*c8dee2aaSAndroid Build Coastguard Worker        writer->appendU64("sparseTileSizeInBytes", fDevice.sparseTileSizeInBytes);
1904*c8dee2aaSAndroid Build Coastguard Worker    }
1905*c8dee2aaSAndroid Build Coastguard Worker#endif
1906*c8dee2aaSAndroid Build Coastguard Worker    writer->endObject();
1907*c8dee2aaSAndroid Build Coastguard Worker
1908*c8dee2aaSAndroid Build Coastguard Worker    writer->appendCString("queue", fQueue.label.UTF8String);
1909*c8dee2aaSAndroid Build Coastguard Worker    writer->appendBool("disconnected", fDisconnected);
1910*c8dee2aaSAndroid Build Coastguard Worker
1911*c8dee2aaSAndroid Build Coastguard Worker    writer->endObject();
1912*c8dee2aaSAndroid Build Coastguard Worker}
1913*c8dee2aaSAndroid Build Coastguard Worker#endif
1914*c8dee2aaSAndroid Build Coastguard Worker
1915*c8dee2aaSAndroid Build Coastguard WorkerGR_NORETAIN_END
1916