function [rc,rcExt] = PsychGPUControl(cmd, varargin) % [rc,rcExt] = PsychGPUControl(cmd, arg); -- Control low-level GPU settings. % % PsychGPUControl calls into external helper tools to change certain % low-level operating settings of your systems graphics card (GPU). % % Not all operating systems and GPU's support this. The function will % do nothing on unsupported OS/GPU combos. % % Currently OS/X doesn't support this function at all, and on MS-Windows % and GNU/Linux, only recent ATI GPU's with recent drivers do support it. % % All subfunctions return an optional 'rc' return code of zero on success, % non-zero on error or if the feature is unsupported. % 'rcExt' is optional as well and its meaning depends on the subfunction. % % Subfunctions and their syntax & meaning: % % [rc,rcExt] = PsychGPUControl('SetDitheringEnabled', enableFlag); % - Depending on the setting of 'enableFlag', either enable (=1) or % disable (=0) display color dithering on all connected displays. % 'rcExt' returns the version of the low-level dithering API (currently 1 or 2) % that had to be used in order to overcome compatibility issues (Windows/AMD % only). 'rcExt' is only non-empty when the API was reported by the low-level % function. % % Under normal circumstances, the GPU should decide itself if dithering % should be used or not. This function allows you to override the GPU's % automatic choice. % % rc = PsychGPUControl('SetGPUPerformance', gpuPerformance); % - Select the performance state of the GPU. 'gpuPerformance' can be set to % 0 if the GPU shall automatically adjust its performance and power- % consumption, or to one of 10 fixed levels between 1 and 10, where 1 means % the lowest performance - and power consumption, whereas 10 means the % highest performance - and maximum power consumption. % % If in doubt, choose 10 for best accuracy of visual stimulus onset timing, % 0 for non-critical activities to leave the decision up to the graphics % driver and GPU. % % % rc = PsychGPUControl('FullScreenWindowDisablesCompositor', flag [, screenIds]); % - Select if desktop composition should be disabled for displays where % a Psychtoolbox fullscreen onscreen window is displayed. 'flag' == 1 means % to disable composition for fullscreen windows, 0 means to enable composition % for fullscreen windows. You usually want composition to be disabled, as this % is currently the only way to get decent timing and precise visual stimulus % onset timestamping. The optional vector of 'screenIds' selects which screens % should be affected by the change. If left out or set to [], all detected % screens will be changed. This will also apply a workaround for a limitation in % Ubuntu Linux standard Unity GUI (actually in the Compiz compositor) when used on % a single-x-screen setup with multiple video outputs, e.g., a single x-screen, % dual-display stereo setup. For the workaround to work, you may have to logout % and login again once, as sometimes the system does not seem to pick up the new % settings until a logout/login cycle. % % % rc = PsychGPUControl('EnableCompizMultiDisplayWorkaround', enableFlag); % - Enable or disable workaround for a limitation of the Ubuntu Linux Unity GUI % if one wants to do multi-display visual stimulation on a single x-screen setup % or on X-Screen 0 of a multi x-screen setup. Trying to open a Psychtoolbox % fullscreen window on PTB screen 0 aka X-Screen 0 if that X-Screen has multiple % active displays attached, e.g., for dual-display stereo stimulation, can fail % to work without severe visual stimulation timing problems. This is due to a % design limitation of Unity's Compiz desktop compositor, as of Ubuntu 16.04.0 LTS % and earlier distribution versions. You can either solve this problem by switching % to a different desktop GUI environment like GNOME-3 or KDE, or by attaching all % visual stimulation displays to a secondary X-Screen, e.g., screen 1 (run XOrgConfCreator % to set up such a setup). If you want to use Unity, you can use this function to % enable a workaround that fixes the problem, either immediately or after a logout % and login. There are no known downsides of the workaround when using your system % with a single display attached. The downside of the workaround on multi-display % setups will be that while Psychtoolbox will work fine, during your regular use of % the desktop GUI, automatic window placement and resizing of regular GUI applications % may be suboptimal due to the workaround interfering. Windows will no longer maximize % on a single display screen if you ask them to, but over the whole desktop. You can % use this function to enable and disable the workaround if you tend to use your desktop % not only for multi-display stimulation, but also for regular multi-display desktop use. % % Set 'enableFlag' to 1 to enable the workaround for visual stimulation, and 0 % to disable the workaround for more ergonomic multi-display desktop use. % % History: % 3.01.2010 mk Written. % 19.04.2010 mk Add quotes around path to command to protect against % blanks in path to executable. % 16.01.2011 mk Add function to control desktop composition on Linux with % Compiz. % 18.04.2013 mk Add use of 64-Bit ATIRadeonperf_Linux64 exe on 64-Bit Linux. % 26.04.2016 mk Add 'EnableCompizMultiDisplayWorkaround' for multi-display setups. % 10.07.2017 MR Add 'rcExt' for returning the dithering API version used by ATIRadeonperf_Windows. % Suppress the output of ATIRadeonperf_Windows as long as there are no errors. % 08.10.2019 mk Workaround dither enable <-> disable confusion bug on AMD Windows for at least % dither api version 1 in the underlying ATIRadeonperf_Windows. Tested to fix % identity LUT problems on Windows-10 with AMD Raven. if nargin < 1 error('Subfunction command argument missing!'); end rcExt = []; if strcmpi(cmd, 'SetDitheringEnabled') if isempty(varargin) error('SetDitheringEnabled: enableFlag argument missing!'); end enable = varargin{1}; if ~ismember(enable, 0:1) error('SetDitheringEnabled: Invalid enableFlag argument, not 0 or 1!'); end % Command code 1 means: Control ditherstate, according to 2nd arg 0 or 1 == disable, enable. cmdpostfix = sprintf(' 1 %i', enable); [rc, rcExt] = executeRadeoncmd(cmdpostfix); if IsWin && rc == 0 && ~isempty(rcExt) && rcExt == 1 % A dither en/disable was successfully executed on a Windows AMD driver with % dither api version 1. Due to a bug in the current ATIRadeonperf_Windows.exe % however, at least calls on this api version 1 have the en/disable flag % switched! enable = 1 maps to DISABLE, whereas enable = 0 maps to ENABLE, % so our caller gets the exact opposite of what was desired! % This at least for PTB 3.0.15 and 3.0.16 as of 8th October 2019. % % Fix this by calling the dither setup again with switched enable argument: cmdpostfix = sprintf(' 1 %i', 1 - enable); [rc, rcExt] = executeRadeoncmd(cmdpostfix); end return; end if strcmpi(cmd, 'SetGPUPerformance') if isempty(varargin) error('SetGPUPerformance: gpuPerformance argument missing!'); end gpuperf = varargin{1}; if ~ismember(gpuperf, 0:10) error('SetGPUPerformance: Invalid gpuPerformance argument, not an integer in range 0 - 10!'); end % Map range 1 to 5 to "minimum performance" on ATI GPU's: if gpuperf > 0 && gpuperf <= 5 perfflag = 2; end % Map range 6 to 10 to "maximum performance" on ATI GPU's: if gpuperf > 5 && gpuperf <= 10 perfflag = 1; end if gpuperf == 0 perfflag = 0; end % Command code 0 means: Control performance state: 0 = AUTO, 1 = MAX, 2 = MIN. cmdpostfix = sprintf(' 0 %i', perfflag); rc = executeRadeoncmd(cmdpostfix); return; end if strcmpi(cmd, 'FullScreenWindowDisablesCompositor') if isempty(varargin) error('FullScreenWindowDisablesCompositor: flag argument missing!'); end compositorOff = varargin{1}; if ~ismember(compositorOff, 0:1) error('FullScreenWindowsDisableCompositor: Invalid flag argument, not an integer of value 0 or 1!'); end if length(varargin) < 2 || isempty(varargin{2}) screenIds = Screen('Screens'); else screenIds = varargin{2}; end % Which OS? if ~IsLinux % Nothing to do on non-Linux as compositor handling is % implemented in Screen internally: rc = 1; return; end % We only know how to do this for Compiz, so we try that. Settings are persistent % across sessions and take effect immediately: rc = []; for screenId=screenIds if compositorOff % Enable un-redirection: Fullscreen windows aren't subject to treatment by compositor, % but can do (e.g. page-flipping) whatever they want: newstate = 'dis'; rc(end+1) = system(sprintf('dconf write /org/compiz/profiles/unity/plugins/composite/unredirect-fullscreen-windows true')); else % Disable un-redirection: Fullscreen windows get composited as all other windows: newstate = 'en'; rc(end+1) = system(sprintf('dconf write /org/compiz/profiles/unity/plugins/composite/unredirect-fullscreen-windows false')); end if ~all(rc) rc = 0; fprintf('PsychGPUControl:FullScreenWindowDisablesCompositor: Desktop composition for fullscreen windows on screen %i %sabled.\n', screenId, newstate); else rc = 1; fprintf('PsychGPUControl:FullScreenWindowDisablesCompositor: FAILED to %sable desktop composition for fullscreen windows on screen %i!\n', newstate, screenId); fprintf('This can cause visual onset timing problems! See ''help SyncTrouble'' - the Linux specific subsection for tips.\n'); end end return; end if strcmpi(cmd, 'EnableCompizMultiDisplayWorkaround') if isempty(varargin) error('EnableCompizMultiDisplayWorkaround: flag argument missing!'); end workaround = varargin{1}; if ~ismember(workaround, 0:1) error('EnableCompizMultiDisplayWorkaround: Invalid flag argument, not an integer of value 0 or 1!'); end % Which OS? if ~IsLinux % Nothing to do on non-Linux as this is a Compiz on Linux only thing: rc = 1; return; end % Settings are persistent across sessions and (should) take effect immediately: rc = []; if workaround newstate = 'en'; rc(end+1) = system(sprintf('dconf write /org/compiz/profiles/unity/plugins/core/outputs "[''50000x50000'']"')); rc(end+1) = system(sprintf('dconf write /org/compiz/profiles/unity/plugins/core/detect-outputs false')); else newstate = 'dis'; rc(end+1) = system(sprintf('dconf write /org/compiz/profiles/unity/plugins/core/detect-outputs true')); end if ~all(rc) rc = 0; fprintf('PsychGPUControl:EnableCompizMultiDisplayWorkaround: Compiz workaround %sabled.\n', newstate); fprintf('On some setups it may be neccessary to logout and login again for the workaround to be effective.\n'); else rc = 1; fprintf('PsychGPUControl:EnableCompizMultiDisplayWorkaround: FAILED to %sable Compiz workaround!\n', newstate); fprintf('This can cause visual onset timing problems on setups with multiple stimulation displays attached to screen 0!\n'); fprintf('However, single-display setups, or multi-display setups on separate X-Screens, should be unaffected. Other desktop\n'); fprintf('environments like GNOME-3 or KDE, would not be affected either.\n'); end return; end error('Invalid subfunction provided. Read the help for valid commands!'); return; %#ok end function [rc, rcExt] = executeRadeoncmd(cmdpostfix) rcExt = []; if IsOSX % A no-op on OS/X, as this is not supported at all. rc = 1; return; end if IsLinux % For 32-Bit Linux: cmdprefix = '/PsychContributed/ATIRadeonperf_Linux'; end if IsLinux(1) % For 64-Bit Linux: We have a dedicated 64-Bit executable, so we % do not require installation of 32-Bit compatibility libraries on % 64-Bit Linux systems just for our little exe here, as that would % be wasteful: cmdprefix = '/PsychContributed/ATIRadeonperf_Linux64'; end if IsWin % For 32-Bit or 64-Bit Windows we always use a 32-Bit executable: cmdprefix = '/PsychContributed/ATIRadeonperf_Windows.exe'; end % Does the executable exist? Otherwise error out early: if ~exist([PsychtoolboxRoot cmdprefix], 'file') rc = 1; return; end % Create quoted version of path, so blanks in path are handled properly: doCmd = strcat('"', [PsychtoolboxRoot cmdprefix ' '] ,'"'); % Call final command, return its return status code: [rc, msg] = system([doCmd cmdpostfix]); % Code has it backwards 1 = success, 0 = failure. Remap to our % convention: rc = 1 - rc; if IsWin % Extract dithering API version. i = strfind(msg, 'dithering API v'); if ~isempty(i) rcExt = sscanf(msg(i(1):end),'dithering API v%d'); end end % Output potential status or error messages, unless it is a message % that signals we are not executing on a AMD/ATI GPU with Catalyst % driver, ie., that the whole thing was a no-op: if rc~=0 && isempty(strfind(msg, 'ADL library not found!')) disp(msg); end return; end