Mercurial > sdl-ios-xcode
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, ¶m); | |
813 policy = SCHED_RR; | |
814 param.sched_priority = sched_get_priority_max (policy); | |
815 pthread_setschedparam (current_thread, policy, ¶m); | |
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 } |