comparison src/video/quartz/SDL_QuartzVideo.m @ 588:2c6510c0a304

Darrell added support for emulated SDL_DOUBLEBUF on MacOSX
author Sam Lantinga <slouken@libsdl.org>
date Sat, 01 Feb 2003 19:59:23 +0000
parents cd41dca47fff
children 864e2d2a9a55
comparison
equal deleted inserted replaced
587:f00ccf8d8edc 588:2c6510c0a304
29 29
30 /* Bootstrap binding, enables entry point into the driver */ 30 /* Bootstrap binding, enables entry point into the driver */
31 VideoBootStrap QZ_bootstrap = { 31 VideoBootStrap QZ_bootstrap = {
32 "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice 32 "Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
33 }; 33 };
34
34 35
35 /* Bootstrap functions */ 36 /* Bootstrap functions */
36 static int QZ_Available () { 37 static int QZ_Available () {
37 return 1; 38 return 1;
38 } 39 }
358 int gamma_error; 359 int gamma_error;
359 NSRect screen_rect; 360 NSRect screen_rect;
360 361
361 gamma_error = QZ_FadeGammaOut (this, &gamma_table); 362 gamma_error = QZ_FadeGammaOut (this, &gamma_table);
362 363
364 /* Release double buffer stuff */
365 if ( mode_flags & (SDL_HWSURFACE|SDL_DOUBLEBUF)) {
366 quit_thread = YES;
367 SDL_SemPost (sem1);
368 SDL_WaitThread (thread, NULL);
369 SDL_DestroySemaphore (sem1);
370 SDL_DestroySemaphore (sem2);
371 free (sw_buffers[0]);
372 }
373
363 /* 374 /*
364 Release the OpenGL context 375 Release the OpenGL context
365 Do this first to avoid trash on the display before fade 376 Do this first to avoid trash on the display before fade
366 */ 377 */
367 if ( mode_flags & SDL_OPENGL ) { 378 if ( mode_flags & SDL_OPENGL ) {
370 CGLSetFullScreen (NULL); 381 CGLSetFullScreen (NULL);
371 } 382 }
372 383
373 /* Restore original screen resolution/bpp */ 384 /* Restore original screen resolution/bpp */
374 CGDisplaySwitchToMode (display_id, save_mode); 385 CGDisplaySwitchToMode (display_id, save_mode);
375 CGDisplayRelease (display_id); 386 CGReleaseAllDisplays ();
376 ShowMenuBar (); 387 ShowMenuBar ();
377 388
378 /* 389 /*
379 Reset the main screen's rectangle 390 Reset the main screen's rectangle
380 See comment in QZ_SetVideoFullscreen for why we do this 391 See comment in QZ_SetVideoFullscreen for why we do this
406 int height, int bpp, Uint32 flags) { 417 int height, int bpp, Uint32 flags) {
407 int exact_match; 418 int exact_match;
408 int gamma_error; 419 int gamma_error;
409 SDL_QuartzGammaTable gamma_table; 420 SDL_QuartzGammaTable gamma_table;
410 NSRect screen_rect; 421 NSRect screen_rect;
422 CGError error;
411 423
412 /* Destroy any previous mode */ 424 /* Destroy any previous mode */
413 if (video_set == SDL_TRUE) 425 if (video_set == SDL_TRUE)
414 QZ_UnsetVideoMode (this); 426 QZ_UnsetVideoMode (this);
415 427
425 437
426 /* Fade display to zero gamma */ 438 /* Fade display to zero gamma */
427 gamma_error = QZ_FadeGammaOut (this, &gamma_table); 439 gamma_error = QZ_FadeGammaOut (this, &gamma_table);
428 440
429 /* Put up the blanking window (a window above all other windows) */ 441 /* Put up the blanking window (a window above all other windows) */
430 if ( CGDisplayNoErr != CGDisplayCapture (display_id) ) { 442 if (getenv ("SDL_SINGLEDISPLAY"))
443 error = CGDisplayCapture (display_id);
444 else
445 error = CGCaptureAllDisplays ();
446
447 if ( CGDisplayNoErr != error ) {
431 SDL_SetError ("Failed capturing display"); 448 SDL_SetError ("Failed capturing display");
432 goto ERR_NO_CAPTURE; 449 goto ERR_NO_CAPTURE;
433 } 450 }
434 451
435 /* Do the physical switch */ 452 /* Do the physical switch */
449 current->flags |= SDL_PREALLOC; 466 current->flags |= SDL_PREALLOC;
450 467
451 this->UpdateRects = QZ_DirectUpdate; 468 this->UpdateRects = QZ_DirectUpdate;
452 this->LockHWSurface = QZ_LockHWSurface; 469 this->LockHWSurface = QZ_LockHWSurface;
453 this->UnlockHWSurface = QZ_UnlockHWSurface; 470 this->UnlockHWSurface = QZ_UnlockHWSurface;
454 471
455 /* Setup some mode-dependant info */ 472 /* Setup double-buffer emulation */
456 if ( CGSDisplayCanHWFill (display_id) ) { 473 if ( flags & SDL_DOUBLEBUF ) {
457 this->info.blit_fill = 1; 474
458 this->FillHWRect = QZ_FillHWRect; 475 /*
476 Setup a software backing store for reasonable results when
477 double buffering is requested (since a single-buffered hardware
478 surface looks hideous).
479
480 The actual screen blit occurs in a separate thread to allow
481 other blitting while waiting on the VBL (and hence results in higher framerates).
482 */
483 this->LockHWSurface = NULL;
484 this->UnlockHWSurface = NULL;
485 this->UpdateRects = NULL;
486
487 current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
488 this->UpdateRects = QZ_DoubleBufferUpdate;
489 this->LockHWSurface = QZ_LockDoubleBuffer;
490 this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
491 this->FlipHWSurface = QZ_FlipDoubleBuffer;
492
493 current->pixels = malloc (current->pitch * current->h * 2);
494 if (current->pixels == NULL) {
495 SDL_OutOfMemory ();
496 goto ERR_DOUBLEBUF;
497 }
498
499 sw_buffers[0] = current->pixels;
500 sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
501
502 quit_thread = NO;
503 sem1 = SDL_CreateSemaphore (0);
504 sem2 = SDL_CreateSemaphore (1);
505 thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
459 } 506 }
460 507
461 if ( CGDisplayCanSetPalette (display_id) ) 508 if ( CGDisplayCanSetPalette (display_id) )
462 current->flags |= SDL_HWPALETTE; 509 current->flags |= SDL_HWPALETTE;
463 510
509 mode_flags = current->flags; 556 mode_flags = current->flags;
510 557
511 return current; 558 return current;
512 559
513 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ 560 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
514 ERR_NO_GL: CGDisplaySwitchToMode (display_id, save_mode); 561 ERR_NO_GL:
515 ERR_NO_SWITCH: CGDisplayRelease (display_id); 562 ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode);
563 ERR_NO_SWITCH: CGReleaseAllDisplays ();
516 ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); } 564 ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); }
517 ERR_NO_MATCH: return NULL; 565 ERR_NO_MATCH: return NULL;
518 } 566 }
519 567
520 static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width, 568 static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
521 int height, int bpp, Uint32 flags) { 569 int height, int bpp, Uint32 flags) {
522 unsigned int style; 570 unsigned int style;
719 767
720 if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) ) 768 if ( CGDisplayNoErr != CGDisplaySetPalette (display_id, palette) )
721 return 0; 769 return 0;
722 770
723 return 1; 771 return 1;
772 }
773
774 static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) {
775
776 return 1;
777 }
778
779 static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) {
780
781 }
782
783 /* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
784 static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) {
785
786 union
787 {
788 UInt64 i;
789 Nanoseconds ns;
790 } temp;
791
792 temp.i = seconds * 1000000000.0;
793
794 return NanosecondsToAbsolute ( temp.ns );
795 }
796
797 static int QZ_ThreadFlip (_THIS) {
798
799 Uint8 *src, *dst;
800 int skip, len, h;
801
802 /*
803 Give this thread the highest scheduling priority possible,
804 in the hopes that it will immediately run after the VBL delay
805 */
806 {
807 pthread_t current_thread;
808 int policy;
809 struct sched_param param;
810
811 current_thread = pthread_self ();
812 pthread_getschedparam (current_thread, &policy, &param);
813 policy = SCHED_RR;
814 param.sched_priority = sched_get_priority_max (policy);
815 pthread_setschedparam (current_thread, policy, &param);
816 }
817
818 while (1) {
819
820 SDL_SemWait (sem1);
821 if (quit_thread)
822 return 0;
823
824 dst = CGDisplayBaseAddress (display_id);
825 src = current_buffer;
826 len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
827 h = SDL_VideoSurface->h;
828 skip = SDL_VideoSurface->pitch;
829
830 /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
831 {
832
833 /* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
834 double refreshRate;
835 double linesPerSecond;
836 double target;
837 double position;
838 double adjustment;
839 AbsoluteTime nextTime;
840 CFNumberRef refreshRateCFNumber;
841
842 refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
843 if ( NULL == refreshRateCFNumber ) {
844 SDL_SetError ("Mode has no refresh rate");
845 goto ERROR;
846 }
847
848 if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
849 SDL_SetError ("Error getting refresh rate");
850 goto ERROR;
851 }
852
853 if ( 0 == refreshRate ) {
854
855 SDL_SetError ("Display has no refresh rate, using 60hz");
856
857 /* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
858 refreshRate = 60.0;
859 }
860
861 linesPerSecond = refreshRate * h;
862 target = h;
863
864 /* Figure out the first delay so we start off about right */
865 position = CGDisplayBeamPosition (display_id);
866 if (position > target)
867 position = 0;
868
869 adjustment = (target - position) / linesPerSecond;
870
871 nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
872
873 MPDelayUntil (&nextTime);
874 }
875
876
877 /* On error, skip VBL delay */
878 ERROR:
879
880 while ( h-- ) {
881
882 memcpy (dst, src, len);
883 src += skip;
884 dst += skip;
885 }
886
887 /* signal flip completion */
888 SDL_SemPost (sem2);
889 }
890
891 return 0;
892 }
893
894 static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) {
895
896 /* wait for previous flip to complete */
897 SDL_SemWait (sem2);
898
899 current_buffer = surface->pixels;
900
901 if (surface->pixels == sw_buffers[0])
902 surface->pixels = sw_buffers[1];
903 else
904 surface->pixels = sw_buffers[0];
905
906 /* signal worker thread to do the flip */
907 SDL_SemPost (sem1);
908
909 return 0;
910 }
911
912
913 static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) {
914
915 /* perform a flip if someone calls updaterects on a doublebuferred surface */
916 this->FlipHWSurface (this, SDL_VideoSurface);
724 } 917 }
725 918
726 static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) { 919 static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) {
727 #pragma unused(this,num_rects,rects) 920 #pragma unused(this,num_rects,rects)
728 } 921 }