Legacy vs. modern rendering: the hidden challenges of game porting

By Arthur Pedroso, Game Engineer at Kokku

As classic games stand the test of time, there is a growing demand to revive them on modern platforms. However, porting older technologies to present-day platforms introduces new challenges that emerge due to the intrinsic differences between legacy and modern rendering technologies, demanding careful adaptation from developers.

Legacy rendering systems vs. modern ones

Older rendering systems were generally designed around fixed-function rendering pipelines or specialized hardware architectures, reflecting the constraints of the graphics devices available at the time. Within this approach, the graphics programmer would have to work by changing the state of the graphical device and feeding the GPU with data on the correct steps of the pipeline. On some platforms, like consoles, this could vary based on the existence of specialized hardware components that required low-level programming to be used effectively.


(Legacy OpenGL fixed-function rendering pipeline)

The legacy rendering approaches had specific characteristics based on each platform due to the specialized hardware used in each platform. The PS2, for example, had vector processing units that needed to be programmed in assembly to be used to their maximum efficiency. These specific hardware implementations reflected not only in the rendering techniques and architecture but also in how assets were loaded and processed throughout the game.



(Hardware architecture of the Playstation 2)

As graphics technology evolved, modern graphics APIs (DX12, Vulkan, Metal) emerged as a solution to the need for greater control over rendering systems, shifting more responsibility to the user and providing deeper access to the underlying hardware. This allows programmers to use the hardware more efficiently, with greater emphasis on multithreaded execution and lesser driver overhead, improving the performance and visuals of titles that use these technologies. However, the stark difference between this philosophy and that of older platforms can bring significant costs to the porting of older games to newer platforms.

Porting the render architecture

Despite the big differences between modern and legacy APIs, replicating an old rendering pipeline through a modern API is feasible, even if it requires extensive adaptation. It is, however, important to note how the original architecture of the render system is ported.

Some older games designed with portability in mind implemented a graphical abstraction layer to make it easier to adapt the game to multiple platforms available at the time. In these cases, porting the rendering to newer platforms tends to be straightforward, only requiring a new implementation of the graphical middleware API, since the game logic already uses the middleware.

However, many legacy games had rendering calls directly in their source code, often scattered across different engine modules. One approach to handling this is aggregating these API calls into a centralized rendering module translating them to a modern rendering model. This would preserve much of the rendering logic while avoiding unpredictable issues caused by the rework of the game’s engine architecture, though it may require workarounds for legacy behaviors. However, this approach can also limit modern optimization and introduce performance issues.

Alternatively, one could do a complete refactoring of the game’s rendering system, which allows for a fully modernized and optimized architecture. While this has the potential to bring major visual and performance improvements, the rework could also bring new challenges. Rendering logic could be intertwined with other engine systems and refactoring it could risk altering the original game’s visuals or breaking game logic dependent on graphics behavior.

Assets

One of the first points to consider when porting older titles to modern platforms is the format in which the assets were encoded and stored. Older games frequently used proprietary formats for textures, meshes, and animations, often optimized for platform-specific constraints such as memory bandwidth, compression, and streaming. Additionally, game assets could also be encoded and exported in a specific way for the application of a particular rendering technique tailored to the rendering pipeline of the host platform. Depending on the way the game is ported, assets may need to be re-exported or decoded in runtime for compatibility with the newer rendering system.

Shaders

Another key challenge to look out for when porting games from legacy platforms is handling shaders. As mentioned earlier, older rendering techniques were generally achieved by configuring the graphics device’s state and providing it with specific inputs, rather than using programmable shaders like those in modern APIs.

Although laborious, these techniques and systems can be adapted with modern shaders, with difficulty depending on the original implementation. If they are separated and centered in specific files or similar regions of code, they can be more easily found, grouped, and translated into newer platforms. Parsers and translation tools could even be developed to interpret and translate these files, depending on the situation. However, if the rendering logic is spread across multiple engine systems, the process of translating all the techniques would be much harder to automate and would require a lot of labor-intensive reverse engineering and translation work.

The Nintendo Wii for example, had a unique hardware feature called TEV (texture environment unit) that provided graphics programmers with the ability to combine multiple textures through multiple stages, where each stage could apply different operations to the texture. This added crucial flexibility to the usage of the fixed-pipeline of the Wii and allowed the implementation of more complex rendering techniques at a lower cost. As a result, porting Wii games to modern platforms often requires recreating these effects through shaders.

Optimization issues

As mentioned before, emulating older rendering systems with modern APIs can lead to performance issues. This can occur for a variety of reasons, with a common one arising from the difference in how these APIs deal with device state changes. Legacy rendering systems based on fixed pipelines worked around constant manipulation of the device state, while modern APIs need to batch these state changes into command lists linked to a PSO (pipeline state object). Doing these changes constantly could result in higher driver overhead and reduced performance.

Regardless of the specific performance issue that may be encountered during porting, it is important to consider that some level of optimization work will likely be required, even for simpler games.

Middle layer graphics API

As mentioned earlier, implementing modern rendering systems graphics APIs demands significant effort, which greatly increases depending on the number of target platforms with different graphics APIs. To address this, a practical solution could be the usage of a graphics abstraction layer APIs. They provide an abstraction of rendering concepts, allowing developers to implement their rendering architecture once while ensuring compatibility with multiple platforms.

Notable examples include: The Forge Framework, which provides a robust abstraction of lower-level concepts of modern rendering APIs, and BGFX, which has a higher-level approach to rendering APIs abstraction.

Opportunities for Improvement

While the porting of older rendering systems presents challenges, it also offers opportunities for great visual improvements at a low implementation cost given the features available on modern hardware. An effective example is the usage of post-processing techniques, which can often be applied regardless of the characteristics of the assets originally used in the game. Examples include: antialiasing techniques, shadow filters, screen-space ambient occlusion, color grading, and depth of field.


(On the left, is an example of an antialiasing technique. On the right, a screen-space ambient occlusion example)

Conclusion

Porting old rendering systems to current platforms showcases the evolution of graphics programming, revealing unique challenges and opportunities encountered when adapting older technologies.

Different approaches to porting vary in cost, flexibility, and potential improvements. It is always important to analyze the game’s source code to understand the solutions used at the time to deal with hardware limitations and to tailor the porting of the rendering system to the project’s needs.

Be it a cost-effective wrapper around the original rendering API, or a complete rework of the rendering architecture, developers should carefully weigh the challenges and opportunities presented during the porting process.