// // 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. // // DisplayMtl.mm: Metal implementation of DisplayImpl #ifdef UNSAFE_BUFFERS_BUILD # pragma allow_unsafe_buffers #endif #include "libANGLE/renderer/metal/DisplayMtl.h" #include #include "common/apple_platform_utils.h" #include "common/system_utils.h" #include "gpu_info_util/SystemInfo.h" #include "libANGLE/Context.h" #include "libANGLE/Display.h" #include "libANGLE/Surface.h" #include "libANGLE/renderer/driver_utils.h" #include "libANGLE/renderer/metal/CompilerMtl.h" #include "libANGLE/renderer/metal/ContextMtl.h" #include "libANGLE/renderer/metal/DeviceMtl.h" #include "libANGLE/renderer/metal/IOSurfaceSurfaceMtl.h" #include "libANGLE/renderer/metal/ImageMtl.h" #include "libANGLE/renderer/metal/SurfaceMtl.h" #include "libANGLE/renderer/metal/SyncMtl.h" #include "libANGLE/renderer/metal/mtl_common.h" #include "libANGLE/trace.h" #include "mtl_command_buffer.h" #include "platform/PlatformMethods.h" #if ANGLE_METAL_XCODE_BUILDS_SHADERS # include "libANGLE/renderer/metal/shaders/mtl_internal_shaders_metallib.h" #elif ANGLE_METAL_HAS_PREBUILT_INTERNAL_SHADERS # include "mtl_internal_shaders_metallib.h" #else # include "libANGLE/renderer/metal/shaders/mtl_internal_shaders_src_autogen.h" #endif #include "EGL/eglext.h" namespace rx { static EGLint GetDepthSize(GLint internalformat) { switch (internalformat) { case GL_STENCIL_INDEX8: return 0; case GL_DEPTH_COMPONENT16: return 16; case GL_DEPTH_COMPONENT24: return 24; case GL_DEPTH_COMPONENT32_OES: return 32; case GL_DEPTH_COMPONENT32F: return 32; case GL_DEPTH24_STENCIL8: return 24; case GL_DEPTH32F_STENCIL8: return 32; default: // UNREACHABLE(internalformat); return 0; } } static EGLint GetStencilSize(GLint internalformat) { switch (internalformat) { case GL_STENCIL_INDEX8: return 8; case GL_DEPTH_COMPONENT16: return 0; case GL_DEPTH_COMPONENT24: return 0; case GL_DEPTH_COMPONENT32_OES: return 0; case GL_DEPTH_COMPONENT32F: return 0; case GL_DEPTH24_STENCIL8: return 8; case GL_DEPTH32F_STENCIL8: return 8; default: // UNREACHABLE(internalformat); return 0; } } bool IsMetalDisplayAvailable() { return angle::IsMetalRendererAvailable(); } DisplayImpl *CreateMetalDisplay(const egl::DisplayState &state) { return new DisplayMtl(state); } DisplayMtl::DisplayMtl(const egl::DisplayState &state) : DisplayImpl(state), mDisplay(nullptr), mStateCache(mFeatures) {} DisplayMtl::~DisplayMtl() {} egl::Error DisplayMtl::initialize(egl::Display *display) { ASSERT(IsMetalDisplayAvailable()); angle::Result result = initializeImpl(display); if (result != angle::Result::Continue) { terminate(); return egl::Error(EGL_NOT_INITIALIZED); } return egl::NoError(); } angle::Result DisplayMtl::initializeImpl(egl::Display *display) { ANGLE_MTL_OBJC_SCOPE { mDisplay = display; mMetalDevice = getMetalDeviceMatchingAttribute(display->getAttributeMap()); // If we can't create a device, fail initialization. if (!mMetalDevice.get()) { return angle::Result::Stop; } mMetalDeviceVendorId = mtl::GetDeviceVendorId(mMetalDevice); mCapsInitialized = false; initializeFeatures(); if (mFeatures.requireGpuFamily2.enabled && !supportsEitherGPUFamily(1, 2)) { ANGLE_MTL_LOG("Could not initialize: Metal device does not support Mac GPU family 2."); return angle::Result::Stop; } if (mFeatures.disableMetalOnNvidia.enabled && isNVIDIA()) { ANGLE_MTL_LOG("Could not initialize: Metal not supported on NVIDIA GPUs."); return angle::Result::Stop; } mCmdQueue = angle::adoptObjCPtr([mMetalDevice newCommandQueue]); ANGLE_TRY(mFormatTable.initialize(this)); ANGLE_TRY(initializeShaderLibrary()); mUtils = std::make_unique(); return angle::Result::Continue; } } void DisplayMtl::terminate() { mUtils = nullptr; mCmdQueue.reset(); mDefaultShaders = nil; mMetalDevice = nil; mSharedEventListener = nil; mCapsInitialized = false; mMetalDeviceVendorId = 0; mComputedAMDBronze = false; mIsAMDBronze = false; } bool DisplayMtl::testDeviceLost() { return false; } egl::Error DisplayMtl::restoreLostDevice(const egl::Display *display) { return egl::NoError(); } std::string DisplayMtl::getRendererDescription() { ANGLE_MTL_OBJC_SCOPE { std::string desc = "ANGLE Metal Renderer"; if (mMetalDevice) { desc += ": "; desc += mMetalDevice.get().name.UTF8String; } return desc; } } std::string DisplayMtl::getVendorString() { return GetVendorString(mMetalDeviceVendorId); } std::string DisplayMtl::getVersionString(bool includeFullVersion) { if (!includeFullVersion) { // For WebGL contexts it's inappropriate to include any // additional version information, but Chrome requires // something to be present here. return "Unspecified Version"; } ANGLE_MTL_OBJC_SCOPE { NSProcessInfo *procInfo = [NSProcessInfo processInfo]; return procInfo.operatingSystemVersionString.UTF8String; } } DeviceImpl *DisplayMtl::createDevice() { return new DeviceMtl(); } angle::ObjCPtr> DisplayMtl::getMetalDeviceMatchingAttribute( const egl::AttributeMap &attribs) { #if TARGET_OS_OSX || TARGET_OS_MACCATALYST auto deviceList = angle::adoptObjCPtr(MTLCopyAllDevices()); EGLAttrib high = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_HIGH_ANGLE, 0); EGLAttrib low = attribs.get(EGL_PLATFORM_ANGLE_DEVICE_ID_LOW_ANGLE, 0); uint64_t deviceId = angle::GetSystemDeviceIdFromParts(static_cast(high), static_cast(low)); // Check EGL_ANGLE_platform_angle_device_id to see if a device was specified. if (deviceId != 0) { for (id device in deviceList.get()) { if ([device registryID] == deviceId) { return device; } } } auto externalGPUs = angle::adoptObjCPtr([[NSMutableArray alloc] init]); auto integratedGPUs = angle::adoptObjCPtr([[NSMutableArray alloc] init]); auto discreteGPUs = angle::adoptObjCPtr([[NSMutableArray alloc] init]); for (id device in deviceList.get()) { if (device.removable) { [externalGPUs addObject:device]; } else if (device.lowPower) { [integratedGPUs addObject:device]; } else { [discreteGPUs addObject:device]; } } // TODO(kpiddington: External GPU support. Do we prefer high power / low bandwidth for general // WebGL applications? // Can we support hot-swapping in GPU's? if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, 0) == EGL_HIGH_POWER_ANGLE) { // Search for a discrete GPU first. for (id device in discreteGPUs.get()) { if (![device isHeadless]) { return device; } } } else if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, 0) == EGL_LOW_POWER_ANGLE) { // If we've selected a low power device, look through integrated devices. for (id device in integratedGPUs.get()) { if (![device isHeadless]) { return device; } } } const std::string preferredDeviceString = angle::GetPreferredDeviceString(); if (!preferredDeviceString.empty()) { for (id device in deviceList.get()) { if ([device.name.lowercaseString containsString:[NSString stringWithUTF8String:preferredDeviceString.c_str()]]) { NSLog(@"Using Metal Device: %@", [device name]); return device; } } } #endif // If we can't find anything, or are on a platform that doesn't support power options, create a // default device. return angle::adoptObjCPtr(MTLCreateSystemDefaultDevice()); } egl::Error DisplayMtl::waitClient(const gl::Context *context) { auto contextMtl = GetImplAs(context); angle::Result result = contextMtl->finishCommandBuffer(); if (result != angle::Result::Continue) { return egl::Error(EGL_BAD_ACCESS); } return egl::NoError(); } egl::Error DisplayMtl::waitNative(const gl::Context *context, EGLint engine) { UNIMPLEMENTED(); return egl::NoError(); } egl::Error DisplayMtl::waitUntilWorkScheduled() { for (auto context : mState.contextMap) { auto contextMtl = GetImplAs(context.second); contextMtl->flushCommandBuffer(mtl::WaitUntilScheduled); } return egl::NoError(); } SurfaceImpl *DisplayMtl::createWindowSurface(const egl::SurfaceState &state, EGLNativeWindowType window, const egl::AttributeMap &attribs) { return new WindowSurfaceMtl(this, state, window, attribs); } SurfaceImpl *DisplayMtl::createPbufferSurface(const egl::SurfaceState &state, const egl::AttributeMap &attribs) { return new PBufferSurfaceMtl(this, state, attribs); } SurfaceImpl *DisplayMtl::createPbufferFromClientBuffer(const egl::SurfaceState &state, EGLenum buftype, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) { switch (buftype) { case EGL_IOSURFACE_ANGLE: return new IOSurfaceSurfaceMtl(this, state, clientBuffer, attribs); default: UNREACHABLE(); } return nullptr; } SurfaceImpl *DisplayMtl::createPixmapSurface(const egl::SurfaceState &state, NativePixmapType nativePixmap, const egl::AttributeMap &attribs) { UNIMPLEMENTED(); return static_cast(0); } ImageImpl *DisplayMtl::createImage(const egl::ImageState &state, const gl::Context *context, EGLenum target, const egl::AttributeMap &attribs) { return new ImageMtl(state, context); } rx::ContextImpl *DisplayMtl::createContext(const gl::State &state, gl::ErrorSet *errorSet, const egl::Config *configuration, const gl::Context *shareContext, const egl::AttributeMap &attribs) { return new ContextMtl(state, errorSet, attribs, this); } StreamProducerImpl *DisplayMtl::createStreamProducerD3DTexture( egl::Stream::ConsumerType consumerType, const egl::AttributeMap &attribs) { UNIMPLEMENTED(); return nullptr; } ShareGroupImpl *DisplayMtl::createShareGroup(const egl::ShareGroupState &state) { return new ShareGroupMtl(state); } ExternalImageSiblingImpl *DisplayMtl::createExternalImageSibling(const gl::Context *context, EGLenum target, EGLClientBuffer buffer, const egl::AttributeMap &attribs) { switch (target) { case EGL_METAL_TEXTURE_ANGLE: return new TextureImageSiblingMtl(buffer, attribs); default: UNREACHABLE(); return nullptr; } } gl::Version DisplayMtl::getMaxSupportedESVersion() const { #if TARGET_OS_SIMULATOR // Simulator should be able to support ES3, despite not supporting iOS GPU // Family 3 in its entirety. // FIXME: None of the feature conditions are checked for simulator support. return gl::Version(3, 0); #else if (supportsEitherGPUFamily(4, 1)) { return mtl::kMaxSupportedGLVersion; } return gl::Version(2, 0); #endif } gl::Version DisplayMtl::getMaxConformantESVersion() const { return std::min(getMaxSupportedESVersion(), gl::Version(3, 0)); } EGLSyncImpl *DisplayMtl::createSync() { return new EGLSyncMtl(); } egl::Error DisplayMtl::makeCurrent(egl::Display *display, egl::Surface *drawSurface, egl::Surface *readSurface, gl::Context *context) { if (!context) { return egl::NoError(); } return egl::NoError(); } void DisplayMtl::generateExtensions(egl::DisplayExtensions *outExtensions) const { outExtensions->iosurfaceClientBuffer = true; outExtensions->surfacelessContext = true; outExtensions->noConfigContext = true; outExtensions->displayTextureShareGroup = true; outExtensions->displaySemaphoreShareGroup = true; outExtensions->mtlTextureClientBuffer = true; outExtensions->waitUntilWorkScheduled = true; if (mFeatures.hasEvents.enabled) { // MTLSharedEvent is only available since Metal 2.1 outExtensions->fenceSync = true; outExtensions->waitSync = true; } // Note that robust resource initialization is not yet implemented. We only expose // this extension so that ANGLE can be initialized in Chrome. WebGL will fail to use // this extension (anglebug.com/42263503) outExtensions->robustResourceInitializationANGLE = true; // EGL_KHR_image outExtensions->image = true; outExtensions->imageBase = true; // EGL_ANGLE_metal_create_context_ownership_identity outExtensions->metalCreateContextOwnershipIdentityANGLE = true; // EGL_ANGLE_metal_shared_event_sync outExtensions->mtlSyncSharedEventANGLE = true; // EGL_ANGLE_metal_commands_scheduled_sync outExtensions->mtlSyncCommandsScheduledANGLE = true; } void DisplayMtl::generateCaps(egl::Caps *outCaps) const { outCaps->textureNPOT = true; } void DisplayMtl::initializeFrontendFeatures(angle::FrontendFeatures *features) const { // The Metal backend's handling of compile and link is thread-safe ANGLE_FEATURE_CONDITION(features, compileJobIsThreadSafe, true); ANGLE_FEATURE_CONDITION(features, linkJobIsThreadSafe, true); } void DisplayMtl::populateFeatureList(angle::FeatureList *features) { mFeatures.populateFeatureList(features); } EGLenum DisplayMtl::EGLDrawingBufferTextureTarget() { // TODO(anglebug.com/42264909): Apple's implementation conditionalized this on // MacCatalyst and whether it was running on ARM64 or X64, preferring // EGL_TEXTURE_RECTANGLE_ANGLE. Metal can bind IOSurfaces to regular 2D // textures, and rectangular textures don't work in the SPIR-V Metal // backend, so for the time being use EGL_TEXTURE_2D on all platforms. return EGL_TEXTURE_2D; } egl::ConfigSet DisplayMtl::generateConfigs() { // NOTE(hqle): generate more config permutations egl::ConfigSet configs; const gl::Version &maxVersion = getMaxSupportedESVersion(); ASSERT(maxVersion >= gl::Version(2, 0)); bool supportsES3 = maxVersion >= gl::Version(3, 0); egl::Config config; // Native stuff config.nativeVisualID = 0; config.nativeVisualType = 0; config.nativeRenderable = EGL_TRUE; config.colorBufferType = EGL_RGB_BUFFER; config.luminanceSize = 0; config.alphaMaskSize = 0; config.transparentType = EGL_NONE; // Pbuffer config.bindToTextureTarget = EGLDrawingBufferTextureTarget(); config.maxPBufferWidth = 4096; config.maxPBufferHeight = 4096; config.maxPBufferPixels = 4096 * 4096; // Caveat config.configCaveat = EGL_NONE; // Misc config.sampleBuffers = 0; config.samples = 0; config.level = 0; config.bindToTextureRGB = EGL_FALSE; config.bindToTextureRGBA = EGL_TRUE; config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; #if TARGET_OS_OSX || TARGET_OS_MACCATALYST config.minSwapInterval = 0; config.maxSwapInterval = 1; #else config.minSwapInterval = 1; config.maxSwapInterval = 1; #endif config.renderTargetFormat = GL_RGBA8; config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); config.renderableType = config.conformant; config.matchNativePixmap = EGL_NONE; config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT; constexpr int samplesSupported[] = {0, 4}; for (int samples : samplesSupported) { config.samples = samples; config.sampleBuffers = (samples == 0) ? 0 : 1; // Buffer sizes config.redSize = 8; config.greenSize = 8; config.blueSize = 8; config.alphaSize = 8; config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; // With DS config.depthSize = 24; config.stencilSize = 8; config.depthStencilFormat = GL_DEPTH24_STENCIL8; configs.add(config); // With D config.depthSize = 24; config.stencilSize = 0; config.depthStencilFormat = GL_DEPTH_COMPONENT24; configs.add(config); // With S config.depthSize = 0; config.stencilSize = 8; config.depthStencilFormat = GL_STENCIL_INDEX8; configs.add(config); // No DS config.depthSize = 0; config.stencilSize = 0; config.depthStencilFormat = GL_NONE; configs.add(config); // Tests like dEQP-GLES2.functional.depth_range.* assume EGL_DEPTH_SIZE is properly set even // if renderConfig attributes are set to glu::RenderConfig::DONT_CARE config.depthSize = GetDepthSize(config.depthStencilFormat); config.stencilSize = GetStencilSize(config.depthStencilFormat); configs.add(config); } return configs; } bool DisplayMtl::isValidNativeWindow(EGLNativeWindowType window) const { ANGLE_MTL_OBJC_SCOPE { NSObject *layer = (__bridge NSObject *)(window); return [layer isKindOfClass:[CALayer class]]; } } egl::Error DisplayMtl::validateClientBuffer(const egl::Config *configuration, EGLenum buftype, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) const { switch (buftype) { case EGL_IOSURFACE_ANGLE: if (!IOSurfaceSurfaceMtl::ValidateAttributes(clientBuffer, attribs)) { return egl::Error(EGL_BAD_ATTRIBUTE); } break; default: UNREACHABLE(); return egl::Error(EGL_BAD_ATTRIBUTE); } return egl::NoError(); } egl::Error DisplayMtl::validateImageClientBuffer(const gl::Context *context, EGLenum target, EGLClientBuffer clientBuffer, const egl::AttributeMap &attribs) const { switch (target) { case EGL_METAL_TEXTURE_ANGLE: return TextureImageSiblingMtl::ValidateClientBuffer(this, clientBuffer, attribs); default: UNREACHABLE(); return egl::Error(EGL_BAD_ATTRIBUTE); } } gl::Caps DisplayMtl::getNativeCaps() const { ensureCapsInitialized(); return mNativeCaps; } const gl::TextureCapsMap &DisplayMtl::getNativeTextureCaps() const { ensureCapsInitialized(); return mNativeTextureCaps; } const gl::Extensions &DisplayMtl::getNativeExtensions() const { ensureCapsInitialized(); return mNativeExtensions; } const gl::Limitations &DisplayMtl::getNativeLimitations() const { ensureCapsInitialized(); return mNativeLimitations; } const ShPixelLocalStorageOptions &DisplayMtl::getNativePixelLocalStorageOptions() const { ensureCapsInitialized(); return mNativePLSOptions; } void DisplayMtl::ensureCapsInitialized() const { if (mCapsInitialized) { return; } mCapsInitialized = true; // Reset mNativeCaps = gl::Caps(); // Fill extension and texture caps initializeExtensions(); initializeTextureCaps(); // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf mNativeCaps.maxElementIndex = std::numeric_limits::max() - 1; mNativeCaps.max3DTextureSize = 2048; #if TARGET_OS_OSX || TARGET_OS_MACCATALYST mNativeCaps.max2DTextureSize = 16384; // On macOS exclude [[position]] from maxVaryingVectors. mNativeCaps.maxVaryingVectors = 31 - 1; mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 124 - 4; #elif TARGET_OS_SIMULATOR mNativeCaps.max2DTextureSize = 8192; mNativeCaps.maxVaryingVectors = 31 - 1; mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 124 - 4; #else if (supportsAppleGPUFamily(3)) { mNativeCaps.max2DTextureSize = 16384; mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 124; mNativeCaps.maxVaryingVectors = mNativeCaps.maxVertexOutputComponents / 4; } else { mNativeCaps.max2DTextureSize = 8192; mNativeCaps.maxVertexOutputComponents = mNativeCaps.maxFragmentInputComponents = 60; mNativeCaps.maxVaryingVectors = mNativeCaps.maxVertexOutputComponents / 4; } #endif mNativeCaps.maxArrayTextureLayers = 2048; mNativeCaps.maxLODBias = std::log2(mNativeCaps.max2DTextureSize) + 1; mNativeCaps.maxCubeMapTextureSize = mNativeCaps.max2DTextureSize; mNativeCaps.maxRenderbufferSize = mNativeCaps.max2DTextureSize; mNativeCaps.minAliasedPointSize = 1; // NOTE(hqle): Metal has some problems drawing big point size even though // Metal-Feature-Set-Tables.pdf says that max supported point size is 511. We limit it to 64 // for now. http://anglebug.com/42263403 mNativeCaps.maxAliasedPointSize = 511; mNativeCaps.minAliasedLineWidth = 1.0f; mNativeCaps.maxAliasedLineWidth = 1.0f; if (supportsEitherGPUFamily(2, 1) && !mFeatures.limitMaxDrawBuffersForTesting.enabled) { mNativeCaps.maxDrawBuffers = mtl::kMaxRenderTargets; mNativeCaps.maxColorAttachments = mtl::kMaxRenderTargets; } else { mNativeCaps.maxDrawBuffers = mtl::kMaxRenderTargetsOlderGPUFamilies; mNativeCaps.maxColorAttachments = mtl::kMaxRenderTargetsOlderGPUFamilies; } ASSERT(static_cast(mNativeCaps.maxDrawBuffers) <= mtl::kMaxRenderTargets); ASSERT(static_cast(mNativeCaps.maxColorAttachments) <= mtl::kMaxRenderTargets); mNativeCaps.maxFramebufferWidth = mNativeCaps.max2DTextureSize; mNativeCaps.maxFramebufferHeight = mNativeCaps.max2DTextureSize; mNativeCaps.maxViewportWidth = mNativeCaps.max2DTextureSize; mNativeCaps.maxViewportHeight = mNativeCaps.max2DTextureSize; bool isCatalyst = TARGET_OS_MACCATALYST; mMaxColorTargetBits = mtl::kMaxColorTargetBitsApple1To3; if (supportsMacGPUFamily(1) || isCatalyst) { mMaxColorTargetBits = mtl::kMaxColorTargetBitsMacAndCatalyst; } else if (supportsAppleGPUFamily(4)) { mMaxColorTargetBits = mtl::kMaxColorTargetBitsApple4Plus; } if (mFeatures.limitMaxColorTargetBitsForTesting.enabled) { // Set so we have enough for RGBA8 on every attachment // but not enough for RGBA32UI. mMaxColorTargetBits = mNativeCaps.maxColorAttachments * 32; } // MSAA mNativeCaps.maxSamples = mFormatTable.getMaxSamples(); mNativeCaps.maxSampleMaskWords = 1; mNativeCaps.maxColorTextureSamples = mNativeCaps.maxSamples; mNativeCaps.maxDepthTextureSamples = mNativeCaps.maxSamples; mNativeCaps.maxIntegerSamples = mNativeCaps.maxSamples; mNativeCaps.maxVertexAttributes = mtl::kMaxVertexAttribs; mNativeCaps.maxVertexAttribBindings = mtl::kMaxVertexAttribs; mNativeCaps.maxVertexAttribRelativeOffset = std::numeric_limits::max(); mNativeCaps.maxVertexAttribStride = std::numeric_limits::max(); // glGet() use signed integer as parameter so we have to use GLint's max here, not GLuint. mNativeCaps.maxElementsIndices = std::numeric_limits::max(); mNativeCaps.maxElementsVertices = std::numeric_limits::max(); // Looks like all floats are IEEE according to the docs here: mNativeCaps.vertexHighpFloat.setIEEEFloat(); mNativeCaps.vertexMediumpFloat.setIEEEFloat(); mNativeCaps.vertexLowpFloat.setIEEEFloat(); mNativeCaps.fragmentHighpFloat.setIEEEFloat(); mNativeCaps.fragmentMediumpFloat.setIEEEFloat(); mNativeCaps.fragmentLowpFloat.setIEEEFloat(); mNativeCaps.vertexHighpInt.setTwosComplementInt(32); mNativeCaps.vertexMediumpInt.setTwosComplementInt(32); mNativeCaps.vertexLowpInt.setTwosComplementInt(32); mNativeCaps.fragmentHighpInt.setTwosComplementInt(32); mNativeCaps.fragmentMediumpInt.setTwosComplementInt(32); mNativeCaps.fragmentLowpInt.setTwosComplementInt(32); GLuint maxDefaultUniformVectors = mtl::kDefaultUniformsMaxSize / (sizeof(GLfloat) * 4); const GLuint maxDefaultUniformComponents = maxDefaultUniformVectors * 4; // Uniforms are implemented using a uniform buffer, so the max number of uniforms we can // support is the max buffer range divided by the size of a single uniform (4X float). mNativeCaps.maxVertexUniformVectors = maxDefaultUniformVectors; mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Vertex] = maxDefaultUniformComponents; mNativeCaps.maxFragmentUniformVectors = maxDefaultUniformVectors; mNativeCaps.maxShaderUniformComponents[gl::ShaderType::Fragment] = maxDefaultUniformComponents; mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Vertex] = mtl::kMaxShaderUBOs; mNativeCaps.maxShaderUniformBlocks[gl::ShaderType::Fragment] = mtl::kMaxShaderUBOs; mNativeCaps.maxCombinedUniformBlocks = mtl::kMaxGLUBOBindings; // Note that we currently implement textures as combined image+samplers, so the limit is // the minimum of supported samplers and sampled images. mNativeCaps.maxCombinedTextureImageUnits = mtl::kMaxGLSamplerBindings; mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Fragment] = mtl::kMaxShaderSamplers; mNativeCaps.maxShaderTextureImageUnits[gl::ShaderType::Vertex] = mtl::kMaxShaderSamplers; // No info from Metal given, use default GLES3 spec values: mNativeCaps.minProgramTexelOffset = -8; mNativeCaps.maxProgramTexelOffset = 7; // NOTE(hqle): support storage buffer. const uint32_t maxPerStageStorageBuffers = 0; mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Vertex] = maxPerStageStorageBuffers; mNativeCaps.maxShaderStorageBlocks[gl::ShaderType::Fragment] = maxPerStageStorageBuffers; mNativeCaps.maxCombinedShaderStorageBlocks = maxPerStageStorageBuffers; // Fill in additional limits for UBOs and SSBOs. mNativeCaps.maxUniformBufferBindings = mNativeCaps.maxCombinedUniformBlocks; static_assert(mtl::kMaxUBOSize <= gl::IMPLEMENTATION_MAX_UNIFORM_BLOCK_SIZE); mNativeCaps.maxUniformBlockSize = mtl::kMaxUBOSize; // Default according to GLES 3.0 spec. if (supportsAppleGPUFamily(1)) { mNativeCaps.uniformBufferOffsetAlignment = 16; // on Apple based GPU's We can ignore data types when setting constant buffer // alignment at 16. } else { mNativeCaps.uniformBufferOffsetAlignment = 256; // constant buffers on all other GPUs must be aligned to 256. } mNativeCaps.maxShaderStorageBufferBindings = 0; mNativeCaps.maxShaderStorageBlockSize = 0; mNativeCaps.shaderStorageBufferOffsetAlignment = 0; // UBO plus default uniform limits const uint32_t maxCombinedUniformComponents = maxDefaultUniformComponents + mtl::kMaxUBOSize * mtl::kMaxShaderUBOs / 4; for (gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes) { mNativeCaps.maxCombinedShaderUniformComponents[shaderType] = maxCombinedUniformComponents; } mNativeCaps.maxCombinedShaderOutputResources = 0; mNativeCaps.maxTransformFeedbackInterleavedComponents = gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS; mNativeCaps.maxTransformFeedbackSeparateAttributes = gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS; mNativeCaps.maxTransformFeedbackSeparateComponents = gl::IMPLEMENTATION_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS; // GL_OES_get_program_binary mNativeCaps.programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE); // GL_APPLE_clip_distance / GL_ANGLE_clip_cull_distance mNativeCaps.maxClipDistances = 8; // Metal doesn't support GL_TEXTURE_COMPARE_MODE=GL_NONE for shadow samplers mNativeLimitations.noShadowSamplerCompareModeNone = true; // Apple platforms require PVRTC1 textures to be squares. mNativeLimitations.squarePvrtc1 = true; if (mFeatures.disableProgrammableBlending.enabled || !supportsAppleGPUFamily(1)) { const MTLReadWriteTextureTier readWriteTextureTier = [mMetalDevice readWriteTextureSupport]; if (readWriteTextureTier != MTLReadWriteTextureTierNone) { const bool rasterOrderGroupsSupported = !mFeatures.disableRasterOrderGroups.enabled && [mMetalDevice areRasterOrderGroupsSupported]; if (rasterOrderGroupsSupported && isAMD()) { mNativeLimitations.noRasterOrderGroupWithoutAttachmentZero = true; } } } } void DisplayMtl::initializeExtensions() const { // Reset mNativeExtensions = gl::Extensions(); // Enable this for simple buffer readback testing, but some functionality is missing. // NOTE(hqle): Support full mapBufferRangeEXT extension. mNativeExtensions.mapbufferOES = true; mNativeExtensions.mapBufferRangeEXT = true; mNativeExtensions.textureStorageEXT = true; mNativeExtensions.clipControlEXT = true; mNativeExtensions.drawBuffersEXT = true; mNativeExtensions.drawBuffersIndexedEXT = true; mNativeExtensions.drawBuffersIndexedOES = true; mNativeExtensions.fboRenderMipmapOES = true; mNativeExtensions.fragDepthEXT = true; mNativeExtensions.conservativeDepthEXT = true; mNativeExtensions.framebufferBlitANGLE = true; mNativeExtensions.framebufferBlitNV = true; mNativeExtensions.framebufferMultisampleANGLE = true; mNativeExtensions.polygonModeANGLE = true; mNativeExtensions.polygonOffsetClampEXT = true; mNativeExtensions.stencilTexturingANGLE = true; mNativeExtensions.copyTextureCHROMIUM = true; mNativeExtensions.copyCompressedTextureCHROMIUM = false; mNativeExtensions.textureMirrorClampToEdgeEXT = true; mNativeExtensions.depthClampEXT = true; // EXT_debug_marker is not implemented yet, but the entry points must be exposed for the // Metal backend to be used in Chrome (http://anglebug.com/42263519) mNativeExtensions.debugMarkerEXT = true; mNativeExtensions.robustnessEXT = true; mNativeExtensions.robustnessKHR = true; mNativeExtensions.textureBorderClampOES = false; // not implemented yet mNativeExtensions.multiDrawIndirectEXT = true; mNativeExtensions.translatedShaderSourceANGLE = true; mNativeExtensions.discardFramebufferEXT = true; mNativeExtensions.multisampledRenderToTextureEXT = (supportsAppleGPUFamily(1) || mFeatures.enableMultisampledRenderToTextureOnNonTilers.enabled) && mFeatures.hasShaderStencilOutput.enabled && mFeatures.hasDepthAutoResolve.enabled && mFeatures.hasStencilAutoResolve.enabled; // Enable EXT_blend_minmax mNativeExtensions.blendMinmaxEXT = true; mNativeExtensions.EGLImageOES = true; mNativeExtensions.EGLImageExternalOES = false; // NOTE(hqle): Support GL_OES_EGL_image_external_essl3. mNativeExtensions.EGLImageExternalEssl3OES = false; mNativeExtensions.memoryObjectEXT = false; mNativeExtensions.memoryObjectFdEXT = false; mNativeExtensions.semaphoreEXT = false; mNativeExtensions.semaphoreFdEXT = false; mNativeExtensions.instancedArraysANGLE = true; mNativeExtensions.instancedArraysEXT = mNativeExtensions.instancedArraysANGLE; mNativeExtensions.robustBufferAccessBehaviorKHR = false; mNativeExtensions.EGLSyncOES = false; mNativeExtensions.occlusionQueryBooleanEXT = true; mNativeExtensions.disjointTimerQueryEXT = true; mNativeCaps.queryCounterBitsTimeElapsed = 64; mNativeCaps.queryCounterBitsTimestamp = 0; mNativeExtensions.textureFilterAnisotropicEXT = true; mNativeCaps.maxTextureAnisotropy = 16; mNativeExtensions.textureNpotOES = true; mNativeExtensions.texture3DOES = true; mNativeExtensions.textureShadowLodEXT = true; if ([mMetalDevice areProgrammableSamplePositionsSupported]) { mNativeExtensions.textureMultisampleANGLE = true; } mNativeExtensions.sampleVariablesOES = true; if ([mMetalDevice supportsPullModelInterpolation]) { mNativeExtensions.shaderMultisampleInterpolationOES = true; mNativeCaps.subPixelInterpolationOffsetBits = 4; if (supportsAppleGPUFamily(1)) { mNativeCaps.minInterpolationOffset = -0.5f; mNativeCaps.maxInterpolationOffset = +0.5f; } else { // On non-Apple GPUs, the actual range is usually // [-0.5, +0.4375] but due to framebuffer Y-flip // the effective range for the Y direction will be // [-0.4375, +0.5] when the default FBO is bound. mNativeCaps.minInterpolationOffset = -0.4375f; // -0.5 + (2 ^ -4) mNativeCaps.maxInterpolationOffset = +0.4375f; // +0.5 - (2 ^ -4) } } mNativeExtensions.shaderNoperspectiveInterpolationNV = true; mNativeExtensions.shaderTextureLodEXT = true; mNativeExtensions.standardDerivativesOES = true; mNativeExtensions.elementIndexUintOES = true; // GL_OES_get_program_binary mNativeExtensions.getProgramBinaryOES = true; // GL_APPLE_clip_distance mNativeExtensions.clipDistanceAPPLE = true; // GL_ANGLE_clip_cull_distance mNativeExtensions.clipCullDistanceANGLE = true; // GL_NV_pixel_buffer_object mNativeExtensions.pixelBufferObjectNV = true; mNativeExtensions.packReverseRowOrderANGLE = true; if (mFeatures.hasEvents.enabled) { // MTLSharedEvent is only available since Metal 2.1 // GL_NV_fence mNativeExtensions.fenceNV = true; // GL_OES_EGL_sync mNativeExtensions.EGLSyncOES = true; } // GL_KHR_parallel_shader_compile mNativeExtensions.parallelShaderCompileKHR = true; mNativeExtensions.baseInstanceEXT = mFeatures.hasBaseVertexInstancedDraw.enabled; mNativeExtensions.baseVertexBaseInstanceANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled; mNativeExtensions.baseVertexBaseInstanceShaderBuiltinANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled; mNativeExtensions.drawElementsBaseVertexEXT = mFeatures.hasBaseVertexInstancedDraw.enabled; mNativeExtensions.drawElementsBaseVertexOES = mFeatures.hasBaseVertexInstancedDraw.enabled; // Metal uses the opposite provoking vertex as GLES so emulation is required to use the GLES // behaviour. Allow users to change the provoking vertex for improved performance. mNativeExtensions.provokingVertexANGLE = true; // GL_EXT_blend_func_extended mNativeExtensions.blendFuncExtendedEXT = true; mNativeCaps.maxDualSourceDrawBuffers = 1; // GL_ANGLE_shader_pixel_local_storage. if (!mFeatures.disableProgrammableBlending.enabled && supportsAppleGPUFamily(1)) { // Programmable blending is supported on all Apple GPU families, and is always coherent. mNativePLSOptions.type = ShPixelLocalStorageType::FramebufferFetch; // Raster order groups are NOT required to make framebuffer fetch coherent, however, they // may improve performance by allowing finer grained synchronization (e.g., by assigning // attachments to different raster order groups when they don't depend on each other). bool rasterOrderGroupsSupported = !mFeatures.disableRasterOrderGroups.enabled && supportsAppleGPUFamily(4); mNativePLSOptions.fragmentSyncType = rasterOrderGroupsSupported ? ShFragmentSynchronizationType::RasterOrderGroups_Metal : ShFragmentSynchronizationType::Automatic; mNativeExtensions.shaderPixelLocalStorageANGLE = true; mNativeExtensions.shaderPixelLocalStorageCoherentANGLE = true; } else { MTLReadWriteTextureTier readWriteTextureTier = [mMetalDevice readWriteTextureSupport]; if (readWriteTextureTier != MTLReadWriteTextureTierNone) { mNativePLSOptions.type = ShPixelLocalStorageType::ImageLoadStore; // Raster order groups are required to make PLS coherent when using read_write textures. bool rasterOrderGroupsSupported = !mFeatures.disableRasterOrderGroups.enabled && [mMetalDevice areRasterOrderGroupsSupported]; mNativePLSOptions.fragmentSyncType = rasterOrderGroupsSupported ? ShFragmentSynchronizationType::RasterOrderGroups_Metal : ShFragmentSynchronizationType::NotSupported; mNativePLSOptions.supportsNativeRGBA8ImageFormats = !mFeatures.disableRWTextureTier2Support.enabled && readWriteTextureTier == MTLReadWriteTextureTier2; mNativeExtensions.shaderPixelLocalStorageANGLE = true; mNativeExtensions.shaderPixelLocalStorageCoherentANGLE = rasterOrderGroupsSupported; // Set up PLS caps here because the higher level context won't have enough info to set // them up itself. Shader images and other ES3.1 caps aren't fully exposed yet. static_assert(mtl::kMaxShaderImages >= gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES); mNativeCaps.maxPixelLocalStoragePlanes = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; mNativeCaps.maxCombinedDrawBuffersAndPixelLocalStoragePlanes = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES + gl::IMPLEMENTATION_MAX_DRAW_BUFFERS; mNativeCaps.maxShaderImageUniforms[gl::ShaderType::Fragment] = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; mNativeCaps.maxImageUnits = gl::IMPLEMENTATION_MAX_PIXEL_LOCAL_STORAGE_PLANES; } } // Apple Silicon doesn't support image memory barriers, so we ignore the PLS "noncoherent" // qualifier on that hardware. mNativePLSOptions.supportsNoncoherent = !supportsAppleGPUFamily(1); // "The GPUs in Apple3 through Apple8 families only support memory barriers for compute command // encoders, and for vertex-to-vertex and vertex-to-fragment stages of render command encoders." mHasFragmentMemoryBarriers = !supportsAppleGPUFamily(3); } void DisplayMtl::initializeTextureCaps() const { mNativeTextureCaps.clear(); mFormatTable.generateTextureCaps(this, &mNativeTextureCaps); // Re-verify texture extensions. mNativeExtensions.setTextureExtensionSupport(mNativeTextureCaps); // When ETC2/EAC formats are natively supported, enable ANGLE-specific extension string to // expose them to WebGL. In other case, mark potentially-available ETC1 extension as // emulated. if (supportsAppleGPUFamily(1) && gl::DetermineCompressedTextureETCSupport(mNativeTextureCaps)) { mNativeExtensions.compressedTextureEtcANGLE = true; } else { mNativeLimitations.emulatedEtc1 = true; } // Enable EXT_compressed_ETC1_RGB8_sub_texture if ETC1 is supported. mNativeExtensions.compressedETC1RGB8SubTextureEXT = mNativeExtensions.compressedETC1RGB8TextureOES; // Enable ASTC sliced 3D, requires MTLGPUFamilyApple3 if (supportsAppleGPUFamily(3) && mNativeExtensions.textureCompressionAstcLdrKHR) { mNativeExtensions.textureCompressionAstcSliced3dKHR = true; } // Enable ASTC HDR, requires MTLGPUFamilyApple6 if (supportsAppleGPUFamily(6) && mNativeExtensions.textureCompressionAstcLdrKHR) { mNativeExtensions.textureCompressionAstcHdrKHR = true; } // Disable all depth buffer and stencil buffer readback extensions until we need them mNativeExtensions.readDepthNV = false; mNativeExtensions.readStencilNV = false; mNativeExtensions.depthBufferFloat2NV = false; } void DisplayMtl::initializeFeatures() { bool isOSX = TARGET_OS_OSX; bool isCatalyst = TARGET_OS_MACCATALYST; bool isSimulator = TARGET_OS_SIMULATOR; bool isARM = ANGLE_APPLE_IS_ARM; ApplyFeatureOverrides(&mFeatures, getState().featureOverrides); if (mState.featureOverrides.allDisabled) { return; } ANGLE_FEATURE_CONDITION((&mFeatures), allowGenMultipleMipsPerPass, true); ANGLE_FEATURE_CONDITION((&mFeatures), forceBufferGPUStorage, false); ANGLE_FEATURE_CONDITION((&mFeatures), hasExplicitMemBarrier, (isOSX || isCatalyst) && !isARM); ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthAutoResolve, supportsEitherGPUFamily(3, 2)); ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilAutoResolve, supportsEitherGPUFamily(5, 2)); ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve, supportsEitherGPUFamily(3, 1)); // Apple2 does not support comparison functions in MTLSamplerState ANGLE_FEATURE_CONDITION((&mFeatures), allowRuntimeSamplerCompareMode, supportsEitherGPUFamily(3, 1)); // AMD does not support sample_compare with gradients ANGLE_FEATURE_CONDITION((&mFeatures), allowSamplerCompareGradient, !isAMD()); // http://anglebug.com/40644746 // Stencil blit shader is not compiled on Intel & NVIDIA, need investigation. ANGLE_FEATURE_CONDITION((&mFeatures), hasShaderStencilOutput, !isIntel() && !isNVIDIA()); ANGLE_FEATURE_CONDITION((&mFeatures), hasTextureSwizzle, supportsEitherGPUFamily(3, 2) && !isSimulator); ANGLE_FEATURE_CONDITION((&mFeatures), avoidStencilTextureSwizzle, isIntel()); // http://crbug.com/1136673 // Fence sync is flaky on Nvidia ANGLE_FEATURE_CONDITION((&mFeatures), hasEvents, !isNVIDIA()); ANGLE_FEATURE_CONDITION((&mFeatures), hasCheapRenderPass, (isOSX || isCatalyst) && !isARM); // http://anglebug.com/42263788 // D24S8 is unreliable on AMD. ANGLE_FEATURE_CONDITION((&mFeatures), forceD24S8AsUnsupported, isAMD()); // Base Vertex drawing is only supported since GPU family 3. ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw, isOSX || isCatalyst || supportsAppleGPUFamily(3)); ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch, isOSX || isCatalyst || supportsAppleGPUFamily(4)); ANGLE_FEATURE_CONDITION((&mFeatures), allowSeparateDepthStencilBuffers, supportsAppleGPUFamily(1) && !isSimulator); ANGLE_FEATURE_CONDITION((&mFeatures), emulateTransformFeedback, true); ANGLE_FEATURE_CONDITION((&mFeatures), emulateAlphaToCoverage, isSimulator || !supportsAppleGPUFamily(1)); ANGLE_FEATURE_CONDITION((&mFeatures), writeHelperSampleMask, supportsAppleGPUFamily(1)); ANGLE_FEATURE_CONDITION((&mFeatures), multisampleColorFormatShaderReadWorkaround, isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), copyIOSurfaceToNonIOSurfaceForReadOptimization, isIntel() || isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), copyTextureToBufferForReadOptimization, isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), forceNonCSBaseMipmapGeneration, isIntel()); ANGLE_FEATURE_CONDITION((&mFeatures), preemptivelyStartProvokingVertexCommandBuffer, isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseStagedBufferUpdates, isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseManagedStorageModeForBuffers, isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), alwaysUseSharedStorageModeForBuffers, isIntel()); ANGLE_FEATURE_CONDITION((&mFeatures), useShadowBuffersWhenAppropriate, isIntel()); // At least one of these must not be set. ASSERT(!mFeatures.alwaysUseManagedStorageModeForBuffers.enabled || !mFeatures.alwaysUseSharedStorageModeForBuffers.enabled); ANGLE_FEATURE_CONDITION((&mFeatures), uploadDataToIosurfacesWithStagingBuffers, isAMD()); // Render passes can be rendered without attachments on Apple4 , mac2 hardware. ANGLE_FEATURE_CONDITION(&(mFeatures), allowRenderpassWithoutAttachment, supportsEitherGPUFamily(4, 2)); ANGLE_FEATURE_CONDITION((&mFeatures), enableInMemoryMtlLibraryCache, true); ANGLE_FEATURE_CONDITION((&mFeatures), enableParallelMtlLibraryCompilation, true); // Uploading texture data via staging buffers improves performance on all tested systems. // http://anglebug.com/40644905: Disabled on intel due to some texture formats uploading // incorrectly with staging buffers ANGLE_FEATURE_CONDITION(&mFeatures, alwaysPreferStagedTextureUploads, true); ANGLE_FEATURE_CONDITION(&mFeatures, disableStagedInitializationOfPackedTextureFormats, isIntel() || isAMD()); ANGLE_FEATURE_CONDITION((&mFeatures), generateShareableShaders, true); // http://anglebug.com/42266609: NVIDIA GPUs are unsupported due to scarcity of the hardware. ANGLE_FEATURE_CONDITION((&mFeatures), disableMetalOnNvidia, true); // The AMDMTLBronzeDriver seems to have bugs flushing vertex data to the GPU during some kinds // of buffer uploads which require a flush to work around. ANGLE_FEATURE_CONDITION((&mFeatures), flushAfterStreamVertexData, isAMDBronzeDriver()); // TODO(anglebug.com/40096869): GPUs that don't support Mac GPU family 2 or greater are // unsupported by the Metal backend. ANGLE_FEATURE_CONDITION((&mFeatures), requireGpuFamily2, true); // http://anglebug.com/42266744: Rescope global variables which are only used in one function to // be function local. Disabled on AMD FirePro devices: http://anglebug.com/40096898 ANGLE_FEATURE_CONDITION((&mFeatures), rescopeGlobalVariables, !isAMDFireProDevice()); // Apple-specific pre-transform for explicit cubemap derivatives ANGLE_FEATURE_CONDITION((&mFeatures), preTransformTextureCubeGradDerivatives, supportsAppleGPUFamily(1)); // Apple-specific cubemap derivative transformation is not compatible with // the textureGrad shadow sampler emulation. The latter is used only on AMD. ASSERT(!mFeatures.preTransformTextureCubeGradDerivatives.enabled || mFeatures.allowSamplerCompareGradient.enabled); // Metal compiler optimizations may remove infinite loops causing crashes later in shader // execution. http://crbug.com/41486305 ANGLE_FEATURE_CONDITION((&mFeatures), ensureLoopForwardProgress, true); } angle::Result DisplayMtl::initializeShaderLibrary() { angle::ObjCPtr err; #if ANGLE_METAL_XCODE_BUILDS_SHADERS || ANGLE_METAL_HAS_PREBUILT_INTERNAL_SHADERS mDefaultShaders = mtl::CreateShaderLibraryFromStaticBinary(getMetalDevice(), gDefaultMetallib, &err); #else const bool disableFastMath = false; const bool usesInvariance = true; mDefaultShaders = mtl::CreateShaderLibrary(getMetalDevice(), gDefaultMetallibSrc, {}, disableFastMath, usesInvariance, &err); #endif if (err) { ERR() << "Internal error: " << err.get().localizedDescription.UTF8String; return angle::Result::Stop; } return angle::Result::Continue; } id DisplayMtl::getDefaultShadersLib() { return mDefaultShaders; } bool DisplayMtl::supportsAppleGPUFamily(uint8_t iOSFamily) const { return mtl::SupportsAppleGPUFamily(getMetalDevice(), iOSFamily); } bool DisplayMtl::supportsMacGPUFamily(uint8_t macFamily) const { return mtl::SupportsMacGPUFamily(getMetalDevice(), macFamily); } bool DisplayMtl::supportsEitherGPUFamily(uint8_t iOSFamily, uint8_t macFamily) const { return supportsAppleGPUFamily(iOSFamily) || supportsMacGPUFamily(macFamily); } bool DisplayMtl::supports32BitFloatFiltering() const { #if !TARGET_OS_WATCH return [mMetalDevice supports32BitFloatFiltering]; #else return false; #endif } bool DisplayMtl::supportsBCTextureCompression() const { if (@available(macCatalyst 16.4, iOS 16.4, *)) { return [mMetalDevice supportsBCTextureCompression]; } #if TARGET_OS_MACCATALYST return true; // Always true on old Catalyst #else return false; // Always false on old iOS #endif } bool DisplayMtl::supportsDepth24Stencil8PixelFormat() const { #if TARGET_OS_OSX || TARGET_OS_MACCATALYST return [mMetalDevice isDepth24Stencil8PixelFormatSupported]; #else return false; #endif } bool DisplayMtl::isAMD() const { return angle::IsAMD(mMetalDeviceVendorId); } bool DisplayMtl::isAMDBronzeDriver() const { if (!isAMD()) { return false; } if (mComputedAMDBronze) { return mIsAMDBronze; } // All devices known to be covered by AMDMTlBronzeDriver. // // Note that we can not compare substrings because some devices // (AMD Radeon Pro 560) are substrings of ones supported by a // later driver (AMD Radeon Pro 5600M). NSString *kMTLBronzeDeviceNames[22] = { @"FirePro D300", @"FirePro D500", @"FirePro D700", @"Radeon R9 M290", @"Radeon R9 M290X", @"Radeon R9 M370X", @"Radeon R9 M380", @"Radeon R9 M390", @"Radeon R9 M395", @"Radeon Pro 450", @"Radeon Pro 455", @"Radeon Pro 460", @"Radeon Pro 555", @"Radeon Pro 555X", @"Radeon Pro 560", @"Radeon Pro 560X", @"Radeon Pro 570", @"Radeon Pro 570X", @"Radeon Pro 575", @"Radeon Pro 575X", @"Radeon Pro 580", @"Radeon Pro 580X"}; for (size_t i = 0; i < ArraySize(kMTLBronzeDeviceNames); ++i) { if ([[mMetalDevice name] hasSuffix:kMTLBronzeDeviceNames[i]]) { mIsAMDBronze = true; break; } } mComputedAMDBronze = true; return mIsAMDBronze; } bool DisplayMtl::isAMDFireProDevice() const { if (!isAMD()) { return false; } return [[mMetalDevice name] containsString:@"FirePro"]; } bool DisplayMtl::isIntel() const { return angle::IsIntel(mMetalDeviceVendorId); } bool DisplayMtl::isNVIDIA() const { return angle::IsNVIDIA(mMetalDeviceVendorId); } bool DisplayMtl::isSimulator() const { return TARGET_OS_SIMULATOR; } angle::ObjCPtr DisplayMtl::getOrCreateSharedEventListener() { if (!mSharedEventListener) { ANGLE_MTL_OBJC_SCOPE { mSharedEventListener = angle::adoptObjCPtr([[MTLSharedEventListener alloc] init]); ASSERT(mSharedEventListener); // Failure here most probably means a sandbox issue. } } return mSharedEventListener; } } // namespace rx