blob: 650b4940c75fdd2d6968e3f4224e6de6ec34f6a6 [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
8#include "GrMtlGpu.h"
9
Timothy Liang49528b62018-08-02 14:18:37 -040010#include "GrMtlBuffer.h"
Timothy Liange70604e2018-07-19 09:49:46 -040011#include "GrMtlGpuCommandBuffer.h"
Greg Daniel4a081e22017-08-04 09:34:44 -040012#include "GrMtlTexture.h"
Timothy Liange886e802018-07-02 16:03:28 -040013#include "GrMtlTextureRenderTarget.h"
14#include "GrMtlUtil.h"
Ethan Nicholas01063512018-10-08 16:58:25 -040015#include "GrRenderTargetPriv.h"
Timothy Liangff19c8f2018-07-11 13:27:21 -040016#include "GrTexturePriv.h"
Timothy Liangef21d7e2018-07-02 17:03:30 -040017#include "SkConvertPixels.h"
Timothy Liange30739a2018-07-31 10:51:17 -040018#include "SkSLCompiler.h"
19
20#import <simd/simd.h>
Greg Daniel4a081e22017-08-04 09:34:44 -040021
Greg Daniel6b7e0e22017-07-12 16:21:09 -040022#if !__has_feature(objc_arc)
23#error This file must be compiled with Arc. Use -fobjc-arc flag
24#endif
25
Greg Danielcebcb842017-07-31 10:45:52 -040026static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
27 // Mac OSX
28#ifdef SK_BUILD_FOR_MAC
29 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) {
30 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2;
31 return true;
32 }
33 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) {
34 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1;
35 return true;
36 }
37#endif
38
39 // iOS Family group 3
40#ifdef SK_BUILD_FOR_IOS
41 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) {
42 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2;
43 return true;
44 }
45 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
46 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
47 return true;
48 }
49
50 // iOS Family group 2
51 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) {
52 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3;
53 return true;
54 }
55 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) {
56 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2;
57 return true;
58 }
59 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) {
60 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
61 return true;
62 }
63
64 // iOS Family group 1
65 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) {
66 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3;
67 return true;
68 }
69 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
70 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2;
71 return true;
72 }
73 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) {
74 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
75 return true;
76 }
77#endif
78 // No supported feature sets were found
79 return false;
80}
81
Brian Salomon384fab42017-12-07 12:33:05 -050082sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options,
83 id<MTLDevice> device, id<MTLCommandQueue> queue) {
Greg Danielb76a72a2017-07-13 15:07:54 -040084 if (!device || !queue) {
85 return nullptr;
86 }
Greg Danielcebcb842017-07-31 10:45:52 -040087 MTLFeatureSet featureSet;
88 if (!get_feature_set(device, &featureSet)) {
89 return nullptr;
90 }
Brian Salomon384fab42017-12-07 12:33:05 -050091 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet));
Greg Daniele5ddff52017-07-05 16:49:36 -040092}
93
Greg Danielb76a72a2017-07-13 15:07:54 -040094GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
Greg Danielcebcb842017-07-31 10:45:52 -040095 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet)
Greg Danielb76a72a2017-07-13 15:07:54 -040096 : INHERITED(context)
97 , fDevice(device)
Timothy Liange30739a2018-07-31 10:51:17 -040098 , fQueue(queue)
Jim Van Verth568eb8d2019-04-04 14:30:42 -040099 , fCmdBuffer(nil)
Timothy Liange30739a2018-07-31 10:51:17 -0400100 , fCompiler(new SkSL::Compiler())
101 , fCopyManager(this)
Jim Van Verthc03cfaf2019-04-18 13:11:01 -0400102 , fResourceProvider(this)
103 , fBufferManager(this) {
Greg Danielcebcb842017-07-31 10:45:52 -0400104 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
105 fCaps = fMtlCaps;
Greg Danielb76a72a2017-07-13 15:07:54 -0400106}
Greg Daniel4a081e22017-08-04 09:34:44 -0400107
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400108GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer(
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400109 GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds,
Timothy Liange70604e2018-07-19 09:49:46 -0400110 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
111 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
Jim Van Verthf16e0742019-04-02 14:33:03 -0400112 SK_BEGIN_AUTORELEASE_BLOCK
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400113 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo);
Jim Van Verthf16e0742019-04-02 14:33:03 -0400114 SK_END_AUTORELEASE_BLOCK
Timothy Liange70604e2018-07-19 09:49:46 -0400115}
116
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400117GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture,
118 GrSurfaceOrigin origin) {
Timothy Liange70604e2018-07-19 09:49:46 -0400119 return new GrMtlGpuTextureCommandBuffer(this, texture, origin);
120}
121
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400122void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400123 GrMtlGpuRTCommandBuffer* mtlRTCmdBuffer =
124 reinterpret_cast<GrMtlGpuRTCommandBuffer*>(buffer->asRTCommandBuffer());
125 if (mtlRTCmdBuffer) {
126 mtlRTCmdBuffer->submit();
127 }
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400128 delete buffer;
129}
130
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400131id<MTLCommandBuffer> GrMtlGpu::commandBuffer() {
132 if (!fCmdBuffer) {
133 SK_BEGIN_AUTORELEASE_BLOCK
134 fCmdBuffer = [fQueue commandBuffer];
135 SK_END_AUTORELEASE_BLOCK
136 }
137 return fCmdBuffer;
138}
139
Timothy Liangef21d7e2018-07-02 17:03:30 -0400140void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400141 [fCmdBuffer commit];
142 if (SyncQueue::kForce_SyncQueue == sync) {
143 [fCmdBuffer waitUntilCompleted];
144 }
Jim Van Verth8cc42482019-04-05 14:16:37 -0400145 if (MTLCommandBufferStatusError == fCmdBuffer.status) {
146 NSString* description = fCmdBuffer.error.localizedDescription;
147 const char* errorString = [description UTF8String];
148 SkDebugf("Error submitting command buffer: %s\n", errorString);
149 }
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400150 fCmdBuffer = nil;
Timothy Liangef21d7e2018-07-02 17:03:30 -0400151}
152
Brian Salomondbf70722019-02-07 11:31:24 -0500153sk_sp<GrGpuBuffer> GrMtlGpu::onCreateBuffer(size_t size, GrGpuBufferType type,
154 GrAccessPattern accessPattern, const void* data) {
Brian Salomon12d22642019-01-29 14:38:50 -0500155 return GrMtlBuffer::Make(this, size, type, accessPattern, data);
Timothy Liang49528b62018-08-02 14:18:37 -0400156}
157
Timothy Liangff19c8f2018-07-11 13:27:21 -0400158static bool check_max_blit_width(int widthInPixels) {
159 if (widthInPixels > 32767) {
160 SkASSERT(false); // surfaces should not be this wide anyway
161 return false;
162 }
163 return true;
164}
165
166bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
167 GrColorType dataColorType, const GrMipLevel texels[],
168 int mipLevelCount) {
169 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
Jim Van Verth444b3092019-03-11 11:31:23 -0400170 // The assumption is either that we have no mipmaps, or that our rect is the entire texture
171 SkASSERT(1 == mipLevelCount ||
172 (0 == left && 0 == top && width == tex->width() && height == tex->height()));
173
174 // We assume that if the texture has mip levels, we either upload to all the levels or just the
175 // first.
176 SkASSERT(1 == mipLevelCount || mipLevelCount == (tex->texturePriv().maxMipMapLevel() + 1));
177
178 // If we're uploading compressed data then we should be using uploadCompressedTexData
179 SkASSERT(!GrPixelConfigIsCompressed(GrColorTypeToPixelConfig(dataColorType,
180 GrSRGBEncoded::kNo)));
181
Timothy Liangff19c8f2018-07-11 13:27:21 -0400182 if (!check_max_blit_width(width)) {
183 return false;
184 }
185 if (width == 0 || height == 0) {
186 return false;
187 }
188 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
189 return false;
190 }
191
192 id<MTLTexture> mtlTexture = tex->mtlTexture();
193 SkASSERT(mtlTexture);
194 // Either upload only the first miplevel or all miplevels
195 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
196
Jim Van Verth444b3092019-03-11 11:31:23 -0400197 // TODO: implement some way of reusing transfer buffers?
198 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400199
Jim Van Verth444b3092019-03-11 11:31:23 -0400200 SkTArray<size_t> individualMipOffsets(mipLevelCount);
201 individualMipOffsets.push_back(0);
202 size_t combinedBufferSize = width * bpp * height;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400203 int currentWidth = width;
204 int currentHeight = height;
Jim Van Verth444b3092019-03-11 11:31:23 -0400205 if (!texels[0].fPixels) {
206 combinedBufferSize = 0;
207 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400208
Jim Van Verth444b3092019-03-11 11:31:23 -0400209 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
210 // config. This works with the assumption that the bytes in pixel config is always a power of 2.
211 SkASSERT((bpp & (bpp - 1)) == 0);
212 const size_t alignmentMask = 0x3 | (bpp - 1);
213 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; currentMipLevel++) {
214 currentWidth = SkTMax(1, currentWidth/2);
215 currentHeight = SkTMax(1, currentHeight/2);
216
217 if (texels[currentMipLevel].fPixels) {
218 const size_t trimmedSize = currentWidth * bpp * currentHeight;
219 const size_t alignmentDiff = combinedBufferSize & alignmentMask;
220 if (alignmentDiff != 0) {
221 combinedBufferSize += alignmentMask - alignmentDiff + 1;
222 }
223 individualMipOffsets.push_back(combinedBufferSize);
224 combinedBufferSize += trimmedSize;
225 } else {
226 individualMipOffsets.push_back(0);
227 }
228 }
229 if (0 == combinedBufferSize) {
230 // We don't actually have any data to upload so just return success
231 return true;
232 }
233
Jim Van Verth686046b2019-03-18 15:39:22 -0400234 NSUInteger options = 0;
Jim Van Verth444b3092019-03-11 11:31:23 -0400235#ifdef SK_BUILD_FOR_MAC
236 options |= MTLResourceStorageModeManaged;
237#else
238 options |= MTLResourceStorageModeShared;
239#endif
240 // TODO: Create GrMtlTransferBuffer
241 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize
Jim Van Verth686046b2019-03-18 15:39:22 -0400242 options: options];
Jim Van Verth444b3092019-03-11 11:31:23 -0400243 if (nil == transferBuffer) {
244 return false;
245 }
246
247 char* buffer = (char*) transferBuffer.contents;
248
249 currentWidth = width;
250 currentHeight = height;
251 int layerHeight = tex->height();
252 MTLOrigin origin = MTLOriginMake(left, top, 0);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400253
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400254 id<MTLBlitCommandEncoder> blitCmdEncoder = [this->commandBuffer() blitCommandEncoder];
Timothy Liangff19c8f2018-07-11 13:27:21 -0400255 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
Jim Van Verth444b3092019-03-11 11:31:23 -0400256 if (texels[currentMipLevel].fPixels) {
257 SkASSERT(1 == mipLevelCount || currentHeight == layerHeight);
258 const size_t trimRowBytes = currentWidth * bpp;
259 const size_t rowBytes = texels[currentMipLevel].fRowBytes
260 ? texels[currentMipLevel].fRowBytes
261 : trimRowBytes;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400262
Jim Van Verth444b3092019-03-11 11:31:23 -0400263 // copy data into the buffer, skipping the trailing bytes
264 char* dst = buffer + individualMipOffsets[currentMipLevel];
265 const char* src = (const char*)texels[currentMipLevel].fPixels;
266 SkRectMemcpy(dst, trimRowBytes, src, rowBytes, trimRowBytes, currentHeight);
267
268 [blitCmdEncoder copyFromBuffer: transferBuffer
269 sourceOffset: individualMipOffsets[currentMipLevel]
270 sourceBytesPerRow: trimRowBytes
271 sourceBytesPerImage: trimRowBytes*currentHeight
Jim Van Verth686046b2019-03-18 15:39:22 -0400272 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
Jim Van Verth444b3092019-03-11 11:31:23 -0400273 toTexture: mtlTexture
274 destinationSlice: 0
275 destinationLevel: currentMipLevel
276 destinationOrigin: origin];
277 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400278 currentWidth = SkTMax(1, currentWidth/2);
279 currentHeight = SkTMax(1, currentHeight/2);
Jim Van Verth444b3092019-03-11 11:31:23 -0400280 layerHeight = currentHeight;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400281 }
282 [blitCmdEncoder endEncoding];
Jim Van Verth8cc42482019-04-05 14:16:37 -0400283#ifdef SK_BUILD_FOR_MAC
284 // for Managed resources, need to tell driver to sync CPU and GPU copies
285 [transferBuffer didModifyRange: NSMakeRange(0, combinedBufferSize)];
286#endif
Timothy Liangff19c8f2018-07-11 13:27:21 -0400287
288 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
289 tex->texturePriv().markMipMapsDirty();
290 }
Jim Van Verth444b3092019-03-11 11:31:23 -0400291
Timothy Liangff19c8f2018-07-11 13:27:21 -0400292 return true;
293}
294
Jim Van Verth686046b2019-03-18 15:39:22 -0400295bool GrMtlGpu::clearTexture(GrMtlTexture* tex, GrColorType dataColorType) {
296 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
297
298 // If we're uploading compressed data then we should be using uploadCompressedTexData
299 SkASSERT(!GrPixelConfigIsCompressed(GrColorTypeToPixelConfig(dataColorType,
300 GrSRGBEncoded::kNo)));
301
302 id<MTLTexture> mtlTexture = tex->mtlTexture();
303 SkASSERT(mtlTexture);
304 // Either upload only the first miplevel or all miplevels
305 int mipLevelCount = (int)mtlTexture.mipmapLevelCount;
306
307 // TODO: implement some way of reusing transfer buffers?
308 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
309
310 SkTArray<size_t> individualMipOffsets(mipLevelCount);
311 individualMipOffsets.push_back(0);
312 int width = tex->width();
313 int height = tex->height();
314 size_t combinedBufferSize = width * bpp * height;
315 int currentWidth = width;
316 int currentHeight = height;
317
318 // The alignment must be at least 4 bytes and a multiple of the bytes per pixel of the image
319 // config. This works with the assumption that the bytes in pixel config is always a power of 2.
320 // TODO: can we just copy from a single buffer the size of the top level w/o a perf penalty?
321 SkASSERT((bpp & (bpp - 1)) == 0);
322 const size_t alignmentMask = 0x3 | (bpp - 1);
323 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; currentMipLevel++) {
324 currentWidth = SkTMax(1, currentWidth/2);
325 currentHeight = SkTMax(1, currentHeight/2);
326
327 const size_t trimmedSize = currentWidth * bpp * currentHeight;
328 const size_t alignmentDiff = combinedBufferSize & alignmentMask;
329 if (alignmentDiff != 0) {
330 combinedBufferSize += alignmentMask - alignmentDiff + 1;
331 }
332 individualMipOffsets.push_back(combinedBufferSize);
333 combinedBufferSize += trimmedSize;
334 }
335 if (0 == combinedBufferSize) {
336 // We don't actually have any data to upload so just return success
337 return true;
338 }
339
340 // TODO: Create GrMtlTransferBuffer
341 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: combinedBufferSize
342 options: MTLResourceStorageModePrivate];
343 if (nil == transferBuffer) {
344 return false;
345 }
346
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400347 id<MTLBlitCommandEncoder> blitCmdEncoder = [this->commandBuffer() blitCommandEncoder];
Jim Van Verth686046b2019-03-18 15:39:22 -0400348 // clear the buffer to transparent black
349 NSRange clearRange;
350 clearRange.location = 0;
351 clearRange.length = combinedBufferSize;
352 [blitCmdEncoder fillBuffer: transferBuffer
353 range: clearRange
354 value: 0];
355
356 // now copy buffer to texture
357 currentWidth = width;
358 currentHeight = height;
359 MTLOrigin origin = MTLOriginMake(0, 0, 0);
360 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
361 const size_t rowBytes = currentWidth * bpp;
362
363 [blitCmdEncoder copyFromBuffer: transferBuffer
364 sourceOffset: individualMipOffsets[currentMipLevel]
365 sourceBytesPerRow: rowBytes
366 sourceBytesPerImage: rowBytes*currentHeight
367 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
368 toTexture: mtlTexture
369 destinationSlice: 0
370 destinationLevel: currentMipLevel
371 destinationOrigin: origin];
372 currentWidth = SkTMax(1, currentWidth/2);
373 currentHeight = SkTMax(1, currentHeight/2);
374 }
375 [blitCmdEncoder endEncoding];
376
377 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
378 tex->texturePriv().markMipMapsDirty();
379 }
380
381 return true;
382}
383
Ethan Nicholas01063512018-10-08 16:58:25 -0400384GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
385 int width,
386 int height) {
387 SkASSERT(width >= rt->width());
388 SkASSERT(height >= rt->height());
389
390 int samples = rt->numStencilSamples();
391
392 const GrMtlCaps::StencilFormat& sFmt = this->mtlCaps().preferredStencilFormat();
393
Jim Van Verthf16e0742019-04-02 14:33:03 -0400394 SK_BEGIN_AUTORELEASE_BLOCK
Ethan Nicholas01063512018-10-08 16:58:25 -0400395 GrMtlStencilAttachment* stencil(GrMtlStencilAttachment::Create(this,
396 width,
397 height,
398 samples,
399 sFmt));
400 fStats.incStencilAttachmentCreates();
401 return stencil;
Jim Van Verthf16e0742019-04-02 14:33:03 -0400402 SK_END_AUTORELEASE_BLOCK
Ethan Nicholas01063512018-10-08 16:58:25 -0400403}
404
Greg Daniel4a081e22017-08-04 09:34:44 -0400405sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500406 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400407 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
408
409 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
410 return nullptr;
411 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400412 MTLPixelFormat format;
413 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
414 return nullptr;
415 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400416
Jim Van Verth1676cb92019-01-15 13:24:45 -0500417 if (GrPixelConfigIsCompressed(desc.fConfig)) {
418 return nullptr; // TODO: add compressed texture support
419 }
420
Greg Daniel4a081e22017-08-04 09:34:44 -0400421 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400422
Jim Van Verth61610be2019-03-07 15:33:49 -0500423 sk_sp<GrMtlTexture> tex;
424 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liang58f153d2018-07-02 17:36:20 -0400425 // 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 -0500426 // requested, this TexDesc describes the resolved texture. Therefore we always have samples
427 // set to 1.
Timothy Liang58f153d2018-07-02 17:36:20 -0400428 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
429 texDesc.textureType = MTLTextureType2D;
430 texDesc.pixelFormat = format;
431 texDesc.width = desc.fWidth;
432 texDesc.height = desc.fHeight;
433 texDesc.depth = 1;
434 texDesc.mipmapLevelCount = mipLevels;
435 texDesc.sampleCount = 1;
436 texDesc.arrayLength = 1;
437 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
438 // Make all textures have private gpu only access. We can use transfer buffers or textures
439 // to copy to them.
440 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400441 texDesc.usage = MTLTextureUsageShaderRead;
442 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400443
444 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
445 if (mipLevels > 1) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400446 mipMapsStatus = GrMipMapsStatus::kValid;
447 for (int i = 0; i < mipLevels; ++i) {
448 if (!texels[i].fPixels) {
449 mipMapsStatus = GrMipMapsStatus::kDirty;
450 break;
Timothy Liang58f153d2018-07-02 17:36:20 -0400451 }
452 }
453 }
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400454
Greg Daniel4a081e22017-08-04 09:34:44 -0400455 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400456 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400457 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400458 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400459 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400460 }
461
462 if (!tex) {
463 return nullptr;
464 }
465
Timothy Liangff19c8f2018-07-11 13:27:21 -0400466 auto colorType = GrPixelConfigToColorType(desc.fConfig);
467 if (mipLevelCount && texels[0].fPixels) {
468 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
469 mipLevelCount)) {
470 tex->unref();
471 return nullptr;
472 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400473 }
474
475 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
Jim Van Verth686046b2019-03-18 15:39:22 -0400476 this->clearTexture(tex.get(), colorType);
Greg Daniel4a081e22017-08-04 09:34:44 -0400477 }
Jim Van Verth686046b2019-03-18 15:39:22 -0400478 SK_END_AUTORELEASE_BLOCK
479
Timothy Liangff19c8f2018-07-11 13:27:21 -0400480 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400481}
Timothy Liange886e802018-07-02 16:03:28 -0400482
483static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
484 GrWrapOwnership ownership) {
485 GrMtlTextureInfo textureInfo;
486 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
487 return nil;
488 }
489 return GrGetMTLTexture(textureInfo.fTexture, ownership);
490}
491
492static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
493 GrMtlTextureInfo textureInfo;
494 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
495 return nil;
496 }
497 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
498}
499
500static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
501 bool isRenderTarget, GrPixelConfig config) {
502 if (isRenderTarget) {
503 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
504 }
505 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
506 surfaceDesc->fWidth = mtlTexture.width;
507 surfaceDesc->fHeight = mtlTexture.height;
508 surfaceDesc->fConfig = config;
509 surfaceDesc->fSampleCnt = 1;
510}
511
512sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
Brian Salomonfa2ebea2019-01-24 15:58:58 -0500513 GrWrapOwnership ownership,
514 GrWrapCacheable cacheable, GrIOType ioType) {
Timothy Liange886e802018-07-02 16:03:28 -0400515 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
516 if (!mtlTexture) {
517 return nullptr;
518 }
519
520 GrSurfaceDesc surfDesc;
521 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
522
Brian Salomonfa2ebea2019-01-24 15:58:58 -0500523 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture, cacheable, ioType);
Timothy Liange886e802018-07-02 16:03:28 -0400524}
525
526sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
527 int sampleCnt,
Brian Salomonaa6ca0a2019-01-24 16:03:07 -0500528 GrWrapOwnership ownership,
529 GrWrapCacheable cacheable) {
Timothy Liange886e802018-07-02 16:03:28 -0400530 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
531 if (!mtlTexture) {
532 return nullptr;
533 }
534
535 GrSurfaceDesc surfDesc;
536 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
537 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
538 if (!surfDesc.fSampleCnt) {
539 return nullptr;
540 }
541
Brian Salomonaa6ca0a2019-01-24 16:03:07 -0500542 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture,
543 cacheable);
Timothy Liange886e802018-07-02 16:03:28 -0400544}
545
546sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
547 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
548 if (backendRT.sampleCnt() > 1) {
549 return nullptr;
550 }
551 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
552 if (!mtlTexture) {
553 return nullptr;
554 }
555
556 GrSurfaceDesc surfDesc;
557 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
558
559 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
560}
561
562sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
563 const GrBackendTexture& backendTex, int sampleCnt) {
564 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
565 GrWrapOwnership::kBorrow_GrWrapOwnership);
566 if (!mtlTexture) {
567 return nullptr;
568 }
569
570 GrSurfaceDesc surfDesc;
571 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
572 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
573 if (!surfDesc.fSampleCnt) {
574 return nullptr;
575 }
576
577 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
578}
579
Jim Van Verth686046b2019-03-18 15:39:22 -0400580bool GrMtlGpu::onRegenerateMipMapLevels(GrTexture* texture) {
Jim Van Verth9896a0d2019-04-10 15:11:12 -0400581 GrMtlTexture* grMtlTexture = static_cast<GrMtlTexture*>(texture);
582 id<MTLTexture> mtlTexture = grMtlTexture->mtlTexture();
583
584 // Automatic mipmap generation is only supported by color-renderable formats
585 if (!fMtlCaps->isConfigRenderable(texture->config()) &&
586 // We have pixel configs marked as textureable-only that use RGBA8 as the internal format
587 MTLPixelFormatRGBA8Unorm != mtlTexture.pixelFormat) {
588 return false;
589 }
590
591 id<MTLBlitCommandEncoder> blitCmdEncoder = [this->commandBuffer() blitCommandEncoder];
592 [blitCmdEncoder generateMipmapsForTexture: mtlTexture];
593 [blitCmdEncoder endEncoding];
594
595 return true;
Jim Van Verth686046b2019-03-18 15:39:22 -0400596}
597
Martin Vejdarskid3536122019-03-21 19:11:11 +0900598#if GR_TEST_UTILS
Robert Phillips646f6372018-09-25 09:31:10 -0400599bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrColorType colorType, int w, int h, bool texturable,
600 bool renderable, GrMipMapped mipMapped,
601 const void* srcData, size_t srcRowBytes,
602 GrMtlTextureInfo* info) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400603 SkASSERT(texturable || renderable);
604 if (!texturable) {
605 SkASSERT(GrMipMapped::kNo == mipMapped);
606 SkASSERT(!srcData);
607 }
608
Robert Phillips646f6372018-09-25 09:31:10 -0400609 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo);
610
Timothy Liang760dbc42018-07-17 13:28:20 -0400611 MTLPixelFormat format;
612 if (!GrPixelConfigToMTLFormat(config, &format)) {
613 return false;
614 }
615 if (texturable && !fMtlCaps->isConfigTexturable(config)) {
616 return false;
617 }
618 if (renderable && !fMtlCaps->isConfigRenderable(config)) {
619 return false;
620 }
621 // Currently we don't support uploading pixel data when mipped.
622 if (srcData && GrMipMapped::kYes == mipMapped) {
623 return false;
624 }
625 if(!check_max_blit_width(w)) {
626 return false;
627 }
628
Jim Van Verth61610be2019-03-07 15:33:49 -0500629 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liang760dbc42018-07-17 13:28:20 -0400630 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
631 MTLTextureDescriptor* desc =
632 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
633 width: w
634 height: h
635 mipmapped: mipmapped];
636 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
637 desc.storageMode = MTLStorageModePrivate;
638 desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
639 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
640 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
641
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400642 size_t bpp = GrColorTypeBytesPerPixel(colorType);
Jim Van Verth8cc42482019-04-05 14:16:37 -0400643 if (!srcRowBytes) {
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400644 srcRowBytes = w * bpp;
Jim Van Verth8cc42482019-04-05 14:16:37 -0400645#ifdef SK_BUILD_FOR_MAC
646 // On MacOS, the fillBuffer command needs a range with a multiple of 4 bytes
647 srcRowBytes = ((srcRowBytes + 3) & (~3));
648#endif
Timothy Liang760dbc42018-07-17 13:28:20 -0400649 }
Jim Van Verth8cc42482019-04-05 14:16:37 -0400650 size_t bufferSize = srcRowBytes * h;
651 NSUInteger options = 0; // TODO: consider other options here
Timothy Liang4e679842018-08-08 14:21:12 -0400652#ifdef SK_BUILD_FOR_MAC
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400653 options |= MTLResourceStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400654#else
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400655 options |= MTLResourceStorageModeShared;
Timothy Liang4e679842018-08-08 14:21:12 -0400656#endif
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400657
658 // TODO: Create GrMtlTransferBuffer
Jim Van Verth8cc42482019-04-05 14:16:37 -0400659 id<MTLBuffer> transferBuffer;
660 if (srcData) {
661 transferBuffer = [fDevice newBufferWithBytes: srcData
662 length: bufferSize
663 options: options];
664 } else {
665 transferBuffer = [fDevice newBufferWithLength: bufferSize
666 options: options];
667 }
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400668 if (nil == transferBuffer) {
669 return false;
670 }
Timothy Liang760dbc42018-07-17 13:28:20 -0400671
Timothy Liang760dbc42018-07-17 13:28:20 -0400672 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
673 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
Jim Van Verth8cc42482019-04-05 14:16:37 -0400674 if (!srcData) {
675 [blitCmdEncoder fillBuffer: transferBuffer
676 range: NSMakeRange(0, bufferSize)
677 value: 0];
678 }
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400679 [blitCmdEncoder copyFromBuffer: transferBuffer
680 sourceOffset: 0
Jim Van Verth8cc42482019-04-05 14:16:37 -0400681 sourceBytesPerRow: srcRowBytes
682 sourceBytesPerImage: bufferSize
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400683 sourceSize: MTLSizeMake(w, h, 1)
684 toTexture: testTexture
685 destinationSlice: 0
686 destinationLevel: 0
Jim Van Verth8cc42482019-04-05 14:16:37 -0400687 destinationOrigin: MTLOriginMake(0, 0, 0)];
Timothy Liang760dbc42018-07-17 13:28:20 -0400688 [blitCmdEncoder endEncoding];
689 [cmdBuffer commit];
690 [cmdBuffer waitUntilCompleted];
691
692 info->fTexture = GrReleaseId(testTexture);
Jim Van Verth61610be2019-03-07 15:33:49 -0500693 SK_END_AUTORELEASE_BLOCK
694
Timothy Liang760dbc42018-07-17 13:28:20 -0400695 return true;
696}
697
698GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h,
Robert Phillips646f6372018-09-25 09:31:10 -0400699 GrColorType colorType, bool isRT,
700 GrMipMapped mipMapped, size_t rowBytes) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400701 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
702 return GrBackendTexture();
703 }
704 GrMtlTextureInfo info;
Robert Phillips646f6372018-09-25 09:31:10 -0400705 if (!this->createTestingOnlyMtlTextureInfo(colorType, w, h, true, isRT, mipMapped, pixels,
706 rowBytes, &info)) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400707 return {};
708 }
709
Robert Phillips646f6372018-09-25 09:31:10 -0400710 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo);
711
Timothy Liang760dbc42018-07-17 13:28:20 -0400712 GrBackendTexture backendTex(w, h, mipMapped, info);
713 backendTex.fConfig = config;
714 return backendTex;
715}
716
717bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400718 SkASSERT(GrBackendApi::kMetal == tex.backend());
Timothy Liang760dbc42018-07-17 13:28:20 -0400719
720 GrMtlTextureInfo info;
721 if (!tex.getMtlTextureInfo(&info)) {
722 return false;
723 }
724 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
725 GrWrapOwnership::kBorrow_GrWrapOwnership);
726 if (!mtlTexture) {
727 return false;
728 }
729 return mtlTexture.usage & MTLTextureUsageShaderRead;
730}
731
732void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400733 SkASSERT(GrBackendApi::kMetal == tex.fBackend);
Timothy Liang760dbc42018-07-17 13:28:20 -0400734
735 GrMtlTextureInfo info;
736 if (tex.getMtlTextureInfo(&info)) {
737 // Adopts the metal texture so that ARC will clean it up.
738 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
739 }
740}
741
Brian Osman2d010b62018-08-09 10:55:09 -0400742GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400743 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
744 return GrBackendRenderTarget();
745 }
Robert Phillips646f6372018-09-25 09:31:10 -0400746
Timothy Liang760dbc42018-07-17 13:28:20 -0400747 GrMtlTextureInfo info;
Robert Phillips646f6372018-09-25 09:31:10 -0400748 if (!this->createTestingOnlyMtlTextureInfo(ct, w, h, false, true, GrMipMapped::kNo, nullptr,
749 0, &info)) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400750 return {};
751 }
752
Robert Phillips646f6372018-09-25 09:31:10 -0400753 GrPixelConfig config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
754
Timothy Liang760dbc42018-07-17 13:28:20 -0400755 GrBackendRenderTarget backendRT(w, h, 1, info);
756 backendRT.fConfig = config;
757 return backendRT;
758}
759
760void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400761 SkASSERT(GrBackendApi::kMetal == rt.fBackend);
Timothy Liang760dbc42018-07-17 13:28:20 -0400762
763 GrMtlTextureInfo info;
764 if (rt.getMtlTextureInfo(&info)) {
765 this->testingOnly_flushGpuAndSync();
766 // Adopts the metal texture so that ARC will clean it up.
767 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
768 }
769}
770
771void GrMtlGpu::testingOnly_flushGpuAndSync() {
772 this->submitCommandBuffer(kForce_SyncQueue);
773}
774#endif // GR_TEST_UTILS
775
Timothy Liange35055f2018-07-20 16:53:00 -0400776static int get_surface_sample_cnt(GrSurface* surf) {
777 if (const GrRenderTarget* rt = surf->asRenderTarget()) {
778 return rt->numColorSamples();
779 }
780 return 0;
781}
782
783bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
784 GrSurface* src, GrSurfaceOrigin srcOrigin,
785 const SkIRect& srcRect, const SkIPoint& dstPoint) {
786#ifdef SK_DEBUG
787 int dstSampleCnt = get_surface_sample_cnt(dst);
788 int srcSampleCnt = get_surface_sample_cnt(src);
789 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
790 src->config(), srcSampleCnt, srcOrigin,
791 srcRect, dstPoint, dst == src));
792#endif
Timothy Liange30739a2018-07-31 10:51:17 -0400793 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
794 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
Timothy Liange35055f2018-07-20 16:53:00 -0400795
796 // Flip rect if necessary
797 SkIRect srcMtlRect;
798 srcMtlRect.fLeft = srcRect.fLeft;
799 srcMtlRect.fRight = srcRect.fRight;
800 SkIRect dstRect;
801 dstRect.fLeft = dstPoint.fX;
802 dstRect.fRight = dstPoint.fX + srcRect.width();
803
804 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
805 srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
806 srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
807 } else {
808 srcMtlRect.fTop = srcRect.fTop;
809 srcMtlRect.fBottom = srcRect.fBottom;
810 }
811
812 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
813 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
814 } else {
815 dstRect.fTop = dstPoint.fY;
816 }
817 dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
818
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400819 id<MTLBlitCommandEncoder> blitCmdEncoder = [this->commandBuffer() blitCommandEncoder];
Timothy Liange35055f2018-07-20 16:53:00 -0400820 [blitCmdEncoder copyFromTexture: srcTex
821 sourceSlice: 0
822 sourceLevel: 0
823 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
824 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
825 toTexture: dstTex
826 destinationSlice: 0
827 destinationLevel: 0
828 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
829 [blitCmdEncoder endEncoding];
830
831 return true;
832}
833
Timothy Liang70c787a2018-08-01 09:56:25 -0400834bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
835 GrSurface* src, GrSurfaceOrigin srcOrigin,
836 const SkIRect& srcRect, const SkIPoint& dstPoint) {
837#ifdef SK_DEBUG
838 int dstSampleCnt = get_surface_sample_cnt(dst);
839 int srcSampleCnt = get_surface_sample_cnt(src);
840 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
841 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
842 src->config(), srcSampleCnt, srcOrigin,
843 srcRect, dstPoint, dst == src));
844 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
845 src->config(), SkToBool(src->asTexture())));
846 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
847 SkToBool(src->asTexture())));
848#endif
849 GrSurfaceDesc surfDesc;
850 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
851 surfDesc.fWidth = srcRect.width();
852 surfDesc.fHeight = srcRect.height();
853 surfDesc.fConfig = dst->config();
854 surfDesc.fSampleCnt = 1;
855
856 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
857 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
858 textureDesc.width = srcRect.width();
859 textureDesc.height = srcRect.height();
860 textureDesc.mipmapLevelCount = 1;
861 textureDesc.usage |= MTLTextureUsageRenderTarget;
862
863 sk_sp<GrMtlTexture> transferTexture =
864 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
865 SkBudgeted::kYes,
866 surfDesc,
867 textureDesc,
868 GrMipMapsStatus::kNotAllocated);
869
870 GrSurfaceOrigin transferOrigin = dstOrigin;
871 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
872 SkToBool(transferTexture->asRenderTarget()),
873 src->config(),
874 SkToBool(src->asTexture())));
875 // TODO: Eventually we will need to handle resolves either in this function or make a separate
876 // copySurfaceAsResolveThenBlit().
877 if (!this->copySurface(transferTexture.get(), transferOrigin,
878 src, srcOrigin,
879 srcRect, SkIPoint::Make(0, 0))) {
880 return false;
881 }
882
883 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
884 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
885 get_surface_sample_cnt(dst),
886 dstOrigin,
887 transferTexture->config(),
888 get_surface_sample_cnt(transferTexture.get()),
889 transferOrigin,
890 transferRect, dstPoint, false));
891 if (!this->copySurface(dst, dstOrigin,
892 transferTexture.get(), transferOrigin,
893 transferRect, dstPoint)) {
894 return false;
895 }
896 return true;
897}
898
Timothy Liange35055f2018-07-20 16:53:00 -0400899bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
900 GrSurface* src, GrSurfaceOrigin srcOrigin,
901 const SkIRect& srcRect,
902 const SkIPoint& dstPoint,
903 bool canDiscardOutsideDstRect) {
904
905 GrPixelConfig dstConfig = dst->config();
906 GrPixelConfig srcConfig = src->config();
907
908 int dstSampleCnt = get_surface_sample_cnt(dst);
909 int srcSampleCnt = get_surface_sample_cnt(src);
910
911 if (dstSampleCnt > 1 || srcSampleCnt > 1) {
Timothy Liang70c787a2018-08-01 09:56:25 -0400912 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve().
Timothy Liange35055f2018-07-20 16:53:00 -0400913 return false;
914 }
915
Jim Van Verth61610be2019-03-07 15:33:49 -0500916 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -0400917 bool success = false;
Timothy Liange30739a2018-07-31 10:51:17 -0400918 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
919 src->config(), SkToBool(src->asTexture()))) {
920 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
921 canDiscardOutsideDstRect);
922 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
923 srcConfig, srcSampleCnt, srcOrigin,
924 srcRect, dstPoint, dst == src)) {
Timothy Liange35055f2018-07-20 16:53:00 -0400925 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
Timothy Liang70c787a2018-08-01 09:56:25 -0400926 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
927 SkToBool(src->asTexture()))) {
928 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
929 srcRect, dstPoint);
Timothy Liange35055f2018-07-20 16:53:00 -0400930 }
931 if (success) {
932 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
933 srcRect.width(), srcRect.height());
934 this->didWriteToSurface(dst, dstOrigin, &dstRect);
935 }
936 return success;
Jim Van Verth61610be2019-03-07 15:33:49 -0500937 SK_END_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -0400938}
939
Timothy Lianga8046af2018-07-19 09:58:00 -0400940bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
941 GrColorType srcColorType, const GrMipLevel texels[],
942 int mipLevelCount) {
943 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
Jim Van Verth686046b2019-03-18 15:39:22 -0400944 // TODO: In principle we should be able to support pure rendertargets as well, but
945 // until we find a use case we'll only support texture rendertargets.
Timothy Lianga8046af2018-07-19 09:58:00 -0400946 if (!mtlTexture) {
947 return false;
948 }
949 if (!mipLevelCount) {
950 return false;
951 }
952#ifdef SK_DEBUG
953 for (int i = 0; i < mipLevelCount; i++) {
954 SkASSERT(texels[i].fPixels);
955 }
956#endif
Jim Van Verth61610be2019-03-07 15:33:49 -0500957 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Lianga8046af2018-07-19 09:58:00 -0400958 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels,
959 mipLevelCount);
Jim Van Verth61610be2019-03-07 15:33:49 -0500960 SK_END_AUTORELEASE_BLOCK
Timothy Lianga8046af2018-07-19 09:58:00 -0400961}
962
Timothy Liangef21d7e2018-07-02 17:03:30 -0400963bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
964 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400965 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400966 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400967 return false;
968 }
969 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
970 return false;
971 }
972
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400973 id<MTLBuffer> transferBuffer;
974 int bpp = GrColorTypeBytesPerPixel(dstColorType);
975 size_t transBufferRowBytes = bpp * width;
Jim Van Verth61610be2019-03-07 15:33:49 -0500976 SK_BEGIN_AUTORELEASE_BLOCK
Timothy Liange35055f2018-07-20 16:53:00 -0400977 bool doResolve = get_surface_sample_cnt(surface) > 1;
Timothy Liange30739a2018-07-31 10:51:17 -0400978 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
Jim Van Verth8cc42482019-04-05 14:16:37 -0400979 if (!mtlTexture || [mtlTexture isFramebufferOnly]) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400980 return false;
981 }
982
Timothy Liangef21d7e2018-07-02 17:03:30 -0400983 size_t transBufferImageBytes = transBufferRowBytes * height;
984
985 // TODO: implement some way of reusing buffers instead of making a new one every time.
Jim Van Verth8cc42482019-04-05 14:16:37 -0400986 NSUInteger options = 0;
987#ifdef SK_BUILD_FOR_MAC
988 options |= MTLResourceStorageModeManaged;
989#else
990 options |= MTLResourceStorageModeShared;
991#endif
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400992 transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
Jim Van Verth8cc42482019-04-05 14:16:37 -0400993 options: options];
Timothy Liangef21d7e2018-07-02 17:03:30 -0400994
Jim Van Verth568eb8d2019-04-04 14:30:42 -0400995 id<MTLBlitCommandEncoder> blitCmdEncoder = [this->commandBuffer() blitCommandEncoder];
Timothy Liangef21d7e2018-07-02 17:03:30 -0400996 [blitCmdEncoder copyFromTexture: mtlTexture
997 sourceSlice: 0
998 sourceLevel: 0
999 sourceOrigin: MTLOriginMake(left, top, 0)
1000 sourceSize: MTLSizeMake(width, height, 1)
1001 toBuffer: transferBuffer
1002 destinationOffset: 0
1003 destinationBytesPerRow: transBufferRowBytes
1004 destinationBytesPerImage: transBufferImageBytes];
Jim Van Verth8cc42482019-04-05 14:16:37 -04001005#ifdef SK_BUILD_FOR_MAC
1006 // Sync GPU data back to the CPU
1007 [blitCmdEncoder synchronizeResource: transferBuffer];
1008#endif
Timothy Liangef21d7e2018-07-02 17:03:30 -04001009 [blitCmdEncoder endEncoding];
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001010 SK_END_AUTORELEASE_BLOCK
Timothy Liangef21d7e2018-07-02 17:03:30 -04001011
1012 this->submitCommandBuffer(kForce_SyncQueue);
1013 const void* mappedMemory = transferBuffer.contents;
1014
1015 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
Jim Van Verth8cc42482019-04-05 14:16:37 -04001016
Timothy Liangef21d7e2018-07-02 17:03:30 -04001017 return true;
Jim Van Verth568eb8d2019-04-04 14:30:42 -04001018
Timothy Liangef21d7e2018-07-02 17:03:30 -04001019}
Robert Phillips5b5d84c2018-08-09 15:12:18 -04001020