blob: eb0f3306b98af216110bcad31f87037ec2d728c3 [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"
Timothy Liangff19c8f2018-07-11 13:27:21 -040015#include "GrTexturePriv.h"
Timothy Liangef21d7e2018-07-02 17:03:30 -040016#include "SkConvertPixels.h"
Timothy Liange30739a2018-07-31 10:51:17 -040017#include "SkSLCompiler.h"
18
19#import <simd/simd.h>
Greg Daniel4a081e22017-08-04 09:34:44 -040020
Greg Daniel6b7e0e22017-07-12 16:21:09 -040021#if !__has_feature(objc_arc)
22#error This file must be compiled with Arc. Use -fobjc-arc flag
23#endif
24
Greg Danielcebcb842017-07-31 10:45:52 -040025static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
26 // Mac OSX
27#ifdef SK_BUILD_FOR_MAC
28 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) {
29 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2;
30 return true;
31 }
32 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) {
33 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1;
34 return true;
35 }
36#endif
37
38 // iOS Family group 3
39#ifdef SK_BUILD_FOR_IOS
40 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) {
41 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2;
42 return true;
43 }
44 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
45 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
46 return true;
47 }
48
49 // iOS Family group 2
50 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) {
51 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3;
52 return true;
53 }
54 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) {
55 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2;
56 return true;
57 }
58 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) {
59 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
60 return true;
61 }
62
63 // iOS Family group 1
64 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) {
65 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3;
66 return true;
67 }
68 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
69 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2;
70 return true;
71 }
72 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) {
73 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
74 return true;
75 }
76#endif
77 // No supported feature sets were found
78 return false;
79}
80
Brian Salomon384fab42017-12-07 12:33:05 -050081sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options,
82 id<MTLDevice> device, id<MTLCommandQueue> queue) {
Greg Danielb76a72a2017-07-13 15:07:54 -040083 if (!device || !queue) {
84 return nullptr;
85 }
Greg Danielcebcb842017-07-31 10:45:52 -040086 MTLFeatureSet featureSet;
87 if (!get_feature_set(device, &featureSet)) {
88 return nullptr;
89 }
Brian Salomon384fab42017-12-07 12:33:05 -050090 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet));
Greg Daniele5ddff52017-07-05 16:49:36 -040091}
92
Greg Danielb76a72a2017-07-13 15:07:54 -040093GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
Greg Danielcebcb842017-07-31 10:45:52 -040094 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet)
Greg Danielb76a72a2017-07-13 15:07:54 -040095 : INHERITED(context)
96 , fDevice(device)
Timothy Liange30739a2018-07-31 10:51:17 -040097 , fQueue(queue)
98 , fCompiler(new SkSL::Compiler())
99 , fCopyManager(this)
100 , fResourceProvider(this) {
Greg Danielcebcb842017-07-31 10:45:52 -0400101
102 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
103 fCaps = fMtlCaps;
104
Timothy Liangef21d7e2018-07-02 17:03:30 -0400105 fCmdBuffer = [fQueue commandBuffer];
Greg Danielb76a72a2017-07-13 15:07:54 -0400106}
Greg Daniel4a081e22017-08-04 09:34:44 -0400107
Timothy Liange70604e2018-07-19 09:49:46 -0400108GrGpuRTCommandBuffer* GrMtlGpu::createCommandBuffer(
109 GrRenderTarget* renderTarget, GrSurfaceOrigin origin,
110 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
111 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
112 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, colorInfo, stencilInfo);
113}
114
115GrGpuTextureCommandBuffer* GrMtlGpu::createCommandBuffer(GrTexture* texture,
116 GrSurfaceOrigin origin) {
117 return new GrMtlGpuTextureCommandBuffer(this, texture, origin);
118}
119
Timothy Liangef21d7e2018-07-02 17:03:30 -0400120void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
121 SkASSERT(fCmdBuffer);
122 [fCmdBuffer commit];
123 if (SyncQueue::kForce_SyncQueue == sync) {
124 [fCmdBuffer waitUntilCompleted];
125 }
126 fCmdBuffer = [fQueue commandBuffer];
127}
128
Timothy Liang49528b62018-08-02 14:18:37 -0400129GrBuffer* GrMtlGpu::onCreateBuffer(size_t size, GrBufferType type, GrAccessPattern accessPattern,
130 const void* data) {
131 return GrMtlBuffer::Create(this, size, type, accessPattern, data);
132}
133
Timothy Liangff19c8f2018-07-11 13:27:21 -0400134static bool check_max_blit_width(int widthInPixels) {
135 if (widthInPixels > 32767) {
136 SkASSERT(false); // surfaces should not be this wide anyway
137 return false;
138 }
139 return true;
140}
141
142bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
143 GrColorType dataColorType, const GrMipLevel texels[],
144 int mipLevelCount) {
145 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
146 if (!check_max_blit_width(width)) {
147 return false;
148 }
149 if (width == 0 || height == 0) {
150 return false;
151 }
152 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
153 return false;
154 }
155
156 id<MTLTexture> mtlTexture = tex->mtlTexture();
157 SkASSERT(mtlTexture);
158 // Either upload only the first miplevel or all miplevels
159 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
160
161 MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture);
162 transferDesc.mipmapLevelCount = mipLevelCount;
163 transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
Timothy Liang4e679842018-08-08 14:21:12 -0400164#ifdef SK_BUILD_FOR_MAC
Timothy Liangff19c8f2018-07-11 13:27:21 -0400165 transferDesc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400166#else
167 transferDesc.storageMode = MTLStorageModeShared;
168#endif
Timothy Liangff19c8f2018-07-11 13:27:21 -0400169 // TODO: implement some way of reusing transfer textures
170 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor:transferDesc];
171 SkASSERT(transferTexture);
172
173 int currentWidth = width;
174 int currentHeight = height;
175 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
176 MTLOrigin origin = MTLOriginMake(left, top, 0);
177
178 SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat);
179 SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount);
180
181 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
182 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
183 size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes
184 : bpp * currentWidth;
185 SkASSERT(texels[currentMipLevel].fPixels);
186 if (rowBytes < bpp * currentWidth || rowBytes % bpp) {
187 return false;
188 }
189 [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height)
190 mipmapLevel: currentMipLevel
191 withBytes: texels[currentMipLevel].fPixels
192 bytesPerRow: rowBytes];
193
194 [blitCmdEncoder copyFromTexture: transferTexture
195 sourceSlice: 0
196 sourceLevel: currentMipLevel
197 sourceOrigin: origin
198 sourceSize: MTLSizeMake(width, height, 1)
199 toTexture: mtlTexture
200 destinationSlice: 0
201 destinationLevel: currentMipLevel
202 destinationOrigin: origin];
203 currentWidth = SkTMax(1, currentWidth/2);
204 currentHeight = SkTMax(1, currentHeight/2);
205 }
206 [blitCmdEncoder endEncoding];
207
208 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
209 tex->texturePriv().markMipMapsDirty();
210 }
211 return true;
212}
213
Greg Daniel4a081e22017-08-04 09:34:44 -0400214sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500215 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400216 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
217
218 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
219 return nullptr;
220 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400221 MTLPixelFormat format;
222 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
223 return nullptr;
224 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400225
226 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400227
Timothy Liang58f153d2018-07-02 17:36:20 -0400228 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
229 // requested, this TexDesc describes the resolved texture. Therefore we always have samples set
230 // to 1.
231 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
232 texDesc.textureType = MTLTextureType2D;
233 texDesc.pixelFormat = format;
234 texDesc.width = desc.fWidth;
235 texDesc.height = desc.fHeight;
236 texDesc.depth = 1;
237 texDesc.mipmapLevelCount = mipLevels;
238 texDesc.sampleCount = 1;
239 texDesc.arrayLength = 1;
240 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
241 // Make all textures have private gpu only access. We can use transfer buffers or textures
242 // to copy to them.
243 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400244 texDesc.usage = MTLTextureUsageShaderRead;
245 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400246
247 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
248 if (mipLevels > 1) {
Timothy Liangff19c8f2018-07-11 13:27:21 -0400249 mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty;
250#ifdef SK_DEBUG
251 for (int i = 1; i < mipLevels; ++i) {
252 if (mipMapsStatus == GrMipMapsStatus::kValid) {
253 SkASSERT(texels[i].fPixels);
254 } else {
255 SkASSERT(!texels[i].fPixels);
Timothy Liang58f153d2018-07-02 17:36:20 -0400256 }
257 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400258#endif
Timothy Liang58f153d2018-07-02 17:36:20 -0400259 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400260 sk_sp<GrMtlTexture> tex;
261 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400262 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400263 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400264 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400265 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400266 }
267
268 if (!tex) {
269 return nullptr;
270 }
271
Timothy Liangff19c8f2018-07-11 13:27:21 -0400272 auto colorType = GrPixelConfigToColorType(desc.fConfig);
273 if (mipLevelCount && texels[0].fPixels) {
274 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
275 mipLevelCount)) {
276 tex->unref();
277 return nullptr;
278 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400279 }
280
281 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
282 // Do initial clear of the texture
283 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400284 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400285}
Timothy Liange886e802018-07-02 16:03:28 -0400286
287static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
288 GrWrapOwnership ownership) {
289 GrMtlTextureInfo textureInfo;
290 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
291 return nil;
292 }
293 return GrGetMTLTexture(textureInfo.fTexture, ownership);
294}
295
296static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
297 GrMtlTextureInfo textureInfo;
298 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
299 return nil;
300 }
301 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
302}
303
304static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
305 bool isRenderTarget, GrPixelConfig config) {
306 if (isRenderTarget) {
307 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
308 }
309 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
310 surfaceDesc->fWidth = mtlTexture.width;
311 surfaceDesc->fHeight = mtlTexture.height;
312 surfaceDesc->fConfig = config;
313 surfaceDesc->fSampleCnt = 1;
314}
315
316sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
317 GrWrapOwnership ownership) {
318 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
319 if (!mtlTexture) {
320 return nullptr;
321 }
322
323 GrSurfaceDesc surfDesc;
324 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
325
326 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture);
327}
328
329sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
330 int sampleCnt,
331 GrWrapOwnership ownership) {
332 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
333 if (!mtlTexture) {
334 return nullptr;
335 }
336
337 GrSurfaceDesc surfDesc;
338 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
339 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
340 if (!surfDesc.fSampleCnt) {
341 return nullptr;
342 }
343
344 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture);
345}
346
347sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
348 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
349 if (backendRT.sampleCnt() > 1) {
350 return nullptr;
351 }
352 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
353 if (!mtlTexture) {
354 return nullptr;
355 }
356
357 GrSurfaceDesc surfDesc;
358 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
359
360 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
361}
362
363sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
364 const GrBackendTexture& backendTex, int sampleCnt) {
365 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
366 GrWrapOwnership::kBorrow_GrWrapOwnership);
367 if (!mtlTexture) {
368 return nullptr;
369 }
370
371 GrSurfaceDesc surfDesc;
372 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
373 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
374 if (!surfDesc.fSampleCnt) {
375 return nullptr;
376 }
377
378 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
379}
380
Timothy Liang760dbc42018-07-17 13:28:20 -0400381#ifdef GR_TEST_UTILS
382bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrPixelConfig config, int w, int h, bool texturable,
383 bool renderable, GrMipMapped mipMapped,
384 const void* srcData, GrMtlTextureInfo* info) {
385 SkASSERT(texturable || renderable);
386 if (!texturable) {
387 SkASSERT(GrMipMapped::kNo == mipMapped);
388 SkASSERT(!srcData);
389 }
390
391 MTLPixelFormat format;
392 if (!GrPixelConfigToMTLFormat(config, &format)) {
393 return false;
394 }
395 if (texturable && !fMtlCaps->isConfigTexturable(config)) {
396 return false;
397 }
398 if (renderable && !fMtlCaps->isConfigRenderable(config)) {
399 return false;
400 }
401 // Currently we don't support uploading pixel data when mipped.
402 if (srcData && GrMipMapped::kYes == mipMapped) {
403 return false;
404 }
405 if(!check_max_blit_width(w)) {
406 return false;
407 }
408
409 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
410 MTLTextureDescriptor* desc =
411 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
412 width: w
413 height: h
414 mipmapped: mipmapped];
415 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
416 desc.storageMode = MTLStorageModePrivate;
417 desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
418 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
419 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
420
421 SkAutoTMalloc<GrColor> srcBuffer;
422 if (!srcData) {
423 srcBuffer.reset(w * h);
424 memset(srcBuffer, 0, w * h * sizeof(GrColor));
425 srcData = srcBuffer;
426 }
427 SkASSERT(srcData);
Timothy Liang4e679842018-08-08 14:21:12 -0400428#ifdef SK_BUILD_FOR_MAC
Timothy Liang760dbc42018-07-17 13:28:20 -0400429 desc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400430#else
431 desc.storageMode = MTLStorageModeShared;
432#endif
Timothy Liang760dbc42018-07-17 13:28:20 -0400433 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor: desc];
434 auto colorType = GrPixelConfigToColorType(config);
435 int rowBytes = w * GrColorTypeBytesPerPixel(colorType);
436 MTLOrigin origin = MTLOriginMake(0, 0, 0);
437
438 SkASSERT(testTexture.pixelFormat == transferTexture.pixelFormat);
439 SkASSERT(testTexture.sampleCount == transferTexture.sampleCount);
440
441 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
442 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
443 int currentWidth = w;
444 int currentHeight = h;
445 for (int mipLevel = 0; mipLevel < (int)testTexture.mipmapLevelCount; mipLevel++) {
446 [transferTexture replaceRegion: MTLRegionMake2D(0, 0, currentWidth, currentHeight)
447 mipmapLevel: mipLevel
448 withBytes: srcData
449 bytesPerRow: rowBytes];
450
451 [blitCmdEncoder copyFromTexture: transferTexture
452 sourceSlice: 0
453 sourceLevel: mipLevel
454 sourceOrigin: origin
455 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
456 toTexture: testTexture
457 destinationSlice: 0
458 destinationLevel: mipLevel
459 destinationOrigin: origin];
460 currentWidth = SkTMax(1, currentWidth/2);
461 currentHeight = SkTMax(1, currentHeight/2);
462 }
463 [blitCmdEncoder endEncoding];
464 [cmdBuffer commit];
465 [cmdBuffer waitUntilCompleted];
466
467 info->fTexture = GrReleaseId(testTexture);
468 return true;
469}
470
471GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h,
472 GrPixelConfig config, bool isRT,
473 GrMipMapped mipMapped) {
474 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
475 return GrBackendTexture();
476 }
477 GrMtlTextureInfo info;
478 if (!this->createTestingOnlyMtlTextureInfo(config, w, h, true, isRT, mipMapped, pixels,
479 &info)) {
480 return {};
481 }
482
483 GrBackendTexture backendTex(w, h, mipMapped, info);
484 backendTex.fConfig = config;
485 return backendTex;
486}
487
488bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
489 SkASSERT(kMetal_GrBackend == tex.backend());
490
491 GrMtlTextureInfo info;
492 if (!tex.getMtlTextureInfo(&info)) {
493 return false;
494 }
495 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
496 GrWrapOwnership::kBorrow_GrWrapOwnership);
497 if (!mtlTexture) {
498 return false;
499 }
500 return mtlTexture.usage & MTLTextureUsageShaderRead;
501}
502
503void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
504 SkASSERT(kMetal_GrBackend == tex.fBackend);
505
506 GrMtlTextureInfo info;
507 if (tex.getMtlTextureInfo(&info)) {
508 // Adopts the metal texture so that ARC will clean it up.
509 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
510 }
511}
512
Brian Osman2d010b62018-08-09 10:55:09 -0400513GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400514 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
515 return GrBackendRenderTarget();
516 }
Brian Osman2d010b62018-08-09 10:55:09 -0400517 auto config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
Timothy Liang760dbc42018-07-17 13:28:20 -0400518 if (kUnknown_GrPixelConfig == config) {
519 return {};
520 }
521 GrMtlTextureInfo info;
522 if (!this->createTestingOnlyMtlTextureInfo(config, w, h, false, true, GrMipMapped::kNo, nullptr,
523 &info)) {
524 return {};
525 }
526
527 GrBackendRenderTarget backendRT(w, h, 1, info);
528 backendRT.fConfig = config;
529 return backendRT;
530}
531
532void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
533 SkASSERT(kMetal_GrBackend == rt.fBackend);
534
535 GrMtlTextureInfo info;
536 if (rt.getMtlTextureInfo(&info)) {
537 this->testingOnly_flushGpuAndSync();
538 // Adopts the metal texture so that ARC will clean it up.
539 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
540 }
541}
542
543void GrMtlGpu::testingOnly_flushGpuAndSync() {
544 this->submitCommandBuffer(kForce_SyncQueue);
545}
546#endif // GR_TEST_UTILS
547
Timothy Liange35055f2018-07-20 16:53:00 -0400548static int get_surface_sample_cnt(GrSurface* surf) {
549 if (const GrRenderTarget* rt = surf->asRenderTarget()) {
550 return rt->numColorSamples();
551 }
552 return 0;
553}
554
555bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
556 GrSurface* src, GrSurfaceOrigin srcOrigin,
557 const SkIRect& srcRect, const SkIPoint& dstPoint) {
558#ifdef SK_DEBUG
559 int dstSampleCnt = get_surface_sample_cnt(dst);
560 int srcSampleCnt = get_surface_sample_cnt(src);
561 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
562 src->config(), srcSampleCnt, srcOrigin,
563 srcRect, dstPoint, dst == src));
564#endif
Timothy Liange30739a2018-07-31 10:51:17 -0400565 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
566 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
Timothy Liange35055f2018-07-20 16:53:00 -0400567
568 // Flip rect if necessary
569 SkIRect srcMtlRect;
570 srcMtlRect.fLeft = srcRect.fLeft;
571 srcMtlRect.fRight = srcRect.fRight;
572 SkIRect dstRect;
573 dstRect.fLeft = dstPoint.fX;
574 dstRect.fRight = dstPoint.fX + srcRect.width();
575
576 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
577 srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
578 srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
579 } else {
580 srcMtlRect.fTop = srcRect.fTop;
581 srcMtlRect.fBottom = srcRect.fBottom;
582 }
583
584 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
585 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
586 } else {
587 dstRect.fTop = dstPoint.fY;
588 }
589 dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
590
591 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
592 [blitCmdEncoder copyFromTexture: srcTex
593 sourceSlice: 0
594 sourceLevel: 0
595 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
596 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
597 toTexture: dstTex
598 destinationSlice: 0
599 destinationLevel: 0
600 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
601 [blitCmdEncoder endEncoding];
602
603 return true;
604}
605
Timothy Liang70c787a2018-08-01 09:56:25 -0400606bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
607 GrSurface* src, GrSurfaceOrigin srcOrigin,
608 const SkIRect& srcRect, const SkIPoint& dstPoint) {
609#ifdef SK_DEBUG
610 int dstSampleCnt = get_surface_sample_cnt(dst);
611 int srcSampleCnt = get_surface_sample_cnt(src);
612 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
613 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
614 src->config(), srcSampleCnt, srcOrigin,
615 srcRect, dstPoint, dst == src));
616 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
617 src->config(), SkToBool(src->asTexture())));
618 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
619 SkToBool(src->asTexture())));
620#endif
621 GrSurfaceDesc surfDesc;
622 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
623 surfDesc.fWidth = srcRect.width();
624 surfDesc.fHeight = srcRect.height();
625 surfDesc.fConfig = dst->config();
626 surfDesc.fSampleCnt = 1;
627
628 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
629 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
630 textureDesc.width = srcRect.width();
631 textureDesc.height = srcRect.height();
632 textureDesc.mipmapLevelCount = 1;
633 textureDesc.usage |= MTLTextureUsageRenderTarget;
634
635 sk_sp<GrMtlTexture> transferTexture =
636 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
637 SkBudgeted::kYes,
638 surfDesc,
639 textureDesc,
640 GrMipMapsStatus::kNotAllocated);
641
642 GrSurfaceOrigin transferOrigin = dstOrigin;
643 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
644 SkToBool(transferTexture->asRenderTarget()),
645 src->config(),
646 SkToBool(src->asTexture())));
647 // TODO: Eventually we will need to handle resolves either in this function or make a separate
648 // copySurfaceAsResolveThenBlit().
649 if (!this->copySurface(transferTexture.get(), transferOrigin,
650 src, srcOrigin,
651 srcRect, SkIPoint::Make(0, 0))) {
652 return false;
653 }
654
655 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
656 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
657 get_surface_sample_cnt(dst),
658 dstOrigin,
659 transferTexture->config(),
660 get_surface_sample_cnt(transferTexture.get()),
661 transferOrigin,
662 transferRect, dstPoint, false));
663 if (!this->copySurface(dst, dstOrigin,
664 transferTexture.get(), transferOrigin,
665 transferRect, dstPoint)) {
666 return false;
667 }
668 return true;
669}
670
Timothy Liange35055f2018-07-20 16:53:00 -0400671bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
672 GrSurface* src, GrSurfaceOrigin srcOrigin,
673 const SkIRect& srcRect,
674 const SkIPoint& dstPoint,
675 bool canDiscardOutsideDstRect) {
676
677 GrPixelConfig dstConfig = dst->config();
678 GrPixelConfig srcConfig = src->config();
679
680 int dstSampleCnt = get_surface_sample_cnt(dst);
681 int srcSampleCnt = get_surface_sample_cnt(src);
682
683 if (dstSampleCnt > 1 || srcSampleCnt > 1) {
Timothy Liang70c787a2018-08-01 09:56:25 -0400684 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve().
Timothy Liange35055f2018-07-20 16:53:00 -0400685 return false;
686 }
687
688 bool success = false;
Timothy Liange30739a2018-07-31 10:51:17 -0400689 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
690 src->config(), SkToBool(src->asTexture()))) {
691 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
692 canDiscardOutsideDstRect);
693 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
694 srcConfig, srcSampleCnt, srcOrigin,
695 srcRect, dstPoint, dst == src)) {
Timothy Liange35055f2018-07-20 16:53:00 -0400696 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
Timothy Liang70c787a2018-08-01 09:56:25 -0400697 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
698 SkToBool(src->asTexture()))) {
699 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
700 srcRect, dstPoint);
Timothy Liange35055f2018-07-20 16:53:00 -0400701 }
702 if (success) {
703 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
704 srcRect.width(), srcRect.height());
705 this->didWriteToSurface(dst, dstOrigin, &dstRect);
706 }
707 return success;
708}
709
Timothy Lianga8046af2018-07-19 09:58:00 -0400710bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
711 GrColorType srcColorType, const GrMipLevel texels[],
712 int mipLevelCount) {
713 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
714 if (!mtlTexture) {
715 return false;
716 }
717 if (!mipLevelCount) {
718 return false;
719 }
720#ifdef SK_DEBUG
721 for (int i = 0; i < mipLevelCount; i++) {
722 SkASSERT(texels[i].fPixels);
723 }
724#endif
725 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels,
726 mipLevelCount);
727}
728
Timothy Liangef21d7e2018-07-02 17:03:30 -0400729bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
730 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400731 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400732 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400733 return false;
734 }
735 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
736 return false;
737 }
738
Timothy Liange35055f2018-07-20 16:53:00 -0400739 bool doResolve = get_surface_sample_cnt(surface) > 1;
Timothy Liange30739a2018-07-31 10:51:17 -0400740 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
Timothy Liangef21d7e2018-07-02 17:03:30 -0400741 if (!mtlTexture) {
742 return false;
743 }
744
745 int bpp = GrColorTypeBytesPerPixel(dstColorType);
746 size_t transBufferRowBytes = bpp * width;
747 size_t transBufferImageBytes = transBufferRowBytes * height;
748
749 // TODO: implement some way of reusing buffers instead of making a new one every time.
750 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
751 options: MTLResourceStorageModeShared];
752
753 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
754 [blitCmdEncoder copyFromTexture: mtlTexture
755 sourceSlice: 0
756 sourceLevel: 0
757 sourceOrigin: MTLOriginMake(left, top, 0)
758 sourceSize: MTLSizeMake(width, height, 1)
759 toBuffer: transferBuffer
760 destinationOffset: 0
761 destinationBytesPerRow: transBufferRowBytes
762 destinationBytesPerImage: transBufferImageBytes];
763 [blitCmdEncoder endEncoding];
764
765 this->submitCommandBuffer(kForce_SyncQueue);
766 const void* mappedMemory = transferBuffer.contents;
767
768 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
769 return true;
770}