glimp.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #ifndef USE_SDL
  21. #include "../../idlib/precompiled.h"
  22. #include "../../renderer/tr_local.h"
  23. #include "local.h"
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include <fcntl.h>
  27. #include <unistd.h>
  28. extern "C" {
  29. # include "libXNVCtrl/NVCtrlLib.h"
  30. }
  31. idCVar sys_videoRam( "sys_videoRam", "0", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_INTEGER, "Texture memory on the video card (in megabytes) - 0: autodetect", 0, 512 );
  32. Display *dpy = NULL;
  33. static int scrnum = 0;
  34. Window win = 0;
  35. bool dga_found = false;
  36. static GLXContext ctx = NULL;
  37. static bool vidmode_ext = false;
  38. static int vidmode_MajorVersion = 0, vidmode_MinorVersion = 0; // major and minor of XF86VidExtensions
  39. static XF86VidModeModeInfo **vidmodes;
  40. static int num_vidmodes;
  41. static bool vidmode_active = false;
  42. // backup gamma ramp
  43. static int save_rampsize = 0;
  44. static unsigned short *save_red, *save_green, *save_blue;
  45. void GLimp_WakeBackEnd(void *a) {
  46. common->DPrintf("GLimp_WakeBackEnd stub\n");
  47. }
  48. #ifdef ID_GL_HARDLINK
  49. void GLimp_EnableLogging(bool log) {
  50. static bool logging;
  51. if (log != logging)
  52. {
  53. common->DPrintf("GLimp_EnableLogging - disabled at compile time (ID_GL_HARDLINK)\n");
  54. logging = log;
  55. }
  56. }
  57. #endif
  58. void GLimp_FrontEndSleep() {
  59. common->DPrintf("GLimp_FrontEndSleep stub\n");
  60. }
  61. void *GLimp_BackEndSleep() {
  62. common->DPrintf("GLimp_BackEndSleep stub\n");
  63. return 0;
  64. }
  65. bool GLimp_SpawnRenderThread(void (*a) ()) {
  66. common->DPrintf("GLimp_SpawnRenderThread stub\n");
  67. return false;
  68. }
  69. void GLimp_ActivateContext() {
  70. assert( dpy );
  71. assert( ctx );
  72. qglXMakeCurrent( dpy, win, ctx );
  73. }
  74. void GLimp_DeactivateContext() {
  75. assert( dpy );
  76. qglXMakeCurrent( dpy, None, NULL );
  77. }
  78. /*
  79. =================
  80. GLimp_SaveGamma
  81. save and restore the original gamma of the system
  82. =================
  83. */
  84. void GLimp_SaveGamma() {
  85. if ( save_rampsize ) {
  86. return;
  87. }
  88. assert( dpy );
  89. XF86VidModeGetGammaRampSize( dpy, scrnum, &save_rampsize);
  90. save_red = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
  91. save_green = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
  92. save_blue = (unsigned short *)malloc(save_rampsize*sizeof(unsigned short));
  93. XF86VidModeGetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
  94. }
  95. /*
  96. =================
  97. GLimp_RestoreGamma
  98. save and restore the original gamma of the system
  99. =================
  100. */
  101. void GLimp_RestoreGamma() {
  102. if (!save_rampsize)
  103. return;
  104. XF86VidModeSetGammaRamp( dpy, scrnum, save_rampsize, save_red, save_green, save_blue);
  105. free(save_red); free(save_green); free(save_blue);
  106. save_rampsize = 0;
  107. }
  108. /*
  109. =================
  110. GLimp_SetGamma
  111. gamma ramp is generated by the renderer from r_gamma and r_brightness for 256 elements
  112. the size of the gamma ramp can not be changed on X (I need to confirm this)
  113. =================
  114. */
  115. void GLimp_SetGamma(unsigned short red[256], unsigned short green[256], unsigned short blue[256]) {
  116. if ( dpy ) {
  117. int size;
  118. GLimp_SaveGamma();
  119. XF86VidModeGetGammaRampSize( dpy, scrnum, &size);
  120. common->DPrintf("XF86VidModeGetGammaRampSize: %d\n", size);
  121. if ( size > 256 ) {
  122. // silly generic resample
  123. int i;
  124. unsigned short *l_red, *l_green, *l_blue;
  125. l_red = (unsigned short *)malloc(size*sizeof(unsigned short));
  126. l_green = (unsigned short *)malloc(size*sizeof(unsigned short));
  127. l_blue = (unsigned short *)malloc(size*sizeof(unsigned short));
  128. //int r_size = 256;
  129. int r_i; float r_f;
  130. for(i=0; i<size-1; i++) {
  131. r_f = (float)i*255.0f/(float)(size-1);
  132. r_i = (int)floor(r_f);
  133. r_f -= (float)r_i;
  134. l_red[i] = (int)round((1.0f-r_f)*(float)red[r_i]+r_f*(float)red[r_i+1]);
  135. l_green[i] = (int)round((1.0f-r_f)*(float)green[r_i]+r_f*(float)green[r_i+1]);
  136. l_blue[i] = (int)round((1.0f-r_f)*(float)blue[r_i]+r_f*(float)blue[r_i+1]);
  137. }
  138. l_red[size-1] = red[255]; l_green[size-1] = green[255]; l_blue[size-1] = blue[255];
  139. XF86VidModeSetGammaRamp( dpy, scrnum, size, l_red, l_green, l_blue );
  140. free(l_red); free(l_green); free(l_blue);
  141. } else {
  142. XF86VidModeSetGammaRamp( dpy, scrnum, size, red, green, blue );
  143. }
  144. }
  145. }
  146. void GLimp_Shutdown() {
  147. if ( dpy ) {
  148. Sys_XUninstallGrabs();
  149. GLimp_RestoreGamma();
  150. qglXDestroyContext( dpy, ctx );
  151. #if !defined( ID_GL_HARDLINK )
  152. GLimp_dlclose();
  153. #endif
  154. XDestroyWindow( dpy, win );
  155. if ( vidmode_active ) {
  156. XF86VidModeSwitchToMode( dpy, scrnum, vidmodes[0] );
  157. }
  158. XFlush( dpy );
  159. // FIXME: that's going to crash
  160. //XCloseDisplay( dpy );
  161. vidmode_active = false;
  162. dpy = NULL;
  163. win = 0;
  164. ctx = NULL;
  165. }
  166. }
  167. void GLimp_SwapBuffers() {
  168. assert( dpy );
  169. qglXSwapBuffers( dpy, win );
  170. }
  171. /*
  172. GLX_TestDGA
  173. Check for DGA - update in_dgamouse if needed
  174. */
  175. void GLX_TestDGA() {
  176. int dga_MajorVersion = 0, dga_MinorVersion = 0;
  177. assert( dpy );
  178. #if defined( ID_ENABLE_DGA )
  179. if ( !XF86DGAQueryVersion( dpy, &dga_MajorVersion, &dga_MinorVersion ) ) {
  180. // unable to query, probalby not supported
  181. common->Printf( "Failed to detect DGA DirectVideo Mouse\n" );
  182. cvarSystem->SetCVarBool( "in_dgamouse", false );
  183. dga_found = false;
  184. } else {
  185. common->Printf( "DGA DirectVideo Mouse (Version %d.%d) initialized\n",
  186. dga_MajorVersion, dga_MinorVersion );
  187. dga_found = true;
  188. }
  189. #else
  190. dga_found = false;
  191. #endif
  192. }
  193. /*
  194. ** XErrorHandler
  195. ** the default X error handler exits the application
  196. ** I found out that on some hosts some operations would raise X errors (GLXUnsupportedPrivateRequest)
  197. ** but those don't seem to be fatal .. so the default would be to just ignore them
  198. ** our implementation mimics the default handler behaviour (not completely cause I'm lazy)
  199. */
  200. int idXErrorHandler(Display * l_dpy, XErrorEvent * ev) {
  201. char buf[1024];
  202. common->Printf( "Fatal X Error:\n" );
  203. common->Printf( " Major opcode of failed request: %d\n", ev->request_code );
  204. common->Printf( " Minor opcode of failed request: %d\n", ev->minor_code );
  205. common->Printf( " Serial number of failed request: %lu\n", ev->serial );
  206. XGetErrorText( l_dpy, ev->error_code, buf, 1024 );
  207. common->Printf( "%s\n", buf );
  208. return 0;
  209. }
  210. bool GLimp_OpenDisplay( void ) {
  211. if ( dpy ) {
  212. return true;
  213. }
  214. if ( cvarSystem->GetCVarInteger( "net_serverDedicated" ) == 1 ) {
  215. common->DPrintf( "not opening the display: dedicated server\n" );
  216. return false;
  217. }
  218. common->Printf( "Setup X display connection\n" );
  219. // that should be the first call into X
  220. if ( !XInitThreads() ) {
  221. common->Printf("XInitThreads failed\n");
  222. return false;
  223. }
  224. // set up our custom error handler for X failures
  225. XSetErrorHandler( &idXErrorHandler );
  226. if ( !( dpy = XOpenDisplay(NULL) ) ) {
  227. common->Printf( "Couldn't open the X display\n" );
  228. return false;
  229. }
  230. scrnum = DefaultScreen( dpy );
  231. return true;
  232. }
  233. /*
  234. ===============
  235. GLX_Init
  236. ===============
  237. */
  238. int GLX_Init(glimpParms_t a) {
  239. int attrib[] = {
  240. GLX_RGBA, // 0
  241. GLX_RED_SIZE, 8, // 1, 2
  242. GLX_GREEN_SIZE, 8, // 3, 4
  243. GLX_BLUE_SIZE, 8, // 5, 6
  244. GLX_DOUBLEBUFFER, // 7
  245. GLX_DEPTH_SIZE, 24, // 8, 9
  246. GLX_STENCIL_SIZE, 8, // 10, 11
  247. GLX_ALPHA_SIZE, 8, // 12, 13
  248. None
  249. };
  250. // these match in the array
  251. #define ATTR_RED_IDX 2
  252. #define ATTR_GREEN_IDX 4
  253. #define ATTR_BLUE_IDX 6
  254. #define ATTR_DEPTH_IDX 9
  255. #define ATTR_STENCIL_IDX 11
  256. #define ATTR_ALPHA_IDX 13
  257. Window root;
  258. XVisualInfo *visinfo;
  259. XSetWindowAttributes attr;
  260. XSizeHints sizehints;
  261. unsigned long mask;
  262. int colorbits, depthbits, stencilbits;
  263. int tcolorbits, tdepthbits, tstencilbits;
  264. int actualWidth, actualHeight;
  265. int i;
  266. const char *glstring;
  267. if ( !GLimp_OpenDisplay() ) {
  268. return false;
  269. }
  270. common->Printf( "Initializing OpenGL display\n" );
  271. root = RootWindow( dpy, scrnum );
  272. actualWidth = glConfig.vidWidth;
  273. actualHeight = glConfig.vidHeight;
  274. // Get video mode list
  275. if ( !XF86VidModeQueryVersion( dpy, &vidmode_MajorVersion, &vidmode_MinorVersion ) ) {
  276. vidmode_ext = false;
  277. common->Printf("XFree86-VidModeExtension not available\n");
  278. } else {
  279. vidmode_ext = true;
  280. common->Printf("Using XFree86-VidModeExtension Version %d.%d\n",
  281. vidmode_MajorVersion, vidmode_MinorVersion);
  282. }
  283. GLX_TestDGA();
  284. if ( vidmode_ext ) {
  285. int best_fit, best_dist, dist, x, y;
  286. XF86VidModeGetAllModeLines( dpy, scrnum, &num_vidmodes, &vidmodes );
  287. // Are we going fullscreen? If so, let's change video mode
  288. if ( a.fullScreen ) {
  289. best_dist = 9999999;
  290. best_fit = -1;
  291. for (i = 0; i < num_vidmodes; i++) {
  292. if (a.width > vidmodes[i]->hdisplay ||
  293. a.height > vidmodes[i]->vdisplay)
  294. continue;
  295. x = a.width - vidmodes[i]->hdisplay;
  296. y = a.height - vidmodes[i]->vdisplay;
  297. dist = (x * x) + (y * y);
  298. if (dist < best_dist) {
  299. best_dist = dist;
  300. best_fit = i;
  301. }
  302. }
  303. if (best_fit != -1) {
  304. actualWidth = vidmodes[best_fit]->hdisplay;
  305. actualHeight = vidmodes[best_fit]->vdisplay;
  306. // change to the mode
  307. XF86VidModeSwitchToMode(dpy, scrnum, vidmodes[best_fit]);
  308. vidmode_active = true;
  309. // Move the viewport to top left
  310. // FIXME: center?
  311. XF86VidModeSetViewPort(dpy, scrnum, 0, 0);
  312. common->Printf( "Free86-VidModeExtension Activated at %dx%d\n", actualWidth, actualHeight );
  313. } else {
  314. a.fullScreen = false;
  315. common->Printf( "Free86-VidModeExtension: No acceptable modes found\n" );
  316. }
  317. } else {
  318. common->Printf( "XFree86-VidModeExtension: not fullscreen, ignored\n" );
  319. }
  320. }
  321. // color, depth and stencil
  322. colorbits = 24;
  323. depthbits = 24;
  324. stencilbits = 8;
  325. for (i = 0; i < 16; i++) {
  326. // 0 - default
  327. // 1 - minus colorbits
  328. // 2 - minus depthbits
  329. // 3 - minus stencil
  330. if ((i % 4) == 0 && i) {
  331. // one pass, reduce
  332. switch (i / 4) {
  333. case 2:
  334. if (colorbits == 24)
  335. colorbits = 16;
  336. break;
  337. case 1:
  338. if (depthbits == 24)
  339. depthbits = 16;
  340. else if (depthbits == 16)
  341. depthbits = 8;
  342. case 3:
  343. if (stencilbits == 24)
  344. stencilbits = 16;
  345. else if (stencilbits == 16)
  346. stencilbits = 8;
  347. }
  348. }
  349. tcolorbits = colorbits;
  350. tdepthbits = depthbits;
  351. tstencilbits = stencilbits;
  352. if ((i % 4) == 3) { // reduce colorbits
  353. if (tcolorbits == 24)
  354. tcolorbits = 16;
  355. }
  356. if ((i % 4) == 2) { // reduce depthbits
  357. if (tdepthbits == 24)
  358. tdepthbits = 16;
  359. else if (tdepthbits == 16)
  360. tdepthbits = 8;
  361. }
  362. if ((i % 4) == 1) { // reduce stencilbits
  363. if (tstencilbits == 24)
  364. tstencilbits = 16;
  365. else if (tstencilbits == 16)
  366. tstencilbits = 8;
  367. else
  368. tstencilbits = 0;
  369. }
  370. if (tcolorbits == 24) {
  371. attrib[ATTR_RED_IDX] = 8;
  372. attrib[ATTR_GREEN_IDX] = 8;
  373. attrib[ATTR_BLUE_IDX] = 8;
  374. } else {
  375. // must be 16 bit
  376. attrib[ATTR_RED_IDX] = 4;
  377. attrib[ATTR_GREEN_IDX] = 4;
  378. attrib[ATTR_BLUE_IDX] = 4;
  379. }
  380. attrib[ATTR_DEPTH_IDX] = tdepthbits; // default to 24 depth
  381. attrib[ATTR_STENCIL_IDX] = tstencilbits;
  382. visinfo = qglXChooseVisual(dpy, scrnum, attrib);
  383. if (!visinfo) {
  384. continue;
  385. }
  386. common->Printf( "Using %d/%d/%d Color bits, %d Alpha bits, %d depth, %d stencil display.\n",
  387. attrib[ATTR_RED_IDX], attrib[ATTR_GREEN_IDX],
  388. attrib[ATTR_BLUE_IDX], attrib[ATTR_ALPHA_IDX],
  389. attrib[ATTR_DEPTH_IDX],
  390. attrib[ATTR_STENCIL_IDX]);
  391. glConfig.colorBits = tcolorbits;
  392. glConfig.depthBits = tdepthbits;
  393. glConfig.stencilBits = tstencilbits;
  394. break;
  395. }
  396. if (!visinfo) {
  397. common->Printf("Couldn't get a visual\n");
  398. return false;
  399. }
  400. // window attributes
  401. attr.background_pixel = BlackPixel(dpy, scrnum);
  402. attr.border_pixel = 0;
  403. attr.colormap = XCreateColormap(dpy, root, visinfo->visual, AllocNone);
  404. attr.event_mask = X_MASK;
  405. if (vidmode_active) {
  406. mask = CWBackPixel | CWColormap | CWSaveUnder | CWBackingStore |
  407. CWEventMask | CWOverrideRedirect;
  408. attr.override_redirect = True;
  409. attr.backing_store = NotUseful;
  410. attr.save_under = False;
  411. } else {
  412. mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
  413. }
  414. win = XCreateWindow(dpy, root, 0, 0,
  415. actualWidth, actualHeight,
  416. 0, visinfo->depth, InputOutput,
  417. visinfo->visual, mask, &attr);
  418. XStoreName(dpy, win, GAME_NAME);
  419. // don't let the window be resized
  420. // FIXME: allow resize (win32 does)
  421. sizehints.flags = PMinSize | PMaxSize;
  422. sizehints.min_width = sizehints.max_width = actualWidth;
  423. sizehints.min_height = sizehints.max_height = actualHeight;
  424. XSetWMNormalHints(dpy, win, &sizehints);
  425. XMapWindow( dpy, win );
  426. if ( vidmode_active ) {
  427. XMoveWindow( dpy, win, 0, 0 );
  428. }
  429. XFlush(dpy);
  430. XSync(dpy, False);
  431. ctx = qglXCreateContext(dpy, visinfo, NULL, True);
  432. XSync(dpy, False);
  433. // Free the visinfo after we're done with it
  434. XFree(visinfo);
  435. qglXMakeCurrent(dpy, win, ctx);
  436. glstring = (const char *) qglGetString(GL_RENDERER);
  437. common->Printf("GL_RENDERER: %s\n", glstring);
  438. glstring = (const char *) qglGetString(GL_EXTENSIONS);
  439. common->Printf("GL_EXTENSIONS: %s\n", glstring);
  440. // FIXME: here, software GL test
  441. glConfig.isFullscreen = a.fullScreen;
  442. if ( glConfig.isFullscreen ) {
  443. Sys_GrabMouseCursor( true );
  444. }
  445. return true;
  446. }
  447. /*
  448. ===================
  449. GLimp_Init
  450. This is the platform specific OpenGL initialization function. It
  451. is responsible for loading OpenGL, initializing it,
  452. creating a window of the appropriate size, doing
  453. fullscreen manipulations, etc. Its overall responsibility is
  454. to make sure that a functional OpenGL subsystem is operating
  455. when it returns to the ref.
  456. If there is any failure, the renderer will revert back to safe
  457. parameters and try again.
  458. ===================
  459. */
  460. bool GLimp_Init( glimpParms_t a ) {
  461. if ( !GLimp_OpenDisplay() ) {
  462. return false;
  463. }
  464. #ifndef ID_GL_HARDLINK
  465. if ( !GLimp_dlopen() ) {
  466. return false;
  467. }
  468. #endif
  469. if (!GLX_Init(a)) {
  470. return false;
  471. }
  472. return true;
  473. }
  474. /*
  475. ===================
  476. GLimp_SetScreenParms
  477. ===================
  478. */
  479. bool GLimp_SetScreenParms( glimpParms_t parms ) {
  480. return true;
  481. }
  482. #endif
  483. /*
  484. ================
  485. Sys_GetVideoRam
  486. returns in megabytes
  487. open your own display connection for the query and close it
  488. using the one shared with GLimp_Init is not stable
  489. ================
  490. */
  491. int Sys_GetVideoRam( void ) {
  492. #ifdef USE_SDL
  493. return 128;
  494. #else
  495. static int run_once = 0;
  496. int major, minor, value;
  497. Display *l_dpy;
  498. int l_scrnum;
  499. if ( run_once ) {
  500. return run_once;
  501. }
  502. if ( sys_videoRam.GetInteger() ) {
  503. run_once = sys_videoRam.GetInteger();
  504. return sys_videoRam.GetInteger();
  505. }
  506. // try a few strategies to guess the amount of video ram
  507. common->Printf( "guessing video ram ( use +set sys_videoRam to force ) ..\n" );
  508. if ( !GLimp_OpenDisplay( ) ) {
  509. run_once = 64;
  510. return run_once;
  511. }
  512. l_dpy = dpy;
  513. l_scrnum = scrnum;
  514. // go for nvidia ext first
  515. if ( XNVCTRLQueryVersion( l_dpy, &major, &minor ) ) {
  516. common->Printf( "found XNVCtrl extension %d.%d\n", major, minor );
  517. if ( XNVCTRLIsNvScreen( l_dpy, l_scrnum ) ) {
  518. if ( XNVCTRLQueryAttribute( l_dpy, l_scrnum, 0, NV_CTRL_VIDEO_RAM, &value ) ) {
  519. run_once = value / 1024;
  520. return run_once;
  521. } else {
  522. common->Printf( "XNVCtrlQueryAttribute NV_CTRL_VIDEO_RAM failed\n" );
  523. }
  524. } else {
  525. common->Printf( "default screen %d is not controlled by NVIDIA driver\n", l_scrnum );
  526. }
  527. }
  528. // try ATI /proc read ( for the lack of a better option )
  529. int fd;
  530. if ( ( fd = open( "/proc/dri/0/umm", O_RDONLY ) ) != -1 ) {
  531. int len;
  532. char umm_buf[ 1024 ];
  533. char *line;
  534. if ( ( len = read( fd, umm_buf, 1024 ) ) != -1 ) {
  535. // should be way enough to get the full file
  536. // grab "free LFB = " line and "free Inv = " lines
  537. umm_buf[ len-1 ] = '\0';
  538. line = umm_buf;
  539. line = strtok( umm_buf, "\n" );
  540. int total = 0;
  541. while ( line ) {
  542. if ( strlen( line ) >= 13 && strstr( line, "max LFB =" ) == line ) {
  543. total += atoi( line + 12 );
  544. } else if ( strlen( line ) >= 13 && strstr( line, "max Inv =" ) == line ) {
  545. total += atoi( line + 12 );
  546. }
  547. line = strtok( NULL, "\n" );
  548. }
  549. if ( total ) {
  550. run_once = total / 1048576;
  551. // round to the lower 16Mb
  552. run_once &= ~15;
  553. return run_once;
  554. }
  555. } else {
  556. common->Printf( "read /proc/dri/0/umm failed: %s\n", strerror( errno ) );
  557. }
  558. }
  559. common->Printf( "guess failed, return default low-end VRAM setting ( 64MB VRAM )\n" );
  560. run_once = 64;
  561. return run_once;
  562. #endif
  563. }