0ad/source/ps/Util.cpp
janwas 7a5655edde # major refactoring of system-dependent code (simplifies build system)
cpu.cpp: avoided the need for wrapper functions by calling the
OS-specific function directly (declared in central header, implemented
in the platform's cpp file)

avoid the need for init in cpu and ia32 via if(!init) Init() pattern.

optimized memcpy now requires SSE support

remove error-prone CAS macro; replace with cpu_CAS
config: no longer require inline asm for float->int conversions
lib_error: remove special-case in CHECK_ERR for windows (no longer
needed)

This was SVN commit r5365.
2007-09-23 15:36:29 +00:00

316 lines
9.1 KiB
C++

#include "precompiled.h"
#include "lib/posix/posix_utsname.h"
#include "lib/posix/posix_sock.h"
#include "lib/res/file/path.h"
#include "lib/res/file/vfs.h"
#include "lib/ogl.h"
#include "lib/timer.h"
#include "lib/sysdep/gfx.h"
#include "lib/sysdep/snd.h"
#include "lib/sysdep/cpu.h"
#include "lib/res/res.h"
#include "lib/res/graphics/tex.h"
#include "ps/GameSetup/Config.h"
#include "ps/GameSetup/GameSetup.h"
#include "ps/Game.h"
#include "renderer/Renderer.h"
#include "maths/MathUtil.h"
#include "graphics/GameView.h"
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("write_sys_info");
// get_cpu_info and gfx_detect already called during init - see call site
snd_detect();
struct utsname un;
uname(&un);
char N_path[PATH_MAX];
(void)file_make_full_native_path("../logs/system_info.txt", N_path);
FILE* f = fopen(N_path, "w");
if(!f)
return;
// current timestamp (redundant WRT OS timestamp, but that is not
// visible when people are posting this file's contents online)
{
char timestamp_buf[100] = {'\0'};
time_t seconds;
time(&seconds);
struct tm* t = gmtime(&seconds);
const size_t chars_written = strftime(timestamp_buf, ARRAY_SIZE(timestamp_buf), "(generated %Y-%m-%d %H:%M:%S UTC)", t);
debug_assert(chars_written != 0);
fprintf(f, "%s\n\n", timestamp_buf);
}
// 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(), cpu_NumPackages(), cpu_CoresPerPackage(), cpu_LogicalPerCore());
const double cpu_freq = 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 : %lu MiB; %lu MiB free\n", cpu_MemorySize(CPU_MEM_TOTAL)/MiB, cpu_MemorySize(CPU_MEM_AVAILABLE)/MiB);
// graphics
fprintf(f, "Graphics Card : %s\n", gfx_card);
fprintf(f, "OpenGL Drivers : %s; %s\n", glGetString(GL_VERSION), gfx_drv_ver);
fprintf(f, "Video Mode : %dx%d:%d@%d\n", g_xres, g_yres, g_bpp, g_freq);
// sound
fprintf(f, "Sound Card : %s\n", snd_card);
fprintf(f, "Sound Drivers : %s\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(uint 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());
fclose(f);
f = 0;
}
// not thread-safe!
static const wchar_t* HardcodedErrorString(int err)
{
char description[200];
error_description_r((LibError)err, description, ARRAY_SIZE(description));
static wchar_t output_buf[200];
mbstowcs(output_buf, description, ARRAY_SIZE(output_buf));
return output_buf;
}
// not thread-safe!
const wchar_t* ErrorString(int err)
{
// language file not available (yet)
return HardcodedErrorString(err);
// TODO: load from language file
}
static NextNumberedFilenameInfo screenshot_nfi;
// <extension> 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 char* extension)
{
// get next available numbered filename
// .. bake extension into format string.
// note: %04d -> always 4 digits, so sorting by filename works correctly.
char file_format_string[PATH_MAX];
snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension);
file_format_string[PATH_MAX-1] = '\0';
char fn[PATH_MAX];
next_numbered_filename(file_format_string, &screenshot_nfi, fn);
const char* atom_fn = file_make_unique_fn_copy(fn);
const int w = g_xres, h = g_yres;
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(!strcasecmp(extension, "bmp"))
{
fmt = GL_BGR;
flags |= TEX_BGR;
}
const size_t img_size = w * h * bpp/8;
const size_t hdr_size = tex_hdr_size(fn);
FileIOBuf buf = file_buf_alloc(hdr_size+img_size, atom_fn, FB_FROM_HEAP);
GLvoid* img = (u8*)buf + hdr_size;
Tex t;
if(tex_wrap(w, h, bpp, flags, img, &t) < 0)
return;
glReadPixels(0, 0, w, h, fmt, GL_UNSIGNED_BYTE, img);
(void)tex_write(&t, fn);
(void)tex_free(&t);
(void)file_buf_free(buf, FB_FROM_HEAP);
}
// Similar to WriteScreenshot, but generates an image of size 640*tiles x 480*tiles.
void WriteBigScreenshot(const char* extension, int tiles)
{
// get next available numbered filename
// .. bake extension into format string.
// note: %04d -> always 4 digits, so sorting by filename works correctly.
char file_format_string[PATH_MAX];
snprintf(file_format_string, PATH_MAX, "screenshots/screenshot%%04d.%s", extension);
file_format_string[PATH_MAX-1] = '\0';
char fn[PATH_MAX];
next_numbered_filename(file_format_string, &screenshot_nfi, fn);
const char* atom_fn = file_make_unique_fn_copy(fn);
// 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;
debug_assert(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(!strcasecmp(extension, "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(fn);
void* tile_data = malloc(tile_size);
if(!tile_data)
WARN_ERR_RETURN(ERR::NO_MEM);
FileIOBuf img_buf = file_buf_alloc(hdr_size+img_size, atom_fn, FB_FROM_HEAP);
Tex t;
GLvoid* img = (u8*)img_buf + hdr_size;
if(tex_wrap(img_w, img_h, bpp, flags, img, &t) < 0)
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);
// 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);
Render();
// 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;
cpu_memcpy(dest, src, tile_w * bpp/8);
}
}
}
// 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);
}
(void)tex_write(&t, fn);
(void)tex_free(&t);
free(tile_data);
(void)file_buf_free(img_buf, FB_FROM_HEAP);
}