// HEADER FILES ------------------------------------------------------------ #include #include "doomtype.h" #include "templates.h" #include "i_system.h" #include "i_video.h" #include "v_video.h" #include "v_pfx.h" #include "stats.h" #include "version.h" #include "c_console.h" #include "sdlglvideo.h" #include "gl/system/gl_system.h" #include "r_defs.h" #include "gl/gl_functions.h" //#include "gl/gl_intern.h" #include "gl/renderer/gl_renderer.h" #include "gl/system/gl_framebuffer.h" #include "gl/shaders/gl_shader.h" #include "gl/utility/gl_templates.h" #include "gl/textures/gl_material.h" #include "gl/system/gl_cvars.h" // MACROS ------------------------------------------------------------------ // TYPES ------------------------------------------------------------------- IMPLEMENT_ABSTRACT_CLASS(SDLGLFB) struct MiniModeInfo { WORD Width, Height; }; // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern IVideo *Video; // extern int vid_renderer; EXTERN_CVAR (Float, Gamma) EXTERN_CVAR (Int, vid_displaybits) EXTERN_CVAR (Int, vid_renderer) // PUBLIC DATA DEFINITIONS ------------------------------------------------- CUSTOM_CVAR(Int, gl_vid_multisample, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL ) { Printf("This won't take effect until "GAMENAME" is restarted.\n"); } RenderContext gl; // PRIVATE DATA DEFINITIONS ------------------------------------------------ // Dummy screen sizes to pass when windowed static MiniModeInfo WinModes[] = { { 320, 200 }, { 320, 240 }, { 400, 225 }, // 16:9 { 400, 300 }, { 480, 270 }, // 16:9 { 480, 360 }, { 512, 288 }, // 16:9 { 512, 384 }, { 640, 360 }, // 16:9 { 640, 400 }, { 640, 480 }, { 720, 480 }, // 16:10 { 720, 540 }, { 800, 450 }, // 16:9 { 800, 500 }, // 16:10 { 800, 600 }, { 848, 480 }, // 16:9 { 960, 600 }, // 16:10 { 960, 720 }, { 1024, 576 }, // 16:9 { 1024, 600 }, // 17:10 { 1024, 640 }, // 16:10 { 1024, 768 }, { 1088, 612 }, // 16:9 { 1152, 648 }, // 16:9 { 1152, 720 }, // 16:10 { 1152, 864 }, { 1280, 720 }, // 16:9 { 1280, 800 }, // 16:10 { 1280, 960 }, { 1344, 756 }, // 16:9 { 1360, 768 }, // 16:9 { 1400, 787 }, // 16:9 { 1400, 875 }, // 16:10 { 1440, 900 }, { 1400, 1050 }, { 1600, 900 }, // 16:9 { 1600, 1000 }, // 16:10 { 1600, 1200 }, { 1680, 1050 }, // 16:10 { 1920, 1080 }, // 16:9 { 1920, 1200 }, // 16:10 { 2054, 1536 }, { 2560, 1440 }, // 16:9 { 2880, 1800 } // 16:10 }; // CODE -------------------------------------------------------------------- SDLGLVideo::SDLGLVideo (int parm) { IteratorBits = 0; IteratorFS = false; if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { fprintf( stderr, "Video initialization failed: %s\n", SDL_GetError( ) ); } GetContext(gl); #ifndef _WIN32 // mouse cursor is visible by default on linux systems, we disable it by default SDL_ShowCursor (0); #endif } SDLGLVideo::~SDLGLVideo () { if (GLRenderer != NULL) GLRenderer->FlushTextures(); } void SDLGLVideo::StartModeIterator (int bits, bool fs) { IteratorMode = 0; IteratorBits = bits; IteratorFS = fs; } bool SDLGLVideo::NextMode (int *width, int *height, bool *letterbox) { if (IteratorBits != 8) return false; if (!IteratorFS) { if ((unsigned)IteratorMode < sizeof(WinModes)/sizeof(WinModes[0])) { *width = WinModes[IteratorMode].Width; *height = WinModes[IteratorMode].Height; ++IteratorMode; return true; } } else { SDL_Rect **modes = SDL_ListModes (NULL, SDL_FULLSCREEN|SDL_HWSURFACE); if (modes != NULL && modes[IteratorMode] != NULL) { *width = modes[IteratorMode]->w; *height = modes[IteratorMode]->h; ++IteratorMode; return true; } } return false; } DFrameBuffer *SDLGLVideo::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old) { static int retry = 0; static int owidth, oheight; PalEntry flashColor; // int flashAmount; if (old != NULL) { // Reuse the old framebuffer if its attributes are the same SDLGLFB *fb = static_cast (old); if (fb->Width == width && fb->Height == height) { bool fsnow = (fb->Screen->flags & SDL_FULLSCREEN) != 0; if (fsnow != fullscreen) { SDL_WM_ToggleFullScreen (fb->Screen); } return old; } // old->GetFlash (flashColor, flashAmount); delete old; } else { flashColor = 0; // flashAmount = 0; } SDLGLFB *fb = new OpenGLFrameBuffer (0, width, height, 32, 60, fullscreen); retry = 0; // If we could not create the framebuffer, try again with slightly // different parameters in this order: // 1. Try with the closest size // 2. Try in the opposite screen mode with the original size // 3. Try in the opposite screen mode with the closest size // This is a somewhat confusing mass of recursion here. while (fb == NULL || !fb->IsValid ()) { if (fb != NULL) { delete fb; } switch (retry) { case 0: owidth = width; oheight = height; case 2: // Try a different resolution. Hopefully that will work. I_ClosestResolution (&width, &height, 8); break; case 1: // Try changing fullscreen mode. Maybe that will work. width = owidth; height = oheight; fullscreen = !fullscreen; break; default: // I give up! I_FatalError ("Could not create new screen (%d x %d)", owidth, oheight); fprintf( stderr, "!!! [SDLGLVideo::CreateFrameBuffer] Got beyond I_FatalError !!!" ); return NULL; //[C] actually this shouldn't be reached; probably should be replaced with an ASSERT } ++retry; fb = static_cast(CreateFrameBuffer (width, height, fullscreen, NULL)); } // fb->SetFlash (flashColor, flashAmount); return fb; } void SDLGLVideo::SetWindowedScale (float scale) { } bool SDLGLVideo::SetResolution (int width, int height, int bits) { // FIXME: Is it possible to do this without completely destroying the old // interface? #ifndef NO_GL if (GLRenderer != NULL) GLRenderer->FlushTextures(); I_ShutdownGraphics(); Video = new SDLGLVideo(0); if (Video == NULL) I_FatalError ("Failed to initialize display"); #if (defined(WINDOWS)) || defined(WIN32) bits=32; #else bits=24; #endif V_DoModeSetup(width, height, bits); #endif return true; // We must return true because the old video context no longer exists. } //========================================================================== // // // //========================================================================== bool SDLGLVideo::SetupPixelFormat(bool allowsoftware, bool nostencil, int multisample) { int stencil; if (!nostencil) { stencil=1; SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 24 ); SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 8 ); // SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); if (multisample > 0) { SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 ); SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, multisample ); } } else { // Use the cheapest mode available and let's hope the driver can handle this... stencil=0; SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 4 ); SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 ); SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 4 ); SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 4 ); SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 ); //SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 )*/ } if (stencil==0) { gl.flags|=RFL_NOSTENCIL; } return true; } //========================================================================== // // // //========================================================================== bool SDLGLVideo::InitHardware (bool allowsoftware, bool nostencil, int multisample) { if (!SetupPixelFormat(allowsoftware, nostencil, multisample)) { Printf ("R_OPENGL: Reverting to software mode...\n"); return false; } return true; } // FrameBuffer implementation ----------------------------------------------- SDLGLFB::SDLGLFB (void *, int width, int height, int, int, bool fullscreen) : DFrameBuffer (width, height) { static int localmultisample=-1; if (localmultisample<0) localmultisample=gl_vid_multisample; int i; m_Lock=0; UpdatePending = false; if (!static_cast(Video)->InitHardware(false, gl_vid_compatibility, localmultisample)) { vid_renderer = 0; return; } // Mac OS X version will crash when entering fullscreen mode with BPP <= 8 // Also it may crash with BPP == 16 on some configurations // It seems 24 and 32 bits are safe values // So value of vid_displaybits is ignored and hardcoded constant is used instead Screen = SDL_SetVideoMode (width, height, #if defined(__APPLE__) 32, #else // ! __APPLE__ vid_displaybits, #endif // __APPLE__ SDL_HWSURFACE|SDL_HWPALETTE|SDL_OPENGL | SDL_GL_DOUBLEBUFFER|SDL_ANYFORMAT| (fullscreen ? SDL_FULLSCREEN : 0)); if (Screen == NULL) return; m_supportsGamma = -1 != SDL_GetGammaRamp(m_origGamma[0], m_origGamma[1], m_origGamma[2]); #if defined(__APPLE__) // Need to set title here because a window is not created yet when calling the same function from main() char caption[100]; mysnprintf(caption, countof(caption), GAMESIG " %s (%s)", GetVersionString(), GetGitTime()); SDL_WM_SetCaption(caption, NULL); #endif // __APPLE__ } SDLGLFB::~SDLGLFB () { if (m_supportsGamma) { SDL_SetGammaRamp(m_origGamma[0], m_origGamma[1], m_origGamma[2]); } } void SDLGLFB::InitializeState() { int value = 0; SDL_GL_GetAttribute( SDL_GL_STENCIL_SIZE, &value ); if (!value) { Printf("Failed to use stencil buffer!\n"); //[C] is it needed to recreate buffer in "cheapest mode"? gl.flags|=RFL_NOSTENCIL; } } bool SDLGLFB::CanUpdate () { if (m_Lock != 1) { if (m_Lock > 0) { UpdatePending = true; --m_Lock; } return false; } return true; } void SDLGLFB::SetGammaTable(WORD *tbl) { SDL_SetGammaRamp(&tbl[0], &tbl[256], &tbl[512]); } bool SDLGLFB::Lock(bool buffered) { m_Lock++; Buffer = MemBuffer; return true; } bool SDLGLFB::Lock () { return Lock(false); } void SDLGLFB::Unlock () { if (UpdatePending && m_Lock == 1) { Update (); } else if (--m_Lock <= 0) { m_Lock = 0; } } bool SDLGLFB::IsLocked () { return m_Lock>0;// true; } bool SDLGLFB::IsFullscreen () { return (Screen->flags & SDL_FULLSCREEN) != 0; } bool SDLGLFB::IsValid () { return DFrameBuffer::IsValid() && Screen != NULL; } void SDLGLFB::SetVSync( bool vsync ) { #if defined (__APPLE__) const GLint value = vsync ? 1 : 0; CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &value ); #endif } void SDLGLFB::NewRefreshRate () { } void SDLGLFB::SwapBuffers() { SDL_GL_SwapBuffers (); }