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 constexpr uint32_t kVertexAttribBufferStrideAlignment = 4;
146 // Alignment requirement for offset passed to setVertex|FragmentBuffer
147 #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
148 constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256;
149 #else
150 constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4;
151 #endif
152 constexpr uint32_t kIndexBufferOffsetAlignment = 4;
153 constexpr uint32_t kArgumentBufferOffsetAlignment = kUniformBufferSettingOffsetMinAlignment;
154 constexpr uint32_t kTextureToBufferBlittingAlignment = 256;
155
156 // Front end binding limits
157 constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers;
158 constexpr uint32_t kMaxGLUBOBindings = 2 * kMaxShaderUBOs;
159
160 // Binding index start for vertex data buffers:
161 constexpr uint32_t kVboBindingIndexStart = 0;
162
163 // Binding index for default attribute buffer:
164 constexpr uint32_t kDefaultAttribsBindingIndex = kVboBindingIndexStart + kMaxVertexAttribs;
165 // Binding index for driver uniforms:
166 constexpr uint32_t kDriverUniformsBindingIndex = kDefaultAttribsBindingIndex + 1;
167 // Binding index for default uniforms:
168 constexpr uint32_t kDefaultUniformsBindingIndex = kDefaultAttribsBindingIndex + 3;
169 // Binding index for Transform Feedback Buffers (4)
170 constexpr uint32_t kTransformFeedbackBindingIndex = kDefaultUniformsBindingIndex + 1;
171 // Binding index for shadow samplers' compare modes
172 constexpr uint32_t kShadowSamplerCompareModesBindingIndex = kTransformFeedbackBindingIndex + 4;
173 // Binding index for UBO's argument buffer
174 constexpr uint32_t kUBOArgumentBufferBindingIndex = kShadowSamplerCompareModesBindingIndex + 1;
175
176 constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported
177
178 static const char *kUnassignedAttributeString = " __unassigned_attribute__";
179
180 // This special constant is used to indicate that a particular vertex descriptor's buffer layout
181 // index is unused.
182 constexpr MTLVertexStepFunction kVertexStepFunctionInvalid =
183 static_cast<MTLVertexStepFunction>(0xff);
184
185 constexpr int kEmulatedAlphaValue = 1;
186
187 constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t);
188
189 constexpr gl::Version kMaxSupportedGLVersion = gl::Version(3, 0);
190
191 // Work-around the enum is not available on macOS
192 #if (TARGET_OS_OSX && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101600)) || TARGET_OS_MACCATALYST
193 constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionNone;
194 #else
195 constexpr MTLBlitOption kBlitOptionRowLinearPVRTC = MTLBlitOptionRowLinearPVRTC;
196 #endif
197
198 #if defined(__IPHONE_13_0) || defined(__MAC_10_15)
199 # define ANGLE_MTL_SWIZZLE_AVAILABLE 1
200 using TextureSwizzleChannels = MTLTextureSwizzleChannels;
201 using RenderStages = MTLRenderStages;
202 constexpr MTLRenderStages kRenderStageVertex = MTLRenderStageVertex;
203 constexpr MTLRenderStages kRenderStageFragment = MTLRenderStageFragment;
204 #else
205 # define ANGLE_MTL_SWIZZLE_AVAILABLE 0
206 using TextureSwizzleChannels = int;
207 using RenderStages = int;
208 constexpr RenderStages kRenderStageVertex = 1;
209 constexpr RenderStages kRenderStageFragment = 2;
210 #endif
211
212 enum class PixelType
213 {
214 Int,
215 UInt,
216 Float,
217 EnumCount,
218 };
219
220 template <typename T>
221 struct ImplTypeHelper;
222
223 // clang-format off
224 #define ANGLE_IMPL_TYPE_HELPER_GL(OBJ) \
225 template<> \
226 struct ImplTypeHelper<gl::OBJ> \
227 { \
228 using ImplType = OBJ##Mtl; \
229 };
230 // clang-format on
231
232 ANGLE_GL_OBJECTS_X(ANGLE_IMPL_TYPE_HELPER_GL)
233
234 template <>
235 struct ImplTypeHelper<egl::Display>
236 {
237 using ImplType = DisplayMtl;
238 };
239
240 template <>
241 struct ImplTypeHelper<egl::Image>
242 {
243 using ImplType = ImageMtl;
244 };
245
246 template <typename T>
247 using GetImplType = typename ImplTypeHelper<T>::ImplType;
248
249 template <typename T>
250 GetImplType<T> *GetImpl(const T *_Nonnull glObject)
251 {
252 return GetImplAs<GetImplType<T>>(glObject);
253 }
254
255 // This class wraps Objective-C pointer inside, it will manage the lifetime of
256 // the Objective-C pointer. Changing pointer is not supported outside subclass.
257 template <typename T>
258 class WrappedObject
259 {
260 public:
261 WrappedObject() = default;
262 ~WrappedObject() { release(); }
263
264 bool valid() const { return (mMetalObject != nil); }
265
266 T get() const { return mMetalObject; }
267 inline void reset() { release(); }
268
269 operator T() const { return get(); }
270
271 protected:
272 inline void set(T obj) { retainAssign(obj); }
273
274 void retainAssign(T obj)
275 {
276
277 #if !__has_feature(objc_arc)
278 T retained = obj;
279 [retained retain];
280 #endif
281 release();
282 mMetalObject = obj;
283 }
284
285 void unretainAssign(T obj)
286 {
287 release();
288 mMetalObject = obj;
289 }
290
291 private:
292 void release()
293 {
294 #if !__has_feature(objc_arc)
295 [mMetalObject release];
296 #endif
297 mMetalObject = nil;
298 }
299
300 T mMetalObject = nil;
301 };
302
303 // Because ARC enablement is a compile-time choice, and we compile this header
304 // both ways, we need a separate copy of our code when ARC is enabled.
305 #if __has_feature(objc_arc)
306 # define adoptObjCObj adoptObjCObjArc
307 #endif
308 template <typename T>
309 class AutoObjCPtr;
310 template <typename T>
311 using AutoObjCObj = AutoObjCPtr<T *>;
312 template <typename U>
313 AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT) __attribute__((__warn_unused_result__));
314
315 // This class is similar to WrappedObject, however, it allows changing the
316 // internal pointer with public methods.
317 template <typename T>
318 class AutoObjCPtr : public WrappedObject<T>
319 {
320 public:
321 using ParentType = WrappedObject<T>;
322
323 AutoObjCPtr() {}
324
325 AutoObjCPtr(const std::nullptr_t &theNull) {}
326
327 AutoObjCPtr(const AutoObjCPtr &src) { this->retainAssign(src.get()); }
328
329 AutoObjCPtr(AutoObjCPtr &&src) { this->transfer(std::forward<AutoObjCPtr>(src)); }
330
331 // Take ownership of the pointer
332 AutoObjCPtr(T &&src)
333 {
334 this->retainAssign(src);
335 src = nil;
336 }
337
338 AutoObjCPtr &operator=(const AutoObjCPtr &src)
339 {
340 this->retainAssign(src.get());
341 return *this;
342 }
343
344 AutoObjCPtr &operator=(AutoObjCPtr &&src)
345 {
346 this->transfer(std::forward<AutoObjCPtr>(src));
347 return *this;
348 }
349
350 // Take ownership of the pointer
351 AutoObjCPtr &operator=(T &&src)
352 {
353 this->retainAssign(src);
354 src = nil;
355 return *this;
356 }
357
358 AutoObjCPtr &operator=(std::nullptr_t theNull)
359 {
360 this->set(nil);
361 return *this;
362 }
363
364 bool operator==(const AutoObjCPtr &rhs) const { return (*this) == rhs.get(); }
365
366 bool operator==(T rhs) const { return this->get() == rhs; }
367
368 bool operator==(std::nullptr_t theNull) const { return this->get() == nullptr; }
369
370 bool operator!=(std::nullptr_t) const { return this->get() != nullptr; }
371
372 inline operator bool() { return this->get(); }
373
374 bool operator!=(const AutoObjCPtr &rhs) const { return (*this) != rhs.get(); }
375
376 bool operator!=(T rhs) const { return this->get() != rhs; }
377
378 using ParentType::retainAssign;
379
380 template <typename U>
381 friend AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT)
382 __attribute__((__warn_unused_result__));
383
384 private:
385 enum AdoptTag
386 {
387 Adopt
388 };
389 AutoObjCPtr(T src, AdoptTag) { this->unretainAssign(src); }
390
391 void transfer(AutoObjCPtr &&src)
392 {
393 this->retainAssign(std::move(src.get()));
394 src.reset();
395 }
396 };
397
398 template <typename U>
399 inline AutoObjCObj<U> adoptObjCObj(U *NS_RELEASES_ARGUMENT src)
400 {
401 #if __has_feature(objc_arc)
402 return src;
403 #elif defined(OBJC_NO_GC)
404 return AutoObjCPtr<U *>(src, AutoObjCPtr<U *>::Adopt);
405 #else
406 # error "ObjC GC not supported."
407 #endif
408 }
409
410 // NOTE: SharedEvent is only declared on iOS 12.0+ or mac 10.14+
411 #if defined(__IPHONE_12_0) || defined(__MAC_10_14)
412 # define ANGLE_MTL_EVENT_AVAILABLE 1
413 using SharedEventRef = AutoObjCPtr<id<MTLSharedEvent>>;
414 #else
415 # define ANGLE_MTL_EVENT_AVAILABLE 0
416 using SharedEventRef = AutoObjCObj<NSObject>;
417 #endif
418
419 // The native image index used by Metal back-end, the image index uses native mipmap level instead
420 // of "virtual" level modified by OpenGL's base level.
421 using MipmapNativeLevel = gl::LevelIndexWrapper<uint32_t>;
422
423 constexpr MipmapNativeLevel kZeroNativeMipLevel(0);
424
425 class ImageNativeIndexIterator;
426
427 class ImageNativeIndex final
428 {
429 public:
430 ImageNativeIndex() = delete;
431 ImageNativeIndex(const gl::ImageIndex &src, GLint baseLevel)
432 {
433 mNativeIndex = gl::ImageIndex::MakeFromType(src.getType(), src.getLevelIndex() - baseLevel,
434 src.getLayerIndex(), src.getLayerCount());
435 }
436
437 static ImageNativeIndex FromBaseZeroGLIndex(const gl::ImageIndex &src)
438 {
439 return ImageNativeIndex(src, 0);
440 }
441
442 MipmapNativeLevel getNativeLevel() const
443 {
444 return MipmapNativeLevel(mNativeIndex.getLevelIndex());
445 }
446
447 gl::TextureType getType() const { return mNativeIndex.getType(); }
448 GLint getLayerIndex() const { return mNativeIndex.getLayerIndex(); }
449 GLint getLayerCount() const { return mNativeIndex.getLayerCount(); }
450 GLint cubeMapFaceIndex() const { return mNativeIndex.cubeMapFaceIndex(); }
451
452 bool isLayered() const { return mNativeIndex.isLayered(); }
453 bool hasLayer() const { return mNativeIndex.hasLayer(); }
454 bool has3DLayer() const { return mNativeIndex.has3DLayer(); }
455 bool usesTex3D() const { return mNativeIndex.usesTex3D(); }
456
457 bool valid() const { return mNativeIndex.valid(); }
458
459 ImageNativeIndexIterator getLayerIterator(GLint layerCount) const;
460
461 private:
462 gl::ImageIndex mNativeIndex;
463 };
464
465 class ImageNativeIndexIterator final
466 {
467 public:
468 ImageNativeIndex next() { return ImageNativeIndex(mNativeIndexIte.next(), 0); }
469 ImageNativeIndex current() const { return ImageNativeIndex(mNativeIndexIte.current(), 0); }
470 bool hasNext() const { return mNativeIndexIte.hasNext(); }
471
472 private:
473 // This class is only constructable from ImageNativeIndex
474 friend class ImageNativeIndex;
475
476 explicit ImageNativeIndexIterator(const gl::ImageIndexIterator &baseZeroSrc)
477 : mNativeIndexIte(baseZeroSrc)
478 {}
479
480 gl::ImageIndexIterator mNativeIndexIte;
481 };
482
483 using ClearColorValueBytes = std::array<uint8_t, 4 * sizeof(float)>;
484
485 class ClearColorValue
486 {
487 public:
488 constexpr ClearColorValue()
489 : mType(PixelType::Float), mRedF(0), mGreenF(0), mBlueF(0), mAlphaF(0)
490 {}
491 constexpr ClearColorValue(float r, float g, float b, float a)
492 : mType(PixelType::Float), mRedF(r), mGreenF(g), mBlueF(b), mAlphaF(a)
493 {}
494 constexpr ClearColorValue(int32_t r, int32_t g, int32_t b, int32_t a)
495 : mType(PixelType::Int), mRedI(r), mGreenI(g), mBlueI(b), mAlphaI(a)
496 {}
497 constexpr ClearColorValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
498 : mType(PixelType::UInt), mRedU(r), mGreenU(g), mBlueU(b), mAlphaU(a)
499 {}
500 constexpr ClearColorValue(const ClearColorValue &src)
501 : mType(src.mType), mValueBytes(src.mValueBytes)
502 {}
503
504 MTLClearColor toMTLClearColor() const;
505
506 PixelType getType() const { return mType; }
507
508 const ClearColorValueBytes &getValueBytes() const { return mValueBytes; }
509
510 ClearColorValue &operator=(const ClearColorValue &src);
511
512 void setAsFloat(float r, float g, float b, float a);
513 void setAsInt(int32_t r, int32_t g, int32_t b, int32_t a);
514 void setAsUInt(uint32_t r, uint32_t g, uint32_t b, uint32_t a);
515
516 private:
517 PixelType mType;
518
519 union
520 {
521 struct
522 {
523 float mRedF, mGreenF, mBlueF, mAlphaF;
524 };
525 struct
526 {
527 int32_t mRedI, mGreenI, mBlueI, mAlphaI;
528 };
529 struct
530 {
531 uint32_t mRedU, mGreenU, mBlueU, mAlphaU;
532 };
533
534 ClearColorValueBytes mValueBytes;
535 };
536 };
537
538 class CommandQueue;
539 class ErrorHandler
540 {
541 public:
542 virtual ~ErrorHandler() {}
543
544 virtual void handleError(GLenum error,
545 const char *file,
546 const char *function,
547 unsigned int line) = 0;
548
549 virtual void handleError(NSError *_Nullable error,
550 const char *file,
551 const char *function,
552 unsigned int line) = 0;
553 };
554
555 class Context : public ErrorHandler
556 {
557 public:
558 Context(DisplayMtl *displayMtl);
559 _Nullable id<MTLDevice> getMetalDevice() const;
560 mtl::CommandQueue &cmdQueue();
561
562 DisplayMtl *getDisplay() const { return mDisplay; }
563
564 protected:
565 DisplayMtl *mDisplay;
566 };
567
568 #define ANGLE_MTL_CHECK(context, test, error) \
569 do \
570 { \
571 if (ANGLE_UNLIKELY(!(test))) \
572 { \
573 context->handleError(error, __FILE__, ANGLE_FUNCTION, __LINE__); \
574 return angle::Result::Stop; \
575 } \
576 } while (0)
577
578 #define ANGLE_MTL_TRY(context, test) ANGLE_MTL_CHECK(context, test, GL_INVALID_OPERATION)
579
580 #define ANGLE_MTL_UNREACHABLE(context) \
581 UNREACHABLE(); \
582 ANGLE_MTL_TRY(context, false)
583
584 } // namespace mtl
585 } // namespace rx
586
587 #endif /* LIBANGLE_RENDERER_METAL_MTL_COMMON_H_ */
588