diff options
author | Xanadu <none@none> | 2010-07-17 22:58:24 +0200 |
---|---|---|
committer | Xanadu <none@none> | 2010-07-17 22:58:24 +0200 |
commit | b5f8bfd66561e4a63fa8c28961b829a35ceb2fb0 (patch) | |
tree | 6691fda7c0985077aeb6ff3a93e829447dddd736 /externals/g3dlite/G3D.lib/source/System.cpp | |
parent | ec244dbe366e84a93c8fa1ef294af4a2e4e3b0b1 (diff) | |
parent | dc510c9a143de1977daedea0aefb9589c01adde2 (diff) |
Merge
--HG--
branch : trunk
Diffstat (limited to 'externals/g3dlite/G3D.lib/source/System.cpp')
-rw-r--r-- | externals/g3dlite/G3D.lib/source/System.cpp | 1864 |
1 files changed, 1864 insertions, 0 deletions
diff --git a/externals/g3dlite/G3D.lib/source/System.cpp b/externals/g3dlite/G3D.lib/source/System.cpp new file mode 100644 index 00000000000..748e13257f1 --- /dev/null +++ b/externals/g3dlite/G3D.lib/source/System.cpp @@ -0,0 +1,1864 @@ +/** + @file System.cpp + + @maintainer Morgan McGuire, matrix@graphics3d.com + + Note: every routine must call init() first. + + There are two kinds of detection used in this file. At compile + time, the _MSC_VER #define is used to determine whether x86 assembly + can be used at all. At runtime, processor detection is used to + determine if we can safely call the routines that use that assembly. + + @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm + @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1 + @cite Michael Herf http://www.stereopsis.com/memcpy.html + + @created 2003-01-25 + @edited 2008-09-02 + */ + +#include "G3D/platform.h" +#include "G3D/System.h" +#include "G3D/debug.h" +#include "G3D/fileutils.h" +#include "G3D/TextOutput.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/Crypto.h" +#include "G3D/prompt.h" +#include "G3D/Log.h" +#include <time.h> + +#include <cstring> +#include <cstdio> + +// Uncomment the following line to turn off G3D::System memory +// allocation and use the operating system's malloc. +//#define NO_BUFFERPOOL + +#if defined(__i386__) || defined(__x86_64__) || defined(G3D_WIN32) +# define G3D_NOT_OSX_PPC +#endif + +#ifdef G3D_WIN32 + +# include <conio.h> +# include <sys/timeb.h> +# include "G3D/RegistryUtil.h" + +#elif defined(G3D_LINUX) + + #include <stdlib.h> + #include <stdio.h> + #include <errno.h> + #include <sys/types.h> + #include <sys/select.h> + #include <termios.h> + #include <unistd.h> + #include <sys/ioctl.h> + #include <sys/time.h> + #include <pthread.h> + +#elif defined(G3D_OSX) + + #include <stdlib.h> + #include <stdio.h> + #include <errno.h> + #include <sys/types.h> + #include <sys/sysctl.h> + #include <sys/select.h> + #include <sys/time.h> + #include <termios.h> + #include <unistd.h> + #include <pthread.h> + #include <mach-o/arch.h> + + #include <sstream> + #include <CoreServices/CoreServices.h> +#endif + +#if defined(SSE) + #include <xmmintrin.h> +#endif + +namespace G3D { + +struct CpuInfo +{ +public: + int m_cpuSpeed; + bool m_hasCPUID; + bool m_hasRDTSC; + bool m_hasMMX; + bool m_hasSSE; + bool m_hasSSE2; + bool m_hasSSE3; + bool m_has3DNOW; + char m_cpuVendorStr[1024]; +}; + +// helper macro to call cpuid functions and return all values +#ifdef _MSC_VER + + // VC on Intel +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + __asm mov eax, func \ + __asm cpuid \ + __asm mov areg, eax \ + __asm mov breg, ebx \ + __asm mov creg, ecx \ + __asm mov dreg, edx + +#elif defined(__GNUC__) && defined(G3D_OSX_INTEL) + // GCC on OS X intel +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + areg = 0; \ + breg = 0; \ + creg = 0; \ + dreg = 0; +#else + // Any other compiler/platform, likely GCC +# define CALL_CPUID(func, areg, breg, creg, dreg) \ + __asm__ ( \ + "cpuid \n": \ + "=a" (areg), \ + "=b" (breg), \ + "=c" (creg), \ + "=d" (dreg): \ + "a" (func) \ + ); +#endif + +// this holds the data directory set by the application (currently GApp) for use by findDataFile +static char g_appDataDir[FILENAME_MAX] = ""; + +static CpuInfo g_cpuInfo = { + 0, false, false, false, false, false, false, false, {'U', 'n', 'k', 'n', 'o', 'w', 'n', '\0'}}; + +static G3DEndian _machineEndian = G3D_LITTLE_ENDIAN; +static char _cpuArchCstr[1024]; +static char _operatingSystemCstr[1024]; + +#ifdef G3D_WIN32 +/** Used by getTick() for timing */ +static LARGE_INTEGER _start; +static LARGE_INTEGER _counterFrequency; +#else +static struct timeval _start; +#endif + +static char versionCstr[1024]; +System::OutOfMemoryCallback System::outOfMemoryCallback = NULL; + +#ifdef G3D_OSX +long System::m_OSXCPUSpeed; +double System::m_secondsPerNS; +#endif + +/** The Real-World time of System::getTick() time 0. Set by initTime */ +static RealTime realWorldGetTickTime0; + + +static unsigned int maxSupportedCPUIDLevel = 0; +static unsigned int maxSupportedExtendedLevel = 0; + +/** Checks if the CPUID command is available on the processor (called from init) */ +static void checkForCPUID(); + +/** ReadRead the standard processor extensions. Called from init(). */ +static void getStandardProcessorExtensions(); + +/** Called from init */ +static void initTime(); + + +std::string System::findDataFile +(const std::string& full, + bool errorIfNotFound) { + + if (fileExists(full)) { + return full; + } + + std::string initialAppDataDir(g_appDataDir); + + std::string name = filenameBaseExt(full); + std::string originalPath = filenamePath(full); + + // Search several paths + Array<std::string> pathBase; + + int backlen = 4; + + // add what should be the current working directory + pathBase.append(""); + + // add application specified data directory to be searched first + pathBase.append(initialAppDataDir); + + // try walking back along the directory tree + std::string prev = ""; + for (int i = 0; i < backlen; ++i) { + pathBase.append(originalPath + prev); + prev = prev + "../"; + } + + prev = "../"; + for (int i = 0; i < backlen; ++i) { + pathBase.append(prev); + prev = prev + "../"; + } + + // Hard-code in likely install directories + int ver = G3D_VER; + std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100); + + if (G3D_VER % 100 != 0) { + lname = lname + format("-b%02d/", ver % 100); + } else { + lname = lname + "/"; + } + + // Look in some other likely places +# ifdef G3D_WIN32 + std::string lpath = "libraries/G3D/"; + pathBase.append(std::string("c:/") + lpath); + pathBase.append(std::string("d:/") + lpath); + pathBase.append(std::string("e:/") + lpath); + pathBase.append(std::string("f:/") + lpath); + pathBase.append(std::string("g:/") + lpath); + pathBase.append(std::string("x:/") + lpath); +# endif +# if defined(G3D_LINUX) + pathBase.append("/usr/local/"); + pathBase.append("/course/cs224/"); + pathBase.append("/map/gfx0/common/games/"); +# endif +# if defined(G3D_FREEBSD) + pathBase.append("/usr/local/"); + pathBase.append("/usr/local/371/"); + pathBase.append("/usr/cs-local/371/"); +# endif +# if defined(G3D_OSX) + pathBase.append("/usr/local/" + lname); + pathBase.append("/Volumes/McGuire/Projects/"); +# endif + + // Add the library name to all variations + int N = pathBase.size(); + for (int i = 0; i < N; ++i) { + pathBase.append(pathBase[i] + lname); + pathBase.append(pathBase[i] + "G3D/"); + } + + Array<std::string> subDir; + subDir.append("", "font/", "sky/", "gui/"); + subDir.append("image/", "quake2/", "quake2/players/"); + subDir.append("quake3/", "SuperShader/", "ifs/", "3ds/"); + subDir.append("quake2/speedway/"); + + Array<std::string> path; + for (int p = 0; p < pathBase.size(); ++p) { + for (int s = 0; s < subDir.size(); ++s) { + path.append(pathBase[p] + subDir[s]); + path.append(pathBase[p] + "data/" + subDir[s]); + path.append(pathBase[p] + "data-files/" + subDir[s]); + } + } + + for (int i = 0; i < path.length(); ++i) { + std::string filename = path[i] + name; + if (fileExists(filename)) { + logPrintf("\nWARNING: Could not find '%s' so '%s' " + "was substituted.\n", full.c_str(), + filename.c_str()); + return filename; + } + } + + if (errorIfNotFound) { + // Generate an error message + std::string locations; + for (int i = 0; i < path.size(); ++i) { + locations += path[i] + name + "\n"; + } + alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations); + } + // Not found + return ""; +} + + +void System::setAppDataDir(const std::string& path) { + // just copy the path, it needs to be valid + strncpy(g_appDataDir, path.c_str(), sizeof(g_appDataDir)); +} + + +std::string demoFindData(bool errorIfNotFound) { + // Directories that might contain the data + Array<std::string> potential; + + // Look back up the directory tree + std::string x = "../"; + std::string f = ""; + for (int i = 0; i < 6; ++i) { + potential.append(f); + f = f + x; + } + + // Hard-code in likely install directories + int ver = G3D_VER; + std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100); + + if (G3D_VER % 100 != 0) { + lname = lname + format("-b%02d/", ver % 100); + } else { + lname = lname + "/"; + } + + std::string lpath = "libraries/" + lname; + #ifdef G3D_WIN32 + potential.append(std::string("c:/") + lpath); + potential.append(std::string("d:/") + lpath); + potential.append(std::string("e:/") + lpath); + potential.append(std::string("f:/") + lpath); + potential.append(std::string("g:/") + lpath); + potential.append(std::string("x:/") + lpath); + #elif defined(G3D_LINUX) + potential.append("/usr/local/" + lname); + potential.append("/course/cs224/"); + potential.append("/map/gfx0/common/games/"); + #elif defined(G3D_FREEBSD) + potential.append("/usr/local/" + lname); + potential.append("/usr/local/371/") + potential.append("/usr/cs-local/371/") + #elif defined(G3D_OSX) + potential.append("/usr/local/" + lname); + potential.append("/Volumes/McGuire/Projects/"); + potential.append("/Volumes/McGuire/Projects/G3D/"); + #endif + + // Scan all potentials for the font directory + for (int p = 0; p < potential.size(); ++p) { + std::string path = potential[p]; + //debugPrintf("Looking at: %sdata\n", path.c_str()); + if (fileExists(path + "data") && fileExists(path + "data/font")) { + return path + "data/"; + } + if (fileExists(path + "data-files") && fileExists(path + "data-files/font")) { + return path + "data-files/"; + } + } + + if (errorIfNotFound) { + const char* choice[] = {"Exit"}; + + prompt("Demo Error", "The demo could not locate the data directory. " + "The data is required to run this demo. If you have not downloaded " + "the data zipfile, get it from http://g3d-cpp.sf.net. If you have " + "downloaded it, it needs to be no more than 4 directories above the " + "demo directory.", choice, 1, true); + } + + return ""; +} + +bool System::hasCPUID() { + init(); + return g_cpuInfo.m_hasCPUID; +} + +bool System::hasRDTSC() { + init(); + return g_cpuInfo.m_hasRDTSC; +} + + +bool System::hasSSE() { + init(); + return g_cpuInfo.m_hasSSE; +} + + +bool System::hasSSE2() { + init(); + return g_cpuInfo.m_hasSSE2; +} + +bool System::hasSSE3() { + init(); + return g_cpuInfo.m_hasSSE3; +} + +bool System::hasMMX() { + init(); + return g_cpuInfo.m_hasMMX; +} + + +bool System::has3DNow() { + init(); + return g_cpuInfo.m_has3DNOW; +} + + +const std::string& System::cpuVendor() { + init(); + static const std::string _cpuVendor = g_cpuInfo.m_cpuVendorStr; + return _cpuVendor; +} + + +G3DEndian System::machineEndian() { + init(); + return _machineEndian; +} + +const std::string& System::operatingSystem() { + init(); + static const std::string _operatingSystem =_operatingSystemCstr; + return _operatingSystem; +} + + +const std::string& System::cpuArchitecture() { + init(); + static const std::string _cpuArch = _cpuArchCstr; + return _cpuArch; +} + +const std::string& System::build() { + const static std::string b = +# ifdef _DEBUG + "Debug"; +# else + "Release"; +# endif + + return b; +} + +const std::string& System::version() { + init(); + + static const std::string _version = versionCstr; + return _version; +} + + +void System::init() { + // Cannot use most G3D data structures or utility functions in here because + // they are not initialized. + + static bool initialized = false; + + if (initialized) { + return; + } + + initialized = true; + + if ((G3D_VER % 100) != 0) { + sprintf(versionCstr, "G3D %d.%02d beta %d", + G3D_VER / 10000, + (G3D_VER / 100) % 100, + G3D_VER % 100); + } else { + sprintf(versionCstr, "G3D %d.%02d", + G3D_VER / 10000, + (G3D_VER / 100) % 100); + } + + // First of all we check if the CPUID command is available + checkForCPUID(); + + // Figure out if this machine is little or big endian. + { + int32 a = 1; + if (*(uint8*)&a == 1) { + _machineEndian = G3D_LITTLE_ENDIAN; + } else { + _machineEndian = G3D_BIG_ENDIAN; + } + } + +# ifdef G3D_NOT_OSX_PPC + // Process the CPUID information + if (g_cpuInfo.m_hasCPUID) { + // We read the standard CPUID level 0x00000000 which should + // be available on every x86 processor. This fills out + // a string with the processor vendor tag. + unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0, edxreg = 0; + + CALL_CPUID(0x00, eaxreg, ebxreg, ecxreg, edxreg); + + // Then we connect the single register values to the vendor string + *((unsigned int*) g_cpuInfo.m_cpuVendorStr) = ebxreg; + *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 4)) = edxreg; + *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 8)) = ecxreg; + g_cpuInfo.m_cpuVendorStr[12] = '\0'; + + // We can also read the max. supported standard CPUID level + maxSupportedCPUIDLevel = eaxreg & 0xFFFF; + + // Then we read the ext. CPUID level 0x80000000 + CALL_CPUID(0x80000000, eaxreg, ebxreg, ecxreg, edxreg); + + // ...to check the max. supported extended CPUID level + maxSupportedExtendedLevel = eaxreg; + + // Then we switch to the specific processor vendors. + // Fill out _cpuArch based on this information. It will + // be overwritten by the next block of code on Windows, + // but on Linux will stand. + switch (ebxreg) { + case 0x756E6547: // GenuineIntel + strcpy(_cpuArchCstr, "Intel Processor"); + break; + + case 0x68747541: // AuthenticAMD + strcpy(_cpuArchCstr, "AMD Processor"); + break; + + case 0x69727943: // CyrixInstead + strcpy(_cpuArchCstr, "Cyrix Processor"); + break; + + default: + strcpy(_cpuArchCstr, "Unknown Processor Vendor"); + break; + } + } + #endif // G3D_NOT_OSX_PPC + + #ifdef G3D_WIN32 + bool success = RegistryUtil::readInt32 + ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", g_cpuInfo.m_cpuSpeed); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + char* arch; + switch (systemInfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + arch = "Intel"; + break; + + case PROCESSOR_ARCHITECTURE_MIPS: + arch = "MIPS"; + break; + + case PROCESSOR_ARCHITECTURE_ALPHA: + arch = "Alpha"; + break; + + case PROCESSOR_ARCHITECTURE_PPC: + arch = "Power PC"; + break; + + default: + arch = "Unknown"; + } + + uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress; + sprintf(_cpuArchCstr, "%d x %d-bit %s processor", + systemInfo.dwNumberOfProcessors, + (int)(::log((double)maxAddr) / ::log(2.0) + 2.0), + arch); + // _CPUSpeed / (1024.0 * 1024)); + + OSVERSIONINFO osVersionInfo; + osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + success = GetVersionEx(&osVersionInfo) != 0; + + if (success) { + sprintf(_operatingSystemCstr, "Windows %d.%d build %d Platform %d %s", + osVersionInfo.dwMajorVersion, + osVersionInfo.dwMinorVersion, + osVersionInfo.dwBuildNumber, + osVersionInfo.dwPlatformId, + osVersionInfo.szCSDVersion); + } else { + strcpy(_operatingSystemCstr, "Windows"); + } + + #elif defined(G3D_LINUX) + + { + // Shell out to the 'uname' command + FILE* f = popen("uname -a", "r"); + + int len = 100; + char* r = (char*)::malloc(len * sizeof(char)); + fgets(r, len, f); + // Remove trailing newline + if (r[strlen(r) - 1] == '\n') { + r[strlen(r) - 1] = '\0'; + } + fclose(f); + + strcpy(_operatingSystemCstr, r); + ::free(r); + } + + #elif defined(G3D_OSX) + + // Operating System: + SInt32 macVersion; + Gestalt(gestaltSystemVersion, &macVersion); + + int major = (macVersion >> 8) & 0xFF; + int minor = (macVersion >> 4) & 0xF; + int revision = macVersion & 0xF; + + sprintf(_operatingSystemCstr, "OS X %x.%x.%x", major, minor, revision); + + // Clock Cycle Timing Information: + Gestalt('pclk', &System::m_OSXCPUSpeed); + g_cpuInfo.m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024)); + m_secondsPerNS = 1.0 / 1.0e9; + + // System Architecture: + const NXArchInfo* pInfo = NXGetLocalArchInfo(); + + if (pInfo) { + strcpy(_cpuArchCstr, pInfo->description); + + switch (pInfo->cputype) { + case CPU_TYPE_POWERPC: + switch(pInfo->cpusubtype){ + case CPU_SUBTYPE_POWERPC_750: + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + strcpy(g_cpuInfo.m_cpuVendorStr, "Motorola"); + break; + case CPU_SUBTYPE_POWERPC_970: + strcpy(g_cpuInfo.m_cpuVendorStr, "IBM"); + break; + } + break; + + case CPU_TYPE_I386: + strcpy(g_cpuInfo.m_cpuVendorStr, "Intel"); + break; + } + } + #endif + + initTime(); + + getStandardProcessorExtensions(); +} + +static void checkForCPUID() { + unsigned int bitChanged = 0; + + // We've to check if we can toggle the flag register bit 21. + // If we can't the processor does not support the CPUID command. + +#if defined(_MSC_VER) + __asm { + push eax + push ebx + pushfd + pushfd + pop eax + mov ebx, eax + xor eax, 0x00200000 + push eax + popfd + pushfd + pop eax + popfd + xor eax, ebx + mov bitChanged, eax + pop ebx + pop eax + } +#elif defined(__GNUC__) && defined(i386) && !defined(G3D_OSX_INTEL) + // 32-bit g++ + __asm__ ( + "pushfl # Get original EFLAGS \n" + "pushfl \n" + "popl %%eax \n" + "movl %%eax, %%ecx \n" + "xorl $0x200000, %%eax # Flip ID bit in EFLAGS \n" + "pushl %%eax # Save new EFLAGS value on stack \n" + "popfl # Replace current EFLAGS value \n" + "pushfl # Get new EFLAGS \n" + "popl %%eax # Store new EFLAGS in EAX \n" + "popfl \n" + "xorl %%ecx, %%eax # Can not toggle ID bit, \n" + "movl %%eax, %0 # We have CPUID support \n" + : "=m" (bitChanged) + : // No inputs + : "%eax", "%ecx" + ); +#elif defined(__GNUC__) && defined(__x86_64__) && !defined(G3D_OSX_INTEL) + // x86_64 has SSE and CPUID + + bitChanged = 1; +#else + // Unknown architecture + bitChanged = 0; +#endif + + g_cpuInfo.m_hasCPUID = ((bitChanged == 0) ? false : true); +} + +void getStandardProcessorExtensions() { +#if !defined(G3D_OSX) || defined(G3D_OSX_INTEL) + if (! g_cpuInfo.m_hasCPUID) { + return; + } + + unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0; + unsigned int features = 0; + + // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf + // call cpuid with function 0x01 in EAX + + // Invoking CPUID with '1' in EAX fills out edx with a bit string. + // The bits of this value indicate the presence or absence of + // useful processor features. + CALL_CPUID(0x01, eaxreg, ebxreg, ecxreg, features); + + #define checkBit(var, bit) ((var & (1 << bit)) ? true : false) + + g_cpuInfo.m_hasRDTSC = checkBit(features, 16); + g_cpuInfo.m_hasMMX = checkBit(features, 23); + g_cpuInfo.m_hasSSE = checkBit(features, 25); + g_cpuInfo.m_hasSSE2 = checkBit(features, 26); + g_cpuInfo.m_hasSSE3 = checkBit(ecxreg, 0); + + if (maxSupportedExtendedLevel >= 0x80000001) { + // function 0x80000001 changes bit 31 of edx to 3dnow support flag + CALL_CPUID(0x80000001, eaxreg, ebxreg, ecxreg, features); + g_cpuInfo.m_has3DNOW = checkBit(features, 31); + } else { + g_cpuInfo.m_has3DNOW = false; + } + + #undef checkBit +#endif +} + +#if defined(SSE) + +// Copy in 128 bytes chunks, where each chunk contains 8*float32x4 = 8 * 4 * 4 bytes = 128 bytes +// +// +void memcpySSE2(void* dst, const void* src, int nbytes) { + int remainingBytes = nbytes; + + if (nbytes > 128) { + + // Number of chunks + int N = nbytes / 128; + + float* restrict d = (float*)dst; + const float* restrict s = (const float*)src; + + // Finish when the destination pointer has moved 8N elements + float* stop = d + (N * 8 * 4); + + while (d < stop) { + // Inner loop unrolled 8 times + const __m128 r0 = _mm_loadu_ps(s); + const __m128 r1 = _mm_loadu_ps(s + 4); + const __m128 r2 = _mm_loadu_ps(s + 8); + const __m128 r3 = _mm_loadu_ps(s + 12); + const __m128 r4 = _mm_loadu_ps(s + 16); + const __m128 r5 = _mm_loadu_ps(s + 20); + const __m128 r6 = _mm_loadu_ps(s + 24); + const __m128 r7 = _mm_loadu_ps(s + 28); + + _mm_storeu_ps(d, r0); + _mm_storeu_ps(d + 4, r1); + _mm_storeu_ps(d + 8, r2); + _mm_storeu_ps(d + 12, r3); + _mm_storeu_ps(d + 16, r4); + _mm_storeu_ps(d + 20, r5); + _mm_storeu_ps(d + 24, r6); + _mm_storeu_ps(d + 28, r7); + + s += 32; + d += 32; + } + + remainingBytes -= N * 8 * 4 * 4; + } + + if (remainingBytes > 0) { + // Memcpy the rest + memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes); + } +} +#else + + // Fall back to memcpy + void memcpySSE2(void *dst, const void *src, int nbytes) { + memcpy(dst, src, nbytes); + } + +#endif + +#if defined(G3D_WIN32) && defined(SSE) +/** Michael Herf's fast memcpy */ +void memcpyMMX(void* dst, const void* src, int nbytes) { + int remainingBytes = nbytes; + + if (nbytes > 64) { + _asm { + mov esi, src + mov edi, dst + mov ecx, nbytes + shr ecx, 6 // 64 bytes per iteration + + loop1: + movq mm1, 0[ESI] // Read in source data + movq mm2, 8[ESI] + movq mm3, 16[ESI] + movq mm4, 24[ESI] + movq mm5, 32[ESI] + movq mm6, 40[ESI] + movq mm7, 48[ESI] + movq mm0, 56[ESI] + + movntq 0[EDI], mm1 // Non-temporal stores + movntq 8[EDI], mm2 + movntq 16[EDI], mm3 + movntq 24[EDI], mm4 + movntq 32[EDI], mm5 + movntq 40[EDI], mm6 + movntq 48[EDI], mm7 + movntq 56[EDI], mm0 + + add esi, 64 + add edi, 64 + dec ecx + jnz loop1 + + emms + } + remainingBytes -= ((nbytes >> 6) << 6); + } + + if (remainingBytes > 0) { + // Memcpy the rest + memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes); + } +} + +#else + // Fall back to memcpy + void memcpyMMX(void *dst, const void *src, int nbytes) { + memcpy(dst, src, nbytes); + } + +#endif + + +void System::memcpy(void* dst, const void* src, size_t numBytes) { + if (System::hasSSE2() && System::hasMMX()) { + G3D::memcpyMMX(dst, src, numBytes); + } else if (System::hasSSE() && System::hasMMX()) { + G3D::memcpyMMX(dst, src, numBytes); + } else { + ::memcpy(dst, src, numBytes); + } +} + + +/** Michael Herf's fastest memset. n32 must be filled with the same + character repeated. */ +#if defined(G3D_WIN32) && defined(SSE) + +// On x86 processors, use MMX +void memfill(void *dst, int n32, unsigned long i) { + + int originalSize = i; + int bytesRemaining = i; + + if (i > 16) { + + bytesRemaining = i % 16; + i -= bytesRemaining; + __asm { + movq mm0, n32 + punpckldq mm0, mm0 + mov edi, dst + + loopwrite: + + movntq 0[edi], mm0 + movntq 8[edi], mm0 + + add edi, 16 + sub i, 16 + jg loopwrite + + emms + } + } + + if (bytesRemaining > 0) { + ::memset((uint8*)dst + (originalSize - bytesRemaining), n32, bytesRemaining); + } +} + +#else + +// For non x86 processors, we fall back to the standard memset +void memfill(void *dst, int n32, unsigned long i) { + ::memset(dst, n32, i); +} + +#endif + + +void System::memset(void* dst, uint8 value, size_t numBytes) { + if (System::hasSSE() && System::hasMMX()) { + uint32 v = value; + v = v + (v << 8) + (v << 16) + (v << 24); + G3D::memfill(dst, v, numBytes); + } else { + ::memset(dst, value, numBytes); + } +} + + +std::string& System::appName() { + static std::string n = filenameBase(currentProgramFilename()); + return n; +} + + +std::string System::currentProgramFilename() { + char filename[2048]; + + #ifdef G3D_WIN32 + { + GetModuleFileNameA(NULL, filename, sizeof(filename)); + } + #else + { + int ret = readlink("/proc/self/exe", filename, sizeof(filename)); + + // In case of an error, leave the handling up to the caller + if (ret == -1) { + return ""; + } + + debugAssert((int)sizeof(filename) > ret); + + // Ensure proper NULL termination + filename[ret] = 0; + } + #endif + + return filename; +} + + +void System::sleep(RealTime t) { + + // Overhead of calling this function. + static const RealTime OVERHEAD = .000006; + + RealTime now = time(); + RealTime wakeupTime = now + t - OVERHEAD; + + RealTime remainingTime = wakeupTime - now; + RealTime sleepTime = 0; + + while (remainingTime > 0) { + + + if (remainingTime > 0.001) { + // Safe to use Sleep with a time... sleep for half the remaining time + sleepTime = max(remainingTime * .5, 0.0005); + } else if (remainingTime > 0.0001) { + // Safe to use Sleep with a zero time; + // causes the program to yield only + // the current time slice, and then return. + sleepTime = 0; + } else { + // Not safe to use Sleep; busy wait + sleepTime = -1; + } + + if (sleepTime >= 0) { + #ifdef G3D_WIN32 + // Translate to milliseconds + Sleep((int)(sleepTime * 1e3)); + #else + // Translate to microseconds + usleep((int)(sleepTime * 1e6)); + #endif + } + + now = time(); + remainingTime = wakeupTime - now; + } +} + + +void System::consoleClearScreen() { + #ifdef G3D_WIN32 + system("cls"); + #else + system("clear"); + #endif +} + + +bool System::consoleKeyPressed() { + #ifdef G3D_WIN32 + + return _kbhit() != 0; + + #else + + static const int STDIN = 0; + static bool initialized = false; + + if (! initialized) { + // Use termios to turn off line buffering + termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + #ifdef G3D_LINUX + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; + + #else + + timeval timeout; + fd_set rdset; + + FD_ZERO(&rdset); + FD_SET(STDIN, &rdset); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + return select(STDIN + 1, &rdset, NULL, NULL, &timeout); + #endif + #endif +} + + +int System::consoleReadKey() { + #ifdef G3D_WIN32 + return _getch(); + #else + char c; + read(0, &c, 1); + return c; + #endif +} + + +void initTime() { + #ifdef G3D_WIN32 + if (QueryPerformanceFrequency(&_counterFrequency)) { + QueryPerformanceCounter(&_start); + } + + struct _timeb t; + _ftime(&t); + + realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0); + + #else + gettimeofday(&_start, NULL); + // "sse" = "seconds since epoch". The time + // function returns the seconds since the epoch + // GMT (perhaps more correctly called UTC). + time_t gmt = time(NULL); + + // No call to free or delete is needed, but subsequent + // calls to asctime, ctime, mktime, etc. might overwrite + // local_time_vals. + tm* localTimeVals = localtime(&gmt); + + time_t local = gmt; + + if (localTimeVals) { + // tm_gmtoff is already corrected for daylight savings. + local = local + localTimeVals->tm_gmtoff; + } + + realWorldGetTickTime0 = local; + #endif +} + + +RealTime System::time() { + init(); + #ifdef G3D_WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + return ((RealTime)(now.QuadPart - _start.QuadPart) / + _counterFrequency.QuadPart) + realWorldGetTickTime0; + #else + // Linux resolution defaults to 100Hz. + // There is no need to do a separate RDTSC call as gettimeofday + // actually uses RDTSC when on systems that support it, otherwise + // it uses the system clock. + struct timeval now; + gettimeofday(&now, NULL); + + return (now.tv_sec - _start.tv_sec) + + (now.tv_usec - _start.tv_usec) / 1e6 + + realWorldGetTickTime0; + #endif +} + + +//////////////////////////////////////////////////////////////// + +#define REALPTR_TO_USERPTR(x) ((uint8*)(x) + sizeof (void *)) +#define USERPTR_TO_REALPTR(x) ((uint8*)(x) - sizeof (void *)) +#define REALBLOCK_SIZE(x) ((x) + sizeof (void *)) + +class BufferPool { +public: + + /** Only store buffers up to these sizes (in bytes) in each pool-> + Different pools have different management strategies. + + A large block is preallocated for tiny buffers; they are used with + tremendous frequency. Other buffers are allocated as demanded. + Tiny buffers are 128 bytes long because that seems to align well with + cache sizes on many machines. + */ + enum {tinyBufferSize = 128, smallBufferSize = 1024, medBufferSize = 4096}; + + /** + Most buffers we're allowed to store. + 128000 * 128 = 16 MB (preallocated) + 2048 * 1024 = 2 MB (allocated on demand) + 1024 * 4096 = 4 MB (allocated on demand) + */ + enum {maxTinyBuffers = 128000, maxSmallBuffers = 2048, maxMedBuffers = 1024}; + +private: + + class MemBlock { + public: + void* ptr; + size_t bytes; + + inline MemBlock() : ptr(NULL), bytes(0) {} + inline MemBlock(void* p, size_t b) : ptr(p), bytes(b) {} + }; + + MemBlock smallPool[maxSmallBuffers]; + int smallPoolSize; + + MemBlock medPool[maxMedBuffers]; + int medPoolSize; + + /** The tiny pool is a single block of storage into which all tiny + objects are allocated. This provides better locality for + small objects and avoids the search time, since all tiny + blocks are exactly the same size. */ + void* tinyPool[maxTinyBuffers]; + int tinyPoolSize; + + /** Pointer to the data in the tiny pool */ + void* tinyHeap; + +# ifdef G3D_WIN32 + CRITICAL_SECTION mutex; +# else + pthread_mutex_t mutex; +# endif + + /** Provide synchronization between threads */ + void lock() { +# ifdef G3D_WIN32 + EnterCriticalSection(&mutex); +# else + pthread_mutex_lock(&mutex); +# endif + } + + void unlock() { +# ifdef G3D_WIN32 + LeaveCriticalSection(&mutex); +# else + pthread_mutex_unlock(&mutex); +# endif + } + + /** + Malloc out of the tiny heap. Returns NULL if allocation failed. + */ + inline void* tinyMalloc(size_t bytes) { + // Note that we ignore the actual byte size + // and create a constant size block. + (void)bytes; + assert(tinyBufferSize >= bytes); + + void* ptr = NULL; + + if (tinyPoolSize > 0) { + --tinyPoolSize; + + // Return the old last pointer from the freelist + ptr = tinyPool[tinyPoolSize]; + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + assert(tinyPool[tinyPoolSize - 1] != ptr); + // "System::malloc heap corruption detected: " + // "the last two pointers on the freelist are identical (during tinyMalloc)."); + } +# endif + + // NULL out the entry to help detect corruption + tinyPool[tinyPoolSize] = NULL; + } + + return ptr; + } + + /** Returns true if this is a pointer into the tiny heap. */ + bool inTinyHeap(void* ptr) { + return + (ptr >= tinyHeap) && + (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize); + } + + void tinyFree(void* ptr) { + assert(ptr); + assert(tinyPoolSize < maxTinyBuffers); + // "Tried to free a tiny pool buffer when the tiny pool freelist is full."); + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + void* prevOnHeap = tinyPool[tinyPoolSize - 1]; + assert(prevOnHeap != ptr); +// "System::malloc heap corruption detected: " +// "the last two pointers on the freelist are identical (during tinyFree)."); + } +# endif + + assert(tinyPool[tinyPoolSize] == NULL); + + // Put the pointer back into the free list + tinyPool[tinyPoolSize] = ptr; + ++tinyPoolSize; + + } + + void flushPool(MemBlock* pool, int& poolSize) { + for (int i = 0; i < poolSize; ++i) { + ::free(pool[i].ptr); + pool[i].ptr = NULL; + pool[i].bytes = 0; + } + poolSize = 0; + } + + + /** Allocate out of a specific pool-> Return NULL if no suitable + memory was found. + + */ + void* malloc(MemBlock* pool, int& poolSize, size_t bytes) { + + // OPT: find the smallest block that satisfies the request. + + // See if there's something we can use in the buffer pool-> + // Search backwards since usually we'll re-use the last one. + for (int i = (int)poolSize - 1; i >= 0; --i) { + if (pool[i].bytes >= bytes) { + // We found a suitable entry in the pool-> + + // No need to offset the pointer; it is already offset + void* ptr = pool[i].ptr; + + // Remove this element from the pool + --poolSize; + pool[i] = pool[poolSize]; + + return ptr; + } + } + + return NULL; + } + +public: + + /** Count of memory allocations that have occurred. */ + int totalMallocs; + int mallocsFromTinyPool; + int mallocsFromSmallPool; + int mallocsFromMedPool; + + /** Amount of memory currently allocated (according to the application). + This does not count the memory still remaining in the buffer pool, + but does count extra memory required for rounding off to the size + of a buffer. + Primarily useful for detecting leaks.*/ + // TODO: make me an atomic int! + volatile int bytesAllocated; + + BufferPool() { + totalMallocs = 0; + + mallocsFromTinyPool = 0; + mallocsFromSmallPool = 0; + mallocsFromMedPool = 0; + + bytesAllocated = true; + + tinyPoolSize = 0; + tinyHeap = NULL; + + smallPoolSize = 0; + + medPoolSize = 0; + + + // Initialize the tiny heap as a bunch of pointers into one + // pre-allocated buffer. + tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize); + for (int i = 0; i < maxTinyBuffers; ++i) { + tinyPool[i] = (uint8*)tinyHeap + (tinyBufferSize * i); + } + tinyPoolSize = maxTinyBuffers; + +# ifdef G3D_WIN32 + InitializeCriticalSection(&mutex); +# else + pthread_mutex_init(&mutex, NULL); +# endif + } + + + ~BufferPool() { + ::free(tinyHeap); +# ifdef G3D_WIN32 + DeleteCriticalSection(&mutex); +# else + // No destruction on pthreads +# endif + } + + + void* realloc(void* ptr, size_t bytes) { + if (ptr == NULL) { + return malloc(bytes); + } + + if (inTinyHeap(ptr)) { + if (bytes <= tinyBufferSize) { + // The old pointer actually had enough space. + return ptr; + } else { + // Free the old pointer and malloc + + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, tinyBufferSize); + tinyFree(ptr); + return newPtr; + + } + } else { + // In one of our heaps. + + // See how big the block really was + size_t realSize = *(uint32*)USERPTR_TO_REALPTR(ptr); + if (bytes <= realSize) { + // The old block was big enough. + return ptr; + } + + // Need to reallocate + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, realSize); + free(ptr); + return newPtr; + } + } + + + void* malloc(size_t bytes) { + lock(); + ++totalMallocs; + + if (bytes <= tinyBufferSize) { + + void* ptr = tinyMalloc(bytes); + + if (ptr) { + ++mallocsFromTinyPool; + unlock(); + return ptr; + } + + } + + // Failure to allocate a tiny buffer is allowed to flow + // through to a small buffer + if (bytes <= smallBufferSize) { + + void* ptr = malloc(smallPool, smallPoolSize, bytes); + + if (ptr) { + ++mallocsFromSmallPool; + unlock(); + return ptr; + } + + } else if (bytes <= medBufferSize) { + // Note that a small allocation failure does *not* fall + // through into a medium allocation because that would + // waste the medium buffer's resources. + + void* ptr = malloc(medPool, medPoolSize, bytes); + + if (ptr) { + ++mallocsFromMedPool; + unlock(); + debugAssertM(ptr != NULL, "BufferPool::malloc returned NULL"); + return ptr; + } + } + + bytesAllocated += REALBLOCK_SIZE(bytes); + unlock(); + + // Heap allocate + + // Allocate 4 extra bytes for our size header (unfortunate, + // since malloc already added its own header). + void* ptr = ::malloc(REALBLOCK_SIZE(bytes)); + + if (ptr == NULL) { + // Flush memory pools to try and recover space + flushPool(smallPool, smallPoolSize); + flushPool(medPool, medPoolSize); + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + + if (ptr == NULL) { + if ((System::outOfMemoryCallback != NULL) && + (System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), true) == true)) { + // Re-attempt the malloc + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + } + + if (ptr == NULL) { + if (System::outOfMemoryCallback != NULL) { + // Notify the application + System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), false); + } +# ifdef G3D_DEBUG + debugPrintf("::malloc(%d) returned NULL\n", REALBLOCK_SIZE(bytes)); +# endif + debugAssertM(ptr != NULL, + "::malloc returned NULL. Either the " + "operating system is out of memory or the " + "heap is corrupt."); + return NULL; + } + + *(uint32*)ptr = bytes; + + return REALPTR_TO_USERPTR(ptr); + } + + + void free(void* ptr) { + if (ptr == NULL) { + // Free does nothing on null pointers + return; + } + + assert(isValidPointer(ptr)); + + if (inTinyHeap(ptr)) { + lock(); + tinyFree(ptr); + unlock(); + return; + } + + uint32 bytes = *(uint32*)USERPTR_TO_REALPTR(ptr); + + lock(); + if (bytes <= smallBufferSize) { + if (smallPoolSize < maxSmallBuffers) { + smallPool[smallPoolSize] = MemBlock(ptr, bytes); + ++smallPoolSize; + unlock(); + return; + } + } else if (bytes <= medBufferSize) { + if (medPoolSize < maxMedBuffers) { + medPool[medPoolSize] = MemBlock(ptr, bytes); + ++medPoolSize; + unlock(); + return; + } + } + bytesAllocated -= REALBLOCK_SIZE(bytes); + unlock(); + + // Free; the buffer pools are full or this is too big to store. + ::free(USERPTR_TO_REALPTR(ptr)); + } + + std::string performance() const { + if (totalMallocs > 0) { + int pooled = mallocsFromTinyPool + + mallocsFromSmallPool + + mallocsFromMedPool; + + int total = totalMallocs; + + return format("malloc performance: %5.1f%% <= %db, %5.1f%% <= %db, " + "%5.1f%% <= %db, %5.1f%% > %db", + 100.0 * mallocsFromTinyPool / total, + BufferPool::tinyBufferSize, + 100.0 * mallocsFromSmallPool / total, + BufferPool::smallBufferSize, + 100.0 * mallocsFromMedPool / total, + BufferPool::medBufferSize, + 100.0 * (1.0 - (double)pooled / total), + BufferPool::medBufferSize); + } else { + return "No System::malloc calls made yet."; + } + } + + std::string status() const { + return format("preallocated shared buffers: %5d/%d x %db", + maxTinyBuffers - tinyPoolSize, maxTinyBuffers, tinyBufferSize); + } +}; + +// Dynamically allocated because we need to ensure that +// the buffer pool is still around when the last global variable +// is deallocated. +static BufferPool* bufferpool = NULL; + +std::string System::mallocPerformance() { +#ifndef NO_BUFFERPOOL + return bufferpool->performance(); +#else + return "NO_BUFFERPOOL"; +#endif +} + +std::string System::mallocStatus() { +#ifndef NO_BUFFERPOOL + return bufferpool->status(); +#else + return "NO_BUFFERPOOL"; +#endif +} + + +void System::resetMallocPerformanceCounters() { +#ifndef NO_BUFFERPOOL + bufferpool->totalMallocs = 0; + bufferpool->mallocsFromMedPool = 0; + bufferpool->mallocsFromSmallPool = 0; + bufferpool->mallocsFromTinyPool = 0; +#endif +} + + +#ifndef NO_BUFFERPOOL +inline void initMem() { + // Putting the test here ensures that the system is always + // initialized, even when globals are being allocated. + static bool initialized = false; + if (! initialized) { + bufferpool = new BufferPool(); + initialized = true; + } +} +#endif + + +void* System::malloc(size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->malloc(bytes); +#else + return ::malloc(bytes); +#endif +} + +void* System::calloc(size_t n, size_t x) { +#ifndef NO_BUFFERPOOL + void* b = System::malloc(n * x); + debugAssertM(b != NULL, "System::malloc returned NULL"); + debugAssertM(isValidHeapPointer(b), "System::malloc returned an invalid pointer"); + System::memset(b, 0, n * x); + return b; +#else + return ::calloc(n, x); +#endif +} + + +void* System::realloc(void* block, size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->realloc(block, bytes); +#else + return ::realloc(block, bytes); +#endif +} + + +void System::free(void* p) { +#ifndef NO_BUFFERPOOL + bufferpool->free(p); +#else + return ::free(p); +#endif +} + + +void* System::alignedMalloc(size_t bytes, size_t alignment) { + + alwaysAssertM(isPow2(alignment), "alignment must be a power of 2"); + + // We must align to at least a word boundary. + alignment = iMax(alignment, sizeof(void *)); + + // Pad the allocation size with the alignment size and the + // size of the redirect pointer. + size_t totalBytes = bytes + alignment + sizeof(void*); + + size_t truePtr = (size_t)System::malloc(totalBytes); + + if (truePtr == 0) { + // malloc returned NULL + return NULL; + } + + debugAssert(isValidHeapPointer((void*)truePtr)); + #ifdef G3D_WIN32 + // The blocks we return will not be valid Win32 debug heap + // pointers because they are offset + // debugAssert(_CrtIsValidPointer((void*)truePtr, totalBytes, TRUE) ); + #endif + + // The return pointer will be the next aligned location (we must at least + // leave space for the redirect pointer, however). + size_t alignedPtr = truePtr + sizeof(void*); + + // 2^n - 1 has the form 1111... in binary. + uint32 bitMask = (alignment - 1); + + // Advance forward until we reach an aligned location. + while ((alignedPtr & bitMask) != 0) { + alignedPtr += sizeof(void*); + } + + debugAssert(alignedPtr - truePtr + bytes <= totalBytes); + + // Immediately before the aligned location, write the true array location + // so that we can free it correctly. + size_t* redirectPtr = (size_t *)(alignedPtr - sizeof(void *)); + redirectPtr[0] = truePtr; + + debugAssert(isValidHeapPointer((void*)truePtr)); + + #ifdef G3D_WIN32 + debugAssert( _CrtIsValidPointer((void*)alignedPtr, bytes, TRUE) ); + #endif + return (void *)alignedPtr; +} + + +void System::alignedFree(void* _ptr) { + if (_ptr == NULL) { + return; + } + + size_t alignedPtr = (size_t)_ptr; + + // Back up one word from the pointer the user passed in. + // We now have a pointer to a pointer to the true start + // of the memory block. + size_t* redirectPtr = (size_t*)(alignedPtr - sizeof(void *)); + + // Dereference that pointer so that ptr = true start + void* truePtr = (void*)redirectPtr[0]; + + debugAssert(isValidHeapPointer((void*)truePtr)); + System::free(truePtr); +} + + +void System::setEnv(const std::string& name, const std::string& value) { + std::string cmd = name + "=" + value; +# ifdef G3D_WIN32 + _putenv(cmd.c_str()); +# else + // Many linux implementations of putenv expect char* + putenv(const_cast<char*>(cmd.c_str())); +# endif +} + +const char* System::getEnv(const std::string& name) { + return getenv(name.c_str()); +} + +static void var(TextOutput& t, const std::string& name, const std::string& val) { + t.writeSymbols(name,"="); + t.writeString(val); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const bool val) { + t.writeSymbols(name, "=", val ? "Yes" : "No"); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const int val) { + t.writeSymbols(name,"="); + t.writeNumber(val); + t.writeNewline(); +} + +void System::describeSystem( + std::string& s) { + + TextOutput t; + describeSystem(t); + t.commitString(s); +} + +void System::describeSystem( + TextOutput& t) { + + t.writeSymbols("App", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Name", System::currentProgramFilename()); + char cwd[1024]; + getcwd(cwd, 1024); + var(t, "cwd", std::string(cwd)); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("OS", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Name", System::operatingSystem()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("CPU", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Vendor", System::cpuVendor()); + var(t, "Architecture", System::cpuArchitecture()); + var(t, "hasCPUID", System::hasCPUID()); + var(t, "hasMMX", System::hasMMX()); + var(t, "hasSSE", System::hasSSE()); + var(t, "hasSSE2", System::hasSSE2()); + var(t, "hasSSE3", System::hasSSE3()); + var(t, "has3DNow", System::has3DNow()); + var(t, "hasRDTSC", System::hasRDTSC()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("G3D", "{"); + t.writeNewline(); + t.pushIndent(); + var(t, "Link version", G3D_VER); + var(t, "Compile version", System::version()); + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); +} + + +int System::cpuSpeedMHz() { + return g_cpuInfo.m_cpuSpeed; +} + + +void System::setClipboardText(const std::string& s) { +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, s.size() + 1); + if (hMem) { + char *pMem = (char*)GlobalLock(hMem); + strcpy(pMem, s.c_str()); + GlobalUnlock(hMem); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + } + + CloseClipboard(); + GlobalFree(hMem); + } +# endif +} + + +std::string System::getClipboardText() { + std::string s; + +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HANDLE h = GetClipboardData(CF_TEXT); + + if (h) { + char* temp = (char*)GlobalLock(h); + if (temp) { + s = temp; + } + temp = NULL; + GlobalUnlock(h); + } + CloseClipboard(); + } +# endif + return s; +} + + +std::string System::currentDateString() { + time_t t1; + ::time(&t1); + tm* t = localtime(&t1); + return format("%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); +} + + +} // namespace |