blob: 37e7608a416dd239e913273d28f0e4bd532f7ccd [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)
99 , fCompiler(new SkSL::Compiler())
100 , fCopyManager(this)
101 , fResourceProvider(this) {
Greg Danielcebcb842017-07-31 10:45:52 -0400102
103 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
104 fCaps = fMtlCaps;
105
Timothy Liangef21d7e2018-07-02 17:03:30 -0400106 fCmdBuffer = [fQueue commandBuffer];
Greg Danielb76a72a2017-07-13 15:07:54 -0400107}
Greg Daniel4a081e22017-08-04 09:34:44 -0400108
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400109GrGpuRTCommandBuffer* GrMtlGpu::getCommandBuffer(
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400110 GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const SkRect& bounds,
Timothy Liange70604e2018-07-19 09:49:46 -0400111 const GrGpuRTCommandBuffer::LoadAndStoreInfo& colorInfo,
112 const GrGpuRTCommandBuffer::StencilLoadAndStoreInfo& stencilInfo) {
Ethan Nicholas56d19a52018-10-15 11:26:20 -0400113 return new GrMtlGpuRTCommandBuffer(this, renderTarget, origin, bounds, colorInfo, stencilInfo);
Timothy Liange70604e2018-07-19 09:49:46 -0400114}
115
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400116GrGpuTextureCommandBuffer* GrMtlGpu::getCommandBuffer(GrTexture* texture,
117 GrSurfaceOrigin origin) {
Timothy Liange70604e2018-07-19 09:49:46 -0400118 return new GrMtlGpuTextureCommandBuffer(this, texture, origin);
119}
120
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400121void GrMtlGpu::submit(GrGpuCommandBuffer* buffer) {
122 delete buffer;
123}
124
Timothy Liangef21d7e2018-07-02 17:03:30 -0400125void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
126 SkASSERT(fCmdBuffer);
127 [fCmdBuffer commit];
128 if (SyncQueue::kForce_SyncQueue == sync) {
129 [fCmdBuffer waitUntilCompleted];
130 }
131 fCmdBuffer = [fQueue commandBuffer];
132}
133
Timothy Liang49528b62018-08-02 14:18:37 -0400134GrBuffer* GrMtlGpu::onCreateBuffer(size_t size, GrBufferType type, GrAccessPattern accessPattern,
135 const void* data) {
136 return GrMtlBuffer::Create(this, size, type, accessPattern, data);
137}
138
Timothy Liangff19c8f2018-07-11 13:27:21 -0400139static bool check_max_blit_width(int widthInPixels) {
140 if (widthInPixels > 32767) {
141 SkASSERT(false); // surfaces should not be this wide anyway
142 return false;
143 }
144 return true;
145}
146
147bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
148 GrColorType dataColorType, const GrMipLevel texels[],
149 int mipLevelCount) {
150 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
151 if (!check_max_blit_width(width)) {
152 return false;
153 }
154 if (width == 0 || height == 0) {
155 return false;
156 }
157 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
158 return false;
159 }
160
161 id<MTLTexture> mtlTexture = tex->mtlTexture();
162 SkASSERT(mtlTexture);
163 // Either upload only the first miplevel or all miplevels
164 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
165
166 MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture);
167 transferDesc.mipmapLevelCount = mipLevelCount;
168 transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
Timothy Liang4e679842018-08-08 14:21:12 -0400169#ifdef SK_BUILD_FOR_MAC
Timothy Liangff19c8f2018-07-11 13:27:21 -0400170 transferDesc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400171#else
172 transferDesc.storageMode = MTLStorageModeShared;
173#endif
Timothy Liangff19c8f2018-07-11 13:27:21 -0400174 // TODO: implement some way of reusing transfer textures
175 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor:transferDesc];
176 SkASSERT(transferTexture);
177
178 int currentWidth = width;
179 int currentHeight = height;
180 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
181 MTLOrigin origin = MTLOriginMake(left, top, 0);
182
183 SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat);
184 SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount);
185
186 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
187 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
188 size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes
189 : bpp * currentWidth;
190 SkASSERT(texels[currentMipLevel].fPixels);
191 if (rowBytes < bpp * currentWidth || rowBytes % bpp) {
192 return false;
193 }
194 [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height)
195 mipmapLevel: currentMipLevel
196 withBytes: texels[currentMipLevel].fPixels
197 bytesPerRow: rowBytes];
198
199 [blitCmdEncoder copyFromTexture: transferTexture
200 sourceSlice: 0
201 sourceLevel: currentMipLevel
202 sourceOrigin: origin
203 sourceSize: MTLSizeMake(width, height, 1)
204 toTexture: mtlTexture
205 destinationSlice: 0
206 destinationLevel: currentMipLevel
207 destinationOrigin: origin];
208 currentWidth = SkTMax(1, currentWidth/2);
209 currentHeight = SkTMax(1, currentHeight/2);
210 }
211 [blitCmdEncoder endEncoding];
212
213 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
214 tex->texturePriv().markMipMapsDirty();
215 }
216 return true;
217}
218
Ethan Nicholas01063512018-10-08 16:58:25 -0400219GrStencilAttachment* GrMtlGpu::createStencilAttachmentForRenderTarget(const GrRenderTarget* rt,
220 int width,
221 int height) {
222 SkASSERT(width >= rt->width());
223 SkASSERT(height >= rt->height());
224
225 int samples = rt->numStencilSamples();
226
227 const GrMtlCaps::StencilFormat& sFmt = this->mtlCaps().preferredStencilFormat();
228
229 GrMtlStencilAttachment* stencil(GrMtlStencilAttachment::Create(this,
230 width,
231 height,
232 samples,
233 sFmt));
234 fStats.incStencilAttachmentCreates();
235 return stencil;
236}
237
Greg Daniel4a081e22017-08-04 09:34:44 -0400238sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500239 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400240 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
241
242 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
243 return nullptr;
244 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400245 MTLPixelFormat format;
246 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
247 return nullptr;
248 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400249
250 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400251
Timothy Liang58f153d2018-07-02 17:36:20 -0400252 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
253 // requested, this TexDesc describes the resolved texture. Therefore we always have samples set
254 // to 1.
255 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
256 texDesc.textureType = MTLTextureType2D;
257 texDesc.pixelFormat = format;
258 texDesc.width = desc.fWidth;
259 texDesc.height = desc.fHeight;
260 texDesc.depth = 1;
261 texDesc.mipmapLevelCount = mipLevels;
262 texDesc.sampleCount = 1;
263 texDesc.arrayLength = 1;
264 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
265 // Make all textures have private gpu only access. We can use transfer buffers or textures
266 // to copy to them.
267 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400268 texDesc.usage = MTLTextureUsageShaderRead;
269 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400270
271 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
272 if (mipLevels > 1) {
Timothy Liangff19c8f2018-07-11 13:27:21 -0400273 mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty;
274#ifdef SK_DEBUG
275 for (int i = 1; i < mipLevels; ++i) {
276 if (mipMapsStatus == GrMipMapsStatus::kValid) {
277 SkASSERT(texels[i].fPixels);
278 } else {
279 SkASSERT(!texels[i].fPixels);
Timothy Liang58f153d2018-07-02 17:36:20 -0400280 }
281 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400282#endif
Timothy Liang58f153d2018-07-02 17:36:20 -0400283 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400284 sk_sp<GrMtlTexture> tex;
285 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400286 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400287 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400288 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400289 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400290 }
291
292 if (!tex) {
293 return nullptr;
294 }
295
Timothy Liangff19c8f2018-07-11 13:27:21 -0400296 auto colorType = GrPixelConfigToColorType(desc.fConfig);
297 if (mipLevelCount && texels[0].fPixels) {
298 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
299 mipLevelCount)) {
300 tex->unref();
301 return nullptr;
302 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400303 }
304
305 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
306 // Do initial clear of the texture
307 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400308 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400309}
Timothy Liange886e802018-07-02 16:03:28 -0400310
311static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
312 GrWrapOwnership ownership) {
313 GrMtlTextureInfo textureInfo;
314 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
315 return nil;
316 }
317 return GrGetMTLTexture(textureInfo.fTexture, ownership);
318}
319
320static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
321 GrMtlTextureInfo textureInfo;
322 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
323 return nil;
324 }
325 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
326}
327
328static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
329 bool isRenderTarget, GrPixelConfig config) {
330 if (isRenderTarget) {
331 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
332 }
333 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
334 surfaceDesc->fWidth = mtlTexture.width;
335 surfaceDesc->fHeight = mtlTexture.height;
336 surfaceDesc->fConfig = config;
337 surfaceDesc->fSampleCnt = 1;
338}
339
340sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
341 GrWrapOwnership ownership) {
342 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
343 if (!mtlTexture) {
344 return nullptr;
345 }
346
347 GrSurfaceDesc surfDesc;
348 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
349
350 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture);
351}
352
353sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
354 int sampleCnt,
355 GrWrapOwnership ownership) {
356 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
357 if (!mtlTexture) {
358 return nullptr;
359 }
360
361 GrSurfaceDesc surfDesc;
362 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
363 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
364 if (!surfDesc.fSampleCnt) {
365 return nullptr;
366 }
367
368 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture);
369}
370
371sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
372 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
373 if (backendRT.sampleCnt() > 1) {
374 return nullptr;
375 }
376 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
377 if (!mtlTexture) {
378 return nullptr;
379 }
380
381 GrSurfaceDesc surfDesc;
382 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
383
384 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
385}
386
387sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
388 const GrBackendTexture& backendTex, int sampleCnt) {
389 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
390 GrWrapOwnership::kBorrow_GrWrapOwnership);
391 if (!mtlTexture) {
392 return nullptr;
393 }
394
395 GrSurfaceDesc surfDesc;
396 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
397 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
398 if (!surfDesc.fSampleCnt) {
399 return nullptr;
400 }
401
402 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
403}
404
Timothy Liang760dbc42018-07-17 13:28:20 -0400405#ifdef GR_TEST_UTILS
Robert Phillips646f6372018-09-25 09:31:10 -0400406bool GrMtlGpu::createTestingOnlyMtlTextureInfo(GrColorType colorType, int w, int h, bool texturable,
407 bool renderable, GrMipMapped mipMapped,
408 const void* srcData, size_t srcRowBytes,
409 GrMtlTextureInfo* info) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400410 SkASSERT(texturable || renderable);
411 if (!texturable) {
412 SkASSERT(GrMipMapped::kNo == mipMapped);
413 SkASSERT(!srcData);
414 }
415
Robert Phillips646f6372018-09-25 09:31:10 -0400416 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo);
417
Timothy Liang760dbc42018-07-17 13:28:20 -0400418 MTLPixelFormat format;
419 if (!GrPixelConfigToMTLFormat(config, &format)) {
420 return false;
421 }
422 if (texturable && !fMtlCaps->isConfigTexturable(config)) {
423 return false;
424 }
425 if (renderable && !fMtlCaps->isConfigRenderable(config)) {
426 return false;
427 }
428 // Currently we don't support uploading pixel data when mipped.
429 if (srcData && GrMipMapped::kYes == mipMapped) {
430 return false;
431 }
432 if(!check_max_blit_width(w)) {
433 return false;
434 }
435
436 bool mipmapped = mipMapped == GrMipMapped::kYes ? true : false;
437 MTLTextureDescriptor* desc =
438 [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
439 width: w
440 height: h
441 mipmapped: mipmapped];
442 desc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
443 desc.storageMode = MTLStorageModePrivate;
444 desc.usage = texturable ? MTLTextureUsageShaderRead : 0;
445 desc.usage |= renderable ? MTLTextureUsageRenderTarget : 0;
446 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor: desc];
447
448 SkAutoTMalloc<GrColor> srcBuffer;
449 if (!srcData) {
450 srcBuffer.reset(w * h);
451 memset(srcBuffer, 0, w * h * sizeof(GrColor));
452 srcData = srcBuffer;
453 }
454 SkASSERT(srcData);
Timothy Liang4e679842018-08-08 14:21:12 -0400455#ifdef SK_BUILD_FOR_MAC
Timothy Liang760dbc42018-07-17 13:28:20 -0400456 desc.storageMode = MTLStorageModeManaged;
Timothy Liang4e679842018-08-08 14:21:12 -0400457#else
458 desc.storageMode = MTLStorageModeShared;
459#endif
Timothy Liang760dbc42018-07-17 13:28:20 -0400460 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor: desc];
Robert Phillips646f6372018-09-25 09:31:10 -0400461 size_t trimRowBytes = w * GrColorTypeBytesPerPixel(colorType);
462 if (!srcRowBytes) {
463 srcRowBytes = trimRowBytes;
464 }
465
Timothy Liang760dbc42018-07-17 13:28:20 -0400466 MTLOrigin origin = MTLOriginMake(0, 0, 0);
467
468 SkASSERT(testTexture.pixelFormat == transferTexture.pixelFormat);
469 SkASSERT(testTexture.sampleCount == transferTexture.sampleCount);
470
471 id<MTLCommandBuffer> cmdBuffer = [fQueue commandBuffer];
472 id<MTLBlitCommandEncoder> blitCmdEncoder = [cmdBuffer blitCommandEncoder];
473 int currentWidth = w;
474 int currentHeight = h;
475 for (int mipLevel = 0; mipLevel < (int)testTexture.mipmapLevelCount; mipLevel++) {
476 [transferTexture replaceRegion: MTLRegionMake2D(0, 0, currentWidth, currentHeight)
477 mipmapLevel: mipLevel
478 withBytes: srcData
Robert Phillips646f6372018-09-25 09:31:10 -0400479 bytesPerRow: srcRowBytes];
Timothy Liang760dbc42018-07-17 13:28:20 -0400480
481 [blitCmdEncoder copyFromTexture: transferTexture
482 sourceSlice: 0
483 sourceLevel: mipLevel
484 sourceOrigin: origin
485 sourceSize: MTLSizeMake(currentWidth, currentHeight, 1)
486 toTexture: testTexture
487 destinationSlice: 0
488 destinationLevel: mipLevel
489 destinationOrigin: origin];
490 currentWidth = SkTMax(1, currentWidth/2);
491 currentHeight = SkTMax(1, currentHeight/2);
492 }
493 [blitCmdEncoder endEncoding];
494 [cmdBuffer commit];
495 [cmdBuffer waitUntilCompleted];
496
497 info->fTexture = GrReleaseId(testTexture);
498 return true;
499}
500
501GrBackendTexture GrMtlGpu::createTestingOnlyBackendTexture(const void* pixels, int w, int h,
Robert Phillips646f6372018-09-25 09:31:10 -0400502 GrColorType colorType, bool isRT,
503 GrMipMapped mipMapped, size_t rowBytes) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400504 if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
505 return GrBackendTexture();
506 }
507 GrMtlTextureInfo info;
Robert Phillips646f6372018-09-25 09:31:10 -0400508 if (!this->createTestingOnlyMtlTextureInfo(colorType, w, h, true, isRT, mipMapped, pixels,
509 rowBytes, &info)) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400510 return {};
511 }
512
Robert Phillips646f6372018-09-25 09:31:10 -0400513 GrPixelConfig config = GrColorTypeToPixelConfig(colorType, GrSRGBEncoded::kNo);
514
Timothy Liang760dbc42018-07-17 13:28:20 -0400515 GrBackendTexture backendTex(w, h, mipMapped, info);
516 backendTex.fConfig = config;
517 return backendTex;
518}
519
520bool GrMtlGpu::isTestingOnlyBackendTexture(const GrBackendTexture& tex) const {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400521 SkASSERT(GrBackendApi::kMetal == tex.backend());
Timothy Liang760dbc42018-07-17 13:28:20 -0400522
523 GrMtlTextureInfo info;
524 if (!tex.getMtlTextureInfo(&info)) {
525 return false;
526 }
527 id<MTLTexture> mtlTexture = GrGetMTLTexture(info.fTexture,
528 GrWrapOwnership::kBorrow_GrWrapOwnership);
529 if (!mtlTexture) {
530 return false;
531 }
532 return mtlTexture.usage & MTLTextureUsageShaderRead;
533}
534
535void GrMtlGpu::deleteTestingOnlyBackendTexture(const GrBackendTexture& tex) {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400536 SkASSERT(GrBackendApi::kMetal == tex.fBackend);
Timothy Liang760dbc42018-07-17 13:28:20 -0400537
538 GrMtlTextureInfo info;
539 if (tex.getMtlTextureInfo(&info)) {
540 // Adopts the metal texture so that ARC will clean it up.
541 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
542 }
543}
544
Brian Osman2d010b62018-08-09 10:55:09 -0400545GrBackendRenderTarget GrMtlGpu::createTestingOnlyBackendRenderTarget(int w, int h, GrColorType ct) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400546 if (w > this->caps()->maxRenderTargetSize() || h > this->caps()->maxRenderTargetSize()) {
547 return GrBackendRenderTarget();
548 }
Robert Phillips646f6372018-09-25 09:31:10 -0400549
Timothy Liang760dbc42018-07-17 13:28:20 -0400550 GrMtlTextureInfo info;
Robert Phillips646f6372018-09-25 09:31:10 -0400551 if (!this->createTestingOnlyMtlTextureInfo(ct, w, h, false, true, GrMipMapped::kNo, nullptr,
552 0, &info)) {
Timothy Liang760dbc42018-07-17 13:28:20 -0400553 return {};
554 }
555
Robert Phillips646f6372018-09-25 09:31:10 -0400556 GrPixelConfig config = GrColorTypeToPixelConfig(ct, GrSRGBEncoded::kNo);
557
Timothy Liang760dbc42018-07-17 13:28:20 -0400558 GrBackendRenderTarget backendRT(w, h, 1, info);
559 backendRT.fConfig = config;
560 return backendRT;
561}
562
563void GrMtlGpu::deleteTestingOnlyBackendRenderTarget(const GrBackendRenderTarget& rt) {
Greg Danielbdf12ad2018-10-12 09:31:11 -0400564 SkASSERT(GrBackendApi::kMetal == rt.fBackend);
Timothy Liang760dbc42018-07-17 13:28:20 -0400565
566 GrMtlTextureInfo info;
567 if (rt.getMtlTextureInfo(&info)) {
568 this->testingOnly_flushGpuAndSync();
569 // Adopts the metal texture so that ARC will clean it up.
570 GrGetMTLTexture(info.fTexture, GrWrapOwnership::kAdopt_GrWrapOwnership);
571 }
572}
573
574void GrMtlGpu::testingOnly_flushGpuAndSync() {
575 this->submitCommandBuffer(kForce_SyncQueue);
576}
577#endif // GR_TEST_UTILS
578
Timothy Liange35055f2018-07-20 16:53:00 -0400579static int get_surface_sample_cnt(GrSurface* surf) {
580 if (const GrRenderTarget* rt = surf->asRenderTarget()) {
581 return rt->numColorSamples();
582 }
583 return 0;
584}
585
586bool GrMtlGpu::copySurfaceAsBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
587 GrSurface* src, GrSurfaceOrigin srcOrigin,
588 const SkIRect& srcRect, const SkIPoint& dstPoint) {
589#ifdef SK_DEBUG
590 int dstSampleCnt = get_surface_sample_cnt(dst);
591 int srcSampleCnt = get_surface_sample_cnt(src);
592 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
593 src->config(), srcSampleCnt, srcOrigin,
594 srcRect, dstPoint, dst == src));
595#endif
Timothy Liange30739a2018-07-31 10:51:17 -0400596 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
597 id<MTLTexture> srcTex = GrGetMTLTextureFromSurface(src, false);
Timothy Liange35055f2018-07-20 16:53:00 -0400598
599 // Flip rect if necessary
600 SkIRect srcMtlRect;
601 srcMtlRect.fLeft = srcRect.fLeft;
602 srcMtlRect.fRight = srcRect.fRight;
603 SkIRect dstRect;
604 dstRect.fLeft = dstPoint.fX;
605 dstRect.fRight = dstPoint.fX + srcRect.width();
606
607 if (kBottomLeft_GrSurfaceOrigin == srcOrigin) {
608 srcMtlRect.fTop = srcTex.height - srcRect.fBottom;
609 srcMtlRect.fBottom = srcTex.height - srcRect.fTop;
610 } else {
611 srcMtlRect.fTop = srcRect.fTop;
612 srcMtlRect.fBottom = srcRect.fBottom;
613 }
614
615 if (kBottomLeft_GrSurfaceOrigin == dstOrigin) {
616 dstRect.fTop = dstTex.height - dstPoint.fY - srcMtlRect.height();
617 } else {
618 dstRect.fTop = dstPoint.fY;
619 }
620 dstRect.fBottom = dstRect.fTop + srcMtlRect.height();
621
622 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
623 [blitCmdEncoder copyFromTexture: srcTex
624 sourceSlice: 0
625 sourceLevel: 0
626 sourceOrigin: MTLOriginMake(srcMtlRect.x(), srcMtlRect.y(), 0)
627 sourceSize: MTLSizeMake(srcMtlRect.width(), srcMtlRect.height(), 1)
628 toTexture: dstTex
629 destinationSlice: 0
630 destinationLevel: 0
631 destinationOrigin: MTLOriginMake(dstRect.x(), dstRect.y(), 0)];
632 [blitCmdEncoder endEncoding];
633
634 return true;
635}
636
Timothy Liang70c787a2018-08-01 09:56:25 -0400637bool GrMtlGpu::copySurfaceAsDrawThenBlit(GrSurface* dst, GrSurfaceOrigin dstOrigin,
638 GrSurface* src, GrSurfaceOrigin srcOrigin,
639 const SkIRect& srcRect, const SkIPoint& dstPoint) {
640#ifdef SK_DEBUG
641 int dstSampleCnt = get_surface_sample_cnt(dst);
642 int srcSampleCnt = get_surface_sample_cnt(src);
643 SkASSERT(dstSampleCnt == 0); // dst shouldn't be a render target
644 SkASSERT(!this->mtlCaps().canCopyAsBlit(dst->config(), dstSampleCnt, dstOrigin,
645 src->config(), srcSampleCnt, srcOrigin,
646 srcRect, dstPoint, dst == src));
647 SkASSERT(!this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
648 src->config(), SkToBool(src->asTexture())));
649 SkASSERT(this->mtlCaps().canCopyAsDrawThenBlit(dst->config(),src->config(),
650 SkToBool(src->asTexture())));
651#endif
652 GrSurfaceDesc surfDesc;
653 surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
654 surfDesc.fWidth = srcRect.width();
655 surfDesc.fHeight = srcRect.height();
656 surfDesc.fConfig = dst->config();
657 surfDesc.fSampleCnt = 1;
658
659 id<MTLTexture> dstTex = GrGetMTLTextureFromSurface(dst, false);
660 MTLTextureDescriptor* textureDesc = GrGetMTLTextureDescriptor(dstTex);
661 textureDesc.width = srcRect.width();
662 textureDesc.height = srcRect.height();
663 textureDesc.mipmapLevelCount = 1;
664 textureDesc.usage |= MTLTextureUsageRenderTarget;
665
666 sk_sp<GrMtlTexture> transferTexture =
667 GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this,
668 SkBudgeted::kYes,
669 surfDesc,
670 textureDesc,
671 GrMipMapsStatus::kNotAllocated);
672
673 GrSurfaceOrigin transferOrigin = dstOrigin;
674 SkASSERT(this->mtlCaps().canCopyAsDraw(transferTexture->config(),
675 SkToBool(transferTexture->asRenderTarget()),
676 src->config(),
677 SkToBool(src->asTexture())));
678 // TODO: Eventually we will need to handle resolves either in this function or make a separate
679 // copySurfaceAsResolveThenBlit().
680 if (!this->copySurface(transferTexture.get(), transferOrigin,
681 src, srcOrigin,
682 srcRect, SkIPoint::Make(0, 0))) {
683 return false;
684 }
685
686 SkIRect transferRect = SkIRect::MakeXYWH(0, 0, srcRect.width(), srcRect.height());
687 SkASSERT(this->mtlCaps().canCopyAsBlit(dst->config(),
688 get_surface_sample_cnt(dst),
689 dstOrigin,
690 transferTexture->config(),
691 get_surface_sample_cnt(transferTexture.get()),
692 transferOrigin,
693 transferRect, dstPoint, false));
694 if (!this->copySurface(dst, dstOrigin,
695 transferTexture.get(), transferOrigin,
696 transferRect, dstPoint)) {
697 return false;
698 }
699 return true;
700}
701
Timothy Liange35055f2018-07-20 16:53:00 -0400702bool GrMtlGpu::onCopySurface(GrSurface* dst, GrSurfaceOrigin dstOrigin,
703 GrSurface* src, GrSurfaceOrigin srcOrigin,
704 const SkIRect& srcRect,
705 const SkIPoint& dstPoint,
706 bool canDiscardOutsideDstRect) {
707
708 GrPixelConfig dstConfig = dst->config();
709 GrPixelConfig srcConfig = src->config();
710
711 int dstSampleCnt = get_surface_sample_cnt(dst);
712 int srcSampleCnt = get_surface_sample_cnt(src);
713
714 if (dstSampleCnt > 1 || srcSampleCnt > 1) {
Timothy Liang70c787a2018-08-01 09:56:25 -0400715 SkASSERT(false); // Currently dont support MSAA. TODO: add copySurfaceAsResolve().
Timothy Liange35055f2018-07-20 16:53:00 -0400716 return false;
717 }
718
719 bool success = false;
Timothy Liange30739a2018-07-31 10:51:17 -0400720 if (this->mtlCaps().canCopyAsDraw(dst->config(), SkToBool(dst->asRenderTarget()),
721 src->config(), SkToBool(src->asTexture()))) {
722 success = fCopyManager.copySurfaceAsDraw(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint,
723 canDiscardOutsideDstRect);
724 } else if (this->mtlCaps().canCopyAsBlit(dstConfig, dstSampleCnt, dstOrigin,
725 srcConfig, srcSampleCnt, srcOrigin,
726 srcRect, dstPoint, dst == src)) {
Timothy Liange35055f2018-07-20 16:53:00 -0400727 success = this->copySurfaceAsBlit(dst, dstOrigin, src, srcOrigin, srcRect, dstPoint);
Timothy Liang70c787a2018-08-01 09:56:25 -0400728 } else if (this->mtlCaps().canCopyAsDrawThenBlit(dst->config(), src->config(),
729 SkToBool(src->asTexture()))) {
730 success = this->copySurfaceAsDrawThenBlit(dst, dstOrigin, src, srcOrigin,
731 srcRect, dstPoint);
Timothy Liange35055f2018-07-20 16:53:00 -0400732 }
733 if (success) {
734 SkIRect dstRect = SkIRect::MakeXYWH(dstPoint.x(), dstPoint.y(),
735 srcRect.width(), srcRect.height());
736 this->didWriteToSurface(dst, dstOrigin, &dstRect);
737 }
738 return success;
739}
740
Timothy Lianga8046af2018-07-19 09:58:00 -0400741bool GrMtlGpu::onWritePixels(GrSurface* surface, int left, int top, int width, int height,
742 GrColorType srcColorType, const GrMipLevel texels[],
743 int mipLevelCount) {
744 GrMtlTexture* mtlTexture = static_cast<GrMtlTexture*>(surface->asTexture());
745 if (!mtlTexture) {
746 return false;
747 }
748 if (!mipLevelCount) {
749 return false;
750 }
751#ifdef SK_DEBUG
752 for (int i = 0; i < mipLevelCount; i++) {
753 SkASSERT(texels[i].fPixels);
754 }
755#endif
756 return this->uploadToTexture(mtlTexture, left, top, width, height, srcColorType, texels,
757 mipLevelCount);
758}
759
Timothy Liangef21d7e2018-07-02 17:03:30 -0400760bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
761 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400762 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400763 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400764 return false;
765 }
766 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
767 return false;
768 }
769
Timothy Liange35055f2018-07-20 16:53:00 -0400770 bool doResolve = get_surface_sample_cnt(surface) > 1;
Timothy Liange30739a2018-07-31 10:51:17 -0400771 id<MTLTexture> mtlTexture = GrGetMTLTextureFromSurface(surface, doResolve);
Timothy Liangef21d7e2018-07-02 17:03:30 -0400772 if (!mtlTexture) {
773 return false;
774 }
775
776 int bpp = GrColorTypeBytesPerPixel(dstColorType);
777 size_t transBufferRowBytes = bpp * width;
778 size_t transBufferImageBytes = transBufferRowBytes * height;
779
780 // TODO: implement some way of reusing buffers instead of making a new one every time.
781 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
782 options: MTLResourceStorageModeShared];
783
784 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
785 [blitCmdEncoder copyFromTexture: mtlTexture
786 sourceSlice: 0
787 sourceLevel: 0
788 sourceOrigin: MTLOriginMake(left, top, 0)
789 sourceSize: MTLSizeMake(width, height, 1)
790 toBuffer: transferBuffer
791 destinationOffset: 0
792 destinationBytesPerRow: transBufferRowBytes
793 destinationBytesPerImage: transBufferImageBytes];
794 [blitCmdEncoder endEncoding];
795
796 this->submitCommandBuffer(kForce_SyncQueue);
797 const void* mappedMemory = transferBuffer.contents;
798
799 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
800 return true;
801}
Robert Phillips5b5d84c2018-08-09 15:12:18 -0400802