1
\$\begingroup\$

When writing a game, you can do many things in many ways. But not all the machines the code will be ran on support all the ways of doing it.

How can you query for the capabilities of the machine hardware programmatically?

For example, there are ways in openGL to detect if certain openGL methods are supported by the GPU, you can also see if certain extensions are supported, query the openGL version, ect, but that still doesn't give you all the data (I'm pretty sure you cannot query the amount of VRAM the machine has).

Can you do a hardware query system-wide? To ask for the number of cores, CPU frequencies, L2 cache size, amount of ram/vram, sound card capabilities and any other system specifics so you could instruct your code how to behave (for example, to not try to play sound on 64 channels if sound card supports only 32, if vram is limited use smaller textures, etc)?

It would be nice to query for such data on startup to see if machine supports minimum requirements and then have the data globally available to the code so certain feature can be turned on or off or substituted by maybe less efficient but supported code for that machine.

I remember GTA IV had a gfx menu where it showed the amount of VRAM available and the amount required for the selected setup.

Anyway, is there a way to do what I ask?

\$\endgroup\$
  • 2
    \$\begingroup\$ I think you should break the question into more specialized ones(and find which you actually need), "how do I query everything" is a bit too broad in my opinion. \$\endgroup\$ – wondra Nov 27 '16 at 12:46
2
\$\begingroup\$

You're asking a lot of unrelated questions, but here goes...

  • Check supported OpenGL version.

    glGetString(GL_VERSION);
    
  • Check supported extensions.

    glGetIntegerv(GL_NUM_EXETENSIONS, *);
    glGetStringi(GL_EXTENSIONS, n); // n == the result of GL_NUM_EXTENSIONS
    
  • Identify vendor. This will return something like "ATI Technologies" or "NVIDIA Corporation."

    glGetString(GL_VENDOR);
    
  • Identify renderer. This can return the ASIC name or the software renderer.

    glGetString(GL_RENDERER);
    
  • Check for specific capabilities. For example, check for the max number of supported texture units, which is implementation-specific:

    glGetIntegerv(GL_MAX_TEXTURE_UNITS, *);
    
  • Check for GPU capabilities with vendor-specific extensions and libraries. I'm not allowed to have more than two links in my post, so just copy-pasta:

    • GL_NVX_gpu_memory_info

    http://developer.download.nvidia.com/opengl/specs/GL_NVX_gpu_memory_info.txt

    • GL_ATI_meminfo

    https://www.opengl.org/registry/specs/ATI/meminfo.txt

    • WGL_AMD_gpu_association

    https://www.opengl.org/registry/specs/AMD/wgl_gpu_association.txt

    • AMD GPU Services library (for DirectX)

    https://github.com/GPUOpen-LibrariesAndSDKs/AGS_SDK

Additional hardware information such as CPU and VRAM details are platform-specific and fall outside of OpenGL's scope. For that answer, I need to know what platform you care about. Since you didn't tell us, I'll tell you how do it on the one I (and most of us) care about.

#include <Windows.h> // Must be included FIRST
#include <Psapi.h>

#include <thread>
#include <string>
#include <vector>

#include <iostream>

#define COUTPUT(str, num) std::cout << str << ": " << num << '\n';

int main()
{
    /***** CPU Stuff *****/
    // Get number of CPU cores
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724958(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724381(v=vs.85).aspx
    COUTPUT("Number of processor cores", std::thread::hardware_concurrency()); // C++11
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);
    COUTPUT("Number of processor cores", sysInfo.dwNumberOfProcessors); // Pre-C++11

    // Get CPU frequency
    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383713(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);
    std::string arr[] { "KB", "MB", "GB" };
    int i = 0;
    double speed = 0;

    for (i = 3; i > 0; --i)
    {
        double power = pow(1024, i);
        if (power <= freq.QuadPart)
        {
            speed = freq.QuadPart / power;
            break;
        }
    }
    std::string formatted = std::to_string(speed) + ' ' + arr[i];
    COUTPUT("CPU frequency", formatted);

    // Get CPU cache sizes
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms686694(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683194(v=vs.85).aspx
    DWORD bufSize = 0;
    GetLogicalProcessorInformation(0, &bufSize);
    std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(bufSize/sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
    GetLogicalProcessorInformation(buffer.data(), &bufSize);

    // Note: Multicore processors will have multiple L1 caches, one for each 
    // core, so extra work here is needed if you want to report each one. 
    // For this example, I just read one. L2/L3 cache is usually shared, so 
    // the extra checks for those shouldn't be needed, but I did them anyway.
    for (int i = 0; i != buffer.size(); ++i)
    {
        if (buffer[i].Relationship == RelationCache)
        {
            static bool bAlreadyReadL1 = false, bAlreadyReadL2 = false, bAlreadyReadL3 = false;
            if (bAlreadyReadL1 && bAlreadyReadL2 && bAlreadyReadL3)
                break;

            if (!bAlreadyReadL1 && buffer[i].Cache.Level == 1)
            {
                COUTPUT("CPU L1 cache size", buffer[i].Cache.Size);
                COUTPUT("CPU L1 cache line size", buffer[i].Cache.LineSize);
                bAlreadyReadL1 = true;
            }
            else if (!bAlreadyReadL2 && buffer[i].Cache.Level == 2)
            {
                COUTPUT("CPU L2 cache size", buffer[i].Cache.Size);
                COUTPUT("CPU L2 cache line size", buffer[i].Cache.LineSize);
                bAlreadyReadL2 = true;
            }
            else if (!bAlreadyReadL3 && buffer[i].Cache.Level == 3)
            {
                COUTPUT("CPU L3 cache size", buffer[i].Cache.Size);
                COUTPUT("CPU L3 cache line size", buffer[i].Cache.LineSize);
                bAlreadyReadL3 = true;
            }
        }
    }

    /***** Memory Stuff *****/
    // Physical memory
    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa366589(v=vs.85).aspx
    MEMORYSTATUSEX memStatus;
    memStatus.dwLength = sizeof(memStatus);
    GlobalMemoryStatusEx(&memStatus);

    COUTPUT("Approximate percentage of physical memory that is in use", memStatus.dwMemoryLoad);
    COUTPUT("Amount of actual physical memory, in bytes", memStatus.ullTotalPhys);
    COUTPUT("Amount of physical memory currently available, in bytes", memStatus.ullAvailPhys);
    COUTPUT("Current committed memory limit for the system or the current process, whichever is smaller, in bytes", memStatus.ullTotalPageFile);
    COUTPUT("Maximum amount of memory the current process can commit, in bytes", memStatus.ullAvailPageFile);
    COUTPUT("Size of the user-mode portion of the virtual address space of the calling process, in bytes", memStatus.ullTotalVirtual);
    COUTPUT("Amount of unreserved and uncommitted memory currently in the user-mode portion of the virtual address space of the calling process, in bytes", memStatus.ullAvailVirtual);

    // Virtual memory
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684874(v=vs.85).aspx
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683219(v=vs.85).aspx
    PROCESS_MEMORY_COUNTERS_EX memCounters;
    memCounters.cb = sizeof(memCounters);
    GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&memCounters), memCounters.cb);

    COUTPUT("Number of page faults", memCounters.PageFaultCount);
    COUTPUT("Peak working set size, in bytes", memCounters.PeakWorkingSetSize);
    COUTPUT("Working set size, in bytes", memCounters.WorkingSetSize);
    COUTPUT("Peak paged pool usage, in bytes", memCounters.QuotaPeakPagedPoolUsage);
    COUTPUT("Current paged pool usage, in bytes", memCounters.QuotaPagedPoolUsage);
    COUTPUT("Peak nonpaged pool usage, in bytes", memCounters.QuotaPeakNonPagedPoolUsage);
    COUTPUT("Current nonpaged pool usage, in bytes", memCounters.QuotaNonPagedPoolUsage);
    COUTPUT("Total amount of memory that the memory manager has committed for this running process", memCounters.PagefileUsage); // Win8+
    COUTPUT("Total amount of memory that the memory manager has committed for this running process", memCounters.PrivateUsage);  // Win7 and below
    COUTPUT("Peak value in bytes of the Commit Charge during the lifetime of this process", memCounters.PeakPagefileUsage);

    return 0;
}
\$\endgroup\$
1
\$\begingroup\$

Windows specific: you can use the Microsoft DirectX Diagnostics API to query the information gathered by the DirectX Diagnostics Tool (DxDiag).

Alternatively, you can call DxDiag via ShellExecute or the like, dump the results to file & then parse them.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.