319 lines
11 KiB
C++
319 lines
11 KiB
C++
/**
|
|
* 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 <typename T, bool condition>
|
|
using optional = std::conditional_t<condition, T, NoMember>;
|
|
|
|
template <bool hasStaging>
|
|
using StagingBuffer = optional<vk::raii::Buffer, hasStaging>;
|
|
|
|
template <bool hasStaging>
|
|
using StagingDeviceMemory = optional<vk::raii::DeviceMemory, hasStaging>;
|
|
|
|
template <typename T, bool isMapped>
|
|
using MappedMemory = optional<std::span<T>, isMapped>;
|
|
}
|
|
|
|
export namespace vulkan_render {
|
|
|
|
enum class BufferMode {
|
|
STATIC,
|
|
CPU_ACCESS,
|
|
STAGING_ACCESS
|
|
};
|
|
|
|
template <BufferMode mode>
|
|
class BaseBuffer {
|
|
public:
|
|
const Context &context;
|
|
|
|
vk::raii::Buffer handle = nullptr;
|
|
vk::raii::DeviceMemory memory = nullptr;
|
|
|
|
NO_UNIQUE_ADDRESS StagingBuffer<mode == BufferMode::STAGING_ACCESS> stagingHandle = nullptr;
|
|
NO_UNIQUE_ADDRESS StagingDeviceMemory<mode == BufferMode::STAGING_ACCESS> stagingMemory = nullptr;
|
|
|
|
std::size_t memorySize;
|
|
|
|
explicit BaseBuffer(const Context &context, const std::size_t memorySize) :
|
|
context(context),
|
|
memorySize(memorySize) {}
|
|
};
|
|
|
|
template <typename T, vk::BufferUsageFlagBits usage, BufferMode mode>
|
|
class Buffer : public BaseBuffer<mode> {
|
|
public:
|
|
NO_UNIQUE_ADDRESS MappedMemory<T, mode != BufferMode::STATIC> content;
|
|
static constexpr std::size_t elementSize = sizeof(T);
|
|
|
|
template <typename U>
|
|
Buffer(const Context &context, const std::span<U> data)
|
|
requires (mode == BufferMode::STATIC && std::same_as<std::remove_const_t<U>, T>)
|
|
: BaseBuffer<mode>(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<T*>(mappedMemory));
|
|
this->memory.unmapMemory();
|
|
}
|
|
|
|
Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::CPU_ACCESS)
|
|
: BaseBuffer<mode>(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<T>(static_cast<T*>(mappedMemory), size);
|
|
std::ranges::fill(content, T{});
|
|
}
|
|
|
|
Buffer(const Context &context, const std::size_t size) requires (mode == BufferMode::STAGING_ACCESS)
|
|
: BaseBuffer<mode>(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<T>(static_cast<T*>(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<vk::raii::DeviceMemory> allocated = this->context.AllocateBufferMemory(
|
|
buffer,
|
|
memoryFlags
|
|
);
|
|
allocated.has_value()
|
|
) {
|
|
memory = std::move(allocated).value();
|
|
buffer.bindMemory(memory, 0);
|
|
}
|
|
}
|
|
};
|
|
|
|
enum class TextureMode {
|
|
PARTIAL,
|
|
RENDER,
|
|
DEPTH
|
|
};
|
|
|
|
template <TextureMode mode>
|
|
class Texture {
|
|
public:
|
|
const Context &context;
|
|
const std::uint32_t width;
|
|
const std::uint32_t height;
|
|
|
|
NO_UNIQUE_ADDRESS MappedMemory<std::byte, mode == TextureMode::RENDER> 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
|
|
) requires (mode == TextureMode::PARTIAL)
|
|
: context(context), width(width), height(height), format(format) {
|
|
MakeInstance(
|
|
vk::Flags(vk::ImageUsageFlagBits::eTransferDst) | vk::ImageUsageFlagBits::eSampled,
|
|
vk::MemoryPropertyFlagBits::eDeviceLocal,
|
|
handle,
|
|
memory
|
|
);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// ReSharper disable once CppNonExplicitConvertingConstructor
|
|
Texture(Texture<TextureMode::PARTIAL> &&from) requires (mode == TextureMode::RENDER) :
|
|
context(from.context),
|
|
width(from.width),
|
|
height(from.height),
|
|
format(from.format),
|
|
handle(std::move(from.handle)),
|
|
view(std::move(from.view)),
|
|
memory(std::move(from.memory)) {
|
|
if (*from.view == nullptr) {
|
|
LOG(ERROR, "Corrupted render texture created, source data is not complete.");
|
|
}
|
|
}
|
|
|
|
void Init(const vk::ImageAspectFlags &flags) requires (mode == TextureMode::PARTIAL) {
|
|
MakeView(flags);
|
|
}
|
|
|
|
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<vk::raii::DeviceMemory> 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
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
using PartialTexture = Texture<TextureMode::PARTIAL>;
|
|
using DepthTexture = Texture<TextureMode::DEPTH>;
|
|
using RenderTexture = Texture<TextureMode::RENDER>;
|
|
|
|
template <typename T>
|
|
class Uniform {
|
|
public:
|
|
std::vector<Buffer<T, vk::BufferUsageFlagBits::eUniformBuffer, BufferMode::CPU_ACCESS>> buffers;
|
|
Uniform(const Context &context, std::uint32_t maxFramesInFlight) {
|
|
buffers.reserve(maxFramesInFlight);
|
|
for (std::uint32_t i = 0; i < maxFramesInFlight; i++) {
|
|
buffers.push_back(Buffer<T, vk::BufferUsageFlagBits::eUniformBuffer, BufferMode::CPU_ACCESS>(context, 1));
|
|
}
|
|
}
|
|
};
|
|
|
|
class Sampler {
|
|
public:
|
|
vk::raii::Sampler handle;
|
|
|
|
explicit Sampler(const Context &context) : handle(MakeInstance(context)) {}
|
|
|
|
private:
|
|
static vk::raii::Sampler MakeInstance(const Context &context) {
|
|
const vk::SamplerCreateInfo samplerInfo {
|
|
.magFilter = vk::Filter::eNearest,
|
|
.minFilter = vk::Filter::eLinear,
|
|
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
|
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
|
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
|
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
|
.mipLodBias = 0.0f,
|
|
.anisotropyEnable = vk::False,
|
|
.compareEnable = vk::False,
|
|
.compareOp = vk::CompareOp::eAlways,
|
|
.minLod = 0.0f,
|
|
.maxLod = 0.0f,
|
|
.borderColor = vk::BorderColor::eIntOpaqueBlack,
|
|
.unnormalizedCoordinates = vk::False
|
|
};
|
|
return context.device.handle.createSampler(samplerInfo);
|
|
}
|
|
};
|
|
|
|
} |