How I learned Vulkan and wrote a small game engine with it #7
Replies: 4 comments 4 replies
|
Very nice work! I am also in the midst of learning a graphics API (WebGPU) via the process of making a big project, but I'm focusing more on post-processing effects and compute shaders. Please mention me if you end up implementing the bloom shader, WebGPU (and I think Vulan aswell..?) does not allow changing the render target mid-pass, so the bloom implementation I used which involves successively downsampling and upsampling across different miplevels required 2n render passes to fully use, and compute shaders weren't usable since none of the texture formats that would work are supported by storage textures in WebGPU, and I would love to see if you come up with a cleaner solution in Vulkan. But congrats on the work done so far, I agree completely with "trial by fire" method of learning graphics programming and starting some project that requires you to learn as you go. |
|
Looking really nice. I'm going through a similar process right now but with OpenGL. I notice you were computing all your skinned meshes in a separate compute step and outputting them to a buffer, I guess to be used later by the vertex shader. I think you already have your joint matrices for each instance of a mesh you're rendering, so why not compute the meshes in the vertex shader? |
|
Looks real good! I'm going to learn Metal on macOS with your study path! |
|
This was an interesting read and congrats on the vk renderer! One thing that jumped out to me was you mentioning a frame graph as a potential way to automate synchronization. I've found that tracking the 'current state' on the resource with some utility functions and enums that wrap common groupings of layout/state/usage is a really nice in-between for ergonomics and simplicity. So for image barriers for example, I have more or less someResource.transition(commandBuffer, ImageState::TransferDst);or grouped barriers array<VkImageMemoryBarrier2> barriers{
input0.transitionBarrier(ImageState::ComputeShaderRead),
input1.transitionBarrier(ImageState::ComputeShaderRead),
output.transitionBarrier(ImageState::ComputeShaderWrite),
};
vkCmdPipelineBarrier2(...);The synchronization is still manual and inlined with the pass recording, but it's much less verbose and it handles dynamic changes to before states from e.g. toggling passes on and off. It can also skip redundant barriers on a single resource. Frame graph could make the whole synchronization automagical and it would allow fancy stuff like moving barriers over passes to combine the calls or drop globally redundant memory barriers, but it's also a lot more complexity and can make it more difficult to figure out why some barrier is/isn't emitted. |
New article which explains how I wrote my Vulkan engine: https://edw.is/learning-vulkan/
Free free to leave comments and provide your thoughts!