Index: ps/trunk/source/ps/VideoMode.cpp =================================================================== --- ps/trunk/source/ps/VideoMode.cpp (revision 9545) +++ ps/trunk/source/ps/VideoMode.cpp (revision 9546) @@ -1,382 +1,382 @@ /* Copyright (C) 2010 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "VideoMode.h" #include "graphics/Camera.h" #include "graphics/GameView.h" #include "gui/GUIManager.h" #include "lib/ogl.h" #include "lib/external_libraries/sdl.h" #include "lib/sysdep/gfx.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/Game.h" #include "ps/GameSetup/Config.h" #include "renderer/Renderer.h" static int DEFAULT_WINDOW_W = 1024; static int DEFAULT_WINDOW_H = 768; static int DEFAULT_FULLSCREEN_W = 1024; static int DEFAULT_FULLSCREEN_H = 768; CVideoMode g_VideoMode; CVideoMode::CVideoMode() : m_IsInitialised(false), m_PreferredW(0), m_PreferredH(0), m_PreferredBPP(0), m_PreferredFreq(0), m_ConfigW(0), m_ConfigH(0), m_ConfigBPP(0), m_ConfigFullscreen(false), m_ConfigForceS3TCEnable(true), m_WindowedW(DEFAULT_WINDOW_W), m_WindowedH(DEFAULT_WINDOW_H) { // (m_ConfigFullscreen defaults to false, so users don't get stuck if // e.g. half the filesystem is missing and the config files aren't loaded) } void CVideoMode::ReadConfig() { bool windowed = !m_ConfigFullscreen; CFG_GET_USER_VAL("windowed", Bool, windowed); m_ConfigFullscreen = !windowed; CFG_GET_USER_VAL("xres", Int, m_ConfigW); CFG_GET_USER_VAL("yres", Int, m_ConfigH); CFG_GET_USER_VAL("bpp", Int, m_ConfigBPP); CFG_GET_USER_VAL("force_s3tc_enable", Bool, m_ConfigForceS3TCEnable); } bool CVideoMode::SetVideoMode(int w, int h, int bpp, bool fullscreen) { Uint32 flags = SDL_OPENGL; if (fullscreen) flags |= SDL_FULLSCREEN; else flags |= SDL_RESIZABLE; SDL_Surface* screen = SDL_SetVideoMode(w, h, bpp, flags); if (!screen) { // If fullscreen fails, try windowed mode if (fullscreen) { LOGWARNING(L"Failed to set the video mode to fullscreen for the chosen resolution " L"%dx%d:%d (\"%hs\"), falling back to windowed mode", w, h, bpp, SDL_GetError()); // Using default size for the window for now, as the attempted setting // could be as large, or larger than the screen size. return SetVideoMode(DEFAULT_WINDOW_W, DEFAULT_WINDOW_H, bpp, false); } else { LOGERROR(L"SetVideoMode failed: %dx%d:%d %d (\"%hs\")", w, h, bpp, fullscreen ? 1 : 0, SDL_GetError()); return false; } } if (fullscreen) SDL_WM_GrabInput(SDL_GRAB_ON); else SDL_WM_GrabInput(SDL_GRAB_OFF); m_IsFullscreen = fullscreen; // Grab the current video settings m_CurrentW = screen->w; m_CurrentH = screen->h; m_CurrentBPP = bpp; // getting bpp from surface not supported in wsdl g_xres = m_CurrentW; g_yres = m_CurrentH; return true; } bool CVideoMode::InitSDL() { ENSURE(!m_IsInitialised); ReadConfig(); EnableS3TC(); // preferred video mode = current desktop settings // (command line params may override these) - gfx_get_video_mode(&m_PreferredW, &m_PreferredH, &m_PreferredBPP, &m_PreferredFreq); + gfx::GetVideoMode(&m_PreferredW, &m_PreferredH, &m_PreferredBPP, &m_PreferredFreq); int w = m_ConfigW; int h = m_ConfigH; if (m_ConfigFullscreen) { // If fullscreen and no explicit size set, default to the desktop resolution if (w == 0 || h == 0) { w = m_PreferredW; h = m_PreferredH; } } // If no size determined, default to something sensible if (w == 0 || h == 0) { w = DEFAULT_WINDOW_W; h = DEFAULT_WINDOW_H; } if (!m_ConfigFullscreen) { // Limit the window to the screen size (if known) if (m_PreferredW) w = std::min(w, m_PreferredW); if (m_PreferredH) h = std::min(h, m_PreferredH); } int bpp = GetBestBPP(); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, g_VSync ? 1 : 0); if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen)) { // Fall back to a smaller depth buffer // (The rendering may be ugly but this helps when running in VMware) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); if (!SetVideoMode(w, h, bpp, m_ConfigFullscreen)) return false; } // Work around a bug in the proprietary Linux ATI driver (at least versions 8.16.20 and 8.14.13). // The driver appears to register its own atexit hook on context creation. // If this atexit hook is called before SDL_Quit destroys the OpenGL context, // some kind of double-free problem causes a crash and lockup in the driver. // Calling SDL_Quit twice appears to be harmless, though, and avoids the problem // by destroying the context *before* the driver's atexit hook is called. // (Note that atexit hooks are guaranteed to be called in reverse order of their registration.) atexit(SDL_Quit); // End work around. ogl_Init(); // required after each mode change // (TODO: does that mean we need to call this when toggling fullscreen later?) if (SDL_SetGamma(g_Gamma, g_Gamma, g_Gamma) < 0) LOGWARNING(L"SDL_SetGamma failed"); m_IsInitialised = true; if (!m_ConfigFullscreen) { m_WindowedW = w; m_WindowedH = h; } return true; } bool CVideoMode::InitNonSDL() { ENSURE(!m_IsInitialised); ReadConfig(); EnableS3TC(); m_IsInitialised = true; return true; } void CVideoMode::Shutdown() { ENSURE(m_IsInitialised); m_IsInitialised = false; } void CVideoMode::EnableS3TC() { // On Linux we have to try hard to get S3TC compressed texture support. // If the extension is already provided by default, that's fine. // Otherwise we should enable the 'force_s3tc_enable' environment variable // and (re)initialise the video system, so that Mesa provides the extension // (if the driver at least supports decompression). // (This overrides the force_s3tc_enable specified via driconf files.) // Otherwise we should complain to the user, and stop using compressed textures. // // Setting the environment variable causes Mesa to print an ugly message to stderr // ("ATTENTION: default value of option force_s3tc_enable overridden by environment."), // so it'd be nicer to skip that if S3TC will be supported by default, // but reinitialising video is a pain (and it might do weird things when fullscreen) // so we just unconditionally set it (unless our config file explicitly disables it). #if !(OS_WIN || OS_MACOSX) // (assume Mesa is used for all non-Windows non-Mac platforms) if (m_ConfigForceS3TCEnable) setenv("force_s3tc_enable", "true", 0); #endif } bool CVideoMode::ResizeWindow(int w, int h) { ENSURE(m_IsInitialised); // Ignore if not windowed if (m_IsFullscreen) return true; // Ignore if the size hasn't changed if (w == m_WindowedW && h == m_WindowedH) return true; int bpp = GetBestBPP(); if (!SetVideoMode(w, h, bpp, false)) return false; m_WindowedW = w; m_WindowedH = h; UpdateRenderer(w, h); return true; } bool CVideoMode::SetFullscreen(bool fullscreen) { // This might get called before initialisation by psDisplayError; // if so then silently fail if (!m_IsInitialised) return false; // Check whether this is actually a change if (fullscreen == m_IsFullscreen) return true; if (!m_IsFullscreen) { // Windowed -> fullscreen: int w = 0, h = 0; // If a fullscreen size was configured, use that; else use the desktop size; else use a default if (m_ConfigFullscreen) { w = m_ConfigW; h = m_ConfigH; } if (w == 0 || h == 0) { w = m_PreferredW; h = m_PreferredH; } if (w == 0 || h == 0) { w = DEFAULT_FULLSCREEN_W; h = DEFAULT_FULLSCREEN_H; } int bpp = GetBestBPP(); if (!SetVideoMode(w, h, bpp, fullscreen)) return false; UpdateRenderer(m_CurrentW, m_CurrentH); return true; } else { // Fullscreen -> windowed: // Go back to whatever the previous window size was int w = m_WindowedW, h = m_WindowedH; int bpp = GetBestBPP(); if (!SetVideoMode(w, h, bpp, fullscreen)) return false; UpdateRenderer(w, h); return true; } } bool CVideoMode::ToggleFullscreen() { return SetFullscreen(!m_IsFullscreen); } void CVideoMode::UpdateRenderer(int w, int h) { if (w < 2) w = 2; // avoid GL errors caused by invalid sizes if (h < 2) h = 2; g_xres = w; g_yres = h; SViewPort vp = { 0, 0, w, h }; if (CRenderer::IsInitialised()) { g_Renderer.SetViewport(vp); g_Renderer.Resize(w, h); } if (g_GUI) g_GUI->UpdateResolution(); if (g_Console) g_Console->UpdateScreenSize(w, h); if (g_Game) g_Game->GetView()->SetViewport(vp); } int CVideoMode::GetBestBPP() { if (m_ConfigBPP) return m_ConfigBPP; if (m_PreferredBPP) return m_PreferredBPP; return 32; } int CVideoMode::GetXRes() { ENSURE(m_IsInitialised); return m_CurrentW; } int CVideoMode::GetYRes() { ENSURE(m_IsInitialised); return m_CurrentH; } int CVideoMode::GetBPP() { ENSURE(m_IsInitialised); return m_CurrentBPP; } Index: ps/trunk/source/ps/Util.cpp =================================================================== --- ps/trunk/source/ps/Util.cpp (revision 9545) +++ ps/trunk/source/ps/Util.cpp (revision 9546) @@ -1,404 +1,406 @@ /* Copyright (C) 2009 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "ps/Util.h" #include "lib/posix/posix_utsname.h" #include "lib/posix/posix_sock.h" #include "lib/ogl.h" #include "lib/timer.h" #include "lib/bits.h" // round_up #include "lib/allocators/shared_ptr.h" #include "lib/sysdep/sysdep.h" // sys_OpenFile #include "lib/sysdep/gfx.h" #include "lib/sysdep/snd.h" #include "lib/sysdep/cpu.h" #include "lib/sysdep/os_cpu.h" #include "lib/sysdep/arch/x86_x64/topology.h" #include "lib/sysdep/smbios.h" #include "lib/tex/tex.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/GameSetup.h" #include "ps/Game.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" #include "ps/VideoMode.h" #include "renderer/Renderer.h" #include "maths/MathUtil.h" #include "graphics/GameView.h" extern CStrW g_CursorName; static std::string SplitExts(const char *exts) { std::string str = exts; std::string ret = ""; size_t idx = str.find_first_of(" "); while(idx != std::string::npos) { if(idx >= str.length() - 1) { ret += str; break; } ret += str.substr(0, idx); ret += "\n"; str = str.substr(idx + 1); idx = str.find_first_of(" "); } return ret; } void WriteSystemInfo() { TIMER(L"write_sys_info"); // get_cpu_info and gfx_detect already called during init - see call site snd_detect(); struct utsname un; uname(&un); OsPath pathname = psLogDir()/"system_info.txt"; FILE* f = sys_OpenFile(pathname, "w"); if(!f) return; // current timestamp (redundant WRT OS timestamp, but that is not // visible when people are posting this file's contents online) { wchar_t timestampBuf[100] = {'\0'}; time_t seconds; time(&seconds); struct tm* t = gmtime(&seconds); const size_t charsWritten = wcsftime(timestampBuf, ARRAY_SIZE(timestampBuf), L"(generated %Y-%m-%d %H:%M:%S UTC)", t); ENSURE(charsWritten != 0); fprintf(f, "%ls\n\n", timestampBuf); } // OS fprintf(f, "OS : %s %s (%s)\n", un.sysname, un.release, un.version); // CPU fprintf(f, "CPU : %s, %s (%dx%dx%d)", un.machine, cpu_IdentifierString(), (int)cpu_topology_NumPackages(), (int)cpu_topology_CoresPerPackage(), (int)cpu_topology_LogicalPerCore()); const double cpu_freq = os_cpu_ClockFrequency(); if(cpu_freq != 0.0f) { if(cpu_freq < 1e9) fprintf(f, ", %.2f MHz\n", cpu_freq*1e-6); else fprintf(f, ", %.2f GHz\n", cpu_freq*1e-9); } else fprintf(f, "\n"); // memory fprintf(f, "Memory : %u MiB; %u MiB free\n", (unsigned)os_cpu_MemorySize(), (unsigned)os_cpu_MemoryAvailable()); // graphics - fprintf(f, "Graphics Card : %ls\n", gfx_card); - fprintf(f, "OpenGL Drivers : %s; %ls\n", glGetString(GL_VERSION), gfx_drv_ver); + const std::wstring cardName = gfx::CardName(); + const std::wstring driverInfo = gfx::DriverInfo(); + fprintf(f, "Graphics Card : %ls\n", cardName.c_str()); + fprintf(f, "OpenGL Drivers : %s; %ls\n", glGetString(GL_VERSION), driverInfo.c_str()); fprintf(f, "Video Mode : %dx%d:%d\n", g_VideoMode.GetXRes(), g_VideoMode.GetYRes(), g_VideoMode.GetBPP()); // sound fprintf(f, "Sound Card : %ls\n", snd_card); fprintf(f, "Sound Drivers : %ls\n", snd_drv_ver); // // network name / ips // // note: can't use un.nodename because it is for an // "implementation-defined communications network". char hostname[128] = "(unknown)"; (void)gethostname(hostname, sizeof(hostname)-1); // -1 makes sure it's 0-terminated. if the function fails, // we display "(unknown)" and will skip IP output below. fprintf(f, "Network Name : %s", hostname); { // ignore exception here - see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=114032 hostent* host = gethostbyname(hostname); if(!host) goto no_ip; struct in_addr** ips = (struct in_addr**)host->h_addr_list; if(!ips) goto no_ip; // output all IPs (> 1 if using VMware or dual ethernet) fprintf(f, " ("); for(size_t i = 0; i < 256 && ips[i]; i++) // safety { // separate entries but avoid trailing comma if(i != 0) fprintf(f, ", "); fprintf(f, "%s", inet_ntoa(*ips[i])); } fprintf(f, ")"); } no_ip: fprintf(f, "\n"); // OpenGL extensions (write them last, since it's a lot of text) const char* exts = ogl_ExtensionString(); if (!exts) exts = "{unknown}"; fprintf(f, "\nOpenGL Extensions: \n%s\n", SplitExts(exts).c_str()); // System Management BIOS (even more text than OpenGL extensions) std::string smbios = SMBIOS::StringizeStructures(SMBIOS::GetStructures()); fprintf(f, "\nSMBIOS: \n%s\n", smbios.c_str()); fclose(f); f = 0; } // not thread-safe! static const wchar_t* HardcodedErrorString(int err) { static wchar_t description[200]; StatusDescription((Status)err, description, ARRAY_SIZE(description)); return description; } // not thread-safe! const wchar_t* ErrorString(int err) { // language file not available (yet) return HardcodedErrorString(err); // TODO: load from language file } // write the specified texture to disk. // note: cannot be made const because the image may have to be // transformed to write it out in the format determined by 's extension. Status tex_write(Tex* t, const VfsPath& filename) { DynArray da; RETURN_STATUS_IF_ERR(tex_encode(t, filename.Extension(), &da)); // write to disk Status ret = INFO::OK; { shared_ptr file = DummySharedPtr(da.base); const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos); if(bytes_written > 0) ENSURE(bytes_written == (ssize_t)da.pos); else ret = (Status)bytes_written; } (void)da_free(&da); return ret; } static size_t s_nextScreenshotNumber; // identifies the file format that is to be written // (case-insensitive). examples: "bmp", "png", "jpg". // BMP is good for quick output at the expense of large files. void WriteScreenshot(const VfsPath& extension) { // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath basenameFormat(L"screenshots/screenshot%04d"); const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension); VfsPath filename; fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); const size_t w = (size_t)g_xres, h = (size_t)g_yres; const size_t bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; // we want writing BMP to be as fast as possible, // so read data from OpenGL in BMP format to obviate conversion. if(extension == L".bmp") { fmt = GL_BGR; flags |= TEX_BGR; } // Hide log messages and re-render RenderLogger(false); Render(); RenderLogger(true); const size_t img_size = w * h * bpp/8; const size_t hdr_size = tex_hdr_size(filename); shared_ptr buf; AllocateAligned(buf, hdr_size+img_size, maxSectorSize); GLvoid* img = buf.get() + hdr_size; Tex t; if(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0) return; glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img); if (tex_write(&t, filename) == INFO::OK) { OsPath realPath; g_VFS->GetRealPath(filename, realPath); LOGMESSAGERENDER(L"Screenshot written to '%ls'", realPath.string().c_str()); } else LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str()); tex_free(&t); } // Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles. void WriteBigScreenshot(const VfsPath& extension, int tiles) { // If the game hasn't started yet then use WriteScreenshot to generate the image. if(g_Game == NULL){ WriteScreenshot(L".bmp"); return; } // get next available numbered filename // note: %04d -> always 4 digits, so sorting by filename works correctly. const VfsPath basenameFormat(L"screenshots/screenshot%04d"); const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension); VfsPath filename; fs_util::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename); // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and // hope the screen is actually large enough for that. const int tile_w = 640, tile_h = 480; ENSURE(g_xres >= tile_w && g_yres >= tile_h); const int img_w = tile_w*tiles, img_h = tile_h*tiles; const int bpp = 24; GLenum fmt = GL_RGB; int flags = TEX_BOTTOM_UP; // we want writing BMP to be as fast as possible, // so read data from OpenGL in BMP format to obviate conversion. if(extension == L".bmp") { fmt = GL_BGR; flags |= TEX_BGR; } const size_t img_size = img_w * img_h * bpp/8; const size_t tile_size = tile_w * tile_h * bpp/8; const size_t hdr_size = tex_hdr_size(filename); void* tile_data = malloc(tile_size); if(!tile_data) { WARN_IF_ERR(ERR::NO_MEM); return; } shared_ptr img_buf; AllocateAligned(img_buf, hdr_size+img_size, maxSectorSize); Tex t; GLvoid* img = img_buf.get() + hdr_size; if(tex_wrap(img_w, img_h, bpp, flags, img_buf, hdr_size, &t) < 0) { free(tile_data); return; } ogl_WarnIfError(); // Resize various things so that the sizes and aspect ratios are correct { g_Renderer.Resize(tile_w, tile_h); SViewPort vp = { 0, 0, tile_w, tile_h }; g_Game->GetView()->GetCamera()->SetViewPort(vp); g_Game->GetView()->GetCamera()->SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV); } // Temporarily move everything onto the front buffer, so the user can // see the exciting progress as it renders (and can tell when it's finished). // (It doesn't just use SwapBuffers, because it doesn't know whether to // call the SDL version or the Atlas version.) GLint oldReadBuffer, oldDrawBuffer; glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer); glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer); glDrawBuffer(GL_FRONT); glReadBuffer(GL_FRONT); // Hide the cursor CStrW oldCursor = g_CursorName; g_CursorName = L""; // Render each tile for (int tile_y = 0; tile_y < tiles; ++tile_y) { for (int tile_x = 0; tile_x < tiles; ++tile_x) { // Adjust the camera to render the appropriate region g_Game->GetView()->GetCamera()->SetProjectionTile(tiles, tile_x, tile_y); RenderLogger(false); RenderGui(false); Render(); RenderGui(true); RenderLogger(true); // Copy the tile pixels into the main image glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data); for (int y = 0; y < tile_h; ++y) { void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8; void* src = (char*)tile_data + y * tile_w * bpp/8; memcpy(dest, src, tile_w * bpp/8); } } } // Restore the old cursor g_CursorName = oldCursor; // Restore the buffer settings glDrawBuffer(oldDrawBuffer); glReadBuffer(oldReadBuffer); // Restore the viewport settings { g_Renderer.Resize(g_xres, g_yres); SViewPort vp = { 0, 0, g_xres, g_yres }; g_Game->GetView()->GetCamera()->SetViewPort(vp); g_Game->GetView()->GetCamera()->SetProjection(CGameView::defaultNear, CGameView::defaultFar, CGameView::defaultFOV); g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0); } if (tex_write(&t, filename) == INFO::OK) { OsPath realPath; g_VFS->GetRealPath(filename, realPath); LOGMESSAGERENDER(L"Screenshot written to '%ls'", realPath.string().c_str()); } else LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str()); tex_free(&t); free(tile_data); } Index: ps/trunk/source/ps/GameSetup/HWDetect.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/HWDetect.cpp (revision 9545) +++ ps/trunk/source/ps/GameSetup/HWDetect.cpp (revision 9546) @@ -1,566 +1,566 @@ /* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "scripting/ScriptingHost.h" #include "scriptinterface/ScriptInterface.h" #include "lib/ogl.h" #include "lib/svn_revision.h" #include "lib/timer.h" #include "lib/utf8.h" #include "lib/res/graphics/ogl_tex.h" #include "lib/posix/posix_utsname.h" #include "lib/sysdep/cpu.h" #include "lib/sysdep/gfx.h" #include "lib/sysdep/numa.h" #include "lib/sysdep/os_cpu.h" #include "lib/sysdep/snd.h" #if ARCH_X86_X64 # include "lib/sysdep/arch/x86_x64/cache.h" # include "lib/sysdep/arch/x86_x64/topology.h" #endif #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/UserReport.h" #include "ps/VideoMode.h" #include "ps/GameSetup/Config.h" static void ReportGLLimits(ScriptInterface& scriptInterface, CScriptValRooted settings); #if ARCH_X86_X64 CScriptVal ConvertCaches(ScriptInterface& scriptInterface, IdxCache idxCache) { CScriptVal ret; scriptInterface.Eval("[]", ret); for (size_t idxLevel = 0; idxLevel < x86_x64_Cache::maxLevels; ++idxLevel) { const x86_x64_Cache* pcache = x86_x64_Caches(idxCache+idxLevel); if (pcache->type == x86_x64_Cache::kNull || pcache->numEntries == 0) continue; CScriptVal cache; scriptInterface.Eval("({})", cache); scriptInterface.SetProperty(cache.get(), "type", (u32)pcache->type); scriptInterface.SetProperty(cache.get(), "level", (u32)pcache->level); scriptInterface.SetProperty(cache.get(), "associativity", (u32)pcache->associativity); scriptInterface.SetProperty(cache.get(), "linesize", (u32)pcache->entrySize); scriptInterface.SetProperty(cache.get(), "sharedby", (u32)pcache->sharedBy); scriptInterface.SetProperty(cache.get(), "totalsize", (u32)pcache->TotalSize()); scriptInterface.SetPropertyInt(ret.get(), idxLevel, cache); } return ret; } CScriptVal ConvertTLBs(ScriptInterface& scriptInterface) { CScriptVal ret; scriptInterface.Eval("[]", ret); for(size_t i = 0; ; i++) { const x86_x64_Cache* ptlb = x86_x64_Caches(TLB+i); if (!ptlb) break; CScriptVal tlb; scriptInterface.Eval("({})", tlb); scriptInterface.SetProperty(tlb.get(), "type", (u32)ptlb->type); scriptInterface.SetProperty(tlb.get(), "level", (u32)ptlb->level); scriptInterface.SetProperty(tlb.get(), "associativity", (u32)ptlb->associativity); scriptInterface.SetProperty(tlb.get(), "pagesize", (u32)ptlb->entrySize); scriptInterface.SetProperty(tlb.get(), "entries", (u32)ptlb->numEntries); scriptInterface.SetPropertyInt(ret.get(), i, tlb); } return ret; } #endif // The Set* functions will override the default behaviour, unless the user // has explicitly set a config variable to override that. // (TODO: This is an ugly abuse of the config system) static bool IsOverridden(const char* setting) { EConfigNamespace ns = g_ConfigDB.GetValueNamespace(CFG_COMMAND, setting); return !(ns == CFG_LAST || ns == CFG_DEFAULT); } void SetDisableAudio(void* UNUSED(cbdata), bool disabled) { g_DisableAudio = disabled; } void SetDisableS3TC(void* UNUSED(cbdata), bool disabled) { if (!IsOverridden("nos3tc")) ogl_tex_override(OGL_TEX_S3TC, disabled ? OGL_TEX_DISABLE : OGL_TEX_ENABLE); } void SetDisableShadows(void* UNUSED(cbdata), bool disabled) { if (!IsOverridden("shadows")) g_Shadows = !disabled; } void SetDisableFancyWater(void* UNUSED(cbdata), bool disabled) { if (!IsOverridden("fancywater")) g_FancyWater = !disabled; } void SetRenderPath(void* UNUSED(cbdata), std::string renderpath) { if (!IsOverridden("fancywater")) g_RenderPath = renderpath; } void RunHardwareDetection() { TIMER(L"RunHardwareDetection"); ScriptInterface& scriptInterface = g_ScriptingHost.GetScriptInterface(); scriptInterface.RegisterFunction("SetDisableAudio"); scriptInterface.RegisterFunction("SetDisableS3TC"); scriptInterface.RegisterFunction("SetDisableShadows"); scriptInterface.RegisterFunction("SetDisableFancyWater"); scriptInterface.RegisterFunction("SetRenderPath"); // Load the detection script: const wchar_t* scriptName = L"hwdetect/hwdetect.js"; CVFSFile file; if (file.Load(g_VFS, scriptName) != PSRETURN_OK) { LOGERROR(L"Failed to load hardware detection script"); return; } Status err; // ignore encoding errors std::wstring code = wstring_from_utf8(file.GetAsString(), &err); scriptInterface.LoadScript(scriptName, code); // Collect all the settings we'll pass to the script: // (We'll use this same data for the opt-in online reporting system, so it // includes some fields that aren't directly useful for the hwdetect script) CScriptValRooted settings; scriptInterface.Eval("({})", settings); scriptInterface.SetProperty(settings.get(), "os_unix", OS_UNIX); scriptInterface.SetProperty(settings.get(), "os_linux", OS_LINUX); scriptInterface.SetProperty(settings.get(), "os_macosx", OS_MACOSX); scriptInterface.SetProperty(settings.get(), "os_win", OS_WIN); scriptInterface.SetProperty(settings.get(), "arch_ia32", ARCH_IA32); scriptInterface.SetProperty(settings.get(), "arch_amd64", ARCH_AMD64); #ifdef NDEBUG scriptInterface.SetProperty(settings.get(), "build_debug", 0); #else scriptInterface.SetProperty(settings.get(), "build_debug", 1); #endif scriptInterface.SetProperty(settings.get(), "build_datetime", std::string(__DATE__ " " __TIME__)); scriptInterface.SetProperty(settings.get(), "build_revision", std::wstring(svn_revision)); scriptInterface.SetProperty(settings.get(), "build_msc", (int)MSC_VERSION); scriptInterface.SetProperty(settings.get(), "build_icc", (int)ICC_VERSION); scriptInterface.SetProperty(settings.get(), "build_gcc", (int)GCC_VERSION); - scriptInterface.SetProperty(settings.get(), "gfx_card", std::wstring(gfx_card)); - scriptInterface.SetProperty(settings.get(), "gfx_drv_ver", std::wstring(gfx_drv_ver)); - scriptInterface.SetProperty(settings.get(), "gfx_mem", gfx_mem); + scriptInterface.SetProperty(settings.get(), "gfx_card", gfx::CardName()); + scriptInterface.SetProperty(settings.get(), "gfx_drv_ver", gfx::DriverInfo()); + scriptInterface.SetProperty(settings.get(), "gfx_mem", gfx::MemorySizeMiB()); scriptInterface.SetProperty(settings.get(), "snd_card", std::wstring(snd_card)); scriptInterface.SetProperty(settings.get(), "snd_drv_ver", std::wstring(snd_drv_ver)); ReportGLLimits(scriptInterface, settings); scriptInterface.SetProperty(settings.get(), "video_xres", g_VideoMode.GetXRes()); scriptInterface.SetProperty(settings.get(), "video_yres", g_VideoMode.GetYRes()); scriptInterface.SetProperty(settings.get(), "video_bpp", g_VideoMode.GetBPP()); struct utsname un; uname(&un); scriptInterface.SetProperty(settings.get(), "uname_sysname", std::string(un.sysname)); scriptInterface.SetProperty(settings.get(), "uname_release", std::string(un.release)); scriptInterface.SetProperty(settings.get(), "uname_version", std::string(un.version)); scriptInterface.SetProperty(settings.get(), "uname_machine", std::string(un.machine)); #if OS_LINUX { std::ifstream ifs("/etc/lsb-release"); if (ifs.good()) { std::string str((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); scriptInterface.SetProperty(settings.get(), "linux_release", str); } } #endif scriptInterface.SetProperty(settings.get(), "cpu_identifier", std::string(cpu_IdentifierString())); scriptInterface.SetProperty(settings.get(), "cpu_frequency", os_cpu_ClockFrequency()); scriptInterface.SetProperty(settings.get(), "cpu_pagesize", (u32)os_cpu_PageSize()); scriptInterface.SetProperty(settings.get(), "cpu_largepagesize", (u32)os_cpu_LargePageSize()); scriptInterface.SetProperty(settings.get(), "cpu_numprocs", (u32)os_cpu_NumProcessors()); #if ARCH_X86_X64 scriptInterface.SetProperty(settings.get(), "cpu_numpackages", (u32)cpu_topology_NumPackages()); scriptInterface.SetProperty(settings.get(), "cpu_coresperpackage", (u32)cpu_topology_CoresPerPackage()); scriptInterface.SetProperty(settings.get(), "cpu_logicalpercore", (u32)cpu_topology_LogicalPerCore()); scriptInterface.SetProperty(settings.get(), "cpu_numcaches", (u32)cache_topology_NumCaches()); #endif scriptInterface.SetProperty(settings.get(), "numa_numnodes", (u32)numa_NumNodes()); scriptInterface.SetProperty(settings.get(), "numa_factor", numa_Factor()); scriptInterface.SetProperty(settings.get(), "numa_interleaved", numa_IsMemoryInterleaved()); scriptInterface.SetProperty(settings.get(), "ram_total", (u32)os_cpu_MemorySize()); scriptInterface.SetProperty(settings.get(), "ram_total_os", (u32)os_cpu_QueryMemorySize()); scriptInterface.SetProperty(settings.get(), "ram_free", (u32)os_cpu_MemoryAvailable()); #if ARCH_X86_X64 scriptInterface.SetProperty(settings.get(), "x86_frequency", x86_x64_ClockFrequency()); scriptInterface.SetProperty(settings.get(), "x86_vendor", (u32)x86_x64_Vendor()); scriptInterface.SetProperty(settings.get(), "x86_model", (u32)x86_x64_Model()); scriptInterface.SetProperty(settings.get(), "x86_family", (u32)x86_x64_Family()); u32 caps0, caps1, caps2, caps3; x86_x64_caps(&caps0, &caps1, &caps2, &caps3); scriptInterface.SetProperty(settings.get(), "x86_caps[0]", caps0); scriptInterface.SetProperty(settings.get(), "x86_caps[1]", caps1); scriptInterface.SetProperty(settings.get(), "x86_caps[2]", caps2); scriptInterface.SetProperty(settings.get(), "x86_caps[3]", caps3); scriptInterface.SetProperty(settings.get(), "x86_icaches", ConvertCaches(scriptInterface, L1I)); scriptInterface.SetProperty(settings.get(), "x86_dcaches", ConvertCaches(scriptInterface, L1D)); scriptInterface.SetProperty(settings.get(), "x86_tlbs", ConvertTLBs(scriptInterface)); #endif // Send the same data to the reporting system g_UserReporter.SubmitReport("hwdetect", 7, scriptInterface.StringifyJSON(settings.get(), false)); // Run the detection script: scriptInterface.CallFunctionVoid(scriptInterface.GetGlobalObject(), "RunHardwareDetection", settings); } // We use some constants that aren't provided by glext.h on some old systems. // Define all the necessary ones that are missing from GL_GLEXT_VERSION 39 (Mesa 7.0) // since that's probably an old enough baseline: #ifndef GL_VERSION_3_0 # define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 # define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 #endif #ifndef GL_EXT_transform_feedback # define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A # define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B # define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 #endif #ifndef GL_ARB_geometry_shader4 # define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 # define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD # define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE # define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF # define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 # define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 #endif // Also need some more for OS X 10.5: #ifndef GL_EXT_texture_array # define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF #endif static void ReportGLLimits(ScriptInterface& scriptInterface, CScriptValRooted settings) { const char* errstr = "(error)"; #define INTEGER(id) do { \ GLint i = -1; \ glGetIntegerv(GL_##id, &i); \ if (ogl_SquelchError(GL_INVALID_ENUM)) \ scriptInterface.SetProperty(settings.get(), "GL_" #id, errstr); \ else \ scriptInterface.SetProperty(settings.get(), "GL_" #id, i); \ } while (false) #define INTEGER2(id) do { \ GLint i[2] = { -1, -1 }; \ glGetIntegerv(GL_##id, i); \ if (ogl_SquelchError(GL_INVALID_ENUM)) { \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[0]", errstr); \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[1]", errstr); \ } else { \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[0]", i[0]); \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[1]", i[1]); \ } \ } while (false) #define FLOAT(id) do { \ GLfloat f = std::numeric_limits::quiet_NaN(); \ glGetFloatv(GL_##id, &f); \ if (ogl_SquelchError(GL_INVALID_ENUM)) \ scriptInterface.SetProperty(settings.get(), "GL_" #id, errstr); \ else \ scriptInterface.SetProperty(settings.get(), "GL_" #id, f); \ } while (false) #define FLOAT2(id) do { \ GLfloat f[2] = { std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN() }; \ glGetFloatv(GL_##id, f); \ if (ogl_SquelchError(GL_INVALID_ENUM)) { \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[0]", errstr); \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[1]", errstr); \ } else { \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[0]", f[0]); \ scriptInterface.SetProperty(settings.get(), "GL_" #id "[1]", f[1]); \ } \ } while (false) #define STRING(id) do { \ const char* c = (const char*)glGetString(GL_##id); \ if (!c) c = ""; \ if (ogl_SquelchError(GL_INVALID_ENUM)) c = errstr; \ scriptInterface.SetProperty(settings.get(), "GL_" #id, std::string(c)); \ } while (false) #define VERTEXPROGRAM(id) do { \ GLint i = -1; \ pglGetProgramivARB(GL_VERTEX_PROGRAM_ARB, GL_##id, &i); \ if (ogl_SquelchError(GL_INVALID_ENUM)) \ scriptInterface.SetProperty(settings.get(), "GL_VERTEX_PROGRAM_ARB.GL_" #id, errstr); \ else \ scriptInterface.SetProperty(settings.get(), "GL_VERTEX_PROGRAM_ARB.GL_" #id, i); \ } while (false) #define FRAGMENTPROGRAM(id) do { \ GLint i = -1; \ pglGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_##id, &i); \ if (ogl_SquelchError(GL_INVALID_ENUM)) \ scriptInterface.SetProperty(settings.get(), "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, errstr); \ else \ scriptInterface.SetProperty(settings.get(), "GL_FRAGMENT_PROGRAM_ARB.GL_" #id, i); \ } while (false) #define BOOL(id) INTEGER(id) ogl_WarnIfError(); // Core OpenGL 1.3: // (We don't bother checking extension strings for anything older than 1.3; // it'll just produce harmless warnings) STRING(VERSION); STRING(VENDOR); STRING(RENDERER); STRING(EXTENSIONS); INTEGER(MAX_LIGHTS); INTEGER(MAX_CLIP_PLANES); // Skip MAX_COLOR_MATRIX_STACK_DEPTH (only in imaging subset) INTEGER(MAX_MODELVIEW_STACK_DEPTH); INTEGER(MAX_PROJECTION_STACK_DEPTH); INTEGER(MAX_TEXTURE_STACK_DEPTH); INTEGER(SUBPIXEL_BITS); INTEGER(MAX_3D_TEXTURE_SIZE); INTEGER(MAX_TEXTURE_SIZE); INTEGER(MAX_CUBE_MAP_TEXTURE_SIZE); INTEGER(MAX_PIXEL_MAP_TABLE); INTEGER(MAX_NAME_STACK_DEPTH); INTEGER(MAX_LIST_NESTING); INTEGER(MAX_EVAL_ORDER); INTEGER2(MAX_VIEWPORT_DIMS); INTEGER(MAX_ATTRIB_STACK_DEPTH); INTEGER(MAX_CLIENT_ATTRIB_STACK_DEPTH); INTEGER(AUX_BUFFERS); BOOL(RGBA_MODE); BOOL(INDEX_MODE); BOOL(DOUBLEBUFFER); BOOL(STEREO); FLOAT2(ALIASED_POINT_SIZE_RANGE); FLOAT2(SMOOTH_POINT_SIZE_RANGE); FLOAT(SMOOTH_POINT_SIZE_GRANULARITY); FLOAT2(ALIASED_LINE_WIDTH_RANGE); FLOAT2(SMOOTH_LINE_WIDTH_RANGE); FLOAT(SMOOTH_LINE_WIDTH_GRANULARITY); // Skip MAX_CONVOLUTION_WIDTH, MAX_CONVOLUTION_HEIGHT (only in imaging subset) INTEGER(MAX_ELEMENTS_INDICES); INTEGER(MAX_ELEMENTS_VERTICES); INTEGER(MAX_TEXTURE_UNITS); INTEGER(SAMPLE_BUFFERS); INTEGER(SAMPLES); // TODO: compressed texture formats INTEGER(RED_BITS); INTEGER(GREEN_BITS); INTEGER(BLUE_BITS); INTEGER(ALPHA_BITS); INTEGER(INDEX_BITS); INTEGER(DEPTH_BITS); INTEGER(STENCIL_BITS); INTEGER(ACCUM_RED_BITS); INTEGER(ACCUM_GREEN_BITS); INTEGER(ACCUM_BLUE_BITS); INTEGER(ACCUM_ALPHA_BITS); // Core OpenGL 2.0 (treated as extensions): if (ogl_HaveExtension("GL_EXT_texture_lod_bias")) FLOAT(MAX_TEXTURE_LOD_BIAS_EXT); // Skip GL_ARB_occlusion_query's QUERY_COUNTER_BITS_ARB since it'd need GetQueryiv if (ogl_HaveExtension("GL_ARB_shading_language_100")) STRING(SHADING_LANGUAGE_VERSION_ARB); if (ogl_HaveExtension("GL_ARB_vertex_shader")) { INTEGER(MAX_VERTEX_ATTRIBS_ARB); INTEGER(MAX_VERTEX_UNIFORM_COMPONENTS_ARB); INTEGER(MAX_VARYING_FLOATS_ARB); INTEGER(MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB); INTEGER(MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB); } if (ogl_HaveExtension("GL_ARB_fragment_shader")) INTEGER(MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB); if (ogl_HaveExtension("GL_ARB_vertex_shader") || ogl_HaveExtension("GL_ARB_fragment_shader") || ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program")) { INTEGER(MAX_TEXTURE_IMAGE_UNITS_ARB); INTEGER(MAX_TEXTURE_COORDS_ARB); } if (ogl_HaveExtension("GL_ARB_draw_buffers")) INTEGER(MAX_DRAW_BUFFERS_ARB); // Core OpenGL 3.0: if (ogl_HaveExtension("GL_EXT_gpu_shader4")) { INTEGER(MIN_PROGRAM_TEXEL_OFFSET); // no _EXT version of these in glext.h INTEGER(MAX_PROGRAM_TEXEL_OFFSET); } if (ogl_HaveExtension("GL_EXT_framebuffer_object")) { INTEGER(MAX_COLOR_ATTACHMENTS_EXT); INTEGER(MAX_RENDERBUFFER_SIZE_EXT); } if (ogl_HaveExtension("GL_EXT_framebuffer_multisample")) { INTEGER(MAX_SAMPLES_EXT); } if (ogl_HaveExtension("GL_EXT_texture_array")) { INTEGER(MAX_ARRAY_TEXTURE_LAYERS_EXT); } if (ogl_HaveExtension("GL_EXT_transform_feedback")) { INTEGER(MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT); INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT); INTEGER(MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT); } // Other interesting extensions: if (ogl_HaveExtension("GL_EXT_texture_filter_anisotropic")) { FLOAT(MAX_TEXTURE_MAX_ANISOTROPY_EXT); } if (ogl_HaveExtension("GL_ARB_texture_rectangle")) { INTEGER(MAX_RECTANGLE_TEXTURE_SIZE_ARB); } if (ogl_HaveExtension("GL_ARB_vertex_program") || ogl_HaveExtension("GL_ARB_fragment_program")) { INTEGER(MAX_PROGRAM_MATRICES_ARB); INTEGER(MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB); } if (ogl_HaveExtension("GL_ARB_vertex_program")) { VERTEXPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB); VERTEXPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB); VERTEXPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB); VERTEXPROGRAM(MAX_PROGRAM_PARAMETERS_ARB); VERTEXPROGRAM(MAX_PROGRAM_ATTRIBS_ARB); VERTEXPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB); if (ogl_HaveExtension("GL_ARB_fragment_program")) { // The spec seems to say these should be supported, but // Mesa complains about them so let's not bother /* VERTEXPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB); VERTEXPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB); */ } } if (ogl_HaveExtension("GL_ARB_fragment_program")) { FRAGMENTPROGRAM(MAX_PROGRAM_ENV_PARAMETERS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_LOCAL_PARAMETERS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_ALU_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_TEX_INDIRECTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_TEMPORARIES_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_PARAMETERS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_ATTRIBS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_TEMPORARIES_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_PARAMETERS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ATTRIBS_ARB); if (ogl_HaveExtension("GL_ARB_vertex_program")) { // The spec seems to say these should be supported, but // Intel drivers on Windows complain about them so let's not bother /* FRAGMENTPROGRAM(MAX_PROGRAM_ADDRESS_REGISTERS_ARB); FRAGMENTPROGRAM(MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB); */ } } if (ogl_HaveExtension("GL_ARB_geometry_shader4")) { INTEGER(MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB); INTEGER(MAX_GEOMETRY_OUTPUT_VERTICES_ARB); INTEGER(MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB); INTEGER(MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB); INTEGER(MAX_GEOMETRY_VARYING_COMPONENTS_ARB); INTEGER(MAX_VERTEX_VARYING_COMPONENTS_ARB); } } Index: ps/trunk/source/ps/GameSetup/GameSetup.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 9545) +++ ps/trunk/source/ps/GameSetup/GameSetup.cpp (revision 9546) @@ -1,1157 +1,1152 @@ /* Copyright (C) 2011 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "lib/app_hooks.h" #include "lib/input.h" #include "lib/ogl.h" #include "lib/timer.h" #include "lib/external_libraries/sdl.h" #include "lib/file/common/file_stats.h" #include "lib/res/h_mgr.h" #include "lib/res/graphics/cursor.h" #include "lib/res/sound/snd_mgr.h" #include "lib/sysdep/cursor.h" #include "lib/sysdep/cpu.h" #include "lib/sysdep/gfx.h" #include "lib/tex/tex.h" #if OS_WIN #include "lib/sysdep/os/win/wversion.h" #endif #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/ConfigDB.h" #include "ps/Filesystem.h" #include "ps/Font.h" #include "ps/Game.h" #include "ps/Globals.h" #include "ps/Hotkey.h" #include "ps/Joystick.h" #include "ps/Loader.h" #include "ps/Overlay.h" #include "ps/Profile.h" #include "ps/ProfileViewer.h" #include "ps/UserReport.h" #include "ps/Util.h" #include "ps/VideoMode.h" #include "ps/World.h" #include "graphics/CinemaTrack.h" #include "graphics/GameView.h" #include "graphics/LightEnv.h" #include "graphics/MapReader.h" #include "graphics/MaterialManager.h" #include "graphics/TerrainTextureManager.h" #include "renderer/Renderer.h" #include "renderer/VertexBufferManager.h" #include "maths/MathUtil.h" #include "simulation2/Simulation2.h" #include "scripting/ScriptingHost.h" #include "scripting/ScriptGlue.h" #include "scriptinterface/ScriptInterface.h" #include "scriptinterface/ScriptStats.h" #include "maths/scripting/JSInterface_Vector3D.h" #include "ps/scripting/JSInterface_Console.h" #include "gui/GUI.h" #include "gui/GUIManager.h" #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" #include "gui/scripting/ScriptFunctions.h" #include "sound/JSI_Sound.h" #include "network/NetServer.h" #include "network/NetClient.h" #include "ps/Pyrogenesis.h" // psSetLogDir #include "ps/GameSetup/Atlas.h" #include "ps/GameSetup/GameSetup.h" #include "ps/GameSetup/Paths.h" #include "ps/GameSetup/Config.h" #include "ps/GameSetup/CmdLineArgs.h" #include "ps/GameSetup/HWDetect.h" #if !(OS_WIN || OS_MACOSX) // assume all other platforms use X11 for wxWidgets #define MUST_INIT_X11 1 #include #else #define MUST_INIT_X11 0 #endif #include ERROR_GROUP(System); ERROR_TYPE(System, SDLInitFailed); ERROR_TYPE(System, VmodeFailed); ERROR_TYPE(System, RequiredExtensionsMissing); bool g_DoRenderGui = true; bool g_DoRenderLogger = true; bool g_DoRenderCursor = true; static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code static void SetTextureQuality(int quality) { int q_flags; GLint filter; retry: // keep this in sync with SANE_TEX_QUALITY_DEFAULT switch(quality) { // worst quality case 0: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_NEAREST; break; // [perf] add bilinear filtering case 1: q_flags = OGL_TEX_HALF_RES|OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] no longer reduce resolution case 2: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR; break; // [vmem] add mipmaps case 3: q_flags = OGL_TEX_HALF_BPP; filter = GL_NEAREST_MIPMAP_LINEAR; break; // [perf] better filtering case 4: q_flags = OGL_TEX_HALF_BPP; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [vmem] no longer reduce bpp case SANE_TEX_QUALITY_DEFAULT: q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // [perf] add anisotropy case 6: // TODO: add anisotropic filtering q_flags = OGL_TEX_FULL_QUALITY; filter = GL_LINEAR_MIPMAP_LINEAR; break; // invalid default: debug_warn(L"SetTextureQuality: invalid quality"); quality = SANE_TEX_QUALITY_DEFAULT; // careful: recursion doesn't work and we don't want to duplicate // the "sane" default values. goto retry; } ogl_tex_set_defaults(q_flags, filter); } //---------------------------------------------------------------------------- // GUI integration //---------------------------------------------------------------------------- // display progress / description in loading screen void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task) { g_ScriptingHost.GetScriptInterface().SetGlobal("g_Progress", percent, true); g_ScriptingHost.GetScriptInterface().SetGlobal("g_LoadDescription", pending_task, true); g_GUI->SendEventToAll("progress"); } void Render() { ogl_WarnIfError(); CStr skystring = "255 0 255"; CFG_GET_USER_VAL("skycolor", String, skystring); CColor skycol; GUI::ParseString(skystring.FromUTF8(), skycol); g_Renderer.SetClearColor(skycol.AsSColor4ub()); // prepare before starting the renderer frame if (g_Game && g_Game->IsGameStarted()) g_Game->GetView()->BeginFrame(); // start new frame g_Renderer.BeginFrame(); ogl_WarnIfError(); if (g_Game && g_Game->IsGameStarted()) g_Game->GetView()->Render(); ogl_WarnIfError(); // set up overlay mode glPushAttrib(GL_ENABLE_BIT); glEnable(GL_TEXTURE_2D); glDisable(GL_CULL_FACE); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); ogl_WarnIfError(); g_Renderer.RenderTextOverlays(); // Temp GUI message GeeTODO PROFILE_START("render gui"); if(g_DoRenderGui) g_GUI->Draw(); PROFILE_END("render gui"); ogl_WarnIfError(); // Text: // Use the GL_ALPHA texture as the alpha channel with a flat colouring glDisable(GL_ALPHA_TEST); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); // Added -- glEnable(GL_TEXTURE_2D); // -- GL glLoadIdentity(); PROFILE_START("render console"); g_Console->Render(); PROFILE_END("render console"); ogl_WarnIfError(); PROFILE_START("render logger"); if(g_DoRenderLogger) g_Logger->Render(); PROFILE_END("render logger"); ogl_WarnIfError(); // Profile information PROFILE_START("render profiling"); g_ProfileViewer.RenderProfile(); PROFILE_END("render profiling"); ogl_WarnIfError(); // Draw the cursor (or set the Windows cursor, on Windows) if (g_DoRenderCursor) { PROFILE("render cursor"); CStrW cursorName = g_CursorName; if (cursorName.empty()) { cursor_draw(g_VFS, NULL, g_mouse_x, g_yres-g_mouse_y); } else { if (cursor_draw(g_VFS, cursorName.c_str(), g_mouse_x, g_yres-g_mouse_y) < 0) LOGWARNING(L"Failed to draw cursor '%ls'", cursorName.c_str()); } } // restore glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopAttrib(); g_Renderer.EndFrame(); ogl_WarnIfError(); } static void RegisterJavascriptInterfaces() { // maths JSI_Vector3D::init(); // graphics CGameView::ScriptingInit(); // renderer CRenderer::ScriptingInit(); // sound JSI_Sound::ScriptingInit(); // ps JSI_Console::init(); // GUI CGUI::ScriptingInit(); GuiScriptingInit(g_ScriptingHost.GetScriptInterface()); } static void InitScripting() { TIMER(L"InitScripting"); // Create the scripting host. This needs to be done before the GUI is created. // [7ms] new ScriptingHost; RegisterJavascriptInterfaces(); } static size_t OperatingSystemFootprint() { #if OS_WIN switch(wversion_Number()) { case WVERSION_2K: case WVERSION_XP: return 150; case WVERSION_XP64: return 200; default: // newer Windows version: assume the worst, and don't warn case WVERSION_VISTA: return 300; case WVERSION_7: return 250; } #else return 200; #endif } static size_t ChooseCacheSize() { // (all sizes in MiB and signed to allow temporarily negative computations) const ssize_t total = (ssize_t)os_cpu_MemorySize(); // (NB: os_cpu_MemoryAvailable is useless on Linux because free memory // is marked as "in use" by OS caches.) const ssize_t os = (ssize_t)OperatingSystemFootprint(); const ssize_t game = 300; // estimated working set ssize_t cache = 500; // upper bound: total size of our data // the cache reserves contiguous address space, which is a precious // resource on 32-bit systems, so don't use too much: if(ARCH_IA32 || sizeof(void*) == 4) cache = std::min(cache, (ssize_t)200); // try to leave over enough memory for the OS and game cache = std::min(cache, total-os-game); // always provide at least this much to ensure correct operation cache = std::max(cache, (ssize_t)64); debug_printf(L"Cache: %d (total: %d) MiB\n", (int)cache, (int)total); return size_t(cache)*MiB; } ErrorReactionInternal psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED(flags)) { // If we're fullscreen, then sometimes (at least on some particular drivers on Linux) // displaying the error dialog hangs the desktop since the dialog box is behind the // fullscreen window. So we just force the game to windowed mode before displaying the dialog. // (But only if we're in the main thread, and not if we're being reentrant.) if (ThreadUtil::IsMainThread()) { static bool reentering = false; if (!reentering) { reentering = true; g_VideoMode.SetFullscreen(false); reentering = false; } } // We don't actually implement the error display here, so return appropriately return ERI_NOT_IMPLEMENTED; } static void InitVfs(const CmdLineArgs& args) { TIMER(L"InitVfs"); const Paths paths(args); OsPath logs(paths.Logs()); CreateDirectories(logs, 0700); psSetLogDir(logs); // desired location for crashlog is now known. update AppHooks ASAP // (particularly before the following error-prone operations): AppHooks hooks = {0}; hooks.bundle_logs = psBundleLogs; hooks.get_log_dir = psLogDir; hooks.display_error = psDisplayError; app_hooks_update(&hooks); const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); g_VFS->Mount(L"screenshots/", paths.Data()/"screenshots"/""); const OsPath readonlyConfig = paths.RData()/"config"/""; g_VFS->Mount(L"config/", readonlyConfig); if(readonlyConfig != paths.Config()) g_VFS->Mount(L"config/", paths.Config()); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads) std::vector mods = args.GetMultiple("mod"); mods.push_back("public"); if(!args.Has("onlyPublicFiles")) mods.push_back("internal"); OsPath modArchivePath = paths.Cache()/"mods"; OsPath modLoosePath = paths.RData()/"mods"; for (size_t i = 0; i < mods.size(); ++i) { size_t priority = i; size_t flags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_MUST_EXIST; OsPath modName(mods[i]); g_VFS->Mount(L"", modLoosePath / modName/"", flags, priority); g_VFS->Mount(L"", modArchivePath / modName/"", flags, priority); } // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. } static void InitPs(bool setup_gui, const CStrW& gui_page, CScriptVal initData) { { // console TIMER(L"ps_console"); g_Console->UpdateScreenSize(g_xres, g_yres); // Calculate and store the line spacing CFont font(CONSOLE_FONT); g_Console->m_iFontHeight = font.GetLineSpacing(); g_Console->m_iFontWidth = font.GetCharacterWidth(L'C'); g_Console->m_charsPerPage = (size_t)(g_xres / g_Console->m_iFontWidth); // Offset by an arbitrary amount, to make it fit more nicely g_Console->m_iFontOffset = 7; } // hotkeys { TIMER(L"ps_lang_hotkeys"); LoadHotkeys(); } if (!setup_gui) { // We do actually need *some* kind of GUI loaded, so use the // (currently empty) Atlas one g_GUI->SwitchPage(L"page_atlas.xml", initData); return; } // GUI uses VFS, so this must come after VFS init. g_GUI->SwitchPage(gui_page, initData); } static void InitInput() { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); g_Joystick.Initialise(); // register input handlers // This stack is constructed so the first added, will be the last // one called. This is important, because each of the handlers // has the potential to block events to go further down // in the chain. I.e. the last one in the list added, is the // only handler that can block all messages before they are // processed. in_add_handler(game_view_handler); in_add_handler(CProfileViewer::InputThunk); in_add_handler(conInputHandler); in_add_handler(HotkeyInputHandler); // gui_handler needs to be registered after (i.e. called before!) the // hotkey handler so that input boxes can be typed in without // setting off hotkeys. in_add_handler(gui_handler); // must be registered after (called before) the GUI which relies on these globals in_add_handler(GlobalsInputHandler); } static void ShutdownPs() { SAFE_DELETE(g_GUI); SAFE_DELETE(g_Console); // disable the special Windows cursor, or free textures for OGL cursors cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y); } static void InitRenderer() { TIMER(L"InitRenderer"); if(g_NoGLS3TC) ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE); if(g_NoGLAutoMipmap) ogl_tex_override(OGL_TEX_AUTO_MIPMAP_GEN, OGL_TEX_DISABLE); // create renderer new CRenderer; // set renderer options from command line options - NOVBO must be set before opening the renderer g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO); g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows); g_Renderer.SetOptionBool(CRenderer::OPT_FANCYWATER,g_FancyWater); g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath)); g_Renderer.SetOptionFloat(CRenderer::OPT_LODBIAS, g_LodBias); // create terrain related stuff new CTerrainTextureManager; // create the material manager new CMaterialManager; g_Renderer.Open(g_xres,g_yres); // Setup lighting environment. Since the Renderer accesses the // lighting environment through a pointer, this has to be done before // the first Frame. g_Renderer.SetLightEnv(&g_LightEnv); // I haven't seen the camera affecting GUI rendering and such, but the // viewport has to be updated according to the video mode SViewPort vp; vp.m_X=0; vp.m_Y=0; vp.m_Width=g_xres; vp.m_Height=g_yres; g_Renderer.SetViewport(vp); ColorActivateFastImpl(); } static void InitSDL() { if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER|SDL_INIT_NOPARACHUTE) < 0) { LOGERROR(L"SDL library initialization failed: %hs", SDL_GetError()); throw PSERROR_System_SDLInitFailed(); } atexit(SDL_Quit); SDL_EnableUNICODE(1); } static void ShutdownSDL() { SDL_Quit(); sys_cursor_reset(); } void EndGame() { SAFE_DELETE(g_NetServer); SAFE_DELETE(g_NetClient); SAFE_DELETE(g_Game); } void Shutdown(int UNUSED(flags)) { EndGame(); ShutdownPs(); // Must delete g_GUI before g_ScriptingHost in_reset_handlers(); // destroy actor related stuff TIMER_BEGIN(L"shutdown actor stuff"); delete &g_MaterialManager; TIMER_END(L"shutdown actor stuff"); // destroy terrain related stuff TIMER_BEGIN(L"shutdown TexMan"); delete &g_TexMan; TIMER_END(L"shutdown TexMan"); // destroy renderer TIMER_BEGIN(L"shutdown Renderer"); delete &g_Renderer; g_VBMan.Shutdown(); TIMER_END(L"shutdown Renderer"); tex_codec_unregister_all(); TIMER_BEGIN(L"shutdown SDL"); ShutdownSDL(); TIMER_END(L"shutdown SDL"); g_VideoMode.Shutdown(); TIMER_BEGIN(L"shutdown UserReporter"); g_UserReporter.Deinitialize(); TIMER_END(L"shutdown UserReporter"); TIMER_BEGIN(L"shutdown ScriptingHost"); delete &g_ScriptingHost; TIMER_END(L"shutdown ScriptingHost"); TIMER_BEGIN(L"shutdown ConfigDB"); delete &g_ConfigDB; TIMER_END(L"shutdown ConfigDB"); // resource // first shut down all resource owners, and then the handle manager. TIMER_BEGIN(L"resource modules"); snd_shutdown(); g_VFS.reset(); // this forcibly frees all open handles (thus preventing real leaks), // and makes further access to h_mgr impossible. h_mgr_shutdown(); file_stats_dump(); TIMER_END(L"resource modules"); TIMER_BEGIN(L"shutdown misc"); timer_DisplayClientTotals(); CNetHost::Deinitialize(); SAFE_DELETE(g_ScriptStatsTable); // should be last, since the above use them SAFE_DELETE(g_Logger); delete &g_Profiler; delete &g_ProfileViewer; TIMER_END(L"shutdown misc"); } #if OS_UNIX void SetDefaultIfLocaleInvalid() { // On misconfigured systems with incorrect locale settings, we'll die // with a C++ exception when some code tries to use locales. // To avoid death, we'll detect the problem here and warn the user and // reset to the default C locale. // For informing the user of the problem, use the list of env vars that // glibc setlocale looks at. (LC_ALL is checked first, and LANG last.) const char* const LocaleEnvVars[] = { "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", "LANG" }; try { // this constructor is similar to setlocale(LC_ALL, ""), // but instead of returning NULL, it throws runtime_error // when the first locale env variable found contains an invalid value std::locale(""); } catch (std::runtime_error&) { LOGWARNING(L"Invalid locale settings"); for (size_t i = 0; i < ARRAY_SIZE(LocaleEnvVars); i++) { if (char* envval = getenv(LocaleEnvVars[i])) LOGWARNING(L" %hs=\"%hs\"", LocaleEnvVars[i], envval); else LOGWARNING(L" %hs=\"(unset)\"", LocaleEnvVars[i]); } // We should set LC_ALL since it overrides LANG if (setenv("LC_ALL", std::locale::classic().name().c_str(), 1)) debug_warn(L"Invalid locale settings, and unable to set LC_ALL env variable."); else LOGWARNING(L"Setting LC_ALL env variable to: %hs", getenv("LC_ALL")); } } #else void SetDefaultIfLocaleInvalid() { // Do nothing on Windows } #endif void EarlyInit() { // If you ever want to catch a particular allocation: //_CrtSetBreakAlloc(232647); ThreadUtil::SetMainThread(); debug_SetThreadName("main"); // add all debug_printf "tags" that we are interested in: debug_filter_add(L"TIMER"); cpu_ConfigureFloatingPoint(); timer_LatchStartTime(); SetDefaultIfLocaleInvalid(); // Because we do GL calls from a secondary thread, Xlib needs to // be told to support multiple threads safely. // This is needed for Atlas, but we have to call it before any other // Xlib functions (e.g. the ones used when drawing the main menu // before launching Atlas) #if MUST_INIT_X11 int status = XInitThreads(); if (status == 0) debug_printf(L"Error enabling thread-safety via XInitThreads\n"); #endif // Initialise the low-quality rand function srand(time(NULL)); // NOTE: this rand should *not* be used for simulation! } bool Autostart(const CmdLineArgs& args); void Init(const CmdLineArgs& args, int UNUSED(flags)) { h_mgr_init(); // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. InitVfs(args); // This must come after VFS init, which sets the current directory // (required for finding our output log files). g_Logger = new CLogger; // Special command-line mode to dump the entity schemas instead of running the game. // (This must be done after loading VFS etc, but should be done before wasting time // on anything else.) if (args.Has("dumpSchema")) { CSimulation2 sim(NULL, NULL); sim.LoadDefaultScripts(); std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc); f << sim.GenerateSchema(); std::cout << "Generated entity.rng\n"; exit(0); } // override ah_translate with our i18n code. AppHooks hooks = {0}; hooks.translate = psTranslate; hooks.translate_free = psTranslateFree; app_hooks_update(&hooks); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); CNetHost::Initialize(); new CProfileViewer; new CProfileManager; // before any script code g_ScriptStatsTable = new CScriptStatsTable; g_ProfileViewer.AddRootTable(g_ScriptStatsTable); InitScripting(); // before GUI // g_ConfigDB, command line args, globals CONFIG_Init(args); if (!g_Quickstart) g_UserReporter.Initialize(); // after config } void InitGraphics(const CmdLineArgs& args, int flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; if(setup_vmode) { InitSDL(); if (!g_VideoMode.InitSDL()) throw PSERROR_System_VmodeFailed(); // abort startup SDL_WM_SetCaption("0 A.D.", "0 A.D."); } - // needed by ogl_tex to detect broken gfx card/driver combos, - // but takes a while due to WMI startup, so make it optional. - if(!g_Quickstart) - gfx_detect(); - RunHardwareDetection(); tex_codec_register_all(); const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); ogl_WarnIfError(); if(!g_Quickstart) { WriteSystemInfo(); // note: no longer vfs_display here. it's dog-slow due to unbuffered // file output and very rarely needed. } if(g_DisableAudio) { // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. snd_disable(true); } g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); // (must come after SetVideoMode, since it calls ogl_Init) const char* missing = ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", NULL); if(missing) { wchar_t buf[500]; swprintf_s(buf, ARRAY_SIZE(buf), L"The %hs extension doesn't appear to be available on your computer." L" The game may still work, though - you are welcome to try at your own risk." L" If not or it doesn't look right, upgrade your graphics card.", missing ); DEBUG_DISPLAY_ERROR(buf); // TODO: i18n } if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar")) { DEBUG_DISPLAY_ERROR( L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer." L" Shadows are not available and overall graphics quality might suffer." L" You are advised to try installing newer drivers and/or upgrade your graphics card."); g_Shadows = false; } ogl_WarnIfError(); InitRenderer(); InitInput(); ogl_WarnIfError(); try { if (!Autostart(args)) { const bool setup_gui = ((flags & INIT_NO_GUI) == 0); InitPs(setup_gui, L"page_pregame.xml", JSVAL_VOID); } } catch (PSERROR_Game_World_MapLoadFailed e) { // Map Loading failed // Start the engine so we have a GUI InitPs(true, L"page_pregame.xml", JSVAL_VOID); // Call script function to do the actual work // (delete game data, switch GUI page, show error, etc.) CancelLoad(CStr(e.what()).FromUTF8()); } } void RenderGui(bool RenderingState) { g_DoRenderGui = RenderingState; } void RenderLogger(bool RenderingState) { g_DoRenderLogger = RenderingState; } void RenderCursor(bool RenderingState) { g_DoRenderCursor = RenderingState; } bool Autostart(const CmdLineArgs& args) { /* * Handle various command-line options, for quick testing of various features: * -autostart=name -- map name for scenario, or rms name for random map * -autostart-playername=name -- multiplayer player name * -autostart-host -- multiplayer host mode * -autostart-players=2 -- number of players * -autostart-client -- multiplayer client mode * -autostart-ip=127.0.0.1 -- multiplayer connect to 127.0.0.1 * -autostart-random=104 -- random map, optional seed value = 104 (default is 0, random is -1) * -autostart-size=192 -- random map size in tiles = 192 (default is 192) * * Examples: * -autostart=Acropolis -autostart-host -autostart-players=2 -- Host game on Acropolis map, 2 players * -autostart=latium -autostart-random=-1 -- Start single player game on latium random map, random rng seed */ CStr autoStartName = args.Get("autostart"); if (autoStartName.empty()) { return false; } g_Game = new CGame(); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); CScriptValRooted attrs; scriptInterface.Eval("({})", attrs); CScriptVal settings; scriptInterface.Eval("({})", settings); CScriptVal playerData; scriptInterface.Eval("([])", playerData); // Set different attributes for random or scenario game if (args.Has("autostart-random")) { CStr seedArg = args.Get("autostart-random"); // Default seed is 0 uint32 seed = 0; if (!seedArg.empty()) { if (seedArg.compare("-1") == 0) { // Random seed value seed = rand(); } else { seed = seedArg.ToULong(); } } // Random map definition will be loaded from JSON file, so we need to parse it std::wstring scriptPath = L"maps/random/" + autoStartName.FromUTF8() + L".json"; CScriptValRooted scriptData = scriptInterface.ReadJSONFile(scriptPath); if (!scriptData.undefined() && scriptInterface.GetProperty(scriptData.get(), "settings", settings)) { // JSON loaded ok - copy script name over to game attributes std::wstring scriptFile; scriptInterface.GetProperty(settings.get(), "Script", scriptFile); scriptInterface.SetProperty(attrs.get(), "script", scriptFile); // RMS filename } else { // Problem with JSON file LOGERROR(L"Error reading random map script '%ls'", scriptPath.c_str()); throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details."); } // Get optional map size argument (default 192) uint mapSize = 192; if (args.Has("autostart-size")) { CStr size = args.Get("autostart-size"); mapSize = size.ToUInt(); } scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random")); scriptInterface.SetProperty(settings.get(), "Seed", seed); // Random seed scriptInterface.SetProperty(settings.get(), "Size", mapSize); // Random map size (in patches) // Get optional number of players (default 2) size_t numPlayers = 2; if (args.Has("autostart-players")) { CStr num = args.Get("autostart-players"); numPlayers = num.ToUInt(); } // Set up player data for (size_t i = 0; i < numPlayers; ++i) { CScriptVal player; scriptInterface.Eval("({})", player); scriptInterface.SetProperty(player.get(), "Civ", std::string("hele")); scriptInterface.SetPropertyInt(playerData.get(), i, player); } } else { scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName)); scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario")); } // Set player data for AIs // attrs.settings = { PlayerData: [ { AI: ... }, ... ] }: /* * Handle command-line options for AI: * -autostart-ai=1:dummybot -autostart-ai=2:dummybot -- adds the dummybot AI to players 1 and 2 */ if (args.Has("autostart-ai")) { std::vector aiArgs = args.GetMultiple("autostart-ai"); for (size_t i = 0; i < aiArgs.size(); ++i) { CScriptVal player; scriptInterface.Eval("({})", player); int playerID = aiArgs[i].BeforeFirst(":").ToInt(); CStr name = aiArgs[i].AfterFirst(":"); scriptInterface.SetProperty(player.get(), "AI", std::string(name)); scriptInterface.SetPropertyInt(playerData.get(), playerID-1, player); } } // Add player data to map settings scriptInterface.SetProperty(settings.get(), "PlayerData", playerData); // Add map settings to game attributes scriptInterface.SetProperty(attrs.get(), "settings", settings); CScriptVal mpInitData; g_GUI->GetScriptInterface().Eval("({isNetworked:true, playerAssignments:{}})", mpInitData); g_GUI->GetScriptInterface().SetProperty(mpInitData.get(), "attribs", CScriptVal(g_GUI->GetScriptInterface().CloneValueFromOtherContext(scriptInterface, attrs.get()))); // Get optional playername CStrW userName = L"anonymous"; if (args.Has("autostart-playername")) { userName = args.Get("autostart-playername").FromUTF8(); } if (args.Has("autostart-host")) { InitPs(true, L"page_loading.xml", mpInitData.get()); size_t maxPlayers = 2; if (args.Has("autostart-players")) { maxPlayers = args.Get("autostart-players").ToUInt(); } g_NetServer = new CNetServer(maxPlayers); g_NetServer->UpdateGameAttributes(attrs.get(), scriptInterface); bool ok = g_NetServer->SetupConnection(); ENSURE(ok); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); g_NetClient->SetupConnection("127.0.0.1"); } else if (args.Has("autostart-client")) { InitPs(true, L"page_loading.xml", mpInitData.get()); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); CStr ip = "127.0.0.1"; if (args.Has("autostart-ip")) { ip = args.Get("autostart-ip"); } bool ok = g_NetClient->SetupConnection(ip); ENSURE(ok); } else { g_Game->SetPlayerID(1); g_Game->StartGame(attrs); LDR_NonprogressiveLoad(); PSRETURN ret = g_Game->ReallyStartGame(); ENSURE(ret == PSRETURN_OK); InitPs(true, L"page_session.xml", JSVAL_VOID); } return true; } void CancelLoad(const CStrW& message) { //Cancel loader LDR_Cancel(); // Call the cancelOnError GUI function, defined in ..gui/common/functions_utility_error.js // So all GUI pages that load games should include this script if (g_GUI && g_GUI->HasPages()) { JSContext* cx = g_ScriptingHost.getContext(); jsval fval, rval; JSBool ok = JS_GetProperty(cx, g_GUI->GetScriptObject(), "cancelOnError", &fval); ENSURE(ok); jsval msgval = ToJSVal(message); if (ok && !JSVAL_IS_VOID(fval)) ok = JS_CallFunctionValue(cx, g_GUI->GetScriptObject(), fval, 1, &msgval, &rval); } } Index: ps/trunk/source/lib/ogl.cpp =================================================================== --- ps/trunk/source/lib/ogl.cpp (revision 9545) +++ ps/trunk/source/lib/ogl.cpp (revision 9546) @@ -1,491 +1,467 @@ /* Copyright (c) 2011 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * OpenGL helper functions. */ #include "precompiled.h" #include "lib/ogl.h" #include #include #include #include "lib/external_libraries/sdl.h" #include "lib/debug.h" #include "lib/sysdep/gfx.h" #include "lib/res/h_mgr.h" #if MSC_VERSION #pragma comment(lib, "opengl32.lib") #endif //---------------------------------------------------------------------------- // extensions //---------------------------------------------------------------------------- // define extension function pointers extern "C" { #define FUNC(ret, name, params) ret (GL_CALL_CONV *p##name) params; #define FUNC2(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameARB) params; #define FUNC3(ret, nameARB, nameCore, version, params) ret (GL_CALL_CONV *p##nameCore) params; #include "lib/external_libraries/glext_funcs.h" #undef FUNC3 #undef FUNC2 #undef FUNC } static const char* exts = NULL; static bool have_30, have_21, have_20, have_15, have_14, have_13, have_12; // return a C string of unspecified length containing a space-separated // list of all extensions the OpenGL implementation advertises. // (useful for crash logs). const char* ogl_ExtensionString() { ENSURE(exts && "call ogl_Init before using this function"); return exts; } // paranoia: newer drivers may forget to advertise an extension // indicating support for something that has been folded into the core. // we therefore check for all extensions known to be offered by the // GL implementation present on the user's system; ogl_HaveExtension will // take this into account. // the app can therefore just ask for extensions and not worry about this. static bool isImplementedInCore(const char* ext) { #define MATCH(known_ext)\ if(!strcmp(ext, #known_ext))\ return true; if(have_30) { MATCH(GL_EXT_gpu_shader4); MATCH(GL_NV_conditional_render); MATCH(GL_ARB_color_buffer_float); MATCH(GL_ARB_depth_buffer_float); MATCH(GL_ARB_texture_float); MATCH(GL_EXT_packed_float); MATCH(GL_EXT_texture_shared_exponent); MATCH(GL_EXT_framebuffer_object); MATCH(GL_NV_half_float); MATCH(GL_ARB_half_float_pixel); MATCH(GL_EXT_framebuffer_multisample); MATCH(GL_EXT_framebuffer_blit); MATCH(GL_EXT_texture_integer); MATCH(GL_EXT_texture_array); MATCH(GL_EXT_packed_depth_stencil); MATCH(GL_EXT_draw_buffers2); MATCH(GL_EXT_texture_compression_rgtc); MATCH(GL_EXT_transform_feedback); MATCH(GL_APPLE_vertex_array_object); MATCH(GL_EXT_framebuffer_sRGB); } if(have_21) { MATCH(GL_ARB_pixel_buffer_object); MATCH(GL_EXT_texture_sRGB); } if(have_20) { MATCH(GL_ARB_shader_objects); MATCH(GL_ARB_vertex_shader); MATCH(GL_ARB_fragment_shader); MATCH(GL_ARB_shading_language_100); MATCH(GL_ARB_draw_buffers); MATCH(GL_ARB_texture_non_power_of_two); MATCH(GL_ARB_point_sprite); MATCH(GL_EXT_blend_equation_separate); } if(have_15) { MATCH(GL_ARB_vertex_buffer_object); MATCH(GL_ARB_occlusion_query); MATCH(GL_EXT_shadow_funcs); } if(have_14) { MATCH(GL_SGIS_generate_mipmap); MATCH(GL_NV_blend_square); MATCH(GL_ARB_depth_texture); MATCH(GL_ARB_shadow); MATCH(GL_EXT_fog_coord); MATCH(GL_EXT_multi_draw_arrays); MATCH(GL_ARB_point_parameters); MATCH(GL_EXT_secondary_color); MATCH(GL_EXT_blend_func_separate); MATCH(GL_EXT_stencil_wrap); MATCH(GL_ARB_texture_env_crossbar); MATCH(GL_EXT_texture_lod_bias); MATCH(GL_ARB_texture_mirrored_repeat); MATCH(GL_ARB_window_pos); // These extensions were added to GL 1.2, but as part of the optional // imaging subset; they're only guaranteed as of GL 1.4: MATCH(GL_EXT_blend_color); MATCH(GL_EXT_blend_minmax); MATCH(GL_EXT_blend_subtract); } if(have_13) { MATCH(GL_ARB_texture_compression); MATCH(GL_ARB_texture_cube_map); MATCH(GL_ARB_multisample); MATCH(GL_ARB_multitexture); MATCH(GL_ARB_transpose_matrix); MATCH(GL_ARB_texture_env_add); MATCH(GL_ARB_texture_env_combine); MATCH(GL_ARB_texture_env_dot3); MATCH(GL_ARB_texture_border_clamp); } if(have_12) { MATCH(GL_EXT_texture3D); MATCH(GL_EXT_bgra); MATCH(GL_EXT_packed_pixels); MATCH(GL_EXT_rescale_normal); MATCH(GL_EXT_separate_specular_color); MATCH(GL_SGIS_texture_edge_clamp); MATCH(GL_SGIS_texture_lod); MATCH(GL_EXT_draw_range_elements); // Skip the extensions that only affect the imaging subset } #undef MATCH return false; } // check if the extension is supported by the OpenGL implementation. // takes subsequently added core support for some extensions into account. bool ogl_HaveExtension(const char* ext) { ENSURE(exts && "call ogl_Init before using this function"); if(isImplementedInCore(ext)) return true; const char *p = exts, *end; // make sure ext is valid & doesn't contain spaces if(!ext || ext == '\0' || strchr(ext, ' ')) return false; for(;;) { p = strstr(p, ext); if(!p) return false; // string not found - extension not supported end = p + strlen(ext); // end of current substring // make sure the substring found is an entire extension string, // i.e. it starts and ends with ' ' if((p == exts || p[-1] == ' ') && // valid start AND (*end == ' ' || *end == '\0')) // valid end return true; p = end; } } // check if the OpenGL implementation is at least at . // (format: "%d.%d" major minor) bool ogl_HaveVersion(const char* desired_version) { int desired_major, desired_minor; if(sscanf_s(desired_version, "%d.%d", &desired_major, &desired_minor) != 2) { DEBUG_WARN_ERR(ERR::LOGIC); // invalid version string return false; } // guaranteed to be of the form major.minor[.release]. const char* version = (const char*)glGetString(GL_VERSION); int major, minor; if(!version || sscanf_s(version, "%d.%d", &major, &minor) != 2) { DEBUG_WARN_ERR(ERR::LOGIC); // GL_VERSION invalid return false; } // note: don't just compare characters - major and minor may be >= 10. return (major > desired_major) || (major == desired_major && minor >= desired_minor); } // check if all given extension strings (passed as const char* parameters, // terminated by a 0 pointer) are supported by the OpenGL implementation, // as determined by ogl_HaveExtension. // returns 0 if all are present; otherwise, the first extension in the // list that's not supported (useful for reporting errors). // // note: dummy parameter is necessary to access parameter va_list. // // // rationale: this interface is more convenient than individual // ogl_HaveExtension calls and allows reporting which extension is missing. // // one disadvantage is that there is no way to indicate that either one // of 2 extensions would be acceptable, e.g. (ARB|EXT)_texture_env_dot3. // this is isn't so bad, since they wouldn't be named differently // if there weren't non-trivial changes between them. for that reason, // we refrain from equivalence checks (which would boil down to // string-matching known extensions to their equivalents). const char* ogl_HaveExtensions(int dummy, ...) { const char* ext; va_list args; va_start(args, dummy); for(;;) { ext = va_arg(args, const char*); // end of list reached; all were present => return 0. if(!ext) break; // not found => return name of missing extension. if(!ogl_HaveExtension(ext)) break; } va_end(args); return ext; } // to help when running with no hardware acceleration and only OpenGL 1.1 // (e.g. testing the game in virtual machines), we define dummy versions of // some extension functions which our graphics code assumes exist. // it will render incorrectly but at least it shouldn't crash. static void GL_CALL_CONV dummy_glDrawRangeElementsEXT(GLenum mode, GLuint, GLuint, GLsizei count, GLenum type, GLvoid* indices) { glDrawElements(mode, count, type, indices); } static void GL_CALL_CONV dummy_glActiveTextureARB(int) { } static void GL_CALL_CONV dummy_glClientActiveTextureARB(int) { } static void GL_CALL_CONV dummy_glMultiTexCoord2fARB(int, float s, float t) { glTexCoord2f(s, t); } static void GL_CALL_CONV dummy_glMultiTexCoord3fARB(int, float s, float t, float r) { glTexCoord3f(s, t, r); } static void importExtensionFunctions() { // It should be safe to load the ARB function pointers even if the // extension isn't advertised, since we won't actually use them without // checking for the extension. // (TODO: this calls ogl_HaveVersion far more times than is necessary - // we should probably use the have_* variables instead) #define FUNC(ret, name, params) p##name = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#name); #define FUNC23(pname, ret, nameARB, nameCore, version, params) \ pname = NULL; \ if(ogl_HaveVersion(version)) \ pname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameCore); \ if(!pname) /* use the ARB name if the driver lied about what version it supports */ \ pname = (ret (GL_CALL_CONV*) params)SDL_GL_GetProcAddress(#nameARB); #define FUNC2(ret, nameARB, nameCore, version, params) FUNC23(p##nameARB, ret, nameARB, nameCore, version, params) #define FUNC3(ret, nameARB, nameCore, version, params) FUNC23(p##nameCore, ret, nameARB, nameCore, version, params) #include "lib/external_libraries/glext_funcs.h" #undef FUNC3 #undef FUNC2 #undef FUNC23 #undef FUNC // fall back to the dummy functions when extensions (or equivalent core support) are missing if(!ogl_HaveExtension("GL_EXT_draw_range_elements")) { pglDrawRangeElementsEXT = &dummy_glDrawRangeElementsEXT; } if(!ogl_HaveExtension("GL_ARB_multitexture")) { pglActiveTextureARB = &dummy_glActiveTextureARB; pglClientActiveTextureARB = &dummy_glClientActiveTextureARB; pglMultiTexCoord2fARB = &dummy_glMultiTexCoord2fARB; pglMultiTexCoord3fARB = &dummy_glMultiTexCoord3fARB; } } //---------------------------------------------------------------------------- static void dump_gl_error(GLenum err) { debug_printf(L"OGL| "); #define E(e) case e: debug_printf(L"%ls\n", WIDEN(#e)); break; switch (err) { E(GL_INVALID_ENUM) E(GL_INVALID_VALUE) E(GL_INVALID_OPERATION) E(GL_STACK_OVERFLOW) E(GL_STACK_UNDERFLOW) E(GL_OUT_OF_MEMORY) E(GL_INVALID_FRAMEBUFFER_OPERATION_EXT) default: debug_printf(L"Unknown GL error: %04x\n", err); break; } #undef E } #ifndef ogl_WarnIfError // don't include this function if it has been defined (in ogl.h) as a no-op void ogl_WarnIfError() { // glGetError may return multiple errors, so we poll it in a loop. // the debug_printf should only happen once (if this is set), though. bool error_enountered = false; GLenum first_error = 0; for(;;) { GLenum err = glGetError(); if(err == GL_NO_ERROR) break; if(!error_enountered) first_error = err; error_enountered = true; dump_gl_error(err); } if(error_enountered) debug_printf(L"OpenGL error(s) occurred: %04x\n", (int)first_error); } #endif // ignore and reset the specified error (as returned by glGetError). // any other errors that have occurred are reported as ogl_WarnIfError would. // // this is useful for suppressing annoying error messages, e.g. // "invalid enum" for GL_CLAMP_TO_EDGE even though we've already // warned the user that their OpenGL implementation is too old. bool ogl_SquelchError(GLenum err_to_ignore) { // glGetError may return multiple errors, so we poll it in a loop. // the debug_printf should only happen once (if this is set), though. bool error_enountered = false; bool error_ignored = false; GLenum first_error = 0; for(;;) { GLenum err = glGetError(); if(err == GL_NO_ERROR) break; if(err == err_to_ignore) { error_ignored = true; continue; } if(!error_enountered) first_error = err; error_enountered = true; dump_gl_error(err); } if(error_enountered) debug_printf(L"OpenGL error(s) occurred: %04x\n", (int)first_error); return error_ignored; } //---------------------------------------------------------------------------- // feature and limit detect //---------------------------------------------------------------------------- GLint ogl_max_tex_size = -1; // [pixels] GLint ogl_max_tex_units = -1; // limit on GL_TEXTUREn - -// set sysdep/gfx.h gfx_card and gfx_drv_ver. called by gfx_detect. -// -// fails if OpenGL not ready for use. -// gfx_card and gfx_drv_ver are unchanged on failure. -Status ogl_get_gfx_info() -{ - const char* vendor = (const char*)glGetString(GL_VENDOR); - const char* renderer = (const char*)glGetString(GL_RENDERER); - const char* version = (const char*)glGetString(GL_VERSION); - // can fail if OpenGL not yet initialized, - // or if called between glBegin and glEnd. - if(!vendor || !renderer || !version) - WARN_RETURN(ERR::AGAIN); - - swprintf_s(gfx_card, ARRAY_SIZE(gfx_card), L"%hs %hs", vendor, renderer); - - // add "OpenGL" to differentiate this from the real driver version - // (returned by platform-specific detect routines). - swprintf_s(gfx_drv_ver, ARRAY_SIZE(gfx_drv_ver), L"OpenGL %hs", version); - return INFO::OK; -} - - // call after each video mode change, since thereafter extension functions // may have changed [address]. void ogl_Init() { // cache extension list and versions for oglHave*. // note: this is less about performance (since the above are not // time-critical) than centralizing the 'OpenGL is ready' check. exts = (const char*)glGetString(GL_EXTENSIONS); ENSURE(exts); // else: called before OpenGL is ready for use have_12 = ogl_HaveVersion("1.2"); have_13 = ogl_HaveVersion("1.3"); have_14 = ogl_HaveVersion("1.4"); have_15 = ogl_HaveVersion("1.5"); have_20 = ogl_HaveVersion("2.0"); have_21 = ogl_HaveVersion("2.1"); have_30 = ogl_HaveVersion("3.0"); importExtensionFunctions(); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &ogl_max_tex_size); glGetIntegerv(GL_MAX_TEXTURE_UNITS, &ogl_max_tex_units); } Index: ps/trunk/source/lib/res/graphics/ogl_tex.cpp =================================================================== --- ps/trunk/source/lib/res/graphics/ogl_tex.cpp (revision 9545) +++ ps/trunk/source/lib/res/graphics/ogl_tex.cpp (revision 9546) @@ -1,1068 +1,1066 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * wrapper for all OpenGL texturing calls. provides caching, hotloading * and lifetime management. */ #include "precompiled.h" #include "ogl_tex.h" #include #include "lib/app_hooks.h" #include "lib/ogl.h" #include "lib/bits.h" #include "lib/sysdep/gfx.h" #include "lib/tex/tex.h" #include "lib/res/h_mgr.h" #include "lib/fnv_hash.h" //---------------------------------------------------------------------------- // OpenGL helper routines //---------------------------------------------------------------------------- static bool filter_valid(GLint filter) { switch(filter) { case GL_NEAREST: case GL_LINEAR: case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: return true; default: return false; } } static bool wrap_valid(GLint wrap) { switch(wrap) { case GL_CLAMP: case GL_CLAMP_TO_EDGE: case GL_CLAMP_TO_BORDER: case GL_REPEAT: case GL_MIRRORED_REPEAT: return true; default: return false; } } static bool filter_uses_mipmaps(GLint filter) { switch(filter) { case GL_NEAREST_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: case GL_LINEAR_MIPMAP_LINEAR: return true; default: return false; } } static bool fmt_is_s3tc(GLenum fmt) { switch(fmt) { case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return true; default: return false; } } // determine OpenGL texture format, given and Tex . static GLint choose_fmt(size_t bpp, size_t flags) { const bool alpha = (flags & TEX_ALPHA) != 0; const bool bgr = (flags & TEX_BGR ) != 0; const bool grey = (flags & TEX_GREY ) != 0; const size_t dxt = flags & TEX_DXT; // S3TC if(dxt != 0) { switch(dxt) { case DXT1A: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; case 1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; case 3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case 5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; default: DEBUG_WARN_ERR(ERR::LOGIC); // invalid DXT value return 0; } } // uncompressed switch(bpp) { case 8: ENSURE(grey); return GL_LUMINANCE; case 16: return GL_LUMINANCE_ALPHA; case 24: ENSURE(!alpha); return bgr? GL_BGR : GL_RGB; case 32: ENSURE(alpha); return bgr? GL_BGRA : GL_RGBA; default: DEBUG_WARN_ERR(ERR::LOGIC); // invalid bpp return 0; } UNREACHABLE; } //---------------------------------------------------------------------------- // quality mechanism //---------------------------------------------------------------------------- static GLint default_filter = GL_LINEAR; // one of the GL *minify* filters static int default_q_flags = OGL_TEX_FULL_QUALITY; // OglTexQualityFlags static bool q_flags_valid(int q_flags) { const size_t bits = OGL_TEX_FULL_QUALITY|OGL_TEX_HALF_BPP|OGL_TEX_HALF_RES; // unrecognized bits are set - invalid if((q_flags & ~bits) != 0) return false; // "full quality" but other reduction bits are set - invalid if(q_flags & OGL_TEX_FULL_QUALITY && q_flags & ~OGL_TEX_FULL_QUALITY) return false; return true; } // change default settings - these affect performance vs. quality. // may be overridden for individual textures via parameter to // ogl_tex_upload or ogl_tex_set_filter, respectively. // // pass 0 to keep the current setting; defaults and legal values are: // - q_flags: OGL_TEX_FULL_QUALITY; combination of OglTexQualityFlags // - filter: GL_LINEAR; any valid OpenGL minification filter void ogl_tex_set_defaults(int q_flags, GLint filter) { if(q_flags) { ENSURE(q_flags_valid(q_flags)); default_q_flags = q_flags; } if(filter) { ENSURE(filter_valid(filter)); default_filter = filter; } } // choose an internal format for based on the given q_flags. static GLint choose_int_fmt(GLenum fmt, int q_flags) { // true => 4 bits per component; otherwise, 8 const bool half_bpp = (q_flags & OGL_TEX_HALF_BPP) != 0; // early-out for S3TC textures: they don't need an internal format // (because upload is via glCompressedTexImage2DARB), but we must avoid // triggering the default case below. we might as well return a // meaningful value (i.e. int_fmt = fmt). if(fmt_is_s3tc(fmt)) return fmt; switch(fmt) { // 8bpp case GL_LUMINANCE: return half_bpp? GL_LUMINANCE4 : GL_LUMINANCE8; case GL_INTENSITY: return half_bpp? GL_INTENSITY4 : GL_INTENSITY8; case GL_ALPHA: return half_bpp? GL_ALPHA4 : GL_ALPHA8; // 16bpp case GL_LUMINANCE_ALPHA: return half_bpp? GL_LUMINANCE4_ALPHA4 : GL_LUMINANCE8_ALPHA8; // 24bpp case GL_RGB: case GL_BGR: // note: BGR can't be used as internal format return half_bpp? GL_RGB4 : GL_RGB8; // 32bpp case GL_RGBA: case GL_BGRA: // note: BGRA can't be used as internal format return half_bpp? GL_RGBA4 : GL_RGBA8; default: { wchar_t buf[100]; swprintf_s(buf, ARRAY_SIZE(buf), L"choose_int_fmt: fmt 0x%x isn't covered! please add it", fmt); DEBUG_DISPLAY_ERROR(buf); DEBUG_WARN_ERR(ERR::LOGIC); // given fmt isn't covered! please add it. // fall back to a reasonable default return half_bpp? GL_RGB4 : GL_RGB8; } } UNREACHABLE; } //---------------------------------------------------------------------------- // texture state to allow seamless reload //---------------------------------------------------------------------------- // see "Texture Parameters" in docs. // all GL state tied to the texture that must be reapplied after reload. // (this mustn't get too big, as it's stored in the already sizeable OglTex) struct OglTexState { // glTexParameter // note: there are more options, but they do not look to // be important and will not be applied after a reload! // in particular, LOD_BIAS isn't needed because that is set for // the entire texturing unit via glTexEnv. // .. texture filter // note: this is the minification filter value; magnification filter // is GL_NEAREST if it's GL_NEAREST, otherwise GL_LINEAR. // we don't store mag_filter explicitly because it // doesn't appear useful - either apps can tolerate LINEAR, or // mipmaps aren't called for and filter could be NEAREST anyway). GLint filter; // .. wrap mode // note: to simplify things, we assume that apps will never want to // set S/T modes independently. it that becomes necessary, // it's easy to add. GLint wrap; // .. anisotropy // note: ignored unless EXT_texture_filter_anisotropic is supported. GLfloat anisotropy; }; // fill the given state object with default values. static void state_set_to_defaults(OglTexState* ots) { ots->filter = default_filter; ots->wrap = GL_REPEAT; ots->anisotropy = 1.0f; } // send all state to OpenGL (actually the currently bound texture). // called from ogl_tex_upload. static void state_latch(OglTexState* ots) { // filter const GLint filter = ots->filter; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); const GLint mag_filter = (filter == GL_NEAREST)? GL_NEAREST : GL_LINEAR; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); // wrap const GLint wrap = ots->wrap; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap); // .. only CLAMP and REPEAT are guaranteed to be available. // if we're using one of the others, we squelch the error that // may have resulted if this GL implementation is old. if(wrap != GL_CLAMP && wrap != GL_REPEAT) ogl_SquelchError(GL_INVALID_ENUM); // anisotropy const GLfloat anisotropy = ots->anisotropy; if (anisotropy != 1.0f && ogl_tex_has_anisotropy()) { glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy); } } //---------------------------------------------------------------------------- // texture resource object //---------------------------------------------------------------------------- // ideally we would split OglTex into data and state objects as in // SndData / VSrc. this gives us the benefits of caching while still // leaving each "instance" (state object, which owns a data reference) // free to change its state. however, unlike in OpenAL, there is no state // independent of the data object - all parameters are directly tied to the // GL texture object. therefore, splitting them up is impossible. // (we shouldn't even keep the texel data in memory since that's already // covered by the FS cache). // // given that multiple "instances" share the state stored here, we conclude: // - a refcount is necessary to prevent ogl_tex_upload from freeing // as long as other instances are active. // - concurrent use risks cross-talk (if the 2nd "instance" changes state and // the first is reloaded, its state may change to that of the 2nd) // // as bad as it sounds, the latter issue isn't a problem: we do not expect // multiple instances of the same texture where someone changes its filter. // even if it is reloaded, the differing state is not critical. // the alternative is even worse: disabling *all* caching/reuse would // really hurt performance and h_mgr doesn't support only disallowing // reuse of active objects (this would break the index lookup code, since // multiple instances may then exist). // note: make sure these values fit inside OglTex.flags (only 16 bits) enum OglTexFlags { // "the texture is currently uploaded"; reset in dtor. OT_IS_UPLOADED = 1, // "the enclosed Tex object is valid and holds a texture"; // reset in dtor and after ogl_tex_upload's tex_free. OT_TEX_VALID = 2, //size_t tex_valid : 1; // "reload() should automatically re-upload the texture" (because // it had been uploaded before the reload); never reset. OT_NEED_AUTO_UPLOAD = 4, // (used for validating flags) OT_ALL_FLAGS = OT_IS_UPLOADED|OT_TEX_VALID|OT_NEED_AUTO_UPLOAD }; struct OglTex { Tex t; // allocated by OglTex_reload; indicates the texture is currently uploaded. GLuint id; // ogl_tex_upload calls choose_fmt to determine these from . // however, its caller may override those values via parameters. // note: these are stored here to allow retrieving via ogl_tex_get_format; // they are only used within ogl_tex_upload. GLenum fmt; GLint int_fmt; OglTexState state; // OglTexQualityFlags u8 q_flags; // to which Texture Mapping Unit was this bound? u8 tmu; u16 flags; }; H_TYPE_DEFINE(OglTex); static void OglTex_init(OglTex* ot, va_list args) { Tex* wrapped_tex = va_arg(args, Tex*); if(wrapped_tex) { ot->t = *wrapped_tex; // indicate ot->t is now valid, thus skipping loading from file. // note: ogl_tex_wrap prevents actual reloads from happening. ot->flags |= OT_TEX_VALID; } state_set_to_defaults(&ot->state); ot->q_flags = default_q_flags; } static void OglTex_dtor(OglTex* ot) { if(ot->flags & OT_TEX_VALID) { tex_free(&ot->t); ot->flags &= ~OT_TEX_VALID; } // note: do not check if OT_IS_UPLOADED is set, because we allocate // OglTex.id without necessarily having done an upload. glDeleteTextures(1, &ot->id); ot->id = 0; ot->flags &= ~OT_IS_UPLOADED; } static Status OglTex_reload(OglTex* ot, const PIVFS& vfs, const VfsPath& pathname, Handle h) { // we're reusing a freed but still in-memory OglTex object if(ot->flags & OT_IS_UPLOADED) return INFO::OK; // if we don't already have the texture in memory (*), load from file. // * this happens if the texture is "wrapped". if(!(ot->flags & OT_TEX_VALID)) { shared_ptr file; size_t fileSize; RETURN_STATUS_IF_ERR(vfs->LoadFile(pathname, file, fileSize)); if(tex_decode(file, fileSize, &ot->t) >= 0) ot->flags |= OT_TEX_VALID; } glGenTextures(1, &ot->id); // if it had already been uploaded before this reload, // re-upload it (this also does state_latch). if(ot->flags & OT_NEED_AUTO_UPLOAD) (void)ogl_tex_upload(h); return INFO::OK; } static Status OglTex_validate(const OglTex* ot) { if(ot->flags & OT_TEX_VALID) { RETURN_STATUS_IF_ERR(tex_validate(&ot->t)); // width, height // (note: this is done here because tex.cpp doesn't impose any // restrictions on dimensions, while OpenGL does). size_t w = ot->t.w; size_t h = ot->t.h; // .. == 0; texture file probably not loaded successfully. if(w == 0 || h == 0) WARN_RETURN(ERR::_11); // .. greater than max supported tex dimension. // no-op if ogl_Init not yet called if(w > (size_t)ogl_max_tex_size || h > (size_t)ogl_max_tex_size) WARN_RETURN(ERR::_12); // .. not power-of-2. // note: we can't work around this because both NV_texture_rectangle // and subtexture require work for the client (changing tex coords). // TODO: ARB_texture_non_power_of_two if(!is_pow2(w) || !is_pow2(h)) WARN_RETURN(ERR::_13); } // texture state if(!filter_valid(ot->state.filter)) WARN_RETURN(ERR::_14); if(!wrap_valid(ot->state.wrap)) WARN_RETURN(ERR::_15); // misc if(!q_flags_valid(ot->q_flags)) WARN_RETURN(ERR::_16); if(ot->tmu >= 128) // unexpected that there will ever be this many WARN_RETURN(ERR::_17); if(ot->flags > OT_ALL_FLAGS) WARN_RETURN(ERR::_18); // .. note: don't check ot->fmt and ot->int_fmt - they aren't set // until during ogl_tex_upload. return INFO::OK; } static Status OglTex_to_string(const OglTex* ot, wchar_t* buf) { swprintf_s(buf, H_STRING_LEN, L"OglTex id=%d flags=%x", ot->id, ot->flags); return INFO::OK; } // load and return a handle to the texture given in . // for a list of supported formats, see tex.h's tex_load. Handle ogl_tex_load(const PIVFS& vfs, const VfsPath& pathname, size_t flags) { Tex* wrapped_tex = 0; // we're loading from file return h_alloc(H_OglTex, vfs, pathname, flags, wrapped_tex); } // return Handle to an existing object, if it has been loaded and // is still in memory; otherwise, a negative error code. Handle ogl_tex_find(const VfsPath& pathname) { const uintptr_t key = fnv_hash(pathname.string().c_str(), pathname.string().length()*sizeof(pathname.string()[0])); return h_find(H_OglTex, key); } // make the given Tex object ready for use as an OpenGL texture // and return a handle to it. this will be as if its contents // had been loaded by ogl_tex_load. // // we need only add bookkeeping information and "wrap" it in // a resource object (accessed via Handle), hence the name. // // isn't strictly needed but should describe the texture so that // h_filename will return a meaningful comment for debug purposes. // note: because we cannot guarantee that callers will pass distinct // "filenames", caching is disabled for the created object. this avoids // mistakenly reusing previous objects that share the same comment. Handle ogl_tex_wrap(Tex* t, const PIVFS& vfs, const VfsPath& pathname, size_t flags) { // this object may not be backed by a file ("may", because // someone could do tex_load and then ogl_tex_wrap). // if h_mgr asks for a reload, the dtor will be called but // we won't be able to reconstruct it. therefore, disallow reloads. // (they are improbable anyway since caller is supposed to pass a // 'descriptive comment' instead of filename, but don't rely on that) // also disable caching as explained above. flags |= RES_DISALLOW_RELOAD|RES_NO_CACHE; return h_alloc(H_OglTex, vfs, pathname, flags, t); } // free all resources associated with the texture and make further // use of it impossible. (subject to refcount) Status ogl_tex_free(Handle& ht) { return h_free(ht, H_OglTex); } //---------------------------------------------------------------------------- // state setters (see "Texture Parameters" in docs) //---------------------------------------------------------------------------- // we require the below functions be called before uploading; this avoids // potentially redundant glTexParameter calls (we'd otherwise need to always // set defaults because we don't know if an override is forthcoming). // raise a debug warning if the texture has already been uploaded // (except in the few cases where this is allowed; see below). // this is so that you will notice incorrect usage - only one instance of a // texture should be active at a time, because otherwise they vie for // control of one shared OglTexState. static void warn_if_uploaded(Handle ht, const OglTex* ot) { #ifndef NDEBUG // we do not require users of this module to remember if they've // already uploaded a texture (inconvenient). since they also can't // tell if the texture was newly loaded (due to h_alloc interface), // we have to squelch this warning in 2 cases: // - it's ogl_tex_loaded several times (i.e. refcount > 1) and the // caller (typically a higher-level LoadTexture) is setting filter etc. // - caller is using our Handle as a caching mechanism, and calls // ogl_tex_set_* before every use of the texture. note: this // need not fall under the above check, e.g. if freed but cached. // workaround is that ogl_tex_set_* won't call us if the // same state values are being set (harmless anyway). int refs = h_get_refcnt(ht); if(refs > 1) return; // don't complain if(ot->flags & OT_IS_UPLOADED) DEBUG_WARN_ERR(ERR::LOGIC); // ogl_tex_set_*: texture already uploaded and shouldn't be changed #else // (prevent warnings; the alternative of wrapping all call sites in // #ifndef is worse) UNUSED2(ht); UNUSED2(ot); #endif } // override default filter (as set above) for this texture. // must be called before uploading (raises a warning if called afterwards). // filter is as defined by OpenGL; it is applied for both minification and // magnification (for rationale and details, see OglTexState) Status ogl_tex_set_filter(Handle ht, GLint filter) { H_DEREF(ht, OglTex, ot); if(!filter_valid(filter)) WARN_RETURN(ERR::INVALID_PARAM); if(ot->state.filter != filter) { warn_if_uploaded(ht, ot); ot->state.filter = filter; } return INFO::OK; } // override default wrap mode (GL_REPEAT) for this texture. // must be called before uploading (raises a warning if called afterwards). // wrap is as defined by OpenGL and applies to both S and T coordinates // (rationale: see OglTexState). Status ogl_tex_set_wrap(Handle ht, GLint wrap) { H_DEREF(ht, OglTex, ot); if(!wrap_valid(wrap)) WARN_RETURN(ERR::INVALID_PARAM); if(ot->state.wrap != wrap) { warn_if_uploaded(ht, ot); ot->state.wrap = wrap; } return INFO::OK; } // override default anisotropy for this texture. // must be called before uploading (raises a warning if called afterwards). Status ogl_tex_set_anisotropy(Handle ht, GLfloat anisotropy) { H_DEREF(ht, OglTex, ot); if(anisotropy < 1.0f) WARN_RETURN(ERR::INVALID_PARAM); if(ot->state.anisotropy != anisotropy) { warn_if_uploaded(ht, ot); ot->state.anisotropy = anisotropy; } return INFO::OK; } //---------------------------------------------------------------------------- // upload //---------------------------------------------------------------------------- // OpenGL has several features that are helpful for uploading but not // available in all implementations. we check for their presence but // provide for user override (in case they don't work on a card/driver // combo we didn't test). // tristate; -1 is undecided static int have_auto_mipmap_gen = -1; static int have_s3tc = -1; static int have_anistropy = -1; // override the default decision and force/disallow use of the // given feature. should be called from ah_override_gl_upload_caps. void ogl_tex_override(OglTexOverrides what, OglTexAllow allow) { ENSURE(allow == OGL_TEX_ENABLE || allow == OGL_TEX_DISABLE); const bool enable = (allow == OGL_TEX_ENABLE); switch(what) { case OGL_TEX_S3TC: have_s3tc = enable; break; case OGL_TEX_AUTO_MIPMAP_GEN: have_auto_mipmap_gen = enable; break; case OGL_TEX_ANISOTROPY: have_anistropy = enable; break; default: DEBUG_WARN_ERR(ERR::LOGIC); // invalid break; } } // detect caps (via OpenGL extension list) and give an app_hook the chance to // override this (e.g. via list of card/driver combos on which S3TC breaks). // called once from the first ogl_tex_upload. static void detect_gl_upload_caps() { // note: gfx_card will be empty if running in quickstart mode; // in that case, we won't be able to check for known faulty cards. // detect features, but only change the variables if they were at // "undecided" (if overrides were set before this, they must remain). if(have_auto_mipmap_gen == -1) { have_auto_mipmap_gen = ogl_HaveExtension("GL_SGIS_generate_mipmap"); } if(have_s3tc == -1) { // note: we don't bother checking for GL_S3_s3tc - it is incompatible // and irrelevant (was never widespread). have_s3tc = ogl_HaveExtensions(0, "GL_ARB_texture_compression", "GL_EXT_texture_compression_s3tc", NULL) == 0; } if(have_anistropy == -1) { have_anistropy = ogl_HaveExtension("GL_EXT_texture_filter_anisotropic"); } // allow app hook to make ogl_tex_override calls if(AH_IS_DEFINED(override_gl_upload_caps)) { ah_override_gl_upload_caps(); } // no app hook defined - have our own crack at blacklisting some hardware. else { + const std::wstring cardName = gfx::CardName(); // rationale: janwas's laptop's S3 card blows up if S3TC is used // (oh, the irony). it'd be annoying to have to share this between all // projects, hence this default implementation here. - if(!wcscmp(gfx_card, L"S3 SuperSavage/IXC 1014")) - { - if(wcsstr(gfx_drv_ver, L"ssicdnt.dll (2.60.115)")) - ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE); - } + if(cardName == L"S3 SuperSavage/IXC 1014") + ogl_tex_override(OGL_TEX_S3TC, OGL_TEX_DISABLE); } } // take care of mipmaps. if they are called for by , either // arrange for OpenGL to create them, or see to it that the Tex object // contains them (if need be, creating them in software). // sets *plevels_to_skip to influence upload behavior (depending on // whether mipmaps are needed and the quality settings). // returns 0 to indicate success; otherwise, caller must disable // mipmapping by switching filter to e.g. GL_LINEAR. static Status get_mipmaps(Tex* t, GLint filter, int q_flags, int* plevels_to_skip) { // decisions: // .. does filter call for uploading mipmaps? const bool need_mipmaps = filter_uses_mipmaps(filter); // .. does the image data include mipmaps? (stored as separate // images after the regular texels) const bool includes_mipmaps = (t->flags & TEX_MIPMAPS) != 0; // .. is this texture in S3TC format? (more generally, "compressed") const bool is_s3tc = (t->flags & TEX_DXT) != 0; *plevels_to_skip = TEX_BASE_LEVEL_ONLY; if(!need_mipmaps) return INFO::OK; // image already contains pregenerated mipmaps; we need do nothing. // this is the nicest case, because they are fastest to load // (no extra processing needed) and typically filtered better than // if automatically generated. if(includes_mipmaps) *plevels_to_skip = 0; // t contains mipmaps // OpenGL supports automatic generation; we need only // activate that and upload the base image. else if(have_auto_mipmap_gen) { // note: we assume GL_GENERATE_MIPMAP and GL_GENERATE_MIPMAP_SGIS // have the same values - it's heavily implied by the spec // governing 'promoted' ARB extensions and just plain makes sense. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); } // image is S3TC-compressed and the previous 2 alternatives weren't // available; we're going to cheat and just disable mipmapping. // rationale: having tex_transform add mipmaps would be slow (since // all<->all transforms aren't implemented, it'd have to decompress // from S3TC first), and DDS images ought to include mipmaps! else if(is_s3tc) return ERR::FAIL; // NOWARN // image is uncompressed and we're on an old OpenGL implementation; // we will generate mipmaps in software. else { RETURN_STATUS_IF_ERR(tex_transform_to(t, t->flags|TEX_MIPMAPS)); *plevels_to_skip = 0; // t contains mipmaps } // t contains mipmaps; we can apply our resolution reduction trick: if(*plevels_to_skip == 0) { // this saves texture memory by skipping some of the lower // (high-resolution) mip levels. // // note: we don't just use GL_TEXTURE_BASE_LEVEL because it would // require uploading unused levels, which is wasteful. // .. can be expanded to reduce to 1/4, 1/8 by encoding factor in q_flags. const size_t reduce = (q_flags & OGL_TEX_HALF_RES)? 2 : 1; *plevels_to_skip = (int)ceil_log2(reduce); } return INFO::OK; } // tex_util_foreach_mipmap callbacks: upload the given level to OpenGL. struct UploadParams { GLenum fmt; GLint int_fmt; }; static void upload_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t UNUSED(level_data_size), void* RESTRICT cbData) { const UploadParams* up = (const UploadParams*)cbData; glTexImage2D(GL_TEXTURE_2D, (GLint)level, up->int_fmt, (GLsizei)level_w, (GLsizei)level_h, 0, up->fmt, GL_UNSIGNED_BYTE, level_data); } static void upload_compressed_level(size_t level, size_t level_w, size_t level_h, const u8* RESTRICT level_data, size_t level_data_size, void* RESTRICT cbData) { const UploadParams* up = (const UploadParams*)cbData; pglCompressedTexImage2DARB(GL_TEXTURE_2D, (GLint)level, up->fmt, (GLsizei)level_w, (GLsizei)level_h, 0, (GLsizei)level_data_size, level_data); } // upload the texture in the specified (internal) format. // split out of ogl_tex_upload because it was too big. // // pre: is valid for OpenGL use; texture is bound. static void upload_impl(Tex* t, GLenum fmt, GLint int_fmt, int levels_to_skip) { const GLsizei w = (GLsizei)t->w; const GLsizei h = (GLsizei)t->h; const size_t bpp = t->bpp; const u8* data = (const u8*)tex_get_data(t); const UploadParams up = { fmt, int_fmt }; if(t->flags & TEX_DXT) tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 4, upload_compressed_level, (void*)&up); else tex_util_foreach_mipmap(w, h, bpp, data, levels_to_skip, 1, upload_level, (void*)&up); } // upload the texture to OpenGL. // if not 0, parameters override the following: // fmt_ovr : OpenGL format (e.g. GL_RGB) decided from bpp / Tex flags; // q_flags_ovr : global default "quality vs. performance" flags; // int_fmt_ovr : internal format (e.g. GL_RGB8) decided from fmt / q_flags. // // side effects: // - enables texturing on TMU 0 and binds the texture to it; // - frees the texel data! see ogl_tex_get_data. Status ogl_tex_upload(const Handle ht, GLenum fmt_ovr, int q_flags_ovr, GLint int_fmt_ovr) { ONCE(detect_gl_upload_caps()); H_DEREF(ht, OglTex, ot); Tex* t = &ot->t; ENSURE(q_flags_valid(q_flags_ovr)); // we don't bother verifying *fmt_ovr - there are too many values // upload already happened; no work to do. // (this also happens if a cached texture is "loaded") if(ot->flags & OT_IS_UPLOADED) return INFO::OK; if(ot->flags & OT_TEX_VALID) { // decompress S3TC if that's not supported by OpenGL. if((t->flags & TEX_DXT) && !have_s3tc) (void)tex_transform_to(t, t->flags & ~TEX_DXT); // determine fmt and int_fmt, allowing for user override. ot->fmt = choose_fmt(t->bpp, t->flags); if(fmt_ovr) ot->fmt = fmt_ovr; if(q_flags_ovr) ot->q_flags = q_flags_ovr; ot->int_fmt = choose_int_fmt(ot->fmt, ot->q_flags); if(int_fmt_ovr) ot->int_fmt = int_fmt_ovr; // now actually send to OpenGL: ogl_WarnIfError(); { // (note: we know ht is valid due to H_DEREF, but ogl_tex_bind can // fail in debug builds if OglTex.id isn't a valid texture name) RETURN_STATUS_IF_ERR(ogl_tex_bind(ht, ot->tmu)); int levels_to_skip; if(get_mipmaps(t, ot->state.filter, ot->q_flags, &levels_to_skip) < 0) // error => disable mipmapping ot->state.filter = GL_LINEAR; // (note: if first time, applies our defaults/previous overrides; // otherwise, replays all state changes) state_latch(&ot->state); upload_impl(t, ot->fmt, ot->int_fmt, levels_to_skip); } ogl_WarnIfError(); ot->flags |= OT_IS_UPLOADED; // see rationale for at declaration of OglTex. // note: tex_free is safe even if this OglTex was wrapped - // the Tex contains a mem handle. int refs = h_get_refcnt(ht); if(refs == 1) { // note: we verify above that OT_TEX_VALID is set tex_free(t); ot->flags &= ~OT_TEX_VALID; } } ot->flags |= OT_NEED_AUTO_UPLOAD; return INFO::OK; } //---------------------------------------------------------------------------- // getters //---------------------------------------------------------------------------- // retrieve texture dimensions and bits per pixel. // all params are optional and filled if non-NULL. Status ogl_tex_get_size(Handle ht, size_t* w, size_t* h, size_t* bpp) { H_DEREF(ht, OglTex, ot); if(w) *w = ot->t.w; if(h) *h = ot->t.h; if(bpp) *bpp = ot->t.bpp; return INFO::OK; } // retrieve TexFlags and the corresponding OpenGL format. // the latter is determined during ogl_tex_upload and is 0 before that. // all params are optional and filled if non-NULL. Status ogl_tex_get_format(Handle ht, size_t* flags, GLenum* fmt) { H_DEREF(ht, OglTex, ot); if(flags) *flags = ot->t.flags; if(fmt) { ENSURE(ot->flags & OT_IS_UPLOADED); *fmt = ot->fmt; } return INFO::OK; } // retrieve pointer to texel data. // // note: this memory is freed after a successful ogl_tex_upload for // this texture. after that, the pointer we retrieve is NULL but // the function doesn't fail (negative return value) by design. // if you still need to get at the data, add a reference before // uploading it or read directly from OpenGL (discouraged). Status ogl_tex_get_data(Handle ht, u8** p) { H_DEREF(ht, OglTex, ot); *p = tex_get_data(&ot->t); return INFO::OK; } // retrieve colour of 1x1 mipmap level extern Status ogl_tex_get_average_colour(Handle ht, u32* p) { H_DEREF(ht, OglTex, ot); warn_if_uploaded(ht, ot); *p = tex_get_average_colour(&ot->t); return INFO::OK; } //---------------------------------------------------------------------------- // misc API //---------------------------------------------------------------------------- // bind the texture to the specified unit [number] in preparation for // using it in rendering. if is 0, texturing is disabled instead. // // side effects: // - changes the active texture unit; // - (if return value is 0:) texturing was enabled/disabled on that unit. // // notes: // - assumes multitexturing is available. // - not necessary before calling ogl_tex_upload! // - on error, the unit's texture state is unchanged; see implementation. Status ogl_tex_bind(Handle ht, size_t unit) { // note: there are many call sites of glActiveTextureARB, so caching // those and ignoring redundant sets isn't feasible. pglActiveTextureARB((int)(GL_TEXTURE0+unit)); // special case: disable texturing if(ht == 0) { glDisable(GL_TEXTURE_2D); return INFO::OK; } // if this fails, the texture unit's state remains unchanged. // we don't bother catching that and disabling texturing because a // debug warning is raised anyway, and it's quite unlikely. H_DEREF(ht, OglTex, ot); ot->tmu = (u8)unit; // if 0, there's a problem in the OglTex reload/dtor logic. // binding it results in whiteness, which can have many causes; // we therefore complain so this one can be ruled out. ENSURE(ot->id != 0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, ot->id); return INFO::OK; } // apply the specified transforms (as in tex_transform) to the image. // must be called before uploading (raises a warning if called afterwards). Status ogl_tex_transform(Handle ht, size_t transforms) { H_DEREF(ht, OglTex, ot); Status ret = tex_transform(&ot->t, transforms); return ret; } // change the pixel format to that specified by . // (note: this is equivalent to ogl_tex_transform(ht, ht_flags^new_flags). Status ogl_tex_transform_to(Handle ht, size_t new_flags) { H_DEREF(ht, OglTex, ot); Status ret = tex_transform_to(&ot->t, new_flags); return ret; } // return whether native S3TC support is available bool ogl_tex_has_s3tc() { // ogl_tex_upload must be called before this ENSURE(have_s3tc != -1); return (have_s3tc != 0); } // return whether anisotropic filtering support is available bool ogl_tex_has_anisotropy() { // ogl_tex_upload must be called before this ENSURE(have_anistropy != -1); return (have_anistropy != 0); } Index: ps/trunk/source/lib/ogl.h =================================================================== --- ps/trunk/source/lib/ogl.h (revision 9545) +++ ps/trunk/source/lib/ogl.h (revision 9546) @@ -1,154 +1,144 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * OpenGL helper functions. */ #ifndef INCLUDED_OGL #define INCLUDED_OGL #include "lib/external_libraries/opengl.h" /** * initialization: import extension function pointers and do feature detect. * call before using any other function, and after each video mode change. * fails if OpenGL not ready for use. **/ extern void ogl_Init(); //----------------------------------------------------------------------------- // extensions /** * check if an extension is supported by the OpenGL implementation. * * takes subsequently added core support for some extensions into account * (in case drivers forget to advertise extensions). * * @param ext extension string; exact case. * @return bool. **/ extern bool ogl_HaveExtension(const char* ext); /** * make sure the OpenGL implementation version matches or is newer than * the given version. * * @param version version string; format: ("%d.%d", major, minor). * example: "1.2". **/ extern bool ogl_HaveVersion(const char* version); /** * check if a list of extensions are all supported (as determined by * ogl_HaveExtension). * * @param dummy value ignored; varargs requires a placeholder. * follow it by a list of const char* extension string parameters, * terminated by a 0 pointer. * @return 0 if all are present; otherwise, the first extension in the * list that's not supported (useful for reporting errors). **/ extern const char* ogl_HaveExtensions(int dummy, ...) SENTINEL_ARG; /** * get a list of all supported extensions. * * useful for crash logs / system information. * * @return read-only C string of unspecified length containing all * advertised extension names, separated by space. **/ extern const char* ogl_ExtensionString(); // declare extension function pointers #if OS_WIN # define GL_CALL_CONV __stdcall #else # define GL_CALL_CONV #endif #define FUNC(ret, name, params) EXTERN_C ret (GL_CALL_CONV *p##name) params; #define FUNC2(ret, nameARB, nameCore, version, params) EXTERN_C ret (GL_CALL_CONV *p##nameARB) params; #define FUNC3(ret, nameARB, nameCore, version, params) EXTERN_C ret (GL_CALL_CONV *p##nameCore) params; #include "lib/external_libraries/glext_funcs.h" #undef FUNC3 #undef FUNC2 #undef FUNC // leave GL_CALL_CONV defined for ogl.cpp //----------------------------------------------------------------------------- // errors /** * raise a warning (break into the debugger) if an OpenGL error is pending. * resets the OpenGL error state afterwards. * * when an error is reported, insert calls to this in a binary-search scheme * to quickly narrow down the actual error location. * * reports a bogus invalid_operation error if called before OpenGL is * initialized, so don't! * * disabled in release mode for efficiency and to avoid annoying errors. **/ extern void ogl_WarnIfError(); #ifdef NDEBUG # define ogl_WarnIfError() #endif /** * ignore and reset the specified OpenGL error. * * this is useful for suppressing annoying error messages, e.g. * "invalid enum" for GL_CLAMP_TO_EDGE even though we've already * warned the user that their OpenGL implementation is too old. * * call after the fact, i.e. the error has been raised. if another or * different error is pending, those are reported immediately. * * @param err_to_ignore: one of the glGetError enums. * @return true if the requested error was seen and ignored **/ extern bool ogl_SquelchError(GLenum err_to_ignore); //----------------------------------------------------------------------------- // implementation limits / feature detect extern GLint ogl_max_tex_size; /// [pixels] extern GLint ogl_max_tex_units; /// limit on GL_TEXTUREn -/** - * set sysdep/gfx.h gfx_card and gfx_drv_ver. called by gfx_detect. - * - * fails if OpenGL not ready for use. - * gfx_card and gfx_drv_ver are unchanged on failure. - * - * @return Status - **/ -extern Status ogl_get_gfx_info(); - #endif // #ifndef INCLUDED_OGL Index: ps/trunk/source/lib/sysdep/os/unix/x/x.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/unix/x/x.cpp (revision 9545) +++ ps/trunk/source/lib/sysdep/os/unix/x/x.cpp (revision 9546) @@ -1,352 +1,353 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // X Window System-specific code #include "precompiled.h" #if OS_LINUX # define HAVE_X 1 #else # define HAVE_X 0 #endif #if HAVE_X #include "lib/debug.h" #include "lib/sysdep/gfx.h" #define Cursor X__Cursor #include #include #include #include "SDL/SDL.h" #include "SDL/SDL_syswm.h" #include #undef Cursor #undef Status static Display *SDL_Display; static Window SDL_Window; static void (*Lock_Display)(void); static void (*Unlock_Display)(void); static wchar_t *selection_data=NULL; static size_t selection_size=0; -// useful for choosing a video mode. not called by detect(). -// if we fail, outputs are unchanged (assumed initialized to defaults) -Status gfx_get_video_mode(int* xres, int* yres, int* bpp, int* freq) +namespace gfx { + +Status GetVideoMode(int* xres, int* yres, int* bpp, int* freq) { Display* disp = XOpenDisplay(0); if(!disp) WARN_RETURN(ERR::FAIL); int screen = XDefaultScreen(disp); /* 2004-07-13 NOTE: The XDisplayWidth/Height functions don't actually return the current display mode - they return the size of the root window. This means that users with "Virtual Desktops" bigger than what their monitors/graphics card can handle will have to set their 0AD screen resolution manually. There's supposed to be an X extension that can give you the actual display mode, probably including refresh rate info etc, but it's not worth researching and implementing that at this stage. */ if(xres) *xres = XDisplayWidth(disp, screen); if(yres) *yres = XDisplayHeight(disp, screen); if(bpp) *bpp = XDefaultDepth(disp, screen); if(freq) *freq = 0; XCloseDisplay(disp); return INFO::OK; } -// useful for determining aspect ratio. not called by detect(). -// if we fail, outputs are unchanged (assumed initialized to defaults) -Status gfx_get_monitor_size(int& width_mm, int& height_mm) +Status GetMonitorSize(int& width_mm, int& height_mm) { Display* disp = XOpenDisplay(0); if(!disp) WARN_RETURN(ERR::FAIL); int screen = XDefaultScreen(disp); - width_mm=XDisplayWidthMM(disp, screen); - height_mm=XDisplayHeightMM(disp, screen); + width_mm = XDisplayWidthMM(disp, screen); + height_mm = XDisplayHeightMM(disp, screen); XCloseDisplay(disp); return INFO::OK; } +} // namespace gfx + + /* Oh, boy, this is heavy stuff... http://www.freedesktop.org/standards/clipboards-spec/clipboards.txt http://www.mail-archive.com/xfree86@xfree86.org/msg15594.html http://michael.toren.net/mirrors/doc/X-copy+paste.txt http://devdocs.wesnoth.org/clipboard_8cpp-source.html http://tronche.com/gui/x/xlib/window-information/XGetWindowProperty.html http://www.jwz.org/doc/x-cut-and-paste.html The basic run-down on X Selection Handling: * One window owns the "current selection" at any one time * Accessing the Selection (i.e. "paste"), Step-by-step * Ask the X server for the current selection owner * Ask the selection owner window to convert the selection into a format we can understand (XA_STRING - Latin-1 string - for now) * The converted result is stored as a property of the *selection owner* window. It is possible to specify the current application window as the target - but that'd require some X message handling... easier to skip that.. * The final step is to acquire the property value of the selection owner window Notes: An "Atom" is a server-side object that represents a string by an index into some kind of table or something. Pretty much like a handle that refers to one unique string. Atoms are used here to refer to property names and value formats. Expansions: * Implement UTF-8 format support (should be interresting for international users) */ wchar_t *sys_clipboard_get() { Display *disp=XOpenDisplay(NULL); if (!disp) return NULL; // We use CLIPBOARD as the default, since the CLIPBOARD semantics are much // closer to windows semantics. Atom selSource=XInternAtom(disp, "CLIPBOARD", False); Window selOwner=XGetSelectionOwner(disp, selSource); if (selOwner == None) { // However, since many apps don't use CLIPBOARD, but use PRIMARY instead // we use XA_PRIMARY as a fallback clipboard. This is true for xterm, // for example. selSource=XA_PRIMARY; selOwner=XGetSelectionOwner(disp, selSource); } if (selOwner != None) { Atom pty=XInternAtom(disp, "SelectionPropertyTemp", False); XConvertSelection(disp, selSource, XA_STRING, pty, selOwner, CurrentTime); XFlush(disp); Atom type; int format=0, result=0; unsigned long len=0, bytes_left=0, dummy=0; unsigned char *data=NULL; // Get the length of the property and some attributes // bytes_left will contain the length of the selection result = XGetWindowProperty (disp, selOwner, pty, 0, 0, // offset - len 0, // Delete 0==FALSE AnyPropertyType,//flag &type, // return type &format, // return format &len, &bytes_left, &data); if (result != Success) debug_printf(L"clipboard_get: result: %d type:%lu len:%lu format:%d bytes_left:%lu\n", result, type, len, format, bytes_left); if (result == Success && bytes_left > 0) { result = XGetWindowProperty (disp, selOwner, pty, 0, bytes_left, 0, AnyPropertyType, &type, &format, &len, &dummy, &data); if (result == Success) { debug_printf(L"clipboard_get: XGetWindowProperty succeeded, returning data\n"); debug_printf(L"clipboard_get: data was: \"%hs\", type was %lu, XA_STRING atom is %lu\n", data, type, XA_STRING); if (type == XA_STRING) //Latin-1: Just copy into low byte of wchar_t { wchar_t *ret=(wchar_t *)malloc((bytes_left+1)*sizeof(wchar_t)); std::copy(data, data+bytes_left, ret); ret[bytes_left]=0; return ret; } // TODO: Handle UTF8 strings } else { debug_printf(L"clipboard_get: XGetWindowProperty failed!\n"); return NULL; } } } return NULL; } Status sys_clipboard_free(wchar_t *clip_buf) { free(clip_buf); return INFO::OK; } /** * An SDL Event filter that intercepts other applications' requests for the * X selection buffer. * * @see x11_clipboard_init * @see sys_clipboard_set */ int clipboard_filter(const SDL_Event *event) { /* Pass on all non-window manager specific events immediately */ /* And do nothing if we don't actually have a clip-out to send out */ if (event->type != SDL_SYSWMEVENT || !selection_data) return 1; /* Handle window-manager specific clipboard events */ /* (Note: libsdl must be compiled with X11 support (SDL_VIDEO_DRIVER_X11 in SDL_config.h) - else you'll get errors like "'struct SDL_SysWMmsg' has no member named 'xevent'") */ switch (event->syswm.msg->event.xevent.type) { /* Copy the selection from our buffer to the requested property, and convert to the requested target format */ case SelectionRequest: { XSelectionRequestEvent *req; XEvent sevent; req = &event->syswm.msg->event.xevent.xselectionrequest; sevent.xselection.type = SelectionNotify; sevent.xselection.display = req->display; sevent.xselection.selection = req->selection; sevent.xselection.target = req->target; sevent.xselection.property = None; sevent.xselection.requestor = req->requestor; sevent.xselection.time = req->time; // Simply strip all non-Latin1 characters and replace with '?' // We should support XA_UTF8 if (req->target == XA_STRING) { size_t size = wcslen(selection_data); u8 *buf = (u8 *)alloca(size); for (size_t i=0;irequestor, req->property, sevent.xselection.target, 8, PropModeReplace, buf, size); sevent.xselection.property = req->property; } // TODO Add more target formats XSendEvent(SDL_Display,req->requestor,False,0,&sevent); XSync(SDL_Display, False); } break; } return 1; } /** * Initialization for X clipboard handling, called on-demand by * sys_clipboard_set. */ Status x11_clipboard_init() { SDL_SysWMinfo info; SDL_VERSION(&info.version); if ( SDL_GetWMInfo(&info) ) { /* Save the information for later use */ if ( info.subsystem == SDL_SYSWM_X11 ) { SDL_Display = info.info.x11.display; SDL_Window = info.info.x11.window; Lock_Display = info.info.x11.lock_func; Unlock_Display = info.info.x11.unlock_func; /* Enable the special window hook events */ SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); SDL_SetEventFilter(clipboard_filter); return INFO::OK; } else { return ERR::FAIL; } } return INFO::OK; } /** * Set the Selection (i.e. "copy") * * Step-by-step (X11) *
    *
  • Store the selection text in a local buffer *
  • Tell the X server that we want to own the selection *
  • Listen for Selection events and respond to them as appropriate *
*/ Status sys_clipboard_set(const wchar_t *str) { ONCE(x11_clipboard_init()); debug_printf(L"sys_clipboard_set: %ls\n", str); if (selection_data) { free(selection_data); selection_data = NULL; } selection_size = (wcslen(str)+1)*sizeof(wchar_t); selection_data = (wchar_t *)malloc(selection_size); wcscpy(selection_data, str); Lock_Display(); // Like for the clipboard_get code above, we rather use CLIPBOARD than // PRIMARY - more windows'y behaviour there. Atom clipboard_atom = XInternAtom(SDL_Display, "CLIPBOARD", False); XSetSelectionOwner(SDL_Display, clipboard_atom, SDL_Window, CurrentTime); XSetSelectionOwner(SDL_Display, XA_PRIMARY, SDL_Window, CurrentTime); Unlock_Display(); return INFO::OK; } #endif // #if HAVE_X Index: ps/trunk/source/lib/sysdep/os/osx/osx.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/osx/osx.cpp (revision 9545) +++ ps/trunk/source/lib/sysdep/os/osx/osx.cpp (revision 9546) @@ -1,103 +1,102 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "precompiled.h" #include "lib/lib.h" #include "lib/sysdep/sysdep.h" #include "lib/sysdep/gfx.h" #include // "copy" text into the clipboard. replaces previous contents. Status sys_clipboard_set(const wchar_t* text) { return INFO::OK; } // allow "pasting" from clipboard. returns the current contents if they // can be represented as text, otherwise 0. // when it is no longer needed, the returned pointer must be freed via // sys_clipboard_free. (NB: not necessary if zero, but doesn't hurt) wchar_t* sys_clipboard_get(void) { // Remember to implement sys_clipboard_free when implementing this method! return NULL; } // frees memory used by , which must have been returned by // sys_clipboard_get. see note above. Status sys_clipboard_free(wchar_t* copy) { // Since clipboard_get never returns allocated memory (unimplemented), we // should only ever get called with a NULL pointer. ENSURE(!copy); return INFO::OK; } -/** - * get current video mode. - * - * this is useful when choosing a new video mode. - * - * @param xres, yres (optional out) resolution [pixels] - * @param bpp (optional out) bits per pixel - * @param freq (optional out) vertical refresh rate [Hz] - * @return Status; INFO::OK unless: some information was requested - * (i.e. pointer is non-NULL) but cannot be returned. - * on failure, the outputs are all left unchanged (they are - * assumed initialized to defaults) - **/ -Status gfx_get_video_mode(int* xres, int* yres, int* bpp, int* freq) +namespace gfx { + +Status GetVideoMode(int* xres, int* yres, int* bpp, int* freq) { // TODO Implement - return ERR::NOT_SUPPORTED; + return ERR::NOT_SUPPORTED; // NOWARN } +Status GetMonitorSize(int* xres, int* yres, int* bpp, int* freq) +{ + // TODO Implement + return ERR::NOT_SUPPORTED; // NOWARN +} + +} // namespace gfx + OsPath sys_ExecutablePathname() { static char name[PATH_MAX]; static bool init = false; if ( !init ) { init = true; char temp[PATH_MAX]; u32 size = PATH_MAX; if (_NSGetExecutablePath( temp, &size )) return OsPath(); realpath(temp, name); } // On OS X, we might be in a bundle. In this case set its name as our name. char* app = strstr(name, ".app"); if (app) { // Remove everything after the .app *(app + strlen(".app")) = '\0'; debug_printf(L"app bundle name: %hs\n", name); } return name; } + +} // namespace gfx Index: ps/trunk/source/lib/sysdep/os/win/wgfx.cpp =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wgfx.cpp (revision 9545) +++ ps/trunk/source/lib/sysdep/os/win/wgfx.cpp (revision 9546) @@ -1,206 +1,195 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * graphics card detection on Windows. */ #include "precompiled.h" -#include "lib/sysdep/gfx.h" +#include "lib/sysdep/os/win/wgfx.h" +#include "lib/sysdep/gfx.h" #include "lib/sysdep/os/win/wdll_ver.h" #include "lib/sysdep/os/win/wutil.h" #include "lib/sysdep/os/win/wmi.h" #if MSC_VERSION #pragma comment(lib, "advapi32.lib") // registry #endif -// useful for choosing a video mode. -// if we fail, outputs are unchanged (assumed initialized to defaults) -Status gfx_get_video_mode(int* xres, int* yres, int* bpp, int* freq) -{ - DEVMODEA dm; - memset(&dm, 0, sizeof(dm)); - dm.dmSize = sizeof(dm); - // dm.dmDriverExtra already set to 0 by memset - - // (Win2k: don't use EnumDisplaySettingsW - BoundsChecker reports it causes - // a memory overrun, even if called as the very first thing before - // static CRT initialization.) - if(!EnumDisplaySettingsA(0, ENUM_CURRENT_SETTINGS, &dm)) - WARN_RETURN(ERR::FAIL); - - // EnumDisplaySettings is documented to set the values of the following: - const DWORD expectedFlags = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL|DM_DISPLAYFREQUENCY|DM_DISPLAYFLAGS; - ENSURE((dm.dmFields & expectedFlags) == expectedFlags); - - if(xres) - *xres = (int)dm.dmPelsWidth; - if(yres) - *yres = (int)dm.dmPelsHeight; - if(bpp) - *bpp = (int)dm.dmBitsPerPel; - if(freq) - *freq = (int)dm.dmDisplayFrequency; - - return INFO::OK; -} - - -// useful for determining aspect ratio. -// if we fail, outputs are unchanged (assumed initialized to defaults) -Status gfx_get_monitor_size(int& width_mm, int& height_mm) -{ - // (DC for the primary monitor's entire screen) - HDC dc = GetDC(0); - width_mm = GetDeviceCaps(dc, HORZSIZE); - height_mm = GetDeviceCaps(dc, VERTSIZE); - ReleaseDC(0, dc); - return INFO::OK; -} - - -//----------------------------------------------------------------------------- - -static Status win_get_gfx_card() -{ - WmiMap wmiMap; - RETURN_STATUS_IF_ERR(wmi_GetClass(L"Win32_VideoController", wmiMap)); - swprintf_s(gfx_card, GFX_CARD_LEN, L"%ls", wmiMap[L"Caption"].bstrVal); - return INFO::OK; -} - - // note: this implementation doesn't require OpenGL to be initialized. static Status AppendDriverVersionsFromRegistry(VersionList& versionList) { // rationale: // - we could easily determine the 2d driver via EnumDisplaySettings, // but we want to query the actual OpenGL driver. see // http://www.opengl.org/discussion_boards/ubb/Forum3/HTML/009679.html ; // in short, we need the exact OpenGL driver version because some // driver packs (e.g. Omega) mix and match DLLs. // - an alternative implementation would be to enumerate all // DLLs loaded into the process, and check for a glBegin export. // that requires toolhelp/PSAPI (a bit complicated) and telling // ICD/opengl32.dll apart (not future-proof). // - therefore, we stick with the OpenGLDrivers approach. since there is // no good way to determine which of the subkeys (e.g. nvoglnt) is // active (several may exist due to previously removed drivers), // we just display all of them. it is obvious from looking at // gfx_card which one is correct; we thus avoid driver-specific // name checks and reporting incorrectly. wchar_t dllName[MAX_PATH+1]; HKEY hkDrivers; const wchar_t* key = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers"; if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hkDrivers) != 0) WARN_RETURN(ERR::FAIL); // for each subkey (i.e. installed OpenGL driver): for(DWORD i = 0; ; i++) { wchar_t driverName[32]; DWORD driverNameLength = ARRAY_SIZE(driverName); const LONG err = RegEnumKeyExW(hkDrivers, i, driverName, &driverNameLength, 0, 0, 0, 0); if(err == ERROR_NO_MORE_ITEMS) { if(i == 0) { RegCloseKey(hkDrivers); return ERR::NOT_SUPPORTED; // NOWARN (ATI and NVidia don't create sub-keys on Windows 7) } break; } ENSURE(err == ERROR_SUCCESS); HKEY hkDriver; if(RegOpenKeyExW(hkDrivers, driverName, 0, KEY_QUERY_VALUE, &hkDriver) == 0) { DWORD dllNameLength = ARRAY_SIZE(dllName)-5; // for ".dll" if(RegQueryValueExW(hkDriver, L"Dll", 0, 0, (LPBYTE)dllName, &dllNameLength) == 0) wdll_ver_Append(dllName, versionList); RegCloseKey(hkDriver); } } // for each value: // (some old drivers, e.g. S3 Super Savage, store their ICD name in a // single REG_SZ value. we therefore include those as well.) for(DWORD i = 0; ; i++) { wchar_t name[100]; // we don't need this, but RegEnumValue fails otherwise. DWORD nameLength = ARRAY_SIZE(name); DWORD type; DWORD dllNameLength = ARRAY_SIZE(dllName)-5; // for ".dll" const DWORD err = RegEnumValueW(hkDrivers, i, name, &nameLength, 0, &type, (LPBYTE)dllName, &dllNameLength); if(err == ERROR_NO_MORE_ITEMS) break; ENSURE(err == ERROR_SUCCESS); if(type == REG_SZ) wdll_ver_Append(dllName, versionList); } RegCloseKey(hkDrivers); return INFO::OK; } -#include "lib/timer.h" static void AppendDriverVersionsFromKnownFiles(VersionList& versionList) { // (check all known file names regardless of gfx_card, which may change and // defeat our parsing. this takes about 5..10 ms) // NVidia wdll_ver_Append(L"nvoglv64.dll", versionList); wdll_ver_Append(L"nvoglv32.dll", versionList); wdll_ver_Append(L"nvoglnt.dll", versionList); // ATI wdll_ver_Append(L"atioglxx.dll", versionList); // Intel wdll_ver_Append(L"ig4icd32.dll", versionList); wdll_ver_Append(L"ig4icd64.dll", versionList); wdll_ver_Append(L"iglicd32.dll", versionList); } -Status win_get_gfx_info() +Status wgfx_CardName(wchar_t* cardName, size_t numChars) { - Status err = win_get_gfx_card(); + WmiMap wmiMap; + RETURN_STATUS_IF_ERR(wmi_GetClass(L"Win32_VideoController", wmiMap)); + swprintf_s(cardName, numChars, L"%ls", wmiMap[L"Caption"].bstrVal); + return INFO::OK; +} + +std::wstring wgfx_DriverInfo() +{ VersionList versionList; if(AppendDriverVersionsFromRegistry(versionList) != INFO::OK) // (fails on Windows 7) AppendDriverVersionsFromKnownFiles(versionList); - if(versionList.empty()) - versionList = L"(unknown)"; - wcscpy_s(gfx_drv_ver, GFX_DRV_VER_LEN, versionList.c_str()); + return versionList; +} + + +//----------------------------------------------------------------------------- +// direct implementations of some gfx functions + +namespace gfx { + +Status GetVideoMode(int* xres, int* yres, int* bpp, int* freq) +{ + DEVMODE dm = { sizeof(dm) }; + + if(!EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &dm)) + WARN_RETURN(ERR::FAIL); + + // EnumDisplaySettings is documented to set the values of the following: + const DWORD expectedFlags = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL|DM_DISPLAYFREQUENCY|DM_DISPLAYFLAGS; + ENSURE((dm.dmFields & expectedFlags) == expectedFlags); + + if(xres) + *xres = (int)dm.dmPelsWidth; + if(yres) + *yres = (int)dm.dmPelsHeight; + if(bpp) + *bpp = (int)dm.dmBitsPerPel; + if(freq) + *freq = (int)dm.dmDisplayFrequency; - return err; + return INFO::OK; } + + +Status GetMonitorSize(int& width_mm, int& height_mm) +{ + // (DC for the primary monitor's entire screen) + const HDC hDC = GetDC(0); + width_mm = GetDeviceCaps(hDC, HORZSIZE); + height_mm = GetDeviceCaps(hDC, VERTSIZE); + ReleaseDC(0, hDC); + return INFO::OK; +} + +} // namespace gfx Index: ps/trunk/source/lib/sysdep/os/win/wgfx.h =================================================================== --- ps/trunk/source/lib/sysdep/os/win/wgfx.h (revision 9545) +++ ps/trunk/source/lib/sysdep/os/win/wgfx.h (revision 9546) @@ -1,28 +1,29 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef INCLUDED_WGFX #define INCLUDED_WGFX -extern Status win_get_gfx_info(); +extern Status wgfx_CardName(wchar_t* cardName, size_t numChars); +extern std::wstring wgfx_DriverInfo(); #endif // #ifndef INCLUDED_WGFX Index: ps/trunk/source/lib/sysdep/gfx.cpp =================================================================== --- ps/trunk/source/lib/sysdep/gfx.cpp (revision 9545) +++ ps/trunk/source/lib/sysdep/gfx.cpp (revision 9546) @@ -1,70 +1,98 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * graphics card detection. */ #include "precompiled.h" #include "lib/sysdep/gfx.h" #include "lib/external_libraries/sdl.h" #include "lib/ogl.h" #if OS_WIN # include "lib/sysdep/os/win/wgfx.h" #endif -wchar_t gfx_card[GFX_CARD_LEN] = L""; -wchar_t gfx_drv_ver[GFX_DRV_VER_LEN] = L""; +namespace gfx { -int gfx_mem = -1; // [MiB]; approximate - - -// detect graphics card and set the above information. -void gfx_detect() +std::wstring CardName() { - // TODO: add sizeof(FB)? - gfx_mem = (SDL_GetVideoInfo()->video_mem) / 1048576; // [MiB] - - // try platform-specific version: they return more - // detailed information, and don't require OpenGL to be ready. #if OS_WIN - if(win_get_gfx_info() < 0) + wchar_t cardName[128]; + if(wgfx_CardName(cardName, ARRAY_SIZE(cardName)) != INFO::OK) #endif { - // the OpenGL version should always work, unless OpenGL isn't ready for use, - // or we were called between glBegin and glEnd. - ogl_get_gfx_info(); + const char* vendor = (const char*)glGetString(GL_VENDOR); + const char* renderer = (const char*)glGetString(GL_RENDERER); + // (happens if called before ogl_Init or between glBegin and glEnd.) + if(!vendor || !renderer) + return L""; + swprintf_s(cardName, ARRAY_SIZE(cardName), L"%hs %hs", vendor, renderer); } // remove crap from vendor names. (don't dare touch the model name - // it's too risky, there are too many different strings) -#define SHORTEN(what, chars_to_keep)\ - if(!wcsncmp(gfx_card, what, ARRAY_SIZE(what)-1))\ - memmove(gfx_card+chars_to_keep, gfx_card+ARRAY_SIZE(what)-1, (wcslen(gfx_card)-(ARRAY_SIZE(what)-1)+1)*sizeof(wchar_t)); +#define SHORTEN(what, charsToKeep)\ + if(!wcsncmp(cardName, what, ARRAY_SIZE(what)-1))\ + memmove(cardName+charsToKeep, cardName+ARRAY_SIZE(what)-1, (wcslen(cardName)-(ARRAY_SIZE(what)-1)+1)*sizeof(wchar_t)); SHORTEN(L"ATI Technologies Inc.", 3); SHORTEN(L"NVIDIA Corporation", 6); SHORTEN(L"S3 Graphics", 2); // returned by EnumDisplayDevices SHORTEN(L"S3 Graphics, Incorporated", 2); // returned by GL_VENDOR #undef SHORTEN + + return cardName; +} + + +std::wstring DriverInfo() +{ + std::wstring driverInfo; +#if OS_WIN + driverInfo = wgfx_DriverInfo(); + if(driverInfo.empty()) +#endif + { + const char* version = (const char*)glGetString(GL_VERSION); + if(version) + { + // add "OpenGL" to differentiate this from the real driver version + // (returned by platform-specific detect routines). + driverInfo = std::wstring(L"OpenGL ") + std::wstring(version, version+strlen(version)); + } + } + + if(driverInfo.empty()) + return L"(unknown)"; + return driverInfo; } + + +size_t MemorySizeMiB() +{ + // (maybe add the size of the framebuffer?) + return (SDL_GetVideoInfo()->video_mem) / 1048576; // [MiB] +} + +} // namespace gfx Index: ps/trunk/source/lib/sysdep/gfx.h =================================================================== --- ps/trunk/source/lib/sysdep/gfx.h (revision 9545) +++ ps/trunk/source/lib/sysdep/gfx.h (revision 9546) @@ -1,86 +1,67 @@ /* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * graphics card detection. */ #ifndef INCLUDED_GFX #define INCLUDED_GFX -const size_t GFX_CARD_LEN = 128; -/** - * description of graphics card. - * initial value is "". - **/ -extern wchar_t gfx_card[GFX_CARD_LEN]; - -// note: increased from 64 by Joe Cocovich; this large size is necessary -// because there must be enough space to list the versions of all drivers -// mentioned in the registry (including unused remnants). -const size_t GFX_DRV_VER_LEN = 256; -/** - * (OpenGL) graphics driver identification and version. - * initial value is "". - **/ -extern wchar_t gfx_drv_ver[GFX_DRV_VER_LEN]; +namespace gfx { /** - * approximate amount of graphics memory [MiB] + * @return description of graphics card, +* or L"" if unknown. **/ -extern int gfx_mem; +LIB_API std::wstring CardName(); /** - * detect graphics card and set the above information. + * @return string describing the graphics driver and its version, + * or L"" if unknown. **/ -extern void gfx_detect(); +LIB_API std::wstring DriverInfo(); +LIB_API size_t MemorySizeMiB(); /** - * get current video mode. - * - * this is useful when choosing a new video mode. + * (useful for choosing a new video mode) * * @param xres, yres (optional out) resolution [pixels] * @param bpp (optional out) bits per pixel * @param freq (optional out) vertical refresh rate [Hz] - * @return Status; INFO::OK unless: some information was requested - * (i.e. pointer is non-NULL) but cannot be returned. - * on failure, the outputs are all left unchanged (they are - * assumed initialized to defaults) + * @return Status (if negative, outputs were left unchanged) **/ -extern Status gfx_get_video_mode(int* xres, int* yres, int* bpp, int* freq); +LIB_API Status GetVideoMode(int* xres, int* yres, int* bpp, int* freq); /** - * get monitor dimensions. - * - * this is useful for determining aspect ratio. + * (useful for determining aspect ratio) * * @param width_mm (out) screen width [mm] * @param height_mm (out) screen height [mm] - * @return Status. on failure, the outputs are all left unchanged - * on failure, the outputs are all left unchanged (they are - * assumed initialized to defaults) + * @return Status (if if negative, outputs were left unchanged) **/ -extern Status gfx_get_monitor_size(int& width_mm, int& height_mm); +LIB_API Status GetMonitorSize(int& width_mm, int& height_mm); + +} // namespace gfx #endif // #ifndef INCLUDED_GFX