diff options
| -rw-r--r-- | .clang-format | 9 | ||||
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | api-proto.txt | 264 | ||||
| -rw-r--r-- | api-test/CMakeLists.txt | 15 | ||||
| -rw-r--r-- | api-test/main.cpp | 77 | ||||
| -rw-r--r-- | api-test/vk-command-buffer.cpp | 25 | ||||
| -rw-r--r-- | api-test/vk-device.cpp | 519 | ||||
| -rw-r--r-- | include/api-test/buffer.hpp | 25 | ||||
| -rw-r--r-- | include/api-test/freelist.hpp | 32 | ||||
| -rw-r--r-- | include/api-test/handle.hpp | 8 | ||||
| -rw-r--r-- | include/api-test/resource-manager.hpp | 22 | ||||
| -rw-r--r-- | include/api-test/texture.hpp | 29 | ||||
| -rw-r--r-- | include/api-test/vk-command-buffer.hpp | 14 | ||||
| -rw-r--r-- | include/api-test/vk-device.hpp | 128 | ||||
| -rw-r--r-- | include/resource-manager.hpp | 6 | ||||
| -rw-r--r-- | include/window.hpp | 2 | ||||
| -rw-r--r-- | src/resource-manager.cpp | 2 | ||||
| -rw-r--r-- | src/window.cpp | 9 |
18 files changed, 1182 insertions, 6 deletions
diff --git a/.clang-format b/.clang-format index b5daee6..ffb3010 100644 --- a/.clang-format +++ b/.clang-format @@ -1,3 +1,12 @@ +AccessModifierOffset: 0 +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: true + AlignCompound: true +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: true +AlignOperands: Align AlignEscapedNewlines: Right AlignTrailingComments: true ColumnLimit: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d62555..429fe21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,6 @@ project(path-tracer VERSION 1.0 LANGUAGES CXX) find_package(imgui CONFIG REQUIRED) find_package(nlohmann_json CONFIG REQUIRED) find_package(Vulkan REQUIRED) -find_package(Threads REQUIRED) if(UNIX AND NOT APPLE) find_package(X11 REQUIRED) @@ -33,4 +32,5 @@ endif() # endif() add_subdirectory(src) +add_subdirectory(api-test) add_subdirectory(shaders) diff --git a/api-proto.txt b/api-proto.txt new file mode 100644 index 0000000..751f390 --- /dev/null +++ b/api-proto.txt @@ -0,0 +1,264 @@ +enum CMD_BUF_TYPE { + GRAPHICS, + COPY, + ASYNC_COMPUTE, +} + +class command_buffer { + void start(); + void stop(); + + VkCommandBuffer handle; + queue owner; +} + +class graphics_command_buffer : command_buffer { + void image_barrier(handle tex, State new_state) { + texture = rm::get_texture(tex); + + cmd.barrier(texture.img.device_image, texture.current_state, new_state); + + texture.current_state = new_state; + } +} + +class device_swapchain { + VkSurfaceKHR surface; + VkFormat surface_format; + VkExtent extent; + VkSwapchain swapchain; +} + +class device_buffer { + VkBuffer handle; + VmaAllocation allocation; + VkDeviceAddr gpu_address; +} + +class device_image { + VkImage handle; + VmaAllocation allocation; + array<uint, max_usage> bindless_id; // TODO: Handle this in a higher level ? + + VkImageView whole_image_view; + vector<VkImageView> mips_view; + + const uint invalid_id = -1; +} + +class device_command_buffer { + CMD_BUF_TYPE buf_type = CMD_BUF_TYPE::MAX; + VkCommandBuffer handle; +} + +class global_descriptor_set { + VkDescritptorSetLayout layout; + VkDescriptorSet set; +}; + +class device { + device_image create_image(TextureDesc desc) { + img {}; + img.handle = vmaCreateImage(&img.alloc); + } + + device_buffer create_buffer(BufferDesc desc); + + device_command_buffer allocate_command_buffer(uint32_t count); + + submit(span<device_command_buffer> cmd_buffers, semaphore) + + wait(fence); + wait_idle(); + + private: + + create_surface(); + create_swapchain(); + + device_swapchain sc; + + array<device_image> sc_images; + array<queue, CMD_BUF_TYPE::MAX> queues; + + global_descriptor_set global_set; + // bindless + // set 0 global, textures, samplers + // set 1 materials buffer addr, camera, etc + // set 2 per mesh vertex attributes buffers addr + // set 2 per instance (transforms, etc) + pipeline_layout compute; // set 0 & 1 + pipeline_layout graphics; // set 0, 1, 2 +} + +class renderer { + renderer() { + device_images = device::get_swapchain_images(); + + for (device_images) { + sc_images.push(ressourcemanager::create_texture(desc, images[i])) + } + } + + void start_frame() { + vframe_index++; + device::wait(finish); + + img_index = device::get_image(sc) + cur_image = images[img_index]; + } + void finish_frame() { + device::submit(to_submit, wait_submit); + device::present(wait_submit, finish); + } + + command_buffer start_record() { + auto buf = frames[current].command_buffers.pop(); + buf.start(); + device::submit(span(command_buffers[vframe_index][0...last_cmd_buf_index[vframe_index]]), wait_submit); + device::present(); + } + + void stop_record(command_buffer buf) { + buf.stop(); + auto buf = frames[current][buf_type][++last_cmd_buf_index[vframe_index]]; + } + + private: + + vector<handle> sc_images; + + // Virtual frames stuff + array<semaphore, vframes_count> wait_image; + array<semaphore, vframes_count> wait_present; + array<fence, vframes_count> finish; + array<fence, vframes_count> command_pool; + // One per queue type + array<array<command_buffer, vframe_max_buf>, vframes_count> command_buffers; + + array<uint, vframes_count> last_cmd_buf_index = { 0 }; + + uint vframe_index; + constexpr uint vframe_max_buf = 10; + constexpr uint vframes_count = 3; +} + +class ressourcemanager { + handle create_texture(TextureDesc desc) { + new_image = device::create_image(desc); + + return textures.push({ + .desc = desc, + .img = new_image, + }) + } + + handle create_texture(TextureDesc desc, device_image img) { + return textures.push({ + .desc = desc, + .img = img, + }) + } + + handle create_pipeline(PipelineDesc desc) + + const Texture& get_texture(handle texture_handle); + +private: + FreeList textures; +} + +class PipelineDesc { + // TODO: Where to store views ? + span<Texture> color_textures; + Texture depth_texture; +} + +class TextureDesc { + Vector size; + UsageFlag usages; + Format format; +} + +class BufferDesc { + uint size; + UsageFlag usages; +} + +class Texture { + const TextureDesc desc; + const device_image img; + Layout current_layout = initial_layout; +} + +class Buffer { + const BufferDesc desc; + const device_buffer buffer; +} + +class mesh { + array<attribute_buffer> attributes; + buffer index; +} + +class mesh_instance { + handle mesh; + mat4 transform; +} + +class renderpass { // shadow example + renderpass() { + shadow_tex = rm::create_texture({ + .size = { 1024, 1024 }, + .usages = DEPTH_STENCIL_ATTACHMENT, + .format = VK_FORMAT_D24, + }); + + pso = rm::create_pipeline({ + .color = null, + .depth = scene_depth, // contains format + }) + } + + render() { + handle meshes[] = scene.get_meshes(FILTER_NONE); + buf = renderer::start_record(); + buf.start_renderpass({ + .color_attachments + .depth_attachment = shadow_tex, + }); + + buf.bind_pipeline(pso); + + foreach(mesh : meshes) { + buf.draw_index(mesh); + } + + buf.stop_renderpass(); + } + + std::vector<Texture> color_attachments; + + shadow_tex; + pipeline pso; +} + +renderer::add_render_pass() + +TODOS : +1. implement : + * image loading + * resizing + * mesh drawing + +IDEAS : +1. Manage device resources with handles instead of having immutable ones in high level resources + PROS: + * Allows to manage (swap, create, delete, invalide) device resources easily + CONS: + * Can loose track of a resource easily + +RESOURCES : +* SEED Halcyon presentation https://www.ea.com/seed/news/syysgraph-2018-modern-graphics-abstractions-real-time-ray-tracing +* Cryengine master class https://www.youtube.com/watch?v=34S3onEr3r8 +* Nicebyte tweet api https://twitter.com/nice_byte/status/1579187394880090112 diff --git a/api-test/CMakeLists.txt b/api-test/CMakeLists.txt new file mode 100644 index 0000000..3b39ea3 --- /dev/null +++ b/api-test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable( + api-test + main.cpp + vk-device.cpp + ${CMAKE_SOURCE_DIR}/src/window.cpp + ${CMAKE_SOURCE_DIR}/src/utils.cpp + ${CMAKE_SOURCE_DIR}/src/vulkan-loader.cpp +) + +target_compile_definitions(api-test PUBLIC WINDOWS VK_NO_PROTOTYPES VK_USE_PLATFORM_WIN32_KHR) + +target_include_directories(api-test SYSTEM PUBLIC ${Vulkan_INCLUDE_DIRS}) +target_include_directories(api-test SYSTEM PUBLIC ${CMAKE_SOURCE_DIR}/thirdparty) +target_include_directories(api-test PUBLIC ${CMAKE_SOURCE_DIR}/include) +target_include_directories(api-test PUBLIC ${CMAKE_SOURCE_DIR}/include/api-test) diff --git a/api-test/main.cpp b/api-test/main.cpp new file mode 100644 index 0000000..61db6be --- /dev/null +++ b/api-test/main.cpp @@ -0,0 +1,77 @@ +#include <cassert> +#include <cstdio> +#include <vk_mem_alloc.h> + +#include "window.hpp" +#include "vk-device.hpp" +// #include "resource-manager.hpp" + +#define ENABLE_RENDERDOC + +#ifdef ENABLE_RENDERDOC +#include <renderdoc.h> + +RENDERDOC_API_1_4_1 *rdoc_api = nullptr; +#endif + +int main() { + +#ifdef ENABLE_RENDERDOC + if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) { + auto RENDERDOC_GetAPI = + (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI"); + int ret = + RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api); + assert(ret == 1); + } +#endif + + window wnd { 1280U, 720U }; + +#ifdef ENABLE_RENDERDOC + if(rdoc_api != nullptr) rdoc_api->StartFrameCapture(nullptr, nullptr); +#endif + + vkdevice device { wnd }; + + auto gpu_texture = device.create_texture({ + .width = 1024U, + .height = 1024U, + .usages = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .format = VK_FORMAT_R8G8B8A8_UNORM, + .type = VK_IMAGE_TYPE_2D, + }); + + auto gpu_buffer = device.get_buffer(device.create_buffer({ + .size = 1024U * sizeof(uint32_t), + .usages = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + .memory_properties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .memory_usage = VMA_MEMORY_USAGE_GPU_ONLY, + })); + + auto staging_buffer = device.get_buffer(device.create_buffer({ + .size = 1024U * sizeof(uint32_t), + .usages = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .memory_properties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + .memory_usage = VMA_MEMORY_USAGE_CPU_TO_GPU, + })); + + uint64_t t = 7; + (*(uint64_t*)staging_buffer.mapped_ptr) = t; + + auto command_buffer = device.get_command_buffer(); + + command_buffer.copy(staging_buffer, gpu_texture); + + device.submit(command_buffer); + +#ifdef ENABLE_RENDERDOC + if(rdoc_api != nullptr) rdoc_api->EndFrameCapture(nullptr, nullptr); +#endif + + while(wnd.isOpen) { + wnd.poll_events(); + } + + return 0; +} diff --git a/api-test/vk-command-buffer.cpp b/api-test/vk-command-buffer.cpp new file mode 100644 index 0000000..b9eed19 --- /dev/null +++ b/api-test/vk-command-buffer.cpp @@ -0,0 +1,25 @@ +#include "vk-command-buffer.hpp" + +#include <cassert> + +#include "vulkan-loader.hpp" + +#ifdef _DEBUG +#define VKCHECK(result) assert(result == VK_SUCCESS); +#else +#define VKCHECK(result) result; +#endif + +void command_buffer::start() { + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.pNext = nullptr; + begin_info.flags = 0U; + begin_info.pInheritanceInfo = nullptr; + + VKCHECK(vkBeginCommandBuffer(handle, &begin_info)) +} + +void command_buffer::stop() { + VKCHECK(vkEndCommandBuffer(handle)) +} diff --git a/api-test/vk-device.cpp b/api-test/vk-device.cpp new file mode 100644 index 0000000..4b7c0d0 --- /dev/null +++ b/api-test/vk-device.cpp @@ -0,0 +1,519 @@ +#include "vk-device.hpp" +#include "color.hpp" + +#include <cassert> +#include <iostream> +#include <thread> +#include <vector> + +#define VMA_IMPLEMENTATION +#include <vk_mem_alloc.h> + +#include "vulkan-loader.hpp" +#include "window.hpp" + +#ifdef _DEBUG +#define VKCHECK(result) assert(result == VK_SUCCESS); +#else +#define VKCHECK(result) result; +#endif + +VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity, + VkDebugUtilsMessageTypeFlagsEXT type, + const VkDebugUtilsMessengerCallbackDataEXT* data, + void*) { + const char* severity_cstr = nullptr; + switch (severity) { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: + severity_cstr = "VERBOSE"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: + severity_cstr = "INFO"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: + severity_cstr = "WARNING"; + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: + severity_cstr = "ERROR"; + break; + default: + break; + } + + const char* type_cstr = nullptr; + switch (type) { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: + type_cstr = "GENERAL"; + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: + type_cstr = "VALIDATION"; + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: + type_cstr = "PERFORMANCE"; + break; + default: + break; + } + + std::cerr << "[" << severity_cstr << "_" << type_cstr << "]: " << data->pMessage << std::endl; + return VK_FALSE; +} + +vkdevice::vkdevice(const window& window) + : queues{ + { + .usages = VK_QUEUE_GRAPHICS_BIT, + }, + // { .usages = VK_QUEUE_TRANSFER_BIT, }, + } { + load_vulkan(); + create_instance(); + +#ifdef _DEBUG + create_debug_layer_callback(); +#endif // _DEBUG + + create_device(); + + create_memory_allocator(); + + create_surface(window); + + create_swapchain(); +} + +handle<device_texture> vkdevice::create_texture(const texture_desc& desc) { + device_texture device_texture = {}; + device_texture.desc = desc; + + assert(desc.mips <= texture_desc::max_mips); + + VkImageCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.imageType = desc.type; + create_info.format = desc.format; + create_info.extent = { + .width = desc.width, + .height = desc.height, + .depth = desc.depth + }; + create_info.mipLevels = desc.mips; + create_info.arrayLayers = 1; + create_info.samples = VK_SAMPLE_COUNT_1_BIT; + create_info.tiling = VK_IMAGE_TILING_OPTIMAL; + create_info.usage = desc.usages; + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = nullptr; + create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = 0U; + alloc_create_info.usage = VMA_MEMORY_USAGE_GPU_ONLY; + alloc_create_info.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + alloc_create_info.preferredFlags = 0U; + alloc_create_info.memoryTypeBits = 0U; + alloc_create_info.pool = nullptr; + alloc_create_info.pUserData = nullptr; + + VKCHECK(vmaCreateImage(gpu_allocator, &create_info, &alloc_create_info, &device_texture.handle, &device_texture.alloc, nullptr)) + + if ((desc.usages & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0U || + (desc.usages & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0U) { + create_views(device_texture); + } + + return { textures.add(device_texture) }; +} + +handle<device_buffer> vkdevice::create_buffer(const buffer_desc& desc) { + device_buffer device_buffer = {}; + device_buffer.desc = desc; + + VkBufferCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.size = desc.size; + create_info.usage = desc.usages; + create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0; + create_info.pQueueFamilyIndices = nullptr; + + VmaAllocatorCreateFlags alloc_create_flags = + desc.memory_usage != VMA_MEMORY_USAGE_GPU_ONLY ? + VMA_ALLOCATION_CREATE_MAPPED_BIT : 0U; + + VmaAllocationCreateInfo alloc_create_info = {}; + alloc_create_info.flags = alloc_create_flags; + alloc_create_info.usage = static_cast<VmaMemoryUsage>(desc.memory_usage); + alloc_create_info.requiredFlags = desc.memory_properties; + alloc_create_info.preferredFlags = 0U; + alloc_create_info.memoryTypeBits = 0U; + alloc_create_info.pool = nullptr; + alloc_create_info.pUserData = nullptr; + + VmaAllocationInfo alloc_info = {}; + + VKCHECK(vmaCreateBuffer(gpu_allocator, &create_info, &alloc_create_info, &device_buffer.handle, &device_buffer.alloc, &alloc_info)) + + device_buffer.mapped_ptr = alloc_info.pMappedData; + + return { buffers.add(device_buffer) }; +} + +void vkdevice::submit(std::span<graphics_command_buffer> command_buffers) { + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; + submit_info.waitSemaphoreCount; + submit_info.pWaitSemaphores; + submit_info.pWaitDstStageMask; + submit_info.commandBufferCount; + submit_info.pCommandBuffers; + submit_info.signalSemaphoreCount; + submit_info.pSignalSemaphores; + + vkQueueSubmit() +} + + +// private + +void vkdevice::create_views(device_texture& texture) { + VkImageAspectFlags aspect_mask = ((texture.desc.usages & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0U) + ? VK_IMAGE_ASPECT_COLOR_BIT + : VK_IMAGE_ASPECT_DEPTH_BIT; + VkImageViewCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.image = texture.handle; + create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; + create_info.format = texture.desc.format; + create_info.components = { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_G, + .b = VK_COMPONENT_SWIZZLE_B, + .a = VK_COMPONENT_SWIZZLE_A, + }; + create_info.subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, // TODO: Choose either color or depth based on format ? + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + for (auto mip_index{ 0 }; mip_index < texture.desc.mips; mip_index++) { + create_info.subresourceRange.baseMipLevel = mip_index; + VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.mips_views[mip_index])) + } + + create_info.subresourceRange.baseMipLevel = 0; + create_info.subresourceRange.levelCount = texture.desc.mips; + VKCHECK(vkCreateImageView(device, &create_info, nullptr, &texture.whole_view)) +} + +void vkdevice::create_surface(const window& window) { + VkWin32SurfaceCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.hinstance = GetModuleHandle(nullptr); + create_info.hwnd = window.handle; + + VKCHECK(vkCreateWin32SurfaceKHR(instance, &create_info, nullptr, &surface)) +} + +void vkdevice::create_swapchain() { + VkBool32 supported = VK_FALSE; + for (auto index{ 0U }; index < QueueTypes::MAX; index++) { + auto& queue = queues[index]; + VKCHECK(vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue.index, surface, &supported)) + + assert(supported); + } + + VkSurfaceCapabilitiesKHR surface_capabilities = {}; + VKCHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surface_capabilities)) + + VkExtent2D extent = surface_capabilities.currentExtent; + + if (extent.width < surface_capabilities.minImageExtent.width) { + extent.width = surface_capabilities.minImageExtent.width; + } else if (extent.width > surface_capabilities.maxImageExtent.width) { + extent.width = surface_capabilities.maxImageExtent.width; + } + + if (extent.height < surface_capabilities.minImageExtent.height) { + extent.height = surface_capabilities.minImageExtent.height; + } else if (extent.height > surface_capabilities.maxImageExtent.height) { + extent.height = surface_capabilities.maxImageExtent.height; + } + + auto supported_formats_count = 0U; + VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, VK_NULL_HANDLE)) + + std::vector<VkSurfaceFormatKHR> supported_formats{ supported_formats_count }; + VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &supported_formats_count, supported_formats.data())) + + + auto supported_present_modes_count = 0U; + VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, VK_NULL_HANDLE)) + + std::vector<VkPresentModeKHR> supported_present_modes{ supported_present_modes_count }; + + VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &supported_present_modes_count, supported_present_modes.data())) + + VkSwapchainCreateInfoKHR create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.surface = surface; + create_info.minImageCount = surface_capabilities.minImageCount; + create_info.imageFormat = supported_formats[0].format; + create_info.imageColorSpace = supported_formats[0].colorSpace; + create_info.imageExtent = extent; + create_info.imageArrayLayers = 1U; + create_info.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; + create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + create_info.queueFamilyIndexCount = 0U; + create_info.pQueueFamilyIndices = nullptr; + create_info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + create_info.presentMode = supported_present_modes[0]; + create_info.clipped = VK_TRUE; + create_info.oldSwapchain = nullptr; + + VKCHECK(vkCreateSwapchainKHR(device, &create_info, nullptr, &swapchain)) +} + +// ----------------- vkadapter ----------------- + +void vkdevice::create_instance() { + VkApplicationInfo app_info = {}; + app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + app_info.pApplicationName = "path-tracer"; + app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.pEngineName = "gpu-path-tracer"; + app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0); + app_info.apiVersion = VK_API_VERSION_1_3; + + constexpr const char* instance_layers[] = { + "VK_LAYER_KHRONOS_validation", + }; + + constexpr const char* instance_extensions[] = { + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + "VK_KHR_surface", + "VK_KHR_win32_surface" + }; + + VkInstanceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.pApplicationInfo = &app_info; + create_info.enabledLayerCount = sizeof(instance_layers) / sizeof(instance_layers[0]); + create_info.ppEnabledLayerNames = instance_layers; + create_info.enabledExtensionCount = sizeof(instance_extensions) / sizeof(instance_extensions[0]); + create_info.ppEnabledExtensionNames = instance_extensions; + + VKCHECK(vkCreateInstance(&create_info, nullptr, &instance)) + + load_instance_functions(instance); +} + +void vkdevice::create_device() { + pick_physical_device(); + + VkDeviceQueueCreateInfo queue_create_infos[QueueTypes::MAX] = {}; + float queue_priority = 1.f; + + for (auto index{ 0U }; index < QueueTypes::MAX; index++) { + auto& queue = queues[index]; + auto queue_index = pick_queue(queue.usages); + + queue_create_infos[index].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_infos[index].pNext = nullptr; + queue_create_infos[index].flags = 0U; + queue_create_infos[index].queueFamilyIndex = 0; + queue_create_infos[index].queueCount = 1; + queue_create_infos[index].pQueuePriorities = &queue_priority; + + queue.index = queue_index; + } + + constexpr const char* device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; + + VkPhysicalDeviceVulkan12Features physical_device_12_features = {}; + physical_device_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + physical_device_12_features.pNext = VK_NULL_HANDLE; + physical_device_12_features.bufferDeviceAddress = VK_TRUE; + physical_device_12_features.runtimeDescriptorArray = VK_TRUE; + physical_device_12_features.shaderStorageImageArrayNonUniformIndexing = VK_TRUE; + physical_device_12_features.shaderSampledImageArrayNonUniformIndexing = VK_TRUE; + physical_device_12_features.descriptorBindingPartiallyBound = VK_TRUE; + physical_device_12_features.descriptorBindingPartiallyBound = VK_TRUE; + physical_device_12_features.descriptorBindingUpdateUnusedWhilePending = VK_TRUE; + physical_device_12_features.imagelessFramebuffer = VK_TRUE; + + VkPhysicalDeviceVulkan13Features physical_device_13_features = {}; + physical_device_13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + physical_device_13_features.pNext = &physical_device_12_features; + physical_device_13_features.synchronization2 = VK_TRUE; + physical_device_13_features.dynamicRendering = VK_TRUE; + + VkDeviceCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + create_info.pNext = &physical_device_13_features; + create_info.flags = 0U; + create_info.queueCreateInfoCount = QueueTypes::MAX; + create_info.pQueueCreateInfos = queue_create_infos; + create_info.enabledLayerCount = 0; + create_info.ppEnabledLayerNames = nullptr; + create_info.enabledExtensionCount = sizeof(device_extensions) / sizeof(device_extensions[0]); + create_info.ppEnabledExtensionNames = device_extensions; + create_info.pEnabledFeatures = nullptr; + + VKCHECK(vkCreateDevice(physical_device, &create_info, nullptr, &device)) + + load_device_functions(device); + + for (auto& queue : queues) { + vkGetDeviceQueue(device, queue.index, 0, &queue.handle); + } + + create_command_pools(); +} + +void vkdevice::create_memory_allocator() { + VmaAllocatorCreateInfo create_info = {}; + create_info.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + create_info.physicalDevice = physical_device; + create_info.device = device; + create_info.instance = instance; + create_info.vulkanApiVersion = VK_API_VERSION_1_2; + + VKCHECK(vmaCreateAllocator(&create_info, &gpu_allocator)) +} + +void vkdevice::create_command_pools() { + VkCommandPoolCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + create_info.pNext = nullptr; + create_info.flags = 0U; + + for (auto& queue : queues) { + create_info.queueFamilyIndex = queue.index; + + VKCHECK(vkCreateCommandPool(device, &create_info, nullptr, &queue.command_pool)) + } +} + +uint32_t vkdevice::pick_queue(VkQueueFlags queue_usages) const { + auto queue_properties_count = 0U; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, nullptr); + + std::vector<VkQueueFamilyProperties> queue_properties{ queue_properties_count }; + vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_properties_count, queue_properties.data()); + + for (auto properties_index{ 0U }; properties_index < queue_properties_count; properties_index++) { + if ((queue_properties[properties_index].queueFlags & queue_usages) == queue_usages) { + return properties_index; + } + } + + assert(true); + return UINT_MAX; +} + +void vkdevice::pick_physical_device() { + auto physical_devices_count = 0U; + vkEnumeratePhysicalDevices(instance, &physical_devices_count, nullptr); + + assert(physical_devices_count > 0); + + std::vector<VkPhysicalDevice> physical_devices{ physical_devices_count }; + vkEnumeratePhysicalDevices(instance, &physical_devices_count, physical_devices.data()); + + for (auto& physical_dev : physical_devices) { + if (device_support_features(physical_dev)) { + physical_device = physical_dev; + return; + } + } + + assert(true); +} + +bool vkdevice::device_support_features(VkPhysicalDevice physical_dev) { + VkPhysicalDeviceProperties physical_device_properties = {}; + + vkGetPhysicalDeviceProperties(physical_dev, &physical_device_properties); + + // Vulkan 1.2 physical device features support + { + VkPhysicalDeviceVulkan12Features vulkan_12_features = {}; + vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + + VkPhysicalDeviceFeatures2 physical_device_features = {}; + physical_device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + physical_device_features.pNext = &vulkan_12_features; + + vkGetPhysicalDeviceFeatures2(physical_dev, &physical_device_features); + + // clang-format off + if (vulkan_12_features.bufferDeviceAddress == VK_FALSE || + vulkan_12_features.runtimeDescriptorArray == VK_FALSE || + vulkan_12_features.shaderStorageImageArrayNonUniformIndexing == VK_FALSE || + vulkan_12_features.shaderSampledImageArrayNonUniformIndexing == VK_FALSE || + vulkan_12_features.descriptorBindingPartiallyBound == VK_FALSE || + vulkan_12_features.descriptorBindingPartiallyBound == VK_FALSE || + vulkan_12_features.descriptorBindingUpdateUnusedWhilePending == VK_FALSE || + vulkan_12_features.imagelessFramebuffer == VK_FALSE) { + return false; + } + // clang-format on + } + + // Vulkan 1.3 physical device features support + { + VkPhysicalDeviceVulkan13Features vulkan_13_features = {}; + vulkan_13_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + + VkPhysicalDeviceFeatures2 physical_device_features = {}; + physical_device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + physical_device_features.pNext = &vulkan_13_features; + + vkGetPhysicalDeviceFeatures2(physical_dev, &physical_device_features); + + if (vulkan_13_features.synchronization2 == VK_FALSE || + vulkan_13_features.dynamicRendering == VK_FALSE) { + return false; + } + } + + return true; +} + +void vkdevice::create_debug_layer_callback() { + VkDebugUtilsMessengerCreateInfoEXT create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + create_info.pNext = nullptr; + create_info.flags = 0U; + create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; + create_info.pfnUserCallback = debug_callback; + create_info.pUserData = nullptr; + + VKCHECK(vkCreateDebugUtilsMessengerEXT(instance, &create_info, nullptr, &debug_messenger)) +} diff --git a/include/api-test/buffer.hpp b/include/api-test/buffer.hpp new file mode 100644 index 0000000..500e15b --- /dev/null +++ b/include/api-test/buffer.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include <cstdint> + +#include <vulkan/vulkan_core.h> + +#include "handle.hpp" + +struct buffer_desc { + size_t size; + VkBufferUsageFlags usages; + VkMemoryPropertyFlags memory_properties; + uint32_t memory_usage; +}; + +struct device_buffer; + +class buffer { +public: + buffer(handle<device_buffer> handle); + +private: + handle<device_buffer> handle; + +}; diff --git a/include/api-test/freelist.hpp b/include/api-test/freelist.hpp new file mode 100644 index 0000000..e5af6f5 --- /dev/null +++ b/include/api-test/freelist.hpp @@ -0,0 +1,32 @@ +#include <numeric> +#include <vector> + +template<typename T, const size_t initial_size = 1024U> +class freelist { + public: + freelist() + : holes(initial_size), + items(initial_size) { + std::iota(holes.begin(), holes.end(), 0); + } + + uint32_t add(const T& item) { + const auto id = holes.back(); + holes.pop_back(); + + items[id] = std::move(item); + + return id; + } + + void remove(uint32_t item_id) { + holes.push_back(item_id); + } + + const T& operator[](uint32_t index) { + return items[index]; + } + + std::vector<T> items; + std::vector<uint32_t> holes; +}; diff --git a/include/api-test/handle.hpp b/include/api-test/handle.hpp new file mode 100644 index 0000000..90c1ade --- /dev/null +++ b/include/api-test/handle.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include <cstdint> + +template<typename T> +struct handle { + const uint32_t handle; +}; diff --git a/include/api-test/resource-manager.hpp b/include/api-test/resource-manager.hpp new file mode 100644 index 0000000..f414367 --- /dev/null +++ b/include/api-test/resource-manager.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include "handle.hpp" +#include "freelist.hpp" +#include "texture.hpp" +#include "buffer.hpp" + +class resourcemanager { +public: + + handle<texture> create_texture(const texture_desc& desc); + + handle<buffer> create_buffer(const buffer_desc& desc); + + const texture& get_texture(handle<texture> handle); + + const buffer& get_buffer(handle<buffer> handle); + +private: + freelist<texture> textures; + freelist<buffer> buffers; +}; diff --git a/include/api-test/texture.hpp b/include/api-test/texture.hpp new file mode 100644 index 0000000..9632c5e --- /dev/null +++ b/include/api-test/texture.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <cstdint> + +#include <vulkan/vulkan_core.h> + +#include "handle.hpp" + +struct texture_desc { + uint32_t width = 1U; + uint32_t height = 1U; + uint32_t depth = 1U; + uint32_t mips = 1U; + VkImageUsageFlags usages; + VkFormat format; + VkImageType type; + + static constexpr auto max_mips = 16U; +}; + +struct device_texture; + +class texture { +public: + texture(handle<device_texture> handle); + +private: + handle<device_texture> handle; +}; diff --git a/include/api-test/vk-command-buffer.hpp b/include/api-test/vk-command-buffer.hpp new file mode 100644 index 0000000..c1f1899 --- /dev/null +++ b/include/api-test/vk-command-buffer.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include <vulkan/vulkan_core.h> + +class command_buffer { + void start(); + void stop(); + + VkCommandBuffer handle; +}; + +class graphics_command_buffer : command_buffer { + +}; diff --git a/include/api-test/vk-device.hpp b/include/api-test/vk-device.hpp new file mode 100644 index 0000000..f931c70 --- /dev/null +++ b/include/api-test/vk-device.hpp @@ -0,0 +1,128 @@ +#pragma once + +#include <cstdint> + +#include <vulkan/vulkan_core.h> + +#include "handle.hpp" +#include "freelist.hpp" +#include "texture.hpp" +#include "buffer.hpp" + +using VmaAllocator = struct VmaAllocator_T*; +using VmaAllocation = struct VmaAllocation_T*; + +// typedef struct VkInstance_T* VkInstance; +// typedef struct VkDevice_T* VkDevice; +// typedef struct VkPhysicalDevice_T* VkPhysicalDevice; +// typedef struct VkQueue_T* VkQueue; +// typedef uint32_t VkFlags; +// typedef VkFlags VkQueueUsageFlags; +// typedef struct VkImage_T* VkImage; +// typedef struct VkImageView_T* VkImageView; +// typedef struct VkBuffer_T* VkBuffer; +// typedef enum VkImageType VkImageType; +// typedef enum VkFormat VkFormat; +// typedef VkFlags VkImageUsageFlags; +// +// #ifdef _DEBUG +// typedef struct VkDebugUtilsMessengerEXT_T* VkDebugUtilsMessengerEXT; +// #endif // _DEBUG + + +struct device_texture { + VkImage handle; + VkImageView whole_view; + VkImageView mips_views[texture_desc::max_mips] = { VK_NULL_HANDLE }; + VmaAllocation alloc; + + texture_desc desc; +}; + +struct device_buffer { + VkBuffer handle; + VmaAllocation alloc; + VkDeviceAddress device_address; + void* mapped_ptr; + + buffer_desc desc; +}; + +class window; + +class vkdevice { +public: + + vkdevice(const window& window); + + handle<device_texture> create_texture(const texture_desc& desc); + + handle<device_buffer> create_buffer(const buffer_desc& desc); + + const device_texture& get_texture(handle<device_texture> handle) { + return textures[handle.handle]; + } + + const device_buffer& get_buffer(handle<device_buffer> handle) { + return buffers[handle.handle]; + } + + void submit(/*std::span<command_buffer>*/); + +private: + + void create_views(device_texture& texture); + + void create_surface(const window& window); + + void create_swapchain(); + + VkSurfaceKHR surface; + VkSwapchainKHR swapchain; + + // TODO: Wrap in a struct called adapter ? + void create_instance(); + + void create_device(); + + void create_memory_allocator(); + + void create_command_pools(); + + void pick_physical_device(); + + [[nodiscard]] uint32_t pick_queue(VkQueueFlags usages) const; + + static bool device_support_features(VkPhysicalDevice physical_dev); + + void create_debug_layer_callback(); + + + enum QueueTypes : uint32_t { + GRAPHICS, + // COPY, + MAX, + }; + + struct queue { + VkQueue handle; + uint32_t index; + VkQueueFlags usages; + VkCommandPool command_pool; + }; + + queue queues[QueueTypes::MAX]; + + VkInstance instance; + VkPhysicalDevice physical_device; + VkDevice device; + + VmaAllocator gpu_allocator; + +#ifdef _DEBUG + VkDebugUtilsMessengerEXT debug_messenger; +#endif // _DEBUG + + freelist<device_texture> textures; + freelist<device_buffer> buffers; +}; diff --git a/include/resource-manager.hpp b/include/resource-manager.hpp new file mode 100644 index 0000000..2a8a787 --- /dev/null +++ b/include/resource-manager.hpp @@ -0,0 +1,6 @@ +#pragma once + +class resourcemanager { + public: + static const resourcemanager resource_manager; +}; diff --git a/include/window.hpp b/include/window.hpp index aeea39a..33fce4f 100644 --- a/include/window.hpp +++ b/include/window.hpp @@ -146,7 +146,7 @@ class window { static LRESULT CALLBACK window_procedure(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam); - HWND window_handle {}; + HWND handle {}; WNDCLASS window_class {}; LPVOID main_fiber; LPVOID message_loop_fiber; diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp new file mode 100644 index 0000000..e0b00aa --- /dev/null +++ b/src/resource-manager.cpp @@ -0,0 +1,2 @@ +#include "resourcemanager.hpp" +static const resour diff --git a/src/window.cpp b/src/window.cpp index d57d44a..d2cc5f4 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -214,13 +214,14 @@ window::window(uint32_t desired_width, uint32_t desired_height) RECT rect { 0, 0, (int32_t)desired_width, (int32_t)desired_height }; DWORD style = WS_OVERLAPPEDWINDOW; - if (FAILED(AdjustWindowRect(&rect, style, FALSE))) + if (FAILED(AdjustWindowRect(&rect, style, FALSE))) { log_last_error(); + } main_fiber = ConvertThreadToFiber(nullptr); message_loop_fiber = CreateFiber(0, &::message_loop, this); - window_handle = CreateWindowA( + handle = CreateWindowA( "default-window", "path-tracer", style, @@ -234,13 +235,13 @@ window::window(uint32_t desired_width, uint32_t desired_height) this ); - ShowWindow(window_handle, SW_SHOW); + ShowWindow(handle, SW_SHOW); isOpen = true; } window::~window() { - DestroyWindow(window_handle); + DestroyWindow(handle); UnregisterClassA(window_class.lpszClassName, GetModuleHandleA(nullptr)); } |
