• Advertisement
  • Popular Tags

  • Popular Now

  • Advertisement
  • Similar Content

    • By khawk
      LunarG has released new Vulkan SDKs for Windows, Linux, and macOS based on the 1.1.73 header. The new SDK includes:
      New extensions: VK_ANDROID_external_memory_android_hardware_buffer VK_EXT_descriptor_indexing VK_AMD_shader_core_properties VK_NV_shader_subgroup_partitioned Many bug fixes, increased validation coverage and accuracy improvements, and feature additions Developers can download the SDK from LunarXchange at https://vulkan.lunarg.com/sdk/home.

      View full story
    • By khawk
      LunarG has released new Vulkan SDKs for Windows, Linux, and macOS based on the 1.1.73 header. The new SDK includes:
      New extensions: VK_ANDROID_external_memory_android_hardware_buffer VK_EXT_descriptor_indexing VK_AMD_shader_core_properties VK_NV_shader_subgroup_partitioned Many bug fixes, increased validation coverage and accuracy improvements, and feature additions Developers can download the SDK from LunarXchange at https://vulkan.lunarg.com/sdk/home.
    • By mark_braga
      I have a pretty good experience with multi gpu programming in D3D12. Now looking at Vulkan, although there are a few similarities, I cannot wrap my head around a few things due to the extremely sparse documentation (typical Khronos...)
      In D3D12 -> You create a resource on GPU0 that is visible to GPU1 by setting the VisibleNodeMask to (00000011 where last two bits set means its visible to GPU0 and GPU1)
      In Vulkan - I can see there is the VkBindImageMemoryDeviceGroupInfoKHR struct which you add to the pNext chain of VkBindImageMemoryInfoKHR and then call vkBindImageMemory2KHR. You also set the device indices which I assume is the same as the VisibleNodeMask except instead of a mask it is an array of indices. Till now it's fine.
      Let's look at a typical SFR scenario:  Render left eye using GPU0 and right eye using GPU1
      You have two textures. pTextureLeft is exclusive to GPU0 and pTextureRight is created on GPU1 but is visible to GPU0 so it can be sampled from GPU0 when we want to draw it to the swapchain. This is in the D3D12 world. How do I map this in Vulkan? Do I just set the device indices for pTextureRight as { 0, 1 }
      Now comes the command buffer submission part that is even more confusing.
      There is the struct VkDeviceGroupCommandBufferBeginInfoKHR. It accepts a device mask which I understand is similar to creating a command list with a certain NodeMask in D3D12.
      So for GPU1 -> Since I am only rendering to the pTextureRight, I need to set the device mask as 2? (00000010)
      For GPU0 -> Since I only render to pTextureLeft and finally sample pTextureLeft and pTextureRight to render to the swap chain, I need to set the device mask as 1? (00000001)
      The same applies to VkDeviceGroupSubmitInfoKHR?
      Now the fun part is it does not work  . Both command buffers render to the textures correctly. I verified this by reading back the textures and storing as png. The left texture is sampled correctly in the final composite pass. But I get a black in the area where the right texture should appear. Is there something that I am missing in this? Here is a code snippet too
      void Init() { RenderTargetInfo info = {}; info.pDeviceIndices = { 0, 0 }; CreateRenderTarget(&info, &pTextureLeft); // Need to share this on both GPUs info.pDeviceIndices = { 0, 1 }; CreateRenderTarget(&info, &pTextureRight); } void DrawEye(CommandBuffer* pCmd, uint32_t eye) { // Do the draw // Begin with device mask depending on eye pCmd->Open((1 << eye)); // If eye is 0, we need to do some extra work to composite pTextureRight and pTextureLeft if (eye == 0) { DrawTexture(0, 0, width * 0.5, height, pTextureLeft); DrawTexture(width * 0.5, 0, width * 0.5, height, pTextureRight); } // Submit to the correct GPU pQueue->Submit(pCmd, (1 << eye)); } void Draw() { DrawEye(pRightCmd, 1); DrawEye(pLeftCmd, 0); }  
    • By turanszkij
      Hi,
      I finally managed to get the DX11 emulating Vulkan device working but everything is flipped vertically now because Vulkan has a different clipping space. What are the best practices out there to keep these implementation consistent? I tried using a vertically flipped viewport, and while it works on Nvidia 1050, the Vulkan debug layer is throwing error messages that this is not supported in the spec so it might not work on others. There is also the possibility to flip the clip scpace position Y coordinate before writing out with vertex shader, but that requires changing and recompiling every shader. I could also bake it into the camera projection matrices, though I want to avoid that because then I need to track down for the whole engine where I upload matrices... Any chance of an easy extension or something? If not, I will probably go with changing the vertex shaders.
    • By Alexa Savchenko
      I publishing for manufacturing our ray tracing engines and products on graphics API (C++, Vulkan API, GLSL460, SPIR-V): https://github.com/world8th/satellite-oem
      For end users I have no more products or test products. Also, have one simple gltf viewer example (only source code).
      In 2016 year had idea for replacement of screen space reflections, but in 2018 we resolved to finally re-profile project as "basis of render engine". In Q3 of 2017 year finally merged to Vulkan API. 
       
       
  • Advertisement
  • Advertisement
Sign in to follow this  

Vulkan Vulkan Tutorial

This topic is 671 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Advertisement

Thanks for this! This is one of the better Vulkan learning articles I've seen by far.

And the tutorial I've been looking for since starting my own work with Vulkan.

It's nice to see a clean, simplified writeup with code of how to get a triangle rendering.
Without it just being commented code.

Share this post


Link to post
Share on other sites

Thanks for this! This is one of the better Vulkan learning articles I've seen by far.

And the tutorial I've been looking for since starting my own work with Vulkan.

It's nice to see a clean, simplified writeup with code of how to get a triangle rendering.
Without it just being commented code.

 

You are welcome and thanks for the feedback.

 

Also, I just published a follow up with the parts I left out. Once you are done with this one you can check out the next one: http://av.dfki.de/~jhenriques/vulkan_shaders.html ;)

Share this post


Link to post
Share on other sites

May I ask why everyone tends to load the function pointers dynamically *by hand* instead of leveraging the Vulkan SDK?

If seen this in paper from Intel. But why?

Share this post


Link to post
Share on other sites

I've seen it a few times too. I think it's because the Vulkan SDK is ultimately a 3rd party SDK (Made by LunarG). And some people may want to avoid 3rd party libraries if possible, or at least have the option.

Personally, I use the LunarG SDK, but I'll probably remove it at some point to have finer control over the DLL loading while Vulkan's existance is not guaranteed on a system.

Share this post


Link to post
Share on other sites

Thanks! Or as we say in Texas, "Many Garcias!"

 

I always say, "If I can figure out how to draw a triangle with a platform, I can make games with it."

 

I'm still trying to put together my OGL 4.5 basic engine, but I'll probably make use of your tutorial there as soon as I finish that project, hopefully in the next month or so. Although, I'm not sure if I have to build a new computer in order to use Vulkan or not. It's on my agenda to build a new computer in the next couple of months anyway. Mine is 7 years old and still chugging along nicely. An update is over due. I'm hoping to get into VR programming this year and my GPU won't cut it. So, time for a GTX 1080. But anyway, I'll go through your tutorial at some point I imagine.

Share this post


Link to post
Share on other sites

I tried compiling the code off github with VS 2015. First, I'm getting a lot of implicit conversion errors. I had to look at my own code to see how I've gotten past this in the past. You can do a (LPCWSTR) conversion explicitly on all the strings, but you can also just add the letter 'L' before each string. (I may have run into one problem where LPCWSTR didn't work right.)

 

When I run it, *base is a null_ptr and it crashes in the assert function. Oh wait. That's intentional. The problem is in the layers.

 

Installing the Vulkan SDK seems to be necessary on my computer. It went from 2 layers to 15 after installing the SDK and it found the layer it was looking for. On to the next problem.

 

The vertex shader file had a mismatched name. Replaced this code:

    //fileHandle = CreateFile( L"..\\vert.spv", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
    fileHandle = CreateFile(L"simple.vert", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

 

Hmmmm... maybe that wasn't the right solution. Now it blows completely apart on vkCreateShaderModule() with the following error:

 


Unhandled exception at 0x0F334530 (VkLayer_core_validation.dll) in JoseEnriques.exe: 0xC0000005: Access violation reading location 0x0458A808.

 

Oh. Hey! It helps if you read the instructions. That was not the correct solution. It needs to be a SPIR-V file, which is why it has a completely different file name. Wow it sure did not like the source code file, I can tell ya that!

 

So, I had to learn to use a make file. I guess that was a good learning experience. Funny that they have the source code for CMake that you have to make in order to build CMake. Finally found the binary so I could make glSlangValidator. Got the SPIR-V files. Had to remove the leading "..\\" before the file names. But after that, it ran like a charm.

 

So, now I've run my first Vulkan program on my computer. Guess my computer can handle it.

 

Thanks for the tutorial. Now that I have working code, I'll go back and read the tutorial.

Edited by BBeck

Share this post


Link to post
Share on other sites

So much code! that's why i'm afraid of starting vulka, I'll never get out of it xD 

 

Didn't you publish it as an article here? Anyhow, excellent tutorial, thank you! 

Share this post


Link to post
Share on other sites

Good tutorial and indeed it's lots of code, but this is what Vulkan was designed to be like :) There's one more skill needed to use it - patience :)

 

There are few things worth mentioning. 

 

- Final color attachment layout transition to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR doesn't have to be done through barrier but it should be rather done with end of the render pass. Notice, when creating the render pass, it's possible to fill initialLayout and finalLayout fields in the VkAttachmentDescription structure. This way no additional barrier is needed if we know that color attachment at the very end must change layout to PRESENT_SRC_KHR.

 

- Barriers ( and other synchronisation mechanisms ) are tricky and in more complex cases it's hard to get it right. It's good to group them together and submit in one go. For example, when the layout of each swapchain image is changed initially to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, then single vkCmdPipelineBarrier() should be submitted with n-barriers ( depends how many images we have ).

 

- If I understand correct, the sample code assumes the presentation and graphics queue are same queue:

        if( supportsPresent && ( queueFamilyProperties[j].queueFlags & VK_QUEUE_GRAPHICS_BIT ) ) {
            context.physicalDevice = physicalDevices[i];
            context.physicalDeviceProperties = deviceProperties;
            context.presentQueueIdx = j;
            break;
        }

This isn't good assumption. There may be queue that supports presentation, but not graphics and vice versa. In most of video cards it's very unlikely ( have't seen such case yet ), but it is possible, so...

 

- ...Why one may want to synchronise work being submitted to the same queue? The answer is simple: despite "implicit ordering guarantees" the submitted work may overlap. In the example synchronisation between vkQueuePresentKHR() and rendering with command buffer is necessary.

 

- vkQueuePresentKHR works as if it was a command+submission to the queue.  It may be dispatched while the previous drawing is still happening. Presentation engine may work aside the rendering engine and those two works may overlap even if they are submitted sequentially to the same queue.

 

- There's a suspicious code at the very end:

    VkPresentInfoKHR presentInfo = {};
    presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
    presentInfo.waitSemaphoreCount = 1;
    presentInfo.pWaitSemaphores = &renderingCompleteSemaphore;
    presentInfo.swapchainCount = 1;
    presentInfo.pSwapchains = &context.swapChain;
    presentInfo.pImageIndices = &nextImageIdx;
    presentInfo.pResults = NULL;
    vkQueuePresentKHR( context.presentQueue, &presentInfo );

    vkDestroySemaphore( context.device, presentCompleteSemaphore, NULL );
    vkDestroySemaphore( context.device, renderingCompleteSemaphore, NULL );

It is possible that renderingCompleteSemaphore will be destroyed BEFORE the presentation engine will execute. vkQueuePresentKHR() isn't blocking. I have seen two outcomes of destroying semaphore too early: DEVICE_LOST error and complete system freeze ( on Ubuntu with NVIDIA graphics card using open source driver 364.19 ). I guess it also could just crash :) But I understand this approach as specification clearly says, the semaphore is created as unsignaled and after signaling it can't be reset, so it should be destroyed. This has been noted as a bug and will be addressed. Current situation shows there's no way to tell when such semaphore, which is assigned to presentInfo.pWaitSemaphores can be destroyed. Moreover, it seems like semaphores can be reused, what is done in examples from LunarG. So for now my suggestion is to create as many pairs render/present semaphores as we have images in the swapchain and simply reuse them. Once they are placed in the submitInfo.pSignalSemaphores slot they are being "magically" reset ( I believe it will be clarified soon ). Also it's worth noting, every "...Create()" function may be quite heavy. It depends on implementation and used allocator, but that's why there are to ways of creating Vulkan objects: vk...Create() which is considered to be heavy weight and vk...Allocate() which is considered to be lightweight. So creating and destroying sync primitives every frame isn't good idea.

 

- One more thing I noticed is using fences to know that command buffer has been executed in order to be able to use it again. In general fences should be rather avoided as this is heavyweight device-host synchronisation primitive. In this case, to avoid it is better to go with round-robin and simply double/triple buffer command buffers. While one is rendering, the next one may be recorded. Otherwise if you do the same thing in OpenGL you may be surprised that OpenGL shows better performance ;) Well, synchronisation in Vulkan is HUGE topic. 

 

- About SPIR-V I recommend to look at tool SPIRV-Cross ( https://github.com/KhronosGroup/SPIRV-Cross ). It allows to decompile SPIR-V back into GLSL and few other things. Also there's plan of extending it to be able to generate pipelines from shader programs ( as everyone could see from the tutorial - setting up a pipeline, descriptor sets layouts etc. is a chore but somebody had this idea that lot can be deducted from the shader code ).

 

And last thing :)

 

So much code! that's why i'm afraid of starting vulka, I'll never get out of it xD 

 

Vulkan looks like that. You might try to take shortcuts and for example assume, that your first GPU is the one supporting Vulkan - it may be true. You may assume that extensions like swapchain exists everywhere - this also might be true... sometimes. What you may not know is that Vulkan is not only graphics API and in the future you may have devices in your PC or mobile phone which support Vulkan, but they don't support graphics ( only at least one compute queue is REQUIRED, others are not ). For example it could be DSP chipset that supports Vulkan. That's why initial enumeration and double checking is required. It may be deal breaker for some people, some other will swallow it and do code :)

Edited by j_uk_dev

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement