diff --git a/CMakeLists.txt b/CMakeLists.txt index 13afa8c..fee275c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ target_sources(${PROJECT_NAME} PUBLIC vulkan-cpp/uniform_buffer.cppm vulkan-cpp/descriptor_resource.cppm vulkan-cpp/texture.cppm + vulkan-cpp/dyn/buffer.cppm ) install( diff --git a/demos/17-buffer-device-address/CMakeLists.txt b/demos/17-buffer-device-address/CMakeLists.txt new file mode 100644 index 0000000..dd8c7ed --- /dev/null +++ b/demos/17-buffer-device-address/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.0) +project(buffer-device-address CXX) + +build_application( + SOURCES + application.cpp + + PACKAGES + vulkan-cpp + Vulkan + glfw3 + glm + stb + tinyobjloader + + LINK_PACKAGES + vulkan-cpp + tinyobjloader + Vulkan::Vulkan +) \ No newline at end of file diff --git a/demos/17-buffer-device-address/README.md b/demos/17-buffer-device-address/README.md new file mode 100644 index 0000000..bf8e784 --- /dev/null +++ b/demos/17-buffer-device-address/README.md @@ -0,0 +1,165 @@ +# Demo 17: Buffer Device Address + + +In this demo 17, will show how to get buffer device addresses working using vulkan-cpp APIs. + +## Enabling Buffer Device Addresses + +To enable buffer device addresses, add onto the `vk::device_features` initialization here: + +```C++ +vk::device_features device_features{ + vk::dynamic_rendering_feature{ { + .dynamicRendering = true, + } }, + vk::descriptor_indexing_feature{ { + .shaderSampledImageArrayNonUniformIndexing = true, + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingPartiallyBound = true, + .descriptorBindingVariableDescriptorCount = true, + .runtimeDescriptorArray = true, + } }, + + // NEW: Add buffer device address feature here + vk::buffer_device_address{ { + .bufferDeviceAddress = true, + } }, +}; +``` + +## Replacing `vk::uniform_buffer` with `vk::dyn::buffer` + +The `vk::dyn::buffer` is meant to be a buffer abstraction similar to `vk::buffer` with an additional API to getting the buffers device address from the buffer. + +Which also does performs an automated configuration to for buffer device address. + +The initialization for the test_ubo variable is quite similar. + +```C++ + +vk::buffer_parameters uniform_params = { + .memory_mask = + physical_device.memory_properties(static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit)), + .usage = + static_cast(vk::buffer_usage::uniform_buffer_bit | + vk::buffer_usage::shader_device_address_bit), + .allocate_flags = vk::memory_allocate_flags::device_address_bit_khr, +}; + +// NEW: Change this from vk::uniform_buffer to vk::dyn::buffer +vk::dyn::buffer test_ubo = + vk::dyn::buffer(logical_device, sizeof(global_uniform), uniform_params); +``` + +## Getting the Device Address + +In the mainloop, you can retrieve the buffer device address through the `.get_device_address` API. + + +When getting the `ubo_addr`, you pass this into the push constant to send in the address to retrieve that specific buffer to access. + +```C++ + +while (!glfwWindowShouldClose(window)) { + // ... + // Performing some operation + global_uniform ubo = { + .model = glm::rotate(glm::mat4(1.0f), + time * glm::radians(90.0f), + glm::vec3(0.0f, 0.0f, 1.0f)), + .view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f)), + .proj = glm::perspective(glm::radians(45.0f), + (float)swapchain_extent.width / + (float)swapchain_extent.height, + 0.1f, + 10.0f) + }; + ubo.proj[1][1] *= -1; + + test_ubo.transfer(std::span(&ubo, 1)); + + // NEW: ADD THIS LINE + const uint64_t ubo_addr = test_ubo.get_device_address(); + + push_constant_data push = { + .texture_index = 0, + + // NEW: Set device address here + .global_ubo_addr = ubo_addr, + }; + main_graphics_pipeline.push_constant( + current, push, stage, 0, sizeof(push_constant_data)); + + // ... +} + +``` + +## Changes to the shader + +### `vertex` shader + +In the vertex shader, we originally had used `layout(set = 0, binding = 0)` to contain a descriptor set that allows us to write our buffer. + +Now, that we are using a buffer device address, we can change that layout to being `layout(buffer_reference, std140)`. + +Here is what `layout(buffer_reference, std140)`: + +- `buffer_reference`: is to tell the resource lookup that the layout of this uniform is not going to be lookedup as a descriptor binding rather as a reference to this buffer. +- `std140`: Just set what the alignment rules are in how these uniform data can be aligned. + +```C++ +#version 450 + +#extension GL_EXT_buffer_reference : require + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTexCoords; +layout(location = 3) in vec3 inNormals; + +layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoords; +layout(location = 2) out vec3 fragNormals; +layout(location = 3) out flat int fragTexIndex; + +// NEW: Replace layout(set=0, binding=1) +layout(buffer_reference, std140) buffer readonly UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +}; + +layout(push_constant) uniform Constants { + int texture_index; + + // NEW: Pass uint64_t from the push_constant_data and this + // specifies what piece of data to access from buffer device address previously stored + UniformBufferObject global_ubo_address; +} push_const; + + + +void main() { + + // NEW: Add this here to retrieve the global uniform buffer data + UniformBufferObject ubo = push_const.global_ubo_address; + + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); + fragColor = inColor; + fragTexCoords = inTexCoords; + fragNormals = inNormals; + fragTexIndex = push_const.texture_index; +} +``` + +## That is it! + +Configuring buffer device address and using this Vulkan feature to access your uniform buffers are now much easier through the use of buffer device address. + +This will reduce the need to have to manage an entire descriptor sets for specifically buffers, and to just provide the address for the shader to directly access the buffer already stored in memory the GPU already has access. + diff --git a/demos/17-buffer-device-address/application.cpp b/demos/17-buffer-device-address/application.cpp new file mode 100644 index 0000000..4ddc950 --- /dev/null +++ b/demos/17-buffer-device-address/application.cpp @@ -0,0 +1,812 @@ +#define GLFW_INCLUDE_VULKAN +#if _WIN32 +#define VK_USE_PLATFORM_WIN32_KHR +#include +#define GLFW_EXPOSE_NATIVE_WIN32 +#include +#include +#else +#include +#include +#endif + +#include +#include +#include +#include +import vk; + +#include +#define GLM_FORCE_RADIANS +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include + +#include + +static VKAPI_ATTR VkBool32 VKAPI_CALL +debug_callback( + [[maybe_unused]] VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, + [[maybe_unused]] VkDebugUtilsMessageTypeFlagsEXT p_message_type, + const VkDebugUtilsMessengerCallbackDataEXT* p_callback_data, + [[maybe_unused]] void* p_user_data) { + + std::print("validation layer:\t\t{}\n\n", p_callback_data->pMessage); + return false; +} + +struct global_uniform { + glm::mat4 model; + glm::mat4 view; + glm::mat4 proj; +}; + +template +void +hash_combine(size_t& seed, const T& v, const Rest&... rest) { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed << 2); + (hash_combine(seed, rest), ...); +} + +namespace std { + + template<> + struct hash { + size_t operator()(const vk::vertex_input& vertex) const { + size_t seed = 0; + hash_combine( + seed, vertex.position, vertex.color, vertex.normals, vertex.uv); + return seed; + } + }; +} + +// Part of this demo for loading a 3D .obj model +class obj_model { +public: + obj_model() = default; + obj_model(const std::filesystem::path& p_filename, + const VkDevice& p_device, + const vk::physical_device& p_physical) { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + std::string warn, err; + + //! @note If we return the constructor then we can check if the mesh + //! loaded successfully + //! @note We also receive hints if the loading is successful! + //! @note Return default constructor automatically returns false means + //! that mesh will return the boolean as false because it wasnt + //! successful + if (!tinyobj::LoadObj(&attrib, + &shapes, + &materials, + &warn, + &err, + p_filename.string().c_str())) { + std::println("Could not load model from path {}", + p_filename.string()); + m_is_loaded = false; + return; + } + + std::vector vertices; + std::vector indices; + std::unordered_map unique_vertices{}; + + for (const auto& shape : shapes) { + for (const auto& index : shape.mesh.indices) { + vk::vertex_input vertex{}; + + // vertices.push_back(vertex); + if (!unique_vertices.contains(vertex)) { + unique_vertices[vertex] = + static_cast(vertices.size()); + vertices.push_back(vertex); + } + + if (index.vertex_index >= 0) { + vertex.position = { + attrib.vertices[3 * index.vertex_index + 0], + attrib.vertices[3 * index.vertex_index + 1], + attrib.vertices[3 * index.vertex_index + 2] + }; + + vertex.color = { + attrib.colors[3 * index.vertex_index + 0], + attrib.colors[3 * index.vertex_index + 1], + attrib.colors[3 * index.vertex_index + 2] + }; + } + + if (index.normal_index >= 0) { + vertex.normals = { + attrib.normals[3 * index.normal_index + 0], + attrib.normals[3 * index.normal_index + 1], + attrib.normals[3 * index.normal_index + 2] + }; + } + + if (index.texcoord_index >= 0) { + vertex.uv = { + attrib.texcoords[2 * index.texcoord_index + 0], + 1.0f - attrib.texcoords[2 * index.texcoord_index + 1] + }; + } + + if (!unique_vertices.contains(vertex)) { + unique_vertices[vertex] = + static_cast(vertices.size()); + vertices.push_back(vertex); + } + + indices.push_back(unique_vertices[vertex]); + } + } + + m_has_indices = (indices.size() > 0) ? true : false; + + if (m_has_indices) { + m_indices_size = indices.size(); + } + m_indices_size = vertices.size(); + m_indices_size = indices.size(); + + //! @brief Creating vertex/index buffers with host visibility flags + const auto property_flags = static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit); + + vk::buffer_parameters vertex_params = { + .memory_mask = p_physical.memory_properties(property_flags), + .property_flags = vk::memory_property::device_local_bit, + .usage = static_cast(vk::buffer_usage::transfer_dst_bit) | + static_cast(vk::buffer_usage::vertex_buffer_bit), + }; + + vk::buffer_parameters index_params = { + .memory_mask = p_physical.memory_properties(property_flags), + .property_flags = static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), + .usage = static_cast(vk::buffer_usage::index_buffer_bit), + }; + + m_vertex_buffer = vk::vertex_buffer(p_device, vertices, vertex_params); + m_index_buffer = vk::index_buffer(p_device, indices, index_params); + m_is_loaded = true; + } + + [[nodiscard]] bool loaded() const { return m_is_loaded; } + + [[nodiscard]] VkBuffer vertex_handle() const { return m_vertex_buffer; } + + [[nodiscard]] VkBuffer index_handle() const { return m_index_buffer; } + + [[nodiscard]] bool has_indices() const { return m_has_indices; } + + [[nodiscard]] uint32_t indices_size() const { return m_indices_size; } + + void draw(const VkCommandBuffer& p_command) { + if (m_has_indices) { + vkCmdDrawIndexed(p_command, m_indices_size, 1, 0, 0, 0); + } + else { + vkCmdDraw(p_command, m_indices_size, 1, 0, 0); + } + } + + void destroy() { + m_vertex_buffer.destroy(); + m_index_buffer.destroy(); + } + +private: + bool m_is_loaded = false; + bool m_has_indices = false; + uint32_t m_indices_size = 0; + vk::vertex_buffer m_vertex_buffer{}; + vk::index_buffer m_index_buffer{}; +}; + +std::vector +get_instance_extensions() { + std::vector extension_names; + uint32_t extension_count = 0; + const char** required_extensions = + glfwGetRequiredInstanceExtensions(&extension_count); + + for (uint32_t i = 0; i < extension_count; i++) { + std::println("Required Extension = {}", required_extensions[i]); + extension_names.emplace_back(required_extensions[i]); + } + + extension_names.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + +#if defined(__APPLE__) + extension_names.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + extension_names.emplace_back( + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); +#endif + + return extension_names; +} + +struct push_constant_data { + uint32_t texture_index = 0; + uint64_t global_ubo_addr = 0; +}; + +int +main() { + //! @note Just added the some test code to test the conan-starter setup code + if (!glfwInit()) { + std::print("glfwInit could not be initialized!\n"); + return -1; + } + + if (!glfwVulkanSupported()) { + std::print("GLFW: Vulkan is not supported!"); + return -1; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + + int width = 800; + int height = 600; + std::string title = "Hello Window"; + GLFWwindow* window = + glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr); + + glfwMakeContextCurrent(window); + + std::array validation_layers = { + "VK_LAYER_KHRONOS_validation", + }; + + // setting up extensions + std::vector global_extensions = get_instance_extensions(); + + vk::debug_message_utility debug_callback_info = { + // .severity essentially takes in vk::message::verbose, + // vk::message::warning, vk::message::error + .severity = + vk::message::verbose | vk::message::warning | vk::message::error, + // .message_type essentially takes in vk::debug. Like: + // vk::debug::general, vk::debug::validation, vk::debug::performance + .message_type = + vk::debug::general | vk::debug::validation | vk::debug::performance, + .callback = debug_callback + }; + + vk::application_params config = { + .name = "vulkan instance", + .version = vk::api_version::vk_1_3, // specify to using vulkan 1.3 + .validations = + validation_layers, // .validation takes in a std::span + .extensions = + global_extensions // .extensions also takes in std::span + }; + + // 1. Setting up vk instance + vk::instance api_instance(config, debug_callback_info); + + if (api_instance.alive()) { + std::println("\napi_instance alive and initiated!!!"); + } + + // setting up physical device + vk::physical_enumeration enumerate_devices{ + .device_type = vk::physical_gpu::integrated, + }; + vk::physical_device physical_device(api_instance, enumerate_devices); + + vk::queue_indices queue_indices = physical_device.family_indices(); + + // setting up logical device + std::array priorities = { 0.f }; + +#if defined(__APPLE__) + std::array extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + "VK_KHR_portability_subset", + }; +#else + std::array extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; +#endif + + std::array format_support = { + vk::format::d32_sfloat, + vk::format::d32_sfloat_s8_uint, + vk::format::d24_unorm_s8_uint + }; + + // We provide a selection of format support that we want to check is + // supported on current hardware device. + VkFormat depth_format = + physical_device.request_depth_format(format_support); + + vk::device_features device_features{ + vk::dynamic_rendering_feature{ { + .dynamicRendering = true, + } }, + vk::descriptor_indexing_feature{ { + .shaderSampledImageArrayNonUniformIndexing = true, + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingPartiallyBound = true, + .descriptorBindingVariableDescriptorCount = true, + .runtimeDescriptorArray = true, + } }, + vk::buffer_device_address{ { + .bufferDeviceAddress = true, + } }, + }; + + vk::device_params logical_device_params = { + .features = device_features.data(), + .queue_priorities = priorities, + .extensions = extensions, + .queue_family_index = queue_indices.graphics, + }; + + vk::device logical_device(physical_device, logical_device_params); + + vk::surface window_surface(api_instance, window); + + vk::surface_params surface_properties = + physical_device.request_surface(window_surface); + + vk::swapchain_params enumerate_swapchain_settings = { + .width = static_cast(width), + .height = static_cast(height), + .present_index = + physical_device.family_indices() + .graphics, // presentation index just uses the graphics index + }; + + vk::swapchain main_swapchain(logical_device, + window_surface, + enumerate_swapchain_settings, + surface_properties); + + if (!main_swapchain.alive()) { + std::println("main_swapchain is nullptr!!!"); + return -1; + } + + // querying presentable images + std::span images = main_swapchain.get_images(); + + std::println("span::size() = {}", images.size()); + uint32_t image_count = static_cast(images.size()); + + // Creating Images + std::vector swapchain_images(image_count); + std::vector swapchain_depth_images(image_count); + + VkExtent2D swapchain_extent = surface_properties.capabilities.currentExtent; + + // Setting up the images + uint32_t layer_count = 1; + uint32_t mip_levels = 1; + for (uint32_t i = 0; i < swapchain_images.size(); i++) { + vk::image_params swapchain_image_config = { + .extent = { .width = swapchain_extent.width, + .height = swapchain_extent.height, }, + .format = surface_properties.format.format, + .memory_mask = physical_device.memory_properties( + vk::memory_property::device_local_bit), + .aspect = vk::image_aspect_flags::color_bit, + .usage = vk::image_usage::color_attachment_bit, + .mip_levels = 1, + .layer_count = 1, + }; + + swapchain_images[i] = + vk::sample_image(logical_device, images[i], swapchain_image_config); + + vk::image_params image_config = { + .extent = { + .width = swapchain_extent.width, + .height = swapchain_extent.height, + }, + .format = depth_format, + .memory_mask = physical_device.memory_properties( + vk::memory_property::device_local_bit), + .aspect = vk::image_aspect_flags::depth_bit, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .mip_levels = 1, + .layer_count = 1, + }; + swapchain_depth_images[i] = + vk::sample_image(logical_device, image_config); + } + + // setting up command buffers + std::vector swapchain_command_buffers(image_count); + for (size_t i = 0; i < swapchain_command_buffers.size(); i++) { + vk::command_params settings = { + .levels = vk::command_levels::primary, + .queue_index = enumerate_swapchain_settings.present_index, + .flags = vk::command_pool_flags::reset, + }; + + swapchain_command_buffers[i] = + vk::command_buffer(logical_device, settings); + } + + // setting up presentation queue to display commands to the screen + vk::queue_params enumerate_present_queue{ + .family = 0, + .index = 0, + }; + vk::device_present_queue presentation_queue( + logical_device, main_swapchain, enumerate_present_queue); + + // gets set with the renderpass + std::array color = { 0.f, 0.5f, 0.5f, 1.f }; + + // Loading graphics pipeline + std::array shader_sources = { + vk::shader_source{ + .filename = + "shader_samples/sample9-buffer-device-address/test.vert.spv", + .stage = vk::shader_stage::vertex, + }, + vk::shader_source{ + .filename = + "shader_samples/sample9-buffer-device-address/test.frag.spv", + .stage = vk::shader_stage::fragment, + }, + }; + + // To render triangle, we do not need to set any vertex attributes + vk::shader_resource_info shader_info = { + .sources = shader_sources, + .vertex_attributes = {} // this is to explicitly set to none, but also + // dont need to set this at all regardless + }; + vk::shader_resource geometry_resource(logical_device, shader_info); + + // Setting up vertex attributes in the test shaders + std::array attribute_entries = { + vk::vertex_attribute_entry{ + .location = 0, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, position), + }, + vk::vertex_attribute_entry{ + .location = 1, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, color), + }, + vk::vertex_attribute_entry{ + .location = 2, + .format = vk::format::rg32_sfloat, + .stride = offsetof(vk::vertex_input, uv), + }, + vk::vertex_attribute_entry{ + .location = 3, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, normals), + } + }; + + std::array attributes = { + vk::vertex_attribute{ + .binding = 0, + .entries = attribute_entries, + .stride = sizeof(vk::vertex_input), + .input_rate = vk::input_rate::vertex, + }, + }; + geometry_resource.vertex_attributes(attributes); + + std::vector entries_set1 = { + vk::descriptor_entry{ + // layout (set = 0, binding = 1) uniform sampler2D textures[]; + .type = vk::buffer::combined_image_sampler, + .binding_point = { + .binding = 1, + .stage = vk::shader_stage::fragment, + }, + .descriptor_count = 1, + .flags = vk::descriptor_bind_flags::partially_bound_bit | + vk::descriptor_bind_flags::variable_descriptor_count_bit | + vk::descriptor_bind_flags::update_after_bind, + } + }; + + uint32_t max_descriptor = 1; + vk::descriptor_layout set1_layout = { + .slot = 0, // indicate specific descriptor slot 0 + .max_sets = image_count, // max descriptors to allocate + .entries = entries_set1, // descriptor layout entries description + .descriptor_counts = std::span(&max_descriptor, 1), + }; + + vk::descriptor_resource set1_resource( + logical_device, + set1_layout, + vk::descriptor_layout_flags::update_after_bind_pool); + + std::array layouts = { + set1_resource.layout(), + }; + + std::array color_blend_attachments = { + vk::color_blend_attachment_state{}, + }; + + std::array dynamic_states = { + vk::dynamic_state::viewport, vk::dynamic_state::scissor + }; + + uint32_t format = static_cast(surface_properties.format.format); + uint32_t vertex_mask = static_cast(vk::shader_stage::vertex); + uint32_t fragment_mask = static_cast(vk::shader_stage::fragment); + uint32_t stage_mask = vertex_mask | fragment_mask; + vk::shader_stage stage = static_cast(stage_mask); + vk::push_constant_range range = { + .stage = stage, + .offset = 0, + .range = sizeof(push_constant_data), + }; + vk::pipeline_params pipeline_configuration = { + .use_render_pipeline = true, + .color_attachment_formats = std::span(&format, 1), + .depth_format = static_cast(depth_format), + .stencil_format = static_cast(depth_format), + .renderpass = nullptr, + .shader_modules = geometry_resource.handles(), + .vertex_attributes = geometry_resource.vertex_attributes(), + .vertex_bind_attributes = geometry_resource.vertex_bind_attributes(), + .descriptor_layouts = layouts, + .color_blend = { + .attachments = color_blend_attachments, + }, + .depth_stencil_enabled = true, + .dynamic_states = dynamic_states, + .push_constants = std::span(&range, 1), + }; + vk::pipeline main_graphics_pipeline(logical_device, pipeline_configuration); + + obj_model test_model(std::filesystem::path("asset_samples/viking_room.obj"), + logical_device, + physical_device); + + vk::buffer_parameters uniform_params = { + .memory_mask = + physical_device.memory_properties(static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit)), + .usage = + static_cast(vk::buffer_usage::uniform_buffer_bit | + vk::buffer_usage::shader_device_address_bit), + .allocate_flags = vk::memory_allocate_flags::device_address_bit_khr, + }; + vk::dyn::buffer test_ubo = + vk::dyn::buffer(logical_device, sizeof(global_uniform), uniform_params); + + vk::texture_params config_texture = { + .memory_mask = + physical_device.memory_properties(static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit)), + }; + vk::texture texture1(logical_device, + std::filesystem::path("asset_samples/viking_room.png"), + config_texture); + + // Setting the texture sampler/image view to descriptor resource + std::array samplers = { + vk::write_image{ + .sampler = texture1.image().sampler(), + .view = texture1.image().image_view(), + .layout = vk::image_layout::shader_read_only_optimal, + }, + }; + + std::array set1_samples = { + vk::write_image_descriptor{ + .dst_binding = 1, + .sample_images = samplers, + } + }; + + set1_resource.update({}, set1_samples); + + VkClearValue clear_color = { + { 0.f, 0.5f, 0.5f, 1.f }, + }; + + VkClearValue depth_value = { + .depthStencil = { .depth = 1.f, .stencil = 0 }, + }; + + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + uint32_t current_frame = presentation_queue.acquire_next_image(); + vk::command_buffer current = swapchain_command_buffers[current_frame]; + + current.begin(vk::command_usage::simulatneous_use_bit); + + swapchain_images[current_frame].memory_barrier( + current, + surface_properties.format.format, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + // Because dynamic rendering does not automatically handle layout + // transitions These memory barriers set the color and depth images for + // the output + swapchain_depth_images[current_frame].memory_barrier( + current, + depth_format, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT); + + vk::rendering_attachment color_render_attachment = { + .image_view = swapchain_images[current_frame].image_view(), + .layout = vk::image_layout::color_optimal, + .resolve_mode = vk::resolved_mode_flags::none, + .resolve_image_view = nullptr, + .resolve_image_layout = vk::image_layout::undefined, + .load = vk::attachment_load::clear, + .store = vk::attachment_store::store, + .clear_values = clear_color + }; + + vk::rendering_attachment depth_stencil_attachment = { + .image_view = swapchain_depth_images[current_frame].image_view(), + .layout = vk::image_layout::depth_stencil_optimal, + .resolve_mode = vk::resolved_mode_flags::none, + .resolve_image_view = nullptr, + .resolve_image_layout = vk::image_layout::undefined, + .load = vk::attachment_load::clear, + .store = vk::attachment_store::store, + .depth_values = depth_value + }; + + vk::rendering_begin_parameters begin_params = { + .render_area = { { 0, 0 }, + { + swapchain_extent.width, + swapchain_extent.height, + }, }, + .layer_count = 1, + .color_attachments = std::span( + &color_render_attachment, 1), + .depth_attachment = depth_stencil_attachment, + .stencil_attachment = depth_stencil_attachment, + }; + + vk::viewport_params viewport = { + .x = 0.0f, + .y = 0.0f, + .width = static_cast(swapchain_extent.width), + .height = static_cast(swapchain_extent.height), + .min_depth = 0.0f, + .max_depth = 1.0f, + }; + current.set_viewport( + 0, 1, std::span(&viewport, 1)); + + vk::scissor_params scissor = { + .offset = { 0, 0 }, + .extent = swapchain_extent, + }; + + current.set_scissor( + 0, 1, std::span(&scissor, 1)); + + current.begin_rendering(begin_params); + + main_graphics_pipeline.bind(current); + + const VkBuffer vertex = test_model.vertex_handle(); + uint64_t offset = 0; + current.bind_vertex_buffers(std::span(&vertex, 1), + std::span(&offset, 1)); + + if (test_model.has_indices()) { + current.bind_index_buffers32(test_model.index_handle()); + } + + static auto start_time = std::chrono::high_resolution_clock::now(); + + auto current_time = std::chrono::high_resolution_clock::now(); + float time = std::chrono::duration( + current_time - start_time) + .count(); + + // We set the uniforms and then we offload that to the GPU + global_uniform ubo = { + .model = glm::rotate(glm::mat4(1.0f), + time * glm::radians(90.0f), + glm::vec3(0.0f, 0.0f, 1.0f)), + .view = glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec3(0.0f, 0.0f, 1.0f)), + .proj = glm::perspective(glm::radians(45.0f), + (float)swapchain_extent.width / + (float)swapchain_extent.height, + 0.1f, + 10.0f) + }; + ubo.proj[1][1] *= -1; + + test_ubo.transfer( + std::span(&ubo, 1)); + + const uint64_t ubo_address = test_ubo.get_device_address(); + push_constant_data push = { + .texture_index = 0, + .global_ubo_addr = ubo_address, + }; + main_graphics_pipeline.push_constant( + current, push, stage, 0, sizeof(push_constant_data)); + + const VkDescriptorSet set1 = set1_resource; + current.bind_descriptors(main_graphics_pipeline.layout(), + VK_PIPELINE_BIND_POINT_GRAPHICS, + std::span(&set1, 1)); + + test_model.draw(current); + + current.end_rendering(); + + swapchain_images[current_frame].memory_barrier( + current, + surface_properties.format.format, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + current.end(); + + // Submitting and then presenting to the screen + std::array commands = { current }; + presentation_queue.submit_async(commands); + presentation_queue.present_frame(current_frame); + } + + logical_device.wait(); + main_swapchain.destroy(); + + texture1.destroy(); + set1_resource.destroy(); + test_ubo.reset(); + test_model.destroy(); + + geometry_resource.destroy(); + main_graphics_pipeline.destroy(); + + for (auto& command : swapchain_command_buffers) { + command.destroy(); + } + + for (auto& image : swapchain_images) { + image.destroy(); + } + + for (auto& image : swapchain_depth_images) { + image.destroy(); + } + + presentation_queue.destroy(); + + logical_device.destroy(); + window_surface.destroy(); + glfwDestroyWindow(window); + api_instance.destroy(); + return 0; +} diff --git a/demos/17-buffer-device-address/conanfile.py b/demos/17-buffer-device-address/conanfile.py new file mode 100644 index 0000000..5d2a70c --- /dev/null +++ b/demos/17-buffer-device-address/conanfile.py @@ -0,0 +1,35 @@ +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout + +class Demo(ConanFile): + name = "game-demo" + version = "1.0" + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeDeps", "CMakeToolchain" + export_source = "CMakeLists.txt", "application.cpp" + + # Putting all of your build-related dependencies here + def build_requirements(self): + self.tool_requires("cmake/[^4.0.0]") + self.tool_requires("ninja/[^1.3.0]") + self.tool_requires("engine3d-cmake-utils/4.0") + + # Setting demo dependencies + def requirements(self): + self.requires("glfw/3.4") + self.requires("glm/1.0.1") + self.requires("stb/cci.20230920") + self.requires("tinyobjloader/2.0.0-rc10") + self.requires("vulkan-cpp/6.0") + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def layout(self): + cmake_layout(self) \ No newline at end of file diff --git a/shader_samples/sample9-buffer-device-address/test.frag b/shader_samples/sample9-buffer-device-address/test.frag new file mode 100644 index 0000000..0a98c20 --- /dev/null +++ b/shader_samples/sample9-buffer-device-address/test.frag @@ -0,0 +1,17 @@ +#version 450 + +#extension GL_EXT_nonuniform_qualifier : require + +layout(location = 0) in vec3 fragColor; +layout(location = 1) in vec2 fragTexCoords; +layout(location = 2) in vec3 fragNormals; +layout(location = 3) in flat int fragTexIndex; + +layout(location = 0) out vec4 outColor; + +layout(set = 0, binding = 1) uniform sampler2D textures[]; + +void main() { + + outColor = texture(textures[nonuniformEXT(fragTexIndex)], fragTexCoords); +} \ No newline at end of file diff --git a/shader_samples/sample9-buffer-device-address/test.frag.spv b/shader_samples/sample9-buffer-device-address/test.frag.spv new file mode 100644 index 0000000..7cc2604 Binary files /dev/null and b/shader_samples/sample9-buffer-device-address/test.frag.spv differ diff --git a/shader_samples/sample9-buffer-device-address/test.vert b/shader_samples/sample9-buffer-device-address/test.vert new file mode 100644 index 0000000..c270eec --- /dev/null +++ b/shader_samples/sample9-buffer-device-address/test.vert @@ -0,0 +1,36 @@ +#version 450 + +#extension GL_EXT_buffer_reference : require + +layout(location = 0) in vec3 inPosition; +layout(location = 1) in vec3 inColor; +layout(location = 2) in vec2 inTexCoords; +layout(location = 3) in vec3 inNormals; + +layout(location = 0) out vec3 fragColor; +layout(location = 1) out vec2 fragTexCoords; +layout(location = 2) out vec3 fragNormals; +layout(location = 3) out flat int fragTexIndex; + +layout(buffer_reference, std140) buffer readonly UniformBufferObject { + mat4 model; + mat4 view; + mat4 proj; +}; + +layout(push_constant) uniform Constants { + int texture_index; + UniformBufferObject global_ubo_address; +} push_const; + + + +void main() { + UniformBufferObject ubo = push_const.global_ubo_address; + + gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); + fragColor = inColor; + fragTexCoords = inTexCoords; + fragNormals = inNormals; + fragTexIndex = push_const.texture_index; +} \ No newline at end of file diff --git a/shader_samples/sample9-buffer-device-address/test.vert.spv b/shader_samples/sample9-buffer-device-address/test.vert.spv new file mode 100644 index 0000000..fcb1ab4 Binary files /dev/null and b/shader_samples/sample9-buffer-device-address/test.vert.spv differ diff --git a/vulkan-cpp/dyn/buffer.cppm b/vulkan-cpp/dyn/buffer.cppm new file mode 100644 index 0000000..b728182 --- /dev/null +++ b/vulkan-cpp/dyn/buffer.cppm @@ -0,0 +1,174 @@ +module; + +#include +#include +#include +#include + +export module vk:buffer_device_address; + +import :types; +import :utilities; + +export namespace vk::dyn { + + class buffer { + public: + buffer() = delete; + + buffer(const VkDevice& p_device, + uint64_t p_device_size, + const buffer_parameters& p_params) + : m_device(p_device) { + construct(p_device_size, p_params); + } + + // Can be invoked to perform invalidation on this buffer + void construct(uint64_t p_device_size, + const buffer_parameters& p_params) { + + m_size_bytes = p_device_size; + + // Constructs this dyn::buffer + VkBufferCreateInfo buffer_ci = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = p_device_size, // size in bytes + .usage = static_cast(p_params.usage), + .sharingMode = p_params.share_mode, + }; + + vk_check(vkCreateBuffer(m_device, &buffer_ci, nullptr, &m_handle), + "vkCreateBuffer"); + + // Required to ensure the memory allocated is correspondent to the + // shader buffer device address bit + VkMemoryAllocateFlagsInfo allocate_flags_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + .pNext = nullptr, + .flags = + static_cast(p_params.allocate_flags), + }; + // retrieving buffer memory requirements + VkMemoryRequirements memory_requirements = {}; + vkGetBufferMemoryRequirements( + m_device, m_handle, &memory_requirements); + uint32_t mapped_memory_requirements = + memory_requirements.memoryTypeBits & p_params.memory_mask; + uint32_t memory_index = + std::countr_zero(mapped_memory_requirements); + + // Required to be set for buffer device addresses + VkMemoryAllocateInfo memory_alloc_info = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &allocate_flags_info, + .allocationSize = memory_requirements.size, + .memoryTypeIndex = memory_index, + }; + + // Allocating for this buffer handle + vk_check(vkAllocateMemory( + m_device, &memory_alloc_info, nullptr, &m_device_memory), + "vkAllocateMemory"); + vk_check(vkBindBufferMemory(m_device, m_handle, m_device_memory, 0), + "vkBindBufferMemory"); + } + + void copy_to_image(const VkCommandBuffer& p_command, + const VkImage& p_image, + std::span p_copies) { + std::vector image_copies(p_copies.size()); + + for (uint32_t i = 0; i < image_copies.size(); i++) { + const buffer_image_copy image_copy = p_copies[i]; + image_copies[i] = { + .bufferOffset = image_copy.offset, + .bufferRowLength = image_copy.row_length, + .bufferImageHeight = image_copy.image_height, + .imageSubresource = { + .aspectMask = static_cast(image_copy.aspect_mask), + .mipLevel = image_copy.mip_level, + .baseArrayLayer = image_copy.base_array_layer, + .layerCount = image_copy.layer_count, + }, + .imageOffset = { + static_cast(image_copy.image_offset.width), + static_cast(image_copy.image_offset.height), + static_cast(image_copy.image_offset.depth), + }, + .imageExtent = { + image_copy.image_extent.width, + image_copy.image_extent.height, + image_copy.image_extent.depth, + }, + }; + } + + vkCmdCopyBufferToImage(p_command, + m_handle, + p_image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + static_cast(image_copies.size()), + image_copies.data()); + } + + //! @brief Destroys this object + void reset() { + if (m_handle != nullptr) { + vkDestroyBuffer(m_device, m_handle, nullptr); + } + + if (m_device_memory != nullptr) { + vkFreeMemory(m_device, m_device_memory, nullptr); + } + } + + template + void transfer(std::span p_data, uint32_t p_offset = 0) { + void* mapped = nullptr; + vk_check(vkMapMemory(m_device, + m_device_memory, + p_offset, + p_data.size_bytes(), + 0, + &mapped), + "vkMapMemory"); + memcpy(mapped, p_data.data(), p_data.size_bytes()); + vkUnmapMemory(m_device, m_device_memory); + } + + //! @brief Allows to retrieve the address to this particular buffer + //! handle + [[nodiscard("Cannot discard get_device_address()")]] const uint64_t + get_device_address() const { + VkBufferDeviceAddressInfo buffer_address_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = m_handle, + }; + + return static_cast( + vkGetBufferDeviceAddress(m_device, &buffer_address_info)); + } + + [[nodiscard("Cannot discard device_memory()")]] VkDeviceMemory + device_memory() const { + return m_device_memory; + } + + [[nodiscard("cannot discard size_bytes()")]] uint32_t size_bytes() + const { + return m_size_bytes; + } + + operator VkBuffer() { return m_handle; } + + operator VkBuffer() const { return m_handle; } + + private: + uint32_t m_size_bytes = 0; + VkDevice m_device = nullptr; + VkBuffer m_handle = nullptr; + VkDeviceMemory m_device_memory; + }; +}; \ No newline at end of file diff --git a/vulkan-cpp/dyn/shader_objects.cppm b/vulkan-cpp/dyn/shader_objects.cppm new file mode 100644 index 0000000..c300481 --- /dev/null +++ b/vulkan-cpp/dyn/shader_objects.cppm @@ -0,0 +1,234 @@ +module; + +#include +#include +#include +#include +#include + +export module vk:shader_objects; + +import :types; +import :utilities; + +export namespace vk::dyn::experimental { + inline namespace v1 { + struct shader_ext_params { + shader_stage stage; + shader_stage next_stage; + shader_code_type code_type; + std::span code_binary{}; + std::string_view name = ""; + std::span descriptor_layouts; + std::span push_constants{}; + }; + + // When querying the binary from the shaders + // If the pData is null, then we can retrieve both either the error to + // check if succeeded or the bytes needed when queried + struct shader_ext_error { + VkResult error; + uint32_t bytes = 0; + }; + + class shader_ext { + public: + shader_ext(const VkDevice& p_device, + const shader_ext_params& p_params) + : m_device(p_device) { + + vkCreateShadersEXT = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCreateShadersEXT")); + + std::println("ShadersEXT = {}", + (vkCreateShadersEXT == nullptr)); + + vkDestroyShaderEXT = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkDestroyShaderEXT")); + vkCmdBindShadersEXT = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdBindShadersEXT")); + vkGetShaderBinaryDataEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkGetShaderBinaryDataEXT")); + + vkCmdSetAlphaToCoverageEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetAlphaToCoverageEnableEXT")); + vkCmdSetColorBlendEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetColorBlendEnableEXT")); + vkCmdSetColorWriteMaskEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetColorWriteMaskEXT")); + vkCmdSetCullModeEXT = reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetCullModeEXT")); + vkCmdSetDepthBiasEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetDepthBiasEnableEXT")); + vkCmdSetDepthCompareOpEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetDepthCompareOpEXT")); + vkCmdSetDepthTestEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetDepthTestEnableEXT")); + vkCmdSetDepthWriteEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetDepthWriteEnableEXT")); + vkCmdSetFrontFaceEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetFrontFaceEXT")); + vkCmdSetPolygonModeEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetPolygonModeEXT")); + vkCmdSetPrimitiveRestartEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetPrimitiveRestartEnableEXT")); + vkCmdSetPrimitiveTopologyEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetPrimitiveTopologyEXT")); + vkCmdSetRasterizationSamplesEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetRasterizationSamplesEXT")); + vkCmdSetRasterizerDiscardEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetRasterizerDiscardEnableEXT")); + vkCmdSetSampleMaskEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetSampleMaskEXT")); + vkCmdSetScissorWithCountEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetScissorWithCountEXT")); + vkCmdSetStencilTestEnableEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetStencilTestEnableEXT")); + vkCmdSetVertexInputEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, "vkCmdSetVertexInputEXT")); + vkCmdSetViewportWithCountEXT = + reinterpret_cast( + vkGetDeviceProcAddr(m_device, + "vkCmdSetViewportWithCountEXT")); + ; + + std::vector push_constants( + p_params.push_constants.size()); + + for (uint32_t i = 0; i < push_constants.size(); i++) { + const push_constant_range data = p_params.push_constants[i]; + push_constants[i] = { + .stageFlags = + static_cast(data.stage), + .offset = data.offset, + .size = data.range, + }; + } + + VkShaderCreateInfoEXT shader_info_ci = { + .sType = VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT, + .pNext = nullptr, + .flags = 0, + .stage = static_cast(p_params.stage), + .nextStage = + static_cast(p_params.next_stage), + .codeType = + static_cast(p_params.code_type), + .codeSize = + static_cast(p_params.code_binary.size()), + .pCode = p_params.code_binary.data(), + .pName = p_params.name.data(), + .setLayoutCount = + static_cast(p_params.descriptor_layouts.size()), + .pSetLayouts = p_params.descriptor_layouts.data(), + .pushConstantRangeCount = + static_cast(push_constants.size()), + .pPushConstantRanges = push_constants.data(), + }; + + vk_check( + vkCreateShadersEXT( + m_device, 1, &shader_info_ci, nullptr, &m_shader_handle), + "vkCreateShadersEXT"); + } + + void bind(const VkCommandBuffer& p_command, + uint32_t p_stage_count, + std::span p_stages) { + vkCmdBindShadersEXT( + p_command, + p_stage_count, + reinterpret_cast( + p_stages.data()), + &m_shader_handle); + } + + void destroy() { + vkDestroyShaderEXT(m_device, m_shader_handle, nullptr); + } + + private: + VkDevice m_device = nullptr; + VkShaderEXT m_shader_handle = nullptr; + + PFN_vkCreateShadersEXT vkCreateShadersEXT{ nullptr }; + PFN_vkDestroyShaderEXT vkDestroyShaderEXT{ nullptr }; + PFN_vkCmdBindShadersEXT vkCmdBindShadersEXT{ nullptr }; + PFN_vkGetShaderBinaryDataEXT vkGetShaderBinaryDataEXT{ nullptr }; + + // With VK_EXT_shader_object pipeline state must be set at command + // buffer creation using these functions + PFN_vkCmdSetAlphaToCoverageEnableEXT + vkCmdSetAlphaToCoverageEnableEXT{ nullptr }; + PFN_vkCmdSetColorBlendEnableEXT vkCmdSetColorBlendEnableEXT{ + nullptr + }; + PFN_vkCmdSetColorWriteMaskEXT vkCmdSetColorWriteMaskEXT{ nullptr }; + PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT{ nullptr }; + PFN_vkCmdSetDepthBiasEnableEXT vkCmdSetDepthBiasEnableEXT{ + nullptr + }; + PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT{ nullptr }; + PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT{ + nullptr + }; + PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT{ + nullptr + }; + PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT{ nullptr }; + PFN_vkCmdSetPolygonModeEXT vkCmdSetPolygonModeEXT{ nullptr }; + PFN_vkCmdSetPrimitiveRestartEnableEXT + vkCmdSetPrimitiveRestartEnableEXT{ nullptr }; + PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{ + nullptr + }; + PFN_vkCmdSetRasterizationSamplesEXT vkCmdSetRasterizationSamplesEXT{ + nullptr + }; + PFN_vkCmdSetRasterizerDiscardEnableEXT + vkCmdSetRasterizerDiscardEnableEXT{ nullptr }; + PFN_vkCmdSetSampleMaskEXT vkCmdSetSampleMaskEXT{ nullptr }; + PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT{ + nullptr + }; + PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{ + nullptr + }; + PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT{ + nullptr + }; + + // VK_EXT_vertex_input_dynamic_state + PFN_vkCmdSetVertexInputEXT vkCmdSetVertexInputEXT{ nullptr }; + }; + }; +}; \ No newline at end of file diff --git a/vulkan-cpp/feature_extensions.cppm b/vulkan-cpp/feature_extensions.cppm index f3999c5..e5d4140 100644 --- a/vulkan-cpp/feature_extensions.cppm +++ b/vulkan-cpp/feature_extensions.cppm @@ -64,6 +64,13 @@ export namespace vk { } }; + using physical_device_features2 = + feature_trait; + using buffer_device_address = feature_trait< + VkPhysicalDeviceBufferDeviceAddressFeatures, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES>; + // Descriptor indexing to access unbounded array of a given uniform using descriptor_indexing_feature = feature_trait< VkPhysicalDeviceDescriptorIndexingFeatures, diff --git a/vulkan-cpp/swapchain.cppm b/vulkan-cpp/swapchain.cppm index a8329a6..74d8c15 100644 --- a/vulkan-cpp/swapchain.cppm +++ b/vulkan-cpp/swapchain.cppm @@ -4,6 +4,7 @@ module; #include #include #include +#include export module vk:swapchain; @@ -30,8 +31,10 @@ export namespace vk { } void create(const swapchain_params& p_settings) { + VkSwapchainCreateInfoKHR swapchain_ci = { .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = nullptr, .surface = m_surface_handler, .minImageCount = m_image_size, .imageFormat = m_surface_params.format.format, @@ -40,10 +43,11 @@ export namespace vk { // formats in vulkan .imageExtent = m_surface_params.capabilities.currentExtent, .imageArrayLayers = 1, - .imageUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_TRANSFER_DST_BIT), - .queueFamilyIndexCount = 1, - .pQueueFamilyIndices = &p_settings.present_index, + + // Remove COLOR_ATTACHMENT flag because its not needed + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, .preTransform = m_surface_params.capabilities.currentTransform, .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, @@ -51,11 +55,11 @@ export namespace vk { static_cast(p_settings.present_mode), .clipped = p_settings.clipped, }; + VkResult res = vkCreateSwapchainKHR( + m_device, &swapchain_ci, nullptr, &m_swapchain_handler); - vk_check( - vkCreateSwapchainKHR( - m_device, &swapchain_ci, nullptr, &m_swapchain_handler), - "vkCreateSwapchainKHR"); + std::println("VkResult = {}", static_cast(res)); + vk_check(res, "vkCreateSwapchainKHR"); } /** @@ -89,6 +93,8 @@ export namespace vk { vkDestroySwapchainKHR(m_device, m_swapchain_handler, nullptr); } + [[nodiscard]] bool alive() const { return m_swapchain_handler; } + operator VkSwapchainKHR() const { return m_swapchain_handler; } operator VkSwapchainKHR() { return m_swapchain_handler; } diff --git a/vulkan-cpp/types.cppm b/vulkan-cpp/types.cppm index efa8e93..65681f0 100644 --- a/vulkan-cpp/types.cppm +++ b/vulkan-cpp/types.cppm @@ -1413,6 +1413,18 @@ export namespace vk { flag_bits_max_enum = VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM }; + enum memory_allocate_flags : uint64_t { + device_mask_bit = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT, + device_address_bit = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + device_address_capture_replay_bit = + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT, + zero_initialize_bit = VK_MEMORY_ALLOCATE_ZERO_INITIALIZE_BIT_EXT, + device_mask_bit_khr = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR, + device_address_bit_khr = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + device_address_capture_replay_bit_khr = + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT + }; + enum class shader_stage { vertex = VK_SHADER_STAGE_VERTEX_BIT, fragment = VK_SHADER_STAGE_FRAGMENT_BIT, @@ -1607,8 +1619,8 @@ export namespace vk { struct buffer_parameters { uint32_t memory_mask = 0; memory_property property_flags; - // VkBufferUsageFlags usage; uint32_t usage; + memory_allocate_flags allocate_flags; VkSharingMode share_mode = VK_SHARING_MODE_EXCLUSIVE; const char* debug_name = nullptr; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = diff --git a/vulkan-cpp/vk.cppm b/vulkan-cpp/vk.cppm index 4262616..5a5e7b9 100644 --- a/vulkan-cpp/vk.cppm +++ b/vulkan-cpp/vk.cppm @@ -22,6 +22,7 @@ export import :index_buffer; export import :uniform_buffer; export import :descriptor_resource; export import :texture; +export import :buffer_device_address; namespace vk { inline namespace v1 {};