/** * Data partition module is responsible for configuration user-specific Vulkan objects * like buffers, textures, transforms and so on. */ module; #if defined(_MSC_VER) #define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]] #else #define NO_UNIQUE_ADDRESS [[no_unique_address]] #endif export module vulkan_render:data; import :hardware; import :resources; import std; import vulkan_hpp; import utils; namespace vulkan_render { struct NoMember { constexpr NoMember() = default; constexpr NoMember(std::nullptr_t) {} }; template using optional = std::conditional_t; template using StagingBuffer = optional; template using StagingDeviceMemory = optional; template using MappedMemory = optional, isMapped>; } export namespace vulkan_render { enum class BufferMode { STATIC, CPU_ACCESS, STAGING_ACCESS }; template class BaseBuffer { public: const Context &context; vk::raii::Buffer handle = nullptr; vk::raii::DeviceMemory memory = nullptr; NO_UNIQUE_ADDRESS StagingBuffer stagingHandle = nullptr; NO_UNIQUE_ADDRESS StagingDeviceMemory stagingMemory = nullptr; std::size_t memorySize; explicit BaseBuffer(const Context &context, const std::size_t memorySize) : context(context), memorySize(memorySize) {} }; template class Buffer : public BaseBuffer { public: NO_UNIQUE_ADDRESS MappedMemory content; static constexpr std::size_t elementSize = sizeof(T); Buffer(const Context &context, const std::span data) requires (mode == BufferMode::STATIC) : BaseBuffer(context, data.size() * elementSize) { MakeInstance( usage, vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent, this->handle, this->memory ); void *mappedMemory = this->memory.mapMemory(0, this->memorySize); std::ranges::copy(data, static_cast(mappedMemory)); this->memory.unmapMemory(); } Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::CPU_ACCESS) : BaseBuffer(context, size * elementSize) { MakeInstance( usage, vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent, this->handle, this->memory ); void *mappedMemory = this->memory.mapMemory(0, this->memorySize); content = std::span(static_cast(mappedMemory), size); std::ranges::fill(content, T{}); } Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::STAGING_ACCESS) : BaseBuffer(context, size * elementSize) { MakeInstance( vk::Flags(vk::BufferUsageFlagBits::eTransferDst) | usage, vk::MemoryPropertyFlagBits::eDeviceLocal, this->handle, this->memory ); MakeInstance( vk::BufferUsageFlagBits::eTransferSrc, vk::Flags(vk::MemoryPropertyFlagBits::eHostVisible) | vk::MemoryPropertyFlagBits::eHostCoherent, this->stagingHandle, this->stagingMemory ); void *mappedMemory = this->stagingMemory.mapMemory(0, this->memorySize); content = std::span(static_cast(mappedMemory), size); std::ranges::fill(content, T{}); } private: void MakeInstance( const vk::BufferUsageFlags &usageFlags, const vk::MemoryPropertyFlags &memoryFlags, vk::raii::Buffer &buffer, vk::raii::DeviceMemory &memory ) { buffer = this->context.device.handle.createBuffer({ .size = this->memorySize, .usage = usageFlags, .sharingMode = vk::SharingMode::eExclusive }); if ( std::optional allocated = this->context.AllocateBufferMemory( buffer, memoryFlags ); allocated.has_value() ) { memory = std::move(allocated).value(); buffer.bindMemory(memory, 0); } } }; enum class TextureMode { RENDER, DEPTH }; template class Texture { public: const Context &context; const std::uint32_t width; const std::uint32_t height; NO_UNIQUE_ADDRESS MappedMemory data; vk::raii::Image handle = nullptr; vk::raii::ImageView view = nullptr; vk::raii::DeviceMemory memory = nullptr; const vk::Format format; Texture( const Context &context, const std::uint32_t width, const std::uint32_t height, const vk::Format &format, const std::span textureData ) requires (mode == TextureMode::RENDER) : context(context), width(width), height(height), data(textureData), format(format) { Buffer< std::byte, vk::BufferUsageFlagBits::eTransferSrc, BufferMode::STATIC > stagingBuffer(context, data); MakeInstance( vk::Flags(vk::ImageUsageFlagBits::eTransferDst) | vk::ImageUsageFlagBits::eSampled, vk::MemoryPropertyFlagBits::eDeviceLocal, handle, memory ); /* TODO: Transition image layout * Copy buffer to image * Transition image layout * Create view */ } Texture( const Context &context, const Surface &surface ) requires (mode == TextureMode::DEPTH) : context(context), width(surface.extent.width), height(surface.extent.height), format(vk::Format::eD32SfloatS8Uint) { MakeInstance( vk::ImageUsageFlagBits::eDepthStencilAttachment, vk::MemoryPropertyFlagBits::eDeviceLocal, handle, memory ); MakeView(vk::ImageAspectFlagBits::eDepth); } Texture(Texture&&) noexcept = default; Texture(const Texture&) = delete; Texture& operator=(const Texture&) = delete; private: void MakeInstance( const vk::ImageUsageFlags &usageFlags, const vk::MemoryPropertyFlags &memoryflags, vk::raii::Image &image, vk::raii::DeviceMemory &memory ) const { image = context.device.handle.createImage({ .imageType = vk::ImageType::e2D, .format = format, .extent = {.width = width, .height = height, .depth = 1}, .mipLevels = 1, .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, .tiling = vk::ImageTiling::eOptimal, .usage = usageFlags, .sharingMode = vk::SharingMode::eExclusive, .initialLayout = vk::ImageLayout::eUndefined, }); if ( std::optional allocated = context.AllocateImageMemory( handle, memoryflags ); allocated.has_value() ) { memory = std::move(allocated).value(); handle.bindMemory(memory, 0); } } void MakeView(const vk::ImageAspectFlags &flags) { view = context.device.handle.createImageView({ .image = handle, .viewType = vk::ImageViewType::e2D, .format = format, .subresourceRange = { .aspectMask = flags, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1 } }); } }; template class Uniform { public: std::vector> buffers; explicit Uniform(const Context &context, std::uint32_t maxFramesInFlight) { buffers.reserve(maxFramesInFlight); for (std::uint32_t i = 0; i < maxFramesInFlight; i++) { buffers.push_back(Buffer(context, 1)); } } }; }