Retired Document
Important: OpenGL was deprecated in macOS 10.14. To create high-performance code on GPUs, use the Metal framework instead. See Metal.
Optimizing OpenGL for High Resolution
OpenGL is a pixel-based API so the NSOpenGLView
class does not provide high-resolution surfaces by default. Because adding more pixels to renderbuffers has performance implications, you must explicitly opt in to support high-resolution screens. It’s easy to enable high-resolution backing for an OpenGL view. When you do, you’ll want to perform a few additional tasks to ensure the best possible high-resolution experience for your users.
Enable High-Resolution Backing for an OpenGL View
You can opt in to high resolution by calling the method setWantsBestResolutionOpenGLSurface:
when you initialize the view, and supplying YES
as an argument:
[self setWantsBestResolutionOpenGLSurface:YES]; |
If you don’t opt in, the system magnifies the rendered results.
The wantsBestResolutionOpenGLSurface
property is relevant only for views to which an NSOpenGLContext
object is bound. Its value does not affect the behavior of other views. For compatibility, wantsBestResolutionOpenGLSurface
defaults to NO
, providing a 1-pixel-per-point framebuffer regardless of the backing scale factor for the display the view occupies. Setting this property to YES
for a given view causes AppKit to allocate a higher-resolution framebuffer when appropriate for the backing scale factor and target display.
To function correctly with wantsBestResolutionOpenGLSurface
set to YES
, a view must perform correct conversions between view units (points) and pixel units as needed. For example, the common practice of passing the width and height of [self bounds]
to glViewport()
will yield incorrect results at high resolution, because the parameters passed to the glViewport()
function must be in pixels. As a result, you’ll get only partial instead of complete coverage of the render surface. Instead, use the backing store bounds:
[self convertRectToBacking:[self bounds]]; |
You can also opt in to high resolution by enabling the Supports Hi-Res Backing setting for the OpenGL view in Xcode, as shown in Figure 3-1.
Set Up the Viewport to Support High Resolution
The viewport dimensions are in pixels relative to the OpenGL surface. Pass the width and height to glViewPort
and use 0,0 for the x
and y
offsets. Listing 3-1 shows how to get the view dimensions in pixels and take the backing store size into account.
Listing 3-1 Setting up the viewport for drawing
- (void)drawRect:(NSRect)rect // NSOpenGLView subclass |
{ |
// Get view dimensions in pixels |
NSRect backingBounds = [self convertRectToBacking:[self bounds]]; |
GLsizei backingPixelWidth = (GLsizei)(backingBounds.size.width), |
backingPixelHeight = (GLsizei)(backingBounds.size.height); |
// Set viewport |
glViewport(0, 0, backingPixelWidth, backingPixelHeight); |
// draw… |
} |
You don’t need to perform rendering in pixels, but you do need to be aware of the coordinate system you want to render in. For example, if you want to render in points, this code will work:
glOrtho(NSWidth(bounds), NSHeight(bounds),...) |
Adjust Model and Texture Assets
If you opt in to high-resolution drawing, you also need to adjust the model and texture assets of your app. For example, when running on a high-resolution display, you might want to choose larger models and more detailed textures to take advantage of the increased number of pixels. Conversely, on a standard-resolution display, you can continue to use smaller models and textures.
If you create and cache textures when you initialize your app, you might want to consider a strategy that accommodates changing the texture based on the resolution of the display.
Check for Calls Defined in Pixel Dimensions
These functions use pixel dimensions:
glViewport (GLint x, GLint y, GLsizei width, GLsizei height)
glScissor (GLint x, GLint y, GLsizei width, GLsizei height)
glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, ...)
glLineWidth (GLfloat width)
glRenderbufferStorage (..., GLsizei width, GLsizei height)
glTexImage2D (..., GLsizei width, GLsizei height, ...)
Tune OpenGL Performance for High Resolution
Performance is an important factor when determining whether to support high-resolution content. The quadrupling of pixels that occurs when you opt in to high resolution requires more work by the fragment processor. If your app performs many per-fragment calculations, the increase in pixels might reduce its frame rate. If your app runs significantly slower at high resolution, consider the following options:
Optimize fragment shader performance. (See Tuning Your OpenGL Application.)
Choose a simpler algorithm to implement in your fragment shader. This reduces the quality of each individual pixel to allow for rendering the overall image at a higher resolution.
Use a fractional scale factor between 1.0 and 2.0. A scale factor of 1.5 provides better quality than a scale factor of 1.0, but it needs to fill fewer pixels than an image scaled to 2.0.
Multisampling antialiasing can be costly with marginal benefit at high resolution. If you are using it, you might want to reconsider.
The best solution depends on the needs of your OpenGL app; you should test more than one of these options and choose the approach that provides the best balance between performance and image quality.
Use a Layer-Backed View to Overlay Text on OpenGL Content
When you draw standard controls and Cocoa text to a layer-backed view, the system handles scaling the contents of that layer for you. You need to perform only a few steps to set and use the layer. Compare the controls and text in standard and high resolutions, as shown in Figure 3-2. The text looks the same on both without any additional work on your part.
Set the
wantsLayer
property of yourNSOpenGLView
subclass toYES
.Enabling the
wantsLayer
property of anNSOpenGLView
object activates layer-backed rendering of the OpenGL view. Drawing a layer-backed OpenGL view proceeds mostly normally through the view’sdrawRect:
method. The layer-backed rendering mode uses its ownNSOpenGLContext
object, which is distinct from theNSOpenGLContext
that the view uses for drawing in non-layer-backed mode.AppKit automatically creates this context and assigns it to the view by invoking the
setOpenGLContext:
method. The view’sopenGLContext
accessor will return the layer-backed OpenGL context (rather than the non-layer-backed context) while the view is operating in layer-backed mode.Create the layer content either as a XIB file or programmatically.
Add the layer to the OpenGL view by calling the
addSublayer:
method.
Use an Application Window for Fullscreen Operation
For the best user experience, if you want your app to run full screen, create a window that covers the entire screen. This approach offers two advantages:
The system provides optimized context performance.
Users will be able to see critical system dialogs above your content.
You should avoid changing the display mode of the system.
Convert the Coordinate Space When Hit Testing
Always convert window event coordinates when performing hit testing in OpenGL. The locationInWindow
method of the NSEvent
class returns the receiver’s location in the base coordinate system of the window. You then need to call the convertPoint:fromView:
method to get the local coordinates for the OpenGL view.
NSPoint aPoint = [theEvent locationInWindow]; |
NSPoint localPoint = [myOpenGLView convertPoint:aPoint fromView:nil]; |
Copyright © 2004, 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-06-04