[updated] there is no retina support on mac (catalina / big sur)

I’m in the “replace debug panels with pretty stuff” phase of my project, so it’s time to get retina support working. My buffer’s not getting set up at the native screen resolution – is there something I need to set to make that happen?

I see that base.win.supports_pixel_zoom()=False

Image: full resolution on window frame, but reduced resolution in window content
osx_bad_buffer_resolution

We are currently disabling HiDPI on Cocoa. See related issues:

I think what we need to do is offer a switch to have the same behaviour as we do on Windows (ie. with dpi-aware)—rendering at full resolution by default while allowing an opt-in to DPI scaling. @el-dee may also wish to weigh in on this.

Feel free to open a new enhancement request in the issue tracker for this.

k… so short-term… : if I do a local build that comments out [_view setWantsBestResolutionOpenGLSurface:NO]; (and figure out how to make a distributable app with a local build of panda3d…), I’d get full-resolution, but potentially have issues when switching to and from fullscreen-mode?

Issue opened: add opt-in for retina/hiDPI support on OSX · Issue #1186 · panda3d/panda3d · GitHub

Looking at my test, I have a workaround by using QPanda3d and subclassing QPanda3DWidget.

Code: subclass of QPanda3dWidget with an explicit scale factor
class QPan (QPanda3DWidget):
    def __init__(self, panda3DWorld, scale=2):
        self.explicit_scale = scale
        super().__init__(panda3DWorld)

    def resizeEvent(self, evt):
        lens = self.panda3DWorld.cam.node().get_lens()
        lens.set_film_size(self.initial_film_size.width() * evt.size().width() / self.initial_size.width(),
                           self.initial_film_size.height() * evt.size().height() / self.initial_size.height())
        self.panda3DWorld.buff.setSize(evt.size().width() * self.explicit_scale, evt.size().height() * self.explicit_scale)


    # Use the paint event to pull the contents of the panda texture to the widget
    def paintEvent(self, event):
        if self.panda3DWorld.screenTexture.mightHaveRamImage():
            self.panda3DWorld.screenTexture.setFormat(Texture.FRgba32)
            data = self.panda3DWorld.screenTexture.getRamImage().getData()
            img = QImage(data, self.panda3DWorld.screenTexture.getXSize(), self.panda3DWorld.screenTexture.getYSize(),
                         QImage.Format_ARGB32).mirrored()
            self.paintSurface.begin(self)

            sz = Point2(self.panda3DWorld.screenTexture.getXSize(), self.panda3DWorld.screenTexture.getYSize()) / self.explicit_scale
            self.paintSurface.drawImage(QRectF(0,0,*sz), img)
            self.paintSurface.end()

This text will be hidden

I had to recollect my memories about the problem (It’s still fuzzy, so please correct me if I say something wrong) :

Panda3D never supported HiDPI on macOS, it relied on Cocoa/AppKit to perform the default upscaling on Retina screen (usually 2x, or 1.8x). (And, starting with Catalina, the default value of the configuration flag changed, triggering the problem and the fix mentioned above.)

If an application does not enable the support of HiDPI screen, the following mechanism occurs : the application writes to the frame buffer as if one point in the framebuffer is a pixel on screen, then Cocoa map the framebuffer onto the backing store using the requested backing scale factor (as set on the window object), then scale again the store to the physical screen resolution.

If the HiDPI support is enabled, AppKit allocates (and can change it seems !) the size of the framebuffer according to the actual screen resolution and backing scale factor. Also, depending on the API, backing store units or view units must be used, otherwise you have magnified or distorted rendering. NSView methods use view units, but some OpenGL functions typically use backing store units (but not all)

So, to support HiDPI, wantsBestResolutionOpenGLSurface must be set to YES but then some of the call to OpenGL functions, i.e. glViewport, glScissor, … must use coordinates converted from view to backing. And if I understand the doc correctly, this can not be cached or done the other way around, using backing store units everywhere and converting to view units when interacting with App Kit, as the size and scale of the framebuffer can change when the display configuration changes or if the window is moved across screens.

When using QT, you are actually bypassing all the view and window management of Panda3D as you render the scene to a texture that is then mapped using the correct coordinate units by QT

See Apple Developer Documentation and Optimizing OpenGL for High Resolution

It sounds like it works pretty much the same way as the dpi-aware setting on Windows, then. We would just need to report the window size as being twice as large so that Panda will do all the math using backing store units and pass them to OpenGL as such.

I think the right action would be to make the dpi-aware mode functional on macOS as it is on Windows.

I’m working on adding dpi-aware support using this postulate : Size and position specified in WindowProperties will be using backing store units (“pixels”) and no longer point units (display independent size). That means a dpi-aware Panda3D application will appears half the size on a retina screen than on a classic screen.

The other possibility is to use point units for WindowProperties, so that the app would have the same visual size on any display, but code that will use the window size to scale things (image, buffer, FoV, …) will no longer behave as expected.

Two implementation detail questions :

  • The config property dpi-aware is in windisplay, should it be promoted to display ?
  • The Info.plist of a bundled app must have the property NSHighResolutionCapable set to YES that means a new flag must be added to build_apps (I might have a way to do this programatically in the app code, but if that doesn’t work that would be needed)

Thanks for working on this! Matching the behaviour of dpi-aware on Windows (ie. having dpi-aware true forces the use of device pixels everywhere) sounds like a good thing to do for now; later we can work out the details of how dimensions could be specified in a device-independent manner in Panda. There is an (unfortunately stagnated) discussion about that here:

If we must add a new flag to build_apps, so be it, but I would much rather it be controlled by dpi-aware if possible.

It took much longer that I expected (Writing code for cocoa is never boring…), but here is eventually the PR that adds support for High-DPI : cocoadisplay: Add support for high-dpi screens by el-dee · Pull Request #1308 · panda3d/panda3d · GitHub

1 Like