64-bit programming for Game Developers

Microsoft Game Technology Group

April 2006

Introduction

Processor manufacturers are already shipping 64-bit processors for the desktop market, and they are planning to stop manufacturing 32-bit processors sometime this year (2006). It is important for game developers to take advantage of the improvements that 64-bit processors offer and to ensure that their legacy applications run correctly on the new processors and 64-bit Windows platforms. This article addresses compatibility and porting issues and help developers ease their transition to 64-bit.

The 64-bit platforms that Microsoft currently has are Windows Server 2003 Service Pack 1, Windows XP Professional x64 Edition (available as OEM or to developers through MSDN), and Windows Vista.

Differences

The first thing most developers notice is that 64-bit processors provide a huge leap in the amount of physical and virtual memory that can be addressed.

Beyond just memory, 64-bit applications that use memory-mapped file I/O benefit greatly from the increased virtual address space. The 64-bit architecture also has improved floating-point performance and faster passing of parameters. Sixty-four-bit processors have double the number of registers, of both general purpose and streaming SIMD extensions (SSE) types, as well as support for SSE and SSE2 instruction sets; some 64-bit processors even support SSE3 instruction sets.

The /LARGEADDRESSAWARE Flag

It is a good practice to specify large-address-aware when building, by using the build flag /LARGEADDRESSAWARE, even if the application is not intended for a 64-bit platform, because of the advantages that are gained at no cost. As explained earlier, enabling this flag for a build allows a 32-bit program to access more memory with special boot options on a 32-bit OS or on a 64-bit OS. However, developers must be careful that pointer assumptions are not made, such as assuming that the high-bit is never set in a 32-bit pointer. In general, enabling the /LARGEADDRESSAWARE flag is a good practice.

Compatibility

Sixty-four-bit Windows operating systems are binary compatible with the IA32 architecture, and the majority of APIs that 32-bit applications use are available through the Windows 32-bit on Windows 64-bit Emulator (WoW64). WoW64 helps ensure that these APIs will work as intended.

WoW64 has an execution layer that handles the marshalling of 32-bit data. WoW64 redirects DLL file requests, redirects some registry branches for 32-bit applications, and reflects some registry branches for 32- and 64-bit applications.

More information on WoW64 can be found at WOW64 Implementation Details

Potential Compatibility Pitfalls

Most applications developed for a 32-bit platform will run without problems on a 64-bit platform. A few applications will have issues, and some of the issues these applications will encounter are listed below:

The most common compatibility issues are installers that execute 16-bit code and not having 64-bit drivers for copy protection schemes.

The next section discusses issues related to porting code to 64-bit native for developers that want to ensure their legacy programs work on 64-bit platforms. It is also for developers who are unfamiliar with 64-bit programming.

Porting to 64-bit

Having the right tools and libraries will help to ease the transition from 32-bit to 64-bit development. The DirectX 9 SDK has libraries to support both x86 and x64 projects. Microsoft Visual Studio 2005 supports both x86 and x64 code generation and comes with libraries optimized for x64 code generation. However it will also be necessary for the developer to distribute the 2005 Visual C runtimes with their applications. Note that Visual Studio 2005 Express SKU does not include the x64 compiler, but that the Standard, Professional, and Team System versions all do.

Developers who are targeting 32-bit platforms can prepare for 64-bit development to make their transition easier later on. When compiling 32-bit projects, developers should use the /Wp64 flag, whichi will generate portability warnings. Switching to 64-bit tools and libraries will probably generate a lot of new build errors initially; so, it is advisable to switch bit-neutral tools and libraries and correct any warnings before switching to a 64-bit build.

Changing tools, changing libraries, and using certain compiler flags will not be enough, though. Assumptions in coding standards must be reevaluated to ensure that current coding standards don't allow portability issues. Portability issues can include pointer truncation, size and alignment of data types, reliance on 32-bit DLLs, use of legacy APIs, assembly code, and old binary files. The major porting issues are explained in more detail below:

Pointer Truncation

Pointers are 64-bits on a 64-bit OS, so casting pointers to other data types can result in truncation and pointer arithmetic can result in corruption. Using the /Wp64 flag will usually provide a warning about this kind of issue, but using polymorphic types (INT_PTR, DWORD_PTR, SIZE_T, UINT_PTR, etc.) when casting pointer types is a good practice to help avoid this issue altogether. Since pointers are 64-bits on new platforms, developers should check the ordering of pointers and data types in classes and structures to reduce or eliminate padding.

Data Types and Binary Files

While pointers increase from 32 bits to 64 on a 64-bit platform, other data types don't. Fixed-precision data types (DWORD32, DWORD64, INT32, INT64, LONG32, LONG64, UINT32, UINT64) can be used in places where the size of the data type must be known; for example, in a binary file structure. The changes in pointer size and data alignment will require special handling to ensure 32-bit-to-64-bit compatibility. More information can be found here: Getting Ready for 64-bit Windows: The New Data Types

Legacy APIs and Data Alignment

Some Win32 APIs have been deprecated and replaced with more neutral API calls such as SetWindowLongPtr in place of SetWindowLong.

The performance penalty for non-aligned accesses is greater on x64 platform than on an x86 platform. The TYPE_ALIGNMENT(t) and the FIELD_OFFSET(t, member) macros can be used to determine alignment information that can used directly by code. Correct use of these aforementioned macros should eliminate potential non-aligned access penalties.

More information on the TYPE_ALIGNMENT macro, the FIELD_OFFSET macro, and general 64-bit programming information can be found at 64-bit Windows Programming: Migration Tips and Getting Ready for 64-bit Windows: Rules for Using Pointers.

Assembly Code

Inline assembly code is not supported on 64-bit platforms and needs to be replaced. Changes in the architecture may have changed application bottlenecks, and C/C++ or intrinsics can achieve similar results with code that is easier to read. The most advisable practice is to switch all assembly code to C or C++. Intrinsics can be used in place of assembly, but should only be used after full profiling and analysis has been performed.

The x87, MMX, and 3dNow! instruction sets are deprecated in 64-bit modes. The instructions sets are still present for backwards compatibility for 32-bit mode, but to avoid compatibility issues in the future, their use in current and future projects is discouraged.

Profiling and Optimization

All developers need to re-profile any applications that are being ported to new architectures. Many applications being ported to 64-bit platforms will have different performance profiles. Developers need to run 64-bit performance tests before assessing what needs to be optimized. The good news is that many traditional optimizations still work on 64-bit platforms. In addition, 64-bit compilers can also perform a lot of optimizations with the correct use of compiler flags and coding hints.

Some structures may have their internal data types reordered to conserve memory space and improve caching. Array indices can be used instead of a full 64-bit pointer in some cases. The /fp:fast flag can improve floating-point optimizing and vectorization. Using __restrict, declspec(restrict), and declspec(noalias) can help the compiler resolve aliasing and improve use of the register file.

More information on /fp:fast can be found at /fp (Specify Floating-Point Behavior)

More information on __restrict can be found at Microsoft-Specific Modifiers

More information on declspec(restrict) can be found at Optimization Best Practices

More information on declspec(noalias) can be found at __declspec(noalias)

Summary

Sixty-four-bit architectures allow developers to push the limitations on how games look, sound, and play. Transitioning from 32-bit programming to 64-bit programming is not trivial, however. By understanding the differences between the two, and by using the newest tools, the transition to 64-bit platforms can be easier and faster.

More information on 64-bit programming can be found at Visual C++ Developer Center: 64-Bit Programming