System Initialization

Memory Optimization

Keep heap allocations to a minimum, and never allocate from the heap within a tight loop.

Stack Allocator

Pool Allocator

Aligned Allocation

// Shift the given address upwards if/as necessary to
// ensure it is aligned to the given number of bytes.
inline uintptr_t AlignAddress(uintptr_t addr, size_t align)
{
    const size_t mask = align - 1;
    assert((align & mask) == 0); // pwr of 2
    return (addr + mask) & ~mask;
}

Iterators

Its a good idea to override the increment/decrement operator for custom containers like a home-written binary tree; the complexity of iterating through the structure in usage is simplified to a ++/--

STL template library/container objects

Strings and resource identifiers

At Naughty Dog, we permit runtime hashing of strings, but we also use C++11’s user-defined literals feature to transform the syntax "any_string"_sid directly into a hashed integer value at compile time

At Naughty Dog, we started out using a variant of the CRC-32 algorithm to hash our strings, and we encountered only a handful of collisions during many years of development on Uncharted and The Last of Us. And when a collision did occur, fixing it was a simple matter of slightly altering one of the strings (e.g., append a “2” or a “b” to one of the strings, or use a totally different but synonymous string). That being said, Naughty Dog has moved to a 64-bit hashing function for The Last of Us Part II and all of our future game titles; this should essentially eliminate the possibility of hash collisions

i++ vs ++i

The postincrement operator increments the contents of the variable after it has been used. This means that writing ++p introduces a data dependency into your code—the CPU must wait for the increment operation to be completed before its value can be used in the expression. On a deeply pipelined CPU, this introduces a stall. On the other hand, with p++ there is no data dependency. The value of the variable can be used immediately, and the increment operation can happen later or in parallel with its use. Either way, no stall is introduced into the pipeline. ... This shouldnt matter in loops anyways due to compiler optimization

Dynamic Arrays

Dynamic arrays are probably best used during development, when you are as yet unsure of the buffer sizes you’ll require. They can always be converted into fixed size arrays once suitable memory budgets have been established.

Development Memory

a PS3 development kit has 256 MiB of retail memory, plus an additional 256 MiB of “debug” memory that is not available on a retail unit. If we store our strings in debug memory, we needn’t worry about their impact on the memory footprint of the final shipping game. (We just need to be careful never to write production code that depends on the strings being available!)

Unicode (Important for localization/translation)

http://www.joelonsoftware.com/articles/Unicode.html

Localization Management

You will need to manage a database of all human-readable strings in your game, so that they can all be reliably translated. The software must display the proper language given the user’s installation settings. The formatting of the strings may be to- tally different in different languages—for example, Chinese is sometimes writ- ten vertically, and Hebrew reads right-to-left. The lengths of the strings will vary greatly from language to language

At Naughty Dog, we use a localization database that we developed in-house. The localization tool’s back end consists of a MySQL database located on a server that is accessible both to the developers within Naughty Dog and also to the various external companies with which we work to translate our text and speech audio clips into the various languages our games support

Game Configuration Examples