blob: fd1611f32f8da82cffca1080d8a4efe166ff27d7 [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
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400108GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer(
Timothy Liange70604e2018-07-19 09:49:46 -0400109 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
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400115GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture,
116 GrSurfaceOrigin origin) {
Timothy Liange70604e2018-07-19 09:49:46 -0400117 return new GrMtlGpuTextureCommandBuffer(this, texture, origin);
118}
119
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400120void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) {
121 delete buffer;
122}
123
Timothy Liangef21d7e2018-07-02 17:03:30 -0400124void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
125 SkASSERT(fCmdBuffer);
126 [fCmdBuffer commit];
127 if (SyncQueue::kForce_SyncQueue == sync) {
128 [fCmdBuffer waitUntilCompleted];
129 }
130 fCmdBuffer = [fQueue commandBuffer];
131}
132
Timothy Liang49528b62018-08-02 14:18:37 -0400133GrBuffer* GrMtlGpu::onCreateBuffer(size_t size, GrBufferType type, GrAccessPattern accessPattern,
134 const void* data) {
135 return GrMtlBuffer::Create(this, size, type, accessPattern, data);
136}
137
Timothy Liangff19c8f2018-07-11 13:27:21 -0400138static bool check_max_blit_width(int widthInPixels) {
139 if (widthInPixels > 32767) {
140 SkASSERT(false); // surfaces should not be this wide anyway
141 return false;
142 }
143 return true;
144}
145
146bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
147 GrColorType dataColorType, const GrMipLevel texels[],
148 int mipLevelCount) {
149 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
150 if (!check_max_blit_width(width)) {
151 return false;
152 }
153 if (width == 0 || height == 0) {
154 return false;
155 }
156 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
157 return false;
158 }
159
160 id<MTLTexture> mtlTexture = tex->mtlTexture();
161 SkASSERT(mtlTexture);
162 // Either upload only the first miplevel or all miplevels
163 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
164
165 MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture);
166 transferDesc.mipmapLevelCount = mipLevelCount;
167 transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
Timothy Liang4e679842018-08-08 14:21:12 -0400168#ifdef SK_BUILD_FOR_MAC
Timothy Liangff19c8f2018-07-11 13:27:21 -0400169 transferDesc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400170#else
171 transferDesc.storageMode = MTLStorageModeShared;
172#endif
Timothy Liangff19c8f2018-07-11 13:27:21 -0400173 // TODO: implement some way of reusing transfer textures
174 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor:transferDesc];
175 SkASSERT(transferTexture);
176
177 int currentWidth = width;
178 int currentHeight = height;
179 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
180 MTLOrigin origin = MTLOriginMake(left, top, 0);
181
182 SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat);
183 SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount);
184
185 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
186 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
187 size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes
188 : bpp * currentWidth;
189 SkASSERT(texels[currentMipLevel].fPixels);
190 if (rowBytes < bpp * currentWidth || rowBytes % bpp) {
191 return false;
192 }
193 [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height)
194 mipmapLevel: currentMipLevel
195 withBytes: texels[currentMipLevel].fPixels
196 bytesPerRow: rowBytes];
197
198 [blitCmdEncoder copyFromTexture: transferTexture
199 sourceSlice: 0
200 sourceLevel: currentMipLevel
201 sourceOrigin: origin
202 sourceSize: MTLSizeMake(width, height, 1)
203 toTexture: mtlTexture
204 destinationSlice: 0
205 destinationLevel: currentMipLevel
206 destinationOrigin: origin];
207 currentWidth = SkTMax(1, currentWidth/2);
208 currentHeight = SkTMax(1, currentHeight/2);
209 }
210 [blitCmdEncoder endEncoding];
211
212 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
213 tex->texturePriv().markMipMapsDirty();
214 }
215 return true;
216}
217
Greg Daniel4a081e22017-08-04 09:34:44 -0400218sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500219 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400220 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
221
222 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
223 return nullptr;
224 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400225 MTLPixelFormat format;
226 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
227 return nullptr;
228 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400229
230 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400231
Timothy Liang58f153d2018-07-02 17:36:20 -0400232 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
233 // requested, this TexDesc describes the resolved texture. Therefore we always have samples set
234 // to 1.
235 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
236 texDesc.textureType = MTLTextureType2D;
237 texDesc.pixelFormat = format;
238 texDesc.width = desc.fWidth;
239 texDesc.height = desc.fHeight;
240 texDesc.depth = 1;
241 texDesc.mipmapLevelCount = mipLevels;
242 texDesc.sampleCount = 1;
243 texDesc.arrayLength = 1;
244 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
245 // Make all textures have private gpu only access. We can use transfer buffers or textures
246 // to copy to them.
247 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400248 texDesc.usage = MTLTextureUsageShaderRead;
249 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400250
251 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
252 if (mipLevels > 1) {
Timothy Liangff19c8f2018-07-11 13:27:21 -0400253 mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty;
254#ifdef SK_DEBUG
255 for (int i = 1; i < mipLevels; ++i) {
256 if (mipMapsStatus == GrMipMapsStatus::kValid) {
257 SkASSERT(texels[i].fPixels);
258 } else {
259 SkASSERT(!texels[i].fPixels);
Timothy Liang58f153d2018-07-02 17:36:20 -0400260 }
261 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400262#endif
Timothy Liang58f153d2018-07-02 17:36:20 -0400263 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400264 sk_sp<GrMtlTexture> tex;
265 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400266 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400267 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400268 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400269 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400270 }
271
272 if (!tex) {
273 return nullptr;
274 }
275
Timothy Liangff19c8f2018-07-11 13:27:21 -0400276 auto colorType = GrPixelConfigToColorType(desc.fConfig);
277 if (mipLevelCount && texels[0].fPixels) {
278 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
279 mipLevelCount)) {
280 tex->unref();
281 return nullptr;
282 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400283 }
284
285 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
286 // Do initial clear of the texture
287 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400288 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400289}
Timothy Liange886e802018-07-02 16:03:28 -0400290
291static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
292 GrWrapOwnership ownership) {
293 GrMtlTextureInfo textureInfo;
294 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
295 return nil;
296 }
297 return GrGetMTLTexture(textureInfo.fTexture, ownership);
298}
299
300static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
301 GrMtlTextureInfo textureInfo;
302 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
303 return nil;
304 }
305 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
306}
307
308static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
309 bool isRenderTarget, GrPixelConfig config) {
310 if (isRenderTarget) {
311 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
312 }
313 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
314 surfaceDesc->fWidth = mtlTexture.width;
315 surfaceDesc->fHeight = mtlTexture.height;
316 surfaceDesc->fConfig = config;
317 surfaceDesc->fSampleCnt = 1;
318}
319
320sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
321 GrWrapOwnership ownership) {
322 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
323 if (!mtlTexture) {
324 return nullptr;
325 }
326
327 GrSurfaceDesc surfDesc;
328 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
329
330 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture);
331}
332
333sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
334 int sampleCnt,
335 GrWrapOwnership ownership) {
336 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
337 if (!mtlTexture) {
338 return nullptr;
339 }
340
341 GrSurfaceDesc surfDesc;
342 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
343 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
344 if (!surfDesc.fSampleCnt) {
345 return nullptr;
346 }
347
348 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture);
349}
350
351sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
352 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
353 if (backendRT.sampleCnt() > 1) {
354 return nullptr;
355 }
356 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
357 if (!mtlTexture) {
358 return nullptr;
359 }
360
361 GrSurfaceDesc surfDesc;
362 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
363
364 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
365}
366
367sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
368 const GrBackendTexture& backendTex, int sampleCnt) {
369 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
370 GrWrapOwnership::kBorrow_GrWrapOwnership);
371 if (!mtlTexture) {
372 return nullptr;
373 }
374
375 GrSurfaceDesc surfDesc;
376 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
377 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
378 if (!surfDesc.fSampleCnt) {
379 return nullptr;
380 }
381
382 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
383}
384
Timothy Liang760dbc42018-07-17 13:28:20 -0400385#ifdef GR_TEST_UTILS
386bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrPixelConfig config, int w, int h, bool texturable,
387 bool renderable, GrMipMapped mipMapped,
388 const void* srcData, GrMtlTextureInfo* info) {
389 SkASSERT(texturable || renderable);
390 if (!texturable) {
391 SkASSERT(GrMipMapped::kNo == mipMapped);
392 SkASSERT(!srcData);
393 }
394
395 MTLPixelFormat format;
396 if (!GrPixelConfigToMTLFormat(config, &format)) {
397 return false;
398 }
399 if (texturable && !fMtlCaps->isConfigTexturable(config)) {
400 return false;
401 }
402 if (renderable && !fMtlCaps->isConfigRenderable(config)) {
403 return false;
404 }
405 // Currently we don't support uploading pixel data when mipped.
406 if (srcData && GrMipMapped::kYes == mipMapped) {
407 return false;
408 }
409 if(!check_max_blit_width(w)) {
410 return false;
411 }
412
413 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
414 MTLTextureDescriptor* desc =
415 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
416 width: w
417 height: h
418 mipmapped: mipmapped];
419 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
420 desc.storageMode = MTLStorageModePrivate;
421 desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
422 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
423 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
424
425 SkAutoTMalloc<GrColor> srcBuffer;
426 if (!srcData) {
427 srcBuffer.reset(w * h);
428 memset(srcBuffer, 0, w * h * sizeof(GrColor));
429 srcData = srcBuffer;
430 }
431 SkASSERT(srcData);
Timothy Liang4e679842018-08-08 14:21:12 -0400432#ifdef SK_BUILD_FOR_MAC
Timothy Liang760dbc42018-07-17 13:28:20 -0400433 desc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400434#else
435 desc.storageMode = MTLStorageModeShared;
436#endif
Timothy Liang760dbc42018-07-17 13:28:20 -0400437 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor: desc];
438 auto colorType = GrPixelConfigToColorType(config);
439 int rowBytes = w * GrColorTypeBytesPerPixel(colorType);
440 MTLOrigin origin = MTLOriginMake(0, 0, 0);
441
442 SkASSERT(testTexture.pixelFormat == transferTexture.pixelFormat);
443 SkASSERT(testTexture.sampleCount == transferTexture.sampleCount);
444
445 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
446 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
447 int currentWidth = w;
448 int currentHeight = h;
449 for (int mipLevel = 0; mipLevel < (int)testTexture.mipmapLevelCount; mipLevel++) {
450 [transferTexture replaceRegion: MTLRegionMake2D(0, 0, currentWidth, currentHeight)
451 mipmapLevel: mipLevel
452 withBytes: srcData
453 bytesPerRow: rowBytes];
454
455 [blitCmdEncoder copyFromTexture: transferTexture
456 sourceSlice: 0
457 sourceLevel: mipLevel
458 sourceOrigin: origin
459 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
460 toTexture: testTexture
461 destinationSlice: 0
462 destinationLevel: mipLevel
463 destinationOrigin: origin];
464 currentWidth = SkTMax(1, currentWidth/2);
465 currentHeight = SkTMax(1, currentHeight/2);
466 }
467 [blitCmdEncoder endEncoding];
468 [cmdBuffer commit];
469 [cmdBuffer waitUntilCompleted];
470
471 info->fTexture = GrReleaseId(testTexture);
472 return true;
473}
474
475GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h,
476 GrPixelConfig config, bool isRT,
477 GrMipMapped mipMapped) {
478 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
479 return GrBackendTexture();
480 }
481 GrMtlTextureInfo info;
482 if (!this->createTestingOnlyMtlTextureInfo(config, w, h, true, isRT, mipMapped, pixels,
483 &info)) {
484 return {};
485 }
486
487 GrBackendTexture backendTex(w, h, mipMapped, info);
488 backendTex.fConfig = config;
489 return backendTex;
490}
491
492bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
493 SkASSERT(kMetal_GrBackend == tex.backend());
494
495 GrMtlTextureInfo info;
496 if (!tex.getMtlTextureInfo(&info)) {
497 return false;
498 }
499 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
500 GrWrapOwnership::kBorrow_GrWrapOwnership);
501 if (!mtlTexture) {
502 return false;
503 }
504 return mtlTexture.usage & MTLTextureUsageShaderRead;
505}
506
507void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
508 SkASSERT(kMetal_GrBackend == tex.fBackend);
509
510 GrMtlTextureInfo info;
511 if (tex.getMtlTextureInfo(&info)) {
512 // Adopts the metal texture so that ARC will clean it up.
513 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
514 }
515}
516
Brian Osman2d010b62018-08-09 10:55:09 -0400517GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400518 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
519 return GrBackendRenderTarget();
520 }
Brian Osman2d010b62018-08-09 10:55:09 -0400521 auto config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
Timothy Liang760dbc42018-07-17 13:28:20 -0400522 if (kUnknown_GrPixelConfig == config) {
523 return {};
524 }
525 GrMtlTextureInfo info;
526 if (!this->createTestingOnlyMtlTextureInfo(config, w, h, false, true, GrMipMapped::kNo, nullptr,
527 &info)) {
528 return {};
529 }
530
531 GrBackendRenderTarget backendRT(w, h, 1, info);
532 backendRT.fConfig = config;
533 return backendRT;
534}
535
536void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
537 SkASSERT(kMetal_GrBackend == rt.fBackend);
538
539 GrMtlTextureInfo info;
540 if (rt.getMtlTextureInfo(&info)) {
541 this->testingOnly_flushGpuAndSync();
542 // Adopts the metal texture so that ARC will clean it up.
543 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
544 }
545}
546
547void GrMtlGpu::testingOnly_flushGpuAndSync() {
548 this->submitCommandBuffer(kForce_SyncQueue);
549}
550#endif // GR_TEST_UTILS
551
Timothy Liange35055f2018-07-20 16:53:00 -0400552static int get_surface_sample_cnt(GrSurface* surf) {
553 if (const GrRenderTarget* rt = surf->asRenderTarget()) {
554 return rt->numColorSamples();
555 }
556 return 0;
557}
558
559bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
560 GrSurface* src, GrSurfaceOrigin srcOrigin,
561 const SkIRect& srcRect, const SkIPoint& dstPoint) {
562#ifdef SK_DEBUG
563 int dstSampleCnt = get_surface_sample_cnt(dst);
564 int srcSampleCnt = get_surface_sample_cnt(src);
565 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
566 src->config(), srcSampleCnt, srcOrigin,
567 srcRect, dstPoint, dst == src));
568#endif
Timothy Liange30739a2018-07-31 10:51:17 -0400569 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
570 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
Timothy Liange35055f2018-07-20 16:53:00 -0400571
572 // Flip rect if necessary
573 SkIRect srcMtlRect;
574 srcMtlRect.fLeft = srcRect.fLeft;
575 srcMtlRect.fRight = srcRect.fRight;
576 SkIRect dstRect;
577 dstRect.fLeft = dstPoint.fX;
578 dstRect.fRight = dstPoint.fX + srcRect.width();
579
580 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
581 srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
582 srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
583 } else {
584 srcMtlRect.fTop = srcRect.fTop;
585 srcMtlRect.fBottom = srcRect.fBottom;
586 }
587
588 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
589 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
590 } else {
591 dstRect.fTop = dstPoint.fY;
592 }
593 dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
594
595 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
596 [blitCmdEncoder copyFromTexture: srcTex
597 sourceSlice: 0
598 sourceLevel: 0
599 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
600 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
601 toTexture: dstTex
602 destinationSlice: 0
603 destinationLevel: 0
604 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
605 [blitCmdEncoder endEncoding];
606
607 return true;
608}
609
Timothy Liang70c787a2018-08-01 09:56:25 -0400610bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
611 GrSurface* src, GrSurfaceOrigin srcOrigin,
612 const SkIRect& srcRect, const SkIPoint& dstPoint) {
613#ifdef SK_DEBUG
614 int dstSampleCnt = get_surface_sample_cnt(dst);
615 int srcSampleCnt = get_surface_sample_cnt(src);
616 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
617 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
618 src->config(), srcSampleCnt, srcOrigin,
619 srcRect, dstPoint, dst == src));
620 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
621 src->config(), SkToBool(src->asTexture())));
622 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
623 SkToBool(src->asTexture())));
624#endif
625 GrSurfaceDesc surfDesc;
626 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
627 surfDesc.fWidth = srcRect.width();
628 surfDesc.fHeight = srcRect.height();
629 surfDesc.fConfig = dst->config();
630 surfDesc.fSampleCnt = 1;
631
632 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
633 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
634 textureDesc.width = srcRect.width();
635 textureDesc.height = srcRect.height();
636 textureDesc.mipmapLevelCount = 1;
637 textureDesc.usage |= MTLTextureUsageRenderTarget;
638
639 sk_sp<GrMtlTexture> transferTexture =
640 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
641 SkBudgeted::kYes,
642 surfDesc,
643 textureDesc,
644 GrMipMapsStatus::kNotAllocated);
645
646 GrSurfaceOrigin transferOrigin = dstOrigin;
647 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
648 SkToBool(transferTexture->asRenderTarget()),
649 src->config(),
650 SkToBool(src->asTexture())));
651 // TODO: Eventually we will need to handle resolves either in this function or make a separate
652 // copySurfaceAsResolveThenBlit().
653 if (!this->copySurface(transferTexture.get(), transferOrigin,
654 src, srcOrigin,
655 srcRect, SkIPoint::Make(0, 0))) {
656 return false;
657 }
658
659 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
660 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
661 get_surface_sample_cnt(dst),
662 dstOrigin,
663 transferTexture->config(),
664 get_surface_sample_cnt(transferTexture.get()),
665 transferOrigin,
666 transferRect, dstPoint, false));
667 if (!this->copySurface(dst, dstOrigin,
668 transferTexture.get(), transferOrigin,
669 transferRect, dstPoint)) {
670 return false;
671 }
672 return true;
673}
674
Timothy Liange35055f2018-07-20 16:53:00 -0400675bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
676 GrSurface* src, GrSurfaceOrigin srcOrigin,
677 const SkIRect& srcRect,
678 const SkIPoint& dstPoint,
679 bool canDiscardOutsideDstRect) {
680
681 GrPixelConfig dstConfig = dst->config();
682 GrPixelConfig srcConfig = src->config();
683
684 int dstSampleCnt = get_surface_sample_cnt(dst);
685 int srcSampleCnt = get_surface_sample_cnt(src);
686
687 if (dstSampleCnt > 1 || srcSampleCnt > 1) {
Timothy Liang70c787a2018-08-01 09:56:25 -0400688 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve().
Timothy Liange35055f2018-07-20 16:53:00 -0400689 return false;
690 }
691
692 bool success = false;
Timothy Liange30739a2018-07-31 10:51:17 -0400693 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
694 src->config(), SkToBool(src->asTexture()))) {
695 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
696 canDiscardOutsideDstRect);
697 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
698 srcConfig, srcSampleCnt, srcOrigin,
699 srcRect, dstPoint, dst == src)) {
Timothy Liange35055f2018-07-20 16:53:00 -0400700 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
Timothy Liang70c787a2018-08-01 09:56:25 -0400701 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
702 SkToBool(src->asTexture()))) {
703 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
704 srcRect, dstPoint);
Timothy Liange35055f2018-07-20 16:53:00 -0400705 }
706 if (success) {
707 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
708 srcRect.width(), srcRect.height());
709 this->didWriteToSurface(dst, dstOrigin, &dstRect);
710 }
711 return success;
712}
713
Timothy Lianga8046af2018-07-19 09:58:00 -0400714bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
715 GrColorType srcColorType, const GrMipLevel texels[],
716 int mipLevelCount) {
717 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
718 if (!mtlTexture) {
719 return false;
720 }
721 if (!mipLevelCount) {
722 return false;
723 }
724#ifdef SK_DEBUG
725 for (int i = 0; i < mipLevelCount; i++) {
726 SkASSERT(texels[i].fPixels);
727 }
728#endif
729 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels,
730 mipLevelCount);
731}
732
Timothy Liangef21d7e2018-07-02 17:03:30 -0400733bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
734 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400735 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400736 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400737 return false;
738 }
739 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
740 return false;
741 }
742
Timothy Liange35055f2018-07-20 16:53:00 -0400743 bool doResolve = get_surface_sample_cnt(surface) > 1;
Timothy Liange30739a2018-07-31 10:51:17 -0400744 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
Timothy Liangef21d7e2018-07-02 17:03:30 -0400745 if (!mtlTexture) {
746 return false;
747 }
748
749 int bpp = GrColorTypeBytesPerPixel(dstColorType);
750 size_t transBufferRowBytes = bpp * width;
751 size_t transBufferImageBytes = transBufferRowBytes * height;
752
753 // TODO: implement some way of reusing buffers instead of making a new one every time.
754 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
755 options: MTLResourceStorageModeShared];
756
757 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
758 [blitCmdEncoder copyFromTexture: mtlTexture
759 sourceSlice: 0
760 sourceLevel: 0
761 sourceOrigin: MTLOriginMake(left, top, 0)
762 sourceSize: MTLSizeMake(width, height, 1)
763 toBuffer: transferBuffer
764 destinationOffset: 0
765 destinationBytesPerRow: transBufferRowBytes
766 destinationBytesPerImage: transBufferImageBytes];
767 [blitCmdEncoder endEncoding];
768
769 this->submitCommandBuffer(kForce_SyncQueue);
770 const void* mappedMemory = transferBuffer.contents;
771
772 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
773 return true;
774}
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400775