LCOV - code coverage report
Current view: top level - engine/render - renderwindow.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 9.2 % 390 36
Test Date: 2026-07-03 02:16:56 Functions: 20.7 % 29 6

            Line data    Source code
       1              : /**
       2              :  * @file renderwindow.cpp
       3              :  * @brief screen rendering functionality
       4              :  *
       5              :  * screen rendering functions, such as background, progress bar
       6              :  * also handles stuff such as main menu rendering and other non-intensive rendering
       7              :  * as well as global rendering settings such as gamma
       8              :  */
       9              : #include "SDL_ttf.h"
      10              : 
      11              : #include "../libprimis-headers/cube.h"
      12              : #include "../../shared/geomexts.h"
      13              : #include "../../shared/glemu.h"
      14              : #include "../../shared/glexts.h"
      15              : 
      16              : #include "hud.h"
      17              : #include "octarender.h"
      18              : #include "rendergl.h"
      19              : #include "renderlights.h"
      20              : #include "rendermodel.h"
      21              : #include "renderparticles.h"
      22              : #include "rendertext.h"
      23              : #include "renderttf.h"
      24              : #include "renderva.h"
      25              : #include "renderwindow.h"
      26              : #include "shader.h"
      27              : #include "shaderparam.h"
      28              : #include "stain.h"
      29              : #include "texture.h"
      30              : 
      31              : #include "interface/console.h"
      32              : #include "interface/control.h"
      33              : #include "interface/input.h"
      34              : #include "interface/menus.h"
      35              : 
      36              : #include "world/octaedit.h"
      37              : 
      38            0 : VARFN(screenw, scr_w, SCR_MINW, -1, SCR_MAXW, initwarning("screen resolution"));
      39            0 : VARFN(screenh, scr_h, SCR_MINH, -1, SCR_MAXH, initwarning("screen resolution"));
      40              : 
      41              : static VAR(menufps, 0, 60, 1000);   //maximum framerate while in main menu
      42              : static VARP(maxfps, 0, 240, 1000);  //maximum framerate while world is being rendered
      43              : 
      44              : VAR(desktopw, 1, 0, 0); //used in iengine.h
      45              : VAR(desktoph, 1, 0, 0); //used in iengine.h
      46              : 
      47              : int screenw = 0,
      48              :     screenh = 0;
      49              : 
      50              : SDL_Window   *screen    = nullptr;
      51              : static SDL_GLContext glcontext = nullptr;
      52              : 
      53              : bool inbetweenframes = false, //used in iengine.h
      54              :      renderedframe = true; //used in iengine.h, set to true by main() call in game and false by setbackgroundinfo(), so true during most circumstances
      55              : 
      56              : /**
      57              :  * @brief helper function for main menu rendering routines
      58              :  *
      59              :  * @return w and h if both are above 1024x768
      60              :  * @return w and h multiplied by the factor by which the smallest dimension is smaller than 1024x768
      61              :  */
      62            0 : static void getbackgroundres(int &w, int &h)
      63              : {
      64            0 :     float wk = 1,
      65            0 :           hk = 1;
      66            0 :     if(w < 1024)
      67              :     {
      68            0 :         wk = 1024.0f/w; //calculate w subsize factor (if greater than 1)
      69              :     }
      70            0 :     if(h < 768)
      71              :     {
      72            0 :         hk = 768.0f/h; //calculate h subsize factor (if greater than 1)
      73              :     }
      74            0 :     wk = hk = std::max(wk, hk); //pick the largest factor and multiply both by this
      75            0 :     w = static_cast<int>(std::ceil(w*wk));
      76            0 :     h = static_cast<int>(std::ceil(h*hk));
      77            0 : }
      78              : 
      79              : static std::string backgroundcaption   = "",
      80              :                    backgroundmapname   = "",
      81              :                    backgroundmapinfo   = "";
      82              : static const Texture *backgroundmapshot = nullptr;
      83              : 
      84            0 : static void bgquad(float x, float y, float w, float h, float tx = 0, float ty = 0, float tw = 1, float th = 1)
      85              : {
      86            0 :     gle::begin(GL_TRIANGLE_STRIP);
      87            0 :     gle::attribf(x,   y);   gle::attribf(tx,      ty);
      88            0 :     gle::attribf(x+w, y);   gle::attribf(tx + tw, ty);
      89            0 :     gle::attribf(x,   y+h); gle::attribf(tx,      ty + th);
      90            0 :     gle::attribf(x+w, y+h); gle::attribf(tx + tw, ty + th);
      91            0 :     gle::end();
      92            0 : }
      93              : 
      94              : // void renderbackgroundview(int, int, const char*, Texture*, const char*, const char*)
      95              : //   Background picture / text handler
      96              : 
      97              : /*
      98              : Notes:
      99              :     * Unsure what 'w' and 'h' refers to, maybe screen resolution?
     100              : */
     101              : 
     102            0 : static void renderbackgroundview(int win_w, int win_h, const char *caption, const Texture *mapshot, const char *mapname, const char *mapinfo)
     103              : {
     104              :     static int lastupdate  = -1,
     105              :                lastw       = -1,
     106              :                lasth       = -1;
     107              :     static float backgroundu = 0,
     108              :                  backgroundv = 0;
     109            0 :     const bool needsRefresh =
     110            0 :         (renderedframe && !mainmenu && lastupdate != lastmillis)
     111            0 :         || lastw != win_w
     112            0 :         || lasth != win_h;
     113            0 :     if(needsRefresh)
     114              :     {
     115            0 :         lastupdate = lastmillis;
     116            0 :         lastw = win_w;
     117            0 :         lasth = win_h;
     118              : 
     119            0 :         backgroundu = randomfloat(1);
     120            0 :         backgroundv = randomfloat(1);
     121              :     }
     122            0 :     else if(lastupdate != lastmillis)
     123              :     {
     124            0 :         lastupdate = lastmillis;
     125              :     }
     126            0 :     hudmatrix.ortho(0, win_w, win_h, 0, -1, 1);
     127            0 :     resethudmatrix();
     128            0 :     resethudshader();
     129              : 
     130            0 :     gle::defvertex(2);
     131            0 :     gle::deftexcoord0();
     132            0 :     settexture("media/interface/background.png", 0); //main menu background
     133            0 :     const float bu = win_w*0.67f/256.0f,
     134            0 :                 bv = win_h*0.67f/256.0f;
     135            0 :     bgquad(0, 0, win_w, win_h, backgroundu, backgroundv, bu, bv);
     136              : 
     137            0 :     glEnable(GL_BLEND);
     138            0 :     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     139            0 :     settexture("media/interface/shadow.png", 3); //peripheral shadow effect
     140            0 :     bgquad(0, 0, win_w, win_h);
     141            0 :     glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
     142              :     // Set position and size of logo
     143            0 :     const float logo_h = (1.f/3.f)*std::min(win_w, win_h),
     144            0 :                 logo_w = logo_h*(2.f/1.f), // Aspect ratio of logo, defined here
     145            0 :                 logo_x = 0.5f*(win_w - logo_w),
     146            0 :                 logo_y = 0.5f*(win_h*0.5f - logo_h);
     147              : 
     148            0 :     settexture( (maxtexsize >= 1024 || maxtexsize == 0) && (hudw() > 1280 || hudh() > 800)
     149              :               ? "<premul>media/interface/logo_1024.png" //1024x wide logo
     150              :               : "<premul>media/interface/logo.png", //512x wide logo for small screens
     151              :         3);
     152            0 :     bgquad(logo_x, logo_y, logo_w, logo_h);
     153              : 
     154            0 :     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     155              : 
     156            0 :     if(caption)
     157              :     {
     158            0 :         int tw = text_width(caption);
     159            0 :         const float tsz = 0.04f*std::min(win_w, win_h)/FONTH,
     160            0 :                     tx  = 0.5f*(win_w - tw*tsz),
     161            0 :                     ty  = win_h - 0.075f*1.5f*std::min(win_w, win_h) - FONTH*tsz;
     162            0 :         pushhudtranslate(tx, ty, tsz);
     163              :         //draw_text(caption, 0, 0);
     164            0 :         ttr.renderttf(caption, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
     165            0 :         pophudmatrix();
     166              :     }
     167            0 :     if(mapshot || mapname)
     168              :     {
     169            0 :         float infowidth = 14*FONTH,
     170            0 :               sz  = 0.35f*std::min(win_w, win_h),
     171            0 :               msz = (0.85f*std::min(win_w, win_h) - sz)/(infowidth + FONTH),
     172            0 :               x   = 0.5f*win_w,
     173            0 :               y   = logo_y+logo_h - sz/15,
     174            0 :               mx  = 0,
     175            0 :               my  = 0,
     176            0 :               mw  = 0,
     177            0 :               mh  = 0;
     178              :         // Prepare text area for map info
     179            0 :         if(mapinfo)
     180              :         {
     181            0 :             text_boundsf(mapinfo, mw, mh, infowidth);
     182            0 :             x -= 0.5f * mw * msz;
     183            0 :             if (mapshot && mapshot!=notexture)
     184              :             {
     185            0 :                 x -= 0.5f*FONTH * msz;
     186            0 :                 mx = sz + FONTH * msz;
     187              :             }
     188              :         }
     189              :         // Map shot was provided and isn't empty
     190            0 :         if(mapshot && mapshot!=notexture)
     191              :         {
     192            0 :             x -= 0.5f * sz;
     193            0 :             resethudshader();
     194            0 :             glBindTexture(GL_TEXTURE_2D, mapshot->id);
     195            0 :             bgquad(x, y, sz, sz);
     196              :         }
     197              :         // Map name was provided
     198            0 :         if(mapname)
     199              :         {
     200            0 :             float tw  = text_widthf(mapname),
     201            0 :                   tsz = sz/(8*FONTH),
     202            0 :                   tx  = std::max(0.5f * (mw*msz - tw * tsz), 0.0f);
     203            0 :             pushhudtranslate(x + mx + tx, y, tsz);
     204              :             //draw_text(mapname, 0, 0);
     205            0 :             ttr.fontsize(42);
     206            0 :             ttr.renderttf(mapname, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
     207            0 :             pophudmatrix();
     208            0 :             my = 1.5f*FONTH*tsz;
     209              :         }
     210              :         // Map info was provided
     211            0 :         if(mapinfo)
     212              :         {
     213            0 :             pushhudtranslate(x + mx, y + my, msz);
     214              :             //draw_text(mapinfo, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, -1, infowidth);
     215            0 :             ttr.fontsize(42);
     216            0 :             ttr.renderttf(mapinfo, {0xFF, 0xFF, 0xFF, 0}, 0, 0);
     217            0 :             pophudmatrix();
     218              :         }
     219              :     }
     220            0 :     glDisable(GL_BLEND);
     221            0 : }
     222              : 
     223            0 : void swapbuffers(bool)
     224              : {
     225            0 :     gle::disable();
     226            0 :     SDL_GL_SwapWindow(screen);
     227            0 : }
     228              : 
     229            0 : static void setbackgroundinfo(const char *caption = nullptr, const Texture *mapshot = nullptr, const char *mapname = nullptr, const char *mapinfo = nullptr)
     230              : {
     231            0 :     renderedframe = false;
     232            0 :     backgroundcaption = std::string(caption ? caption : "");
     233            0 :     backgroundmapshot = mapshot;
     234            0 :     backgroundmapname = std::string(mapname ? mapname : "");
     235            0 :     std::string minfo = std::string(mapinfo ? mapinfo : "");
     236            0 :     if(minfo != backgroundmapinfo)
     237              :     {
     238            0 :         backgroundmapinfo = "";
     239            0 :         if(!minfo.empty())
     240              :         {
     241            0 :             backgroundmapinfo = std::string(mapinfo);
     242              :         }
     243              :         else
     244              :         {
     245            0 :             backgroundmapinfo = "";
     246              :         }
     247              :     }
     248            0 : }
     249              : 
     250            0 : void renderbackground(const char *caption, const Texture *mapshot, const char *mapname, const char *mapinfo, bool force)
     251              : {
     252            0 :     if(!inbetweenframes && !force)
     253              :     {
     254            0 :         return;
     255              :     }
     256            0 :     int w = hudw(),
     257            0 :         h = hudh();
     258            0 :     if(forceaspect)
     259              :     {
     260            0 :         w = std::ceil(h*forceaspect);
     261              :     }
     262            0 :     getbackgroundres(w, h);
     263            0 :     gettextres(w, h);
     264            0 :     if(force)
     265              :     {
     266            0 :         renderbackgroundview(w, h, caption, mapshot, mapname, mapinfo);
     267            0 :         return;
     268              :     }
     269              :     //renders renderbackgroundview three times, with an identical call each time
     270            0 :     for(int i = 0; i < 3; ++i)
     271              :     {
     272            0 :         renderbackgroundview(w, h, caption, mapshot, mapname, mapinfo);
     273            0 :         swapbuffers(false);
     274              :     }
     275            0 :     setbackgroundinfo(caption, mapshot, mapname, mapinfo);
     276              : }
     277              : 
     278            0 : static void restorebackground(int w, int h, bool force = false)
     279              : {
     280            0 :     if(renderedframe)
     281              :     {
     282            0 :         if(!force)
     283              :         {
     284            0 :             return;
     285              :         }
     286            0 :         setbackgroundinfo();
     287              :     }
     288            0 :     renderbackgroundview(w, h, backgroundcaption.c_str(), backgroundmapshot, backgroundmapname.c_str(), backgroundmapinfo.c_str());
     289              : }
     290              : 
     291              : float loadprogress = 0;
     292              : 
     293            0 : static void renderprogressview(int w, int h, float bar, const char *text)   // also used during loading
     294              : {
     295            0 :     hudmatrix.ortho(0, w, h, 0, -1, 1);
     296            0 :     resethudmatrix();
     297            0 :     resethudshader();
     298              : 
     299            0 :     gle::defvertex(2);
     300            0 :     gle::deftexcoord0();
     301              : 
     302            0 :     const float fh = 0.060f*std::min(w, h),
     303            0 :                 fw = fh * 15,
     304            0 :                 fx = renderedframe ? w - fw - fh/4 : 0.5f * (w - fw),
     305            0 :                 fy = renderedframe ? fh/4 : h - fh * 1.5f;
     306            0 :     settexture("media/interface/loading_frame.png", 3);
     307            0 :     bgquad(fx, fy, fw, fh);
     308              : 
     309            0 :     glEnable(GL_BLEND);
     310            0 :     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     311              : 
     312            0 :     const float bw  = fw * (512 - 2*8)/512.0f,
     313            0 :                 bh  = fh * 20/32.0f,
     314            0 :                 bx  = fx + fw * 8/512.0f,
     315            0 :                 by  = fy + fh * 6/32.0f,
     316            0 :                 su1 = 0/32.0f,
     317            0 :                 su2 = 8/32.0f,
     318            0 :                 sw  = fw * 8/512.0f,
     319            0 :                 eu1 = 24/32.0f,
     320            0 :                 eu2 = 32/32.0f,
     321            0 :                 ew  = fw * 8/512.0f,
     322            0 :                 mw  = bw - sw - ew,
     323            0 :                 ex  = bx+sw + std::max(mw*bar, fw * 8/512.0f);
     324            0 :     if(bar > 0)
     325              :     {
     326            0 :         settexture("media/interface/loading_bar.png", 3);
     327            0 :         bgquad(bx, by, sw, bh, su1, 0, su2-su1, 1);
     328            0 :         bgquad(bx+sw, by, ex-(bx+sw), bh, su2, 0, eu1-su2, 1);
     329            0 :         bgquad(ex, by, ew, bh, eu1, 0, eu2-eu1, 1);
     330              :     }
     331            0 :     if(text)
     332              :     {
     333            0 :         const int tw = text_width(text);
     334            0 :         float tsz = bh * 0.6f/FONTH;
     335            0 :         if(tw * tsz > mw)
     336              :         {
     337            0 :             tsz = mw/tw;
     338              :         }
     339            0 :         pushhudtranslate(bx+sw, by + (bh - FONTH*tsz)/2, tsz);
     340              :         //draw_text(text, 0, 0);
     341            0 :         ttr.fontsize(50);
     342            0 :         ttr.renderttf(text, {0xFF, 0xFF, 0xFF, 0}, 0, 4);
     343            0 :         pophudmatrix();
     344              :     }
     345            0 :     glDisable(GL_BLEND);
     346            0 : }
     347              : 
     348              : static VAR(progressbackground, 0, 0, 1); //force rendering progress bar background texture
     349              : static int curvsync = -1;
     350              : 
     351            6 : void renderprogress(float bar, const char *text, bool background)   // also used during loading
     352              : {
     353            6 :     if(!inbetweenframes || drawtex)
     354              :     {
     355            6 :         return;
     356              :     }
     357            0 :     int fps = menufps ? (maxfps ? std::min(maxfps, menufps) : menufps) : maxfps;
     358            0 :     if(fps)
     359              :     {
     360              :         static int lastprogress = 0;
     361            0 :         int ticks = SDL_GetTicks(),
     362            0 :             diff = ticks - lastprogress;
     363            0 :         if(bar > 0 && diff >= 0 && diff < (1000 + fps-1)/fps)
     364              :         {
     365            0 :             return;
     366              :         }
     367            0 :         lastprogress = ticks;
     368              :     }
     369            0 :     int w = hudw(),
     370            0 :         h = hudh();
     371            0 :     if(forceaspect)
     372              :     {
     373            0 :         w = static_cast<int>(std::ceil(h*forceaspect));
     374              :     }
     375            0 :     getbackgroundres(w, h);
     376            0 :     gettextres(w, h);
     377              : 
     378            0 :     bool forcebackground = progressbackground || (mesa_swap_bug && (curvsync || totalmillis==1));
     379            0 :     if(background || forcebackground)
     380              :     {
     381            0 :         restorebackground(w, h, forcebackground);
     382              :     }
     383            0 :     renderprogressview(w, h, bar, text);
     384            0 :     swapbuffers(false);
     385              : }
     386              : 
     387              : static bool initwindowpos = false;
     388              : 
     389            0 : static void setfullscreen(bool enable)
     390              : {
     391            0 :     if(!screen)
     392              :     {
     393            0 :         return;
     394              :     }
     395              :     //initwarning(enable ? "fullscreen" : "windowed");
     396            0 :     SDL_SetWindowFullscreen(screen, enable ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
     397            0 :     if(!enable)
     398              :     {
     399            0 :         SDL_SetWindowSize(screen, scr_w, scr_h);
     400            0 :         if(initwindowpos)
     401              :         {
     402            0 :             int winx = SDL_WINDOWPOS_CENTERED,
     403            0 :                 winy = SDL_WINDOWPOS_CENTERED;
     404            0 :             SDL_SetWindowPosition(screen, winx, winy);
     405            0 :             initwindowpos = false;
     406              :         }
     407              :     }
     408              : }
     409              : 
     410              : //used in iengine only
     411            0 : VARF(fullscreen, 0, 1, 1, setfullscreen(fullscreen!=0));
     412              : 
     413              : /**
     414              :  * @brief Sets the screen resolution
     415              :  *
     416              :  * Sets the window size to w * h pixels, or reduces fullscreen resolution to
     417              :  * w * h pixels.
     418              :  *
     419              :  * @param w width of new screen res
     420              :  * @param h height of new screen res
     421              :  */
     422            1 : void screenres(int w, int h)
     423              : {
     424              :     //need to cast enum to int for std's clamp implementation
     425            1 :     scr_w = std::clamp(w, static_cast<int>(SCR_MINW), static_cast<int>(SCR_MAXW));
     426            1 :     scr_h = std::clamp(h, static_cast<int>(SCR_MINH), static_cast<int>(SCR_MAXH));
     427            1 :     if(screen)
     428              :     {
     429            0 :         scr_w = std::min(scr_w, desktopw);
     430            0 :         scr_h = std::min(scr_h, desktoph);
     431            0 :         if(SDL_GetWindowFlags(screen) & SDL_WINDOW_FULLSCREEN)
     432              :         {
     433            0 :             gl_resize();
     434              :         }
     435              :         else
     436              :         {
     437            0 :             SDL_SetWindowSize(screen, scr_w, scr_h);
     438              :         }
     439              :     }
     440              :     else
     441              :     {
     442            1 :         initwarning("screen resolution");
     443              :     }
     444            1 : }
     445              : 
     446              : 
     447            0 : static void setgamma(int val)
     448              : {
     449            0 :     if(screen && SDL_SetWindowBrightness(screen, val/100.0f) < 0)
     450              :     {
     451            0 :         conoutf(Console_Error, "Could not set gamma: %s", SDL_GetError());
     452              :     }
     453            0 : }
     454              : 
     455              : static int curgamma = 100;
     456            0 : static VARFNP(gamma, reqgamma, 30, 100, 300,
     457              : {
     458              :     if(initing || reqgamma == curgamma)
     459              :     {
     460              :         return;
     461              :     }
     462              :     curgamma = reqgamma;
     463              :     setgamma(curgamma);
     464              : });
     465              : 
     466              : /* restoregamma: sets gamma to the previous set value, useful for reverting bad-
     467              :  * looking gamma trial settings
     468              :  *
     469              :  * used in iengine.h
     470              :  */
     471            0 : void restoregamma()
     472              : {
     473            0 :     if(initing || reqgamma == 100)
     474              :     {
     475            0 :         return;
     476              :     }
     477            0 :     curgamma = reqgamma;
     478            0 :     setgamma(curgamma);
     479              : }
     480              : 
     481            0 : void cleargamma()
     482              : {
     483            0 :     if(curgamma != 100 && screen)
     484              :     {
     485            0 :         SDL_SetWindowBrightness(screen, 1.0f);
     486              :     }
     487            0 : }
     488              : 
     489              : void restorevsync(); //prototype to fix chicken-egg initialization problem caused by VARFP
     490              : 
     491            0 : static VARFP(vsync, 0, 0, 1, restorevsync());                      //vertical sync of framebuffer to refresh rate
     492            0 : static VARFP(vsynctear, 0, 0, 1, { if(vsync) restorevsync(); });   //toggles sdl2's adaptive sync function
     493              : 
     494            0 : void restorevsync()
     495              : {
     496            0 :     if(initing || !glcontext)
     497              :     {
     498            0 :         return;
     499              :     }
     500            0 :     if(!SDL_GL_SetSwapInterval(vsync ? (vsynctear ? -1 : 1) : 0))
     501              :     {
     502            0 :         curvsync = vsync;
     503              :     }
     504              : }
     505              : 
     506              : //used in iengine.h
     507            0 : void setupscreen()
     508              : {
     509              :     //clear prior gl context/screen if present
     510            0 :     if(glcontext)
     511              :     {
     512            0 :         SDL_GL_DeleteContext(glcontext);
     513            0 :         glcontext = nullptr;
     514              :     }
     515            0 :     if(screen)
     516              :     {
     517            0 :         SDL_DestroyWindow(screen);
     518            0 :         screen = nullptr;
     519              :     }
     520            0 :     curvsync = -1;
     521              : 
     522              :     SDL_Rect desktop;
     523            0 :     if(SDL_GetDisplayBounds(0, &desktop) < 0)
     524              :     {
     525            0 :         fatal("failed querying desktop bounds: %s", SDL_GetError());
     526              :     }
     527            0 :     desktopw = desktop.w;
     528            0 :     desktoph = desktop.h;
     529              : 
     530            0 :     if(scr_h < 0)
     531              :     {
     532            0 :         scr_h = SCR_DEFAULTH;
     533              :     }
     534            0 :     if(scr_w < 0)
     535              :     {
     536            0 :         scr_w = (scr_h*desktopw)/desktoph;
     537              :     }
     538            0 :     scr_w = std::min(scr_w, desktopw);
     539            0 :     scr_h = std::min(scr_h, desktoph);
     540              : 
     541            0 :     int winx  = SDL_WINDOWPOS_UNDEFINED,
     542            0 :         winy  = SDL_WINDOWPOS_UNDEFINED,
     543            0 :         winw  = scr_w,
     544            0 :         winh  = scr_h,
     545            0 :         flags = SDL_WINDOW_RESIZABLE;
     546            0 :     if(fullscreen)
     547              :     {
     548            0 :         winw   = desktopw;
     549            0 :         winh   = desktoph;
     550            0 :         flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
     551            0 :         initwindowpos = true;
     552              :     }
     553              : 
     554            0 :     SDL_GL_ResetAttributes();
     555            0 :     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
     556            0 :     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
     557            0 :     SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0);
     558              : 
     559            0 :     uint32_t windowflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS | flags;
     560              :     //create new screen       title          x     y     w     h  flags
     561            0 :     screen = SDL_CreateWindow("Imprimis", winx, winy, winw, winh, windowflags);
     562            0 :     ttr.initttf();
     563            0 :     ttr.openfont("media/interface/font/default.ttf", 24);
     564              : 
     565            0 :     if(!screen)
     566              :     {
     567            0 :         fatal("failed to create OpenGL window: %s", SDL_GetError());
     568              :     }
     569            0 :     SDL_Surface *icon = loadsurface("media/interface/icon.png"); //path to taskbar icon
     570            0 :     if(icon)
     571              :     {
     572            0 :         SDL_SetWindowIcon(screen, icon);
     573            0 :         SDL_FreeSurface(icon); //don't need it any more
     574              :     }
     575              : 
     576            0 :     SDL_SetWindowMinimumSize(screen, SCR_MINW, SCR_MINH);
     577            0 :     SDL_SetWindowMaximumSize(screen, SCR_MAXW, SCR_MAXH);
     578              :     //set opengl version to 4.0
     579            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
     580            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
     581              :     //set core profile
     582            0 :     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
     583            0 :     glcontext = SDL_GL_CreateContext(screen);
     584              : 
     585              :     //
     586            0 :     GLenum err = glewInit();
     587            0 :     if (GLEW_OK != err)
     588              :     {
     589              :       /* Problem: glewInit failed, something is seriously wrong. */
     590            0 :       logoutf("Error: %s", glewGetErrorString(err));
     591              :     }
     592            0 :     logoutf("init: GLEW %s", glewGetString(GLEW_VERSION));
     593              :     //check if OpenGL context is sane
     594            0 :     if(!glcontext)
     595              :     {
     596            0 :         fatal("failed to create OpenGL context: %s", SDL_GetError());
     597              :     }
     598            0 :     SDL_GetWindowSize(screen, &screenw, &screenh);
     599            0 : }
     600              : 
     601              : //full reset of renderer
     602            2 : void resetgl()
     603              : {
     604            2 :     if(!glslversion)
     605              :     {
     606            2 :         conoutf(Console_Error, "Cannot reset GL without GL initialized, operation not performed");
     607            2 :         return;
     608              :     }
     609            0 :     clearchanges(Change_Graphics|Change_Shaders);
     610              : 
     611            0 :     renderbackground("resetting OpenGL");
     612              : 
     613            0 :     rootworld.cleanupva();
     614            0 :     cleanupparticles();
     615            0 :     cleanupstains();
     616            0 :     cleanupmodels();
     617            0 :     cleanupprefabs();
     618            0 :     cleanuptextures();
     619            0 :     cleanuplights();
     620            0 :     cleanupshaders();
     621            0 :     cleanupgl();
     622              : 
     623            0 :     setupscreen();
     624              : 
     625            0 :     inputgrab(grabinput);
     626              : 
     627            0 :     gl_init();
     628              : 
     629            0 :     inbetweenframes = false;
     630              :     //texture reloading
     631            0 :     if(!notexture->reload() ||
     632            0 :        !reloadtexture("<premul>media/interface/logo.png") ||
     633            0 :        !reloadtexture("<premul>media/interface/logo_1024.png") ||
     634            0 :        !reloadtexture("media/interface/background.png") ||
     635            0 :        !reloadtexture("media/interface/shadow.png") ||
     636            0 :        !reloadtexture("media/interface/mapshot_frame.png") ||
     637            0 :        !reloadtexture("media/interface/loading_frame.png") ||
     638            0 :        !reloadtexture("media/interface/loading_bar.png"))
     639              :     {
     640            0 :         fatal("failed to reload core texture");
     641              :     }
     642            0 :     reloadfonts();
     643            0 :     inbetweenframes = true;
     644            0 :     renderbackground("initializing...");
     645            0 :     restoregamma();
     646            0 :     restorevsync();
     647            0 :     initgbuffer();
     648            0 :     reloadshaders();
     649            0 :     reloadtextures();
     650            0 :     rootworld.allchanged(true);
     651              : }
     652              : 
     653              : /**
     654              :  * @brief Delays the frame to the prescribed rate.
     655              :  *
     656              :  * Uses SDL_Delay to delay a frame, given the time the last frame was
     657              :  * rendered and the current time.
     658              :  *
     659              :  * @param millis the time (in ms) since program started
     660              :  * @param curmillis the last registered frame time
     661              :  */
     662            0 : void limitfps(int &millis, int curmillis)
     663              : {
     664            0 :     int limit = (mainmenu || minimized) && menufps ? (maxfps ? std::min(maxfps, menufps) : menufps) : maxfps;
     665            0 :     if(!limit)
     666              :     {
     667            0 :         return;
     668              :     }
     669              :     static int fpserror = 0;
     670            0 :     int delay = 1000/limit - (millis-curmillis);
     671            0 :     if(delay < 0)
     672              :     {
     673            0 :         fpserror = 0;
     674              :     }
     675              :     else
     676              :     {
     677            0 :         fpserror += 1000%limit;
     678            0 :         if(fpserror >= limit)
     679              :         {
     680            0 :             ++delay;
     681            0 :             fpserror -= limit;
     682              :         }
     683            0 :         if(delay > 0)
     684              :         {
     685            0 :             SDL_Delay(delay);
     686            0 :             millis += delay;
     687              :         }
     688              :     }
     689              : }
     690              : 
     691              : #ifdef WIN32
     692              :     // Force Optimus setups to use the NVIDIA GPU
     693              :     // or also for AMD dual graphics
     694              :     extern "C"
     695              :     {
     696              :         #ifdef __GNUC__
     697              :             __attribute__((dllexport))
     698              :         #else
     699              :             __declspec(dllexport)
     700              :         #endif
     701              :             DWORD NvOptimusEnablement = 1;
     702              : 
     703              :         #ifdef __GNUC__
     704              :             __attribute__((dllexport))
     705              :         #else
     706              :             __declspec(dllexport)
     707              :         #endif
     708              :         DWORD AmdPowerXpressRequestHighPerformance = 1;
     709              :     }
     710              : #endif
     711              : 
     712              : static constexpr int maxfpshistory = 60;
     713              : 
     714              : static int fpspos = 0;
     715              : static std::array<int, maxfpshistory> fpshistory;
     716              : 
     717              : //used only in iengine
     718            0 : void resetfpshistory()
     719              : {
     720            0 :     fpshistory.fill(1);
     721            0 :     fpspos = 0;
     722            0 : }
     723              : 
     724              : //used only in iengine
     725            0 : void updatefpshistory(int millis)
     726              : {
     727            0 :     fpshistory[fpspos++] = std::max(1, std::min(1000, millis));
     728            0 :     if(fpspos>=maxfpshistory)
     729              :     {
     730            0 :         fpspos = 0;
     731              :     }
     732            0 : }
     733              : 
     734            1 : void getfps(int &fps, int &bestdiff, int &worstdiff)
     735              : {
     736            1 :     int total = fpshistory.at(maxfpshistory-1),
     737            1 :         best = total,
     738            1 :         worst = total;
     739           61 :     for(const int &millis : fpshistory)
     740              :     {
     741           60 :         total += millis;
     742           60 :         if(millis < best)
     743              :         {
     744            0 :             best = millis;
     745              :         }
     746           60 :         if(millis > worst)
     747              :         {
     748            0 :             worst = millis;
     749              :         }
     750              :     }
     751            1 :     if(total) //guard against div by 0
     752              :     {
     753            0 :         fps = (1000*maxfpshistory)/total;
     754            0 :         bestdiff = 1000/best-fps;
     755            0 :         worstdiff = fps-1000/worst;
     756              :     }
     757              :     else
     758              :     {
     759            1 :         fps = 0;
     760            1 :         bestdiff = 0;
     761            1 :         worstdiff = 0;
     762              :     }
     763            1 : }
     764              : 
     765            1 : static void getfpscmd(const int *raw)
     766              : {
     767            1 :     if(*raw)
     768              :     {
     769            0 :         floatret(1000.0f/fpshistory[(fpspos+maxfpshistory-1)%maxfpshistory]);
     770              :     }
     771              :     else
     772              :     {
     773              :         int fps, bestdiff, worstdiff;
     774            1 :         getfps(fps, bestdiff, worstdiff);
     775            1 :         intret(fps);
     776              :     }
     777            1 : }
     778              : 
     779            1 : void initrenderwindowcmds()
     780              : {
     781            1 :     addcommand("getfps", reinterpret_cast<identfun>(getfpscmd), "i", Id_Command);
     782            1 :     addcommand("resetgl", reinterpret_cast<identfun>(resetgl), "", Id_Command);
     783            1 :     addcommand("screenres", reinterpret_cast<identfun>(screenres), "ii", Id_Command);
     784            1 : }
        

Generated by: LCOV version 2.0-1