diff --git a/aster/include/aster/core/device.h b/aster/include/aster/core/device.h index 5f18d51..8f45fb6 100644 --- a/aster/include/aster/core/device.h +++ b/aster/include/aster/core/device.h @@ -39,6 +39,18 @@ struct Device final void WaitIdle() const; + vk::Device * + operator->() + { + return &m_Device; + } + + const vk::Device * + operator->() const + { + return &m_Device; + } + // Ctor/Dtor Device() = default; Device(const Instance &context, PhysicalDevice &physicalDevice, Features &enabledFeatures, diff --git a/aster/include/aster/core/size.h b/aster/include/aster/core/size.h index f22ee8a..8188b17 100644 --- a/aster/include/aster/core/size.h +++ b/aster/include/aster/core/size.h @@ -34,6 +34,8 @@ struct Size2D return *this; } + bool operator==(const Size2D&) const = default; + operator vk::Extent2D() const { return {m_Width, m_Height}; diff --git a/aster/include/aster/systems/device.h b/aster/include/aster/systems/device.h index b12a190..874af27 100644 --- a/aster/include/aster/systems/device.h +++ b/aster/include/aster/systems/device.h @@ -3,6 +3,8 @@ // Copyright (c) 2020-2025 Anish Bhobe // ============================================= +#pragma once + #include "resource.h" #include "aster/aster.h" @@ -14,11 +16,12 @@ #include "aster/core/physical_device.h" #include "aster/core/pipeline.h" #include "aster/core/sampler.h" +#include "aster/core/size.h" #include "aster/core/swapchain.h" #include #include -#include +#include "EASTL/deque.h" #include #include @@ -44,15 +47,15 @@ struct eastl::hash hash = HashCombine(hash, HashAny(createInfo.addressModeW)); hash = HashCombine(hash, HashAny(static_cast(createInfo.mipLodBias * 1000))); // Resolution of 10^-3 hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable)); - hash = - HashCombine(hash, - HashAny(static_cast(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution + hash = HashCombine( + hash, + HashAny(static_cast(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution hash = HashCombine(hash, HashAny(createInfo.compareEnable)); hash = HashCombine(hash, HashAny(createInfo.compareOp)); hash = HashCombine(hash, HashAny(static_cast(createInfo.minLod * 1000))); // 0.001 resolution is enough. - hash = - HashCombine(hash, - HashAny(static_cast(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp) + hash = HashCombine( + hash, + HashAny(static_cast(createInfo.maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp) hash = HashCombine(hash, HashAny(createInfo.borderColor)); hash = HashCombine(hash, HashAny(createInfo.unnormalizedCoordinates)); @@ -375,9 +378,27 @@ struct DeviceCreateInfo #pragma endregion +namespace _internal +{ +class SyncServer; +} + +class Receipt +{ + void *m_Opaque; + + explicit Receipt(void *opaque) + : m_Opaque{opaque} + { + } + friend _internal::SyncServer; +}; + class Device; struct Frame; +#define DEPRECATE_RAW_CALLS + class Context { protected: @@ -392,12 +413,12 @@ class Context } public: + DEPRECATE_RAW_CALLS void Dependency(const vk::DependencyInfo &dependencyInfo); + void Begin(); void End(); }; -#define DEPRECATE_RAW_CALLS - class GraphicsContext : public Context { protected: @@ -409,22 +430,61 @@ class GraphicsContext : public Context { } + const Pipeline *m_PipelineInUse; + public: DEPRECATE_RAW_CALLS void SetViewport(const vk::Viewport &viewport); void BindVertexBuffer(const Ref &vertexBuffer); 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 DrawIndexed(u32 indexCount); - DEPRECATE_RAW_CALLS void Dependency(const vk::DependencyInfo &dependencyInfo); DEPRECATE_RAW_CALLS void BeginRendering(const vk::RenderingInfo &renderingInfo); 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> m_OwnedBuffers; + eastl::vector> m_OwnedImages; + void Reset(); + + public: + struct ImageData + { + void *m_Data; + usize m_NumBytes; + }; + + void UploadTexture(const Ref &image, const ImageData &data); + + TransferContext(TransferContext &&other) noexcept; + TransferContext &operator=(TransferContext &&other) noexcept; + + DISALLOW_COPY_AND_ASSIGN(TransferContext); +}; + struct Frame { // Persistent - ::Device *m_Device; + Device *m_Device; // TODO: ThreadSafe vk::CommandPool m_Pool; vk::Fence m_FrameAvailableFence; @@ -443,6 +503,7 @@ struct Frame void Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize); GraphicsContext CreateGraphicsContext(); + TransferContext CreateTransferContext(); void WaitUntilReady(); }; @@ -457,7 +518,10 @@ class Device final // TODO: This is single-threaded. vk::Queue m_GraphicsQueue; - u32 m_GraphicsQueueFamily; + u32 m_PrimaryQueueFamily; + + vk::Queue m_TransferQueue; + u32 m_TransferQueueFamily; std::array m_Frames; u32 m_CurrentFrameIdx = 0; @@ -583,8 +647,26 @@ class Device final void Present(Frame &frame, GraphicsContext &graphicsContext); + // + // Context + // ---------------------------------------------------------------------------------------------------- friend Context; friend GraphicsContext; + friend TransferContext; + + vk::CommandPool m_TransferPool; + eastl::deque m_TransferContexts; + eastl::vector m_TransferContextFreeList; + + TransferContext &CreateTransferContext(); + Receipt Submit(Context &context); + + // + // Sync + // ---------------------------------------------------------------------------------------------------- + + std::unique_ptr<_internal::SyncServer> m_SyncServer; + void WaitOn(Receipt recpt); // // Device Methods diff --git a/aster/include/aster/systems/sync_server.h b/aster/include/aster/systems/sync_server.h new file mode 100644 index 0000000..cb5bfc6 --- /dev/null +++ b/aster/include/aster/systems/sync_server.h @@ -0,0 +1,69 @@ +// ============================================= +// Aster: sync_server.h +// Copyright (c) 2020-2025 Anish Bhobe +// ============================================= + +#include "aster/aster.h" + +#include +#include + +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 m_Allocations; + eastl::intrusive_list 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 \ No newline at end of file diff --git a/aster/src/aster/systems/CMakeLists.txt b/aster/src/aster/systems/CMakeLists.txt index ff666dd..96a8bde 100644 --- a/aster/src/aster/systems/CMakeLists.txt +++ b/aster/src/aster/systems/CMakeLists.txt @@ -5,4 +5,5 @@ cmake_minimum_required(VERSION 3.13) target_sources(aster_core PRIVATE "device.cpp" -"commit_manager.cpp") +"commit_manager.cpp" +"sync_server.cpp") diff --git a/aster/src/aster/systems/device.cpp b/aster/src/aster/systems/device.cpp index 1adeeaa..78cde23 100644 --- a/aster/src/aster/systems/device.cpp +++ b/aster/src/aster/systems/device.cpp @@ -9,14 +9,136 @@ #include "core/window.h" #include "systems/resource.h" +#include "aster/systems/sync_server.h" #include "aster/util/files.h" +#include #include static constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::eCompute | 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 systems::DefaultPhysicalDeviceSelector(const PhysicalDevices &physicalDevices) { @@ -125,8 +247,9 @@ systems::Device::CreateTexture2D(const Texture2DCreateInfo &createInfo) VkImage rawImage; VmaAllocation allocation; vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); - auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast(&imageCreateInfo), - &allocationCreateInfo, &rawImage, &allocation, nullptr)); + auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, + reinterpret_cast(&imageCreateInfo), + &allocationCreateInfo, &rawImage, &allocation, nullptr)); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); vk::Image image = rawImage; @@ -156,8 +279,9 @@ systems::Device::CreateTextureCube(const TextureCubeCreateInfo &createInfo) VkImage rawImage; VmaAllocation allocation; vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); - auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast(&imageCreateInfo), - &allocationCreateInfo, &rawImage, &allocation, nullptr)); + auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, + reinterpret_cast(&imageCreateInfo), + &allocationCreateInfo, &rawImage, &allocation, nullptr)); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); vk::Image image = rawImage; @@ -187,8 +311,9 @@ systems::Device::CreateAttachment(const AttachmentCreateInfo &createInfo) VkImage rawImage; VmaAllocation allocation; vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); - auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast(&imageCreateInfo), - &allocationCreateInfo, &rawImage, &allocation, nullptr)); + auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, + reinterpret_cast(&imageCreateInfo), + &allocationCreateInfo, &rawImage, &allocation, nullptr)); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); vk::Image image = rawImage; @@ -213,8 +338,9 @@ systems::Device::CreateDepthStencilImage(const DepthStencilImageCreateInfo &crea VkImage rawImage; VmaAllocation allocation; vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo); - auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, reinterpret_cast(&imageCreateInfo), - &allocationCreateInfo, &rawImage, &allocation, nullptr)); + auto result = static_cast(vmaCreateImage(m_Device.m_Allocator, + reinterpret_cast(&imageCreateInfo), + &allocationCreateInfo, &rawImage, &allocation, nullptr)); ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result); vk::Image image = rawImage; @@ -337,7 +463,7 @@ systems::Device::CreateView(const ViewCreateInfo &createInfo) vk::ImageView view; const auto imageViewCreateInfo = static_cast(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) THEN_ABORT(result); @@ -523,6 +649,8 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre .viewMask = 0, .colorAttachmentCount = 1, .pColorAttachmentFormats = &m_Swapchain.m_Format, + // TODO: Select the right Depth Format based on device. + .depthAttachmentFormat = vk::Format::eD24UnormS8Uint, }; vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { @@ -540,14 +668,14 @@ systems::Device::CreatePipeline(Pipeline &pipelineOut, const GraphicsPipelineCre .layout = pipelineLayout, }; 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) THEN_ABORT(vresult); SetName(pipeline, "Triangle Pipeline"); for (auto &shader : shaders) { - m_Device.m_Device.destroy(shader.module, nullptr); + m_Device->destroy(shader.module, nullptr); } pipelineOut = {&m_Device, pipelineLayout, pipeline, {}}; @@ -709,8 +837,8 @@ systems::Device::CreateShaders( } ComPtr pipelineComposite; - auto slangResult = m_SlangSession->createCompositeComponentType(components.data(), static_cast(components.size()), - pipelineComposite.writeRef()); + auto slangResult = m_SlangSession->createCompositeComponentType( + components.data(), static_cast(components.size()), pipelineComposite.writeRef()); if (slangResult < 0) { @@ -752,7 +880,7 @@ systems::Device::CreateShaders( .pCode = static_cast(kernelCode->getBufferPointer()), }; - result = m_Device.m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &outShader.module); + result = m_Device->createShaderModule(&shaderModuleCreateInfo, nullptr, &outShader.module); if (Failed(result)) { ERROR("Shaders could not be created. Cause: {}", result); @@ -766,7 +894,7 @@ systems::Device::CreateShaders( { if (shader.module) { - m_Device.m_Device.destroy(shader.module, nullptr); + m_Device->destroy(shader.module, nullptr); } } return result; @@ -797,7 +925,7 @@ systems::Device::CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, .pushConstantRangeCount = 0, .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)) { ERROR("Could not create a pipeline layout. Cause: {}", result); @@ -818,7 +946,7 @@ FindAppropriateQueueAllocation(const PhysicalDevice &physicalDevice) { return { .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); } +std::optional +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 +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) : m_Window{createInfo.m_Window} + , m_SyncServer{new _internal::SyncServer{*this}} { assert(createInfo.m_AppName); assert(createInfo.m_PhysicalDeviceSelector); @@ -841,16 +1010,45 @@ systems::Device::Device(const DeviceCreateInfo &createInfo) Features features = createInfo.m_Features; // TODO: Add the other queues - QueueAllocation queueAllocation = FindAppropriateQueueAllocation(physicalDevice); + eastl::fixed_vector queueAllocations; - m_Device = ::Device{ - m_Instance, physicalDevice, features, {&queueAllocation, 1}, createInfo.m_PipelineCacheData, createInfo.m_Name}; + queueAllocations.push_back(FindAppropriateQueueAllocation(physicalDevice)); + auto &primaryQueue = queueAllocations.back(); + u32 primaryQueueIndex = 0; + m_PrimaryQueueFamily = primaryQueue.m_Family; - m_GraphicsQueueFamily = queueAllocation.m_Family; - m_GraphicsQueue = m_Device.GetQueue(m_GraphicsQueueFamily, 0); + u32 transferQueueIndex; + 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()}; + 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 = {}; auto result = slang::createGlobalSession(&globalSessionDesc, m_GlobalSlangSession.writeRef()); 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) { - m_Device.m_Device.destroy(Take(frame.m_FrameAvailableFence), nullptr); - m_Device.m_Device.destroy(Take(frame.m_ImageAcquireSem), nullptr); - m_Device.m_Device.destroy(Take(frame.m_RenderFinishSem), nullptr); - m_Device.m_Device.destroy(Take(frame.m_Pool), nullptr); + m_Device->destroy(Take(frame.m_FrameAvailableFence), nullptr); + m_Device->destroy(Take(frame.m_ImageAcquireSem), nullptr); + m_Device->destroy(Take(frame.m_RenderFinishSem), nullptr); + m_Device->destroy(Take(frame.m_Pool), nullptr); } + m_Device->destroy(Take(m_TransferPool), nullptr); + m_TransferContexts.clear(); m_HashToSamplerIdx.clear(); } @@ -910,19 +1110,19 @@ systems::Device::CreateFrame(u32 frameIndex) const vk::CommandPoolCreateInfo commandPoolCreateInfo = { .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); 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); 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); - AbortIfFailedMV(m_Device.m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &finished), + AbortIfFailedMV(m_Device->createSemaphore(&semaphoreCreateInfo, nullptr, &finished), "Could not create RF semaphore for frame {}.", frameIndex); m_Device.SetName(pool, name.c_str()); @@ -933,7 +1133,7 @@ systems::Device::CreateFrame(u32 frameIndex) DEBUG("Frame {} created successfully.", frameIndex); return Frame{ - .m_Device = &m_Device, + .m_Device = this, .m_Pool = pool, .m_FrameAvailableFence = fence, .m_ImageAcquireSem = acquire, @@ -956,7 +1156,7 @@ systems::Device::GetNextFrame() bool imageAcquired = false; while (!imageAcquired) { - switch (auto result = m_Device.m_Device.acquireNextImageKHR( + switch (auto result = m_Device->acquireNextImageKHR( m_Swapchain.m_Swapchain, MaxValue, currentFrame.m_ImageAcquireSem, nullptr, &imageIndex)) { 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 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_ImageIdx = imageIdx; @@ -1048,7 +1312,7 @@ systems::Frame::CreateGraphicsContext() .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1, }; - AbortIfFailedMV(m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &cmd), + AbortIfFailedMV(m_Device->m_Device->allocateCommandBuffers(&allocateInfo, &cmd), "Command buffer {} alloc failed.", m_FrameIdx); m_CommandBuffers.push_back(cmd); } @@ -1056,10 +1320,33 @@ systems::Frame::CreateGraphicsContext() 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 systems::Frame::WaitUntilReady() { - AbortIfFailedMV(m_Device->m_Device.waitForFences(1, &m_FrameAvailableFence, true, MaxValue), + AbortIfFailedMV(m_Device->m_Device->waitForFences(1, &m_FrameAvailableFence, true, MaxValue), "Waiting for fence {} failed.", m_FrameIdx); } @@ -1069,6 +1356,12 @@ systems::Frame::WaitUntilReady() #pragma region Context Impl // ==================================================================================================== +void +systems::Context::Dependency(const vk::DependencyInfo &dependencyInfo) +{ + m_Cmd.pipelineBarrier2(&dependencyInfo); +} + void systems::Context::Begin() { @@ -1104,6 +1397,7 @@ void systems::GraphicsContext::BindPipeline(const Pipeline &pipeline) { m_Cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + m_PipelineInUse = &pipeline; } void @@ -1118,12 +1412,6 @@ systems::GraphicsContext::DrawIndexed(u32 indexCount) m_Cmd.drawIndexed(indexCount, 1, 0, 0, 0); } -void -systems::GraphicsContext::Dependency(const vk::DependencyInfo &dependencyInfo) -{ - m_Cmd.pipelineBarrier2(&dependencyInfo); -} - void systems::GraphicsContext::BeginRendering(const vk::RenderingInfo &renderingInfo) { @@ -1137,4 +1425,67 @@ systems::GraphicsContext::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, 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(w) * static_cast(h) * static_cast(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 = 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 \ No newline at end of file diff --git a/aster/src/aster/systems/sync_server.cpp b/aster/src/aster/systems/sync_server.cpp new file mode 100644 index 0000000..9ebf7b8 --- /dev/null +++ b/aster/src/aster/systems/sync_server.cpp @@ -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)); + + // 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(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; +} \ No newline at end of file diff --git a/samples/02_box/CMakeLists.txt b/samples/02_box/CMakeLists.txt index c4ebfef..982df96 100644 --- a/samples/02_box/CMakeLists.txt +++ b/samples/02_box/CMakeLists.txt @@ -9,6 +9,7 @@ add_shader(box "shader/box.vert.glsl") add_shader(box "shader/box.frag.glsl") add_shader(box "shader/box.vs.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 util_helper) diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 9e4311b..353ec5d 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -7,30 +7,23 @@ #include "aster/core/buffer.h" #include "aster/core/constants.h" -#include "aster/core/instance.h" -#include "aster/core/device.h" #include "aster/core/image.h" #include "aster/core/physical_device.h" #include "aster/core/pipeline.h" #include "aster/core/swapchain.h" #include "aster/core/window.h" -#include "helpers.h" - #define STB_IMAGE_IMPLEMENTATION -#include "aster/systems/buffer_manager.h" #include "aster/systems/commit_manager.h" -#include "aster/systems/image_manager.h" -#include "aster/systems/resource_manager.h" -#include "aster/systems/view_manager.h" -#include "frame.h" +#include "aster/systems/device.h" +#include "aster/util/files.h" #include "stb_image.h" #include -constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv"; +constexpr auto SHADER_FILE = "triangle"; struct ImageFile { @@ -75,9 +68,6 @@ ImageFile::~ImageFile() m_Data = nullptr; } -vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); -Pipeline CreatePipeline(const systems::CommitManager *resourceManager, const Swapchain *swapchain); - struct Vertex { vec3 m_Position; @@ -99,13 +89,6 @@ main(int, char **) MIN_LOG_LEVEL(Logger::LogType::eInfo); 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 = { .m_Vulkan10Features = {.samplerAnisotropy = true}, @@ -121,47 +104,40 @@ main(int, char **) .descriptorBindingStorageBufferUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .runtimeDescriptorArray = true, + .timelineSemaphore = true, .bufferDeviceAddress = true, .bufferDeviceAddressCaptureReplay = 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 = { .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_Perspective = glm::perspective( - 70_deg, static_cast(swapchain.m_Extent.width) / static_cast(swapchain.m_Extent.height), 0.1f, 100.0f), + 70_deg, static_cast(swapchainSize.m_Width) / static_cast(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, ©Pool), - "Copy command pool creation failed."); - vk::CommandBufferAllocateInfo bufferAllocateInfo = { - .commandPool = copyPool, - .level = vk::CommandBufferLevel::ePrimary, - .commandBufferCount = 1, - }; - AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer), - "Copy command buffer allocation failed."); - } - eastl::array vertices = { Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)}, Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)}, @@ -211,10 +187,10 @@ main(int, char **) assert(loaded); 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()); - auto crate = resourceManager.CombinedImageViews().CreateTexture2D({ + auto crate = device.CreateTexture2DWithView({ .m_Format = vk::Format::eR8G8B8A8Srgb, .m_Extent = {imageFile.m_Width, imageFile.m_Height}, .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); vk::ImageMemoryBarrier2 imageReadyToWrite = { @@ -232,8 +208,8 @@ main(int, char **) .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eTransferDstOptimal, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .image = crate->GetImage(), .subresourceRange = { @@ -256,8 +232,8 @@ main(int, char **) .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .image = crate->GetImage(), .subresourceRange = { @@ -273,77 +249,25 @@ main(int, char **) .pImageMemoryBarriers = &imageReadyToRead, }; - vk::Fence fence; - vk::FenceCreateInfo fenceCreateInfo = {}; - AbortIfFailed(device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); + auto &context = device.CreateTransferContext(); + context.Begin(); - vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - AbortIfFailed(copyBuffer.begin(&beginInfo)); + context.Dependency(imageReadyToWriteDependency); - copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency); + context.UploadTexture(crate->m_Image, {.m_Data = imageFile.m_Data, .m_NumBytes = imageFile.GetSize()}); - 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->GetImage(), vk::ImageLayout::eTransferDstOptimal, 1, - &imageCopy); + context.Dependency(imageReadyToReadDependency); - copyBuffer.pipelineBarrier2(&imageReadyToReadDependency); + context.End(); - AbortIfFailed(copyBuffer.end()); - - vk::SubmitInfo submitInfo = { - .commandBufferCount = 1, - .pCommandBuffers = ©Buffer, - }; - - AbortIfFailed(commandQueue.submit(1, &submitInfo, fence)); - INFO("Submit copy"); - - AbortIfFailed(device.m_Device.waitForFences(1, &fence, true, MaxValue)); - INFO("Fence wait"); - - AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool."); - - device.m_Device.destroy(fence, nullptr); + auto recpt = device.Submit(context); + device.WaitOn(recpt); } - auto ubo = resourceManager.Buffers().CreateStorageBuffer(sizeof camera, "Camera UBO"); + auto ubo = device.CreateStorageBuffer(sizeof camera, "Camera UBO"); ubo->Write(0, sizeof camera, &camera); // Persistent variables - vk::Viewport viewport = { - .x = 0, - .y = static_cast(swapchain.m_Extent.height), - .width = static_cast(swapchain.m_Extent.width), - .height = -static_cast(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(extent.height); - viewport.width = static_cast(extent.width); - viewport.height = -static_cast(extent.height); - scissor.extent = extent; - }; - swapchain.RegisterResizeCallback(resizeViewportScissor); vk::ImageSubresourceRange subresourceRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -361,8 +285,8 @@ main(int, char **) .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eColorAttachmentOptimal, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; vk::DependencyInfo topOfThePipeDependency = { @@ -376,8 +300,8 @@ main(int, char **) .dstAccessMask = vk::AccessFlagBits2::eNone, .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, .newLayout = vk::ImageLayout::ePresentSrcKHR, - .srcQueueFamilyIndex = queueAllocation.m_Family, - .dstQueueFamilyIndex = queueAllocation.m_Family, + .srcQueueFamilyIndex = vk::QueueFamilyIgnored, + .dstQueueFamilyIndex = vk::QueueFamilyIgnored, .subresourceRange = subresourceRange, }; vk::DependencyInfo renderToPresentDependency = { @@ -385,24 +309,21 @@ main(int, char **) .pImageMemoryBarriers = &renderToPresentBarrier, }; - FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> depthImages; - auto initDepthImages = [&depthImages, &frameManager, &resourceManager](const vk::Extent2D extent) { - for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) + auto initDepthImages = [&depthImages, &device](const vk::Extent2D extent) { + for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { - depthImages.push_back( - resourceManager.CombinedImageViews().CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"})); + depthImages.push_back(device.CreateDepthStencilImageWithView({.m_Extent = extent, .m_Name = "Depth"})); } }; - initDepthImages(swapchain.m_Extent); + initDepthImages(swapchainSize); auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) { depthImages.clear(); initDepthImages(extent); }; - swapchain.RegisterResizeCallback(recreateDepthBuffers); struct PCB { @@ -419,6 +340,8 @@ main(int, char **) Time::Init(); + auto prevSwapchainSize = swapchainSize; + INFO("Starting loop"); while (window.Poll()) { @@ -428,21 +351,41 @@ main(int, char **) camera.m_Model *= rotate(mat4{1.0f}, static_cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f)); ubo->Write(0, sizeof camera, &camera); - Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize()); + auto currentFrame = device.GetNextFrame(); - u32 imageIndex = currentFrame->m_ImageIdx; - vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex]; - vk::Image currentImage = swapchain.m_Images[imageIndex]; - vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; - vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View; + prevSwapchainSize = swapchainSize; + swapchainSize = currentFrame.m_SwapchainSize; + if (swapchainSize != prevSwapchainSize) + { + recreateDepthBuffers(swapchainSize); + } + + vk::Viewport viewport = { + .x = 0, + .y = static_cast(swapchainSize.m_Height), + .width = static_cast(swapchainSize.m_Width), + .height = -static_cast(swapchainSize.m_Height), + .minDepth = 0.0, + .maxDepth = 1.0, + }; + + vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = static_cast(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; renderToPresentBarrier.image = currentImage; - vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - AbortIfFailed(cmd.begin(&beginInfo)); + auto context = currentFrame.CreateGraphicsContext(); - cmd.pipelineBarrier2(&topOfThePipeDependency); + context.Begin(); + + context.Dependency(topOfThePipeDependency); // Render eastl::array attachmentInfos = { @@ -466,187 +409,32 @@ main(int, char **) }; vk::RenderingInfo renderingInfo = { - .renderArea = {.extent = swapchain.m_Extent}, + .renderArea = scissor, .layerCount = 1, .colorAttachmentCount = static_cast(attachmentInfos.size()), .pColorAttachments = attachmentInfos.data(), .pDepthAttachment = &depthAttachment, }; - cmd.beginRendering(&renderingInfo); + context.BeginRendering(renderingInfo); - cmd.setViewport(0, 1, &viewport); - cmd.setScissor(0, 1, &scissor); - cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); - cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, - &commitManager.GetDescriptorSet(), 0, nullptr); - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAllGraphics, 0, 12, &pcb); - cmd.draw(static_cast(vertices.size()), 1, 0, 0); + context.SetViewport(viewport); + context.BindPipeline(pipeline); + /*cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, + &commitManager.GetDescriptorSet(), 0, nullptr);*/ + //context.PushConstantBlock(pcb); + context.Draw(3); - cmd.endRendering(); + context.EndRendering(); - cmd.pipelineBarrier2(&renderToPresentDependency); + context.Dependency(renderToPresentDependency); - AbortIfFailed(cmd.end()); + context.End(); - vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; - vk::SubmitInfo submitInfo = { - .waitSemaphoreCount = 1, - .pWaitSemaphores = ¤tFrame->m_ImageAcquireSem, - .pWaitDstStageMask = &waitDstStage, - .commandBufferCount = 1, - .pCommandBuffers = &cmd, - .signalSemaphoreCount = 1, - .pSignalSemaphores = ¤tFrame->m_RenderFinishSem, - }; - AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); - - currentFrame->Present(commandQueue, &swapchain, &surface, window.GetSize()); + device.Present(currentFrame, context); } device.WaitIdle(); - device.m_Device.destroy(copyPool, nullptr); 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 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(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(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 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; -} \ No newline at end of file diff --git a/samples/02_box/shader/triangle.slang b/samples/02_box/shader/triangle.slang new file mode 100644 index 0000000..e178cee --- /dev/null +++ b/samples/02_box/shader/triangle.slang @@ -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; +} \ No newline at end of file diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 47ec432..33fa944 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -4,6 +4,6 @@ cmake_minimum_required(VERSION 3.13) add_subdirectory("00_util") add_subdirectory("01_triangle") -# add_subdirectory("02_box") +add_subdirectory("02_box") # add_subdirectory("03_model_render") # add_subdirectory("04_scenes")