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