Day 11 (Jan 1, 2021) ~~~ Finally Drawing a Triangle (with lots of synchronization necessary)! - kwilson33/learning-vulkan GitHub Wiki

Happy New Year!! Unfortunately I've been sidetracked recently, but I'm glad that today I was finally able to fit some time in to continue learning Vulkan. What better way to start the New Year than finishing the first major leg of my Vulkan learning journey.

happy new year!

Progress Today

Finished:

Here are some major takeaways

1. Since I got kinda cut-off in the middle of the last day, I want to re-iterate what I was doing when I left off, which was defining a subpass dependency.

  • The purpose of subpass dependencies is to control the image layout transitions during the render pass, that are taken care of by the subpasses.
    • Render pass --> subpasses --> subpass dependencies
  • The reason we have to specify a subpass dependency is because one of the two built-in transitions for the render pass assumes the picture is ready at the beginning of the pipeline, when it really isn't. Using a subpass dependency we can tell the render pass to wait until the image is ready.

2. I was running into an issue because I didn't retrieve the queue handle correctly for the presentation queue.

  • In createLogicalDevice() I was doing this:
vkGetDeviceQueue(device, indices.presentationFamily.value(), 0, &graphicsQueue);

instead of this:

vkGetDeviceQueue(device, indices.presentationFamily.value(), 0, &presentationQueue);

3. The stages that the current frame have progressed through are idle and could be used for the next frame (like pipelining instructions in a CPU). You can extend your allow multiple frames in-flight.

  • The app is submitting work very fast to drawFrame() but doesn't check if any of it finishes.
    • If the CPU is submitting work faster than the GPU can keep up with, then the queue will fill up with work. We can fix this by allowing the GPU to process multiple frames at once.
  • To do this, you have to define how many frames can be processed in parallel and give a set of semaphores to each frame.
  • And, when you're drawing a frame using drawFrame() you have to make sure you advance to the next frame, because the way it was first written was to do one frame (with a set of semaphores) at a time, but you have to keep track of which frame is being drawn in drawFrame() so the frame uses the right semaphores.

4. However, all of that above isn't enough for CPU-GPU synchronization. To do this, use fences. Similar to semaphores except we actually wait for them in our own code.

  • Without implementing fences, we still don't actually prevent more than the max # of frames from being submitted. Before implementing fences, there is only GPU-GPU synchronization but no CPU-GPU synchronization to keep track of how work is going.
    • Might be using the frame A objects while frame A is still in-flight.

5. There is a lot of synchronization needed to make sure the CPU sends the correct amount of work to the GPU, and also so the GPU doesn't start writing to a swap chain image already in-use.

  • To take care of the CPU-GPU sync, use fences like explained above.
  • Also, to make sure the same swap chain image isn't being rendered to multiple times at the same time, use fences.
  • Fences are a little easier to comprehend to me, because you can actually see exactly where in the code it is, whereas for semaphores where the barrier is is a little more abstract.

See ya!

Woooooo! Finally got a triangle to pop-up on my screen. There is still SO much to do, for example right now the vertices to draw are hard-coded and there's no vertex buffer right now. But, still even getting a simple triangle to show up took A LOT of work. According to the tutorial, up to this point was ~900 lines of code, but I wrote around ~1400 lines because of all my comments in the code. Keeping track of my work like this has been unbelievably helpful in keeping me motivated to keep going and to help my remember. So many concepts to understand just to do a simple program, but it paid off. I'm glad I kept track of my work, because there is just so much that it's very easy to forget. For example, doing validation layers took so long, and I've already started to forget how to do them. And getting command queues to work also took awhile. But, I don't need to understand everything in depth, just as long as I keep my notes and understand the basics. If I hadn't kept my progress logged somehow I doubt I would have made it past day 2. I'm excited for what the rest of the tutorial teaches :)!

Available validation layers:
~~~~~~~~~~~~~~~~~~~~~~~~
        VK_LAYER_VALVE_steam_overlay
        VK_LAYER_VALVE_steam_fossilize
        VK_LAYER_LUNARG_api_dump
        VK_LAYER_LUNARG_device_simulation
        VK_LAYER_LUNARG_gfxreconstruct
        VK_LAYER_KHRONOS_validation
        VK_LAYER_LUNARG_monitor
        VK_LAYER_LUNARG_screenshot
        VK_LAYER_LUNARG_standard_validation
VK_LAYER_KHRONOS_validation found!
Validation layer requirements fulfilled!

Available Vulkan extensions:
~~~~~~~~~~~~~~~~~~~~~~~~
        VK_KHR_surface
        VK_KHR_win32_surface
        VK_KHR_external_memory_capabilities
        VK_KHR_external_semaphore_capabilities
        VK_KHR_external_fence_capabilities
        VK_KHR_get_physical_device_properties2
        VK_KHR_get_surface_capabilities2
        VK_KHR_device_group_creation
        VK_EXT_swapchain_colorspace
        VK_EXT_debug_report
        VK_EXT_debug_utils

Required GLFW extensions:
~~~~~~~~~~~~~~~~~~~~~~~~
        VK_KHR_surface
        VK_KHR_win32_surface
VK_KHR_surface extension found!
VK_KHR_win32_surface extension found!

Extension requirements fulfilled!

{########## Vulkan instance created. ##########}

{########## Debug messenger setup. ##########}

{########## VkSurfaceKHR object created. ##########}

Physical device set to: Intel(R) UHD Graphics 620
Physical device extension requirements met!

{########## Physical device picked. ##########}

{########## Logical device created. ##########}

Surface Format: 50, Color Space: 0
Presentation Mode: 2
Swap Extent Width: 800, Swap Extent Height: 600

Number of swap chain images: 3

{########## Swap chain created. ##########}

{########## Image views created. ##########}

{########## Render pass created. ##########}

shaders/vert.spv size is 1540 bytes.

shaders/frag.spv size is 608 bytes.

Vertex shader module created.
Fragment shader module created.
Vertex input format specified.
Input assembly specified.
Viewport and scissor rectangle specified.
Rasterizer specified.
Multisampling specified (disabled for now).
Depth & stencil tests specified (disabled for now).
Color blend attachment state specified.
Color blend global settings specified.
Dynamic states specified (disabled for now).
Pipeline layout created.

{########## Graphics pipeline created. ##########}

{########## Framebuffers created. ##########}

{########## Command pool created. ##########}

{########## Command buffers created. ##########}

{########## Semaphores and fences created. ##########}