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