Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/src/OgrePlatformInformation.cpp @ 1

Last change on this file since 1 was 1, checked in by landauf, 17 years ago
File size: 18.9 KB
RevLine 
[1]1/*
2-----------------------------------------------------------------------------
3This source file is part of OGRE
4    (Object-oriented Graphics Rendering Engine)
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This program is free software; you can redistribute it and/or modify it under
11the terms of the GNU Lesser General Public License as published by the Free Software
12Foundation; either version 2 of the License, or (at your option) any later
13version.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
18
19You should have received a copy of the GNU Lesser General Public License along with
20this program; if not, write to the Free Software Foundation, Inc., 59 Temple
21Place - Suite 330, Boston, MA 02111-1307, USA, or go to
22http://www.gnu.org/copyleft/lesser.txt.
23
24You may alternatively use this source under the terms of a specific version of
25the OGRE Unrestricted License provided you have obtained such a license from
26Torus Knot Software Ltd.
27-----------------------------------------------------------------------------
28*/
29#include "OgreStableHeaders.h"
30
31#include "OgrePlatformInformation.h"
32#include "OgreLog.h"
33#include "OgreStringConverter.h"
34
35#if OGRE_COMPILER == OGRE_COMPILER_MSVC
36#include <excpt.h>      // For SEH values
37        #if _MSC_VER >= 1400
38                #include <intrin.h>
39        #endif
40#elif OGRE_COMPILER == OGRE_COMPILER_GNUC
41#include <signal.h>
42#include <setjmp.h>
43
44#endif
45
46// Yes, I known, this file looks very ugly, but there hasn't other ways to do it better.
47
48namespace Ogre {
49
50#if OGRE_CPU == OGRE_CPU_X86
51
52    //---------------------------------------------------------------------
53    // Struct for store CPUID instruction result, compiler-independent
54    //---------------------------------------------------------------------
55    struct CpuidResult
56    {
57        // Note: DO NOT CHANGE THE ORDER, some code based on that.
58        uint _eax;
59        uint _ebx;
60        uint _edx;
61        uint _ecx;
62    };
63
64    //---------------------------------------------------------------------
65    // Compiler-dependent routines
66    //---------------------------------------------------------------------
67
68#if OGRE_COMPILER == OGRE_COMPILER_MSVC
69#pragma warning(push)
70#pragma warning(disable: 4035)  // no return value
71#endif
72
73    //---------------------------------------------------------------------
74    // Detect whether CPU supports CPUID instruction, returns non-zero if supported.
75    static int _isSupportCpuid(void)
76    {
77#if OGRE_COMPILER == OGRE_COMPILER_MSVC
78                // Visual Studio 2005 & 64-bit compilers always supports __cpuid intrinsic
79                // note that even though this is a build rather than runtime setting, all
80                // 64-bit CPUs support this so since binary is 64-bit only we're ok
81        #if _MSC_VER >= 1400 && defined(_M_X64)
82                return true;
83        #else
84                // If we can modify flag register bit 21, the cpu is supports CPUID instruction
85        __asm
86        {
87            // Read EFLAG
88            pushfd
89            pop     eax
90            mov     ecx, eax
91
92            // Modify bit 21
93            xor     eax, 0x200000
94            push    eax
95            popfd
96
97            // Read back EFLAG
98            pushfd
99            pop     eax
100
101            // Restore EFLAG
102            push    ecx
103            popfd
104
105            // Check bit 21 modifiable
106            xor     eax, ecx
107            neg     eax
108            sbb     eax, eax
109
110            // Return values in eax, no return statment requirement here for VC.
111        }
112        #endif
113#elif OGRE_COMPILER == OGRE_COMPILER_GNUC
114        unsigned oldFlags, newFlags;
115        __asm__
116        (
117            "pushfl         \n\t"
118            "pop    %0      \n\t"
119            "mov    %0, %1  \n\t"
120            "xor    %2, %0  \n\t"
121            "push   %0      \n\t"
122            "popfl          \n\t"
123            "pushfl         \n\t"
124            "pop    %0      \n\t"
125            "push   %1      \n\t"
126            "popfl          \n\t"
127            : "=r" (oldFlags), "=r" (newFlags)
128            : "n" (0x200000)
129        );
130        return oldFlags != newFlags;
131
132#else
133        // TODO: Supports other compiler
134        return false;
135#endif
136    }
137
138    //---------------------------------------------------------------------
139    // Performs CPUID instruction with 'query', fill the results, and return value of eax.
140    static uint _performCpuid(int query, CpuidResult& result)
141    {
142#if OGRE_COMPILER == OGRE_COMPILER_MSVC
143        #if _MSC_VER >= 1400
144                int CPUInfo[4];
145                __cpuid(CPUInfo, query);
146                result._eax = CPUInfo[0];
147                result._ebx = CPUInfo[1];
148                result._ecx = CPUInfo[2];
149                result._edx = CPUInfo[3];
150                return result._eax;
151        #else
152        __asm
153        {
154            mov     edi, result
155            mov     eax, query
156            cpuid
157            mov     [edi]._eax, eax
158            mov     [edi]._ebx, ebx
159            mov     [edi]._edx, edx
160            mov     [edi]._ecx, ecx
161            // Return values in eax, no return statment requirement here for VC.
162        }
163        #endif
164#elif OGRE_COMPILER == OGRE_COMPILER_GNUC
165        __asm__
166        (
167            "pushl  %%ebx           \n\t"
168            "cpuid                  \n\t"
169            "movl   %%ebx, %%edi    \n\t"
170            "popl   %%ebx           \n\t"
171            : "=a" (result._eax), "=D" (result._ebx), "=c" (result._ecx), "=d" (result._edx)
172            : "a" (query)
173        );
174        return result._eax;
175
176#else
177        // TODO: Supports other compiler
178        return 0;
179#endif
180    }
181
182#if OGRE_COMPILER == OGRE_COMPILER_MSVC
183#pragma warning(pop)
184#endif
185
186    //---------------------------------------------------------------------
187    // Detect whether or not os support Streaming SIMD Extension.
188#if OGRE_COMPILER == OGRE_COMPILER_GNUC
189    static jmp_buf sIllegalJmpBuf;
190    static void _illegalHandler(int x)
191    {
192        (void)(x); // Unused
193        longjmp(sIllegalJmpBuf, 1);
194    }
195#endif
196    static bool _checkOperatingSystemSupportSSE(void)
197    {
198#if OGRE_COMPILER == OGRE_COMPILER_MSVC
199        /*
200            The FP part of SSE introduces a new architectural state and therefore
201            requires support from the operating system. So even if CPUID indicates
202            support for SSE FP, the application might not be able to use it. If
203            CPUID indicates support for SSE FP, check here whether it is also
204            supported by the OS, and turn off the SSE FP feature bit if there
205            is no OS support for SSE FP.
206
207            Operating systems that do not support SSE FP return an illegal
208            instruction exception if execution of an SSE FP instruction is performed.
209            Here, a sample SSE FP instruction is executed, and is checked for an
210            exception using the (non-standard) __try/__except mechanism
211            of Microsoft Visual C/C++.
212        */
213                // Visual Studio 2005, Both AMD and Intel x64 support SSE
214                // note that even though this is a build rather than runtime setting, all
215                // 64-bit CPUs support this so since binary is 64-bit only we're ok
216        #if _MSC_VER >= 1400 && defined(_M_X64)
217                        return true;
218        #else
219        __try
220        {
221            __asm orps  xmm0, xmm0
222            return true;
223        }
224        __except(EXCEPTION_EXECUTE_HANDLER)
225        {
226            return false;
227        }
228        #endif
229#elif OGRE_COMPILER == OGRE_COMPILER_GNUC
230        // Does gcc have __try/__except similar mechanism?
231        // Use signal, setjmp/longjmp instead.
232        void (*oldHandler)(int);
233        oldHandler = signal(SIGILL, _illegalHandler);
234
235        if (setjmp(sIllegalJmpBuf))
236        {
237            signal(SIGILL, oldHandler);
238            return false;
239        }
240        else
241        {
242            __asm__ __volatile__ ("orps %xmm0, %xmm0");
243            signal(SIGILL, oldHandler);
244            return true;
245        }
246
247#else
248        // TODO: Supports other compiler, assumed is supported by default
249        return true;
250#endif
251    }
252
253    //---------------------------------------------------------------------
254    // Compiler-independent routines
255    //---------------------------------------------------------------------
256
257    static uint queryCpuFeatures(void)
258    {
259#define CPUID_STD_FPU               (1<<0)
260#define CPUID_STD_TSC               (1<<4)
261#define CPUID_STD_CMOV              (1<<15)
262#define CPUID_STD_MMX               (1<<23)
263#define CPUID_STD_SSE               (1<<25)
264#define CPUID_STD_SSE2              (1<<26)
265#define CPUID_STD_HTT               (1<<28)     // EDX[28] - Bit 28 set indicates  Hyper-Threading Technology is supported in hardware.
266
267#define CPUID_STD_SSE3              (1<<0)      // ECX[0] - Bit 0 of standard function 1 indicate SSE3 supported
268
269#define CPUID_FAMILY_ID_MASK        0x0F00      // EAX[11:8] - Bit 11 thru 8 contains family  processor id
270#define CPUID_EXT_FAMILY_ID_MASK    0x0F00000   // EAX[23:20] - Bit 23 thru 20 contains extended family processor id
271#define CPUID_PENTIUM4_ID           0x0F00      // Pentium 4 family processor id
272
273#define CPUID_EXT_3DNOW             (1<<31)
274#define CPUID_EXT_AMD_3DNOWEXT      (1<<30)
275#define CPUID_EXT_AMD_MMXEXT        (1<<22)
276
277        uint features = 0;
278
279        // Supports CPUID instruction ?
280        if (_isSupportCpuid())
281        {
282            CpuidResult result;
283
284            // Has standard feature ?
285            if (_performCpuid(0, result))
286            {
287                // Check vendor strings
288                if (memcmp(&result._ebx, "GenuineIntel", 12) == 0)
289                {
290                    if (result._eax > 2)
291                        features |= PlatformInformation::CPU_FEATURE_PRO;
292
293                    // Check standard feature
294                    _performCpuid(1, result);
295
296                    if (result._edx & CPUID_STD_FPU)
297                        features |= PlatformInformation::CPU_FEATURE_FPU;
298                    if (result._edx & CPUID_STD_TSC)
299                        features |= PlatformInformation::CPU_FEATURE_TSC;
300                    if (result._edx & CPUID_STD_CMOV)
301                        features |= PlatformInformation::CPU_FEATURE_CMOV;
302                    if (result._edx & CPUID_STD_MMX)
303                        features |= PlatformInformation::CPU_FEATURE_MMX;
304                    if (result._edx & CPUID_STD_SSE)
305                        features |= PlatformInformation::CPU_FEATURE_MMXEXT | PlatformInformation::CPU_FEATURE_SSE;
306                    if (result._edx & CPUID_STD_SSE2)
307                        features |= PlatformInformation::CPU_FEATURE_SSE2;
308
309                    if (result._ecx & CPUID_STD_SSE3)
310                        features |= PlatformInformation::CPU_FEATURE_SSE3;
311
312                    // Check to see if this is a Pentium 4 or later processor
313                    if ((result._eax & CPUID_EXT_FAMILY_ID_MASK) ||
314                        (result._eax & CPUID_FAMILY_ID_MASK) == CPUID_PENTIUM4_ID)
315                    {
316                        // Check hyper-threading technology
317                        if (result._edx & CPUID_STD_HTT)
318                            features |= PlatformInformation::CPU_FEATURE_HTT;
319                    }
320                }
321                else if (memcmp(&result._ebx, "AuthenticAMD", 12) == 0)
322                {
323                    features |= PlatformInformation::CPU_FEATURE_PRO;
324
325                    // Check standard feature
326                    _performCpuid(1, result);
327
328                    if (result._edx & CPUID_STD_FPU)
329                        features |= PlatformInformation::CPU_FEATURE_FPU;
330                    if (result._edx & CPUID_STD_TSC)
331                        features |= PlatformInformation::CPU_FEATURE_TSC;
332                    if (result._edx & CPUID_STD_CMOV)
333                        features |= PlatformInformation::CPU_FEATURE_CMOV;
334                    if (result._edx & CPUID_STD_MMX)
335                        features |= PlatformInformation::CPU_FEATURE_MMX;
336                    if (result._edx & CPUID_STD_SSE)
337                        features |= PlatformInformation::CPU_FEATURE_SSE;
338                    if (result._edx & CPUID_STD_SSE2)
339                        features |= PlatformInformation::CPU_FEATURE_SSE2;
340
341                    if (result._ecx & CPUID_STD_SSE3)
342                        features |= PlatformInformation::CPU_FEATURE_SSE3;
343
344                    // Has extended feature ?
345                    if (_performCpuid(0x80000000, result) > 0x80000000)
346                    {
347                        // Check extended feature
348                        _performCpuid(0x80000001, result);
349
350                        if (result._edx & CPUID_EXT_3DNOW)
351                            features |= PlatformInformation::CPU_FEATURE_3DNOW;
352                        if (result._edx & CPUID_EXT_AMD_3DNOWEXT)
353                            features |= PlatformInformation::CPU_FEATURE_3DNOWEXT;
354                        if (result._edx & CPUID_EXT_AMD_MMXEXT)
355                            features |= PlatformInformation::CPU_FEATURE_MMXEXT;
356                    }
357                }
358            }
359        }
360
361        return features;
362    }
363    //---------------------------------------------------------------------
364    static uint _detectCpuFeatures(void)
365    {
366        uint features = queryCpuFeatures();
367
368        const uint sse_features = PlatformInformation::CPU_FEATURE_SSE |
369            PlatformInformation::CPU_FEATURE_SSE2 | PlatformInformation::CPU_FEATURE_SSE3;
370        if ((features & sse_features) && !_checkOperatingSystemSupportSSE())
371        {
372            features &= ~sse_features;
373        }
374
375        return features;
376    }
377    //---------------------------------------------------------------------
378    static String _detectCpuIdentifier(void)
379    {
380                // Supports CPUID instruction ?
381                if (_isSupportCpuid())
382                {
383                        CpuidResult result;
384                        uint nExIds;
385                        char CPUString[0x20];
386                        char CPUBrandString[0x40];
387
388                        StringUtil::StrStreamType detailedIdentStr;
389
390
391                        // Has standard feature ?
392                        if (_performCpuid(0, result))
393                        {
394                                memset(CPUString, 0, sizeof(CPUString));
395                                memset(CPUBrandString, 0, sizeof(CPUBrandString));
396
397                                *((int*)CPUString) = result._ebx;
398                                *((int*)(CPUString+4)) = result._edx;
399                                *((int*)(CPUString+8)) = result._ecx;
400
401                                detailedIdentStr << CPUString;
402
403                                // Calling _performCpuid with 0x80000000 as the query argument
404                                // gets the number of valid extended IDs.
405                                nExIds = _performCpuid(0x80000000, result);
406
407                                for (uint i=0x80000000; i<=nExIds; ++i)
408                                {
409                                        _performCpuid(i, result);
410
411                                        // Interpret CPU brand string and cache information.
412                                        if  (i == 0x80000002)
413                    {
414                                                memcpy(CPUBrandString + 0, &result._eax, sizeof(result._eax));
415                                                memcpy(CPUBrandString + 4, &result._ebx, sizeof(result._ebx));
416                                                memcpy(CPUBrandString + 8, &result._ecx, sizeof(result._ecx));
417                                                memcpy(CPUBrandString + 12, &result._edx, sizeof(result._edx));
418                    }
419                                        else if  (i == 0x80000003)
420                    {
421                                                memcpy(CPUBrandString + 16 + 0, &result._eax, sizeof(result._eax));
422                                                memcpy(CPUBrandString + 16 + 4, &result._ebx, sizeof(result._ebx));
423                                                memcpy(CPUBrandString + 16 + 8, &result._ecx, sizeof(result._ecx));
424                                                memcpy(CPUBrandString + 16 + 12, &result._edx, sizeof(result._edx));
425                    }
426                                        else if  (i == 0x80000004)
427                    {
428                                                memcpy(CPUBrandString + 32 + 0, &result._eax, sizeof(result._eax));
429                                                memcpy(CPUBrandString + 32 + 4, &result._ebx, sizeof(result._ebx));
430                                                memcpy(CPUBrandString + 32 + 8, &result._ecx, sizeof(result._ecx));
431                                                memcpy(CPUBrandString + 32 + 12, &result._edx, sizeof(result._edx));
432                    }
433                                }
434
435                                String brand(CPUBrandString);
436                                StringUtil::trim(brand);
437                                if (!brand.empty())
438                                        detailedIdentStr << ": " << brand;
439
440                                return detailedIdentStr.str();
441                        }
442                }
443
444                return "X86";
445    }
446
447#else   // OGRE_CPU == OGRE_CPU_X86
448
449    //---------------------------------------------------------------------
450    static uint _detectCpuFeatures(void)
451    {
452        return 0;
453    }
454    //---------------------------------------------------------------------
455    static String _detectCpuIdentifier(void)
456    {
457        return "Unknown";
458    }
459
460#endif  // OGRE_CPU
461
462    //---------------------------------------------------------------------
463    // Platform-independent routines, but the returns value are platform-dependent
464    //---------------------------------------------------------------------
465
466    const String& PlatformInformation::getCpuIdentifier(void)
467    {
468        static const String sIdentifier = _detectCpuIdentifier();
469        return sIdentifier;
470    }
471    //---------------------------------------------------------------------
472    uint PlatformInformation::getCpuFeatures(void)
473    {
474        static const uint sFeatures = _detectCpuFeatures();
475        return sFeatures;
476    }
477        //---------------------------------------------------------------------
478        bool PlatformInformation::hasCpuFeature(CpuFeatures feature)
479        {
480                return (getCpuFeatures() & feature) != 0;
481        }
482        //---------------------------------------------------------------------
483        void PlatformInformation::log(Log* pLog)
484        {
485                pLog->logMessage("CPU Identifier & Features");
486                pLog->logMessage("-------------------------");
487                pLog->logMessage(
488                        " *   CPU ID: " + getCpuIdentifier());
489#if OGRE_CPU == OGRE_CPU_X86
490                if(_isSupportCpuid())
491                {
492                        pLog->logMessage(
493                                " *      SSE: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_SSE), true));
494                        pLog->logMessage(
495                                " *     SSE2: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_SSE2), true));
496                        pLog->logMessage(
497                                " *     SSE3: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_SSE3), true));
498                        pLog->logMessage(
499                                " *      MMX: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_MMX), true));
500                        pLog->logMessage(
501                                " *   MMXEXT: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_MMXEXT), true));
502                        pLog->logMessage(
503                                " *    3DNOW: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_3DNOW), true));
504                        pLog->logMessage(
505                                " * 3DNOWEXT: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_3DNOWEXT), true));
506                        pLog->logMessage(
507                                " *     CMOV: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_CMOV), true));
508                        pLog->logMessage(
509                                " *      TSC: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_TSC), true));
510                        pLog->logMessage(
511                                " *      FPU: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_FPU), true));
512                        pLog->logMessage(
513                                " *      PRO: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_PRO), true));
514                        pLog->logMessage(
515                                " *       HT: " + StringConverter::toString(hasCpuFeature(CPU_FEATURE_HTT), true));
516                }
517#endif
518                pLog->logMessage("-------------------------");
519
520        }
521
522
523}
Note: See TracBrowser for help on using the repository browser.