// // Copyright 2019 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // mtl_common.h: // Declares common constants, template classes, and mtl::Context - the MTLDevice container & // error handler base class. // #ifndef LIBANGLE_RENDERER_METAL_MTL_COMMON_H_ #define LIBANGLE_RENDERER_METAL_MTL_COMMON_H_ #import #include #include #include "common/Optional.h" #include "common/PackedEnums.h" #include "common/angleutils.h" #include "common/apple/ObjCPtr.h" #include "common/apple_platform_utils.h" #include "libANGLE/Constants.h" #include "libANGLE/ImageIndex.h" #include "libANGLE/Version.h" #include "libANGLE/angletypes.h" #if defined(ANGLE_MTL_ENABLE_TRACE) # define ANGLE_MTL_LOG(...) NSLog(@__VA_ARGS__) #else # define ANGLE_MTL_LOG(...) (void)0 #endif #define ANGLE_MTL_OBJC_SCOPE ANGLE_APPLE_OBJC_SCOPE #define ANGLE_MTL_RETAIN ANGLE_APPLE_RETAIN #define ANGLE_MTL_RELEASE ANGLE_APPLE_RELEASE namespace egl { class Display; class Image; class Surface; } // namespace egl #define ANGLE_GL_OBJECTS_X(PROC) \ PROC(Buffer) \ PROC(Context) \ PROC(Framebuffer) \ PROC(MemoryObject) \ PROC(Query) \ PROC(Program) \ PROC(ProgramExecutable) \ PROC(Sampler) \ PROC(Semaphore) \ PROC(Texture) \ PROC(TransformFeedback) \ PROC(VertexArray) #define ANGLE_PRE_DECLARE_OBJECT(OBJ) class OBJ; namespace gl { ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_OBJECT) } // namespace gl #define ANGLE_PRE_DECLARE_MTL_OBJECT(OBJ) class OBJ##Mtl; namespace rx { class DisplayMtl; class ContextMtl; class FramebufferMtl; class BufferMtl; class ImageMtl; class VertexArrayMtl; class TextureMtl; class ProgramMtl; class SamplerMtl; class TransformFeedbackMtl; ANGLE_GL_OBJECTS_X(ANGLE_PRE_DECLARE_MTL_OBJECT) namespace mtl { // NOTE(hqle): support variable max number of vertex attributes constexpr uint32_t kMaxVertexAttribs = gl::MAX_VERTEX_ATTRIBS; // Note: This is the max number of render targets the backend supports. // It is NOT how many the device supports which may be lower. If you // increase this number you will also need to edit the shaders in // metal/shaders/common.h. constexpr uint32_t kMaxRenderTargets = 8; // Metal Apple1 iOS devices only support 4 render targets constexpr uint32_t kMaxRenderTargetsOlderGPUFamilies = 4; constexpr uint32_t kMaxColorTargetBitsApple1To3 = 256; constexpr uint32_t kMaxColorTargetBitsApple4Plus = 512; constexpr uint32_t kMaxColorTargetBitsMacAndCatalyst = std::numeric_limits::max(); constexpr uint32_t kMaxShaderUBOs = 16; constexpr uint32_t kMaxUBOSize = 16384; constexpr uint32_t kMaxShaderXFBs = gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS; // The max size of a buffer that will be allocated in shared memory. // NOTE(hqle): This is just a hint. There is no official document on what is the max allowed size // for shared memory. constexpr size_t kSharedMemBufferMaxBufSizeHint = 256 * 1024; constexpr size_t kDefaultAttributeSize = 4 * sizeof(float); // Metal limits constexpr uint32_t kMaxShaderBuffers = 31; constexpr uint32_t kMaxShaderSamplers = 16; constexpr size_t kInlineConstDataMaxSize = 4 * 1024; constexpr size_t kDefaultUniformsMaxSize = 16 * 1024; constexpr uint32_t kMaxViewports = 1; constexpr uint32_t kMaxShaderImages = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; // Restrict in-flight resource usage to 400 MB. // A render pass can use more than 400MB, but the command buffer // will be flushed next time constexpr const size_t kMaximumResidentMemorySizeInBytes = 400 * 1024 * 1024; // Restrict in-flight render passes per command buffer to 16. // The goal is to reduce the number of active render passes on the system at // any one time and this value was determined through experimentation. constexpr uint32_t kMaxRenderPassesPerCommandBuffer = 16; constexpr uint32_t kVertexAttribBufferStrideAlignment = 4; // Alignment requirement for offset passed to setVertex|FragmentBuffer #if TARGET_OS_OSX || TARGET_OS_MACCATALYST constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 256; #else constexpr uint32_t kUniformBufferSettingOffsetMinAlignment = 4; #endif constexpr uint32_t kIndexBufferOffsetAlignment = 4; constexpr uint32_t kArgumentBufferOffsetAlignment = kUniformBufferSettingOffsetMinAlignment; constexpr uint32_t kTextureToBufferBlittingAlignment = 256; // Front end binding limits constexpr uint32_t kMaxGLSamplerBindings = 2 * kMaxShaderSamplers; constexpr uint32_t kMaxGLUBOBindings = 2 * kMaxShaderUBOs; // Binding index start for vertex data buffers: constexpr uint32_t kVboBindingIndexStart = 0; // Binding index for default attribute buffer: constexpr uint32_t kDefaultAttribsBindingIndex = kVboBindingIndexStart + kMaxVertexAttribs; // Binding index for driver uniforms: constexpr uint32_t kDriverUniformsBindingIndex = kDefaultAttribsBindingIndex + 1; // Binding index for default uniforms: constexpr uint32_t kDefaultUniformsBindingIndex = kDefaultAttribsBindingIndex + 3; // Binding index for Transform Feedback Buffers (4) constexpr uint32_t kTransformFeedbackBindingIndex = kDefaultUniformsBindingIndex + 1; // Binding index for shadow samplers' compare modes constexpr uint32_t kShadowSamplerCompareModesBindingIndex = kTransformFeedbackBindingIndex + 4; // Binding index for UBO's argument buffer constexpr uint32_t kUBOArgumentBufferBindingIndex = kShadowSamplerCompareModesBindingIndex + 1; constexpr uint32_t kStencilMaskAll = 0xff; // Only 8 bits stencil is supported // This special constant is used to indicate that a particular vertex descriptor's buffer layout // index is unused. constexpr MTLVertexStepFunction kVertexStepFunctionInvalid = static_cast(0xff); constexpr int kEmulatedAlphaValue = 1; constexpr size_t kOcclusionQueryResultSize = sizeof(uint64_t); constexpr gl::Version kMaxSupportedGLVersion = gl::Version(3, 0); enum class PixelType { Int, UInt, Float, EnumCount, }; template struct ImplTypeHelper; // clang-format off #define ANGLE_IMPL_TYPE_HELPER_GL(OBJ) \ template<> \ struct ImplTypeHelper \ { \ using ImplType = OBJ##Mtl; \ }; // clang-format on ANGLE_GL_OBJECTS_X(ANGLE_IMPL_TYPE_HELPER_GL) template <> struct ImplTypeHelper { using ImplType = DisplayMtl; }; template <> struct ImplTypeHelper { using ImplType = ImageMtl; }; template using GetImplType = typename ImplTypeHelper::ImplType; template GetImplType *GetImpl(const T *glObject) { return GetImplAs>(glObject); } // This class wraps Objective-C pointer inside, it will manage the lifetime of // the Objective-C pointer. Changing pointer is not supported outside subclass. template class WrappedObject { public: WrappedObject() = default; ~WrappedObject() { release(); } bool valid() const { return (mMetalObject != nil); } T get() const { return mMetalObject; } T leakObject() { return std::exchange(mMetalObject, nullptr); } inline void reset() { release(); } operator T() const { return get(); } protected: inline void set(T obj) { retainAssign(obj); } void retainAssign(T obj) { #if !__has_feature(objc_arc) T retained = obj; [retained retain]; #endif release(); mMetalObject = obj; } void unretainAssign(T obj) { release(); mMetalObject = obj; } private: void release() { #if !__has_feature(objc_arc) [mMetalObject release]; #endif mMetalObject = nil; } T mMetalObject = nil; }; // The native image index used by Metal back-end, the image index uses native mipmap level instead // of "virtual" level modified by OpenGL's base level. using MipmapNativeLevel = gl::LevelIndexWrapper; constexpr MipmapNativeLevel kZeroNativeMipLevel(0); class ImageNativeIndexIterator; class ImageNativeIndex final { public: ImageNativeIndex() = delete; ImageNativeIndex(const gl::ImageIndex &src, GLint baseLevel) { mNativeIndex = gl::ImageIndex::MakeFromType(src.getType(), src.getLevelIndex() - baseLevel, src.getLayerIndex(), src.getLayerCount()); } static ImageNativeIndex FromBaseZeroGLIndex(const gl::ImageIndex &src) { return ImageNativeIndex(src, 0); } MipmapNativeLevel getNativeLevel() const { return MipmapNativeLevel(mNativeIndex.getLevelIndex()); } gl::TextureType getType() const { return mNativeIndex.getType(); } GLint getLayerIndex() const { return mNativeIndex.getLayerIndex(); } GLint getLayerCount() const { return mNativeIndex.getLayerCount(); } GLint cubeMapFaceIndex() const { return mNativeIndex.cubeMapFaceIndex(); } bool isLayered() const { return mNativeIndex.isLayered(); } bool hasLayer() const { return mNativeIndex.hasLayer(); } bool has3DLayer() const { return mNativeIndex.has3DLayer(); } bool usesTex3D() const { return mNativeIndex.usesTex3D(); } bool valid() const { return mNativeIndex.valid(); } ImageNativeIndexIterator getLayerIterator(GLint layerCount) const; private: gl::ImageIndex mNativeIndex; }; class ImageNativeIndexIterator final { public: ImageNativeIndex next() { return ImageNativeIndex(mNativeIndexIte.next(), 0); } ImageNativeIndex current() const { return ImageNativeIndex(mNativeIndexIte.current(), 0); } bool hasNext() const { return mNativeIndexIte.hasNext(); } private: // This class is only constructable from ImageNativeIndex friend class ImageNativeIndex; explicit ImageNativeIndexIterator(const gl::ImageIndexIterator &baseZeroSrc) : mNativeIndexIte(baseZeroSrc) {} gl::ImageIndexIterator mNativeIndexIte; }; using ClearColorValueBytes = std::array; class ClearColorValue { public: constexpr ClearColorValue() : mType(PixelType::Float), mRedF(0), mGreenF(0), mBlueF(0), mAlphaF(0) {} constexpr ClearColorValue(float r, float g, float b, float a) : mType(PixelType::Float), mRedF(r), mGreenF(g), mBlueF(b), mAlphaF(a) {} constexpr ClearColorValue(int32_t r, int32_t g, int32_t b, int32_t a) : mType(PixelType::Int), mRedI(r), mGreenI(g), mBlueI(b), mAlphaI(a) {} constexpr ClearColorValue(uint32_t r, uint32_t g, uint32_t b, uint32_t a) : mType(PixelType::UInt), mRedU(r), mGreenU(g), mBlueU(b), mAlphaU(a) {} constexpr ClearColorValue(const ClearColorValue &src) : mType(src.mType), mValueBytes(src.mValueBytes) {} MTLClearColor toMTLClearColor() const; PixelType getType() const { return mType; } const ClearColorValueBytes &getValueBytes() const { return mValueBytes; } ClearColorValue &operator=(const ClearColorValue &src); void setAsFloat(float r, float g, float b, float a); void setAsInt(int32_t r, int32_t g, int32_t b, int32_t a); void setAsUInt(uint32_t r, uint32_t g, uint32_t b, uint32_t a); private: PixelType mType; union { struct { float mRedF, mGreenF, mBlueF, mAlphaF; }; struct { int32_t mRedI, mGreenI, mBlueI, mAlphaI; }; struct { uint32_t mRedU, mGreenU, mBlueU, mAlphaU; }; ClearColorValueBytes mValueBytes; }; }; class CommandQueue; class ErrorHandler { public: virtual ~ErrorHandler() {} virtual void handleError(GLenum error, const char *message, const char *file, const char *function, unsigned int line) = 0; void handleNSError(NSError *error, const char *file, const char *function, unsigned int line) { std::string message; { std::stringstream s; s << "Internal error. Metal error: " << (error != nil ? error.localizedDescription.UTF8String : "nil error"); message = s.str(); } handleError(GL_INVALID_OPERATION, message.c_str(), file, function, line); } }; class Context : public ErrorHandler { public: Context(DisplayMtl *displayMtl); mtl::CommandQueue &cmdQueue(); DisplayMtl *getDisplay() const { return mDisplay; } protected: DisplayMtl *mDisplay; }; #define ANGLE_MTL_CHECK(context, result, nserror) \ do \ { \ auto &localResult = (result); \ auto &localError = (nserror); \ if (ANGLE_UNLIKELY(!localResult || localError)) \ { \ context->handleNSError(localError, __FILE__, ANGLE_FUNCTION, __LINE__); \ return angle::Result::Stop; \ } \ } while (0) } // namespace mtl } // namespace rx #endif /* LIBANGLE_RENDERER_METAL_MTL_COMMON_H_ */