Crafter.Graphics/interfaces/Crafter.Graphics-ImageVulkan.cppm

182 lines
9 KiB
Text
Raw Normal View History

2026-02-03 21:03:11 +01:00
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
2026-02-05 05:22:01 +01:00
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0215-1301 USA
2026-02-03 21:03:11 +01:00
*/
module;
2026-03-09 20:10:19 +01:00
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
2026-03-02 23:53:13 +01:00
#include "vulkan/vulkan.h"
2026-02-03 21:03:11 +01:00
#endif
export module Crafter.Graphics:ImageVulkan;
import std;
import :VulkanBuffer;
export namespace Crafter {
2026-03-09 20:10:19 +01:00
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
2026-02-03 21:03:11 +01:00
template <typename PixelType>
class ImageVulkan {
public:
2026-03-10 19:43:11 +01:00
std::uint16_t width;
std::uint16_t height;
std::uint8_t mipLevels;
2026-02-03 21:03:11 +01:00
VkImage image;
VkDeviceMemory imageMemory;
2026-04-10 22:26:15 +02:00
VulkanBuffer<PixelType, true> buffer;
2026-02-03 21:03:11 +01:00
VkImageView imageView;
2026-02-04 04:12:27 +01:00
VkDescriptorImageInfo descriptor;
2026-03-10 22:32:50 +01:00
void Create(std::uint16_t width, std::uint16_t height, std::uint8_t mipLevels, VkCommandBuffer cmd, VkFormat format, VkImageCreateFlags flags, VkImageLayout layout) {
2026-02-05 05:22:01 +01:00
this->width = width;
this->height = height;
2026-03-10 19:43:11 +01:00
this->mipLevels = mipLevels;
2026-02-03 21:03:11 +01:00
buffer.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, width * height);
2026-02-04 04:12:27 +01:00
VkImageCreateInfo imageInfo = {};
2026-02-03 21:03:11 +01:00
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
2026-03-10 19:43:11 +01:00
imageInfo.mipLevels = mipLevels;
2026-02-03 21:03:11 +01:00
imageInfo.arrayLayers = 1;
imageInfo.format = format;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2026-03-10 22:32:50 +01:00
imageInfo.usage = flags;
2026-02-03 21:03:11 +01:00
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2026-02-04 04:12:27 +01:00
2026-03-09 20:10:19 +01:00
Device::CheckVkResult(vkCreateImage(Device::device, &imageInfo, nullptr, &image));
2026-02-04 04:12:27 +01:00
2026-02-03 21:03:11 +01:00
VkMemoryRequirements memRequirements;
2026-03-09 20:10:19 +01:00
vkGetImageMemoryRequirements(Device::device, image, &memRequirements);
2026-02-04 04:12:27 +01:00
VkMemoryAllocateInfo allocInfo = {};
2026-02-03 21:03:11 +01:00
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
2026-03-09 20:10:19 +01:00
allocInfo.memoryTypeIndex = Device::GetMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
2026-02-04 04:12:27 +01:00
2026-03-09 20:10:19 +01:00
Device::CheckVkResult(vkAllocateMemory(Device::device, &allocInfo, nullptr, &imageMemory));
vkBindImageMemory(Device::device, image, imageMemory, 0);
2026-02-03 21:03:11 +01:00
2026-02-04 04:12:27 +01:00
VkImageViewCreateInfo viewInfo = {};
2026-02-03 21:03:11 +01:00
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format = format;
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
2026-03-10 19:43:11 +01:00
viewInfo.subresourceRange.levelCount = mipLevels;
2026-02-03 21:03:11 +01:00
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
2026-03-09 20:10:19 +01:00
Device::CheckVkResult(vkCreateImageView(Device::device, &viewInfo, nullptr, &imageView));
2026-02-03 21:03:11 +01:00
2026-02-04 04:12:27 +01:00
// Final transition to shader read-only layout
2026-03-10 22:32:50 +01:00
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, layout, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
descriptor = { .imageView = imageView, .imageLayout = layout };
2026-02-03 21:03:11 +01:00
}
2026-03-10 22:32:50 +01:00
void Update(VkCommandBuffer cmd, VkImageLayout layout) {
2026-02-03 21:03:11 +01:00
buffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2026-03-10 22:32:50 +01:00
TransitionImageLayout(cmd, image, layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, 0, mipLevels);
2026-02-03 21:03:11 +01:00
VkBufferImageCopy region{};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = {0, 0, 0};
region.imageExtent = { width, height, 1};
vkCmdCopyBufferToImage(
cmd,
buffer.buffer,
image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1,
&region
);
2026-03-10 20:14:33 +01:00
if(mipLevels > 1) {
2026-03-10 22:32:50 +01:00
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, 1);
2026-03-10 19:43:11 +01:00
for (std::uint16_t i = 1; i < mipLevels; ++i) {
std::uint16_t mipWidth = width >> i;
std::uint16_t mipHeight = height >> i;
std::uint16_t previousMipWidth = width >> (i - std::uint16_t(1));
std::uint16_t previousMipHeight = height >> (i - std::uint16_t(1));
VkImageBlit blit = {};
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
blit.srcOffsets[0] = { 0, 0, 0 };
blit.srcOffsets[1] = { (int32_t)previousMipWidth, (int32_t)previousMipHeight, 1 };
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
blit.dstOffsets[0] = { 0, 0, 0 };
blit.dstOffsets[1] = { (int32_t)mipWidth, (int32_t)mipHeight, 1 };
vkCmdBlitImage(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
2026-03-10 22:32:50 +01:00
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, i, 1);
2026-03-10 19:43:11 +01:00
}
2026-02-04 04:12:27 +01:00
2026-03-10 22:32:50 +01:00
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
} else {
TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, layout, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, 0, mipLevels);
}
2026-02-03 21:03:11 +01:00
}
2026-03-10 20:27:46 +01:00
void Destroy() {
vkDestroyImageView(Device::device, imageView, nullptr);
vkDestroyImage(Device::device, image, nullptr);
vkFreeMemory(Device::device, imageMemory, nullptr);
}
2026-02-04 04:12:27 +01:00
private:
2026-03-10 22:32:50 +01:00
void TransitionImageLayout(VkCommandBuffer cmd, VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkPipelineStageFlags sourceStage, VkPipelineStageFlags destinationStage, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, std::uint32_t mipLevel, std::uint32_t count) {
2026-02-04 04:12:27 +01:00
VkImageMemoryBarrier barrier = {};
2026-02-03 21:03:11 +01:00
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
2026-02-04 04:12:27 +01:00
barrier.image = image;
2026-02-03 21:03:11 +01:00
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2026-02-04 04:12:27 +01:00
barrier.subresourceRange.baseMipLevel = mipLevel;
barrier.subresourceRange.levelCount = count;
2026-02-03 21:03:11 +01:00
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
2026-03-10 22:32:50 +01:00
barrier.srcAccessMask = srcAccessMask;
barrier.dstAccessMask = dstAccessMask;
2026-02-03 21:03:11 +01:00
2026-02-04 04:12:27 +01:00
vkCmdPipelineBarrier(cmd, sourceStage, destinationStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
2026-02-03 21:03:11 +01:00
}
};
#endif
}