Compare commits

...

4 Commits

Author SHA1 Message Date
Anish Bhobe 69aa72770f Model Vertices rendered. 2024-07-14 20:54:32 +02:00
Anish Bhobe 93981bca4c Bindless VBO. 2024-07-14 00:09:14 +02:00
Anish Bhobe 6c15f599aa Texture bindless works! 2024-07-13 01:24:07 +02:00
Anish Bhobe c7ea45bce7 Setup for bindless. 2024-07-12 11:24:58 +02:00
23 changed files with 2124 additions and 141 deletions

View File

@ -11,11 +11,11 @@
void
Buffer::Destroy(const Device *device)
{
if (!m_Buffer)
if (!IsValid())
return;
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
m_Buffer = nullptr;
m_Size &= ~VALID_BUFFER_BIT;
}
void
@ -41,24 +41,21 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
m_Buffer = buffer;
m_Size = size & SIZE_MASK;
m_Allocation = allocation;
m_Mapped = allocationInfo.pMappedData;
vk::MemoryPropertyFlags memoryPropertyFlags;
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)
{
m_Size |= HOST_ACCESSIBLE_BIT;
}
bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
m_Buffer = buffer;
m_Size = size | VALID_BUFFER_BIT | (hostAccessible ? HOST_ACCESSIBLE_BIT : 0);
m_Allocation = allocation;
m_Mapped = allocationInfo.pMappedData;
device->SetName(m_Buffer, name);
}
void
Buffer::Write(const Device *device, usize offset, usize size, void *data)
Buffer::Write(const Device *device, usize offset, usize size, const void *data)
{
assert(IsHostVisible());
@ -93,6 +90,23 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name)
VMA_MEMORY_USAGE_AUTO, name);
}
void
StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
{
if (hostVisible)
{
Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer,
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
VMA_MEMORY_USAGE_AUTO, name);
}
else
{
Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst,
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
}
}
void
VertexBuffer::Init(const Device *device, usize size, cstr name)
{
@ -100,6 +114,13 @@ VertexBuffer::Init(const Device *device, usize size, cstr name)
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
}
void
IndexBuffer::Init(const Device *device, usize size, cstr name)
{
Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
}
void
StagingBuffer::Init(const Device *device, usize size, cstr name)
{

View File

@ -17,19 +17,23 @@ struct Buffer
[[nodiscard]] usize GetSize() const;
[[nodiscard]] bool IsHostVisible() const;
[[nodiscard]] bool IsValid() const;
[[nodiscard]] bool IsMapped() const;
void Destroy(const Device *device);
void Write(const Device *device, usize offset, usize size, void *data);
void Write(const Device *device, usize offset, usize size, const void *data);
protected:
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
// Buffer size is used intrusively by the Render Resource Manager
// If the buffer is Invalid, the remaining data in Buffer is used for other tasks.
usize m_Size = 0;
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63;
constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT;
constexpr static usize VALID_BUFFER_BIT = Cast<usize>(1llu << 63);
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 62;
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | HOST_ACCESSIBLE_BIT);
};
struct UniformBuffer : Buffer
@ -37,12 +41,23 @@ struct UniformBuffer : Buffer
void Init(const Device *device, usize size, cstr name = nullptr);
};
struct StorageBuffer : Buffer
{
void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr);
};
struct VertexBuffer : Buffer
{
void Init(const Device *device, usize size, cstr name = nullptr);
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
};
struct IndexBuffer : Buffer
{
void Init(const Device *device, usize size, cstr name = nullptr);
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
};
struct StagingBuffer : Buffer
{
void Init(const Device *device, usize size, cstr name = nullptr);
@ -60,6 +75,12 @@ Buffer::IsHostVisible() const
return m_Size & HOST_ACCESSIBLE_BIT;
}
inline bool
Buffer::IsValid() const
{
return m_Size & VALID_BUFFER_BIT;
}
inline bool
Buffer::IsMapped() const
{

View File

@ -27,7 +27,7 @@ using f128 = long double;
using b8 = bool;
using b32 = u32;
using usize = size_t;
using p64 = intptr_t;
using uptr = uintptr_t;
using cstr = const char *;
namespace ansi_color

View File

@ -40,6 +40,8 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
[[nodiscard]] inline bool
Failed(const vk::Result result)
{
@ -75,7 +77,7 @@ HashCombine(const usize hash0, const usize hash1)
struct Time
{
static constexpr f64 cMaxDelta = 0.1;
static constexpr f64 MAX_DELTA = 0.1;
inline static f64 m_Elapsed{Qnan<f64>};
inline static f64 m_Delta{Qnan<f64>};
@ -93,7 +95,7 @@ struct Time
{
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
const auto newElapsed = glfwGetTime();
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, cMaxDelta);
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, MAX_DELTA);
m_Elapsed = newElapsed;
}
};

View File

@ -10,20 +10,22 @@
void
Image::Destroy(const Device *device)
{
if (!m_Image)
if (!IsValid())
return;
device->m_Device.destroy(m_View, nullptr);
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
m_Image = nullptr;
}
void
Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipmapped, const cstr name)
Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipmapped,
const cstr name)
{
const u32 mipLevels = isMipmapped ? 1 + Cast<u32>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = vk::Format::eR8G8B8A8Srgb,
.format = imageFormat,
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
.mipLevels = mipLevels,
.arrayLayers = 1,
@ -42,11 +44,31 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = {extent.width, extent.height, 1};
m_MipLevels = mipLevels;
device->SetName(m_Image, name);
}
@ -54,9 +76,10 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
void
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
{
constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = vk::Format::eD32Sfloat,
.format = imageFormat,
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
.mipLevels = 1,
.arrayLayers = 1,
@ -75,9 +98,28 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
VmaAllocation allocation;
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &image, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
vk::ImageView view;
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = image,
.viewType = vk::ImageViewType::e2D,
.format = imageFormat,
.components = {},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
m_Image = image;
m_View = view;
m_Allocation = allocation;
m_Extent = {extent.width, extent.height, 1};

View File

@ -12,19 +12,28 @@ struct Device;
struct Image
{
vk::Image m_Image = nullptr;
vk::ImageView m_View = nullptr;
VmaAllocation m_Allocation = nullptr;
vk::Extent3D m_Extent;
usize m_Size = 0;
u8 m_MipLevels = 1;
[[nodiscard]] bool IsValid() const;
void Destroy(const Device *device);
};
struct Texture : Image
{
void Init(const Device *device, vk::Extent2D extent, bool isMipmapped, cstr name = nullptr);
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipmapped, cstr name = nullptr);
};
struct DepthImage : Image
{
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
};
inline bool
Image::IsValid() const
{
return m_Image;
}

View File

@ -61,7 +61,7 @@ Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Window
break;
case vk::Result::eErrorOutOfDateKHR:
case vk::Result::eSuboptimalKHR:
INFO("Recreating Swapchain. Cause: {}", result);
DEBUG("Recreating Swapchain. Cause: {}", result);
swapchain->Create(window);
break; // Present failed. We do nothing. Frame is skipped.
default:
@ -116,7 +116,7 @@ FrameManager::GetNextFrame(Swapchain *swapchain, const Window *window)
imageAcquired = true;
break; // Image acquired. Break out of loop.
case vk::Result::eErrorOutOfDateKHR:
INFO("Recreating Swapchain. Cause: {}", result);
DEBUG("Recreating Swapchain. Cause: {}", result);
swapchain->Create(window);
break; // Image acquire has failed. We move to the next frame.
default:

View File

@ -60,7 +60,7 @@ ImageFile::Load(cstr fileName)
usize
ImageFile::GetSize() const
{
return m_Width * m_Height * m_NumChannels;
return Cast<usize>(m_Width) * m_Height * m_NumChannels;
}
ImageFile::~ImageFile()
@ -75,7 +75,7 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
struct Vertex
{
vec3 m_Position;
vec2 m_UV0;
vec2 m_TexCoord0;
constexpr static vk::VertexInputBindingDescription
GetBinding(const u32 binding)
@ -97,7 +97,7 @@ struct Vertex
.location = 1,
.binding = binding,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, m_UV0),
.offset = offsetof(Vertex, m_TexCoord0),
},
};
}
@ -185,47 +185,47 @@ main(int, char **)
}
eastl::array vertices = {
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
};
ImageFile imageFile;
@ -236,7 +236,7 @@ main(int, char **)
VertexBuffer vbo;
Texture crate;
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, false, "Crate Texture");
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Crate Texture");
{
StagingBuffer vertexStaging, imageStaging;
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
@ -330,30 +330,8 @@ main(int, char **)
imageStaging.Destroy(&device);
}
vk::ImageView imageView;
vk::Sampler sampler;
{
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = crate.m_Image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eR8G8B8A8Srgb,
.components =
vk::ComponentMapping{
.r = vk::ComponentSwizzle::eIdentity,
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity,
},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &imageView));
vk::SamplerCreateInfo samplerCreateInfo = {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
@ -382,7 +360,7 @@ main(int, char **)
};
vk::DescriptorImageInfo descriptorImageInfo = {
.sampler = sampler,
.imageView = imageView,
.imageView = crate.m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
};
eastl::array writeDescriptors = {
@ -420,13 +398,13 @@ main(int, char **)
.extent = swapchain.m_Extent,
};
auto ResizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
auto resizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
viewport.y = Cast<f32>(extent.height);
viewport.width = Cast<f32>(extent.width);
viewport.height = -Cast<f32>(extent.height);
scissor.extent = extent;
};
swapchain.RegisterResizeCallback(ResizeViewportScissor);
swapchain.RegisterResizeCallback(resizeViewportScissor);
vk::ImageSubresourceRange subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
@ -452,55 +430,19 @@ main(int, char **)
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<vk::ImageView, MAX_FRAMES_IN_FLIGHT> depthViews(frameManager.m_FramesInFlight);
vk::ImageSubresourceRange depthSubresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
u32 index = 0;
for (auto &depthImage : depthImages)
{
auto name = fmt::format("Depth image {}", index);
depthImage.Init(&device, swapchain.m_Extent, name.c_str());
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = depthImage.m_Image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eD32Sfloat,
.components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity},
.subresourceRange = depthSubresourceRange,
};
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index]));
index++;
depthImage.Init(&device, swapchain.m_Extent, "Depth");
}
auto RecreateDepthBuffers = [&device, &depthImages, &depthViews, depthSubresourceRange](vk::Extent2D extent) {
for (auto &depthView : depthViews)
{
device.m_Device.destroy(depthView, nullptr);
}
u32 index = 0;
auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) {
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
depthImage.Init(&device, extent, "Depth");
vk::ImageViewCreateInfo imageViewCreateInfo = {
.image = depthImage.m_Image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eD32Sfloat,
.components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity},
.subresourceRange = depthSubresourceRange,
};
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index]));
++index;
}
};
swapchain.RegisterResizeCallback(RecreateDepthBuffers);
swapchain.RegisterResizeCallback(recreateDepthBuffers);
Time::Init();
@ -518,7 +460,7 @@ main(int, char **)
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
vk::ImageView currentDepthImageView = depthViews[currentFrame->m_FrameIdx];
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View;
topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage;
@ -592,16 +534,11 @@ main(int, char **)
AbortIfFailed(device.m_Device.waitIdle());
for (auto &depthView : depthViews)
{
device.m_Device.destroy(depthView, nullptr);
}
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
}
device.m_Device.destroy(sampler, nullptr);
device.m_Device.destroy(imageView, nullptr);
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr);

View File

@ -0,0 +1,25 @@
# CMakeList.txt ; CMake project for box
cmake_minimum_required(VERSION 3.13)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
find_path(TINYGLTF_INCLUDE_DIRS "tiny_gltf.h")
add_executable(model_render model_render.cpp
pipeline_utils.cpp
pipeline_utils.h
render_resource_manager.cpp
render_resource_manager.h
model_loader.cpp
model_loader.h
)
add_shader(model_render shader/model.vert.glsl)
add_shader(model_render shader/model.frag.glsl)
target_link_libraries(model_render PRIVATE aster_core)
target_link_libraries(model_render PRIVATE util_helper)
target_include_directories(model_render PRIVATE ${TINYGLTF_INCLUDE_DIRS})
add_resource_dir(model_render model)
add_resource_dir(model_render image)

BIN
samples/03_model_render/image/container.jpg (Stored with Git LFS) Normal file

Binary file not shown.

BIN
samples/03_model_render/model/Box.glb (Stored with Git LFS) Normal file

Binary file not shown.

BIN
samples/03_model_render/model/BoxTextured.glb (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,516 @@
// =============================================
// Aster: model_loader.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#define TINYGLTF_NOEXCEPTION
#define JSON_NOEXCEPTION
#define TINYGLTF_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "model_loader.h"
#include "buffer.h"
#include "device.h"
#include "helpers.h"
#include "image.h"
#include "render_resource_manager.h"
#include <EASTL/array.h>
#include <EASTL/fixed_vector.h>
#include <EASTL/span.h>
vec4
VectorToVec4(const std::vector<double> &vec)
{
if (vec.empty())
{
return vec4{0.0f};
}
return {vec[0], vec[1], vec[2], vec[3]};
}
TextureHandle
ModelLoader::LoadImage(vk::CommandBuffer commandBuffer, Texture *texture, StagingBuffer *stagingBuffer,
tinygltf::Image *image)
{
assert(image->component == 4);
texture->Init(m_ResourceManager->m_Device, {.width = Cast<u32>(image->width), .height = Cast<u32>(image->height)},
vk::Format::eR8G8B8A8Srgb, true, image->name.data());
// .srcAccessMask = ,
// .dstAccessMask = ,
// .oldLayout = ,
// .newLayout = ,
// .srcQueueFamilyIndex = ,
// .dstQueueFamilyIndex = ,
// .image = ,
// .subresourceRange =
vk::ImageMemoryBarrier imageStartBarrier = {
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = texture->m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = texture->m_MipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk::ImageMemoryBarrier nextMipBarrier = {
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = texture->m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk::ImageMemoryBarrier imageReadyBarrier = {
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
.dstAccessMask = vk::AccessFlagBits::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = m_TransferQueueIndex,
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
.image = texture->m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = texture->m_MipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk::BufferImageCopy imageCopy = {
.bufferOffset = 0,
.bufferRowLength = Cast<u32>(image->width),
.bufferImageHeight = Cast<u32>(image->height),
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {},
.imageExtent = texture->m_Extent,
};
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &imageStartBarrier);
commandBuffer.copyBufferToImage(stagingBuffer->m_Buffer, texture->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
&imageCopy);
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &nextMipBarrier);
auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); };
i32 prevMipWidth = Cast<i32>(texture->m_Extent.width);
i32 prevMipHeight = Cast<i32>(texture->m_Extent.height);
for (u32 prevMipLevel = 1; prevMipLevel < texture->m_MipLevels; ++prevMipLevel)
{
i32 currentMipWidth = calcNextMip(prevMipWidth);
i32 currentMipHeight = calcNextMip(prevMipHeight);
u32 currentMipLevel = prevMipLevel + 1;
vk::ImageBlit blitRegion = {
.srcSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = prevMipLevel,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcOffsets =
std::array{
vk::Offset3D{0, 0, 0},
vk::Offset3D{prevMipWidth, prevMipHeight, 1},
},
.dstSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = currentMipLevel,
.baseArrayLayer = 0,
.layerCount = 1,
},
.dstOffsets =
std::array{
vk::Offset3D{0, 0, 0},
vk::Offset3D{currentMipWidth, currentMipHeight, 1},
},
};
nextMipBarrier.subresourceRange.baseMipLevel = prevMipLevel;
commandBuffer.blitImage(texture->m_Image, vk::ImageLayout::eTransferSrcOptimal, texture->m_Image,
vk::ImageLayout::eTransferDstOptimal, 1, &blitRegion, vk::Filter::eLinear);
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &nextMipBarrier);
}
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTopOfPipe, {}, 0,
nullptr, 0, nullptr, 1, &imageReadyBarrier);
return m_ResourceManager->Commit(texture);
}
Model
ModelLoader::LoadModel(cstr path, cstr name)
{
namespace fs = std::filesystem;
tinygltf::Model model;
tinygltf::TinyGLTF loader;
const Device *pDevice = m_ResourceManager->m_Device;
const auto fsPath = fs::absolute(path);
const auto ext = fsPath.extension();
if (ext == GLTF_ASCII_FILE_EXTENSION)
{
std::string err;
std::string warn;
if (loader.LoadASCIIFromFile(&model, &err, &warn, fsPath.generic_string()))
{
ERROR_IF(!err.empty(), "{}", err)
ELSE_IF_WARN(!warn.empty(), "{}", warn);
}
}
if (ext == GLTF_BINARY_FILE_EXTENSION)
{
std::string err;
std::string warn;
if (loader.LoadBinaryFromFile(&model, &err, &warn, fsPath.generic_string()))
{
ERROR_IF(!err.empty(), "{}", err)
ELSE_IF_WARN(!warn.empty(), "{}", warn);
}
}
vk::CommandBuffer commandBuffer;
{
vk::CommandBufferAllocateInfo commandBufferAllocateInfo = {
.commandPool = m_CommandPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailed(pDevice->m_Device.allocateCommandBuffers(&commandBufferAllocateInfo, &commandBuffer));
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(commandBuffer.begin(&beginInfo));
}
eastl::vector<StagingBuffer> stagingBuffers;
eastl::vector<Texture> textures;
eastl::vector<TextureHandle> textureHandles;
if (!model.images.empty())
{
u32 numImages = model.images.size();
stagingBuffers.resize(numImages);
textures.resize(numImages);
textureHandles.resize(numImages);
auto stagingPtr = stagingBuffers.data();
auto texturePtr = textures.data();
auto imagePtr = model.images.data();
for (TextureHandle &handle : textureHandles)
{
handle = LoadImage(commandBuffer, texturePtr++, stagingPtr++, imagePtr++);
}
AbortIfFailed(commandBuffer.end());
}
eastl::vector<Material> materials;
StorageBuffer materialsBuffer;
BufferHandle materialsHandle;
if (!model.materials.empty())
{
auto getTextureHandle = [&textureHandles](i32 index) -> TextureHandle {
if (index >= 0)
{
return textureHandles[index];
}
return {};
};
materials.reserve(model.materials.size());
for (auto &material : model.materials)
{
materials.push_back({
.m_AlbedoFactor = VectorToVec4(material.pbrMetallicRoughness.baseColorFactor),
.m_EmissionFactor = VectorToVec4(material.emissiveFactor),
.m_MetalFactor = Cast<f32>(material.pbrMetallicRoughness.metallicFactor),
.m_RoughFactor = Cast<f32>(material.pbrMetallicRoughness.roughnessFactor),
.m_AlbedoTex = getTextureHandle(material.pbrMetallicRoughness.baseColorTexture.index),
.m_NormalTex = getTextureHandle(material.normalTexture.index),
.m_MetalRoughTex = getTextureHandle(material.pbrMetallicRoughness.metallicRoughnessTexture.index),
.m_OcclusionTex = getTextureHandle(material.occlusionTexture.index),
.m_EmissionTex = getTextureHandle(material.emissiveTexture.index),
});
}
usize materialsByteSize = materials.size() * sizeof materials[0];
materialsBuffer.Init(pDevice, materialsByteSize, false, name);
materialsHandle = m_ResourceManager->Commit(&materialsBuffer);
StagingBuffer &materialStaging = stagingBuffers.push_back();
materialStaging.Init(pDevice, materialsByteSize);
materialStaging.Write(pDevice, 0, materialsByteSize, materials.data());
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = materialsByteSize};
commandBuffer.copyBuffer(materialStaging.m_Buffer, materialsBuffer.m_Buffer, 1, &bufferCopy);
}
// TODO: Mesh reordering based on nodes AND OR meshoptimizer
// TODO: Support scenes
eastl::vector<vec4> vertexPositions;
eastl::vector<u32> indices;
eastl::vector<MeshPrimitive> meshPrimitives;
meshPrimitives.reserve(model.meshes.size());
for (auto &mesh : model.meshes)
{
for (auto &prim : mesh.primitives)
{
u32 vertexOffset = vertexPositions.size();
u32 vertexCount;
u32 indexOffset = indices.size();
u32 indexCount;
assert(prim.attributes.contains(APosition));
assert(prim.mode == TINYGLTF_MODE_TRIANGLES);
{
tinygltf::Accessor *posAccessor = &model.accessors[prim.attributes[APosition]];
assert(posAccessor->count <= MaxValue<u32>);
tinygltf::BufferView *posBufferView = &model.bufferViews[posAccessor->bufferView];
tinygltf::Buffer *posBuffer = &model.buffers[posBufferView->buffer];
usize byteOffset = (posAccessor->byteOffset + posBufferView->byteOffset);
vertexCount = posAccessor->count;
vertexPositions.reserve(vertexOffset + vertexCount);
if (posAccessor->type == TINYGLTF_TYPE_VEC4)
{
vec4 *data = Recast<vec4 *>(posBuffer->data.data() + byteOffset);
vertexPositions.insert(vertexPositions.end(), data, data + vertexCount);
}
else if (posAccessor->type == TINYGLTF_TYPE_VEC3)
{
vec3 *data = Recast<vec3 *>(posBuffer->data.data() + byteOffset);
for (u32 i = 0; i < vertexCount; ++i)
{
vertexPositions.push_back(vec4(data[i], 1.0f));
}
}
else if (posAccessor->type == TINYGLTF_TYPE_VEC2)
{
vec2 *data = Recast<vec2 *>(posBuffer->data.data() + byteOffset);
for (u32 i = 0; i < vertexCount; ++i)
{
vertexPositions.push_back(vec4(data[i], 0.0f, 1.0f));
}
}
}
if (prim.indices >= 0)
{
tinygltf::Accessor *indexAccessor = &model.accessors[prim.indices];
assert(indexAccessor->count <= MaxValue<u32>);
tinygltf::BufferView *indexBufferView = &model.bufferViews[indexAccessor->bufferView];
tinygltf::Buffer *indexBuffer = &model.buffers[indexBufferView->buffer];
usize byteOffset = (indexAccessor->byteOffset + indexBufferView->byteOffset);
indexCount = indexAccessor->count;
indices.reserve(indexOffset + indexCount);
if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
{
u32 *data = Recast<u32 *>(indexBuffer->data.data() + byteOffset);
indices.insert(indices.end(), data, data + indexCount);
}
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
{
u16 *data = Recast<u16 *>(indexBuffer->data.data() + byteOffset);
indices.insert(indices.end(), data, data + indexCount);
}
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
{
u8 *data = Recast<u8 *>(indexBuffer->data.data() + byteOffset);
indices.insert(indices.end(), data, data + indexCount);
}
}
else
{
indexCount = vertexCount;
indices.reserve(indexOffset + vertexCount);
for (u32 i = 0; i < indexCount; ++i)
{
indices.push_back(i);
}
}
meshPrimitives.push_back({
.m_VertexOffset = vertexOffset,
.m_FirstIndex = indexOffset,
.m_IndexCount = indexCount,
.m_MaterialIdx = prim.material,
});
}
}
StorageBuffer positionBuffer;
positionBuffer.Init(pDevice, vertexPositions.size() * sizeof vertexPositions[0], false);
BufferHandle positionBufferHandle = m_ResourceManager->Commit(&positionBuffer);
IndexBuffer indexBuffer;
indexBuffer.Init(pDevice, indices.size() * sizeof indices[0]);
{
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0};
StagingBuffer &positionStaging = stagingBuffers.push_back();
positionStaging.Init(pDevice, positionBuffer.GetSize());
positionStaging.Write(pDevice, 0, positionBuffer.GetSize(), vertexPositions.data());
bufferCopy.size = positionBuffer.GetSize();
commandBuffer.copyBuffer(positionStaging.m_Buffer, positionBuffer.m_Buffer, 1, &bufferCopy);
StagingBuffer &indexStaging = stagingBuffers.push_back();
indexStaging.Init(pDevice, indexBuffer.GetSize());
indexStaging.Write(pDevice, 0, indexBuffer.GetSize(), indices.data());
bufferCopy.size = indexBuffer.GetSize();
commandBuffer.copyBuffer(indexStaging.m_Buffer, indexBuffer.m_Buffer, 1, &bufferCopy);
}
AbortIfFailed(commandBuffer.end());
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 0,
.pWaitDstStageMask = 0,
.commandBufferCount = 1,
.pCommandBuffers = &commandBuffer,
};
vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {};
AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence));
AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue<u32>));
pDevice->m_Device.destroy(fence, nullptr);
for (auto &buffer : stagingBuffers)
{
buffer.Destroy(pDevice);
}
return Model{m_ResourceManager, std::move(textures), std::move(textureHandles),
materialsBuffer, materialsHandle, positionBuffer,
positionBufferHandle, indexBuffer, meshPrimitives};
}
Model::Model(RenderResourceManager *resourceManager, eastl::vector<Texture> &&textures,
eastl::vector<TextureHandle> &&textureHandles, const StorageBuffer &materialsBuffer,
BufferHandle materialsHandle, const StorageBuffer &vertexPosBuffer, BufferHandle vertexPosHandle,
const IndexBuffer &indexBuffer, const eastl::vector<MeshPrimitive> &meshPrimitives)
: m_ResourceManager(resourceManager)
, m_Textures(textures)
, m_TextureHandles(textureHandles)
, m_MaterialsBuffer(materialsBuffer)
, m_MaterialsHandle(materialsHandle)
, m_VertexPositions(vertexPosBuffer)
, m_VertexPositionHandle(vertexPosHandle)
, m_IndexBuffer(indexBuffer)
, m_MeshPrimitives(meshPrimitives)
{
}
Model::Model(Model &&other) noexcept
: m_ResourceManager(Take(other.m_ResourceManager))
, m_Textures(std::move(other.m_Textures))
, m_TextureHandles(std::move(other.m_TextureHandles))
, m_MaterialsBuffer(other.m_MaterialsBuffer)
, m_MaterialsHandle(other.m_MaterialsHandle)
, m_VertexPositions(other.m_VertexPositions)
, m_VertexPositionHandle(other.m_VertexPositionHandle)
, m_IndexBuffer(other.m_IndexBuffer)
, m_MeshPrimitives(std::move(other.m_MeshPrimitives))
{
}
Model &
Model::operator=(Model &&other) noexcept
{
if (this == &other)
return *this;
m_ResourceManager = Take(other.m_ResourceManager);
m_Textures = std::move(other.m_Textures);
m_TextureHandles = std::move(other.m_TextureHandles);
m_MaterialsBuffer = other.m_MaterialsBuffer;
m_MaterialsHandle = other.m_MaterialsHandle;
m_VertexPositions = other.m_VertexPositions;
m_VertexPositionHandle = other.m_VertexPositionHandle;
m_IndexBuffer = other.m_IndexBuffer;
m_MeshPrimitives = std::move(other.m_MeshPrimitives);
return *this;
}
Model::~Model()
{
if (!m_ResourceManager)
return;
m_VertexPositions.Destroy(m_ResourceManager->m_Device);
m_IndexBuffer.Destroy(m_ResourceManager->m_Device);
m_ResourceManager->Release(m_VertexPositionHandle);
for (const TextureHandle &handle : m_TextureHandles)
{
m_ResourceManager->Release(handle);
}
m_ResourceManager->Release(m_MaterialsHandle);
for (Texture &texture : m_Textures)
{
texture.Destroy(m_ResourceManager->m_Device);
}
m_MaterialsBuffer.Destroy(m_ResourceManager->m_Device);
}
ModelLoader::ModelLoader(RenderResourceManager *resourceManager, vk::CommandPool commandPool, vk::Queue transferQueue,
u32 transferQueueIndex, u32 graphicsQueueIndex)
: m_ResourceManager(resourceManager)
, m_CommandPool(commandPool)
, m_TransferQueue(transferQueue)
, m_TransferQueueIndex(transferQueueIndex)
, m_GraphicsQueueIndex(graphicsQueueIndex)
{
}

View File

@ -0,0 +1,90 @@
// =============================================
// Aster: model_loader.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "buffer.h"
#include "global.h"
#include "image.h"
#include "render_resource_manager.h"
#include <tiny_gltf.h>
struct TextureHandle;
struct Texture;
constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf";
constexpr auto GLTF_BINARY_FILE_EXTENSION = ".glb";
struct MeshPrimitive
{
u32 m_VertexOffset;
u32 m_FirstIndex;
u32 m_IndexCount;
i32 m_MaterialIdx; // -1 for invalid
};
struct Material
{
vec4 m_AlbedoFactor;
vec4 m_EmissionFactor;
f32 m_MetalFactor;
f32 m_RoughFactor;
TextureHandle m_AlbedoTex;
TextureHandle m_NormalTex;
TextureHandle m_MetalRoughTex;
TextureHandle m_OcclusionTex;
TextureHandle m_EmissionTex;
};
struct Model
{
RenderResourceManager *m_ResourceManager;
eastl::vector<Texture> m_Textures;
eastl::vector<TextureHandle> m_TextureHandles;
StorageBuffer m_MaterialsBuffer;
BufferHandle m_MaterialsHandle;
StorageBuffer m_VertexPositions;
BufferHandle m_VertexPositionHandle;
IndexBuffer m_IndexBuffer;
eastl::vector<MeshPrimitive> m_MeshPrimitives;
Model(RenderResourceManager *resourceManager, eastl::vector<Texture> &&textures,
eastl::vector<TextureHandle> &&textureHandles, const StorageBuffer &materialsBuffer,
BufferHandle materialsHandle, const StorageBuffer &vertexPosBuffer, BufferHandle vertexPosHandle,
const IndexBuffer &indexBuffer, const eastl::vector<MeshPrimitive> &meshPrimitives);
Model(Model &&other) noexcept;
Model &operator=(Model &&other) noexcept;
~Model();
DISALLOW_COPY_AND_ASSIGN(Model);
};
struct ModelLoader
{
RenderResourceManager *const m_ResourceManager;
vk::CommandPool m_CommandPool;
vk::Queue m_TransferQueue;
u32 m_TransferQueueIndex;
u32 m_GraphicsQueueIndex;
ModelLoader(RenderResourceManager *resourceManager, vk::CommandPool commandPool, vk::Queue transferQueue,
u32 transferQueueIndex, u32 graphicsQueueIndex);
TextureHandle
LoadImage(vk::CommandBuffer commandBuffer, Texture *texture, StagingBuffer *stagingBuffer, tinygltf::Image *image);
Model LoadModel(cstr path, cstr name = nullptr);
constexpr static auto ANormal = "NORMAL";
constexpr static auto APosition = "POSITION";
constexpr static auto ATangent = "TANGENT";
constexpr static auto ATexCoord0 = "TEXCOORD_0";
constexpr static auto ATexCoord1 = "TEXCOORD_1";
constexpr static auto AJoints0 = "JOINTS_0";
constexpr static auto AWeights0 = "WEIGHTS_0";
};

View File

@ -0,0 +1,632 @@
// =============================================
// Aster: model_render.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "buffer.h"
#include "constants.h"
#include "context.h"
#include "device.h"
#include "global.h"
#include "image.h"
#include "physical_device.h"
#include "pipeline.h"
#include "swapchain.h"
#include "window.h"
#include "frame.h"
#include "helpers.h"
#include "model_loader.h"
#include "pipeline_utils.h"
#include "render_resource_manager.h"
#include <EASTL/array.h>
#include <stb_image.h>
#include <tiny_gltf.h>
#include <filesystem>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto MODEL_FILE = "model/Box.glb";
struct ImageFile
{
u8 *m_Data = nullptr;
u32 m_Width = 0;
u32 m_Height = 0;
u32 m_NumChannels = 0;
bool m_Constant = false;
bool Load(cstr fileName);
bool Load(vec4 color);
[[nodiscard]] usize GetSize() const;
~ImageFile();
};
struct Camera
{
mat4 m_Model;
mat4 m_View;
mat4 m_Perspective;
};
int
main(int, char **)
{
MIN_LOG_LEVEL(Logger::LogType::eInfo);
Context context = {"ModelRender [WIP]", VERSION};
Window window = {"ModelRender [WIP] (Aster)", &context, {640, 480}};
PhysicalDevices physicalDevices = {&window, &context};
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan12Features =
{
.descriptorIndexing = true,
.shaderSampledImageArrayNonUniformIndexing = true,
.shaderStorageBufferArrayNonUniformIndexing = true,
.shaderStorageImageArrayNonUniformIndexing = true,
.descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless
.descriptorBindingSampledImageUpdateAfterBind = true,
.descriptorBindingStorageImageUpdateAfterBind = true,
.descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true,
},
.m_Vulkan13Features = {.dynamicRendering = true},
};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
Swapchain swapchain = {&window, &device, "Primary Chain"};
RenderResourceManager resourceManager = {&device, 1000};
vk::CommandPool copyPool;
vk::CommandBuffer copyBuffer;
{
vk::CommandPoolCreateInfo poolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueAllocation.m_Family,
};
AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, &copyPool),
"Copy command pool creation failed.");
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
.commandPool = copyPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, &copyBuffer),
"Copy command buffer allocation failed.");
}
ModelLoader modelLoader = {&resourceManager, copyPool, commandQueue, queueAllocation.m_Family,
queueAllocation.m_Family};
auto model = modelLoader.LoadModel(MODEL_FILE);
Pipeline pipeline = CreatePipeline(&device, &swapchain, &resourceManager);
Camera camera = {
.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_Perspective = glm::perspective(
70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f),
};
vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet;
{
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts[1];
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
},
};
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool));
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptorSetLayout,
};
AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &descriptorSet));
}
eastl::array vertices = {
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
};
ImageFile crateImageFile;
ImageFile plainImageFile;
assert(crateImageFile.Load("image/container.jpg"));
INFO("Image {}x{} : {} channels", crateImageFile.m_Width, crateImageFile.m_Height, crateImageFile.m_NumChannels);
assert(plainImageFile.Load({0.7f, 0.4f, 0.1f, 1.0f}));
StorageBuffer pvbo;
Texture crateTexture;
Texture plainTexture;
crateTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false,
"Crate Texture");
plainTexture.Init(&device, {crateImageFile.m_Width, crateImageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false,
"Plain Texture");
pvbo.Init(&device, vertices.size() * sizeof vertices[0], true, "Pull VBO");
pvbo.Write(&device, 0, pvbo.GetSize(), vertices.data());
auto crateTextureId = resourceManager.Commit(&crateTexture);
auto plainTextureId = resourceManager.Commit(&plainTexture);
auto pvboBufferId = resourceManager.Commit(&pvbo);
{
StagingBuffer imageStaging1, imageStaging2;
imageStaging1.Init(&device, crateImageFile.GetSize(), "Image Staging 1");
imageStaging1.Write(&device, 0, crateImageFile.GetSize(), crateImageFile.m_Data);
imageStaging2.Init(&device, plainImageFile.GetSize(), "Image Staging 2");
imageStaging2.Write(&device, 0, plainImageFile.GetSize(), plainImageFile.m_Data);
eastl::array imageReadyToWrite = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crateTexture.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = plainTexture.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
eastl::array imageReadyToRead = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crateTexture.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = plainTexture.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {};
AbortIfFailed(device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(copyBuffer.begin(&beginInfo));
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, Cast<u32>(imageReadyToWrite.size()), imageReadyToWrite.data());
vk::BufferImageCopy imageCopy = {
.bufferOffset = 0,
.bufferRowLength = crateImageFile.m_Width,
.bufferImageHeight = crateImageFile.m_Height,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {},
.imageExtent = {crateImageFile.m_Width, crateImageFile.m_Height, 1},
};
copyBuffer.copyBufferToImage(imageStaging1.m_Buffer, crateTexture.m_Image, vk::ImageLayout::eTransferDstOptimal,
1, &imageCopy);
copyBuffer.copyBufferToImage(imageStaging2.m_Buffer, plainTexture.m_Image, vk::ImageLayout::eTransferDstOptimal,
1, &imageCopy);
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {},
0, nullptr, 0, nullptr, Cast<u32>(imageReadyToRead.size()), imageReadyToRead.data());
AbortIfFailed(copyBuffer.end());
vk::SubmitInfo submitInfo = {
.commandBufferCount = 1,
.pCommandBuffers = &copyBuffer,
};
AbortIfFailed(commandQueue.submit(1, &submitInfo, fence));
INFO("Submit copy");
AbortIfFailed(device.m_Device.waitForFences(1, &fence, true, MaxValue<u64>));
INFO("Fence wait");
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr);
imageStaging1.Destroy(&device);
imageStaging2.Destroy(&device);
}
UniformBuffer ubo;
ubo.Init(&device, sizeof camera, "Camera UBO");
ubo.Write(&device, 0, sizeof camera, &camera);
vk::DescriptorBufferInfo descriptorBufferInfo = {
.buffer = ubo.m_Buffer,
.offset = 0,
.range = ubo.GetSize(),
};
eastl::array writeDescriptors = {
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &descriptorBufferInfo,
},
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
resourceManager.Update();
// Persistent variables
vk::Viewport viewport = {
.x = 0,
.y = Cast<f32>(swapchain.m_Extent.height),
.width = Cast<f32>(swapchain.m_Extent.width),
.height = -Cast<f32>(swapchain.m_Extent.height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
vk::Rect2D scissor = {
.offset = {0, 0},
.extent = swapchain.m_Extent,
};
auto fnResizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
viewport.y = Cast<f32>(extent.height);
viewport.width = Cast<f32>(extent.width);
viewport.height = -Cast<f32>(extent.height);
scissor.extent = extent;
};
swapchain.RegisterResizeCallback(fnResizeViewportScissor);
vk::ImageSubresourceRange subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::ImageMemoryBarrier topOfThePipeBarrier = {
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
vk::ImageMemoryBarrier renderToPresentBarrier = {
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.subresourceRange = subresourceRange,
};
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
{
u32 depthImageIdx = 0;
for (auto &depthImage : depthImages)
{
auto name = fmt::format("Depth {}", depthImageIdx++);
depthImage.Init(&device, swapchain.m_Extent, name.c_str());
}
}
auto fnRecreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) {
u32 depthImageIdx = 0;
for (auto &depthImage : depthImages)
{
auto name = fmt::format("Depth {}", depthImageIdx++);
depthImage.Destroy(&device);
depthImage.Init(&device, extent, name.c_str());
}
};
swapchain.RegisterResizeCallback(fnRecreateDepthBuffers);
Time::Init();
GpuResourceHandle *pushData = &crateTextureId;
GpuResourceHandle *otherPushData = &plainTextureId;
bool prevPressed = false;
auto isSpaceJustPressed = [&prevPressed, &window] {
const bool pressed = glfwGetKey(window.m_Window, GLFW_KEY_SPACE) == GLFW_PRESS;
const bool justPressed = pressed & !prevPressed;
prevPressed = pressed;
return justPressed;
};
struct ModelData
{
BufferHandle m_VertexBuffer;
BufferHandle m_Materials;
};
ModelData modelData = {
.m_VertexBuffer = model.m_VertexPositionHandle,
.m_Materials = model.m_MaterialsHandle,
};
INFO("Starting loop");
while (window.Poll())
{
Time::Update();
if (isSpaceJustPressed())
{
eastl::swap(pushData, otherPushData);
}
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);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window);
u32 imageIndex = currentFrame->m_ImageIdx;
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View;
topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage;
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(cmd.begin(&beginInfo));
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
// Render
eastl::array attachmentInfos = {
vk::RenderingAttachmentInfo{
.imageView = currentImageView,
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
},
};
vk::RenderingAttachmentInfo depthAttachment = {
.imageView = currentDepthImageView,
.imageLayout = vk::ImageLayout::eDepthAttachmentOptimal,
.resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eDontCare,
.clearValue = vk::ClearDepthStencilValue{.depth = 1.0f, .stencil = 0},
};
vk::RenderingInfo renderingInfo = {
.renderArea = {.extent = swapchain.m_Extent},
.layerCount = 1,
.colorAttachmentCount = Cast<u32>(attachmentInfos.size()),
.pColorAttachments = attachmentInfos.data(),
.pDepthAttachment = &depthAttachment,
};
cmd.beginRendering(&renderingInfo);
cmd.setViewport(0, 1, &viewport);
cmd.setScissor(0, 1, &scissor);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
// cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof *pushData, pushData);
// cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, sizeof *pushData, sizeof pvboBufferId,
// &pvboBufferId);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
&resourceManager.m_DescriptorSet, 0, nullptr);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &descriptorSet, 0, nullptr);
// cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
cmd.bindIndexBuffer(model.m_IndexBuffer.m_Buffer, 0, vk::IndexType::eUint32);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof modelData, &modelData);
for (auto &prim : model.m_MeshPrimitives)
{
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, sizeof modelData,
sizeof prim.m_MaterialIdx, &prim.m_MaterialIdx);
cmd.drawIndexed(prim.m_IndexCount, 1, prim.m_FirstIndex, Cast<i32>(prim.m_VertexOffset), 0);
}
cmd.endRendering();
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
AbortIfFailed(cmd.end());
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &currentFrame->m_ImageAcquireSem,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &currentFrame->m_RenderFinishSem,
};
AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
currentFrame->Present(commandQueue, &swapchain, &window);
}
AbortIfFailed(device.m_Device.waitIdle());
for (auto &depthImage : depthImages)
{
depthImage.Destroy(&device);
}
ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr);
crateTexture.Destroy(&device);
plainTexture.Destroy(&device);
pvbo.Destroy(&device);
return 0;
}
bool
ImageFile::Load(vec4 color)
{
constexpr usize size = 512llu * 512llu * 4llu;
u8 *pData = new u8[size];
const vec4 color255 = 255.999f * color;
const glm::vec<4, u8> color8 = color255;
for (usize i = 0; i < size; i += 4)
{
memcpy(pData + i, &color8, sizeof color8);
}
m_Data = pData;
m_Constant = true;
m_Height = 512;
m_Width = 512;
m_NumChannels = 4;
return true;
}
bool
ImageFile::Load(cstr fileName)
{
int width, height, nrChannels;
m_Data = stbi_load(fileName, &width, &height, &nrChannels, 4);
ERROR_IF(!m_Data, "Could not load {}", fileName);
if (!m_Data)
{
return false;
}
m_Width = width;
m_Height = height;
m_NumChannels = 4;
m_Constant = false;
return true;
}
usize
ImageFile::GetSize() const
{
return Cast<usize>(m_Width) * m_Height * m_NumChannels;
}
ImageFile::~ImageFile()
{
if (m_Constant)
{
delete[] Cast<u8 *>(m_Data);
}
else
{
stbi_image_free(m_Data);
}
m_Data = nullptr;
}

View File

@ -0,0 +1,177 @@
// =============================================
// Aster: pipeline_utils.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "pipeline_utils.h"
#include "device.h"
#include "helpers.h"
#include "render_resource_manager.h"
#include "swapchain.h"
#include <EASTL/array.h>
Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain, const RenderResourceManager *resourceManager)
{
// Pipeline Setup
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
{
.stage = vk::ShaderStageFlagBits::eVertex,
.module = vertexShaderModule,
.pName = "main",
},
{
.stage = vk::ShaderStageFlagBits::eFragment,
.module = fragmentShaderModule,
.pName = "main",
},
}};
eastl::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
descriptorSetLayouts.push_back(resourceManager->m_SetLayout);
{
eastl::array descriptorSetLayoutBindings = {
vk::DescriptorSetLayoutBinding{
.binding = 0,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
};
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.bindingCount = Cast<u32>(descriptorSetLayoutBindings.size()),
.pBindings = descriptorSetLayoutBindings.data(),
};
vk::DescriptorSetLayout descriptorSetLayout;
AbortIfFailed(
device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout));
descriptorSetLayouts.push_back(descriptorSetLayout);
}
vk::PushConstantRange pushConstantRange = {
.stageFlags = vk::ShaderStageFlagBits::eAll,
.offset = 0,
.size = 16,
};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = Cast<u32>(descriptorSetLayouts.size()),
.pSetLayouts = descriptorSetLayouts.data(),
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange,
};
vk::PipelineLayout pipelineLayout;
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
device->SetName(pipelineLayout, "Box Layout");
descriptorSetLayouts[0] = nullptr; // Not owned.
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {};
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
.topology = vk::PrimitiveTopology::eTriangleList,
.primitiveRestartEnable = false,
};
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
.viewportCount = 1,
.scissorCount = 1,
};
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
.depthClampEnable = false,
.rasterizerDiscardEnable = false,
.polygonMode = vk::PolygonMode::eFill,
.cullMode = vk::CullModeFlagBits::eNone,
.frontFace = vk::FrontFace::eCounterClockwise,
.depthBiasEnable = false,
.lineWidth = 1.0,
};
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
.rasterizationSamples = vk::SampleCountFlagBits::e1,
.sampleShadingEnable = false,
};
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
.depthTestEnable = true,
.depthWriteEnable = true,
.depthCompareOp = vk::CompareOp::eLess,
};
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
.blendEnable = false,
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
.colorBlendOp = vk::BlendOp::eAdd,
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
.alphaBlendOp = vk::BlendOp::eAdd,
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
};
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
.logicOpEnable = false,
.attachmentCount = 1,
.pAttachments = &colorBlendAttachmentState,
};
eastl::array dynamicStates = {
vk::DynamicState::eScissor,
vk::DynamicState::eViewport,
};
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data(),
};
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
.viewMask = 0,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &swapchain->m_Format,
.depthAttachmentFormat = vk::Format::eD24UnormS8Uint,
};
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
.pNext = &renderingCreateInfo,
.stageCount = Cast<u32>(shaderStages.size()),
.pStages = shaderStages.data(),
.pVertexInputState = &vertexInputStateCreateInfo,
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
.pViewportState = &viewportStateCreateInfo,
.pRasterizationState = &rasterizationStateCreateInfo,
.pMultisampleState = &multisampleStateCreateInfo,
.pDepthStencilState = &depthStencilStateCreateInfo,
.pColorBlendState = &colorBlendStateCreateInfo,
.pDynamicState = &dynamicStateCreateInfo,
.layout = pipelineLayout,
};
vk::Pipeline pipeline;
AbortIfFailed(device->m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline));
device->SetName(pipeline, "Box Pipeline");
device->m_Device.destroy(vertexShaderModule, nullptr);
device->m_Device.destroy(fragmentShaderModule, nullptr);
return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)};
}
vk::ShaderModule
CreateShader(const Device *device, cstr shaderFile)
{
eastl::vector<u32> shaderCode = ReadFile(shaderFile);
const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
.codeSize = shaderCode.size() * sizeof(u32),
.pCode = shaderCode.data(),
};
vk::ShaderModule shaderModule;
AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule),
"Shader {} could not be created.", shaderFile);
return shaderModule;
}

View File

@ -0,0 +1,27 @@
// =============================================
// Aster: pipeline_utils.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
#include "pipeline.h"
#include <EASTL/array.h>
struct RenderResourceManager;
struct Swapchain;
struct Device;
constexpr auto VERTEX_SHADER_FILE = "shader/model.vert.glsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/model.frag.glsl.spv";
struct Vertex
{
vec3 m_Position;
vec2 m_TexCoord0;
};
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain, const RenderResourceManager *resourceManager);

View File

@ -0,0 +1,231 @@
// =============================================
// Aster: pipeline_utils.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "render_resource_manager.h"
#include "buffer.h"
#include "device.h"
#include "helpers.h"
#include "image.h"
#include <EASTL/array.h>
RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info)
: uBufferInfo(info)
{
}
RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo info)
: uImageInfo(info)
{
}
RenderResourceManager::WriteInfo::WriteInfo(vk::BufferView info)
: uBufferView(info)
{
}
BufferHandle
RenderResourceManager::Commit(const StorageBuffer *storageBuffer)
{
const u32 handle = m_BufferFreeList.Alloc();
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
.buffer = storageBuffer->m_Buffer,
.offset = 0,
.range = storageBuffer->GetSize(),
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = BUFFER_BINDING_INDEX,
.dstArrayElement = handle,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
});
m_WriteOwner.emplace_back(HandleType::eBuffer, handle);
return {handle};
}
void
RenderResourceManager::EraseWrites(u32 handleIndex, HandleType handleType)
{
auto writeIter = m_Writes.begin();
auto ownerIter = m_WriteOwner.begin();
const auto ownerEnd = m_WriteOwner.end();
while (ownerIter != ownerEnd)
{
if (ownerIter->first == handleType && ownerIter->second == handleIndex)
{
*writeIter = m_Writes.back();
*ownerIter = m_WriteOwner.back();
m_Writes.pop_back();
m_WriteOwner.pop_back();
return;
}
++ownerIter;
++writeIter;
}
}
void
RenderResourceManager::Release(BufferHandle handle)
{
if (handle.IsInvalid())
return;
EraseWrites(handle.m_Index, HandleType::eBuffer);
m_BufferFreeList.Free(handle.m_Index);
}
void
RenderResourceManager::Release(TextureHandle handle)
{
if (handle.IsInvalid())
return;
EraseWrites(handle.m_Index, HandleType::eTexture);
m_TextureFreeList.Free(handle.m_Index);
}
TextureHandle
RenderResourceManager::Commit(const Texture *texture)
{
const u32 handle = m_TextureFreeList.Alloc();
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = nullptr,
.imageView = texture->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
});
m_Writes.push_back({
.dstSet = m_DescriptorSet,
.dstBinding = TEXTURE_BINDING_INDEX,
.dstArrayElement = handle,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &m_WriteInfos.back().uImageInfo,
});
m_WriteOwner.emplace_back(HandleType::eBuffer, handle);
return {handle};
}
void
RenderResourceManager::Update()
{
if (m_Writes.empty() || m_WriteInfos.empty())
return;
m_Device->m_Device.updateDescriptorSets(m_Writes.size(), m_Writes.data(), 0, nullptr);
m_Writes.clear();
m_WriteInfos.clear();
}
RenderResourceManager::RenderResourceManager(const Device *device, u16 maxSize)
: m_Device(device)
{
vk::PhysicalDeviceProperties properties;
m_Device->m_PhysicalDevice.getProperties(&properties);
u32 buffersCount = eastl::min(properties.limits.maxPerStageDescriptorStorageBuffers - 1024, Cast<u32>(maxSize));
u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast<u32>(maxSize));
vk::SamplerCreateInfo samplerCreateInfo = {
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eClampToBorder,
.addressModeV = vk::SamplerAddressMode::eClampToBorder,
.addressModeW = vk::SamplerAddressMode::eClampToBorder,
.mipLodBias = 0.0f,
.anisotropyEnable = true,
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
.compareEnable = false,
.minLod = 0,
.maxLod = vk::LodClampNone,
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
.unnormalizedCoordinates = false,
};
AbortIfFailed(device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_Sampler));
INFO("Max Buffer Count: {}", buffersCount);
INFO("Max Texture Count: {}", texturesCount);
m_BufferFreeList.Init(buffersCount);
m_TextureFreeList.Init(texturesCount);
eastl::array poolSizes = {
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eStorageBuffer,
.descriptorCount = buffersCount,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = texturesCount,
},
};
const vk::DescriptorPoolCreateInfo poolCreateInfo = {
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
.maxSets = 1,
.poolSizeCount = Cast<u32>(poolSizes.size()),
.pPoolSizes = poolSizes.data(),
};
AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
eastl::vector immutableSamplers(texturesCount, m_Sampler);
eastl::array descriptorLayoutBindings = {
vk::DescriptorSetLayoutBinding{
.binding = BUFFER_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eStorageBuffer,
.descriptorCount = Cast<u32>(buffersCount),
.stageFlags = vk::ShaderStageFlagBits::eAll,
},
vk::DescriptorSetLayoutBinding{
.binding = TEXTURE_BINDING_INDEX,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = Cast<u32>(texturesCount),
.stageFlags = vk::ShaderStageFlagBits::eAll,
.pImmutableSamplers = immutableSamplers.data(),
},
};
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
.bindingCount = Cast<u32>(descriptorLayoutBindings.size()),
.pBindings = descriptorLayoutBindings.data(),
};
AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
.descriptorPool = m_DescriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &m_SetLayout,
};
AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
m_Device->SetName(m_SetLayout, "Bindless Layout");
m_Device->SetName(m_DescriptorPool, "Bindless Pool");
m_Device->SetName(m_DescriptorSet, "Bindless Set");
}
RenderResourceManager::~RenderResourceManager()
{
m_Device->m_Device.destroy(m_Sampler, nullptr);
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
m_Device->m_Device.destroy(m_SetLayout, nullptr);
}

View File

@ -0,0 +1,135 @@
// =============================================
// Aster: pipeline_utils.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "buffer.h"
#include "global.h"
#include <EASTL/deque.h>
#include <EASTL/stack.h>
#include <EASTL/vector_map.h>
struct Device;
struct Texture;
struct UniformStorageBuffer;
struct RenderResourceManager;
struct GpuResourceHandle
{
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
u32 m_Index = INVALID_HANDLE; // Default = invalid
[[nodiscard]] bool
IsInvalid() const
{
return m_Index == INVALID_HANDLE;
}
};
struct BufferHandle : GpuResourceHandle
{
};
struct TextureHandle : GpuResourceHandle
{
};
struct FreeList
{
eastl::stack<u32, eastl::deque<u32>> m_List;
u32 m_MaxVisited = 0;
u32 m_MaxCapacity = 16;
void
Init(u32 maxCapacity)
{
// MaxValue<u32> is 'invalid-handle' so you can't use it as a handle.
assert(maxCapacity < GpuResourceHandle::INVALID_HANDLE);
m_MaxCapacity = maxCapacity;
}
[[nodiscard]] u32
Alloc()
{
if (!m_List.empty())
{
const u32 value = m_List.top();
m_List.pop();
return value;
}
if (m_MaxVisited < m_MaxCapacity)
{
return m_MaxVisited++;
}
ERROR("Out of Handles.") THEN_ABORT(-1);
}
void
Free(u32 index)
{
WARN_IF(index >= m_MaxCapacity, "Trying to free an out-of-bounds index.");
if (index < m_MaxCapacity)
m_List.push(index);
}
};
struct RenderResourceManager
{
private:
union WriteInfo {
vk::DescriptorBufferInfo uBufferInfo;
vk::DescriptorImageInfo uImageInfo;
vk::BufferView uBufferView;
explicit WriteInfo(vk::DescriptorBufferInfo info);
explicit WriteInfo(vk::DescriptorImageInfo info);
explicit WriteInfo(vk::BufferView info);
};
enum class HandleType
{
eBuffer,
eTexture,
};
using WriteOwner = eastl::pair<HandleType, u32>;
eastl::deque<WriteInfo> m_WriteInfos;
eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::vector<WriteOwner> m_WriteOwner;
vk::Sampler m_Sampler;
FreeList m_BufferFreeList;
FreeList m_TextureFreeList;
void EraseWrites(u32 handleIndex, HandleType handleType);
public:
const Device *m_Device;
constexpr static u32 BUFFER_BINDING_INDEX = 0;
constexpr static u32 TEXTURE_BINDING_INDEX = 1;
vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet;
BufferHandle Commit(const StorageBuffer *storageBuffer);
void Release(BufferHandle handle);
TextureHandle Commit(const Texture *texture);
void Release(TextureHandle handle);
void Update();
// Ctor/Dtor
RenderResourceManager(const Device *device, u16 maxSize);
~RenderResourceManager();
DISALLOW_COPY_AND_ASSIGN(RenderResourceManager);
};

View File

@ -0,0 +1,46 @@
#version 450
#pragma shader_stage(fragment)
#extension GL_EXT_nonuniform_qualifier : enable
//layout (location = 0) in vec2 inUV;
layout (location = 0) out vec4 outColor;
struct VertexData {
float position[4];
// float uv[2];
};
struct MaterialData {
float m_AlbedoFactor[4];
float m_EmissionFactor[4];
float m_MetalFactor;
float m_RoughFactor;
uint m_AlbedoTex;
uint m_NormalTex;
uint m_MetalRoughTex;
uint m_OcclusionTex;
uint m_EmissionTex;
};
layout(set = 0, binding = 1) uniform sampler2D textures[];
layout(std430, set = 0, binding = 0) readonly buffer Vertices {
VertexData data[];
} vertexBuffer[];
layout(std430, set = 0, binding = 0) readonly buffer Materials {
MaterialData data[];
} materialsBuffer[];
layout(push_constant) uniform Block {
uint vertexBufferHandle;
uint materialBufferHandle;
uint materialIdx;
};
vec4 ToVec4(float array[4]) {
return vec4(array[0],array[1],array[2],array[3]);
}
void main() {
outColor = ToVec4(materialsBuffer[materialBufferHandle].data[materialIdx].m_AlbedoFactor); // vec4(texture(textures[textureHandle], inUV).rgb, 1.0f);
}

View File

@ -0,0 +1,61 @@
#version 460 core
#pragma shader_stage(vertex)
#extension GL_EXT_nonuniform_qualifier : enable
//layout(location = 0) out vec2 outUV;
struct VertexData {
float position[4];
// float uv[2];
};
struct MaterialData {
float m_AlbedoFactor[4];
float m_EmissionFactor[4];
float m_MetalFactor;
float m_RoughFactor;
uint m_AlbedoTex;
uint m_NormalTex;
uint m_MetalRoughTex;
uint m_OcclusionTex;
uint m_EmissionTex;
};
layout(std430, set = 0, binding = 0) readonly buffer Vertices {
VertexData data[];
} vertexBuffer[];
layout(std430, set = 0, binding = 0) readonly buffer Materials {
MaterialData data[];
} materialsBuffer[];
vec3 GetPosition(uint bufferId, uint vertexIdx) {
return vec3(
vertexBuffer[bufferId].data[vertexIdx].position[0],
vertexBuffer[bufferId].data[vertexIdx].position[1],
vertexBuffer[bufferId].data[vertexIdx].position[2]
);
}
//vec2 GetUV(uint bufferId, uint vertexIdx) {
// return vec2(
// vertexBuffer[bufferId].data[vertexIdx].uv[0],
// vertexBuffer[bufferId].data[vertexIdx].uv[1]
// );
//}
layout(set = 1, binding = 0) uniform Camera {
mat4 model;
mat4 view;
mat4 proj;
} ubo;
layout(push_constant) uniform Block {
uint vertexBufferHandle;
uint materialBufferHandle;
uint materialIdx;
};
void main() {
// outUV = GetUV(vertexBufferHandle, gl_VertexIndex);
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(GetPosition(vertexBufferHandle, gl_VertexIndex), 1.0f);
}

View File

@ -5,3 +5,4 @@ cmake_minimum_required(VERSION 3.13)
add_subdirectory("00_util")
add_subdirectory("01_triangle")
add_subdirectory("02_box")
add_subdirectory("03_model_render")

View File

@ -1,10 +1,11 @@
{
"dependencies": [
"eastl",
"fmt",
"glfw3",
"glm",
"scottt-debugbreak",
"vulkan-memory-allocator",
"eastl"
"tinygltf",
"vulkan-memory-allocator"
]
}