This article was originally published in Polish in issue 4/2025 (119) (July/August 2025) of Programista magazine.
In this article, we will take a quick walk through the history of graphics APIs such as DirectX, OpenGL, Vulkan, and the accompanying development of graphics cards on the one hand, and video games on the other, over the years. We will not be learning how to program in any of these APIs. This article should be understandable and may be engaging for anyone curious about games or graphics…
This article was originally published in Polish in issue 4/2025 (119) (July/August 2025) of Programista magazine.
In this article, we will take a quick walk through the history of graphics APIs such as DirectX, OpenGL, Vulkan, and the accompanying development of graphics cards on the one hand, and video games on the other, over the years. We will not be learning how to program in any of these APIs. This article should be understandable and may be engaging for anyone curious about games or graphics, or at least for those who played games in childhood.
By necessity, the article presents only selected facts and applies certain simplifications. This will not be a chronicle of successive dates and events in the history of computer graphics development. Rather, I want to invite the reader on a journey during which we will jump along the timeline (all the way back to ancient times!) in order to best understand where the current situation in the field of graphics card programming comes from, and how it may continue to develop in the future.
We will focus on platforms used for playing video games at home, and thus primarily on PCs running Windows. The development of graphics on Linux, on mobile devices, on various professional workstations, or on servers could constitute a separate story.
Prehistory
To understand how and for what purpose graphics APIs came into being, we must go back to times when they did not yet exist. The 1980s are, from the point of view of modern computer science, true prehistory. Home computers such as the ZX Spectrum, Commodore 64, Atari, or Amiga reigned supreme at the time. They had limited hardware capabilities. For example, the 16-bit Atari ST was equipped with 256 KB to 1 MB of RAM and a processor running at 8 MHz.
Yet engaging games were already being created for such computers. Their graphics were still 2D, with low resolution and a limited color palette. Even then, however, computers were equipped with hardware acceleration of graphics display in the form of drawing so-called sprites. Thanks to them, it was possible to display moving objects (such as the protagonist or enemy monsters) quickly enough for the player to perceive smooth motion, which would not have been possible if the slow main processor had had to draw them pixel by pixel.
Programming these computers was done at a very low level, often directly in assembly language. Displaying graphics, as well as all other hardware functions, was accomplished by setting appropriate registers or writing to specific memory addresses. Such code was of course specific to a given computer model, which was not a problem as long as all users had the same or similar hardware (possibly extended with additional memory, etc.).
To understand the evolution of graphics APIs, we will look at Figure 1 and return to it repeatedly. The situation described above is illustrated by the first column, labeled (1). We see there that the game calls directly into the hardware, specifically the graphics processor (GPU).
Figure 1. The evolution of games, engines, and graphics APIs
The first PCs
In the second half of the 1980s and the early 1990s, IBM PC–type personal computers began to dominate. This architecture conquered the market, among other reasons, thanks to its modularity and expandability. Various components, such as expansion cards, could be installed into a standardized case and motherboard, and various peripheral devices supplied by many manufacturers could be connected to universal input–output ports.
Early PCs had monochrome monitors with graphics cards such as Hercules, but later color standards like VGA appeared. Anyone who additionally installed a sound card and a CD-ROM drive in their computer could boast that their computer was “multimedia.” They could then buy and run a “multimedia encyclopedia” stored on a CD, which contained not only a huge collection of entries from various fields with textual descriptions, but also images attached to some entries, sounds, and even short video sequences, which at the time made an enormous impression.
Returning, however, to the earliest PCs, the operating system there was MS-DOS, and application programming was still done at a low level, through direct access to hardware. In the modular PC architecture this created problems with ensuring compatibility with various devices. There was no Plug&Play architecture at the time. Each game or program had to be individually adapted to work with many popular device models. Figure 2 shows screenshots from the game “Duke Nukem 3D,” in which one had to manually configure the owned sound card model and the IRQ interrupt number in order for sound to appear in the game.
Figure 2. Sound configuration in the game Duke Nukem 3D
Windows and drivers
While DOS was a single-tasking system, the emergence of Windows provided the ability to work with multiple applications at once. Along with it, a graphical user interface (GUI) based on windows became available, which could overlap each other. Initially, the cooperation model between running programs was cooperative (an application had to yield control to the next one). This, however, meant that a poorly written program could easily hang the entire computer. In newer versions of Windows, such as 95 and 98, and even more so in versions based on the NT kernel (Windows 2000, XP, etc.), applications could no longer so easily hang the entire system or gain access to another process’s memory.
This required isolating applications from direct access to hardware. Such “virtualization,” combined with multitasking, as well as the need to ensure compatibility of different applications with different models of keyboards, mice, printers, monitors, etc., caused a driver to become a necessary intermediary. Supplied by the manufacturer of a given device and installed in the system, it was responsible for controlling its device, while applications performed all operations (e.g., displaying windows, playing sounds, printing) through system APIs (WinAPI).
The emergence of DirectX
Such a state was good for windowed applications, such as text editors, but it was not ideal for games. Figure 3 shows a screenshot from the game “SimCity 2000,” which in the Windows 3.1 version displayed its graphics within a regular window, using the system menu and other controls. As is not hard to guess, game developers would prefer to display all graphics on their own terms, taking control of the full screen. This not only allows games to look more attractive, but also provides better performance, since direct access to the frame buffer bypasses the composition of overlapping windows and the associated additional data copying.
Figure 3. The game SimCity 2000 in the Windows 3.x version
In response to these needs, Microsoft created the DirectX interface. This new API, intended for games and other multimedia applications, provided more efficient access to hardware functions, such as entering fullscreen mode (exclusive fullscreen). The word “direct,” however, does not mean direct access to hardware in as literal a sense as it did on early computers. A driver was still running in the system, as well as other applications, so pressing Alt+Tab caused exiting fullscreen mode.
The letter “X” in the name DirectX, in turn, stood for “something,” which could be one of many components that made up this API. Older versions of DirectX provided separate parts for displaying 2D graphics (DirectDraw), 3D graphics (Direct3D), playing sound (DirectSound), music (DirectMusic), handling keyboards, mice, and other controllers (DirectInput), video playback (DirectShow), and others. Only Direct3D has survived to the present day. The remaining libraries, if they still function to ensure backward compatibility, are nevertheless no longer recommended, as they have been superseded by other, more modern solutions. Today, the term DirectX is therefore synonymous with Direct3D.
Another interesting curiosity may be the origin of the name of the Xbox console. It stands for nothing other than “DirectX Box,” that is, a box with DirectX. When creating its console, Microsoft decided to “pack” a computer with a special version of Windows and DirectX as the API intended for handling graphics and all multimedia by games created for this platform. This is still the case today, where developing games for new generations of Xbox requires the use of DirectX (with console-specific extensions).
What is an API?
At this point it is worth clarifying what we mean when we say API. This abbreviation stands for “Application Programming Interface.” An interface is generally a connector between one thing and another. Just as a graphical user interface (GUI) is a connector that allows a user to control an application, an API constitutes for the programmer a communication protocol between parts of software, for example between a program being written and some library. In practice, an API is a definition of functions, structures, constants, and other code elements, written for example in the form of documentation or a .h header file for the C language.
Graphics APIs are an interface between an application (most often a game) and the graphics driver. An API such as Direct3D constitutes a standard communication protocol that allows issuing commands to draw graphics on the screen, while the driver is responsible for translating them into low-level commands specific to a given GPU model (a discrete graphics card or a graphics unit integrated with the processor). This situation is illustrated by column (2) in Figure 1.
Precursors of 3D
The appearance of three-dimensional graphics in games was not a sudden event. Many links in this evolution can be identified. Most of them are associated with the development of games from the first-person shooter (FPS) genre. One of the first popular games of this type was “Wolfenstein 3D” by id Software. While the three-dimensional walls with applied textures looked impressive, due to the way graphics were rendered in this game the floors and ceilings had to remain flat and single-colored. Only the company’s next shooter – “Doom” – removed this limitation, making it possible to obtain stairs on the map.
Several development studios and their titles can be identified as milestones in the history of 3D game development, including id Software (“Wolfenstein 3D,” “Doom,” “Quake”), Epic MegaGames (“Unreal”), or 3D Realms (“Duke Nukem 3D”). The history of the development of FPS games is excellently told by the 4.5-hour film “FPS: First Person Shooter Documentary” (2023).
The co-founders of these companies, such as John Carmack at id Software and Tim Sweeney at Epic, became cult figures in the community of graphics and game programmers. At the time, they themselves were the lead programmers of their games. Today, each of them is rather a successful entrepreneur, although both remain closely connected to technology. Tim Sweeney still runs Epic, overseeing the development of the Unreal Engine and the game “Fortnite,” while John Carmack has focused on the field of artificial intelligence.
Hardware acceleration of 3D graphics also did not appear immediately. The first 3D games were rendered entirely in software — by the main CPU. A certain “missing link of evolution” was the 3dfx Voodoo card, which was an additional expansion card intended exclusively for 3D graphics and had to be installed in a PC alongside a regular graphics card. It was programmed using its own API called Glide. Only later did standard graphics cards in PCs integrate hardware support for triangle rasterization, which became the standard way of representing 3D objects and remains so to this day.
Meanwhile, games from the first-person shooter genre continue to set new paths in graphics quality, photorealism, and the use of new, advanced graphics techniques. Along with them often come high hardware requirements, which lead many players to purchase a new console, a new computer, or at least a newer graphics card in order to play a newly released title. Games that at one time set a new quality standard included, among others, “Crysis,” “Killzone 2,” “STALKER,” as well as the regularly released new games from the “Call of Duty” series.
What about OpenGL?
So far, we have devoted attention to the DirectX interface, and meanwhile, when describing graphics APIs, it is also necessary to mention its competitor: OpenGL. Many differences between them can be pointed out, as well as many similarities. Both have developed over many decades, providing access to efficient 3D graphics rendering. Both are available for use on Windows, and although most games use DirectX, many major games based on OpenGL have also been released (including games by id Software).
It is difficult to say with complete certainty that one of these APIs is objectively better than the other, provides greater capabilities, or operates with higher performance. One can of course consider many differences and nuances, but in the end these are just interfaces providing access to the same hardware capabilities of the graphics card.
The most important difference lies in who is the creator of a given API, and consequently which platforms it supports. DirectX was created and is still developed by Microsoft, and thus its native environment is Windows and the Xbox console. OpenGL is developed by the Khronos Group, which constitutes a consortium bringing together many companies interested in the development of computer graphics.
Among the members of Khronos we can find all manufacturers of graphics processors (AMD, Intel, Nvidia, Apple, as well as mobile chip vendors — Samsung, Arm, Qualcomm, Imagination), companies creating important graphics software and game platforms (e.g., Valve, Epic, Unity, Adobe), but also many companies one would not expect to find there, such as… Ikea (known for furniture production) or Continental (known for tire manufacturing). All of these companies are, for one reason or another, interested in the development of graphics standards. The list of Khronos members can be found on the Khronos Members website. How many of these companies do you recognize?
Such a situation makes OpenGL a standard supported on many platforms, whether PCs running Windows, Linux, MacOS computers, smartphones running Android, iOS, or many other, more exotic devices. OpenGL also has an extension system that allows GPU manufacturers to add new features to the API.
This means that the choice of a graphics API to use in a given program is dictated primarily by the set of platforms on which that program is to run. Applications portable across many different systems more often choose OpenGL. Meanwhile, for creators of “big” games, a rational choice is DirectX, which provides support for two out of the three platforms intended for such games: PC/Windows and Xbox. PlayStation, after all, has its own non-standard API.
Another difference, apart from the list of supported platforms, is the form of the API itself. DirectX was designed using COM technology and is therefore an object-oriented interface based on the use of classes (or more precisely — interfaces) and their methods. OpenGL, on the other hand, was defined in the C language and therefore uses only global functions, structures, constants, enums, and originally relied on a kind of “state machine,” where we always operate on the object currently set as active. Here is an example code:
glClearColor(0.0f, 0.0f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
int color_location = glGetUniformLocation(shader_program, "Color");
glUniform4f(color_location, v0, v1, v2, v3);
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 3);
glfwSwapBuffers(window);
The “rule of the strong hand” of Microsoft meant that the company was not afraid on many occasions to redefine the entire API, breaking backward compatibility and modernizing it in line with the progress of GPU hardware capabilities. Released in 2004, DirectX 9.0c was a version that dominated among game developers for many years. It was followed by the heavily redesigned DirectX 10, and then DX11 released shortly after in 2009, which once again became the foundation of most PC games for many years.
Meanwhile, OpenGL maintained backward compatibility all the way back to its earliest versions from the 1990s. The advantage of such an approach was undoubtedly the ease of developing and maintaining old code, regardless of whether it concerned game engines or other graphics applications, for example from the CAD domain. The disadvantage, however, was the complexity of the API and, consequently, the difficulty of implementing it on the graphics driver side, the associated performance overhead, as well as the large number of lurking bugs and “special cases.” For example, sometimes two functions do not cooperate with each other in the same way as on another graphics card or as defined in the specification.
We can also consider the speed at which new hardware features appearing in GPUs are made available through an API. It might seem that OpenGL and its successor — Vulkan — by virtue of their openness should develop faster. It turns out, however, that it is DirectX that more often offers new features earlier, before the competition does. This happens because Microsoft has full control over its API, and its development needs to be consulted only with GPU manufacturers used on Windows, of which there are few (AMD, Intel, Nvidia, and more recently Qualcomm). Meanwhile, a very large number of companies participate in the Khronos Group, so the development of technology is based on discussions and votes, in which not only technical aspects but also politics — the interests of individual companies — likely play a role.
Before new hardware features make their way into the API standard, they are often added as non-standard extensions offered by the manufacturer of a given GPU. In OpenGL and Vulkan, extensions are supported natively; however, graphics chip vendors have found a way to add such extensions also to DirectX, offering them in the form of their own libraries: AMD GPU Services (AGS) and NVAPI.
The openness of OpenGL
At the end of the discussion about OpenGL, it is worth taking a closer look at the “Open” part of its name. Many people claim that OpenGL is “open,” while DirectX is “closed.” This may be true in a certain sense, but it depends on which aspect we consider. As we said earlier, an API is only a definition of a programming interface. The documentation of both APIs — DX and OGL — is publicly, legally, and freely available on the Internet (which cannot be said about console documentation — Xbox, PlayStation, or Switch).
There is therefore nothing preventing a sufficiently determined entity with adequate resources from providing a new implementation of such an API. The former court dispute between Oracle and Google concerning the Java interface in the United States provided a precedent-setting ruling, according to which an API cannot be reserved in a way that would legally prevent others from providing an alternative implementation. Such implementations have in fact been created for DirectX — namely the DXVK (for DX10/11) and vkd3d-proton (for DX12) libraries, which implement DirectX functionality using Vulkan. It is thanks to them that games created for Windows run on the Steam Deck, which is, after all, based on Linux.
On the other hand, someone who has an ambitious plan to design and sell their own new GPU must pass a set of tests provided by Khronos (called CTS) in order to advertise it as supporting OpenGL or Vulkan, just as Microsoft provides tests for DirectX (called WHQL). As can be seen, saying with full certainty that “OpenGL is open and DirectX is closed” is not entirely correct. Such a statement rather means that the former is created and developed by a consortium of many companies, while the latter is developed by a single company.
The development of engines
Over the years, not only have graphics cards, their hardware capabilities, and the new functions added to APIs developed, but games themselves have also become increasingly complex. Ever more detailed and realistic graphics, animation, sound and music, artificial intelligence, gameplay mechanics, and other aspects… All of this has made — and continues to make — game development increasingly difficult and increasingly expensive.
In this heat of battle, in order to finish and release a game by the deadline set by the publisher, with as few bugs as possible, and in a form that will appeal to reviewers and players, the concept of a “game engine” emerged relatively long ago. The idea of reusing code in a new project in the form of a separated library of classes or functions is familiar to every programmer. A game engine is nothing more than such a library or framework that constitutes the foundation for building the code of a specific game.
The development of an engine as a separate component began with the reuse of code within a single company. Returning to our example of FPS games, id Software created and continues to develop an engine called id Tech, on which their games are based, while Epic develops its Unreal Engine. An interesting fact is that in 1998 Epic released an FPS shooter called “Unreal,” competing for players’ hearts with Quake. Today, we associate this name rather only with the engine.
Over time, a business model emerged that allowed some companies to license a ready-made engine from others in order to focus exclusively on creating their game instead of developing technology from scratch. Initially, it was not clear whether it was possible to create an engine supporting different game genres. People spoke of engines dedicated to FPS shooters or RTS strategy games.
However, it was possible to develop certain abstractions thanks to which today’s engines (such as Unreal Engine, Unity) are universal and prove effective among creators of games of all kinds. Such an engine provides support for the basic aspects of a game, such as rendering graphics, playing sounds, simulating physics, handling controllers, and many others.
In the graphical aspect, we can speak of moving the level of abstraction to a higher level. When using a graphics API directly, we must work with objects such as a “device,” “buffer,” “texture,” or “shader.” Meanwhile, an engine hides this complexity, offering higher-level objects from which it is easier to compose game code, such as a “scene” and the “actors” standing on it, a “camera,” “light,” or “material.” It is worth noting that these are not some new entities, but concepts known, for example, from cinema, and some of them also from theater, whose origins go back to antiquity (such as a stage, with actors on it dressed in costumes made of various materials).
Thus, the code of a modern game constitutes higher-level logic that focuses on the implementation of a specific title. It uses the functions of a given engine, and only then does the engine make use of a graphics API to render graphics. This situation is illustrated by column (3) in Figure 1. In contemporary game development, someone who programs in C++ and works on engines may be described as a “low-level” programmer, which could provoke objections from embedded systems programmers writing in C or assembly and controlling hardware directly.
Something new is coming
At a certain point, dissatisfaction with the then-current state of graphics APIs could be felt among game programmers. New games released on the consoles available at the time (Xbox One and PlayStation 4) looked fantastic. Meanwhile, PC gamers still had to buy ever newer graphics cards or lower the resolution and level of detail in the game settings in order to achieve sufficient performance. While ordinary users could blame lazy developers and poorly made console ports, programmers placed some of the blame on graphics APIs.
This was because, in the case of consoles, they provided more direct, low-level access to a given GPU. There this is of course simpler, because the hardware is always the same — there is no need to support a wide range of different models from different manufacturers, as on the PC. However, PlayStation has always offered its own low-level API, and even the DirectX interface available on Xbox was enriched with console-specific additions. Meanwhile, on the PC, the then-dominant DirectX 11 and OpenGL were increasingly diverging from how new GPUs were built and had an abstraction that forced the driver to perform a lot of additional work, resulting in greater CPU overhead and thus lower performance.
Some hopes were placed in a new version of OpenGL 3.0. Many programmers were counting on a “fresh start” and a break with backward compatibility (as DirectX had done on several occasions), in order to leave only modern features and provide efficient access to the capabilities of contemporary GPUs. This, however, did not happen. The new OGL 3.0 released in 2008 disappointed “progressive” programmers by preserving backward compatibility. The “conservative” ones could be satisfied, as they did not have to rewrite huge amounts of old graphics application code (e.g., CAD). Defining a separate core profile for modern features and a compatibility profile to ensure compatibility with old features did little to help. Drivers still had to support all of them.
In 2013, AMD released its own graphics API called Mantle. It was low-level and well adapted to the architecture of that company’s graphics cards. However, it did not gain much popularity. Although several games with support for this API were released (including “Battlefield 4” and “Ashes of the Singularity”), it was difficult to persuade developers to devote their time to implementing an API specific to only a single graphics card vendor.
A significant event was the lecture “Approaching Zero Driver Overhead in OpenGL,” delivered at the Game Developers Conference (GDC) in 2014 and prepared jointly by GPU manufacturers that normally compete with each other: AMD, Intel, and Nvidia. They showed which OpenGL functions could be used and which should be avoided so that the driver could spread its wings to achieve the highest possible performance and fully utilize the computational power of the GPU, rather than having to deal with ensuring compatibility with old functions unsuited to modern times.
DirectX 12 and Vulkan
At last, the long-awaited revolution arrived — new graphics APIs appeared, heavily redesigned and breaking with backward compatibility. The first to be released was DirectX 12 in July 2015. Microsoft retained its object-oriented nature based on COM, but redefined the entire API and introduced many new types of objects.
Later, in February 2016, Khronos released the specification of its completely new API called Vulkan. This, in turn, is based — similarly to earlier OpenGL — on C headers, and thus consists of global functions and structures. However, due to the definition of “opaque” pointers to various types of objects, it is in fact an object-oriented interface. It also supports extensions through a clever idea of attaching a chain of new types of structures via a linked list.
Both of these new APIs are characterized by more low-level access to the hardware functions of the graphics card. This is associated with many consequences, more or less pleasant. New types of objects have appeared that the programmer must manage. In addition to the previously known “device,” “texture,” or “buffer,” there are now, among others, a “command buffer,” “descriptor,” and “barrier.” In fact, these had existed in graphics cards for a long time, but previously the driver hid them behind a higher level of API abstraction. Now, when using the new APIs, the engine programmer must use them directly, which on the one hand allows for better performance, but on the other hand constitutes an additional challenge and can lead to various errors, including crashing the entire game. In other words, the engine now has to implement more logic that was previously handled by the graphics driver. This situation is illustrated by column (4) in Figure 1.
It is worth adding that the use of these new APIs does not in itself have to mean a higher FPS counter. After all, it is still about access to the same hardware functions of the GPU. Rendering the same triangles shaded by the same shader on the same computer should in theory take the same amount of time. A simple port of an engine from DX11 or OGL to a new API can even result in a loss of performance if programmers do not devote attention to proper and efficient use of that API and optimization.
What these new APIs primarily change is a “thinner,” and thus also simpler and faster, graphics driver, and consequently a smaller CPU overhead (e.g., when rendering a large number of objects). Only the additional effort put into adapting an engine to DX12 or Vulkan can result in better utilization of the graphics card’s computational power. An example may be the ability to perform some other computations in parallel with graphics rendering, which is known as asynchronous compute. Also on the CPU side, we no longer have to render everything on the main thread or designate a single “rendering thread,” but we can fill many command buffers in parallel on multiple CPU threads, thereby making better use of the ever-increasing number of cores present in modern processors.
Likewise, new functions added to GPUs in recent years have gained support only in these new APIs (DX12 and Vulkan), and not in the old ones (DX11, OGL). These include primarily ray tracing, but also mesh shaders, variable rate shading, or the latest “invention” — GPU work graphs. If we want to take advantage of these features, we are therefore forced to use the new APIs.
Platform support
The question arises which of these new APIs run on which platforms. There are no surprises here. DirectX, as created by Microsoft, received support on Windows, as well as on the Xbox One X|S and Xbox Series X|S consoles. Initially, it was available only on the newly released Windows 10. This was to some extent justified by technical considerations (a new version of the Windows Display Driver Model – WDDM). However, gamers perceived this as an attempt to force them to migrate to a new operating system. After some time, Microsoft therefore added partial support for DX12 also on Windows 7.
Vulkan, in turn, created by the Khronos Group, like OpenGL became a standard widely supported by many hardware and software platforms. It is available on Windows, Linux, and even FreeBSD or Raspberry Pi. On mobile platforms, support was not so obvious. At one point, Google planned to develop its own next-generation graphics API. Ultimately, however, it returned to the “negotiating table,” and today Android phones support Vulkan. Such a turn did not occur at Apple, as a result of which macOS as well as mobile iOS do not support Vulkan natively. Instead, the company with the apple logo developed its own low-level API called Metal, and Vulkan can be used on these platforms only indirectly — through the MoltenVK library.
It is worth emphasizing that Vulkan is, in a sense, the first graphics API that under a single interface offers access to GPUs that are both “large” (such as PCs) and “small” (such as smartphones). Even OpenGL was not such an API, as mobile systems provided a special version, OpenGL ES. In return, Vulkan had to introduce into the API certain elements needed for mobile GPUs, which operate differently than more powerful ones. These include, among others, render passes, which PC programmers may complain about as cumbersome to use and completely unnecessary.
Disappointed hopes?
Did these new APIs (DirectX 12 and Vulkan) achieve the expected success? One could say that they did. Although their adoption was slow at first, currently a large percentage of released games are implemented using them. Those that want to use ray tracing, in fact, have no other choice. The most important engines (Unreal Engine, Unity, Godot) also already offer the possibility of using these APIs. After years of presence on the market, it can be seen that in terms of popularity among games for Windows, Microsoft’s API has once again prevailed, but Vulkan also works well on this system and is used in some games.
Did these APIs fulfill the hopes placed in them? Here, one can have doubts. The low level makes such an API harder to learn and use. The nearly legendary, yet fully true, story is that it takes writing several hundred lines of code to render the first triangle. Before that, one must create and initialize many required objects and provide many of their parameters. Here is an example code performing a single barrier in Vulkan:
VkImageMemoryBarrier2 image_barrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = image,
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};
VkDependencyInfo dependency_info{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.dependencyFlags = 0,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &image_barrier
};
vkCmdPipelineBarrier2(command_buffer, &dependency_info);
At the same time, the advantages that moving the API to a lower level was supposed to bring turned out to be only partial. Creating a single large pipeline state object containing a fixed configuration of the entire graphics pipeline was supposed to eliminate the need for the driver to recompile shaders and prevent game stuttering (hitching). Instead, however, it forces the creation of a new state object for every required combination of parameters, which results either in long game loading times or, once again, stuttering during gameplay — this time caused by the game itself rather than by the driver.
DX12 and Vulkan are still not low-level enough to provide direct control over the GPU and predictable performance. There are still fast and slow “paths.” When allocating video memory, we have no control over when that memory is exhausted and what happens afterward, for example which textures the system moves to RAM, which degrades access performance. When executing a barrier, we cannot be sure which one will be lightning fast and which one will be exceptionally slow because internally it has to re-encode an entire texture. Such examples can be multiplied.
As if that were not enough, these new APIs have many stability problems. With an API this low-level, a simpler driver, and more responsibility resting on the engine, crashes can usually be blamed on the game or engine programmers. This does not change the fact that such problems occur more often and are harder to debug. What is more, it often happens that certain code works correctly on a GPU from one vendor, but displays artifacts on another GPU, and this is still most often the programmer’s fault rather than the driver of the other graphics card. It is therefore not surprising that many even the newest games still offer the option to use DirectX 11 as a choice that ensures better compatibility and stability.
What should one learn?
If you are reading this article to learn something practical and useful for your programming practice, then I hope that this and the next chapter will finally meet your expectations. Perhaps you are wondering which API to choose to learn. Nowadays, I would say perversely: preferably none.
Today, games have become so complex, and their creation so far removed from the direct use of graphics APIs or other libraries, that it is best to master one of the existing, popular engines: Unreal Engine, Unity, or Godot, and focus on creating your game. This applies to the majority of programmers interested in game development — from those who want to learn and have fun creating their amateur productions, through teams making a game in 48 hours as part of a game jam competition, independent developers and small studios that have an idea for a game and want to finish and release it as quickly as possible, to medium-sized and quite large development studios.
Programming graphics directly using a graphics API and the accompanying creation of an engine is therefore, nowadays, a daunting challenge. A few engines, such as the three mentioned above, have dominated the market, and creating a new one from scratch today seems like a project on the scale of creating a new operating system. Practically no one does that from scratch either. Even Google — a rich and powerful company — used Linux as the basis for creating Android.
Another powerful company — Amazon — at one point wanted to create its own game engine called Lumberyard. They also did not start from scratch, but based it on the existing CryEngine. Lumberyard, however, did not gain much popularity and was discontinued. At the same time, another company of Jeff Bezos — Blue Origin — successfully launched a rocket that took him into space. One could therefore say that creating engines is not rocket science — it is something even more difficult 🙂
Let us return, however, to the decision of which technology to learn. Perhaps, like the author of this article, you are more interested in graphics programming itself in its more technical aspects than in making games. In that case, we currently also have many ready-made tools that support such tech art. Unreal Engine itself allows for advanced experimentation with materials and many aspects of rendering. External tools such as Blender or Houdini can also be useful. For playing with graphics and real-time procedural generation, one can in turn use, for example, Resolume, ArKaos, Modul8, or vvvv.
Perhaps you have seen at some point what extraordinary effects can be achieved by writing shaders alone and generating the entire image procedurally. From simple gradients, through impressive fractals, to entire breathtaking 3D scenes with shapes repeating infinitely… Many people share such effects on the X platform. On the demoscene, competitions are even organized in writing such effects from memory and within a limited time. It is worth looking for, for example, video recordings from the “shader showdown” competition at the Revision demoparty. Writing such shaders can, however, be practiced using an online tool based on WebGL: ShaderToy, without the need to write even a single line of C++ code.
Which API to choose?
On the other hand, it must be admitted that game engines still exist and someone has to deal with writing them. This does not apply only to employees of Epic and Unity. It turns out that if we look at top AAA games, many of them are still based on proprietary engines developed for years by their creators. These include, for example, games from the “Horizon,” “Call of Duty,” “Assassin’s Creed,” “GTA,” “Red Dead Redemption,” “Cyberpunk 2077,” or “The Last of Us” series. One can also give examples of smaller, independent 3D games that managed to be created and released based on an in-house engine, such as the Polish production “Jupiter Hell.”
If you have made it to this point in the article and are still considering learning one of the graphics APIs, congratulations! Not everyone dreams of creating and releasing their dream game as quickly as possible. Some people — including the author of this article — are more interested in implementing these lower layers of code, achieving some interesting graphical effects, or even just the awareness that the rendering code is well written and performs efficiently. In that case, the question arises of which graphics API is worth learning.
The answer depends primarily on which platforms our code is supposed to run on. For someone associated with the Apple ecosystem, the natural choice will be Metal. For Linux enthusiasts, Vulkan will be a good choice. For people interested in creating games for PC/Windows and possibly consoles (after getting a job at some development studio that has access to the appropriate devkits and SDKs, since these are not publicly available to hobbyist programmers), I would recommend DirectX 12.
However, it is not necessary to choose between these low-level APIs, which are so difficult to master and use, and learning an existing engine. There are also libraries that offer a higher level of abstraction. Among them one can mention bgfx, WebGPU (whose name is misleading — it is not a browser-only library), and SDL3 (its part labeled GPU). We should remember, however, that these are not true graphics APIs in the sense described in this article — supported directly by the graphics driver — but merely libraries built on top of them. One can also always start learning with the old, good DX11. Learning OpenGL, on the other hand, is difficult to recommend nowadays due to the numerous drawbacks already mentioned above and the fact that it is used increasingly rarely today.
Finally, it is worth mentioning APIs that provide access to the computational power of the GPU but do not offer graphics rendering, instead performing other kinds of computations (often referred to as GPGPU — General-Purpose Computing on GPU), such as those related to machine learning. Such APIs include OpenCL and CUDA.
What will the future bring?
It is difficult to predict exactly how the development of graphics APIs, GPUs, and computer graphics as a whole will unfold. For now, no revolutionary change on the scale of the introduction of DX12 and Vulkan is visible on the horizon. Existing APIs are regularly expanded with new features, and game and engine developers are still learning how to structure their code in order to make the best possible use of them. They share their experiences at conferences such as the already mentioned Game Developers Conference held in San Francisco, as well as an online conference dedicated exclusively to engines: the Rendering Engine Architecture Conference (REAC).
Triangle rasterization has long been, and still remains, the basic way of representing and displaying 3D graphics. However, ray tracing is gaining increasing popularity. One can imagine a future in which, in a number of years, on new generations of GPUs, computational power will be sufficient for this technique to be used to generate the entire image in real time. Abandoning rasterization would simplify the graphics pipeline and make it possible to avoid the use of many “tricks” that are currently employed in graphics. Other representations of graphics are also finding certain applications, such as voxel representation, signed distance fields (SDF), or Gaussian splats.
As for the shape of the API itself, many scenarios are possible. If more people were to reach conclusions similar to those presented above regarding the current low-level APIs, a return to higher-level APIs would be possible. Perhaps it would be possible to find better abstractions that would provide access to modern features (such as ray tracing or the use of multithreading), while at the same time ensuring stability (few crashes) and correctness (avoiding errors regardless of the GPU used) as the default, and maximum performance with low CPU overhead would be achievable after putting in appropriate effort — contrary to how it is now.
One can also imagine the opposite scenario, in which graphics APIs become even more low-level, or perhaps even specific to a given GPU manufacturer. While this would once have been difficult to accept (as shown by the failure of Mantle), now, when direct use of APIs is handled only by a small group of graphics programmers developing game engines, it no longer seems unrealistic to persuade them to use a separate API for GPUs from AMD, Intel, Nvidia, and so on. All of this, however, is merely the author’s speculation, not supported by any information indicating that development would actually head in this direction.
Adam Sawicki July 2025
Disclaimer: This content was first presented as a lecture at the Game Developers’ Scientific Circle “Polygon” at the Warsaw University of Technology in March 2024, and later, in July 2025, published in the form of an article in the magazine “Programista”. I am aware that many pieces of information have been omitted or simplified here in order to present an interesting and coherent story. The intended audience is programmers not necessarily familiar with the details of graphics programming. If you are a programmer advanced in rendering or game programming, you should rather read the article No Graphics API by Sebastian Aaltonen. Nevertheless, I invite you to submit your comments below.
Comments
Copyright © 2004-2026