Compare commits

...
Sign in to create a new pull request.

13 commits

Author SHA1 Message Date
Zed A. Shaw
dbda70e3a0 [BROKEN] This compiles if we use fastgltf 0.9.0, but it says Error::UnsupportedVersion for the file. Earlier versions can't compile because either simdjson has code errors, or the .a/.so is missing important functions. 2025-12-20 00:36:55 -05:00
Zed A. Shaw
85d792e257 Better handling of the glb.gz files. 2025-12-15 11:08:46 -05:00
Zed A. Shaw
0e6d452580 Working fastgltf build, but had to manually include the headers. 2025-12-15 11:04:17 -05:00
Zed A. Shaw
6d29fb718b Fixed by initializing the data. 2025-12-14 23:37:00 -05:00
Zed A. Shaw
c905ef9ca7 [BREAKING] This crashes on Linux, and looks like it's crashing wayland? Testing on Windows. 2025-12-14 13:28:49 -05:00
Zed A. Shaw
5435880d34 Bring in SCC to keep track of how much code this is. 2025-12-14 10:39:30 -05:00
Zed A. Shaw
10fc100e2b Got the triangle working! It only took 10 hours. 2025-12-14 10:19:24 -05:00
Zed A. Shaw
3d36517b4f A bit more refactoring to expose the begin/end transactional nature. 2025-12-10 12:58:55 -05:00
Zed A. Shaw
b5c6774412 Clean out references to 'sky' in the load_shader. 2025-12-09 13:47:39 -05:00
Zed A. Shaw
61931083c6 Refactored the shader loading. 2025-12-09 13:22:44 -05:00
Zed A. Shaw
35198bce6b Pulled the gui out into VkGUI to separate it out. 2025-12-08 00:58:09 -05:00
Zed A. Shaw
4f7ab6db68 Cleaned up the struct inits in the engine too. 2025-12-07 23:56:47 -05:00
Zed A. Shaw
d1d57ab682 Big cleanup to utilize C++20 designated initializers https://en.cppreference.com/w/cpp/language/aggregate_initialization.html#Designated_initializers 2025-12-07 23:26:21 -05:00
26 changed files with 1380 additions and 484 deletions

2
.gitignore vendored
View file

@ -29,3 +29,5 @@ backup
coverage
coverage/*
.venv
*.glb
imgui.ini

View file

@ -13,6 +13,9 @@ shaders:
glslangValidator -V gradient.comp -o gradient.comp.spv
glslangValidator -V sky.comp -o sky.comp.spv
glslangValidator -V gradient_color.comp -o gradient_color.comp.spv
glslangValidator -V colored_triangle.frag -o colored_triangle.frag.spv
glslangValidator -V colored_triangle.vert -o colored_triangle.vert.spv
glslangValidator -V colored_triangle_mesh.vert -o colored_triangle_mesh.vert.spv
%.cpp : %.rl
ragel -I $(ROOT_DIR) -G1 -o $@ $<
@ -23,9 +26,15 @@ shaders:
%.png: %.dot
dot -Tpng $< -o $@
%.glb: %.glb.gz
gunzip -k $<
build:
meson compile -j 10 -C $(ROOT_DIR)/builddir
meshes: basicmesh.glb
@echo "Meshes made"
release_build:
meson --wipe builddir -Db_ndebug=true --buildtype release
meson compile -j 10 -C builddir
@ -50,9 +59,10 @@ debug_run: build
clean:
meson compile --clean -C builddir
rm -f basicmesh.glb
debug_test: build
gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[pathing]"
money:
scc --exclude-dir subprojects
scc --exclude-dir subprojects,vendor

BIN
basicmesh.glb.gz Normal file

Binary file not shown.

13
colored_triangle.frag Normal file
View file

@ -0,0 +1,13 @@
#version 450
//shader input
layout (location = 0) in vec3 inColor;
//output write
layout (location = 0) out vec4 outFragColor;
void main()
{
//return red
outFragColor = vec4(inColor,1.0f);
}

BIN
colored_triangle.frag.spv Normal file

Binary file not shown.

24
colored_triangle.vert Normal file
View file

@ -0,0 +1,24 @@
#version 450
layout (location = 0) out vec3 outColor;
void main()
{
//const array of positions for the triangle
const vec3 positions[3] = vec3[3](
vec3(1.f,1.f, 0.0f),
vec3(-1.f,1.f, 0.0f),
vec3(0.f,-1.f, 0.0f)
);
//const array of colors for the triangle
const vec3 colors[3] = vec3[3](
vec3(1.0f, 0.0f, 0.0f), //red
vec3(0.0f, 1.0f, 0.0f), //green
vec3(00.f, 0.0f, 1.0f) //blue
);
//output the position of each vertex
gl_Position = vec4(positions[gl_VertexIndex], 1.0f);
outColor = colors[gl_VertexIndex];
}

BIN
colored_triangle.vert.spv Normal file

Binary file not shown.

View file

@ -0,0 +1,37 @@
#version 450
#extension GL_EXT_buffer_reference : require
layout (location = 0) out vec3 outColor;
layout (location = 1) out vec2 outUV;
struct Vertex {
vec3 position;
float uv_x;
vec3 normal;
float uv_y;
vec4 color;
};
layout(buffer_reference, std430) readonly buffer VertexBuffer{
Vertex vertices[];
};
//push constants block
layout( push_constant ) uniform constants
{
mat4 render_matrix;
VertexBuffer vertexBuffer;
} PushConstants;
void main()
{
//load vertex data from device adress
Vertex v = PushConstants.vertexBuffer.vertices[gl_VertexIndex];
//output data
gl_Position = PushConstants.render_matrix *vec4(v.position, 1.0f);
outColor = v.color.xyz;
outUV.x = v.uv_x;
outUV.y = v.uv_y;
}

Binary file not shown.

View file

@ -69,7 +69,6 @@ elif build_machine.system() == 'darwin'
endif
vma = subproject('vulkan-memory-allocator').get_variable('vma_allocator_dep')
# vulkan_headers = subproject('vulkan-headers').get_variable('vulkan_headers_dep')
imgui = subproject('imgui',
default_options: {
@ -80,6 +79,13 @@ imgui = subproject('imgui',
sdl2 = subproject('sdl2').get_variable('sdl2_dep')
fastgltf_opts = cmake.subproject_options()
fastgltf_opts.add_cmake_defines({
'FASTGLTF_DOWNLOAD_SIMDJSON': false,
})
fastgltf_proj = cmake.subproject('fastgltf', options: fastgltf_opts)
fastgltf = fastgltf_proj.get_variable('fastgltf_dep')
glm_opts = cmake.subproject_options()
glm_opts.add_cmake_defines({
@ -113,6 +119,7 @@ dependencies += [
glm,
imgui,
sdl2,
fastgltf,
]
sources = [
@ -121,15 +128,21 @@ sources = [
'vk_images.cpp',
'vk_descriptors.cpp',
'vk_pipelines.cpp',
'vk_loader.cpp',
'vk_gui.cpp',
'main.cpp',
]
# have to force this here for...cmake reasons
include_dirs = include_directories('subprojects/fastgltf/include')
tests = [
]
executable('hellovulk', sources,
cpp_args: cpp_args,
link_args: link_args,
include_directories: include_dirs,
win_subsystem: 'windows',
override_options: exe_defaults,
dependencies: dependencies)

View file

View file

@ -2,11 +2,11 @@
void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type)
{
VkDescriptorSetLayoutBinding newbind {};
newbind.binding = binding;
newbind.descriptorCount = 1;
newbind.descriptorType = type;
VkDescriptorSetLayoutBinding newbind{
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
};
bindings.push_back(newbind);
}
@ -25,18 +25,16 @@ VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device,
b.stageFlags |= shaderStages;
}
VkDescriptorSetLayoutCreateInfo info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO
VkDescriptorSetLayoutCreateInfo info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = pNext,
.flags = flags,
.bindingCount = (uint32_t)bindings.size(),
.pBindings = bindings.data(),
};
info.pNext = pNext;
info.pBindings = bindings.data();
info.bindingCount = (uint32_t)bindings.size();
info.flags = flags;
VkDescriptorSetLayout set;
VK_CHECK(vkCreateDescriptorSetLayout(device,
&info, nullptr, &set));
VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &set));
return set;
}
@ -46,19 +44,19 @@ void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets,
{
std::vector<VkDescriptorPoolSize> poolSizes;
for(PoolSizeRatio ratio : poolRatios) {
poolSizes.push_back(VkDescriptorPoolSize{
poolSizes.push_back({
.type = ratio.type,
.descriptorCount = uint32_t(ratio.ratio * maxSets)
});
}
VkDescriptorPoolCreateInfo pool_info = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO
VkDescriptorPoolCreateInfo pool_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = 0,
.maxSets = maxSets,
.poolSizeCount = (uint32_t)poolSizes.size(),
.pPoolSizes = poolSizes.data(),
};
pool_info.flags = 0;
pool_info.maxSets = maxSets;
pool_info.poolSizeCount = (uint32_t)poolSizes.size();
pool_info.pPoolSizes = poolSizes.data();
vkCreateDescriptorPool(device, &pool_info, nullptr, &pool);
}
@ -75,15 +73,13 @@ void DescriptorAllocator::destroy_pool(VkDevice device)
VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout)
{
VkDescriptorSetAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO
VkDescriptorSetAllocateInfo allocInfo{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = pool,
.descriptorSetCount = 1,
.pSetLayouts = &layout,
};
allocInfo.pNext = nullptr;
allocInfo.descriptorPool = pool;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
VkDescriptorSet ds;
VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds));

View file

@ -3,23 +3,18 @@
#include "vk_pipelines.h"
#include <print>
#include <SDL.h>
#include <SDL_vulkan.h>
#include <vk_types.h>
#include <vk_initializers.h>
#include "VkBootstrap.h"
#include <chrono>
#include <thread>
#include "imgui.h"
#include "imgui_impl_sdl2.h"
#include "imgui_impl_vulkan.h"
#include <SDL_vulkan.h>
#define VMA_IMPLEMENTATION
#include "vk_mem_alloc.h"
constexpr bool bUseValidationLayers = true;
constexpr bool bUseValidationLayers = false;
VulkanEngine* loadedEngine = nullptr;
@ -32,18 +27,7 @@ void VulkanEngine::init()
assert(loadedEngine == nullptr);
loadedEngine = this;
// We initialize SDL and create a window with it.
SDL_Init(SDL_INIT_VIDEO);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN);
_window = SDL_CreateWindow(
"Vulkan Engine",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
int(_windowExtent.width),
int(_windowExtent.height),
window_flags);
_window = _gui.init_window(_windowExtent);
init_vulkan();
init_swapchain();
@ -51,7 +35,8 @@ void VulkanEngine::init()
init_sync_structures();
init_descriptors();
init_pipelines();
init_imgui();
_gui.init_imgui(VulkanEngine::Get());
init_default_data();
//everything went fine
_isInitialized = true;
@ -81,14 +66,14 @@ void VulkanEngine::cleanup()
vkDestroyDevice(_device, nullptr);
vkb::destroy_debug_utils_messenger(_instance, _debug_messenger);
vkDestroyInstance(_instance, nullptr);
SDL_DestroyWindow(_window);
_gui.destroy(_window);
}
loadedEngine = nullptr;
}
void VulkanEngine::draw()
{
Transaction VulkanEngine::begin_transaction() {
// ZED: begin command transaction
// wait until the gpu has finished rendering the last frame. Timeout of 1 second
VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000));
get_current_frame()._deletionQueue.flush();
@ -103,44 +88,24 @@ void VulkanEngine::draw()
VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
_drawExtent.width = _drawImage.imageExtent.width;
_drawExtent.height = _drawImage.imageExtent.height;
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
// transition our main draw image into general layout so we can write into it
// we will overwrite it all so we dont care about what was the older layout
return {cmd, swapchainImageIndex};
}
vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
draw_background(cmd);
vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkutil::copy_image_to_image(cmd, _drawImage.image,
_swapchainImages[swapchainImageIndex],
_drawExtent, _swapchainExtent);
vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]);
vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
VK_CHECK(vkEndCommandBuffer(cmd));
void VulkanEngine::commit_transaction(Transaction& t) {
VK_CHECK(vkEndCommandBuffer(t.cmd));
// ZED: submit command to queue
//prepare the submission to the queue.
//we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is ready
//we will signal the _renderSemaphore, to signal that rendering has finished
VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd);
VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(t.cmd);
VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore);
VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore);
VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo,&signalInfo,&waitInfo);
VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo);
//submit command buffer to the queue and execute it.
// _renderFence will now block until the graphic commands finish execution
@ -150,16 +115,15 @@ void VulkanEngine::draw()
// this will put the image we just rendered to into the visible window.
// we want to wait on the _renderSemaphore for that,
// as its necessary that drawing commands have finished before the image is displayed to the user
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = nullptr;
presentInfo.pSwapchains = &_swapchain;
presentInfo.swapchainCount = 1;
presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pImageIndices = &swapchainImageIndex;
VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &get_current_frame()._renderSemaphore,
.swapchainCount = 1,
.pSwapchains = &_swapchain,
.pImageIndices = &t.swapchainImageIndex,
};
VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo));
@ -167,70 +131,135 @@ void VulkanEngine::draw()
_frameNumber++;
}
void VulkanEngine::draw()
{
auto t = begin_transaction();
// ZED: draw the things
_drawExtent.width = _drawImage.imageExtent.width;
_drawExtent.height = _drawImage.imageExtent.height;
// transition our main draw image into general layout so we can write into it
// we will overwrite it all so we dont care about what was the older layout
vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL);
draw_background(t.cmd);
vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
draw_geometry(t.cmd);
// ZED: ?
vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// ZED: ?
vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex],
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
vkutil::copy_image_to_image(t.cmd, _drawImage.image,
_swapchainImages[t.swapchainImageIndex],
_drawExtent, _swapchainExtent);
// ZED: ?
vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
// ZED: end drawing the things
_gui.draw_imgui(_swapchainExtent, t.cmd, _swapchainImageViews[t.swapchainImageIndex]);
// ZED: finalize image and commit command buffer
vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
commit_transaction(t);
}
void VulkanEngine::draw_geometry(VkCommandBuffer cmd)
{
VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr);
vkCmdBeginRendering(cmd, &renderInfo);
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _trianglePipeline);
VkViewport viewport{
.x = 0,
.y = 0,
.width = (float)_drawExtent.width,
.height = (float)_drawExtent.height,
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
vkCmdSetViewport(cmd, 0, 1, &viewport);
VkRect2D scissor{
.offset = { .x = 0, .y = 0, },
.extent = _drawExtent
};
vkCmdSetScissor(cmd, 0, 1, &scissor);
vkCmdDraw(cmd, 3, 1, 0, 0);
// draw rectangle
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline);
GPUDrawPushConstants push_constants{
.worldMatrix = glm::mat4{ 1.0f },
.vertexBuffer = rectangle.vertexBufferAddress,
};
vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants),
&push_constants);
vkCmdBindIndexBuffer(cmd, rectangle.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0);
// draw monkey
size_t model_idx = 2; // 0 cube; 1 sphere; 2 monkey
push_constants.vertexBuffer = testMeshes[model_idx]->meshBuffers.vertexBufferAddress;
vkCmdPushConstants(cmd,
_meshPipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT,
0,
sizeof(GPUDrawPushConstants),
&push_constants);
vkCmdBindIndexBuffer(cmd,
testMeshes[model_idx]->meshBuffers.indexBuffer.buffer,
0,
VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(cmd,
testMeshes[model_idx]->surfaces[0].count,
1,
testMeshes[model_idx]->surfaces[0].startIndex,
0, 0);
vkCmdEndRendering(cmd);
}
void VulkanEngine::run()
{
SDL_Event e;
bool bQuit = false;
bool stop_rendering = false;
//main loop
while(!bQuit)
while(!_gui.should_quit)
{
//Handle events on queue
while(SDL_PollEvent(&e) != 0)
{
//close the window when user alt-f4s or clicks the X button
if(e.type == SDL_QUIT) {
bQuit = true;
}
_gui.poll_event();
if(e.type == SDL_WINDOWEVENT) {
if(e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
stop_rendering = true;
}
if(e.window.event == SDL_WINDOWEVENT_RESTORED) {
stop_rendering = false;
}
}
ImGui_ImplSDL2_ProcessEvent(&e);
}
if(stop_rendering) {
if(_gui.stop_rendering) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
continue;
}
render_imgui();
_gui.render_imgui(backgroundEffects, &currentBackgroundEffect);
draw();
}
}
void VulkanEngine::render_imgui() {
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (ImGui::Begin("background")) {
ComputeEffect& selected = backgroundEffects[currentBackgroundEffect];
ImGui::Text("Selected effect: %s", selected.name);
ImGui::SliderInt("Effect Index", &currentBackgroundEffect,0, backgroundEffects.size() - 1);
ImGui::InputFloat4("data1",(float*)& selected.data.data1);
ImGui::InputFloat4("data2",(float*)& selected.data.data2);
ImGui::InputFloat4("data3",(float*)& selected.data.data3);
ImGui::InputFloat4("data4",(float*)& selected.data.data4);
}
ImGui::End();
ImGui::Render();
}
void VulkanEngine::init_vulkan() {
vkb::InstanceBuilder builder;
@ -251,17 +280,18 @@ void VulkanEngine::init_vulkan() {
SDL_Vulkan_CreateSurface(_window, _instance, &_surface);
//vulkan 1.3 features
VkPhysicalDeviceVulkan13Features features13{};
features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES;
features13.dynamicRendering = true;
features13.synchronization2 = true;
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.synchronization2 = true,
.dynamicRendering = true,
};
//vulkan 1.2 features
VkPhysicalDeviceVulkan12Features features12{};
features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
features12.bufferDeviceAddress = true;
features12.descriptorIndexing = true;
VkPhysicalDeviceVulkan12Features features12{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.descriptorIndexing = true,
.bufferDeviceAddress = true,
};
// use vkbootstrap to select a gpu
// We want a gpu that can write to the SDL surface
@ -284,11 +314,13 @@ void VulkanEngine::init_vulkan() {
_graphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value();
// initialize the memory allocator
VmaAllocatorCreateInfo allocatorInfo{};
allocatorInfo.physicalDevice = _chosenGPU;
allocatorInfo.device = _device;
allocatorInfo.instance = _instance;
allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
VmaAllocatorCreateInfo allocatorInfo{
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
.physicalDevice = _chosenGPU,
.device = _device,
.instance = _instance,
};
vmaCreateAllocator(&allocatorInfo, &_allocator);
_mainDeletionQueue.push_function([&]() {
@ -339,17 +371,18 @@ void VulkanEngine::init_swapchain() {
_drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
_drawImage.imageExtent = drawImageExtent;
VkImageUsageFlags drawImageUsages{};
drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT;
drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VkImageUsageFlags drawImageUsages =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT
| VK_IMAGE_USAGE_STORAGE_BIT
| VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent);
VmaAllocationCreateInfo rimg_allocinfo{};
rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
rimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VmaAllocationCreateInfo rimg_allocinfo{
.usage = VMA_MEMORY_USAGE_GPU_ONLY,
.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
};
//allocate and create the image
vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr);
@ -430,7 +463,7 @@ void VulkanEngine::init_descriptors() {
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 }
};
globalDescriptorAllocator.init_pool(_device, 10, sizes);
_globalDescriptorAllocator.init_pool(_device, 10, sizes);
// make the descriptor set layout for our compute draw
{
@ -441,27 +474,27 @@ void VulkanEngine::init_descriptors() {
// other code
//allocate a descriptor set for our draw image
_drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout);
_drawImageDescriptors = _globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout);
VkDescriptorImageInfo imgInfo{};
imgInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
imgInfo.imageView = _drawImage.imageView;
VkDescriptorImageInfo imgInfo{
.imageView = _drawImage.imageView,
.imageLayout = VK_IMAGE_LAYOUT_GENERAL,
};
VkWriteDescriptorSet drawImageWrite = {};
drawImageWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
drawImageWrite.pNext = nullptr;
drawImageWrite.dstBinding = 0;
drawImageWrite.dstSet = _drawImageDescriptors;
drawImageWrite.descriptorCount = 1;
drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
drawImageWrite.pImageInfo = &imgInfo;
VkWriteDescriptorSet drawImageWrite{
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = _drawImageDescriptors,
.dstBinding = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
.pImageInfo = &imgInfo,
};
vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr),
//make sure both the descriptor allocator and the new layout get cleaned up properly
_mainDeletionQueue.push_function([&]() {
globalDescriptorAllocator.destroy_pool(_device);
_globalDescriptorAllocator.destroy_pool(_device);
vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr);
});
@ -470,77 +503,83 @@ void VulkanEngine::init_descriptors() {
void VulkanEngine::init_pipelines()
{
init_background_pipelines();
init_triangle_pipelines();
init_mesh_pipeline();
init_shaders();
}
void VulkanEngine::load_shader(const char *file_name, const char *entry_point, ComputeEffect effect) {
VkShaderModule shader;
bool good = vkutil::load_shader_module(file_name, _device, &shader);
assert(good && "failed to load shader");
VkPipelineShaderStageCreateInfo stageinfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.module = shader,
.pName = entry_point,
};
VkComputePipelineCreateInfo computePipelineCreateInfo{
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.stage = stageinfo,
.layout = _gradientPipelineLayout,
};
effect.layout = _gradientPipelineLayout;
VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &effect.pipeline));
backgroundEffects.push_back(effect);
vkDestroyShaderModule(_device, shader, nullptr);
_mainDeletionQueue.push_function([=,this]() {
vkDestroyPipeline(_device, effect.pipeline, nullptr);
});
}
void VulkanEngine::init_background_pipelines()
{
VkPipelineLayoutCreateInfo computeLayout{};
computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
computeLayout.pNext = nullptr;
computeLayout.pSetLayouts = &_drawImageDescriptorLayout;
computeLayout.setLayoutCount = 1;
VkPushConstantRange pushConstant{
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.offset = 0,
.size = sizeof(ComputePushConstants),
};
VkPushConstantRange pushConstant{};
pushConstant.offset = 0;
pushConstant.size = sizeof(ComputePushConstants) ;
pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
computeLayout.pPushConstantRanges = &pushConstant;
computeLayout.pushConstantRangeCount = 1;
VkPipelineLayoutCreateInfo computeLayout{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &_drawImageDescriptorLayout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstant,
};
VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout));
VkShaderModule gradientShader;
bool good = vkutil::load_shader_module("gradient_color.comp.spv", _device, &gradientShader);
assert(good && "failed to load gradient_color.comp.spv");
VkShaderModule skyShader;
good = vkutil::load_shader_module("sky.comp.spv", _device, &skyShader);
VkPipelineShaderStageCreateInfo stageinfo{};
stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stageinfo.pNext = nullptr;
stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
stageinfo.module = gradientShader;
stageinfo.pName = "main";
VkComputePipelineCreateInfo computePipelineCreateInfo{};
computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
computePipelineCreateInfo.pNext = nullptr;
computePipelineCreateInfo.layout = _gradientPipelineLayout;
computePipelineCreateInfo.stage = stageinfo;
ComputeEffect gradient;
gradient.layout = _gradientPipelineLayout;
gradient.name = "gradient";
gradient.data = {};
gradient.data.data1 = glm::vec4(1, 0, 0, 1);
gradient.data.data2 = glm::vec4(0, 0, 1, 1);
VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline));
// change the shader module only to create the sky
computePipelineCreateInfo.stage.module = skyShader;
ComputeEffect sky;
sky.layout = _gradientPipelineLayout;
sky.name = "sky";
sky.data = {};
// default sky
sky.data.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97);
VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline));
backgroundEffects.push_back(gradient);
backgroundEffects.push_back(sky);
vkDestroyShaderModule(_device, gradientShader, nullptr);
vkDestroyShaderModule(_device, skyShader, nullptr);
// final cleanup
_mainDeletionQueue.push_function([=,this]() {
vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr);
vkDestroyPipeline(_device, sky.pipeline, nullptr);
vkDestroyPipeline(_device, sky.pipeline, nullptr);
});
}
void VulkanEngine::init_shaders() {
/// gradient shader
load_shader("gradient_color.comp.spv", "main", {
.name="gradient",
.data={
.data1 = glm::vec4(1, 0, 0, 1),
.data2 = glm::vec4(0, 0, 1, 1),
}
});
load_shader("sky.comp.spv", "main", {
.name="sky",
.data = {
.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97)
}
});
}
@ -565,75 +604,229 @@ void VulkanEngine::immediate_submit(std::function<void(VkCommandBuffer cmd)>&& f
VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence));
VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999));
}
void VulkanEngine::init_imgui()
void VulkanEngine::init_mesh_pipeline()
{
// 1: create descriptor pool for IMGUI
// the size of the pool is very oversize, but it's copied from imgui demo
// itself.
VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } };
VkShaderModule triangleFragShader;
if (!vkutil::load_shader_module("colored_triangle.frag.spv", _device, &triangleFragShader)) {
std::print("Error when building the triangle fragment shader module");
}
else {
std::print("Triangle fragment shader succesfully loaded");
}
VkDescriptorPoolCreateInfo pool_info{};
pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
pool_info.maxSets = 1000;
pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes);
pool_info.pPoolSizes = pool_sizes;
VkShaderModule triangleVertexShader;
if (!vkutil::load_shader_module("colored_triangle_mesh.vert.spv", _device, &triangleVertexShader)) {
std::print("Error when building the triangle vertex shader module");
}
else {
std::print("Triangle vertex shader succesfully loaded");
}
VkDescriptorPool imguiPool;
VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool));
VkPushConstantRange bufferRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.offset = 0,
.size = sizeof(GPUDrawPushConstants),
};
// 2: initialize the imgui library
ImGui::CreateContext();
VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
pipeline_layout_info.pPushConstantRanges = &bufferRange;
pipeline_layout_info.pushConstantRangeCount = 1;
ImGui_ImplSDL2_InitForVulkan(_window);
ImGui_ImplVulkan_InitInfo init_info{};
init_info.Instance = _instance;
init_info.PhysicalDevice = _chosenGPU;
init_info.Device = _device;
init_info.Queue = _graphicsQueue;
init_info.DescriptorPool = imguiPool;
init_info.MinImageCount = 3;
init_info.ImageCount = 3;
init_info.UseDynamicRendering = true;
VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_meshPipelineLayout));
init_info.PipelineRenderingCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO};
init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1;
init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &_swapchainImageFormat;
init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT;
ImGui_ImplVulkan_Init(&init_info);
ImGui_ImplVulkan_CreateFontsTexture();
PipelineBuilder pipelineBuilder;
_mainDeletionQueue.push_function([=,this]() {
ImGui_ImplVulkan_Shutdown();
vkDestroyDescriptorPool(_device, imguiPool, nullptr);
pipelineBuilder._pipelineLayout = _meshPipelineLayout;
pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader);
pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL);
pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
pipelineBuilder.set_multisampling_none();
pipelineBuilder.disable_blending();
pipelineBuilder.disable_depthtest();
pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED);
_meshPipeline = pipelineBuilder.build_pipeline(_device);
vkDestroyShaderModule(_device, triangleFragShader, nullptr);
vkDestroyShaderModule(_device, triangleVertexShader, nullptr);
_mainDeletionQueue.push_function([&]() {
vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr);
vkDestroyPipeline(_device, _meshPipeline, nullptr);
});
}
void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView)
void VulkanEngine::init_triangle_pipelines()
{
VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkShaderModule triangleFragShader;
VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr);
if(!vkutil::load_shader_module("colored_triangle.frag.spv",_device, &triangleFragShader)) {
std::print("Error when building the triangle fragment shaders module");
} else {
std::print("Triangle fragment shader successfully loaded");
}
vkCmdBeginRendering(cmd, &renderInfo);
VkShaderModule triangleVertexShader;
if(!vkutil::load_shader_module("colored_triangle.vert.spv", _device, &triangleVertexShader)) {
std::println("Error when building the triangle, vertex shader module");
} else {
std::println("Triangle vertex shader successfully loaded");
}
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd);
VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info();
vkCmdEndRendering(cmd);
VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_trianglePipelineLayout));
PipelineBuilder pipelineBuilder;
pipelineBuilder._pipelineLayout = _trianglePipelineLayout;
pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader);
pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL);
pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
pipelineBuilder.set_multisampling_none();
pipelineBuilder.disable_blending();
pipelineBuilder.disable_depthtest();
// connect the image format we will drw into, from draw image
pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat);
pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED);
_trianglePipeline = pipelineBuilder.build_pipeline(_device);
vkDestroyShaderModule(_device, triangleFragShader, nullptr);
vkDestroyShaderModule(_device, triangleVertexShader, nullptr);
_mainDeletionQueue.push_function([&]() {
vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr);
vkDestroyPipeline(_device, _trianglePipeline, nullptr);
});
}
AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage)
{
VkBufferCreateInfo bufferInfo{
.sType=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = allocSize,
.usage = usage,
};
VmaAllocationCreateInfo vmaallocInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = memoryUsage,
};
AllocatedBuffer newBuffer;
VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo,
&vmaallocInfo, &newBuffer.buffer,
&newBuffer.allocation, &newBuffer.info));
return newBuffer;
}
void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer)
{
vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation);
}
GPUMeshBuffers VulkanEngine::uploadMesh(std::span<uint32_t> indices, std::span<Vertex> vertices)
{
const size_t vertexBufferSize = vertices.size() * sizeof(Vertex);
const size_t indexBufferSize = indices.size() * sizeof(uint32_t);
GPUMeshBuffers newSurface;
newSurface.vertexBuffer = create_buffer(
vertexBufferSize,
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
| VK_BUFFER_USAGE_TRANSFER_DST_BIT
| VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
VkBufferDeviceAddressInfo deviceAddressInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
.buffer = newSurface.vertexBuffer.buffer
};
newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAddressInfo);
newSurface.indexBuffer = create_buffer(indexBufferSize,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_ONLY);
AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
void* data = staging.allocation->GetMappedData();
memcpy(data, vertices.data(), vertexBufferSize);
memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize);
immediate_submit([&](VkCommandBuffer cmd) {
VkBufferCopy vertexCopy{
.srcOffset = 0,
.dstOffset = 0,
.size = vertexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer,
newSurface.vertexBuffer.buffer, 1,
&vertexCopy);
VkBufferCopy indexCopy{
.srcOffset = vertexBufferSize,
.dstOffset = 0,
.size = indexBufferSize,
};
vkCmdCopyBuffer(cmd, staging.buffer,
newSurface.indexBuffer.buffer, 1,
&indexCopy);
});
destroy_buffer(staging);
return newSurface;
}
void VulkanEngine::init_default_data() {
std::array<Vertex,4> rect_vertices;
rect_vertices[0].position = {0.5,-0.5, 0};
rect_vertices[1].position = {0.5,0.5, 0};
rect_vertices[2].position = {-0.5,-0.5, 0};
rect_vertices[3].position = {-0.5,0.5, 0};
rect_vertices[0].color = {0,0, 0,1};
rect_vertices[1].color = { 0.5,0.5,0.5 ,1};
rect_vertices[2].color = { 1,0, 0,1 };
rect_vertices[3].color = { 0,1, 0,1 };
std::array<uint32_t,6> rect_indices;
rect_indices[0] = 0;
rect_indices[1] = 1;
rect_indices[2] = 2;
rect_indices[3] = 2;
rect_indices[4] = 1;
rect_indices[5] = 3;
rectangle = uploadMesh(rect_indices, rect_vertices);
auto basicmesh = loadGltfMeshes(this, "basicmesh.glb");
assert(basicmesh != std::nullopt && "Failed to load basicmesh.glb");
testMeshes = *basicmesh;
_mainDeletionQueue.push_function([&](){
destroy_buffer(rectangle.indexBuffer);
destroy_buffer(rectangle.vertexBuffer);
});
}

View file

@ -5,23 +5,14 @@
#include <vk_types.h>
#include <vk_descriptors.h>
#include "vk_gui.h"
#include "vk_loader.h"
struct ComputePushConstants {
glm::vec4 data1;
glm::vec4 data2;
glm::vec4 data3;
glm::vec4 data4;
struct Transaction {
VkCommandBuffer cmd;
uint32_t swapchainImageIndex = 0;
};
struct ComputeEffect {
const char *name;
VkPipeline pipeline;
VkPipelineLayout layout;
ComputePushConstants data;
};
struct DeletionQueue {
std::deque<std::function<void()>> deletors;
@ -48,7 +39,6 @@ struct FrameData {
DeletionQueue _deletionQueue;
};
constexpr unsigned int FRAME_OVERLAP=2;
class VulkanEngine {
public:
@ -58,7 +48,7 @@ public:
VkPipeline _gradientPipeline;
VkPipelineLayout _gradientPipelineLayout;
DescriptorAllocator globalDescriptorAllocator;
DescriptorAllocator _globalDescriptorAllocator;
VkDescriptorSet _drawImageDescriptors;
VkDescriptorSetLayout _drawImageDescriptorLayout;
@ -77,6 +67,7 @@ public:
std::vector<VkImageView> _swapchainImageViews;
VkExtent2D _swapchainExtent;
// VMA stuff
VmaAllocator _allocator;
@ -104,6 +95,16 @@ public:
// imgui shader stuff
std::vector<ComputeEffect> backgroundEffects;
int currentBackgroundEffect{0};
VkPipelineLayout _trianglePipelineLayout;
VkPipeline _trianglePipeline;
VkPipelineLayout _meshPipelineLayout;
VkPipeline _meshPipeline;
GPUMeshBuffers rectangle;
std::vector<std::shared_ptr<MeshAsset>> testMeshes;
// ZED's REFACTOR
VkGUI _gui;
static VulkanEngine& Get();
@ -121,6 +122,11 @@ public:
void immediate_submit(std::function<void(VkCommandBuffer cmd)>&& function);
Transaction begin_transaction();
void commit_transaction(Transaction& cmd);
GPUMeshBuffers uploadMesh(std::span<uint32_t> indices, std::span<Vertex> vertices);
private:
void init_vulkan();
void init_swapchain();
@ -129,11 +135,19 @@ private:
void init_descriptors();
void init_pipelines();
void init_background_pipelines();
void init_imgui();
void init_shaders();
void init_triangle_pipelines();
void init_mesh_pipeline();
void init_default_data();
void load_shader(const char *file_name, const char *entry_point, ComputeEffect data);
void create_swapchain(uint32_t width, uint32_t height);
AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage);
void destroy_swapchain();
void destroy_buffer(const AllocatedBuffer& buffer);
void draw_background(VkCommandBuffer cmd);
void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView);
void render_imgui();
void draw_geometry(VkCommandBuffer cmd);
};

151
vk_gui.cpp Normal file
View file

@ -0,0 +1,151 @@
#include "vk_gui.h"
#include "vk_initializers.h"
#include <SDL.h>
#include <SDL_vulkan.h>
#include <imgui.h>
#include <imgui_impl_sdl2.h>
#include <imgui_impl_vulkan.h>
#include "vk_engine.h"
struct SDL_Window* VkGUI::init_window(VkExtent2D windowExtent)
{
SDL_Init(SDL_INIT_VIDEO);
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN);
return SDL_CreateWindow(
"Vulkan Engine",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
int(windowExtent.width),
int(windowExtent.height),
window_flags);
}
void VkGUI::init_imgui(VulkanEngine& engine)
{
// 1. create descriptor pool for IMGUI
// the size of the pool is very oversize, but it's copied from imgui demo
// itself.
VkDescriptorPoolSize pool_sizes[] = {
{ VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
{ VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
};
VkDescriptorPoolCreateInfo pool_info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
.maxSets = 1000,
.poolSizeCount = (uint32_t)std::size(pool_sizes),
.pPoolSizes = pool_sizes,
};
VkDescriptorPool imguiPool;
VK_CHECK(vkCreateDescriptorPool(engine._device, &pool_info, nullptr, &imguiPool));
// 2: initialize the imgui library
ImGui::CreateContext();
ImGui_ImplSDL2_InitForVulkan(engine._window);
ImGui_ImplVulkan_InitInfo init_info{
.Instance = engine._instance,
.PhysicalDevice = engine._chosenGPU,
.Device = engine._device,
.Queue = engine._graphicsQueue,
.DescriptorPool = imguiPool,
.MinImageCount = 3,
.ImageCount = 3,
.MSAASamples = VK_SAMPLE_COUNT_1_BIT,
.UseDynamicRendering = true,
.PipelineRenderingCreateInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &engine._swapchainImageFormat,
},
};
ImGui_ImplVulkan_Init(&init_info);
ImGui_ImplVulkan_CreateFontsTexture();
engine._mainDeletionQueue.push_function([=,this]() {
ImGui_ImplVulkan_Shutdown();
vkDestroyDescriptorPool(engine._device, imguiPool, nullptr);
});
}
void VkGUI::draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView)
{
VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkRenderingInfo renderInfo = vkinit::rendering_info(swapchainExtent, &colorAttachment, nullptr);
vkCmdBeginRendering(cmd, &renderInfo);
ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd);
vkCmdEndRendering(cmd);
}
void VkGUI::render_imgui(std::vector<ComputeEffect>& backgroundEffects, int* currentBackgroundEffect)
{
ImGui_ImplVulkan_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
if (ImGui::Begin("background")) {
ComputeEffect& selected = backgroundEffects[*currentBackgroundEffect];
ImGui::Text("Selected effect: %s", selected.name);
ImGui::SliderInt("Effect Index", currentBackgroundEffect,0, backgroundEffects.size() - 1);
ImGui::InputFloat4("data1",(float*)& selected.data.data1);
ImGui::InputFloat4("data2",(float*)& selected.data.data2);
ImGui::InputFloat4("data3",(float*)& selected.data.data3);
ImGui::InputFloat4("data4",(float*)& selected.data.data4);
}
ImGui::End();
ImGui::Render();
}
void VkGUI::destroy(struct SDL_Window* _window)
{
SDL_DestroyWindow(_window);
}
void VkGUI::poll_event() {
SDL_Event event;
while(SDL_PollEvent(&event) != 0) {
//close the window when user alt-f4s or clicks the X button
if(event.type == SDL_QUIT) {
should_quit = true;
}
if(event.type == SDL_WINDOWEVENT) {
if(event.window.event == SDL_WINDOWEVENT_MINIMIZED) {
stop_rendering = true;
}
if(event.window.event == SDL_WINDOWEVENT_RESTORED) {
stop_rendering = false;
}
}
ImGui_ImplSDL2_ProcessEvent(&event);
}
}

18
vk_gui.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include "vk_types.h"
class VulkanEngine;
class VkGUI {
public:
bool should_quit = false;
bool stop_rendering = false;
struct SDL_Window* init_window(VkExtent2D windowExtent);
void init_imgui(VulkanEngine& engine);
void draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView);
void render_imgui(std::vector<ComputeEffect>& backgroundEffects, int* currentBackgroundEffect);
void destroy(struct SDL_Window* _window);
void poll_event();
};

View file

@ -3,28 +3,25 @@
void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout)
{
VkImageMemoryBarrier2 imageBarrier{};
imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
imageBarrier.pNext = nullptr;
imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
imageBarrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT;
imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT;
imageBarrier.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT;
imageBarrier.oldLayout = currentLayout;
imageBarrier.newLayout = newLayout;
VkImageAspectFlags aspectMask = (newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
imageBarrier.subresourceRange = vkinit::image_subresource_range(aspectMask);
imageBarrier.image = image;
VkDependencyInfo depInfo{};
depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
depInfo.pNext = nullptr;
depInfo.imageMemoryBarrierCount = 1;
depInfo.pImageMemoryBarriers = &imageBarrier;
VkImageMemoryBarrier2 imageBarrier{
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2,
.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT,
.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT,
.oldLayout = currentLayout,
.newLayout = newLayout,
.image = image,
.subresourceRange = vkinit::image_subresource_range(aspectMask),
};
VkDependencyInfo depInfo{
.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageBarrier,
};
vkCmdPipelineBarrier2(cmd, &depInfo);
}
@ -33,39 +30,45 @@ void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage de
{
VkImageBlit2 blitRegion{
.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2,
.pNext = nullptr
};
blitRegion.srcOffsets[1].x = srcSize.width;
blitRegion.srcOffsets[1].y = srcSize.height;
blitRegion.srcOffsets[1].z = 1;
blitRegion.srcSubresource = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
};
blitRegion.dstOffsets[1].x = dstSize.width;
blitRegion.dstOffsets[1].y = dstSize.height;
blitRegion.dstOffsets[1].z = 1;
blitRegion.dstSubresource = {
.aspectMask=VK_IMAGE_ASPECT_COLOR_BIT,
.mipLevel=0,
.baseArrayLayer=0,
.layerCount=1,
};
blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blitRegion.srcSubresource.baseArrayLayer = 0;
blitRegion.srcSubresource.layerCount = 1;
blitRegion.srcSubresource.mipLevel = 0;
blitRegion.srcOffsets[1] = {
.x = int32_t(srcSize.width),
.y = int32_t(srcSize.height),
.z = 1,
};
blitRegion.dstSubresource.aspectMask=VK_IMAGE_ASPECT_COLOR_BIT;
blitRegion.dstSubresource.baseArrayLayer=0;
blitRegion.dstSubresource.layerCount=1;
blitRegion.dstSubresource.mipLevel=0;
blitRegion.dstOffsets[1] = {
.x = int32_t(dstSize.width),
.y = int32_t(dstSize.height),
.z = 1,
};
VkBlitImageInfo2 blitInfo{
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr
.sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2,
.srcImage = source,
.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.dstImage = destination,
.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.regionCount = 1,
.pRegions = &blitRegion,
.filter = VK_FILTER_LINEAR,
};
blitInfo.dstImage = destination;
blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
blitInfo.srcImage = source;
blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
blitInfo.filter = VK_FILTER_LINEAR;
blitInfo.regionCount = 1;
blitInfo.pRegions = &blitRegion;
vkCmdBlitImage2(cmd, &blitInfo);
}

View file

@ -3,163 +3,137 @@
VkCommandPoolCreateInfo vkinit::command_pool_create_info(uint32_t queueFamilyIndex,
VkCommandPoolCreateFlags flags /*= 0*/)
{
VkCommandPoolCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
info.pNext = nullptr;
info.queueFamilyIndex = queueFamilyIndex;
info.flags = flags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = flags,
.queueFamilyIndex = queueFamilyIndex,
};
}
VkCommandBufferAllocateInfo vkinit::command_buffer_allocate_info(
VkCommandPool pool, uint32_t count /*= 1*/)
{
VkCommandBufferAllocateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
info.pNext = nullptr;
info.commandPool = pool;
info.commandBufferCount = count;
info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
return info;
return {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = count,
};
}
VkFenceCreateInfo vkinit::fence_create_info(VkFenceCreateFlags flags /*=0*/)
{
VkFenceCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
info.pNext = nullptr;
info.flags = flags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.flags = flags
};
}
VkSemaphoreCreateInfo vkinit::semaphore_create_info(VkSemaphoreCreateFlags flags/*=0*/)
{
VkSemaphoreCreateInfo info = {};
info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
info.pNext = nullptr;
info.flags = flags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.flags = flags,
};
}
VkCommandBufferBeginInfo vkinit::command_buffer_begin_info(VkCommandBufferUsageFlags flags /*= 0*/)
{
VkCommandBufferBeginInfo info = {};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
info.pNext = nullptr;
info.pInheritanceInfo = nullptr;
info.flags = flags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = flags,
.pInheritanceInfo = nullptr,
};
}
VkImageSubresourceRange vkinit::image_subresource_range(VkImageAspectFlags aspectMask)
{
VkImageSubresourceRange subImage {};
subImage.aspectMask = aspectMask;
subImage.baseMipLevel = 0;
subImage.levelCount = VK_REMAINING_MIP_LEVELS;
subImage.baseArrayLayer = 0;
subImage.layerCount = VK_REMAINING_ARRAY_LAYERS;
return subImage;
return {
.aspectMask = aspectMask,
.baseMipLevel = 0,
.levelCount = VK_REMAINING_MIP_LEVELS,
.baseArrayLayer = 0,
.layerCount = VK_REMAINING_ARRAY_LAYERS,
};
}
VkSemaphoreSubmitInfo vkinit::semaphore_submit_info(VkPipelineStageFlags2 stageMask, VkSemaphore semaphore)
{
VkSemaphoreSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO;
submitInfo.pNext = nullptr;
submitInfo.semaphore = semaphore;
submitInfo.stageMask = stageMask;
submitInfo.deviceIndex = 0;
submitInfo.value = 1;
return submitInfo;
return {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
.semaphore = semaphore,
.value = 1,
.stageMask = stageMask,
.deviceIndex = 0,
};
}
VkCommandBufferSubmitInfo vkinit::command_buffer_submit_info(VkCommandBuffer cmd)
{
VkCommandBufferSubmitInfo info{};
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO;
info.pNext = nullptr;
info.commandBuffer = cmd;
info.deviceMask = 0;
return info;
return {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
.commandBuffer = cmd,
.deviceMask = 0,
};
}
VkSubmitInfo2 vkinit::submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo,
VkSemaphoreSubmitInfo* waitSemaphoreInfo)
{
VkSubmitInfo2 info = {};
info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2;
info.pNext = nullptr;
info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1;
info.pWaitSemaphoreInfos = waitSemaphoreInfo;
info.signalSemaphoreInfoCount = signalSemaphoreInfo == nullptr ? 0 : 1;
info.pSignalSemaphoreInfos = signalSemaphoreInfo;
info.commandBufferInfoCount = 1;
info.pCommandBufferInfos = cmd;
return info;
return {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
.waitSemaphoreInfoCount = (waitSemaphoreInfo == nullptr ? 0u : 1u),
.pWaitSemaphoreInfos = waitSemaphoreInfo,
.commandBufferInfoCount = 1,
.pCommandBufferInfos = cmd,
.signalSemaphoreInfoCount = (signalSemaphoreInfo == nullptr ? 0u : 1u),
.pSignalSemaphoreInfos = signalSemaphoreInfo,
};
}
VkImageCreateInfo vkinit::image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent)
{
VkImageCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
info.pNext = nullptr;
info.imageType = VK_IMAGE_TYPE_2D;
info.format = format;
info.extent = extent;
info.mipLevels = 1;
info.arrayLayers = 1;
info.samples = VK_SAMPLE_COUNT_1_BIT;
info.tiling = VK_IMAGE_TILING_OPTIMAL;
info.usage = usageFlags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = extent,
.mipLevels = 1,
.arrayLayers = 1,
.samples = VK_SAMPLE_COUNT_1_BIT,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = usageFlags,
};
}
VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags)
{
VkImageViewCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
info.pNext = nullptr;
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
info.image = image;
info.format = format;
info.subresourceRange.baseMipLevel = 0;
info.subresourceRange.levelCount = 1;
info.subresourceRange.baseArrayLayer = 0;
info.subresourceRange.layerCount = 1;
info.subresourceRange.aspectMask = aspectFlags;
return info;
return {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = image,
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = format,
.subresourceRange = {
.aspectMask = aspectFlags,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
}
};
}
VkRenderingAttachmentInfo vkinit::attachment_info(
VkImageView view, VkClearValue* clear ,VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/)
{
VkRenderingAttachmentInfo colorAttachment {};
colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorAttachment.pNext = nullptr;
colorAttachment.imageView = view;
colorAttachment.imageLayout = layout;
colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
VkRenderingAttachmentInfo colorAttachment{
.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO,
.imageView = view,
.imageLayout = layout,
.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,
};
if (clear) {
colorAttachment.clearValue = *clear;
@ -172,16 +146,38 @@ VkRenderingAttachmentInfo vkinit::attachment_info(
VkRenderingInfo vkinit::rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment,
VkRenderingAttachmentInfo* depthAttachment)
{
VkRenderingInfo renderInfo {};
renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderInfo.pNext = nullptr;
renderInfo.renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent };
renderInfo.layerCount = 1;
renderInfo.colorAttachmentCount = 1;
renderInfo.pColorAttachments = colorAttachment;
renderInfo.pDepthAttachment = depthAttachment;
renderInfo.pStencilAttachment = nullptr;
return renderInfo;
return {
.sType = VK_STRUCTURE_TYPE_RENDERING_INFO,
.renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent },
.layerCount = 1,
.colorAttachmentCount = 1,
.pColorAttachments = colorAttachment,
.pDepthAttachment = depthAttachment,
.pStencilAttachment = nullptr,
};
}
VkPipelineShaderStageCreateInfo vkinit::pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry) {
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = stage,
.module = shader,
.pName = entry,
};
}
VkPipelineLayoutCreateInfo vkinit::pipeline_layout_create_info()
{
return {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.setLayoutCount = 0,
.pSetLayouts = nullptr,
.pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr,
};
}

View file

@ -35,5 +35,9 @@ namespace vkinit {
VkImageView view, VkClearValue* clear ,VkImageLayout layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
VkRenderingInfo rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment);
VkPipelineShaderStageCreateInfo pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry="main");
VkPipelineLayoutCreateInfo pipeline_layout_create_info();
}

141
vk_loader.cpp Normal file
View file

@ -0,0 +1,141 @@
#include "vk_loader.h"
#include "vendor/stb_image.h"
#include <iostream>
#include "vk_engine.h"
#include "vk_initializers.h"
#include "vk_types.h"
#include <glm/gtx/quaternion.hpp>
#include <fastgltf/glm_element_traits.hpp>
#include <fastgltf/core.hpp>
#include <fastgltf/tools.hpp>
constexpr bool OverrideColors = true;
std::optional<std::vector<std::shared_ptr<MeshAsset>>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath)
{
std::println("\nLoading GLTF: {}", filePath.string());
auto data = fastgltf::GltfDataBuffer::FromPath(filePath);
if(data.error() != fastgltf::Error::None) {
std::println("Failed to load glTF: {}={}\n",
typeid(data.error()).name(),
fastgltf::to_underlying(data.error()));
return std::nullopt;
}
constexpr auto gltfOptions = fastgltf::Options::LoadExternalBuffers;
fastgltf::Asset gltf{};
fastgltf::Parser parser{};
auto load = parser.loadGltfBinary(data.get(), filePath.parent_path(), gltfOptions);
switch(load.error()) {
case fastgltf::Error::None:
gltf = std::move(load.get());
break;
case fastgltf::Error::InvalidGLB:
std::println("fastgltf says Error::InvalidGLB {}", filePath.string());
return std::nullopt;
break;
case fastgltf::Error::UnsupportedVersion:
std::println("fastgltf says Error::UnsupportedVersion {}", filePath.string());
return std::nullopt;
break;
case fastgltf::Error::InvalidPath:
std::println("fastgltf says Error::UnsupportedVersion {}",
filePath.string());
return std::nullopt;
break;
default:
std::println("Unknown fastgltf error loading {}", filePath.string());
break;
}
std::vector<std::shared_ptr<MeshAsset>> meshes;
// use the same vectors for all meshes
std::vector<uint32_t> indices;
std::vector<Vertex> vertices;
for(auto& mesh : gltf.meshes) {
MeshAsset newmesh;
newmesh.name = mesh.name;
indices.clear();
indices.clear();
for(auto&& p : mesh.primitives) {
auto& indexAccessor = gltf.accessors[p.indicesAccessor.value()];
GeoSurface newSurface {
.startIndex = (uint32_t)indices.size(),
.count = (uint32_t)indexAccessor.count,
};
size_t initial_vtx = vertices.size();
// load indices
{
indices.reserve(indices.size() + indexAccessor.count);
fastgltf::iterateAccessor<std::uint32_t>(
gltf, indexAccessor, [&](std::uint32_t idx) {
indices.push_back(idx + initial_vtx);
});
}
// load vertex positions
{
fastgltf::Accessor& posAccessor = gltf.accessors[
p.findAttribute("POSITIONS")->accessorIndex];
vertices.resize(vertices.size() + posAccessor.count);
fastgltf::iterateAccessorWithIndex<glm::vec3>(
gltf, posAccessor, [&](glm::vec3 v, size_t index)
{
vertices[initial_vtx + index] = {
.position = v,
.uv_x = 0,
.normal = {1, 0, 0},
.uv_y = 0,
.color = glm::vec4{1.0f},
};
});
}
// load vertex normals
auto normals = p.findAttribute("NORMA:");
if(normals != p.attributes.end()) {
fastgltf::iterateAccessorWithIndex<glm::vec3>(
gltf, gltf.accessors[(*normals).accessorIndex],
[&](glm::vec3 v, size_t index)
{
vertices[initial_vtx + index].normal = v;
});
}
auto uv = p.findAttribute("TEXCOORD_0");
if(uv != p.attributes.end()) {
fastgltf::iterateAccessorWithIndex<glm::vec2>(
gltf, gltf.accessors[(*uv).accessorIndex],
[&](glm::vec2 v, size_t index) {
vertices[initial_vtx + index].uv_x = v.x;
vertices[initial_vtx + index].uv_y = v.y;
});
}
if(OverrideColors) {
for(Vertex& vtx : vertices) {
vtx.color = glm::vec4(vtx.normal, 1.0f);
}
}
newmesh.meshBuffers = engine->uploadMesh(indices, vertices);
meshes.emplace_back(std::make_shared<MeshAsset>(std::move(newmesh)));
}
}
return meshes;
}

22
vk_loader.h Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <vk_types.h>
#include <unordered_map>
#include <filesystem>
struct GeoSurface {
uint32_t startIndex;
uint32_t count;
};
struct MeshAsset {
std::string name;
std::vector<GeoSurface> surfaces;
GPUMeshBuffers meshBuffers;
};
class VulkanEngine;
std::optional<std::vector<std::shared_ptr<MeshAsset>>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath);

View file

@ -23,13 +23,13 @@ bool vkutil::load_shader_module(const char* filePath,
file.close();
VkShaderModuleCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
createInfo.pNext = nullptr;
VkShaderModuleCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = nullptr,
// codeSize has to be in bytes
createInfo.codeSize = buffer.size() * sizeof(uint32_t);
createInfo.pCode = buffer.data();
.codeSize = buffer.size() * sizeof(uint32_t),
.pCode = buffer.data(),
};
VkShaderModule shaderModule;
if(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
@ -40,3 +40,165 @@ bool vkutil::load_shader_module(const char* filePath,
*outShaderModule = shaderModule;
return true;
}
void PipelineBuilder::clear() {
// clear all of the structs we need back to 0 with their correct stype
_inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
_rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
_colorBlendAttachment = {};
_multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
_pipelineLayout = {};
_depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
_renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO };
_shaderStages.clear();
}
VkPipeline PipelineBuilder::build_pipeline(VkDevice device)
{
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1,
};
VkPipelineColorBlendStateCreateInfo colorBlending{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.logicOpEnable = VK_FALSE,
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = 1,
.pAttachments = &_colorBlendAttachment,
};
VkPipelineVertexInputStateCreateInfo _vertexInputInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO
};
VkDynamicState state[] = {
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = 2,
// ZED: we don't need to do this? state is also &state[0]
.pDynamicStates = &state[0],
};
// build the actual pipeline
VkGraphicsPipelineCreateInfo pipelineInfo = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &_renderInfo,
.stageCount = (uint32_t)_shaderStages.size(),
.pStages = _shaderStages.data(),
.pVertexInputState = &_vertexInputInfo,
.pInputAssemblyState = &_inputAssembly,
.pViewportState = &viewportState,
.pRasterizationState = &_rasterizer,
.pMultisampleState = &_multisampling,
.pDepthStencilState = &_depthStencil,
.pColorBlendState = &colorBlending,
.pDynamicState = &dynamicInfo,
.layout = _pipelineLayout,
};
// its easy to error out on create graphics pipeline, so we handle it a bit
// better than the common VK_CHECK case
VkPipeline newPipeline;
auto works = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &newPipeline);
if(works != VK_SUCCESS) {
std::println("failed to create pipeline");
return VK_NULL_HANDLE;
} else {
return newPipeline;
}
}
void PipelineBuilder::set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader)
{
_shaderStages.clear();
_shaderStages.push_back(
vkinit::pipeline_shader_stage_create_info(
VK_SHADER_STAGE_VERTEX_BIT,
vertexShader));
_shaderStages.push_back(
vkinit::pipeline_shader_stage_create_info(
VK_SHADER_STAGE_FRAGMENT_BIT,
fragmentShader));
}
void PipelineBuilder::set_input_topology(VkPrimitiveTopology topology)
{
_inputAssembly.topology = topology;
_inputAssembly.primitiveRestartEnable = VK_FALSE;
}
void PipelineBuilder::set_polygon_mode(VkPolygonMode mode)
{
_rasterizer.polygonMode = mode;
_rasterizer.lineWidth = 1.0f;
}
void PipelineBuilder::set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace)
{
_rasterizer.cullMode = cullMode;
_rasterizer.frontFace = frontFace;
}
void PipelineBuilder::set_multisampling_none()
{
_multisampling.sampleShadingEnable = VK_FALSE;
_multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
_multisampling.minSampleShading = 1.0f;
_multisampling.pSampleMask = nullptr;
_multisampling.alphaToCoverageEnable = VK_FALSE;
_multisampling.alphaToOneEnable = VK_FALSE;
}
void PipelineBuilder::disable_blending()
{
_colorBlendAttachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT
| VK_COLOR_COMPONENT_G_BIT
| VK_COLOR_COMPONENT_B_BIT
| VK_COLOR_COMPONENT_A_BIT;
_colorBlendAttachment.blendEnable = VK_FALSE;
}
void PipelineBuilder::set_color_attachment_format(VkFormat format)
{
_colorAttachmentFormat = format;
_renderInfo.colorAttachmentCount = 1;
_renderInfo.pColorAttachmentFormats = &_colorAttachmentFormat;
}
void PipelineBuilder::set_depth_format(VkFormat format)
{
_renderInfo.depthAttachmentFormat = format;
}
void PipelineBuilder::disable_depthtest()
{
_depthStencil.depthTestEnable = VK_FALSE;
_depthStencil.depthWriteEnable = VK_FALSE;
_depthStencil.depthCompareOp = VK_COMPARE_OP_NEVER;
_depthStencil.depthBoundsTestEnable = VK_FALSE;
_depthStencil.stencilTestEnable = VK_FALSE;
_depthStencil.front = {};
_depthStencil.back = {};
_depthStencil.minDepthBounds = 0.0f;
_depthStencil.maxDepthBounds = 1.0f;
}

View file

@ -2,6 +2,37 @@
#include "vk_types.h"
class PipelineBuilder {
public:
std::vector<VkPipelineShaderStageCreateInfo> _shaderStages;
VkPipelineInputAssemblyStateCreateInfo _inputAssembly;
VkPipelineRasterizationStateCreateInfo _rasterizer;
VkPipelineColorBlendAttachmentState _colorBlendAttachment;
VkPipelineMultisampleStateCreateInfo _multisampling;
VkPipelineLayout _pipelineLayout;
VkPipelineDepthStencilStateCreateInfo _depthStencil;
VkPipelineRenderingCreateInfo _renderInfo;
VkFormat _colorAttachmentFormat;
PipelineBuilder() {
clear();
}
void clear();
VkPipeline build_pipeline(VkDevice device);
void set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader);
void set_input_topology(VkPrimitiveTopology topology);
void set_polygon_mode(VkPolygonMode mode);
void set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace);
void set_multisampling_none();
void disable_blending();
void set_color_attachment_format(VkFormat format);
void set_depth_format(VkFormat format);
void disable_depthtest();
};
namespace vkutil {
bool load_shader_module(const char* filePath,
VkDevice device,

View file

@ -18,6 +18,7 @@
#include <print>
#define GLM_ENABLE_EXPERIMENTAL 1
#include <glm/mat4x4.hpp>
#include <glm/vec4.hpp>
@ -30,6 +31,14 @@
} while (0)
constexpr unsigned int FRAME_OVERLAP=2;
struct AllocatedBuffer {
VkBuffer buffer;
VmaAllocation allocation;
VmaAllocationInfo info;
};
struct AllocatedImage {
VkImage image;
VkImageView imageView;
@ -37,3 +46,38 @@ struct AllocatedImage {
VkExtent3D imageExtent;
VkFormat imageFormat;
};
struct ComputePushConstants {
glm::vec4 data1;
glm::vec4 data2;
glm::vec4 data3;
glm::vec4 data4;
};
struct ComputeEffect {
const char *name;
VkPipeline pipeline;
VkPipelineLayout layout;
ComputePushConstants data;
};
struct Vertex {
glm::vec3 position;
float uv_x;
glm::vec3 normal;
float uv_y;
glm::vec4 color;
};
struct GPUMeshBuffers {
AllocatedBuffer indexBuffer;
AllocatedBuffer vertexBuffer;
VkDeviceAddress vertexBufferAddress;
};
struct GPUDrawPushConstants {
glm::mat4 worldMatrix;
VkDeviceAddress vertexBuffer;
};

9
wraps/fastgltf.wrap Normal file
View file

@ -0,0 +1,9 @@
[wrap-git]
directory=fastgltf
url=https://github.com/spnda/fastgltf.git
revision=v0.9.0
depth=1
method=cmake
[provide]
fastgltf = fastgltf_dep

13
wraps/simdjson.wrap Normal file
View file

@ -0,0 +1,13 @@
[wrap-file]
directory = simdjson-3.3.0
source_url = https://github.com/simdjson/simdjson/archive/refs/tags/v3.3.0.tar.gz
source_filename = simdjson-3.3.0.tar.gz
source_hash = a8c9feff2f19c3ff281d42f0b6b4b18f02236513b99229756fa9a1b14787a58a
patch_filename = simdjson_3.3.0-2_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/simdjson_3.3.0-2/get_patch
patch_hash = 3c39f8a5abac17732b9599a416e6edb09f5f987bcf4e8b46808dbe88040eb40f
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/simdjson_3.3.0-2/simdjson-3.3.0.tar.gz
wrapdb_version = 3.3.0-2
[provide]
simdjson = simdjson_dep