Blog

# Differences in memory management between Direct3D 12 and Vulkan

Fri
26
Jul 2019

Since July 2017 I develop Vulkan Memory Allocator (VMA) – a C++ library that helps with memory management in games and other applications using Vulkan. But because I deal with both Vulkan and DirectX 12 in my everyday work, I think it’s a good idea to compare them.

This is an article about a very specific topic. It may be useful to you if you are a programmer working with both graphics APIs – Direct3D 12 and Vulkan. These two APIs offer a similar set of features and performance. Both are the new generation, explicit, low-level interfaces to the modern graphics hardware (GPUs), so we could compare them back-to-back to show similarities and differences, e.g. in naming things. For example, ID3D12CommandQueue::ExecuteCommandLists function has Vulkan equivalent in form of vkQueueSubmit function. However, this article focuses on just one aspect – memory management, which means the rules and limitation of GPU memory allocation and the creation of resources – images (textures, render targets, depth-stencil surfaces etc.) and buffers (vertex buffers, index buffers, constant/uniform buffers etc.) Chapters below describe pretty much all the aspects of memory management that differ between the two APIs.

Read full article »

Comments | #vulkan #directx #gpu Share

# Slavic Game Jam 2019 and our project

Thu
25
Jul 2019

Over the last weekend I took part in Slavic Game Jam 2019 in Warsaw, Poland. (See website, Facebook event, games at itch.io). It was a big one - over 200 participants, many of them coming from different countries all around Europe. The event started on Thursday with a session of talks in 2 parallel tracks. In the evening there was a pre-party in VooDoo club, with electronic music played from GameBoys and live visuals. The jam started on Friday with the announcement of the theme which was "growth". As always, this was just an inspiration, so participants were free to make any kinds of games.

During the event there was food provided, as well as fruits and vegetables, coffee, and ice cream - all for free, included in the ticket price. Also during the event there was "HydePark" organized in a separate room - something like a small Slot Art Festival where people could reserve time slots to organize their events of any kind - like a talk, a workshop, playing video games, or playing some instruments. It made me wonder if people could come to SGJ, not make any game and still enjoy themselves all the time!

The official communication between the organizers and participants happened on a designated Discord server. Organizers kept us informed about everything what's important by posting announcements to @everyone. And there was a lot happening. For example, they asked us to deliver an exactly 3-second video from our games, from which they later assembled this showreel. They were also making quality photos and posting them during the jam on the Facebook event.

The deadline for games was on Sunday midday. What's interesting is that SGJ was non competitive this time. There were no presentations of the games on stage, no voting or judging by any jury, no winners or prizes. Instead of that, everyone needed to prepare their game to be played by others at their desk. I liked that. I think it might even feel somewhat like preparing a booth at some game expo if taken seriously. Finally, as every good party has a before- and after-party, so in the evening we went to a bar :)

To summarize, I think that in some way it's quite easy to organize a (normal) game jam. You need not invite speakers like for a conference. You need not provide any hardware, as people will bring their own laptops. All you need to do is to have some venue booked for a weekend, and some marketing to invite people to come. Possibly that's why there are so many of such events. My friend once said that taking part in game jams can become a lifestyle - you can go to one almost every week. But SGJ was different. There was so much happening and it was so well organized that I'm sure it required enormous work from everyone involved. Congratulations to the entire crew, KNTG Polygon group from Warsaw University of Technology, volunteers and others!

Regarding the games created during the jam, I could see most of them were developed using Unity. Other technologies were used as well. There were few mobile games, few board games... I couldn't see many VR games. I was developing a game in a team of two, together with my friend Thomas Pendragon - just two programmers. We were planning to use Unreal but we eventually used Unity. We ended up making this game: see entry at itch.io (including binary download for Windows and MacOS).

In our game, you need to "grow" your city by creating a balanced number of places of 5 types (red for building, green for park, blue for water, yellow for airport, gray for road). The city visualization on the left is just eye-candy. You play a tile-matching game like Candy Crush Saga, but with one twist. In the bottom-center there is a Tetris-like indicator that goes up every time you make a match of some color. When all colors are matched, the bottom row is cleared - like in Tetris. If any color goes all the way to the top, you lose, so you need to consider which colors do you match to keep a good balance. That makes the game more strategic. Points are calculated for every match - more if you match 4 or 5 in a row or if something else is matched in the same move. How many points can you reach? The record during the jam was above 1000.

Thomas gave initial idea and designed the game. He did some coding (like the city building on the left), composed the music, added sound effects, made some graphics in Blender, and assembled the rest from some assets. I coded the core logic of the matching game, the whole UI, and juicing, like particle effects and animations.

As a post-mortem of this little project, here is the list of what went right:

  • Using Unity was a good choice because it let us develop game logic quickly using C#. Even if we aren't experts in this engine, we could easily find a solution to any problem as a question already asked and answered somewhere on the Internet, because the engine is so popular.
  • Setting up development environment went quickly and smoothly. We used private Discord channel for messaging, Trello for tasks, Git for version control.
  • Thomas developed on Apple laptop, I developed on Windows PC and still there were almost no problems with compatibility (except line endings in text files, which needed some reconfiguration in Git client).
  • Co-development went smoothly, with few hard to resolve conflicts. I like the fact that Unity scenes are now text files in YAML format, which makes it possible to merge.
  • For the first time I brought Intel NUC (a tiny form factor PC), a FullHD monitor, keyboard, and mouse to a jam and used that for development instead of my old, crappy laptop. Not only it has more powerful components, but the development was much more convenient.
  • We implemented all we planned (except a large task of a separate screen with a high score table), despite we joined the jam Friday evening after work and not at the very beginning, and we went home to sleep every night. There was even no hurry at the end. That means the scope of the design was not too big.
  • There were no critical bugs that would break the game at the time when others came to play it.
  • The game can be lost and the points you earned are displayed in "Game Over" screen together with an information whether it's a new high score. Also the game is not endless as it gets harder over time. I'm especially happy with the "algorithm" for increasing difficulty level I came up with - it's just one color appearing less frequently than the others :)

What went wrong?

  • There was no graphics artist in the team. If there was one, the game would look better, and we would have more time for coding.
  • We didn't do any play testing until the very end. We should have invited other people to play during development, because they could help us find bugs and suggest new things that we didn't come up with.

Comments | #productions #competitions #unity #events Share

# Make Your Game Friendly for Graphics Debugging and Optimization - My Talk from Digital Dragons 2019

Fri
07
Jun 2019

I've recently gave a talk at Digital Dragons conference in Kraków, Poland. It wasn't very technically advanced this time. Most of it should be understandable to everyone working on games - artists, programmers, producers... I've published slides on GPUOpen.com blog, along with slides my colleagues presented last month at other events across Europe. See: AMD at Digital Dragons and Vulkanised Conference.

Comments | #productions #graphics #games #teaching Share

# Remote programming job is usually not an option

Fri
10
May 2019

Every programmer these days receives lots of job offers from recruiters, especially if having profile on LinkedIn. Some people make fun of it. I used to ignore or reject them, telling that “sorry, I’m not looking for a new job at the moment, I’m happy with my current one”. For some time I started to do something different - I tell them that I’m not interested in relocation to California/​London/​Germany/​Iceland/​South Korea/​wherever and ask if I can work remotely. The answer is usually “no”.

This is contrary to a popular belief that programmers can often work from home. I have a remote job now, but this one is unique and I know such job is hard to find. Maybe it’s more frequent when someone develops web pages, mobile apps, or other small programs that a single person can make. A freelancer hunting for specific projects and tasks may have an opportunity to work from anywhere in the world. But if you want to work in a team of many programmers developing a large and complex project, they usually expect you to be full time on site.

You can come from any place in the world and have a great coding talent. You can study solid computer science at your local university or even learn by yourself from the Internet. But it’s unlikely you can make a world-class career or take part in state-of-the-art, innovative projects while staying in your small home town. For that you have to move to one of these technology hubs, like Silicon Valley. This is despite the developments in telecoferencing, Skype, Slack, etc.

Here are screenshots of 12 chats I had with recruiters on LinkedIn since the beginning of this year. I told them I don’t want to relocate and asked them if I could work remotely. 11 of them said “no”. Only 1 said it’s possible.

Comments | #career Share

# Vulkan: Long way to access data

Thu
18
Apr 2019

If you want to access some GPU memory in a shader, there are multiple levels of indirection that you need to go through. Understanding them is an important part of learning Vulkan API. Here is an explanation of this whole path.

Let’s take texture sampling as an example. We will start from shader code and go from there back to GPU memory where pixels of the texture are stored. If you write your shaders in GLSL language, you can use texture function to do sampling. You need to provide name of a sampler, as well as texture coordinates.

vec4 sampledColor = texture(sampler1, texCoords);

Earlier in the shader, the sampler needs to be defined. Together with this definition you need to provide index of a slot where this sampler and texture will be bound when the shader executes. Binding resources to slots under different numbers is a concept that exists in various graphics APIs for some time already. In Vulkan there are actually two numbers: index of a descriptor set and index of specific binding in that set. Sampler definition in GLSL may look like this:

layout(set=0, binding=1) uniform sampler2D sampler1;

What you bind to this slot is not the texture itself, but so-called descriptor. Descriptors are grouped into descriptor sets – objects of type VkDescriptorSet. They are allocated out of VkDescriptorPool (which we ignore here for simplicity) and they must comply with some VkDescriptorSetLayout. When defining layout of a descriptor set, you may specify that binding number 1 will contain combined image sampler. (This is just an example way of doing this. There are other possibilities, like descriptors of type: sampled image, sampler, storage image etc.)

VkDescriptorSetLayoutBinding binding1 = {};
binding1.binding = 1;
binding1.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
binding1.descriptorCount = 1;
binding1.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;

VkDescriptorSetLayoutCreateInfo layoutInfo = {
    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
layoutInfo.bindingCount = 1;
layoutInfo.pBindings = &binding1;

VkDescriptorSetLayout descriptorSetLayout1;
vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &descriptorSetLayout1);

VkDescriptorSetAllocateInfo setAllocInfo = {
    VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
setAllocInfo.descriptorPool = descriptorPool1; // You need to have that already.
setAllocInfo.descriptorSetCount = 1;
setAllocInfo.pSetLayouts = &descriptorSetLayout1;

VkDescriptorSet descriptorSet1;
vkAllocateDescriptorSets(device, &setAllocInfo, &descriptorSet1);

When you have descriptor set layout created, as well as descriptor set based on it allocated, you need to bind the descriptor set as current one under set index 0 in the command buffer that you fill before you can issue a draw call that will use our shader. Function vkCmdBindDescriptorSets is defined for this purpose:

vkCmdBindDescriptorSets(
    commandBuffer1,
    VK_PIPELINE_BIND_POINT_GRAPHICS,
    descriptorSetLayout1,
    0, // firstSet
    1, // descriptorSetCount
    &descriptorSet1,
    0, // dynamicOffsetCount
    nullptr); // pDynamicOffsets

How do you setup the descriptor to point to a specific texture? There are multiple ways to do that. The most basic one is to use vkUpdateDescriptorSets function:

VkDescriptorImageInfo imageInfo = {};
imageInfo.sampler = sampler1;
imageInfo.imageView = imageView1;
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

VkWriteDescriptorSet descriptorWrite = {
    VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET };
descriptorWrite.dstSet = descriptorSet1;
descriptorWrite.dstBinding = 1;
descriptorWrite.descriptorCount = 1;
descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptorWrite.pImageInfo = &imageInfo;

vkUpdateDescriptorSets(
    device,
    1, // descriptorWriteCount
    &descriptorWrite, // pDescriptorWrites
    0, // descriptorCopyCount
    nullptr); // pDescriptorCopies

Please note that this function doesn’t record a command to any command buffer. Descriptor update happens immediately. That’s why you need to do it before you submit your command buffer for execution on GPU and you need to keep this descriptor set alive and unchanged until the command buffer finishes execution.

There are other ways to update a descriptor set. You can e.g. use last two parameters of vkUpdateDescriptorSets function to copy descriptors (which is not recommended for performance reasons), as well as to use some extensions, e.g.: VK_KHR_push_descriptor, VK_KHR_descriptor_update_template.

What we write as value of the descriptor is reference to objects: imageView1 and sampler1. Let’s ignore the sampler and just focus on imageView1. This is an object of type VkImageView. Like in Direct3D 11, an image view is a simple object that encapsulates reference to an image along with a set of additional parameters that let you “view” the image in a certain way, e.g. limit access to a range of mipmap levels, array layers, or reinterpret it as different format.

VkImageViewCreateInfo viewInfo = {
    VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
viewInfo.image = image1;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;

VkImageView imageView1;
vkCreateImageView(device, &viewInfo, nullptr, &imageView1);

As you can see, image view object holds reference to image1. This is an object of type VkImage that represents actual resource, commonly called “texture” in other APIs. It is created from a rich set of parameters, like width, height, pixel format, number of mipmap levels etc.

VkImageCreateInfo imageInfo = {
    VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = 1024;
imageInfo.extent.height = 1024;
imageInfo.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
iamgeInfo.usage =
    VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;

VkImage image1;
vkCreateImage(device, &imageInfo, nullptr, &image1);

It’s not all yet. Unlike previous generation graphics APIs (Direct3D 9 or 11, OpenGL), image or buffer object doesn’t automatically allocate backing memory for its data. You need to do it on your own. What you actually need to do is to first query the image for memory requirements (required size and alignment), then allocate memory block for it and finally bind those two together. Only then the image is usable as a means of accessing the memory, interpreted as colorful pixels of a 2D picture.

VkMemoryRequirements memReq;
vkGetImageMemoryRequirements(device, image1, &memReq);

VkMemoryAllocateInfo allocInfo = {
    VK_STRUTURE_TYPE_MEMORY_ALLOCATE_INFO };
allocInfo.allocationSize = memReq.size;
allocInfo.memoryTypeIndex = 0; // You need to find appropriate index!

VkDeviceMemory memory1;
vkAllocateMemory(device, &allocInfo, nullptr, &memory1);

vkBindImageMemory(
    device,
    image1,
    memory1,
    0); // memoryOffset

In production quality code you should of course check for error codes, e.g. when your allocation fails because you run out of memory. You should also avoid allocating separate memory blocks for each of your images and buffers. It is necessary to allocate bigger memory blocks and manage them manually, assigning parts of them to your resources. You can use last parameter of the binding function to provide offset in bytes from the start of a memory block. You can also simplify this part by using existing library: Vulkan Memory Allocator.

Comments | #graphics #vulkan Share

# WinFontRender - my new library

Thu
14
Mar 2019

Displaying text is a common problem in graphics applications where all you can do is to render textured quads. I've implemented my solution already back in 2007, as part of my old engine The Final Quest 7, which was my master thesis. I've recently come back to this code and improved it because I needed it for the personal project I now work on. Then I thought: Maybe it's a good idea to extract this code into a library? So here it is:

» WinFontRender - small single-header C++ library that renders Windows fonts in graphics applications

It does two things:

1. It renders characters of the font to a texture, tightly packed.

2. It calculates vertices needed to render given text.

Here are more details about the library:

  • License based on MIT, so it's free to use even in a proprietary, closed-source software.
  • Standalone, single file, easy to integrate with your project.
  • Written in C++. Depends only on STL and WinAPI.
  • Agnostic to graphics API. Returns data for your texture and vertices. It is your task to display them using Direct3D, OpenGL, Vulkan or whatever you prefer. Sample application is provided for Direct3D 11.
  • Works on Windows only. Tested using Visual Studio 2017, 64-bit configuration.
  • Renders fonts installed in the system. Doesn't load custom textures or fonts from files.
  • Works with Unicode strings const wchar_t*/std::wstring.
  • Supports multiline text and automatic line break, also on whole word boundary.
  • Supports horizontal and vertical alignment to left/center/right and top/middle/bottom.
  • Supports flexible vertex formats. You can specify separate position and texture coordinate streams and custom vertex strides.
  • Supports various primitive topologies. You can request triangle list, triangle strip with primitive restart index, or triangle strip with degenerate triangles, with or without index buffer.
  • Supports kerning.
  • Fonts are antialiased and can be rendered pixel-perfect.
  • Underline and strikeout can be added.
  • Hit testing functions are available that find the character at a given point.

Comments | #graphics #libraries #productions Share

# Vulkan Memory Allocator Survey March 2019

Mon
04
Mar 2019

Are you a software developer, use Vulkan and the Vulkan Memory Allocator library (or at least considered using it)? If so, please spend a few minutes and help to shape the future of the library by participating in the survey:

» Vulkan Memory Allocator Survey March 2019

Your feedback is greatly appreciated. The survey is anonymous - no personal data is collected like name, e-mail etc. All questions are optional.

Comments | #productions #libraries #vulkan Share

# Programming FreeSync 2 support in Direct3D

Sat
02
Mar 2019

AMD just showed Oasis demo, presenting usage of its FreeSync 2 HDR technology. If you wonder how could you implement same features in your Windows DirectX program or game (it doesn’t matter if you use D3D11 or D3D12), here is an article for you.

But first, a disclaimer: Although I already put it on my “About” page, I’d like to stress that this is my personal blog, so all opinions presented here are my own and do not reflect that of my employer.

Radeon FreeSync (its new, official web page is here: Radeon™ FreeSync™ Technology | FreeSync™ 2 HDR Games) is an AMD technology that covers two different things, which may cause some confusion. First is variable refresh rate, second is HDR. Both of them need to be supported by a monitor. The database of FreeSync compatible monitors and their parameters is: Freesync Monitors.

Read full entry > | Comments | #gpu #directx #windows #graphics Share

Older entries >

Twitter

Pinboard Bookmarks

LinkedIn

Blog Tags

STAT NO AD
[Stat] [STAT NO AD] [Download] [Dropbox] [pub] [Mirror] [Privacy policy]
Copyright © 2004-2019