1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
|
/*
SCREENGetWindowInfo.c
AUTHORS:
mario.kleiner.de@gmail.com mk
PLATFORMS: All
HISTORY:
06/03/07 mk Created.
DESCRIPTION:
Returns all kind of misc information about a specific window in a struct.
This is a catch-all for information that's not of too much interest for regular
users, but useful for Psychtoolbox helper functions (M-Files).
NOTES:
Be careful with length of struct field names! Only names up to 31 characters are
supported by Matlab 5.x (and maybe 6.x -- untested). Larger names cause matching
failure!
*/
#include "Screen.h"
#if PSYCH_SYSTEM == PSYCH_OSX
/* This function defines of Apple undocumented functions are taken from CGSDebug.h, a
* file provided by Alacatia Labs. This is the copyright info associated with it:
*
* Routines for debugging the window server and application drawing.
*
* Copyright (C) 2007-2008 Alacatia Labs
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
* Joe Ranieri joe@alacatia.com
*
*
*/
typedef enum {
/*! Clears all flags. */
kCGSDebugOptionNone = 0,
/*! All screen updates are flashed in yellow. Regions under a DisableUpdate are flashed in orange. Regions that are hardware accellerated are painted green. */
kCGSDebugOptionFlashScreenUpdates = 0x4,
/*! Colors windows green if they are accellerated, otherwise red. Doesn't cause things to refresh properly - leaves excess rects cluttering the screen. */
kCGSDebugOptionColorByAccelleration = 0x20,
/*! Disables shadows on all windows. */
kCGSDebugOptionNoShadows = 0x4000,
/*! Setting this disables the pause after a flash when using FlashScreenUpdates or FlashIdenticalUpdates. */
kCGSDebugOptionNoDelayAfterFlash = 0x20000,
/*! Flushes the contents to the screen after every drawing operation. */
kCGSDebugOptionAutoflushDrawing = 0x40000,
/*! Highlights mouse tracking areas. Doesn't cause things to refresh correctly - leaves excess rectangles cluttering the screen. */
kCGSDebugOptionShowMouseTrackingAreas = 0x100000,
/*! Flashes identical updates in red. */
kCGSDebugOptionFlashIdenticalUpdates = 0x4000000,
/*! Dumps a list of windows to /tmp/WindowServer.winfo.out. This is what Quartz Debug uses to get the window list. */
kCGSDebugOptionDumpWindowListToFile = 0x80000001,
/*! Dumps a list of connections to /tmp/WindowServer.cinfo.out. */
kCGSDebugOptionDumpConnectionListToFile = 0x80000002,
/*! Dumps a very verbose debug log of the WindowServer to /tmp/CGLog_WinServer_<PID>. */
kCGSDebugOptionVerboseLogging = 0x80000006,
/*! Dumps a very verbose debug log of all processes to /tmp/CGLog_<NAME>_<PID>. */
kCGSDebugOptionVerboseLoggingAllApps = 0x80000007,
/*! Dumps a list of hotkeys to /tmp/WindowServer.keyinfo.out. */
kCGSDebugOptionDumpHotKeyListToFile = 0x8000000E,
/*! Dumps information about OpenGL extensions, etc to /tmp/WindowServer.glinfo.out. */
kCGSDebugOptionDumpOpenGLInfoToFile = 0x80000013,
/*! Dumps a list of shadows to /tmp/WindowServer.shinfo.out. */
kCGSDebugOptionDumpShadowListToFile = 0x80000014,
/*! Leopard: Dumps information about caches to `/tmp/WindowServer.scinfo.out`. */
kCGSDebugOptionDumpCacheInformationToFile = 0x80000015,
/*! Leopard: Purges some sort of cache - most likely the same caches dummped with `kCGSDebugOptionDumpCacheInformationToFile`. */
kCGSDebugOptionPurgeCaches = 0x80000016,
/*! Leopard: Dumps a list of windows to `/tmp/WindowServer.winfo.plist`. This is what Quartz Debug on 10.5 uses to get the window list. */
kCGSDebugOptionDumpWindowListToPlist = 0x80000017,
/*! Leopard: DOCUMENTATION PENDING */
kCGSDebugOptionEnableSurfacePurging = 0x8000001B,
// Leopard: 0x8000001C - invalid
/*! Leopard: DOCUMENTATION PENDING */
kCGSDebugOptionDisableSurfacePurging = 0x8000001D,
/*! Leopard: Dumps information about an application's resource usage to `/tmp/CGResources_<NAME>_<PID>`. */
kCGSDebugOptionDumpResourceUsageToFiles = 0x80000020,
// Leopard: 0x80000022 - something about QuartzGL?
// Leopard: Returns the magic mirror to its normal mode. The magic mirror is what the Dock uses to draw the screen reflection. For more information, see `CGSSetMagicMirror`. */
kCGSDebugOptionSetMagicMirrorModeNormal = 0x80000023,
/*! Leopard: Disables the magic mirror. It still appears but draws black instead of a reflection. */
kCGSDebugOptionSetMagicMirrorModeDisabled = 0x80000024,
} CGSDebugOption;
typedef int CGSConnectionID;
extern CGSConnectionID _CGSDefaultConnection(void);
extern CGError CGSGetPerformanceData(CGSConnectionID cid, float *outFPS, float *unk, float *unk2, float *unk3);
extern CGError CGSSetDebugOptions(int options);
extern int CGSGetDebugOptions(int *outCurrentOptions);
#endif
static char useString[] = "info = Screen('GetWindowInfo', windowPtr [, infoType=0] [, auxArg1]);";
static char synopsisString[] =
"Returns a struct with miscellaneous info for the specified onscreen window.\n\n"
"\"windowPtr\" is the handle of the onscreen window for which info should be returned.\n\n"
"\"infoType\" If left out or set to zero, all available information for the 'windowPtr' is returned.\n\n"
"If set to -1, only the OpenGL context of the onscreen window is activated (Expert use only!).\n\n"
"If set to 1, only the rasterbeam position of the associated display device is returned (or -1 if unsupported).\n\n"
"If set to 2, information about the window server is returned (or -1 if unsupported).\n\n"
"If set to 3, low-level window server settings are changed according to 'auxArg1'. Do *not* use, "
"unless you really know what you're doing and have read the relevant PTB source code!\n\n"
"If set to 4, returns a single value with the current activity status of asynchronous flips. "
"1 if a Screen('AsyncFlipBegin') was called and the flip is still active, ie., hasn't "
"been finished with a matching Screen('AsyncFlipEnd') or Screen('AsyncFlipCheckEnd');, zero otherwise."
"You can call this function with an infoType of zero only if no async flips are active, or if the "
"imaging pipeline is fully enabled. This is why you need to use the special infoType 4 to find "
"out if async flips are active.\n\n"
"If set to 5, will start measurement of GPU time for render operations. The clock will start "
"on the next drawing command after this call. The clock will by default stop at the next call to "
"Screen('Flip'), Screen('AsyncFlipBegin'), or Screen('DrawingFinished'). Measured time will "
"include all the time spent by the GPU for preparing the final visual stimulus image for the next "
"flip, including all post-processing operations performed by the imaging pipeline.\n\n"
"If you want to exclude time spent in image post-processing or just measure the time spent for "
"a defined set of drawing commands, you can stop the clock earlier by calling this function with "
"infoType set to 6. In that case, only GPU time spent between the infoType=5 call and the infoType=6 "
"call will be reported, excluding any later drawing commands or imaging pipeline post-processing.\n\n"
"After the measured GPU operations complete, the elapsed rendertime will be returned in the 'GPULastFrameRenderTime' "
"field of the struct that you get when calling with infoType=0.\n"
"Due to the asynchronous nature of GPU rendering, the measured time may not be immediately "
"available after the clock is stopped. In this case, 'GPULastFrameRenderTime' will be zero and "
"you will need to repeat the infoType=0 query later.\n"
"Please note that not all GPU's and operating systems support this function. If the "
"function is unsupported, a value of zero will be returned in the info struct and by any call "
"with 'infoType' of 5 or 6.\n\n"
"An 'infoType' of 7 does return the same information as the default 'infoType' 0, but does not "
"set the window 'windowPtr' as drawing target, does not activate its OpenGL context and only "
"returns information that is safe to return without setting the window as drawing target.\n\n"
"An 'infoType' of 8 returns 1 if the X-Screens primary gpu uses the modesetting-ddx under Linux.\n\n"
"An 'infoType' of 9 returns a struct with interop info needed for interop with certain clients, "
"currently tailored to the needs of OpenGL interop with the OpenXR api on Linux and Windows.\n\n"
"\n"
"The default info struct for 'infoType' 7 and the default 'infoType' 0 contains all kinds of information. "
"Just check its output to see what is returned. Most of this info is not interesting for normal users, "
"mostly provided for internal use by M-Files belonging to Psychtoolbox itself, e.g., display tests.\n\n"
"The info struct contains the following fields:\n"
"----------------------------------------------\n\n"
"Beamposition: Current rasterbeam position of the video scanout cycle.\n"
"LastVBLTimeOfFlip: VBL timestamp of last finished Screen('Flip') operation.\n"
"TimeAtSwapRequest: Timestamp taken prior to submission of the low-level swap command. Useful for micro-benchmarking.\n"
"TimePostSwapRequest: Timestamp taken after submission of the low-level swap command. Useful for micro-benchmarking.\n"
"VBLTimePostFlip: Optional flip completion timestamp from VBLANK timestamping. Useful for micro-benchmarking.\n"
"OSSwapTimestamp: Optional flip completion timestamp from OS-Builtin timestamping. Useful for micro-benchmarking.\n"
"GPULastFrameRenderTime: Duration of all rendering operations, as measured by GPU, if infoType=5 was used.\n"
"RawSwapTimeOfFlip: Raw (uncorrected by high-precision timestamping) timestamp of last finished Screen('Flip') operation.\n"
"LastVBLTime: System time when last vertical blank happened, or the same as "
"LastVBLTimeOfFlip if the system doesn't support queries of this property (currently only OS/X does.)\n"
"VBLCount: Running count of vertical blank intervals since (graphics)system startup. Or zero if not"
"supported by system. Currently only OS/X and Linux do support this with some GPU's.\n"
"VideoRefreshFromBeamposition: Estimate of video refresh cycle from beamposition measurement method.\n"
"GLVendor, GLRenderer, GLVersion: Vendor name, renderer name and version of the OpenGL implementation.\n"
"GLDeviceUUID: The unique device id if supported by the OpenGL implementation, an empty field otherwise.\n"
"StereoMode: Currently selected stereomode, as requested in call to Screen('OpenWindow', ...);\n"
"StereoDrawBuffer: Current drawbuffer for stereo display (0 = left eye, 1 = right eye, 2 = None in mono mode).\n"
"ImagingMode: Currently selected imging pipeline mode, as requested in call to Screen('OpenWindow', ...);\n"
"MultiSampling: Currently selected multisample anti-aliasing mode, as requested in call to Screen('OpenWindow', ...);\n"
"MissedDeadlines: Number of missed Screen('Flip') stimulus onset deadlines, according to internal skip detector.\n"
"FlipCount: Total number of flip command executions, ie., of stimulus updates.\n"
"GuesstimatedMemoryUsageMB: Estimated memory usage of window or texture in Megabytes. Can be very inaccurate or unavailable!\n"
"VBLStartLine, VBLEndline: Start/Endline of vertical blanking interval. The VBLEndline value is not available/valid on all GPU's.\n"
"SwapGroup: Swap group id of the swap group to which this window is assigned. Zero for none.\n"
"SwapBarrier: Swap barrier id of the swap barrier to which this windows swap group is assigned. Zero for none.\n"
"SysWindowHandle: Low-level windowing system specific window handle of the onscreen window.\n"
"SysWindowInteropHandle: Low-level windowing system specific window-related auxiliary handle of the onscreen window.\n"
"ExternalMouseMultFactor: Scaling factor to apply for remapping input coordinates on some systems, e.g., by RemapMouse.m.\n"
"VRRMode: Actual selected mode for VRR stimulus onset scheduling (1 = auto maps to actual choice): 0 = Off, 2 = Simple, 3 = OwnScheduled.\n"
"VRRStyleHint: Style hint code for the current active VRR stimulation timing style, ie. what is assumed about timing behaviour of the paradigm.\n"
"VRRLatencyCompensation: Current estimate of average VRR swapbuffers latency, used for compensating during VRR scheduling in 'OwnScheduled' mode.\n"
"\n"
"The following settings are derived from a builtin detection heuristic, which works on most common GPU's:\n\n"
"GPUCoreId: Symbolic name string that roughly describes the name of the GPU core of the graphics card. This string is arbitrarily\n"
"chosen to roughly group the cores by common capabilities (and quirks). Currently defined are:\n"
"R100 = Very old ATI GPUs, R300 = GPU's roughly starting at Radeon 9000, R500 = Radeon X1000 or later, R600 = Radeon HD2000 or later.\n"
"NV10 = Very old NVidia GPUs, NV30 = NV30 or later, NV40 = Geforce6000/7000 or later, G80 = Geforce8000 or later.\n"
"An empty GPUCoreId string means a different, unspecified core.\n\n"
"DisplayCoreId: Vendor of the display engine / display gpu, NVidia, AMD, Intel, or same as 'GPUCoreId'. May differ from 'GPUCoreId' in hybrid graphics laptops.\n"
"BitsPerColorComponent: Effective color depths of window/framebuffer in bits per color channel component (bpc).\n"
"GLSupportsFBOUpToBpc: 0 = No support for framebuffer objects. Otherwise maximum supported bpc (8, 16, 32).\n"
"GLSupportsBlendingUpToBpc: Maximum supported bpc for hardware accelerated framebuffer blending (alpha blending).\n"
"GLSupportsTexturesUpToBpc: Maximum supported bpc for textures (8, 16, 32).\n"
"GLSupportsFilteringUpToBpc: Maximum supported bpc for hardware accelerated linear filtering of textures (8, 16, 32).\n"
"GLSupportsPrecisionColors: 1 = Hardware can be fully trusted to rasterize perfect 32 bpc colors in floating point color mode "
"without special support of PTB. 0 = Needs special (slower) support from PTB to work.\n"
"GLSupportsFP32Shading: 1 = All internal calculations of the GPU are done with IEEE single precision 32 bit floating point, i.e., "
"very accurate.\n"
"GPUMinorType: Numeric vendor specific gpu id. Chip family on NVidia, display engine revision on AMD atm. -1 if unknown. Subject to change without notice!\n\n";
static char seeAlsoString[] = "OpenWindow, Flip, NominalFrameRate";
PsychError SCREENGetWindowInfo(void)
{
const char *FieldNames[]={ "Beamposition", "LastVBLTimeOfFlip", "LastVBLTime", "VBLCount", "TimeAtSwapRequest", "TimePostSwapRequest", "RawSwapTimeOfFlip",
"VBLTimePostFlip", "OSSwapTimestamp", "GPULastFrameRenderTime", "StereoMode", "ImagingMode", "MultiSampling", "MissedDeadlines", "FlipCount", "StereoDrawBuffer",
"GuesstimatedMemoryUsageMB", "VBLStartline", "VBLEndline", "VideoRefreshFromBeamposition", "GLVendor", "GLRenderer", "GLVersion", "GPUCoreId", "GPUMinorType",
"DisplayCoreId", "GLSupportsFBOUpToBpc", "GLSupportsBlendingUpToBpc", "GLSupportsTexturesUpToBpc", "GLSupportsFilteringUpToBpc", "GLSupportsPrecisionColors",
"GLSupportsFP32Shading", "BitsPerColorComponent", "IsFullscreen", "SpecialFlags", "SwapGroup", "SwapBarrier", "SysWindowHandle", "ExternalMouseMultFactor", "VRRMode",
"VRRStyleHint", "VRRLatencyCompensation", "GLDeviceUUID", "SysWindowInteropHandle" };
const int fieldCount = 44;
PsychGenericScriptType *s;
PsychWindowRecordType *windowRecord;
double beamposition, lastvbl;
int infoType = 0;
double auxArg1, auxArg2, auxArg3;
CGDirectDisplayID displayId;
psych_uint64 postflip_vblcount;
psych_bool onscreen;
int queryState;
unsigned int gpuTimeElapsed;
int gpuMaintype, gpuMinorType;
//all subfunctions should have these two lines.
PsychPushHelp(useString, synopsisString, seeAlsoString);
if(PsychIsGiveHelp()){PsychGiveHelp();return(PsychError_none);};
PsychErrorExit(PsychCapNumInputArgs(5)); //The maximum number of inputs
PsychErrorExit(PsychRequireNumInputArgs(1)); //The required number of inputs
PsychErrorExit(PsychCapNumOutputArgs(1)); //The maximum number of outputs
// Query infoType flag: Defaults to zero.
PsychCopyInIntegerArg(2, FALSE, &infoType);
if (infoType < -1 || infoType > 9) PsychErrorExitMsg(PsychError_user, "Invalid 'infoType' argument specified! Valid are -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.");
// Windowserver info requested?
if (infoType == 2 || infoType == 3) {
// Return info about WindowServer:
#if PSYCH_SYSTEM == PSYCH_OSX
const char *CoreGraphicsFieldNames[]={ "CGSFps", "CGSValue1", "CGSValue2", "CGSValue3", "CGSDebugOptions" };
const int CoreGraphicsFieldCount = 5;
float cgsFPS, val1, val2, val3;
int retIntArg;
// This (undocumented) Apple call retrieves information about performance statistics of
// the Core graphics server, also known as WindowServer or Quartz compositor:
CGSGetPerformanceData(_CGSDefaultConnection(), &cgsFPS, &val1, &val2, &val3);
if (CGSGetDebugOptions(&retIntArg)) {
if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSGetDebugOptions() failed!\n");
}
PsychAllocOutStructArray(1, FALSE, -1, CoreGraphicsFieldCount, CoreGraphicsFieldNames, &s);
PsychSetStructArrayDoubleElement("CGSFps", 0 , cgsFPS, s);
PsychSetStructArrayDoubleElement("CGSValue1", 0, val1, s);
PsychSetStructArrayDoubleElement("CGSValue2", 0, val2, s);
PsychSetStructArrayDoubleElement("CGSValue3", 0, val3, s);
PsychSetStructArrayDoubleElement("CGSDebugOptions", 0, (double) retIntArg, s);
if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) ) {
// Type 3 setup request with auxArg1 provided. Apple auxArg1 as debugFlag setting
// for the CoreGraphics server: DANGEROUS!
if (CGSSetDebugOptions((unsigned int) auxArg1)) {
if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to CGSSetDebugOptions() failed!\n");
}
}
#endif
#if PSYCH_SYSTEM == PSYCH_WINDOWS
psych_uint64 onsetVBLCount, frameId;
double onsetVBLTime, compositionRate;
psych_uint64 targetVBL;
PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
// Query all DWM presentation timing info, return full info as struct in optional return argument '1':
if (PsychOSGetPresentationTimingInfo(windowRecord, TRUE, 0, &onsetVBLCount, &onsetVBLTime, &frameId, &compositionRate, 1)) {
// Query success: Info struct has been created and returned by PsychOSGetPresentationTimingInfo()...
auxArg1 = auxArg2 = 0;
auxArg3 = 2;
// Want us to change settings?
if ( (infoType == 3) && PsychCopyInDoubleArg(3, FALSE, &auxArg1) && PsychCopyInDoubleArg(4, FALSE, &auxArg2) && PsychCopyInDoubleArg(5, FALSE, &auxArg3)) {
if (auxArg1 < 0) auxArg1 = 0;
targetVBL = (psych_uint64) auxArg1;
if (PsychOSSetPresentParameters(windowRecord, targetVBL, (int) auxArg3, auxArg2)) {
if (PsychPrefStateGet_Verbosity() > 5) printf("PTB-DEBUG: GetWindowInfo: Call to PsychOSSetPresentParameters(%i, %i, %f) SUCCESS!\n", (int) auxArg1, (int) auxArg3, auxArg2);
}
else {
if (PsychPrefStateGet_Verbosity() > 1) printf("PTB-WARNING: GetWindowInfo: Call to PsychOSSetPresentParameters() failed!\n");
}
}
}
else {
// Unsupported / Failed:
PsychCopyOutDoubleArg(1, FALSE, -1);
}
#endif
#if PSYCH_SYSTEM == PSYCH_LINUX
if (infoType == 2) {
// MMIO register Read for screenid "auxArg1", register offset "auxArg2":
PsychCopyInDoubleArg(3, TRUE, &auxArg1);
PsychCopyInDoubleArg(4, TRUE, &auxArg2);
PsychCopyOutDoubleArg(1, FALSE, (double) PsychOSKDReadRegister((int) auxArg1, (unsigned int) auxArg2, NULL));
}
if (infoType == 3) {
// MMIO register Write for screenid "auxArg1", register offset "auxArg2", to value "auxArg3":
PsychCopyInDoubleArg(3, TRUE, &auxArg1);
PsychCopyInDoubleArg(4, TRUE, &auxArg2);
PsychCopyInDoubleArg(5, TRUE, &auxArg3);
PsychOSKDWriteRegister((int) auxArg1, (unsigned int) auxArg2, (unsigned int) auxArg3, NULL);
}
#endif
// Done.
return(PsychError_none);
}
// Get the window record:
PsychAllocInWindowRecordArg(kPsychUseDefaultArgPosition, TRUE, &windowRecord);
onscreen = PsychIsOnscreenWindow(windowRecord);
// Only want windows OpenGL context activated?
if (infoType == -1) {
PsychSetGLContext(windowRecord);
return(PsychError_none);
}
if (onscreen) {
// Query rasterbeam position: Will return -1 if unsupported.
PsychGetCGDisplayIDFromScreenNumber(&displayId, windowRecord->screenNumber);
beamposition = (double) PsychGetDisplayBeamPosition(displayId, windowRecord->screenNumber);
}
else {
beamposition = -1;
}
if (infoType == 1) {
// Return the measured beamposition:
PsychCopyOutDoubleArg(1, FALSE, beamposition);
}
else if (infoType == 4) {
// Return async flip state: 1 = Active, 0 = Inactive.
PsychCopyOutDoubleArg(1, FALSE, (((NULL != windowRecord->flipInfo) && (0 != windowRecord->flipInfo->asyncstate)) ? 1 : 0));
}
else if (infoType == 5) {
if (!PsychIsOnscreenWindow(windowRecord)) {
PsychErrorExitMsg(PsychError_user, "Tried to create a GPU rendertime query on a texture or offscreen window. Only supported on onscreen windows!");
}
// Only need OpenGL mastercontext, not full drawingtarget:
PsychSetGLContext(windowRecord);
// Create a GL_EXT_timer_query object for this window:
if (glewIsSupported("GL_EXT_timer_query")) {
// Pending queries finished?
if (windowRecord->gpuRenderTimeQuery > 0) {
PsychErrorExitMsg(PsychError_user, "Tried to create a new GPU rendertime query, but last query not yet finished! Call Screen('Flip') first!");
}
// Generate Query object:
glGenQueries(1, &windowRecord->gpuRenderTimeQuery);
// Emit Query: GPU will measure elapsed processing time in Nanoseconds, starting
// with the first GL command executed after this command:
glBeginQuery(GL_TIME_ELAPSED_EXT, windowRecord->gpuRenderTimeQuery);
// Reset last measurement:
windowRecord->gpuRenderTime = 0;
// Report status "in progress" = 1:
PsychCopyOutDoubleArg(1, FALSE, 1);
}
else {
if (PsychPrefStateGet_Verbosity() > 4) printf("PTB-INFO: GetWindowInfo for infoType 5: GPU timer query objects are unsupported on this platform and GPU. Call ignored!\n");
// Report status "unsupported" = 0:
PsychCopyOutDoubleArg(1, FALSE, 0);
}
}
else if (infoType == 6) {
if (!PsychIsOnscreenWindow(windowRecord)) {
PsychErrorExitMsg(PsychError_user, "Tried to finish a GPU rendertime query on a texture or offscreen window. Only supported on onscreen windows!");
}
// End time measurement for any previously submitted rendering commands if a
// GPU rendertime query was requested, otherwise just no-op:
if (windowRecord->gpuRenderTimeQuery) {
// Only need OpenGL mastercontext, not full drawingtarget:
PsychSetGLContext(windowRecord);
// Unfinished query? If so, finish it.
glGetQueryiv(GL_TIME_ELAPSED_EXT, GL_CURRENT_QUERY, &queryState);
if (queryState > 0) glEndQuery(GL_TIME_ELAPSED_EXT);
}
// Report if a query was pending:
PsychCopyOutDoubleArg(1, FALSE, (windowRecord->gpuRenderTimeQuery) ? 1 : 0);
}
else if (infoType == 8) {
// Modesetting ddx on Linux X11 in use?
#if PSYCH_SYSTEM == PSYCH_LINUX
PsychCopyOutDoubleArg(1, FALSE, PsychOSX11ScreenUsesModesettingDDX(windowRecord->screenNumber));
#else
PsychErrorExitMsg(PsychError_unimplemented, "infoType 8 query is only supported on Linux.");
#endif
}
else if (infoType == 9) {
// Interop info for special external clients, with which we want to share OpenGL contexts and textures, e.g., OpenXR:
const char* FieldNamesInterop[] = { "DeviceContext", "OpenGLContext", "OpenGLContextScreen", "OpenGLDrawable", "OpenGLConfig", "OpenGLVisualId" };
const int fieldCountInterop = 6;
// This gives access to the OpenGL context and associated drawable, resources and config of our parallel background flipperThread.
// Normally the flipperThread would use these to implement special functionality like async flips, vrr, framesequential stereo etc.
// Here the idea is to usually not use the flipperThread, but instead piggyback on / (ab-)use the OpenGL resources normally used
// for that thread for other purposes, e.g., interop with special external components like the PsychOpenXRCore driver and OpenXR.
// Note: For purely single-thread operations without a dedicated context for external use, we also expose Screen's main OpenGL context.
PsychAllocOutStructArray(1, FALSE, -1, fieldCountInterop, FieldNamesInterop, &s);
PsychSetStructArrayUnsignedInt64Element("DeviceContext", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.deviceContext, s);
PsychSetStructArrayUnsignedInt64Element("OpenGLContext", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.glswapcontextObject, s);
PsychSetStructArrayUnsignedInt64Element("OpenGLContextScreen", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.contextObject, s);
PsychSetStructArrayUnsignedInt64Element("OpenGLDrawable", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.windowHandle, s);
#if (PSYCH_SYSTEM == PSYCH_LINUX) && !defined(PTB_USE_WAFFLE) && !defined(PTB_USE_WAYLAND)
// Linux X11/GLX/Xlib only:
PsychSetStructArrayUnsignedInt64Element("OpenGLConfig", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.pixelFormatObject, s);
PsychSetStructArrayUnsignedInt64Element("OpenGLVisualId", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.visualId, s);
#else
PsychSetStructArrayUnsignedInt64Element("OpenGLConfig", 0, (psych_uint64) (size_t) 0, s);
PsychSetStructArrayUnsignedInt64Element("OpenGLVisualId", 0, (psych_uint64) (size_t) 0, s);
#endif
}
else {
// Set OpenGL context (always needed) and drawing target, as setting
// our windowRecord as a drawingtarget is an expected side-effect of
// this function. Quite a bit of PTB M-Functions and usercode rely on
// this. Exception is infoType 7 which asks to omit this call:
if (infoType != 7) {
// Only set as drawing target if it can act as a drawing target ie. onscreen/offscreen/texture.
// Proxywindows like GLOperators() are derived from windows but can't be used as drawing target,
// so only set their OpenGL context:
if (PsychIsOnscreenWindow(windowRecord) || PsychIsOffscreenWindow(windowRecord))
PsychSetDrawingTarget(windowRecord);
else
PsychSetGLContext(windowRecord);
}
// Return all information:
PsychAllocOutStructArray(1, FALSE, -1, fieldCount, FieldNames, &s);
// Rasterbeam position:
PsychSetStructArrayDoubleElement("Beamposition", 0, beamposition, s);
// Time of last vertical blank when a double-buffer swap occured:
if ((windowRecord->flipCount > 0) && (windowRecord->time_at_last_vbl == 0) && !(windowRecord->specialflags & kPsychSkipTimestampingForFlipOnce) &&
(PsychPrefStateGet_VBLTimestampingMode() == 4)) {
// If time_at_last_vbl for an already finished or at least pending flip isn't available and
// we have support for OS-Builtin timestamping enabled, we try to employ OS-Builtin timestamping
// to get a timestamp for the most recent pending or finished flip. If this fails or is unsupported,
// it will have no effect:
PsychOSGetSwapCompletionTimestamp(windowRecord, 0, &(windowRecord->time_at_last_vbl));
}
// Return it - or the value zero if it is (still) undefined/unavailable:
PsychSetStructArrayDoubleElement("LastVBLTimeOfFlip", 0, windowRecord->time_at_last_vbl, s);
// Uncorrected timestamp of flip swap completion:
PsychSetStructArrayDoubleElement("RawSwapTimeOfFlip", 0, windowRecord->rawtime_at_swapcompletion, s);
// Timestamp immediately prior to call to PsychOSFlipWindowBuffers(), i.e., time at swap request submission:
PsychSetStructArrayDoubleElement("TimeAtSwapRequest", 0, windowRecord->time_at_swaprequest, s);
// Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion:
PsychSetStructArrayDoubleElement("TimePostSwapRequest", 0, windowRecord->time_post_swaprequest, s);
// Timestamp immediately after call to PsychOSFlipWindowBuffers() returns, i.e., time at swap request submission completion:
PsychSetStructArrayDoubleElement("VBLTimePostFlip", 0, windowRecord->postflip_vbltimestamp, s);
// Swap completion timestamp for most recently completed swap, according to OS-builtin PsychOSGetSwapCompletionTimestamp() method:
PsychSetStructArrayDoubleElement("OSSwapTimestamp", 0, windowRecord->osbuiltin_swaptime, s);
// Any GPU rendertime queries submitted whose results we shall collect?
if ((infoType != 7) && (windowRecord->gpuRenderTimeQuery)) {
// Yes: Poll if query result is available, otherwise no-op for this invocation:
gpuTimeElapsed = 0;
glGetQueryObjectuiv(windowRecord->gpuRenderTimeQuery, GL_QUERY_RESULT_AVAILABLE, &gpuTimeElapsed);
if (gpuTimeElapsed > 0) {
// Result available: Get it!
glGetQueryObjectuiv(windowRecord->gpuRenderTimeQuery, GL_QUERY_RESULT, &gpuTimeElapsed);
// Destroy query object:
glDeleteQueries(1, &windowRecord->gpuRenderTimeQuery);
windowRecord->gpuRenderTimeQuery = 0;
// Make sure that any successful query with a result at least records 1 Nanosecond of
// used time. Otherwise a context with no gpu activity and zero used time would cause
// a zero result, which also means "no result available" and causes user-script hang:
if (gpuTimeElapsed == 0)
gpuTimeElapsed = 1;
// Convert result in Nanoseconds back to seconds, and assign it:
windowRecord->gpuRenderTime = (double) gpuTimeElapsed / (double) 1e9;
}
}
// Result from last GPU rendertime query as triggered by infoType 5: Zero if undefined.
PsychSetStructArrayDoubleElement("GPULastFrameRenderTime", 0, windowRecord->gpuRenderTime, s);
// Try to determine system time of last VBL on display, independent of any
// flips / bufferswaps.
lastvbl = -1;
postflip_vblcount = 0;
// On supported systems, we can query the OS for the system time of last VBL, so we can
// use the most recent VBL timestamp as baseline for timing calculations,
// instead of one far in the past.
if (onscreen) { lastvbl = PsychOSGetVBLTimeAndCount(windowRecord, &postflip_vblcount); }
// If we couldn't determine this information we just set lastvbl to the last known
// vbl timestamp of last flip -- better than nothing...
if (lastvbl < 0) lastvbl = windowRecord->time_at_last_vbl;
PsychSetStructArrayDoubleElement("LastVBLTime", 0, lastvbl, s);
PsychSetStructArrayDoubleElement("VBLCount", 0, (double) (psych_int64) postflip_vblcount, s);
// Misc. window parameters:
PsychSetStructArrayDoubleElement("StereoMode", 0, windowRecord->stereomode, s);
PsychSetStructArrayDoubleElement("ImagingMode", 0, windowRecord->imagingMode, s);
// FIXME: Caution: specialflags is 64 bits, but double can only return about 53 low-bits correctly.
PsychSetStructArrayDoubleElement("SpecialFlags", 0, (double) windowRecord->specialflags, s);
PsychSetStructArrayDoubleElement("IsFullscreen", 0, (windowRecord->specialflags & kPsychIsFullscreenWindow) ? 1 : 0, s);
PsychSetStructArrayDoubleElement("MultiSampling", 0, windowRecord->multiSample, s);
PsychSetStructArrayDoubleElement("MissedDeadlines", 0, windowRecord->nr_missed_deadlines, s);
PsychSetStructArrayDoubleElement("FlipCount", 0, windowRecord->flipCount, s);
PsychSetStructArrayDoubleElement("StereoDrawBuffer", 0, windowRecord->stereodrawbuffer, s);
PsychSetStructArrayDoubleElement("GuesstimatedMemoryUsageMB", 0, (double) windowRecord->surfaceSizeBytes / 1024 / 1024, s);
PsychSetStructArrayDoubleElement("BitsPerColorComponent", 0, (double) windowRecord->bpc, s);
// Return VBL startline:
PsychSetStructArrayDoubleElement("VBLStartline", 0, (double) windowRecord->VBL_Startline, s);
// And VBL endline:
PsychSetStructArrayDoubleElement("VBLEndline", 0, windowRecord->VBL_Endline, s);
// Video refresh interval duration from beamposition method:
PsychSetStructArrayDoubleElement("VideoRefreshFromBeamposition", 0, windowRecord->ifi_beamestimate, s);
// Swap group assignment and swap barrier assignment, if any:
PsychSetStructArrayDoubleElement("SwapGroup", 0, windowRecord->swapGroup, s);
PsychSetStructArrayDoubleElement("SwapBarrier", 0, windowRecord->swapBarrier, s);
// Windowing system low-level onscreen window handle or equivalent info:
#if (PSYCH_SYSTEM == PSYCH_LINUX) && !defined(PTB_USE_WAYLAND)
// Linux/X11: The X-Window handle 'Window':
PsychSetStructArrayUnsignedInt64Element("SysWindowHandle", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.xwindowHandle, s);
#else
PsychSetStructArrayUnsignedInt64Element("SysWindowHandle", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.windowHandle, s);
#endif
// Windowing system low-level onscreen window related handle used for graphics/display api interop in some way: macOS use only atm.
PsychSetStructArrayUnsignedInt64Element("SysWindowInteropHandle", 0, (psych_uint64) (size_t) windowRecord->targetSpecific.deviceContext, s);
// Scaling factor for input coordinate transformation functions like RemapMouse.m:
PsychSetStructArrayDoubleElement("ExternalMouseMultFactor", 0, windowRecord->externalMouseMultFactor, s);
// Effectively selected VRR mode - ergo excluding 1 for "automatic", as that one would have mapped to a > 1 mode already:
PsychSetStructArrayDoubleElement("VRRMode", 0, windowRecord->vrrMode, s);
// Current assumption about VRR timing style:
PsychSetStructArrayDoubleElement("VRRStyleHint", 0, windowRecord->vrrStyleHint, s);
// Current assumption about VRR system latency:
PsychSetStructArrayDoubleElement("VRRLatencyCompensation", 0, windowRecord->vrrLatencyCompensation, s);
// Which basic GPU architecture is this?
PsychSetStructArrayStringElement("GPUCoreId", 0, windowRecord->gpuCoreId, s);
// FBO's supported, and how deep?
if (windowRecord->gfxcaps & kPsychGfxCapFBO) {
if (windowRecord->gfxcaps & kPsychGfxCapFPFBO32) {
PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 32, s);
} else
if (windowRecord->gfxcaps & kPsychGfxCapFPFBO16) {
PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 16, s);
} else PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 8, s);
}
else {
PsychSetStructArrayDoubleElement("GLSupportsFBOUpToBpc", 0, 0, s);
}
// How deep is alpha blending supported?
if (windowRecord->gfxcaps & kPsychGfxCapFPBlend32) {
PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 32, s);
} else if (windowRecord->gfxcaps & kPsychGfxCapFPBlend16) {
PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 16, s);
} else PsychSetStructArrayDoubleElement("GLSupportsBlendingUpToBpc", 0, 8, s);
// How deep is texture mapping supported?
if (windowRecord->gfxcaps & kPsychGfxCapFPTex32) {
PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 32, s);
} else if (windowRecord->gfxcaps & kPsychGfxCapFPTex16) {
PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 16, s);
} else PsychSetStructArrayDoubleElement("GLSupportsTexturesUpToBpc", 0, 8, s);
// How deep is texture filtering supported?
if (windowRecord->gfxcaps & kPsychGfxCapFPFilter32) {
PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 32, s);
} else if (windowRecord->gfxcaps & kPsychGfxCapFPFilter16) {
PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 16, s);
} else PsychSetStructArrayDoubleElement("GLSupportsFilteringUpToBpc", 0, 8, s);
if (windowRecord->gfxcaps & kPsychGfxCapVCGood) {
PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 1, s);
} else PsychSetStructArrayDoubleElement("GLSupportsPrecisionColors", 0, 0, s);
if (windowRecord->gfxcaps & kPsychGfxCapFP32Shading) {
PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 1, s);
} else PsychSetStructArrayDoubleElement("GLSupportsFP32Shading", 0, 0, s);
// Try to get and return vendor specific gpuMaintype + gpuMinorType:
// On AMD gpuMinorType is the DCE display engine revision, on NVidia the chip family name:
gpuMaintype = 0;
if (!PsychGetGPUSpecs(windowRecord->screenNumber, &gpuMaintype, &gpuMinorType, NULL, NULL)) {
// Query failed or unsupported:
PsychSetStructArrayDoubleElement("GPUMinorType", 0, -1, s);
}
else {
PsychSetStructArrayDoubleElement("GPUMinorType", 0, (double) gpuMinorType, s);
}
switch (gpuMaintype) {
case kPsychGeForce:
PsychSetStructArrayStringElement("DisplayCoreId", 0, "NVidia", s);
break;
case kPsychRadeon:
PsychSetStructArrayStringElement("DisplayCoreId", 0, "AMD", s);
break;
case kPsychIntelIGP:
PsychSetStructArrayStringElement("DisplayCoreId", 0, "Intel", s);
break;
default:
PsychSetStructArrayStringElement("DisplayCoreId", 0, windowRecord->gpuCoreId, s);
break;
}
if (infoType != 7) {
// Renderer information: This comes last, and would fail if async flips
// are active, because it needs PsychSetDrawingTarget, which in turn needs async
// flips to be inactive, unless imaging pipeline is fully enabled:
PsychSetStructArrayStringElement("GLVendor", 0, (char*) glGetString(GL_VENDOR), s);
PsychSetStructArrayStringElement("GLRenderer", 0, (char*) glGetString(GL_RENDERER), s);
PsychSetStructArrayStringElement("GLVersion", 0, (char*) glGetString(GL_VERSION), s);
// Try to get and return the OpenGL device UUID. Useful for uniquely identifying a
// physical gpu in the system. Note the UUID computation is not standardized, so each
// OpenGL driver/vendor may use a different method, ergo UUID are only comparable within
// gpu's of a specific gpu vendor:
if (glewIsSupported("GL_EXT_memory_object")) {
GLint numUUIDS = 0;
PsychGenericScriptType *out_uuid;
psych_uint8 *devUUID = NULL;
// AMD: Mesa radeonsi OpenGL PCI:domain:bus:device:function == Mesa radv. 32-bit per field.
// amdvlk bus:device:function:0
// => Can convert to amdvlk / amdgpu-pro style.
// NVidia: Identical hash between OpenGL and Vulkan.
// Intel and others: Unsupported.
glGetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, &numUUIDS);
if (numUUIDS > 0) {
PsychAllocateNativeUnsignedByteMat(1, GL_UUID_SIZE_EXT, 1, (psych_uint8**) &devUUID, &out_uuid);
glGetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, devUUID);
PsychSetStructArrayNativeElement("GLDeviceUUID", 0, out_uuid, s);
}
}
}
}
// Done.
return(PsychError_none);
}
|