Compare commits
4 Commits
fa8351c866
...
69aa72770f
| Author | SHA1 | Date |
|---|---|---|
|
|
69aa72770f | |
|
|
93981bca4c | |
|
|
6c15f599aa | |
|
|
c7ea45bce7 |
|
|
@ -11,11 +11,11 @@
|
||||||
void
|
void
|
||||||
Buffer::Destroy(const Device *device)
|
Buffer::Destroy(const Device *device)
|
||||||
{
|
{
|
||||||
if (!m_Buffer)
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
|
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
|
||||||
m_Buffer = nullptr;
|
m_Size &= ~VALID_BUFFER_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -41,24 +41,21 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
|
||||||
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
||||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
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;
|
vk::MemoryPropertyFlags memoryPropertyFlags;
|
||||||
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
|
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
|
||||||
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
|
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
|
||||||
if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)
|
bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
|
||||||
{
|
|
||||||
m_Size |= HOST_ACCESSIBLE_BIT;
|
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);
|
device->SetName(m_Buffer, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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());
|
assert(IsHostVisible());
|
||||||
|
|
||||||
|
|
@ -93,6 +90,23 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name)
|
||||||
VMA_MEMORY_USAGE_AUTO, 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
|
void
|
||||||
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
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);
|
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
|
void
|
||||||
StagingBuffer::Init(const Device *device, usize size, cstr name)
|
StagingBuffer::Init(const Device *device, usize size, cstr name)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,19 +17,23 @@ struct Buffer
|
||||||
|
|
||||||
[[nodiscard]] usize GetSize() const;
|
[[nodiscard]] usize GetSize() const;
|
||||||
[[nodiscard]] bool IsHostVisible() const;
|
[[nodiscard]] bool IsHostVisible() const;
|
||||||
|
[[nodiscard]] bool IsValid() const;
|
||||||
[[nodiscard]] bool IsMapped() const;
|
[[nodiscard]] bool IsMapped() const;
|
||||||
|
|
||||||
void Destroy(const Device *device);
|
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:
|
protected:
|
||||||
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
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;
|
usize m_Size = 0;
|
||||||
|
|
||||||
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63;
|
constexpr static usize VALID_BUFFER_BIT = Cast<usize>(1llu << 63);
|
||||||
constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT;
|
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 62;
|
||||||
|
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | HOST_ACCESSIBLE_BIT);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UniformBuffer : Buffer
|
struct UniformBuffer : Buffer
|
||||||
|
|
@ -37,12 +41,23 @@ struct UniformBuffer : Buffer
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
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
|
struct VertexBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
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
|
struct StagingBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||||
|
|
@ -60,6 +75,12 @@ Buffer::IsHostVisible() const
|
||||||
return m_Size & HOST_ACCESSIBLE_BIT;
|
return m_Size & HOST_ACCESSIBLE_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
Buffer::IsValid() const
|
||||||
|
{
|
||||||
|
return m_Size & VALID_BUFFER_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
Buffer::IsMapped() const
|
Buffer::IsMapped() const
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ using f128 = long double;
|
||||||
using b8 = bool;
|
using b8 = bool;
|
||||||
using b32 = u32;
|
using b32 = u32;
|
||||||
using usize = size_t;
|
using usize = size_t;
|
||||||
using p64 = intptr_t;
|
using uptr = uintptr_t;
|
||||||
using cstr = const char *;
|
using cstr = const char *;
|
||||||
|
|
||||||
namespace ansi_color
|
namespace ansi_color
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
|
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
|
||||||
|
|
||||||
|
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
|
||||||
|
|
||||||
[[nodiscard]] inline bool
|
[[nodiscard]] inline bool
|
||||||
Failed(const vk::Result result)
|
Failed(const vk::Result result)
|
||||||
{
|
{
|
||||||
|
|
@ -75,7 +77,7 @@ HashCombine(const usize hash0, const usize hash1)
|
||||||
|
|
||||||
struct Time
|
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_Elapsed{Qnan<f64>};
|
||||||
inline static f64 m_Delta{Qnan<f64>};
|
inline static f64 m_Delta{Qnan<f64>};
|
||||||
|
|
@ -93,7 +95,7 @@ struct Time
|
||||||
{
|
{
|
||||||
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
||||||
const auto newElapsed = glfwGetTime();
|
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;
|
m_Elapsed = newElapsed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,22 @@
|
||||||
void
|
void
|
||||||
Image::Destroy(const Device *device)
|
Image::Destroy(const Device *device)
|
||||||
{
|
{
|
||||||
if (!m_Image)
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
device->m_Device.destroy(m_View, nullptr);
|
||||||
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
|
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
|
||||||
m_Image = nullptr;
|
m_Image = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
const u32 mipLevels = isMipmapped ? 1 + Cast<u32>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
vk::ImageCreateInfo imageCreateInfo = {
|
||||||
.imageType = vk::ImageType::e2D,
|
.imageType = vk::ImageType::e2D,
|
||||||
.format = vk::Format::eR8G8B8A8Srgb,
|
.format = imageFormat,
|
||||||
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
||||||
.mipLevels = mipLevels,
|
.mipLevels = mipLevels,
|
||||||
.arrayLayers = 1,
|
.arrayLayers = 1,
|
||||||
|
|
@ -42,11 +44,31 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
&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_Image = image;
|
||||||
|
m_View = view;
|
||||||
m_Allocation = allocation;
|
m_Allocation = allocation;
|
||||||
m_Extent = {extent.width, extent.height, 1};
|
m_Extent = {extent.width, extent.height, 1};
|
||||||
|
m_MipLevels = mipLevels;
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
device->SetName(m_Image, name);
|
||||||
}
|
}
|
||||||
|
|
@ -54,9 +76,10 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
|
||||||
void
|
void
|
||||||
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
||||||
{
|
{
|
||||||
|
constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
vk::ImageCreateInfo imageCreateInfo = {
|
||||||
.imageType = vk::ImageType::e2D,
|
.imageType = vk::ImageType::e2D,
|
||||||
.format = vk::Format::eD32Sfloat,
|
.format = imageFormat,
|
||||||
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
||||||
.mipLevels = 1,
|
.mipLevels = 1,
|
||||||
.arrayLayers = 1,
|
.arrayLayers = 1,
|
||||||
|
|
@ -75,9 +98,28 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
&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_Image = image;
|
||||||
|
m_View = view;
|
||||||
m_Allocation = allocation;
|
m_Allocation = allocation;
|
||||||
m_Extent = {extent.width, extent.height, 1};
|
m_Extent = {extent.width, extent.height, 1};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,28 @@ struct Device;
|
||||||
struct Image
|
struct Image
|
||||||
{
|
{
|
||||||
vk::Image m_Image = nullptr;
|
vk::Image m_Image = nullptr;
|
||||||
|
vk::ImageView m_View = nullptr;
|
||||||
VmaAllocation m_Allocation = nullptr;
|
VmaAllocation m_Allocation = nullptr;
|
||||||
vk::Extent3D m_Extent;
|
vk::Extent3D m_Extent;
|
||||||
usize m_Size = 0;
|
u8 m_MipLevels = 1;
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsValid() const;
|
||||||
|
|
||||||
void Destroy(const Device *device);
|
void Destroy(const Device *device);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Texture : Image
|
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
|
struct DepthImage : Image
|
||||||
{
|
{
|
||||||
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
|
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
Image::IsValid() const
|
||||||
|
{
|
||||||
|
return m_Image;
|
||||||
|
}
|
||||||
|
|
@ -61,7 +61,7 @@ Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Window
|
||||||
break;
|
break;
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
case vk::Result::eSuboptimalKHR:
|
case vk::Result::eSuboptimalKHR:
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
swapchain->Create(window);
|
swapchain->Create(window);
|
||||||
break; // Present failed. We do nothing. Frame is skipped.
|
break; // Present failed. We do nothing. Frame is skipped.
|
||||||
default:
|
default:
|
||||||
|
|
@ -116,7 +116,7 @@ FrameManager::GetNextFrame(Swapchain *swapchain, const Window *window)
|
||||||
imageAcquired = true;
|
imageAcquired = true;
|
||||||
break; // Image acquired. Break out of loop.
|
break; // Image acquired. Break out of loop.
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
swapchain->Create(window);
|
swapchain->Create(window);
|
||||||
break; // Image acquire has failed. We move to the next frame.
|
break; // Image acquire has failed. We move to the next frame.
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ ImageFile::Load(cstr fileName)
|
||||||
usize
|
usize
|
||||||
ImageFile::GetSize() const
|
ImageFile::GetSize() const
|
||||||
{
|
{
|
||||||
return m_Width * m_Height * m_NumChannels;
|
return Cast<usize>(m_Width) * m_Height * m_NumChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageFile::~ImageFile()
|
ImageFile::~ImageFile()
|
||||||
|
|
@ -75,7 +75,7 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
vec3 m_Position;
|
vec3 m_Position;
|
||||||
vec2 m_UV0;
|
vec2 m_TexCoord0;
|
||||||
|
|
||||||
constexpr static vk::VertexInputBindingDescription
|
constexpr static vk::VertexInputBindingDescription
|
||||||
GetBinding(const u32 binding)
|
GetBinding(const u32 binding)
|
||||||
|
|
@ -97,7 +97,7 @@ struct Vertex
|
||||||
.location = 1,
|
.location = 1,
|
||||||
.binding = binding,
|
.binding = binding,
|
||||||
.format = vk::Format::eR32G32Sfloat,
|
.format = vk::Format::eR32G32Sfloat,
|
||||||
.offset = offsetof(Vertex, m_UV0),
|
.offset = offsetof(Vertex, m_TexCoord0),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -185,47 +185,47 @@ main(int, char **)
|
||||||
}
|
}
|
||||||
|
|
||||||
eastl::array vertices = {
|
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_TexCoord0 = 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_TexCoord0 = 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_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_TexCoord0 = 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_TexCoord0 = 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_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_UV0 = vec2(1.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_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_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_UV0 = vec2(0.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_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_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_UV0 = vec2(1.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_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_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_UV0 = vec2(0.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_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_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_UV0 = vec2(1.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_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_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_UV0 = vec2(0.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_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_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_UV0 = vec2(1.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_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_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_UV0 = vec2(0.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_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_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_UV0 = vec2(1.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_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_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_UV0 = vec2(0.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_UV0 = vec2(1.0f, 1.0f)},
|
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||||
};
|
};
|
||||||
|
|
||||||
ImageFile imageFile;
|
ImageFile imageFile;
|
||||||
|
|
@ -236,7 +236,7 @@ main(int, char **)
|
||||||
VertexBuffer vbo;
|
VertexBuffer vbo;
|
||||||
Texture crate;
|
Texture crate;
|
||||||
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
|
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;
|
StagingBuffer vertexStaging, imageStaging;
|
||||||
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
|
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
|
||||||
|
|
@ -330,30 +330,8 @@ main(int, char **)
|
||||||
imageStaging.Destroy(&device);
|
imageStaging.Destroy(&device);
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::ImageView imageView;
|
|
||||||
vk::Sampler sampler;
|
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 = {
|
vk::SamplerCreateInfo samplerCreateInfo = {
|
||||||
.magFilter = vk::Filter::eLinear,
|
.magFilter = vk::Filter::eLinear,
|
||||||
.minFilter = vk::Filter::eLinear,
|
.minFilter = vk::Filter::eLinear,
|
||||||
|
|
@ -382,7 +360,7 @@ main(int, char **)
|
||||||
};
|
};
|
||||||
vk::DescriptorImageInfo descriptorImageInfo = {
|
vk::DescriptorImageInfo descriptorImageInfo = {
|
||||||
.sampler = sampler,
|
.sampler = sampler,
|
||||||
.imageView = imageView,
|
.imageView = crate.m_View,
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
};
|
};
|
||||||
eastl::array writeDescriptors = {
|
eastl::array writeDescriptors = {
|
||||||
|
|
@ -420,13 +398,13 @@ main(int, char **)
|
||||||
.extent = swapchain.m_Extent,
|
.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.y = Cast<f32>(extent.height);
|
||||||
viewport.width = Cast<f32>(extent.width);
|
viewport.width = Cast<f32>(extent.width);
|
||||||
viewport.height = -Cast<f32>(extent.height);
|
viewport.height = -Cast<f32>(extent.height);
|
||||||
scissor.extent = extent;
|
scissor.extent = extent;
|
||||||
};
|
};
|
||||||
swapchain.RegisterResizeCallback(ResizeViewportScissor);
|
swapchain.RegisterResizeCallback(resizeViewportScissor);
|
||||||
|
|
||||||
vk::ImageSubresourceRange subresourceRange = {
|
vk::ImageSubresourceRange subresourceRange = {
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -452,55 +430,19 @@ main(int, char **)
|
||||||
|
|
||||||
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<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)
|
for (auto &depthImage : depthImages)
|
||||||
{
|
{
|
||||||
auto name = fmt::format("Depth image {}", index);
|
depthImage.Init(&device, swapchain.m_Extent, "Depth");
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto RecreateDepthBuffers = [&device, &depthImages, &depthViews, depthSubresourceRange](vk::Extent2D extent) {
|
auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) {
|
||||||
for (auto &depthView : depthViews)
|
|
||||||
{
|
|
||||||
device.m_Device.destroy(depthView, nullptr);
|
|
||||||
}
|
|
||||||
u32 index = 0;
|
|
||||||
for (auto &depthImage : depthImages)
|
for (auto &depthImage : depthImages)
|
||||||
{
|
{
|
||||||
depthImage.Destroy(&device);
|
depthImage.Destroy(&device);
|
||||||
depthImage.Init(&device, extent, "Depth");
|
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();
|
Time::Init();
|
||||||
|
|
||||||
|
|
@ -518,7 +460,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 = depthViews[currentFrame->m_FrameIdx];
|
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View;
|
||||||
|
|
||||||
topOfThePipeBarrier.image = currentImage;
|
topOfThePipeBarrier.image = currentImage;
|
||||||
renderToPresentBarrier.image = currentImage;
|
renderToPresentBarrier.image = currentImage;
|
||||||
|
|
@ -592,16 +534,11 @@ main(int, char **)
|
||||||
|
|
||||||
AbortIfFailed(device.m_Device.waitIdle());
|
AbortIfFailed(device.m_Device.waitIdle());
|
||||||
|
|
||||||
for (auto &depthView : depthViews)
|
|
||||||
{
|
|
||||||
device.m_Device.destroy(depthView, nullptr);
|
|
||||||
}
|
|
||||||
for (auto &depthImage : depthImages)
|
for (auto &depthImage : depthImages)
|
||||||
{
|
{
|
||||||
depthImage.Destroy(&device);
|
depthImage.Destroy(&device);
|
||||||
}
|
}
|
||||||
device.m_Device.destroy(sampler, nullptr);
|
device.m_Device.destroy(sampler, nullptr);
|
||||||
device.m_Device.destroy(imageView, nullptr);
|
|
||||||
ubo.Destroy(&device);
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -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";
|
||||||
|
};
|
||||||
|
|
@ -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, ©Pool),
|
||||||
|
"Copy command pool creation failed.");
|
||||||
|
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
|
||||||
|
.commandPool = copyPool,
|
||||||
|
.level = vk::CommandBufferLevel::ePrimary,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
};
|
||||||
|
AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer),
|
||||||
|
"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 = ©Buffer,
|
||||||
|
};
|
||||||
|
|
||||||
|
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 = ¤tFrame->m_ImageAcquireSem,
|
||||||
|
.pWaitDstStageMask = &waitDstStage,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
.pCommandBuffers = &cmd,
|
||||||
|
.signalSemaphoreCount = 1,
|
||||||
|
.pSignalSemaphores = ¤tFrame->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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
};
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -5,3 +5,4 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
add_subdirectory("00_util")
|
add_subdirectory("00_util")
|
||||||
add_subdirectory("01_triangle")
|
add_subdirectory("01_triangle")
|
||||||
add_subdirectory("02_box")
|
add_subdirectory("02_box")
|
||||||
|
add_subdirectory("03_model_render")
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
{
|
{
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
"eastl",
|
||||||
"fmt",
|
"fmt",
|
||||||
"glfw3",
|
"glfw3",
|
||||||
"glm",
|
"glm",
|
||||||
"scottt-debugbreak",
|
"scottt-debugbreak",
|
||||||
"vulkan-memory-allocator",
|
"tinygltf",
|
||||||
"eastl"
|
"vulkan-memory-allocator"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue