Added ImageManager and ported Box.

- BufferManager can construct UniformBuffers
- Box uses Managers for all the tasks.
This commit is contained in:
Anish Bhobe 2025-02-23 12:32:10 +01:00
parent 9ca5751a78
commit 88d8a2acc2
10 changed files with 572 additions and 99 deletions

View File

@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core target_sources(aster_core
INTERFACE INTERFACE
"manager.h" "manager.h"
"buffer_manager.h") "buffer_manager.h"
"image_manager.h")

View File

@ -20,5 +20,6 @@ class BufferManager final : public Manager<Buffer>
BufferManager(const Device *device, const u32 maxCount); BufferManager(const Device *device, const u32 maxCount);
Handle CreateStorageBuffer(usize size, cstr name = nullptr); Handle CreateStorageBuffer(usize size, cstr name = nullptr);
Handle CreateUniformBuffer(usize size, cstr name = nullptr);
}; };
} // namespace systems } // namespace systems

View File

@ -0,0 +1,60 @@
// =============================================
// Aster: image_manager.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "aster/aster.h"
#include "aster/core/image.h"
#include "manager.h"
namespace systems
{
struct Texture2DCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct TextureCubeCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
u32 m_Side = 0;
cstr m_Name = nullptr;
bool m_IsSampled = true;
bool m_IsMipMapped = false;
bool m_IsStorage = false;
};
struct AttachmentCreateInfo
{
vk::Format m_Format = vk::Format::eUndefined;
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
struct DepthStencilImageCreateInfo
{
vk::Extent2D m_Extent = {};
cstr m_Name = nullptr;
};
using ImageHandle = Handle<Image>;
class ImageManager final : public Manager<Image>
{
public:
ImageManager(const Device *device, const u32 maxCount);
Handle CreateTexture2D(const Texture2DCreateInfo& createInfo);
Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo);
Handle CreateAttachment(const AttachmentCreateInfo &createInfo);
Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
};
} // namespace systems

View File

@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core target_sources(aster_core
PRIVATE PRIVATE
"manager.cpp" "manager.cpp"
"buffer_manager.cpp") "buffer_manager.cpp"
"image_manager.cpp")

View File

@ -7,10 +7,12 @@
Manager<Buffer> *Manager<Buffer>::m_Instance; Manager<Buffer> *Manager<Buffer>::m_Instance;
systems::BufferManager::Ref using namespace systems;
systems::BufferManager::CreateStorageBuffer(const usize size, const cstr name)
BufferHandle
BufferManager::CreateStorageBuffer(const usize size, const cstr name)
{ {
Ref object = Alloc(); auto [handle, object] = Alloc();
// TODO: Storage and Index buffer are set. // TODO: Storage and Index buffer are set.
// This is hacky and should be improved. // This is hacky and should be improved.
@ -22,11 +24,25 @@ systems::BufferManager::CreateStorageBuffer(const usize size, const cstr name)
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO; constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name); object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
return object; return std::move(handle);
} }
systems::BufferManager::BufferManager(const Device *device, const u32 maxCount) Manager<Buffer>::Handle
BufferManager::CreateUniformBuffer(usize size, cstr name)
{
auto [handle, object] = Alloc();
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
VMA_ALLOCATION_CREATE_MAPPED_BIT;
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
return std::move(handle);
}
BufferManager::BufferManager(const Device *device, const u32 maxCount)
: Manager{device, maxCount} : Manager{device, maxCount}
{ {
SetInstance(this);
} }

View File

@ -0,0 +1,316 @@
// =============================================
// Aster: buffer_manager.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "systems/image_manager.h"
#include "core/device.h"
Manager<Image> *Manager<Image>::m_Instance;
using namespace systems;
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);
vk::ImageCreateInfo ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo);
namespace usage_flags
{
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
constexpr vk::ImageUsageFlags STORAGE =
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
} // namespace usage_flags
ImageHandle
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = {},
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::eCube,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
ImageHandle
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
{
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
vk::ImageView view;
const vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageCreateInfo.format,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
.baseMipLevel = 0,
.levelCount = imageCreateInfo.mipLevels,
.baseArrayLayer = 0,
.layerCount = imageCreateInfo.arrayLayers,
},
};
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result);
auto [handle, object] = Alloc();
object->m_Image = image;
object->m_View = view;
object->m_Allocation = allocation;
object->m_Extent = imageCreateInfo.extent;
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
m_Device->SetName(object->m_Image, createInfo.m_Name);
return handle;
}
vk::ImageCreateInfo
ToImageCreateInfo(const Texture2DCreateInfo &createInfo)
{
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
extent.width, extent.height, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = mipLevels,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const TextureCubeCreateInfo &createInfo)
{
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
auto usage = vk::ImageUsageFlags{};
if (isSampled)
usage |= usage_flags::SAMPLE;
if (isMipMapped)
usage |= usage_flags::MIPMAP;
if (isStorage)
usage |= usage_flags::STORAGE;
return {
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
.imageType = vk::ImageType::e2D,
.format = format,
.extent = {side, side, 1},
.mipLevels = mipLevels,
.arrayLayers = 6,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const AttachmentCreateInfo &createInfo)
{
auto &[format, extent, name] = createInfo;
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}
vk::ImageCreateInfo
ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo)
{
auto &[extent, name] = createInfo;
constexpr vk::Format format = vk::Format::eD24UnormS8Uint;
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
return {
.imageType = vk::ImageType::e2D,
.format = format,
.extent = ToExtent3D(extent, 1),
.mipLevels = 1,
.arrayLayers = 1,
.usage = usage,
};
}
ImageManager::ImageManager(const Device *device, const u32 maxCount)
: Manager{device, maxCount}
{
}

View File

@ -7,6 +7,8 @@ cmake_minimum_required(VERSION 3.13)
add_executable(box "box.cpp" "stb_image.h") add_executable(box "box.cpp" "stb_image.h")
add_shader(box "shader/box.vert.glsl") add_shader(box "shader/box.vert.glsl")
add_shader(box "shader/box.frag.glsl") add_shader(box "shader/box.frag.glsl")
add_shader(box "shader/box.vs.hlsl")
add_shader(box "shader/box.ps.hlsl")
target_link_libraries(box PRIVATE aster_core) target_link_libraries(box PRIVATE aster_core)
target_link_libraries(box PRIVATE util_helper) target_link_libraries(box PRIVATE util_helper)

View File

@ -9,23 +9,25 @@
#include "aster/core/constants.h" #include "aster/core/constants.h"
#include "aster/core/context.h" #include "aster/core/context.h"
#include "aster/core/device.h" #include "aster/core/device.h"
#include "aster/core/image.h"
#include "aster/core/physical_device.h" #include "aster/core/physical_device.h"
#include "aster/core/pipeline.h" #include "aster/core/pipeline.h"
#include "aster/core/swapchain.h" #include "aster/core/swapchain.h"
#include "aster/core/window.h" #include "aster/core/window.h"
#include "aster/core/image.h"
#include "helpers.h" #include "helpers.h"
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "aster/systems/buffer_manager.h"
#include "aster/systems/image_manager.h"
#include "frame.h" #include "frame.h"
#include "stb_image.h" #include "stb_image.h"
#include <EASTL/array.h> #include <EASTL/array.h>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv"; constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv";
struct ImageFile struct ImageFile
{ {
@ -76,32 +78,9 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
struct Vertex struct Vertex
{ {
vec3 m_Position; vec3 m_Position;
f32 m_PositionW = 1.0;
vec2 m_TexCoord0; vec2 m_TexCoord0;
vec2 m_Padding0_ = {0.0f, 0.0f};
constexpr static vk::VertexInputBindingDescription
GetBinding(const u32 binding)
{
return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
}
constexpr static eastl::array<vk::VertexInputAttributeDescription, 2>
GetAttributes(const u32 binding)
{
return {
vk::VertexInputAttributeDescription{
.location = 0,
.binding = binding,
.format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, m_Position),
},
vk::VertexInputAttributeDescription{
.location = 1,
.binding = binding,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, m_TexCoord0),
},
};
}
}; };
struct Camera struct Camera
@ -127,7 +106,8 @@ main(int, char **)
Features enabledDeviceFeatures = { Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true}, .m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan13Features = {.dynamicRendering = true}, .m_Vulkan12Features = {.bufferDeviceAddress = true},
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
}; };
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
@ -135,6 +115,9 @@ main(int, char **)
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"}; Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
Pipeline pipeline = CreatePipeline(&device, &swapchain); Pipeline pipeline = CreatePipeline(&device, &swapchain);
systems::BufferManager bufferManager{&device, 12};
systems::ImageManager imageManager{&device, 12};
Camera camera = { Camera camera = {
.m_Model = {1.0f}, .m_Model = {1.0f},
.m_View = glm::lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)), .m_View = glm::lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)),
@ -155,6 +138,10 @@ main(int, char **)
.type = vk::DescriptorType::eCombinedImageSampler, .type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1, .descriptorCount = 1,
}, },
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
},
}; };
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = { vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()}; .maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
@ -235,24 +222,32 @@ main(int, char **)
assert(loaded); assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
VertexBuffer vbo; auto vbo = bufferManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer").ToPointer();
Texture crate; auto crate = imageManager
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO"); .CreateTexture2D({
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Crate Texture"); .m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture",
})
.ToPointer();
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
{ {
StagingBuffer vertexStaging, imageStaging; StagingBuffer imageStaging;
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
vertexStaging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
imageStaging.Init(&device, imageFile.GetSize(), "Image Staging"); imageStaging.Init(&device, imageFile.GetSize(), "Image Staging");
imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data); imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data);
vk::ImageMemoryBarrier imageReadyToWrite = { vk::ImageMemoryBarrier2 imageReadyToWrite = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.oldLayout = vk::ImageLayout::eUndefined, .oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate.m_Image, .image = crate->m_Image,
.subresourceRange = .subresourceRange =
{ {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
@ -262,13 +257,21 @@ main(int, char **)
.layerCount = 1, .layerCount = 1,
}, },
}; };
vk::DependencyInfo imageReadyToWriteDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageReadyToWrite,
};
vk::ImageMemoryBarrier imageReadyToRead = { vk::ImageMemoryBarrier2 imageReadyToRead = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader,
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferDstOptimal, .oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate.m_Image, .image = crate->m_Image,
.subresourceRange = .subresourceRange =
{ {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
@ -278,6 +281,10 @@ main(int, char **)
.layerCount = 1, .layerCount = 1,
}, },
}; };
vk::DependencyInfo imageReadyToReadDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &imageReadyToRead,
};
vk::Fence fence; vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {}; vk::FenceCreateInfo fenceCreateInfo = {};
@ -285,11 +292,8 @@ main(int, char **)
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(copyBuffer.begin(&beginInfo)); AbortIfFailed(copyBuffer.begin(&beginInfo));
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &imageReadyToWrite);
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = vertexStaging.GetSize()}; copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency);
copyBuffer.copyBuffer(vertexStaging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy);
vk::BufferImageCopy imageCopy = { vk::BufferImageCopy imageCopy = {
.bufferOffset = 0, .bufferOffset = 0,
@ -305,11 +309,10 @@ main(int, char **)
.imageOffset = {}, .imageOffset = {},
.imageExtent = {imageFile.m_Width, imageFile.m_Height, 1}, .imageExtent = {imageFile.m_Width, imageFile.m_Height, 1},
}; };
copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate.m_Image, vk::ImageLayout::eTransferDstOptimal, 1, copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
&imageCopy); &imageCopy);
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, copyBuffer.pipelineBarrier2(&imageReadyToReadDependency);
0, nullptr, 0, nullptr, 1, &imageReadyToRead);
AbortIfFailed(copyBuffer.end()); AbortIfFailed(copyBuffer.end());
@ -327,7 +330,6 @@ main(int, char **)
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool."); AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr); device.m_Device.destroy(fence, nullptr);
vertexStaging.Destroy(&device);
imageStaging.Destroy(&device); imageStaging.Destroy(&device);
} }
@ -351,19 +353,23 @@ main(int, char **)
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler)); AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
} }
UniformBuffer ubo; auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer();
ubo.Init(&device, sizeof camera, "Camera UBO"); ubo->Write(&device, 0, sizeof camera, &camera);
ubo.Write(&device, 0, sizeof camera, &camera);
vk::DescriptorBufferInfo descriptorBufferInfo = { vk::DescriptorBufferInfo descriptorBufferInfo = {
.buffer = ubo.m_Buffer, .buffer = ubo->m_Buffer,
.offset = 0, .offset = 0,
.range = ubo.GetSize(), .range = ubo->GetSize(),
}; };
vk::DescriptorImageInfo descriptorImageInfo = { vk::DescriptorImageInfo descriptorImageInfo = {
.sampler = sampler, .sampler = sampler,
.imageView = crate.m_View, .imageView = crate->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
}; };
vk::DescriptorBufferInfo descriptorStorageBufferInfo = {
.buffer = vbo->m_Buffer,
.offset = 0,
.range = vbo->GetSize(),
};
eastl::array writeDescriptors = { eastl::array writeDescriptors = {
vk::WriteDescriptorSet{ vk::WriteDescriptorSet{
.dstSet = descriptorSet, .dstSet = descriptorSet,
@ -381,6 +387,14 @@ main(int, char **)
.descriptorType = vk::DescriptorType::eCombinedImageSampler, .descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &descriptorImageInfo, .pImageInfo = &descriptorImageInfo,
}, },
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 2,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &descriptorStorageBufferInfo,
},
}; };
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr); device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
@ -414,35 +428,56 @@ main(int, char **)
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 1, .layerCount = 1,
}; };
vk::ImageMemoryBarrier topOfThePipeBarrier = { vk::ImageMemoryBarrier2 topOfThePipeBarrier = {
// For Color Attachment output ref:
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7193#issuecomment-1875960974
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eNone,
.dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined, .oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange, .subresourceRange = subresourceRange,
}; };
vk::ImageMemoryBarrier renderToPresentBarrier = { vk::DependencyInfo topOfThePipeDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &topOfThePipeBarrier,
};
vk::ImageMemoryBarrier2 renderToPresentBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
.dstAccessMask = vk::AccessFlagBits2::eNone,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR, .newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange, .subresourceRange = subresourceRange,
}; };
vk::DependencyInfo renderToPresentDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &renderToPresentBarrier,
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight); eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
for (auto &depthImage : depthImages)
{
depthImage.Init(&device, swapchain.m_Extent, "Depth");
}
auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) { auto initDepthImages = [&imageManager, &depthImages, &frameManager] (const vk::Extent2D extent) {
for (auto &depthImage : depthImages) for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
{ {
depthImage.Destroy(&device); depthImages.push_back(
depthImage.Init(&device, extent, "Depth"); imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer());
} }
}; };
initDepthImages(swapchain.m_Extent);
auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) {
depthImages.clear();
initDepthImages(extent);
};
swapchain.RegisterResizeCallback(recreateDepthBuffers); swapchain.RegisterResizeCallback(recreateDepthBuffers);
Time::Init(); Time::Init();
@ -453,7 +488,7 @@ main(int, char **)
Time::Update(); Time::Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo.Write(&device, 0, sizeof camera, &camera); ubo->Write(&device, 0, sizeof camera, &camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
@ -461,7 +496,7 @@ main(int, char **)
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View; vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View;
topOfThePipeBarrier.image = currentImage; topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage; renderToPresentBarrier.image = currentImage;
@ -469,8 +504,7 @@ main(int, char **)
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(cmd.begin(&beginInfo)); AbortIfFailed(cmd.begin(&beginInfo));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput, cmd.pipelineBarrier2(&topOfThePipeDependency);
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
// Render // Render
eastl::array attachmentInfos = { eastl::array attachmentInfos = {
@ -480,7 +514,7 @@ main(int, char **)
.resolveMode = vk::ResolveModeFlagBits::eNone, .resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear, .loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore, .storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f}, .clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 0.0f},
}, },
}; };
@ -507,14 +541,11 @@ main(int, char **)
cmd.setScissor(0, 1, &scissor); cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr);
usize offsets = 0;
cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets);
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0); cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
cmd.endRendering(); cmd.endRendering();
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe, cmd.pipelineBarrier2(&renderToPresentDependency);
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
AbortIfFailed(cmd.end()); AbortIfFailed(cmd.end());
@ -534,17 +565,9 @@ main(int, char **)
} }
device.WaitIdle(); device.WaitIdle();
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
}
device.m_Device.destroy(sampler, nullptr); device.m_Device.destroy(sampler, nullptr);
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr); device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr); device.m_Device.destroy(copyPool, nullptr);
crate.Destroy(&device);
vbo.Destroy(&device);
return 0; return 0;
} }
@ -582,6 +605,12 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
.descriptorCount = 1, .descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment, .stageFlags = vk::ShaderStageFlagBits::eFragment,
}, },
vk::DescriptorSetLayoutBinding{
.binding = 2,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
}; };
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()), .bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()),
@ -601,15 +630,7 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
device->SetName(pipelineLayout, "Box Layout"); device->SetName(pipelineLayout, "Box Layout");
vk::VertexInputBindingDescription inputBindingDescription = Vertex::GetBinding(0); vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {};
auto inputAttributeDescription = Vertex::GetAttributes(0);
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &inputBindingDescription,
.vertexAttributeDescriptionCount = Cast<u32>(inputAttributeDescription.size()),
.pVertexAttributeDescriptions = inputAttributeDescription.data(),
};
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
.topology = vk::PrimitiveTopology::eTriangleList, .topology = vk::PrimitiveTopology::eTriangleList,
.primitiveRestartEnable = false, .primitiveRestartEnable = false,

View File

@ -0,0 +1,19 @@
struct FS_Input {
float2 UV0 : TEXCOORD0;
};
struct FS_Output
{
float4 ColorTarget : SV_Target0;
};
[[vk::binding(1, 0)]] Texture2D<float4> Texture;
[[vk::binding(1, 0)]] SamplerState Sampler;
FS_Output main(FS_Input StageInput) {
FS_Output output;
output.ColorTarget = float4(Texture.Sample(Sampler, StageInput.UV0).rgb, 1.0);
return output;
}

View File

@ -0,0 +1,36 @@
struct VS_Input
{
uint VertexIndex : SV_VertexID;
};
struct VS_Output
{
float2 UV0 : TEXCOORD0;
float4 VertexPosition : SV_Position;
};
struct CameraData {
float4x4 Model;
float4x4 View;
float4x4 Projection;
};
struct VertexData {
float4 Position;
float2 UV0;
};
[[vk::binding(0, 0)]] ConstantBuffer<CameraData> Camera;
[[vk::binding(2, 0)]] StructuredBuffer<VertexData> Vertices;
VS_Output main(VS_Input StageInput) {
VS_Output output;
output.UV0 = Vertices[StageInput.VertexIndex].UV0;
float4 position = Vertices[StageInput.VertexIndex].Position;
output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position)));
return output;
}