From d78b0c07094c087e473093794de00318452f4bc8 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:20:06 -0700 Subject: [PATCH 01/15] Fixed the transfer function to handle multi-data transfers --- vulkan-cpp/buffer.cppm | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/vulkan-cpp/buffer.cppm b/vulkan-cpp/buffer.cppm index e20c305..97a9d6d 100644 --- a/vulkan-cpp/buffer.cppm +++ b/vulkan-cpp/buffer.cppm @@ -5,6 +5,7 @@ module; #include #include #include +#include export module vk:buffer; @@ -140,8 +141,8 @@ export namespace vk { /** * @brief writing uniforms that are represented into bytes * @param p_data are the bytes to allow GPU to access + * * Example Usage: - * * ```C++ * buffers staging_buffer(logical_device, ...); * @@ -164,6 +165,43 @@ export namespace vk { vkUnmapMemory(m_device, m_device_memory); } + + /** + * @brief transferring multiple ranges of data under a single `vkMapMemory`/`vkUnmapMemory` invocation + * + * + * Example Usage: + * + * ```C++ + * // Under single vkMapMemory call writes all 6 contiguous chunks of data to one staging buffer + * std::array, 6> skybox_faces = m_skybox.faces(); + * staging_buffer.transfer(skybox_faces); + * ``` + * + */ + template requires std::ranges::forward_range + void transfer(Range&& p_range, uint64_t p_offset=0) { + uint64_t total_size = 0; + + for(const auto& data : p_range) { + total_size += std::span(data).size_bytes(); + } + + void* mapped=nullptr; + vk_check(vkMapMemory(m_device, m_device_memory, p_offset, total_size, 0, &mapped), "vkMapMemory"); + + auto* bytes = static_cast(mapped); + uint64_t offset = 0; + + for(const auto& data : p_range) { + auto s = std::span{data}; + std::memcpy(bytes + offset, s.data(), s.size_bytes()); + offset += s.size_bytes(); + } + + vkUnmapMemory(m_device, m_device_memory); + } + /** * @brief Transfers CPU-accessible data from the staging buffer to * the GPU resource image. From 8dc3d542c54965374347de39b8efc9fb921e3f58 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:20:49 -0700 Subject: [PATCH 02/15] Fixed skybox-specific implementation --- demos/13-skybox/skybox.cppm | 737 ++++++++++++++++++++++++++++++++++++ 1 file changed, 737 insertions(+) create mode 100644 demos/13-skybox/skybox.cppm diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm new file mode 100644 index 0000000..243bb39 --- /dev/null +++ b/demos/13-skybox/skybox.cppm @@ -0,0 +1,737 @@ +module; + +#include +#include +#include +#include +#include + +#include + +#define GLM_FORCE_RADIANS +#include +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + +export module skybox; + +import vk; + +constexpr uint64_t +image_layout(VkImageLayout p_old, VkImageLayout p_new) { + // Shift the old_layout into the high 32 bits, and combine with + // new_layout in the low 32 bits. + return (static_cast(p_old) << 32) | static_cast(p_new); +} + +void memory_barrier(const VkCommandBuffer& p_command, + const VkImage& p_image, + VkFormat p_format, + VkImageLayout p_old, + VkImageLayout p_new, + uint32_t p_layer_count = 1) { + // 1. Image Memory Barrier Initialization (using C++ Designated + // Initializers - C++20) + 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 = p_image, + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = p_layer_count, + }, + }; + + VkPipelineStageFlags source_stage = VK_PIPELINE_STAGE_NONE; + VkPipelineStageFlags dst_stages = VK_PIPELINE_STAGE_NONE; + + // 2. Aspect Mask Logic (Keep as if/else, but use helper + // function) + if (p_new == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || + vk::has_stencil_attachment(p_format)) { + + image_memory_barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_DEPTH_BIT; + + // Assuming has_stencil_attachment(p_format) is defined + // elsewhere works as the same as the if-statement, leaving + // it here for testing purposes + // image_memory_barrier.subresourceRange.aspectMask |= + // has_stencil_attachment(p_format) ? + // VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; + if (vk::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; + } + + // 3. Main Transition Logic using Combined Switch + const uint64_t current_layout = image_layout(p_old, p_new); + + switch (current_layout) { + + // UNDEFINED -> SHADER_READ_ONLY_OPTIMAL + case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, + 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; + break; + } + + // UNDEFINED -> GENERAL + case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, 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; + source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + } + + // UNDEFINED -> TRANSFER_DST_OPTIMAL + case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, + 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; + break; + } + + // SHADER_READ_ONLY_OPTIMAL -> TRANSFER_DST_OPTIMAL (Convert + // back from read-only to transferr) + case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 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; + break; + } + + // TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL (Convert + // from updateable texture to shader read-only) + case image_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 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; + break; + } + + // UNDEFINED -> DEPTH_STENCIL_ATTACHMENT_OPTIMAL (Convert + // depth texture from undefined state) + case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, + 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; + break; + } + + // SHADER_READ_ONLY_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL + // (Wait for render pass to complete - Note: This case is + // unusual but kept as per your original logic) + case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL): { + // Note: Your original code had conflicting + // re-assignments for source_stage/dst_stages here. The + // last pair of assignments is used. + image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.dstAccessMask = 0; + source_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + break; + } + + // SHADER_READ_ONLY_OPTIMAL -> COLOR_ATTACHMENT_OPTIMAL + // (Convert back from read-only to color attachment) + case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 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; + break; + } + + // COLOR_ATTACHMENT_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL + // (Convert from updateable color to shader read-only) + case image_layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + 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; + break; + } + + // SHADER_READ_ONLY_OPTIMAL -> + // DEPTH_STENCIL_ATTACHMENT_OPTIMAL (Convert back from + // read-only to depth attachment) + case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 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; + break; + } + + // DEPTH_STENCIL_ATTACHMENT_OPTIMAL -> + // SHADER_READ_ONLY_OPTIMAL (Convert from updateable depth + // texture to shader read-only) + case image_layout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + 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; + break; + } + + default: { + // Unhandled Transition + break; + } + } + + vkCmdPipelineBarrier(p_command, + source_stage, + dst_stages, + 0, // dependencyFlags + 0, + nullptr, + 0, + nullptr, + 1, + &image_memory_barrier); +} + +export struct skybox_uniform { + glm::mat4 proj_view; +}; + + +export class skybox_environment { +public: + skybox_environment(const VkDevice& p_device, const vk::physical_device& p_physical, std::span p_faces, const VkRenderPass& p_renderpass) : m_device(p_device) { + m_physical = p_physical; + m_renderpass = p_renderpass; + + if(p_faces.size() != 6) { + std::println("Cubemap requires 6 faces, received {} count of faces", p_faces.size()); + return; + } + + int w = 0; + int h = 0; + int channels = 0; + std::array, 6> faces{}; + + // faces_arr[0] = stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); + auto* face0 = stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); + int face_width = w; + int face_height = h; + // VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; + VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; + const uint32_t bytes_per_pixel = static_cast(vk::bytes_per_texture_format(image_format)); + auto size_bytes = face_width * face_height * bytes_per_pixel; + + faces[0] = std::span(reinterpret_cast(face0), size_bytes); + + for(size_t i = 1; i < 6; i++) { + // faces_arr[i] = stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); + auto* face_pixels = stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); + faces[i] = std::span(reinterpret_cast(face_pixels), size_bytes); + + if(faces[i].empty()) { + std::println("Could not load face: {}", p_faces[i]); + return; + } + + if (w != face_width || h != face_height) { + std::println( + "Cubemap faces must match dimensions. Face 0 is {}x{}, face {} is {}x{} ({})", + face_width, + face_height, + i, + w, + h, + p_faces[i]); + return; + } + } + + + const uint32_t width = static_cast(face_width); + const uint32_t height = static_cast(face_height); + const VkDeviceSize face_size_bytes = static_cast(width) * static_cast(height) * static_cast(bytes_per_pixel); + const VkDeviceSize total_size_bytes = face_size_bytes * 6; + + + // Creating vk::sample_image + + vk::image_params skybox_params = { + .extent = {.width = width, .height = height, .depth = 1}, + .format = image_format, + .memory_mask = p_physical.memory_properties(vk::memory_property::device_local_bit), + .image_flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + .view_type = VK_IMAGE_VIEW_TYPE_CUBE, + .layer_count = 6, + .array_layers = 6, + // .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + .usage = vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, + }; + m_skybox_image = vk::sample_image(m_device, skybox_params); + + + // perform staging buffer + vk::buffer_parameters staging_params = { + .memory_mask = p_physical.memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_coherent_bit), + .usage = vk::buffer_usage::transfer_src_bit, + }; + + vk::buffer staging(m_device, total_size_bytes, staging_params); + + staging.transfer(faces); + + for(size_t i = 0; i < faces.size(); i++) { + stbi_image_free(faces[i].data()); + } + + vk::command_params upload_params = { + .levels = vk::command_levels::primary, + .queue_index = 0, // graphics queue family index + .flags = vk::command_pool_flags::reset, + }; + vk::command_buffer upload_cmd(m_device, upload_params); + upload_cmd.begin(vk::command_usage::one_time_submit); + + // memory_barrier(upload_cmd, + // m_skybox_image, + // image_format, + // VK_IMAGE_LAYOUT_UNDEFINED, + // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + // 6); + m_skybox_image.memory_barrier(upload_cmd, image_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6); + + // Perform uploads + std::array regions; + + for(uint32_t face = 0; face < regions.size(); face++) { + regions[face] = { + .offset = static_cast(face_size_bytes * face), + .base_array_layer = face, // Copy this specific face region + .image_offset = { .width = 0, .height = 0, .depth = 0 }, + .image_extent = { .width = width, .height = height }, + }; + } + + staging.copy_to_image(upload_cmd, m_skybox_image, regions); + + memory_barrier(upload_cmd, + m_skybox_image, + image_format, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 6); + upload_cmd.end(); + + VkQueue graphics_queue = nullptr; + vkGetDeviceQueue(m_device, 0, 0, &graphics_queue); + const VkCommandBuffer cmd = upload_cmd; + VkSubmitInfo submit = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + vk::vk_check(vkQueueSubmit(graphics_queue, 1, &submit, nullptr), + "vkQueueSubmit(cubemap upload)"); + vk::vk_check(vkQueueWaitIdle(graphics_queue), + "vkQueueWaitIdle(cubemap upload)"); + + upload_cmd.destruct(); + staging.destruct(); + + create_skybox_pipeline(); + } + + + void create_skybox_pipeline() { + create_buffers(); + + std::array attribute_entries = { + vk::vertex_attribute_entry{ + .location = 0, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, position), + }, + vk::vertex_attribute_entry{ + .location = 1, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, color), + }, + vk::vertex_attribute_entry{ + .location = 2, + .format = vk::format::rgb32_sfloat, + .stride = offsetof(vk::vertex_input, normals), + }, + vk::vertex_attribute_entry{ + .location = 3, + .format = vk::format::rg32_sfloat, + .stride = offsetof(vk::vertex_input, uv), + } + }; + std::array attribute = { + vk::vertex_attribute{ + // layout (set = 0, binding = 0) + .binding = 0, + .entries = attribute_entries, + .stride = sizeof(vk::vertex_input), + .input_rate = vk::input_rate::vertex, + }, + }; + + const std::array sources = { + vk::shader_source{ + .filename = "shader_samples/sample7-skybox/skybox.vert.spv", + .stage = vk::shader_stage::vertex, + }, + vk::shader_source{ + .filename = "shader_samples/sample7-skybox/skybox.frag.spv", + .stage = vk::shader_stage::fragment, + }, + }; + + vk::shader_resource_info shader_info = { + .sources = sources, + }; + m_skybox_shaders = vk::shader_resource(m_device, shader_info); + 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::buffer_parameters uniform_params = { + .memory_mask = + m_physical.value().memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), + .usage = vk::buffer_usage::uniform_buffer_bit, + }; + m_skybox_ubo =vk::uniform_buffer(m_device, sizeof(skybox_uniform), uniform_params); + + skybox_uniform identity = { .proj_view = glm::mat4(1.0f) }; + identity.proj_view[1][1] *= -1; + + m_skybox_ubo.transfer( + std::span(&identity, 1)); + + // set=0 bindings: + // - binding 0: UBO (vertex) + // - binding 1: samplerCube (fragment) + std::array entries = { + vk::descriptor_entry{ + .type = vk::descriptor_type::uniform, + .binding_point = + vk::descriptor_binding_point{ .binding = 0, + .stage = vk::shader_stage::vertex }, + .descriptor_count = 1, + }, + vk::descriptor_entry{ + .type = vk::descriptor_type::combined_image_sampler, + .binding_point = + vk::descriptor_binding_point{ + .binding = 1, .stage = vk::shader_stage::fragment }, + .descriptor_count = 1, + }, + }; + + vk::descriptor_layout desc_layout = { + .slot = 0, + .max_sets = 1, + .entries = entries, + }; + m_skybox_descriptors = vk::descriptor_resource(m_device, desc_layout); + + const std::array ubo_writes = { + vk::write_buffer{ .buffer = m_skybox_ubo, + .offset = 0, + .range = static_cast(sizeof(skybox_uniform)) }, + }; + const vk::write_buffer_descriptor ubo_write_desc = { + .dst_binding = 0, + .uniforms = ubo_writes, + }; + + const std::array image_writes = { + vk::write_image{ + .sampler = m_skybox_image.sampler(), + .view = m_skybox_image.image_view(), + .layout = vk::image_layout::shader_read_only_optimal, + }, + }; + const vk::write_image_descriptor image_write_desc = { + .dst_binding = 1, + .sample_images = image_writes, + }; + + m_skybox_descriptors.update(std::span(&ubo_write_desc, 1), + std::span(&image_write_desc, 1)); + + const std::array layouts = { + m_skybox_descriptors.layout(), + }; + + const std::array blend_attachments = { + vk::color_blend_attachment_state{ .blend_enabled = false }, + }; + vk::color_blend_state blend_state = { + .logic_op_enable = false, + .logical_op = vk::logical_op::copy, + .attachments = blend_attachments, + .blend_constants = {}, + }; + + std::array dyn = { + vk::dynamic_state::viewport, + vk::dynamic_state::scissor, + }; + + // pipeline expects a non-const span + std::array pipeline_layouts = layouts; + + vk::pipeline_params pipe_info = { + .renderpass = m_renderpass, + .shader_modules = m_skybox_shaders.handles(), + .vertex_attributes = m_skybox_shaders.vertex_attributes(), // no vertex input + .vertex_bind_attributes = m_skybox_shaders.vertex_bind_attributes(), // no vertex input + .descriptor_layouts = pipeline_layouts, + .input_assembly = vk::input_assembly_state{ + .topology = vk::primitive_topology::triangle_list, + .primitive_restart_enable = false, + }, + .viewport = vk::viewport_state{ .viewport_count = 1, .scissor_count = 1 }, + .rasterization = vk::rasterization_state{ + .polygon_mode = vk::polygon_mode::fill, + .cull_mode = vk::cull_mode::front_bit, + // .cull_mode = vk::cull_mode::none, + // .front_face = vk::front_face::counter_clockwise, + .front_face = vk::front_face::clockwise, + .line_width = 1.f, + }, + .multisample = vk::multisample_state{}, + .color_blend = blend_state, + .depth_stencil_enabled = true, + .depth_stencil = vk::depth_stencil_state{ + .depth_test_enable = true, + .depth_write_enable = false, + .depth_compare_op = vk::compare_op::less_or_equal, + .depth_bounds_test_enable = false, + .stencil_test_enable = false, + }, + .dynamic_states = dyn, + }; + + m_skybox_pipeline = vk::pipeline(m_device, pipe_info); + } + + + void create_buffers() { + 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 + }; + // m_physical = instance_context::physical_driver(); + // m_device = instance_context::logical_device(); + + // std::vector vertices = { + // vk::vertex_input{ + // .position = {-1.0f, 1.0f, -1.0f,} + // }, + // }; + std::vector vertices = { + // Front Face + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + + // Left Face + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + + // Right Face + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + + // Back Face + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + + // Top Face + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + + // Bottom Face + vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}} + }; + + m_skybox_vbo_size = vertices.size(); + + // vk::vertex_params vbo_params = { + // .phsyical_memory_properties = vulkan::instance_context::physical_driver().memory_properties(), + // .vertices = vertices + // }; + vk::buffer_parameters vertex_params = { + .memory_mask = m_physical.value().memory_properties( + vk::memory_property::device_local_bit | + vk::memory_property::host_visible_bit), + .usage = vk::buffer_usage::transfer_dst_bit | + vk::buffer_usage::vertex_buffer_bit, + }; + + m_skybox_vbo = vk::vertex_buffer(m_device, vertices, vertex_params); + } + + void update_uniform(const skybox_uniform&) {} + + void bind(vk::command_buffer p_command) { + m_skybox_pipeline.bind(p_command); + std::array descriptors = { m_skybox_descriptors }; + p_command.bind_descriptors(m_skybox_pipeline.layout(), + VK_PIPELINE_BIND_POINT_GRAPHICS, + descriptors); + // m_skybox_vbo.bind(p_command); + + std::array skybox_buffers = { m_skybox_vbo }; + uint64_t offset = 0; + p_command.bind_vertex_buffers(skybox_buffers, + std::span(&offset, 1)); + } + + void draw(const vk::command_buffer& p_command) { + vkCmdDraw(p_command, m_skybox_vbo_size, 1, 0, 0); + } + + void destruct() { + m_skybox_ubo.destruct(); + m_skybox_image.destruct(); + m_skybox_shaders.destruct(); + m_skybox_pipeline.destruct(); + + m_skybox_vbo.destruct(); + m_skybox_descriptors.destruct(); + } + +private: + vk::uniform_buffer m_skybox_ubo; + VkDevice m_device=nullptr; + std::optional m_physical; + vk::sample_image m_skybox_image; + vk::shader_resource m_skybox_shaders; + vk::pipeline m_skybox_pipeline; + vk::vertex_buffer m_skybox_vbo; + vk::descriptor_resource m_skybox_descriptors; + VkRenderPass m_renderpass; + uint32_t m_skybox_vbo_size; +}; \ No newline at end of file From d1b9c2f6f95c5b0fa33e27f0cf97c46e8936cecc Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:21:27 -0700 Subject: [PATCH 03/15] Exposed layer count parameter to sample_iamge::memory_barrier --- vulkan-cpp/sample_image.cppm | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/vulkan-cpp/sample_image.cppm b/vulkan-cpp/sample_image.cppm index ec5231c..d5a6324 100644 --- a/vulkan-cpp/sample_image.cppm +++ b/vulkan-cpp/sample_image.cppm @@ -269,10 +269,9 @@ export namespace vk { VkFormat p_format, VkImageLayout p_old, VkImageLayout p_new, - uint32_t p_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT) { + uint32_t p_aspect_mask = VK_IMAGE_ASPECT_COLOR_BIT, + uint32_t p_layer_count = 1) { - // 1. Image Memory Barrier Initialization (using C++ Designated - // Initializers - C++20) VkImageMemoryBarrier image_memory_barrier = { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -283,20 +282,18 @@ export namespace vk { .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = m_image, - .subresourceRange = { .aspectMask = - static_cast( - p_aspect_mask), - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1 } + .subresourceRange = { + .aspectMask = static_cast(p_aspect_mask), + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = p_layer_count, + }, }; VkPipelineStageFlags source_stage = VK_PIPELINE_STAGE_NONE; VkPipelineStageFlags dst_stages = VK_PIPELINE_STAGE_NONE; - // 2. Aspect Mask Logic (Keep as if/else, but use helper - // function) if (p_new == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || has_stencil_attachment(p_format)) { From f22e72d4d8e58f0548436a71235791d97d8baa8e Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:23:33 -0700 Subject: [PATCH 04/15] Updated demo 13 application to use skybox demo --- demos/13-skybox/application.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/demos/13-skybox/application.cpp b/demos/13-skybox/application.cpp index 1e37459..ee82d4c 100644 --- a/demos/13-skybox/application.cpp +++ b/demos/13-skybox/application.cpp @@ -30,7 +30,8 @@ #endif import vk; -import environment_map; +// import environment_map; +import skybox; static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( @@ -298,11 +299,21 @@ main() { // gets set with the renderpass std::array color = { 0.f, 0.5f, 0.5f, 1.f }; - environment_map skybox = environment_map( - logical_device, - std::filesystem::path("asset_samples/skybox/monkstown_castle_4k.hdr"), - physical_device, - main_renderpass); + // environment_map skybox = environment_map( + // logical_device, + // std::filesystem::path("asset_samples/skybox/monkstown_castle_4k.hdr"), + // physical_device, + // main_renderpass); + + std::array faces = { + "asset_samples/skybox/right.jpg", + "asset_samples/skybox/left.jpg", + "asset_samples/skybox/top.jpg", + "asset_samples/skybox/bottom.jpg", + "asset_samples/skybox/front.jpg", + "asset_samples/skybox/back.jpg" + }; + skybox_environment skybox = skybox_environment(logical_device, physical_device, faces, main_renderpass); float field_of_view = 45.f; glm::vec3 position = { 3.5f, 4.90f, 36.40f }; From 66d91de1aac9703e55a388a2792f1e1b6e653f3c Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:28:04 -0700 Subject: [PATCH 05/15] Update skybox shader to use the cubemap shaders --- shader_samples/sample7-skybox/skybox.frag | 78 +++++++++--------- shader_samples/sample7-skybox/skybox.frag.spv | Bin 1908 -> 636 bytes 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/shader_samples/sample7-skybox/skybox.frag b/shader_samples/sample7-skybox/skybox.frag index ffcc2b8..b146b1f 100644 --- a/shader_samples/sample7-skybox/skybox.frag +++ b/shader_samples/sample7-skybox/skybox.frag @@ -1,56 +1,56 @@ -// #version 450 +#version 450 -// layout (location=0) in vec3 TexCoords; +layout (location=0) in vec3 TexCoords; -// layout (location=0) out vec4 out_Color; +layout (location=0) out vec4 out_Color; -// layout(set = 0, binding = 1) uniform samplerCube cubeSampler; +layout(set = 0, binding = 1) uniform samplerCube cubeSampler; -// void main() { -// out_Color = texture(cubeSampler, TexCoords); -// } -#version 450 +void main() { + out_Color = texture(cubeSampler, TexCoords); +} +// #version 450 -layout (location=0) in vec3 TexCoords; // Direction vector from vertex shader +// layout (location=0) in vec3 TexCoords; // Direction vector from vertex shader -layout (location=0) out vec4 out_Color; +// layout (location=0) out vec4 out_Color; -layout(set = 0, binding = 1) uniform sampler2D cubeSampler; +// layout(set = 0, binding = 1) uniform samplerCube cubeSampler; -// Constants to convert direction to spherical UVs -const vec2 invAtan = vec2(0.1591, 0.3183); // 1.0 / (2.0 * PI), 1.0 / PI +// // Constants to convert direction to spherical UVs +// const vec2 invAtan = vec2(0.1591, 0.3183); // 1.0 / (2.0 * PI), 1.0 / PI -vec2 SampleEquirectangular(vec3 v) { - // Normalize the input direction - // We only need to grab the point of the vector - vec3 direction = normalize(v); +// vec2 SampleEquirectangular(vec3 v) { +// // Normalize the input direction +// // We only need to grab the point of the vector +// vec3 direction = normalize(v); - // Calculate spherical coordinates - // atan(direction.z, direction.x) = atan(-PI, PI) - // atan(z, x) returns -PI to PI - // asin(y) returns -PI/2 to PI/2 +// // Calculate spherical coordinates +// // atan(direction.z, direction.x) = atan(-PI, PI) +// // atan(z, x) returns -PI to PI +// // asin(y) returns -PI/2 to PI/2 - vec2 uv = vec2(atan(direction.z, direction.x), asin(direction.y)); +// vec2 uv = vec2(atan(direction.z, direction.x), asin(direction.y)); - // Inverses the uv to be between the [0.0, 1.0] range - uv *= invAtan; - uv += 0.5; - return uv; -} +// // Inverses the uv to be between the [0.0, 1.0] range +// uv *= invAtan; +// uv += 0.5; +// return uv; +// } -void main() { - // Get the 2D UV coordinate from the 3D direction - vec2 uv = SampleEquirectangular(TexCoords); +// void main() { +// // Get the 2D UV coordinate from the 3D direction +// vec2 uv = SampleEquirectangular(TexCoords); - // Sample the HDR map - vec3 color = texture(cubeSampler, uv).rgb; +// // Sample the HDR map +// vec3 color = texture(cubeSampler, uv).rgb; - // Simple Reinhard tone mapping (HDR values can be > 1.0, - // so we must compress them to [0, 1] for the screen) - color = color / (color + vec3(1.0)); +// // Simple Reinhard tone mapping (HDR values can be > 1.0, +// // so we must compress them to [0, 1] for the screen) +// color = color / (color + vec3(1.0)); - // Gamma correction - color = pow(color, vec3(1.0/2.2)); +// // Gamma correction +// color = pow(color, vec3(1.0/2.2)); - out_Color = vec4(color, 1.0); -} \ No newline at end of file +// out_Color = vec4(color, 1.0); +// } \ No newline at end of file diff --git a/shader_samples/sample7-skybox/skybox.frag.spv b/shader_samples/sample7-skybox/skybox.frag.spv index 1dd6d547dd9f76d4ca8eb9706730c42847379d45..c9e20ff282772eb96aafde76b769479bbdcc9fab 100644 GIT binary patch literal 636 zcmYk2T}#4n6vj{L)HL5RiU>mMUb?6VqHZL1Gw{No%VFCt2Ghx$QeUr6)lJazn+hIy zcFuYB{LfptbXzk!vZl4{%(m9Ek{AJ&*B9? zj)k7kl@4AF6qQxg>_?9()-c!e-O0rD-uz&>^jG)aowNt&r2TuVM!e@4^Ue3?X9!7<-^^fgY?ELXEYpM*oidz8?Cna%#b&Cub+PM_q}1m=$A&UonpSi&%QJ d1?rPi<5b}Nhg9s;7tp}Jssg*g{i(x~@DF(!H0J;S literal 1908 zcmYk6+fGwa5QZ0r%|Xs0Ac9gr5fwq@EYgCA^-u+aiI+`nx28#HYiWtz>4k~U;bVB| z)0p@I+-Us2z1Ns7GR(|B|C(8AX6?4M&UQN2;(A=4d+FjeORj&NL zQOeckPv0$XITz?0J>r~70{!(%`;rw7PyD=eLj>&G|Lq{U*X!`(UZqkioJ8DqM?$1mM2=IzS|GogN3KJ%gu_3@cI*qVAmuc^%fI};K}!)0E)UF!vNd@zHlB^ed@saPc>N@9)Xx+X5#}ZZP)JpA8AU!Dm*R5;$+PKR$oV0YA{|quv9F zKFLU=qPcJ<@^d!gcD8NVVCE;i|3&%9wJQPJk$m&mL)q+$V{y(Hi|@Oq7&&{DfgFz| zyoE~=V$8(OLdA%BNx)c!J(mqeKEpB|AP4xa zYV$phO2GKe$-~{pB;dvwmkoz+-8l3UoIbN>LP9Mt_7k`Lmu0h`-v-;y?;JR1W&f0f z{hW>1q3=yXHo1v&e(s!*kcZw~m4JQq{A(VYl{P+@@vqB`0~XqW2><{9 From 715a3cadee802e616bef502a9136111b924a39d5 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:29:08 -0700 Subject: [PATCH 06/15] Updated skybox.cppm to remove raw vulkan API usage to using vulkan-cpp equivalent --- demos/13-skybox/skybox.cppm | 656 +++++++++++++++--------------------- 1 file changed, 267 insertions(+), 389 deletions(-) diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm index 243bb39..fd4054f 100644 --- a/demos/13-skybox/skybox.cppm +++ b/demos/13-skybox/skybox.cppm @@ -18,233 +18,23 @@ export module skybox; import vk; -constexpr uint64_t -image_layout(VkImageLayout p_old, VkImageLayout p_new) { - // Shift the old_layout into the high 32 bits, and combine with - // new_layout in the low 32 bits. - return (static_cast(p_old) << 32) | static_cast(p_new); -} - -void memory_barrier(const VkCommandBuffer& p_command, - const VkImage& p_image, - VkFormat p_format, - VkImageLayout p_old, - VkImageLayout p_new, - uint32_t p_layer_count = 1) { - // 1. Image Memory Barrier Initialization (using C++ Designated - // Initializers - C++20) - 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 = p_image, - .subresourceRange = { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = p_layer_count, - }, - }; - - VkPipelineStageFlags source_stage = VK_PIPELINE_STAGE_NONE; - VkPipelineStageFlags dst_stages = VK_PIPELINE_STAGE_NONE; - - // 2. Aspect Mask Logic (Keep as if/else, but use helper - // function) - if (p_new == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL || - vk::has_stencil_attachment(p_format)) { - - image_memory_barrier.subresourceRange.aspectMask = - VK_IMAGE_ASPECT_DEPTH_BIT; - - // Assuming has_stencil_attachment(p_format) is defined - // elsewhere works as the same as the if-statement, leaving - // it here for testing purposes - // image_memory_barrier.subresourceRange.aspectMask |= - // has_stencil_attachment(p_format) ? - // VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT; - if (vk::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; - } - - // 3. Main Transition Logic using Combined Switch - const uint64_t current_layout = image_layout(p_old, p_new); - - switch (current_layout) { - - // UNDEFINED -> SHADER_READ_ONLY_OPTIMAL - case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, - 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; - break; - } - - // UNDEFINED -> GENERAL - case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, 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; - source_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - } - - // UNDEFINED -> TRANSFER_DST_OPTIMAL - case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, - 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; - break; - } - - // SHADER_READ_ONLY_OPTIMAL -> TRANSFER_DST_OPTIMAL (Convert - // back from read-only to transferr) - case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - 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; - break; - } - - // TRANSFER_DST_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL (Convert - // from updateable texture to shader read-only) - case image_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - 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; - break; - } - - // UNDEFINED -> DEPTH_STENCIL_ATTACHMENT_OPTIMAL (Convert - // depth texture from undefined state) - case image_layout(VK_IMAGE_LAYOUT_UNDEFINED, - 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; - break; - } - - // SHADER_READ_ONLY_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL - // (Wait for render pass to complete - Note: This case is - // unusual but kept as per your original logic) - case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL): { - // Note: Your original code had conflicting - // re-assignments for source_stage/dst_stages here. The - // last pair of assignments is used. - image_memory_barrier.srcAccessMask = 0; - image_memory_barrier.dstAccessMask = 0; - source_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - dst_stages = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; - break; - } - - // SHADER_READ_ONLY_OPTIMAL -> COLOR_ATTACHMENT_OPTIMAL - // (Convert back from read-only to color attachment) - case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - 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; - break; - } - - // COLOR_ATTACHMENT_OPTIMAL -> SHADER_READ_ONLY_OPTIMAL - // (Convert from updateable color to shader read-only) - case image_layout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - 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; - break; - } - - // SHADER_READ_ONLY_OPTIMAL -> - // DEPTH_STENCIL_ATTACHMENT_OPTIMAL (Convert back from - // read-only to depth attachment) - case image_layout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - 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; - break; - } - - // DEPTH_STENCIL_ATTACHMENT_OPTIMAL -> - // SHADER_READ_ONLY_OPTIMAL (Convert from updateable depth - // texture to shader read-only) - case image_layout(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, - 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; - break; - } - - default: { - // Unhandled Transition - break; - } - } - - vkCmdPipelineBarrier(p_command, - source_stage, - dst_stages, - 0, // dependencyFlags - 0, - nullptr, - 0, - nullptr, - 1, - &image_memory_barrier); -} - export struct skybox_uniform { glm::mat4 proj_view; }; - export class skybox_environment { public: - skybox_environment(const VkDevice& p_device, const vk::physical_device& p_physical, std::span p_faces, const VkRenderPass& p_renderpass) : m_device(p_device) { + skybox_environment(const VkDevice& p_device, + const vk::physical_device& p_physical, + std::span p_faces, + const VkRenderPass& p_renderpass) + : m_device(p_device) { m_physical = p_physical; m_renderpass = p_renderpass; - if(p_faces.size() != 6) { - std::println("Cubemap requires 6 faces, received {} count of faces", p_faces.size()); + if (p_faces.size() != 6) { + std::println("Cubemap requires 6 faces, received {} count of faces", + p_faces.size()); return; } @@ -252,64 +42,65 @@ public: int h = 0; int channels = 0; std::array, 6> faces{}; - - // faces_arr[0] = stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); - auto* face0 = stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); + + auto* face0 = + stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); int face_width = w; int face_height = h; // VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; - const uint32_t bytes_per_pixel = static_cast(vk::bytes_per_texture_format(image_format)); + const uint32_t bytes_per_pixel = + static_cast(vk::bytes_per_texture_format(image_format)); auto size_bytes = face_width * face_height * bytes_per_pixel; - faces[0] = std::span(reinterpret_cast(face0), size_bytes); + faces[0] = + std::span(reinterpret_cast(face0), size_bytes); - for(size_t i = 1; i < 6; i++) { - // faces_arr[i] = stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); - auto* face_pixels = stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); - faces[i] = std::span(reinterpret_cast(face_pixels), size_bytes); + for (size_t i = 1; i < faces.size(); i++) { + auto* face_pixels = + stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); + faces[i] = std::span( + reinterpret_cast(face_pixels), size_bytes); - if(faces[i].empty()) { + if (faces[i].empty()) { std::println("Could not load face: {}", p_faces[i]); return; } if (w != face_width || h != face_height) { - std::println( - "Cubemap faces must match dimensions. Face 0 is {}x{}, face {} is {}x{} ({})", - face_width, - face_height, - i, - w, - h, - p_faces[i]); + std::println("Cubemap faces must match dimensions. Face 0 is " + "{}x{}, face {} is {}x{} ({})", + face_width, + face_height, + i, + w, + h, + p_faces[i]); return; } } - const uint32_t width = static_cast(face_width); const uint32_t height = static_cast(face_height); - const VkDeviceSize face_size_bytes = static_cast(width) * static_cast(height) * static_cast(bytes_per_pixel); + const VkDeviceSize face_size_bytes = + static_cast(width) * static_cast(height) * + static_cast(bytes_per_pixel); const VkDeviceSize total_size_bytes = face_size_bytes * 6; - - // Creating vk::sample_image - vk::image_params skybox_params = { - .extent = {.width = width, .height = height, .depth = 1}, + .extent = { .width = width, .height = height, .depth = 1 }, .format = image_format, - .memory_mask = p_physical.memory_properties(vk::memory_property::device_local_bit), + .memory_mask = p_physical.memory_properties( + vk::memory_property::device_local_bit), .image_flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, .view_type = VK_IMAGE_VIEW_TYPE_CUBE, .layer_count = 6, .array_layers = 6, - // .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, - .usage = vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, + .usage = + vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, }; m_skybox_image = vk::sample_image(m_device, skybox_params); - // perform staging buffer vk::buffer_parameters staging_params = { .memory_mask = p_physical.memory_properties( @@ -322,7 +113,7 @@ public: staging.transfer(faces); - for(size_t i = 0; i < faces.size(); i++) { + for (size_t i = 0; i < faces.size(); i++) { stbi_image_free(faces[i].data()); } @@ -334,18 +125,16 @@ public: vk::command_buffer upload_cmd(m_device, upload_params); upload_cmd.begin(vk::command_usage::one_time_submit); - // memory_barrier(upload_cmd, - // m_skybox_image, - // image_format, - // VK_IMAGE_LAYOUT_UNDEFINED, - // VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - // 6); - m_skybox_image.memory_barrier(upload_cmd, image_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 6); - + m_skybox_image.memory_barrier(upload_cmd, + image_format, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + 6); + // Perform uploads std::array regions; - for(uint32_t face = 0; face < regions.size(); face++) { + for (uint32_t face = 0; face < regions.size(); face++) { regions[face] = { .offset = static_cast(face_size_bytes * face), .base_array_layer = face, // Copy this specific face region @@ -355,13 +144,12 @@ public: } staging.copy_to_image(upload_cmd, m_skybox_image, regions); - - memory_barrier(upload_cmd, - m_skybox_image, - image_format, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - 6); + + m_skybox_image.memory_barrier(upload_cmd, + image_format, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + 6); upload_cmd.end(); VkQueue graphics_queue = nullptr; @@ -379,9 +167,9 @@ public: .pSignalSemaphores = nullptr, }; vk::vk_check(vkQueueSubmit(graphics_queue, 1, &submit, nullptr), - "vkQueueSubmit(cubemap upload)"); + "vkQueueSubmit(cubemap upload)"); vk::vk_check(vkQueueWaitIdle(graphics_queue), - "vkQueueWaitIdle(cubemap upload)"); + "vkQueueWaitIdle(cubemap upload)"); upload_cmd.destruct(); staging.destruct(); @@ -389,7 +177,6 @@ public: create_skybox_pipeline(); } - void create_skybox_pipeline() { create_buffers(); @@ -449,13 +236,13 @@ public: // .vkSetDebugUtilsObjectNameEXT = nullptr, // }; vk::buffer_parameters uniform_params = { - .memory_mask = - m_physical.value().memory_properties( - vk::memory_property::host_visible_bit | - vk::memory_property::host_cached_bit), + .memory_mask = m_physical.value().memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_cached_bit), .usage = vk::buffer_usage::uniform_buffer_bit, }; - m_skybox_ubo =vk::uniform_buffer(m_device, sizeof(skybox_uniform), uniform_params); + m_skybox_ubo = + vk::uniform_buffer(m_device, sizeof(skybox_uniform), uniform_params); skybox_uniform identity = { .proj_view = glm::mat4(1.0f) }; identity.proj_view[1][1] *= -1; @@ -468,18 +255,18 @@ public: // - binding 1: samplerCube (fragment) std::array entries = { vk::descriptor_entry{ - .type = vk::descriptor_type::uniform, - .binding_point = - vk::descriptor_binding_point{ .binding = 0, - .stage = vk::shader_stage::vertex }, - .descriptor_count = 1, + .type = vk::descriptor_type::uniform, + .binding_point = + vk::descriptor_binding_point{ + .binding = 0, .stage = vk::shader_stage::vertex }, + .descriptor_count = 1, }, vk::descriptor_entry{ - .type = vk::descriptor_type::combined_image_sampler, - .binding_point = + .type = vk::descriptor_type::combined_image_sampler, + .binding_point = vk::descriptor_binding_point{ - .binding = 1, .stage = vk::shader_stage::fragment }, - .descriptor_count = 1, + .binding = 1, .stage = vk::shader_stage::fragment }, + .descriptor_count = 1, }, }; @@ -492,8 +279,9 @@ public: const std::array ubo_writes = { vk::write_buffer{ .buffer = m_skybox_ubo, - .offset = 0, - .range = static_cast(sizeof(skybox_uniform)) }, + .offset = 0, + .range = + static_cast(sizeof(skybox_uniform)) }, }; const vk::write_buffer_descriptor ubo_write_desc = { .dst_binding = 0, @@ -502,9 +290,9 @@ public: const std::array image_writes = { vk::write_image{ - .sampler = m_skybox_image.sampler(), - .view = m_skybox_image.image_view(), - .layout = vk::image_layout::shader_read_only_optimal, + .sampler = m_skybox_image.sampler(), + .view = m_skybox_image.image_view(), + .layout = vk::image_layout::shader_read_only_optimal, }, }; const vk::write_image_descriptor image_write_desc = { @@ -519,9 +307,10 @@ public: m_skybox_descriptors.layout(), }; - const std::array blend_attachments = { - vk::color_blend_attachment_state{ .blend_enabled = false }, - }; + const std::array + blend_attachments = { + vk::color_blend_attachment_state{ .blend_enabled = false }, + }; vk::color_blend_state blend_state = { .logic_op_enable = false, .logical_op = vk::logical_op::copy, @@ -540,83 +329,64 @@ public: vk::pipeline_params pipe_info = { .renderpass = m_renderpass, .shader_modules = m_skybox_shaders.handles(), - .vertex_attributes = m_skybox_shaders.vertex_attributes(), // no vertex input - .vertex_bind_attributes = m_skybox_shaders.vertex_bind_attributes(), // no vertex input + .vertex_attributes = + m_skybox_shaders.vertex_attributes(), // no vertex input + .vertex_bind_attributes = + m_skybox_shaders.vertex_bind_attributes(), // no vertex input .descriptor_layouts = pipeline_layouts, - .input_assembly = vk::input_assembly_state{ - .topology = vk::primitive_topology::triangle_list, - .primitive_restart_enable = false, - }, - .viewport = vk::viewport_state{ .viewport_count = 1, .scissor_count = 1 }, - .rasterization = vk::rasterization_state{ - .polygon_mode = vk::polygon_mode::fill, + .input_assembly = + vk::input_assembly_state{ + .topology = vk::primitive_topology::triangle_list, + .primitive_restart_enable = false, + }, + .viewport = + vk::viewport_state{ .viewport_count = 1, .scissor_count = 1 }, + .rasterization = + vk::rasterization_state{ + .polygon_mode = vk::polygon_mode::fill, .cull_mode = vk::cull_mode::front_bit, - // .cull_mode = vk::cull_mode::none, - // .front_face = vk::front_face::counter_clockwise, + // .cull_mode = vk::cull_mode::none, + // .front_face = vk::front_face::counter_clockwise, .front_face = vk::front_face::clockwise, .line_width = 1.f, - }, + }, .multisample = vk::multisample_state{}, .color_blend = blend_state, .depth_stencil_enabled = true, - .depth_stencil = vk::depth_stencil_state{ - .depth_test_enable = true, - .depth_write_enable = false, - .depth_compare_op = vk::compare_op::less_or_equal, - .depth_bounds_test_enable = false, - .stencil_test_enable = false, - }, + .depth_stencil = + vk::depth_stencil_state{ + .depth_test_enable = true, + .depth_write_enable = false, + .depth_compare_op = vk::compare_op::less_or_equal, + .depth_bounds_test_enable = false, + .stencil_test_enable = false, + }, .dynamic_states = dyn, }; m_skybox_pipeline = vk::pipeline(m_device, pipe_info); } - void create_buffers() { 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 + // 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 }; // m_physical = instance_context::physical_driver(); // m_device = instance_context::logical_device(); @@ -628,58 +398,167 @@ public: // }; std::vector vertices = { // Front Face - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, // Left Face - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, // Right Face - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, // Back Face - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, // Top Face - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, // Bottom Face - vk::vertex_input{{-1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{-1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}}, - vk::vertex_input{{ 1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f}} + vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } }, + vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f } } }; m_skybox_vbo_size = vertices.size(); // vk::vertex_params vbo_params = { - // .phsyical_memory_properties = vulkan::instance_context::physical_driver().memory_properties(), + // .phsyical_memory_properties = + // vulkan::instance_context::physical_driver().memory_properties(), // .vertices = vertices // }; vk::buffer_parameters vertex_params = { @@ -701,7 +580,6 @@ public: p_command.bind_descriptors(m_skybox_pipeline.layout(), VK_PIPELINE_BIND_POINT_GRAPHICS, descriptors); - // m_skybox_vbo.bind(p_command); std::array skybox_buffers = { m_skybox_vbo }; uint64_t offset = 0; @@ -725,7 +603,7 @@ public: private: vk::uniform_buffer m_skybox_ubo; - VkDevice m_device=nullptr; + VkDevice m_device = nullptr; std::optional m_physical; vk::sample_image m_skybox_image; vk::shader_resource m_skybox_shaders; From fa09180d5f84b197f918402a65be8f33cd18ecfd Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 16:57:30 -0700 Subject: [PATCH 07/15] Added documentation comments for the new transfer API --- vulkan-cpp/buffer.cppm | 66 +++++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/vulkan-cpp/buffer.cppm b/vulkan-cpp/buffer.cppm index 97a9d6d..157962d 100644 --- a/vulkan-cpp/buffer.cppm +++ b/vulkan-cpp/buffer.cppm @@ -141,7 +141,7 @@ export namespace vk { /** * @brief writing uniforms that are represented into bytes * @param p_data are the bytes to allow GPU to access - * + * * Example Usage: * ```C++ * buffers staging_buffer(logical_device, ...); @@ -165,36 +165,68 @@ export namespace vk { vkUnmapMemory(m_device, m_device_memory); } - /** - * @brief transferring multiple ranges of data under a single `vkMapMemory`/`vkUnmapMemory` invocation - * - * + * @brief Transfer CPU-accessible data from a sub-range of buffers + * into a single underlying staging allocation + * + * Single mapping block to gather multiple sub-range of data from + * the host then transferring them sequentially into device-visible + * memory, calculating the offsets with the bytes of those + * sub-ranges. + * + * @tparam Range must satisfies the forward_range concept + * @param p_range collection of data sources (containers of spans, + * vectors, arrays, etc) to transfer. + * @param p_offset is the base offset into the Vulkan device memory + * allocation where the mapping begins + * + * @brief Requirements to perform this operation + * - p_range: Must be a valid contiguous buffer (std::span) to + * determine their underlying bytes. + * - alignment: sub-ranges are assumed to be tightly packed by + * default. + * + * @brief Layout of data stored in host-visible memory during + * serialization + * + * [ base offset ........................................ end ] + * |--- Data 0 --|-- Data 1 --|-- Data 2 --| ... |-- Data N --| + * \____________/\___________/\___________/ \___________/ + * offset = 0 size[0] size[0]+size[1] + * * Example Usage: - * + * * ```C++ - * // Under single vkMapMemory call writes all 6 contiguous chunks of data to one staging buffer - * std::array, 6> skybox_faces = m_skybox.faces(); + * // Under single vkMapMemory call writes all 6 contiguous chunks + * of data to one staging buffer std::array, 6> + * skybox_faces = m_skybox.faces(); * staging_buffer.transfer(skybox_faces); * ``` - * - */ - template requires std::ranges::forward_range - void transfer(Range&& p_range, uint64_t p_offset=0) { + * + */ + template + requires std::ranges::forward_range + void transfer(Range&& p_range, uint64_t p_offset = 0) { uint64_t total_size = 0; - for(const auto& data : p_range) { + for (const auto& data : p_range) { total_size += std::span(data).size_bytes(); } - void* mapped=nullptr; - vk_check(vkMapMemory(m_device, m_device_memory, p_offset, total_size, 0, &mapped), "vkMapMemory"); + void* mapped = nullptr; + vk_check(vkMapMemory(m_device, + m_device_memory, + p_offset, + total_size, + 0, + &mapped), + "vkMapMemory"); auto* bytes = static_cast(mapped); uint64_t offset = 0; - for(const auto& data : p_range) { - auto s = std::span{data}; + for (const auto& data : p_range) { + auto s = std::span{ data }; std::memcpy(bytes + offset, s.data(), s.size_bytes()); offset += s.size_bytes(); } From 5e99fbdb9190c71e945d11148eb146aa548378a3 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Wed, 1 Jul 2026 17:29:58 -0700 Subject: [PATCH 08/15] Fix some formatting issues --- demos/13-skybox/application.cpp | 12 +++++------- demos/13-skybox/skybox.cppm | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/demos/13-skybox/application.cpp b/demos/13-skybox/application.cpp index ee82d4c..f15054f 100644 --- a/demos/13-skybox/application.cpp +++ b/demos/13-skybox/application.cpp @@ -306,14 +306,12 @@ main() { // main_renderpass); std::array faces = { - "asset_samples/skybox/right.jpg", - "asset_samples/skybox/left.jpg", - "asset_samples/skybox/top.jpg", - "asset_samples/skybox/bottom.jpg", - "asset_samples/skybox/front.jpg", - "asset_samples/skybox/back.jpg" + "asset_samples/skybox/right.jpg", "asset_samples/skybox/left.jpg", + "asset_samples/skybox/top.jpg", "asset_samples/skybox/bottom.jpg", + "asset_samples/skybox/front.jpg", "asset_samples/skybox/back.jpg" }; - skybox_environment skybox = skybox_environment(logical_device, physical_device, faces, main_renderpass); + skybox_environment skybox = skybox_environment( + logical_device, physical_device, faces, main_renderpass); float field_of_view = 45.f; glm::vec3 position = { 3.5f, 4.90f, 36.40f }; diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm index fd4054f..d70742c 100644 --- a/demos/13-skybox/skybox.cppm +++ b/demos/13-skybox/skybox.cppm @@ -42,7 +42,7 @@ public: int h = 0; int channels = 0; std::array, 6> faces{}; - + auto* face0 = stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); int face_width = w; From e3a0863641a0170ad5fd7edc05b81c147378f81f Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 00:55:55 -0700 Subject: [PATCH 09/15] Fixed the memory_barrier params specified and add to_bytes to return span --- demos/13-skybox/skybox.cppm | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm index d70742c..57cdf1a 100644 --- a/demos/13-skybox/skybox.cppm +++ b/demos/13-skybox/skybox.cppm @@ -22,6 +22,13 @@ export struct skybox_uniform { glm::mat4 proj_view; }; +// Converts raw stbi image stbi_uc* raw pixels pointers to a span +std::span +to_bytes(stbi_uc* p_pixels, uint32_t p_image_size) { + return std::span(reinterpret_cast(p_pixels), + p_image_size); +} + export class skybox_environment { public: skybox_environment(const VkDevice& p_device, @@ -53,14 +60,12 @@ public: static_cast(vk::bytes_per_texture_format(image_format)); auto size_bytes = face_width * face_height * bytes_per_pixel; - faces[0] = - std::span(reinterpret_cast(face0), size_bytes); + faces[0] = to_bytes(face0, size_bytes); for (size_t i = 1; i < faces.size(); i++) { auto* face_pixels = stbi_load(p_faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); - faces[i] = std::span( - reinterpret_cast(face_pixels), size_bytes); + faces[i] = to_bytes(face_pixels, size_bytes); if (faces[i].empty()) { std::println("Could not load face: {}", p_faces[i]); @@ -125,10 +130,14 @@ public: vk::command_buffer upload_cmd(m_device, upload_params); upload_cmd.begin(vk::command_usage::one_time_submit); + // NOTE: explicitly set VK_IMAGE_ASPECT_COLOR_BIT to ensure the wrong + // aspect flags are not being set when using + // vk::sample_image::memory_barrier m_skybox_image.memory_barrier(upload_cmd, image_format, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_ASPECT_COLOR_BIT, 6); // Perform uploads @@ -149,6 +158,7 @@ public: image_format, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + VK_IMAGE_ASPECT_COLOR_BIT, 6); upload_cmd.end(); From eea7d35b41bdd384cac7df0347ae8831fdf23dae Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 01:21:02 -0700 Subject: [PATCH 10/15] Added testing camera controls --- demos/13-skybox/application.cpp | 76 ++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 10 deletions(-) diff --git a/demos/13-skybox/application.cpp b/demos/13-skybox/application.cpp index f15054f..cd2e47a 100644 --- a/demos/13-skybox/application.cpp +++ b/demos/13-skybox/application.cpp @@ -315,8 +315,16 @@ main() { float field_of_view = 45.f; glm::vec3 position = { 3.5f, 4.90f, 36.40f }; + + glm::vec3 rotation = glm::vec3(0.f); + glm::highp_vec4 quaternion{0.f, 0.f, 0.f, 1.f}; + glm::vec3 scale{ 1.f }; glm::vec2 plane = { 0.1f, 5000.f }; + glm::vec2 last_cursor_pos{}; + float yaw = 0.f; + float pitch = 0.f; + bool is_first_frame = true; while (!glfwWindowShouldClose(window)) { glfwPollEvents(); @@ -335,9 +343,6 @@ main() { }; main_renderpass.begin(current, begin_renderpass); - // Binding a graphics pipeline -- before drawing stuff - // Inside of this graphics pipeline bind, is where you want to do the - // drawing stuff to static auto start_time = std::chrono::high_resolution_clock::now(); auto current_time = std::chrono::high_resolution_clock::now(); @@ -345,19 +350,69 @@ main() { current_time - start_time) .count(); + glm::vec3 up = glm::vec3(0.f, 1.f, 0.f); + glm::vec3 down = glm::vec3(0.f, -1.f, 0.f); + glm::vec3 right = glm::vec3(1.f, 0.f, 0.f); + glm::vec3 left = glm::vec3(-1.f, 0.f, 0.f); + glm::vec3 forward = glm::vec3(0.f, 0.f, 1.f); + glm::vec3 backward = glm::vec3(0.f, 0.f, -1.f); + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { - position.z += 1.f; - } - if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { - position.x += 1.f; + position += forward; } + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { - position.z -= 1.f; + position += backward; } + if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) { - position.x -= 1.f; + position -= right; + } + + if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) { + position -= right; + } + + double x_pos, y_pos; + glfwGetCursorPos(window, &x_pos, &y_pos); + + glm::vec2 current_cursor_pos = { x_pos, y_pos }; + + if (is_first_frame) { + last_cursor_pos = current_cursor_pos; + is_first_frame = false; } + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + float mouse_sensitivity = 0.01; + glm::vec2 cursor_dt = current_cursor_pos - last_cursor_pos; + + yaw -= (cursor_dt.x * mouse_sensitivity); + pitch -= (cursor_dt.y * mouse_sensitivity); + + // set rotation + rotation = glm::vec3(yaw, pitch, 0.f); + + auto quat = glm::quat(rotation); + quaternion = glm::vec4({ quat.x, quat.y, quat.z, quat.w }); + } + + if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_RELEASE) { + is_first_frame = true; + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + } + + + last_cursor_pos = current_cursor_pos; + + glm::quat quat = glm::quat({ + quaternion.w, + quaternion.x, + quaternion.y, + quaternion.z, + }); global_uniform ubo = {}; ubo.proj = glm::mat4(1.f); ubo.proj = glm::perspective( @@ -368,7 +423,8 @@ main() { ubo.proj[1][1] *= -1; ubo.view = glm::mat4(1.f); - ubo.view = glm::translate(ubo.view, position); + ubo.view = glm::translate(ubo.view, position) * glm::mat4_cast(quat); + ubo.view = glm::inverse(ubo.view); skybox_uniform sky_ubo = { From 43697cf59e6eef8f70b1ce41e8383e5e37d39960 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 01:21:47 -0700 Subject: [PATCH 11/15] Minor fixes to the application.cppm for demo 13 --- demos/13-skybox/application.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/demos/13-skybox/application.cpp b/demos/13-skybox/application.cpp index cd2e47a..66e2016 100644 --- a/demos/13-skybox/application.cpp +++ b/demos/13-skybox/application.cpp @@ -315,9 +315,9 @@ main() { float field_of_view = 45.f; glm::vec3 position = { 3.5f, 4.90f, 36.40f }; - + glm::vec3 rotation = glm::vec3(0.f); - glm::highp_vec4 quaternion{0.f, 0.f, 0.f, 1.f}; + glm::highp_vec4 quaternion{ 0.f, 0.f, 0.f, 1.f }; glm::vec3 scale{ 1.f }; glm::vec2 plane = { 0.1f, 5000.f }; @@ -360,7 +360,7 @@ main() { if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { position += forward; } - + if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { position += backward; } @@ -383,7 +383,8 @@ main() { is_first_frame = false; } - if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_PRESS) { + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == + GLFW_PRESS) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); float mouse_sensitivity = 0.01; @@ -399,13 +400,13 @@ main() { quaternion = glm::vec4({ quat.x, quat.y, quat.z, quat.w }); } - if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == GLFW_RELEASE) { + if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_MIDDLE) == + GLFW_RELEASE) { is_first_frame = true; glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } - - last_cursor_pos = current_cursor_pos; + last_cursor_pos = current_cursor_pos; glm::quat quat = glm::quat({ quaternion.w, From d918d3e7c58470e5d288cda19964165dc6e2b668 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 15:25:25 -0700 Subject: [PATCH 12/15] Applied some minor maintenance and code cleanup + docs --- demos/13-skybox/skybox.cppm | 406 ++++++++++++++++++++---------------- 1 file changed, 225 insertions(+), 181 deletions(-) diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm index 57cdf1a..734cb04 100644 --- a/demos/13-skybox/skybox.cppm +++ b/demos/13-skybox/skybox.cppm @@ -45,6 +45,7 @@ public: return; } + // Loading in all 6-faces images for the skybox int w = 0; int h = 0; int channels = 0; @@ -54,7 +55,7 @@ public: stbi_load(p_faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); int face_width = w; int face_height = h; - // VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; + VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; const uint32_t bytes_per_pixel = static_cast(vk::bytes_per_texture_format(image_format)); @@ -140,13 +141,13 @@ public: VK_IMAGE_ASPECT_COLOR_BIT, 6); - // Perform uploads std::array regions; for (uint32_t face = 0; face < regions.size(); face++) { + // Copy the specific face region to the image regions[face] = { .offset = static_cast(face_size_bytes * face), - .base_array_layer = face, // Copy this specific face region + .base_array_layer = face, .image_offset = { .width = 0, .height = 0, .depth = 0 }, .image_extent = { .width = width, .height = height }, }; @@ -378,199 +379,239 @@ public: } void create_buffers() { - 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 - }; - // m_physical = instance_context::physical_driver(); - // m_device = instance_context::logical_device(); - // std::vector vertices = { - // vk::vertex_input{ - // .position = {-1.0f, 1.0f, -1.0f,} - // }, - // }; std::vector vertices = { // Front Face - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, // Left Face - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, // Right Face - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, // Back Face - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, // Top Face - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, 1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, // Bottom Face - vk::vertex_input{ { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } }, - vk::vertex_input{ { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f } } + vk::vertex_input{ + { -1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, -1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { -1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, + vk::vertex_input{ + { 1.0f, -1.0f, 1.0f }, + { 1.0f, 1.0f, 1.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f }, + }, }; m_skybox_vbo_size = vertices.size(); - // vk::vertex_params vbo_params = { - // .phsyical_memory_properties = - // vulkan::instance_context::physical_driver().memory_properties(), - // .vertices = vertices - // }; vk::buffer_parameters vertex_params = { .memory_mask = m_physical.value().memory_properties( vk::memory_property::device_local_bit | @@ -582,7 +623,10 @@ public: m_skybox_vbo = vk::vertex_buffer(m_device, vertices, vertex_params); } - void update_uniform(const skybox_uniform&) {} + void update_uniform(const skybox_uniform& p_uniform) { + m_skybox_ubo.transfer( + std::span(&p_uniform, 1)); + } void bind(vk::command_buffer p_command) { m_skybox_pipeline.bind(p_command); @@ -621,5 +665,5 @@ private: vk::vertex_buffer m_skybox_vbo; vk::descriptor_resource m_skybox_descriptors; VkRenderPass m_renderpass; - uint32_t m_skybox_vbo_size; + uint32_t m_skybox_vbo_size=0; }; \ No newline at end of file From 0a6a2a41841346d965b5ebffc7bac5728cebb8ea Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 15:40:18 -0700 Subject: [PATCH 13/15] Updated demo 13 CMake to include skybox.cppm --- demos/13-skybox/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/demos/13-skybox/CMakeLists.txt b/demos/13-skybox/CMakeLists.txt index 6a44aa1..fb440e4 100644 --- a/demos/13-skybox/CMakeLists.txt +++ b/demos/13-skybox/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.27) +cmake_minimum_required(VERSION 4.0) project(skybox CXX) build_application( @@ -26,5 +26,6 @@ target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES TYPE CXX_MODULES FILES - environment_map.cppm + hdri_environment.cppm + skybox.cppm ) \ No newline at end of file From b4c02f5605e0bfa31abacc0c0d095a9d4c627689 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 15:41:28 -0700 Subject: [PATCH 14/15] Renamed environment_map.cppm to hdri_environment.cppm and did some minor fixes in skybox.cppm --- ...ronment_map.cppm => hdri_environment.cppm} | 2 +- demos/13-skybox/skybox.cppm | 294 +++++++++--------- 2 files changed, 148 insertions(+), 148 deletions(-) rename demos/13-skybox/{environment_map.cppm => hdri_environment.cppm} (99%) diff --git a/demos/13-skybox/environment_map.cppm b/demos/13-skybox/hdri_environment.cppm similarity index 99% rename from demos/13-skybox/environment_map.cppm rename to demos/13-skybox/hdri_environment.cppm index 4ce050d..cb9b64e 100644 --- a/demos/13-skybox/environment_map.cppm +++ b/demos/13-skybox/hdri_environment.cppm @@ -29,7 +29,7 @@ module; #include #include -export module environment_map; +export module hdri_environment; import vk; export struct skybox_uniform { diff --git a/demos/13-skybox/skybox.cppm b/demos/13-skybox/skybox.cppm index 734cb04..8338a18 100644 --- a/demos/13-skybox/skybox.cppm +++ b/demos/13-skybox/skybox.cppm @@ -98,12 +98,12 @@ public: .format = image_format, .memory_mask = p_physical.memory_properties( vk::memory_property::device_local_bit), + .usage = + vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, .image_flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, .view_type = VK_IMAGE_VIEW_TYPE_CUBE, .layer_count = 6, .array_layers = 6, - .usage = - vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, }; m_skybox_image = vk::sample_image(m_device, skybox_params); @@ -383,230 +383,230 @@ public: std::vector vertices = { // Front Face vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, // Left Face vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, // Right Face vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, // Back Face vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, // Top Face vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, 1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, 1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, // Bottom Face vk::vertex_input{ - { -1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, -1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, -1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { -1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { -1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, vk::vertex_input{ - { 1.0f, -1.0f, 1.0f }, - { 1.0f, 1.0f, 1.0f }, - { 0.0f, 0.0f, 0.0f }, - { 0.0f, 0.0f }, + .position = { 1.0f, -1.0f, 1.0f }, + .color = { 1.0f, 1.0f, 1.0f }, + .normals = { 0.0f, 0.0f, 0.0f }, + .uv = { 0.0f, 0.0f }, }, }; @@ -665,5 +665,5 @@ private: vk::vertex_buffer m_skybox_vbo; vk::descriptor_resource m_skybox_descriptors; VkRenderPass m_renderpass; - uint32_t m_skybox_vbo_size=0; + uint32_t m_skybox_vbo_size = 0; }; \ No newline at end of file From fdc8b0013c7d5f5109ec88612c9f801d9aa5bff8 Mon Sep 17 00:00:00 2001 From: SpinnerX Date: Thu, 2 Jul 2026 16:23:43 -0700 Subject: [PATCH 15/15] Add README with core skybox logic instructions and walkthrough --- demos/13-skybox/README.md | 194 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 demos/13-skybox/README.md diff --git a/demos/13-skybox/README.md b/demos/13-skybox/README.md new file mode 100644 index 0000000..64edf81 --- /dev/null +++ b/demos/13-skybox/README.md @@ -0,0 +1,194 @@ +## Demo 13 Skybox + +In demo 13, shows how to configure with the vulkan-cpp to get a skybox to work with the most minimal requirements. + +I am going to be using stbi_image library to help with loading the three faces. + +Note: There are two files in demo 13, one called `skybox.cppm` and another called `hdri_environment.cppm`. The `skybox.cppm` file is what this README will walk you through. + +If you are interested in learning how to load an HDRI environment specifically, checkout `hdri_environment.cppm` as the setup is quite similar. Only difference is you would be loading a single image file and changing from `stbi_load` to `stbi_loadf` instead. + +## Loading 6 faces for cubemap + +To load the skybox, I have provided a skybox directory inside of the assets directory. + +In `asset_samples/skybox`, you should see 6 `.jpg` images. These are going to be the images we use. + +Before configuring any vulkan handles, make sure to store the path correctly to those images. As shown below: + +```C++ +std::array faces = { + "asset_samples/skybox/right.jpg", "asset_samples/skybox/left.jpg", + "asset_samples/skybox/top.jpg", "asset_samples/skybox/bottom.jpg", + "asset_samples/skybox/front.jpg", "asset_samples/skybox/back.jpg" +}; +``` + +To load all 6 of those faces, use stbi_image to load those images. The way, how I have it implemented is by doing the following: + +- First we validate that the images specified are 6 images only. +- We load in the first image to specify the parameters for the dimensions, and the total size in bytes of that image (as they should all be the equivalent). +- Starting at i=1, we load the other 5 images into an array, being `std::array, 6>` + +```C++ +if (faces.size() != 6) { + std::println("Cubemap requires 6 faces, received {} count of faces", + faces.size()); + return; +} + +int w = 0; +int h = 0; +int channels = 0; +std::array, 6> faces{}; + +auto* face0 = + stbi_load(faces[0].c_str(), &w, &h, &channels, STBI_rgb_alpha); +int face_width = w; +int face_height = h; + +VkFormat image_format = VK_FORMAT_R8G8B8A8_SRGB; +const uint32_t bytes_per_pixel = + static_cast(vk::bytes_per_texture_format(image_format)); +auto size_bytes = face_width * face_height * bytes_per_pixel; + +faces[0] = to_bytes(face0, size_bytes); + +for (size_t i = 1; i < faces.size(); i++) { + auto* face_pixels = + stbi_load(faces[i].c_str(), &w, &h, &channels, STBI_rgb_alpha); + faces[i] = to_bytes(face_pixels, size_bytes); + + if (faces[i].empty()) { + std::println("Could not load face: {}", faces[i]); + return; + } + + if (w != face_width || h != face_height) { + std::println("Cubemap faces must match dimensions. Face 0 is " + "{}x{}, face {} is {}x{} ({})", + face_width, + face_height, + i, + w, + h, + p_faces[i]); + return; + } +} +``` + +## Configure `vk::sample_image` + +By now you should be aware that `vk::sample_image`, is essentially a wrapper around the creation for the `VkImage`, `VkImageView`, and `VkSampler`. + +For this skybox demo, these are the parameters to configure those handles with: + +```C++ +vk::image_params skybox_params = { + .extent = { .width = width, .height = height, .depth = 1 }, + .format = image_format, + .memory_mask = p_physical.memory_properties( + vk::memory_property::device_local_bit), + .usage = + vk::image_usage::transfer_dst_bit | vk::image_usage::sampled_bit, + .image_flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + .view_type = VK_IMAGE_VIEW_TYPE_CUBE, + .layer_count = 6, + .array_layers = 6, +}; +m_skybox_image = vk::sample_image(m_device, skybox_params); +``` + +- `extent` is to specify the width and height for the given image. Since this is a skybox, the width and height should relatively be the same. +- `format` is set to `VK_FORMAT_R8G8B8A8_SRGB` +- `p_physical.memory_properties` is specified to be `device_local_bit` for allocating this image in high-speed, VRAM-only memory local to the GPU. +- `usage` specifies with `image_usage::transfer_dst_bit` because the demo copies raw pixels from staging into this image, we also bitwise OR `image_usage::sampled_bit` so the fragment shader can read it using `sampler2D`. +- `image_flags` is configured with `VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT` because we are creating this image to store 6 regions for a skybox and this tells the image for what kind of data to expect. +- `view_type` configures `VK_IMAGE_VIEW_TYPE_CUBE` to tell the image not to treat the data as a flat texture array. +- `layer_count` specifies that the image will have 6 distinct image layers +- `array_layers` specifies how many texture slices are allocated to VRAM. + +## Configuring Staging Buffer + +We need to create a staging buffer to allow for CPU-visible data to be transferred to via `host_visible` without manually cashe flushing (host_coherent). Populating this buffer with the skybox pixels we loaded, earlier. + +Using this buffer as an entry point using the `transfer_src_bit` to do a fast data transfer onto the device-local skybox texture. + +```C++ +vk::buffer_parameters staging_params = { + .memory_mask = p_physical.memory_properties( + vk::memory_property::host_visible_bit | + vk::memory_property::host_coherent_bit), + .usage = vk::buffer_usage::transfer_src_bit, +}; + +vk::buffer staging(m_device, total_size_bytes, staging_params); +``` + +- `host_visible_bit | host_coherent_bit` is to allocate the buffer to system RAM or a special region of VRAM the CPU can directly write and automatically become visible to the GPU. +- `vk::buffer_usage::transfer_src_bit` is to tell the use of this buffer handle is specifically to store data, which allows the driver to optimize underlying memory allocations specifically for outbound direct memory transfers to the GPU memory. + +## Transferring the faces pixels data + +Then to perform the actual transfer operation for the pixels for the 6 faces. You just call the following API: + +```C++ +// faces is defined as std::array, 6> +staging.transfer(faces); +``` + +## Cleanup stbi_image pixels + +After transferring the pixels and already written, you can just simply perform cleanup: + +```C++ +for (size_t i = 0; i < faces.size(); i++) { + stbi_image_free(faces[i].data()); +} +``` + +## Mapping each Skybox Face Regions with Vulkan + +The `std::array` allows for executing batch of offset stride copy operations. For each region (per-face), we tightly pack a single cubemap face to its source memory. + +That source memory to an un-offset subresource footprint that targets different base array layers and other subresource parameters. + +Allowing for those 6-face data streams to be represented as a linear buffer stream that optimally laid out for the device. + + +## Submitting to the Queue + +Performing this queue submission is to conclude by actually having the data to be fully copied over to the GPU. + +```C++ +VkQueue graphics_queue = nullptr; +vkGetDeviceQueue(m_device, 0, 0, &graphics_queue); +const VkCommandBuffer cmd = upload_cmd; +VkSubmitInfo submit = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, +}; +vk::vk_check(vkQueueSubmit(graphics_queue, 1, &submit, nullptr), + "vkQueueSubmit(cubemap upload)"); +vk::vk_check(vkQueueWaitIdle(graphics_queue), + "vkQueueWaitIdle(cubemap upload)"); + +upload_cmd.destruct(); +staging.destruct(); +``` + +## Next Steps + +The `skybox.cppm` implementation actually just recreates a separate `vk::pipeline`, `vk::shader_resource`, `vk::descriptor_resource`, and `vk::uniform_buffer` for the skybox shaders. + +If you have been following along in the vulkan-cpp demos where we use `vk::pipeline` (graphics pipeline) API, `skybox.cppm` follows the same way we have always been configuring and initializing those handles. + +This README is meant to give you an idea on the core portions of the code for loading and performing staging correctly with a skybox. Since this demo, has quite a significant amount of code involved. \ No newline at end of file