Jump to content
  • Advertisement
  • 03/20/15 07:47 AM
    Sign in to follow this  

    How to check that a player's PC meets your requirements

    General and Gameplay Programming

    Brain
    • Posted By Brain

    Introduction

    Generally, when you write your game, very little thought will initially be given to system specifications. The usual train of thought might be "well it runs on my system, so i'll publish this as the minimum or recommended specs in a readme file, or on my website". However, what will your game do if the player's PC fails to meet your expectations? This article will outline what sort of things you should be checking for, and when. There is a decidedly windows and DirectX slant to this article as these are my platforms of choice. The concepts are transferable to other platforms and frameworks, however, the source code i give here is not.

    Why should i even attempt to detect system specifications?

    It gives a good impression

    Checking for the user's system specification will ensure that all users who can run your game will run it without issue, and those who cannot run it will be presented with something useful. A game which crashes or even worse, takes down the player's system when they try to start it, with no reason or rhyme as to why will discourage them from trying again, and what's worse they might even go onto Twitter and disparage your game's name. One player spreading bad news about your game is enough to deter many other potential players.

    It cuts down on support issues

    If the player receives a well thought out and instructive error message in the event of a failure, they will know who to contact, and how. The error message could even advise them on what they need to do next before they call you, e.g. to purchase a better graphics card, or delete some programs to free up space, or even to change their display resolution. If they aren't told this beforehand, they will have to contact someone. That someone might be you, and this is your time they will take up which is better spend producing more games.

    It helps with system stability

    Checking for the correct capaibilities beforehand will cut down on the amount of crashes that a player might encounter if their machine isn't quite up to par. As outlined above, a game which crashes is a bad thing, but worse than that, a complete system crash (e.g. by switching to full screen mode with no easy way out) might risk other data on the user's machine, causing damage as well as annoyance.

    How and when should i detect system specifications?

    You should attempt to detect system specifications whenever your game starts. This should preferably be done before any systems are properly initialised, so that windows is still in a state where the user can properly click any error messages away and get back to what they were doing before trying to run your game. In my own game, I have split system specifications detection into several classes, each of which is responsibile for detecting the state of one subsystem. Each is called in turn, with the simplest checks done first as some depend on others for success. It is best to leave the code which checks for system specifications till last in your game, as you won't know what specifications your game needs until this point and are likely to go back and change it repeatedly, otherwise. Important subsystems to check are:
    • System RAM - is there enough to run the game?
    • CPU speed - is the CPU fast enough? Is it multi-core?
    • Hard disk space - is there enough for save game files and other data you might put there?
    • Hard disk speed - will your game fall over when streaming assets from disk?
    • GPU speed and video RAM - Is the graphical performance of the machine sufficient?
    • Network connectivity - Is there a network connection? Is the internet reachable, e.g. to look for updates?
    • Windows version - Is the version of windows up to date enough to do what you need to do?
    I will cover a subset of these checks here, and recommend where you can find code for the others, as to cover every possible thing you might want to check is beyond the scope of this article as many things are system specific.

    Checking system RAM size

    You can check the system RAM size on windows using the GlobalMemoryStatusEx() function, which will tell you amongst other things the amount of free and total RAM, and the amount of free and total pagefile: const ONE_GIG = 1073741824; MEMORYSTATUSEX status; ZeroMemory(&status); status.dwLength = sizeof(status); GlobalMemoryStatusEx(&status); if (status.ullTotalPhys < ONE_GIG) { MessageBox(0, "You don't have enough RAM, 1GB is needed", "Epic Fail", MB_OK); exit(0); }

    Checking video RAM size

    You can check the video RAM size using DXGI, and then based upon this you could load lower resolution textures to cut down on memory usage, or you could outright tell the player to get a new graphics card. I prefer the first of these two options wherever possible, as it gives a more friendly experience. Only once you have exhausted all possibilities should you give up. The code to detect video RAM is relatively simple: #include #include #include int main() { HRESULT hr; D3D_FEATURE_LEVEL FeatureLevel; // Create DXGI factory to enumerate adapters CComPtr DXGIFactory; hr = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void**)&DXGIFactory); if(SUCCEEDED(hr)) { CComPtr Adapter; hr = DXGIFactory->EnumAdapters1(0, &Adapter); if(SUCCEEDED(hr)) { CComPtr Device; CComPtr Context; hr = D3D11CreateDevice(Adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, D3D11_SDK_VERSION, &Device, &FeatureLevel, &Context); if(SUCCEEDED(hr)) { DXGI_ADAPTER_DESC adapterDesc; Adapter->GetDesc(&adapterDesc); std::wstring Description = adapterDesc.Description; INT64 VideoRam = adapterDesc.DedicatedVideoMemory; INT64 SystemRam = adapterDesc.DedicatedSystemMemory; INT64 SharedRam = adapterDesc.SharedSystemMemory; std::wcout << L"***************** GRAPHICS ADAPTER DETAILS ***********************"; std::wcout << L"Adapter Description: " << Description; std::wcout << L"Dedicated Video RAM: " << VideoRam; std::wcout << L"Dedicated System RAM: " << SystemRam; std::wcout << L"Shared System RAM: " << SharedRam; std::wcout << L"PCI ID: " << Description; std::wcout << L"Feature Level: " << FeatureLevel; } } } } The FeatureLevel variable is also useful here, as it will show you which of the graphics card features the PC actually supports.

    Detecting the windows version

    Detecting the windows version may be important if you only wish to support certain types of installation. For example, you might not want the user to run your game on a server, or you might want to ensure, before your game even tries to access DirectX, that they are not running windows XP or earlier if this will have an impact on your game. Detecting the version information of windows is very simple and should be done using the GetVersionEx win32 function: WindowsVersion::WindowsVersion() : Checkable("windows/version") { OSVERSIONINFOEX vi; ZeroMemory(&vi, sizeof(OSVERSIONINFOEX)); vi.dwOSVersionInfoSize = sizeof(vi); GetVersionEx((LPOSVERSIONINFO)&vi); vMajor = vi.dwMajorVersion; vMinor = vi.dwMinorVersion; spMajor = vi.wServicePackMajor; spMinor = vi.wServicePackMinor; Build = vi.dwBuildNumber; Platform = vi.dwPlatformId; ProductType = vi.wProductType; } bool WindowsVersion::IsServer() { return (ProductType == VER_NT_DOMAIN_CONTROLLER || ProductType == VER_NT_SERVER); } bool WindowsVersion::IsGreaterThanXP() { return (Platform == VER_PLATFORM_WIN32_NT && vMajor >= 6); } Please note, however, that there is an important gotcha to this function call. You cannot use it to detect if the user is running windows 8.1, only version 8.0. This is because the call will only return the newer version number if your executable embeds the correct manifest. If you want to detect this, you should use the newer Version helper API from the Windows 8.1 SDK instead. If all you want to do is detect XP, or anything older than windows 8.0, then GetVersionEx will do fine.

    Detecting hard disk space

    Detecting hard disk space is relatively simple, and can be done via the GetDiskFreeSpaceEx function. You should always avoid the simpler GetDiskFreeSpace function, which operates in number of clusters rather than number of free bytes, taking more work to get a simple answer rather than just returning a simple 64 bit value you can check. Using this function is very simple: INT64 userbytes; INT64 totalbytes; INT64 systembytes; BOOL res = GetDiskFreeSpaceEx(L".", (PULARGE_INTEGER)&userbytes, (PULARGE_INTEGER)&totalbytes, (PULARGE_INTEGER)&systembytes); std::cout << "Your disk has " << userbytes << " bytes available for use."; Note the difference between userbytes and systembytes in the example above. The userbytes value is the amount of disk space available to the current user, as the disk might be limited by a quota. The systembytes is the total space ignoring quotas, available to all users. Therefore, you should usually check the first result field.

    Detecting CPU speed

    There are many ways to detect the CPU speed. Some of the more common ones are:
    • Using WMI to read the Win32_Processor information - my personally preferred method
    • Using the machine code CPUID instruction via inline assembly - less portable, but accurate
    • Using a busy loop to calculate CPU - mostly deprecated as this is extremely hard to get right on multi-tasking operating systems, and not recommended outside of kernel level code
    On most modern systems, you are more likely to run into problems with lack of RAM, or lack of a good graphics card before you encounter problems with CPU performance. Your mileage may vary but in my own experience, less time needs to be spent on detecting the CPU speed and more time on other factors as CPU speed is greatly affected by what else is running at the time and how much swapping the system might need to do.

    Conclusion

    The advice above should help you detect your player's specifications effectively. This is of course just the tip of a very big iceberg, and once you start detecting various metrics from a player's system, you will keep thinking of other things you really should check. Don't get carried away though, as it is easy to get distracted trying to detect potential edge cases, rather than just carrying on with the game.

    Article Update Log

    17 Mar 2014: Initial release


      Report Article
    Sign in to follow this  


    User Feedback


    I don't use DirectX so could you point me to how to do these checks if I am using OpenGL instead

    Share this comment


    Link to comment
    Share on other sites

    I don't think it's a good idea to force exit when specs are not met. I remember some app complained about -1B of ram once, when I had like 4GB :)

    Share this comment


    Link to comment
    Share on other sites

    With OpenGL you can poll the video card memory with these, they're vendor specific so you can use glGetString(GL_VENDOR) to decide which functions to call.

     

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

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

     

    an example for AMD would be

     

    GLint vbo_free[4];

    GLint texture_free[4];

    GLint render_buffer_free[4];

    // Poll VRAM usage

    glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, vbo_free);

    glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, texture_free);

    glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, render_buffer_free);

       

    You'll notice it'll grab 4 integers, here's what they are

    param[0] - total memory free in the pool

    param[1] - largest available free block in the pool
    param[2] - total auxiliary memory free
    param[3] - largest auxiliary free block

    Share this comment


    Link to comment
    Share on other sites

    I don't think it's a good idea to force exit when specs are not met. I remember some app complained about -1B of ram once, when I had like 4GB smile.png

     

    This is why you should use GlobalMemoryStatusEx() as listed above, which returns a 64 bit counter of number of available bytes, rather than the older 32 bit calls which are clamped to a max of 4gb, less if they subtract the memory reserved for kernel use...

     

    Also never cast or store your 64 bit return values into 32 bit integers.

     

    This is something you have to be careful of, to make sure whatever checks you put in place are future proof. Wherever possible use 64 bit values and you won't get this problem within your lifetime smile.png

    Share this comment


    Link to comment
    Share on other sites

    With OpenGL you can poll the video card memory with these, they're vendor specific so you can use glGetString(GL_VENDOR) to decide which functions to call.

     

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

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

     

    an example for AMD would be

     

    GLint vbo_free[4];

    GLint texture_free[4];

    GLint render_buffer_free[4];

    // Poll VRAM usage

    glGetIntegerv(GL_VBO_FREE_MEMORY_ATI, vbo_free);

    glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, texture_free);

    glGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, render_buffer_free);

       

    You'll notice it'll grab 4 integers, here's what they are

    param[0] - total memory free in the pool

    param[1] - largest available free block in the pool
    param[2] - total auxiliary memory free
    param[3] - largest auxiliary free block

     

    This sounds a bit less tidy than the directx way. What do you do if it's an intel card for example?

     

    Surely there is a more portable way?

    Share this comment


    Link to comment
    Share on other sites

    It's nice to inform the users that they don't meet the minimum specs, and nice to tell them in what way they don't, but you don't want to force the user to exit the program if your software thinks it won't be able to run. Users should still be able to try.

     

    And having a minimum required number of CPU cores doesn't make too much sense - Maybe the "minimum spec" for the game is two cores at 1.2ghz... and maybe a single-core 2.8 ghz would actually run the game better, but gets locked out because the developer enforced a minimum number of cores. 1 core doesn't equal 1 thread anyway.

     

    Sometimes being too clever can work against you. Your program giving intelligent suggestions is good, but rigid enforcement of those suggestions might be less so.

    Share this comment


    Link to comment
    Share on other sites

    No mention of checking the specs at installation time? I would personally hate to find out that I can't run your game or may not get the full experience after I've gone through the installation.

     

    I see the need to check things at startup to account for changing conditions, but installation time is still the best time to warn the user about potential issues. All of the tips/code you've presented should probably be incorporated into the installer as well.

    Share this comment


    Link to comment
    Share on other sites
    @davehunt I agree on principle that the same checks should be done by the installer at install time.

    However in this day and age does anyone write a custom installer rather than using nsis, installshield or the like? These do a lot of these checks for you in a simple scripted or RAD style manner so you won't need to know how to check them in C++ there...

    Share this comment


    Link to comment
    Share on other sites

     

    This sounds a bit less tidy than the directx way. What do you do if it's an intel card for example?

     

    Surely there is a more portable way?

     

     

    There isn't that I know of, OpenGL doesn't provide information about the memory usage on the GPU, the only way we're able to know is due to vendor specific extensions and I don't think Intel has such an extension or at least that I can find.

    Share this comment


    Link to comment
    Share on other sites
    [quote name="Joshhua5" timestamp="1426813614"][quote name="braindigitalis" timestamp="1426780063"] This sounds a bit less tidy than the directx way. What do you do if it's an intel card for example?   Surely there is a more portable way? [/quote]   There isn't that I know of, OpenGL doesn't provide information about the memory usage on the GPU, the only way we're able to know is due to vendor specific extensions and I don't think Intel has such an extension or at least that I can find.[/quote] It sounds to me like on windows you might be best using my method If you wanted to check video ram then, and then instantiate opengl afterwards? At least the DirectX way cooperates with all known hardware... As for other platforms I bet you could get this information from /proc on Linux...

    Share this comment


    Link to comment
    Share on other sites

    The problem with installation-time checks.is that many game installs are silent when using a system like Steam.

     

    It'd be great if Steam itself provided an optional "Can I even run it?" test for a game before you even buy it... though I suppose an argument can be made that that's one of the purposes of game demos.

    Share this comment


    Link to comment
    Share on other sites

    The problem with installation-time checks.is that many game installs are silent when using a system like Steam.

     

    It'd be great if Steam itself provided an optional "Can I even run it?" test for a game before you even buy it... though I suppose an argument can be made that that's one of the purposes of game demos.

    Yea, but how many games on Steam do offer a demo?

     

    @braindigitalis: Good article =) I think a good addition would be another one about how to actually find out your minimum/recommended specs.

    Share this comment


    Link to comment
    Share on other sites

    I agree with Servant of the Lord.

     

    A few of the snippets are dangerous by itself:

     

    Forcing to exit the program will do harm in systems with rare hardware that returns values you don't expect or in future versions of Windows. I still remember programs that refuse to install because they think my hard drive is full (they can't handle >120GB drives), I don't meet the memory requirements (it doesn't know how to deal when you have >512MB RAM) or think my DirectX version is too old.

    You may think your code is flawless but there's a high chance it won't work somewhere in someone's machine, and is nearly impossible to make future-proof (what if "drives/disks" no longer exist as such in 10 years?).

     

    GetDiskFreeSpaceEx on the current folder is a bad idea because the program may be launched with different "Start from folder" parameters. DLLs can also change this. You should at least enumerate all disks.

     

    Video RAM size needs to be treated with care. For example, Intel cards will return absurdly low dedicated RAM sizes (<64MB) because they rely on shared memory, since it's basically the same thing as the integrated card is using the system memory anyway. Dedicated RAM is just the amount of memory that the card has exlusively reserved for the GPU chip; which is used for the front buffer, and useful in the cases where the OS is low on memory (the OS can't reclaim that reserved chunk).

    To be honest, you shouldn't base your decisions on Video RAM because the returned values are almost meaningless due to high variety of hardware (dedicated cards, Optimus, Intel integrated, AMD APUs).

    It's nice to have for a bug report, but you shouldn't make your application take decisions based on it (if it does, at least users should be able to override them).

     

    PS. "Epic fail" was funny. But shouldn't actually be part of the program. It's really rude to tell a user that he or his machine is an "epic fail". They may not take it well.

    Share this comment


    Link to comment
    Share on other sites

    Yeah - Don't force the user to exit but give the user a choice.  Sometimes the OS will provide a work-around which is satisfactory such as page file size overflow or the performance reduction is tolerable to the player.

    Share this comment


    Link to comment
    Share on other sites

    I don't think it's a good idea to force exit when specs are not met. I remember some app complained about -1B of ram once, when I had like 4GB smile.png

    Exactly, I still remember games that refused to play because they relied on functions reporting up to 2GB of disk space. I knew I had an awful lot more than that but the installer just didn't give an option and exited.

     

    Always let the user have the final decision.

    Share this comment


    Link to comment
    Share on other sites

     

     

    This sounds a bit less tidy than the directx way. What do you do if it's an intel card for example?
     
    Surely there is a more portable way?

     
    There isn't that I know of, OpenGL doesn't provide information about the memory usage on the GPU, the only way we're able to know is due to vendor specific extensions and I don't think Intel has such an extension or at least that I can find.

     


    It sounds to me like on windows you might be best using my method If you wanted to check video ram then, and then instantiate opengl afterwards? At least the DirectX way cooperates with all known hardware...

    As for other platforms I bet you could get this information from /proc on Linux...

     

    I know this is kinda old, but if you are using Windows, there is one way, but I know you and everyone else won't like it... and that is to use DirectDraw.

     

    Now, you don't have to fully initialize DirectDraw, and you can do this when using OpenGL without any interference between the two APIs (I've done it multiple times in the past).

     

    This will work fine, as long as you just use functions like GetAvailableVidMem() or WaitForVerticaBlank().  Don't do anything like try setting the cooperative level, set the display mode or start creating surfaces with it, okay?

    IDirectDraw7* dd = NULL;
    DDSCAPS2 caps;
    DWORD total_memory = 0, free_memory = 0;
    
    HRESULT hr = DirectDrawCreateEx( NULL, (void**) &dd, IID_IDirectDraw7, NULL );
    if( SUCCEEDED( hr ) )
    {
        dd->GetAvailableVidMem( &caps, &total_memory, &free_memory );
        dd->Release();
    }
    

    Good:

    - Can be used anytime.

    - Can tell you the exact amount of video memory, instead of only telling you how much is left (which is retarded IMO).

    - Works even on Intel GPUs (not vendor specific).

     

    Bad:

    - For systems with unified memory, the results aren't guaranteed to be accurate.

    - Doesn't work on any OS except Windows (not portable).

    - Who uses DirectDraw anymore?  A better question would be, does my vendor even check it's DirectDraw driver code?  Use at your own risk.

     

    Just thought I'd share that bit of advice.  Great article btw, learned multiple new things.

     

    Shogun.

    Share this comment


    Link to comment
    Share on other sites


    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!