|
|
Subscribe / Log in / New account

The Linux graphics stack in a nutshell, part 1

The Linux graphics stack in a nutshell, part 1

Posted Dec 20, 2023 4:01 UTC (Wed) by tshow (subscriber, #6411)
In reply to: The Linux graphics stack in a nutshell, part 1 by adobriyan
Parent article: The Linux graphics stack in a nutshell, part 1

> For those who are going to try Vulkan: it takes like 1000 LOC in C to hello world a triangle from scratch. Add few hundred LOC more for a textured triangle.

Vulkan is very verbose, but a lot of this can be mitigated with some macro magic if you're using relatively recent C (and if you're building for Vulkan, why wouldn't you be?). There's a bunch of pure boilerplate you can macro away.

In particular, a lot of the functions take pointers to structures as arguments. This means you wind up doing a lot of:

vkCreateFoo(&(VkFooCreateInfo)
    {
      .sType  = VK_STRUCTURE_TYPE_FOO,
      .thing  = VK_TRUE,
      .answer = 42
    });

In my game engine I've got #defines of the form:

#define STVK(_name, _type) &(Vk##_name) { .sType = VK_STRUCTURE_TYPE_##_type
#define ARG_VkFooCreateInfo(...) STVK(FooCreateInfo, FOO_CREATE_INFO), __VA_ARGS__ }

The above could be done without the STVK() macro by expanding it inline in the ARG_ macro, but unless you're going to autogenerate the macros (say, with a ruby script that greps vulkan_core.h for VK_STRUCTURE_TYPE...) it's less prolix to have the helper macro.

With that, the call becomes:

vkCreateFoo(ARG_VkFooCreateInfo(.thing = VK_TRUE, .answer = 42));

or if you like maximal vertical spread:

vkCreateFoo(
  ARG_VkFooCreateInfo(
    .thing  = VK_TRUE,
    .answer = 42
  )
);

I'm simplifying a bit (most vulkan calls want an allocator pointer, for instance, though you can probably macro that away too unless you plan to use different allocators in different places), but the macro scheme above gets rid of a lot of the boilerplate and makes the code a lot easier to read, IMO. Particularly when you get to things like vkCreateGraphicsPipeline() which takes very large structures.

There's a lot of other places you can do that as well. For example, are you planning to have custom names for your shader entry points? If not, you can macro .pName = "main" into all your VkPipelineShaderStageCreateInfo structures.

You can also clean things up a lot by remembering that when you init a structure inline in C, all members not explicitly mentioned are zeroed, so if you have any lines like .flags = 0, (hint: you do; there are a lot of flag fields with no flags defined yet in Vulkan...) you can get rid of them unless you think it's needed for clarity.

Being low level, Vulkan gives you a lot of flexibility you'll probably never use. Nailing down the bits you won't care about in macros behind the scenes makes it much simpler to work with, though I obviously advise heavily documenting your macros wherever they live in case your assumptions change.


to post comments

The Linux graphics stack in a nutshell, part 1

Posted Dec 20, 2023 10:25 UTC (Wed) by adobriyan (subscriber, #30858) [Link] (6 responses)

I ended up not doing macrology. With IDE autocompletion verbosity it slightly less of a problem.

It is just that "ha-ha-ha you forgot to set VkPipelineColorBlendAttachmentState::blendEnable -- enjoy black screen."

What about rasterizerDiscardEnable?

What about "get physical device which this X instance is running at?".

The Linux graphics stack in a nutshell, part 1

Posted Dec 20, 2023 10:35 UTC (Wed) by zdzichu (subscriber, #17118) [Link] (5 responses)

Article is about modern graphics stack. X is not a part of it.

The Linux graphics stack in a nutshell, part 1

Posted Dec 20, 2023 10:47 UTC (Wed) by adobriyan (subscriber, #30858) [Link] (4 responses)

Vulkan initialisation requires enumerating physical devices, picking one, and then creating logical device to issue commands.

Now imagine there are 2 GPUs in the system, one is connected to monitor with X running, and the other one is not. There is fake Mesa llvmpipe device lying around too.

The Linux graphics stack in a nutshell, part 1

Posted Dec 20, 2023 14:42 UTC (Wed) by makendo (guest, #168314) [Link] (3 responses)

> There is fake Mesa llvmpipe device lying around too.

Lavapipe should identify itself as a software renderer (VK_PHYSICAL_DEVICE_TYPE_CPU), which can help a lot in kicking it away.

The Linux graphics stack in a nutshell, part 1

Posted Dec 23, 2023 13:39 UTC (Sat) by adobriyan (subscriber, #30858) [Link] (2 responses)

The answer seems to be:

Enumerate queue family properties, at least one of them will be able to present to the window surface
(vkGetPhysicalDeviceSurfaceSupportKHR will report Supported == VK_TRUE).

All other GPUs driving or not driving other X servers will not be able to Present to the surface.

Which is good enough.

qfp 0 G 1, P 0 <= no cookie
qfp 1 G 0, P 0
qfp 2 G 0, P 0
qfp 3 G 0, P 0

qfp 0 G 1, P 1 <= GPU which shows the window
qfp 1 G 0, P 0
qfp 2 G 0, P 1
qfp 3 G 0, P 0

I must say, this is simple and not simple at the same time. Every single tutorial assumes 1 GPU.

The Linux graphics stack in a nutshell, part 1

Posted Dec 23, 2023 13:52 UTC (Sat) by adobriyan (subscriber, #30858) [Link] (1 responses)

OK, this is funny. lavapipe is here, ready to help. Filtering by GPU type is required apparently:

# 1070 (active)
qfp 0 G 1, P 1 ***
qfp 1 G 0, P 0
qfp 2 G 0, P 1
qfp 3 G 0, P 0

# 1030 (passive)
qfp 0 G 1, P 0
qfp 1 G 0, P 0
qfp 2 G 0, P 0
qfp 3 G 0, P 0

(lavapipe)
qfp 0 G 1, P 1 *** # P=1 !!!
-p 0 NVIDIA GeForce GTX 1070
-p 1 NVIDIA GeForce GT 1030
-p 2 llvmpipe (LLVM 16.0.6, 256 bits)

Enumeration order varies by distro too!

The Linux graphics stack in a nutshell, part 1

Posted Feb 2, 2024 8:34 UTC (Fri) by daenzer (subscriber, #7050) [Link]

> Enumeration order varies by distro too!

The enumeration order isn't well-defined, and in practice partially depends on the order in which the filesystem enumerates the corresponding *_icd.*.json files (which isn't well-defined either). So the order may differ between otherwise mostly identical systems with the same set of VkPhysicalDevices, and may change after modifications to the *_icd.*.json files.

Unfortunately, many (most?) Vulkan applications just use the first enumerated device by default. To prevent such apps from accidentally using an undesirable device such as a lavapipe one, Mesa ships a device selection layer which moves the "default" device to be enumerated first.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds