function oldClut = LoadIdentityClut(windowPtr, loadOnNextFlip, lutType, disableDithering)
% oldClut = LoadIdentityClut(windowPtr [, loadOnNextFlip=0][, lutType=auto][, disableDithering=1])
%
% Loads an identity clut on the window specified by windowPtr. If
% loadOnNextFlip is set to 1, then the clut will be loaded on the next call
% to Screen('Flip'). By default, the clut will be loaded immediately or on
% the next vertical retrace, depending on OS and graphics card.
% By default, this also tries to disable digital output dithering on supported
% hardware, setting the optional flag 'disableDithering' to zero will
% leave dithering alone.
%
% If you use Linux and have low level hardware access enabled via a call
% to PsychLinuxConfiguration during installation or later, or if you
% use OS/X and have the PsychtoolboxKernelDriver loaded
% ("help PsychtoolboxKernelDriver") and the graphics card is a GPU of the
% AMD Radeon X1000 series or later card, or a equivalent model of the FireGL
% or FirePro series, then this routine will try to use special low-level setup
% code for optimal identity mapping.
% It will also disable digital display dithering if disableDithering == 1.
% On Windows with AMD cards, digital display dithering will also get disabled
% automatically. On other graphics cards, digital display dithering will not
% be affected and needs to be manually controlled by you in some vendor specific way.
%
% On other than AMD cards under Linux or OSX, the function will make
% a best effort to upload a suitable clut, as follows:
%
% This mechanism relies on heuristics to detect the exact type of LUT to
% upload into your graphics card. These heuristics may go wrong, thanks to
% the ever increasing amount of graphics hardware driver bugs in both
% Windows and MacOS/X. For that reason you can also use the function
% SaveIdentityClut() to manually specify either the type of LUT to use
% (overriding the automatic choice), or to specify the complete LUT, or to
% capture the current identity LUT of a display that works. That function
% will store the override Clut in a per-user, per-GPU, per-Screen configuration
% file. If LoadIdentityClut finds such a matching configuration file, it
% will use the LUT specified there, instead of performing an automatic
% selection.
%
% The routine optionally returns the old clut in 'oldClut'.
%
% You can restore the old "original" LUT at any time by calling
% RestoreCluts, or sca, but only until you call clear all! The original
% LUT's are backed up in a global variable for this to work.
%
% If you use a Cambridge Research systems Bits+ or Bits# box or a VPixx Inc.
% DataPixx, ViewPixx or ProPixx device, use the script BitsPlusIdentityClutTest
% for advanced diagnostic and troubleshooting wrt. identity clut's, display
% dithering and other evils which could spoil your day for high bit depth
% visual stimulus display.

% History:
% ??/??/??   mk  Written.
% 05/31/08   mk  Add code to save backup copy of LUT's for later restore.
% 09/09/09   mk  Add more LUT workarounds for Apple's latest f$%#%$#.
% 09/16/09   mk  Even more workarounds for ATI's latest %#$%#$ on Windows.
% 09/20/09   mk  Add option to load LUT type or LUT from config file to
%                override the auto-detection here.
% 05/30/11   mk  Add option to use Screen's built in low-level GPU setup
%                methods to achieve an identity mapping. Fallback to old
%                heuristics if that is unsupported, disabled or failed.
% 03/05/14   mk  Update help texts and some diagnostic output.
% 03/06/14   mk  Allow control if dithering should be touched or not.
% 09/20/14   mk  Silence "unknown gpu" warning for Intel on Linux.
% 04/04/15   mk  Only use PsychGPUControl() dither disable on Windows by
%                default. Only use on Linux + AMD as fallback if low-level
%                dither disable fails.
% 05/18/16   mk  Add detection for Broadcom VC4 SoC gpu in RaspberryPi.
% 08/11/16   mk  Add detection based on winfo.DisplayCoreId on Linux to handle
%                hybrid graphics laptops.
% 06/05/17   mk  Add new lut handling for AMD gfx, to try to deal with pwl lut's
%                in 10 bpc framebuffer mode on OSX. Preload some reasonable
%                identity lut via LoadNormalizedGammaTable before using low-level
%                identity lut setup, which can only deal with discrete lut's, not
%                pwl lut's or such.
% 07/10/17   MR  Added gfxhwtype=5 for AMD graphics cards under Windows (see also 
%                the new variable 'ditherApiVer').
% 06/13/19   mk  Use gfxhwtype=0 on modern AMD DCE-10+ / DCN gpu's in the Linux
%                fallback path. It's the right choice for Linux 5.3+ with DC, and
%                especially crucial on DCN APUs if we really want to do without
%                dedicated low-level setup code for these new GPU gen's.

global ptb_original_gfx_cluts;


if nargin < 1
    error('Invalid number of arguments to LoadIdentityClut.');
end

% If not specified, we'll set the clut to load right away.
if nargin < 2
    loadOnNextFlip = [];
end

if isempty(loadOnNextFlip)
    loadOnNextFlip = 0;
end

if nargin < 3
    lutType = [];
end

if nargin < 4 || isempty(disableDithering)
    disableDithering = 1;
end

% Get screen id for this window:
screenid = Screen('WindowScreenNumber', windowPtr);

% Query what kind of graphics hardware is
% installed to drive the display corresponding to 'windowPtr':

% Query vendor of associated graphics hardware:
winfo = Screen('GetWindowInfo', windowPtr);

% Get current clut for use as backup copy later on:
oldClut = Screen('ReadNormalizedGammaTable', windowPtr);

if disableDithering && IsWin && ~isempty([strfind(winfo.GLVendor, 'AMD') strfind(winfo.GLVendor, 'ATI')])
    fprintf('LoadIdentityClut: Trying to disable digital display dithering.\n');
    % Try to use PsychGPUControl() method to disable display dithering
    % globally on all connected GPUs and displays. We only use this
    % on MS-Windows and it only works with AMD/ATI GPU's,
    % It will no-op silently on other system configurations.
    %
    % We don't use the success status return code of the function, because
    % we don't know how trustworthy it is. Also this only affects dithering,
    % not gamma table identity setup, so the code-pathes below must run anyway
    % for proper setup, even if their dithering disable effect may be redundant.
    [rc, ditherApiVer] = PsychGPUControl('SetDitheringEnabled', 0);
    % As long as there is no clear indication that it was dither API v2,
    % we assume it was the old dithering API v1. Will be only used for AMD anyway.
    if rc~=0 || isempty(ditherApiVer)
        ditherApiVer = 1;
    end
else
    ditherApiVer = 1;
end

% Ask Screen to use low-level setup code to configure the GPU for
% untampered framebuffer pixel paththrough. This is only supported on a
% subset of GPU's under certain conditions if the PsychtoolboxKernelDriver
% is loaded, but should be the most reliable solution if it works:
if ~IsWin
    % Low level control possible for some GPU's (e.g., AMD).

    % For AMD hardware under OSX (and Linux, but doesn't matter yet), we preload identity
    % lut of an ok quality via the conventional method, before using/trying the low-level
    % setup. This in case low-level setup, which can currently only linearize the discrete
    % hw lut's in 8 bpc format, fails to do a good job, because the display driver switches
    % from the discrete hw lut to a piece-wise linear or non-linear lut. This seems to happen
    % on some Apple Macs at least under macOS 10.12 when used in 10 bit native framebuffer mode.
    % In such a case, although our low-level code does successfully load a 8 bpc, 256 slots
    % discrete identity lut, that won't have the desired effect, as that discrete lut is not used.
    % But if we upload a linearized lut conventionally, it should linearize the pwl/non-linear lut
    % in the hardware. Note that for > 8 bit native modes we do not need a perfect 8 bpc pixel
    % passthrough lut, as provided by our low-level setup code. A roughly linear lut will do to
    % keep color/gamma calibration behaving as expected for 10 bpc display.
    if strcmp(winfo.DisplayCoreId, 'AMD')
      % This btw. is identical to gfxhwtype 0 aka LUT type 0 below:
      Screen('LoadNormalizedGammaTable', windowPtr, (linspace(0, 1, 256)' * ones(1, 3)), 0);
      WaitSecs('YieldSecs', 0.25);
    end

    % [] will enable full passthrough and force dithering off.
    % -1 will enable passthrough except for dithering, which is left at the OS default.
    if disableDithering
        % Also disable dithering:
        passthroughrc = Screen('LoadNormalizedGammatable', windowPtr, []);
    else
        % Only identity LUTs, no color conversion, degamma etc., but leave the
        % dither settings untouched, so the OS + graphics driver stays in control:
        passthroughrc = Screen('LoadNormalizedGammatable', windowPtr, -1);
    end
else
    passthroughrc = intmax;
end

if ismember(passthroughrc, [1, 2])
    % Success. How well did we do?
    if passthroughrc == 2
        fprintf('LoadIdentityClut: Used GPU low-level setup code to configure (hopefully) perfect identity pixel passthrough.\n');
    else
        fprintf('LoadIdentityClut: Warning: Used GPU low-level setup code to configure a perfect identity gamma table, but failed at\n');
        fprintf('LoadIdentityClut: Warning: configuring rest of color conversion hardware. This may or may not work.\n');
    end
else
    % No success. Either the low-level setup is unsupported, or it failed:
    if passthroughrc == 0
        fprintf('LoadIdentityClut: Warning: GPU low-level setup code for pixel passthrough failed for some reason! Using fallback...\n');
    elseif IsOSX || IsLinux
        fprintf('LoadIdentityClut: Could not use GPU low-level setup for pixel passthrough. Will use fallback.\n');
        % AMD GPU, aka GPU core of format 'R100', 'R500', ... starting with a 'R'?
        if ~isempty(strfind(winfo.DisplayCoreId, 'AMD'))
            % Some advice for AMD users on Linux and OSX:
            if ~IsLinux || (winfo.GPUMinorType ~= -1)
                fprintf('LoadIdentityClut: On your AMD/ATI GPU, you may get this working by loading the PsychtoolboxKernelDriver\n');
                fprintf('LoadIdentityClut: on OS/X or using a Linux system properly setup with PsychLinuxConfiguration.\n');
            end

            % On AMD try to use PsychGPUControl to force dithering off. This
            % will only work on Catalyst, and has the side effect of disabling
            % dithering on all displays:
            if disableDithering
                PsychGPUControl('SetDitheringEnabled', 0);
            end
        end
    end

    % Carry on with our bag of clut heuristics and other tricks...

    % Raw renderer string, with leading or trailing whitespace trimmed:
    gpuname = strtrim(winfo.GLRenderer);

    % Replace blanks and '/' with underscores:
    gpuname(isspace(gpuname)) = '_';
    gpuname = regexprep( gpuname, '/', '_' );

    % Same game for version string:
    glversion = strtrim(winfo.GLVersion);

    % Replace blanks with underscores:
    glversion(isspace(glversion)) = '_';
    glversion(glversion == '.') = '_';

    % Is there a predefined configuration file for the proper type of LUT to
    % load? Build kind'a unique path and filename for our system config:
    lpath = sprintf('%sIdentityLUT_%s_%s_%s_Screen%i.mat', PsychtoolboxConfigDir, OSName, gpuname, glversion, screenid);

    if exist(lpath, 'file')
        % Configfile! Load it: This will define a variable lutconfig in the
        % workspace:
        load(lpath);

        if isscalar(lutconfig)
            % Single scalar id code given to select among our hard-coded LUT's
            % below:
            gfxhwtype = lutconfig;
            fprintf('LoadIdentityClut: Applying the gamma lookup table of type %i, as specified in the following configuration file:\n%s\n', gfxhwtype, lpath);
        else
            % Full blown LUT given:
            lut = lutconfig;
            if ~isnumeric(lut) || size(lut,1) < 1 || size(lut,2) ~= 3
                sca;
                error('LoadIdentityClut: Loaded data from config file is not a valid LUT! Not a numeric matrix or less than 1 row, or not 3 columns!');
            end
            fprintf('LoadIdentityClut: Applying the gamma lookup table for identity mapping from the following configuration file:\n%s\n', lpath);
            gfxhwtype = -1;
        end

    else
        % No specific config file. Try our best at auto-detection of flawed
        % systems:

        % Query OS type and version:
        compinfo = Screen('Computer');

        if IsOSX
            % Which OS/X version?
            osxversion = sscanf(compinfo.system, '%*s %*s %i.%i.%i');
        end

        if IsLinux
            linuxversion = sscanf(compinfo.system, '%*s %i.%i.%i');
        end

        % We derive type of hardware and thereby our strategy from the vendor name:
        gfxhwtype = winfo.GLVendor;

        if ~isempty(strfind(winfo.DisplayCoreId, 'NVidia')) || (~IsLinux && (~isempty(strfind(gfxhwtype, 'NVIDIA')) || ...
           ~isempty(strfind(gfxhwtype, 'nouveau'))))
            % NVidia card:

            % We start with assumption that it is a "normal" one:
            gfxhwtype = 0;

            % GeForce 250 (e.g., GeForce GTS 250) under MS-Windows?
            if IsWin && ~isempty(strfind(winfo.GPUCoreId, 'G80')) && ~isempty(strfind(winfo.GLRenderer, '250'))
                % GeForce 250 on MS-Windows. Needs LUT type 1, according to Jon Peirce:
                gfxhwtype = 1;
            end

            % Is it a Geforce-8000 or later (G80 core or later) and is this OS/X?
            if IsOSX && ~isempty(strfind(winfo.GPUCoreId, 'G80'))
                % 10.6 - 10.13 Snow Leopard to High Sierra? 10.14+ no longer supports NVidia at all.
                if (osxversion(1) == 10) && (osxversion(2) >= 6)
                    % Yes. One of the releases with an embarassing amount of bugs,
                    % brought to you by Apple. Need to apply an especially ugly
                    % clut to force these cards into an identity mapping:

                    % Peter April reports his GeForce GT 330M on 10.6.8
                    % needs a type 0 LUT, so we handle this case:
                    if ~isempty(strfind(winfo.GLRenderer, '330'))
                        fprintf('LoadIdentityClut: NVidia Geforce GT 330 or later on OS/X 10.6.x or later detected. Enabling type-0 LUT.\n');
                        gfxhwtype = 0;
                    else
                        % Something else: LUT-III is so far correct.
                        fprintf('LoadIdentityClut: NVidia Geforce 8000 or later on OS/X 10.6.x or later detected. Enabling special type-II LUT hacks for totally broken operating systems.\n');
                        gfxhwtype = 3;
                    end
                end
            end
        else
            if ~isempty(strfind(winfo.DisplayCoreId, 'AMD')) || (~IsLinux && (~isempty(strfind(gfxhwtype, 'ATI')) || ~isempty(strfind(gfxhwtype, 'AMD')) || ~isempty(strfind(gfxhwtype, 'Advanced Micro Devices')) || ...
                    ~isempty(strfind(winfo.GLRenderer, 'DRI R')) || ~isempty(strfind(winfo.GLRenderer, 'on ATI R')) || ~isempty(strfind(winfo.GLRenderer, 'on AMD'))))
                % AMD/ATI card:

                % For historical reasons lost in time, start off with type 1:
                gfxhwtype = 1;

                if IsOSX
                    % A good default on macOS seems to be type 0:
                    gfxhwtype = 0;
                end

                if IsWin && ~isempty(strfind(winfo.GPUCoreId, 'R600'))
                    % At least the Radeon HD 3470 under Windows Vista and Linux needs type 0
                    % LUT's. Let's assume for the moment this is true for all R600
                    % cores, ie., all Radeon HD series cards.
                    % gfxhwtype==0 does not work for newer drivers. Since the driver version seems 
                    % to be less indicative for the change, we base our decision on the dither API 
                    % version implemented by AMD's display library (ADL). 
                    if ditherApiVer==2
                        fprintf('LoadIdentityClut: ATI Radeon HD-2000 or later with dither API v2 detected. Using type-5 LUT.\n');
                        gfxhwtype = 5;
                    else
                        fprintf('LoadIdentityClut: ATI Radeon HD-2000 or later detected. Using type-0 LUT.\n');
                        gfxhwtype = 0;
                    end
                elseif IsLinux
                    % AMD GPU with DCE 10+ display engine or with a DCN display engine?
                    % If so, this will use AMD DisplayCore by default and starting with Linux 4.17
                    % and later kernels will require a type 0 lut for a chance of working at all:
                    if (winfo.GPUMinorType == -1 || winfo.GPUMinorType >= 100) && ...
                        ((linuxversion(1) == 4 && linuxversion(2) >= 17) || (linuxversion(1) >= 5))
                        fprintf('LoadIdentityClut: AMD GPU on Linux 4.17+ with AMD DisplayCore detected. Using type-0 LUT.\n');
                        gfxhwtype = 0;

                        % Linux kernel older than 5.3? As of June 2019 has buggy identity LUT handling,
                        % so recommend a kernel upgrade to 5.3+ to make this work:
                        if (linuxversion(1) < 5) || (linuxversion(1) == 5 && linuxversion(2) < 3)
                            fprintf('LoadIdentityClut: Your Linux kernel %i.%i may be too old for proper identity pixel passthrough.\n', linuxversion(1), linuxversion(2));
                            fprintf('LoadIdentityClut: I recommend upgrading to at least Linux 5.3 to get identity passthrough fixed.\n');
                        end

                        % modesetting-ddx in use for this screen / window?
                        if ~IsWayland && Screen('GetWindowInfo', windowPtr, 8)
                            % Yes: This won't work on X-Server 1.20 and earlier, only on
                            % the 21.1 series and later:
                            fprintf('LoadIdentityClut: WARNING! The modesetting-ddx video driver is in use, instead of the standard\n');
                            fprintf('LoadIdentityClut: WARNING! amdgpu/radeon/ati-ddx for AMD GPU''s. This *will* cause failure of\n');
                            fprintf('LoadIdentityClut: WARNING! identity pixel passthrough, unless you are using X-Server 21.1 or later.\n');
                            fprintf('LoadIdentityClut: WARNING! In case of failure, XOrgConfCreator and XOrgConfSelector will allow you\n');
                            fprintf('LoadIdentityClut: WARNING! to switch back to the AMD standard driver to fix this problem..\n');
                        end
                    elseif ~isempty(strfind(winfo.GPUCoreId, 'R600'))
                        % At least the Radeon R9 380 Tonga Pro with DCE10 display engine under Linux with DRI2 Mesa needs type 3
                        % LUT's. Let's assume for the moment this is true for all such R600+ cores, ie., all Radeon HD series cards.
                        fprintf('LoadIdentityClut: ATI Radeon HD-2000 or later on Linux DRI2/DRI3 detected. Using type-3 LUT.\n');
                        gfxhwtype = 3;
                    else
                        % At least the Radeon R3xx/4xx/5xx under Linux with DRI2 Mesa needs type 0
                        % LUT's. Let's assume for the moment this is true for all R600
                        % cores, ie., all Radeon HD series cards.
                        fprintf('LoadIdentityClut: ATI Radeon on Linux DRI2 detected. Using type-0 LUT.\n');
                        gfxhwtype = 0;
                    end
                elseif IsOSX && (~isempty(strfind(winfo.GLRenderer, 'Radeon HD 5')))
                    % At least on OS/X 10.6 with ATI Radeon HD-5000 series,
                    % type 2 seems to be the correct choice, according to Cesar
                    % Ramirez (NASA):
                    fprintf('LoadIdentityClut: ATI Radeon HD-5000 Evergreen on OS/X detected. Using type-2 LUT.\n');
                    gfxhwtype = 2;
                end
            elseif ~isempty(strfind(winfo.DisplayCoreId, 'Intel')) || (~IsLinux && (~isempty(strfind(gfxhwtype, 'Intel'))))
                % Intel card: Type 0 LUT is correct at least on Linux versions
                % < Linux 4.7. Take this as a baseline:
                gfxhwtype = 0;

                % Linux got a new color management for Intel-kms in 4.7+
                if IsLinux
                    osrelease = sscanf(compinfo.kern.osrelease, '%i.%i');
                    if (osrelease(1) > 4) || (osrelease(1) == 4 && osrelease(2) >= 7)
                        % Linux 4.7 or later. New color management for Intel:
                        gfxhwtype = 4;
                    else
                        % Older kernel: Old color management for Intel:
                        gfxhwtype = 0;
                    end
                end

                if IsOSX
                    % Type 2 needed at least on 10.12.1 with at least Intel HD-4000:
                    gfxhwtype = 2;
                end

                fprintf('LoadIdentityClut: Intel integrated graphics chip detected. Using type-%i LUT.\n', gfxhwtype);
            elseif ~isempty(strfind(gfxhwtype, 'Broadcom')) && (~isempty(strfind(winfo.GLRenderer, 'VC4')) || ~isempty(strfind(winfo.GLRenderer, 'V3D')))
                % VC4/V3D of VideoCore-4 or Videocore-6 in RaspberryPi: Type 0 LUT is correct at least on Linux:
                gfxhwtype = 0;
                fprintf('LoadIdentityClut: VideoCore-4/6 SoC graphics chip detected. Using type-0 LUT.\n');
            else
                % Unknown card: Default to NVidia behaviour:
                gfxhwtype = 0;
                warning('LoadIdentityClut: Warning! Unknown graphics hardware detected. Set up identity CLUT may be wrong!'); %#ok<WNTAG>
            end
        end
    end

    % Use LUT type override from command line, if any is given:
    if ~isempty(lutType)
        gfxhwtype = lutType;
        fprintf('LoadIdentityClut: OVERRIDE: Will use identity LUT of type %i, as specified in function call argument.\n', gfxhwtype);
    end

    % We have different CLUT setup code for the different gfxhw-vendors:
    if gfxhwtype == -1;
        % Upload the lut defined in 'lut':
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, lut, loadOnNextFlip);
    end

    if gfxhwtype == 0
        % This works on WindowsXP with NVidia GeForce 7800 and OS/X 10.4.9 PPC
        % with GeForceFX-5200. Both are NVidia cards, so we assume this is a
        % correct setup for NVidia hardware:
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, (0:1/255:1)' * ones(1, 3), loadOnNextFlip);
    end

    if gfxhwtype == 1
        % This works on OS/X 10.4.9 on Intel MacBookPro with ATI Mobility
        % Radeon X1600, also on 10.5.8 with ATI Radeon HD-2600: We assume this
        % is the correct setup for ATI hardware:
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, ((1/256:1/256:1)' * ones(1, 3)), loadOnNextFlip);
    end

    if gfxhwtype == 2 && IsOSX
        % This works on OS/X 10.5 with NVidia GeForce 8800. It is an ugly hack
        % to compensate for the absolutely horrible, embarassing bugs in Apple's NVidia
        % graphics drivers and their clut handling. Does this company still
        % have a functioning Quality control department, or are they all
        % fully occupied fiddling with their iPhones?!?

        % This works on 10.5.8 with Geforce-9200M in the MacMini according to
        % CRS, and with 10.5.8 with Geforce-8800  in the MacPro according to
        % me. We assume this works on all G80 et al. GPU's:
        loadlut = (linspace(0,(1023/1024),1024)' * ones(1, 3));
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, loadlut, loadOnNextFlip);
    end

    if gfxhwtype == 3 && IsOSX
        % This works on OS/X 10.6.0 with NVidia Geforce-9200M according to CRS
        % and with 10.6.0 with Geforce-8800  in the MacPro according to
        % me. We assume this works on all G80 et al. GPU's:
        for i=0:1023
            if ((i / 1024.0) < 0.5)
                loadlut(i+1) = (i / 1024.0);
            else
                loadlut(i+1) = (i / 1024.0) - (1.0/256.0);
            end
        end
        loadlut = loadlut' * ones(1, 3);
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, loadlut, loadOnNextFlip);
    end

    if gfxhwtype == 3 && IsWin
        % This is an experimental variant of the OS/X type 3 lut, but with 256
        % slots. It is supposed for WindowsXP, assuming some NVidia GPU's,
        % e.g., some QuadroFX 3700 GPU's have similar problems:
        for i=0:255
            if ((i / 255.0) < 0.5)
                loadlut(i+1) = (i / 255.0);
            else
                loadlut(i+1) = (i / 255.0) - (1.0/256.0);
            end
        end
        loadlut = loadlut' * ones(1, 3);
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, loadlut, loadOnNextFlip);
    end

    if gfxhwtype == 3 && IsLinux
        % NVidia QuadroFX cards with binary blob and lut's with more than
        % 256 slots. Upload a standard linear lut with matching number of
        % slots:
        [dummy, dummy2, nrslots] = Screen('ReadNormalizedGammatable', windowPtr); %#ok<ASGLU>
        loadlut = (linspace(0,((nrslots-1) / nrslots), nrslots)' * ones(1, 3));
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, loadlut, loadOnNextFlip);
    end

    if gfxhwtype == 4
        % This is a very close approximation of the default gamma table used by the
        % Intel drm/kms display driver for Linux 4.8 and later, after they implemented
        % the new color management. At least valid for Intel HD-4000 IvyBridge down to
        % a match which is accurate to approx. 16 bits.
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, (0:1/256:0.99611)' * ones(1, 3), loadOnNextFlip);
    end

    if gfxhwtype == 5
        % Probably all AMD Radeon cards under Windows with graphics card drivers 
        % Catalyst 15.7.1 or later (a better indicator than the driver version is 
        % the version of the ADL dither API though, which changed in an incompatible  
        % way sometime around 15.7.1). This is equivalent to gfxhwtype==4 but with 
        % the last element set to 1.
        % Tested with HD7750 and RX460 under Win7/Win10. 
        % See also https://github.com/Psychtoolbox-3/Psychtoolbox-3/issues/402.
        oldClut = Screen('LoadNormalizedGammaTable', windowPtr, [(0:254)*256/(2^16-1), 1]' * ones(1, 3), loadOnNextFlip);        
    end    

    if ~ismember(gfxhwtype, [-1,0,1,2,3,4,5])
        sca;
        error('Could not upload identity CLUT to GPU! Invalid LUT or invalid LUT id or other invalid arguments passed?!?');
    end

    % End of high level lut setup path.
end

% Store backup copies of clut's for later restoration by RestoreCluts():

% Create global clut backup cell array if it does not exist yet:
if isempty(ptb_original_gfx_cluts)
    % Create 10 slots for out up to 10 screens:
    ptb_original_gfx_cluts = cell(10,1);
end

% Do we have already a backed up original clut for 'screenid'?
% If so, we don't store this clut, as an earlier invocation of a clut
% manipulation command will have stored the really real original lut:
if isempty(ptb_original_gfx_cluts{screenid + 1})
    % Nope. Store backup:
    ptb_original_gfx_cluts{screenid + 1} = oldClut;
end

return;
