Hello colleagues, in my recent feature I have faced with very fascinated problems placed between legacy and iOS API that was deprecated.
I don't want to make very long story, so shortly in one feature we uses framework that works with AR technology. That framework is using OpenGL technology for multi platforms. Basement of its use is that framework makes UIView with layer CAEAGLLayer with some connection to OpenGL. And everything could looks fine, but CAEAGLLayer was deprecated in iOS12. so it was really long time ago.
Apple has been steering developers towards its modern, high-performance graphics API, Metal, for years.
The direct equivalent of CAEAGLLayer is CAMetalLayer, which is part of the Metal framework. It's a Core Animation layer that manages a pool of drawables (typically MTLTexture objects) that you render into using Metal.
Here are the main errors and issues what could encounter when using UIView with CAEAGLLayer instead of CAMetalLayer:
Critical System-Level Errors
1. Context Loss and Corruption
Siri can knock out the current EAGLContext, causing all OpenGL calls to have no effect ios9 - CAEAGLLayer - OpenGL API's have no effect after Siri input - Stack Overflow. This is a particularly nasty bug where the EAGL context becomes invalid after system interactions, requiring you to force your EAGLContext to be current every time you call presentRenderBuffer.
2. EXC_BAD_ACCESS Crashes
The renderbufferStorage method can return false and cause crashes, particularly when GPU Frame Capture is enabled Stack Overflow. These crashes are often related to memory alignment issues and can be intermittent, making them extremely difficult to debug.
3. Rotation and Layout Crashes
Applications crash specifically during interface rotation changes when the view frame changes iphone - glDrawArrays crash with EXC_BAD_ACCESS - Stack Overflow, often taking several minutes of rotation to reproduce. This happens because CAEAGLLayer doesn't handle frame buffer recreation gracefully during layout changes.
Memory Management Problems
4. Memory Pressure and Leaks
Graphics applications using CAEAGLLayer can consume excessive memory (250MB+) without proper cleanup Memory warning OpenGL iOS Application - Stack Overflow, leading to memory warnings and eventual crashes. The OpenGL ES pipeline doesn't have the same automatic memory management as Metal.
5. Out-of-Memory Crashes
CAEAGLLayer applications are more prone to out-of-memory crashes because:
OpenGL ES has less efficient memory management
Texture memory isn't automatically reclaimed
Frame buffer objects can accumulate without proper cleanup
iOS Version Compatibility Issues
6. iOS Version-Specific Bugs
Different iOS versions introduce unique CAEAGLLayer problems:
iOS 10 introduced specific crashes on iPhone 6S/7 and iPad Pro that didn't exist in previous versions OpenGL ES crash on iPhone ios 10 | Apple Developer Forums
iOS 8.3 had widespread EXC_BAD_ACCESS issues that were fixed in iOS 8.4 GPUImageView EXC_BAD_ACCESS · Issue #2022 ·
Each major iOS update brings new incompatibilities
7. Deprecation Warnings and Future Incompatibility
Starting with iOS 12, CAEAGLLayer is officially deprecated, meaning:
Build warnings in Xcode
No future bug fixes or optimizations
Eventual removal in future iOS versions
App Store submission issues
Performance and Stability Issues
8. Unpredictable Rendering Failures
Frame buffer setup can fail silently
Width and height properties can return 0 unexpectedly ios - setting a CAEAGLLayer properties for OpenGL ES? - Stack Overflow
Rendering context can become invalid without warning
9. GPU Resource Conflicts
Context switching between applications can corrupt the OpenGL state
Background/foreground transitions cause rendering failures
Multiple EAGL contexts can interfere with each other
10. Device-Specific Problems
Some rendering issues only appear on specific device models (iPhone 6S, iPad Pro) while working fine on others OpenGL ES crash on iPhone ios 10 | Apple Developer Forums, making testing and debugging extremely challenging.
11. Thread Safety Problems (Threading and Synchronization Issues)
CAEAGLLayer operations must be called on the main thread ios - OpenGL ES 2.0 renderbufferStorage crash - Stack Overflow, and violations can cause crashes. The OpenGL ES context is not thread-safe, leading to:
Race conditions during rendering
Context corruption from background threads
Synchronization issues with Core Animation
After checking all this out I was mad how could we be still use CAEAGLLayer.
So my next step was to improve current situation/code and to start using something modern.
And my next internal question was what should we do instead of using CAEAGLLayer.
Firstly I found out how deep was CAEAGLLayer deprecated.
In iOS we still have GLKit framework, GLKView and GLKViewController that are using OpenGL ES. Developers might not be directly using CAEAGLLayer, as GLKView manages it internally Thanks to china colleagues. Apple has not deprecated these GLKit classes, though they are built on the deprecated OpenGL ES substrate.
And this is why CAEAGLLayer not crashes all the time and main solution is to start use CAMetalLayer, because it is the direct equivalent of CAEAGLLayer.
Why the Move to Metal is Necessary?
Apple deprecated OpenGL ES for several compelling reasons:
Performance:
Metal provides lower-overhead access to the GPU, reducing CPU overhead and allowing for more complex scenes and better efficiency. This is crucial for achieving high framerates, complex visuals, and good battery life.
Modern Features:
Metal gives developers direct access to modern GPU features and shader languages that OpenGL ES either doesn't support or abstracts poorly.
Control:
Metal offers finer-grained control over the GPU, enabling advanced techniques like GPU-driven rendering and tight resource management.
Apple's Strategic Direction:
Apple is all-in on Metal across all its platforms (iOS, macOS, iPadOS, tvOS). Investing in OpenGL ES means investing in a technology with no future on Apple devices.
The deprecation of CAEAGLLayer is just a symptom of this larger shift.
Moving to CAMetalLayer
While moving to Metal and CAMetalLayer is the primary thing to understand what am I trying to achieve. And I know it.
And common suggestions for you are:
1) Learn Metal and use CAMetalLayer. This is the intended, future-proof replacement for any low-level graphics work involving CAEAGLLayer. Apple's Metal documentation and sample codes are excellent resources.
2) Don't immediately assume you need low-level Metal. For many applications, high-level frameworks like SpriteKit, SceneKit, or even Core Animation are more than capable and far more productive.
3) If Stuck with Legacy OpenGL ES Code:
For now, GLKView may still work as a temporary crutch.
4) Plan a migration to Metal. Apple provides a Metal for OpenGL Developers guide to help with this transition.
5) Use Game Engines: For cross-platform games or complex 3D, let an engine like Unity or Unreal handle the graphics API abstraction.
The deprecation warning is a clear signal to modernize your graphics code. Embracing Metal is the best way to ensure your app remains performant, feature-rich, and compatible with future versions of iOS.
Bridging EAGL with Metal Using MetalEAGLLayer
My Solution was to create MetalEAGLLayer an adapter layer that bridges the gap between CAEAGLLayer expectations and modern Metal rendering.
The MetalEAGLLayer
class serves as a drop-in replacement that maintains API compatibility while leveraging Metal's superior performance and stability.
Key Features of the Adapter
The MetalEAGLLayer
implementation should provide several critical features:
Seamless API Compatibility
The adapter maintains the same interface that frameworks expect fromCAEAGLLayer
, including drawable properties, pixel formats, and configuration methods.Automatic Format Translation
It intelligently converts between EAGL color formats and Metal pixel formats:kEAGLColorFormatRGBA8
maps toMTLPixelFormat.bgra8Unorm
kEAGLColorFormatRGB565
maps toMTLPixelFormat.b5g6r5Unorm
kEAGLColorFormatSRGBA8
maps toMTLPixelFormat.bgra8Unorm_srgb
Dynamic Sizing and Scaling
The adapter automatically handles drawable size updates when the layer bounds change and properly manages content scaling for high-resolution displays.
Implementation
The adapter follows a composition pattern, containing a CAMetalLayer
instance while inheriting from CAEAGLLayer
. This approach allows it to:
- Present a familiar EAGL interface to legacy frameworks
- Internally use Metal for actual rendering operations
- Handle all the necessary property forwarding and format conversions
Here's how the key components work together:
final class MetalEAGLLayer: CAEAGLLayer {
let metalLayer: CAMetalLayer
// Forwards Metal-specific properties
var nextDrawable: CAMetalDrawable? {
return metalLayer.nextDrawable()
}
// Handles EAGL compatibility
func setDrawableProperties(_ properties: [AnyHashable: Any]!) {
// Convert EAGL properties to Metal equivalents
}
}
The practical implementation shows how seamlessly this adapter integrates into existing codebases:
private func createViewWithMetalLayer() -> UIView? {
guard let viewSize else {
debugPrint("createArViewWithMetalLayer failed because viewSize is nil")
return nil
}
let frame = CGRect(origin: .zero, size: viewSize)
let view = UIView(frame: frame)
guard let metalEAGLLayer: MetalEAGLLayer = createMetalLayer(for: view) else {
debugPrint("failed create MetalEAGLLayer in createArViewWithMetalLayer")
return nil
}
// The framework accepts our adapter as if it were a standard CAEAGLLayer
someARFrameworkInstance?.doSomeInitialization(
viewSize: viewSize,
window: metalEAGLLayer // wait CAEAGLLayer, send MetalEAGLLayer
)
view.layer.addSublayer(metalEAGLLayer)
return view
}
So, easy.
Benefits of This Approach
Stability: By using Metal under the hood, applications avoid the unpredictable crashes and compatibility issues associated with deprecated OpenGL ES APIs.
Performance: Metal provides better performance characteristics and more efficient GPU utilization compared to the deprecated OpenGL ES stack.
Future-Proofing: This approach ensures your application will continue to work as iOS evolves, without requiring updates to third-party frameworks.
Minimal Code Changes: The adapter pattern means existing code that expects CAEAGLLayer
requires minimal or no modifications.
Debugging Support: The implementation includes comprehensive debugging support and proper error handling for development builds.
Technical Considerations
When implementing this adapter pattern, consider these important aspects:
Property Synchronization: Ensure all relevant properties between the EAGL and Metal layers stay synchronized, particularly contentsScale
, bounds
, and drawableSize
.
Lifecycle Management: The adapter must properly handle the lifecycle of both the parent EAGL layer and the contained Metal layer.
Format Compatibility: Not all EAGL formats have direct Metal equivalents, so the adapter includes fallback logic for edge cases.
iOS Version Compatibility: Some features like extended dynamic range content require iOS version checks to maintain backward compatibility.
Conclusion
The MetalEAGLLayer
adapter represents a pragmatic solution to a common iOS development challenge. By bridging legacy EAGL APIs with modern Metal rendering, developers can continue using valuable third-party frameworks while benefiting from improved stability and performance.
This approach demonstrates how thoughtful adapter patterns can extend the life of existing codebases while gradually transitioning to newer technologies. As the iOS ecosystem continues to evolve, such bridging solutions become invaluable tools for maintaining application stability and user experience.
The implementation shown here provides a robust foundation that can be extended and customized based on specific framework requirements, making it a versatile solution for many legacy OpenGL ES compatibility scenarios in modern iOS development.
MetalEAGLLayer adapter eliminates those issues because:
Metal is actively maintained - No deprecation warnings or compatibility issues
Better memory management - Automatic resource cleanup and more efficient GPU memory usage
Thread safety - Metal command buffers can be created on any thread
Consistent behavior - No device-specific or iOS version-specific bugs
Future-proof - Apple's recommended graphics API with ongoing optimization
The adapter pattern essentially provides a stable, modern foundation while maintaining compatibility with legacy frameworks that still expect CAEAGLLayer interfaces.
Top comments (0)