Compare commits
4 Commits
fa8351c866
...
69aa72770f
| Author | SHA1 | Date |
|---|---|---|
|
|
69aa72770f | |
|
|
93981bca4c | |
|
|
6c15f599aa | |
|
|
c7ea45bce7 |
|
|
@ -11,11 +11,11 @@
|
|||
void
|
||||
Buffer::Destroy(const Device *device)
|
||||
{
|
||||
if (!m_Buffer)
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
|
||||
m_Buffer = nullptr;
|
||||
m_Size &= ~VALID_BUFFER_BIT;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -41,24 +41,21 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
|
|||
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
||||
|
||||
m_Buffer = buffer;
|
||||
m_Size = size & SIZE_MASK;
|
||||
m_Allocation = allocation;
|
||||
m_Mapped = allocationInfo.pMappedData;
|
||||
|
||||
vk::MemoryPropertyFlags memoryPropertyFlags;
|
||||
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
|
||||
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
|
||||
if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)
|
||||
{
|
||||
m_Size |= HOST_ACCESSIBLE_BIT;
|
||||
}
|
||||
bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
|
||||
|
||||
m_Buffer = buffer;
|
||||
m_Size = size | VALID_BUFFER_BIT | (hostAccessible ? HOST_ACCESSIBLE_BIT : 0);
|
||||
m_Allocation = allocation;
|
||||
m_Mapped = allocationInfo.pMappedData;
|
||||
|
||||
device->SetName(m_Buffer, name);
|
||||
}
|
||||
|
||||
void
|
||||
Buffer::Write(const Device *device, usize offset, usize size, void *data)
|
||||
Buffer::Write(const Device *device, usize offset, usize size, const void *data)
|
||||
{
|
||||
assert(IsHostVisible());
|
||||
|
||||
|
|
@ -93,6 +90,23 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name)
|
|||
VMA_MEMORY_USAGE_AUTO, name);
|
||||
}
|
||||
|
||||
void
|
||||
StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
||||
{
|
||||
if (hostVisible)
|
||||
{
|
||||
Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer,
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||
VMA_MEMORY_USAGE_AUTO, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
||||
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
||||
{
|
||||
|
|
@ -100,6 +114,13 @@ VertexBuffer::Init(const Device *device, usize size, cstr name)
|
|||
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
|
||||
}
|
||||
|
||||
void
|
||||
IndexBuffer::Init(const Device *device, usize size, cstr name)
|
||||
{
|
||||
Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
||||
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
|
||||
}
|
||||
|
||||
void
|
||||
StagingBuffer::Init(const Device *device, usize size, cstr name)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,19 +17,23 @@ struct Buffer
|
|||
|
||||
[[nodiscard]] usize GetSize() const;
|
||||
[[nodiscard]] bool IsHostVisible() const;
|
||||
[[nodiscard]] bool IsValid() const;
|
||||
[[nodiscard]] bool IsMapped() const;
|
||||
|
||||
void Destroy(const Device *device);
|
||||
void Write(const Device *device, usize offset, usize size, void *data);
|
||||
void Write(const Device *device, usize offset, usize size, const void *data);
|
||||
|
||||
protected:
|
||||
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
||||
|
||||
// Buffer size is used intrusively by the Render Resource Manager
|
||||
// If the buffer is Invalid, the remaining data in Buffer is used for other tasks.
|
||||
usize m_Size = 0;
|
||||
|
||||
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63;
|
||||
constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT;
|
||||
constexpr static usize VALID_BUFFER_BIT = Cast<usize>(1llu << 63);
|
||||
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 62;
|
||||
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | HOST_ACCESSIBLE_BIT);
|
||||
};
|
||||
|
||||
struct UniformBuffer : Buffer
|
||||
|
|
@ -37,12 +41,23 @@ struct UniformBuffer : Buffer
|
|||
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||
};
|
||||
|
||||
struct StorageBuffer : Buffer
|
||||
{
|
||||
void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr);
|
||||
};
|
||||
|
||||
struct VertexBuffer : Buffer
|
||||
{
|
||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
||||
};
|
||||
|
||||
struct IndexBuffer : Buffer
|
||||
{
|
||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
||||
};
|
||||
|
||||
struct StagingBuffer : Buffer
|
||||
{
|
||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
||||
|
|
@ -60,6 +75,12 @@ Buffer::IsHostVisible() const
|
|||
return m_Size & HOST_ACCESSIBLE_BIT;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Buffer::IsValid() const
|
||||
{
|
||||
return m_Size & VALID_BUFFER_BIT;
|
||||
}
|
||||
|
||||
inline bool
|
||||
Buffer::IsMapped() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ using f128 = long double;
|
|||
using b8 = bool;
|
||||
using b32 = u32;
|
||||
using usize = size_t;
|
||||
using p64 = intptr_t;
|
||||
using uptr = uintptr_t;
|
||||
using cstr = const char *;
|
||||
|
||||
namespace ansi_color
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
|||
|
||||
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
|
||||
|
||||
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
|
||||
|
||||
[[nodiscard]] inline bool
|
||||
Failed(const vk::Result result)
|
||||
{
|
||||
|
|
@ -75,7 +77,7 @@ HashCombine(const usize hash0, const usize hash1)
|
|||
|
||||
struct Time
|
||||
{
|
||||
static constexpr f64 cMaxDelta = 0.1;
|
||||
static constexpr f64 MAX_DELTA = 0.1;
|
||||
|
||||
inline static f64 m_Elapsed{Qnan<f64>};
|
||||
inline static f64 m_Delta{Qnan<f64>};
|
||||
|
|
@ -93,7 +95,7 @@ struct Time
|
|||
{
|
||||
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
||||
const auto newElapsed = glfwGetTime();
|
||||
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, cMaxDelta);
|
||||
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, MAX_DELTA);
|
||||
m_Elapsed = newElapsed;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,20 +10,22 @@
|
|||
void
|
||||
Image::Destroy(const Device *device)
|
||||
{
|
||||
if (!m_Image)
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
device->m_Device.destroy(m_View, nullptr);
|
||||
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
|
||||
m_Image = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipmapped, const cstr name)
|
||||
Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipmapped,
|
||||
const cstr name)
|
||||
{
|
||||
const u32 mipLevels = isMipmapped ? 1 + Cast<u32>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
|
||||
vk::ImageCreateInfo imageCreateInfo = {
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = vk::Format::eR8G8B8A8Srgb,
|
||||
.format = imageFormat,
|
||||
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
||||
.mipLevels = mipLevels,
|
||||
.arrayLayers = 1,
|
||||
|
|
@ -42,11 +44,31 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
|
|||
VmaAllocation allocation;
|
||||
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||
|
||||
vk::ImageView view;
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = imageFormat,
|
||||
.components = {},
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = mipLevels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||
|
||||
m_Image = image;
|
||||
m_View = view;
|
||||
m_Allocation = allocation;
|
||||
m_Extent = {extent.width, extent.height, 1};
|
||||
m_MipLevels = mipLevels;
|
||||
|
||||
device->SetName(m_Image, name);
|
||||
}
|
||||
|
|
@ -54,9 +76,10 @@ Texture::Init(const Device *device, const vk::Extent2D extent, const bool isMipm
|
|||
void
|
||||
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
||||
{
|
||||
constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
|
||||
vk::ImageCreateInfo imageCreateInfo = {
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = vk::Format::eD32Sfloat,
|
||||
.format = imageFormat,
|
||||
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
|
|
@ -75,9 +98,28 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
|||
VmaAllocation allocation;
|
||||
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
||||
ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
|
||||
|
||||
vk::ImageView view;
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = imageFormat,
|
||||
.components = {},
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||
ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||
|
||||
m_Image = image;
|
||||
m_View = view;
|
||||
m_Allocation = allocation;
|
||||
m_Extent = {extent.width, extent.height, 1};
|
||||
|
||||
|
|
|
|||
|
|
@ -12,19 +12,28 @@ struct Device;
|
|||
struct Image
|
||||
{
|
||||
vk::Image m_Image = nullptr;
|
||||
vk::ImageView m_View = nullptr;
|
||||
VmaAllocation m_Allocation = nullptr;
|
||||
vk::Extent3D m_Extent;
|
||||
usize m_Size = 0;
|
||||
u8 m_MipLevels = 1;
|
||||
|
||||
[[nodiscard]] bool IsValid() const;
|
||||
|
||||
void Destroy(const Device *device);
|
||||
};
|
||||
|
||||
struct Texture : Image
|
||||
{
|
||||
void Init(const Device *device, vk::Extent2D extent, bool isMipmapped, cstr name = nullptr);
|
||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipmapped, cstr name = nullptr);
|
||||
};
|
||||
|
||||
struct DepthImage : Image
|
||||
{
|
||||
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
|
||||
};
|
||||
|
||||
inline bool
|
||||
Image::IsValid() const
|
||||
{
|
||||
return m_Image;
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Window
|
|||
break;
|
||||
case vk::Result::eErrorOutOfDateKHR:
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
INFO("Recreating Swapchain. Cause: {}", result);
|
||||
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||
swapchain->Create(window);
|
||||
break; // Present failed. We do nothing. Frame is skipped.
|
||||
default:
|
||||
|
|
@ -116,7 +116,7 @@ FrameManager::GetNextFrame(Swapchain *swapchain, const Window *window)
|
|||
imageAcquired = true;
|
||||
break; // Image acquired. Break out of loop.
|
||||
case vk::Result::eErrorOutOfDateKHR:
|
||||
INFO("Recreating Swapchain. Cause: {}", result);
|
||||
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||
swapchain->Create(window);
|
||||
break; // Image acquire has failed. We move to the next frame.
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ ImageFile::Load(cstr fileName)
|
|||
usize
|
||||
ImageFile::GetSize() const
|
||||
{
|
||||
return m_Width * m_Height * m_NumChannels;
|
||||
return Cast<usize>(m_Width) * m_Height * m_NumChannels;
|
||||
}
|
||||
|
||||
ImageFile::~ImageFile()
|
||||
|
|
@ -75,7 +75,7 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
|
|||
struct Vertex
|
||||
{
|
||||
vec3 m_Position;
|
||||
vec2 m_UV0;
|
||||
vec2 m_TexCoord0;
|
||||
|
||||
constexpr static vk::VertexInputBindingDescription
|
||||
GetBinding(const u32 binding)
|
||||
|
|
@ -97,7 +97,7 @@ struct Vertex
|
|||
.location = 1,
|
||||
.binding = binding,
|
||||
.format = vk::Format::eR32G32Sfloat,
|
||||
.offset = offsetof(Vertex, m_UV0),
|
||||
.offset = offsetof(Vertex, m_TexCoord0),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -185,47 +185,47 @@ main(int, char **)
|
|||
}
|
||||
|
||||
eastl::array vertices = {
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(0.0f, 0.0f)},
|
||||
Vertex{.m_Position = vec3(-0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(0.0f, 1.0f)},
|
||||
Vertex{.m_Position = vec3(0.5f, 0.5f, 0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||
};
|
||||
|
||||
ImageFile imageFile;
|
||||
|
|
@ -236,7 +236,7 @@ main(int, char **)
|
|||
VertexBuffer vbo;
|
||||
Texture crate;
|
||||
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
|
||||
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, false, "Crate Texture");
|
||||
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, vk::Format::eR8G8B8A8Srgb, false, "Crate Texture");
|
||||
{
|
||||
StagingBuffer vertexStaging, imageStaging;
|
||||
vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
|
||||
|
|
@ -330,30 +330,8 @@ main(int, char **)
|
|||
imageStaging.Destroy(&device);
|
||||
}
|
||||
|
||||
vk::ImageView imageView;
|
||||
vk::Sampler sampler;
|
||||
{
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = crate.m_Image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = vk::Format::eR8G8B8A8Srgb,
|
||||
.components =
|
||||
vk::ComponentMapping{
|
||||
.r = vk::ComponentSwizzle::eIdentity,
|
||||
.g = vk::ComponentSwizzle::eIdentity,
|
||||
.b = vk::ComponentSwizzle::eIdentity,
|
||||
.a = vk::ComponentSwizzle::eIdentity,
|
||||
},
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &imageView));
|
||||
vk::SamplerCreateInfo samplerCreateInfo = {
|
||||
.magFilter = vk::Filter::eLinear,
|
||||
.minFilter = vk::Filter::eLinear,
|
||||
|
|
@ -382,7 +360,7 @@ main(int, char **)
|
|||
};
|
||||
vk::DescriptorImageInfo descriptorImageInfo = {
|
||||
.sampler = sampler,
|
||||
.imageView = imageView,
|
||||
.imageView = crate.m_View,
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
};
|
||||
eastl::array writeDescriptors = {
|
||||
|
|
@ -420,13 +398,13 @@ main(int, char **)
|
|||
.extent = swapchain.m_Extent,
|
||||
};
|
||||
|
||||
auto ResizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
|
||||
auto resizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
|
||||
viewport.y = Cast<f32>(extent.height);
|
||||
viewport.width = Cast<f32>(extent.width);
|
||||
viewport.height = -Cast<f32>(extent.height);
|
||||
scissor.extent = extent;
|
||||
};
|
||||
swapchain.RegisterResizeCallback(ResizeViewportScissor);
|
||||
swapchain.RegisterResizeCallback(resizeViewportScissor);
|
||||
|
||||
vk::ImageSubresourceRange subresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
|
|
@ -452,55 +430,19 @@ main(int, char **)
|
|||
|
||||
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
|
||||
eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
|
||||
eastl::fixed_vector<vk::ImageView, MAX_FRAMES_IN_FLIGHT> depthViews(frameManager.m_FramesInFlight);
|
||||
|
||||
vk::ImageSubresourceRange depthSubresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
u32 index = 0;
|
||||
for (auto &depthImage : depthImages)
|
||||
{
|
||||
auto name = fmt::format("Depth image {}", index);
|
||||
depthImage.Init(&device, swapchain.m_Extent, name.c_str());
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = depthImage.m_Image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = vk::Format::eD32Sfloat,
|
||||
.components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity},
|
||||
.subresourceRange = depthSubresourceRange,
|
||||
};
|
||||
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index]));
|
||||
|
||||
index++;
|
||||
depthImage.Init(&device, swapchain.m_Extent, "Depth");
|
||||
}
|
||||
|
||||
auto RecreateDepthBuffers = [&device, &depthImages, &depthViews, depthSubresourceRange](vk::Extent2D extent) {
|
||||
for (auto &depthView : depthViews)
|
||||
{
|
||||
device.m_Device.destroy(depthView, nullptr);
|
||||
}
|
||||
u32 index = 0;
|
||||
auto recreateDepthBuffers = [&device, &depthImages](vk::Extent2D extent) {
|
||||
for (auto &depthImage : depthImages)
|
||||
{
|
||||
depthImage.Destroy(&device);
|
||||
depthImage.Init(&device, extent, "Depth");
|
||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = depthImage.m_Image,
|
||||
.viewType = vk::ImageViewType::e2D,
|
||||
.format = vk::Format::eD32Sfloat,
|
||||
.components = vk::ComponentMapping{.r = vk::ComponentSwizzle::eIdentity},
|
||||
.subresourceRange = depthSubresourceRange,
|
||||
};
|
||||
AbortIfFailed(device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &depthViews[index]));
|
||||
++index;
|
||||
}
|
||||
};
|
||||
swapchain.RegisterResizeCallback(RecreateDepthBuffers);
|
||||
swapchain.RegisterResizeCallback(recreateDepthBuffers);
|
||||
|
||||
Time::Init();
|
||||
|
||||
|
|
@ -518,7 +460,7 @@ main(int, char **)
|
|||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
||||
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
|
||||
vk::ImageView currentDepthImageView = depthViews[currentFrame->m_FrameIdx];
|
||||
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx].m_View;
|
||||
|
||||
topOfThePipeBarrier.image = currentImage;
|
||||
renderToPresentBarrier.image = currentImage;
|
||||
|
|
@ -592,16 +534,11 @@ main(int, char **)
|
|||
|
||||
AbortIfFailed(device.m_Device.waitIdle());
|
||||
|
||||
for (auto &depthView : depthViews)
|
||||
{
|
||||
device.m_Device.destroy(depthView, nullptr);
|
||||
}
|
||||
for (auto &depthImage : depthImages)
|
||||
{
|
||||
depthImage.Destroy(&device);
|
||||
}
|
||||
device.m_Device.destroy(sampler, nullptr);
|
||||
device.m_Device.destroy(imageView, nullptr);
|
||||
ubo.Destroy(&device);
|
||||
device.m_Device.destroy(descriptorPool, nullptr);
|
||||
device.m_Device.destroy(copyPool, nullptr);
|
||||
|
|
|
|||
|
|
@ -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("01_triangle")
|
||||
add_subdirectory("02_box")
|
||||
add_subdirectory("03_model_render")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
{
|
||||
"dependencies": [
|
||||
"eastl",
|
||||
"fmt",
|
||||
"glfw3",
|
||||
"glm",
|
||||
"scottt-debugbreak",
|
||||
"vulkan-memory-allocator",
|
||||
"eastl"
|
||||
"tinygltf",
|
||||
"vulkan-memory-allocator"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue