diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index e70862f..84ab932 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -352,6 +352,10 @@ auto VulkanRenderer::descriptors_init() -> void .add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) .build(m_logger, m_vkb.dev, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + m_vk.deletion_queue.emplace([&]() { + vkDestroyDescriptorSetLayout( + m_vkb.dev, m_vk.gpu_scene_data_descriptor_layout, nullptr); + }); } auto VulkanRenderer::pipelines_init() -> void @@ -628,6 +632,59 @@ auto VulkanRenderer::default_data_init() -> void destroy_buffer(m_vk.rectangle.index_buffer); destroy_buffer(m_vk.rectangle.vertex_buffer); }); + + { + // Solid color images + auto const white = smath::pack_unorm4x8(smath::Vec4 { 1, 1, 1, 1 }); + m_vk.white_image = create_image(&white, VkExtent3D { 1, 1, 1 }, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + + auto const black = smath::pack_unorm4x8(smath::Vec4 { 0, 0, 0, 1 }); + m_vk.black_image = create_image(&black, VkExtent3D { 1, 1, 1 }, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + + auto const gray + = smath::pack_unorm4x8(smath::Vec4 { 0.6f, 0.6f, 0.6f, 1 }); + m_vk.gray_image = create_image(&gray, VkExtent3D { 1, 1, 1 }, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + + // Error checkerboard image + auto const magenta = smath::pack_unorm4x8(smath::Vec4 { 1, 0, 1, 1 }); + std::array checkerboard; + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + checkerboard[y * 16 + x] + = ((x % 2) ^ (y % 2)) ? magenta : black; + } + } + m_vk.error_image + = create_image(checkerboard.data(), VkExtent3D { 16, 16, 1 }, + VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); + } + + VkSamplerCreateInfo sampler_ci {}; + sampler_ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + sampler_ci.pNext = nullptr; + + sampler_ci.magFilter = VK_FILTER_NEAREST; + sampler_ci.minFilter = VK_FILTER_NEAREST; + vkCreateSampler( + m_vkb.dev, &sampler_ci, nullptr, &m_vk.default_sampler_nearest); + + sampler_ci.magFilter = VK_FILTER_LINEAR; + sampler_ci.minFilter = VK_FILTER_LINEAR; + vkCreateSampler( + m_vkb.dev, &sampler_ci, nullptr, &m_vk.default_sampler_linear); + + m_vk.deletion_queue.emplace([&]() { + vkDestroySampler(m_vkb.dev, m_vk.default_sampler_linear, nullptr); + vkDestroySampler(m_vkb.dev, m_vk.default_sampler_nearest, nullptr); + + destroy_image(m_vk.error_image); + destroy_image(m_vk.gray_image); + destroy_image(m_vk.black_image); + destroy_image(m_vk.white_image); + }); } auto VulkanRenderer::render() -> void @@ -925,60 +982,22 @@ auto VulkanRenderer::create_draw_image(uint32_t width, uint32_t height) -> void { destroy_draw_image(); - m_vk.draw_image.format = VK_FORMAT_R16G16B16A16_SFLOAT; - m_vk.draw_image.extent = { - width, - height, - 1, - }; - - VkImageCreateInfo rimg_ci { vkinit::image_create_info( - m_vk.draw_image.format, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT - | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - m_vk.draw_image.extent) }; - VmaAllocationCreateInfo rimg_alloci {}; - rimg_alloci.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_alloci.requiredFlags - = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - vmaCreateImage(m_vk.allocator, &rimg_ci, &rimg_alloci, - &m_vk.draw_image.image, &m_vk.draw_image.allocation, nullptr); - - VkImageViewCreateInfo rview_ci - = vkinit::imageview_create_info(m_vk.draw_image.format, - m_vk.draw_image.image, VK_IMAGE_ASPECT_COLOR_BIT); - VK_CHECK(m_logger, - vkCreateImageView( - m_vkb.dev, &rview_ci, nullptr, &m_vk.draw_image.image_view)); + auto const flags { VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT }; + m_vk.draw_image = create_image( + { width, height, 1 }, VK_FORMAT_R16G16B16A16_SFLOAT, flags); } auto VulkanRenderer::create_depth_image(uint32_t width, uint32_t height) -> void { destroy_depth_image(); - m_vk.depth_image.format = VK_FORMAT_D32_SFLOAT; - m_vk.depth_image.extent = { width, height, 1 }; - - VkImageCreateInfo rimg_ci { vkinit::image_create_info( - m_vk.depth_image.format, - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT - | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, - m_vk.depth_image.extent) }; - VmaAllocationCreateInfo rimg_alloci {}; - rimg_alloci.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_alloci.requiredFlags - = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - - vmaCreateImage(m_vk.allocator, &rimg_ci, &rimg_alloci, - &m_vk.depth_image.image, &m_vk.depth_image.allocation, nullptr); - - VkImageViewCreateInfo rview_ci - = vkinit::imageview_create_info(m_vk.depth_image.format, - m_vk.depth_image.image, VK_IMAGE_ASPECT_DEPTH_BIT); - VK_CHECK(m_logger, - vkCreateImageView( - m_vkb.dev, &rview_ci, nullptr, &m_vk.depth_image.image_view)); + auto const flags { VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT }; + m_vk.depth_image + = create_image({ width, height, 1 }, VK_FORMAT_D32_SFLOAT, flags); } auto VulkanRenderer::destroy_depth_image() -> void @@ -1061,6 +1080,112 @@ auto VulkanRenderer::destroy_swapchain() -> void m_vk.swapchain_extent = { 0, 0 }; } +auto VulkanRenderer::create_image(VkExtent3D size, VkFormat format, + VkImageUsageFlags flags, bool mipmapped) -> AllocatedImage +{ + AllocatedImage new_image; + new_image.format = format; + new_image.extent = size; + + auto img_ci { vkinit::image_create_info(format, flags, size) }; + if (mipmapped) { + img_ci.mipLevels = static_cast(std::floor( + std::log2(std::max(size.width, size.height)))) + + 1; + } + + VmaAllocationCreateInfo alloc_ci {}; + alloc_ci.usage = VMA_MEMORY_USAGE_GPU_ONLY; + alloc_ci.requiredFlags + = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + VK_CHECK(m_logger, + vmaCreateImage(m_vk.allocator, &img_ci, &alloc_ci, &new_image.image, + &new_image.allocation, nullptr)); + + VkImageAspectFlags aspect_flag { VK_IMAGE_ASPECT_COLOR_BIT }; + if (format == VK_FORMAT_D32_SFLOAT) { + aspect_flag = VK_IMAGE_ASPECT_DEPTH_BIT; + } + + auto const view_ci { vkinit::imageview_create_info( + format, new_image.image, aspect_flag) }; + VK_CHECK(m_logger, + vkCreateImageView(m_vkb.dev, &view_ci, nullptr, &new_image.image_view)); + + return new_image; +} + +auto VulkanRenderer::create_image(void const *data, VkExtent3D size, + VkFormat format, VkImageUsageFlags flags, bool mipmapped) -> AllocatedImage +{ + size_t data_size { + static_cast(size.depth) * static_cast(size.width) + * static_cast(size.height) * 4, + }; + auto const upload_buffer { + create_buffer(data_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VMA_MEMORY_USAGE_CPU_TO_GPU), + }; + + VmaAllocationInfo info {}; + vmaGetAllocationInfo(m_vk.allocator, upload_buffer.allocation, &info); + + void *mapped_data { reinterpret_cast(info.pMappedData) }; + if (!mapped_data) { + VkResult res = vmaMapMemory( + m_vk.allocator, upload_buffer.allocation, (void **)&mapped_data); + assert(res == VK_SUCCESS); + } + defer({ + if (info.pMappedData == nullptr) { + vmaUnmapMemory(m_vk.allocator, upload_buffer.allocation); + } + }); + + memcpy(mapped_data, data, data_size); + + auto const new_image { + create_image(size, format, + flags | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + mipmapped), + }; + + immediate_submit([&](VkCommandBuffer cmd) { + vkutil::transition_image(cmd, new_image.image, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkBufferImageCopy copy_region {}; + copy_region.bufferOffset = 0; + copy_region.bufferRowLength = 0; + copy_region.bufferImageHeight = 0; + + copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_region.imageSubresource.mipLevel = 0; + copy_region.imageSubresource.baseArrayLayer = 0; + copy_region.imageSubresource.layerCount = 1; + copy_region.imageExtent = size; + + vkCmdCopyBufferToImage(cmd, upload_buffer.buffer, new_image.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + vkutil::transition_image(cmd, new_image.image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + }); + + destroy_buffer(upload_buffer); + + return new_image; +} + +auto VulkanRenderer::destroy_image(AllocatedImage const &img) -> void +{ + vkDestroyImageView(m_vkb.dev, img.image_view, nullptr); + vmaDestroyImage(m_vk.allocator, img.image, img.allocation); +} + auto VulkanRenderer::create_buffer(size_t alloc_size, VkBufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer { diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index 2887ce9..d804a67 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -63,6 +63,11 @@ private: auto destroy_depth_image() -> void; auto recreate_swapchain(uint32_t width, uint32_t height) -> void; auto destroy_swapchain() -> void; + auto create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags flags, + bool mipmapped = false) -> AllocatedImage; + auto create_image(void const *data, VkExtent3D size, VkFormat format, + VkImageUsageFlags flags, bool mipmapped = false) -> AllocatedImage; + auto destroy_image(AllocatedImage const &img) -> void; auto create_buffer(size_t alloc_size, VkBufferUsageFlags usage, VmaMemoryUsage memory_usage) -> AllocatedBuffer; @@ -129,6 +134,14 @@ private: uint64_t frame_number { 0 }; std::vector> test_meshes; + + AllocatedImage white_image {}; + AllocatedImage black_image {}; + AllocatedImage gray_image {}; + AllocatedImage error_image {}; + + VkSampler default_sampler_linear; + VkSampler default_sampler_nearest; } m_vk; SDL_Window *m_window { nullptr };