home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-04-18 | 40.1 KB | 927 lines | [TEXT/MPCC] |
- /*
- TimeVideo.c
- Copyright © Denis G. Pelli, 1992, 1993, 1994, 1995
-
- TimeVideo does a thorough test of the timing and synchronization of each video
- screen, as well as the integrity of the clut hardware and software, and saves
- the results in a self-explanatory text file, “TimeVideo results”. TimeVideo
- runs on all Macs, requiring only System 6.05 or better. A discussion
- of movies, lookup table animation, and synchronization appears in the
- VideoToolbox “Video synch” file. The many video driver bugs uncovered by
- TimeVideo and its predecessors are reported in that file. (For example, version 3 of the
- TrueVision NuVista driver assumes zero start in 16- and 32-bit depth modes.) Please
- add your results by emailing your “TimeVideo results” to denis@xp.psych.nyu.edu.
-
- Despite Apple’s rules that require it, a few video drivers don’t allow you to
- read the clut (i.e. use cscGetEntries), or crash if you attempt it. GDGetEntries
- (in GDVideo.c) checks against a list of known offenders and returns a statusErr
- if the cscGetEntries call is not usable. If we can’t use cscGetEntries then we fall back to a
- visual test of the clut, comparing a particular way of loading the clut against
- the standard way of loading the clut. Any visible change between these two
- conditions suggests an error in the non-standard way of loading the clut.
-
- SaveAndRestoreDevice(device);
- Each time you call SaveAndRestoreDevice with a device different from the last it
- restores the former device to its last state. A NULL device counts as different,
- in that it provokes restoration of the previous device. The initial state of this
- routine is as though you had just called it with the NULL device.
-
- BUGS:
- Version 1.0 hung up on a PowerBook 170 with an Envisio video adapter. I didn’t
- have a chance to figure out why. It works fine on my ordinary PowerBook 170.
-
- HISTORY:
- 8/23/92 dgp wrote it, based on my TimeCPU.c
- 8/26/92 dgp print the driver version only if it's nonzero.
- added summary at end of printout.
- 9/9/92 dgp changed printout of system vbl rate from %.1f to %.2f
- Added movie rate to measurements and printout.
- 9/15/92 dgp made compatible with System 6.04. Added header to results file.
- 9/17/92 dgp Added QUICKLY switch, to select CopyBits or CopyBitsQuickly.
- 10/5/92 dgp cosmetic changes
- 10/6/92 dgp report program version. Use frames/clut update as the criterion for summary.
- 10/9/92 dgp Fixed summary. Too slow means MORE than one frame per clut update.
- Fixed restoration of clut in direct modes (i.e. 16 and 32 bit modes).
- Renamed to GDRestoreDeviceClut and put it in GDVideo.c.
- 10/13/92 dgp Tom Busey reported that frames were going uncounted
- during the clut timing, which seems to be a problem with some video drivers.
- TimeVideo now reports a frame count based solely on timing in secs and
- the separately measured frame rate, which seems to be a reliable.
- It also double checks the timing in secs vs frames, and if
- it finds a discrepancy, prints a warning to the screen & file.
- 10/20/92 dgp Added VBL/frame to summary.
- 12/8/92 dgp Minor editing of comments.
- 12/9/92 dgp 1.01 Added _atexit(RestoreCluts) in case user quits prematurely.
- 12/11/92 dgp 1.02 GDRestoreBlackAndWhite allocates stack space for linearTable
- only if we need it.
- 12/30/92 dgp 1.03 cosmetic changes.
- 12/30/92 dgp 1.04 Enhanced summary to account for NAN when no clut access is allowed.
- 12/30/92 dgp 1.05 Use GDClutSize().
- 1/6/93 dgp 1.06 Simplified GDRestoreBlackAndWhite, eliminated linearTable.
- 1/7/93 dgp 1.07 Included in VideoToolbox-1-93.sea
- 1/8/93 dgp 1.08 Added Info-Mac to report file.
- 1/11/93 dgp 1.09 In response to bug report from jonathan brecher, added support
- for computers that lack Color QuickDraw.
- 1/15/93 dgp 1.10 Added version resource.
- 1/18/93 dgp 1.11 Updated the explanatory text.
- 1/24/93 dgp 1.12 Updated the explanatory text.
- 2/6/93 dgp 1.13 Report ROM version.
- 2/15/93 dgp 1.14 Report ROM version as 124+6*256.
- 2/22/93 dgp 1.15 Recompiled.
- 2/27/93 dgp 1.16 Recompiled with new Identify.c.
- 3/10/93 dgp 1.17 Choose a valid mode for which to print the frame rate.
- Moved all the timing code into GDInfo.c, so this routine just prints.
- 3/12/93 dgp Reorganized the printout and added TestCluts's capabilities.
- 3/13/93 dgp Added high-priority timing.
- 3/16/93 dgp 2.0 Added visual hash inspection.
- 3/31/93 dgp 2.1 Release version, clut test seems to work.
- 4/5/93 dgp 2.2 Recognize gray1 error.
- 4/6/93 dgp 2.3 Remove assumption that mode numbers imply certain number of bits.
- 4/13/93 dgp 2.3.1 Only set results file type when it is created, not when appending.
- 4/15/93 dgp Display Testing window with gray wedge on test screen.
- 4/16/93 dgp 2.4b Restore compatibility with 1-bit qd.
- 4/19/93 dgp 2.4.1b Support old Mac II 24-bit NuBus addressing. Fixed two places
- where I used garbage in place of a linear color table when
- gdType==directType. Use new GDNewLinearColorTable.
- 4/19/93 dgp 2.4.2b Estimate color transformation matrix.
- 4/19/93 dgp 2.4.3b Use color transformation matrix to assess gray error.
- 4/19/93 dgp 2.4.4b Fixed RectToAddress to always return a 32-bit address,
- so it won't crash under System 6.
- 4/25/93 dgp 2.4.5b Correct for -0.5 bias in estimating rgb gains in TestClut.c
- 4/25/93 dgp 2.4.6b Test both color and gray modes; tolerance=3 lsb.
- 4/25/93 dgp 2.4.7b Worked around New Palette Manager bugs in original 32-bit QD.
- 4/27/93 dgp 2.4.8b Fixed stale-cache bug in SetPixelsQuickly. Cleaned up mode
- indexing.
- 4/28/93 dgp 2.4.9b Rewrote code dealing with SetDepth and HasDepth, wrote GDHasMode().
- Ignore "isGray" for 16 and 32-bit modes.
- 5/11/93 dgp 2.4.9bb Fixed bug in GDTestClutHash in GDTestClut.c in response to bug
- report by Jonathan Brecher.
- 5/17/93 dgp 2.4.9bbb Changed SetEntriesQuickly to respect the color/gray mode of device.
- 5/18/93 dgp 2.4.9b4 Check that card->device is not NULL.
- 5/18/93 dgp 2.4.9b5 Suppress printing of identity color transformation.
- 5/18/93 dgp 2.4.9b6 Note that 16 and 32 bit depths are always in color mode.
- 5/18/93 dgp 2.5 Release version.
- 6/2/93 dgp 2.5.1 Updated text reference to info-mac.
- 7/7/93 dgp 2.5.2 Recompiled with new GDDacSize, for compatibility with
- Radius PowerView.
- 1/13/94 dgp 2.5.3 Updated description of VideoToolbox.
- 6/7/94 dgp 3.0.0 New C-based version of CopyBitsQuickly. Fixed versions of CopyBitsQuickly,
- SetPixelsQuickly, and SetEntriesQuickly that are compatible with dirty ROMs.
- 6/14/94 dgp 3.0.1 Copy Monaco-font style resource from application into the "TimeVideo report"
- document.
- 6/19/94 dgp 3.0.2 Various cosmetic changes, recompile with latest CopyBitsQuickly and
- SetEntriesQuickly.
- 7/8/94 dgp 3.0.3 Recompiled with new THINK C 7.03.
- 7/29/94 dgp Eliminated use of "#s" printf format, since it's not supported by
- Metrowerks CodeWarrior C.
- 8/1/94 dgp 3.0.4 Recompiled with latest sources.
- 8/14/94 dgp Added workaround for [][][][!isGray] bug in Metrowerks CodeWarrior compiler.
- 9/5/94 dgp removed assumption in printf's that int==short.
- 10/12/94 dgp Used Metrowerks CodeWarrior C to produce fat binary.
- 10/20/94 dgp 3.5 Defined BLANKLINE in GDInfo.h to work around bug in
- Metrowerks CW4.5 bug in handling of \r.
- 11/17/94 dgp 3.6 updated the text describing VideoToolbox.
- 11/17/94 dgp 3.6.1 added explanation of “GDSetEntries ... NAN ... frames”.
- 11/30/94 dgp Use Display Manager to test all display modes. Untested.
- 12/29/94 dgp 3.6.2 WriteAndReadClut now waits for vbl before reading.
- 1/7/95 dgp 3.6.3 Got Display Manager calls to work.
- 3/18/94 dgp 3.6.4 Recompiled with CodeWarrior 5.5, which supposedly fixs some C bugs.
- 3/22/95 dgp 3.6.5 Added check for bad Dome driver, and enhanced detection of CW compiler version.
- 4/1/95 dgp Updated my address. Added advice about how to interpret cscSetEntries/cscGetEntries
- errors.
- 4/7/95 dgp 3.6.6 Read A7 draft of "Designing Cards & Drivers for Power Mac Computers", and
- added support for multiple resolutions on PCI Macs.
- 4/8/95 dgp 3.7.0 Polished the explanatory text that appears at the beginning of the results file.
- Disabled WAIT_FOR_VBL in WriteAndReadClut() in GDTestClut.c.
- 4/18/95 dgp Call SVersion() only if _SlotManager trap is available.
- */
- #include "VideoToolbox.h"
- #include <Traps.h>
- #include <math.h>
- #include <assert.h>
- #include <ROMDefs.h> // catDisplay,typeVideo
- #include "GDInfo.h"
- void TimeVideo(void);
- void PrintVideoInfo(FILE *o[2],VideoInfo *card);
- void ReportRGBGains(FILE *o[2],Boolean quickly,Boolean isGray,VideoInfo *card);
- long GDColors(GDHandle device);
- char setEntriesString[][18]={"cscSetEntries","SetEntriesQuickly"};
- char colorGrayString[][6]={"color","gray"};
- char ColorGrayString[][6]={"Color","Gray"};
- #if UNIVERSAL_HEADERS
- #include <Displays.h>
- #else
- enum{kDMDriverNotDisplayMgrAwareErr = -6228};
- extern pascal OSErr DMSetDisplayMode(GDHandle theDevice,unsigned long mode
- ,unsigned long *depthMode,unsigned long reserved,Handle displayState)
- ={0x303C,0x0A11,0xABEB};
- extern pascal OSErr DMCheckDisplayMode(GDHandle theDevice,unsigned long mode
- ,unsigned long depthMode,unsigned long *switchFlags,unsigned long reserved
- ,Boolean *modeOk)={0x303C,0x0C12,0xABEB};
- enum {
- gestaltDisplayMgrAttr = 'dply', /* Display Manager attributes */
- gestaltDisplayMgrPresent = 0 /* True if Display Mgr is present */
- };
- #endif
-
- /*
- These enum definitions for use with the cscGetNextResolution driver status call
- are taken from the A7 draft (Feb 10,'95) of Apple's Designing PCI Cards and Drivers for
- Power Mac Computers. Undoubtedly these enum definitions will appear in some future
- version of Video.h, perhaps with CodeWarrior 6, at which time they can be deleted from here.
- */
- enum{
- kDisplayModeIDCurrent=0
- ,kDisplayModeIDInvalid=0xffffffff
- ,kDisplayModeIDFindFirstResolution=0xfffffffe
- ,kDisplayModeIDNoMoreResolutions=0xfffffffd
- };
-
- #define VERSION "3.7.0"
-
- void SaveAndRestoreDevice(GDHandle device);
-
- void main(void)
- {
- StackGrow(30000L+MAX_SCREENS*sizeof(VideoInfo));
- Require(gestaltOriginalQD);
- TimeVideo();
- }
-
- void TimeVideo(void)
- {
- long system;
- static unsigned long v[256],colorMax;
- FILE *o[2],*dataFile;
- int newDataFile;
- unsigned long time;
- Str255 todayStr;
- int i,j,d,error;
- int cards,width,height;
- int quickly,isGray,doTest,ok;
- Rect r,screenRect;
- WindowPtr window;
- short doVisualTests,flags;
- VideoInfo *card;
- Handle rsrc;
- int rsrcFile;
- static char string[999],datafilename[]="TimeVideo results";
- long value;
- Boolean displayMgrOk,slotMgrPresent,testMultipleResolutions;
- SpBlock spBlock;
-
- assert(StackSpace()>5000);
- MaximizeConsoleHeight();
- #if (THINK_C || THINK_CPLUS)
- console_options.title="\pTimeVideo";
- #endif
- printf("\n"); // ask THINK C to initialize quickdraw
- GetDateTime(&time);
- srand(time);
- srandU(time);
- sprintf(string,"Welcome to TimeVideo " VERSION "\n\n"
- "This program will thoroughly test all your video devices and save the "
- "results in the text file “%s”. Don’t be alarmed by the strange "
- "antics of your screens. Everything will soon be back to normal. Just sit back "
- "and enjoy the show.",datafilename);
- sprintf(string,"%s You may quit at any time by hitting Command-period.\n",string);
- printf(BreakLines(string,78));
- if(1)printf(BreakLines("\n"
- "TimeVideo times everything: the video frames, interrupts, and cluts. And it "
- "determines what fraction of the screen you can fill with a real-time movie shown "
- "by CopyBits() or CopyBitsQuickly(). Then it does write-then-read tests of the "
- "clut, to make sure the video hardware and software "
- "are working correctly.\n",78));
- doVisualTests=Choose(1,"\nWould you like the testing to pause for visual inspections?\n",noYes,2);
- value=0;
- Gestalt(gestaltDisplayMgrAttr,&value);
- displayMgrOk=value&(1<<gestaltDisplayMgrPresent);
- if(displayMgrOk)
- testMultipleResolutions=Choose(0,"Test multiple resolutions of each display?\n",noYes,2);
- else testMultipleResolutions=0;
-
- printf("\n");
- dataFile=fopen(datafilename,"r");
- newDataFile=(dataFile==NULL);
- if(newDataFile){
- // try to copy SimpleText style resource from TimeVideo application into our document.
- rsrc=GetResource('styl',128);
- if(rsrc!=NULL){
- c2pstr(datafilename);
- CreateResFile((unsigned char *)datafilename);
- rsrcFile=OpenResFile((unsigned char *)datafilename);
- p2cstr((unsigned char *)datafilename);
- if(rsrcFile!=-1){
- DetachResource(rsrc);
- UseResFile(rsrcFile);
- AddResource(rsrc,'styl',128,(ConstStr255Param)"/pTimeVideo report style");
- CloseResFile(rsrcFile);
- }else ReleaseResource(rsrc);
- }
- }else fclose(dataFile);
- o[0]=stdout;
- o[1]=dataFile=fopen(datafilename,"a"); /* Append to data file */
- if(dataFile==NULL){
- printf("Could neither open nor create “%s”. Perhaps your disk is locked.\n"
- ,datafilename);
- newDataFile=0;
- }
- if(newDataFile){
- SetFileInfo(datafilename,'TEXT','ttxt');
- fprintf(dataFile,BreakLines(
- "This file reports the timing and accuracy of all your video screens, "
- "as measured by TimeVideo, a component of the VideoToolbox. "
- "TimeVideo runs on all Macs, requiring only System 6.05 or better. To quickly "
- "test a large number of computers, run TimeVideo from a floppy disk; "
- "all the results will accumulate in a single results file.\n"
- "\nTHE VIDEO TOOLBOX\n"
- "The VideoToolbox is a collection of two hundred C subroutines and "
- "several demo and utility programs that I and others have written to do "
- "visual psychophysics with Macintosh computers. It is fully compatible "
- "with 680x0 and PowerPC Macs and with Metrowerks CodeWarrior C 5 and "
- "Symantec THINK C 7 compilers. It's free and may not be sold without "
- "permission. It should be useful to anyone who wants to present "
- "accurately specified visual stimuli or use the Mac for psychometric "
- "experiments. The text file \"Video synch\" discusses all the ways of "
- "synchronizing programs to video displays and the many pitfalls to avoid. "
- "The TimeVideo application checks out the timing of all video devices in "
- "anticipation of their use in critical real-time applications, e.g. "
- "movies or lookup table animation. Low-level routines control video "
- "timing and lookup tables, display real-time movies, and implement the "
- "luminance-control algorithms suggested by Pelli and Zhang (1991). (D.G. "
- "Pelli and L. Zhang, 1991, \"Accurate control of contrast on "
- "microcomputer displays.\" Vision Research, 31, 1337-1350. Reprints are "
- "available.) In particular, GetPixelsQuickly and SetPixelsQuickly peek "
- "and poke pixels in bitmaps and pixmaps, CopyBitsQuickly and CopyWindows "
- "faithfully copy between bit/pixmaps and the screen, WindowToEPS saves an "
- "image to disk, for later printing or incorporation into a document, and "
- "SetEntriesQuickly and GDSetEntries load the screen's color lookup table, "
- "all without any of QuickDraw's color translations. High-level routines "
- "help analyze psychophysical experiments (e.g. graphing or "
- "maximum-likelihood fitting of psychometric data). Assign.c is a runtime "
- "C interpreter for C assignment statements, which is useful for "
- "controlling experiments and sharing calibration data. "
- "Many of the routines are Mac-specific, but some very "
- "useful routines, e.g. the luminance-control, statistics, "
- "maximum-likelihood fitting algorithms, and the runtime interpreter are "
- "written in Standard C and will work on any computer. "
- "Documentation is in the source files themselves. "
- "This collection has been continually updated since 1991. "
- "You can download the latest version of "
- "\"video-toolbox\" from a public archive, e.g.:\n"
- "ftp://sumex-aim.stanford.edu/info-mac/dev/src/\n"
- "ftp://mirrors.aol.com/pub/info-mac/dev/src/\n"
- "ftp://grind.isca.uiowa.edu/mac/infomac/dev/src/\n"
- "ftp://ftp.uu.net/archive/systems/mac/info-mac/dev/src/\n"
- "ftp://amug.org/pub/ftp1/info-mac/dev/src/\n"
- "ftp://wuarchive.wustl.edu/systems/mac/info-mac/dev/src/\n"
- "ftp://src.doc.ic.ac.uk/packages/mac/info-mac/dev/src/\n"
- "ftp://ftp.stolaf.edu/pub/macpsych/\n"
- "CompuServe://MacDev forum/Library 4 \"C and Pascal\"/VIDEOT.SEA\n"
- "Or you can request a file by email; for instructions send a query to "
- "Info-Mac-Request@sumex-aim.stanford.edu."
- "\n\nTo get future versions automatically, just send me your name and email address. "
- "Each time I post a new version of the VideoToolbox to the Info-Mac "
- "Archives, I email a copy to everyone on the subscription list; there are "
- "currently over one hundred subscribers.\n",78));
- fprintf(dataFile,
- "Denis Pelli, denis@xp.psych.nyu.edu\n"
- "Until July, 1995: Institute for Sensory Research, Syracuse University, "
- "Syracuse, NY 13244-5290, USA.\n"
- "Thereafter: Department of Psychology, New York University, "
- "6 Washington Place, New York, NY 10003, USA\n"
- "\n");
- fprintf(dataFile,BreakLines(
- "\nTIME VIDEO\n"
- "For each video card, TimeVideo measures the video frame rate, frequency of VBL "
- "interrupts (ought to be one per frame), how long it takes to load the clut "
- "(ought to be one frame or less), "
- "and how much of the screen you can fill with a real-time one-image-per-frame "
- "movie shown by CopyBits() or CopyBitsQuickly(). It then performs a random "
- "write-then-read test of the Color Lookup Table (clut). This tests the clut "
- "memory hardware and the software used to write and read the clut. We test "
- "writing by GDSetEntries(), which makes a cscSetEntries call to the video driver, and, "
- "if possible, we also test writing by SetEntriesQuickly(), which accesses the hardware "
- "directly. (SetEntriesQuickly supports only a few video cards.) "
- "In either case, the clut is read by GDGetEntries, "
- "which makes a cscGetEntries call to the video driver. "
- "The testing is thorough; many video devices fail "
- "at least part of the test. All the driver errors uncovered to date appear in the "
- "VideoToolbox \"Video bugs\" text file, and have been reported to the video card's "
- "manufacturer. Add your results by emailing this file to "
- "denis@xp.psych.nyu.edu\n"
- "\nHappily, we don't know of any cases of errors in cscSetEntries itself, "
- "and we've never run across a hardware failure of the CLUT memory. "
- "Our experience is that errors in the cscSetEntries/cscGetEntries write-then-read "
- "test of the CLUT occur in two ways: "
- "\n1. The driver does not implement cscSetGamma and the fixed gamma table is "
- "not the identity transformation, i.e. \"uncorrected gamma table\". "
- "TimeVideo always calls cscSetGamma, requesting an identity transform. "
- "In all drivers, the RGB values supplied in a cscSetEntries call "
- "are transformed by the gamma table before being loaded into the CLUT. "
- "The presence of a non-identity "
- "gamma table is usually obvious from a glance at the color matrix, since the gains "
- "in the matrix will exceed 1 by about twice the fractional rms error indicated by ±%%. "
- "\n2. The CLUT is set correctly, but the driver incorrectly implements cscGetEntries. "
- "Bugs in cscGetEntries are usually obvious from a glance at the color matrix, "
- "e.g. nonzero gains off the diagonal, or the pattern of errors.\n"
- "\nErrors reported by TimeVideo are usually due to bugs in the video driver software "
- "in the ROM of the video card (or built-in video device). If the bug will "
- "interfere with your experiments, "
- "then to use the card (or built-in video) you must either fix/replace the driver or "
- "bypass the driver, using SetEntriesQuickly to access the hardware directly "
- "(if SetEntriesQuickly supports it). "
- "I suggest that you try replacing the driver, because this will keep your "
- "software hardware-independent. The VideoToolbox \"Video synch\" document explains "
- "how to fix the the Mac IIci video driver, patching or replacing the buggy version "
- "0 .Display_Video_Apple_RBV1 driver by copying the bug-free version 1 of the same "
- "driver from the Mac IIsi. It is very likely that an analogous approach "
- "could be used to fix/replace the buggy version 0, 1, and 2 .Display_Video_Apple_DAFB "
- "drivers in the Quadra 700, 750, and 900, by the bug-free version 3 or 5 of that driver "
- "in the Centris 650 or the LC 475. And please report your bugs; the bug-free revised drivers "
- "are probably the result of our past bug reports.\n"
- "\nGLOSSARY\n"
- "A video frame is a complete refresh of your video screen. (With interlacing it takes "
- "two fields to do a complete refresh, i.e. a frame, but graphics displays usually are "
- "not interlaced and have a single field per frame.) To show a movie, you will want "
- "to reload the image once per frame; the table shows how big that image can be, "
- "as a fraction of the screen area, and still be reloaded once per frame. "
- "(Some video cards have multiple video \"pages\"--call GDGetPageCnt()--that "
- "can be switched by calling GDSetPageDrawn() and GDSetPageShown().) "
- "A \"VBL\" "
- "interrupt is produced by your video card, nominally once per frame, but "
- "some video cards produce more, which is poor, but not serious. (The "
- "VBLInstall.c program will deal with it.) Suppressed interrupts during clut "
- "updates are bad. It means that the driver disables the VBL interrupt for too "
- "long while it's loading the clut. This will throw off any interrupt-based "
- "attempt to count frames. (The clut is the color lookup table of your video card.) "
- "Lookup table animation, e.g. for temporal modulation of contrast, requires that "
- "you reload the clut once per frame, so it's very important that this be fast "
- "enough. "
- "The first call to SetEntriesQuickly() for each device is slow--a cache is "
- "filled; the reported times are for subsequent calls. "
- "Each video card can be in \"Color\" or \"Gray\" mode, as set by the Control "
- "Panel:Monitors or the Macintosh Toolbox call SetDepth(). "
- "In \"Gray\" mode all colors are transformed to luminance-equivalent "
- "grays. This is done by the video driver, when loading the clut, "
- "but only if the pixelSize≤8. "
- "TimeVideo does a linear regression on the results of write-then-read tests of the CLUT "
- "to measure the driver's color transformation matrix. It reports the matrix "
- "if it is other than the identity transformation:\n"
- "(ROut) (1 0 0) (RIn)\n"
- "(GOut)=(0 1 0)x(GIn)\n"
- "(BOut) (0 0 1) (BIn)\n"
- "The clut tests try loading the clut serially, one entry at a time, and "
- "all at once; some video drivers fail the serial test. For more explanation see "
- "the text file called \"Video synch\" on the VideoToolbox disk.\n"
- "\n"
- "\"cscSetEntries ... NAN ... frames\"= the driver refuses to load the CLUT.\n"
- "\"ok\"= passed all tests.\n"
- "\"!gray\"= passed the color test, but is supposed to be in gray mode.\n"
- "\"!color\"= passed the gray test, but is supposed to be in color mode.\n"
- "\"!serial\"= passed when loaded all at once, but failed when loaded serially.\n"
- "\"bad\"= read did not equal write and the error is reported explicitly.\n"
- "We know that SetEntriesQuickly is \"!serial\" on the Quadra, alas.\n"
- "\nThis is a SimpleText document using the Monaco font to line up the \n"
- "columns properly.\n\n",78));
- }
- ffprintf(o,"TimeVideo version " VERSION "\n");
- GetDateTime(&time);
- IUDateString(time,longDate,todayStr);
- ffprintf(o,"%s.\n",p2cstr(todayStr));
- ffprintf(o,"%s\n",IdentifyCompiler());
-
- // Timer.c requires the Revised Time Manager, which appeared in System 6.0.3.
- // SetDepth() is part of the "new" Palette Manager, which appeared in System 6.0.5.
- Gestalt(gestaltSystemVersion,&system);
- if(system<0x605)PrintfExit("Sorry. Your System is too old; I need at least System 6.0.5.\n");
- card=(VideoInfo *)NewPtrClear(MAX_SCREENS*sizeof(*card));
- if(card==NULL)PrintfExit("Need more memory. "
- "Use Finder's File:Get Info to increase TimeVideo's allocation.\n");
- ffprintf(o,"%s\n",BreakLines(IdentifyMachine(),78));
- ffprintf(o,"Tick rate is %.1f Hz.",TickRate());
- ffprintf(o," System-based VBL rate is %.1f Hz.\n",GDFrameRate(NULL));
- if(QD8Exists())_atexit(RestoreCluts);// In case user quits prematurely.
- if(TrapAvailable(_SlotManager))slotMgrPresent=(SVersion(&spBlock)==noErr);
- else slotMgrPresent=0;
- spBlock.spSlot=0;
- spBlock.spID=0;
- for(i=0;;i++){
- if(QD8Exists()){
- if(testMultipleResolutions){ // index through the display modes, one by one
- unsigned long currentDisplayModeID,depthModeUL,switchFlags;
- Boolean modeOk;
- unsigned long horizontalPixels,verticalLines; // not used
- Fixed refreshRate; // not used
- unsigned short maxDepthMode; // not used
- static int deviceCounter=0;
- static unsigned long displayModeID=kDisplayModeIDFindFirstResolution;
-
- if(slotMgrPresent){
- spBlock.spExtDev=0;
- spBlock.spHwDev=0;
- spBlock.spCategory=catDisplay;
- spBlock.spCType=typeVideo;
- }
- nextDisplayModeID:
- if(slotMgrPresent){
- // use slot manager to find every driver displayModeID
- spBlock.spTBMask=3; // ignore DrvrHW and DrvrSW
- spBlock.spParamData=1; // next resource, any slot, whether enabled or disabled
- error=SGetTypeSRsrc(&spBlock);
- if(error){
- // done
- SaveAndRestoreDevice(NULL);
- break;
- }
- displayModeID=(unsigned char)spBlock.spID; // "resource reference number"
- if(spBlock.spRefNum==0){
- spBlock.spTBMask=2; // ignore DrvrSW
- spBlock.spParamData=2; // find enabled resource in same slot with same DrvrHW
- spBlock.spID=0;
- error=SGetTypeSRsrc(&spBlock);
- if(error)goto nextDisplayModeID;
- spBlock.spID=displayModeID; // restore
- }
- card[i].device=GetDeviceByRefNum(spBlock.spRefNum);
- SaveAndRestoreDevice(card[i].device);
- if(card[i].device==NULL)goto nextDisplayModeID;
- }else{
- // PCI bus: use new driver calls to test each displayModeID of each driver
- card[i].device=GetScreenDevice(deviceCounter);
- SaveAndRestoreDevice(card[i].device);
- if(card[i].device==NULL)break; // no more video devices; go home.
- error=GDGetNextResolution(card[i].device,displayModeID
- ,&displayModeID,&horizontalPixels,&verticalLines
- ,&refreshRate,&maxDepthMode);
- if(error || displayModeID==kDisplayModeIDNoMoreResolutions
- || displayModeID==kDisplayModeIDInvalid){
- deviceCounter++;
- displayModeID=kDisplayModeIDFindFirstResolution;
- // error merely indicates that driver doesn't support the Display Manager
- if(error)goto testDeviceAnyway;
- goto nextDisplayModeID;
- }
- }
- error=DMCheckDisplayMode(card[i].device,displayModeID,firstVidMode,&switchFlags,0,&modeOk);
- if(!modeOk)goto nextDisplayModeID;
- error=GDGetDisplayMode(card[i].device,¤tDisplayModeID,NULL,NULL,NULL);
- if(error || currentDisplayModeID!=displayModeID){
- depthModeUL=firstVidMode;
- error=DMSetDisplayMode(card[i].device,displayModeID,&depthModeUL,0,NULL);
- if(error==kDMDriverNotDisplayMgrAwareErr)goto testDeviceAnyway;
- if(error)goto nextDisplayModeID;
- }
- testDeviceAnyway:;
- }else{
- // index through the video devices
- card[i].device=GetScreenDevice(i);
- SaveAndRestoreDevice(card[i].device);
- if(card[i].device==NULL)break;
- }
- }else card[i].device=NULL;
- ffprintf(o,"\n%s\n",BreakLines(IdentifyVideo(card[i].device),78));
- printf("Getting card info. . ." "\r");
- error=GDInfo(&card[i]);
-
- // Specify what tests we want.
- for(d=0;d<6;d++)for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
- card[i].depth[d].clut[quickly][isGray].read.doTest=1;
- }
- if(card[i].device!=NULL)isGray=!TestDeviceAttribute(card[i].device,gdDevType);
- else isGray=0;
- // Further test deepest directType and clutType modes
- d=5;
- while(card[i].depth[d].pixelSize>=0 && card[i].depth[d].pixelSize<16)d--;
- if(d>=0)for(quickly=0;quickly<2;quickly++){
- int notIsGray=!isGray;
- card[i].depth[d].clut[quickly][isGray].hash.doTest=doVisualTests;
- card[i].depth[d].clut[quickly][notIsGray].read.doTest=1;
- }
- d=3;
- while(card[i].depth[d].pixelSize==0 || card[i].depth[d].pixelSize>8)d--;
- if(d>=0)for(quickly=0;quickly<2;quickly++){
- int notIsGray=!isGray;
- card[i].depth[d].clut[quickly][isGray].hash.doTest=doVisualTests;
- card[i].depth[d].clut[quickly][notIsGray].read.doTest=1;
- }
-
- // Open window
- if(QD8Exists()){
- r=screenRect=(*card[i].device)->gdRect;
- }else{
- CopyQuickDrawGlobals(); // make sure qd is valid.
- r=screenRect=qd.screenBits.bounds;
- }
- OffsetRect(&r,-r.left,-r.top);
- width=(r.right/=3);
- if(width>256)width=r.right=256;
- height=r.bottom=width;
- CenterRectInRect(&r,&screenRect);
- if(QD8Exists())window=(WindowPtr)NewCWindow(NULL,&r,"\pCLUT Colors",TRUE,noGrowDocProc,(WindowPtr) -1L,0,0);
- else window=NewWindow(NULL,&r,"\pTesting",TRUE,noGrowDocProc,(WindowPtr) -1L,0,0);
-
- for(d=0;d<6;d++)for(isGray=0;isGray<2;isGray++){
- if(card[i].depth[d].pixelSize==0)continue;
- doTest=0;
- for(quickly=0;quickly<2;quickly++){
- doTest|=card[i].depth[d].clut[quickly][isGray].read.doTest;
- doTest|=card[i].depth[d].clut[quickly][isGray].hash.doTest;
- doTest|=card[i].depth[d].clut[quickly][isGray].visual.doTest;
- }
- if(!doTest)continue;
- if(card[i].device!=NULL){
- if(card[i].depth[d].mode!=(**card[i].device).gdMode
- || isGray!=!TestDeviceAttribute(card[i].device,gdDevType)){
- // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100
- // at all legal depths.
- if(card[i].depth[d].pixelSize>0)
- ok=HasDepth(card[i].device,card[i].depth[d].pixelSize,1,!isGray);
- else ok=1;
- // Mac IIci Sys 6.07: SetDepth only accepts mode, not pixelSize.
- if(ok)error=SetDepth(card[i].device,card[i].depth[d].mode,1,!isGray);
- if(card[i].depth[d].mode!=(**card[i].device).gdMode
- || isGray!=!TestDeviceAttribute(card[i].device,gdDevType))continue;
- }
- }else if(card[i].depth[d].pixelSize!=1 || isGray)continue;
- GDInfo(&card[i]);
-
- // Fill window with spectrum of all colors.
- colorMax=GDColors(card[i].device)-1;
- for(j=0;j<width;j++)v[j]=(j*colorMax+width/2)/(width-1);
- for(j=0;j<height;j++)SetWindowPixelsQuickly(window,0,j,v,width);
-
- if(!card[i].depth[d].timeTested){
- error=GDInfoTime(&card[i]);
- for(j=0;j<height;j++)SetWindowPixelsQuickly(window,0,j,v,width);//refresh
- }
- for(quickly=0;quickly<2;quickly++){
- printf(BLANKLINE);
- printf("%d-bit %s pixels: testing clut: %s . . ." "\r"
- ,(int)card[i].depth[d].pixelSize,colorGrayString[isGray]
- ,setEntriesString[quickly]);
- if(quickly)flags=testClutQuicklyFlag;
- else flags=0;
- error=GDTestClut(o,flags,&card[i]);
- error=GDTestClut(o,flags|testClutSeriallyFlag,&card[i]);
- error=GDTestClutHash(flags,&card[i]);
- }
- }
- DisposeWindow(window);
- if(QD8Exists()){
- // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 at all legal depths.
- // and SetDepth only accepts mode, not depth.
- // ok=HasDepth(card[i].device,oldPixelSize,1,oldIsColor);
- // if(ok)error=SetDepth(card[i].device,oldDepthMode,1,oldIsColor);
- }
- printf(BLANKLINE);
- printf("Getting card info. . ." "\r");
- error=GDInfo(&card[i]);
- printf(BLANKLINE);
- PrintVideoInfo(o,&card[i]);
- if(dataFile!=NULL)fflush(dataFile);
- if(kbhit())getcharUnbuffered(); // Give console a chance to detect Command-.
- if(!QD8Exists())break;
- }
- cards=i;
- if(GDVersion(card[i].device)==100
- && EqualString("\p.Display_Video_Apple_RBV1",GDName(card[i].device),1,1)){
- ffprintf(o,
- "NOTE: the built-in driver in the Mac IIci (.Display_Video_Apple_RBV1 version 0)\n"
- "has a bug that causes it to crash if you attempt to read the clut. A temporary\n"
- "patch has been applied that has fixed the driver until the next reboot, as\n"
- "explained in the VideoToolbox file \"Video synch\".\n");
- }
- if(dataFile!=NULL){
- fprintf(dataFile,"\n\n");
- fclose(dataFile);
- sprintf(string,"\nThe text file \"%s\" explains all the results.\n",datafilename);
- printf(BreakLines(string,78));
- }
- DisposePtr((Ptr)card);
- }
-
- void PrintVideoInfo(FILE *o[2],VideoInfo *card)
- {
- short i,d,tested,quickly,isGray,grayClut,reportRGB;
- char string[100];
- static char s1[]="%-33s",s2[]="%6s",s3[]=" %-9s\n";
- VideoCardClutTest *clut;
-
- if(card->device!=NULL)isGray=!TestDeviceAttribute(card->device,gdDevType);
- else isGray=0;
- ffprintf(o,"%d-bit dacs. ",(int)card->dacSize);
- ffprintf(o,"%dx%d pixels. ",(int)card->width,(int)card->height);
- // ffprintf(o,"%s mode. ",ColorGrayString[isGray]);
- ffprintf(o,"\n");
-
- if(card->basicTested){
- ffprintf(o,s1,"pixel size");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%d ",(int)card->depth[d].pixelSize);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"bits");
-
- if(QD8Exists()){
- // I haven't tried to look for 1-bit QD's page swapping scheme
- ffprintf(o,s1,"pages");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%d ",(int)card->depth[d].pages);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"");
-
- // Meaningless in 1-bit QD
- ffprintf(o,s1,"mode");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"0x%x ",card->depth[d].mode);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"");
- }
- }
-
- if(card->timeTested){
- ffprintf(o,s1,"frame rate");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.1f ",card->depth[d].frameRate);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"Hz");
-
- ffprintf(o,s1,"interrupts per frame");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.1f ",card->depth[d].vblPerFrame);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"");
-
- ffprintf(o,s1,"CopyBits movie size");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].movieRate/card->depth[d].frameRate);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"screen");
-
- ffprintf(o,s1,"CopyBitsQuickly movie size");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].movieRateQuickly/card->depth[d].frameRate);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"screen");
-
- ffprintf(o,s1,"CopyBits data rate");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].movieRate
- *card->depth[d].pixelSize
- *card->width*card->height/8./1024./1024.);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"MB/s");
-
- ffprintf(o,s1,"CopyBitsQuickly data rate");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].movieRateQuickly
- *card->depth[d].pixelSize
- *card->width*card->height/8./1024./1024.);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"MB/s");
- }
-
- if(card->device!=NULL && (**card->device).gdType!=fixedType && card->timeTested){
- ffprintf(o,s1,"cscSetEntries duration");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].framesPerClutUpdate);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"frames");
-
- ffprintf(o,s1,"cscSetEntries suppresses ints. for");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.1f ",card->depth[d].missingFramesPerClutUpdate);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"frames");
-
- ffprintf(o,s1,"GDSetEntriesHighPriority duration");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].framesPerClutUpdateHighPriority);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"frames");
-
- if(card->setEntriesQuickly){
- ffprintf(o,s1,"SetEntriesQuickly duration");
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- sprintf(string,"%.2f",card->depth[d].framesPerClutUpdateQuickly);
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"frames");
- }
- }
- if(card->device!=NULL && (**card->device).gdType!=fixedType && card->clutTested){
- for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
- tested=0;
- for(d=0;d<6;d++)if(card->depth[d].pixelSize)
- if(card->depth[d].clut[quickly][isGray].hash.tested)tested=1;
- if(tested){
- sprintf(string,"%s hash inspection",setEntriesString[quickly]);
- ffprintf(o,s1,string);
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- clut=&card->depth[d].clut[quickly][isGray];
- if(clut->hash.tested){
- if(clut->hash.errors)
- sprintf(string,"fail");
- else sprintf(string,"ok");
- }else sprintf(string,"");
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"");
- }
- }
-
- for(quickly=0;quickly<2;quickly++)for(isGray=0;isGray<2;isGray++){
- tested=0;
- for(d=0;d<6;d++)if(card->depth[d].pixelSize)
- if(card->depth[d].clut[quickly][isGray].read.tested)tested=1;
- if(tested){
- reportRGB=0;
- sprintf(string,"%s: %s test"
- ,colorGrayString[isGray],setEntriesString[quickly]);
- ffprintf(o,s1,string);
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- clut=&card->depth[d].clut[quickly][isGray];
- grayClut=isGray && card->depth[d].pixelSize<=8;
- if(clut->read.tested){
- if(clut->read.errors){
- if(clut->read.errorsAtOnce)sprintf(string,"bad");
- else sprintf(string,"!serial");
- }else if(grayClut!=clut->read.identity)sprintf(string,"ok");
- else sprintf(string,"!%s",colorGrayString[grayClut]);
- }else sprintf(string,"");
- ffprintf(o,s2,string);
- if(card->depth[d].pixelSize<=8)reportRGB=!clut->read.identity;
- }
- ffprintf(o,s3,"");
- if(reportRGB)ReportRGBGains(o,quickly,isGray,card);
- }
-
- tested=0;
- for(d=0;d<6;d++)if(card->depth[d].pixelSize)
- if(card->depth[d].clut[quickly][isGray].visual.tested)tested=1;
- if(tested){
- sprintf(string,"%s visual inspection",setEntriesString[quickly]);
- ffprintf(o,s1,string);
- for(d=0;d<6;d++)if(card->depth[d].pixelSize){
- clut=&card->depth[d].clut[quickly][isGray];
- if(clut->visual.tested){
- if(clut->visual.errors){
- if(!clut->visual.errorsAtOnce)sprintf(string,"!serial");
- else sprintf(string,"bad");
- }else sprintf(string,"ok");
- }else sprintf(string,"");
- ffprintf(o,s2,string);
- }
- ffprintf(o,s3,"");
- }
- }
- }
- for(i=0;i<2;i++)if(o[i]!=NULL)fflush(o[i]); // Save to disk, just in case.
- }
-
- // Print a 3x3 color matrix representing a best fit to the observed relation between
- // the colors we read and the colors we write.
- void ReportRGBGains(FILE *o[2],Boolean quickly,Boolean isGray,VideoInfo *card)
- {
- short j,k,d,depths;
- VideoCardClutTest *clut;
- double rgbGain[3][3],rgbError[3];
- Boolean integralGains,bad;
-
- depths=0;
- for(j=0;j<3;j++){
- for(k=0;k<3;k++)rgbGain[j][k]=0.;
- rgbError[j]=0.0;
- }
- integralGains=1;
- for(d=0;d<6;d++)if(card->depth[d].pixelSize && card->depth[d].pixelSize<=8){
- clut=&card->depth[d].clut[quickly][isGray];
- bad=0;
- for(j=0;j<3;j++)bad|=clut->read.rgbError[j]>2.0*(1<<(16-4));
- if(bad)continue;
- for(j=0;j<3;j++){
- for(k=0;k<3;k++)rgbGain[j][k]+=clut->read.rgbGain[j][k];
- rgbError[j]+=clut->read.rgbError[j];
- }
- depths++;
- }
- for(j=0;j<3;j++){
- for(k=0;k<3;k++){
- rgbGain[j][k]/=depths;
- integralGains&=(rgbGain[j][k]==floor(rgbGain[j][k]));
- }
- rgbError[j]/=depths;
- }
- if(IsFinite(rgbGain[0][0]))for(j=0;j<3;j++){
- ffprintf(o," (%cOut±%4.1f%%)%c"
- ,"RGB"[j],rgbError[j]*100./USHRT_MAX," = "[j]);
- if(integralGains)ffprintf(o,"(%1.0f %1.0f %1.0f)"
- ,rgbGain[j][0],rgbGain[j][1],rgbGain[j][2]);
- else ffprintf(o,"(%3.2f %3.2f %3.2f)"
- ,rgbGain[j][0],rgbGain[j][1],rgbGain[j][2]);
- ffprintf(o,"%c(%cIn)\n"," x "[j],"RGB"[j]);
- }
- }
-
- // On Mac IIci, Sys 6.07, HasDepth returns "mode" of 0x100 at all legal depths.
- // and SetDepth only accepts mode, not depth.
- // ok=HasDepth(card[i].device,oldPixelSize,1,oldIsColor);
- // if(ok)error=SetDepth(card[i].device,oldDepthMode,1,oldIsColor);
-
- void SaveAndRestoreDevice(GDHandle device)
- {
- static Boolean firstTime=1,displayMgrOk,colorQdOk;
- static GDHandle oldDevice=NULL;
- static unsigned short oldDepthMode=firstVidMode,oldIsColor=1;
- static unsigned long oldDisplayModeID=kDisplayModeIDInvalid,displayModeID;
- unsigned long depthModeUL;
- int error;
- long value;
-
- if(firstTime){
- Gestalt(gestaltSystemVersion,&value);
- assert(value>=0x605); // need New Palette Manager for SetDepth
- Gestalt(gestaltQuickdrawVersion,&value);
- colorQdOk=value>=gestalt8BitQD;
- value=0;
- Gestalt(gestaltDisplayMgrAttr,&value);
- displayMgrOk=value&(1<<gestaltDisplayMgrPresent);
- firstTime=0;
- }
- if(colorQdOk && device!=oldDevice){
- // restore old device to old state
- if(oldDevice!=NULL){
- if(displayMgrOk && oldDisplayModeID!=kDisplayModeIDInvalid){
- error=GDGetDisplayMode(oldDevice,&displayModeID,NULL,NULL,NULL);
- if(displayModeID!=oldDisplayModeID){
- depthModeUL=oldDepthMode;
- error=DMSetDisplayMode(oldDevice,oldDisplayModeID,&depthModeUL,0,NULL);
- }
- }
- error=SetDepth(oldDevice,oldDepthMode,1,oldIsColor);
- }
- // save old state of new device
- oldDevice=device;
- oldDisplayModeID=kDisplayModeIDInvalid; // default, in case call fails
- if(oldDevice!=NULL){
- if(displayMgrOk)
- error=GDGetDisplayMode(oldDevice,&oldDisplayModeID,&oldDepthMode,NULL,NULL);
- oldDepthMode=(**device).gdMode;
- oldIsColor=TestDeviceAttribute(device,gdDevType);
- }
- }
- }
-
-