blob: 41ed23e3a543f9ca3dfe870a5fcfd882d4dd39e2 [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
Greg Daniel4a081e22017-08-04 09:34:44 -040010#include "GrMtlTexture.h"
Timothy Liange886e802018-07-02 16:03:28 -040011#include "GrMtlTextureRenderTarget.h"
12#include "GrMtlUtil.h"
Timothy Liangff19c8f2018-07-11 13:27:21 -040013#include "GrTexturePriv.h"
Timothy Liangef21d7e2018-07-02 17:03:30 -040014#include "SkConvertPixels.h"
Greg Daniel4a081e22017-08-04 09:34:44 -040015
Greg Daniel6b7e0e22017-07-12 16:21:09 -040016#if !__has_feature(objc_arc)
17#error This file must be compiled with Arc. Use -fobjc-arc flag
18#endif
19
Greg Danielcebcb842017-07-31 10:45:52 -040020static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
21 // Mac OSX
22#ifdef SK_BUILD_FOR_MAC
23 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v2]) {
24 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v2;
25 return true;
26 }
27 if ([device supportsFeatureSet:MTLFeatureSet_OSX_GPUFamily1_v1]) {
28 *featureSet = MTLFeatureSet_OSX_GPUFamily1_v1;
29 return true;
30 }
31#endif
32
33 // iOS Family group 3
34#ifdef SK_BUILD_FOR_IOS
35 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]) {
36 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v2;
37 return true;
38 }
39 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
40 *featureSet = MTLFeatureSet_iOS_GPUFamily3_v1;
41 return true;
42 }
43
44 // iOS Family group 2
45 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v3]) {
46 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v3;
47 return true;
48 }
49 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v2]) {
50 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v2;
51 return true;
52 }
53 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1]) {
54 *featureSet = MTLFeatureSet_iOS_GPUFamily2_v1;
55 return true;
56 }
57
58 // iOS Family group 1
59 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v3]) {
60 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v3;
61 return true;
62 }
63 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v2]) {
64 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v2;
65 return true;
66 }
67 if ([device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily1_v1]) {
68 *featureSet = MTLFeatureSet_iOS_GPUFamily1_v1;
69 return true;
70 }
71#endif
72 // No supported feature sets were found
73 return false;
74}
75
Brian Salomon384fab42017-12-07 12:33:05 -050076sk_sp<GrGpu> GrMtlGpu::Make(GrContext* context, const GrContextOptions& options,
77 id<MTLDevice> device, id<MTLCommandQueue> queue) {
Greg Danielb76a72a2017-07-13 15:07:54 -040078 if (!device || !queue) {
79 return nullptr;
80 }
Greg Danielcebcb842017-07-31 10:45:52 -040081 MTLFeatureSet featureSet;
82 if (!get_feature_set(device, &featureSet)) {
83 return nullptr;
84 }
Brian Salomon384fab42017-12-07 12:33:05 -050085 return sk_sp<GrGpu>(new GrMtlGpu(context, options, device, queue, featureSet));
Greg Daniele5ddff52017-07-05 16:49:36 -040086}
87
Greg Danielb76a72a2017-07-13 15:07:54 -040088GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
Greg Danielcebcb842017-07-31 10:45:52 -040089 id<MTLDevice> device, id<MTLCommandQueue> queue, MTLFeatureSet featureSet)
Greg Danielb76a72a2017-07-13 15:07:54 -040090 : INHERITED(context)
91 , fDevice(device)
92 , fQueue(queue) {
Greg Danielcebcb842017-07-31 10:45:52 -040093
94 fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
95 fCaps = fMtlCaps;
96
Timothy Liangef21d7e2018-07-02 17:03:30 -040097 fCmdBuffer = [fQueue commandBuffer];
Timothy Lianga80a0122018-07-13 19:19:36 +000098
99 MTLTextureDescriptor* txDesc = [[MTLTextureDescriptor alloc] init];
100 txDesc.textureType = MTLTextureType3D;
101 txDesc.height = 64;
102 txDesc.width = 64;
103 txDesc.depth = 64;
104 txDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;
105 txDesc.arrayLength = 1;
106 txDesc.mipmapLevelCount = 1;
107 id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor:txDesc];
108 // To get ride of unused var warning
109 int width = [testTexture width];
110 SkDebugf("width: %d\n", width);
111 // Unused queue warning fix
112 SkDebugf("ptr to queue: %p\n", fQueue);
Greg Danielb76a72a2017-07-13 15:07:54 -0400113}
Greg Daniel4a081e22017-08-04 09:34:44 -0400114
Timothy Liangef21d7e2018-07-02 17:03:30 -0400115void GrMtlGpu::submitCommandBuffer(SyncQueue sync) {
116 SkASSERT(fCmdBuffer);
117 [fCmdBuffer commit];
118 if (SyncQueue::kForce_SyncQueue == sync) {
119 [fCmdBuffer waitUntilCompleted];
120 }
121 fCmdBuffer = [fQueue commandBuffer];
122}
123
Timothy Liangff19c8f2018-07-11 13:27:21 -0400124static bool check_max_blit_width(int widthInPixels) {
125 if (widthInPixels > 32767) {
126 SkASSERT(false); // surfaces should not be this wide anyway
127 return false;
128 }
129 return true;
130}
131
132bool GrMtlGpu::uploadToTexture(GrMtlTexture* tex, int left, int top, int width, int height,
133 GrColorType dataColorType, const GrMipLevel texels[],
134 int mipLevelCount) {
135 SkASSERT(this->caps()->isConfigTexturable(tex->config()));
136 if (!check_max_blit_width(width)) {
137 return false;
138 }
139 if (width == 0 || height == 0) {
140 return false;
141 }
142 if (GrPixelConfigToColorType(tex->config()) != dataColorType) {
143 return false;
144 }
145
146 id<MTLTexture> mtlTexture = tex->mtlTexture();
147 SkASSERT(mtlTexture);
148 // Either upload only the first miplevel or all miplevels
149 SkASSERT(1 == mipLevelCount || mipLevelCount == (int)mtlTexture.mipmapLevelCount);
150
151 MTLTextureDescriptor* transferDesc = GrGetMTLTextureDescriptor(mtlTexture);
152 transferDesc.mipmapLevelCount = mipLevelCount;
153 transferDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
154 transferDesc.storageMode = MTLStorageModeManaged;
155 // TODO: implement some way of reusing transfer textures
156 id<MTLTexture> transferTexture = [fDevice newTextureWithDescriptor:transferDesc];
157 SkASSERT(transferTexture);
158
159 int currentWidth = width;
160 int currentHeight = height;
161 size_t bpp = GrColorTypeBytesPerPixel(dataColorType);
162 MTLOrigin origin = MTLOriginMake(left, top, 0);
163
164 SkASSERT(mtlTexture.pixelFormat == transferTexture.pixelFormat);
165 SkASSERT(mtlTexture.sampleCount == transferTexture.sampleCount);
166
167 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
168 for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
169 size_t rowBytes = texels[currentMipLevel].fRowBytes ? texels[currentMipLevel].fRowBytes
170 : bpp * currentWidth;
171 SkASSERT(texels[currentMipLevel].fPixels);
172 if (rowBytes < bpp * currentWidth || rowBytes % bpp) {
173 return false;
174 }
175 [transferTexture replaceRegion: MTLRegionMake2D(left, top, width, height)
176 mipmapLevel: currentMipLevel
177 withBytes: texels[currentMipLevel].fPixels
178 bytesPerRow: rowBytes];
179
180 [blitCmdEncoder copyFromTexture: transferTexture
181 sourceSlice: 0
182 sourceLevel: currentMipLevel
183 sourceOrigin: origin
184 sourceSize: MTLSizeMake(width, height, 1)
185 toTexture: mtlTexture
186 destinationSlice: 0
187 destinationLevel: currentMipLevel
188 destinationOrigin: origin];
189 currentWidth = SkTMax(1, currentWidth/2);
190 currentHeight = SkTMax(1, currentHeight/2);
191 }
192 [blitCmdEncoder endEncoding];
193
194 if (mipLevelCount < (int) tex->mtlTexture().mipmapLevelCount) {
195 tex->texturePriv().markMipMapsDirty();
196 }
197 return true;
198}
199
Greg Daniel4a081e22017-08-04 09:34:44 -0400200sk_sp<GrTexture> GrMtlGpu::onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
Brian Salomon58389b92018-03-07 13:01:25 -0500201 const GrMipLevel texels[], int mipLevelCount) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400202 int mipLevels = !mipLevelCount ? 1 : mipLevelCount;
203
204 if (!fMtlCaps->isConfigTexturable(desc.fConfig)) {
205 return nullptr;
206 }
Timothy Liang58f153d2018-07-02 17:36:20 -0400207 MTLPixelFormat format;
208 if (!GrPixelConfigToMTLFormat(desc.fConfig, &format)) {
209 return nullptr;
210 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400211
212 bool renderTarget = SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag);
Greg Daniel4a081e22017-08-04 09:34:44 -0400213
Timothy Liang58f153d2018-07-02 17:36:20 -0400214 // This TexDesc refers to the texture that will be read by the client. Thus even if msaa is
215 // requested, this TexDesc describes the resolved texture. Therefore we always have samples set
216 // to 1.
217 MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init];
218 texDesc.textureType = MTLTextureType2D;
219 texDesc.pixelFormat = format;
220 texDesc.width = desc.fWidth;
221 texDesc.height = desc.fHeight;
222 texDesc.depth = 1;
223 texDesc.mipmapLevelCount = mipLevels;
224 texDesc.sampleCount = 1;
225 texDesc.arrayLength = 1;
226 texDesc.cpuCacheMode = MTLCPUCacheModeWriteCombined;
227 // Make all textures have private gpu only access. We can use transfer buffers or textures
228 // to copy to them.
229 texDesc.storageMode = MTLStorageModePrivate;
Timothy Liangff19c8f2018-07-11 13:27:21 -0400230 texDesc.usage = MTLTextureUsageShaderRead;
231 texDesc.usage |= renderTarget ? MTLTextureUsageRenderTarget : 0;
Timothy Liang58f153d2018-07-02 17:36:20 -0400232
233 GrMipMapsStatus mipMapsStatus = GrMipMapsStatus::kNotAllocated;
234 if (mipLevels > 1) {
Timothy Liangff19c8f2018-07-11 13:27:21 -0400235 mipMapsStatus = texels[0].fPixels ? GrMipMapsStatus::kValid : GrMipMapsStatus::kDirty;
236#ifdef SK_DEBUG
237 for (int i = 1; i < mipLevels; ++i) {
238 if (mipMapsStatus == GrMipMapsStatus::kValid) {
239 SkASSERT(texels[i].fPixels);
240 } else {
241 SkASSERT(!texels[i].fPixels);
Timothy Liang58f153d2018-07-02 17:36:20 -0400242 }
243 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400244#endif
Timothy Liang58f153d2018-07-02 17:36:20 -0400245 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400246 sk_sp<GrMtlTexture> tex;
247 if (renderTarget) {
Greg Daniel4a081e22017-08-04 09:34:44 -0400248 tex = GrMtlTextureRenderTarget::CreateNewTextureRenderTarget(this, budgeted,
Timothy Liang58f153d2018-07-02 17:36:20 -0400249 desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400250 } else {
Timothy Liang58f153d2018-07-02 17:36:20 -0400251 tex = GrMtlTexture::CreateNewTexture(this, budgeted, desc, texDesc, mipMapsStatus);
Greg Daniel4a081e22017-08-04 09:34:44 -0400252 }
253
254 if (!tex) {
255 return nullptr;
256 }
257
Timothy Liangff19c8f2018-07-11 13:27:21 -0400258 auto colorType = GrPixelConfigToColorType(desc.fConfig);
259 if (mipLevelCount && texels[0].fPixels) {
260 if (!this->uploadToTexture(tex.get(), 0, 0, desc.fWidth, desc.fHeight, colorType, texels,
261 mipLevelCount)) {
262 tex->unref();
263 return nullptr;
264 }
Greg Daniel4a081e22017-08-04 09:34:44 -0400265 }
266
267 if (desc.fFlags & kPerformInitialClear_GrSurfaceFlag) {
268 // Do initial clear of the texture
269 }
Timothy Liangff19c8f2018-07-11 13:27:21 -0400270 return std::move(tex);
Greg Daniel4a081e22017-08-04 09:34:44 -0400271}
Timothy Liange886e802018-07-02 16:03:28 -0400272
273static id<MTLTexture> get_texture_from_backend(const GrBackendTexture& backendTex,
274 GrWrapOwnership ownership) {
275 GrMtlTextureInfo textureInfo;
276 if (!backendTex.getMtlTextureInfo(&textureInfo)) {
277 return nil;
278 }
279 return GrGetMTLTexture(textureInfo.fTexture, ownership);
280}
281
282static id<MTLTexture> get_texture_from_backend(const GrBackendRenderTarget& backendRT) {
283 GrMtlTextureInfo textureInfo;
284 if (!backendRT.getMtlTextureInfo(&textureInfo)) {
285 return nil;
286 }
287 return GrGetMTLTexture(textureInfo.fTexture, GrWrapOwnership::kBorrow_GrWrapOwnership);
288}
289
290static inline void init_surface_desc(GrSurfaceDesc* surfaceDesc, id<MTLTexture> mtlTexture,
291 bool isRenderTarget, GrPixelConfig config) {
292 if (isRenderTarget) {
293 SkASSERT(MTLTextureUsageRenderTarget & mtlTexture.usage);
294 }
295 surfaceDesc->fFlags = isRenderTarget ? kRenderTarget_GrSurfaceFlag : kNone_GrSurfaceFlags;
296 surfaceDesc->fWidth = mtlTexture.width;
297 surfaceDesc->fHeight = mtlTexture.height;
298 surfaceDesc->fConfig = config;
299 surfaceDesc->fSampleCnt = 1;
300}
301
302sk_sp<GrTexture> GrMtlGpu::onWrapBackendTexture(const GrBackendTexture& backendTex,
303 GrWrapOwnership ownership) {
304 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex, ownership);
305 if (!mtlTexture) {
306 return nullptr;
307 }
308
309 GrSurfaceDesc surfDesc;
310 init_surface_desc(&surfDesc, mtlTexture, false, backendTex.config());
311
312 return GrMtlTexture::MakeWrappedTexture(this, surfDesc, mtlTexture);
313}
314
315sk_sp<GrTexture> GrMtlGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex,
316 int sampleCnt,
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, true, backendTex.config());
325 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
326 if (!surfDesc.fSampleCnt) {
327 return nullptr;
328 }
329
330 return GrMtlTextureRenderTarget::MakeWrappedTextureRenderTarget(this, surfDesc, mtlTexture);
331}
332
333sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendRenderTarget(const GrBackendRenderTarget& backendRT) {
334 // TODO: Revisit this when the Metal backend is completed. It may support MSAA render targets.
335 if (backendRT.sampleCnt() > 1) {
336 return nullptr;
337 }
338 id<MTLTexture> mtlTexture = get_texture_from_backend(backendRT);
339 if (!mtlTexture) {
340 return nullptr;
341 }
342
343 GrSurfaceDesc surfDesc;
344 init_surface_desc(&surfDesc, mtlTexture, true, backendRT.config());
345
346 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
347}
348
349sk_sp<GrRenderTarget> GrMtlGpu::onWrapBackendTextureAsRenderTarget(
350 const GrBackendTexture& backendTex, int sampleCnt) {
351 id<MTLTexture> mtlTexture = get_texture_from_backend(backendTex,
352 GrWrapOwnership::kBorrow_GrWrapOwnership);
353 if (!mtlTexture) {
354 return nullptr;
355 }
356
357 GrSurfaceDesc surfDesc;
358 init_surface_desc(&surfDesc, mtlTexture, true, backendTex.config());
359 surfDesc.fSampleCnt = this->caps()->getRenderTargetSampleCount(sampleCnt, surfDesc.fConfig);
360 if (!surfDesc.fSampleCnt) {
361 return nullptr;
362 }
363
364 return GrMtlRenderTarget::MakeWrappedRenderTarget(this, surfDesc, mtlTexture);
365}
366
Timothy Liangef21d7e2018-07-02 17:03:30 -0400367bool GrMtlGpu::onReadPixels(GrSurface* surface, int left, int top, int width, int height,
368 GrColorType dstColorType, void* buffer, size_t rowBytes) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400369 SkASSERT(surface);
Timothy Liangff19c8f2018-07-11 13:27:21 -0400370 if (!check_max_blit_width(width)) {
Timothy Liangef21d7e2018-07-02 17:03:30 -0400371 return false;
372 }
373 if (GrPixelConfigToColorType(surface->config()) != dstColorType) {
374 return false;
375 }
376
377 id<MTLTexture> mtlTexture = nil;
378 GrMtlRenderTarget* renderTarget = static_cast<GrMtlRenderTarget*>(surface->asRenderTarget());
379 GrMtlTexture* texture;
380 if (renderTarget) {
381 // TODO: Handle resolving rt here when MSAA is supported. Right now we just grab the render
382 // texture since we cannot currently have a multi-sampled rt.
383 mtlTexture = renderTarget->mtlRenderTexture();
384 } else {
385 texture = static_cast<GrMtlTexture*>(surface->asTexture());
386 if (texture) {
387 mtlTexture = texture->mtlTexture();
388 }
389 }
390
391 if (!mtlTexture) {
392 return false;
393 }
394
395 int bpp = GrColorTypeBytesPerPixel(dstColorType);
396 size_t transBufferRowBytes = bpp * width;
397 size_t transBufferImageBytes = transBufferRowBytes * height;
398
399 // TODO: implement some way of reusing buffers instead of making a new one every time.
400 id<MTLBuffer> transferBuffer = [fDevice newBufferWithLength: transBufferImageBytes
401 options: MTLResourceStorageModeShared];
402
403 id<MTLBlitCommandEncoder> blitCmdEncoder = [fCmdBuffer blitCommandEncoder];
404 [blitCmdEncoder copyFromTexture: mtlTexture
405 sourceSlice: 0
406 sourceLevel: 0
407 sourceOrigin: MTLOriginMake(left, top, 0)
408 sourceSize: MTLSizeMake(width, height, 1)
409 toBuffer: transferBuffer
410 destinationOffset: 0
411 destinationBytesPerRow: transBufferRowBytes
412 destinationBytesPerImage: transBufferImageBytes];
413 [blitCmdEncoder endEncoding];
414
415 this->submitCommandBuffer(kForce_SyncQueue);
416 const void* mappedMemory = transferBuffer.contents;
417
418 SkRectMemcpy(buffer, rowBytes, mappedMemory, transBufferRowBytes, transBufferRowBytes, height);
419 return true;
420}