diff --git a/demos/12-loading-models/application.cpp b/demos/12-loading-models/application.cpp index c99a1d0..00ccd2e 100644 --- a/demos/12-loading-models/application.cpp +++ b/demos/12-loading-models/application.cpp @@ -154,15 +154,29 @@ class obj_model { } m_indices_size = vertices.size(); m_indices_size = indices.size(); - vk::vertex_params vertex_info = { - .phsyical_memory_properties = p_physical.memory_properties(), + + //! @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::index_params index_info = { .phsyical_memory_properties = - p_physical.memory_properties() }; + 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_info); - m_index_buffer = vk::index_buffer(p_device, indices, index_info); + 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; } @@ -296,8 +310,10 @@ main() { // 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 = - vk::select_depth_format(physical_device, format_support); + physical_device.request_depth_format(format_support); vk::queue_indices queue_indices = physical_device.family_indices(); std::println("Graphics Queue Family Index = {}", queue_indices.graphics); @@ -308,17 +324,19 @@ main() { std::array priorities = { 0.f }; #if defined(__APPLE__) - std::array extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, - "VK_KHR_portability_subset" }; + std::array extensions = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + "VK_KHR_portability_subset", + }; #else std::array extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; #endif vk::device_features device_features{ vk::descriptor_indexing_feature{ { + .descriptorBindingSampledImageUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .descriptorBindingVariableDescriptorCount = true, - .descriptorBindingSampledImageUpdateAfterBind = true, } }, vk::dynamic_rendering_feature{ { .dynamicRendering = true, @@ -338,7 +356,7 @@ main() { std::println("Starting implementation of the swapchain!!!"); vk::surface_params surface_properties = - vk::enumerate_surface(physical_device, window_surface); + physical_device.request_surface(window_surface); if (surface_properties.format.format != VK_FORMAT_UNDEFINED) { std::println("Surface Format.format is not undefined!!!"); @@ -371,14 +389,17 @@ main() { 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 }, + .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, - .phsyical_memory_properties = physical_device.memory_properties(), }; swapchain_images[i] = @@ -386,14 +407,17 @@ main() { // Creating Images for depth buffering vk::image_params image_config = { - .extent = { .width = swapchain_extent.width, - .height = swapchain_extent.height }, + .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, - .phsyical_memory_properties = physical_device.memory_properties(), }; swapchain_depth_images[i] = vk::sample_image(logical_device, image_config); @@ -608,13 +632,17 @@ main() { logical_device, physical_device); - // Setting up descriptor sets for handling uniforms - vk::uniform_params test_ubo_info = { - .phsyical_memory_properties = physical_device.memory_properties() + // Loading a texture + + 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::uniform_buffer test_ubo = - vk::uniform_buffer(logical_device, sizeof(global_uniform), test_ubo_info); - // std::println("uniform_buffer.alive() = {}", test_ubo.alive()); + vk::uniform_buffer test_ubo = vk::uniform_buffer( + logical_device, sizeof(global_uniform), uniform_params); std::array uniforms0 = { vk::write_buffer{ @@ -630,9 +658,11 @@ main() { }, }; - // Loading a texture - vk::texture_info config_texture = { - .phsyical_memory_properties = physical_device.memory_properties(), + 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"), diff --git a/demos/13-skybox/application.cpp b/demos/13-skybox/application.cpp index dd67a79..8c1a757 100644 --- a/demos/13-skybox/application.cpp +++ b/demos/13-skybox/application.cpp @@ -141,7 +141,7 @@ main() { // 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); + physical_device.request_depth_format(format_support); vk::queue_indices queue_indices = physical_device.family_indices(); std::println("Graphics Queue Family Index = {}", queue_indices.graphics); @@ -170,7 +170,7 @@ main() { std::println("Starting implementation of the swapchain!!!"); vk::surface_params surface_properties = - vk::enumerate_surface(physical_device, window_surface); + physical_device.request_surface(window_surface); if (surface_properties.format.format != VK_FORMAT_UNDEFINED) { std::println("Surface Format.format is not undefined!!!"); @@ -203,14 +203,17 @@ main() { 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 }, + .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, - .phsyical_memory_properties = physical_device.memory_properties(), + // .phsyical_memory_properties = physical_device.memory_properties(), }; swapchain_images[i] = @@ -221,11 +224,12 @@ main() { .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, - .phsyical_memory_properties = physical_device.memory_properties(), }; swapchain_depth_images[i] = vk::sample_image(logical_device, image_config); @@ -324,7 +328,7 @@ main() { environment_map skybox = environment_map( logical_device, std::filesystem::path("asset_samples/skybox/monkstown_castle_4k.hdr"), - physical_device.memory_properties(), + physical_device, main_renderpass); // editor camera properties diff --git a/demos/13-skybox/environment_map.cppm b/demos/13-skybox/environment_map.cppm index 7e08b4e..f2ed59d 100644 --- a/demos/13-skybox/environment_map.cppm +++ b/demos/13-skybox/environment_map.cppm @@ -41,21 +41,20 @@ public: environment_map(const VkDevice& p_device, const std::filesystem::path& p_filename, - VkPhysicalDeviceMemoryProperties p_memory_properties, + vk::physical_device& p_physical_device, VkRenderPass p_renderpass) - : m_device(p_device) { - create_hdr_skybox(p_filename, p_memory_properties); + : m_device(p_device) + , m_physical_device(&p_physical_device) { + create_hdr_skybox(p_filename); - create_skybox_pipeline(p_memory_properties, p_renderpass); + create_skybox_pipeline(p_renderpass); } // ~environment_map() { // destroy(); // } - void create_hdr_skybox( - const std::filesystem::path& p_filename, - VkPhysicalDeviceMemoryProperties p_memory_properties) { + void create_hdr_skybox(const std::filesystem::path& p_filename) { stbi_set_flip_vertically_on_load(true); int w, h, channels; @@ -77,12 +76,14 @@ public: const uint64_t image_size = total_size_bytes; // Creating staging buffer - uint32_t property_flag = vk::memory_property::host_visible_bit | - vk::memory_property::host_cached_bit; vk::buffer_parameters staging_buffer_params = { - .physical_memory_properties = p_memory_properties, - .property_flags = static_cast(property_flag), - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .memory_mask = m_physical_device->memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), + .property_flags = static_cast( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), + .usage = static_cast(vk::buffer_usage::transfer_src_bit), }; vk::buffer_stream staging_buffer = vk::buffer_stream( @@ -93,10 +94,11 @@ public: .extent = { .width = width, .height = height, }, .format = texture_format, .property = vk::memory_property::device_local_bit, + .memory_mask = m_physical_device->memory_properties(vk::memory_property::device_local_bit), .aspect = vk::image_aspect_flags::color_bit, - .usage = vk::image_usage::transfer_dst_bit | - vk::image_usage::sampled_bit, - .phsyical_memory_properties = p_memory_properties, + .usage = vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, + .mip_levels = 1, + .layer_count = 1, }; m_skybox_image = vk::sample_image(m_device, skybox_image_params); @@ -164,27 +166,7 @@ public: stbi_set_flip_vertically_on_load(false); } - void create_buffers(VkPhysicalDeviceMemoryProperties p_memory_properties) { - std::vector skyboxVertices = { - // positions - -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, - 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, - - -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, - -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, - - 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, - - -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, - - -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, - 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, - - -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, - 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f - }; + void create_buffers() { std::vector vertices = { // Front Face @@ -344,17 +326,24 @@ public: { 0.0f, 0.0f } } }; - vk::vertex_params vbo_params = { - .phsyical_memory_properties = p_memory_properties, + const uint32_t property = + static_cast(vk::buffer_usage::transfer_dst_bit) | + static_cast(vk::buffer_usage::vertex_buffer_bit); + vk::buffer_parameters vertex_params = { + .memory_mask = m_physical_device->memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), + .property_flags = vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit, + .usage = static_cast(vk::buffer_usage::transfer_dst_bit | + vk::buffer_usage::vertex_buffer_bit), }; m_skybox_vbo_size = vertices.size(); - m_skybox_vbo = vk::vertex_buffer(m_device, vertices, vbo_params); + m_skybox_vbo = vk::vertex_buffer(m_device, vertices, vertex_params); } - void create_skybox_pipeline( - VkPhysicalDeviceMemoryProperties p_memory_properties, - const VkRenderPass& p_renderpass) { - create_buffers(p_memory_properties); + void create_skybox_pipeline(const VkRenderPass& p_renderpass) { + create_buffers(); std::array attribute_entries = { vk::vertex_attribute_entry{ .location = 0, @@ -405,20 +394,34 @@ public: m_skybox_shaders.vertex_attributes(attribute); // set=0 binding=0 UBO: mat4 VP - vk::uniform_params ubo_params = { - .phsyical_memory_properties = p_memory_properties, - .debug_name = "skybox_ubo", - .vkSetDebugUtilsObjectNameEXT = nullptr, + // vk::uniform_params ubo_params = { + // .phsyical_memory_properties = p_memory_properties, + // .debug_name = "skybox_ubo", + // .vkSetDebugUtilsObjectNameEXT = nullptr, + // }; + // const uint32_t property = + // static_cast(vk::memory_property::host_visible_bit) | + // static_cast(vk::memory_property::host_cached_bit); + vk::buffer_parameters uniform_params = { + // .memory_mask = p_memory_mask, + // .memory_mask = + // m_physical_device->memory_properties(static_cast(property)), + .memory_mask = m_physical_device->memory_properties( + vk::memory_property::host_cached_bit), + .usage = + static_cast(vk::buffer_usage::uniform_buffer_bit), }; m_skybox_ubo = - vk::uniform_buffer(m_device, sizeof(skybox_uniform), ubo_params); + vk::uniform_buffer(m_device, sizeof(skybox_uniform), uniform_params); // vk::uniform_buffer(m_device, sizeof(skybox_uniform), ubo_params); skybox_uniform identity = { .proj_view = glm::mat4(1.0f) }; identity.proj_view[1][1] *= -1; std::span bytes(reinterpret_cast(&identity), 1); - m_skybox_ubo.transfer(bytes); + std::println("Before transfer!"); + // m_skybox_ubo.transfer(bytes); + std::println("After transfer!"); // set=0 bindings: // - binding 0: UBO (vertex) @@ -538,8 +541,6 @@ public: } void update_uniform(const skybox_uniform& p_ubo) { - // m_skybox_ubo.transfer(std::span(&p_ubo, - // 1)); m_skybox_ubo.transfer(std::span(&p_ubo, 1)); } @@ -549,7 +550,12 @@ public: p_current.bind_descriptors(m_skybox_pipeline.layout(), VK_PIPELINE_BIND_POINT_GRAPHICS, descriptors); - m_skybox_vbo.bind(p_current); + // m_skybox_vbo.bind(p_current); + + std::array skybox_buffers = { m_skybox_vbo }; + uint64_t offset = 0; + p_current.bind_vertex_buffers(skybox_buffers, + std::span(&offset, 1)); } void draw(const VkCommandBuffer& p_current) { @@ -580,7 +586,7 @@ public: private: VkDevice m_device = nullptr; - + vk::physical_device* m_physical_device = nullptr; vk::sample_image m_skybox_image; vk::shader_resource m_skybox_shaders{}; diff --git a/vulkan-cpp/buffer_streams.cppm b/vulkan-cpp/buffer_streams.cppm index 595e68f..991515b 100644 --- a/vulkan-cpp/buffer_streams.cppm +++ b/vulkan-cpp/buffer_streams.cppm @@ -3,6 +3,7 @@ module; #include #include #include +#include export module vk:buffer_streams; @@ -29,12 +30,12 @@ export namespace vk { * @param p_device is the logical device to construct the buffer * handles * @param p_device_size is size in bytes of the buffer to be created - * @param p_settings are additional parameters for the buffer + * @param p_params are additional parameters for the buffer * handles */ buffer_stream(const VkDevice& p_device, uint64_t p_device_size, - const buffer_parameters& p_settings) + const buffer_parameters& p_params) : m_device(p_device) { VkBufferCreateInfo buffer_ci = { @@ -42,28 +43,27 @@ export namespace vk { .pNext = nullptr, .flags = 0, .size = p_device_size, // size in bytes - .usage = p_settings.usage, - .sharingMode = p_settings.share_mode, + .usage = static_cast(p_params.usage), + .sharingMode = p_params.share_mode, }; vk_check( vkCreateBuffer(p_device, &buffer_ci, nullptr, &m_handle), "vkCreateBuffer"); - // 2. retrieving buffer memory requirements + // retrieving buffer memory requirements VkMemoryRequirements memory_requirements = {}; vkGetBufferMemoryRequirements( p_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); - // 3. selects the required memory requirements for this specific - // buffer allocations - uint32_t memory_index = select_memory_requirements( - p_settings.physical_memory_properties, - memory_requirements, - p_settings.property_flags); + std::println("Memory Requirement Mapped: {} (Buffer Stream)", + mapped_memory_requirements); + std::println("Memory Index: {}", memory_index); - // 4. allocatring the necessary memory based on memory - // requirements for the buffer handles VkMemoryAllocateInfo memory_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memory_requirements.size, @@ -79,14 +79,13 @@ export namespace vk { .objectHandle = (uint64_t) m_handle, // specify vulkan to what object handle this is .pObjectName = - p_settings - .debug_name // specify what type of buffer this is + p_params.debug_name // specify what type of buffer this is }; - if (p_settings.vkSetDebugUtilsObjectNameEXT != nullptr) { + if (p_params.vkSetDebugUtilsObjectNameEXT != nullptr) { // vkSetDebugUtilsObjectNameEXT(m_device, &debug_info); - p_settings.vkSetDebugUtilsObjectNameEXT(m_device, - &debug_info); + p_params.vkSetDebugUtilsObjectNameEXT(m_device, + &debug_info); } #endif vk_check( diff --git a/vulkan-cpp/buffer_streams32.cppm b/vulkan-cpp/buffer_streams32.cppm index 2442195..d088dc1 100644 --- a/vulkan-cpp/buffer_streams32.cppm +++ b/vulkan-cpp/buffer_streams32.cppm @@ -4,6 +4,7 @@ module; #include #include #include +#include export module vk:buffer_streams32; @@ -28,7 +29,7 @@ export namespace vk { .pNext = nullptr, .flags = 0, .size = p_device_size, // size in bytes - .usage = p_params.usage, + .usage = static_cast(p_params.usage), .sharingMode = p_params.share_mode, }; @@ -40,16 +41,11 @@ export namespace vk { VkMemoryRequirements memory_requirements = {}; vkGetBufferMemoryRequirements( p_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); - // 3. selects the required memory requirements for this specific - // buffer allocations - uint32_t memory_index = select_memory_requirements( - p_params.physical_memory_properties, - memory_requirements, - p_params.property_flags); - - // 4. allocatring the necessary memory based on memory - // requirements for the buffer handles VkMemoryAllocateInfo memory_alloc_info = { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memory_requirements.size, diff --git a/vulkan-cpp/device_present_queue.cppm b/vulkan-cpp/device_present_queue.cppm index 67311c1..6f399e0 100644 --- a/vulkan-cpp/device_present_queue.cppm +++ b/vulkan-cpp/device_present_queue.cppm @@ -42,8 +42,6 @@ export namespace vk { //! @return true if this queue is out of date // Can occur when acquired_next_image or present_frame are out of // date indication swapchain resizeability. - // TODO: Change this to using C++'s exceptions for handling - // out-of-date invalidation cases bool out_of_date(bool p_is_reset = true) { // The return value we return bool return_value = false; diff --git a/vulkan-cpp/index_buffer.cppm b/vulkan-cpp/index_buffer.cppm index 73a6977..523eb81 100644 --- a/vulkan-cpp/index_buffer.cppm +++ b/vulkan-cpp/index_buffer.cppm @@ -12,29 +12,24 @@ export import :buffer_streams32; export namespace vk { inline namespace v1 { + + /** + * @brief Test implementation for index buffers + * + * This implementatino is meant to be used for an example. + * + * Though this can be used into your own code if you would like. + */ class index_buffer { public: index_buffer() = default; index_buffer(const VkDevice& p_device, std::span p_indices, - const index_params& p_info) + const buffer_parameters& p_params) : m_device(p_device) { - buffer_parameters index_params = { - .physical_memory_properties = - p_info.phsyical_memory_properties, - .property_flags = static_cast( - memory_property::host_visible_bit | - memory_property::host_cached_bit), - .usage = static_cast( - buffer_usage::index_buffer_bit), - .debug_name = p_info.debug_name.c_str(), - .vkSetDebugUtilsObjectNameEXT = - p_info.vkSetDebugUtilsObjectNameEXT - }; - - m_index_buffer = buffer_stream32( - m_device, p_indices.size_bytes(), index_params); + m_index_buffer = + buffer_stream32(m_device, p_indices.size_bytes(), p_params); m_index_buffer.write(p_indices); } diff --git a/vulkan-cpp/physical_device.cppm b/vulkan-cpp/physical_device.cppm index c266b71..4965d1e 100644 --- a/vulkan-cpp/physical_device.cppm +++ b/vulkan-cpp/physical_device.cppm @@ -3,6 +3,7 @@ module; #include #include #include +#include export module vk:physical_device; @@ -95,11 +96,167 @@ export namespace vk { return physical_memory_properties; } + [[nodiscard]] uint32_t memory_properties( + memory_property p_property_required) const { + allocation_params return_params = {}; + + VkPhysicalDeviceMemoryProperties memory_properties; + vkGetPhysicalDeviceMemoryProperties(m_physical_device, + &memory_properties); + + uint32_t mask = 0; + for (uint32_t i = 0; i < memory_properties.memoryTypeCount; + i++) { + auto type_flags = + memory_properties.memoryTypes[i].propertyFlags; + + if ((type_flags & p_property_required) == + p_property_required) { + mask |= ((1 << i)); + } + } + return mask; + } + + /** + * @brief Requests the depth format from the physical device + * + * @param p_format_supported are the arbitrary depth format + * selections to select if either are available. + * @return the format that is the one of the requested depth + * formats. + */ + [[nodiscard]] VkFormat request_depth_format( + std::span p_format_supported) { + + return request_compatible_formats( + p_format_supported, + VK_IMAGE_TILING_OPTIMAL, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); + } + + /** + * @brief Requests a format and select an arbitrary format if those + * are available to select those + * + * @param p_tiling is selecting arrangements of the data format + * @param p_feature_flag is the bitmask selection for the image + * format + */ + [[nodiscard]] VkFormat request_format( + std::span p_format_supported, + uint32_t p_tiling, + uint32_t p_feature_flag) { + return request_compatible_formats( + p_format_supported, + static_cast(p_tiling), + static_cast(p_feature_flag)); + } + + [[nodiscard]] surface_params request_surface( + const VkSurfaceKHR& p_surface, + uint32_t p_format = VK_FORMAT_B8G8R8A8_SRGB, + uint32_t p_colorspace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + surface_params surface_properties{}; + vk_check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + m_physical_device, + p_surface, + &surface_properties.capabilities), + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); + + uint32_t format_count = 0; + std::vector formats; + vk_check( + vkGetPhysicalDeviceSurfaceFormatsKHR( + m_physical_device, p_surface, &format_count, nullptr), + "vkGetPhysicalDeviceSurfaceFormatsKHR"); + + formats.resize(format_count); + + vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR(m_physical_device, + p_surface, + &format_count, + formats.data()), + "vkGetPhysicalDeviceSurfaceFormatsKHR"); + + // These are format and colorspaces selected by the user. + VkFormat selected_format = static_cast(p_format); + VkColorSpaceKHR color_space = + static_cast(p_colorspace); + + for (const auto& format : formats) { + if (format.format == selected_format && + format.colorSpace == color_space) { + surface_properties.format = format; + } + } + + if (surface_properties.format.format == VK_FORMAT_UNDEFINED) { + surface_properties.format = formats[0]; + } + + // Requesting the image size based on the surface capabilitie + surface_properties.image_size = + request_surface_image_size(surface_properties.capabilities); + return surface_properties; + } + operator VkPhysicalDevice() { return m_physical_device; } operator VkPhysicalDevice() const { return m_physical_device; } private: + uint32_t request_surface_image_size( + const VkSurfaceCapabilitiesKHR& p_capabilities) { + uint32_t requested_images = p_capabilities.minImageCount + 1; + + uint32_t final_image_count = 0; + + if ((p_capabilities.maxImageCount > 0) and + (requested_images > p_capabilities.maxImageCount)) { + final_image_count = p_capabilities.maxImageCount; + } + else { + final_image_count = requested_images; + } + + return final_image_count; + } + + VkFormat request_compatible_formats( + std::span p_format_supported, + VkImageTiling p_tiling, + VkFormatFeatureFlags p_feature_flag) { + VkFormat format = VK_FORMAT_UNDEFINED; + + for (uint32_t i = 0; i < p_format_supported.size(); i++) { + VkFormat current = + static_cast(p_format_supported[i]); + VkFormatProperties format_properties; + vkGetPhysicalDeviceFormatProperties( + m_physical_device, current, &format_properties); + + switch (p_tiling) { + case VK_IMAGE_TILING_LINEAR: + if (format_properties.linearTilingFeatures & + p_feature_flag) { + format = current; + } + break; + case VK_IMAGE_TILING_OPTIMAL: + if (format_properties.optimalTilingFeatures & + p_feature_flag) { + format = current; + } + break; + default: + break; + } + } + + return format; + } + VkPhysicalDevice enumerate_physical_devices( const VkInstance& p_instance, const physical_gpu& p_physical_device_type) { diff --git a/vulkan-cpp/sample_image.cppm b/vulkan-cpp/sample_image.cppm index 00fee2a..5b0d1cb 100644 --- a/vulkan-cpp/sample_image.cppm +++ b/vulkan-cpp/sample_image.cppm @@ -3,6 +3,7 @@ module; #include #include #include +#include export module vk:sample_image; @@ -30,24 +31,24 @@ export namespace vk { public: sample_image() = default; sample_image(const VkDevice& p_device, - const image_params& p_image_properties) + const image_params& p_image_params) : m_device(p_device) { // 1. creating VkImage handle VkImageCreateInfo image_ci = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, - .flags = p_image_properties.image_flags, + .flags = p_image_params.image_flags, .imageType = VK_IMAGE_TYPE_2D, - .format = p_image_properties.format, - .extent = { .width = p_image_properties.extent.width, - .height = p_image_properties.extent.height, - .depth = 1 }, - .mipLevels = p_image_properties.mip_levels, - .arrayLayers = p_image_properties.array_layers, + .format = p_image_params.format, + .extent = { .width = p_image_params.extent.width, + .height = p_image_params.extent.height, + .depth = 1, }, + .mipLevels = p_image_params.mip_levels, + .arrayLayers = p_image_params.array_layers, .samples = VK_SAMPLE_COUNT_1_BIT, .tiling = VK_IMAGE_TILING_OPTIMAL, .usage = - static_cast(p_image_properties.usage), + static_cast(p_image_params.usage), .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -61,13 +62,14 @@ export namespace vk { VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements( p_device, m_image, &memory_requirements); - // uint32_t memory_type_index = - // vk::image_memory_requirements(p_image_properties.physical_device, - // p_device, m_image); - uint32_t memory_index = select_memory_requirements( - p_image_properties.phsyical_memory_properties, - memory_requirements, - p_image_properties.property); + + uint32_t mapped_memory_requirements = + memory_requirements.memoryTypeBits & + p_image_params.memory_mask; + + // Retrieving the next available bits that have been mapped + uint32_t memory_index = + std::countr_zero(mapped_memory_requirements); // 4. Allocate info VkMemoryAllocateInfo memory_alloc_info = { @@ -96,20 +98,21 @@ export namespace vk { .flags = 0, .image = m_image, // .viewType = VK_IMAGE_VIEW_TYPE_2D, - .viewType = p_image_properties.view_type, - .format = p_image_properties.format, - .components = { .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY }, - .subresourceRange = { .aspectMask = - static_cast( - p_image_properties.aspect), - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = - p_image_properties.layer_count }, + .viewType = p_image_params.view_type, + .format = p_image_params.format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = static_cast(p_image_params.aspect), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = p_image_params.layer_count, + }, }; vk_check(vkCreateImageView( @@ -121,15 +124,15 @@ export namespace vk { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .magFilter = p_image_properties.range.min, - .minFilter = p_image_properties.range.max, + .magFilter = p_image_params.range.min, + .minFilter = p_image_params.range.max, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = static_cast( - p_image_properties.address_mode_u), + p_image_params.address_mode_u), .addressModeV = static_cast( - p_image_properties.addrses_mode_v), + p_image_params.addrses_mode_v), .addressModeW = static_cast( - p_image_properties.addrses_mode_w), + p_image_params.addrses_mode_w), .mipLodBias = 0.0f, .anisotropyEnable = false, .maxAnisotropy = 1, @@ -148,32 +151,30 @@ export namespace vk { sample_image(const VkDevice& p_device, const VkImage& p_image, - const image_params& p_image_properties) + const image_params& p_image_params) : m_device(p_device) , m_image(p_image) { - // Needs to create VkImageView after VkImage - // because VkImageView expects a VkImage to be binded to a singl - // VkDeviceMemory beforehand + VkImageViewCreateInfo image_view_ci = { .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, .image = m_image, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = p_image_properties.format, - .components = { .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY }, - .subresourceRange = { .aspectMask = - static_cast( - p_image_properties.aspect), - .baseMipLevel = 0, - .levelCount = - p_image_properties.mip_levels, - .baseArrayLayer = 0, - .layerCount = - p_image_properties.layer_count }, + .format = p_image_params.format, + .components = { + .r = VK_COMPONENT_SWIZZLE_IDENTITY, + .g = VK_COMPONENT_SWIZZLE_IDENTITY, + .b = VK_COMPONENT_SWIZZLE_IDENTITY, + .a = VK_COMPONENT_SWIZZLE_IDENTITY, + }, + .subresourceRange = { + .aspectMask = static_cast(p_image_params.aspect), + .baseMipLevel = 0, + .levelCount = p_image_params.mip_levels, + .baseArrayLayer = 0, + .layerCount = p_image_params.layer_count, + }, }; vk_check(vkCreateImageView( @@ -185,15 +186,15 @@ export namespace vk { .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .magFilter = p_image_properties.range.min, - .minFilter = p_image_properties.range.max, + .magFilter = p_image_params.range.min, + .minFilter = p_image_params.range.max, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = static_cast( - p_image_properties.address_mode_u), + p_image_params.address_mode_u), .addressModeV = static_cast( - p_image_properties.addrses_mode_v), + p_image_params.addrses_mode_v), .addressModeW = static_cast( - p_image_properties.addrses_mode_w), + p_image_params.addrses_mode_w), .mipLodBias = 0.0f, .anisotropyEnable = false, .maxAnisotropy = 1, @@ -254,166 +255,7 @@ export namespace vk { VkFormat p_format, VkImageLayout p_old, VkImageLayout p_new) { - /* - VkImageMemoryBarrier image_memory_barrier = { - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = 0, - .oldLayout = p_old, - .newLayout = p_new, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = m_image, - .subresourceRange = { .aspectMask = - VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 } - }; - - VkPipelineStageFlags source_stage; - VkPipelineStageFlags dst_stages; - - if (p_new == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || - (p_format == VK_FORMAT_D16_UNORM) || - (p_format == VK_FORMAT_X8_D24_UNORM_PACK32) || - (p_format == VK_FORMAT_D32_SFLOAT) || - (p_format == VK_FORMAT_S8_UINT) || - (p_format == VK_FORMAT_D16_UNORM_S8_UINT) || - (p_format == VK_FORMAT_D24_UNORM_S8_UINT)) { - image_memory_barrier.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_DEPTH_BIT; - - if (has_stencil_attachment(p_format)) { - image_memory_barrier.subresourceRange.aspectMask |= - VK_IMAGE_ASPECT_STENCIL_BIT; - } - } - else { - image_memory_barrier.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_COLOR_BIT; - } - - if (p_old == VK_IMAGE_LAYOUT_UNDEFINED && - p_new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = - VK_ACCESS_SHADER_READ_BIT; - source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - else if (p_old == VK_IMAGE_LAYOUT_UNDEFINED && - p_new == VK_IMAGE_LAYOUT_GENERAL) { - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = - VK_ACCESS_SHADER_READ_BIT; - - source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - - if (p_old == VK_IMAGE_LAYOUT_UNDEFINED && - p_new == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = - VK_ACCESS_TRANSFER_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dst_stages = VK_PIPELINE_STAGE_TRANSFER_BIT; - } // Convert back from read-only to updateable - else if (p_old == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && - p_new == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.dstAccessMask = - VK_ACCESS_TRANSFER_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dst_stages = VK_PIPELINE_STAGE_TRANSFER_BIT; - } // Convert from updateable texture to shader read-only - else if (p_old == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && - p_new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.dstAccessMask - = VK_ACCESS_SHADER_READ_BIT; - - source_stage = VK_PIPELINE_STAGE_TRANSFER_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } // Convert depth texture from undefined state to depth-stencil - buffer else if (p_old == VK_IMAGE_LAYOUT_UNDEFINED && p_new == - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dst_stages = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; - } // Wait for render pass to complete - else if (p_old == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && - p_new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = - 0; // VK_ACCESS_SHADER_READ_BIT; - image_memory_barrier.dstAccessMask = 0; - source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dst_stages = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; - dst_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - source_stage = - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dst_stages = - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } // Convert back from - read-only to color attachment else if (p_old == - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && p_new == - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.dstAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dst_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - } // Convert from updateable texture to shader read-only - else if (p_old == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL && - p_new == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - image_memory_barrier.dstAccessMask = - VK_ACCESS_SHADER_READ_BIT; - - source_stage = - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dst_stages = - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } // Convert back from - read-only to depth attachment else if (p_old == - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && p_new == - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_SHADER_READ_BIT; image_memory_barrier.dstAccessMask = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - - source_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - dst_stages = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - } // Convert from updateable depth texture to shader read-only - else if (p_old == - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL && p_new == - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - image_memory_barrier.srcAccessMask = - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - image_memory_barrier.dstAccessMask = - VK_ACCESS_SHADER_READ_BIT; - - source_stage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - } - - vkCmdPipelineBarrier(p_command, - source_stage, - dst_stages, - 0, - 0, - nullptr, - 0, - nullptr, - 1, - &image_memory_barrier); - */ // 1. Image Memory Barrier Initialization (using C++ Designated // Initializers - C++20) VkImageMemoryBarrier image_memory_barrier = { diff --git a/vulkan-cpp/shader_resource.cppm b/vulkan-cpp/shader_resource.cppm index 18e204f..09bf2b4 100644 --- a/vulkan-cpp/shader_resource.cppm +++ b/vulkan-cpp/shader_resource.cppm @@ -93,7 +93,8 @@ export namespace vk { m_vertex_binding_attributes[i] = { .binding = attribute.binding, .stride = attribute.stride, - .inputRate = to_input_rate(attribute.input_rate), + .inputRate = + static_cast(attribute.input_rate), }; // then setting up the vertex attributes for the vertex data diff --git a/vulkan-cpp/swapchain.cppm b/vulkan-cpp/swapchain.cppm index e52978e..a8329a6 100644 --- a/vulkan-cpp/swapchain.cppm +++ b/vulkan-cpp/swapchain.cppm @@ -22,8 +22,7 @@ export namespace vk { : m_device(p_device) , m_surface_handler(p_surface) , m_surface_params(p_surface_properties) { - m_image_size = - surface_image_size(m_surface_params.capabilities); + m_image_size = m_surface_params.image_size; std::println("Surface Image Size = {}", m_image_size); diff --git a/vulkan-cpp/texture.cppm b/vulkan-cpp/texture.cppm index 24baf13..d906f44 100644 --- a/vulkan-cpp/texture.cppm +++ b/vulkan-cpp/texture.cppm @@ -33,10 +33,9 @@ export namespace vk { memory_property::host_cached_bit; buffer_parameters staging_buffer_config = { - .physical_memory_properties = - p_config.phsyical_memory_properties, + .memory_mask = p_config.memory_mask, .property_flags = static_cast(property_flag), - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + .usage = static_cast(buffer_usage::transfer_src_bit), }; buffer_stream staging( p_device, p_data.size(), staging_buffer_config); @@ -112,10 +111,8 @@ export namespace vk { } // TODO: Remove redundant struct and replace with vk::image_params - struct texture_info { - // for getting image memory requirements for the texture - VkPhysicalDeviceMemoryProperties phsyical_memory_properties; - std::filesystem::path filepath; + struct texture_params { + uint32_t memory_mask = 0; uint32_t mip_levels = 1; uint32_t layer_count = 1; }; @@ -128,7 +125,7 @@ export namespace vk { // to make the API's consistent. texture(const VkDevice& p_device, const image_extent& p_extent, - VkPhysicalDeviceMemoryProperties p_property) + uint32_t p_memory_mask) : m_device(p_device) , m_extent(p_extent) { @@ -139,9 +136,10 @@ export namespace vk { image_params config_image = { .extent = m_extent, .format = static_cast(format::r8g8b8a8_unorm), + .memory_mask = p_memory_mask, .usage = - image_usage::transfer_dst_bit | image_usage::sampled_bit, - .phsyical_memory_properties = p_property + static_cast(image_usage::transfer_dst_bit) | + static_cast(image_usage::sampled_bit), }; int bytes_per_pixel = bytes_per_texture_format(config_image.format); @@ -165,7 +163,7 @@ export namespace vk { // to make the API's consistent. texture(const VkDevice& p_device, const std::filesystem::path& p_filename, - const texture_info& p_texture_info) + const texture_params& p_texture_params) : m_device(p_device) { // 1. load from file int w, h; @@ -194,19 +192,19 @@ export namespace vk { m_extent.width * m_extent.height * bytes_per_pixel; // uint32_t layer_count = 1; - uint32_t layer_count = p_texture_info.layer_count; + uint32_t layer_count = p_texture_params.layer_count; uint32_t image_size = image_layer_sizes_with_bytes * layer_count; image_params config_image = { .extent = m_extent, .format = texture_format, + .memory_mask = p_texture_params.memory_mask, .usage = - image_usage::transfer_dst_bit | image_usage::sampled_bit, - .mip_levels = p_texture_info.mip_levels, - .layer_count = p_texture_info.layer_count, - .phsyical_memory_properties = - p_texture_info.phsyical_memory_properties, + static_cast(image_usage::transfer_dst_bit) | + static_cast(image_usage::sampled_bit), + .mip_levels = p_texture_params.mip_levels, + .layer_count = p_texture_params.layer_count, }; // Ensures the image data is valid before continuing diff --git a/vulkan-cpp/types.cppm b/vulkan-cpp/types.cppm index b0bfac2..9dd0b56 100644 --- a/vulkan-cpp/types.cppm +++ b/vulkan-cpp/types.cppm @@ -486,6 +486,12 @@ export namespace vk { std::string description; }; + struct allocation_params { + // uint32_t size=0; + uint32_t memory_supported_mask = 0; + // uint32_t memory_index=0; + }; + //! @brief Defines the enum types for a selection of gpu device // types to select according to your hardware specs @@ -524,6 +530,7 @@ export namespace vk { struct surface_params { VkSurfaceCapabilitiesKHR capabilities; VkSurfaceFormatKHR format; + uint32_t image_size = 0; // requested surface image size }; struct queue_params { @@ -1102,10 +1109,10 @@ export namespace vk { * - instance-based specification next data entry * */ - enum class input_rate : uint8_t { - vertex, - instance, - max_enum, + enum class input_rate : uint32_t { + vertex = VK_VERTEX_INPUT_RATE_VERTEX, + instance = VK_VERTEX_INPUT_RATE_INSTANCE, + max_enum = VK_VERTEX_INPUT_RATE_MAX_ENUM, }; //! @brief Equivalent to doing VkSampleCountFlagBits but simplified @@ -1248,71 +1255,80 @@ export namespace vk { }; /** - * @brief memory_property is a representation of vulkan's - * VkMemoryPropertyFlags. - * - * @param device_local_bit + * @brief Wrapper enum class for VkMemoryPropertyFlags * - * Meaning: indicates memory allocated with this type is most efficient - * for the GPU to access. \n + * Defines the physical locations and CPU-to-GPU access behavior for + * allocated memory heaps. * - * Implications: The memory with this bit typically - * resides on the GPU's VRAM. Accessing memory directly from GPU's since - * its faster. \n - * - * Usage: For resources that are primarily accessed by the GPU in the - * case of textures, vertex buffers, and framebuffers. If a memory type - * has this bit associated with it, the heap memory will also have be - * set along with VK_MEMORY_HEAP_DEVICE_LOCAL_BIT. \n + * @param device_local_bit + * - Used for high-speed GPU access. + * - Memory that is physically located on the GPU VRAM. + * - Usage: Performant-critical resources such as vertex buffers, + * framebuffers, textures, etc. CPU access is usually impossible unless + * Bar-Resize is used. * * @param host_visible_bit - * - * Meaning: Indicates memory alloated can be mapped to host's (CPU) - * address space using the vkMapMemory API. \n - * - * Implications: ALlows CPU to directly - * read from and write to memory. Crucial for transferring data between - * CPU to GPU. \n - * - * Usage: Use-case is for staging buffers, where data initially - * uploaded from CPU before being copied to device-local memory or for - * resourcfes that need frequent CPU updates. \n + * - Used for CPU-to-GPU transfers. + * - Memory can be mapped to CPU-address space via vkMapMemory + * - Usage: staging buffers. Allowing CPU to view and write data, + * the GPU will start to use. * * @param host_coherent_bit - * - * Meaning: Indicates host cache managemnet commands - * (vkFlushMappedMemoryRanges and vkInvalidateMappedMemoryRanges) are - * not needed. Writes made by host will automatically become visible to - * the device, and writes made by device will automatically be visible - * to the host. \n - * - * Implications: Simplifies memory synchronization between CPU and GPU. - * Though can lead to slower CPU access if it means bypassing the CPU - * caches or involving more complex cache coherence protocols. \n - * - * Usage: Used with 'VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT' for easy data - * transfers, especially for frequent updated data where manual flushing - * would be cumbersome. \n - * + * - Used for automatic synchronization + * - Removes need for manual cache flushing/invalidation. + * - Implies writing from CPU are automatically visible to the GPU (and + * vice-versa). + * - Usage: Combined with `host_visible_bit` for frequent updated data + * (like uniform buffers) to avoid complex manual sync calls. * * @param host_cached_bit + * - Used for fast CPU reads. + * - Memory stored in CPU's L1/L2/L3 cache heirarchy. + * - Implies massive performance boost for CPU reads. Without this, CPU + * reads from host-visible memory which can be slow. + * - Usage: Use when needing for Readback information such as retrieving + * computed data on the GPU or analyzing results done in the compute + * shaders, even. + * + * Additional Considerations: In cases `host_coherent_bit` is NOT set, + * MUST manually call `vkInvalidateMappedMemoryRanges` before reading to + * ensure the CPU cache isn't hoilding onto outdated data. * - * Meaning: Indicates memory allocated with this type is cached on the - * host (CPU). \n + * @param lazily_allocated_bit + * - Used for memory virtualization (mobile/tile-based optimziation). + * - GPU doesn't actually allocated physical VRAM for this until the + * moment it is accessed. + * - Implies data stays "on-chip" (like depth-buffer or renderpass), it + * may never be written to VRAM at all. + * - Can be used to save massive amounts of battery/bandwidth on mobile + * GPU's. + * - Usage: Strictly for "Transient Attachments" (Depth/Stencil or MSAA + * buffers) which are created and destroyed in a single renderpass. * - * Implications: Host memory accesses (read/writes) to this memory - * type will go through CPU cache heirarchy. Significantly improves - * performance where random access patterns. If not set on - * `HOST_VISIBLE` memory, CPU accesses are often uncached and - * write-combined, meanming writes should be sequential and reads should - * be avoided for good performance. \n + * @param device_protected_bit + * - Content Security (DRM) + * - Places memory in a secure heap that cannot be read by the CPU or + * non-protected GPU queue's. + * - Prevents unauthorized memory scraping. If you try to copy this + * memory to a non-protected buffer, this operation will fail. + * - Usage: Critical data protection (video streaming/DRM) or sensitive + * computed data. * - * Usage: Does well for CPU-side reading of data written to GPU - * (screenshots or feedback data) and for CPU-side writing of data to be - * accessed randomly. Flag usually implies explicit cache management - * (flushing/invalidating) is required if `HOST_COHERENT_BIT` is not - * also set. \n + * @param device_coherent_bit_amd + * - Performs fine-grained GPU-to-GPU synchronization (on AMD hardware). + * - Ensures memory writes from one GPU shader are immediately visible + * to other parts of the GPU without explicit cache flushes. + * - Usage: GPGPU or compute-heavy tasks on specifically AMD hardware + * where manual barriers overhead is too high. * + * @param rdma_capable_bit_nv + * - Performs direct peer-to-peer transfer (to NVIDIA hardware). + * - Allows for external devices (like 100GB NICs or other GPUs) to + * read/write to this memory directly via Remote Direct Memory Access. + * - Bypasses the GPU entirely for network-to-GPU transfers, reducing + * latency close to near zero. + * - Usage: Can be used if you need to set the memory property for + * ultra-low latecy videop streaming * */ enum memory_property : uint32_t { @@ -1426,27 +1442,6 @@ export namespace vk { VkBuffer dst; }; - struct vertex_params { - VkPhysicalDeviceMemoryProperties phsyical_memory_properties; - std::string debug_name; - PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = - nullptr; - }; - - struct index_params { - VkPhysicalDeviceMemoryProperties phsyical_memory_properties; - std::string debug_name; - PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = - nullptr; - }; - - struct uniform_params { - VkPhysicalDeviceMemoryProperties phsyical_memory_properties; - std::string debug_name; - PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = - nullptr; - }; - struct descriptor_binding_point { uint32_t binding; shader_stage stage; @@ -1491,6 +1486,7 @@ export namespace vk { image_extent extent{}; VkFormat format = VK_FORMAT_UNDEFINED; memory_property property = memory_property::device_local_bit; + uint32_t memory_mask = 0; image_aspect_flags aspect = image_aspect_flags::color_bit; uint32_t usage; VkImageCreateFlags image_flags = 0; @@ -1498,7 +1494,6 @@ export namespace vk { uint32_t mip_levels = 1; uint32_t layer_count = 1; uint32_t array_layers = 1; - VkPhysicalDeviceMemoryProperties phsyical_memory_properties; filter_range range{ .min = VK_FILTER_LINEAR, .max = VK_FILTER_LINEAR, @@ -1521,9 +1516,10 @@ export namespace vk { }; struct buffer_parameters { - VkPhysicalDeviceMemoryProperties physical_memory_properties; + uint32_t memory_mask = 0; memory_property property_flags; - VkBufferUsageFlags usage; + // VkBufferUsageFlags usage; + uint32_t usage; VkSharingMode share_mode = VK_SHARING_MODE_EXCLUSIVE; const char* debug_name = nullptr; PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = diff --git a/vulkan-cpp/uniform_buffer.cppm b/vulkan-cpp/uniform_buffer.cppm index 74ce15c..94a236c 100644 --- a/vulkan-cpp/uniform_buffer.cppm +++ b/vulkan-cpp/uniform_buffer.cppm @@ -4,6 +4,7 @@ module; #include #include #include +#include export module vk:uniform_buffer; @@ -24,22 +25,12 @@ export namespace vk { uniform_buffer() = default; uniform_buffer(const VkDevice& p_device, uint64_t p_size_bytes, - const uniform_params& p_uniform_info) + const buffer_parameters& p_uniform_params) : m_device(p_device) , m_size_bytes(p_size_bytes) { - buffer_parameters uniform_info = { - .physical_memory_properties = - p_uniform_info.phsyical_memory_properties, - .property_flags = static_cast( - memory_property::host_visible_bit | - memory_property::host_coherent_bit), - .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - .debug_name = p_uniform_info.debug_name.c_str(), - .vkSetDebugUtilsObjectNameEXT = - p_uniform_info.vkSetDebugUtilsObjectNameEXT - }; + std::println("vk::uniform_buffer!"); m_uniform_handle = - buffer_stream(m_device, p_size_bytes, uniform_info); + buffer_stream(m_device, p_size_bytes, p_uniform_params); } [[nodiscard]] bool alive() const { return m_uniform_handle; } diff --git a/vulkan-cpp/utilities.cppm b/vulkan-cpp/utilities.cppm index 1ece33b..5e1a009 100644 --- a/vulkan-cpp/utilities.cppm +++ b/vulkan-cpp/utilities.cppm @@ -14,17 +14,11 @@ export import :types; export namespace vk { inline namespace v1 { - void vk_check(const VkResult& p_result, - const std::string& p_name, - const std::source_location& p_source = {}) { + void vk_check(const VkResult& p_result, const std::string& p_name) { if (p_result != VK_SUCCESS) { - std::println( - "File {} on line {} failed VkResult check", - std::filesystem::relative(p_source.file_name()).string(), - p_source.line()); - std::println("Current Function Location = {}", - p_source.function_name()); - std::println("{} VkResult returned: {}", p_name, (int)p_result); + std::println("{} VkResult returned: {}", + p_name, + static_cast(p_result)); } } @@ -42,150 +36,6 @@ export namespace vk { return queue_family_properties; } - VkFormat select_compatible_formats( - const VkPhysicalDevice& p_physical, - std::span p_format_selection, - VkImageTiling p_tiling, - VkFormatFeatureFlags p_feature_flag) { - VkFormat format = VK_FORMAT_UNDEFINED; - - for (size_t i = 0; i < p_format_selection.size(); i++) { - VkFormat current_format = - static_cast(p_format_selection[i]); - VkFormatProperties format_properties; - vkGetPhysicalDeviceFormatProperties( - p_physical, current_format, &format_properties); - - if (p_tiling == VK_IMAGE_TILING_LINEAR) { - if (format_properties.linearTilingFeatures & - p_feature_flag) { - format = current_format; - } - } - else if (p_tiling == VK_IMAGE_TILING_OPTIMAL and - format_properties.optimalTilingFeatures & - p_feature_flag) { - format = current_format; - } - } - - return format; - } - - VkFormat select_depth_format( - const VkPhysicalDevice& p_physical, - std::span p_format_selection) { - - VkFormat format = select_compatible_formats( - p_physical, - p_format_selection, - VK_IMAGE_TILING_OPTIMAL, - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); - return format; - } - - uint32_t physical_memory_properties( - const VkPhysicalDevice& p_physical, - uint32_t p_type_filter, - VkMemoryPropertyFlags p_property_flag) { - VkPhysicalDeviceMemoryProperties mem_props; - vkGetPhysicalDeviceMemoryProperties(p_physical, &mem_props); - - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - if ((p_type_filter & (1 << i)) and - (mem_props.memoryTypes[i].propertyFlags & - p_property_flag) == p_property_flag) { - return i; - } - } - - return -1; - } - - surface_params enumerate_surface(const VkPhysicalDevice& p_physical, - const VkSurfaceKHR& p_surface) { - surface_params enumerate_surface_properties{}; - vk_check(vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - p_physical, - p_surface, - &enumerate_surface_properties.capabilities), - "vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - - uint32_t format_count = 0; - std::vector formats; - vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( - p_physical, p_surface, &format_count, nullptr), - "vkGetPhysicalDeviceSurfaceFormatsKHR"); - - formats.resize(format_count); - - vk_check(vkGetPhysicalDeviceSurfaceFormatsKHR( - p_physical, p_surface, &format_count, formats.data()), - "vkGetPhysicalDeviceSurfaceFormatsKHR"); - - for (const auto& format : formats) { - if (format.format == VK_FORMAT_B8G8R8A8_SRGB && - format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { - enumerate_surface_properties.format = format; - } - } - - if (enumerate_surface_properties.format.format == - VK_FORMAT_UNDEFINED) { - enumerate_surface_properties.format = formats[0]; - } - - return enumerate_surface_properties; - } - - uint32_t surface_image_size( - const VkSurfaceCapabilitiesKHR& p_capabilities) { - uint32_t requested_images = p_capabilities.minImageCount + 1; - - uint32_t final_image_count = 0; - - if ((p_capabilities.maxImageCount > 0) and - (requested_images > p_capabilities.maxImageCount)) { - final_image_count = p_capabilities.maxImageCount; - } - else { - final_image_count = requested_images; - } - - return final_image_count; - } - - VkSampler create_sampler(const VkDevice& p_device, - const filter_range& p_range, - VkSamplerAddressMode p_address_mode) { - VkSamplerCreateInfo sampler_info = { - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = p_range.min, - .minFilter = p_range.max, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = p_address_mode, - .addressModeV = p_address_mode, - .addressModeW = p_address_mode, - .mipLodBias = 0.0f, - .anisotropyEnable = false, - .maxAnisotropy = 1, - .compareEnable = false, - .compareOp = VK_COMPARE_OP_ALWAYS, - .minLod = 0.0f, - .maxLod = 0.0f, - .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, - .unnormalizedCoordinates = false - }; - - VkSampler sampler = nullptr; - VkResult res = - vkCreateSampler(p_device, &sampler_info, nullptr, &sampler); - vk_check(res, "vkCreateSampler"); - return sampler; - } - VkSemaphore create_semaphore(const VkDevice& p_device) { // creating semaphores VkSemaphoreCreateInfo semaphore_ci = { @@ -201,43 +51,6 @@ export namespace vk { return semaphore; } - uint32_t image_memory_requirements(const VkPhysicalDevice& p_physical, - const VkDevice& p_device, - const VkImage& p_image, - memory_property p_property) { - VkMemoryRequirements memory_requirements; - vkGetImageMemoryRequirements( - p_device, p_image, &memory_requirements); - - uint32_t type_filter = memory_requirements.memoryTypeBits; - VkMemoryPropertyFlags property_flag = - static_cast(p_property); - - VkPhysicalDeviceMemoryProperties mem_props; - vkGetPhysicalDeviceMemoryProperties(p_physical, &mem_props); - - for (uint32_t i = 0; i < mem_props.memoryTypeCount; i++) { - if ((type_filter & (1 << i)) and - (mem_props.memoryTypes[i].propertyFlags & property_flag) == - property_flag) { - return i; - } - } - - return -1; - } - - VkVertexInputRate to_input_rate(input_rate p_input_rate) { - switch (p_input_rate) { - case input_rate::vertex: - return VK_VERTEX_INPUT_RATE_VERTEX; - case input_rate::instance: - return VK_VERTEX_INPUT_RATE_INSTANCE; - default: - return VK_VERTEX_INPUT_RATE_MAX_ENUM; - } - } - bool has_depth_specified(image_layout p_layout) { if (p_layout == image_layout::depth_stencil_optimal) { return true; @@ -249,26 +62,6 @@ export namespace vk { return false; } - uint32_t select_memory_requirements( - VkPhysicalDeviceMemoryProperties p_physical_memory_props, - VkMemoryRequirements p_memory_requirements, - memory_property p_property) { - uint32_t memory_bits = p_memory_requirements.memoryTypeBits; - VkMemoryPropertyFlags property_flag = - static_cast(p_property); - - for (uint32_t i = 0; i < p_physical_memory_props.memoryTypeCount; - i++) { - if ((memory_bits & (1 << i)) and - (p_physical_memory_props.memoryTypes[i].propertyFlags & - property_flag) == property_flag) { - return i; - } - } - - return -1; - } - int bytes_per_texture_format(VkFormat p_format) { switch (p_format) { case VK_FORMAT_R8_SINT: @@ -311,6 +104,42 @@ export namespace vk { sizeof(p_data)); } + /** + * @brief Bitwise OR Overloads Adapters + * + * These operator overloads allow for `enum class`'s which are strongly + * typed to be temporarily truncates the types to their original type. + * + * Performing bitwise OR operation to those particular enum class types + * that can perform those operations. + * + */ + inline memory_property operator|(memory_property p_lhs, + memory_property p_rhs) { + // Lets us truncate the underlying type of the enum (class) to allow + // it to be bitwise OR'd + using T = std::underlying_type_t; + return static_cast(static_cast(p_lhs) | + static_cast(p_rhs)); + } + + inline buffer_usage operator|(buffer_usage p_lhs, buffer_usage p_rhs) { + // Lets us truncate the underlying type of the enum (class) to allow + // it to be bitwise OR'd + using T = std::underlying_type_t; + return static_cast(static_cast(p_lhs) | + static_cast(p_rhs)); + } + + inline image_aspect_flags operator|(image_aspect_flags p_lhs, + image_aspect_flags p_rhs) { + // Lets us truncate the underlying type of the enum (class) to allow + // it to be bitwise OR'd + using T = std::underlying_type_t; + return static_cast(static_cast(p_lhs) | + static_cast(p_rhs)); + } + /** * @brief GPU memory is optimized differently for different tasks. An * image optimized for 'Transfer Destination' (filling with bytes) can diff --git a/vulkan-cpp/vertex_buffer.cppm b/vulkan-cpp/vertex_buffer.cppm index 1f66afe..5ff3d0c 100644 --- a/vulkan-cpp/vertex_buffer.cppm +++ b/vulkan-cpp/vertex_buffer.cppm @@ -17,55 +17,46 @@ export namespace vk { * @brief vulkan implementation for loading in vertices to a vulkan * buffer handle * - * This implementation automates handle in loading the vertices and its - * memories for it + * TODO: Example implementation. Should consider implementing your own + * vertex buffer handle specifically to your needs. + * + * If you'd like to use this for getting a head start to loading your + * vertices, this is there to provide an implementation example */ class vertex_buffer { public: vertex_buffer() = default; + vertex_buffer(const VkDevice& p_device, std::span p_vertices, - const vertex_params& p_vertex_info) + const buffer_parameters& p_params) : m_device(p_device) { - // 1. creating staging buffer - // uint32_t property_flags = memory_property::host_visible_bit | - // memory_property::host_cached_bit; uint32_t buffer_usage = - // buffer_usage::transfer_src_bit | - // buffer_usage::storage_buffer_bit; - + // Staging buffer + const uint32_t transfer = + static_cast(buffer_usage::transfer_src_bit); + const uint32_t storage = + static_cast(buffer_usage::storage_buffer_bit); + uint32_t usage = transfer | storage; buffer_parameters staging_buffer_params = { - .physical_memory_properties = - p_vertex_info.phsyical_memory_properties, - // .property_flags = (memory_property)property_flags, + .memory_mask = p_params.memory_mask, .property_flags = static_cast( memory_property::host_visible_bit | memory_property::host_cached_bit), - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, - .debug_name = p_vertex_info.debug_name.c_str(), + .usage = + static_cast(buffer_usage::transfer_src_bit) | + static_cast(buffer_usage::storage_buffer_bit), + .debug_name = p_params.debug_name, .vkSetDebugUtilsObjectNameEXT = - p_vertex_info.vkSetDebugUtilsObjectNameEXT + p_params.vkSetDebugUtilsObjectNameEXT }; buffer_stream staging_buffer( m_device, p_vertices.size_bytes(), staging_buffer_params); staging_buffer.transfer(p_vertices); - // 3.) Now creating our actual vertex buffer handler - buffer_parameters vertex_params = { - .physical_memory_properties = - p_vertex_info.phsyical_memory_properties, - .property_flags = memory_property::device_local_bit, - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - }; - m_vertex_handler = buffer_stream( - m_device, p_vertices.size_bytes(), vertex_params); - - // 4. Copy data from staging buffer to the actual vertex buffer - // itself! buffer_copy_info info = { .src = staging_buffer, - // .dst = m_vertex_handler }; - // copy(m_device, info, m_size_bytes); + // Creating vertex buffer handle + m_vertex_handler = + buffer_stream(m_device, p_vertices.size_bytes(), p_params); // 1. Retrieve the first queue // TODO: Use vk::device_queue for this @@ -86,13 +77,10 @@ export namespace vk { enumerate_command_info); copy_command_buffer.begin(command_usage::one_time_submit); - // VkBufferCopy copy_region{}; - // copy_region.size = (VkDeviceSize)m_size_bytes; - // vkCmdCopyBuffer( - // copy_command_buffer, staging_buffer, m_vertex_handler, 1, - // ©_region); + copy_command_buffer.copy_buffer( staging_buffer, m_vertex_handler, p_vertices.size_bytes()); + copy_command_buffer.end(); VkCommandBuffer temp = copy_command_buffer; VkSubmitInfo submit_info{}; @@ -103,8 +91,6 @@ export namespace vk { vkQueueSubmit(temp_graphics_queue, 1, &submit_info, nullptr); vkQueueWaitIdle(temp_graphics_queue); - // vkFreeCommandBuffers(, command_pool, 1, ©_cmd_buffer); - // vkDestroyCommandPool(driver, command_pool, nullptr); copy_command_buffer.destroy(); // 5. cleanup staging buffer -- no longer used