Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
Util.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2009 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "ps/Util.h"
21 
23 #include "lib/ogl.h"
24 #include "lib/timer.h"
25 #include "lib/bits.h" // round_up
27 #include "lib/sysdep/sysdep.h" // sys_OpenFile
28 #include "lib/sysdep/gfx.h"
29 #include "lib/sysdep/snd.h"
30 #include "lib/sysdep/cpu.h"
31 #include "lib/sysdep/os_cpu.h"
32 #if ARCH_X86_X64
34 #endif
35 #include "lib/sysdep/smbios.h"
36 #include "lib/tex/tex.h"
37 
38 #include "ps/GameSetup/Config.h"
39 #include "ps/GameSetup/GameSetup.h"
40 #include "ps/Game.h"
41 #include "ps/CLogger.h"
42 #include "ps/Filesystem.h"
43 #include "ps/VideoMode.h"
44 #include "renderer/Renderer.h"
45 #include "maths/MathUtil.h"
46 #include "graphics/GameView.h"
47 
48 extern CStrW g_CursorName;
49 
50 static std::string SplitExts(const char *exts)
51 {
52  std::string str = exts;
53  std::string ret = "";
54  size_t idx = str.find_first_of(" ");
55  while(idx != std::string::npos)
56  {
57  if(idx >= str.length() - 1)
58  {
59  ret += str;
60  break;
61  }
62 
63  ret += str.substr(0, idx);
64  ret += "\n";
65  str = str.substr(idx + 1);
66  idx = str.find_first_of(" ");
67  }
68 
69  return ret;
70 }
71 
72 
74 {
75  TIMER(L"write_sys_info");
76 
77  // get_cpu_info and gfx_detect already called during init - see call site
78  snd_detect();
79 
80  struct utsname un;
81  uname(&un);
82 
83  OsPath pathname = psLogDir()/"system_info.txt";
84  FILE* f = sys_OpenFile(pathname, "w");
85  if(!f)
86  return;
87 
88  // current timestamp (redundant WRT OS timestamp, but that is not
89  // visible when people are posting this file's contents online)
90  {
91  wchar_t timestampBuf[100] = {'\0'};
92  time_t seconds;
93  time(&seconds);
94  struct tm* t = gmtime(&seconds);
95  const size_t charsWritten = wcsftime(timestampBuf, ARRAY_SIZE(timestampBuf), L"(generated %Y-%m-%d %H:%M:%S UTC)", t);
96  ENSURE(charsWritten != 0);
97  fprintf(f, "%ls\n\n", timestampBuf);
98  }
99 
100  // OS
101  fprintf(f, "OS : %s %s (%s)\n", un.sysname, un.release, un.version);
102 
103  // CPU
104  fprintf(f, "CPU : %s, %s", un.machine, cpu_IdentifierString());
105 #if ARCH_X86_X64
106  fprintf(f, " (%dx%dx%d)", (int)topology::NumPackages(), (int)topology::CoresPerPackage(), (int)topology::LogicalPerCore());
107 #endif
108  double cpuClock = os_cpu_ClockFrequency(); // query OS (may fail)
109 #if ARCH_X86_X64
110  if(cpuClock <= 0.0)
111  cpuClock = x86_x64::ClockFrequency(); // measure (takes a few ms)
112 #endif
113  if(cpuClock > 0.0)
114  {
115  if(cpuClock < 1e9)
116  fprintf(f, ", %.2f MHz\n", cpuClock*1e-6);
117  else
118  fprintf(f, ", %.2f GHz\n", cpuClock*1e-9);
119  }
120  else
121  fprintf(f, "\n");
122 
123  // memory
124  fprintf(f, "Memory : %u MiB; %u MiB free\n", (unsigned)os_cpu_MemorySize(), (unsigned)os_cpu_MemoryAvailable());
125 
126  // graphics
127  const std::wstring cardName = gfx::CardName();
128  const std::wstring driverInfo = gfx::DriverInfo();
129  fprintf(f, "Graphics Card : %ls\n", cardName.c_str());
130  fprintf(f, "OpenGL Drivers : %s; %ls\n", glGetString(GL_VERSION), driverInfo.c_str());
131  fprintf(f, "Video Mode : %dx%d:%d\n", g_VideoMode.GetXRes(), g_VideoMode.GetYRes(), g_VideoMode.GetBPP());
132 
133  // sound
134  fprintf(f, "Sound Card : %ls\n", snd_card);
135  fprintf(f, "Sound Drivers : %ls\n", snd_drv_ver);
136 
137  // OpenGL extensions (write them last, since it's a lot of text)
138  const char* exts = ogl_ExtensionString();
139  if (!exts) exts = "{unknown}";
140  fprintf(f, "\nOpenGL Extensions: \n%s\n", SplitExts(exts).c_str());
141 
142  // System Management BIOS (even more text than OpenGL extensions)
143  std::string smbios = SMBIOS::StringizeStructures(SMBIOS::GetStructures());
144  fprintf(f, "\nSMBIOS: \n%s\n", smbios.c_str());
145 
146  fclose(f);
147  f = 0;
148 }
149 
150 
151 // not thread-safe!
152 static const wchar_t* HardcodedErrorString(int err)
153 {
154  static wchar_t description[200];
155  StatusDescription((Status)err, description, ARRAY_SIZE(description));
156  return description;
157 }
158 
159 // not thread-safe!
160 const wchar_t* ErrorString(int err)
161 {
162  // language file not available (yet)
163  return HardcodedErrorString(err);
164 
165  // TODO: load from language file
166 }
167 
168 
169 
170 // write the specified texture to disk.
171 // note: <t> cannot be made const because the image may have to be
172 // transformed to write it out in the format determined by <fn>'s extension.
173 Status tex_write(Tex* t, const VfsPath& filename)
174 {
175  DynArray da;
176  RETURN_STATUS_IF_ERR(tex_encode(t, filename.Extension(), &da));
177 
178  // write to disk
179  Status ret = INFO::OK;
180  {
181  shared_ptr<u8> file = DummySharedPtr(da.base);
182  const ssize_t bytes_written = g_VFS->CreateFile(filename, file, da.pos);
183  if(bytes_written > 0)
184  ENSURE(bytes_written == (ssize_t)da.pos);
185  else
186  ret = (Status)bytes_written;
187  }
188 
189  (void)da_free(&da);
190  return ret;
191 }
192 
193 
195 
196 // <extension> identifies the file format that is to be written
197 // (case-insensitive). examples: "bmp", "png", "jpg".
198 // BMP is good for quick output at the expense of large files.
200 {
201  // get next available numbered filename
202  // note: %04d -> always 4 digits, so sorting by filename works correctly.
203  const VfsPath basenameFormat(L"screenshots/screenshot%04d");
204  const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
205  VfsPath filename;
206  vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
207 
208  const size_t w = (size_t)g_xres, h = (size_t)g_yres;
209  const size_t bpp = 24;
210  GLenum fmt = GL_RGB;
211  int flags = TEX_BOTTOM_UP;
212  // we want writing BMP to be as fast as possible,
213  // so read data from OpenGL in BMP format to obviate conversion.
214  if(extension == L".bmp")
215  {
216 #if !CONFIG2_GLES // GLES doesn't support BGR
217  fmt = GL_BGR;
218  flags |= TEX_BGR;
219 #endif
220  }
221 
222  // Hide log messages and re-render
223  RenderLogger(false);
224  Render();
225  RenderLogger(true);
226 
227  const size_t img_size = w * h * bpp/8;
228  const size_t hdr_size = tex_hdr_size(filename);
229  shared_ptr<u8> buf;
230  AllocateAligned(buf, hdr_size+img_size, maxSectorSize);
231  GLvoid* img = buf.get() + hdr_size;
232  Tex t;
233  if(tex_wrap(w, h, bpp, flags, buf, hdr_size, &t) < 0)
234  return;
235  glReadPixels(0, 0, (GLsizei)w, (GLsizei)h, fmt, GL_UNSIGNED_BYTE, img);
236 
237  if (tex_write(&t, filename) == INFO::OK)
238  {
239  OsPath realPath;
240  g_VFS->GetRealPath(filename, realPath);
241  LOGMESSAGERENDER(L"Screenshot written to '%ls'", realPath.string().c_str());
242  }
243  else
244  LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str());
245 
246  tex_free(&t);
247 }
248 
249 
250 
251 // Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
252 void WriteBigScreenshot(const VfsPath& extension, int tiles)
253 {
254  // If the game hasn't started yet then use WriteScreenshot to generate the image.
255  if(g_Game == NULL){ WriteScreenshot(L".bmp"); return; }
256 
257  // get next available numbered filename
258  // note: %04d -> always 4 digits, so sorting by filename works correctly.
259  const VfsPath basenameFormat(L"screenshots/screenshot%04d");
260  const VfsPath filenameFormat = basenameFormat.ChangeExtension(extension);
261  VfsPath filename;
262  vfs::NextNumberedFilename(g_VFS, filenameFormat, s_nextScreenshotNumber, filename);
263 
264  // Slightly ugly and inflexible: Always draw 640*480 tiles onto the screen, and
265  // hope the screen is actually large enough for that.
266  const int tile_w = 640, tile_h = 480;
267  ENSURE(g_xres >= tile_w && g_yres >= tile_h);
268 
269  const int img_w = tile_w*tiles, img_h = tile_h*tiles;
270  const int bpp = 24;
271  GLenum fmt = GL_RGB;
272  int flags = TEX_BOTTOM_UP;
273  // we want writing BMP to be as fast as possible,
274  // so read data from OpenGL in BMP format to obviate conversion.
275  if(extension == L".bmp")
276  {
277 #if !CONFIG2_GLES // GLES doesn't support BGR
278  fmt = GL_BGR;
279  flags |= TEX_BGR;
280 #endif
281  }
282 
283  const size_t img_size = img_w * img_h * bpp/8;
284  const size_t tile_size = tile_w * tile_h * bpp/8;
285  const size_t hdr_size = tex_hdr_size(filename);
286  void* tile_data = malloc(tile_size);
287  if(!tile_data)
288  {
290  return;
291  }
292  shared_ptr<u8> img_buf;
293  AllocateAligned(img_buf, hdr_size+img_size, maxSectorSize);
294 
295  Tex t;
296  GLvoid* img = img_buf.get() + hdr_size;
297  if(tex_wrap(img_w, img_h, bpp, flags, img_buf, hdr_size, &t) < 0)
298  {
299  free(tile_data);
300  return;
301  }
302 
303  ogl_WarnIfError();
304 
305  // Resize various things so that the sizes and aspect ratios are correct
306  {
307  g_Renderer.Resize(tile_w, tile_h);
308  SViewPort vp = { 0, 0, tile_w, tile_h };
309  g_Game->GetView()->GetCamera()->SetViewPort(vp);
311  }
312 
313 #if !CONFIG2_GLES
314  // Temporarily move everything onto the front buffer, so the user can
315  // see the exciting progress as it renders (and can tell when it's finished).
316  // (It doesn't just use SwapBuffers, because it doesn't know whether to
317  // call the SDL version or the Atlas version.)
318  GLint oldReadBuffer, oldDrawBuffer;
319  glGetIntegerv(GL_READ_BUFFER, &oldReadBuffer);
320  glGetIntegerv(GL_DRAW_BUFFER, &oldDrawBuffer);
321  glDrawBuffer(GL_FRONT);
322  glReadBuffer(GL_FRONT);
323 #endif
324 
325  // Hide the cursor
326  CStrW oldCursor = g_CursorName;
327  g_CursorName = L"";
328 
329  // Render each tile
330  for (int tile_y = 0; tile_y < tiles; ++tile_y)
331  {
332  for (int tile_x = 0; tile_x < tiles; ++tile_x)
333  {
334  // Adjust the camera to render the appropriate region
335  g_Game->GetView()->GetCamera()->SetProjectionTile(tiles, tile_x, tile_y);
336 
337  RenderLogger(false);
338  RenderGui(false);
339  Render();
340  RenderGui(true);
341  RenderLogger(true);
342 
343  // Copy the tile pixels into the main image
344  glReadPixels(0, 0, tile_w, tile_h, fmt, GL_UNSIGNED_BYTE, tile_data);
345  for (int y = 0; y < tile_h; ++y)
346  {
347  void* dest = (char*)img + ((tile_y*tile_h + y) * img_w + (tile_x*tile_w)) * bpp/8;
348  void* src = (char*)tile_data + y * tile_w * bpp/8;
349  memcpy(dest, src, tile_w * bpp/8);
350  }
351  }
352  }
353 
354  // Restore the old cursor
355  g_CursorName = oldCursor;
356 
357 #if !CONFIG2_GLES
358  // Restore the buffer settings
359  glDrawBuffer(oldDrawBuffer);
360  glReadBuffer(oldReadBuffer);
361 #endif
362 
363  // Restore the viewport settings
364  {
365  g_Renderer.Resize(g_xres, g_yres);
366  SViewPort vp = { 0, 0, g_xres, g_yres };
367  g_Game->GetView()->GetCamera()->SetViewPort(vp);
369  g_Game->GetView()->GetCamera()->SetProjectionTile(1, 0, 0);
370  }
371 
372  if (tex_write(&t, filename) == INFO::OK)
373  {
374  OsPath realPath;
375  g_VFS->GetRealPath(filename, realPath);
376  LOGMESSAGERENDER(L"Screenshot written to '%ls'", realPath.string().c_str());
377  }
378  else
379  LOGERROR(L"Error writing screenshot to '%ls'", filename.string().c_str());
380 
381  tex_free(&t);
382  free(tile_data);
383 }
void WriteSystemInfo()
Definition: Util.cpp:73
double os_cpu_ClockFrequency()
Definition: os_cpu.cpp:43
size_t NumPackages()
Definition: topology.cpp:244
const char * cpu_IdentifierString()
Definition: arm.cpp:46
#define WARN_IF_ERR(expression)
Definition: status.h:265
static const char * exts
Definition: ogl.cpp:61
size_t LogicalPerCore()
Definition: topology.cpp:256
const OsPath & psLogDir()
Definition: Pyrogenesis.cpp:96
const Status OK
Definition: status.h:386
#define LOGERROR
Definition: CLogger.h:35
void NextNumberedFilename(const PIVFS &fs, const VfsPath &pathnameFormat, size_t &nextNumber, VfsPath &nextPathname)
Determine the next available pathname with a given format.
Definition: vfs_util.cpp:95
char version[16]
Definition: wutsname.h:35
std::string StringizeStructures(const Structures *structures)
Definition: smbios.cpp:711
CStrW g_CursorName
Definition: Config.cpp:31
int g_xres
Definition: Config.cpp:58
static const uintptr_t maxSectorSize
Definition: alignment.h:82
int GetYRes()
Definition: VideoMode.cpp:470
const wchar_t * ErrorString(int err)
Definition: Util.cpp:160
provides a memory range that can be expanded but doesn&#39;t waste physical memory or relocate itself...
Definition: dynarray.h:39
size_t tex_hdr_size(const VfsPath &filename)
return the minimum header size (i.e.
Definition: tex.cpp:703
void RenderGui(bool RenderingState)
enable/disable rendering of the GUI (intended mainly for screenshots)
Definition: GameSetup.cpp:1056
indicates B and R pixel components are exchanged.
Definition: tex.h:163
void SetCameraProjection()
Definition: GameView.cpp:1061
void WriteScreenshot(const VfsPath &extension)
Definition: Util.cpp:199
const Structures * GetStructures()
Definition: smbios.cpp:689
std::wstring CardName()
Definition: gfx.cpp:41
flags &amp; TEX_ORIENTATION is a field indicating orientation, i.e.
Definition: tex.h:190
static const wchar_t * HardcodedErrorString(int err)
Definition: Util.cpp:152
#define ARRAY_SIZE(name)
size_t os_cpu_MemorySize()
Definition: os_cpu.cpp:63
std::wstring DriverInfo()
Definition: gfx.cpp:70
#define g_Renderer
Definition: Renderer.h:61
FILE * sys_OpenFile(const OsPath &pathname, const char *mode)
open a file like with fopen (but taking an OsPath argument).
Definition: unix.cpp:373
char machine[9]
Definition: wutsname.h:36
CVideoMode g_VideoMode
Definition: VideoMode.cpp:42
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
u8 * base
Definition: dynarray.h:41
Definition: path.h:75
char release[9]
Definition: wutsname.h:34
const String & string() const
Definition: path.h:123
size_t os_cpu_MemoryAvailable()
Definition: bcpu.cpp:98
size_t CoresPerPackage()
Definition: topology.cpp:250
double ClockFrequency()
measure the CPU clock frequency via rdtsc and timer_Time.
Definition: x86_x64.cpp:429
const char * ogl_ExtensionString()
get a list of all supported extensions.
Definition: ogl.cpp:68
int g_yres
Definition: Config.cpp:58
char sysname[9]
Definition: wutsname.h:32
CCamera * GetCamera()
Definition: GameView.cpp:390
CGame * g_Game
Globally accessible pointer to the CGame object.
Definition: Game.cpp:56
i64 Status
Error handling system.
Definition: status.h:171
int uname(struct utsname *un)
Definition: wutsname.cpp:30
shared_ptr< T > DummySharedPtr(T *ptr)
Definition: shared_ptr.h:38
void Render()
Definition: GameSetup.cpp:192
stores all data describing an image.
Definition: tex.h:210
Status tex_write(Tex *t, const VfsPath &filename)
Definition: Util.cpp:173
wchar_t snd_drv_ver[SND_DRV_VER_LEN]
sound driver identification and version.
Definition: snd.cpp:36
size_t pos
Definition: dynarray.h:46
wchar_t snd_card[SND_CARD_LEN]
description of sound card.
Definition: snd.cpp:35
void SetViewPort(const SViewPort &viewport)
Definition: Camera.cpp:136
intptr_t ssize_t
Definition: wposix_types.h:82
#define TIMER(description)
Measures the time taken to execute code up until end of the current scope; displays it via debug_prin...
Definition: timer.h:108
static std::string SplitExts(const char *exts)
Definition: Util.cpp:50
CGameView * GetView()
Get the pointer to the game view object.
Definition: Game.h:128
const char * extension
Definition: mongoose.cpp:1736
void snd_detect()
detect sound card and set the above information.
Definition: snd.cpp:38
void RenderLogger(bool RenderingState)
Definition: GameSetup.cpp:1061
static Status AllocateAligned(shared_ptr< T > &p, size_t size, size_t alignment=cacheLineSize)
Definition: shared_ptr.h:66
Path ChangeExtension(Path extension) const
Definition: path.h:185
Path Extension() const
Definition: path.h:176
wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
Definition: status.cpp:79
Status tex_wrap(size_t w, size_t h, size_t bpp, size_t flags, const shared_ptr< u8 > &data, size_t ofs, Tex *t)
store the given image data into a Tex object; this will be as if it had been loaded via tex_load...
Definition: tex.cpp:593
#define LOGMESSAGERENDER
Definition: CLogger.h:33
Status da_free(DynArray *da)
free all memory (address space + physical) that constitutes the given array.
Definition: dynarray.cpp:80
void ogl_WarnIfError()
raise a warning (break into the debugger) if an OpenGL error is pending.
Definition: ogl.cpp:398
int GetXRes()
Definition: VideoMode.cpp:464
int GetBPP()
Definition: VideoMode.cpp:476
PIVFS g_VFS
Definition: Filesystem.cpp:30
const Status NO_MEM
Definition: status.h:430
static size_t s_nextScreenshotNumber
Definition: Util.cpp:194
void SetProjectionTile(int tiles, int tile_x, int tile_y)
Definition: Camera.cpp:68
void WriteBigScreenshot(const VfsPath &extension, int tiles)
Definition: Util.cpp:252
void tex_free(Tex *t)
free all resources associated with the image and make further use of it impossible.
Definition: tex.cpp:610
Status tex_encode(Tex *t, const OsPath &extension, DynArray *da)
encode a texture into a memory buffer in the desired file format.
Definition: tex.cpp:750
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276