From 6b5442527fe4b8b0eff4146f4c6e875fe842cada Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 28 Jul 2024 21:59:09 +0200 Subject: [PATCH] Added an HDR Skycube. --- aster/global.h | 63 +++- aster/image.cpp | 167 ++++++++- aster/image.h | 11 + samples/00_util/gpu_resource_manager.cpp | 137 ++++++- samples/00_util/gpu_resource_manager.h | 39 +- samples/00_util/gui.cpp | 7 +- samples/00_util/helpers.h | 3 + samples/03_model_render/CMakeLists.txt | 11 +- .../{model_loader.cpp => asset_loader.cpp} | 336 ++++++++++++++---- .../{model_loader.h => asset_loader.h} | 20 +- samples/03_model_render/ibl_helpers.cpp | 249 +++++++++++++ samples/03_model_render/ibl_helpers.h | 15 + samples/03_model_render/model_render.cpp | 101 ++++-- samples/03_model_render/pipeline_utils.cpp | 157 +++++++- samples/03_model_render/pipeline_utils.h | 5 + .../03_model_render/shader/background.ps.hlsl | 22 ++ .../03_model_render/shader/background.vs.hlsl | 32 ++ .../shader/bindless_structs.hlsli | 26 +- .../shader/eqrectToCube.cs.hlsl | 58 +++ .../shader/graphics_structs.hlsli | 41 +++ samples/03_model_render/shader/model.ps.hlsl | 20 +- samples/03_model_render/shader/model.vs.hlsl | 4 +- 22 files changed, 1356 insertions(+), 168 deletions(-) rename samples/03_model_render/{model_loader.cpp => asset_loader.cpp} (71%) rename samples/03_model_render/{model_loader.h => asset_loader.h} (84%) create mode 100644 samples/03_model_render/ibl_helpers.cpp create mode 100644 samples/03_model_render/ibl_helpers.h create mode 100644 samples/03_model_render/shader/background.ps.hlsl create mode 100644 samples/03_model_render/shader/background.vs.hlsl create mode 100644 samples/03_model_render/shader/eqrectToCube.cs.hlsl create mode 100644 samples/03_model_render/shader/graphics_structs.hlsli diff --git a/aster/global.h b/aster/global.h index b6cf119..4389a7c 100644 --- a/aster/global.h +++ b/aster/global.h @@ -100,12 +100,73 @@ struct Time } }; -[[nodiscard]] inline usize +[[nodiscard]] constexpr usize ClosestMultiple(const usize val, const usize of) { return of * ((val + of - 1) / of); } +[[nodiscard]] constexpr u32 +ClosestMultiple(const u32 val, const u32 of) +{ + return of * ((val + of - 1) / of); +} + +[[nodiscard]] constexpr bool +IsPowerOfTwo(const usize val) +{ + return val && !(val & (val - 1)); +} + +[[nodiscard]] constexpr bool +IsPowerOfTwo(const u32 val) +{ + return val && !(val & (val - 1)); +} + +[[nodiscard]] constexpr usize +ClosestLargerPowerOfTwo(usize val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val |= val >> 32; + val++; + return val; +} + +[[nodiscard]] constexpr usize +ClosestPowerOfTwo(const usize val) +{ + const usize largerPo2 = ClosestLargerPowerOfTwo(val); + const usize smallerPo2 = largerPo2 >> 1; + return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2; +} + +[[nodiscard]] constexpr u32 +ClosestLargerPowerOfTwo(u32 val) +{ + val--; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val++; + return val; +} + +[[nodiscard]] constexpr u32 +ClosestPowerOfTwo(const u32 val) +{ + const u32 largerPo2 = ClosestLargerPowerOfTwo(val); + const u32 smallerPo2 = largerPo2 >> 1; + return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2; +} + template <> struct fmt::formatter : nested_formatter { diff --git a/aster/image.cpp b/aster/image.cpp index b490b0e..88eb538 100644 --- a/aster/image.cpp +++ b/aster/image.cpp @@ -22,6 +22,9 @@ void Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipMapped, const cstr name) { + WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)", + extent.width, extent.height, name ? name : ""); + const u32 mipLevels = isMipMapped ? 1 + Cast(floor(log2(eastl::max(extent.width, extent.height)))) : 1; assert(mipLevels <= MIP_MASK); @@ -34,7 +37,7 @@ Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageF vk::ImageCreateInfo imageCreateInfo = { .imageType = vk::ImageType::e2D, .format = imageFormat, - .extent = {.width = extent.width, .height = extent.height, .depth = 1}, + .extent = ToExtent3D(extent, 1), .mipLevels = mipLevels, .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, @@ -75,7 +78,92 @@ Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageF m_Image = image; m_View = view; m_Allocation = allocation; - m_Extent = {extent.width, extent.height, 1}; + m_Extent = imageCreateInfo.extent; + m_MipLevels_ = mipLevels | OWNED_BIT | VALID_BIT; + + device->SetName(m_Image, name); +} + +/* +Cube map Faces info. + +TODO: Correct this based on the actual layout for upside down viewport. + +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| +z | 4 | -y | +| -z | 5 | -y | + +Remember, we use upside down viewport. + +*/ + +void +TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name) +{ + WARN_IF(!IsPowerOfTwo(cubeSide), "Image {1} is {0}x{0} (Non Power of Two)", cubeSide, name ? name : ""); + + const u32 mipLevels = isMipMapped ? 1 + Cast(floor(log2(cubeSide))) : 1; + assert(mipLevels <= MIP_MASK); + + auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst; + if (isMipMapped) + { + usage |= vk::ImageUsageFlagBits::eTransferSrc; + } + + vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1}; + + vk::ImageCreateInfo imageCreateInfo = { + .flags = vk::ImageCreateFlagBits::eCubeCompatible, + .imageType = vk::ImageType::e2D, + .format = imageFormat, + .extent = extent, + .mipLevels = mipLevels, + .arrayLayers = 6, + .samples = vk::SampleCountFlagBits::e1, + .tiling = vk::ImageTiling::eOptimal, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + }; + constexpr VmaAllocationCreateInfo allocationCreateInfo = { + .flags = {}, + .usage = VMA_MEMORY_USAGE_AUTO, + }; + + VkImage image; + VmaAllocation allocation; + auto result = Cast(vmaCreateImage(device->m_Allocator, Recast(&imageCreateInfo), + &allocationCreateInfo, &image, &allocation, nullptr)); + ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result); + + vk::ImageView view; + vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = image, + .viewType = vk::ImageViewType::eCube, + .format = imageFormat, + .components = {}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = mipLevels, + .baseArrayLayer = 0, + .layerCount = 6, + }, + }; + result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); + ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); + + m_Image = image; + m_View = view; + m_Allocation = allocation; + m_Extent = extent; m_MipLevels_ = mipLevels | OWNED_BIT | VALID_BIT; device->SetName(m_Image, name); @@ -87,7 +175,7 @@ AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imag vk::ImageCreateInfo imageCreateInfo = { .imageType = vk::ImageType::e2D, .format = imageFormat, - .extent = {.width = extent.width, .height = extent.height, .depth = 1}, + .extent = ToExtent3D(extent, 1), .mipLevels = 1, .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, @@ -128,7 +216,7 @@ AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imag m_Image = image; m_View = view; m_Allocation = allocation; - m_Extent = {extent.width, extent.height, 1}; + m_Extent = imageCreateInfo.extent; m_MipLevels_ = 1 | OWNED_BIT | VALID_BIT; device->SetName(m_Image, name); @@ -141,7 +229,7 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name) vk::ImageCreateInfo imageCreateInfo = { .imageType = vk::ImageType::e2D, .format = imageFormat, - .extent = {.width = extent.width, .height = extent.height, .depth = 1}, + .extent = ToExtent3D(extent, 1), .mipLevels = 1, .arrayLayers = 1, .samples = vk::SampleCountFlagBits::e1, @@ -182,7 +270,74 @@ DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name) m_Image = image; m_View = view; m_Allocation = allocation; - m_Extent = {extent.width, extent.height, 1}; + m_Extent = imageCreateInfo.extent; + m_MipLevels_ = 1 | OWNED_BIT | VALID_BIT; + + device->SetName(m_Image, name); +} + +void +StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled, + cstr name) +{ + + // Reasoning: + // Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for + // results. + auto usage = + vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst; + if (isSampled) + { + WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)", + extent.width, extent.height, name ? name : ""); + usage |= vk::ImageUsageFlagBits::eSampled; + } + + vk::ImageCreateInfo imageCreateInfo = { + .imageType = vk::ImageType::e2D, + .format = imageFormat, + .extent = ToExtent3D(extent, 1), + .mipLevels = 1, + .arrayLayers = 1, + .samples = vk::SampleCountFlagBits::e1, + .tiling = vk::ImageTiling::eOptimal, + .usage = usage, + .sharingMode = vk::SharingMode::eExclusive, + .initialLayout = vk::ImageLayout::eUndefined, + }; + constexpr VmaAllocationCreateInfo allocationCreateInfo = { + .flags = {}, + .usage = VMA_MEMORY_USAGE_AUTO, + }; + + VkImage image; + VmaAllocation allocation; + auto result = Cast(vmaCreateImage(device->m_Allocator, Recast(&imageCreateInfo), + &allocationCreateInfo, &image, &allocation, nullptr)); + ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result); + + vk::ImageView view; + const vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = image, + .viewType = vk::ImageViewType::e2D, + .format = imageFormat, + .components = {}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view); + ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result); + + m_Image = image; + m_View = view; + m_Allocation = allocation; + m_Extent = imageCreateInfo.extent; m_MipLevels_ = 1 | OWNED_BIT | VALID_BIT; device->SetName(m_Image, name); diff --git a/aster/image.h b/aster/image.h index a7f82c6..e382958 100644 --- a/aster/image.h +++ b/aster/image.h @@ -59,6 +59,12 @@ struct Texture : Image void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr); }; +struct TextureCube : Texture +{ + void + Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped = false, cstr name = nullptr); +}; + struct AttachmentImage : Image { void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name = nullptr); @@ -69,6 +75,11 @@ struct DepthImage : Image void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr); }; +struct StorageTexture : Texture +{ + void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isSampled, cstr name = nullptr); +}; + inline bool Image::IsValid() const { diff --git a/samples/00_util/gpu_resource_manager.cpp b/samples/00_util/gpu_resource_manager.cpp index 33a5473..5ee35e1 100644 --- a/samples/00_util/gpu_resource_manager.cpp +++ b/samples/00_util/gpu_resource_manager.cpp @@ -22,7 +22,7 @@ TextureManager::Init(const u32 maxCapacity) TextureHandle TextureManager::Commit(Texture *texture) { - ERROR_IF(!texture->IsValid() || !texture->IsOwned(), "Buffer must be valid and owned for commital") + ERROR_IF(!texture || !texture->IsValid() || !texture->IsOwned(), "Texture must be valid and owned for commital") THEN_ABORT(-1); if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) @@ -38,7 +38,7 @@ TextureManager::Commit(Texture *texture) static_assert(std::is_trivially_copyable_v); *allocatedTexture = *texture; - // Take ownership of the buffer. + // Take ownership of the texture. texture->m_MipLevels_ &= ~Texture::OWNED_BIT; return {index}; @@ -102,7 +102,8 @@ BufferManager::Init(const u32 maxCapacity) BufferHandle BufferManager::Commit(StorageBuffer *buffer) { - ERROR_IF(!buffer->IsValid() || !buffer->IsOwned(), "Buffer must be valid and owned for commital") THEN_ABORT(-1); + ERROR_IF(!buffer || !buffer->IsValid() || !buffer->IsOwned(), "Buffer must be valid and owned for commital") + THEN_ABORT(-1); if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) { @@ -171,6 +172,27 @@ BufferManager::Destroy(const Device *device) } } +StorageTextureHandle +StorageTextureManager::Commit(StorageTexture *texture) +{ + const TextureHandle tx = TextureManager::Commit(texture); + return {tx.m_Index}; +} + +StorageTexture * +StorageTextureManager::Fetch(const StorageTextureHandle handle) +{ + assert(!handle.IsInvalid()); + + return Recast(&m_Textures[handle.m_Index]); +} + +void +StorageTextureManager::Release(const Device *device, const StorageTextureHandle handle) +{ + TextureManager::Release(device, {handle.m_Index}); +} + GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info) : uBufferInfo(info) { @@ -259,6 +281,19 @@ GpuResourceManager::Release(BufferHandle handle) #endif } +void +GpuResourceManager::Release(StorageBuffer *storageBuffer, const BufferHandle handle) +{ + assert(storageBuffer); + assert(!storageBuffer->IsValid()); + + StorageBuffer *internal = m_BufferManager.Fetch(handle); + *storageBuffer = *internal; + internal->m_Size_ &= ~StorageBuffer::OWNED_BIT; + + Release(handle); +} + void GpuResourceManager::Release(TextureHandle handle) { @@ -274,6 +309,19 @@ GpuResourceManager::Release(TextureHandle handle) #endif } +void +GpuResourceManager::Release(Texture *texture, TextureHandle handle) +{ + assert(texture); + assert(!texture->IsValid()); + + Texture *internal = m_TextureManager.Fetch(handle); + *texture = *internal; + internal->m_MipLevels_ &= ~Texture::OWNED_BIT; + + Release(handle); +} + TextureHandle GpuResourceManager::Commit(Texture *texture) { @@ -303,6 +351,63 @@ GpuResourceManager::Commit(Texture *texture) return {handle}; } +StorageTextureHandle +GpuResourceManager::Commit(StorageTexture *storageTexture) +{ + StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture); + + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ + .sampler = nullptr, + .imageView = storageTexture->m_View, + .imageLayout = vk::ImageLayout::eGeneral, + }); + + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = STORAGE_TEXTURE_BINDING_INDEX, + .dstArrayElement = handle.m_Index, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageImage, + .pImageInfo = &m_WriteInfos.back().uImageInfo, + }); + + m_WriteOwner.emplace_back(HandleType::eStorageTexture, handle.m_Index); + +#if !defined(NDEBUG) + ++m_CommitedStorageTextureCount; +#endif + + return {handle}; +} + +void +GpuResourceManager::Release(StorageTextureHandle handle) +{ + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eTexture); + + m_StorageTextureManager.Release(m_Device, handle); + +#if !defined(NDEBUG) + --m_CommitedStorageTextureCount; +#endif +} + +void +GpuResourceManager::Release(StorageTexture *texture, const StorageTextureHandle handle) +{ + assert(texture); + assert(!texture->IsValid()); + + StorageTexture *internal = m_StorageTextureManager.Fetch(handle); + *texture = *internal; + internal->m_MipLevels_ &= ~StorageTexture::OWNED_BIT; + + Release(handle); +} + void GpuResourceManager::Update() { @@ -316,7 +421,7 @@ GpuResourceManager::Update() m_WriteOwner.clear(); } -GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) +GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize) : m_Device(device) { vk::PhysicalDeviceProperties properties; @@ -324,6 +429,8 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) u32 buffersCount = eastl::min(properties.limits.maxPerStageDescriptorStorageBuffers - 1024, Cast(maxSize)); u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast(maxSize)); + u32 storageTexturesCount = + eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast(maxSize)); // TODO: Switch to bindless samplers / multiple sampler configurations vk::SamplerCreateInfo samplerCreateInfo = { @@ -346,9 +453,11 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) INFO("Max Buffer Count: {}", buffersCount); INFO("Max Texture Count: {}", texturesCount); + INFO("Max Storage Texture Count: {}", storageTexturesCount); m_BufferManager.Init(buffersCount); m_TextureManager.Init(texturesCount); + m_StorageTextureManager.Init(storageTexturesCount); eastl::array poolSizes = { vk::DescriptorPoolSize{ @@ -359,7 +468,12 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) .type = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = texturesCount, }, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eStorageImage, + .descriptorCount = storageTexturesCount, + }, }; + const vk::DescriptorPoolCreateInfo poolCreateInfo = { .flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind, .maxSets = 1, @@ -373,6 +487,7 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) eastl::array layoutBindingFlags = { bindingFlags, bindingFlags, + bindingFlags, }; vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = { @@ -395,6 +510,12 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) .stageFlags = vk::ShaderStageFlagBits::eAll, .pImmutableSamplers = immutableSamplers.data(), }, + vk::DescriptorSetLayoutBinding{ + .binding = STORAGE_TEXTURE_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eStorageImage, + .descriptorCount = Cast(storageTexturesCount), + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, }; static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size()); const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { @@ -423,12 +544,14 @@ GpuResourceManager::GpuResourceManager(const Device *device, u16 maxSize) GpuResourceManager::~GpuResourceManager() { #if !defined(NDEBUG) - WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0, "Resources alive: SSBO = {}, Textures = {}", - m_CommitedBufferCount, m_CommitedTextureCount); + WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0 || m_CommitedStorageTextureCount > 0, + "Resources alive: SSBO = {}, Textures = {}, RWTexture = {}", m_CommitedBufferCount, m_CommitedTextureCount, + m_CommitedStorageTextureCount); #endif m_BufferManager.Destroy(m_Device); m_TextureManager.Destroy(m_Device); + m_StorageTextureManager.Destroy(m_Device); m_Device->m_Device.destroy(m_ImmutableSampler, nullptr); m_Device->m_Device.destroy(m_DescriptorPool, nullptr); m_Device->m_Device.destroy(m_SetLayout, nullptr); @@ -441,6 +564,7 @@ GpuResourceManager::GpuResourceManager(GpuResourceManager &&other) noexcept , m_ImmutableSampler(other.m_ImmutableSampler) , m_BufferManager(std::move(other.m_BufferManager)) , m_TextureManager(std::move(other.m_TextureManager)) + , m_StorageTextureManager(std::move(other.m_StorageTextureManager)) , m_Device(Take(other.m_Device)) , m_DescriptorPool(other.m_DescriptorPool) , m_SetLayout(other.m_SetLayout) @@ -462,6 +586,7 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept m_ImmutableSampler = other.m_ImmutableSampler; m_BufferManager = std::move(other.m_BufferManager); m_TextureManager = std::move(other.m_TextureManager); + m_StorageTextureManager = std::move(other.m_StorageTextureManager); m_Device = Take(other.m_Device); // Ensure taken. m_DescriptorPool = other.m_DescriptorPool; m_SetLayout = other.m_SetLayout; diff --git a/samples/00_util/gpu_resource_manager.h b/samples/00_util/gpu_resource_manager.h index ec4238d..a0f2f34 100644 --- a/samples/00_util/gpu_resource_manager.h +++ b/samples/00_util/gpu_resource_manager.h @@ -12,6 +12,7 @@ struct Device; struct Texture; +struct StorageTexture; struct StorageBuffer; struct GpuResourceHandle @@ -34,6 +35,10 @@ struct TextureHandle : GpuResourceHandle { }; +struct StorageTextureHandle : GpuResourceHandle +{ +}; + struct TextureManager { eastl::vector m_Textures; @@ -60,6 +65,13 @@ struct BufferManager void Destroy(const Device *device); }; +struct StorageTextureManager : TextureManager +{ + StorageTextureHandle Commit(StorageTexture *texture); + StorageTexture *Fetch(StorageTextureHandle handle); + void Release(const Device *device, StorageTextureHandle handle); +}; + struct GpuResourceManager { private: @@ -81,6 +93,7 @@ struct GpuResourceManager { eBuffer, eTexture, + eStorageTexture, }; using WriteOwner = eastl::pair; @@ -93,29 +106,38 @@ struct GpuResourceManager BufferManager m_BufferManager; TextureManager m_TextureManager; + StorageTextureManager m_StorageTextureManager; void EraseWrites(u32 handleIndex, HandleType handleType); public: - const Device *m_Device; + Device *m_Device; constexpr static u32 BUFFER_BINDING_INDEX = 0; constexpr static u32 TEXTURE_BINDING_INDEX = 1; + constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2; vk::DescriptorPool m_DescriptorPool; vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSet m_DescriptorSet; - BufferHandle Commit(StorageBuffer *storageBuffer); - void Write(BufferHandle handle, usize offset, usize size, const void *data); - void Release(BufferHandle handle); - TextureHandle Commit(Texture *texture); - void Release(TextureHandle handle); + BufferHandle Commit(StorageBuffer *storageBuffer); // Commit to GPU and take Ownership + void Write(BufferHandle handle, usize offset, usize size, const void *data); // Write to buffer + void Release(BufferHandle handle); // Release and Destroy + void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return - void Update(); + TextureHandle Commit(Texture *texture); // Commit to GPU and take Ownership + void Release(TextureHandle handle); // Release and Destroy + void Release(Texture *texture, TextureHandle handle); // Release and Return + + StorageTextureHandle Commit(StorageTexture *storageTexture); + void Release(StorageTextureHandle handle); + void Release(StorageTexture *texture, StorageTextureHandle handle); + + void Update(); // Update all the descriptors required. // Ctor/Dtor - GpuResourceManager(const Device *device, u16 maxSize); + GpuResourceManager(Device *device, u16 maxSize); ~GpuResourceManager(); GpuResourceManager(GpuResourceManager &&other) noexcept; @@ -124,6 +146,7 @@ struct GpuResourceManager #if !defined(NDEBUG) usize m_CommitedBufferCount = 0; usize m_CommitedTextureCount = 0; + usize m_CommitedStorageTextureCount = 0; #endif DISALLOW_COPY_AND_ASSIGN(GpuResourceManager); diff --git a/samples/00_util/gui.cpp b/samples/00_util/gui.cpp index a5a6a94..ec48a88 100644 --- a/samples/00_util/gui.cpp +++ b/samples/00_util/gui.cpp @@ -8,7 +8,6 @@ #include "context.h" #include "device.h" #include "helpers.h" -#include "image.h" #include "window.h" #include @@ -166,11 +165,13 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk: { // OPTICK_EVENT(); +#if !defined(NDEBUG) constexpr vk::DebugUtilsLabelEXT label = { .pLabelName = "UI pass", - .color = std::array{0.0f, 0.0f, 1.0f, 1.0f}, + .color = std::array{0.9f, 0.9f, 1.0f, 1.0f}, }; commandBuffer.beginDebugUtilsLabelEXT(&label); +#endif vk::RenderingAttachmentInfo attachmentInfo = { .imageView = view, .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, @@ -194,7 +195,9 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk: commandBuffer.endRendering(); +#if !defined(NDEBUG) commandBuffer.endDebugUtilsLabelEXT(); +#endif } void diff --git a/samples/00_util/helpers.h b/samples/00_util/helpers.h index 04fdc73..f135cb8 100644 --- a/samples/00_util/helpers.h +++ b/samples/00_util/helpers.h @@ -22,6 +22,9 @@ eastl::vector ReadFile(cstr fileName); eastl::vector ReadFileBytes(cstr fileName, bool errorOnFail = true); bool WriteFileBytes(cstr fileName, eastl::span data); +template +using StackString = eastl::fixed_string; + #define AbortIfFailed(RESULT) \ do \ { \ diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index ed5eca8..b5603c8 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -8,15 +8,20 @@ find_path(TINYGLTF_INCLUDE_DIRS "tiny_gltf.h") add_executable(model_render model_render.cpp pipeline_utils.cpp pipeline_utils.h - model_loader.cpp - model_loader.h + asset_loader.cpp + asset_loader.h light_manager.cpp light_manager.h nodes.cpp - nodes.h) + nodes.h + ibl_helpers.cpp + ibl_helpers.h) add_shader(model_render shader/model.vs.hlsl) add_shader(model_render shader/model.ps.hlsl) +add_shader(model_render shader/eqrectToCube.cs.hlsl) +add_shader(model_render shader/background.vs.hlsl) +add_shader(model_render shader/background.ps.hlsl) target_link_libraries(model_render PRIVATE aster_core) target_link_libraries(model_render PRIVATE util_helper) diff --git a/samples/03_model_render/model_loader.cpp b/samples/03_model_render/asset_loader.cpp similarity index 71% rename from samples/03_model_render/model_loader.cpp rename to samples/03_model_render/asset_loader.cpp index 852385a..1447069 100644 --- a/samples/03_model_render/model_loader.cpp +++ b/samples/03_model_render/asset_loader.cpp @@ -1,5 +1,5 @@ // ============================================= -// Aster: model_loader.cpp +// Aster: asset_loader.cpp // Copyright (c) 2020-2024 Anish Bhobe // ============================================= @@ -10,7 +10,7 @@ #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION -#include "model_loader.h" +#include "asset_loader.h" #include "buffer.h" #include "device.h" @@ -28,6 +28,8 @@ #undef LoadImage #endif +constexpr vk::CommandBufferBeginInfo OneTimeCmdBeginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + vec4 VectorToVec4(const std::vector &vec) { @@ -52,24 +54,178 @@ VectorToVec3(const std::vector &vec) return {vec[0], vec[1], vec[2]}; } +void +AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const +{ + const Device *pDevice = m_ResourceManager->m_Device; + ERROR_IF(texture->IsValid(), "Expected invalid image.") THEN_ABORT(-1); + + i32 x, y, nChannels; + const f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4); + assert(nChannels == 3); + + ERROR_IF(!data, "Could not load {}", path) THEN_ABORT(-1); + + u32 width = Cast(x); + u32 height = Cast(y); + + StagingBuffer stagingBuffer; + texture->Init(m_ResourceManager->m_Device, {width, height}, vk::Format::eR32G32B32A32Sfloat, false, path); + assert(texture->IsValid()); + stagingBuffer.Init(m_ResourceManager->m_Device, (sizeof *data) * x * y * 4, "HDR Staging Buffer"); + stagingBuffer.Write(m_ResourceManager->m_Device, 0, stagingBuffer.GetSize(), data); + +#pragma region Setup Copy/Sync primitives + vk::BufferImageCopy2 copyRegion = { + .bufferOffset = 0, + .bufferRowLength = width, + .bufferImageHeight = height, + .imageSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {0, 0, 0}, + .imageExtent = texture->m_Extent, + }; + vk::CopyBufferToImageInfo2 stagingInfo = { + .srcBuffer = stagingBuffer.m_Buffer, + .dstImage = texture->m_Image, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = 1, + .pRegions = ©Region, + }; + vk::ImageMemoryBarrier2 readyToStageBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + vk::DependencyInfo readyToStageDependency = { + .memoryBarrierCount = 0, + .bufferMemoryBarrierCount = 0, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &readyToStageBarrier, + }; + vk::ImageMemoryBarrier2 postStagingBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader | vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = m_TransferQueueIndex, + .dstQueueFamilyIndex = m_GraphicsQueueIndex, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + vk::DependencyInfo postStagingDependency = { + .memoryBarrierCount = 0, + .bufferMemoryBarrierCount = 0, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &postStagingBarrier, + }; +#pragma endregion + + AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo)); + +#if !defined(NDEBUG) + StackString<128> loadActionName = "Load: "; + loadActionName += name ? name : path; + vk::DebugUtilsLabelEXT debugLabel = { + .pLabelName = loadActionName.c_str(), + .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); +#endif + + m_CommandBuffer.pipelineBarrier2(&readyToStageDependency); + m_CommandBuffer.copyBufferToImage2(&stagingInfo); + m_CommandBuffer.pipelineBarrier2(&postStagingDependency); + +#if !defined(NDEBUG) + m_CommandBuffer.endDebugUtilsLabelEXT(); +#endif + + AbortIfFailed(m_CommandBuffer.end()); + + vk::SubmitInfo submitInfo = { + .waitSemaphoreCount = 0, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &m_CommandBuffer, + }; + + vk::Fence fence; + vk::FenceCreateInfo fenceCreateInfo = {}; + AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); + AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence)); + AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); + pDevice->m_Device.destroy(fence, nullptr); + + AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {})); + + stagingBuffer.Destroy(pDevice); +} + TextureHandle -ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const +AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const { assert(image->component == 4); + assert(image->height > 0 && image->width > 0); + + u32 height = Cast(image->height); + u32 width = Cast(image->width); vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm; Texture texture; usize byteSize = image->image.size(); - texture.Init(m_ResourceManager->m_Device, {.width = Cast(image->width), .height = Cast(image->height)}, - imageFormat, true, image->name.data()); + texture.Init(m_ResourceManager->m_Device, {.width = width, .height = height}, imageFormat, true, + image->name.data()); stagingBuffer->Init(m_ResourceManager->m_Device, byteSize); stagingBuffer->Write(m_ResourceManager->m_Device, 0, byteSize, image->image.data()); - vk::ImageMemoryBarrier imageStartBarrier = { - .srcAccessMask = vk::AccessFlagBits::eNone, - .dstAccessMask = vk::AccessFlagBits::eTransferWrite, +#if !defined(NDEBUG) + StackString<128> loadActionName = "Load: "; + loadActionName += image->name.empty() ? "" : image->name.c_str(); + vk::DebugUtilsLabelEXT debugLabel = { + .pLabelName = loadActionName.c_str(), + .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); +#endif + +#pragma region Barriers and Blits + + vk::ImageMemoryBarrier2 imageStartBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, .oldLayout = vk::ImageLayout::eUndefined, .newLayout = vk::ImageLayout::eTransferDstOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -84,10 +240,18 @@ ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, boo .layerCount = 1, }, }; + vk::DependencyInfo imageStartDependency = { + .memoryBarrierCount = 0, + .bufferMemoryBarrierCount = 0, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageStartBarrier, + }; - vk::ImageMemoryBarrier nextMipBarrier = { - .srcAccessMask = vk::AccessFlagBits::eTransferWrite, - .dstAccessMask = vk::AccessFlagBits::eTransferRead, + vk::ImageMemoryBarrier2 nextMipBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, .oldLayout = vk::ImageLayout::eTransferDstOptimal, .newLayout = vk::ImageLayout::eTransferSrcOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -102,10 +266,16 @@ ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, boo .layerCount = 1, }, }; + vk::DependencyInfo interMipDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &nextMipBarrier, + }; - vk::ImageMemoryBarrier imageReadyBarrier = { - .srcAccessMask = vk::AccessFlagBits::eTransferRead, - .dstAccessMask = vk::AccessFlagBits::eShaderRead, + vk::ImageMemoryBarrier2 imageReadyBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferRead, + .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferSrcOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = m_TransferQueueIndex, @@ -120,8 +290,12 @@ ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, boo .layerCount = 1, }, }; + vk::DependencyInfo imageReadyDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageReadyBarrier, + }; - vk::BufferImageCopy imageCopy = { + vk::BufferImageCopy2 imageCopy = { .bufferOffset = 0, .bufferRowLength = Cast(image->width), .bufferImageHeight = Cast(image->height), @@ -135,16 +309,49 @@ ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, boo .imageOffset = {}, .imageExtent = texture.m_Extent, }; + vk::CopyBufferToImageInfo2 stagingCopyInfo = { + .srcBuffer = stagingBuffer->m_Buffer, + .dstImage = texture.m_Image, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = 1, + .pRegions = &imageCopy, + }; - m_CommandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, 0, - nullptr, 0, nullptr, 1, &imageStartBarrier); - m_CommandBuffer.copyBufferToImage(stagingBuffer->m_Buffer, texture.m_Image, vk::ImageLayout::eTransferDstOptimal, 1, - &imageCopy); - m_CommandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, 0, - nullptr, 0, nullptr, 1, &nextMipBarrier); + vk::ImageBlit2 blitRegion = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + + vk::BlitImageInfo2 mipBlitInfo = { + .srcImage = texture.m_Image, + .srcImageLayout = vk::ImageLayout::eTransferSrcOptimal, + .dstImage = texture.m_Image, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = 1, + .pRegions = &blitRegion, + .filter = vk::Filter::eLinear, + }; + +#pragma endregion + + m_CommandBuffer.pipelineBarrier2(&imageStartDependency); + m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo); + m_CommandBuffer.pipelineBarrier2(&interMipDependency); auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); }; + // Mip Mapping + i32 prevMipWidth = Cast(texture.m_Extent.width); i32 prevMipHeight = Cast(texture.m_Extent.height); @@ -155,51 +362,37 @@ ModelLoader::LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, boo i32 currentMipHeight = calcNextMip(prevMipHeight); u32 currentMipLevel = prevMipLevel + 1; - vk::ImageBlit blitRegion = { - .srcSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = prevMipLevel, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .srcOffsets = - std::array{ - vk::Offset3D{0, 0, 0}, - vk::Offset3D{prevMipWidth, prevMipHeight, 1}, - }, - .dstSubresource = - { - .aspectMask = vk::ImageAspectFlagBits::eColor, - .mipLevel = currentMipLevel, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .dstOffsets = - std::array{ - vk::Offset3D{0, 0, 0}, - vk::Offset3D{currentMipWidth, currentMipHeight, 1}, - }, + blitRegion.srcSubresource.mipLevel = prevMipLevel; + blitRegion.srcOffsets = std::array{ + vk::Offset3D{0, 0, 0}, + vk::Offset3D{prevMipWidth, prevMipHeight, 1}, + }; + blitRegion.dstSubresource.mipLevel = currentMipLevel; + blitRegion.dstOffsets = std::array{ + vk::Offset3D{0, 0, 0}, + vk::Offset3D{currentMipWidth, currentMipHeight, 1}, }; nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel; - m_CommandBuffer.blitImage(texture.m_Image, vk::ImageLayout::eTransferSrcOptimal, texture.m_Image, - vk::ImageLayout::eTransferDstOptimal, 1, &blitRegion, vk::Filter::eLinear); - m_CommandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eTransfer, {}, - 0, nullptr, 0, nullptr, 1, &nextMipBarrier); + + m_CommandBuffer.blitImage2(&mipBlitInfo); + m_CommandBuffer.pipelineBarrier2(&interMipDependency); prevMipHeight = currentMipHeight; prevMipWidth = currentMipWidth; } - m_CommandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, - {}, 0, nullptr, 0, nullptr, 1, &imageReadyBarrier); + m_CommandBuffer.pipelineBarrier2(&imageReadyDependency); + +#if !defined(NDEBUG) + m_CommandBuffer.endDebugUtilsLabelEXT(); +#endif return m_ResourceManager->Commit(&texture); } Model -ModelLoader::LoadModel(cstr path, cstr name, bool batched) +AssetLoader::LoadModelToGpu(cstr path, cstr name) { namespace fs = std::filesystem; tinygltf::Model model; @@ -230,10 +423,17 @@ ModelLoader::LoadModel(cstr path, cstr name, bool batched) } } - { - vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; - AbortIfFailed(m_CommandBuffer.begin(&beginInfo)); - } + AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo)); + +#if !defined(NDEBUG) + StackString<128> loadActionName = "Load: "; + loadActionName += name ? name : path; + vk::DebugUtilsLabelEXT debugLabel = { + .pLabelName = loadActionName.c_str(), + .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel); +#endif eastl::vector stagingBuffers; @@ -258,7 +458,7 @@ ModelLoader::LoadModel(cstr path, cstr name, bool batched) } auto *image = &model.images[index]; - TextureHandle handle = LoadImage(&stagingBuffers.push_back(), image, isSrgb); + TextureHandle handle = LoadImageToGpu(&stagingBuffers.push_back(), image, isSrgb); textureHandleMap.emplace(index, handle); return handle; }; @@ -608,6 +808,9 @@ ModelLoader::LoadModel(cstr path, cstr name, bool batched) #pragma endregion +#if !defined(NDEBUG) + m_CommandBuffer.endDebugUtilsLabelEXT(); +#endif AbortIfFailed(m_CommandBuffer.end()); vk::SubmitInfo submitInfo = { @@ -624,8 +827,7 @@ ModelLoader::LoadModel(cstr path, cstr name, bool batched) AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); pDevice->m_Device.destroy(fence, nullptr); - AbortIfFailed(pDevice->m_Device.resetCommandPool( - m_CommandPool, batched ? vk::CommandPoolResetFlags{} : vk::CommandPoolResetFlagBits::eReleaseResources)); + AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {})); for (auto &buffer : stagingBuffers) { @@ -726,7 +928,7 @@ Model::Update() } } -ModelLoader::ModelLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex, +AssetLoader::AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex, u32 graphicsQueueIndex) : m_ResourceManager(resourceManager) , m_TransferQueue(transferQueue) @@ -741,15 +943,19 @@ ModelLoader::ModelLoader(GpuResourceManager *resourceManager, vk::Queue transfer AbortIfFailedM(pDevice->m_Device.createCommandPool(&poolCreateInfo, nullptr, &m_CommandPool), "Transfer command pool creation failed."); + pDevice->SetName(m_CommandPool, "Asset Loader Command Pool"); + const vk::CommandBufferAllocateInfo commandBufferAllocateInfo = { .commandPool = m_CommandPool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1, }; AbortIfFailed(pDevice->m_Device.allocateCommandBuffers(&commandBufferAllocateInfo, &m_CommandBuffer)); + + pDevice->SetName(m_CommandBuffer, "Asset Loader Command Buffer"); } -ModelLoader::~ModelLoader() +AssetLoader::~AssetLoader() { if (m_ResourceManager) { @@ -757,7 +963,7 @@ ModelLoader::~ModelLoader() } } -ModelLoader::ModelLoader(ModelLoader &&other) noexcept +AssetLoader::AssetLoader(AssetLoader &&other) noexcept : m_ResourceManager(Take(other.m_ResourceManager)) , m_CommandPool(other.m_CommandPool) , m_CommandBuffer(other.m_CommandBuffer) @@ -767,8 +973,8 @@ ModelLoader::ModelLoader(ModelLoader &&other) noexcept { } -ModelLoader & -ModelLoader::operator=(ModelLoader &&other) noexcept +AssetLoader & +AssetLoader::operator=(AssetLoader &&other) noexcept { if (this == &other) return *this; diff --git a/samples/03_model_render/model_loader.h b/samples/03_model_render/asset_loader.h similarity index 84% rename from samples/03_model_render/model_loader.h rename to samples/03_model_render/asset_loader.h index bf9f8ec..853c44d 100644 --- a/samples/03_model_render/model_loader.h +++ b/samples/03_model_render/asset_loader.h @@ -1,5 +1,5 @@ // ============================================= -// Aster: model_loader.h +// Aster: asset_loader.h // Copyright (c) 2020-2024 Anish Bhobe // ============================================= @@ -16,6 +16,7 @@ namespace tinygltf struct Image; } +struct Image; struct TextureHandle; struct Texture; @@ -86,7 +87,7 @@ struct Model const Model &operator=(const Model &) = delete; }; -struct ModelLoader +struct AssetLoader { GpuResourceManager *m_ResourceManager; vk::CommandPool m_CommandPool; @@ -95,8 +96,9 @@ struct ModelLoader u32 m_TransferQueueIndex; u32 m_GraphicsQueueIndex; - TextureHandle LoadImage(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const; - Model LoadModel(cstr path, cstr name = nullptr, bool batched = false); + void LoadHdrImage(Texture *texture, cstr path, cstr name = nullptr) const; + TextureHandle LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const; + Model LoadModelToGpu(cstr path, cstr name = nullptr); constexpr static auto ANormal = "NORMAL"; constexpr static auto APosition = "POSITION"; @@ -107,12 +109,12 @@ struct ModelLoader constexpr static auto AJoints0 = "JOINTS_0"; constexpr static auto AWeights0 = "WEIGHTS_0"; - ModelLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex, + AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex, u32 graphicsQueueIndex); - ~ModelLoader(); + ~AssetLoader(); - ModelLoader(ModelLoader &&other) noexcept; - ModelLoader &operator=(ModelLoader &&other) noexcept; + AssetLoader(AssetLoader &&other) noexcept; + AssetLoader &operator=(AssetLoader &&other) noexcept; - DISALLOW_COPY_AND_ASSIGN(ModelLoader); + DISALLOW_COPY_AND_ASSIGN(AssetLoader); }; \ No newline at end of file diff --git a/samples/03_model_render/ibl_helpers.cpp b/samples/03_model_render/ibl_helpers.cpp new file mode 100644 index 0000000..1f05fe6 --- /dev/null +++ b/samples/03_model_render/ibl_helpers.cpp @@ -0,0 +1,249 @@ +// ============================================= +// Aster: ibl_helpers.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "ibl_helpers.h" + +#include "device.h" +#include "gpu_resource_manager.h" +#include "helpers.h" +#include "image.h" +#include "asset_loader.h" +#include "pipeline_utils.h" + +constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrectToCube.cs.hlsl.spv"; + +TextureHandle +CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv, + const cstr name) +{ + GpuResourceManager *resMan = assetLoader->m_ResourceManager; + const Device *pDevice = resMan->m_Device; + + TextureCube cubeMap; + cubeMap.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, false, name ? name : "Env CubeMap"); + + StorageTexture stagingTexture; + stagingTexture.Init(pDevice, {cubeSide * 3, cubeSide * 2}, vk::Format::eR16G16B16A16Sfloat, false, "EnvStaging"); + auto envStagingHandle = resMan->Commit(&stagingTexture); + + vk::ImageSubresourceRange stagingSubresRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + vk::ImageSubresourceRange cubeSubresRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 6, + }; + + vk::ImageMemoryBarrier2 readyToWriteBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eGeneral, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = stagingTexture.m_Image, + .subresourceRange = stagingSubresRange, + }; + vk::DependencyInfo readyToWriteDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &readyToWriteBarrier, + }; + vk::ImageMemoryBarrier2 stagingReadyForTransfer = { + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = stagingTexture.m_Image, + .subresourceRange = stagingSubresRange, + }; + vk::ImageMemoryBarrier2 cubeReadyForTransfer = { + .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eTransferDstOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = cubeMap.m_Image, + .subresourceRange = cubeSubresRange, + }; + eastl::array transferBarriers = {stagingReadyForTransfer, cubeReadyForTransfer}; + vk::DependencyInfo preTransferDependency = { + .imageMemoryBarrierCount = transferBarriers.size(), + .pImageMemoryBarriers = transferBarriers.data(), + }; + vk::ImageMemoryBarrier2 cubemapToRead = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, + .dstAccessMask = vk::AccessFlagBits2::eShaderSampledRead, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = cubeMap.m_Image, + .subresourceRange = cubeSubresRange, + }; + vk::DependencyInfo cubemapToReadDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &cubemapToRead, + }; + + eastl::array imageCopies; + for (i32 i = 0; i < 6; ++i) + { + imageCopies[i] = vk::ImageCopy2{ + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffset = + { + (i % 3) * Cast(cubeSide), + (i / 3) * Cast(cubeSide), + 0, + }, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = Cast(i), + .layerCount = 1, + }, + .dstOffset = {0, 0, 0}, + .extent = {cubeSide, cubeSide, 1}, + }; + } + + vk::CopyImageInfo2 copyFlatToCube = { + .srcImage = stagingTexture.m_Image, + .srcImageLayout = vk::ImageLayout::eTransferSrcOptimal, + .dstImage = cubeMap.m_Image, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = imageCopies.size(), + .pRegions = imageCopies.data(), + }; + + struct WorkloadPushConstants + { + TextureHandle m_HdrEnvHandle; + StorageTextureHandle m_OutputTexture; + u32 m_CubeSide; + }; + +#pragma region Pipeline Creation etc + const auto shaderModule = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); + + const vk::PipelineShaderStageCreateInfo shaderStageCreateInfo = { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = shaderModule, + .pName = "main", + }; + + vk::PushConstantRange pcr = { + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .offset = 0, + .size = sizeof(WorkloadPushConstants), + }; + + vk::PipelineLayout pipelineLayout; + const vk::PipelineLayoutCreateInfo layoutCreateInfo = { + .setLayoutCount = 1, + .pSetLayouts = &resMan->m_SetLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pcr, + }; + AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout)); + + const vk::ComputePipelineCreateInfo computePipelineCreateInfo = { + .stage = shaderStageCreateInfo, + .layout = pipelineLayout, + }; + + vk::Pipeline pipeline; + AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, 1, &computePipelineCreateInfo, + nullptr, &pipeline)); + + pDevice->m_Device.destroy(shaderModule, nullptr); + +#pragma endregion + + WorkloadPushConstants pushConstants = { + .m_HdrEnvHandle = hdrEnv, .m_OutputTexture = envStagingHandle, .m_CubeSide = cubeSide}; + + resMan->Update(); + + auto cmd = assetLoader->m_CommandBuffer; + constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + AbortIfFailed(cmd.begin(&beginInfo)); + +#if !defined(NDEBUG) + StackString<128> labelName = "Eqrect -> Cubemap: "; + labelName += name ? name : ""; + vk::DebugUtilsLabelEXT label = { + .pLabelName = labelName.c_str(), + .color = std::array{1.0f, 1.0f, 1.0f, 1.0f}, + }; + cmd.beginDebugUtilsLabelEXT(&label); +#endif + + cmd.pipelineBarrier2(&readyToWriteDependency); + + cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr); + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof pushConstants, &pushConstants); + cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); + + cmd.pipelineBarrier2(&preTransferDependency); + + cmd.copyImage2(©FlatToCube); + + cmd.pipelineBarrier2(&cubemapToReadDependency); + +#if !defined(NDEBUG) + cmd.endDebugUtilsLabelEXT(); +#endif + + AbortIfFailed(cmd.end()); + + vk::SubmitInfo submitInfo = { + .waitSemaphoreCount = 0, + .pWaitDstStageMask = nullptr, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + }; + + vk::Fence fence; + vk::FenceCreateInfo fenceCreateInfo = {}; + AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence)); + AbortIfFailed(computeQueue.submit(1, &submitInfo, fence)); + AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue)); + pDevice->m_Device.destroy(fence, nullptr); + + AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {})); + + resMan->Release(envStagingHandle); + pDevice->m_Device.destroy(pipeline, nullptr); + pDevice->m_Device.destroy(pipelineLayout, nullptr); + + return resMan->Commit(&cubeMap); +} diff --git a/samples/03_model_render/ibl_helpers.h b/samples/03_model_render/ibl_helpers.h new file mode 100644 index 0000000..983d7e6 --- /dev/null +++ b/samples/03_model_render/ibl_helpers.h @@ -0,0 +1,15 @@ +// ============================================= +// Aster: ibl_helpers.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" +#include "gpu_resource_manager.h" + +struct Texture; +struct TextureCube; +struct AssetLoader; + +TextureHandle CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, cstr name = nullptr); \ No newline at end of file diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index e9d019f..3bd225c 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -20,7 +20,8 @@ #include "gpu_resource_manager.h" #include "gui.h" -#include "model_loader.h" +#include "ibl_helpers.h" +#include "asset_loader.h" #include "pipeline_utils.h" #include @@ -30,12 +31,26 @@ constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto PIPELINE_CACHE_FILE = "PipelineCacheData.bin"; +constexpr auto MODEL_FILE = "model/DamagedHelmet.glb"; +constexpr auto BACKDROP_FILE = "image/photo_studio_loft_hall_4k.hdr"; +constexpr u32 INIT_WIDTH = 1280; +constexpr u32 INIT_HEIGHT = 720; struct Camera { mat4 m_View; mat4 m_Perspective; - vec4 m_Position; + mat4 m_InverseView; + mat4 m_InversePerspective; + vec3 m_Position; + f32 m_PositionHomogenousPad_ = 1.0f; + + void + CalculateInverses() + { + m_InverseView = inverse(m_View); + m_InversePerspective = inverse(m_Perspective); + } }; struct CameraController @@ -57,9 +72,13 @@ struct CameraController , m_Camera{ .m_View = lookAt(position, target, UP), .m_Perspective = glm::perspective(vFov, aspectRatio, 0.1f, 100.0f), - .m_Position = vec4{0.0f, 2.0f, 2.0f, 1.0f}, + .m_Position = position, } { + const vec3 dir = normalize(target - vec3(position)); + m_Pitch = asin(dir.y); + m_Yaw = acos(-dir.z / sqrt(1.0f - dir.y * dir.y)); + m_Camera.CalculateInverses(); } void @@ -67,6 +86,8 @@ struct CameraController { m_AspectRatio = aspectRatio; m_Camera.m_Perspective = glm::perspective(m_Fov, aspectRatio, 0.1f, 100.0f); + + m_Camera.CalculateInverses(); } void @@ -77,6 +98,8 @@ struct CameraController f32 cosPitch = cos(m_Pitch); const vec3 target = vec3(sin(m_Yaw) * cosPitch, sin(m_Pitch), -cos(m_Yaw) * cosPitch); m_Camera.m_View = lookAt(position, position + target, UP); + + m_Camera.CalculateInverses(); } void @@ -87,8 +110,21 @@ struct CameraController f32 cosPitch = cos(m_Pitch); const vec3 target = vec3(sin(m_Yaw) * cosPitch, sin(m_Pitch), -cos(m_Yaw) * cosPitch); - const vec3 position = vec3{m_Camera.m_Position}; + const vec3 position = m_Camera.m_Position; m_Camera.m_View = lookAt(position, position + target, UP); + + m_Camera.CalculateInverses(); + } + + void + SetLookAt(const vec3 &target) + { + const vec3 dir = normalize(target - m_Camera.m_Position); + m_Pitch = acos(dir.y); + m_Yaw = acos(dir.z / sqrt(1.0f - dir.y * dir.y)); + m_Camera.m_View = lookAt(m_Camera.m_Position, m_Camera.m_Position + target, UP); + + m_Camera.CalculateInverses(); } }; @@ -98,7 +134,7 @@ main(int, char **) MIN_LOG_LEVEL(Logger::LogType::eInfo); Context context = {"ModelRender [WIP]", VERSION}; - Window window = {"ModelRender [WIP] (Aster)", &context, {640, 480}}; + Window window = {"ModelRender [WIP] (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}}; PhysicalDevices physicalDevices = {&window, &context}; PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); @@ -130,22 +166,28 @@ main(int, char **) auto pipelineCacheData = ReadFileBytes(PIPELINE_CACHE_FILE, false); QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); - Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, pipelineCacheData, "Primary Device"}; - vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0); + Device device = {&context, &deviceToUse, &enabledDeviceFeatures, + {queueAllocation}, pipelineCacheData, "Primary Device"}; + vk::Queue graphicsQueue = device.GetQueue(queueAllocation.m_Family, 0); Swapchain swapchain = {&window, &device, "Primary Chain"}; GpuResourceManager resourceManager = {&device, 1000}; - ModelLoader modelLoader = {&resourceManager, commandQueue, queueAllocation.m_Family, queueAllocation.m_Family}; + AssetLoader assetLoader = {&resourceManager, graphicsQueue, queueAllocation.m_Family, queueAllocation.m_Family}; LightManager lightManager = LightManager{&resourceManager}; - Model model = modelLoader.LoadModel(MODEL_FILE); + Model model = assetLoader.LoadModelToGpu(MODEL_FILE); + Texture environment; + assetLoader.LoadHdrImage(&environment, BACKDROP_FILE); + auto envHandle = resourceManager.Commit(&environment); + + TextureHandle texCube = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 1024, envHandle, "Cube Env"); + + resourceManager.Release(envHandle); vk::Format attachmentFormat = vk::Format::eR8G8B8A8Srgb; - Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager); - //lightManager.AddDirectional(vec3(0.0f, -1.0f, 0.0f), {0.0f, 0.7f, 0.0f}); - //lightManager.AddPoint(vec3{2.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, 15.0f); - //lightManager.AddPoint(vec3{-2.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, 15.0f); + Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager); + Pipeline backGroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager); lightManager.AddPoint(vec3{-5.0f, -5.0f, 5.0f}, vec3{1.0f}, 30.0f, 16.0f); lightManager.AddPoint(vec3{5.0f, -5.0f, 5.0f}, vec3{1.0f}, 30.0f, 16.0f); @@ -179,7 +221,7 @@ main(int, char **) vk::Extent2D internalResolution = {1920, 1080}; - CameraController cameraController = {vec3{0.0f, 2.0f, 2.0f}, vec3{0.0f}, 70_deg, + CameraController cameraController = {vec3{0.0f, 0.0f, 2.0f}, vec3{0.0f}, 70_deg, Cast(swapchain.m_Extent.width) / Cast(swapchain.m_Extent.height)}; UniformBuffer ubo; @@ -326,11 +368,12 @@ main(int, char **) } gui::Init(&context, &device, &window, swapchain.m_Format, Cast(swapchain.m_ImageViews.size()), - queueAllocation.m_Family, commandQueue); + queueAllocation.m_Family, graphicsQueue); bool rotating = false; bool lockToScreen = true; i32 height = Cast(internalResolution.height); - vec2 camPitchYaw = vec2(0.0f); + f32 camPitch = glm::degrees(cameraController.m_Pitch); + f32 camYaw = glm::degrees(cameraController.m_Yaw); vec3 camPosition = cameraController.m_Camera.m_Position; vk::Extent2D inputResolution = internalResolution; @@ -349,7 +392,7 @@ main(int, char **) gui::Begin("Settings"); gui::Text("Window Resolution: %ux%u", swapchain.m_Extent.width, swapchain.m_Extent.height); - gui::Text("FrameBuffer Resolution %ux%u", internalResolution.width, internalResolution.height); + gui::Text("Render Resolution: %ux%u", internalResolution.width, internalResolution.height); gui::Checkbox("Lock Resolution to Window", &lockToScreen); if (!lockToScreen) { @@ -390,15 +433,20 @@ main(int, char **) gui::Text("Delta: %0.6f ms", 1000.0f * Time::m_Delta); gui::Text("FPS: %0.6f", 1.0f / Time::m_Delta); gui::Separator(); - if (gui::DragFloat2("Camera Orientation", Recast(&camPitchYaw))) + gui::PushItemWidth(100); + bool yawChange = gui::DragFloat("Camera Yaw", &camYaw); + bool pitchChange = gui::DragFloat("Camera Pitch", &camPitch, 1, -89.0f, 89.0f); + if (yawChange || pitchChange) { - cameraController.SetPitchYaw(glm::radians(camPitchYaw.x), glm::radians(camPitchYaw.y)); + camYaw = camYaw - floor((camYaw + 180.0f) / 360.0f) * 360.0f; + cameraController.SetPitchYaw(glm::radians(camPitch), glm::radians(camYaw)); } if (gui::InputFloat3("Camera Position", Recast(&camPosition))) { cameraController.SetPosition(camPosition); } gui::Checkbox("Rotate", &rotating); + gui::PopItemWidth(); if (gui::Button("Exit")) { window.RequestExit(); @@ -413,6 +461,7 @@ main(int, char **) rotate(model.GetModelTransform(), Cast(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f))); } model.Update(); + cameraController.m_Camera.CalculateInverses(); ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window); @@ -485,17 +534,20 @@ main(int, char **) 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, &resourceManager.m_DescriptorSet, 0, nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &descriptorSet, 0, nullptr); cmd.bindIndexBuffer(model.m_IndexBuffer.m_Buffer, 0, vk::IndexType::eUint32); + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + u32 pcbOffset = 0; cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles, &model.m_Handles); pcbOffset += sizeof model.m_Handles; + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube); + pcbOffset += sizeof texCube; cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); pcbOffset += sizeof lightManager.m_MetaInfo; @@ -512,6 +564,9 @@ main(int, char **) cmd.drawIndexed(prim.m_IndexCount, 1, prim.m_FirstIndex, Cast(prim.m_VertexOffset), 0); } + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backGroundPipeline.m_Pipeline); + cmd.draw(3, 1, 0, 0); + cmd.endRendering(); cmd.pipelineBarrier2(&postRenderDependencies); @@ -563,13 +618,15 @@ main(int, char **) .signalSemaphoreCount = 1, .pSignalSemaphores = ¤tFrame->m_RenderFinishSem, }; - AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); + AbortIfFailed(graphicsQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); - currentFrame->Present(commandQueue, &swapchain, &window); + currentFrame->Present(graphicsQueue, &swapchain, &window); } AbortIfFailed(device.m_Device.waitIdle()); + resourceManager.Release(texCube); + pipelineCacheData = device.DumpPipelineCache(); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); diff --git a/samples/03_model_render/pipeline_utils.cpp b/samples/03_model_render/pipeline_utils.cpp index 642c51c..53179d2 100644 --- a/samples/03_model_render/pipeline_utils.cpp +++ b/samples/03_model_render/pipeline_utils.cpp @@ -8,7 +8,6 @@ #include "device.h" #include "gpu_resource_manager.h" #include "helpers.h" -#include "swapchain.h" #include @@ -58,7 +57,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou vk::PushConstantRange pushConstantRange = { .stageFlags = vk::ShaderStageFlagBits::eAll, .offset = 0, - .size = 36, + .size = 40, }; vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { @@ -88,7 +87,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = vk::PolygonMode::eFill, - .cullMode = vk::CullModeFlagBits::eNone, + .cullMode = vk::CullModeFlagBits::eBack, .frontFace = vk::FrontFace::eCounterClockwise, .depthBiasEnable = false, .lineWidth = 1.0, @@ -151,7 +150,8 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou .layout = pipelineLayout, }; vk::Pipeline pipeline; - AbortIfFailed(device->m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline)); + AbortIfFailed( + device->m_Device.createGraphicsPipelines(device->m_PipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); device->SetName(pipeline, "Box Pipeline"); device->m_Device.destroy(vertexShaderModule, nullptr); @@ -160,6 +160,155 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; } +Pipeline +CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager) +{ + // Pipeline Setup + auto vertexShaderModule = CreateShader(device, BACKGROUND_VERTEX_SHADER_FILE); + auto fragmentShaderModule = CreateShader(device, BACKGROUND_FRAGMENT_SHADER_FILE); + + eastl::array shaderStages = {{ + { + .stage = vk::ShaderStageFlagBits::eVertex, + .module = vertexShaderModule, + .pName = "main", + }, + { + .stage = vk::ShaderStageFlagBits::eFragment, + .module = fragmentShaderModule, + .pName = "main", + }, + }}; + + eastl::vector descriptorSetLayouts; + + descriptorSetLayouts.push_back(resourceManager->m_SetLayout); + + { + eastl::array descriptorSetLayoutBindings = { + vk::DescriptorSetLayoutBinding{ + .binding = 0, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + }; + vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { + .bindingCount = Cast(descriptorSetLayoutBindings.size()), + .pBindings = descriptorSetLayoutBindings.data(), + }; + vk::DescriptorSetLayout descriptorSetLayout; + AbortIfFailed( + device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout)); + descriptorSetLayouts.push_back(descriptorSetLayout); + } + + vk::PushConstantRange pushConstantRange = { + .stageFlags = vk::ShaderStageFlagBits::eAll, + .offset = 0, + .size = 40, + }; + + vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { + .setLayoutCount = Cast(descriptorSetLayouts.size()), + .pSetLayouts = descriptorSetLayouts.data(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstantRange, + }; + vk::PipelineLayout pipelineLayout; + AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); + device->SetName(pipelineLayout, "Box Layout"); + + descriptorSetLayouts[0] = nullptr; // Not owned. + + vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {}; + vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = { + .topology = vk::PrimitiveTopology::eTriangleList, + .primitiveRestartEnable = false, + }; + + vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = { + .viewportCount = 1, + .scissorCount = 1, + }; + + vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = { + .depthClampEnable = false, + .rasterizerDiscardEnable = false, + .polygonMode = vk::PolygonMode::eFill, + .cullMode = vk::CullModeFlagBits::eBack, + .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::eLessOrEqual, + }; + vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { + .blendEnable = false, + .srcColorBlendFactor = vk::BlendFactor::eSrcColor, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }; + vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = { + .logicOpEnable = false, + .attachmentCount = 1, + .pAttachments = &colorBlendAttachmentState, + }; + + eastl::array dynamicStates = { + vk::DynamicState::eScissor, + vk::DynamicState::eViewport, + }; + + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = { + .dynamicStateCount = Cast(dynamicStates.size()), + .pDynamicStates = dynamicStates.data(), + }; + + vk::PipelineRenderingCreateInfo renderingCreateInfo = { + .viewMask = 0, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &attachmentFormat, + .depthAttachmentFormat = vk::Format::eD24UnormS8Uint, + }; + + vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { + .pNext = &renderingCreateInfo, + .stageCount = 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(device->m_PipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline)); + device->SetName(pipeline, "BG Pipeline"); + + device->m_Device.destroy(vertexShaderModule, nullptr); + device->m_Device.destroy(fragmentShaderModule, nullptr); + + return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; +} + vk::ShaderModule CreateShader(const Device *device, cstr shaderFile) { diff --git a/samples/03_model_render/pipeline_utils.h b/samples/03_model_render/pipeline_utils.h index df9e29f..bf9b495 100644 --- a/samples/03_model_render/pipeline_utils.h +++ b/samples/03_model_render/pipeline_utils.h @@ -15,5 +15,10 @@ struct Device; constexpr auto VERTEX_SHADER_FILE = "shader/model.vs.hlsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/model.ps.hlsl.spv"; +constexpr auto BACKGROUND_VERTEX_SHADER_FILE = "shader/background.vs.hlsl.spv"; +constexpr auto BACKGROUND_FRAGMENT_SHADER_FILE = "shader/background.ps.hlsl.spv"; + vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); Pipeline CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager); +Pipeline +CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const GpuResourceManager *resourceManager); \ No newline at end of file diff --git a/samples/03_model_render/shader/background.ps.hlsl b/samples/03_model_render/shader/background.ps.hlsl new file mode 100644 index 0000000..8d5bb82 --- /dev/null +++ b/samples/03_model_render/shader/background.ps.hlsl @@ -0,0 +1,22 @@ +#include "graphics_structs.hlsli" + +struct PS_Input +{ + float3 WorldPosition : POSITION; +}; + +struct PS_Output +{ + float4 Color : SV_Target0; +}; + +PS_Output main(PS_Input StageInput) +{ + PS_Output StageOutput; + + float3 Direction = normalize(StageInput.WorldPosition - Camera.Position.xyz); + float4 Color = TextureCubes[PushConstant.EnvCubeHandle].Sample(ImmutableSamplers[PushConstant.EnvCubeHandle], Direction); + StageOutput.Color = float4(Uncharted2Tonemap(Color.rgb), Color.a); + + return StageOutput; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/background.vs.hlsl b/samples/03_model_render/shader/background.vs.hlsl new file mode 100644 index 0000000..e409d1a --- /dev/null +++ b/samples/03_model_render/shader/background.vs.hlsl @@ -0,0 +1,32 @@ +#include "graphics_structs.hlsli" + + +struct VS_Input +{ + uint VertexIndex : SV_VertexID; +}; + +struct VS_Output +{ + float4 VertexPosition : SV_Position; + float3 WorldPosition : POSITION; +}; + +VS_Output main(VS_Input StageInput) +{ + float3 Points[] = + { + float3(-1.0f, -1.0f, 1.0f), + float3(3.0f, -1.0f, 1.0f), + float3(-1.0f, 3.0f, 1.0f), + }; + + VS_Output StageOutput; + StageOutput.VertexPosition = float4(Points[StageInput.VertexIndex], 1.0f); + + float4 ClipSpace = mul(Camera.InvProjection, float4(Points[StageInput.VertexIndex], 1.0f)); + float4 WorldSpace = mul(Camera.InvView, ClipSpace / ClipSpace.wwww); + StageOutput.WorldPosition = WorldSpace.xyz; + + return StageOutput; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/bindless_structs.hlsli b/samples/03_model_render/shader/bindless_structs.hlsli index f974761..8601077 100644 --- a/samples/03_model_render/shader/bindless_structs.hlsli +++ b/samples/03_model_render/shader/bindless_structs.hlsli @@ -26,19 +26,6 @@ struct MaterialData uint EmissionTex; }; -struct Block -{ - uint VertexBufferHandle; - uint VertexDataHandle; - uint MaterialBufferHandle; - uint NodeBufferHandle; - uint LightHandle; - uint PointLightIndexer; - uint DirectionalLightIndexer; - int MaterialIdx; - uint NodeIdx; -}; - struct PointLight { float Position[3]; @@ -55,13 +42,6 @@ struct DirectionalLight float Intensity; }; -struct CameraData -{ - float4x4 View; - float4x4 Projection; - float4 Position; -}; - // Little Endian storage. First short is least significant. #define IndexerCount(Indexer) (Indexer & 0xFFFF) #define IndexerOffset(Indexer) ((Indexer & 0xFFFF0000) >> 16); @@ -78,9 +58,7 @@ static const float PI = 3.14159265f; [[vk::binding(0, 0)]] StructuredBuffer DirectionalLightBuffer[]; [[vk::binding(1, 0)]] Texture2D Textures[]; +[[vk::binding(1, 0)]] TextureCube TextureCubes[]; [[vk::binding(1, 0)]] SamplerState ImmutableSamplers[]; -[[vk::binding(0, 1)]] ConstantBuffer Camera; - -[[vk::push_constant]] -Block PushConstant; \ No newline at end of file +[[vk::binding(2, 0)]] RWTexture2D StorageTextures[]; diff --git a/samples/03_model_render/shader/eqrectToCube.cs.hlsl b/samples/03_model_render/shader/eqrectToCube.cs.hlsl new file mode 100644 index 0000000..021d583 --- /dev/null +++ b/samples/03_model_render/shader/eqrectToCube.cs.hlsl @@ -0,0 +1,58 @@ +#include "bindless_structs.hlsli" + +struct Block +{ + uint HdrEnvHandle; + uint OutputTextureHandle; + int CubeSize; +}; + +[[vk::push_constant]] +Block pcb; + +float2 SampleSphericalMap(float3 v) +{ + const float2 invAtan = float2(0.1591f, 0.3183f); + float2 uv = float2(atan2(v.x, v.z), asin(v.y)); + uv *= invAtan; + uv += 0.5; + return uv; +} + +/* +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| -z | 4 | -y | +| +z | 5 | -y | +*/ + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float3 LocalDir; + float HalfSide = pcb.CubeSize / 2; + float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2)); + + if (GlobalInvocationID.z < 2) + { + LocalDir = float3(AxisSign * HalfSide, GlobalInvocationID.y - HalfSide, (GlobalInvocationID.x - HalfSide) * AxisSign); + } + else if (GlobalInvocationID.z < 4) + { + LocalDir = float3(GlobalInvocationID.x - HalfSide, -AxisSign * HalfSide, -AxisSign * (GlobalInvocationID.y - HalfSide)); + } + else + { + LocalDir = float3((GlobalInvocationID.x - HalfSide) * AxisSign, GlobalInvocationID.y - HalfSide, -AxisSign * HalfSide); + } + + uint TileX = GlobalInvocationID.z % 3; + uint TileY = GlobalInvocationID.z / 3; + + float2 UV = SampleSphericalMap(normalize(LocalDir)); + StorageTextures[pcb.OutputTextureHandle][GlobalInvocationID.xy + uint2(TileX, TileY) * pcb.CubeSize] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0); +} \ No newline at end of file diff --git a/samples/03_model_render/shader/graphics_structs.hlsli b/samples/03_model_render/shader/graphics_structs.hlsli new file mode 100644 index 0000000..4f45bfa --- /dev/null +++ b/samples/03_model_render/shader/graphics_structs.hlsli @@ -0,0 +1,41 @@ +#include "bindless_structs.hlsli" + +struct CameraData +{ + float4x4 View; + float4x4 Projection; + float4x4 InvView; + float4x4 InvProjection; + float4 Position; +}; + +struct Block +{ + uint VertexBufferHandle; + uint VertexDataHandle; + uint MaterialBufferHandle; + uint NodeBufferHandle; + uint EnvCubeHandle; + uint LightHandle; + uint PointLightIndexer; + uint DirectionalLightIndexer; + int MaterialIdx; + uint NodeIdx; +}; + +[[vk::binding(0, 1)]] ConstantBuffer Camera; + +[[vk::push_constant]] +Block PushConstant; + +float3 Uncharted2Tonemap(float3 color) +{ + float A = 0.15f; + float B = 0.50f; + float C = 0.10f; + float D = 0.20f; + float E = 0.02f; + float F = 0.30f; + float W = 11.2f; + return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/model.ps.hlsl b/samples/03_model_render/shader/model.ps.hlsl index c716893..f9b5e2b 100644 --- a/samples/03_model_render/shader/model.ps.hlsl +++ b/samples/03_model_render/shader/model.ps.hlsl @@ -1,4 +1,4 @@ -#include "bindless_structs.hlsli" +#include "graphics_structs.hlsli" struct FS_Input { @@ -10,7 +10,7 @@ struct FS_Input struct FS_Output { - float4 ColorTarget : SV_Target0; + float4 ColorTarget : SV_Target0; }; float4 GetAlbedo(float2 UV, float4 InColor) @@ -48,7 +48,7 @@ float3 GetNormal(float3 Position, float3 Normal, float2 UV) float3 T = normalize(q1 * st2.y - q2 * st1.y).xyz; float3 B = -normalize(cross(N, T)); - float3x3 TBN = float3x3(T, B, N); // Construction is Row by Row. + float3x3 TBN = float3x3(T, B, N); // Construction is Row by Row. return normalize(mul(TangentSpaceNormal, TBN)); // Post multiple to avoid transpose. } @@ -97,7 +97,7 @@ float GeometrySmith(float NdotV, float NdotL, float Roughness) // https://en.wikipedia.org/wiki/Schlick%27s_approximation float3 FresnelSchlick(float cosine, float3 F_0) { - return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. + return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. } float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 Normal, float Metallic, float Roughness, float3 F_0, float3 LightDir, float LightDistance) @@ -217,18 +217,6 @@ float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Pos return Contrib; } -float3 Uncharted2Tonemap(float3 color) -{ - float A = 0.15f; - float B = 0.50f; - float C = 0.10f; - float D = 0.20f; - float E = 0.02f; - float F = 0.30f; - float W = 11.2f; - return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; -} - FS_Output main(FS_Input StageInput) { FS_Output Output; diff --git a/samples/03_model_render/shader/model.vs.hlsl b/samples/03_model_render/shader/model.vs.hlsl index 1a82cfc..ec65fcc 100644 --- a/samples/03_model_render/shader/model.vs.hlsl +++ b/samples/03_model_render/shader/model.vs.hlsl @@ -1,8 +1,8 @@ -#include "bindless_structs.hlsli" +#include "graphics_structs.hlsli" struct VS_Input { - uint VertexIndex : SV_VertexID; + uint VertexIndex : SV_VertexID; }; struct VS_Output