How to discover what is the native resolution of a display?

dear mario

you previously alerted me that finding the maximum available screen resolution does not, in general, provide the native resolution, as the computer may offer a resolution higher than native. Indeed, that’s true of the 15" macBook Pro. However, you have emphasized many times that to get the best performancem, it’s important to run at native resolution . Thus would be highly desirable to be able to detect that issue in software. You said there’s no easy way to discover the native resolution.

Here’s an idea. What if we set the screen resolution to the highest available, and then call

screenBufferRect=Screen(‘Rect’,o.screen); % What the software sees.
screenRect=Screen(‘Rect’,o.screen,1); % What the observer sees.

won’t screenRect tell us the native resolution?

Best

Denis

p.s
Here’s code to determine the maximum resolution available.

res=Screen(‘Resolutions’,iScreen);
machine.maxSize{iScreen}=[0 0];
for i=1:length(res)
if res(i).width>machine.maxSize{iScreen}(1)
machine.maxSize{iScreen}=[res(i).width res(i).height];
end
end

Someone asked, in an earlier post, how I changed screen resolution. I use the Psychtoolbox Screen ‘Resolution’ command. Like this:

if oo(1).permissionToChangeResolution
% CAUTION: Screen forbids changing video display settings via
% Screen(‘Resolutions’) while onscreen windows are open.
s=GetSecs;
fprintf(‘WARNING: Trying to optimize screen resolution for this test. … ‘);
if ~isempty(Screen(‘windows’))
fprintf(’\n’);
warning(‘Closing all open window(s), as required, before changing screen resolution.’);
Screen(‘CloseAll’);
window=[];
oo(1).pleaseReopenWindow=true;
end
oo(1).oldResolution=Screen(‘Resolution’,oo(1).screen,oo(1).nativeWidth,oo(1).nativeHeight);
res=Screen(‘Resolution’,oo(1).screen);
fprintf('Done (%.1f s). ',GetSecs-s);
if res.width==oo(1).nativeWidth
fprintf(‘SUCCESS!\n’);
else
warning(‘FAILED.’);
res
end
end

hi Denis,

by definition the native resolution of an LCD panel is the same as the maximum resolution, i.e. when there is 1:1 virtual -> physical pixel mapping happening.

the issue with the Retina MacBook Pros is that the default resolution is pixel-doubled, and is therefore lower than the native resolution of the panel. the pixel-doubling reduces GPU load, as most applications don’t need to be calculating and pushing the very high native resolution that the panels are capable of…

That used to be like that, but:

a) I bought a monitor recently which exposes a 3840x2100@30Hz mode over HDMI, although it natively has 2560x1440@144Hz resolution and refresh rate, and then apparently down-scales and potentially resamples in time if you actually feed that to it. So there are exceptions. However, the monitor does mark its native mode as preferred mode, so the OS will choose the preferred/native mode by default when plugging in such a monitor, not the weird mode.

In general, good ol’ analog VGA CRT monitors do not have a native resolution, they work with any combo on resolution x refresh rate within the limits of their electronics. So for timing and stimulus integrity it doesn’t matter what you choose, although Denis certainly knows more enough about how CRT’s behave, given that he wrote an article about it over 2 decades ago.

In general, most externally connected digital displays only have one native mode in which no resampling in time and space is applied. This mode is marked as preferred mode, so operating systems will choose that mode by default when plugging in the monitor over a digital link of sufficient bandwidth. Most of the time that native mode happens to coincide with the advertised maximum resolution and refresh rate, so the “go for the biggest” heuristic tends to work. The catch here is that high resolutions may need modern graphics cards, high quality cables, support for the latest HDMI or DisplayPort versions, and sufficient bandwidth. Things like driving multiple displays in parallel, or at increased color bitdepth, or on not top-notch cables can cause the display driver to restrict allowed modes to less than the maximum, in which case it might be impossible to drive the display in a mode that doesn’t screw up timing or quality. On Displayport the gpu performs link-training to measure link signal quality and restrict to the highest setting achievable without trouble.

So if you buy a new monitor, make sure that its native resolution x refresh rate matches what your gpu and cables can actually support.

For internal displays, and especially HiDPI / Retina displays the situation is different in my experience:

a) Sane operating systems like Linux and Windows: The reported maximum video mode is the native resolution, so the heuristic works. At least i’ve never seen anything else.

b) Apples trainwreck: Depending on specific macOS version and type of Apple hw, it can be pixel doubling or other rescaling by the desktop compositor, ie. the compositor runs suitable shaders on the gpu. On iMacs however, the system does something else: First, i lies about the available video modes and their properties, so the OS API’s for figuring this out are all broken and expose non-existent resolutions as native unscaled display resolutions. Then the compositor upscales images from one of the advertised modes (in the settings GUI) to a resolution way higher than what the display can do. This is the resolution in which the system framebuffer (== the gpu display frontend) operates. Then it programs the gpu’s own hardware scaler to downscale that way too high resolution back to the native resolution of the panel (== in realtime during scanout by the gpu display backend). Why? Only the differently gifted people inside Apple may know.

PTB tries to deal with Apples special nightmare by always switching the display to the preferreed (== hopefully native) resolution iff it advertises such a resolution. Then it uses its own rescaling on the gpu to make things right’ish, unless you use the PsychImaging(‘AddTask’,‘General’,‘UseRetinaResolution’) task to tell it to not try to hack around this mess. Probably people should always use that now, given that our hacks break down at some scaled resolutions. The default behaviour is mostly for backwards compatibility, but given how little Apple cares about such concepts, this is a hopeless uphill battle.

On iMac’s, where the OS lies about preferred modes / native resolutions of Retina panels as well, or doesn’t even expose them, PTB has a built in hard-coded lookup table of all iMac models produced in the last decade, and uses values from that table. I gathered that table from Wikipedia pages. This of course means that any new iMac model will be broken at the beginning until somebody cares to update and release a new PTB.

The bottom line:

a) Don’t buy Apple products, or at least don’t use macOS.

b) If you ignored a), just don’t try to set a resolution. Just open an onscreen window and leave this to PTB, that’s the most likely way to get it to work properly. Use PsychImaging(‘AddTask’,‘General’,‘UseRetinaResolution’) in your scripts whenever you can, to avoid various more macOS bugs. This all applies to fullscreen windows only, because proper timing can only be achieved on fullscreen windows. For “windowed” windows and half-transparent fullscreen windows, PTB doesn’t care and lets you do whatever, because timing is unfixably broken from the beginning, so who cares? Atm. none of the common desktop OS’es allow for any reliable timing for non-fullscreen windows. Linux’ “new” Wayland display system would fix this, but after over 8 years of development it still has so many other shortcomings wrt. low level control for neuroscience use that this is not really an option for most use cases. It is good enough for general desktop use though, so give it another half a decade and we might get there…

c) Not setting a resolution on other os’es or external displays will usually let the OS make the best choice. If you don’t want to render stimuli at the display native resolution, use PTB’s panel fitter for rescaling - that’s the advice since over six years. PTB will rescale in a way compatible with correct timing.

d) If you absolutely want to set a resolution and find it automatically, the max heuristic is your best guess. But if you absolutely want to set a resolution on a setup for actual data collection, the best way would be to simply look up the native resolution and refresh rate of your monitor in its manual, and hard-code that into the script.

Probably i’m missing something in my description. But the gist for Denis is that if you do nothing about resolution, you are probably doing the right thing, especially on the iToys.

Dear Mario
Thanks. In the interim (like you) I implemented a lookup table for 15" MacBook Pros and 27" iMacs, since that’s what I use for experiments. (There have been several models per year). Apple’s spec pages list the native resolution for all macs, as far as I can tell. That works.

I am a big fan of PsychImaging(‘AddTask’,‘General’,‘UseRetinaResolution’) and use it practically always. However, “do nothing” isn’t practical in my lab because resolution is sticky and I don’t know what was run yesterday, when a student might have used the System Preference:Displays to increase magnification to read something. It’s my impression that UseRetinaResolution does not completely override the resolution set in Displays. What is working for me is to use Resolution, if necessary, to set the native resolution and then call UseRetinaResolution. That works. However, it’s not portable because it depends on my lookup table which only includes 15" MacBook Pros and 27" iMacs.

Might we enhance Screen to return the preferred resolution? You’ve documented that it’s not perfectly reliable but it would still be a decent fallback for the case when the computer isn’t in my native resolution table.

nativeRes=Screen('NativeResolution, windowOrScreen);

Best
Denis

well, i had no idea (obviously) that things had got so messy!

You don’t need a lookup table apart from iMac’s, PTB will override your choice of resolution anyway and switch to the panels native resolution.

Screen() doesn’t switch resolutions iff the display doesn’t mark a native resolution and iff switching resolution wouldn’t help for improved timing. If there isn’t a known native resolution, it will switch to a video mode that is compatible with the current video mode but provides proper timing. That’s the case for iMac’s, because whatever compatible resolution is chosen by PTB at the end, or just left as is, because it is already deemed compatible, the gpu will on-the-fly rescale to native panel resolution.

This is all true for fullscreen windows. PTB does nothing for other windows because no amount of resolution switching could fix the broken timing.

One could consider also always switching to the resolution from the lookup table on iMacs, even if it doesn’t improve timing any further. Ie. more just for consistency. I thought about that at the time, but rejected the approach for some reasons i can’t remember anymore. Probably because it reduces choice/control that somebody might find useful for some reason. I don’t care strongly either way.

I also thought about something like Screen(‘NativeResolution’) but i’m not psyched about it at all. It would be easy to implement on Linux/X11 and doable in the usual unreliable and painful way on OSX, but i don’t know of a straightforward way to do it on Windows, and i don’t know if it would be easily supportable on Linux + Wayland either. It smells like a potential compatibility and maintenance troublemaker if people start to expect it. Already now on OSX we have to maintain that lookup table - error prone, and rely on system private api’s i found via tedious reverse engineering, which can break at any time with any future macOS release. I don’t want to increase dependency of our public api on such hacks without very good reasons, given how little the iToys company cares about compatibility and not breaking things.

Dear Mario
Thx. I see the challenge. However, it seems incoherent to strongly urge
users to use native resolution, and then not offer any way for software to check whether they are using native resolution. Psychologists use this kind of situation to create “learned helplessness”. My suggestion would be to respond to the need, within what’s practical. Could you offer us Screen(‘NativeResolution’), and include a second output argument that indicates how reliable the answer is?
Best
Denis

Dear Mario and friends
I’m still hoping to find a reliable way to discover the native resolution. I’ve read that monitors today are supposed to return EDID information and it appears, though I’m not sure, that this includes the native resolution. I’ve found discussions on how to get this info, and it requires many steps. [Links below.]
System Profiler provides this info, and we can call it from Terminal (which we can do from MATLAB using the system() call).

system_profiler SPDisplaysDataType

lists the native resolution. It appears that a grep script could extract the native resolution from the system_profile output. The EDID is an industry wide standard whereas system_profiler is specific to macOS. But Mario seems to be saying that using max resolution may be ok on other OSes.

For the time being I’m sharing my current code which tries to do a table lookup, and failing, that, uses max resolution. It returns a second argument indicating confidence of the answer.

By the way, SwitchResX has a button that creates a readable EDID file, showing the native resolution on the timing example.

Any advice/experience on using EDID or system_profiler to get native resolution?
Best
Denis
ps
Links on using EDID under macOS




https://michaelcivitillo.com/overriding-edid-on-osx-for-external-monitors/
pps
function [nativeResolution,confidence]=NativeResolution(screen)
% [nativeResolution,confidence]=NativeResolution(screen)
%
% You optionally specify the screen number (default 0 the main screen), and
% it returns “nativeResolution” which is a two-vector [width height] plus
% an integer rating of confidence, from 1 (low) to 3 (high).
%
% Modern LCD screens are built out of hardware pixels. Native resolution
% specifies how many. Modern drivers allow you specify other resolutions
% and it remaps. The Psychtoolbox Screen(‘Resolutions’) command allows you
% to discover what resolutions are available, and switch to any of them.
% Best performance in terms of image quality and timing is often attained
% by running the experiment at native resolution. And any attempt to
% estimate the effect of the hardware resolution on image quality will be
% based on an estimate of the native resolution. Alas, there is currently
% no way, through software, to find out what a display’s native resolution
% is. This routine is an effort toward filling that void. The brute force
% approach of listing model numbers is tedious, but still practical.
% Perhaps someone will discover a better way to determine native resolution
% through software. I wonder if one might look at the list of offered
% resolutons and deduce from the ratios within the list which one is native.
%
%% Estimating the screen’s native resolution.
% First, we take the highest resolution in the device’s list as an estimate
% of the native resolution. Alas, Mario Kleiner warns that this rule of
% thumb is not reliable, and I confirm that, finding that among the
% resolutions offered by Screen(‘Resolutions’) for my 15" MacBook Pro is a
% resolution that is larger than the native resolution. Thus I consulted
% the apple documents and made an exhaustive table for 15" MacBook Pros
% with Retina display and 27" iMac. When possible, the table overrides the
% rule of thumb.

if nargin<1
screen=0;
end
confidence=0;
if screen==0 && ismac
%% LOOK UP NATIVE RES, IF TABULATED.
switch MacModelName
case {‘iMac15,1’ ‘iMac17,1’ ‘iMac18,3’ ‘iMac19,1’}
% iMac (Retina 5K, 27-inch, Mid 2015)
% iMac (Retina 5K, 27-inch, Late 2015)
% iMac (Retina 5K, 27-inch, 2017)
% iMac (Retina 5K, 27-inch, 2019)
% 27" iMac
nativeResolution=[5120 2880];
case {‘MacBookPro10,1’ ‘MacBookPro11,2’ ‘MacBookPro11,3’ …
‘MacBookPro11,4’ ‘MacBookPro11,5’ ‘MacBookPro13,3’ …
‘MacBookPro14,3’ ‘MacBookPro15,1’ ‘MacBookPro15,3’}
% 15" MacBook Pro
nativeResolution=[2880 1800];
case ‘MacBookPro16,1’
% 16" MacBook Pro
nativeResolution=[3072 1920];
end
confidence=3; % Based on Apple’s official specs. Reliable.
end
if confidence<1
%% FIND MAX RES, AS ESTIMATE OF NATIVE RES.
res=Screen(‘Resolutions’,screen);
nativeResolution=[0 0];
for i=1:length(res)
if res(i).width>nativeResolution(1)
nativeResolution=[res(i).width res(i).height];
end
end
confidence=1; % A rule of thumb, known to be slightly wrong in some cases.
end