blob: 430983f6ecf9483948bd82e980b7cc380720c7d1 [file] [log] [blame]
Greg Daniele5ddff52017-07-05 16:49:36 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "src/gpu/mtl/GrMtlGpu.h"
Greg Daniele5ddff52017-07-05 16:49:36 -04009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "src/core/SkConvertPixels.h"
11#include "src/gpu/GrRenderTargetPriv.h"
12#include "src/gpu/GrTexturePriv.h"
13#include "src/gpu/mtl/GrMtlBuffer.h"
14#include "src/gpu/mtl/GrMtlCommandBuffer.h"
15#include "src/gpu/mtl/GrMtlGpuCommandBuffer.h"
16#include "src/gpu/mtl/GrMtlTexture.h"
17#include "src/gpu/mtl/GrMtlTextureRenderTarget.h"
18#include "src/gpu/mtl/GrMtlUtil.h"
19#include "src/sksl/SkSLCompiler.h"
Timothy Liange30739a2018-07-31 10:51:17 -040020
21#import <simd/simd.h>
Greg Daniel4a081e22017-08-04 09:34:44 -040022
Greg Daniel6b7e0e22017-07-12 16:21:09 -040023#if !__has_feature(objc_arc)
24#error This file must be compiled with Arc. Use -fobjc-arc flag
25#endif
26
Greg Danielcebcb842017-07-31 10:45:52 -040027static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
28 // Mac OSX
29#ifdef SK_BUILD_FOR_MAC
30 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) {
31 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2;
32 return true;
33 }
34 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) {
35 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1;
36 return true;
37 }
38#endif
39
40 // iOS Family group 3
41#ifdef SK_BUILD_FOR_IOS
42 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) {
43 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2;
44 return true;
45 }
46 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
47 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
48 return true;
49 }
50
51 // iOS Family group 2
52 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) {
53 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3;
54 return true;
55 }
56 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) {
57 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2;
58 return true;
59 }
60 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) {
61 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
62 return true;
63 }
64
65 // iOS Family group 1
66 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) {
67 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3;
68 return true;
69 }
70 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
71 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2;
72 return true;
73 }
74 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) {
75 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
76 return true;
77 }
78#endif
79 // No supported feature sets were found
80 return false;
81}
82
Brian Salomon384fab42017-12-07 12:33:05 -050083sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options,
84 id<MTLDevice> device, id<MTLCommandQueue> queue) {
Greg Danielb76a72a2017-07-13 15:07:54 -040085 if (!device || !queue) {
86 return nullptr;
87 }
Greg Danielcebcb842017-07-31 10:45:52 -040088 MTLFeatureSet featureSet;
89 if (!get_feature_set(device, &featureSet)) {
90 return nullptr;
91 }
Brian Salomon384fab42017-12-07 12:33:05 -050092 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet));
Greg Daniele5ddff52017-07-05 16:49:36 -040093}
94
Greg Danielb76a72a2017-07-13 15:07:54 -040095GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
Greg Danielcebcb842017-07-31 10:45:52 -040096 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet)
Greg Danielb76a72a2017-07-13 15:07:54 -040097 : INHERITED(context)
98 , fDevice(device)
Timothy Liange30739a2018-07-31 10:51:17 -040099 , fQueue(queue)
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400100 , fCmdBuffer(nullptr)
Timothy Liange30739a2018-07-31 10:51:17 -0400101 , fCompiler(new SkSL::Compiler())
102 , fCopyManager(this)
Jim Van Verthc03cfaf2019-04-18 13:11:01 -0400103 , fResourceProvider(this)
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400104 , fDisconnected(false) {
Greg Danielcebcb842017-07-31 10:45:52 -0400105 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
106 fCaps = fMtlCaps;
Greg Danielb76a72a2017-07-13 15:07:54 -0400107}
Greg Daniel4a081e22017-08-04 09:34:44 -0400108
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400109GrMtlGpu::~GrMtlGpu() {
110 if (!fDisconnected) {
111 this->destroyResources();
112 }
113}
114
115void GrMtlGpu::disconnect(DisconnectType type) {
116 INHERITED::disconnect(type);
117
118 if (DisconnectType::kCleanup == type) {
119 this->destroyResources();
120 } else {
121 delete fCmdBuffer;
122 fCmdBuffer = nullptr;
123
124 fQueue = nil;
125 fDevice = nil;
126
127 fDisconnected = true;
128 }
129}
130
131void GrMtlGpu::destroyResources() {
132 // Will implicitly delete the command buffer
133 this->submitCommandBuffer(SyncQueue::kForce_SyncQueue);
134
135 fQueue = nil;
136 fDevice = nil;
137}
138
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400139GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer(
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400140 GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds,
Timothy Liange70604e2018-07-19 09:49:46 -0400141 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
142 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
Jim Van Verthf16e0742019-04-02 14:33:03 -0400143 SK_BEGIN_AUTORELEASE_BLOCK
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400144 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo);
Jim Van Verthf16e0742019-04-02 14:33:03 -0400145 SK_END_AUTORELEASE_BLOCK
Timothy Liange70604e2018-07-19 09:49:46 -0400146}
147
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400148GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture,
149 GrSurfaceOrigin origin) {
Timothy Liange70604e2018-07-19 09:49:46 -0400150 return new GrMtlGpuTextureCommandBuffer(this, texture, origin);
151}
152
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400153void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400154 GrMtlGpuRTCommandBuffer* mtlRTCmdBuffer =
155 reinterpret_cast<GrMtlGpuRTCommandBuffer*>(buffer->asRTCommandBuffer());
156 if (mtlRTCmdBuffer) {
157 mtlRTCmdBuffer->submit();
158 }
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400159 delete buffer;
160}
161
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400162GrMtlCommandBuffer* GrMtlGpu::commandBuffer() {
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400163 if (!fCmdBuffer) {
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400164 fCmdBuffer = GrMtlCommandBuffer::Create(fQueue);
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400165 }
166 return fCmdBuffer;
167}
168
Timothy Liangef21d7e2018-07-02 17:03:30 -0400169void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400170 if (fCmdBuffer) {
171 fCmdBuffer->commit(SyncQueue::kForce_SyncQueue == sync);
172 delete fCmdBuffer;
173 fCmdBuffer = nullptr;
Timothy Liangef21d7e2018-07-02 17:03:30 -0400174 }
Timothy Liangef21d7e2018-07-02 17:03:30 -0400175}
176
Brian Salomondbf70722019-02-07 11:31:24 -0500177sk_sp<GrGpuBuffer> GrMtlGpu::onCreateBuffer(size_t size, GrGpuBufferType type,
178 GrAccessPattern accessPattern, const void* data) {
Brian Salomon12d22642019-01-29 14:38:50 -0500179 return GrMtlBuffer::Make(this, size, type, accessPattern, data);
Timothy Liang49528b62018-08-02 14:18:37 -0400180}
181
Timothy Liangff19c8f2018-07-11 13:27:21 -0400182static bool check_max_blit_width(int widthInPixels) {
183 if (widthInPixels > 32767) {
184 SkASSERT(false); // surfaces should not be this wide anyway
185 return false;
186 }
187 return true;
188}
189
190bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
191 GrColorType dataColorType, const GrMipLevel texels[],
192 int mipLevelCount) {
193 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
Jim Van Verth444b3092019-03-11 11:31:23 -0400194 // The assumption is either that we have no mipmaps, or that our rect is the entire texture
195 SkASSERT(1 == mipLevelCount ||
196 (0 == left && 0 == top && width == tex->width() && height == tex->height()));
197
198 // We assume that if the texture has mip levels, we either upload to all the levels or just the
199 // first.
200 SkASSERT(1 == mipLevelCount || mipLevelCount == (tex->texturePriv().maxMipMapLevel() + 1));
201
202 // If we're uploading compressed data then we should be using uploadCompressedTexData
203 SkASSERT(!GrPixelConfigIsCompressed(GrColorTypeToPixelConfig(dataColorType,
204 GrSRGBEncoded::kNo)));
205
Timothy Liangff19c8f2018-07-11 13:27:21 -0400206 if (!check_max_blit_width(width)) {
207 return false;
208 }
209 if (width == 0 || height == 0) {
210 return false;
211 }
212 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
213 return false;
214 }
215
216 id<MTLTexture> mtlTexture = tex->mtlTexture();
217 SkASSERT(mtlTexture);
218 // Either upload only the first miplevel or all miplevels
219 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
220
Jim Van Verth444b3092019-03-11 11:31:23 -0400221 // TODO: implement some way of reusing transfer buffers?
222 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400223
Jim Van Verth444b3092019-03-11 11:31:23 -0400224 SkTArray<size_t> individualMipOffsets(mipLevelCount);
225 individualMipOffsets.push_back(0);
226 size_t combinedBufferSize = width * bpp * height;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400227 int currentWidth = width;
228 int currentHeight = height;
Jim Van Verth444b3092019-03-11 11:31:23 -0400229 if (!texels[0].fPixels) {
230 combinedBufferSize = 0;
231 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400232
Jim Van Verth444b3092019-03-11 11:31:23 -0400233 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
234 // config. This works with the assumption that the bytes in pixel config is always a power of 2.
235 SkASSERT((bpp & (bpp - 1)) == 0);
236 const size_t alignmentMask = 0x3 | (bpp - 1);
237 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; currentMipLevel++) {
238 currentWidth = SkTMax(1, currentWidth/2);
239 currentHeight = SkTMax(1, currentHeight/2);
240
241 if (texels[currentMipLevel].fPixels) {
242 const size_t trimmedSize = currentWidth * bpp * currentHeight;
243 const size_t alignmentDiff = combinedBufferSize & alignmentMask;
244 if (alignmentDiff != 0) {
245 combinedBufferSize += alignmentMask - alignmentDiff + 1;
246 }
247 individualMipOffsets.push_back(combinedBufferSize);
248 combinedBufferSize += trimmedSize;
249 } else {
250 individualMipOffsets.push_back(0);
251 }
252 }
253 if (0 == combinedBufferSize) {
254 // We don't actually have any data to upload so just return success
255 return true;
256 }
257
Jim Van Verthdd15d692019-04-22 15:29:53 -0400258 sk_sp<GrMtlBuffer> transferBuffer = GrMtlBuffer::Make(this, combinedBufferSize,
259 GrGpuBufferType::kXferCpuToGpu,
260 kStream_GrAccessPattern);
261 if (!transferBuffer) {
Jim Van Verth444b3092019-03-11 11:31:23 -0400262 return false;
263 }
264
Jim Van Verthdd15d692019-04-22 15:29:53 -0400265 char* buffer = (char*) transferBuffer->map();
266 size_t bufferOffset = transferBuffer->offset();
Jim Van Verth444b3092019-03-11 11:31:23 -0400267
268 currentWidth = width;
269 currentHeight = height;
270 int layerHeight = tex->height();
271 MTLOrigin origin = MTLOriginMake(left, top, 0);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400272
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400273 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
Timothy Liangff19c8f2018-07-11 13:27:21 -0400274 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
Jim Van Verth444b3092019-03-11 11:31:23 -0400275 if (texels[currentMipLevel].fPixels) {
276 SkASSERT(1 == mipLevelCount || currentHeight == layerHeight);
277 const size_t trimRowBytes = currentWidth * bpp;
278 const size_t rowBytes = texels[currentMipLevel].fRowBytes
279 ? texels[currentMipLevel].fRowBytes
280 : trimRowBytes;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400281
Jim Van Verth444b3092019-03-11 11:31:23 -0400282 // copy data into the buffer, skipping the trailing bytes
283 char* dst = buffer + individualMipOffsets[currentMipLevel];
284 const char* src = (const char*)texels[currentMipLevel].fPixels;
285 SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight);
286
Jim Van Verthdd15d692019-04-22 15:29:53 -0400287 [blitCmdEncoder copyFromBuffer: transferBuffer->mtlBuffer()
288 sourceOffset: bufferOffset + individualMipOffsets[currentMipLevel]
Jim Van Verth444b3092019-03-11 11:31:23 -0400289 sourceBytesPerRow: trimRowBytes
290 sourceBytesPerImage: trimRowBytes*currentHeight
Jim Van Verth686046b2019-03-18 15:39:22 -0400291 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
Jim Van Verth444b3092019-03-11 11:31:23 -0400292 toTexture: mtlTexture
293 destinationSlice: 0
294 destinationLevel: currentMipLevel
295 destinationOrigin: origin];
296 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400297 currentWidth = SkTMax(1, currentWidth/2);
298 currentHeight = SkTMax(1, currentHeight/2);
Jim Van Verth444b3092019-03-11 11:31:23 -0400299 layerHeight = currentHeight;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400300 }
Jim Van Verthdd15d692019-04-22 15:29:53 -0400301 transferBuffer->unmap();
Timothy Liangff19c8f2018-07-11 13:27:21 -0400302
303 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
304 tex->texturePriv().markMipMapsDirty();
305 }
Jim Van Verth444b3092019-03-11 11:31:23 -0400306
Timothy Liangff19c8f2018-07-11 13:27:21 -0400307 return true;
308}
309
Jim Van Verth686046b2019-03-18 15:39:22 -0400310bool GrMtlGpu::clearTexture(GrMtlTexture* tex, GrColorType dataColorType) {
311 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
312
313 // If we're uploading compressed data then we should be using uploadCompressedTexData
314 SkASSERT(!GrPixelConfigIsCompressed(GrColorTypeToPixelConfig(dataColorType,
315 GrSRGBEncoded::kNo)));
316
317 id<MTLTexture> mtlTexture = tex->mtlTexture();
318 SkASSERT(mtlTexture);
319 // Either upload only the first miplevel or all miplevels
320 int mipLevelCount = (int)mtlTexture.mipmapLevelCount;
321
322 // TODO: implement some way of reusing transfer buffers?
323 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
324
325 SkTArray<size_t> individualMipOffsets(mipLevelCount);
326 individualMipOffsets.push_back(0);
327 int width = tex->width();
328 int height = tex->height();
329 size_t combinedBufferSize = width * bpp * height;
330 int currentWidth = width;
331 int currentHeight = height;
332
333 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
334 // config. This works with the assumption that the bytes in pixel config is always a power of 2.
335 // TODO: can we just copy from a single buffer the size of the top level w/o a perf penalty?
336 SkASSERT((bpp & (bpp - 1)) == 0);
337 const size_t alignmentMask = 0x3 | (bpp - 1);
338 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; currentMipLevel++) {
339 currentWidth = SkTMax(1, currentWidth/2);
340 currentHeight = SkTMax(1, currentHeight/2);
341
342 const size_t trimmedSize = currentWidth * bpp * currentHeight;
343 const size_t alignmentDiff = combinedBufferSize & alignmentMask;
344 if (alignmentDiff != 0) {
345 combinedBufferSize += alignmentMask - alignmentDiff + 1;
346 }
347 individualMipOffsets.push_back(combinedBufferSize);
348 combinedBufferSize += trimmedSize;
349 }
350 if (0 == combinedBufferSize) {
351 // We don't actually have any data to upload so just return success
352 return true;
353 }
354
355 // TODO: Create GrMtlTransferBuffer
356 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize
357 options: MTLResourceStorageModePrivate];
358 if (nil == transferBuffer) {
359 return false;
360 }
361
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400362 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
Jim Van Verth686046b2019-03-18 15:39:22 -0400363 // clear the buffer to transparent black
364 NSRange clearRange;
365 clearRange.location = 0;
366 clearRange.length = combinedBufferSize;
367 [blitCmdEncoder fillBuffer: transferBuffer
368 range: clearRange
369 value: 0];
370
371 // now copy buffer to texture
372 currentWidth = width;
373 currentHeight = height;
374 MTLOrigin origin = MTLOriginMake(0, 0, 0);
375 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
376 const size_t rowBytes = currentWidth * bpp;
377
378 [blitCmdEncoder copyFromBuffer: transferBuffer
379 sourceOffset: individualMipOffsets[currentMipLevel]
380 sourceBytesPerRow: rowBytes
381 sourceBytesPerImage: rowBytes*currentHeight
382 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
383 toTexture: mtlTexture
384 destinationSlice: 0
385 destinationLevel: currentMipLevel
386 destinationOrigin: origin];
387 currentWidth = SkTMax(1, currentWidth/2);
388 currentHeight = SkTMax(1, currentHeight/2);
389 }
Jim Van Verth686046b2019-03-18 15:39:22 -0400390
391 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
392 tex->texturePriv().markMipMapsDirty();
393 }
394
395 return true;
396}
397
Ethan Nicholas01063512018-10-08 16:58:25 -0400398GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
399 int width,
400 int height) {
401 SkASSERT(width >= rt->width());
402 SkASSERT(height >= rt->height());
403
404 int samples = rt->numStencilSamples();
405
406 const GrMtlCaps::StencilFormat& sFmt = this->mtlCaps().preferredStencilFormat();
407
Jim Van Verthf16e0742019-04-02 14:33:03 -0400408 SK_BEGIN_AUTORELEASE_BLOCK
Ethan Nicholas01063512018-10-08 16:58:25 -0400409 GrMtlStencilAttachment* stencil(GrMtlStencilAttachment::Create(this,
410 width,
411 height,
412 samples,
413 sFmt));
414 fStats.incStencilAttachmentCreates();
415 return stencil;
Jim Van Verthf16e0742019-04-02 14:33:03 -0400416 SK_END_AUTORELEASE_BLOCK
Ethan Nicholas01063512018-10-08 16:58:25 -0400417}
418
Greg Daniel4a081e22017-08-04 09:34:44 -0400419sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500420 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400421 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
422
423 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
424 return nullptr;
425 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400426 MTLPixelFormat format;
427 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
428 return nullptr;
429 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400430
Jim Van Verth1676cb92019-01-15 13:24:45 -0500431 if (GrPixelConfigIsCompressed(desc.fConfig)) {
432 return nullptr; // TODO: add compressed texture support
433 }
434
Greg Daniel4a081e22017-08-04 09:34:44 -0400435 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400436
Jim Van Verth61610be2019-03-07 15:33:49 -0500437 sk_sp<GrMtlTexture> tex;
438 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liang58f153d2018-07-02 17:36:20 -0400439 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
Jim Van Verth61610be2019-03-07 15:33:49 -0500440 // requested, this TexDesc describes the resolved texture. Therefore we always have samples
441 // set to 1.
Timothy Liang58f153d2018-07-02 17:36:20 -0400442 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
443 texDesc.textureType = MTLTextureType2D;
444 texDesc.pixelFormat = format;
445 texDesc.width = desc.fWidth;
446 texDesc.height = desc.fHeight;
447 texDesc.depth = 1;
448 texDesc.mipmapLevelCount = mipLevels;
449 texDesc.sampleCount = 1;
450 texDesc.arrayLength = 1;
451 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
452 // Make all textures have private gpu only access. We can use transfer buffers or textures
453 // to copy to them.
454 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400455 texDesc.usage = MTLTextureUsageShaderRead;
456 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400457
458 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
459 if (mipLevels > 1) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400460 mipMapsStatus = GrMipMapsStatus::kValid;
461 for (int i = 0; i < mipLevels; ++i) {
462 if (!texels[i].fPixels) {
463 mipMapsStatus = GrMipMapsStatus::kDirty;
464 break;
Timothy Liang58f153d2018-07-02 17:36:20 -0400465 }
466 }
467 }
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400468
Greg Daniel4a081e22017-08-04 09:34:44 -0400469 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400470 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400471 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400472 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400473 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400474 }
475
476 if (!tex) {
477 return nullptr;
478 }
479
Timothy Liangff19c8f2018-07-11 13:27:21 -0400480 auto colorType = GrPixelConfigToColorType(desc.fConfig);
481 if (mipLevelCount && texels[0].fPixels) {
482 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
483 mipLevelCount)) {
484 tex->unref();
485 return nullptr;
486 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400487 }
488
489 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
Jim Van Verth686046b2019-03-18 15:39:22 -0400490 this->clearTexture(tex.get(), colorType);
Greg Daniel4a081e22017-08-04 09:34:44 -0400491 }
Jim Van Verth686046b2019-03-18 15:39:22 -0400492 SK_END_AUTORELEASE_BLOCK
493
Timothy Liangff19c8f2018-07-11 13:27:21 -0400494 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400495}
Timothy Liange886e802018-07-02 16:03:28 -0400496
497static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
498 GrWrapOwnership ownership) {
499 GrMtlTextureInfo textureInfo;
500 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
501 return nil;
502 }
503 return GrGetMTLTexture(textureInfo.fTexture, ownership);
504}
505
506static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
507 GrMtlTextureInfo textureInfo;
508 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
509 return nil;
510 }
511 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
512}
513
514static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
515 bool isRenderTarget, GrPixelConfig config) {
516 if (isRenderTarget) {
517 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
518 }
519 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
520 surfaceDesc->fWidth = mtlTexture.width;
521 surfaceDesc->fHeight = mtlTexture.height;
522 surfaceDesc->fConfig = config;
523 surfaceDesc->fSampleCnt = 1;
524}
525
526sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
Brian Salomonfa2ebea2019-01-24 15:58:58 -0500527 GrWrapOwnership ownership,
528 GrWrapCacheable cacheable, GrIOType ioType) {
Timothy Liange886e802018-07-02 16:03:28 -0400529 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
530 if (!mtlTexture) {
531 return nullptr;
532 }
533
534 GrSurfaceDesc surfDesc;
535 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
536
Brian Salomonfa2ebea2019-01-24 15:58:58 -0500537 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, ioType);
Timothy Liange886e802018-07-02 16:03:28 -0400538}
539
540sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
541 int sampleCnt,
Brian Salomonaa6ca0a2019-01-24 16:03:07 -0500542 GrWrapOwnership ownership,
543 GrWrapCacheable cacheable) {
Timothy Liange886e802018-07-02 16:03:28 -0400544 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
545 if (!mtlTexture) {
546 return nullptr;
547 }
548
549 GrSurfaceDesc surfDesc;
550 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
551 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
552 if (!surfDesc.fSampleCnt) {
553 return nullptr;
554 }
555
Brian Salomonaa6ca0a2019-01-24 16:03:07 -0500556 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture,
557 cacheable);
Timothy Liange886e802018-07-02 16:03:28 -0400558}
559
560sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
561 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
562 if (backendRT.sampleCnt() > 1) {
563 return nullptr;
564 }
565 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
566 if (!mtlTexture) {
567 return nullptr;
568 }
569
570 GrSurfaceDesc surfDesc;
571 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
572
573 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
574}
575
576sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
577 const GrBackendTexture& backendTex, int sampleCnt) {
578 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
579 GrWrapOwnership::kBorrow_GrWrapOwnership);
580 if (!mtlTexture) {
581 return nullptr;
582 }
583
584 GrSurfaceDesc surfDesc;
585 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
586 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
587 if (!surfDesc.fSampleCnt) {
588 return nullptr;
589 }
590
591 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
592}
593
Jim Van Verth686046b2019-03-18 15:39:22 -0400594bool GrMtlGpu::onRegenerateMipMapLevels(GrTexture* texture) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400595 GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture);
596 id<MTLTexture> mtlTexture = grMtlTexture->mtlTexture();
597
598 // Automatic mipmap generation is only supported by color-renderable formats
599 if (!fMtlCaps->isConfigRenderable(texture->config()) &&
600 // We have pixel configs marked as textureable-only that use RGBA8 as the internal format
601 MTLPixelFormatRGBA8Unorm != mtlTexture.pixelFormat) {
602 return false;
603 }
604
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400605 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400606 [blitCmdEncoder generateMipmapsForTexture: mtlTexture];
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400607
608 return true;
Jim Van Verth686046b2019-03-18 15:39:22 -0400609}
610
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400611bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrPixelConfig config, MTLPixelFormat format,
612 int w, int h, bool texturable,
Robert Phillips646f6372018-09-25 09:31:10 -0400613 bool renderable, GrMipMapped mipMapped,
614 const void* srcData, size_t srcRowBytes,
615 GrMtlTextureInfo* info) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400616 SkASSERT(texturable || renderable);
617 if (!texturable) {
618 SkASSERT(GrMipMapped::kNo == mipMapped);
619 SkASSERT(!srcData);
620 }
621
Timothy Liang760dbc42018-07-17 13:28:20 -0400622 if (texturable && !fMtlCaps->isConfigTexturable(config)) {
623 return false;
624 }
625 if (renderable && !fMtlCaps->isConfigRenderable(config)) {
626 return false;
627 }
628 // Currently we don't support uploading pixel data when mipped.
629 if (srcData && GrMipMapped::kYes == mipMapped) {
630 return false;
631 }
632 if(!check_max_blit_width(w)) {
633 return false;
634 }
635
Jim Van Verth61610be2019-03-07 15:33:49 -0500636 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liang760dbc42018-07-17 13:28:20 -0400637 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
638 MTLTextureDescriptor* desc =
639 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
640 width: w
641 height: h
642 mipmapped: mipmapped];
643 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
644 desc.storageMode = MTLStorageModePrivate;
645 desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
646 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
647 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
648
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400649 size_t bpp = GrBytesPerPixel(config);
Jim Van Verth8cc42482019-04-05 14:16:37 -0400650 if (!srcRowBytes) {
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400651 srcRowBytes = w * bpp;
Jim Van Verth8cc42482019-04-05 14:16:37 -0400652#ifdef SK_BUILD_FOR_MAC
653 // On MacOS, the fillBuffer command needs a range with a multiple of 4 bytes
654 srcRowBytes = ((srcRowBytes + 3) & (~3));
655#endif
Timothy Liang760dbc42018-07-17 13:28:20 -0400656 }
Jim Van Verth8cc42482019-04-05 14:16:37 -0400657 size_t bufferSize = srcRowBytes * h;
658 NSUInteger options = 0; // TODO: consider other options here
Timothy Liang4e679842018-08-08 14:21:12 -0400659#ifdef SK_BUILD_FOR_MAC
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400660 options |= MTLResourceStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400661#else
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400662 options |= MTLResourceStorageModeShared;
Timothy Liang4e679842018-08-08 14:21:12 -0400663#endif
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400664
665 // TODO: Create GrMtlTransferBuffer
Jim Van Verth8cc42482019-04-05 14:16:37 -0400666 id<MTLBuffer> transferBuffer;
667 if (srcData) {
668 transferBuffer = [fDevice newBufferWithBytes: srcData
669 length: bufferSize
670 options: options];
671 } else {
672 transferBuffer = [fDevice newBufferWithLength: bufferSize
673 options: options];
674 }
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400675 if (nil == transferBuffer) {
676 return false;
677 }
Timothy Liang760dbc42018-07-17 13:28:20 -0400678
Timothy Liang760dbc42018-07-17 13:28:20 -0400679 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
680 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
Jim Van Verth8cc42482019-04-05 14:16:37 -0400681 if (!srcData) {
682 [blitCmdEncoder fillBuffer: transferBuffer
683 range: NSMakeRange(0, bufferSize)
684 value: 0];
685 }
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400686 [blitCmdEncoder copyFromBuffer: transferBuffer
687 sourceOffset: 0
Jim Van Verth8cc42482019-04-05 14:16:37 -0400688 sourceBytesPerRow: srcRowBytes
689 sourceBytesPerImage: bufferSize
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400690 sourceSize: MTLSizeMake(w, h, 1)
691 toTexture: testTexture
692 destinationSlice: 0
693 destinationLevel: 0
Jim Van Verth8cc42482019-04-05 14:16:37 -0400694 destinationOrigin: MTLOriginMake(0, 0, 0)];
Timothy Liang760dbc42018-07-17 13:28:20 -0400695 [blitCmdEncoder endEncoding];
696 [cmdBuffer commit];
697 [cmdBuffer waitUntilCompleted];
698
699 info->fTexture = GrReleaseId(testTexture);
Jim Van Verth61610be2019-03-07 15:33:49 -0500700 SK_END_AUTORELEASE_BLOCK
701
Timothy Liang760dbc42018-07-17 13:28:20 -0400702 return true;
703}
704
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400705static bool mtl_format_to_pixel_config(MTLPixelFormat format, GrPixelConfig* config) {
706 GrPixelConfig dontCare;
707 if (!config) {
708 config = &dontCare;
709 }
710
711 switch (format) {
712 case MTLPixelFormatInvalid:
713 *config = kUnknown_GrPixelConfig;
714 return false;
715 case MTLPixelFormatRGBA8Unorm:
716 *config = kRGBA_8888_GrPixelConfig;
717 return true;
718 case MTLPixelFormatRG8Unorm:
719 *config = kRG_88_GrPixelConfig;
720 return true;
721 case MTLPixelFormatBGRA8Unorm:
722 *config = kBGRA_8888_GrPixelConfig;
723 return true;
724 case MTLPixelFormatRGBA8Unorm_sRGB:
725 *config = kSRGBA_8888_GrPixelConfig;
726 return true;
727 case MTLPixelFormatBGRA8Unorm_sRGB:
728 *config = kSBGRA_8888_GrPixelConfig;
729 return true;
730 case MTLPixelFormatRGB10A2Unorm:
731 *config = kRGBA_1010102_GrPixelConfig;
732 return true;
733#ifdef SK_BUILD_FOR_IOS
734 case MTLPixelFormatB5G6R5Unorm:
735 *config = kRGB_565_GrPixelConfig;
736 return true;
737 case MTLPixelFormatABGR4Unorm:
738 *config = kRGBA_4444_GrPixelConfig;
739 return true;
740#endif
741 case MTLPixelFormatR8Unorm:
742 *config = kAlpha_8_GrPixelConfig;
743 return true;
744 case MTLPixelFormatRGBA32Float:
745 *config = kRGBA_float_GrPixelConfig;
746 return true;
747 case MTLPixelFormatRG32Float:
748 *config = kRG_float_GrPixelConfig;
749 return true;
750 case MTLPixelFormatRGBA16Float:
751 *config = kRGBA_half_GrPixelConfig;
752 return true;
753 case MTLPixelFormatR16Float:
754 *config = kAlpha_half_GrPixelConfig;
755 return true;
756#ifdef SK_BUILD_FOR_IOS
757 case MTLPixelFormatETC2_RGB8:
758 *config = kRGB_ETC1_GrPixelConfig;
759 return true;
760#endif
761 default:
762 return false;
763 }
764
765 SK_ABORT("Unexpected config");
766 return false;
767}
768
769GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(int w, int h,
770 const GrBackendFormat& format,
771 GrMipMapped mipMapped,
772 GrRenderable renderable,
773 const void* pixels, size_t rowBytes) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400774 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
775 return GrBackendTexture();
776 }
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400777
778 const GrMTLPixelFormat* mtlFormat = format.getMtlFormat();
779 if (!mtlFormat) {
780 return GrBackendTexture();
Timothy Liang760dbc42018-07-17 13:28:20 -0400781 }
782
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400783 GrPixelConfig config;
784
785 if (!mtl_format_to_pixel_config(static_cast<MTLPixelFormat>(*mtlFormat), &config)) {
786 return GrBackendTexture();
787 }
788
789 GrMtlTextureInfo info;
790 if (!this->createTestingOnlyMtlTextureInfo(config, static_cast<MTLPixelFormat>(*mtlFormat),
791 w, h, true,
792 GrRenderable::kYes == renderable, mipMapped,
793 pixels, rowBytes, &info)) {
794 return {};
795 }
Robert Phillips646f6372018-09-25 09:31:10 -0400796
Timothy Liang760dbc42018-07-17 13:28:20 -0400797 GrBackendTexture backendTex(w, h, mipMapped, info);
798 backendTex.fConfig = config;
799 return backendTex;
800}
801
Robert Phillipsf0ced622019-05-16 09:06:25 -0400802void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
803 SkASSERT(GrBackendApi::kMetal == tex.fBackend);
804
805 GrMtlTextureInfo info;
806 if (tex.getMtlTextureInfo(&info)) {
807 // Adopts the metal texture so that ARC will clean it up.
808 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
809 }
810}
811
812#if GR_TEST_UTILS
Timothy Liang760dbc42018-07-17 13:28:20 -0400813bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400814 SkASSERT(GrBackendApi::kMetal == tex.backend());
Timothy Liang760dbc42018-07-17 13:28:20 -0400815
816 GrMtlTextureInfo info;
817 if (!tex.getMtlTextureInfo(&info)) {
818 return false;
819 }
820 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
821 GrWrapOwnership::kBorrow_GrWrapOwnership);
822 if (!mtlTexture) {
823 return false;
824 }
825 return mtlTexture.usage & MTLTextureUsageShaderRead;
826}
827
Brian Osman2d010b62018-08-09 10:55:09 -0400828GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400829 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
830 return GrBackendRenderTarget();
831 }
Robert Phillips646f6372018-09-25 09:31:10 -0400832
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400833 GrPixelConfig config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
834
835 MTLPixelFormat format;
836 if (!GrPixelConfigToMTLFormat(config, &format)) {
837 return GrBackendRenderTarget();
Timothy Liang760dbc42018-07-17 13:28:20 -0400838 }
839
Robert Phillips9dbcdcc2019-05-13 10:40:06 -0400840 GrMtlTextureInfo info;
841 if (!this->createTestingOnlyMtlTextureInfo(config, format, w, h, false, true,
842 GrMipMapped::kNo, nullptr, 0, &info)) {
843 return {};
844 }
Robert Phillips646f6372018-09-25 09:31:10 -0400845
Timothy Liang760dbc42018-07-17 13:28:20 -0400846 GrBackendRenderTarget backendRT(w, h, 1, info);
847 backendRT.fConfig = config;
848 return backendRT;
849}
850
851void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400852 SkASSERT(GrBackendApi::kMetal == rt.fBackend);
Timothy Liang760dbc42018-07-17 13:28:20 -0400853
854 GrMtlTextureInfo info;
855 if (rt.getMtlTextureInfo(&info)) {
856 this->testingOnly_flushGpuAndSync();
857 // Adopts the metal texture so that ARC will clean it up.
858 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
859 }
860}
861
862void GrMtlGpu::testingOnly_flushGpuAndSync() {
863 this->submitCommandBuffer(kForce_SyncQueue);
864}
865#endif // GR_TEST_UTILS
866
Timothy Liange35055f2018-07-20 16:53:00 -0400867static int get_surface_sample_cnt(GrSurface* surf) {
868 if (const GrRenderTarget* rt = surf->asRenderTarget()) {
869 return rt->numColorSamples();
870 }
871 return 0;
872}
873
874bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
875 GrSurface* src, GrSurfaceOrigin srcOrigin,
876 const SkIRect& srcRect, const SkIPoint& dstPoint) {
877#ifdef SK_DEBUG
878 int dstSampleCnt = get_surface_sample_cnt(dst);
879 int srcSampleCnt = get_surface_sample_cnt(src);
880 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
881 src->config(), srcSampleCnt, srcOrigin,
882 srcRect, dstPoint, dst == src));
883#endif
Timothy Liange30739a2018-07-31 10:51:17 -0400884 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
885 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
Timothy Liange35055f2018-07-20 16:53:00 -0400886
887 // Flip rect if necessary
888 SkIRect srcMtlRect;
889 srcMtlRect.fLeft = srcRect.fLeft;
890 srcMtlRect.fRight = srcRect.fRight;
891 SkIRect dstRect;
892 dstRect.fLeft = dstPoint.fX;
893 dstRect.fRight = dstPoint.fX + srcRect.width();
894
895 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
896 srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
897 srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
898 } else {
899 srcMtlRect.fTop = srcRect.fTop;
900 srcMtlRect.fBottom = srcRect.fBottom;
901 }
902
903 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
904 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
905 } else {
906 dstRect.fTop = dstPoint.fY;
907 }
908 dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
909
Jim Van Verth75c3ae42019-04-22 17:07:53 -0400910 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
Timothy Liange35055f2018-07-20 16:53:00 -0400911 [blitCmdEncoder copyFromTexture: srcTex
912 sourceSlice: 0
913 sourceLevel: 0
914 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
915 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
916 toTexture: dstTex
917 destinationSlice: 0
918 destinationLevel: 0
919 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
Timothy Liange35055f2018-07-20 16:53:00 -0400920
921 return true;
922}
923
Timothy Liang70c787a2018-08-01 09:56:25 -0400924bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
925 GrSurface* src, GrSurfaceOrigin srcOrigin,
926 const SkIRect& srcRect, const SkIPoint& dstPoint) {
927#ifdef SK_DEBUG
928 int dstSampleCnt = get_surface_sample_cnt(dst);
929 int srcSampleCnt = get_surface_sample_cnt(src);
930 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
931 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
932 src->config(), srcSampleCnt, srcOrigin,
933 srcRect, dstPoint, dst == src));
934 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
935 src->config(), SkToBool(src->asTexture())));
936 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
937 SkToBool(src->asTexture())));
938#endif
939 GrSurfaceDesc surfDesc;
940 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
941 surfDesc.fWidth = srcRect.width();
942 surfDesc.fHeight = srcRect.height();
943 surfDesc.fConfig = dst->config();
944 surfDesc.fSampleCnt = 1;
945
946 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
947 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
948 textureDesc.width = srcRect.width();
949 textureDesc.height = srcRect.height();
950 textureDesc.mipmapLevelCount = 1;
951 textureDesc.usage |= MTLTextureUsageRenderTarget;
952
953 sk_sp<GrMtlTexture> transferTexture =
954 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
955 SkBudgeted::kYes,
956 surfDesc,
957 textureDesc,
958 GrMipMapsStatus::kNotAllocated);
959
960 GrSurfaceOrigin transferOrigin = dstOrigin;
961 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
962 SkToBool(transferTexture->asRenderTarget()),
963 src->config(),
964 SkToBool(src->asTexture())));
965 // TODO: Eventually we will need to handle resolves either in this function or make a separate
966 // copySurfaceAsResolveThenBlit().
967 if (!this->copySurface(transferTexture.get(), transferOrigin,
968 src, srcOrigin,
969 srcRect, SkIPoint::Make(0, 0))) {
970 return false;
971 }
972
973 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
974 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
975 get_surface_sample_cnt(dst),
976 dstOrigin,
977 transferTexture->config(),
978 get_surface_sample_cnt(transferTexture.get()),
979 transferOrigin,
980 transferRect, dstPoint, false));
981 if (!this->copySurface(dst, dstOrigin,
982 transferTexture.get(), transferOrigin,
983 transferRect, dstPoint)) {
984 return false;
985 }
986 return true;
987}
988
Timothy Liange35055f2018-07-20 16:53:00 -0400989bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
990 GrSurface* src, GrSurfaceOrigin srcOrigin,
991 const SkIRect& srcRect,
992 const SkIPoint& dstPoint,
993 bool canDiscardOutsideDstRect) {
994
995 GrPixelConfig dstConfig = dst->config();
996 GrPixelConfig srcConfig = src->config();
997
998 int dstSampleCnt = get_surface_sample_cnt(dst);
999 int srcSampleCnt = get_surface_sample_cnt(src);
1000
1001 if (dstSampleCnt > 1 || srcSampleCnt > 1) {
Timothy Liang70c787a2018-08-01 09:56:25 -04001002 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve().
Timothy Liange35055f2018-07-20 16:53:00 -04001003 return false;
1004 }
1005
Jim Van Verth61610be2019-03-07 15:33:49 -05001006 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -04001007 bool success = false;
Timothy Liange30739a2018-07-31 10:51:17 -04001008 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
1009 src->config(), SkToBool(src->asTexture()))) {
1010 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
1011 canDiscardOutsideDstRect);
1012 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
1013 srcConfig, srcSampleCnt, srcOrigin,
1014 srcRect, dstPoint, dst == src)) {
Timothy Liange35055f2018-07-20 16:53:00 -04001015 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
Timothy Liang70c787a2018-08-01 09:56:25 -04001016 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
1017 SkToBool(src->asTexture()))) {
1018 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
1019 srcRect, dstPoint);
Timothy Liange35055f2018-07-20 16:53:00 -04001020 }
1021 if (success) {
1022 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
1023 srcRect.width(), srcRect.height());
1024 this->didWriteToSurface(dst, dstOrigin, &dstRect);
1025 }
1026 return success;
Jim Van Verth61610be2019-03-07 15:33:49 -05001027 SK_END_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -04001028}
1029
Timothy Lianga8046af2018-07-19 09:58:00 -04001030bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
1031 GrColorType srcColorType, const GrMipLevel texels[],
1032 int mipLevelCount) {
1033 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
Jim Van Verth686046b2019-03-18 15:39:22 -04001034 // TODO: In principle we should be able to support pure rendertargets as well, but
1035 // until we find a use case we'll only support texture rendertargets.
Timothy Lianga8046af2018-07-19 09:58:00 -04001036 if (!mtlTexture) {
1037 return false;
1038 }
1039 if (!mipLevelCount) {
1040 return false;
1041 }
1042#ifdef SK_DEBUG
1043 for (int i = 0; i < mipLevelCount; i++) {
1044 SkASSERT(texels[i].fPixels);
1045 }
1046#endif
Jim Van Verth61610be2019-03-07 15:33:49 -05001047 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Lianga8046af2018-07-19 09:58:00 -04001048 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels,
1049 mipLevelCount);
Jim Van Verth61610be2019-03-07 15:33:49 -05001050 SK_END_AUTORELEASE_BLOCK
Timothy Lianga8046af2018-07-19 09:58:00 -04001051}
1052
Timothy Liangef21d7e2018-07-02 17:03:30 -04001053bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
1054 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -04001055 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -04001056 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -04001057 return false;
1058 }
1059 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
1060 return false;
1061 }
1062
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001063 id<MTLBuffer> transferBuffer;
1064 int bpp = GrColorTypeBytesPerPixel(dstColorType);
1065 size_t transBufferRowBytes = bpp * width;
Jim Van Verth61610be2019-03-07 15:33:49 -05001066 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -04001067 bool doResolve = get_surface_sample_cnt(surface) > 1;
Timothy Liange30739a2018-07-31 10:51:17 -04001068 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
Jim Van Verth8cc42482019-04-05 14:16:37 -04001069 if (!mtlTexture || [mtlTexture isFramebufferOnly]) {
Timothy Liangef21d7e2018-07-02 17:03:30 -04001070 return false;
1071 }
1072
Timothy Liangef21d7e2018-07-02 17:03:30 -04001073 size_t transBufferImageBytes = transBufferRowBytes * height;
1074
1075 // TODO: implement some way of reusing buffers instead of making a new one every time.
Jim Van Verth8cc42482019-04-05 14:16:37 -04001076 NSUInteger options = 0;
1077#ifdef SK_BUILD_FOR_MAC
1078 options |= MTLResourceStorageModeManaged;
1079#else
1080 options |= MTLResourceStorageModeShared;
1081#endif
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001082 transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
Jim Van Verth8cc42482019-04-05 14:16:37 -04001083 options: options];
Timothy Liangef21d7e2018-07-02 17:03:30 -04001084
Jim Van Verth75c3ae42019-04-22 17:07:53 -04001085 id<MTLBlitCommandEncoder> blitCmdEncoder = this->commandBuffer()->getBlitCommandEncoder();
Timothy Liangef21d7e2018-07-02 17:03:30 -04001086 [blitCmdEncoder copyFromTexture: mtlTexture
1087 sourceSlice: 0
1088 sourceLevel: 0
1089 sourceOrigin: MTLOriginMake(left, top, 0)
1090 sourceSize: MTLSizeMake(width, height, 1)
1091 toBuffer: transferBuffer
1092 destinationOffset: 0
1093 destinationBytesPerRow: transBufferRowBytes
1094 destinationBytesPerImage: transBufferImageBytes];
Jim Van Verth8cc42482019-04-05 14:16:37 -04001095#ifdef SK_BUILD_FOR_MAC
1096 // Sync GPU data back to the CPU
1097 [blitCmdEncoder synchronizeResource: transferBuffer];
1098#endif
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001099 SK_END_AUTORELEASE_BLOCK
Timothy Liangef21d7e2018-07-02 17:03:30 -04001100
1101 this->submitCommandBuffer(kForce_SyncQueue);
1102 const void* mappedMemory = transferBuffer.contents;
1103
1104 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
Jim Van Verth8cc42482019-04-05 14:16:37 -04001105
Timothy Liangef21d7e2018-07-02 17:03:30 -04001106 return true;
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001107
Timothy Liangef21d7e2018-07-02 17:03:30 -04001108}
Robert Phillips5b5d84c2018-08-09 15:12:18 -04001109