Compare commits

...

4 Commits

Author SHA1 Message Date
Anish Bhobe 07f3a237b5 Depth buffer added. 2024-07-11 09:56:17 +02:00
Anish Bhobe ef6ca6a79e Refactor to add move ops. 2024-07-11 09:11:15 +02:00
Anish Bhobe 52b3c671c6 Refactor Frames into util. 2024-07-10 20:56:18 +02:00
Anish Bhobe b48fb3168d Completed textured box. 2024-07-10 13:14:08 +02:00
27 changed files with 8862 additions and 229 deletions

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
*.bin filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.glb filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text

View File

@ -6,42 +6,43 @@ A Vulkan based renderer created with Vulkan 1.3 in C++.
- [ ] Forward Rendering - [ ] Forward Rendering
- [ ] glTF 2.0 Support - [ ] glTF 2.0 Support
- [ ] Load Vertex Data - [ ] Load Vertex Data
- [ ] Load Material Data - [ ] Load Material Data
- [ ] Load Animation Data - [ ] Load Animation Data
- [ ] Load Camera - [ ] Load Camera
- [ ] Load Lights - [ ] Load Lights
- [ ] Support Specular Materials - [ ] Support Specular Materials
- [ ] Bindless Descriptors
- [ ] PBR - [ ] PBR
- [ ] IBL - [ ] IBL
- [ ] Shadows v1 - [ ] Shadows v1
- [ ] Omnidirectional Cubemap Shadows - [ ] Omnidirectional Cubemap Shadows
- [ ] Spot Lights - [ ] Spot Lights
- [ ] Directional Shadows - [ ] Directional Shadows
- [ ] Cascaded Shadows - [ ] Cascaded Shadows
- [ ] PCF - [ ] PCF
- [ ] Simplified Descriptor Creation Pipeline - [ ] Simplified Descriptor Creation Pipeline
- [ ] Deferred Rendering - [ ] Deferred Rendering
- [ ] Ambient Occlusion - [ ] Ambient Occlusion
- [ ] SSAO - [ ] SSAO
- [ ] HBAO - [ ] HBAO
- [ ] VXAO/SDFAO - [ ] VXAO/SDFAO
- [ ] RTX AO - [ ] RTX AO
- [ ] Reflection - [ ] Reflection
- [ ] ScreenSpace Reflection (SSR) - [ ] ScreenSpace Reflection (SSR)
- [ ] Cubemap/Probe Reflection - [ ] Cubemap/Probe Reflection
- [ ] Forward+ Rendering - [ ] Forward+ Rendering
- [ ] Global Illumination - [ ] Global Illumination
- [ ] Precomputed Radiance Transfer - [ ] Precomputed Radiance Transfer
- [ ] Voxel Cone Tracing - [ ] Voxel Cone Tracing
- [ ] SDFGI - [ ] SDFGI
- [ ] RTXGI - [ ] RTXGI
- [ ] Shadows v2 - [ ] Shadows v2
- [ ] Omnidirectional Dual Paraboloid Shadows - [ ] Omnidirectional Dual Paraboloid Shadows
- [ ] Perspective Shadow Mapping - [ ] Perspective Shadow Mapping
- [ ] RTX Shadows - [ ] RTX Shadows
- [ ] Animation - [ ] Animation
- [ ] Skeletal Animation - [ ] Skeletal Animation
- [ ] TBD - [ ] TBD
- [ ] Particle Effects - [ ] Particle Effects
- [ ] Full Path Tracing - [ ] Full Path Tracing

View File

@ -33,7 +33,9 @@ set(SOURCE_FILES
device.cpp device.cpp
swapchain.cpp swapchain.cpp
pipeline.cpp pipeline.cpp
buffer.cpp) buffer.cpp
image.cpp
image.h)
add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES}) add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES})
set_property(TARGET aster_core PROPERTY CXX_STANDARD 20) set_property(TARGET aster_core PROPERTY CXX_STANDARD 20)

View File

@ -5,6 +5,7 @@
#include "buffer.h" #include "buffer.h"
#include "device.h"
#include <ranges> #include <ranges>
void void

View File

@ -5,7 +5,6 @@
#pragma once #pragma once
#include "device.h"
#include "global.h" #include "global.h"
struct Device; struct Device;

View File

@ -122,3 +122,19 @@ Context::~Context()
glfwTerminate(); glfwTerminate();
} }
Context::Context(Context &&other) noexcept
: m_Instance(Take(other.m_Instance))
, m_DebugMessenger(Take(other.m_DebugMessenger))
{
}
Context &
Context::operator=(Context &&other) noexcept
{
if (this == &other)
return *this;
m_Instance = Take(other.m_Instance);
m_DebugMessenger = Take(other.m_DebugMessenger);
return *this;
}

View File

@ -23,4 +23,10 @@ struct Context final
// Ctor/Dtor // Ctor/Dtor
Context(cstr appName, Version version, bool enableValidation = true); Context(cstr appName, Version version, bool enableValidation = true);
~Context(); ~Context();
// Move
Context(Context &&other) noexcept;
Context &operator=(Context &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Context);
}; };

View File

@ -108,4 +108,24 @@ Device::GetQueue(const u32 familyIndex, const u32 queueIndex) const
vk::Queue queue; vk::Queue queue;
m_Device.getQueue(familyIndex, queueIndex, &queue); m_Device.getQueue(familyIndex, queueIndex, &queue);
return queue; return queue;
}
Device::Device(Device &&other) noexcept
: m_Name(std::move(other.m_Name))
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
, m_Device(Take(other.m_Device))
, m_Allocator(Take(other.m_Allocator))
{
}
Device &
Device::operator=(Device &&other) noexcept
{
if (this == &other)
return *this;
m_Name = std::move(other.m_Name);
m_PhysicalDevice = Take(other.m_PhysicalDevice);
m_Device = Take(other.m_Device);
m_Allocator = Take(other.m_Allocator);
return *this;
} }

View File

@ -28,13 +28,20 @@ struct Device final
vk::Device m_Device = nullptr; vk::Device m_Device = nullptr;
VmaAllocator m_Allocator = nullptr; VmaAllocator m_Allocator = nullptr;
template <typename T>
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const;
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
// Ctor/Dtor
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name); const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
~Device(); ~Device();
template <typename T> // Move
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const; Device(Device &&other) noexcept;
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const; Device &operator=(Device &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Device);
}; };
template <typename T> template <typename T>

View File

@ -34,7 +34,11 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
#define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__) #define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__)
#define FORCE_TODO static_assert(false) #define DISALLOW_COPY_AND_ASSIGN(CLASS_NAME) \
CLASS_NAME(const CLASS_NAME &other) = delete; \
CLASS_NAME &operator=(const CLASS_NAME &other) = delete
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
[[nodiscard]] inline bool [[nodiscard]] inline bool
Failed(const vk::Result result) Failed(const vk::Result result)

85
aster/image.cpp Normal file
View File

@ -0,0 +1,85 @@
// =============================================
// Aster: image.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "image.h"
#include "device.h"
void
Image::Destroy(const Device *device)
{
if (!m_Image)
return;
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)
{
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,
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
.mipLevels = mipLevels,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
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);
m_Image = image;
m_Allocation = allocation;
m_Extent = {extent.width, extent.height, 1};
device->SetName(m_Image, name);
}
void
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
{
vk::ImageCreateInfo imageCreateInfo = {
.imageType = vk::ImageType::e2D,
.format = vk::Format::eD32Sfloat,
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
.mipLevels = 1,
.arrayLayers = 1,
.samples = vk::SampleCountFlagBits::e1,
.tiling = vk::ImageTiling::eOptimal,
.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
.sharingMode = vk::SharingMode::eExclusive,
.initialLayout = vk::ImageLayout::eUndefined,
};
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
.usage = VMA_MEMORY_USAGE_AUTO,
};
VkImage image;
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);
m_Image = image;
m_Allocation = allocation;
m_Extent = {extent.width, extent.height, 1};
device->SetName(m_Image, name);
}

30
aster/image.h Normal file
View File

@ -0,0 +1,30 @@
// =============================================
// Aster: image.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
struct Device;
struct Image
{
vk::Image m_Image = nullptr;
VmaAllocation m_Allocation = nullptr;
vk::Extent3D m_Extent;
usize m_Size = 0;
void Destroy(const Device *device);
};
struct Texture : Image
{
void Init(const Device *device, vk::Extent2D extent, bool isMipmapped, cstr name = nullptr);
};
struct DepthImage : Image
{
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
};

View File

@ -37,7 +37,7 @@ GetSurfaceFormats(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
[[nodiscard]] eastl::vector<vk::PresentModeKHR> [[nodiscard]] eastl::vector<vk::PresentModeKHR>
GetSurfacePresentModes(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface); GetSurfacePresentModes(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
struct PhysicalDevice struct PhysicalDevice final
{ {
vk::PhysicalDevice m_PhysicalDevice; vk::PhysicalDevice m_PhysicalDevice;
vk::PhysicalDeviceProperties m_DeviceProperties; vk::PhysicalDeviceProperties m_DeviceProperties;

View File

@ -12,6 +12,7 @@
Swapchain::Swapchain(const Window *window, const Device *device, NameString &&name) Swapchain::Swapchain(const Window *window, const Device *device, NameString &&name)
: m_Device(device) : m_Device(device)
, m_Name(std::move(name)) , m_Name(std::move(name))
, m_Format(vk::Format::eUndefined)
{ {
this->Create(window); this->Create(window);
} }
@ -21,6 +22,32 @@ Swapchain::~Swapchain()
this->Cleanup(); this->Cleanup();
} }
Swapchain::Swapchain(Swapchain &&other) noexcept
: m_Device(other.m_Device)
, m_Swapchain(Take(other.m_Swapchain))
, m_Name(std::move(other.m_Name))
, m_Extent(other.m_Extent)
, m_Format(other.m_Format)
, m_Images(std::move(other.m_Images))
, m_ImageViews(std::move(other.m_ImageViews))
{
}
Swapchain &
Swapchain::operator=(Swapchain &&other) noexcept
{
if (this == &other)
return *this;
m_Device = other.m_Device;
m_Swapchain = Take(other.m_Swapchain);
m_Name = std::move(other.m_Name);
m_Extent = other.m_Extent;
m_Format = other.m_Format;
m_Images = std::move(other.m_Images);
m_ImageViews = std::move(other.m_ImageViews);
return *this;
}
void void
Swapchain::Create(const Window *window) Swapchain::Create(const Window *window)
{ {
@ -150,6 +177,17 @@ Swapchain::Create(const Window *window)
} }
DEBUG("Swapchain {} Image Views created.", m_Name); DEBUG("Swapchain {} Image Views created.", m_Name);
for (auto &callback : m_ResizeCallbacks)
{
callback(m_Extent);
}
}
void
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
{
m_ResizeCallbacks.emplace_back(callback);
} }
void void

View File

@ -15,6 +15,8 @@ struct Device;
struct Swapchain final struct Swapchain final
{ {
using FnResizeCallback = eastl::function<void(vk::Extent2D)>;
const Device *m_Device; const Device *m_Device;
vk::SwapchainKHR m_Swapchain; vk::SwapchainKHR m_Swapchain;
NameString m_Name; NameString m_Name;
@ -23,9 +25,20 @@ struct Swapchain final
eastl::fixed_vector<vk::Image, 4> m_Images; eastl::fixed_vector<vk::Image, 4> m_Images;
eastl::fixed_vector<vk::ImageView, 4> m_ImageViews; eastl::fixed_vector<vk::ImageView, 4> m_ImageViews;
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
void Create(const Window *window);
void RegisterResizeCallback(FnResizeCallback &&callback);
// Ctor/Dtor
Swapchain(const Window *window, const Device *device, NameString &&name); Swapchain(const Window *window, const Device *device, NameString &&name);
~Swapchain(); ~Swapchain();
void Create(const Window *window);
// Move
Swapchain(Swapchain &&other) noexcept;
Swapchain &operator=(Swapchain &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Swapchain);
private: private:
void Cleanup(); void Cleanup();

View File

@ -79,7 +79,7 @@ Window::~Window()
DEBUG("Surface Destroyed"); DEBUG("Surface Destroyed");
} }
if (m_Window != nullptr) if (m_Window)
{ {
glfwDestroyWindow(m_Window); glfwDestroyWindow(m_Window);
m_Window = nullptr; m_Window = nullptr;
@ -87,3 +87,23 @@ Window::~Window()
DEBUG("Window '{}' Destroyed", m_Name); DEBUG("Window '{}' Destroyed", m_Name);
} }
Window::Window(Window &&other) noexcept
: m_Context(other.m_Context)
, m_Window(Take(other.m_Window))
, m_Surface(Take(other.m_Surface))
, m_Name(Take(other.m_Name))
{
}
Window &
Window::operator=(Window &&other) noexcept
{
if (this == &other)
return *this;
m_Context = other.m_Context;
m_Window = Take(other.m_Window);
m_Surface = Take(other.m_Surface);
m_Name = Take(other.m_Name);
return *this;
}

View File

@ -34,4 +34,10 @@ struct Window final
// Ctor/Dtor // Ctor/Dtor
Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen = false); Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen = false);
~Window(); ~Window();
// Move
Window(Window &&other) noexcept;
Window &operator=(Window &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(Window);
}; };

View File

@ -2,7 +2,11 @@
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
add_library(util_helper STATIC helpers.h helpers.cpp) add_library(util_helper STATIC
helpers.h
helpers.cpp
frame.cpp
frame.h)
target_link_libraries(util_helper PRIVATE aster_core) target_link_libraries(util_helper PRIVATE aster_core)
target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

137
samples/00_util/frame.cpp Normal file
View File

@ -0,0 +1,137 @@
// =============================================
// Aster: frame.cpp
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#include "frame.h"
#include "device.h"
#include "helpers.h"
#include "swapchain.h"
Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount)
: m_FrameIdx(0)
, m_ImageIdx(0)
{
m_Device = device;
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueFamilyIndex,
};
AbortIfFailedMV(device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool),
"Could not command pool for frame {}", frameCount);
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
AbortIfFailedMV(device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence),
"Could not create a fence for frame {}", frameCount);
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem),
"Could not create IA semaphore for frame {}.", frameCount);
AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem),
"Could not create RF semaphore for frame {}.", frameCount);
const vk::CommandBufferAllocateInfo allocateInfo = {
.commandPool = m_Pool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailed(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer));
DEBUG("Frame {} created successfully.", frameCount);
}
void
Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Window *window)
{
vk::PresentInfoKHR presentInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &m_RenderFinishSem,
.swapchainCount = 1,
.pSwapchains = &swapchain->m_Swapchain,
.pImageIndices = &m_ImageIdx,
.pResults = nullptr,
};
switch (auto result = commandQueue.presentKHR(&presentInfo))
{
case vk::Result::eSuccess:
break;
case vk::Result::eErrorOutOfDateKHR:
case vk::Result::eSuboptimalKHR:
INFO("Recreating Swapchain. Cause: {}", result);
swapchain->Create(window);
break; // Present failed. We do nothing. Frame is skipped.
default:
AbortIfFailedM(result, "Swapchain Present failed.");
}
}
Frame::~Frame()
{
m_Device->m_Device.destroy(m_RenderFinishSem, nullptr);
m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr);
m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr);
m_Device->m_Device.destroy(m_Pool, nullptr);
DEBUG("Destoryed Frame");
}
// Frame Manager
FrameManager::FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight)
: m_Device(device)
, m_FramesInFlight(framesInFlight)
{
for (u32 i = 0; i < framesInFlight; ++i)
{
m_Frames.emplace_back(device, queueFamilyIndex, i);
}
}
Frame *
FrameManager::GetNextFrame(Swapchain *swapchain, const Window *window)
{
Frame *currentFrame = &m_Frames[m_CurrentFrameIdx];
u32 frameIndex = m_CurrentFrameIdx;
m_CurrentFrameIdx = (m_CurrentFrameIdx + 1) % m_FramesInFlight;
AbortIfFailedMV(m_Device->m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>),
"Waiting for fence {} failed.", frameIndex);
u32 imageIndex;
bool imageAcquired = false;
while (!imageAcquired)
{
auto result = m_Device->m_Device.acquireNextImageKHR(swapchain->m_Swapchain, MaxValue<u64>,
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
switch (result)
{
case vk::Result::eSuccess:
case vk::Result::eSuboptimalKHR: // Suboptimal can still render. Better to let this go for semaphores etc.
imageAcquired = true;
break; // Image acquired. Break out of loop.
case vk::Result::eErrorOutOfDateKHR:
INFO("Recreating Swapchain. Cause: {}", result);
swapchain->Create(window);
break; // Image acquire has failed. We move to the next frame.
default:
AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex);
}
}
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
AbortIfFailedMV(m_Device->m_Device.resetFences(1, &currentFrame->m_FrameAvailableFence), "Fence {} reset failed.",
frameIndex);
AbortIfFailedMV(m_Device->m_Device.resetCommandPool(currentFrame->m_Pool, {}), "Command pool {} reset failed.",
frameIndex);
currentFrame->m_ImageIdx = imageIndex;
return currentFrame;
}

47
samples/00_util/frame.h Normal file
View File

@ -0,0 +1,47 @@
// =============================================
// Aster: frame.h
// Copyright (c) 2020-2024 Anish Bhobe
// =============================================
#pragma once
#include "global.h"
#include "helpers.h"
#include <EASTL/fixed_vector.h>
struct Device;
struct Window;
struct Swapchain;
struct Frame
{
// Persistent
const Device *m_Device;
vk::CommandPool m_Pool;
vk::CommandBuffer m_CommandBuffer;
vk::Fence m_FrameAvailableFence;
vk::Semaphore m_ImageAcquireSem;
vk::Semaphore m_RenderFinishSem;
u32 m_FrameIdx;
// Transient
u32 m_ImageIdx;
void Present(vk::Queue commandQueue, Swapchain *swapchain, const Window *window);
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
~Frame();
};
struct FrameManager
{
const Device *m_Device;
eastl::fixed_vector<Frame, 4> m_Frames{};
u32 m_CurrentFrameIdx = 0;
u32 m_FramesInFlight = 0;
FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight);
Frame *GetNextFrame(Swapchain *swapchain, const Window *window);
};

View File

@ -16,4 +16,27 @@ class PhysicalDevices;
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices); PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice); QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
eastl::vector<u32> ReadFile(cstr fileName); eastl::vector<u32> ReadFile(cstr fileName);
#define AbortIfFailed(RESULT) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
do \
{ \
vk::Result _checkResultValue_; \
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedM(RESULT, MSG) \
do \
{ \
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
} while (false)

View File

@ -4,9 +4,13 @@ cmake_minimum_required(VERSION 3.13)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
add_executable(box box.cpp) add_executable(box box.cpp stb_image.h)
add_shader(box shader/box.vert.glsl) add_shader(box shader/box.vert.glsl)
add_shader(box shader/box.frag.glsl) add_shader(box shader/box.frag.glsl)
target_link_libraries(box PRIVATE aster_core) target_link_libraries(box PRIVATE aster_core)
target_link_libraries(box PRIVATE util_helper) target_link_libraries(box PRIVATE util_helper)
add_custom_target(copy-resource-files ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/image/ ${CMAKE_CURRENT_BINARY_DIR}/
DEPENDS box)

View File

@ -7,43 +7,67 @@
#include "constants.h" #include "constants.h"
#include "context.h" #include "context.h"
#include "device.h" #include "device.h"
#include "physical_device.h"
#include "window.h"
#include "global.h" #include "global.h"
#include "physical_device.h"
#include "pipeline.h" #include "pipeline.h"
#include "swapchain.h" #include "swapchain.h"
#include "window.h"
#include "helpers.h" #include "helpers.h"
#define STB_IMAGE_IMPLEMENTATION
#include "frame.h"
#include "image.h"
#include "stb_image.h"
#include <EASTL/array.h> #include <EASTL/array.h>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv"; constexpr auto VERTEX_SHADER_FILE = "shader/box.vert.glsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.frag.glsl.spv";
#define AbortIfFailed(RESULT) \ struct ImageFile
do \ {
{ \ void *m_Data = nullptr;
vk::Result _checkResultValue_; \ u32 m_Width = 0;
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \ u32 m_Height = 0;
THEN_ABORT(_checkResultValue_); \ u32 m_NumChannels = 0;
} while (false)
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \ bool Load(cstr fileName);
do \ [[nodiscard]] usize GetSize() const;
{ \ ~ImageFile();
vk::Result _checkResultValue_; \ };
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
THEN_ABORT(_checkResultValue_); \
} while (false)
#define AbortIfFailedM(RESULT, MSG) \ bool
do \ ImageFile::Load(cstr fileName)
{ \ {
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \ int width, height, nrChannels;
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \ m_Data = stbi_load(fileName, &width, &height, &nrChannels, 4);
} while (false) ERROR_IF(!m_Data, "Could not load {}", fileName);
if (!m_Data)
{
return false;
}
m_Width = width;
m_Height = height;
m_NumChannels = 4;
return true;
}
usize
ImageFile::GetSize() const
{
return m_Width * m_Height * m_NumChannels;
}
ImageFile::~ImageFile()
{
stbi_image_free(m_Data);
m_Data = nullptr;
}
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain); Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
@ -51,6 +75,7 @@ Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
struct Vertex struct Vertex
{ {
vec3 m_Position; vec3 m_Position;
vec2 m_UV0;
constexpr static vk::VertexInputBindingDescription constexpr static vk::VertexInputBindingDescription
GetBinding(const u32 binding) GetBinding(const u32 binding)
@ -58,7 +83,7 @@ struct Vertex
return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex}; return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
} }
constexpr static eastl::array<vk::VertexInputAttributeDescription, 1> constexpr static eastl::array<vk::VertexInputAttributeDescription, 2>
GetAttributes(const u32 binding) GetAttributes(const u32 binding)
{ {
return { return {
@ -68,6 +93,12 @@ struct Vertex
.format = vk::Format::eR32G32B32Sfloat, .format = vk::Format::eR32G32B32Sfloat,
.offset = offsetof(Vertex, m_Position), .offset = offsetof(Vertex, m_Position),
}, },
vk::VertexInputAttributeDescription{
.location = 1,
.binding = binding,
.format = vk::Format::eR32G32Sfloat,
.offset = offsetof(Vertex, m_UV0),
},
}; };
} }
}; };
@ -79,19 +110,6 @@ struct Camera
mat4 m_Perspective; mat4 m_Perspective;
}; };
struct Frame
{
const Device *m_Device;
vk::CommandPool m_Pool;
vk::CommandBuffer m_CommandBuffer;
vk::Fence m_FrameAvailableFence;
vk::Semaphore m_ImageAcquireSem;
vk::Semaphore m_RenderFinishSem;
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
~Frame();
};
int int
main(int, char **) main(int, char **)
{ {
@ -105,7 +123,10 @@ main(int, char **)
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.dynamicRendering = true}}; Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true},
.m_Vulkan13Features = {.dynamicRendering = true},
};
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"}; Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
@ -123,12 +144,18 @@ main(int, char **)
vk::DescriptorSet descriptorSet; vk::DescriptorSet descriptorSet;
{ {
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts.front(); vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts.front();
vk::DescriptorPoolSize poolSize = { eastl::array poolSizes = {
.type = vk::DescriptorType::eUniformBuffer, vk::DescriptorPoolSize{
.descriptorCount = 1, .type = vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
},
vk::DescriptorPoolSize{
.type = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
},
}; };
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = { vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
.maxSets = 1, .poolSizeCount = 1, .pPoolSizes = &poolSize}; .maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool)); AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool));
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = { vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
@ -157,25 +184,99 @@ main(int, char **)
"Copy command buffer allocation failed."); "Copy command buffer allocation failed.");
} }
// eastl::array<Vertex, 3> vertices{};
eastl::array vertices = { eastl::array vertices = {
vec3(-0.5f, -0.5f, -0.5f), vec3(0.5f, -0.5f, -0.5f), vec3(0.5f, 0.5f, -0.5f), vec3(0.5f, 0.5f, -0.5f), Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
vec3(-0.5f, 0.5f, -0.5f), vec3(-0.5f, -0.5f, -0.5f), vec3(-0.5f, -0.5f, 0.5f), vec3(0.5f, -0.5f, 0.5f), Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_UV0 = vec2(1.0f, 0.0f)},
vec3(0.5f, 0.5f, 0.5f), vec3(0.5f, 0.5f, 0.5f), vec3(-0.5f, 0.5f, 0.5f), vec3(-0.5f, -0.5f, 0.5f), Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
vec3(-0.5f, 0.5f, 0.5f), vec3(-0.5f, 0.5f, -0.5f), vec3(-0.5f, -0.5f, -0.5f), vec3(-0.5f, -0.5f, -0.5f), Vertex{.m_Position = vec3(-0.5f, -0.5f, -0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
vec3(-0.5f, -0.5f, 0.5f), vec3(-0.5f, 0.5f, 0.5f), vec3(0.5f, 0.5f, 0.5f), vec3(0.5f, 0.5f, -0.5f), Vertex{.m_Position = vec3(-0.5f, 0.5f, -0.5f), .m_UV0 = vec2(0.0f, 1.0f)},
vec3(0.5f, -0.5f, -0.5f), vec3(0.5f, -0.5f, -0.5f), vec3(0.5f, -0.5f, 0.5f), vec3(0.5f, 0.5f, 0.5f), Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_UV0 = vec2(1.0f, 1.0f)},
vec3(-0.5f, -0.5f, -0.5f), vec3(0.5f, -0.5f, -0.5f), vec3(0.5f, -0.5f, 0.5f), vec3(0.5f, -0.5f, 0.5f),
vec3(-0.5f, -0.5f, 0.5f), vec3(-0.5f, -0.5f, -0.5f), vec3(-0.5f, 0.5f, -0.5f), vec3(0.5f, 0.5f, -0.5f), Vertex{.m_Position = vec3(-0.5f, -0.5f, 0.5f), .m_UV0 = vec2(0.0f, 0.0f)},
vec3(0.5f, 0.5f, 0.5f), vec3(0.5f, 0.5f, 0.5f), vec3(-0.5f, 0.5f, 0.5f), vec3(-0.5f, 0.5f, -0.5f), 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_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_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_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_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)},
}; };
ImageFile imageFile;
bool loaded = imageFile.Load("image/container.jpg");
assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
VertexBuffer vbo; VertexBuffer vbo;
Texture crate;
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO"); vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
crate.Init(&device, {imageFile.m_Width, imageFile.m_Height}, false, "Crate Texture");
{ {
StagingBuffer staging; StagingBuffer vertexStaging, imageStaging;
staging.Init(&device, vertices.size() * sizeof vertices[0], "Staging"); vertexStaging.Init(&device, vertices.size() * sizeof vertices[0], "Vertex Staging");
staging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data()); vertexStaging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
imageStaging.Init(&device, imageFile.GetSize(), "Image Staging");
INFO("fine {}", imageFile.GetSize());
imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data);
vk::ImageMemoryBarrier imageReadyToWrite = {
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk::ImageMemoryBarrier imageReadyToRead = {
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family,
.dstQueueFamilyIndex = queueAllocation.m_Family,
.image = crate.m_Image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vk::Fence fence; vk::Fence fence;
vk::FenceCreateInfo fenceCreateInfo = {}; vk::FenceCreateInfo fenceCreateInfo = {};
@ -183,9 +284,31 @@ main(int, char **)
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
AbortIfFailed(copyBuffer.begin(&beginInfo)); AbortIfFailed(copyBuffer.begin(&beginInfo));
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eHost, vk::PipelineStageFlagBits::eTransfer, {}, 0,
nullptr, 0, nullptr, 1, &imageReadyToWrite);
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = staging.GetSize()}; vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = vertexStaging.GetSize()};
copyBuffer.copyBuffer(staging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy); copyBuffer.copyBuffer(vertexStaging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy);
vk::BufferImageCopy imageCopy = {
.bufferOffset = 0,
.bufferRowLength = imageFile.m_Width,
.bufferImageHeight = imageFile.m_Height,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {},
.imageExtent = {imageFile.m_Width, imageFile.m_Height, 1},
};
copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate.m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
&imageCopy);
copyBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {},
0, nullptr, 0, nullptr, 1, &imageReadyToRead);
AbortIfFailed(copyBuffer.end()); AbortIfFailed(copyBuffer.end());
@ -203,7 +326,50 @@ main(int, char **)
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool."); AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr); device.m_Device.destroy(fence, nullptr);
staging.Destroy(&device); vertexStaging.Destroy(&device);
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,
.mipmapMode = vk::SamplerMipmapMode::eLinear,
.addressModeU = vk::SamplerAddressMode::eRepeat,
.addressModeV = vk::SamplerAddressMode::eRepeat,
.addressModeW = vk::SamplerAddressMode::eRepeat,
.mipLodBias = 0.2,
.anisotropyEnable = true,
.maxAnisotropy = 1.0f,
.compareEnable = false,
.minLod = 0,
.maxLod = 4,
.unnormalizedCoordinates = false,
};
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
} }
UniformBuffer ubo; UniformBuffer ubo;
@ -214,15 +380,30 @@ main(int, char **)
.offset = 0, .offset = 0,
.range = ubo.GetSize(), .range = ubo.GetSize(),
}; };
vk::WriteDescriptorSet writeDescriptors = { vk::DescriptorImageInfo descriptorImageInfo = {
.dstSet = descriptorSet, .sampler = sampler,
.dstBinding = 0, .imageView = imageView,
.dstArrayElement = 0, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &descriptorBufferInfo,
}; };
device.m_Device.updateDescriptorSets(1, &writeDescriptors, 0, nullptr); eastl::array writeDescriptors = {
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 0,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eUniformBuffer,
.pBufferInfo = &descriptorBufferInfo,
},
vk::WriteDescriptorSet{
.dstSet = descriptorSet,
.dstBinding = 1,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.pImageInfo = &descriptorImageInfo,
},
};
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
// Persistent variables // Persistent variables
vk::Viewport viewport = { vk::Viewport viewport = {
@ -239,6 +420,14 @@ main(int, char **)
.extent = swapchain.m_Extent, .extent = swapchain.m_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);
vk::ImageSubresourceRange subresourceRange = { vk::ImageSubresourceRange subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0, .baseMipLevel = 0,
@ -261,58 +450,75 @@ main(int, char **)
.subresourceRange = subresourceRange, .subresourceRange = subresourceRange,
}; };
// Frames FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames; eastl::fixed_vector<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) 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)
{ {
frames.emplace_back(&device, queueAllocation.m_Family, i); 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++;
} }
auto RecreateDepthBuffers = [&device, &depthImages, &depthViews, depthSubresourceRange](vk::Extent2D extent) {
for (auto &depthView : depthViews)
{
device.m_Device.destroy(depthView, nullptr);
}
u32 index = 0;
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);
Time::Init(); Time::Init();
INFO("Starting loop"); INFO("Starting loop");
u32 frameIndex = 0;
while (window.Poll()) while (window.Poll())
{ {
Frame *currentFrame = &frames[frameIndex];
Time::Update(); Time::Update();
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo.Write(&device, 0, sizeof camera, &camera); ubo.Write(&device, 0, sizeof camera, &camera);
AbortIfFailedMV(device.m_Device.waitForFences(1, &currentFrame->m_FrameAvailableFence, true, MaxValue<u64>), Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window);
"Waiting for fence {} failed.", frameIndex);
u32 imageIndex;
auto result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
if (Failed(result))
{
switch (result)
{
case vk::Result::eErrorOutOfDateKHR:
case vk::Result::eSuboptimalKHR:
INFO("Recreating Swapchain. Cause: {}", result);
swapchain.Create(&window);
viewport.y = Cast<f32>(swapchain.m_Extent.height);
viewport.width = Cast<f32>(swapchain.m_Extent.width);
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
scissor.extent = swapchain.m_Extent;
continue; // Image acquire has failed. We move to the next frame.
default:
AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex);
}
}
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
AbortIfFailedMV(device.m_Device.resetFences(1, &currentFrame->m_FrameAvailableFence), "Fence {} reset failed.",
frameIndex);
AbortIfFailedMV(device.m_Device.resetCommandPool(currentFrame->m_Pool, {}), "Command pool {} reset failed.",
frameIndex);
u32 imageIndex = currentFrame->m_ImageIdx;
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
vk::Image currentImage = swapchain.m_Images[imageIndex]; vk::Image currentImage = swapchain.m_Images[imageIndex];
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
vk::ImageView currentDepthImageView = depthViews[currentFrame->m_FrameIdx];
topOfThePipeBarrier.image = currentImage; topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage; renderToPresentBarrier.image = currentImage;
@ -324,20 +530,32 @@ main(int, char **)
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier); {}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
// Render // Render
vk::RenderingAttachmentInfo attachmentInfo = { eastl::array attachmentInfos = {
.imageView = currentImageView, vk::RenderingAttachmentInfo{
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal, .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, .resolveMode = vk::ResolveModeFlagBits::eNone,
.loadOp = vk::AttachmentLoadOp::eClear, .loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore, .storeOp = vk::AttachmentStoreOp::eDontCare,
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f}, .clearValue = vk::ClearDepthStencilValue{.depth = 1.0f, .stencil = 0},
}; };
vk::RenderingInfo renderingInfo = { vk::RenderingInfo renderingInfo = {
.renderArea = {.extent = swapchain.m_Extent}, .renderArea = {.extent = swapchain.m_Extent},
.layerCount = 1, .layerCount = 1,
.colorAttachmentCount = 1, .colorAttachmentCount = Cast<u32>(attachmentInfos.size()),
.pColorAttachments = &attachmentInfo, .pColorAttachments = attachmentInfos.data(),
.pDepthAttachment = &depthAttachment,
}; };
cmd.beginRendering(&renderingInfo); cmd.beginRendering(&renderingInfo);
@ -369,77 +587,30 @@ main(int, char **)
}; };
AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
vk::PresentInfoKHR presentInfo = { currentFrame->Present(commandQueue, &swapchain, &window);
.waitSemaphoreCount = 1,
.pWaitSemaphores = &currentFrame->m_RenderFinishSem,
.swapchainCount = 1,
.pSwapchains = &swapchain.m_Swapchain,
.pImageIndices = &imageIndex,
.pResults = nullptr,
};
result = commandQueue.presentKHR(&presentInfo);
if (Failed(result))
{
switch (result)
{
case vk::Result::eErrorOutOfDateKHR:
case vk::Result::eSuboptimalKHR:
INFO("Recreating Swapchain. Cause: {}", result);
swapchain.Create(&window);
viewport.y = Cast<f32>(swapchain.m_Extent.height);
viewport.width = Cast<f32>(swapchain.m_Extent.width);
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
scissor.extent = swapchain.m_Extent;
break; // Present failed. We redo the frame.
default:
AbortIfFailedM(result, "Swapchain Present failed.");
}
}
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
} }
AbortIfFailed(device.m_Device.waitIdle()); 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); ubo.Destroy(&device);
device.m_Device.destroy(descriptorPool, nullptr); device.m_Device.destroy(descriptorPool, nullptr);
device.m_Device.destroy(copyPool, nullptr); device.m_Device.destroy(copyPool, nullptr);
crate.Destroy(&device);
vbo.Destroy(&device); vbo.Destroy(&device);
return 0; return 0;
} }
Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount)
{
m_Device = device;
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueFamilyIndex,
};
AbortIfFailedMV(device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool),
"Could not command pool for frame {}", frameCount);
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
AbortIfFailedMV(device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence),
"Could not create a fence for frame {}", frameCount);
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem),
"Could not create IA semaphore for frame {}.", frameCount);
AbortIfFailedMV(device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem),
"Could not create RF semaphore for frame {}.", frameCount);
const vk::CommandBufferAllocateInfo allocateInfo = {
.commandPool = m_Pool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailed(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer));
DEBUG("Frame {} created successfully.", frameCount);
}
Pipeline Pipeline
CreatePipeline(const Device *device, const Swapchain *swapchain) CreatePipeline(const Device *device, const Swapchain *swapchain)
{ {
@ -460,15 +631,23 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
}, },
}}; }};
vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding = { eastl::array descriptorSetLayoutBinding = {
.binding = 0, vk::DescriptorSetLayoutBinding{
.descriptorType = vk::DescriptorType::eUniformBuffer, .binding = 0,
.descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer,
.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, .descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eVertex,
},
vk::DescriptorSetLayoutBinding{
.binding = 1,
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eFragment,
},
}; };
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
.bindingCount = 1, .bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()),
.pBindings = &descriptorSetLayoutBinding, .pBindings = descriptorSetLayoutBinding.data(),
}; };
vk::DescriptorSetLayout descriptorSetLayout; vk::DescriptorSetLayout descriptorSetLayout;
AbortIfFailed( AbortIfFailed(
@ -517,8 +696,9 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
.sampleShadingEnable = false, .sampleShadingEnable = false,
}; };
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = { vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
.depthTestEnable = false, .depthTestEnable = true,
.depthWriteEnable = false, .depthWriteEnable = true,
.depthCompareOp = vk::CompareOp::eLess,
}; };
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
.blendEnable = false, .blendEnable = false,
@ -551,6 +731,7 @@ CreatePipeline(const Device *device, const Swapchain *swapchain)
.viewMask = 0, .viewMask = 0,
.colorAttachmentCount = 1, .colorAttachmentCount = 1,
.pColorAttachmentFormats = &swapchain->m_Format, .pColorAttachmentFormats = &swapchain->m_Format,
.depthAttachmentFormat = vk::Format::eD32Sfloat,
}; };
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
@ -591,14 +772,4 @@ CreateShader(const Device *device, cstr shaderFile)
AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule), AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule),
"Shader {} could not be created.", shaderFile); "Shader {} could not be created.", shaderFile);
return shaderModule; return shaderModule;
}
Frame::~Frame()
{
m_Device->m_Device.destroy(m_RenderFinishSem, nullptr);
m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr);
m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr);
m_Device->m_Device.destroy(m_Pool, nullptr);
DEBUG("Destoryed Frame");
} }

BIN
samples/02_box/image/container.jpg (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,9 +1,11 @@
#version 450 #version 450
#pragma shader_stage(fragment) #pragma shader_stage(fragment)
layout (location = 0) in vec3 inColor; layout (location = 0) in vec2 inUV;
layout (location = 0) out vec4 outColor; layout (location = 0) out vec4 outColor;
layout(binding = 1) uniform sampler2D tex;
void main() { void main() {
outColor = vec4(inColor, 1.0); outColor = vec4(texture(tex, inUV).rgb, 1.0f);
} }

View File

@ -2,8 +2,9 @@
#pragma shader_stage(vertex) #pragma shader_stage(vertex)
layout(location=0) in vec4 position; layout(location=0) in vec4 position;
layout(location=1) in vec2 uv0;
layout(location=0) out vec3 outColor; layout(location=0) out vec2 outUV;
layout(binding=0) uniform Camera { layout(binding=0) uniform Camera {
mat4 model; mat4 model;
@ -12,6 +13,7 @@ layout(binding=0) uniform Camera {
} ubo; } ubo;
void main() { void main() {
outUV = uv0;
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(position.xyz, 1.0f); gl_Position = ubo.proj * ubo.view * ubo.model * vec4(position.xyz, 1.0f);
outColor = vec3(0.5f, 0.3f, 0.1f); // outColor = vec3(0.5f, 0.3f, 0.1f);
} }

7988
samples/02_box/stb_image.h Normal file

File diff suppressed because it is too large Load Diff