diff --git a/demos/15-dynamic-rendering/CMakeLists.txt b/demos/15-dynamic-rendering/CMakeLists.txt new file mode 100644 index 0000000..c60be78 --- /dev/null +++ b/demos/15-dynamic-rendering/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 4.0) +project(dynamic-rendering 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/15-dynamic-rendering/README.md b/demos/15-dynamic-rendering/README.md new file mode 100644 index 0000000..dca1d6e --- /dev/null +++ b/demos/15-dynamic-rendering/README.md @@ -0,0 +1,332 @@ +# Demo 15 -- Dynamic Rendering + +This demo shows how to get dynamic rendering setup. + + +## Setting the Physical Device Features + +Before using dynamic rendering, you need to set the dynamic rendering feature to be enabled. vulkan-cpp now has support for enabling this feature. + +This introduces a struct called `vk::device_features` which performs the chaining operation at compile-time to automatically assign the `.pNext` parameter when you add them into the initializer list as shown below. + +```C++ +vk::device_features device_features{ + vk::dynamic_rendering_feature{ { + .dynamicRendering = true, + } }, +}; +``` + +Then `vk::device_features` provides you a way to get access to the internal pointer to the entire chain of the pNext pointer itself through an API call `.data()`. + +When constructing a logical device, here is how you can pass in that pointer. + +```C++ + + +vk::device_params logical_params = { + // setting logical device to the enabled features + .features = device_features.data(), + .... +}; + +vk::device logical_device(physical, logical_params); +``` + +## Previously + +Originally before dynamic rendering, you would have to create a renderpass handle and framebuffers handles: + +```C++ +std::array renderpass_attachments = { + // color attachment + vk::attachment{ + .format = surface_properties.format.format, + .layout = vk::image_layout::color_optimal, + .samples = vk::sample_bit::count_1, + .load = vk::attachment_load::clear, + .store = vk::attachment_store::store, + .stencil_load = vk::attachment_load::dont_care, + .stencil_store = vk::attachment_store::dont_care, + .initial_layout = vk::image_layout::undefined, + .final_layout = vk::image_layout::present_src_khr, + }, + // depth attachment + vk::attachment{ + .format = depth_format, + .layout = vk::image_layout::depth_stencil_optimal, + .samples = vk::sample_bit::count_1, + .load = vk::attachment_load::clear, + .store = vk::attachment_store::dont_care, + .stencil_load = vk::attachment_load::dont_care, + .stencil_store = vk::attachment_store::dont_care, + .initial_layout = vk::image_layout::undefined, + .final_layout = vk::image_layout::depth_stencil_read_only_optimal, + }, +}; + +vk::renderpass main_renderpass(logical_device, renderpass_attachments); + +std::vector swapchain_framebuffers(image_count); +for (uint32_t i = 0; i < swapchain_framebuffers.size(); i++) { + std::array + image_view_attachments = { swapchain_images[i].image_view(), + swapchain_depth_images[i].image_view() }; + + vk::framebuffer_params framebuffer_info = { + .renderpass = main_renderpass, + .views = image_view_attachments, + .extent = swapchain_extent + }; + swapchain_framebuffers[i] = + vk::framebuffer(logical_device, framebuffer_info); +} +``` + + + +Then following by `vkCmdBeGinRenderPass` and `vkCmdEndRenderPass` calls to start rendering within that renderpass instance. +Which would look like this: + +```C++ +vk::renderpass_begin_params begin_renderpass = { + .extent = swapchain_extent, + .current_framebuffer = swapchain_framebuffers[current_frame], + .color = color, + .subpass = vk::subpass_contents::inline_bit +}; +main_renderpass.begin(current, begin_renderpass); + +main_renderpass.end(); +``` + +## With Dynamic Rendering + +Now with Dynamic Rendering. You no longer need to keep track and create framebuffers and renderpass handles at all. + +You can completely remove the creation of the renderpass and framebuffer handles. Replace `vk::attachment` with `vk::rendering_attachment`. + + +## Preparing Graphics Pipeline + +Before getting dynamic rendering to work, you must also set a few parameters for the graphics pipeline to set the `VkPipelineRenderingCreateInfo`. + +This rendering create info parameters are used to specify attachment information the graphics pipeline should expect to receive. + +```C++ +vk::pipeline_params pipeline_configuration = { + .use_render_pipeline = true, // Dynamic Rendering + .color_attachment_formats = std::span(&format, 1), // Dynamic Rendering + .depth_format = static_cast(depth_format), // Dynamic Rendering + .stencil_format = static_cast(depth_format), // Dynamic Rendering + .renderpass = nullptr, + .shader_modules = geometry_resource.handles(), + .vertex_attributes = geometry_resource.vertex_attributes(), + .vertex_bind_attributes = geometry_resource.vertex_bind_attributes(), + .color_blend = { + .attachments = color_blend_attachments, + }, + .depth_stencil_enabled = true, + .dynamic_states = dynamic_states, +}; +vk::pipeline main_graphics_pipeline(logical_device, pipeline_configuration); +``` + + +## Setting up Rendering Attachments +Here are what the rendering attachments look like for dynamic rendering: + +```C++ +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 +}; +``` + + +## Calling vkCmdBeginRendering/vkCmdEndRendering +Then instead of calling `vkCmdBeginRenderPass` you call `vkCmdBeginRendering` instead. Which will look quite similar to calling the BeginRenderPass API. + + + +```C++ +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); + +// always follow up with `.end_rendering()` +current.end_rendering(); +``` + + +## Image Layout Transitions + +One of the things renderpasses do for you is automatically handling layout transitions. With dynamic rendering this is not automatically handled for you. + +So, you have to perform image memory barriers before you perform the `.begin_rendering` call. + +Before the rendering attachments, do the following: + +```C++ +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); +``` + +Then after `.end_rendering` follow-up with this memory barrier call. + +```C++ +swapchain_images[current_frame].memory_barrier( + current, + surface_properties.format.format, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); +``` + +## Final Result + +This is what it should look like altogether. + +```C++ +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); +vkCmdDraw(current, 3, 1, 0, 0); + +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); +``` + \ No newline at end of file diff --git a/demos/15-dynamic-rendering/application.cpp b/demos/15-dynamic-rendering/application.cpp new file mode 100644 index 0000000..ecc065d --- /dev/null +++ b/demos/15-dynamic-rendering/application.cpp @@ -0,0 +1,608 @@ +#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; +} + +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 = + // vk::select_depth_format(physical_device, format_support); + VkFormat depth_format = + physical_device.request_depth_format(format_support); + + vk::device_features device_features{ + vk::dynamic_rendering_feature{ { + .dynamicRendering = 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); + + // querying presentable images + std::span images = main_swapchain.get_images(); + 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/sample1/test.vert.spv", + .stage = vk::shader_stage::vertex }, + vk::shader_source{ .filename = "shader_samples/sample1/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); + + 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); + 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(), + .color_blend = { + .attachments = color_blend_attachments, + }, + .depth_stencil_enabled = true, + .dynamic_states = dynamic_states, + }; + vk::pipeline main_graphics_pipeline(logical_device, pipeline_configuration); + + 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); + vkCmdDraw(current, 3, 1, 0, 0); + + 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(); + + 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/15-dynamic-rendering/conanfile.py b/demos/15-dynamic-rendering/conanfile.py new file mode 100644 index 0000000..eb977f5 --- /dev/null +++ b/demos/15-dynamic-rendering/conanfile.py @@ -0,0 +1,37 @@ +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") + + # Putting all of your packages here + # To build engine3d/1.0 locally do the following: + # conan create . --name=engine3d --version=0.1.0 --user=local --channel=12345 + 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/vulkan-cpp/command_buffer.cppm b/vulkan-cpp/command_buffer.cppm index 30661f8..e0343e0 100644 --- a/vulkan-cpp/command_buffer.cppm +++ b/vulkan-cpp/command_buffer.cppm @@ -3,6 +3,7 @@ module; #include #include #include +#include export module vk:command_buffer; @@ -165,6 +166,144 @@ export namespace vk { vkEndCommandBuffer(m_command_buffer); } + /** + * @brief Begin recording dynamic rendering pass instance + * + * Example Usage: + * ```C++ + * + * vk::command_buffer current_command = ...; + * + * std::array color_attachments = { + * vk::rendering_attachment{ + * .image_view = ..., + * .image_layout = ..., + * .resolve_mode = ..., + * .resolve_image_layout = ..., + * .load = ..., + * .store = ..., + * .clear_values = {...}, + * } + * }; + * + * std::array depth_attachments = { + * vk::rendering_attachment{ + * .image_view = ..., + * .image_layout = ..., + * .resolve_mode = ..., + * .resolve_image_layout = ..., + * .load = ..., + * .store = ..., + * .clear_values = {...}, + * } + * }; + * + * vk;:rendering_begin_parameters begin_params = { + * .render_area = {x, y}, + * .color_attachments = color_attachments, + * .depth_attachments = depth_attachments, + * }; + * current_command.begin_rendering(begin_params); + * + * current_command.end(); + * + * ``` + * + */ + void begin_rendering( + const rendering_begin_parameters& p_parameters) { + std::vector color_attachments( + p_parameters.color_attachments.size()); + + // Loading and setting color attachments (if any are set) + for (size_t i = 0; i < color_attachments.size(); i++) { + rendering_attachment color_attach = + p_parameters.color_attachments[i]; + color_attachments[i] = { + .sType = + VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR, + .pNext = nullptr, + .imageView = color_attach.image_view, + .imageLayout = + static_cast(color_attach.layout), + .resolveMode = static_cast( + color_attach.resolve_mode), + .resolveImageView = color_attach.resolve_image_view, + .resolveImageLayout = static_cast( + color_attach.resolve_image_layout), + .loadOp = + static_cast(color_attach.load), + .storeOp = + static_cast(color_attach.store), + .clearValue = color_attach.clear_values, + }; + } + + // Loading and setting depth attachments (if any are set) + rendering_attachment depth_attach = + p_parameters.depth_attachment; + VkRenderingAttachmentInfo depth_attachment = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = depth_attach.image_view, + .imageLayout = + static_cast(depth_attach.layout), + .resolveMode = static_cast( + depth_attach.resolve_mode), + .resolveImageView = depth_attach.resolve_image_view, + .resolveImageLayout = static_cast( + depth_attach.resolve_image_layout), + .loadOp = + static_cast(depth_attach.load), + .storeOp = + static_cast(depth_attach.store), + .clearValue = depth_attach.depth_values, + }; + + // Loading and setting stencil attachments (if any are set) + rendering_attachment stencil_attach = + p_parameters.stencil_attachment; + VkRenderingAttachmentInfo stencil_attachment = { + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .pNext = nullptr, + .imageView = stencil_attach.image_view, + .imageLayout = + static_cast(stencil_attach.layout), + .resolveMode = static_cast( + stencil_attach.resolve_mode), + .resolveImageView = stencil_attach.resolve_image_view, + .resolveImageLayout = static_cast( + stencil_attach.resolve_image_layout), + .loadOp = + static_cast(stencil_attach.load), + .storeOp = + static_cast(stencil_attach.store), + .clearValue = stencil_attach.depth_values, + }; + + VkRenderingInfo rendering_begin_info = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR, + .pNext = nullptr, + .flags = static_cast( + p_parameters.rendering_flags), + .renderArea = p_parameters.render_area, + .layerCount = p_parameters.layer_count, + .viewMask = p_parameters.view_mask, + .colorAttachmentCount = + static_cast(color_attachments.size()), + .pColorAttachments = color_attachments.data(), + .pDepthAttachment = &depth_attachment, + .pStencilAttachment = &stencil_attachment, + }; + + vkCmdBeginRendering(m_command_buffer, &rendering_begin_info); + } + + /** + * @brief End recording for dynamic rendering pass + */ + void end_rendering() { vkCmdEndRendering(m_command_buffer); } + /** * @brief Transferring the raw geomtric data of vertices to the * vertex input stage of the graphics pipeline. @@ -463,6 +602,47 @@ export namespace vk { p_commands.data()); } + void set_viewport(uint32_t p_first_viewport, + uint32_t p_viewport_count, + std::span p_params) { + std::vector viewports(p_params.size()); + + for (uint32_t i = 0; i < viewports.size(); i++) { + const auto viewport = p_params[i]; + viewports[i] = { + .x = viewport.x, + .y = viewport.x, + .width = viewport.width, + .height = viewport.height, + .minDepth = viewport.min_depth, + .maxDepth = viewport.max_depth, + }; + } + vkCmdSetViewport(m_command_buffer, + p_first_viewport, + p_viewport_count, + viewports.data()); + } + + void set_scissor(uint32_t p_first_scissor, + uint32_t p_scissor_count, + std::span p_params) { + std::vector scissors(p_params.size()); + + for (uint32_t i = 0; i < scissors.size(); i++) { + const auto scissor = p_params[i]; + scissors[i] = { + .offset = scissor.offset, + .extent = scissor.extent, + }; + } + + vkCmdSetScissor(m_command_buffer, + p_first_scissor, + p_scissor_count, + scissors.data()); + } + /** * @brief Explicitly API to properly do command buffer cleanup */ diff --git a/vulkan-cpp/pipeline.cppm b/vulkan-cpp/pipeline.cppm index fffc433..ef1c9d6 100644 --- a/vulkan-cpp/pipeline.cppm +++ b/vulkan-cpp/pipeline.cppm @@ -99,6 +99,10 @@ export namespace vk { * and scissor to configure for this graphics pipeline */ struct pipeline_params { + bool use_render_pipeline = false; + std::span color_attachment_formats = {}; + const uint32_t depth_format; + const uint32_t stencil_format; VkRenderPass renderpass = nullptr; std::span shader_modules{}; std::span @@ -129,11 +133,12 @@ export namespace vk { * * @param p_device is logical device to create the graphics pipeline * handles - * @param p_info are the parameters for creating the pipelines with + * @param p_params are the parameters for creating the pipelines + * with */ - pipeline(const VkDevice& p_device, const pipeline_params& p_info) + pipeline(const VkDevice& p_device, const pipeline_params& p_params) : m_device(p_device) { - configure(p_info); + configure(p_params); } /** @@ -160,28 +165,28 @@ export namespace vk { * * ``` * - * @param p_info is the parameters required to set the graphics + * @param p_params is the parameters required to set the graphics * pipeline handles * * More info on vulkan's official * [docs](https://docs.vulkan.org/refpages/latest/refpages/source/vkCreateGraphicsPipelines.html) */ - void configure(const pipeline_params& p_info) { + void configure(const pipeline_params& p_params) { std::vector - pipeline_shader_stages(p_info.shader_modules.size()); + pipeline_shader_stages(p_params.shader_modules.size()); uint32_t shader_src_index = 0; // 1. Load in and setup the VKShaderModule handlers for // VkPipeline - for (const shader_handle& src : p_info.shader_modules) { + for (const shader_handle& src : p_params.shader_modules) { VkShaderStageFlags stage = static_cast(src.stage); pipeline_shader_stages[shader_src_index] = VkPipelineShaderStageCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = (VkShaderStageFlagBits)stage, + .stage = static_cast(stage), .module = src.module, .pName = "main" }; @@ -191,9 +196,9 @@ export namespace vk { // 2. Setting up the vertex attribute details for VkPipeline std::span - bind_attributes = p_info.vertex_bind_attributes; + bind_attributes = p_params.vertex_bind_attributes; std::span attributes = - p_info.vertex_attributes; + p_params.vertex_attributes; VkPipelineVertexInputStateCreateInfo vertex_input_info = { .sType = @@ -210,16 +215,16 @@ export namespace vk { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .topology = static_cast( - p_info.input_assembly.topology), + p_params.input_assembly.topology), .primitiveRestartEnable = - p_info.input_assembly.primitive_restart_enable, + p_params.input_assembly.primitive_restart_enable, }; VkPipelineViewportStateCreateInfo viewport_state = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .viewportCount = p_info.viewport.viewport_count, - .scissorCount = p_info.viewport.scissor_count, + .viewportCount = p_params.viewport.viewport_count, + .scissorCount = p_params.viewport.scissor_count, }; // if lineWidth is zero, validation layers will occur @@ -228,74 +233,76 @@ export namespace vk { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .depthClampEnable = - p_info.rasterization.depth_clamp_enabled, + p_params.rasterization.depth_clamp_enabled, .rasterizerDiscardEnable = - p_info.rasterization.rasterizer_discard_enabled, + p_params.rasterization.rasterizer_discard_enabled, .polygonMode = static_cast( - p_info.rasterization.polygon_mode), + p_params.rasterization.polygon_mode), .cullMode = static_cast( - p_info.rasterization.cull_mode), - .frontFace = - static_cast(p_info.rasterization.front_face), - .depthBiasEnable = p_info.rasterization.depth_bias_enabled, + p_params.rasterization.cull_mode), + .frontFace = static_cast( + p_params.rasterization.front_face), + .depthBiasEnable = + p_params.rasterization.depth_bias_enabled, .depthBiasConstantFactor = - p_info.rasterization.depth_bias_constant, - .depthBiasClamp = p_info.rasterization.depth_bias_clamp, + p_params.rasterization.depth_bias_constant, + .depthBiasClamp = p_params.rasterization.depth_bias_clamp, .depthBiasSlopeFactor = - p_info.rasterization.depth_bias_slope, - .lineWidth = p_info.rasterization.line_width + p_params.rasterization.depth_bias_slope, + .lineWidth = p_params.rasterization.line_width }; VkPipelineMultisampleStateCreateInfo multisampling_ci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .rasterizationSamples = static_cast( - p_info.multisample.rasterization_samples), - .sampleShadingEnable = p_info.multisample.shading_enabled, - .minSampleShading = p_info.multisample.min_shading, - .pSampleMask = p_info.multisample.p_sample_masks.data(), + p_params.multisample.rasterization_samples), + .sampleShadingEnable = p_params.multisample.shading_enabled, + .minSampleShading = p_params.multisample.min_shading, + .pSampleMask = p_params.multisample.p_sample_masks.data(), .alphaToCoverageEnable = - p_info.multisample.alpha_to_coverage_enable, - .alphaToOneEnable = p_info.multisample.alpha_to_one_enable, + p_params.multisample.alpha_to_coverage_enable, + .alphaToOneEnable = + p_params.multisample.alpha_to_one_enable, }; std::vector color_blend_attachments( - p_info.color_blend.attachments.size()); + p_params.color_blend.attachments.size()); for (size_t i = 0; i < color_blend_attachments.size(); i++) { color_blend_attachments[i] = { .blendEnable = - p_info.color_blend.attachments[i].blend_enabled, + p_params.color_blend.attachments[i].blend_enabled, .srcColorBlendFactor = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .src_color_blend_factor), // Enabled: alpha blending .dstColorBlendFactor = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .dst_color_blend_factor), // Enabled: alpha blending .colorBlendOp = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .color_blend_op), // Enabled: alpha blending .srcAlphaBlendFactor = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .src_alpha_blend_factor), // Enabled: alpha blending .dstAlphaBlendFactor = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .dst_alpha_blend_factor), // Enabled: alpha blending .alphaBlendOp = static_cast( - p_info.color_blend.attachments[i] + p_params.color_blend.attachments[i] .alpha_blend_op), // Enabled: alpha blending .colorWriteMask = static_cast( - p_info.color_blend.attachments[i].color_write_mask), + p_params.color_blend.attachments[i].color_write_mask), }; } VkPipelineColorBlendStateCreateInfo color_blending_ci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .logicOpEnable = p_info.color_blend.logic_op_enable, + .logicOpEnable = p_params.color_blend.logic_op_enable, .logicOp = static_cast( - p_info.color_blend.logical_op), // Optional + p_params.color_blend.logical_op), // Optional .attachmentCount = static_cast(color_blend_attachments.size()), .pAttachments = color_blend_attachments.data(), @@ -307,13 +314,13 @@ export namespace vk { // Using ranges to load in the floats from an arbitrary array // into this. Though it should only be valid to accept only 4 // floats rather then N arbitrary floats in this buffer. - if (!p_info.color_blend.blend_constants.empty()) { + if (!p_params.color_blend.blend_constants.empty()) { // Get the first 4 elements in the span as those are // the data we are to set the .blendConstants to. // As .blendConstants only take up to 4 elements in the // array. std::span color_blend_constants = - p_info.color_blend.blend_constants.first<4>(); + p_params.color_blend.blend_constants.first<4>(); std::ranges::copy(color_blend_constants.begin(), color_blend_constants.end(), color_blending_ci.blendConstants); @@ -323,15 +330,16 @@ export namespace vk { pipeline_deth_stencil_state_ci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, - .depthTestEnable = p_info.depth_stencil.depth_test_enable, + .depthTestEnable = + p_params.depth_stencil.depth_test_enable, .depthWriteEnable = - p_info.depth_stencil.depth_write_enable, + p_params.depth_stencil.depth_write_enable, .depthCompareOp = static_cast( - p_info.depth_stencil.depth_compare_op), + p_params.depth_stencil.depth_compare_op), .depthBoundsTestEnable = - p_info.depth_stencil.depth_bounds_test_enable, + p_params.depth_stencil.depth_bounds_test_enable, .stencilTestEnable = - p_info.depth_stencil.stencil_test_enable, + p_params.depth_stencil.stencil_test_enable, }; //! @note -- pipeline states needs to be baked into the pipeline @@ -340,9 +348,9 @@ export namespace vk { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .dynamicStateCount = - static_cast(p_info.dynamic_states.size()), + static_cast(p_params.dynamic_states.size()), .pDynamicStates = reinterpret_cast( - p_info.dynamic_states.data()) + p_params.dynamic_states.data()) }; // Specifies layout of the uniforms (data resources) to be used @@ -350,8 +358,8 @@ export namespace vk { VkPipelineLayoutCreateInfo pipeline_layout_ci = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = - static_cast(p_info.descriptor_layouts.size()), - .pSetLayouts = p_info.descriptor_layouts.data(), + static_cast(p_params.descriptor_layouts.size()), + .pSetLayouts = p_params.descriptor_layouts.data(), }; vk_check( @@ -359,9 +367,26 @@ export namespace vk { m_device, &pipeline_layout_ci, nullptr, &m_pipeline_layout), "vkCreatePipelineLayout"); + VkPipelineRenderingCreateInfo rendering_ci = { + // .sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_INFO_KHR, + .sType = + VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR, + .pNext = nullptr, + .colorAttachmentCount = static_cast( + p_params.color_attachment_formats.size()), + .pColorAttachmentFormats = + reinterpret_cast( + p_params.color_attachment_formats.data()), + .depthAttachmentFormat = + static_cast(p_params.depth_format), + .stencilAttachmentFormat = + static_cast(p_params.stencil_format), + }; + VkGraphicsPipelineCreateInfo graphics_pipeline_ci = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, + .pNext = + p_params.use_render_pipeline ? &rendering_ci : nullptr, .flags = 0, .stageCount = static_cast(pipeline_shader_stages.size()), @@ -373,11 +398,11 @@ export namespace vk { .pMultisampleState = &multisampling_ci, .pDepthStencilState = &pipeline_deth_stencil_state_ci, .pColorBlendState = &color_blending_ci, - .pDynamicState = (p_info.depth_stencil_enabled) + .pDynamicState = (p_params.depth_stencil_enabled) ? &dynamic_state_ci : nullptr, .layout = m_pipeline_layout, - .renderPass = p_info.renderpass, + .renderPass = p_params.renderpass, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = -1 diff --git a/vulkan-cpp/sample_image.cppm b/vulkan-cpp/sample_image.cppm index 5b0d1cb..668dc55 100644 --- a/vulkan-cpp/sample_image.cppm +++ b/vulkan-cpp/sample_image.cppm @@ -251,10 +251,12 @@ export namespace vk { * ``` * */ - void memory_barrier(const VkCommandBuffer& p_command, - VkFormat p_format, - VkImageLayout p_old, - VkImageLayout p_new) { + void memory_barrier( + const VkCommandBuffer& p_command, + VkFormat p_format, + VkImageLayout p_old, + VkImageLayout p_new, + uint32_t p_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT) { // 1. Image Memory Barrier Initialization (using C++ Designated // Initializers - C++20) @@ -269,7 +271,8 @@ export namespace vk { .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = m_image, .subresourceRange = { .aspectMask = - VK_IMAGE_ASPECT_COLOR_BIT, + static_cast( + p_aspect_mask), .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, @@ -461,6 +464,34 @@ export namespace vk { break; } + // LAYOUT_UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL + // Transition to a color attachment + case image_layout( + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL): { + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dst_stages = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } + + // COLOR_ATTACHMENT_OPTIMAL -> PRESENT_SRC_KHR + // Transition from being a color attachment to being a + // presentable operation + case image_layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR): { + image_memory_barrier.srcAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + image_memory_barrier.dstAccessMask = 0; + + source_stage = + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dst_stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + } + default: { // Unhandled Transition break; diff --git a/vulkan-cpp/types.cppm b/vulkan-cpp/types.cppm index 9dd0b56..9d7511f 100644 --- a/vulkan-cpp/types.cppm +++ b/vulkan-cpp/types.cppm @@ -722,6 +722,25 @@ export namespace vk { shader_read_only_optimal = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL }; + enum class resolved_mode_flags : uint32_t { + none = VK_RESOLVE_MODE_NONE, + sample_zero_bit = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT, + average_bit = VK_RESOLVE_MODE_AVERAGE_BIT, + min_bit = VK_RESOLVE_MODE_MIN_BIT, + max_bit = VK_RESOLVE_MODE_MAX_BIT, + // external_format_downsample_bit = + // VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_BIT_ANDROID, + // custom_bit = VK_RESOLVE_MODE_CUSTOM_BIT_EXT, + none_khr = VK_RESOLVE_MODE_NONE_KHR, + sample_zero_bit_khr = VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR, + average_bit_khr = VK_RESOLVE_MODE_AVERAGE_BIT_KHR, + min_bit_khr = VK_RESOLVE_MODE_MIN_BIT_KHR, + max_bit_khr = VK_RESOLVE_MODE_MAX_BIT_KHR, + + external_format_downsample_android = + VK_RESOLVE_MODE_EXTERNAL_FORMAT_DOWNSAMPLE_ANDROID + }; + enum primitive_topology : uint8_t { point_light = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, line_light = VK_PRIMITIVE_TOPOLOGY_LINE_LIST, @@ -1211,6 +1230,20 @@ export namespace vk { VK_PIPELINE_BIND_POINT_MAX_ENUM // VK_PIPELINE_BIND_POINT_MAX_ENUM }; + struct viewport_params { + float x = 0.f; + float y = 0.f; + float width = 0.f; + float height = 0.f; + float min_depth = 0.f; + float max_depth = 1.f; + }; + + struct scissor_params { + VkOffset2D offset; + VkExtent2D extent; + }; + /** * @brief Specifies a specific attachment that a renderpass may operate * using @@ -1231,6 +1264,40 @@ export namespace vk { image_layout final_layout; }; + /** + * + * @brief Rendering attachment specifically used for performing + * attachments specifically for dynamic rendering. + * + */ + struct rendering_attachment { + VkImageView image_view = nullptr; + image_layout layout; + resolved_mode_flags resolve_mode; + VkImageView resolve_image_view = nullptr; + image_layout resolve_image_layout; + attachment_load load; + attachment_store store; + VkClearValue clear_values; + VkClearValue depth_values; + }; + + /** + * + * @brief Performing begin/end semantics for rendering specifically when + * dynamic rendering has been enabled. + * + */ + struct rendering_begin_parameters { + uint32_t rendering_flags = 0; + VkRect2D render_area{}; + uint32_t layer_count = 1; + uint32_t view_mask = 0; + std::span color_attachments{}; + const rendering_attachment depth_attachment{}; + const rendering_attachment stencil_attachment{}; + }; + struct renderpass_begin_params { VkExtent2D extent; VkFramebuffer current_framebuffer = nullptr;