[WIP] Added a transfer context for uploads.

This commit is contained in:
Anish Bhobe 2025-05-08 00:34:59 +02:00
parent 7351415ebf
commit 63282c3587
11 changed files with 854 additions and 358 deletions

View File

@ -39,6 +39,18 @@ struct Device final
void WaitIdle() const; void WaitIdle() const;
vk::Device *
operator->()
{
return &m_Device;
}
const vk::Device *
operator->() const
{
return &m_Device;
}
// Ctor/Dtor // Ctor/Dtor
Device() = default; Device() = default;
Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures, Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,

View File

@ -34,6 +34,8 @@ struct Size2D
return *this; return *this;
} }
bool operator==(const Size2D&) const = default;
operator vk::Extent2D() const operator vk::Extent2D() const
{ {
return {m_Width, m_Height}; return {m_Width, m_Height};

View File

@ -3,6 +3,8 @@
// Copyright (c) 2020-2025 Anish Bhobe // Copyright (c) 2020-2025 Anish Bhobe
// ============================================= // =============================================
#pragma once
#include "resource.h" #include "resource.h"
#include "aster/aster.h" #include "aster/aster.h"
@ -14,11 +16,12 @@
#include "aster/core/physical_device.h" #include "aster/core/physical_device.h"
#include "aster/core/pipeline.h" #include "aster/core/pipeline.h"
#include "aster/core/sampler.h" #include "aster/core/sampler.h"
#include "aster/core/size.h"
#include "aster/core/swapchain.h" #include "aster/core/swapchain.h"
#include <EASTL/hash_map.h> #include <EASTL/hash_map.h>
#include <EASTL/optional.h> #include <EASTL/optional.h>
#include <EASTL/vector_map.h> #include "EASTL/deque.h"
#include <slang-com-ptr.h> #include <slang-com-ptr.h>
#include <slang.h> #include <slang.h>
@ -44,14 +47,14 @@ struct eastl::hash<vk::SamplerCreateInfo>
hash = HashCombine(hash, HashAny(createInfo.addressModeW)); hash = HashCombine(hash, HashAny(createInfo.addressModeW));
hash = HashCombine(hash, HashAny(static_cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3 hash = HashCombine(hash, HashAny(static_cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3
hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable)); hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable));
hash = hash = HashCombine(
HashCombine(hash, hash,
HashAny(static_cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution HashAny(static_cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution
hash = HashCombine(hash, HashAny(createInfo.compareEnable)); hash = HashCombine(hash, HashAny(createInfo.compareEnable));
hash = HashCombine(hash, HashAny(createInfo.compareOp)); hash = HashCombine(hash, HashAny(createInfo.compareOp));
hash = HashCombine(hash, HashAny(static_cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough. hash = HashCombine(hash, HashAny(static_cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough.
hash = hash = HashCombine(
HashCombine(hash, hash,
HashAny(static_cast<usize>(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp) HashAny(static_cast<usize>(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
hash = HashCombine(hash, HashAny(createInfo.borderColor)); hash = HashCombine(hash, HashAny(createInfo.borderColor));
hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates)); hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates));
@ -375,9 +378,27 @@ struct DeviceCreateInfo
#pragma endregion #pragma endregion
namespace _internal
{
class SyncServer;
}
class Receipt
{
void *m_Opaque;
explicit Receipt(void *opaque)
: m_Opaque{opaque}
{
}
friend _internal::SyncServer;
};
class Device; class Device;
struct Frame; struct Frame;
#define DEPRECATE_RAW_CALLS
class Context class Context
{ {
protected: protected:
@ -392,12 +413,12 @@ class Context
} }
public: public:
DEPRECATE_RAW_CALLS void Dependency(const vk::DependencyInfo &dependencyInfo);
void Begin(); void Begin();
void End(); void End();
}; };
#define DEPRECATE_RAW_CALLS
class GraphicsContext : public Context class GraphicsContext : public Context
{ {
protected: protected:
@ -409,22 +430,61 @@ class GraphicsContext : public Context
{ {
} }
const Pipeline *m_PipelineInUse;
public: public:
DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport); DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport);
void BindVertexBuffer(const Ref<VertexBuffer> &vertexBuffer); void BindVertexBuffer(const Ref<VertexBuffer> &vertexBuffer);
void BindPipeline(const Pipeline &pipeline); void BindPipeline(const Pipeline &pipeline);
void
PushConstantBlock(auto &block)
{
m_Cmd.pushConstants(m_PipelineInUse->m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, sizeof block, &block);
}
void Draw(u32 vertexCount); void Draw(u32 vertexCount);
void DrawIndexed(u32 indexCount); void DrawIndexed(u32 indexCount);
DEPRECATE_RAW_CALLS void Dependency(const vk::DependencyInfo &dependencyInfo);
DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo); DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo);
void EndRendering(); void EndRendering();
}; };
class TransferContext : public Context
{
protected:
friend Device;
friend Frame;
Device *m_Device;
explicit TransferContext(Device &device, const vk::CommandBuffer cmd)
: Context{cmd}
, m_Device{&device}
{
}
eastl::vector<Ref<Buffer>> m_OwnedBuffers;
eastl::vector<Ref<Image>> m_OwnedImages;
void Reset();
public:
struct ImageData
{
void *m_Data;
usize m_NumBytes;
};
void UploadTexture(const Ref<Image> &image, const ImageData &data);
TransferContext(TransferContext &&other) noexcept;
TransferContext &operator=(TransferContext &&other) noexcept;
DISALLOW_COPY_AND_ASSIGN(TransferContext);
};
struct Frame struct Frame
{ {
// Persistent // Persistent
::Device *m_Device; Device *m_Device;
// TODO: ThreadSafe // TODO: ThreadSafe
vk::CommandPool m_Pool; vk::CommandPool m_Pool;
vk::Fence m_FrameAvailableFence; vk::Fence m_FrameAvailableFence;
@ -443,6 +503,7 @@ struct Frame
void Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize); void Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize);
GraphicsContext CreateGraphicsContext(); GraphicsContext CreateGraphicsContext();
TransferContext CreateTransferContext();
void WaitUntilReady(); void WaitUntilReady();
}; };
@ -457,7 +518,10 @@ class Device final
// TODO: This is single-threaded. // TODO: This is single-threaded.
vk::Queue m_GraphicsQueue; vk::Queue m_GraphicsQueue;
u32 m_GraphicsQueueFamily; u32 m_PrimaryQueueFamily;
vk::Queue m_TransferQueue;
u32 m_TransferQueueFamily;
std::array<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames; std::array<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames;
u32 m_CurrentFrameIdx = 0; u32 m_CurrentFrameIdx = 0;
@ -583,8 +647,26 @@ class Device final
void Present(Frame &frame, GraphicsContext &graphicsContext); void Present(Frame &frame, GraphicsContext &graphicsContext);
//
// Context
// ----------------------------------------------------------------------------------------------------
friend Context; friend Context;
friend GraphicsContext; friend GraphicsContext;
friend TransferContext;
vk::CommandPool m_TransferPool;
eastl::deque<TransferContext> m_TransferContexts;
eastl::vector<u32> m_TransferContextFreeList;
TransferContext &CreateTransferContext();
Receipt Submit(Context &context);
//
// Sync
// ----------------------------------------------------------------------------------------------------
std::unique_ptr<_internal::SyncServer> m_SyncServer;
void WaitOn(Receipt recpt);
// //
// Device Methods // Device Methods

View File

@ -0,0 +1,69 @@
// =============================================
// Aster: sync_server.h
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "aster/aster.h"
#include <EASTL/deque.h>
#include <EASTL/intrusive_list.h>
namespace systems
{
class Receipt;
class Device;
} // namespace systems
namespace systems::_internal
{
struct TimelinePoint
{
u64 m_WaitValue;
u64 m_NextValue;
};
class SyncServer
{
struct Entry : eastl::intrusive_list_node
{
vk::Semaphore m_Semaphore;
TimelinePoint m_CurrentPoint;
static Entry Create(Device &device);
void Destroy(Device &device);
void Wait(Device &device);
void Next();
};
Device *m_Device;
eastl::deque<Entry> m_Allocations;
eastl::intrusive_list<Entry> m_FreeList;
public:
Receipt Allocate();
void Free(Receipt);
void WaitOn(Receipt);
private:
static Entry &GetEntry(Receipt receipt);
// Inner Alloc/Free functions.
Entry &AllocateEntry();
void FreeEntry(Entry &entry);
// Constructor/Destructor
explicit SyncServer(Device &device);
public:
~SyncServer();
// Move Constructors.
SyncServer(SyncServer &&other) noexcept;
SyncServer &operator=(SyncServer &&other) noexcept;
friend Device;
DISALLOW_COPY_AND_ASSIGN(SyncServer);
};
} // namespace systems::_internal

View File

@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.13)
target_sources(aster_core target_sources(aster_core
PRIVATE PRIVATE
"device.cpp" "device.cpp"
"commit_manager.cpp") "commit_manager.cpp"
"sync_server.cpp")

View File

@ -9,14 +9,136 @@
#include "core/window.h" #include "core/window.h"
#include "systems/resource.h" #include "systems/resource.h"
#include "aster/systems/sync_server.h"
#include "aster/util/files.h" #include "aster/util/files.h"
#include <EASTL/vector_map.h>
#include <fmt/ranges.h> #include <fmt/ranges.h>
static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT =
QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute | QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute |
QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer; QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer;
constexpr static u32
GetFormatSize(const vk::Format format)
{
switch (format)
{
case vk::Format::eUndefined:
return 0;
case vk::Format::eR8Unorm:
case vk::Format::eR8Snorm:
case vk::Format::eR8Uscaled:
case vk::Format::eR8Sscaled:
case vk::Format::eR8Uint:
case vk::Format::eR8Sint:
case vk::Format::eR8Srgb:
return 1;
case vk::Format::eR8G8Unorm:
case vk::Format::eR8G8Snorm:
case vk::Format::eR8G8Uscaled:
case vk::Format::eR8G8Sscaled:
case vk::Format::eR8G8Uint:
case vk::Format::eR8G8Sint:
case vk::Format::eR8G8Srgb:
return 2;
case vk::Format::eR8G8B8Unorm:
case vk::Format::eR8G8B8Snorm:
case vk::Format::eR8G8B8Uscaled:
case vk::Format::eR8G8B8Sscaled:
case vk::Format::eR8G8B8Uint:
case vk::Format::eR8G8B8Sint:
case vk::Format::eR8G8B8Srgb:
case vk::Format::eB8G8R8Unorm:
case vk::Format::eB8G8R8Snorm:
case vk::Format::eB8G8R8Uscaled:
case vk::Format::eB8G8R8Sscaled:
case vk::Format::eB8G8R8Uint:
case vk::Format::eB8G8R8Sint:
case vk::Format::eB8G8R8Srgb:
return 3;
case vk::Format::eR8G8B8A8Unorm:
case vk::Format::eR8G8B8A8Snorm:
case vk::Format::eR8G8B8A8Uscaled:
case vk::Format::eR8G8B8A8Sscaled:
case vk::Format::eR8G8B8A8Uint:
case vk::Format::eR8G8B8A8Sint:
case vk::Format::eR8G8B8A8Srgb:
case vk::Format::eB8G8R8A8Unorm:
case vk::Format::eB8G8R8A8Snorm:
case vk::Format::eB8G8R8A8Uscaled:
case vk::Format::eB8G8R8A8Sscaled:
case vk::Format::eB8G8R8A8Uint:
case vk::Format::eB8G8R8A8Sint:
case vk::Format::eB8G8R8A8Srgb:
return 4;
case vk::Format::eR16Unorm:
case vk::Format::eR16Snorm:
case vk::Format::eR16Uscaled:
case vk::Format::eR16Sscaled:
case vk::Format::eR16Uint:
case vk::Format::eR16Sint:
case vk::Format::eR16Sfloat:
return 2;
case vk::Format::eR16G16Unorm:
case vk::Format::eR16G16Snorm:
case vk::Format::eR16G16Uscaled:
case vk::Format::eR16G16Sscaled:
case vk::Format::eR16G16Uint:
case vk::Format::eR16G16Sint:
case vk::Format::eR16G16Sfloat:
return 4;
case vk::Format::eR16G16B16Unorm:
case vk::Format::eR16G16B16Snorm:
case vk::Format::eR16G16B16Uscaled:
case vk::Format::eR16G16B16Sscaled:
case vk::Format::eR16G16B16Uint:
case vk::Format::eR16G16B16Sint:
case vk::Format::eR16G16B16Sfloat:
return 6;
case vk::Format::eR16G16B16A16Unorm:
case vk::Format::eR16G16B16A16Snorm:
case vk::Format::eR16G16B16A16Uscaled:
case vk::Format::eR16G16B16A16Sscaled:
case vk::Format::eR16G16B16A16Uint:
case vk::Format::eR16G16B16A16Sint:
case vk::Format::eR16G16B16A16Sfloat:
return 8;
case vk::Format::eR32Uint:
case vk::Format::eR32Sint:
case vk::Format::eR32Sfloat:
return 4;
case vk::Format::eR32G32Uint:
case vk::Format::eR32G32Sint:
case vk::Format::eR32G32Sfloat:
return 8;
case vk::Format::eR32G32B32Uint:
case vk::Format::eR32G32B32Sint:
case vk::Format::eR32G32B32Sfloat:
return 12;
case vk::Format::eR32G32B32A32Uint:
case vk::Format::eR32G32B32A32Sint:
case vk::Format::eR32G32B32A32Sfloat:
return 16;
case vk::Format::eD16Unorm:
return 2;
case vk::Format::eD32Sfloat:
return 4;
case vk::Format::eS8Uint:
return 1;
case vk::Format::eD16UnormS8Uint:
return 6;
case vk::Format::eD24UnormS8Uint:
return 4;
case vk::Format::eD32SfloatS8Uint:
return 5;
default:
TODO("Esoteric Formats");
}
return 0;
}
PhysicalDevice PhysicalDevice
systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices) systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices)
{ {
@ -125,7 +247,8 @@ systems::Device::CreateTexture2D(const Texture2DCreateInfo &createInfo)
VkImage rawImage; VkImage rawImage;
VmaAllocation allocation; VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo), auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator,
reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr)); &allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
@ -156,7 +279,8 @@ systems::Device::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
VkImage rawImage; VkImage rawImage;
VmaAllocation allocation; VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo), auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator,
reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr)); &allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
@ -187,7 +311,8 @@ systems::Device::CreateAttachment(const AttachmentCreateInfo &createInfo)
VkImage rawImage; VkImage rawImage;
VmaAllocation allocation; VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo), auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator,
reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr)); &allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
@ -213,7 +338,8 @@ systems::Device::CreateDepthStencilImage(const DepthStencilImageCreateInfo &crea
VkImage rawImage; VkImage rawImage;
VmaAllocation allocation; VmaAllocation allocation;
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo), auto result = static_cast<vk::Result>(vmaCreateImage(m_Device.m_Allocator,
reinterpret_cast<VkImageCreateInfo *>(&imageCreateInfo),
&allocationCreateInfo, &rawImage, &allocation, nullptr)); &allocationCreateInfo, &rawImage, &allocation, nullptr));
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
@ -337,7 +463,7 @@ systems::Device::CreateView(const ViewCreateInfo<Image> &createInfo)
vk::ImageView view; vk::ImageView view;
const auto imageViewCreateInfo = static_cast<vk::ImageViewCreateInfo>(createInfo); const auto imageViewCreateInfo = static_cast<vk::ImageViewCreateInfo>(createInfo);
auto result = m_Device.m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); auto result = m_Device->createImageView(&imageViewCreateInfo, nullptr, &view);
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result) ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
THEN_ABORT(result); THEN_ABORT(result);
@ -523,6 +649,8 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre
.viewMask = 0, .viewMask = 0,
.colorAttachmentCount = 1, .colorAttachmentCount = 1,
.pColorAttachmentFormats = &m_Swapchain.m_Format, .pColorAttachmentFormats = &m_Swapchain.m_Format,
// TODO: Select the right Depth Format based on device.
.depthAttachmentFormat = vk::Format::eD24UnormS8Uint,
}; };
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
@ -540,14 +668,14 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre
.layout = pipelineLayout, .layout = pipelineLayout,
}; };
vk::Pipeline pipeline; vk::Pipeline pipeline;
auto vresult = m_Device.m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline); auto vresult = m_Device->createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline);
ERROR_IF(Failed(vresult), "Could not create a graphics pipeline. Cause: {}", vresult) ERROR_IF(Failed(vresult), "Could not create a graphics pipeline. Cause: {}", vresult)
THEN_ABORT(vresult); THEN_ABORT(vresult);
SetName(pipeline, "Triangle Pipeline"); SetName(pipeline, "Triangle Pipeline");
for (auto &shader : shaders) for (auto &shader : shaders)
{ {
m_Device.m_Device.destroy(shader.module, nullptr); m_Device->destroy(shader.module, nullptr);
} }
pipelineOut = {&m_Device, pipelineLayout, pipeline, {}}; pipelineOut = {&m_Device, pipelineLayout, pipeline, {}};
@ -709,8 +837,8 @@ systems::Device::CreateShaders(
} }
ComPtr<slang::IComponentType> pipelineComposite; ComPtr<slang::IComponentType> pipelineComposite;
auto slangResult = m_SlangSession->createCompositeComponentType(components.data(), static_cast<u32>(components.size()), auto slangResult = m_SlangSession->createCompositeComponentType(
pipelineComposite.writeRef()); components.data(), static_cast<u32>(components.size()), pipelineComposite.writeRef());
if (slangResult < 0) if (slangResult < 0)
{ {
@ -752,7 +880,7 @@ systems::Device::CreateShaders(
.pCode = static_cast<const u32 *>(kernelCode->getBufferPointer()), .pCode = static_cast<const u32 *>(kernelCode->getBufferPointer()),
}; };
result = m_Device.m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &outShader.module); result = m_Device->createShaderModule(&shaderModuleCreateInfo, nullptr, &outShader.module);
if (Failed(result)) if (Failed(result))
{ {
ERROR("Shaders could not be created. Cause: {}", result); ERROR("Shaders could not be created. Cause: {}", result);
@ -766,7 +894,7 @@ systems::Device::CreateShaders(
{ {
if (shader.module) if (shader.module)
{ {
m_Device.m_Device.destroy(shader.module, nullptr); m_Device->destroy(shader.module, nullptr);
} }
} }
return result; return result;
@ -797,7 +925,7 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout,
.pushConstantRangeCount = 0, .pushConstantRangeCount = 0,
.pPushConstantRanges = nullptr, .pPushConstantRanges = nullptr,
}; };
vk::Result result = m_Device.m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout); vk::Result result = m_Device->createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout);
if (Failed(result)) if (Failed(result))
{ {
ERROR("Could not create a pipeline layout. Cause: {}", result); ERROR("Could not create a pipeline layout. Cause: {}", result);
@ -818,7 +946,7 @@ FindAppropriateQueueAllocation(const PhysicalDevice &physicalDevice)
{ {
return { return {
.m_Family = queueFamilyInfo.m_Index, .m_Family = queueFamilyInfo.m_Index,
.m_Count = queueFamilyInfo.m_Count, .m_Count = 1,
}; };
} }
} }
@ -826,8 +954,49 @@ FindAppropriateQueueAllocation(const PhysicalDevice &physicalDevice)
THEN_ABORT(vk::Result::eErrorUnknown); THEN_ABORT(vk::Result::eErrorUnknown);
} }
std::optional<QueueAllocation>
FindAsyncTransferQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFamilyIndex)
{
for (auto &queueFamilyInfo : physicalDevice.m_QueueFamilies)
{
if (queueFamilyInfo.m_Index == primaryQueueFamilyIndex)
continue;
if (queueFamilyInfo.m_Support == QueueSupportFlagBits::eTransfer)
{
return QueueAllocation{
.m_Family = queueFamilyInfo.m_Index,
.m_Count = 1,
};
}
}
WARN("No async transfer queue. Falling back to primary queue");
return std::nullopt;
}
std::optional<QueueAllocation>
FindAsyncComputeQueue(const PhysicalDevice &physicalDevice, u32 primaryQueueFamilyIndex)
{
for (auto &queueFamilyInfo : physicalDevice.m_QueueFamilies)
{
if (queueFamilyInfo.m_Index == primaryQueueFamilyIndex)
continue;
if (queueFamilyInfo.m_Support == QueueSupportFlagBits::eCompute)
{
return QueueAllocation{
.m_Family = queueFamilyInfo.m_Index,
.m_Count = 1,
};
}
}
WARN("No async compute queue. Falling back to primary queue");
return std::nullopt;
}
systems::Device::Device(const DeviceCreateInfo &createInfo) systems::Device::Device(const DeviceCreateInfo &createInfo)
: m_Window{createInfo.m_Window} : m_Window{createInfo.m_Window}
, m_SyncServer{new _internal::SyncServer{*this}}
{ {
assert(createInfo.m_AppName); assert(createInfo.m_AppName);
assert(createInfo.m_PhysicalDeviceSelector); assert(createInfo.m_PhysicalDeviceSelector);
@ -841,16 +1010,45 @@ systems::Device::Device(const DeviceCreateInfo &createInfo)
Features features = createInfo.m_Features; Features features = createInfo.m_Features;
// TODO: Add the other queues // TODO: Add the other queues
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(physicalDevice); eastl::fixed_vector<QueueAllocation, 4, false> queueAllocations;
m_Device = ::Device{ queueAllocations.push_back(FindAppropriateQueueAllocation(physicalDevice));
m_Instance, physicalDevice, features, {&queueAllocation, 1}, createInfo.m_PipelineCacheData, createInfo.m_Name}; auto &primaryQueue = queueAllocations.back();
u32 primaryQueueIndex = 0;
m_PrimaryQueueFamily = primaryQueue.m_Family;
m_GraphicsQueueFamily = queueAllocation.m_Family; u32 transferQueueIndex;
m_GraphicsQueue = m_Device.GetQueue(m_GraphicsQueueFamily, 0); if (const auto asyncTransfer = FindAsyncTransferQueue(physicalDevice, m_PrimaryQueueFamily))
{
const QueueAllocation allocation = asyncTransfer.value();
queueAllocations.push_back(allocation);
m_TransferQueueFamily = allocation.m_Family;
transferQueueIndex = 0;
}
else
{
transferQueueIndex = primaryQueue.m_Count;
++primaryQueue.m_Count;
m_TransferQueueFamily = m_PrimaryQueueFamily;
}
// TODO: Async Compute
m_Device = ::Device{m_Instance, physicalDevice, features, queueAllocations, createInfo.m_PipelineCacheData,
createInfo.m_Name};
m_GraphicsQueue = m_Device.GetQueue(m_PrimaryQueueFamily, primaryQueueIndex);
m_TransferQueue = m_Device.GetQueue(m_TransferQueueFamily, transferQueueIndex);
m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()}; m_Swapchain = Swapchain{m_Surface, m_Device, m_Window.get().GetSize()};
const vk::CommandPoolCreateInfo transferPoolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer,
.queueFamilyIndex = m_TransferQueueFamily,
};
AbortIfFailed(m_Device->createCommandPool(&transferPoolCreateInfo, nullptr, &m_TransferPool));
constexpr SlangGlobalSessionDesc globalSessionDesc = {}; constexpr SlangGlobalSessionDesc globalSessionDesc = {};
auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef()); auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef());
ERROR_IF(result < 0, "Could not create a slang global session.") THEN_ABORT(result); ERROR_IF(result < 0, "Could not create a slang global session.") THEN_ABORT(result);
@ -885,11 +1083,13 @@ systems::Device::~Device()
{ {
for (auto &frame : m_Frames) for (auto &frame : m_Frames)
{ {
m_Device.m_Device.destroy(Take(frame.m_FrameAvailableFence), nullptr); m_Device->destroy(Take(frame.m_FrameAvailableFence), nullptr);
m_Device.m_Device.destroy(Take(frame.m_ImageAcquireSem), nullptr); m_Device->destroy(Take(frame.m_ImageAcquireSem), nullptr);
m_Device.m_Device.destroy(Take(frame.m_RenderFinishSem), nullptr); m_Device->destroy(Take(frame.m_RenderFinishSem), nullptr);
m_Device.m_Device.destroy(Take(frame.m_Pool), nullptr); m_Device->destroy(Take(frame.m_Pool), nullptr);
} }
m_Device->destroy(Take(m_TransferPool), nullptr);
m_TransferContexts.clear();
m_HashToSamplerIdx.clear(); m_HashToSamplerIdx.clear();
} }
@ -910,19 +1110,19 @@ systems::Device::CreateFrame(u32 frameIndex)
const vk::CommandPoolCreateInfo commandPoolCreateInfo = { const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient, .flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = m_GraphicsQueueFamily, .queueFamilyIndex = m_PrimaryQueueFamily,
}; };
AbortIfFailedMV(m_Device.m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &pool), AbortIfFailedMV(m_Device->createCommandPool(&commandPoolCreateInfo, nullptr, &pool),
"Could not command pool for frame {}", frameIndex); "Could not command pool for frame {}", frameIndex);
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled}; constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
AbortIfFailedMV(m_Device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence), AbortIfFailedMV(m_Device->createFence(&fenceCreateInfo, nullptr, &fence),
"Could not create a fence for frame {}", frameIndex); "Could not create a fence for frame {}", frameIndex);
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {}; constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &acquire), AbortIfFailedMV(m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &acquire),
"Could not create IA semaphore for frame {}.", frameIndex); "Could not create IA semaphore for frame {}.", frameIndex);
AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &finished), AbortIfFailedMV(m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &finished),
"Could not create RF semaphore for frame {}.", frameIndex); "Could not create RF semaphore for frame {}.", frameIndex);
m_Device.SetName(pool, name.c_str()); m_Device.SetName(pool, name.c_str());
@ -933,7 +1133,7 @@ systems::Device::CreateFrame(u32 frameIndex)
DEBUG("Frame {} created successfully.", frameIndex); DEBUG("Frame {} created successfully.", frameIndex);
return Frame{ return Frame{
.m_Device = &m_Device, .m_Device = this,
.m_Pool = pool, .m_Pool = pool,
.m_FrameAvailableFence = fence, .m_FrameAvailableFence = fence,
.m_ImageAcquireSem = acquire, .m_ImageAcquireSem = acquire,
@ -956,7 +1156,7 @@ systems::Device::GetNextFrame()
bool imageAcquired = false; bool imageAcquired = false;
while (!imageAcquired) while (!imageAcquired)
{ {
switch (auto result = m_Device.m_Device.acquireNextImageKHR( switch (auto result = m_Device->acquireNextImageKHR(
m_Swapchain.m_Swapchain, MaxValue<u64>, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex)) m_Swapchain.m_Swapchain, MaxValue<u64>, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex))
{ {
case vk::Result::eSuccess: case vk::Result::eSuccess:
@ -1019,12 +1219,76 @@ systems::Device::Present(Frame &frame, GraphicsContext &graphicsContext)
} }
} }
systems::TransferContext &
systems::Device::CreateTransferContext()
{
if (!m_TransferContextFreeList.empty())
{
u32 freeIndex = m_TransferContextFreeList.back();
m_TransferContextFreeList.pop_back();
TransferContext &context = m_TransferContexts[freeIndex];
return context;
}
vk::CommandBuffer cmd;
vk::CommandBufferAllocateInfo allocateInfo = {
.commandPool = m_TransferPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailed(m_Device->allocateCommandBuffers(&allocateInfo, &cmd));
m_TransferContexts.push_back(TransferContext{*this, cmd});
return m_TransferContexts.back();
}
systems::Receipt
systems::Device::Submit(Context &context)
{
const auto rect = m_SyncServer->Allocate();
auto entry = m_SyncServer->GetEntry(rect);
auto [wait, next] = entry.m_CurrentPoint;
vk::TimelineSemaphoreSubmitInfo timelineSubmit = {
.waitSemaphoreValueCount = 1,
.pWaitSemaphoreValues = &wait,
.signalSemaphoreValueCount = 1,
.pSignalSemaphoreValues = &next,
};
// TODO: Separate per context
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eAllCommands;
const vk::SubmitInfo submitInfo = {
.pNext = &timelineSubmit,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &entry.m_Semaphore,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &context.m_Cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &entry.m_Semaphore,
};
vk::Result result = m_GraphicsQueue.submit(1, &submitInfo, {});
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
THEN_ABORT(result);
return rect;
}
void
systems::Device::WaitOn(Receipt recpt)
{
m_SyncServer->WaitOn(recpt);
}
void void
systems::Frame::Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize) systems::Frame::Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize)
{ {
AbortIfFailedMV(m_Device->m_Device.resetFences(1, &m_FrameAvailableFence), "Fence {} reset failed.", m_FrameIdx); AbortIfFailedMV(m_Device->m_Device->resetFences(1, &m_FrameAvailableFence), "Fence {} reset failed.", m_FrameIdx);
AbortIfFailedMV(m_Device->m_Device.resetCommandPool(m_Pool, {}), "Command pool {} reset failed.", m_FrameIdx); AbortIfFailedMV(m_Device->m_Device->resetCommandPool(m_Pool, {}), "Command pool {} reset failed.", m_FrameIdx);
m_CommandBuffersAllocated = 0; m_CommandBuffersAllocated = 0;
m_ImageIdx = imageIdx; m_ImageIdx = imageIdx;
@ -1048,7 +1312,7 @@ systems::Frame::CreateGraphicsContext()
.level = vk::CommandBufferLevel::ePrimary, .level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1, .commandBufferCount = 1,
}; };
AbortIfFailedMV(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &cmd), AbortIfFailedMV(m_Device->m_Device->allocateCommandBuffers(&allocateInfo, &cmd),
"Command buffer {} alloc failed.", m_FrameIdx); "Command buffer {} alloc failed.", m_FrameIdx);
m_CommandBuffers.push_back(cmd); m_CommandBuffers.push_back(cmd);
} }
@ -1056,10 +1320,33 @@ systems::Frame::CreateGraphicsContext()
return GraphicsContext{cmd}; return GraphicsContext{cmd};
} }
systems::TransferContext
systems::Frame::CreateTransferContext()
{
vk::CommandBuffer cmd;
if (m_CommandBuffers.size() > m_CommandBuffersAllocated)
{
cmd = m_CommandBuffers[m_CommandBuffersAllocated++];
}
else
{
const vk::CommandBufferAllocateInfo allocateInfo{
.commandPool = m_Pool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailedMV(m_Device->m_Device->allocateCommandBuffers(&allocateInfo, &cmd),
"Command buffer {} alloc failed.", m_FrameIdx);
m_CommandBuffers.push_back(cmd);
}
return TransferContext{*m_Device, cmd};
}
void void
systems::Frame::WaitUntilReady() systems::Frame::WaitUntilReady()
{ {
AbortIfFailedMV(m_Device->m_Device.waitForFences(1, &m_FrameAvailableFence, true, MaxValue<u64>), AbortIfFailedMV(m_Device->m_Device->waitForFences(1, &m_FrameAvailableFence, true, MaxValue<u64>),
"Waiting for fence {} failed.", m_FrameIdx); "Waiting for fence {} failed.", m_FrameIdx);
} }
@ -1069,6 +1356,12 @@ systems::Frame::WaitUntilReady()
#pragma region Context Impl #pragma region Context Impl
// ==================================================================================================== // ====================================================================================================
void
systems::Context::Dependency(const vk::DependencyInfo &dependencyInfo)
{
m_Cmd.pipelineBarrier2(&dependencyInfo);
}
void void
systems::Context::Begin() systems::Context::Begin()
{ {
@ -1104,6 +1397,7 @@ void
systems::GraphicsContext::BindPipeline(const Pipeline &pipeline) systems::GraphicsContext::BindPipeline(const Pipeline &pipeline)
{ {
m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
m_PipelineInUse = &pipeline;
} }
void void
@ -1118,12 +1412,6 @@ systems::GraphicsContext::DrawIndexed(u32 indexCount)
m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0); m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0);
} }
void
systems::GraphicsContext::Dependency(const vk::DependencyInfo &dependencyInfo)
{
m_Cmd.pipelineBarrier2(&dependencyInfo);
}
void void
systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo) systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo)
{ {
@ -1137,4 +1425,67 @@ systems::GraphicsContext::EndRendering()
m_Cmd.endRendering(); m_Cmd.endRendering();
} }
void
systems::TransferContext::Reset()
{
m_OwnedImages.clear();
m_OwnedBuffers.clear();
AbortIfFailed(m_Cmd.reset({}));
}
void
systems::TransferContext::UploadTexture(const Ref<Image> &image, const ImageData &data)
{
ERROR_IF(not(image and image->IsValid()), "Invalid image");
auto [w, h, d] = image->m_Extent;
auto formatSize = GetFormatSize(image->m_Format);
auto expectedByteSize = static_cast<u64>(w) * static_cast<u64>(h) * static_cast<u64>(d) * formatSize;
ERROR_IF(expectedByteSize != data.m_NumBytes, "Mismatch in data size {} vs image size {} ({}x{}x{}x{})",
data.m_NumBytes, expectedByteSize, w, h, d, formatSize);
const Ref<StagingBuffer> stagingBuffer = m_Device->CreateStagingBuffer(data.m_NumBytes);
stagingBuffer->Write(0, data.m_NumBytes, data.m_Data);
const vk::BufferImageCopy bufferImageCopy = {
.bufferOffset = 0,
.bufferRowLength = w,
.bufferImageHeight = h,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {},
.imageExtent = image->m_Extent,
};
m_Cmd.copyBufferToImage(stagingBuffer->m_Buffer, image->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
&bufferImageCopy);
m_OwnedBuffers.push_back(stagingBuffer);
m_OwnedImages.push_back(image);
}
systems::TransferContext::TransferContext(TransferContext &&other) noexcept
: Context{other.m_Cmd}
, m_Device{Take(other.m_Device)}
, m_OwnedBuffers{std::move(other.m_OwnedBuffers)}
, m_OwnedImages{std::move(other.m_OwnedImages)}
{
}
systems::TransferContext &
systems::TransferContext::operator=(TransferContext &&other) noexcept
{
if (this == &other)
return *this;
m_Cmd = other.m_Cmd;
m_Device = Take(other.m_Device);
m_OwnedBuffers = std::move(other.m_OwnedBuffers);
m_OwnedImages = std::move(other.m_OwnedImages);
return *this;
}
#pragma endregion #pragma endregion

View File

@ -0,0 +1,144 @@
// =============================================
// Aster: sync_server.cpp
// Copyright (c) 2020-2025 Anish Bhobe
// =============================================
#include "aster/systems/sync_server.h"
#include "aster/systems/device.h"
using namespace systems::_internal;
SyncServer::Entry
SyncServer::Entry::Create(Device &device)
{
constexpr static vk::SemaphoreTypeCreateInfo TYPE_CREATE_INFO = {
.semaphoreType = vk::SemaphoreType::eTimeline,
.initialValue = 0,
};
constexpr static vk::SemaphoreCreateInfo SEMAPHORE_CREATE_INFO = {.pNext = &TYPE_CREATE_INFO};
vk::Semaphore semaphore;
AbortIfFailed(device.m_Device->createSemaphore(&SEMAPHORE_CREATE_INFO, nullptr, &semaphore));
return {
.m_Semaphore = semaphore,
.m_CurrentPoint = {.m_WaitValue = 0, .m_NextValue = 1},
};
}
void
SyncServer::Entry::Destroy(Device &device)
{
if (m_Semaphore)
{
device.m_Device->destroy(Take(m_Semaphore), nullptr);
}
}
void
SyncServer::Entry::Wait(Device &device)
{
const vk::SemaphoreWaitInfo waitInfo = {
.semaphoreCount = 1,
.pSemaphores = &m_Semaphore,
.pValues = &m_CurrentPoint.m_NextValue,
};
// This blocks.
// So `m_NextValue` is not modified while we wait for the signal.
AbortIfFailed(device.m_Device->waitSemaphores(&waitInfo, MaxValue<u64>));
// Thus, this is safe.
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue;
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue + 1;
}
void
SyncServer::Entry::Next()
{
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue;
++m_CurrentPoint.m_NextValue;
}
systems::Receipt
SyncServer::Allocate()
{
auto &entry = AllocateEntry();
return Receipt{&entry};
}
void
SyncServer::Free(const Receipt receipt)
{
FreeEntry(GetEntry(receipt));
}
void
SyncServer::WaitOn(const Receipt receipt)
{
auto &entry = GetEntry(receipt);
entry.Wait(*m_Device);
FreeEntry(entry);
}
SyncServer::Entry &
SyncServer::AllocateEntry()
{
if (not m_FreeList.empty())
{
auto &alloc = m_FreeList.back();
m_FreeList.pop_back();
return alloc;
}
m_Allocations.push_back(Entry::Create(*m_Device));
return m_Allocations.back();
}
void
SyncServer::FreeEntry(Entry &entry)
{
entry.Next();
m_FreeList.push_back(entry);
}
SyncServer::Entry &
SyncServer::GetEntry(Receipt receipt)
{
return *static_cast<Entry *>(receipt.m_Opaque);
}
SyncServer::SyncServer(Device &device)
: m_Device{&device}
{
}
SyncServer::~SyncServer()
{
if (m_Device && !m_Allocations.empty())
{
for (auto &entry : m_Allocations)
{
entry.Destroy(*m_Device);
}
m_Device = nullptr;
}
}
SyncServer::SyncServer(SyncServer &&other) noexcept
: m_Device{Take(other.m_Device)}
, m_Allocations{std::move(other.m_Allocations)}
, m_FreeList{Take(other.m_FreeList)}
{
}
SyncServer &
SyncServer::operator=(SyncServer &&other) noexcept
{
if (this == &other)
return *this;
m_Device = Take(other.m_Device);
m_Allocations = std::move(other.m_Allocations);
m_FreeList = Take(other.m_FreeList);
return *this;
}

View File

@ -9,6 +9,7 @@ add_shader(box "shader/box.vert.glsl")
add_shader(box "shader/box.frag.glsl") add_shader(box "shader/box.frag.glsl")
add_shader(box "shader/box.vs.hlsl") add_shader(box "shader/box.vs.hlsl")
add_shader(box "shader/box.ps.hlsl") add_shader(box "shader/box.ps.hlsl")
add_resource_dir(box "shader/")
target_link_libraries(box PRIVATE aster_core) target_link_libraries(box PRIVATE aster_core)
target_link_libraries(box PRIVATE util_helper) target_link_libraries(box PRIVATE util_helper)

View File

@ -7,30 +7,23 @@
#include "aster/core/buffer.h" #include "aster/core/buffer.h"
#include "aster/core/constants.h" #include "aster/core/constants.h"
#include "aster/core/instance.h"
#include "aster/core/device.h"
#include "aster/core/image.h" #include "aster/core/image.h"
#include "aster/core/physical_device.h" #include "aster/core/physical_device.h"
#include "aster/core/pipeline.h" #include "aster/core/pipeline.h"
#include "aster/core/swapchain.h" #include "aster/core/swapchain.h"
#include "aster/core/window.h" #include "aster/core/window.h"
#include "helpers.h"
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#include "aster/systems/buffer_manager.h"
#include "aster/systems/commit_manager.h" #include "aster/systems/commit_manager.h"
#include "aster/systems/image_manager.h" #include "aster/systems/device.h"
#include "aster/systems/resource_manager.h" #include "aster/util/files.h"
#include "aster/systems/view_manager.h"
#include "frame.h"
#include "stb_image.h" #include "stb_image.h"
#include <EASTL/array.h> #include <EASTL/array.h>
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv"; constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv";
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv";
constexpr auto SHADER_FILE = "triangle";
struct ImageFile struct ImageFile
{ {
@ -75,9 +68,6 @@ ImageFile::~ImageFile()
m_Data = nullptr; m_Data = nullptr;
} }
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
Pipeline CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain);
struct Vertex struct Vertex
{ {
vec3 m_Position; vec3 m_Position;
@ -99,13 +89,6 @@ main(int, char **)
MIN_LOG_LEVEL(Logger::LogType::eInfo); MIN_LOG_LEVEL(Logger::LogType::eInfo);
Window window = {"Box (Aster)", {640, 480}}; Window window = {"Box (Aster)", {640, 480}};
Instance context = {"Box", VERSION};
Surface surface = {&context, &window, "Primary"};
PhysicalDevices physicalDevices = {&surface, &context};
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
Features enabledDeviceFeatures = { Features enabledDeviceFeatures = {
.m_Vulkan10Features = {.samplerAnisotropy = true}, .m_Vulkan10Features = {.samplerAnisotropy = true},
@ -121,47 +104,40 @@ main(int, char **)
.descriptorBindingStorageBufferUpdateAfterBind = true, .descriptorBindingStorageBufferUpdateAfterBind = true,
.descriptorBindingPartiallyBound = true, .descriptorBindingPartiallyBound = true,
.runtimeDescriptorArray = true, .runtimeDescriptorArray = true,
.timelineSemaphore = true,
.bufferDeviceAddress = true, .bufferDeviceAddress = true,
.bufferDeviceAddressCaptureReplay = true, .bufferDeviceAddressCaptureReplay = true,
}, },
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}, .m_Vulkan13Features = {.synchronization2 = true, .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 = {&surface, &device, window.GetSize(), "Primary Chain"};
systems::ResourceManager resourceManager{&device}; systems::Device device{{
.m_Window = window,
.m_Features = enabledDeviceFeatures,
.m_AppName = "Box",
.m_AppVersion = VERSION,
.m_ShaderSearchPaths = {"shader/"},
}};
systems::CommitManager commitManager{&device, 12, 12, 12, resourceManager.Samplers().CreateSampler({})}; // TODO: Device in CommitManager.
systems::CommitManager commitManager{&device.m_Device, 12, 12, 12, device.CreateSampler({})};
Pipeline pipeline = CreatePipeline(&commitManager, &swapchain); Pipeline pipeline;
auto pipelineResult =
device.CreatePipeline(pipeline, {.m_Shaders = {
{.m_ShaderFile = SHADER_FILE, .m_EntryPoints = {"vsmain", "fsmain"}},
}});
ERROR_IF(pipelineResult, "Could not create pipeline. Cause: {}", pipelineResult.What())
THEN_ABORT(pipelineResult.uNone);
auto swapchainSize = device.GetSwapchainSize();
Camera camera = { Camera camera = {
.m_Model = {1.0f}, .m_Model = {1.0f},
.m_View = lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)), .m_View = lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)),
.m_Perspective = glm::perspective( .m_Perspective = glm::perspective(
70_deg, static_cast<f32>(swapchain.m_Extent.width) / static_cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f), 70_deg, static_cast<f32>(swapchainSize.m_Width) / static_cast<f32>(swapchainSize.m_Height), 0.1f, 100.0f),
}; };
vk::CommandPool copyPool;
vk::CommandBuffer copyBuffer;
{
vk::CommandPoolCreateInfo poolCreateInfo = {
.flags = vk::CommandPoolCreateFlagBits::eTransient,
.queueFamilyIndex = queueAllocation.m_Family,
};
AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, &copyPool),
"Copy command pool creation failed.");
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
.commandPool = copyPool,
.level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, &copyBuffer),
"Copy command buffer allocation failed.");
}
eastl::array vertices = { 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, 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(1.0f, 0.0f)},
@ -211,10 +187,10 @@ main(int, char **)
assert(loaded); assert(loaded);
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels); INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
auto vbo = resourceManager.Buffers().CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer"); auto vbo = device.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer");
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data()); vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
auto crate = resourceManager.CombinedImageViews().CreateTexture2D({ auto crate = device.CreateTexture2DWithView({
.m_Format = vk::Format::eR8G8B8A8Srgb, .m_Format = vk::Format::eR8G8B8A8Srgb,
.m_Extent = {imageFile.m_Width, imageFile.m_Height}, .m_Extent = {imageFile.m_Width, imageFile.m_Height},
.m_Name = "Crate Texture", .m_Name = "Crate Texture",
@ -222,7 +198,7 @@ main(int, char **)
{ {
auto imageStaging = resourceManager.Buffers().CreateStagingBuffer(imageFile.GetSize(), "Image Staging"); auto imageStaging = device.CreateStagingBuffer(imageFile.GetSize(), "Image Staging");
imageStaging->Write(0, imageFile.GetSize(), imageFile.m_Data); imageStaging->Write(0, imageFile.GetSize(), imageFile.m_Data);
vk::ImageMemoryBarrier2 imageReadyToWrite = { vk::ImageMemoryBarrier2 imageReadyToWrite = {
@ -232,8 +208,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite, .dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.oldLayout = vk::ImageLayout::eUndefined, .oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = crate->GetImage(), .image = crate->GetImage(),
.subresourceRange = .subresourceRange =
{ {
@ -256,8 +232,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eShaderRead, .dstAccessMask = vk::AccessFlagBits2::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferDstOptimal, .oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.image = crate->GetImage(), .image = crate->GetImage(),
.subresourceRange = .subresourceRange =
{ {
@ -273,77 +249,25 @@ main(int, char **)
.pImageMemoryBarriers = &imageReadyToRead, .pImageMemoryBarriers = &imageReadyToRead,
}; };
vk::Fence fence; auto &context = device.CreateTransferContext();
vk::FenceCreateInfo fenceCreateInfo = {}; context.Begin();
AbortIfFailed(device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; context.Dependency(imageReadyToWriteDependency);
AbortIfFailed(copyBuffer.begin(&beginInfo));
copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency); context.UploadTexture(crate->m_Image, {.m_Data = imageFile.m_Data, .m_NumBytes = imageFile.GetSize()});
vk::BufferImageCopy imageCopy = { context.Dependency(imageReadyToReadDependency);
.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->GetImage(), vk::ImageLayout::eTransferDstOptimal, 1,
&imageCopy);
copyBuffer.pipelineBarrier2(&imageReadyToReadDependency); context.End();
AbortIfFailed(copyBuffer.end()); auto recpt = device.Submit(context);
device.WaitOn(recpt);
vk::SubmitInfo submitInfo = {
.commandBufferCount = 1,
.pCommandBuffers = &copyBuffer,
};
AbortIfFailed(commandQueue.submit(1, &submitInfo, fence));
INFO("Submit copy");
AbortIfFailed(device.m_Device.waitForFences(1, &fence, true, MaxValue<u64>));
INFO("Fence wait");
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
device.m_Device.destroy(fence, nullptr);
} }
auto ubo = resourceManager.Buffers().CreateStorageBuffer(sizeof camera, "Camera UBO"); auto ubo = device.CreateStorageBuffer(sizeof camera, "Camera UBO");
ubo->Write(0, sizeof camera, &camera); ubo->Write(0, sizeof camera, &camera);
// Persistent variables // Persistent variables
vk::Viewport viewport = {
.x = 0,
.y = static_cast<f32>(swapchain.m_Extent.height),
.width = static_cast<f32>(swapchain.m_Extent.width),
.height = -static_cast<f32>(swapchain.m_Extent.height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
vk::Rect2D scissor = {
.offset = {0, 0},
.extent = swapchain.m_Extent,
};
auto resizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
viewport.y = static_cast<f32>(extent.height);
viewport.width = static_cast<f32>(extent.width);
viewport.height = -static_cast<f32>(extent.height);
scissor.extent = extent;
};
swapchain.RegisterResizeCallback(resizeViewportScissor);
vk::ImageSubresourceRange subresourceRange = { vk::ImageSubresourceRange subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
@ -361,8 +285,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined, .oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.subresourceRange = subresourceRange, .subresourceRange = subresourceRange,
}; };
vk::DependencyInfo topOfThePipeDependency = { vk::DependencyInfo topOfThePipeDependency = {
@ -376,8 +300,8 @@ main(int, char **)
.dstAccessMask = vk::AccessFlagBits2::eNone, .dstAccessMask = vk::AccessFlagBits2::eNone,
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
.newLayout = vk::ImageLayout::ePresentSrcKHR, .newLayout = vk::ImageLayout::ePresentSrcKHR,
.srcQueueFamilyIndex = queueAllocation.m_Family, .srcQueueFamilyIndex = vk::QueueFamilyIgnored,
.dstQueueFamilyIndex = queueAllocation.m_Family, .dstQueueFamilyIndex = vk::QueueFamilyIgnored,
.subresourceRange = subresourceRange, .subresourceRange = subresourceRange,
}; };
vk::DependencyInfo renderToPresentDependency = { vk::DependencyInfo renderToPresentDependency = {
@ -385,24 +309,21 @@ main(int, char **)
.pImageMemoryBarriers = &renderToPresentBarrier, .pImageMemoryBarriers = &renderToPresentBarrier,
}; };
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
eastl::fixed_vector<Ref<ImageView>, MAX_FRAMES_IN_FLIGHT> depthImages; eastl::fixed_vector<Ref<ImageView>, MAX_FRAMES_IN_FLIGHT> depthImages;
auto initDepthImages = [&depthImages, &frameManager, &resourceManager](const vk::Extent2D extent) { auto initDepthImages = [&depthImages, &device](const vk::Extent2D extent) {
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
{ {
depthImages.push_back( depthImages.push_back(device.CreateDepthStencilImageWithView({.m_Extent = extent, .m_Name = "Depth"}));
resourceManager.CombinedImageViews().CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}));
} }
}; };
initDepthImages(swapchain.m_Extent); initDepthImages(swapchainSize);
auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) { auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) {
depthImages.clear(); depthImages.clear();
initDepthImages(extent); initDepthImages(extent);
}; };
swapchain.RegisterResizeCallback(recreateDepthBuffers);
struct PCB struct PCB
{ {
@ -419,6 +340,8 @@ main(int, char **)
Time::Init(); Time::Init();
auto prevSwapchainSize = swapchainSize;
INFO("Starting loop"); INFO("Starting loop");
while (window.Poll()) while (window.Poll())
{ {
@ -428,21 +351,41 @@ main(int, char **)
camera.m_Model *= rotate(mat4{1.0f}, static_cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); camera.m_Model *= rotate(mat4{1.0f}, static_cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
ubo->Write(0, sizeof camera, &camera); ubo->Write(0, sizeof camera, &camera);
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); auto currentFrame = device.GetNextFrame();
u32 imageIndex = currentFrame->m_ImageIdx; prevSwapchainSize = swapchainSize;
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; swapchainSize = currentFrame.m_SwapchainSize;
vk::Image currentImage = swapchain.m_Images[imageIndex]; if (swapchainSize != prevSwapchainSize)
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; {
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View; recreateDepthBuffers(swapchainSize);
}
vk::Viewport viewport = {
.x = 0,
.y = static_cast<f32>(swapchainSize.m_Height),
.width = static_cast<f32>(swapchainSize.m_Width),
.height = -static_cast<f32>(swapchainSize.m_Height),
.minDepth = 0.0,
.maxDepth = 1.0,
};
vk::Rect2D scissor = {
.offset = {0, 0},
.extent = static_cast<vk::Extent2D>(swapchainSize),
};
vk::ImageView currentImageView = currentFrame.m_SwapchainImageView;
vk::Image currentImage = currentFrame.m_SwapchainImage;
vk::ImageView currentDepthImageView = depthImages[currentFrame.m_FrameIdx]->m_View;
topOfThePipeBarrier.image = currentImage; topOfThePipeBarrier.image = currentImage;
renderToPresentBarrier.image = currentImage; renderToPresentBarrier.image = currentImage;
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; auto context = currentFrame.CreateGraphicsContext();
AbortIfFailed(cmd.begin(&beginInfo));
cmd.pipelineBarrier2(&topOfThePipeDependency); context.Begin();
context.Dependency(topOfThePipeDependency);
// Render // Render
eastl::array attachmentInfos = { eastl::array attachmentInfos = {
@ -466,187 +409,32 @@ main(int, char **)
}; };
vk::RenderingInfo renderingInfo = { vk::RenderingInfo renderingInfo = {
.renderArea = {.extent = swapchain.m_Extent}, .renderArea = scissor,
.layerCount = 1, .layerCount = 1,
.colorAttachmentCount = static_cast<u32>(attachmentInfos.size()), .colorAttachmentCount = static_cast<u32>(attachmentInfos.size()),
.pColorAttachments = attachmentInfos.data(), .pColorAttachments = attachmentInfos.data(),
.pDepthAttachment = &depthAttachment, .pDepthAttachment = &depthAttachment,
}; };
cmd.beginRendering(&renderingInfo); context.BeginRendering(renderingInfo);
cmd.setViewport(0, 1, &viewport); context.SetViewport(viewport);
cmd.setScissor(0, 1, &scissor); context.BindPipeline(pipeline);
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); /*cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &commitManager.GetDescriptorSet(), 0, nullptr);*/
&commitManager.GetDescriptorSet(), 0, nullptr); //context.PushConstantBlock(pcb);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb); context.Draw(3);
cmd.draw(static_cast<u32>(vertices.size()), 1, 0, 0);
cmd.endRendering(); context.EndRendering();
cmd.pipelineBarrier2(&renderToPresentDependency); context.Dependency(renderToPresentDependency);
AbortIfFailed(cmd.end()); context.End();
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; device.Present(currentFrame, context);
vk::SubmitInfo submitInfo = {
.waitSemaphoreCount = 1,
.pWaitSemaphores = &currentFrame->m_ImageAcquireSem,
.pWaitDstStageMask = &waitDstStage,
.commandBufferCount = 1,
.pCommandBuffers = &cmd,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &currentFrame->m_RenderFinishSem,
};
AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
currentFrame->Present(commandQueue, &swapchain, &surface, window.GetSize());
} }
device.WaitIdle(); device.WaitIdle();
device.m_Device.destroy(copyPool, nullptr);
return 0; return 0;
} }
Pipeline
CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain)
{
// Pipeline Setup
auto *device = resourceManager->m_Device;
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",
},
}};
auto descriptorSetLayout = resourceManager->GetDescriptorSetLayout();
vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eAllGraphics,
.offset = 0,
.size = 12,
};
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
.setLayoutCount = 1,
.pSetLayouts = &descriptorSetLayout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pcr,
};
vk::PipelineLayout pipelineLayout;
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
device->SetName(pipelineLayout, "Box Layout");
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 = static_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 = static_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, {}};
}
vk::ShaderModule
CreateShader(const Device *device, cstr shaderFile)
{
eastl::vector<u32> shaderCode = ReadFile(shaderFile);
const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
.codeSize = shaderCode.size() * sizeof(u32),
.pCode = shaderCode.data(),
};
vk::ShaderModule shaderModule;
AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule),
"Shader {} could not be created.", shaderFile);
return shaderModule;
}

View File

@ -0,0 +1,46 @@
struct Vertex {
float3 point;
float3 color;
};
struct VSIn {
uint VertexIndex : SV_VertexID;
};
struct VSOut
{
float4 Pos : SV_POSITION;
float3 Color : COLOR0;
};
static const float3 points[] = {
float3(-0.5f, -0.5f, 0.5f),
float3(0.5f, -0.5f, 0.5f),
float3(0.0f, 0.5f, 0.5f),
};
static const float3 colors[] = {
float3(1.0f, 0.0f, 0.0f),
float3(0.0f, 1.0f, 0.0f),
float3(0.0f, 0.0f, 1.0f),
};
[shader("vertex")]
VSOut vsmain(VSIn input) {
VSOut output;
output.Pos = float4(points[input.VertexIndex], 1.0f);
output.Color = colors[input.VertexIndex];
return output;
}
struct FSOut {
float4 Color;
};
[shader("fragment")]
FSOut fsmain(VSOut input) {
FSOut outp;
outp.Color = float4(input.Color, 1.0);
return outp;
}

View File

@ -4,6 +4,6 @@ cmake_minimum_required(VERSION 3.13)
add_subdirectory("00_util") add_subdirectory("00_util")
add_subdirectory("01_triangle") add_subdirectory("01_triangle")
# add_subdirectory("02_box") add_subdirectory("02_box")
# add_subdirectory("03_model_render") # add_subdirectory("03_model_render")
# add_subdirectory("04_scenes") # add_subdirectory("04_scenes")