1 //
2 // Copyright 2019 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // mtl_common.h:
7 // Declares common constants, template classes, and mtl::Context - the MTLDevice container &
8 // error handler base class.
9 //
10
11 #ifndef LIBANGLE_RENDERER_METAL_MTL_COMMON_H_
12 #define LIBANGLE_RENDERER_METAL_MTL_COMMON_H_
13
14 #import <Metal/Metal.h>
15
16 #include <TargetConditionals.h>
17
18 #include <string>
19
20 #include "common/Optional.h"
21 #include "common/PackedEnums.h"
22 #include "common/angleutils.h"
23 #include "common/apple_platform_utils.h"
24 #include "libANGLE/Constants.h"
25 #include "libANGLE/ImageIndex.h"
26 #include "libANGLE/Version.h"
27 #include "libANGLE/angletypes.h"
28
29 #if TARGET_OS_IPHONE
30 # if !defined(__IPHONE_11_0)
31 # define __IPHONE_11_0 110000
32 # endif
33 # if !defined(ANGLE_IOS_DEPLOY_TARGET)
34 # define ANGLE_IOS_DEPLOY_TARGET __IPHONE_11_0
35 # endif
36 # if !defined(__IPHONE_OS_VERSION_MAX_ALLOWED)
37 # define __IPHONE_OS_VERSION_MAX_ALLOWED __IPHONE_11_0
38 # endif
39 # if !defined(__TV_OS_VERSION_MAX_ALLOWED)
40 # define __TV_OS_VERSION_MAX_ALLOWED __IPHONE_11_0
41 # endif
42 #endif
43
44 #if !defined(TARGET_OS_MACCATALYST)
45 # define TARGET_OS_MACCATALYST 0
46 #endif
47
48 #if defined(__ARM_ARCH)
49 # define ANGLE_MTL_ARM (__ARM_ARCH != 0)
50 #else
51 # define ANGLE_MTL_ARM 0
52 #endif
53
54 #define ANGLE_MTL_OBJC_SCOPE @autoreleasepool
55
56 #if !__has_feature(objc_arc)
57 # define ANGLE_MTL_AUTORELEASE autorelease
58 # define ANGLE_MTL_RETAIN retain
59 # define ANGLE_MTL_RELEASE release
60 #else
61 # define ANGLE_MTL_AUTORELEASE self
62 # define ANGLE_MTL_RETAIN self
63 # define ANGLE_MTL_RELEASE self
64 #endif
65
66 #define ANGLE_MTL_UNUSED __attribute__((unused))
67
68 #if defined(ANGLE_MTL_ENABLE_TRACE)
69 # define ANGLE_MTL_LOG(...) NSLog(@__VA_ARGS__)
70 #else
71 # define ANGLE_MTL_LOG(...) (void)0
72 #endif
73
74 namespace egl
75 {
76 class Display;
77 class Image;
78 class Surface;
79 } // namespace egl
80
81 #define ANGLE_GL_OBJECTS_X(PROC) \
82 PROC(Buffer) \
83 PROC(Context) \
84 PROC(Framebuffer) \
85 PROC(MemoryObject) \
86 PROC(Query) \
87 PROC(Program) \
88 PROC(Sampler) \
89 PROC(Semaphore) \
90 PROC(Texture) \
91 PROC(TransformFeedback) \
92 PROC(VertexArray)
93
94 #define ANGLE_PRE_DECLARE_OBJECT(OBJ) class OBJ;
95
96 namespace gl
97 {
98 ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_OBJECT)
99 } // namespace gl
100
101 #define ANGLE_PRE_DECLARE_MTL_OBJECT(OBJ) class OBJ##Mtl;
102
103 namespace rx
104 {
105 class DisplayMtl;
106 class ContextMtl;
107 class FramebufferMtl;
108 class BufferMtl;
109 class ImageMtl;
110 class VertexArrayMtl;
111 class TextureMtl;
112 class ProgramMtl;
113 class SamplerMtl;
114 class TransformFeedbackMtl;
115
ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_MTL_OBJECT)116 ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_MTL_OBJECT)
117
118 namespace mtl
119 {
120
121 // NOTE(hqle): support variable max number of vertex attributes
122 constexpr uint32_t kMaxVertexAttribs = gl::MAX_VERTEX_ATTRIBS;
123 // NOTE(hqle): support variable max number of render targets
124 constexpr uint32_t kMaxRenderTargets = 4;
125
126 constexpr uint32_t kMaxShaderUBOs = 12;
127 constexpr uint32_t kMaxUBOSize = 16384;
128
129 constexpr uint32_t kMaxShaderXFBs = gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS;
130
131 // The max size of a buffer that will be allocated in shared memory.
132 // NOTE(hqle): This is just a hint. There is no official document on what is the max allowed size
133 // for shared memory.
134 constexpr size_t kSharedMemBufferMaxBufSizeHint = 128 * 1024;
135
136 constexpr size_t kDefaultAttributeSize = 4 * sizeof(float);
137
138 // Metal limits
139 constexpr uint32_t kMaxShaderBuffers = 31;
140 constexpr uint32_t kMaxShaderSamplers = 16;
141 constexpr size_t kInlineConstDataMaxSize = 4 * 1024;
142 constexpr size_t kDefaultUniformsMaxSize = 4 * 1024;
143 constexpr uint32_t kMaxViewports = 1;
144
145 // Restrict in-flight resource usage to 400 MB.
146 // A render pass can use more than 400MB, but the command buffer
147 // will be flushed next time
148 constexpr const size_t kMaximumResidentMemorySizeInBytes = 400 * 1024 * 1024;
149
150 constexpr uint32_t kVertexAttribBufferStrideAlignment = 4;
151 // Alignment requirement for offset passed to setVertex|FragmentBuffer
152 #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
153 constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
154 #else
155 constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
156 #endif
157 constexpr uint32_t kIndexBufferOffsetAlignment = 4;
158 constexpr uint32_t kArgumentBufferOffsetAlignment = kUniformBufferSettingOffsetMinAlignment;
159 constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
160
161 // Front end binding limits
162 constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
163 constexpr uint32_t kMaxGLUBOBindings = 2 * kMaxShaderUBOs;
164
165 // Binding index start for vertex data buffers:
166 constexpr uint32_t kVboBindingIndexStart = 0;
167
168 // Binding index for default attribute buffer:
169 constexpr uint32_t kDefaultAttribsBindingIndex = kVboBindingIndexStart + kMaxVertexAttribs;
170 // Binding index for driver uniforms:
171 constexpr uint32_t kDriverUniformsBindingIndex = kDefaultAttribsBindingIndex + 1;
172 // Binding index for default uniforms:
173 constexpr uint32_t kDefaultUniformsBindingIndex = kDefaultAttribsBindingIndex + 3;
174 // Binding index for Transform Feedback Buffers (4)
175 constexpr uint32_t kTransformFeedbackBindingIndex = kDefaultUniformsBindingIndex + 1;
176 // Binding index for shadow samplers' compare modes
177 constexpr uint32_t kShadowSamplerCompareModesBindingIndex = kTransformFeedbackBindingIndex + 4;
178 // Binding index for UBO's argument buffer
179 constexpr uint32_t kUBOArgumentBufferBindingIndex = kShadowSamplerCompareModesBindingIndex + 1;
180
181 constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported
182
183 static const char *kUnassignedAttributeString = " __unassigned_attribute__";
184
185 // This special constant is used to indicate that a particular vertex descriptor's buffer layout
186 // index is unused.
187 constexpr MTLVertexStepFunction kVertexStepFunctionInvalid =
188 static_cast<MTLVertexStepFunction>(0xff);
189
190 constexpr int kEmulatedAlphaValue = 1;
191
192 constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
193
194 constexpr gl::Version kMaxSupportedGLVersion = gl::Version(3, 0);
195
196 // Work-around the enum is not available on macOS
197 #if (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MAX_ALLOWED < 110000)) || TARGET_OS_MACCATALYST
198 constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionNone;
199 #else
200 constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionRowLinearPVRTC;
201 #endif
202
203 #if defined(__IPHONE_13_0) || defined(__MAC_10_15)
204 # define ANGLE_MTL_SWIZZLE_AVAILABLE 1
205 using TextureSwizzleChannels = MTLTextureSwizzleChannels;
206 using RenderStages = MTLRenderStages;
207 constexpr MTLRenderStages kRenderStageVertex = MTLRenderStageVertex;
208 constexpr MTLRenderStages kRenderStageFragment = MTLRenderStageFragment;
209 #else
210 # define ANGLE_MTL_SWIZZLE_AVAILABLE 0
211 using TextureSwizzleChannels = int;
212 using RenderStages = int;
213 constexpr RenderStages kRenderStageVertex = 1;
214 constexpr RenderStages kRenderStageFragment = 2;
215 #endif
216
217 enum class PixelType
218 {
219 Int,
220 UInt,
221 Float,
222 EnumCount,
223 };
224
225 template <typename T>
226 struct ImplTypeHelper;
227
228 // clang-format off
229 #define ANGLE_IMPL_TYPE_HELPER_GL(OBJ) \
230 template<> \
231 struct ImplTypeHelper<gl::OBJ> \
232 { \
233 using ImplType = OBJ##Mtl; \
234 };
235 // clang-format on
236
237 ANGLE_GL_OBJECTS_X(ANGLE_IMPL_TYPE_HELPER_GL)
238
239 template <>
240 struct ImplTypeHelper<egl::Display>
241 {
242 using ImplType = DisplayMtl;
243 };
244
245 template <>
246 struct ImplTypeHelper<egl::Image>
247 {
248 using ImplType = ImageMtl;
249 };
250
251 template <typename T>
252 using GetImplType = typename ImplTypeHelper<T>::ImplType;
253
254 template <typename T>
255 GetImplType<T> *GetImpl(const T *glObject)
256 {
257 return GetImplAs<GetImplType<T>>(glObject);
258 }
259
260 // This class wraps Objective-C pointer inside, it will manage the lifetime of
261 // the Objective-C pointer. Changing pointer is not supported outside subclass.
262 template <typename T>
263 class WrappedObject
264 {
265 public:
266 WrappedObject() = default;
267 ~WrappedObject() { release(); }
268
269 bool valid() const { return (mMetalObject != nil); }
270
271 T get() const { return mMetalObject; }
272 inline void reset() { release(); }
273
274 operator T() const { return get(); }
275
276 protected:
277 inline void set(T obj) { retainAssign(obj); }
278
279 void retainAssign(T obj)
280 {
281
282 #if !__has_feature(objc_arc)
283 T retained = obj;
284 [retained retain];
285 #endif
286 release();
287 mMetalObject = obj;
288 }
289
290 void unretainAssign(T obj)
291 {
292 release();
293 mMetalObject = obj;
294 }
295
296 private:
297 void release()
298 {
299 #if !__has_feature(objc_arc)
300 [mMetalObject release];
301 #endif
302 mMetalObject = nil;
303 }
304
305 T mMetalObject = nil;
306 };
307
308 // Because ARC enablement is a compile-time choice, and we compile this header
309 // both ways, we need a separate copy of our code when ARC is enabled.
310 #if __has_feature(objc_arc)
311 # define adoptObjCObj adoptObjCObjArc
312 #endif
313 template <typename T>
314 class AutoObjCPtr;
315 template <typename T>
316 using AutoObjCObj = AutoObjCPtr<T *>;
317 template <typename U>
318 AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT) __attribute__((__warn_unused_result__));
319
320 // This class is similar to WrappedObject, however, it allows changing the
321 // internal pointer with public methods.
322 template <typename T>
323 class AutoObjCPtr : public WrappedObject<T>
324 {
325 public:
326 using ParentType = WrappedObject<T>;
327
328 AutoObjCPtr() {}
329
330 AutoObjCPtr(const std::nullptr_t &theNull) {}
331
332 AutoObjCPtr(const AutoObjCPtr &src) { this->retainAssign(src.get()); }
333
334 AutoObjCPtr(AutoObjCPtr &&src) { this->transfer(std::forward<AutoObjCPtr>(src)); }
335
336 // Take ownership of the pointer
337 AutoObjCPtr(T &&src)
338 {
339 this->retainAssign(src);
340 src = nil;
341 }
342
343 AutoObjCPtr &operator=(const AutoObjCPtr &src)
344 {
345 this->retainAssign(src.get());
346 return *this;
347 }
348
349 AutoObjCPtr &operator=(AutoObjCPtr &&src)
350 {
351 this->transfer(std::forward<AutoObjCPtr>(src));
352 return *this;
353 }
354
355 // Take ownership of the pointer
356 AutoObjCPtr &operator=(T &&src)
357 {
358 this->retainAssign(src);
359 src = nil;
360 return *this;
361 }
362
363 AutoObjCPtr &operator=(std::nullptr_t theNull)
364 {
365 this->set(nil);
366 return *this;
367 }
368
369 bool operator==(const AutoObjCPtr &rhs) const { return (*this) == rhs.get(); }
370
371 bool operator==(T rhs) const { return this->get() == rhs; }
372
373 bool operator==(std::nullptr_t theNull) const { return this->get() == nullptr; }
374
375 bool operator!=(std::nullptr_t) const { return this->get() != nullptr; }
376
377 inline operator bool() { return this->get(); }
378
379 bool operator!=(const AutoObjCPtr &rhs) const { return (*this) != rhs.get(); }
380
381 bool operator!=(T rhs) const { return this->get() != rhs; }
382
383 using ParentType::retainAssign;
384
385 template <typename U>
386 friend AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT)
387 __attribute__((__warn_unused_result__));
388
389 private:
390 enum AdoptTag
391 {
392 Adopt
393 };
394 AutoObjCPtr(T src, AdoptTag) { this->unretainAssign(src); }
395
396 void transfer(AutoObjCPtr &&src)
397 {
398 this->retainAssign(std::move(src.get()));
399 src.reset();
400 }
401 };
402
403 template <typename U>
404 inline AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT src)
405 {
406 #if __has_feature(objc_arc)
407 return src;
408 #elif defined(OBJC_NO_GC)
409 return AutoObjCPtr<U *>(src, AutoObjCPtr<U *>::Adopt);
410 #else
411 # error "ObjC GC not supported."
412 #endif
413 }
414
415 // NOTE: SharedEvent is only declared on iOS 12.0+ or mac 10.14+
416 #if defined(__IPHONE_12_0) || defined(__MAC_10_14)
417 # define ANGLE_MTL_EVENT_AVAILABLE 1
418 using SharedEventRef = AutoObjCPtr<id<MTLSharedEvent>>;
419 #else
420 # define ANGLE_MTL_EVENT_AVAILABLE 0
421 using SharedEventRef = AutoObjCObj<NSObject>;
422 #endif
423
424 // The native image index used by Metal back-end, the image index uses native mipmap level instead
425 // of "virtual" level modified by OpenGL's base level.
426 using MipmapNativeLevel = gl::LevelIndexWrapper<uint32_t>;
427
428 constexpr MipmapNativeLevel kZeroNativeMipLevel(0);
429
430 class ImageNativeIndexIterator;
431
432 class ImageNativeIndex final
433 {
434 public:
435 ImageNativeIndex() = delete;
436 ImageNativeIndex(const gl::ImageIndex &src, GLint baseLevel)
437 {
438 mNativeIndex = gl::ImageIndex::MakeFromType(src.getType(), src.getLevelIndex() - baseLevel,
439 src.getLayerIndex(), src.getLayerCount());
440 }
441
442 static ImageNativeIndex FromBaseZeroGLIndex(const gl::ImageIndex &src)
443 {
444 return ImageNativeIndex(src, 0);
445 }
446
447 MipmapNativeLevel getNativeLevel() const
448 {
449 return MipmapNativeLevel(mNativeIndex.getLevelIndex());
450 }
451
452 gl::TextureType getType() const { return mNativeIndex.getType(); }
453 GLint getLayerIndex() const { return mNativeIndex.getLayerIndex(); }
454 GLint getLayerCount() const { return mNativeIndex.getLayerCount(); }
455 GLint cubeMapFaceIndex() const { return mNativeIndex.cubeMapFaceIndex(); }
456
457 bool isLayered() const { return mNativeIndex.isLayered(); }
458 bool hasLayer() const { return mNativeIndex.hasLayer(); }
459 bool has3DLayer() const { return mNativeIndex.has3DLayer(); }
460 bool usesTex3D() const { return mNativeIndex.usesTex3D(); }
461
462 bool valid() const { return mNativeIndex.valid(); }
463
464 ImageNativeIndexIterator getLayerIterator(GLint layerCount) const;
465
466 private:
467 gl::ImageIndex mNativeIndex;
468 };
469
470 class ImageNativeIndexIterator final
471 {
472 public:
473 ImageNativeIndex next() { return ImageNativeIndex(mNativeIndexIte.next(), 0); }
474 ImageNativeIndex current() const { return ImageNativeIndex(mNativeIndexIte.current(), 0); }
475 bool hasNext() const { return mNativeIndexIte.hasNext(); }
476
477 private:
478 // This class is only constructable from ImageNativeIndex
479 friend class ImageNativeIndex;
480
481 explicit ImageNativeIndexIterator(const gl::ImageIndexIterator &baseZeroSrc)
482 : mNativeIndexIte(baseZeroSrc)
483 {}
484
485 gl::ImageIndexIterator mNativeIndexIte;
486 };
487
488 using ClearColorValueBytes = std::array<uint8_t, 4 * sizeof(float)>;
489
490 class ClearColorValue
491 {
492 public:
493 constexpr ClearColorValue()
494 : mType(PixelType::Float), mRedF(0), mGreenF(0), mBlueF(0), mAlphaF(0)
495 {}
496 constexpr ClearColorValue(float r, float g, float b, float a)
497 : mType(PixelType::Float), mRedF(r), mGreenF(g), mBlueF(b), mAlphaF(a)
498 {}
499 constexpr ClearColorValue(int32_t r, int32_t g, int32_t b, int32_t a)
500 : mType(PixelType::Int), mRedI(r), mGreenI(g), mBlueI(b), mAlphaI(a)
501 {}
502 constexpr ClearColorValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
503 : mType(PixelType::UInt), mRedU(r), mGreenU(g), mBlueU(b), mAlphaU(a)
504 {}
505 constexpr ClearColorValue(const ClearColorValue &src)
506 : mType(src.mType), mValueBytes(src.mValueBytes)
507 {}
508
509 MTLClearColor toMTLClearColor() const;
510
511 PixelType getType() const { return mType; }
512
513 const ClearColorValueBytes &getValueBytes() const { return mValueBytes; }
514
515 ClearColorValue &operator=(const ClearColorValue &src);
516
517 void setAsFloat(float r, float g, float b, float a);
518 void setAsInt(int32_t r, int32_t g, int32_t b, int32_t a);
519 void setAsUInt(uint32_t r, uint32_t g, uint32_t b, uint32_t a);
520
521 private:
522 PixelType mType;
523
524 union
525 {
526 struct
527 {
528 float mRedF, mGreenF, mBlueF, mAlphaF;
529 };
530 struct
531 {
532 int32_t mRedI, mGreenI, mBlueI, mAlphaI;
533 };
534 struct
535 {
536 uint32_t mRedU, mGreenU, mBlueU, mAlphaU;
537 };
538
539 ClearColorValueBytes mValueBytes;
540 };
541 };
542
543 class CommandQueue;
544 class ErrorHandler
545 {
546 public:
547 virtual ~ErrorHandler() {}
548
549 virtual void handleError(GLenum error,
550 const char *file,
551 const char *function,
552 unsigned int line) = 0;
553
554 virtual void handleError(NSError *error,
555 const char *file,
556 const char *function,
557 unsigned int line) = 0;
558 };
559
560 class Context : public ErrorHandler
561 {
562 public:
563 Context(DisplayMtl *displayMtl);
564 mtl::CommandQueue &cmdQueue();
565
566 DisplayMtl *getDisplay() const { return mDisplay; }
567
568 protected:
569 DisplayMtl *mDisplay;
570 };
571
572 #define ANGLE_MTL_CHECK(context, test, error) \
573 do \
574 { \
575 if (ANGLE_UNLIKELY(!(test))) \
576 { \
577 context->handleError(error, __FILE__, ANGLE_FUNCTION, __LINE__); \
578 return angle::Result::Stop; \
579 } \
580 } while (0)
581
582 #define ANGLE_MTL_TRY(context, test) ANGLE_MTL_CHECK(context, test, GL_INVALID_OPERATION)
583
584 #define ANGLE_MTL_UNREACHABLE(context) \
585 UNREACHABLE(); \
586 ANGLE_MTL_TRY(context, false)
587
588 } // namespace mtl
589 } // namespace rx
590
591 #endif /* LIBANGLE_RENDERER_METAL_MTL_COMMON_H_ */
592