Game Optimization for Consoles: Practices That Can Improve PC Performance Too

18 MIN READ

Konstantin Oleschenko

/// UE C++ Developer

    Get the week's
    best stories

    Game optimization is something we work with frequently, so in this article, my colleagues and I will discuss the best practices for optimizing console builds that can also help improve game performance on PC.

    Together with Oleksandr Makeiev, Unity Developer, I’ll cover optimization in Unity. Kostiantyn Oleshchenko, Project Lead of Unreal Department, will talk about the challenges specific to Unreal Engine, while Render Developer Yevhen Karpenko will separately explain optimization from the rendering perspective.

    Game optimization on PC and consoles differs in several ways. A well-optimized PC game provides a better player experience, but optimization is not always a strict requirement. Console games, on the other hand, must meet numerous criteria before they can be released at all. A strict and stable frame rate, no excessively long static screens, limitations on RAM usage, and many other factors are important when bringing a game to console markets.

    Console games must maintain a stable FPS, although the target can vary depending on the complexity of the game and the supported screen resolution, whether 4K, Full HD, or lower.

    The most common targets are 30 FPS for Gen 8 consoles and Nintendo Switch, 60 FPS for most games on PS5 and Xbox Series, and in some cases up to 120 FPS on PS5 and Xbox Series. Since console hardware is known in advance, developers have to choose an appropriate resolution and frame rate that allow them to preserve as much visual quality as possible.

    Optimization in Unity

    There are different ways to optimize games. Nadiia Havrylenko, Unity Developer at Pingle Studio, shares her experience with optimizing games built in Unity.

    For both PC and consoles, the optimization process is similar and consists of two stages: identifying the problem and solving it.

    Unity provides several tools for identifying problems, including Profiler, Memory Profiler, and Frame Debugger. When porting a game, the target platform may also provide profiling tools developed specifically for that hardware. Making proper use of devkit functionality allows developers to investigate issues in greater depth, which is especially useful when general-purpose PC tools cannot reveal problems caused by platform-specific differences.

    Solving these problems requires a creative approach. Once an issue has been identified, you need to put in the effort to solve it completely or find an acceptable compromise.

    Optimization can generally be divided into three areas: CPU, GPU, and memory.

    The most common practices include:

    Modifying slow sections of code. Everyone understands the importance of code quality, but not everyone gives it enough attention. Rewriting slow sections of code, moving them to an asynchronous thread so they can run in the background, or caching data for later reuse will most likely provide an immediate FPS improvement in the build. However, these approaches may require additional memory.

    Proper memory usage. To improve game performance, you need to keep track of memory usage. Identifying and properly fixing memory-related issues in Unity requires a deep understanding of memory management and knowledge of the structure of each particular project.

    Unity Garbage Collector handles garbage cleanup for us, but it can put additional pressure on the CPU and memory, so this should always be kept in mind even when it is running in the background. If one frame generates a large amount of garbage, another frame may spend many milliseconds cleaning it up.

    From a GPU optimization perspective, once you identify a frame or scene that is particularly expensive to render, you can:

    • improve batching;
    • optimize the shaders involved;
    • bake more data to avoid calculating it at runtime, although this will require more memory;
    • move some calculations to the CPU, which will increase CPU time but reduce the load on the GPU.

    Post-processing also deserves additional attention because many packages are optimized primarily for PC rather than consoles. GPU optimization often has its own platform-specific nuances.

    Console optimization requires meticulous work, and we usually spend a considerable amount of time on it. By the end of the process, you may discover that some optimizations do not require any compromise in quality and can benefit every platform. These can include optimized algorithms that still produce the same result, asynchronous or parallel execution approaches, or simple oversights in settings or hierarchy that were discovered and fixed.

    Other optimizations, such as lowering texture quality, simplifying post-processing, or limiting the number of objects that can be created, will affect image quality, memory usage, and CPU load and, in rare cases, may even limit gameplay. In these situations, it makes sense to apply them only to platforms that actually need them.

    Oleksandr Makeiev, Unity Developer at Pingle Studio, shares some additional thoughts on optimizing games in Unity.

    Different optimizations can help a game run more consistently, with fewer crashes and a higher FPS.

    Each type of problem requires different actions, but the general approach to solving them in Unity is almost always the same. Your best friend here is Unity Profiler.

    This tool helps diagnose the root causes of game performance problems. Using Profiler together with Deep Profiling makes it possible to see memory allocations and CPU time, all tied to specific functions in the game code. Unity Profiler consists of many different tools, but the most important ones for optimization are the Profiler window and Memory Profiler.

    Here are some of the most common problems and ways to address them:

    Long performance drops and freezes. These can be identified by spikes in the Profiler window graph. Some of the most common causes of freezes and performance drops are Garbage Collector activity, creating large numbers of objects within a single frame, and synchronous execution of expensive algorithms.

    To reduce GC execution time and frequency, you need to use Profiler to find functions that generate large amounts of garbage and optimize them so they do not leave behind objects that will no longer be used.

    Sometimes demanding functions occupy a significant portion of the frame and cannot simply be removed. In these cases, you can consider moving them to asynchronous execution or at least distributing the workload across several frames using Coroutines.

    Insufficient memory. This is a common problem for console games because the available memory is significantly lower than on PC. It is also one of the main causes of critical failures and crashes.

    Memory Profiler allows you to see which objects consume a large portion of memory and which remain in memory despite no longer being used. Textures are a common example because they have native and managed parts that reference one another, preventing the GC from identifying them for memory release.

    A useful tip: always name the textures you create.

    Keeping entire arrays or lists of objects that will no longer be used, or creating new arrays and lists instead of reusing existing ones, can also lead to memory leaks. One example would be lists of vectors needed during level generation but no longer required during subsequent gameplay.

    Long loading screens are often caused by processes that do not necessarily need to happen immediately. Sometimes certain assets can be loaded later if they are outside the frame or not even close to the player. These objects can instead be loaded during gameplay.

    All the problems mentioned above can also contribute to slow game loading. Generating large amounts of garbage, forcing the GC to run several times per frame, slows down the loading process. Using Profiler to reduce the execution time of generation and loading functions should help.

    Low overall game performance. It is important to understand where the game’s bottleneck is. Depending on whether the limitation comes from the CPU or GPU, different approaches should be considered.

    For CPU optimization, try reducing constant workload in Update() methods or any other frequently called functions. Even a very small reduction in processing time can result in significant improvements if a function is called thousands of times per frame. Everything described in the previous sections can also help improve overall performance.

    For GPU optimization, consider using Levels of Detail, a system that displays a less detailed version of a model depending on how much screen space it occupies.

    Dynamic Resolution can also provide a significant performance improvement by smoothing out periods of high load when many objects and effects are visible on screen. Lowering the resolution during these moments is usually not very noticeable, especially when combined with a fast upscaling method, but it helps maintain a stable frame rate.

    If the game performs expensive physics calculations, these should also be optimized. This may involve using Layers for physics interactions, choosing less expensive physics methods, running calculations less frequently, or reducing the precision of physics calculations.

    And most importantly, do not try to optimize something that already works well enough. Solve problems as they appear.

    Optimization in Unreal Engine

    Kostiantyn Oleshchenko, Project Lead of Unreal Department, makes a similar point: regardless of the platform, the general optimization tasks remain the same, identifying problems and solving them, while taking the specifics of Unreal Engine into account.

    One of the first things I recommend looking at is the rendering type: Forward, which is more commonly associated with mobile platforms, or Deferred, which is the default and is suitable for most platforms. Choosing the rendering type that best fits your particular game is the first step toward optimal performance in any Unreal Engine project.

    The next thing to consider is platform limitations. This is especially relevant for consoles and mobile platforms. On PC, attention should be paid to configuration presets: the lower the minimum system requirements, the wider the potential audience.

    The most common limitations on console and mobile platforms include:

    Post-processing. With Forward rendering, many post-processing effects will not work, so they need to be reworked. For example, post-processing effects can be replaced with tools such as Tonemapper and configured to achieve the best possible image quality.

    Decals. These do not work with Forward rendering. They can be relatively easily replaced with Plane + Material, producing almost the same visual result with significantly less performance overhead.

    Light sources and other lighting-related limitations. Mobile platforms have significant restrictions on the number of Movable light sources. This usually requires substantial reworking, such as replacing Movable lights with static lighting, rebaking lightmaps, and so on. Movable lighting is then used only in areas where it is absolutely necessary.

    The same applies to assets, including textures, skeletal animation binaries, and shader representations, which may also need to be recompiled into internal formats used by the platform to achieve better performance. There are many restrictions associated with this. For example, the PS5 audio codec supports only a certain bitrate range, which may require particularly detailed audio files to be resampled.

    Naturally, meshes are also optimized by adding simpler LODs, using different compression algorithms to recompress textures, simplifying materials without noticeably affecting visual quality, and so on.

    The next topic worth discussing is performance spikes, sudden FPS drops that last for a short period of time and occur in virtually any porting project.

    We divide them into three general categories.

    VFX

    Many Unreal Engine games contain demanding visual effects that may work well on PCs equipped with powerful GPUs. However, they can cause problems even on current-generation consoles.

    To reduce the performance load caused by VFX, we work in two main directions.

    We collaborate closely with VFX and Tech Art teams to optimize every possible effect and find the right balance between visual quality and performance.

    When necessary, we also fine-tune Memory Management logic to make the most efficient use of the available memory budget. This is especially relevant for platforms such as Switch, Oculus, and PICO.

    Sound effects

    If a project uses Wwise, it makes working with audio significantly easier. Otherwise, we analyze how sounds and effects are used and loaded, then reorganize their categories because the engine uses these categories to determine whether a particular sound should remain permanently cached or can already be unloaded.

    Fine-tuning the sound cache takes considerable time, but it can provide an almost ideal balance between performance and memory usage.

    For example, Android handles SoundCue audio relatively poorly, with CPU load approximately three to four times higher than an equivalent SoundWave. In Android projects, this therefore requires reworking the system toward a more performance-oriented workflow.

    PSO spikes

    These performance drops occur when in-game lighting suddenly changes as a result of particular gameplay scenarios. In most cases, they can be addressed through careful PSO cache configuration.

    This process often takes considerable time. It involves collecting data, compiling the collected data into a dedicated file that can then be used when creating subsequent builds, configuring cache collection logic when the application launches, and other related work. However, the result is worth the effort.

    Rendering Optimization

    A significant portion of potential optimization work comes from rendering. Yevhen Karpenko, one of Pingle’s key render engineers, shares his perspective on optimization.

    When porting a game to consoles, developers may encounter several rendering-related challenges:

    • Consoles from different generations and manufacturers have different architectures, requiring rendering code to be optimized and implemented specifically for each platform.
    • Consoles generally have less powerful hardware than modern PCs, which may require reducing graphical quality.
    • Consoles within the same generation may have different GPUs and memory architectures, requiring rendering optimization for each individual platform.

    Even consoles from the same generation and manufacturer may have different models with different specifications. Examples include PS4, PS4 Pro, and PS4 Slim, as well as Xbox One, Xbox One S, and Xbox One X. This can require different rendering functionality even within the same console family.

    Another very important issue is compatibility between console graphics APIs and the graphics API used by the original game. The easiest case is porting a DirectX game to Xbox consoles because the Xbox graphics API is almost entirely compatible with DirectX, apart from certain differences.

    If the original game was implemented using another API, incompatibilities and differences between APIs may arise. These can include the declarative model used to form rendering commands, differences between coordinate systems, the concept of rendering states, shader languages, and shader models.

    OpenGL, DirectX, and Metal are three popular graphics APIs used for game and 3D application development, and when porting to specific game consoles, developers may encounter the challenges described above.

    All three graphics APIs use their own coordinate systems to determine the position and orientation of objects in 3D space. OpenGL and Vulkan use an upward-facing Y-axis. DirectX, Metal, and consoles use a downward-facing Y-axis in Clip Space and texture coordinates.

    OpenGL uses a depth range of [-1, 1] in Clip Space, where the near plane is -1 and the far plane is 1. DirectX and Metal use a depth range of [0, 1], where the near plane is 0 and the far plane is 1.

    These differences can create problems when transferring code between APIs. A common solution involves flipping the Y-axis in shaders when porting from OpenGL/Vulkan and adjusting depth buffer calculations depending on the API.

    There are also key differences in how rendering commands are created in Metal, OpenGL, and DirectX. Vulkan, DirectX 12, and Metal focus on a low-level, command-buffer-oriented approach. Developers explicitly record rendering commands into a queue that the GPU can then execute efficiently.

    OpenGL and DirectX 11 use a more state-oriented approach. Developers set different states, such as textures and shaders, which affect subsequent rendering calls. DirectX 11, like OpenGL, uses a state-based approach, although with a more object-oriented structure.

    Vulkan, DirectX 12, and Metal pre-record rendering commands into a command buffer. This allows the GPU to optimize execution and reduces driver overhead. OpenGL and DirectX 11 depend on state changes, which can lead to excessive calls and less efficient execution of complex scenes with frequent state changes.

    Differences between shader models are another challenge when porting rendering systems to consoles. Shader models are specifications that define the capabilities and functions available to GPU shaders. Different versions provide different levels of complexity and functionality, affecting the visual effects that can be achieved in games and applications.

    Shader models define the set of instructions, data types, and functions supported by the GPU for shader programs.

    Shader models can provide features such as:

    • more complex lighting models, including physically based rendering;
    • advanced texture filtering methods, such as anisotropic filtering;
    • tessellation shaders for detailed geometry;
    • compute shaders for general-purpose calculations and asynchronous GPU computing;
    • higher-precision floating-point numbers, resulting in more accurate calculations and fewer visual artifacts.

    Different consoles may implement shader-model functionality differently depending on the cases described earlier. This requires developers to perform more detailed rendering porting work. It may involve full or partial backward compatibility with the shader-model implementation used by the original game, implementing missing shader-model functionality, or developing an alternative solution.

    Graphics APIs also have their own shader programming languages and shader systems, which may be incompatible with one another. GLSL, HLSL, and Metal Shading Language (MSL) are all used to write shaders, but they target different graphics APIs and have several important differences.

    All three can be used to create vertex, pixel, and geometry shaders that control how objects are displayed on screen. However, they have different syntax, shader compilation processes, loading and reflection systems, and different approaches to binding resources and data to shaders.

    When porting a game to consoles, shader code and the shader compilation system can become a major challenge from the rendering perspective because different graphics APIs, especially console APIs, may implement shader resource binding, compilation, and shader capabilities at very different levels of abstraction to achieve better performance and hardware utilization.

    The same applies to assets. Textures, skeletal animation binaries, shader reflection data, and other resources may need to be recompiled into internal formats used by the console to achieve better performance.

    In addition to all of this, consoles generally have less powerful hardware than modern PCs, as mentioned earlier, so various techniques can be used when implementing rendering.

    One such technique is upscaling. In video games, upscaling refers to methods that increase the resolution of a rendered image. This becomes particularly important when moving a game from PC, where resolution can vary significantly, to consoles with more standardized target resolutions.

    When upscaling is used, the game is rendered at a resolution that is optimal for the console in order to maintain 60 FPS. This resolution can be significantly lower than commonly expected standards, for example 720p, and is then increased to a more acceptable output resolution using software or hardware capabilities available on the console.

    When it comes to assets, texture optimization is used in most cases. This can include changing formats, converting textures into internal console formats, or converting them into packed formats that allow faster texture loading.

    Simply reducing texture resolution can also be useful, particularly when upscaling is involved. The original texture resolution may no longer be necessary if the game itself is rendered at a lower resolution.

    Shader optimization can involve rewriting inefficient sections of code, choosing more appropriate data types, and selecting less complex and more efficient shading algorithms.

    One real optimization example from a Pingle project involved optimizing the geometry vertex format, which made it possible to accelerate both geometry transfer to the GPU and its rendering.

    Another example is implementing simpler and less performance-intensive shading algorithms, such as less complex shading methods or Screen Space Ambient Occlusion.

    Real-time calculation algorithms and rendering passes can also be replaced with precomputed alternatives. Examples include baking shadow maps, environment maps, and reflection maps, effectively replacing rendering passes that require significant console computing resources with data calculated in advance.

    Conclusion: Why Improve a PC Build Through Console Optimization?

    Sometimes we see poorly optimized PC games begin to perform better after patches are released. However, this is far from an ideal approach, especially considering that relatively few games today are developed exclusively for PC.

    If you are planning to release a game on several platforms simultaneously, it is extremely important to pay attention to optimizing the console versions. The optimization required for consoles can also improve the performance of the PC version.

    By applying console optimization techniques to PC versions, developers can ensure strong game performance. This is particularly important in a market where stability and smooth gameplay have a significant impact on player satisfaction.

    Share

    book a free consultation

    We‘ll gladly discuss your brand, business goals, and tasks. After submitting the form, we will contact you within 24 hours.

    Olga Chernova

    /// Business Development Director