/* Stores content of GPU's surface address registers of the surfaces that * correspond to the windowRecords frontBuffers. Only called inside * PsychFlipWindowBuffers() immediately before triggering a double-buffer * swap. The values are used as reference values: If another readout of * these registers shows values different from the ones stored preflip, * then that is a certain indicator that bufferswap has happened. */ void PsychStoreGPUSurfaceAddresses(PsychWindowRecordType* windowRecord) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX int gpuMaintype, gpuMinortype; // If we are called, we know that 'windowRecord' is an onscreen window. int screenId = windowRecord->screenNumber; // Just need to check if GPU low-level access is supported: if (!PsychOSIsKernelDriverAvailable(screenId)) return; // We only support Radeon GPU's with pre DCE-4 display engine, nothing more recent: if (!PsychGetGPUSpecs(screenId, &gpuMaintype, &gpuMinortype, NULL, NULL) || (gpuMaintype != kPsychRadeon) || (gpuMinortype >= 0x40)) { return; } // Driver is online: Read the registers, but only for primary crtc in a multi-crtc config: windowRecord->gpu_preflip_Surfaces[0] = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_PRIMARY_SURFACE_ADDRESS : RADEON_D2GRPH_PRIMARY_SURFACE_ADDRESS, NULL); windowRecord->gpu_preflip_Surfaces[1] = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_SECONDARY_SURFACE_ADDRESS : RADEON_D2GRPH_SECONDARY_SURFACE_ADDRESS, NULL); #endif return; }
int PsychPrefStateGet_ScreenToCrtcId(int screenId, int rankId) { return(PsychScreenToCrtcId(screenId, rankId)); }
/* PsychWaitForBufferswapPendingOrFinished() * Waits until a bufferswap for window windowRecord has either already happened or * bufferswap is certain. * Input values: * windowRecord struct of onscreen window to monitor. * timestamp = Deadline for abortion of flip detection at input. * * Return values: * timestamp = System time at polling loop exit. * beamposition = Beamposition at polling loop exit. * * Return value: FALSE if swap happened already, TRUE if swap is imminent. */ psych_bool PsychWaitForBufferswapPendingOrFinished(PsychWindowRecordType* windowRecord, double* timestamp, int *beamposition) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX int gpuMaintype, gpuMinortype; CGDirectDisplayID displayID; unsigned int primarySurface, secondarySurface; unsigned int updateStatus; double deadline = *timestamp; // If we are called, we know that 'windowRecord' is an onscreen window. int screenId = windowRecord->screenNumber; // Retrieve display id and screen size spec that is needed later... PsychGetCGDisplayIDFromScreenNumber(&displayID, screenId); #define RADEON_D1GRPH_UPDATE 0x6144 #define RADEON_D2GRPH_UPDATE 0x6944 #define RADEON_SURFACE_UPDATE_PENDING 4 #define RADEON_SURFACE_UPDATE_TAKEN 8 // Just need to check if GPU low-level access is supported: if (!PsychOSIsKernelDriverAvailable(screenId)) return(FALSE); // We only support Radeon GPU's with pre DCE-4 display engine, nothing more recent: if (!PsychGetGPUSpecs(screenId, &gpuMaintype, &gpuMinortype, NULL, NULL) || (gpuMaintype != kPsychRadeon) || (gpuMinortype >= 0x40)) { return(FALSE); } // Driver is online. Enter polling loop: while (TRUE) { // Read surface address registers: primarySurface = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_PRIMARY_SURFACE_ADDRESS : RADEON_D2GRPH_PRIMARY_SURFACE_ADDRESS, NULL); secondarySurface = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_SECONDARY_SURFACE_ADDRESS : RADEON_D2GRPH_SECONDARY_SURFACE_ADDRESS, NULL); // Read update status registers: updateStatus = PsychOSKDReadRegister(screenId, (PsychScreenToCrtcId(screenId, 0) < 1) ? RADEON_D1GRPH_UPDATE : RADEON_D2GRPH_UPDATE, NULL); PsychGetAdjustedPrecisionTimerSeconds(timestamp); if (primarySurface!=windowRecord->gpu_preflip_Surfaces[0] || secondarySurface!=windowRecord->gpu_preflip_Surfaces[1] || (updateStatus & (RADEON_SURFACE_UPDATE_PENDING | RADEON_SURFACE_UPDATE_TAKEN)) || (*timestamp > deadline)) { // Abort condition: Exit loop. break; } if (PsychPrefStateGet_Verbosity() > 9) { printf("PTB-DEBUG: Head %i: primarySurface=%p : secondarySurface=%p : updateStatus=%i\n", PsychScreenToCrtcId(screenId, 0), primarySurface, secondarySurface, updateStatus); } // Sleep slacky at least 200 microseconds, then retry: PsychYieldIntervalSeconds(0.0002); }; // Take timestamp and beamposition: *beamposition = PsychGetDisplayBeamPosition(displayID, screenId); PsychGetAdjustedPrecisionTimerSeconds(timestamp); // Exit due to timeout? if (*timestamp > deadline) { // Mark timestamp as invalid due to timeout: *timestamp = -1; } // Return FALSE if bufferswap happened already, TRUE if swap is still pending: return((updateStatus & RADEON_SURFACE_UPDATE_PENDING) ? TRUE : FALSE); #else // On Windows, we always return "swap happened": return(FALSE); #endif }
/* PsychFixupNative10BitFramebufferEnableAfterEndOfSceneMarker() * * Undo changes made by the graphics driver to the framebuffer pixel format control register * as part of an OpenGL/Graphics op that marks "End of Scene", e.g., a glClear() command, that * would revert the framebuffers opmode to standard 8bpc mode and thereby kill our 10 bpc mode * setting. * * This routine *must* be called after each such problematic "End of scene" marker command like * glClear(). The routine does nothing if 10bpc mode is not enabled/requested for the corresponding * display head associated with the given onscreen window. It rewrites the control register on * 10 bpc configured windows to basically undo the unwanted change of the gfx-driver *before* * a vertical retrace cycle starts, ie., before that changes take effect (The register is double- * buffered and latched to update only at VSYNC time, so we just have to be quick enough). * * * Expected Sequence of operations is: * 1. Some EOS command like glClear() issued. * 2. EOS command schedules ctrl register update to "bad" value at next VSYNC. * 3. This routine gets called, detects need for fixup, glGetError() waits for "2." to finish. * 4. This routine undos the "bad" value change request by overwriting the latch with our * "good" value --> Scheduled for next VSYNC. Then it returns... * 5. At next VSYNC or old "good" value is overwritten/latched with our new old "good" value, * --> "good value" persists, framebuffer stays in 2101010 configuration --> All good. * * So far the theory, let's see if this really works in real world... * * This is not needed in Carbon+AGL windowed mode, as the driver doesnt' mess with the control * register there, but that mode has its own share of drawback, e.g., generally reduced performance * and less robust stimulus onset timing and timestamping... Life's full of tradeoffs... */ void PsychFixupNative10BitFramebufferEnableAfterEndOfSceneMarker(PsychWindowRecordType* windowRecord) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX int headiter, headid, screenId; unsigned int ctlreg, lutreg, val1, val2; int gpuMaintype, gpuMinortype; // Fixup needed? Only if 10bpc mode is supposed to be active! Early exit if not: if (!(windowRecord->specialflags & kPsychNative10bpcFBActive)) return; // Map windows screen to gfx-headid aka register subset. TODO : We'll need something better, // more generic, abstracted out for the future, but as a starter this will do: screenId = windowRecord->screenNumber; // We only support Radeon GPU's, nothing else: if (!PsychGetGPUSpecs(screenId, &gpuMaintype, &gpuMinortype, NULL, NULL) || (gpuMaintype != kPsychRadeon) || (gpuMinortype >= 0xffff)) { return; } // This command must be called with the OpenGL context of the given windowRecord active, so // we can rely on glGetError() waiting for the correct pipeline to settle! Wait via glGetError() // for the end-of-scene marker to finish completely, so our register write happens after // the "wrong" register write of that command. glFinish() doesn't work here for unknown // reasons - probably it waits too long or whatever. Pretty shaky this stuff... glGetError(); // Ok, now rewrite the double-buffered (latched) register with our "good" value for keeping // the 10 bpc framebuffer online: // Iterate over range of all assigned heads for this screenId 'i' and reconfigure them: for (headiter = 0; headiter < kPsychMaxPossibleCrtcs; headiter++) { // Map screenid to headid for headiter'th head: headid = PsychScreenToCrtcId(screenId, headiter); // We're done as soon as we encounter invalid negative headid. if (headid < 0) break; // Select Radeon HW registers for corresponding heads: if (gpuMinortype < 0x40) { // DCE-3 and earlier: lutreg = (headid == 0) ? RADEON_D1GRPH_LUT_SEL : RADEON_D2GRPH_LUT_SEL; ctlreg = (headid == 0) ? RADEON_D1GRPH_CONTROL : RADEON_D2GRPH_CONTROL; } else { // DCE-4 and later: if (headid > DCE4_MAXHEADID) { printf("PTB-ERROR: Invalid headId %i (greater than max %i) provided for DCE-4+ display engine!\n", headid, DCE4_MAXHEADID); return; } lutreg = EVERGREEN_DC_LUT_10BIT_BYPASS + crtcoff[headid]; ctlreg = EVERGREEN_GRPH_CONTROL + crtcoff[headid]; } // Get current state of registers at high debug levels: if (PsychPrefStateGet_Verbosity() > 9) { val1 = PsychOSKDReadRegister(screenId, lutreg, NULL); val2 = PsychOSKDReadRegister(screenId, ctlreg, NULL); printf("PTB-DEBUG: PsychFixupNative10BitFramebufferEnableAfterEndOfSceneMarker(): Screen %i, head %i: LUT = %i [%i], GRPHCONT = %i [%i]\n", screenId, headid, val1 & (0x1 << 8), val1, val2 & (0x1 << 8), val2); } // One-liner read-modify-write op, which simply sets bit 8 of the register - the "10 bit LUT bypass" bit: PsychOSKDWriteRegister(screenId, lutreg, (0x1 << 8) | PsychOSKDReadRegister(screenId, lutreg, NULL), NULL); // One-liner read-modify-write op, which simply sets bit 8 of the register - the "Enable 2101010 mode" bit: PsychOSKDWriteRegister(screenId, ctlreg, (0x1 << 8) | PsychOSKDReadRegister(screenId, ctlreg, NULL), NULL); // Debug output, if wanted: if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: PsychFixupNative10BitFramebufferEnableAfterEndOfSceneMarker(): ARGB2101010 bit set on screen %i, head %i.\n", screenId, headid); } #endif // Done. return; }
/* PsychEnableNative10BitFramebuffer() - Enable/Disable native 10 bpc RGB framebuffer modes. * * This function enables or disables the native ARGB2101010 framebuffer readout mode of supported * graphics hardware. Currently the ATI Radeon X1000/HD2000/HD3000 and later cards should allow this. * * This needs support from the Psychtoolbox kernel level support driver for low-level register reads * and writes to the GPU registers. * * 'windowRecord' Is used to find the Id of the screen for which mode should be changed, as well as enable * flags to see if a change is required at all, and the OpenGL context for some specific * fixups. A value of NULL will try to apply the operation to all heads, but may only work * for *disabling* 10 bpc mode, not for enabling it -- Mostly useful for a master reset to * system default, e.g., as part of error handling or Screen shutdown handling. * 'enable' True = Enable ABGR2101010 support, False = Disable ARGB2101010 support, reenable ARGB8888 support. * */ psych_bool PsychEnableNative10BitFramebuffer(PsychWindowRecordType* windowRecord, psych_bool enable) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX int i, si, ei, headid, headiter, screenId; unsigned int lutreg, ctlreg, value, status; int gpuMaintype, gpuMinortype; // Child protection: if (windowRecord && !PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_internal, "Invalid non-onscreen windowRecord provided!!!"); // Either screenid from windowRecord or our special -1 "all Screens" Id: screenId = (windowRecord) ? windowRecord->screenNumber : -1; // We only support Radeon GPU's, nothing else: if (!PsychGetGPUSpecs(screenId, &gpuMaintype, &gpuMinortype, NULL, NULL) || (gpuMaintype != kPsychRadeon) || (gpuMinortype >= 0xffff)) { return(FALSE); } // Define range of screens: Either a single specific one, or all: si = (screenId!=-1) ? screenId : 0; ei = (screenId!=-1) ? screenId+1 : PsychGetNumDisplays(); // Loop over all target screens: for (i = si; i < ei; i++) { // Iterate over range of all assigned heads for this screenId 'i' and reconfigure them: for (headiter = 0; headiter < kPsychMaxPossibleCrtcs; headiter++) { // Map screenid to headid for headiter'th head: headid = PsychScreenToCrtcId(i, headiter); // We're done as soon as we encounter invalid negative headid. if (headid < 0) break; // Select Radeon HW registers for corresponding heads: if (gpuMinortype < 0x40) { // DCE-3 and earlier: lutreg = (headid == 0) ? RADEON_D1GRPH_LUT_SEL : RADEON_D2GRPH_LUT_SEL; ctlreg = (headid == 0) ? RADEON_D1GRPH_CONTROL : RADEON_D2GRPH_CONTROL; } else { // DCE-4 and later: if (headid > DCE4_MAXHEADID) { printf("PTB-ERROR: Invalid headId %i (greater than max %i) provided for DCE-4+ display engine!\n", headid, DCE4_MAXHEADID); return(false); } lutreg = EVERGREEN_DC_LUT_10BIT_BYPASS + crtcoff[headid]; ctlreg = EVERGREEN_GRPH_CONTROL + crtcoff[headid]; } // Enable or Disable? if (enable) { // Enable: // Switch hardware LUT's to bypass mode: // We set bit 8 to enable "bypass LUT in 2101010 mode": value = PsychOSKDReadRegister(screenId, lutreg, &status); if (status) { printf("PTB-ERROR: Failed to set 10 bit framebuffer mode (LUTReg read failed).\n"); return(false); } // Set the bypass bit: value = value | 0x1 << 8; PsychOSKDWriteRegister(screenId, lutreg, value, &status); if (status) { printf("PTB-ERROR: Failed to set 10 bit framebuffer mode (LUTReg write failed).\n"); return(false); } // Only reconfigure framebuffer scanout if this is really our true Native10bpc hack: // This is usually skipped on FireGL/FirePro GPU's as their drivers do it already... if (windowRecord->specialflags & kPsychNative10bpcFBActive) { // Switch CRTC to ABGR2101010 readout mode: // We set bit 8 to enable that mode: value = PsychOSKDReadRegister(screenId, ctlreg, &status); if (status) { printf("PTB-ERROR: Failed to set 10 bit framebuffer mode (CTLReg read failed).\n"); return(false); } // Set 2101010 mode bit: value = value | 0x1 << 8; PsychOSKDWriteRegister(screenId, ctlreg, value, &status); if (status) { printf("PTB-ERROR: Failed to set 10 bit framebuffer mode (CTLReg write failed).\n"); return(false); } } // Pipe should be in 10 bpc mode now... if (PsychPrefStateGet_Verbosity() > 2) printf("PTB-INFO: System framebuffer switched to ARGB2101010 mode for screen %i [head %i].\n", i, headid); } else { // Disable: // Only reconfigure framebuffer scanout if this is really our true Native10bpc hack: // This is usually skipped on FireGL/FirePro GPU's as their drivers do it already... if (windowRecord->specialflags & kPsychNative10bpcFBActive) { // Switch CRTC to ABGR8888 readout mode: // We clear bit 8 to enable that mode: value = PsychOSKDReadRegister(screenId, ctlreg, &status); if (status) { // This codepath gets always called in PsychCloseWindow(), so we should skip it // silently if register read fails, as this is expected on MS-Windows and on all // non-Radeon hardware and if kernel driver isn't loaded: if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-ERROR: Failed to set 8 bit framebuffer mode (CTLReg read failed).\n"); return(false); } else if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In disable 10bpc: Readreg. ctlreg yields %lx\n", value); // Clear 2101010 mode bit: value = value & ~(0x1 << 8); PsychOSKDWriteRegister(screenId, ctlreg, value, &status); if (status) { printf("PTB-ERROR: Failed to set 8 bit framebuffer mode (CTLReg write failed).\n"); return(false); } else if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In disable 10bpc: ctlreg reset\n"); // Wait 500 msecs for GPU to settle: PsychWaitIntervalSeconds(0.5); } // Switch hardware LUT's to standard mapping mode: // We clear bit 8 to disable "bypass LUT in 2101010 mode": value = PsychOSKDReadRegister(screenId, lutreg, &status); if (status) { printf("PTB-ERROR: Failed to set 8 bit framebuffer mode (LUTReg read failed).\n"); return(false); } else if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In disable 10bpc: Readreg. lutreg yields %lx\n", value); // Clear LUT bypass bit: value = value & ~(0x1 << 8); PsychOSKDWriteRegister(screenId, lutreg, value, &status); if (status) { printf("PTB-ERROR: Failed to set 8 bit framebuffer mode (LUTReg write failed).\n"); return(false); } else if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: In disable 10bpc: lutreg reset\n"); // Pipe should be in 8 bpc mode now... if (PsychPrefStateGet_Verbosity() > 2) printf("PTB-INFO: System framebuffer switched to standard ARGB8888 mode for screen %i [head %i].\n", i, headid); } } // Next display head... } // Next screenId. // Done. return(TRUE); #else // This cool stuff not supported on the uncool Windows OS: return(FALSE); #endif }
/* PsychSetGPUIdentityPassthrough() - Control identity passthrough of framebuffer 8 bpc pixel values to encoders/connectors: * * This function enables or disables bit depths truncation or dithering of digital display output ports of supported * graphics hardware, and optionally loads a identity LUT into the hardware and configures other parts of the GPU's * color management for untampered passthrough of framebuffer pixels. * Currently the ATI Radeon X1000/HD2000/HD3000/HD4000/HD5000/HD6000 and later cards should allow this. * * This needs support from the Psychtoolbox kernel level support driver for low-level register reads * and writes to the GPU registers. * * * 'windowRecord' Is used to find the Id of the screen for which mode should be changed. If set to NULL then... * 'screenId' ... is used to determine the screenId for the screen. Otherwise 'screenId' is ignored. * 'passthroughEnable' Zero = Disable passthrough: Currently only reenables dithering, otherwise a no-op. * 1 = Enable passthrough, if possible. * 'changeDithering' FALSE = Don't touch dither control, TRUE = Control dithering enable/disable if possible. * * Returns: * * 0xffffffff if feature unsupported by given OS/Driver/GPU combo. * 0 = On failure to establish passthrough. * 1 = On partial success: Dithering disabled and identityt LUT loaded, but other GPU color transformation * features may not be configured optimally for passthrough. * 2 = On full success, as far as can be determined by software. * */ unsigned int PsychSetGPUIdentityPassthrough(PsychWindowRecordType* windowRecord, int screenId, psych_bool passthroughEnable, psych_bool changeDithering) { #if PSYCH_SYSTEM == PSYCH_OSX || PSYCH_SYSTEM == PSYCH_LINUX unsigned int rc, rcret; int head, iter; // Init return code to "unsupported": rcret = 0xffffffff; // Child protection: if (windowRecord && !PsychIsOnscreenWindow(windowRecord)) PsychErrorExitMsg(PsychError_internal, "Invalid non-onscreen windowRecord provided!!!"); // Either screenid from windowRecord or as passed in: if (windowRecord) screenId = windowRecord->screenNumber; // Check if kernel driver is enabled, otherwise this won't work: if (!PsychOSIsKernelDriverAvailable(screenId)) { if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup requested, but this is not supported without kernel driver.\n"); return(0xffffffff); } // Try to enable or disable dithering on display: if (changeDithering) PsychSetOutputDithering(windowRecord, screenId, (passthroughEnable) ? 0 : 1); // We're done if this an actual passthrough disable, as a full disable isn't yet implemented: if (!passthroughEnable) return(0); // Start with head undefined: head = -1; for (iter = 0; iter < kPsychMaxPossibleCrtcs; iter++) { if (screenId >= 0) { // Positive screenId: Apply to all crtc's for this screenId: // Is there an iter'th crtc assigned to this screen? head = PsychScreenToCrtcId(screenId, iter); // If end of list of associated crtc's for this screenId reached, then we're done: if (head < 0) break; } else { // Negative screenId -> Only affect one head defined by screenId: if (head < 0) { // Setup single target head in this iteration: head = -screenId; } else { // Single target head already set up: We're done: break; } } // Check if remaining GPU is already configured for untampered identity passthrough: rc = PsychOSKDGetLUTState(screenId, head, (PsychPrefStateGet_Verbosity() > 4) ? 1 : 0); if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: [screen %i, head %i] 1st LUT query rc = %i.\n", screenId, head, rc); if (rc == 0xffffffff) { // Unsupported for this GPU. We're done: if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup on screenid %i, head %i requested, but this is not supported on this GPU.\n", screenId, head); rcret = 0xffffffff; continue; } // Perfect identity passthrough already configured? if (rc == 2) { // Yes. We're successfully done! if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup I completed on screenid %i, head %i. Perfect passthrough should work now.\n", screenId, head); rcret = 2; continue; } // No. Try to setup GPU for passthrough: if (!PsychOSKDLoadIdentityLUT(screenId, head)) { // Failed. if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup requested on screenid %i, head %i, but setup failed.\n", screenId, head); rcret = 0xffffffff; continue; } // Make sure, GPU's gamma table can settle by waiting 250 msecs: PsychYieldIntervalSeconds(0.250); // Setup supposedly successfully finished. Re-Query state: rc = PsychOSKDGetLUTState(screenId, head, (PsychPrefStateGet_Verbosity() > 4) ? 1 : 0); if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: [screen %i, head %i] 2nd LUT query rc = %i.\n", screenId, head, rc); // Perfect identity passthrough now configured? if (rc == 2) { // Yes. We're successfully done! if(PsychPrefStateGet_Verbosity() > 3) printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Perfect passthrough should work now.\n", screenId, head); rcret = 2; continue; } if (rc == 3) { // Not quite. We've done what we could. A perfect identity LUT is setup, but the rest of the hw // isn't in that a great shape. This may or may not be good enough... if(PsychPrefStateGet_Verbosity() > 3) { printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Sort of ok passthrough achieved. May or may not work.\n", screenId, head); printf("PTB-INFO: A perfect identity gamma table is loaded, but the other GPU color transformation settings are still suboptimal.\n"); } rcret = 1; continue; } // Ok, we failed. if(PsychPrefStateGet_Verbosity() > 3) { printf("PTB-INFO: GPU framebuffer passthrough setup II completed on screenid %i, head %i. Failed to establish identity passthrough!\n", screenId, head); printf("PTB-INFO: Could not upload a perfect identity LUT. May still work due to hopefully disabled dithering, who knows?\n"); } rcret = 0; } return(rcret); #else // This cool stuff not supported on the uncool Windows OS: if(PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GPU framebuffer passthrough setup requested, but this is not supported on MS-Windows.\n"); return(0xffffffff); #endif }