From c6987a9d5d78846991c02830cb9ae71aba26d872 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 1 Sep 2024 12:48:49 +0200 Subject: [PATCH 1/6] Fixed buffers, and restructured samples. --- aster/buffer.cpp | 7 +-- aster/config.h | 1 + aster/constants.h | 43 +++++++++++++++++-- aster/device.cpp | 8 ++++ aster/device.h | 2 + samples/00_util/CMakeLists.txt | 2 - samples/01_triangle/triangle.cpp | 3 +- samples/02_box/box.cpp | 2 +- samples/03_model_render/CMakeLists.txt | 2 + .../gpu_resource_manager.cpp | 0 .../gpu_resource_manager.h | 0 samples/03_model_render/model_render.cpp | 6 +-- 12 files changed, 61 insertions(+), 15 deletions(-) rename samples/{00_util => 03_model_render}/gpu_resource_manager.cpp (100%) rename samples/{00_util => 03_model_render}/gpu_resource_manager.h (100%) diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 176f897..92746c3 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -21,6 +21,7 @@ void Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name) { + assert(!IsValid()); assert(size <= SIZE_MASK); vk::BufferCreateInfo bufferCreateInfo = { @@ -104,7 +105,7 @@ StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr nam else { Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); + 0, VMA_MEMORY_USAGE_AUTO, name); } } @@ -112,14 +113,14 @@ void VertexBuffer::Init(const Device *device, usize size, cstr name) { Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst, - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); + 0, VMA_MEMORY_USAGE_AUTO, name); } void IndexBuffer::Init(const Device *device, usize size, cstr name) { Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst, - VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name); + 0, VMA_MEMORY_USAGE_AUTO, name); } void diff --git a/aster/config.h b/aster/config.h index 846fc6e..44b5bb4 100644 --- a/aster/config.h +++ b/aster/config.h @@ -8,6 +8,7 @@ #define GLM_FORCE_RADIANS #define GLM_FORCE_DEPTH_ZERO_TO_ONE #define GLM_FORCE_QUATERNIONS_XYZW +#define GLM_ENABLE_EXPERIMENTAL #define GLFW_INCLUDE_VULKAN #define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1 diff --git a/aster/constants.h b/aster/constants.h index 0889612..f6525b2 100644 --- a/aster/constants.h +++ b/aster/constants.h @@ -11,6 +11,7 @@ #include #include +#include using c8 = char; using u8 = uint8_t; @@ -57,22 +58,26 @@ Recast(TFrom &&in) return reinterpret_cast(std::forward(in)); } -constexpr f32 operator""_deg(long double degrees) +constexpr f32 +operator""_deg(long double degrees) { return glm::radians(Cast(degrees)); } -constexpr f32 operator""_deg(unsigned long long int degrees) +constexpr f32 +operator""_deg(unsigned long long int degrees) { return glm::radians(Cast(degrees)); } -constexpr f32 operator""_rad(long double rads) +constexpr f32 +operator""_rad(long double rads) { return Cast(rads); } -constexpr f32 operator""_rad(unsigned long long int rads) +constexpr f32 +operator""_rad(unsigned long long int rads) { return Cast(rads); } @@ -110,6 +115,36 @@ constexpr Version VERSION = { .m_Patch = 0, }; +constexpr u32 +Kilobyte(const u32 in) +{ + return in * 1024; +} + +constexpr usize +Kilobyte(const usize in) +{ + return in * 1024; +} + +constexpr u32 +Megabyte(const u32 in) +{ + return in * 1024 * 1024; +} + +constexpr usize +Megabyte(const usize in) +{ + return in * 1024 * 1024; +} + +constexpr usize +Gigabyte(const usize in) +{ + return in * 1024 * 1024 * 1024; +} + template constexpr T MaxValue = std::numeric_limits::max(); diff --git a/aster/device.cpp b/aster/device.cpp index 87837dd..8d67dba 100644 --- a/aster/device.cpp +++ b/aster/device.cpp @@ -142,6 +142,14 @@ Device::DumpPipelineCache() const return pipelineCacheData; } +void +Device::WaitIdle() const +{ + auto deviceIdleResult = m_Device.waitIdle(); + ERROR_IF(Failed(deviceIdleResult), "Device Idling Failed. Cause: {}", deviceIdleResult) + THEN_ABORT(deviceIdleResult); +} + Device::Device(Device &&other) noexcept : m_Name(std::move(other.m_Name)) , m_PhysicalDevice(Take(other.m_PhysicalDevice)) diff --git a/aster/device.h b/aster/device.h index 373bc87..f3ad2d2 100644 --- a/aster/device.h +++ b/aster/device.h @@ -37,6 +37,8 @@ struct Device final [[nodiscard]] eastl::vector DumpPipelineCache() const; + void WaitIdle() const; + // Ctor/Dtor Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures, const eastl::vector &queueAllocations, NameString &&name); diff --git a/samples/00_util/CMakeLists.txt b/samples/00_util/CMakeLists.txt index fdd2ffa..c122983 100644 --- a/samples/00_util/CMakeLists.txt +++ b/samples/00_util/CMakeLists.txt @@ -9,8 +9,6 @@ add_library(util_helper STATIC helpers.cpp frame.cpp frame.h - gpu_resource_manager.cpp - gpu_resource_manager.h gui.cpp gui.h) diff --git a/samples/01_triangle/triangle.cpp b/samples/01_triangle/triangle.cpp index 53798db..980a5e5 100644 --- a/samples/01_triangle/triangle.cpp +++ b/samples/01_triangle/triangle.cpp @@ -331,8 +331,7 @@ main(int, char **) frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT; } - auto result = device.m_Device.waitIdle(); - ERROR_IF(Failed(result), "Wait idle failed. Cause: {}", result); + device.WaitIdle(); device.m_Device.destroy(copyPool, nullptr); vbo.Destroy(&device); diff --git a/samples/02_box/box.cpp b/samples/02_box/box.cpp index 9e2a041..1951dc5 100644 --- a/samples/02_box/box.cpp +++ b/samples/02_box/box.cpp @@ -531,7 +531,7 @@ main(int, char **) currentFrame->Present(commandQueue, &swapchain, &window); } - AbortIfFailed(device.m_Device.waitIdle()); + device.WaitIdle(); for (auto &depthImage : depthImages) { diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index 9a0faa9..a6940c5 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(model_render model_render.cpp asset_loader.h light_manager.cpp light_manager.h + gpu_resource_manager.cpp + gpu_resource_manager.h nodes.cpp nodes.h ibl_helpers.cpp diff --git a/samples/00_util/gpu_resource_manager.cpp b/samples/03_model_render/gpu_resource_manager.cpp similarity index 100% rename from samples/00_util/gpu_resource_manager.cpp rename to samples/03_model_render/gpu_resource_manager.cpp diff --git a/samples/00_util/gpu_resource_manager.h b/samples/03_model_render/gpu_resource_manager.h similarity index 100% rename from samples/00_util/gpu_resource_manager.h rename to samples/03_model_render/gpu_resource_manager.h diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index ca5f7ef..7b6fd1a 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -133,8 +133,8 @@ main(int, char **) { MIN_LOG_LEVEL(Logger::LogType::eInfo); - Context context = {"ModelRender [WIP]", VERSION}; - Window window = {"ModelRender [WIP] (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}}; + Context context = {"ModelRender", VERSION}; + Window window = {"ModelRender (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}}; PhysicalDevices physicalDevices = {&window, &context}; PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); @@ -689,7 +689,7 @@ main(int, char **) currentFrame->Present(graphicsQueue, &swapchain, &window); } - AbortIfFailed(device.m_Device.waitIdle()); + device.WaitIdle(); environment.Destroy(&resourceManager); -- 2.43.1 From 978ed648fd895b6182db9deda00dbd9128f30378 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sat, 7 Sep 2024 18:58:45 +0200 Subject: [PATCH 2/6] [WIP] Using Device Addr to simplify objs. --- add_shader.cmake | 8 +- aster/buffer.cpp | 40 +- aster/buffer.h | 8 + aster/constants.h | 2 +- aster/device.cpp | 1 + samples/01_triangle/CMakeLists.txt | 2 +- .../model/AlphaBlendModeTest.glb | 3 + samples/03_model_render/model_render.cpp | 2 - samples/04_scenes/CMakeLists.txt | 29 + samples/04_scenes/asset_loader.cpp | 1070 +++++++++++++++++ samples/04_scenes/asset_loader.h | 111 ++ samples/04_scenes/camera.h | 98 ++ samples/04_scenes/core_components.h | 49 + samples/04_scenes/ecs_adapter.h | 21 + samples/04_scenes/image/.gitattributes | 2 + samples/04_scenes/main.cpp | 529 ++++++++ .../04_scenes/model/AlphaBlendModeTest.glb | 3 + samples/04_scenes/model/Box.glb | 3 + samples/04_scenes/model/DamagedHelmet.glb | 3 + .../model/MarbleBust/marble_bust_01.bin | 3 + .../model/MarbleBust/marble_bust_01_4k.gltf | 161 +++ .../textures/marble_bust_01_diff_4k.jpg | 3 + .../textures/marble_bust_01_nor_gl_4k.jpg | 3 + .../textures/marble_bust_01_rough_4k.jpg | 3 + samples/04_scenes/model/OrientationTest.glb | 3 + samples/04_scenes/pipeline_utils.cpp | 189 +++ samples/04_scenes/pipeline_utils.h | 20 + samples/04_scenes/render_resource_manager.cpp | 943 +++++++++++++++ samples/04_scenes/render_resource_manager.h | 250 ++++ samples/04_scenes/shader/model.frag.glsl | 18 + samples/04_scenes/shader/model.vert.glsl | 56 + samples/04_scenes/shader/model.vs.hlsl | 62 + samples/CMakeLists.txt | 1 + vcpkg.json | 7 +- 34 files changed, 3695 insertions(+), 11 deletions(-) create mode 100644 samples/03_model_render/model/AlphaBlendModeTest.glb create mode 100644 samples/04_scenes/CMakeLists.txt create mode 100644 samples/04_scenes/asset_loader.cpp create mode 100644 samples/04_scenes/asset_loader.h create mode 100644 samples/04_scenes/camera.h create mode 100644 samples/04_scenes/core_components.h create mode 100644 samples/04_scenes/ecs_adapter.h create mode 100644 samples/04_scenes/image/.gitattributes create mode 100644 samples/04_scenes/main.cpp create mode 100644 samples/04_scenes/model/AlphaBlendModeTest.glb create mode 100644 samples/04_scenes/model/Box.glb create mode 100644 samples/04_scenes/model/DamagedHelmet.glb create mode 100644 samples/04_scenes/model/MarbleBust/marble_bust_01.bin create mode 100644 samples/04_scenes/model/MarbleBust/marble_bust_01_4k.gltf create mode 100644 samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg create mode 100644 samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg create mode 100644 samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg create mode 100644 samples/04_scenes/model/OrientationTest.glb create mode 100644 samples/04_scenes/pipeline_utils.cpp create mode 100644 samples/04_scenes/pipeline_utils.h create mode 100644 samples/04_scenes/render_resource_manager.cpp create mode 100644 samples/04_scenes/render_resource_manager.h create mode 100644 samples/04_scenes/shader/model.frag.glsl create mode 100644 samples/04_scenes/shader/model.vert.glsl create mode 100644 samples/04_scenes/shader/model.vs.hlsl diff --git a/add_shader.cmake b/add_shader.cmake index e4738ca..d9c2ccb 100644 --- a/add_shader.cmake +++ b/add_shader.cmake @@ -33,4 +33,10 @@ function(add_shader TARGET SHADER) # Make sure our build depends on this output. set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE) target_sources(${TARGET} PRIVATE ${current-output-path}) -endfunction(add_shader) \ No newline at end of file +endfunction(add_shader) + +function(add_shaders TARGET SHADERS) + foreach(shader IN ${SHADERS}) + add_shader(TARGET ${shader}) + endforeach() +endfunction(add_shaders) \ No newline at end of file diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 92746c3..2085cc5 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -95,17 +95,51 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name) void StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name) { + Init(device, size, hostVisible, false, name); +} + +void +StorageBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name) +{ + vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer; + if (deviceAddress) + { + usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress; + } if (hostVisible) { - Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer, + Allocate(device, size, usage, VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, VMA_MEMORY_USAGE_AUTO, name); } else { - Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst, - 0, VMA_MEMORY_USAGE_AUTO, name); + usage |= vk::BufferUsageFlagBits::eTransferDst; + Allocate(device, size, usage, 0, + VMA_MEMORY_USAGE_AUTO, name); + } +} + +void +StorageIndexBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name) +{ + vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer; + if (deviceAddress) + { + usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress; + } + if (hostVisible) + { + Allocate(device, size, usage, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + VMA_MEMORY_USAGE_AUTO, name); + } + else + { + usage |= vk::BufferUsageFlagBits::eTransferDst; + Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name); } } diff --git a/aster/buffer.h b/aster/buffer.h index 4bb7917..cee1edb 100644 --- a/aster/buffer.h +++ b/aster/buffer.h @@ -9,6 +9,8 @@ struct Device; +// TODO Refactor the Buffer Hierarchy + struct Buffer { vk::Buffer m_Buffer = nullptr; @@ -49,6 +51,12 @@ struct UniformBuffer : Buffer struct StorageBuffer : Buffer { void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr); + void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr); +}; + +struct StorageIndexBuffer : StorageBuffer +{ + void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr); }; struct VertexBuffer : Buffer diff --git a/aster/constants.h b/aster/constants.h index f6525b2..563bd8f 100644 --- a/aster/constants.h +++ b/aster/constants.h @@ -11,7 +11,7 @@ #include #include -#include +#include using c8 = char; using u8 = uint8_t; diff --git a/aster/device.cpp b/aster/device.cpp index 8d67dba..e6157f0 100644 --- a/aster/device.cpp +++ b/aster/device.cpp @@ -81,6 +81,7 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features }; const VmaAllocatorCreateInfo allocatorCreateInfo = { + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, .physicalDevice = m_PhysicalDevice, .device = m_Device, .pVulkanFunctions = &vmaVulkanFunctions, diff --git a/samples/01_triangle/CMakeLists.txt b/samples/01_triangle/CMakeLists.txt index 9aa4b20..9a0b007 100644 --- a/samples/01_triangle/CMakeLists.txt +++ b/samples/01_triangle/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) -add_executable(triangle "triangle.cpp") +add_executable(triangle triangle.cpp) add_shader(triangle shader/triangle.vert.glsl) add_shader(triangle shader/triangle.frag.glsl) diff --git a/samples/03_model_render/model/AlphaBlendModeTest.glb b/samples/03_model_render/model/AlphaBlendModeTest.glb new file mode 100644 index 0000000..3c7e82a --- /dev/null +++ b/samples/03_model_render/model/AlphaBlendModeTest.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:688786ffa64990071cfd93c96f29917fddd3d254d3f0e48039d80d3b5ac0d8c7 +size 3017136 diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 7b6fd1a..2296562 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -584,8 +584,6 @@ main(int, char **) 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, &perFrameDescriptor, 0, - nullptr); cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0, nullptr); diff --git a/samples/04_scenes/CMakeLists.txt b/samples/04_scenes/CMakeLists.txt new file mode 100644 index 0000000..ef40a12 --- /dev/null +++ b/samples/04_scenes/CMakeLists.txt @@ -0,0 +1,29 @@ +# CMakeList.txt ; CMake project for box + +cmake_minimum_required(VERSION 3.13) + +#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address") +find_path(TINYGLTF_INCLUDE_DIRS "tiny_gltf.h") + +find_package(EnTT REQUIRED CONFIG) + +add_executable(scene_render main.cpp + render_resource_manager.cpp render_resource_manager.h + asset_loader.cpp asset_loader.h + pipeline_utils.cpp pipeline_utils.h + core_components.h + ecs_adapter.h + camera.h) + +add_shader(scene_render shader/model.frag.glsl) +add_shader(scene_render shader/model.vert.glsl) +# add_shader(scene_render shader/model.vs.hlsl) + +target_link_libraries(scene_render PRIVATE aster_core) +target_link_libraries(scene_render PRIVATE util_helper) +target_link_libraries(scene_render PRIVATE EnTT::EnTT) + +target_include_directories(scene_render PRIVATE ${TINYGLTF_INCLUDE_DIRS}) + +add_resource_dir(scene_render model) +add_resource_dir(scene_render image) diff --git a/samples/04_scenes/asset_loader.cpp b/samples/04_scenes/asset_loader.cpp new file mode 100644 index 0000000..cf7d5b9 --- /dev/null +++ b/samples/04_scenes/asset_loader.cpp @@ -0,0 +1,1070 @@ +// ============================================= +// Aster: asset_loader.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#define TINYGLTF_NOEXCEPTION +#define JSON_NOEXCEPTION + +#define TINYGLTF_IMPLEMENTATION +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#include "asset_loader.h" + +#include "buffer.h" +#include "device.h" +#include "image.h" + +#include "core_components.h" +#include "helpers.h" +#include "render_resource_manager.h" + +#include +#include +#include +#include +#include + +#include + +#if defined(LoadImage) +#undef LoadImage +#endif + +struct Nodes; +constexpr vk::CommandBufferBeginInfo OneTimeCmdBeginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + +vec4 +VectorToVec4(const std::vector &vec) +{ + if (vec.empty()) + { + return vec4{0.0f}; + } + assert(vec.size() == 4); + + return glm::make_vec4(vec.data()); +} + +vec3 +VectorToVec3(const std::vector &vec, const f32 defaultScalar = 0.0f) +{ + if (vec.empty()) + { + return vec3{defaultScalar}; + } + assert(vec.size() == 3); + + return glm::make_vec3(vec.data()); +} + +quat +VectorToQuat(const std::vector &vec) +{ + if (vec.empty()) + { + return glm::identity(); + } + assert(vec.size() == 4); + + return glm::make_quat(vec.data()); +} + +void +Model::Destroy(RenderResourceManager *resourceManager, EcsRegistry *registry) +{ + for (auto texture : m_Textures) + { + resourceManager->Release(texture); + } + m_Textures.clear(); + + registry->destroy(m_Entities.begin(), m_Entities.end()); + m_Entities.clear(); + + resourceManager->Release(Take(m_IndexHandle)); + resourceManager->Release(Take(m_VertexDataHandle)); + resourceManager->Release(Take(m_VertexDataHandle)); + resourceManager->Release(Take(m_MaterialHandle)); + + m_RootEntity = {}; +} + +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; + 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); + + stbi_image_free(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(ASTER_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(ASTER_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); +} + +void +GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout, + vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage) +{ +#if !defined(ASTER_NDEBUG) + vk::DebugUtilsLabelEXT label = { + .pLabelName = "Generate Mipmap", + .color = std::array{0.9f, 0.9f, 0.9f, 1.0f}, + }; + commandBuffer.beginDebugUtilsLabelEXT(&label); +#endif + + vk::ImageMemoryBarrier2 imageStartBarrier = { + .srcStageMask = prevStage, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .oldLayout = initialLayout, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .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 = texture->m_LayerCount, + }, + }; + vk::ImageMemoryBarrier2 mipsStartBarrier = imageStartBarrier; + mipsStartBarrier.dstAccessMask = vk::AccessFlagBits2::eTransferWrite; + mipsStartBarrier.oldLayout = vk::ImageLayout::eUndefined; + mipsStartBarrier.newLayout = vk::ImageLayout::eTransferDstOptimal; + mipsStartBarrier.subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 1, + .levelCount = texture->GetMipLevels() - 1, + .baseArrayLayer = 0, + .layerCount = texture->m_LayerCount, + }; + eastl::fixed_vector startBarriers = { + mipsStartBarrier, + }; + if (initialLayout != imageStartBarrier.newLayout) + { + startBarriers.push_back(imageStartBarrier); + } + + vk::DependencyInfo imageStartDependency = { + .imageMemoryBarrierCount = Cast(startBarriers.size()), + .pImageMemoryBarriers = startBarriers.data(), + }; + + 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, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = texture->m_LayerCount, + }, + }; + vk::DependencyInfo interMipDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &nextMipBarrier, + }; + + vk::ImageMemoryBarrier2 imageReadyBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = finalStage, + .dstAccessMask = vk::AccessFlagBits2::eShaderRead, + .oldLayout = vk::ImageLayout::eTransferSrcOptimal, + .newLayout = finalLayout, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture->m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = texture->GetMipLevels(), + .baseArrayLayer = 0, + .layerCount = texture->m_LayerCount, + }, + }; + vk::DependencyInfo imageReadyDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageReadyBarrier, + }; + + vk::ImageBlit2 blitRegion = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseArrayLayer = 0, + .layerCount = texture->m_LayerCount, + }, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseArrayLayer = 0, + .layerCount = texture->m_LayerCount, + }, + }; + + 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, + }; + + auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); }; + + // Mip Mapping + + commandBuffer.pipelineBarrier2(&imageStartDependency); + + i32 prevMipWidth = Cast(texture->m_Extent.width); + i32 prevMipHeight = Cast(texture->m_Extent.height); + + u32 maxPrevMip = texture->GetMipLevels() - 1; + for (u32 prevMipLevel = 0; prevMipLevel < maxPrevMip; ++prevMipLevel) + { + i32 currentMipWidth = calcNextMip(prevMipWidth); + i32 currentMipHeight = calcNextMip(prevMipHeight); + u32 currentMipLevel = prevMipLevel + 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; + + commandBuffer.blitImage2(&mipBlitInfo); + commandBuffer.pipelineBarrier2(&interMipDependency); + + prevMipHeight = currentMipHeight; + prevMipWidth = currentMipWidth; + } + + commandBuffer.pipelineBarrier2(&imageReadyDependency); +#if !defined(ASTER_NDEBUG) + commandBuffer.endDebugUtilsLabelEXT(); +#endif +} + +TextureHandle +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 = 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()); + +#if !defined(ASTER_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, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = texture.m_Image, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + vk::DependencyInfo imageStartDependency = { + .memoryBarrierCount = 0, + .bufferMemoryBarrierCount = 0, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageStartBarrier, + }; + + vk::ImageMemoryBarrier2 postStagingBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .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 = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &postStagingBarrier, + }; + + vk::BufferImageCopy2 imageCopy = { + .bufferOffset = 0, + .bufferRowLength = Cast(image->width), + .bufferImageHeight = Cast(image->height), + .imageSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {}, + .imageExtent = texture.m_Extent, + }; + vk::CopyBufferToImageInfo2 stagingCopyInfo = { + .srcBuffer = stagingBuffer->m_Buffer, + .dstImage = texture.m_Image, + .dstImageLayout = vk::ImageLayout::eTransferDstOptimal, + .regionCount = 1, + .pRegions = &imageCopy, + }; + +#pragma endregion + + m_CommandBuffer.pipelineBarrier2(&imageStartDependency); + m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo); + m_CommandBuffer.pipelineBarrier2(&postStagingDependency); + + GenerateMipMaps(m_CommandBuffer, &texture, vk::ImageLayout::eTransferSrcOptimal, + vk::ImageLayout::eShaderReadOnlyOptimal); + +#if !defined(ASTER_NDEBUG) + m_CommandBuffer.endDebugUtilsLabelEXT(); +#endif + + return m_ResourceManager->CommitTexture(&texture); +} + +template +struct CRequiresPostLoadProcess +{ +}; + +void +AssetLoader::ProcessNode(tinygltf::Model *model, eastl::vector *vertexPositions, + eastl::vector *vertexData, eastl::vector *indices, + eastl::vector *entities, const std::function &loadMaterial, int current, + Entity parent) +{ + auto *node = &model->nodes[current]; + + CDynamicTransform dynamicTransform; + if (!node->matrix.empty()) + { + vec3 skew; + vec4 perspective; + mat4 matrix = glm::make_mat4(node->matrix.data()); + glm::decompose(matrix, dynamicTransform.m_Scale, dynamicTransform.m_Rotation, dynamicTransform.m_Position, skew, + perspective); + dynamicTransform.m_Rotation = glm::conjugate(dynamicTransform.m_Rotation); + } + else { + dynamicTransform = { + .m_Position = VectorToVec3(node->translation), + .m_Rotation = VectorToQuat(node->rotation), + .m_Scale = VectorToVec3(node->scale, 1.0f), + }; + } + + auto nodeRoot = m_Registry->create(); + entities->push_back(nodeRoot); + m_Registry->emplace(nodeRoot, dynamicTransform); + m_Registry->emplace(nodeRoot); + m_Registry->emplace>(nodeRoot, parent); + + if (node->mesh >= 0) + { + auto *mesh = &model->meshes[node->mesh]; + u32 vertexOffset = Cast(vertexPositions->size()); + u32 indexOffset = Cast(indices->size()); + for (auto &prim : mesh->primitives) + { + u32 vertexCount = 0; + u32 indexCount = 0; + +#pragma region Position + assert(prim.attributes.contains(APosition)); + assert(prim.mode == TINYGLTF_MODE_TRIANGLES); + { + tinygltf::Accessor *posAccessor = &model->accessors[prim.attributes[APosition]]; + + assert(posAccessor->count <= MaxValue); + + tinygltf::BufferView *posBufferView = &model->bufferViews[posAccessor->bufferView]; + tinygltf::Buffer *posBuffer = &model->buffers[posBufferView->buffer]; + usize byteOffset = (posAccessor->byteOffset + posBufferView->byteOffset); + + vertexCount = Cast(posAccessor->count); + vertexPositions->reserve(vertexOffset + vertexCount); + + if (posAccessor->type == TINYGLTF_TYPE_VEC4) + { + vec4 *data = Recast(posBuffer->data.data() + byteOffset); + vertexPositions->insert(vertexPositions->end(), data, data + vertexCount); + } + else if (posAccessor->type == TINYGLTF_TYPE_VEC3) + { + vec3 *data = Recast(posBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + vertexPositions->push_back(vec4(data[i], 1.0f)); + } + } + else if (posAccessor->type == TINYGLTF_TYPE_VEC2) + { + vec2 *data = Recast(posBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + vertexPositions->push_back(vec4(data[i], 0.0f, 1.0f)); + } + } + } +#pragma endregion +#pragma region Vertex Data + vertexData->resize(vertexPositions->size()); + + // Normal Coords + if (prim.attributes.contains(ANormal)) + { + tinygltf::Accessor *normAccessor = &model->accessors[prim.attributes[ANormal]]; + + assert(normAccessor->count <= MaxValue); + + tinygltf::BufferView *normBufferView = &model->bufferViews[normAccessor->bufferView]; + tinygltf::Buffer *normBuffer = &model->buffers[normBufferView->buffer]; + usize byteOffset = (normAccessor->byteOffset + normBufferView->byteOffset); + + if (normAccessor->type == TINYGLTF_TYPE_VEC4) + { + vec4 *data = Recast(normBuffer->data.data() + byteOffset); + + vec4 *end = data + vertexCount; + u32 idx = vertexOffset; + vec4 *it = data; + while (it != end) + { + vertexData->at(idx++).m_Normal = *(it++); + } + } + else if (normAccessor->type == TINYGLTF_TYPE_VEC3) + { + vec3 *data = Recast(normBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + auto norm = vec4(data[i], 0.0f); + vertexData->at(vertexOffset + i).m_Normal = norm; + } + } + else if (normAccessor->type == TINYGLTF_TYPE_VEC2) + { + vec2 *data = Recast(normBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + auto norm = vec4(data[i], 0.0f, 0.0f); + vertexData->at(vertexOffset + i).m_Normal = norm; + } + } + } + + // UV0 + if (prim.attributes.contains(ATexCoord0)) + { + tinygltf::Accessor *uvAccessor = &model->accessors[prim.attributes[ATexCoord0]]; + + assert(uvAccessor->count <= MaxValue); + + tinygltf::BufferView *uvBufferView = &model->bufferViews[uvAccessor->bufferView]; + tinygltf::Buffer *uvBuffer = &model->buffers[uvBufferView->buffer]; + usize byteOffset = (uvAccessor->byteOffset + uvBufferView->byteOffset); + + assert(uvAccessor->type == TINYGLTF_TYPE_VEC2 && + uvAccessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT); + { + vec2 *data = Recast(uvBuffer->data.data() + byteOffset); + vec2 *end = data + vertexCount; + u32 idx = vertexOffset; + vec2 *it = data; + while (it != end) + { + vertexData->at(idx++).m_TexCoord0 = *(it++); + } + } + } + + if (prim.attributes.contains(AColor0)) + { + tinygltf::Accessor *colorAccessor = &model->accessors[prim.attributes[AColor0]]; + + assert(colorAccessor->count <= MaxValue); + + tinygltf::BufferView *colorBufferView = &model->bufferViews[colorAccessor->bufferView]; + tinygltf::Buffer *colorBuffer = &model->buffers[colorBufferView->buffer]; + usize byteOffset = (colorAccessor->byteOffset + colorBufferView->byteOffset); + + if (colorAccessor->type == TINYGLTF_TYPE_VEC4) + { + vec4 *data = Recast(colorBuffer->data.data() + byteOffset); + + vec4 *end = data + vertexCount; + u32 idx = vertexOffset; + vec4 *it = data; + while (it != end) + { + vertexData->at(idx++).m_Color0 = *(it++); + } + } + else if (colorAccessor->type == TINYGLTF_TYPE_VEC3) + { + vec3 *data = Recast(colorBuffer->data.data() + byteOffset); + for (u32 i = 0; i < vertexCount; ++i) + { + auto color = vec4(data[i], 1.0f); + vertexData->at(vertexOffset + i).m_Color0 = color; + } + } + } +#pragma endregion +#pragma region Indices + // Indices + if (prim.indices >= 0) + { + tinygltf::Accessor *indexAccessor = &model->accessors[prim.indices]; + + assert(indexAccessor->count <= MaxValue); + + tinygltf::BufferView *indexBufferView = &model->bufferViews[indexAccessor->bufferView]; + tinygltf::Buffer *indexBuffer = &model->buffers[indexBufferView->buffer]; + usize byteOffset = (indexAccessor->byteOffset + indexBufferView->byteOffset); + + indexCount = Cast(indexAccessor->count); + indices->reserve(indexOffset + indexCount); + + if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) + { + u32 *data = Recast(indexBuffer->data.data() + byteOffset); + indices->insert(indices->end(), data, data + indexCount); + } + else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) + { + u16 *data = Recast(indexBuffer->data.data() + byteOffset); + indices->insert(indices->end(), data, data + indexCount); + } + else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) + { + u8 *data = Recast(indexBuffer->data.data() + byteOffset); + indices->insert(indices->end(), data, data + indexCount); + } + } + else + { + indexCount = vertexCount; + indices->reserve(indexOffset + vertexCount); + for (u32 i = 0; i < indexCount; ++i) + { + indices->push_back(i); + } + } +#pragma endregion + + auto entity = m_Registry->create(); + entities->push_back(entity); + + m_Registry->emplace(entity, CMesh{ + .m_VertexPositionPtr = vertexOffset * sizeof vec4, + .m_VertexDataPtr = vertexOffset * sizeof VertexData, + .m_FirstIndex = indexOffset, + .m_IndexCount = indexCount, + }); + m_Registry->emplace>(entity); + m_Registry->emplace(entity); + m_Registry->emplace(entity); + m_Registry->emplace>(entity, nodeRoot); + if (prim.material >= 0) + { + m_Registry->emplace(entity, sizeof Material * loadMaterial(prim.material)); + m_Registry->emplace>(entity); + } + + vertexOffset += vertexCount; + indexOffset += indexCount; + } + } + + for (auto childIdx : node->children) + { + ProcessNode(model, vertexPositions, vertexData, indices, entities, loadMaterial, childIdx, nodeRoot); + } +} + +Model +AssetLoader::LoadModelToGpu(cstr path, cstr name) +{ + namespace fs = std::filesystem; + tinygltf::Model model; + tinygltf::TinyGLTF loader; + + const Device *pDevice = m_ResourceManager->m_Device; + + const auto fsPath = fs::absolute(path); + const auto ext = fsPath.extension(); + if (ext == GLTF_ASCII_FILE_EXTENSION) + { + std::string err; + std::string warn; + if (loader.LoadASCIIFromFile(&model, &err, &warn, fsPath.generic_string())) + { + ERROR_IF(!err.empty(), "{}", err) + ELSE_IF_WARN(!warn.empty(), "{}", warn); + } + } + if (ext == GLTF_BINARY_FILE_EXTENSION) + { + std::string err; + std::string warn; + if (loader.LoadBinaryFromFile(&model, &err, &warn, fsPath.generic_string())) + { + ERROR_IF(!err.empty(), "{}", err) + ELSE_IF_WARN(!warn.empty(), "{}", warn); + } + } + + AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo)); + +#if !defined(ASTER_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; + + // TODO: Mesh reordering based on nodes AND OR meshoptimizer + // TODO: Support scenes + + eastl::vector vertexPositions; + eastl::vector vertexData; + eastl::vector indices; + eastl::hash_map textureHandleMap; + eastl::hash_map materialsIndirection; + eastl::vector materials; + eastl::vector entities; + + auto getTextureHandle = [this, &textureHandleMap, &stagingBuffers, &model](i32 index, + bool isSrgb) -> TextureHandle { + if (index < 0) + { + return {}; + } + const auto iter = textureHandleMap.find(index); + if (iter != textureHandleMap.end()) + { + return iter->second; + } + + auto *image = &model.images[index]; + TextureHandle handle = LoadImageToGpu(&stagingBuffers.push_back(), image, isSrgb); + textureHandleMap.emplace(index, handle); + return handle; + }; + auto loadMaterial = [&materials, &getTextureHandle, &model, &materialsIndirection](i32 materialIdx) -> u32 { + auto materialFind = materialsIndirection.find(materialIdx); + if (materialFind != materialsIndirection.end()) + { + return materialFind->second; + } + + u32 index = Cast(materials.size()); + auto *material = &model.materials[materialIdx]; + materials.push_back({ + .m_AlbedoFactor = VectorToVec4(material->pbrMetallicRoughness.baseColorFactor), + .m_EmissionFactor = VectorToVec3(material->emissiveFactor), + .m_MetalFactor = Cast(material->pbrMetallicRoughness.metallicFactor), + .m_RoughFactor = Cast(material->pbrMetallicRoughness.roughnessFactor), + .m_AlbedoTex = getTextureHandle(material->pbrMetallicRoughness.baseColorTexture.index, true), + .m_NormalTex = getTextureHandle(material->normalTexture.index, false), + .m_MetalRoughTex = getTextureHandle(material->pbrMetallicRoughness.metallicRoughnessTexture.index, false), + .m_OcclusionTex = getTextureHandle(material->occlusionTexture.index, false), + .m_EmissionTex = getTextureHandle(material->emissiveTexture.index, true), + }); + materialsIndirection[materialIdx] = index; + return index; + }; + + Entity modelRootEntity = m_Registry->create(); + m_Registry->emplace(modelRootEntity); + m_Registry->emplace(modelRootEntity); + entities.push_back(modelRootEntity); + + assert(model.defaultScene >= 0); + { + auto *scene = &model.scenes[model.defaultScene]; + for (auto nodeIdx : scene->nodes) + { + ProcessNode(&model, &vertexPositions, &vertexData, &indices, &entities, loadMaterial, nodeIdx, + modelRootEntity); + } + } + +#pragma region Staging / Transfer / Uploads + IndexHandle indexHandle; + + GeometryHandle vertexPositionHandle; + GeometryHandle vertexDataHandle; + MaterialHandle materialsHandle; + { + // TODO: Make this work on non-ReBAR via transfers. + auto uploadBufferData = [cmd = this->m_CommandBuffer, &stagingBuffers, pDevice](const Buffer *buffer, + const void *data) { + vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = buffer->GetSize()}; + StagingBuffer &stagingBuffer = stagingBuffers.push_back(); + stagingBuffer.Init(pDevice, bufferCopy.size); + stagingBuffer.Write(pDevice, 0, bufferCopy.size, data); + cmd.copyBuffer(stagingBuffer.m_Buffer, buffer->m_Buffer, 1, &bufferCopy); + }; + + usize vertexPositionsByteSize = vertexPositions.size() * sizeof vertexPositions[0]; + usize vertexPositionsBaseAddr; + vertexPositionHandle = + m_ResourceManager->CreateGeometryBuffer(vertexPositionsByteSize, alignof(vec4), &vertexPositionsBaseAddr); + m_ResourceManager->Write(vertexPositionHandle, 0, vertexPositionsByteSize, vertexPositions.data()); + + usize vertexDataByteSize = vertexData.size() * sizeof vertexData[0]; + usize vertexDataBaseAddr; + vertexDataHandle = + m_ResourceManager->CreateGeometryBuffer(vertexDataByteSize, alignof(VertexData), &vertexDataBaseAddr); + m_ResourceManager->Write(vertexDataHandle, 0, vertexDataByteSize, vertexData.data()); + + usize materialsByteSize = materials.size() * sizeof materials[0]; + usize materialsBaseAddr; + materialsHandle = + m_ResourceManager->CreateMaterialBuffer(materialsByteSize, alignof(VertexData), &materialsBaseAddr); + m_ResourceManager->Write(materialsHandle, 0, materialsByteSize, materials.data()); + + usize indexByteSize = indices.size() * sizeof indices[0]; + u32 firstIndex; + indexHandle = m_ResourceManager->CreateIndexBuffer(indexByteSize, alignof(u32), &firstIndex); + m_ResourceManager->Write(indexHandle, 0, indexByteSize, indices.data()); + + // TODO(Bob): Replace ByteOffsets with BufferAddress. + auto postProcessMeshView = m_Registry->view>(); + for (auto [meshEntity, mesh] : postProcessMeshView.each()) + { + mesh.m_FirstIndex += firstIndex; + mesh.m_VertexDataPtr += vertexDataBaseAddr; + mesh.m_VertexPositionPtr += vertexPositionsBaseAddr; + m_Registry->remove>(meshEntity); + } + auto postProcessMaterialView = m_Registry->view>(); + for (auto [materialEntity, material] : postProcessMaterialView.each()) + { + material.m_MaterialPtr += materialsBaseAddr; + m_Registry->remove>(materialEntity); + } + } + +#pragma endregion + +#if !defined(ASTER_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, {})); + + for (auto &buffer : stagingBuffers) + { + buffer.Destroy(pDevice); + } + + eastl::vector textureHandles; + textureHandles.reserve(textureHandleMap.size()); + + for (auto &[key, val] : textureHandleMap) + { + textureHandles.emplace_back(val); + } + +#if !defined(ASTER_NDEBUG) + assert(m_Registry->view>().empty()); + assert(m_Registry->view>().empty()); + auto x0 = m_Registry->view>(); + auto x1 = m_Registry->view>(); + assert(x0.begin() == x0.end()); + assert(x1.begin() == x1.end()); +#endif + + // TODO("Model Handle needs to be returned. Ideally a single node with model component."); + + return Model{ + .m_Textures = std::move(textureHandles), + .m_Entities = std::move(entities), + .m_IndexHandle = indexHandle, + .m_VertexPositionHandle = vertexPositionHandle, + .m_VertexDataHandle = vertexDataHandle, + .m_MaterialHandle = materialsHandle, + .m_RootEntity = modelRootEntity, + }; +} + +AssetLoader::AssetLoader(RenderResourceManager *resourceManager, EcsRegistry *registry, vk::Queue transferQueue, + u32 transferQueueIndex, u32 graphicsQueueIndex) + : m_ResourceManager(resourceManager) + , m_Registry(registry) + , m_TransferQueue(transferQueue) + , m_TransferQueueIndex(transferQueueIndex) + , m_GraphicsQueueIndex(graphicsQueueIndex) +{ + const Device *pDevice = resourceManager->m_Device; + const vk::CommandPoolCreateInfo poolCreateInfo = { + .flags = vk::CommandPoolCreateFlagBits::eTransient, + .queueFamilyIndex = transferQueueIndex, + }; + 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"); +} + +AssetLoader::~AssetLoader() +{ + if (m_ResourceManager) + { + m_ResourceManager->m_Device->m_Device.destroy(m_CommandPool, nullptr); + } +} + +AssetLoader::AssetLoader(AssetLoader &&other) noexcept + : m_ResourceManager(Take(other.m_ResourceManager)) + , m_Registry(other.m_Registry) + , m_CommandPool(other.m_CommandPool) + , m_CommandBuffer(other.m_CommandBuffer) + , m_TransferQueue(other.m_TransferQueue) + , m_TransferQueueIndex(other.m_TransferQueueIndex) + , m_GraphicsQueueIndex(other.m_GraphicsQueueIndex) +{ +} + +AssetLoader & +AssetLoader::operator=(AssetLoader &&other) noexcept +{ + if (this == &other) + return *this; + m_ResourceManager = Take(other.m_ResourceManager); + m_Registry = Take(other.m_Registry); + m_CommandPool = other.m_CommandPool; + m_CommandBuffer = other.m_CommandBuffer; + m_TransferQueue = other.m_TransferQueue; + m_TransferQueueIndex = other.m_TransferQueueIndex; + m_GraphicsQueueIndex = other.m_GraphicsQueueIndex; + return *this; +} \ No newline at end of file diff --git a/samples/04_scenes/asset_loader.h b/samples/04_scenes/asset_loader.h new file mode 100644 index 0000000..8ea83a5 --- /dev/null +++ b/samples/04_scenes/asset_loader.h @@ -0,0 +1,111 @@ +// ============================================= +// Aster: asset_loader.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" + +#include "buffer.h" +#include "render_resource_manager.h" + +#include "ecs_adapter.h" + +namespace tinygltf +{ +class Model; +struct Image; +} // namespace tinygltf + +struct Image; +struct Texture; + +constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf"; +constexpr auto GLTF_BINARY_FILE_EXTENSION = ".glb"; + +struct Material +{ + vec4 m_AlbedoFactor; // 16 16 + vec3 m_EmissionFactor; // 12 28 + f32 m_MetalFactor; // 04 32 + f32 m_RoughFactor; // 04 36 + TextureHandle m_AlbedoTex; // 04 40 + TextureHandle m_NormalTex; // 04 44 + TextureHandle m_MetalRoughTex; // 04 48 + TextureHandle m_OcclusionTex; // 04 52 + TextureHandle m_EmissionTex; // 04 56 + + static constexpr usize ALIGNMENT = 4; +}; + +static_assert(sizeof(Material) == 56); + +struct VertexData +{ + vec4 m_Normal; + vec2 m_TexCoord0 = vec2{0.0f, 0.0f}; + vec2 m_TexCoord1 = vec2{0.0f, 0.0f}; + vec4 m_Color0 = vec4{1.0f, 1.0f, 1.0f, 1.0f}; + + static constexpr usize ALIGNMENT = 16; +}; + +struct Model +{ + eastl::vector m_Textures; + eastl::vector m_Entities; + + IndexHandle m_IndexHandle; + GeometryHandle m_VertexPositionHandle; + GeometryHandle m_VertexDataHandle; + MaterialHandle m_MaterialHandle; + Entity m_RootEntity; + + void Destroy(RenderResourceManager *resourceManager, EcsRegistry *registry); +}; + +struct AssetLoader +{ + RenderResourceManager *m_ResourceManager; + entt::registry *m_Registry; + + vk::CommandPool m_CommandPool; + vk::CommandBuffer m_CommandBuffer; + vk::Queue m_TransferQueue; + u32 m_TransferQueueIndex; + u32 m_GraphicsQueueIndex; + + void LoadHdrImage(Texture *texture, cstr path, cstr name = nullptr) const; + Model LoadModelToGpu(cstr path, cstr name = nullptr); + + constexpr static auto ANormal = "NORMAL"; + constexpr static auto APosition = "POSITION"; + constexpr static auto ATangent = "TANGENT"; + constexpr static auto ATexCoord0 = "TEXCOORD_0"; + constexpr static auto ATexCoord1 = "TEXCOORD_1"; + constexpr static auto AColor0 = "COLOR_0"; + constexpr static auto AJoints0 = "JOINTS_0"; + constexpr static auto AWeights0 = "WEIGHTS_0"; + + AssetLoader(RenderResourceManager *resourceManager, EcsRegistry *registry, vk::Queue transferQueue, + u32 transferQueueIndex, u32 graphicsQueueIndex); + ~AssetLoader(); + + AssetLoader(AssetLoader &&other) noexcept; + AssetLoader &operator=(AssetLoader &&other) noexcept; + + private: + TextureHandle LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const; + void + ProcessNode(tinygltf::Model *model, eastl::vector *vertexPositions, eastl::vector *vertexData, + eastl::vector *indices, eastl::vector *entities, const std::function &loadMaterial, int current, Entity parent); + + public: + DISALLOW_COPY_AND_ASSIGN(AssetLoader); +}; + +void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout, + vk::ImageLayout finalLayout, + vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands, + vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands); \ No newline at end of file diff --git a/samples/04_scenes/camera.h b/samples/04_scenes/camera.h new file mode 100644 index 0000000..72ab822 --- /dev/null +++ b/samples/04_scenes/camera.h @@ -0,0 +1,98 @@ +// ============================================= +// Aster: camera.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "global.h" + +struct Camera +{ + mat4 m_View; + mat4 m_Perspective; + 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 +{ + constexpr static vec3 UP = vec3(0.0f, 1.0f, 0.0f); + + f32 m_Fov; + f32 m_Pitch; + f32 m_Yaw; + f32 m_AspectRatio; + + Camera m_Camera; + + CameraController(const vec3 &position, const vec3 &target, const f32 vFov, const f32 aspectRatio) + : m_Fov(vFov) + , m_Pitch{0.0f} + , m_Yaw{0.0f} + , m_AspectRatio{aspectRatio} + , m_Camera{ + .m_View = lookAt(position, target, UP), + .m_Perspective = glm::perspective(vFov, aspectRatio, 0.1f, 100.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 + SetAspectRatio(const f32 aspectRatio) + { + m_AspectRatio = aspectRatio; + m_Camera.m_Perspective = glm::perspective(m_Fov, aspectRatio, 0.1f, 100.0f); + + m_Camera.CalculateInverses(); + } + + void + SetPosition(const vec3 &position) + { + m_Camera.m_Position = vec4(position, 1.0f); + + 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 + SetPitchYaw(f32 pitch, f32 yaw) + { + m_Pitch = pitch; + m_Yaw = yaw; + + f32 cosPitch = cos(m_Pitch); + const vec3 target = vec3(sin(m_Yaw) * cosPitch, sin(m_Pitch), -cos(m_Yaw) * cosPitch); + 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(); + } +}; diff --git a/samples/04_scenes/core_components.h b/samples/04_scenes/core_components.h new file mode 100644 index 0000000..7bdc91e --- /dev/null +++ b/samples/04_scenes/core_components.h @@ -0,0 +1,49 @@ +// ============================================= +// Aster: core_components.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" + +template +struct CDirty +{ + using RelatedComponentType = TComponent; +}; + +template +struct CParent +{ + using RelatedComponentType = TComponent; + entt::entity m_ParentEntity = NULL_ENTITY; +}; + +struct CDynamicTransform +{ + vec3 m_Position = vec3{0.0f}; + quat m_Rotation = glm::identity(); + vec3 m_Scale = vec3{1.0f}; +}; + +struct CStaticTransform +{}; + +struct CGlobalTransform +{ + mat4 m_Transform = glm::identity(); +}; + +struct CMaterial +{ + uptr m_MaterialPtr; +}; + +struct CMesh +{ + uptr m_VertexPositionPtr; + uptr m_VertexDataPtr; + u32 m_FirstIndex; + u32 m_IndexCount; +}; \ No newline at end of file diff --git a/samples/04_scenes/ecs_adapter.h b/samples/04_scenes/ecs_adapter.h new file mode 100644 index 0000000..3cc09bf --- /dev/null +++ b/samples/04_scenes/ecs_adapter.h @@ -0,0 +1,21 @@ +// ============================================= +// Aster: ecs_adapter.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include + +using EcsRegistry = entt::registry; +using Entity = entt::entity; +template +using Without = entt::exclude_t; + +[[nodiscard]] +inline bool Exists(Entity entity) +{ + return entity != entt::null; +} + +constexpr Entity NULL_ENTITY = entt::null; \ No newline at end of file diff --git a/samples/04_scenes/image/.gitattributes b/samples/04_scenes/image/.gitattributes new file mode 100644 index 0000000..77a7139 --- /dev/null +++ b/samples/04_scenes/image/.gitattributes @@ -0,0 +1,2 @@ +*.hdr filter=lfs diff=lfs merge=lfs -text +*.exr filter=lfs diff=lfs merge=lfs -text diff --git a/samples/04_scenes/main.cpp b/samples/04_scenes/main.cpp new file mode 100644 index 0000000..db43aef --- /dev/null +++ b/samples/04_scenes/main.cpp @@ -0,0 +1,529 @@ +// ============================================= +// Aster: main.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "context.h" +#include "device.h" +#include "helpers.h" +#include "physical_device.h" +#include "render_resource_manager.h" +#include "swapchain.h" +#include "window.h" + +#include "asset_loader.h" +#include "camera.h" +#include "core_components.h" + +#include "ecs_adapter.h" +#include "frame.h" +#include "image.h" +#include "pipeline.h" + +#include "pipeline_utils.h" + +constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; +constexpr auto PIPELINE_CACHE_FILE = "PipelineCacheData.bin"; +constexpr auto MODEL_FILE = "model/DamagedHelmet.glb"; +constexpr auto MODEL_FILE2 = "model/Box.glb"; +constexpr auto BACKDROP_FILE = "image/photo_studio_loft_hall_4k.hdr"; +constexpr u32 INIT_WIDTH = 640; +constexpr u32 INIT_HEIGHT = 480; + +int +main(int, char *[]) +{ + MIN_LOG_LEVEL(Logger::LogType::eInfo); + + Context context = {"Scene Render [WIP]", VERSION}; + Window window = {"Scene Render [WIP] (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}}; + + PhysicalDevices physicalDevices = {&window, &context}; + PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); + + usize physicalDeviceOffsetAlignment = deviceToUse.m_DeviceProperties.limits.minUniformBufferOffsetAlignment; + + vk::Extent2D internalResolution = {1920, 1080}; + internalResolution.width = (internalResolution.height * INIT_WIDTH) / INIT_HEIGHT; + + CameraController cameraController = {vec3{0.0f, 0.0f, 2.0f}, vec3{0.0f}, 70_deg, + Cast(internalResolution.width) / Cast(internalResolution.height)}; + + INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); + + Features enabledDeviceFeatures = { + .m_Vulkan10Features = + { + .samplerAnisotropy = true, + .shaderInt64 = true, + }, + .m_Vulkan12Features = + { + .descriptorIndexing = true, + .shaderSampledImageArrayNonUniformIndexing = true, + .shaderStorageBufferArrayNonUniformIndexing = true, + .shaderStorageImageArrayNonUniformIndexing = true, + .descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless + .descriptorBindingSampledImageUpdateAfterBind = true, + .descriptorBindingStorageImageUpdateAfterBind = true, + .descriptorBindingStorageBufferUpdateAfterBind = true, + .descriptorBindingPartiallyBound = true, + .runtimeDescriptorArray = true, + .bufferDeviceAddress = true, + .bufferDeviceAddressCaptureReplay = true, + }, + .m_Vulkan13Features = + { + .synchronization2 = true, + .dynamicRendering = true, + }, + }; + + auto attachmentFormat = vk::Format::eR8G8B8A8Srgb; + auto pipelineCacheData = ReadFileBytes(PIPELINE_CACHE_FILE, false); + + QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse); + Device device = {&context, &deviceToUse, &enabledDeviceFeatures, + {queueAllocation}, pipelineCacheData, "Primary Device"}; + vk::Queue graphicsQueue = device.GetQueue(queueAllocation.m_Family, 0); + Swapchain swapchain = {&window, &device, "Primary Chain"}; + + RenderResourceManager resourceManager = {&device, 1024}; + EcsRegistry registry; + + AssetLoader assetLoader = {&resourceManager, ®istry, graphicsQueue, queueAllocation.m_Family, + queueAllocation.m_Family}; + + Model model = assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"); + Model model2 = assetLoader.LoadModelToGpu(MODEL_FILE2, "Main Model 2"); + registry.get(model2.m_RootEntity).m_Position.x += 1.0f; + + UniformBuffer ubo; + ubo.Init(&device, sizeof cameraController.m_Camera, "Desc1 UBO"); + ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); + + Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager); + + vk::DescriptorPool descriptorPool; + vk::DescriptorSet perFrameDescriptor; + + { + vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts[1]; + eastl::array poolSizes = { + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 3, + }, + }; + vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = { + .maxSets = 1, .poolSizeCount = Cast(poolSizes.size()), .pPoolSizes = poolSizes.data()}; + AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool)); + + vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = { + .descriptorPool = descriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &descriptorSetLayout, + }; + AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &perFrameDescriptor)); + } + + vk::DescriptorBufferInfo cameraBufferInfo = { + .buffer = ubo.m_Buffer, + .offset = 0, + .range = sizeof(Camera), + }; + eastl::array writeDescriptors = { + vk::WriteDescriptorSet{ + .dstSet = perFrameDescriptor, + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .pBufferInfo = &cameraBufferInfo, + }, + }; + device.m_Device.updateDescriptorSets(Cast(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr); + + // Persistent variables + vk::Viewport viewport = { + .x = 0, + .y = Cast(internalResolution.height), + .width = Cast(internalResolution.width), + .height = -Cast(internalResolution.height), + .minDepth = 0.0, + .maxDepth = 1.0, + }; + + vk::Rect2D scissor = { + .offset = {0, 0}, + .extent = internalResolution, + }; + + vk::ImageSubresourceRange subresourceRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + vk::ImageMemoryBarrier2 preRenderBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, + .srcAccessMask = vk::AccessFlagBits2::eNone, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eUndefined, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .subresourceRange = subresourceRange, + }; + vk::DependencyInfo preRenderDependencies = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &preRenderBarrier, + }; + + vk::ImageMemoryBarrier2 renderToBlitBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .dstAccessMask = vk::AccessFlagBits2::eTransferRead, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, + .newLayout = vk::ImageLayout::eTransferSrcOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .subresourceRange = subresourceRange, + }; + vk::ImageMemoryBarrier2 acquireToTransferDstBarrier = { + .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 = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .subresourceRange = subresourceRange, + }; + eastl::array postRenderBarriers = { + renderToBlitBarrier, + acquireToTransferDstBarrier, + }; + vk::DependencyInfo postRenderDependencies = { + .imageMemoryBarrierCount = Cast(postRenderBarriers.size()), + .pImageMemoryBarriers = postRenderBarriers.data(), + }; + + vk::ImageMemoryBarrier2 transferDstToGuiRenderBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .oldLayout = vk::ImageLayout::eTransferDstOptimal, + .newLayout = vk::ImageLayout::eColorAttachmentOptimal, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .subresourceRange = subresourceRange, + }; + vk::DependencyInfo preGuiDependencies = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &transferDstToGuiRenderBarrier, + }; + + vk::ImageMemoryBarrier2 prePresentBarrier = { + .srcStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, + .srcAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe, + .dstAccessMask = vk::AccessFlagBits2::eNone, + .oldLayout = vk::ImageLayout::eColorAttachmentOptimal, + .newLayout = vk::ImageLayout::ePresentSrcKHR, + .srcQueueFamilyIndex = queueAllocation.m_Family, + .dstQueueFamilyIndex = queueAllocation.m_Family, + .subresourceRange = subresourceRange, + }; + vk::DependencyInfo prePresentDependencies = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &prePresentBarrier, + }; + + FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT}; + eastl::fixed_vector depthImages(frameManager.m_FramesInFlight); + eastl::fixed_vector attachmentImages(frameManager.m_FramesInFlight); + { + auto depthIter = depthImages.begin(); + auto attachmentIter = attachmentImages.begin(); + for (u32 index = 0; index < frameManager.m_FramesInFlight; ++index) + { + auto name = fmt::format("Depth Frame{}", index); + depthIter->Init(&device, internalResolution, name.c_str()); + + name = fmt::format("Attachment0 Frame{}", index); + attachmentIter->Init(&device, internalResolution, attachmentFormat, name.c_str()); + + ++depthIter; + ++attachmentIter; + } + } + + struct NodeData + { + mat4 m_Transform; + uptr m_VertexPositionPtr; + uptr m_VertexDataPtr; + uptr m_MaterialPtr; + + // TODO: Remove + u32 m_FirstIndex; + u32 m_IndexCount; + }; + + eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> perFrameNodeData(frameManager.m_FramesInFlight); + eastl::fixed_vector perFrameNodeBuffer(frameManager.m_FramesInFlight); + for (auto &bufferHandle : perFrameNodeBuffer) + { + StorageBuffer buffer; + buffer.Init(&device, sizeof(NodeData) * 100'000, true); + bufferHandle = resourceManager.Commit(&buffer); + } + + swapchain.RegisterResizeCallback( + [&cameraController, &internalResolution, &viewport, &scissor](vk::Extent2D extent) { + cameraController.SetAspectRatio(Cast(extent.width) / Cast(extent.height)); + + internalResolution.width = Cast(Cast(internalResolution.height) * cameraController.m_AspectRatio); + + viewport.y = Cast(internalResolution.height); + viewport.width = Cast(internalResolution.width); + viewport.height = -Cast(internalResolution.height); + scissor.extent = internalResolution; + }); + + auto sortByParentHier = [®istry](Entity a, Entity b) { + const auto parent = registry.try_get>(b); + return parent && parent->m_ParentEntity == a; + }; + registry.sort>(sortByParentHier); + + Time::Init(); + + auto rootNodeUpdateView = registry.view(Without>{}); + + auto nodeWithParentsUpdateView = registry.view, CGlobalTransform>(); + nodeWithParentsUpdateView.use>(); + + auto renderableObjectsGroup = registry.group(); + + resourceManager.Update(); + + while (window.Poll()) + { + Time::Update(); + + auto *rot = ®istry.get(model.m_RootEntity).m_Rotation; + *rot = glm::rotate(*rot, Cast(30_deg * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f}); + + Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window); + + u32 imageIndex = currentFrame->m_ImageIdx; + vk::Image currentSwapchainImage = swapchain.m_Images[imageIndex]; + vk::ImageView currentSwapchainImageView = swapchain.m_ImageViews[imageIndex]; + vk::CommandBuffer cmd = currentFrame->m_CommandBuffer; + + DepthImage *currentDepthImage = &depthImages[currentFrame->m_FrameIdx]; + AttachmentImage *currentAttachment = &attachmentImages[currentFrame->m_FrameIdx]; + + if (currentAttachment->m_Extent.width != internalResolution.width || + currentAttachment->m_Extent.height != internalResolution.height) + { + auto name = fmt::format("Depth Frame{}", currentFrame->m_FrameIdx); + currentDepthImage->Destroy(&device); + currentDepthImage->Init(&device, internalResolution, name.c_str()); + + name = fmt::format("Attachment0 Frame{}", currentFrame->m_FrameIdx); + currentAttachment->Destroy(&device); + currentAttachment->Init(&device, internalResolution, attachmentFormat, name.c_str()); + } + + vk::ImageView currentDepthImageView = currentDepthImage->m_View; + vk::Image currentImage = currentAttachment->m_Image; + vk::ImageView currentImageView = currentAttachment->m_View; + + preRenderBarrier.image = currentImage; + postRenderBarriers[0].image = currentImage; + postRenderBarriers[1].image = currentSwapchainImage; + transferDstToGuiRenderBarrier.image = currentSwapchainImage; + prePresentBarrier.image = currentSwapchainImage; + + ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); + + for (auto [entity, dynTransform, globalTransform] : rootNodeUpdateView.each()) + { + auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale); + auto rotation = glm::toMat4(dynTransform.m_Rotation); + auto translation = glm::translate(mat4{1.0f}, dynTransform.m_Position); + + globalTransform.m_Transform = translation * rotation * scale; + } + + // Has been sorted and ordered by parent. + for (auto [entity, dynTransform, parent, globalTransform] : nodeWithParentsUpdateView.each()) + { + auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale); + auto rotation = glm::toMat4(dynTransform.m_Rotation); + auto translation = glm::translate(mat4{1.0f}, dynTransform.m_Position); + + globalTransform.m_Transform = + registry.get(parent.m_ParentEntity).m_Transform * translation * rotation * scale; + } + + usize objectCount = renderableObjectsGroup.size(); + auto *nodeData = &perFrameNodeData[currentFrame->m_FrameIdx]; + nodeData->clear(); + nodeData->reserve(objectCount); + for (auto [entity, globalTransform, mesh, material] : renderableObjectsGroup.each()) + { + nodeData->push_back({ + .m_Transform = globalTransform.m_Transform, + .m_VertexPositionPtr = mesh.m_VertexPositionPtr, + .m_VertexDataPtr = mesh.m_VertexDataPtr, + .m_MaterialPtr = material.m_MaterialPtr, + .m_FirstIndex = mesh.m_FirstIndex, + .m_IndexCount = mesh.m_IndexCount, + }); + } + resourceManager.Write(perFrameNodeBuffer[currentFrame->m_FrameIdx], 0, objectCount * sizeof(NodeData), + nodeData->data()); + + vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + AbortIfFailed(cmd.begin(&beginInfo)); + + cmd.pipelineBarrier2(&preRenderDependencies); + + // Render + eastl::array attachmentInfos = { + vk::RenderingAttachmentInfo{ + .imageView = currentImageView, + .imageLayout = vk::ImageLayout::eColorAttachmentOptimal, + .resolveMode = vk::ResolveModeFlagBits::eNone, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eStore, + .clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f}, + }, + }; + + vk::RenderingAttachmentInfo depthAttachment = { + .imageView = currentDepthImageView, + .imageLayout = vk::ImageLayout::eDepthAttachmentOptimal, + .resolveMode = vk::ResolveModeFlagBits::eNone, + .loadOp = vk::AttachmentLoadOp::eClear, + .storeOp = vk::AttachmentStoreOp::eDontCare, + .clearValue = vk::ClearDepthStencilValue{.depth = 1.0f, .stencil = 0}, + }; + + vk::RenderingInfo renderingInfo = { + .renderArea = {.extent = ToExtent2D(currentAttachment->m_Extent)}, + .layerCount = 1, + .colorAttachmentCount = Cast(attachmentInfos.size()), + .pColorAttachments = attachmentInfos.data(), + .pDepthAttachment = &depthAttachment, + }; + + cmd.beginRendering(&renderingInfo); + + cmd.setViewport(0, 1, &viewport); + cmd.setScissor(0, 1, &scissor); + 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, &perFrameDescriptor, 0, + nullptr); + + //TODO("Unify index buffers"); + cmd.bindIndexBuffer(resourceManager.GetIndexBuffer(), 0, vk::IndexType::eUint32); + + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); + + // TODO("Get the data to the GPU"); + // auto nodeHandle = perFrameNodeBuffer[currentFrame->m_FrameIdx]; + auto &nodeBuffer = perFrameNodeData[currentFrame->m_FrameIdx]; + for (auto &node : nodeBuffer) + { + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof node, &node); + cmd.drawIndexed(node.m_IndexCount, 1, node.m_FirstIndex, 0, 0); + } + + cmd.endRendering(); + + cmd.pipelineBarrier2(&postRenderDependencies); + + vk::ImageBlit blitRegion = { + .srcSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .srcOffsets = + std::array{ + vk::Offset3D{0, 0, 0}, + ToOffset3D(currentAttachment->m_Extent), + }, + .dstSubresource = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .dstOffsets = + std::array{ + vk::Offset3D{0, 0, 0}, + vk::Offset3D{Cast(swapchain.m_Extent.width), Cast(swapchain.m_Extent.height), 1}, + }, + }; + cmd.blitImage(currentImage, postRenderBarriers[0].newLayout, currentSwapchainImage, + postRenderBarriers[1].newLayout, 1, &blitRegion, vk::Filter::eLinear); + + cmd.pipelineBarrier2(&preGuiDependencies); + + cmd.pipelineBarrier2(&prePresentDependencies); + + AbortIfFailed(cmd.end()); + + vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput; + vk::SubmitInfo submitInfo = { + .waitSemaphoreCount = 1, + .pWaitSemaphores = ¤tFrame->m_ImageAcquireSem, + .pWaitDstStageMask = &waitDstStage, + .commandBufferCount = 1, + .pCommandBuffers = &cmd, + .signalSemaphoreCount = 1, + .pSignalSemaphores = ¤tFrame->m_RenderFinishSem, + }; + AbortIfFailed(graphicsQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence)); + + currentFrame->Present(graphicsQueue, &swapchain, &window); + } + + device.WaitIdle(); + + for (auto bufferHandle : perFrameNodeBuffer) + { + resourceManager.Release(bufferHandle); + } + for (auto depthImage : depthImages) + { + depthImage.Destroy(&device); + } + for (auto attachmentImage : attachmentImages) + { + attachmentImage.Destroy(&device); + } + ubo.Destroy(&device); + + device.m_Device.destroy(descriptorPool, nullptr); + + model.Destroy(&resourceManager, ®istry); + model2.Destroy(&resourceManager, ®istry); +} \ No newline at end of file diff --git a/samples/04_scenes/model/AlphaBlendModeTest.glb b/samples/04_scenes/model/AlphaBlendModeTest.glb new file mode 100644 index 0000000..3c7e82a --- /dev/null +++ b/samples/04_scenes/model/AlphaBlendModeTest.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:688786ffa64990071cfd93c96f29917fddd3d254d3f0e48039d80d3b5ac0d8c7 +size 3017136 diff --git a/samples/04_scenes/model/Box.glb b/samples/04_scenes/model/Box.glb new file mode 100644 index 0000000..5445f06 --- /dev/null +++ b/samples/04_scenes/model/Box.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ed52f7192b8311d700ac0ce80644e3852cd01537e4d62241b9acba023da3d54e +size 1664 diff --git a/samples/04_scenes/model/DamagedHelmet.glb b/samples/04_scenes/model/DamagedHelmet.glb new file mode 100644 index 0000000..a8c16b5 --- /dev/null +++ b/samples/04_scenes/model/DamagedHelmet.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1e3b04de97b11de564ce6e53b95f02954a297f0008183ac63a4f5974f6b32d8 +size 3773916 diff --git a/samples/04_scenes/model/MarbleBust/marble_bust_01.bin b/samples/04_scenes/model/MarbleBust/marble_bust_01.bin new file mode 100644 index 0000000..16abb3b --- /dev/null +++ b/samples/04_scenes/model/MarbleBust/marble_bust_01.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bb757c1bd00bfe42005c84e7f76df98ce04a532e595a2ddda8a0335cd959e33 +size 416608 diff --git a/samples/04_scenes/model/MarbleBust/marble_bust_01_4k.gltf b/samples/04_scenes/model/MarbleBust/marble_bust_01_4k.gltf new file mode 100644 index 0000000..bec0067 --- /dev/null +++ b/samples/04_scenes/model/MarbleBust/marble_bust_01_4k.gltf @@ -0,0 +1,161 @@ +{ + "asset": { + "generator": "Khronos glTF Blender I/O v1.6.16", + "version": "2.0" + }, + "scene": 0, + "scenes": [ + { + "name": "Scene", + "nodes": [ + 0 + ] + } + ], + "nodes": [ + { + "mesh": 0, + "name": "marble_bust_01", + "translation": [ + 0, + 0.028335653245449066, + 0 + ] + } + ], + "materials": [ + { + "doubleSided": true, + "name": "marble_bust_01", + "normalTexture": { + "index": 0 + }, + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 1 + }, + "metallicFactor": 0, + "metallicRoughnessTexture": { + "index": 2 + } + } + } + ], + "meshes": [ + { + "name": "marble_bust_01", + "primitives": [ + { + "attributes": { + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 + }, + "indices": 3, + "material": 0 + } + ] + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 1 + }, + { + "sampler": 0, + "source": 2 + } + ], + "images": [ + { + "mimeType": "image/jpeg", + "name": "marble_bust_01_nor_gl", + "uri": "textures/marble_bust_01_nor_gl_4k.jpg" + }, + { + "mimeType": "image/jpeg", + "name": "marble_bust_01_diff", + "uri": "textures/marble_bust_01_diff_4k.jpg" + }, + { + "mimeType": "image/jpeg", + "name": "marble_bust_01_arm", + "uri": "textures/marble_bust_01_rough_4k.jpg" + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 9746, + "max": [ + 0.14886942505836487, + 0.48668384552001953, + 0.1551172435283661 + ], + "min": [ + -0.12288019061088562, + -0.028259359300136566, + -0.1445964276790619 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 9746, + "type": "VEC3" + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 9746, + "type": "VEC2" + }, + { + "bufferView": 3, + "componentType": 5123, + "count": 52368, + "type": "SCALAR" + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteLength": 116952, + "byteOffset": 0 + }, + { + "buffer": 0, + "byteLength": 116952, + "byteOffset": 116952 + }, + { + "buffer": 0, + "byteLength": 77968, + "byteOffset": 233904 + }, + { + "buffer": 0, + "byteLength": 104736, + "byteOffset": 311872 + } + ], + "samplers": [ + { + "magFilter": 9729, + "minFilter": 9987 + } + ], + "buffers": [ + { + "byteLength": 416608, + "uri": "marble_bust_01.bin" + } + ] +} \ No newline at end of file diff --git a/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg new file mode 100644 index 0000000..30d4791 --- /dev/null +++ b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:986a5a956453e3a4e84ca58e16a408c72e423bf8233c4d2a395c59fd94358c83 +size 1671267 diff --git a/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg new file mode 100644 index 0000000..d9e6693 --- /dev/null +++ b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:61645b8878ea6d04bd95379bdffb1a7b07ef955dfba7c7ef3a60f6927473aef4 +size 1541349 diff --git a/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg new file mode 100644 index 0000000..5e44993 --- /dev/null +++ b/samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3994d1690406dc076581e636accced3a03c061536984e21fa8818ef5dda17c00 +size 2156013 diff --git a/samples/04_scenes/model/OrientationTest.glb b/samples/04_scenes/model/OrientationTest.glb new file mode 100644 index 0000000..9713071 --- /dev/null +++ b/samples/04_scenes/model/OrientationTest.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a91cf448f37de06ab61bc615e123692dd29ac185e6c69e1fdbf1cf53e41045b2 +size 38920 diff --git a/samples/04_scenes/pipeline_utils.cpp b/samples/04_scenes/pipeline_utils.cpp new file mode 100644 index 0000000..50812b3 --- /dev/null +++ b/samples/04_scenes/pipeline_utils.cpp @@ -0,0 +1,189 @@ +// ============================================= +// Aster: pipeline_utils.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "pipeline_utils.h" + +#include "device.h" +#include "render_resource_manager.h" +#include "helpers.h" + +#include + +Pipeline +CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager) +{ + // Pipeline Setup + 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", + }, + }}; + + 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::DescriptorSetLayoutBinding{ + .binding = 1, + .descriptorType = vk::DescriptorType::eUniformBuffer, + .descriptorCount = 1, + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + vk::DescriptorSetLayoutBinding{ + .binding = 2, + .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 = 96, + }; + + 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::eLess, + }; + vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { + .blendEnable = false, + .srcColorBlendFactor = vk::BlendFactor::eSrcColor, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor, + .colorBlendOp = vk::BlendOp::eAdd, + .srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, + .alphaBlendOp = vk::BlendOp::eAdd, + .colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | + vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA, + }; + vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = { + .logicOpEnable = false, + .attachmentCount = 1, + .pAttachments = &colorBlendAttachmentState, + }; + + eastl::array dynamicStates = { + vk::DynamicState::eScissor, + vk::DynamicState::eViewport, + }; + + vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = { + .dynamicStateCount = Cast(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, "Box Pipeline"); + + device->m_Device.destroy(vertexShaderModule, nullptr); + device->m_Device.destroy(fragmentShaderModule, nullptr); + + return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; +} + +vk::ShaderModule +CreateShader(const Device *device, cstr shaderFile) +{ + eastl::vector 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; +} diff --git a/samples/04_scenes/pipeline_utils.h b/samples/04_scenes/pipeline_utils.h new file mode 100644 index 0000000..01ac264 --- /dev/null +++ b/samples/04_scenes/pipeline_utils.h @@ -0,0 +1,20 @@ +// ============================================= +// Aster: pipeline_utils.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" +#include "pipeline.h" + +struct RenderResourceManager; +struct Swapchain; +struct Device; + +constexpr auto VERTEX_SHADER_FILE = "shader/model.vert.glsl.spv"; +constexpr auto FRAGMENT_SHADER_FILE = "shader/model.frag.glsl.spv"; + +vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); +Pipeline +CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager); diff --git a/samples/04_scenes/render_resource_manager.cpp b/samples/04_scenes/render_resource_manager.cpp new file mode 100644 index 0000000..aa3bc41 --- /dev/null +++ b/samples/04_scenes/render_resource_manager.cpp @@ -0,0 +1,943 @@ +// ============================================= +// Aster: render_resource_manager.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "render_resource_manager.h" + +#include "buffer.h" +#include "device.h" +#include "helpers.h" +#include "image.h" + +#include + +void +TextureManager::Init(const u32 maxCapacity) +{ + m_MaxCapacity = maxCapacity; + m_FreeHead = GpuResourceHandle::INVALID_HANDLE; +} + +TextureHandle +TextureManager::Commit(Texture *texture) +{ + ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for commital") + THEN_ABORT(-1); + + if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) + { + const u32 index = m_FreeHead; + + Texture *allocatedTexture = &m_Textures[index]; + + assert(!allocatedTexture->IsValid()); + m_FreeHead = *Recast(allocatedTexture); + + // Ensure it is copyable. + static_assert(std::is_trivially_copyable_v); + *allocatedTexture = *texture; + + // Take ownership of the texture. + texture->m_Flags_ &= ~Texture::OWNED_BIT; + + return {index}; + } + + const u32 index = Cast(m_Textures.size()); + if (index < m_MaxCapacity) + { + Texture *allocatedTexture = &m_Textures.push_back(); + + // Ensure it is copyable. + static_assert(std::is_trivially_copyable_v); + *allocatedTexture = *texture; + + texture->m_Flags_ &= ~Texture::OWNED_BIT; + + return {index}; + } + + ERROR("Out of Buffers") THEN_ABORT(-1); +} + +Texture * +TextureManager::Fetch(const TextureHandle handle) +{ + assert(!handle.IsInvalid()); + + return &m_Textures[handle.m_Index]; +} + +void +TextureManager::Release(const Device *device, const TextureHandle handle) +{ + assert(!handle.IsInvalid()); + + Texture *allocatedTexture = &m_Textures[handle.m_Index]; + allocatedTexture->Destroy(device); + + assert(!allocatedTexture->IsValid()); + *Recast(allocatedTexture) = m_FreeHead; + + m_FreeHead = handle.m_Index; +} + +void +TextureManager::Destroy(const Device *device) +{ + for (auto &texture : m_Textures) + { + texture.Destroy(device); + } +} + +void +BufferManager::Init(const u32 maxCapacity) +{ + m_MaxCapacity = maxCapacity; + m_FreeHead = GpuResourceHandle::INVALID_HANDLE; +} + +BufferHandle +BufferManager::Commit(StorageBuffer *buffer) +{ + ERROR_IF(!buffer || !buffer->IsValid() || !buffer->IsOwned(), "Buffer must be valid and owned for commital") + THEN_ABORT(-1); + + if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) + { + const u32 index = m_FreeHead; + + StorageBuffer *allocatedBuffer = &m_Buffers[index]; + + assert(!allocatedBuffer->IsValid()); + m_FreeHead = *Recast(allocatedBuffer); + + // Ensure it is copyable. + static_assert(std::is_trivially_copyable_v); + *allocatedBuffer = *buffer; + + // Take ownership of the buffer. + buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT; + + return {index}; + } + + const u32 index = Cast(m_Buffers.size()); + if (index < m_MaxCapacity) + { + StorageBuffer *allocatedBuffer = &m_Buffers.push_back(); + + // Ensure it is copyable. + static_assert(std::is_trivially_copyable_v); + *allocatedBuffer = *buffer; + + buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT; + + return {index}; + } + + ERROR("Out of Buffers") THEN_ABORT(-1); +} + +StorageBuffer * +BufferManager::Fetch(const BufferHandle handle) +{ + assert(!handle.IsInvalid()); + + return &m_Buffers[handle.m_Index]; +} + +void +BufferManager::Release(const Device *device, const BufferHandle handle) +{ + assert(!handle.IsInvalid()); + + StorageBuffer *allocatedBuffer = &m_Buffers[handle.m_Index]; + allocatedBuffer->Destroy(device); + + assert(!allocatedBuffer->IsValid()); + *Recast(allocatedBuffer) = m_FreeHead; + + m_FreeHead = handle.m_Index; +} + +void +BufferManager::Destroy(const Device *device) +{ + for (auto &buffer : m_Buffers) + { + buffer.Destroy(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}); +} + +usize +HashSamplerCreateInfo(const vk::SamplerCreateInfo *createInfo) +{ + usize hash = HashAny(createInfo->flags); + hash = HashCombine(hash, HashAny(createInfo->magFilter)); + hash = HashCombine(hash, HashAny(createInfo->minFilter)); + hash = HashCombine(hash, HashAny(createInfo->mipmapMode)); + hash = HashCombine(hash, HashAny(createInfo->addressModeU)); + hash = HashCombine(hash, HashAny(createInfo->addressModeV)); + hash = HashCombine(hash, HashAny(createInfo->addressModeW)); + hash = HashCombine(hash, HashAny(Cast(createInfo->mipLodBias * 1000))); // Resolution of 10^-3 + hash = HashCombine(hash, HashAny(createInfo->anisotropyEnable)); + hash = HashCombine(hash, + HashAny(Cast(createInfo->maxAnisotropy * 0x10))); // 16:1 Anisotropy is enough resolution + hash = HashCombine(hash, HashAny(createInfo->compareEnable)); + hash = HashCombine(hash, HashAny(createInfo->compareOp)); + hash = HashCombine(hash, HashAny(Cast(createInfo->minLod * 1000))); // 0.001 resolution is enough. + hash = HashCombine(hash, + HashAny(Cast(createInfo->maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp) + hash = HashCombine(hash, HashAny(createInfo->borderColor)); + hash = HashCombine(hash, HashAny(createInfo->unnormalizedCoordinates)); + + return hash; +} + +void +SamplerManager::Init(usize size) +{ + m_Samplers.reserve(size); + m_SamplerHashes.reserve(size); +} + +SamplerHandle +SamplerManager::Create(const Device *device, const vk::SamplerCreateInfo *createInfo) +{ + const usize hash = HashSamplerCreateInfo(createInfo); + + for (u32 index = 0; usize samplerHash : m_SamplerHashes) + { + if (samplerHash == hash) + { + return {index}; + } + ++index; + } + + vk::Sampler sampler; + AbortIfFailed(device->m_Device.createSampler(createInfo, nullptr, &sampler)); + const u32 index = Cast(m_SamplerHashes.size()); + m_SamplerHashes.push_back(hash); + m_Samplers.push_back(sampler); + return {index}; +} + +vk::Sampler +SamplerManager::Fetch(const SamplerHandle handle) +{ + assert(!handle.IsInvalid()); + + return m_Samplers[handle.m_Index]; +} + +void +SamplerManager::Destroy(const Device *device) +{ + for (const auto &sampler : m_Samplers) + { + device->m_Device.destroy(sampler, nullptr); + } + m_Samplers.clear(); + m_SamplerHashes.clear(); +} + +void +VirtualizedBufferPool::InitStorage(const Device *device, usize bufferMaxSize) +{ + auto buffer = std::make_unique(); + buffer->Init(device, bufferMaxSize, true, true, "Unified Geometry Buffer"); + m_BackingBuffer = std::move(buffer); + + vk::BufferDeviceAddressInfo addressInfo = { + .buffer = m_BackingBuffer->m_Buffer, + }; + m_BufferPtr = device->m_Device.getBufferAddress(&addressInfo); + + const VmaVirtualBlockCreateInfo virtualBlockCreateInfo = { + .size = bufferMaxSize, + }; + AbortIfFailed(Cast(vmaCreateVirtualBlock(&virtualBlockCreateInfo, &m_Block))); +} + +void +VirtualizedBufferPool::InitIndex(const Device *device, usize bufferMaxSize) +{ + auto buffer = std::make_unique(); + buffer->Init(device, bufferMaxSize, true, true, "Unified Index Buffer"); + m_BackingBuffer = std::move(buffer); + + vk::BufferDeviceAddressInfo addressInfo = { + .buffer = m_BackingBuffer->m_Buffer, + }; + m_BufferPtr = device->m_Device.getBufferAddress(&addressInfo); + + const VmaVirtualBlockCreateInfo virtualBlockCreateInfo = { + .size = bufferMaxSize, + }; + AbortIfFailed(Cast(vmaCreateVirtualBlock(&virtualBlockCreateInfo, &m_Block))); +} + +void +VirtualizedBufferPool::UpdateToGpu(const Device *device) +{ + // Unrequired until adding the non-ReBAR support. +} + +VirtualizedBufferHandle +VirtualizedBufferPool::Create(usize size, usize alignment) +{ + const VmaVirtualAllocationCreateInfo virtualAllocationCreateInfo = { + .size = size, + .alignment = alignment, + }; + VmaVirtualAllocation allocation; + usize offset; + AbortIfFailed(vmaVirtualAllocate(m_Block, &virtualAllocationCreateInfo, &allocation, &offset)); + const VirtualBuffer virtualBuffer = { + .m_Allocation = allocation, + .m_Offset = offset, + .m_Size = size, + }; + + u32 index; + VirtualBuffer *allocVBuf; + if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE) + { + index = m_FreeHead; + allocVBuf = &m_VirtualBuffers[index]; + + m_FreeHead = *Recast(allocVBuf); + } + else + { + index = Cast(m_VirtualBuffers.size()); + allocVBuf = &m_VirtualBuffers.push_back(); + } + + // Ensure it is copyable. + static_assert(std::is_trivially_copyable_v); + *allocVBuf = virtualBuffer; + m_Dirty = true; + + return {index}; +} + +uptr +VirtualizedBufferPool::FetchOffset(VirtualizedBufferHandle handle) +{ + assert(!handle.IsInvalid()); + return m_VirtualBuffers[handle.m_Index].m_Offset; +} + +void +VirtualizedBufferPool::Release(VirtualizedBufferHandle handle) +{ + assert(!handle.IsInvalid()); + + VirtualBuffer *virtualBuffer = &m_VirtualBuffers[handle.m_Index]; + vmaVirtualFree(m_Block, virtualBuffer->m_Allocation); + + *Recast(virtualBuffer) = m_FreeHead; + + m_FreeHead = handle.m_Index; +} + +void +VirtualizedBufferPool::Write(VirtualizedBufferHandle handle, usize offset, usize size, const void *data) +{ + if (handle.IsInvalid()) + return; + + assert(m_BackingBuffer->IsMapped() && "Non ReBAR not supported."); + + const VirtualBuffer *virtualBuffer = &m_VirtualBuffers[handle.m_Index]; + assert(offset + size <= virtualBuffer->m_Size); + + u8 *target = m_BackingBuffer->m_Mapped + virtualBuffer->m_Offset + offset; + memcpy(target, data, size); +} + +void +VirtualizedBufferPool::Destroy(const Device *device) +{ + m_BackingBuffer->Destroy(device); + m_BackingBuffer.reset(); +} + +RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info) + : uBufferInfo(info) +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo info) + : uImageInfo(info) +{ +} + +RenderResourceManager::WriteInfo::WriteInfo(vk::BufferView info) + : uBufferView(info) +{ +} + +BufferHandle +RenderResourceManager::Commit(StorageBuffer *storageBuffer) +{ + const BufferHandle handle = m_BufferManager.Commit(storageBuffer); + + m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{ + .buffer = storageBuffer->m_Buffer, + .offset = 0, + .range = storageBuffer->GetSize(), + }); + + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = BUFFER_BINDING_INDEX, + .dstArrayElement = handle.m_Index, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .pBufferInfo = &m_WriteInfos.back().uBufferInfo, + }); + + m_WriteOwner.emplace_back(HandleType::eBuffer, handle.m_Index); + +#if !defined(ASTER_NDEBUG) + ++m_CommitedBufferCount; +#endif + + return handle; +} + +void +RenderResourceManager::Write(const BufferHandle handle, const usize offset, const usize size, const void *data) +{ + m_BufferManager.Fetch(handle)->Write(m_Device, offset, size, data); +} + +void +RenderResourceManager::EraseWrites(u32 handleIndex, HandleType handleType) +{ + auto writeIter = m_Writes.begin(); + auto ownerIter = m_WriteOwner.begin(); + const auto ownerEnd = m_WriteOwner.end(); + + while (ownerIter != ownerEnd) + { + if (ownerIter->first == handleType && ownerIter->second == handleIndex) + { + *writeIter = m_Writes.back(); + *ownerIter = m_WriteOwner.back(); + m_Writes.pop_back(); + m_WriteOwner.pop_back(); + return; + } + + ++ownerIter; + ++writeIter; + } +} + +void +RenderResourceManager::Release(BufferHandle handle) +{ + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eBuffer); + + m_BufferManager.Release(m_Device, handle); + +#if !defined(ASTER_NDEBUG) + --m_CommitedBufferCount; +#endif +} + +void +RenderResourceManager::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 +RenderResourceManager::Release(TextureHandle handle) +{ + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eTexture); + + m_TextureManager.Release(m_Device, handle); + +#if !defined(ASTER_NDEBUG) + --m_CommitedTextureCount; +#endif +} + +void +RenderResourceManager::Release(Texture *texture, TextureHandle handle) +{ + assert(texture); + assert(!texture->IsValid()); + + Texture *internal = m_TextureManager.Fetch(handle); + *texture = *internal; + internal->m_Flags_ &= ~Texture::OWNED_BIT; + + Release(handle); +} + +TextureHandle +RenderResourceManager::CommitTexture(Texture *texture, const SamplerHandle sampler) +{ + TextureHandle handle = m_TextureManager.Commit(texture); + + const vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler); + + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ + .sampler = samplerImpl, + .imageView = texture->m_View, + .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + }); + + m_Writes.push_back({ + .dstSet = m_DescriptorSet, + .dstBinding = TEXTURE_BINDING_INDEX, + .dstArrayElement = handle.m_Index, + .descriptorCount = 1, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .pImageInfo = &m_WriteInfos.back().uImageInfo, + }); + + m_WriteOwner.emplace_back(HandleType::eTexture, handle.m_Index); + +#if !defined(ASTER_NDEBUG) + ++m_CommitedTextureCount; +#endif + + return {handle}; +} + +StorageTextureHandle +RenderResourceManager::CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler) +{ + StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture); + + vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler); + + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ + .sampler = samplerImpl, + .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(ASTER_NDEBUG) + ++m_CommitedStorageTextureCount; +#endif + + return {handle}; +} + +void +RenderResourceManager::Release(StorageTextureHandle handle) +{ + if (handle.IsInvalid()) + return; + + EraseWrites(handle.m_Index, HandleType::eTexture); + + m_StorageTextureManager.Release(m_Device, handle); + +#if !defined(ASTER_NDEBUG) + --m_CommitedStorageTextureCount; +#endif +} + +void +RenderResourceManager::Release(StorageTexture *texture, const StorageTextureHandle handle) +{ + assert(texture); + assert(!texture->IsValid()); + + StorageTexture *internal = m_StorageTextureManager.Fetch(handle); + *texture = *internal; + internal->m_Flags_ &= ~StorageTexture::OWNED_BIT; + + Release(handle); +} + +void +RenderResourceManager::Update() +{ + // Descriptor Updates + if (!m_Writes.empty()) + { + m_Device->m_Device.updateDescriptorSets(Cast(m_Writes.size()), m_Writes.data(), 0, nullptr); + + m_Writes.clear(); + m_WriteInfos.clear(); + m_WriteOwner.clear(); + } + + // Sub-system updates + m_Geometry.UpdateToGpu(m_Device); +} + +RenderResourceManager::RenderResourceManager(Device *device, u16 maxSize, bool useBufferAddress) + : m_Device(device) + , m_UseBufferAddr(useBufferAddress) +{ + vk::PhysicalDeviceProperties properties; + m_Device->m_PhysicalDevice.getProperties(&properties); + + 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)); + + INFO("Max Buffer Count: {}", buffersCount); + INFO("Max Texture Count: {}", texturesCount); + INFO("Max Storage Texture Count: {}", storageTexturesCount); + + m_Geometry.InitStorage(device, Megabyte(128u)); + m_Index.InitIndex(device, Megabyte(8u)); + m_Material.InitStorage(device, Kilobyte(560u)); + m_BufferManager.Init(buffersCount); + m_TextureManager.Init(texturesCount); + m_StorageTextureManager.Init(storageTexturesCount); + m_SamplerManager.Init(storageTexturesCount); + + m_DefaultSamplerCreateInfo = { + .magFilter = vk::Filter::eLinear, + .minFilter = vk::Filter::eLinear, + .mipmapMode = vk::SamplerMipmapMode::eLinear, + .addressModeU = vk::SamplerAddressMode::eRepeat, + .addressModeV = vk::SamplerAddressMode::eRepeat, + .addressModeW = vk::SamplerAddressMode::eRepeat, + .mipLodBias = 0.0f, + .anisotropyEnable = true, + .maxAnisotropy = properties.limits.maxSamplerAnisotropy, + .compareEnable = false, + .minLod = 0, + .maxLod = VK_LOD_CLAMP_NONE, + .borderColor = vk::BorderColor::eFloatOpaqueBlack, + .unnormalizedCoordinates = false, + }; + + m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo)); + + eastl::array poolSizes = { + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eStorageBuffer, + .descriptorCount = buffersCount, + }, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = texturesCount, + }, + vk::DescriptorPoolSize{ + .type = vk::DescriptorType::eStorageImage, + .descriptorCount = storageTexturesCount, + }, + }; + + const vk::DescriptorPoolCreateInfo poolCreateInfo = { + .flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind, + .maxSets = 1, + .poolSizeCount = Cast(poolSizes.size()), + .pPoolSizes = poolSizes.data(), + }; + AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool)); + + vk::DescriptorBindingFlags bindingFlags = + vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind; + eastl::array layoutBindingFlags = { + bindingFlags, + bindingFlags, + bindingFlags, + }; + + vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = { + .bindingCount = Cast(layoutBindingFlags.size()), + .pBindingFlags = layoutBindingFlags.data(), + }; + + eastl::array descriptorLayoutBindings = { + vk::DescriptorSetLayoutBinding{ + .binding = BUFFER_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eStorageBuffer, + .descriptorCount = Cast(buffersCount), + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + vk::DescriptorSetLayoutBinding{ + .binding = TEXTURE_BINDING_INDEX, + .descriptorType = vk::DescriptorType::eCombinedImageSampler, + .descriptorCount = Cast(texturesCount), + .stageFlags = vk::ShaderStageFlagBits::eAll, + }, + 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 = { + .pNext = &bindingFlagsCreateInfo, + .flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool, + .bindingCount = Cast(descriptorLayoutBindings.size()), + .pBindings = descriptorLayoutBindings.data(), + }; + AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout)); + + // One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending) + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html + // https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc + const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = { + .descriptorPool = m_DescriptorPool, + .descriptorSetCount = 1, + .pSetLayouts = &m_SetLayout, + }; + AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet)); + + m_Device->SetName(m_SetLayout, "Bindless Layout"); + m_Device->SetName(m_DescriptorPool, "Bindless Pool"); + m_Device->SetName(m_DescriptorSet, "Bindless Set"); + + // NOTE: This needs to be synced with the destructor manually. + assert(Commit(m_Geometry.m_BackingBuffer.get()).m_Index == UNIFIED_GEOMETRY_DATA_HANDLE_INDEX); // Making an assumption to avoid extra bindings. +} + +RenderResourceManager::~RenderResourceManager() +{ + // NOTE: Matches the constructor. + Release(BufferHandle{0}); + +#if !defined(ASTER_NDEBUG) + WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0 || m_CommitedStorageTextureCount > 0, + "Resources alive: SSBO = {}, Textures = {}, RWTexture = {}", m_CommitedBufferCount, m_CommitedTextureCount, + m_CommitedStorageTextureCount); +#endif + + m_Geometry.Destroy(m_Device); + m_Index.Destroy(m_Device); + m_Material.Destroy(m_Device); + m_BufferManager.Destroy(m_Device); + m_TextureManager.Destroy(m_Device); + m_StorageTextureManager.Destroy(m_Device); + m_SamplerManager.Destroy(m_Device); + m_Device->m_Device.destroy(m_DescriptorPool, nullptr); + m_Device->m_Device.destroy(m_SetLayout, nullptr); +} + +RenderResourceManager::RenderResourceManager(RenderResourceManager &&other) noexcept + : m_WriteInfos(std::move(other.m_WriteInfos)) + , m_Writes(std::move(other.m_Writes)) + , m_WriteOwner(std::move(other.m_WriteOwner)) + , m_Geometry(std::move(other.m_Geometry)) + , m_Index(std::move(other.m_Index)) + , m_Material(std::move(other.m_Material)) + , m_BufferManager(std::move(other.m_BufferManager)) + , m_TextureManager(std::move(other.m_TextureManager)) + , m_StorageTextureManager(std::move(other.m_StorageTextureManager)) + , m_SamplerManager(std::move(other.m_SamplerManager)) + , m_Device(Take(other.m_Device)) + , m_DescriptorPool(other.m_DescriptorPool) + , m_SetLayout(other.m_SetLayout) + , m_DescriptorSet(other.m_DescriptorSet) + , m_UseBufferAddr(other.m_UseBufferAddr) +#if !defined(ASTER_NDEBUG) + , m_CommitedBufferCount(other.m_CommitedBufferCount) + , m_CommitedTextureCount(other.m_CommitedTextureCount) + , m_CommitedStorageTextureCount(other.m_CommitedStorageTextureCount) +#endif +{ + assert(!other.m_Device); +} + +RenderResourceManager & +RenderResourceManager::operator=(RenderResourceManager &&other) noexcept +{ + if (this == &other) + return *this; + m_WriteInfos = std::move(other.m_WriteInfos); + m_Writes = std::move(other.m_Writes); + m_WriteOwner = std::move(other.m_WriteOwner); + m_Geometry = std::move(other.m_Geometry); + m_Index = std::move(other.m_Index); + m_Material = std::move(other.m_Material); + m_BufferManager = std::move(other.m_BufferManager); + m_TextureManager = std::move(other.m_TextureManager); + m_StorageTextureManager = std::move(other.m_StorageTextureManager); + m_SamplerManager = std::move(other.m_SamplerManager); + m_Device = Take(other.m_Device); // Ensure taken. + m_DescriptorPool = other.m_DescriptorPool; + m_SetLayout = other.m_SetLayout; + m_DescriptorSet = other.m_DescriptorSet; + m_UseBufferAddr = other.m_UseBufferAddr; +#if !defined(ASTER_NDEBUG) + m_CommitedBufferCount = other.m_CommitedBufferCount; + m_CommitedTextureCount = other.m_CommitedTextureCount; + m_CommitedStorageTextureCount = other.m_CommitedStorageTextureCount; +#endif + + assert(!other.m_Device); + return *this; +} + +SamplerHandle +RenderResourceManager::CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo) +{ + return m_SamplerManager.Create(m_Device, samplerCreateInfo); +} + +GeometryHandle +RenderResourceManager::CreateGeometryBuffer(usize size, usize alignment, uptr* addr) +{ + GeometryHandle handle = {m_Geometry.Create(size, alignment).m_Index}; + + if (addr) + { + *addr = FetchAddress(handle); + } + + return handle; +} + +uptr +RenderResourceManager::FetchAddress(GeometryHandle handle) +{ + return (m_UseBufferAddr ? m_Geometry.m_BufferPtr : 0) + m_Geometry.FetchOffset(handle); +} + +void +RenderResourceManager::Write(GeometryHandle handle, usize offset, usize size, const void *data) +{ + m_Geometry.Write(handle, offset, size, data); +} + +void +RenderResourceManager::Release(GeometryHandle handle) +{ + if (handle.IsInvalid()) + return; + + m_Geometry.Release(handle); +} + +MaterialHandle +RenderResourceManager::CreateMaterialBuffer(usize size, usize alignment, uptr* addr) +{ + MaterialHandle handle = {m_Material.Create(size, alignment).m_Index}; + + if (addr) + { + *addr = FetchAddress(handle); + } + + return handle; +} + +usize +RenderResourceManager::FetchAddress(MaterialHandle handle) +{ + return (m_UseBufferAddr ? m_Material.m_BufferPtr : 0) + m_Material.FetchOffset(handle); +} + +void +RenderResourceManager::Write(MaterialHandle handle, usize offset, usize size, const void *data) +{ + m_Material.Write(handle, offset, size, data); +} + +void +RenderResourceManager::Release(MaterialHandle handle) +{ + if (handle.IsInvalid()) + return; + + m_Material.Release(handle); +} + +IndexHandle +RenderResourceManager::CreateIndexBuffer(usize size, usize alignment, u32 *firstIndex) +{ + IndexHandle handle = {m_Index.Create(size, alignment).m_Index}; + + if (firstIndex) + { + *firstIndex = FetchIndex(handle); + } + + return handle; +} + +u32 +RenderResourceManager::FetchIndex(IndexHandle handle) +{ + return Cast(m_Index.FetchOffset(handle) / sizeof(u32)); +} + +void +RenderResourceManager::Write(IndexHandle handle, usize offset, usize size, const void *data) +{ + m_Index.Write(handle, offset, size, data); +} + +void +RenderResourceManager::Release(IndexHandle handle) +{ + if (handle.IsInvalid()) + return; + + m_Index.Release(handle); +} + +vk::Buffer +RenderResourceManager::GetIndexBuffer() const +{ + return m_Index.m_BackingBuffer->m_Buffer; +} diff --git a/samples/04_scenes/render_resource_manager.h b/samples/04_scenes/render_resource_manager.h new file mode 100644 index 0000000..4f5b871 --- /dev/null +++ b/samples/04_scenes/render_resource_manager.h @@ -0,0 +1,250 @@ +// ============================================= +// Aster: render_resource_manager.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" + +#include +#include + +struct Device; +struct Texture; +struct StorageTexture; +struct StorageBuffer; +struct Buffer; + +struct GpuResourceHandle +{ + constexpr static u32 INVALID_HANDLE = MaxValue; + u32 m_Index = INVALID_HANDLE; // Default = invalid + + [[nodiscard]] bool + IsInvalid() const + { + return m_Index == INVALID_HANDLE; + } +}; + +struct BufferHandle : GpuResourceHandle +{ +}; + +struct TextureHandle : GpuResourceHandle +{ +}; + +struct StorageTextureHandle : GpuResourceHandle +{ +}; + +struct SamplerHandle : GpuResourceHandle +{ +}; + +struct VirtualizedBufferHandle : GpuResourceHandle +{ +}; + +struct GeometryHandle : VirtualizedBufferHandle +{ +}; + +struct IndexHandle : VirtualizedBufferHandle +{ +}; + +struct MaterialHandle : VirtualizedBufferHandle +{ +}; + +struct TextureManager +{ + eastl::vector m_Textures; + u32 m_MaxCapacity; + u32 m_FreeHead; + + void Init(u32 maxCapacity); + TextureHandle Commit(Texture *texture); + Texture *Fetch(TextureHandle handle); + void Release(const Device *device, TextureHandle handle); + void Destroy(const Device *device); +}; + +struct BufferManager +{ + eastl::vector m_Buffers; + u32 m_MaxCapacity; + u32 m_FreeHead; + + void Init(u32 maxCapacity); + BufferHandle Commit(StorageBuffer *buffer); + StorageBuffer *Fetch(BufferHandle handle); + void Release(const Device *device, BufferHandle handle); + void Destroy(const Device *device); +}; + +struct StorageTextureManager : TextureManager +{ + StorageTextureHandle Commit(StorageTexture *texture); + StorageTexture *Fetch(StorageTextureHandle handle); + void Release(const Device *device, StorageTextureHandle handle); +}; + +struct SamplerManager +{ + // There can only be so many samplers. + eastl::vector m_Samplers; + eastl::vector m_SamplerHashes; + + void Init(usize size); + SamplerHandle Create(const Device *device, const vk::SamplerCreateInfo *createInfo); + vk::Sampler Fetch(SamplerHandle handle); + void Destroy(const Device *device); +}; + +struct VirtualizedBufferPool +{ + // TODO: Use buffer device address + std::unique_ptr m_BackingBuffer; + uptr m_BufferPtr; + VmaVirtualBlock m_Block; + + struct VirtualBuffer + { + VmaVirtualAllocation m_Allocation; + usize m_Offset; + usize m_Size; + }; + + eastl::vector m_VirtualBuffers; + u32 m_FreeHead = GpuResourceHandle::INVALID_HANDLE; + bool m_Dirty = false; + + void InitStorage(const Device *device, usize bufferMaxSize); + void InitIndex(const Device *device, usize bufferMaxSize); + + // Sync the offset buffer if required. + // FUTURE(Bob): Handle the writes for non-ReBAR system. + void UpdateToGpu(const Device *device); + + VirtualizedBufferHandle Create(usize size, usize alignment); + usize FetchOffset(VirtualizedBufferHandle handle); + void Release(VirtualizedBufferHandle handle); + void Write(VirtualizedBufferHandle handle, usize offset, usize size, const void *data); + void Destroy(const Device *device); +}; + +struct RenderResourceManager +{ + private: + union WriteInfo { + vk::DescriptorBufferInfo uBufferInfo; + vk::DescriptorImageInfo uImageInfo; + vk::BufferView uBufferView; + + WriteInfo() + { + } + + explicit WriteInfo(vk::DescriptorBufferInfo info); + explicit WriteInfo(vk::DescriptorImageInfo info); + explicit WriteInfo(vk::BufferView info); + }; + + enum class HandleType + { + eBuffer, + eTexture, + eStorageTexture, + }; + + using WriteOwner = eastl::pair; + + eastl::deque m_WriteInfos; + eastl::vector m_Writes; + eastl::vector m_WriteOwner; + + vk::Sampler m_DefaultSampler; + + VirtualizedBufferPool m_Geometry; + VirtualizedBufferPool m_Index; + VirtualizedBufferPool m_Material; + BufferManager m_BufferManager; + TextureManager m_TextureManager; + StorageTextureManager m_StorageTextureManager; + SamplerManager m_SamplerManager; + + void EraseWrites(u32 handleIndex, HandleType handleType); + + public: + 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; + + constexpr static u32 UNIFIED_GEOMETRY_DATA_HANDLE_INDEX = 0; + constexpr static u32 UNIFIED_GEOMETRY_OFFSET_HANDLE_INDEX = 1; + constexpr static u32 MATERIAL_HANDLE_INDEX = 2; + + vk::SamplerCreateInfo m_DefaultSamplerCreateInfo; + + vk::DescriptorPool m_DescriptorPool; + vk::DescriptorSetLayout m_SetLayout; + vk::DescriptorSet m_DescriptorSet; + + bool m_UseBufferAddr; + + 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 + + TextureHandle CommitTexture(Texture *texture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership + void Release(TextureHandle handle); // Release and Destroy + void Release(Texture *texture, TextureHandle handle); // Release and Return + + StorageTextureHandle CommitStorageTexture(StorageTexture *storageTexture, + SamplerHandle sampler = {}); // Commit to GPU and take Ownership + void Release(StorageTextureHandle handle); // Release and Destroy + void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return + + SamplerHandle CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo); + + GeometryHandle CreateGeometryBuffer(usize size, usize alignment, uptr *addr = nullptr); + uptr FetchAddress(GeometryHandle handle); + void Write(GeometryHandle handle, usize offset, usize size, const void *data); + void Release(GeometryHandle handle); + + MaterialHandle CreateMaterialBuffer(usize size, usize alignment, uptr *addr = nullptr); + uptr FetchAddress(MaterialHandle handle); + void Write(MaterialHandle handle, usize offset, usize size, const void *data); + void Release(MaterialHandle handle); + + IndexHandle CreateIndexBuffer(usize size, usize alignment, u32 *firstIndex = nullptr); + u32 FetchIndex(IndexHandle handle); + void Write(IndexHandle handle, usize offset, usize size, const void *data); + void Release(IndexHandle handle); + + vk::Buffer GetIndexBuffer() const; + + void Update(); // Update all the descriptors required. + + // Ctor/Dtor + RenderResourceManager(Device *device, u16 maxSize, bool useBufferAddress = true); + ~RenderResourceManager(); + + RenderResourceManager(RenderResourceManager &&other) noexcept; + RenderResourceManager &operator=(RenderResourceManager &&other) noexcept; + +#if !defined(ASTER_NDEBUG) + usize m_CommitedBufferCount = 0; + usize m_CommitedTextureCount = 0; + usize m_CommitedStorageTextureCount = 0; +#endif + + DISALLOW_COPY_AND_ASSIGN(RenderResourceManager); +}; \ No newline at end of file diff --git a/samples/04_scenes/shader/model.frag.glsl b/samples/04_scenes/shader/model.frag.glsl new file mode 100644 index 0000000..3bec0da --- /dev/null +++ b/samples/04_scenes/shader/model.frag.glsl @@ -0,0 +1,18 @@ +#version 450 +#pragma shader_stage(fragment) +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference : require + +layout (location = 2) in vec4 inColor; +layout (location = 0) out vec4 outColor; + +layout(push_constant) uniform Constants { + mat4 globalTransform; + uint64_t vertexPos; + uint64_t vertexDat; + uint64_t materialIdx; +} pcb; + +void main() { + outColor = vec4(inColor.rgb, 1.0f); +} \ No newline at end of file diff --git a/samples/04_scenes/shader/model.vert.glsl b/samples/04_scenes/shader/model.vert.glsl new file mode 100644 index 0000000..50083d8 --- /dev/null +++ b/samples/04_scenes/shader/model.vert.glsl @@ -0,0 +1,56 @@ +#version 450 +#pragma shader_stage(vertex) +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference : require + +layout(location=0) out vec4 outWorldNormal; +layout(location=1) out vec4 outWorldPosition; +layout(location=2) out vec4 outColor; +layout(location=3) out vec2 outUV0; + +struct VertexData { + vec4 Normal; + vec2 TexCoord0; + vec2 TexCoord1; + vec4 Color; +}; + +layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef { + vec4 Positions[]; +}; + +layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VDataRef { + VertexData Data[]; +}; + +layout(set=1, binding=0) uniform Camera { + mat4 View; // 64 + mat4 Projection; // 128 + mat4 InvView; // 192 + mat4 InvProjection; // 256 + vec4 Position; // 272 +} camera; + +layout(push_constant) uniform Constants { + mat4 globalTransform; + VPositionRef vertexPos; + VDataRef vertexDat; + uint64_t materialIdx; +} pcb; + +void main() { + vec3 colors[] = { + vec3( 1.0f, 0.0f, 0.0f ), + vec3( 0.0f, 1.0f, 0.0f ), + vec3( 0.0f, 0.0f, 1.0f ), + }; + + gl_Position = camera.Projection * camera.View * pcb.globalTransform * vec4(pcb.vertexPos.Positions[gl_VertexIndex].xyz, 1.0f); + outColor = vec4(pcb.vertexDat.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]); + + // TODO + // layout(location=0) out vec4 outWorldNormal; + // layout(location=1) out vec4 outWorldPosition; + // layout(location=2) out vec4 outColor; + // layout(location=3) out vec2 outUV0; +} \ No newline at end of file diff --git a/samples/04_scenes/shader/model.vs.hlsl b/samples/04_scenes/shader/model.vs.hlsl new file mode 100644 index 0000000..0ac6b79 --- /dev/null +++ b/samples/04_scenes/shader/model.vs.hlsl @@ -0,0 +1,62 @@ + +struct VS_Input +{ + uint VertexIndex : SV_VertexID; +}; + +struct VS_Out { + float4 WorldNormal : NORMAL; + float4 WorldPosition : POSITION; + float4 Color : COLOR0; + float2 TexCoord0 : TEXCOORD0; +}; + +struct CameraData +{ + float4x4 View; // 64 + float4x4 Projection; // 128 + float4x4 InvView; // 192 + float4x4 InvProjection; // 256 + float4 Position; // 272 +}; + +[[vk::binding(0, 1)]] ConstantBuffer Camera; + +struct VertexData { + float4 Normal; + float2 TexCoord0; + float2 TexCoord1; + float4 Color; +}; + +[[vk::binding(0, 0)]] ByteAddressBuffer GeometryBuffer[]; + +layout(set=1, binding=0) uniform Camera { + float4x4 View; // 64 + float4x4 Projection; // 128 + float4x4 InvView; // 192 + float4x4 InvProjection; // 256 + float4 Position; // 272 +} Camera; + +layout(push_constant) uniform Constants { + mat4 globalTransform; + uint64 vertexPos; + VDataRef vertexDat; + uint64_t materialIdx; +} pcb; + +void main() { + VS_Output Output; + + float4 GlobalPosition = mul(globalTransform, GeometryBuffer[0].Load()); + float4 ClipSpace = mul(Camera.View, GlobalPosition); + + Output.VertexPosition = mul(Camera.Projection, ClipSpace); + Output.WorldPosition = GlobalPosition; + Output.UV0 = GetUV(StageInput.VertexIndex); + Output.VertexColor = GetColor(StageInput.VertexIndex); + + Output.WorldNormal = mul(GetNormalTransform(PushConstant.NodeIdx), GetNormal(StageInput.VertexIndex)); + return Output; +} \ No newline at end of file diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index a157b3a..e7456b4 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory("00_util") add_subdirectory("01_triangle") add_subdirectory("02_box") add_subdirectory("03_model_render") +add_subdirectory("04_scenes") diff --git a/vcpkg.json b/vcpkg.json index 75046db..fac4263 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,13 +7,14 @@ { "name": "imgui", "features": [ + "docking-experimental", "glfw-binding", - "vulkan-binding", - "docking-experimental" + "vulkan-binding" ] }, "scottt-debugbreak", "tinygltf", - "vulkan-memory-allocator" + "vulkan-memory-allocator", + "entt" ] } -- 2.43.1 From 8f6dc4677d1069eefb54112ab15bed3fdce3a511 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Mon, 9 Sep 2024 20:55:10 +0200 Subject: [PATCH 3/6] [WIP] IBL etc integrated. --- samples/04_scenes/CMakeLists.txt | 11 +- samples/04_scenes/asset_loader.cpp | 3 +- samples/04_scenes/asset_loader.h | 4 - samples/04_scenes/ibl_helpers.cpp | 395 ++++++++++++++++++ samples/04_scenes/ibl_helpers.h | 28 ++ .../image/photo_studio_loft_hall_4k.hdr | 3 + samples/04_scenes/light_manager.cpp | 303 ++++++++++++++ samples/04_scenes/light_manager.h | 91 ++++ samples/04_scenes/main.cpp | 29 +- samples/04_scenes/pipeline_utils.cpp | 167 +++++++- samples/04_scenes/pipeline_utils.h | 5 + samples/04_scenes/shader/background.frag.glsl | 35 ++ samples/04_scenes/shader/background.vert.glsl | 24 ++ .../04_scenes/shader/bindless_structs.glsl | 53 +++ .../04_scenes/shader/bindless_structs.hlsli | 69 +++ samples/04_scenes/shader/brdf_lut.cs.hlsl | 82 ++++ .../shader/diffuse_irradiance.cs.hlsl | 52 +++ .../04_scenes/shader/eqrect_to_cube.cs.hlsl | 29 ++ .../04_scenes/shader/graphics_bindings.glsl | 42 ++ samples/04_scenes/shader/ibl_common.hlsli | 77 ++++ samples/04_scenes/shader/model.frag.glsl | 292 ++++++++++++- samples/04_scenes/shader/model.vert.glsl | 46 +- samples/04_scenes/shader/model.vs.hlsl | 62 --- samples/04_scenes/shader/prefilter.cs.hlsl | 68 +++ 24 files changed, 1842 insertions(+), 128 deletions(-) create mode 100644 samples/04_scenes/ibl_helpers.cpp create mode 100644 samples/04_scenes/ibl_helpers.h create mode 100644 samples/04_scenes/image/photo_studio_loft_hall_4k.hdr create mode 100644 samples/04_scenes/light_manager.cpp create mode 100644 samples/04_scenes/light_manager.h create mode 100644 samples/04_scenes/shader/background.frag.glsl create mode 100644 samples/04_scenes/shader/background.vert.glsl create mode 100644 samples/04_scenes/shader/bindless_structs.glsl create mode 100644 samples/04_scenes/shader/bindless_structs.hlsli create mode 100644 samples/04_scenes/shader/brdf_lut.cs.hlsl create mode 100644 samples/04_scenes/shader/diffuse_irradiance.cs.hlsl create mode 100644 samples/04_scenes/shader/eqrect_to_cube.cs.hlsl create mode 100644 samples/04_scenes/shader/graphics_bindings.glsl create mode 100644 samples/04_scenes/shader/ibl_common.hlsli delete mode 100644 samples/04_scenes/shader/model.vs.hlsl create mode 100644 samples/04_scenes/shader/prefilter.cs.hlsl diff --git a/samples/04_scenes/CMakeLists.txt b/samples/04_scenes/CMakeLists.txt index ef40a12..ba57faf 100644 --- a/samples/04_scenes/CMakeLists.txt +++ b/samples/04_scenes/CMakeLists.txt @@ -13,11 +13,18 @@ add_executable(scene_render main.cpp pipeline_utils.cpp pipeline_utils.h core_components.h ecs_adapter.h - camera.h) + camera.h + ibl_helpers.cpp ibl_helpers.h + light_manager.cpp light_manager.h) add_shader(scene_render shader/model.frag.glsl) add_shader(scene_render shader/model.vert.glsl) -# add_shader(scene_render shader/model.vs.hlsl) +add_shader(scene_render shader/eqrect_to_cube.cs.hlsl) +add_shader(scene_render shader/diffuse_irradiance.cs.hlsl) +add_shader(scene_render shader/prefilter.cs.hlsl) +add_shader(scene_render shader/brdf_lut.cs.hlsl) +add_shader(scene_render shader/background.vert.glsl) +add_shader(scene_render shader/background.frag.glsl) target_link_libraries(scene_render PRIVATE aster_core) target_link_libraries(scene_render PRIVATE util_helper) diff --git a/samples/04_scenes/asset_loader.cpp b/samples/04_scenes/asset_loader.cpp index cf7d5b9..4290083 100644 --- a/samples/04_scenes/asset_loader.cpp +++ b/samples/04_scenes/asset_loader.cpp @@ -99,9 +99,8 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const i32 x, y, nChannels; f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4); - assert(nChannels == 3); - ERROR_IF(!data, "Could not load {}", path) THEN_ABORT(-1); + assert(nChannels == 3); u32 width = Cast(x); u32 height = Cast(y); diff --git a/samples/04_scenes/asset_loader.h b/samples/04_scenes/asset_loader.h index 8ea83a5..dc5ce9d 100644 --- a/samples/04_scenes/asset_loader.h +++ b/samples/04_scenes/asset_loader.h @@ -35,8 +35,6 @@ struct Material TextureHandle m_MetalRoughTex; // 04 48 TextureHandle m_OcclusionTex; // 04 52 TextureHandle m_EmissionTex; // 04 56 - - static constexpr usize ALIGNMENT = 4; }; static_assert(sizeof(Material) == 56); @@ -47,8 +45,6 @@ struct VertexData vec2 m_TexCoord0 = vec2{0.0f, 0.0f}; vec2 m_TexCoord1 = vec2{0.0f, 0.0f}; vec4 m_Color0 = vec4{1.0f, 1.0f, 1.0f, 1.0f}; - - static constexpr usize ALIGNMENT = 16; }; struct Model diff --git a/samples/04_scenes/ibl_helpers.cpp b/samples/04_scenes/ibl_helpers.cpp new file mode 100644 index 0000000..b52e48e --- /dev/null +++ b/samples/04_scenes/ibl_helpers.cpp @@ -0,0 +1,395 @@ +// ============================================= +// Aster: ibl_helpers.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "ibl_helpers.h" + +#include "EASTL/fixed_vector.h" +#include "EASTL/tuple.h" +#include "asset_loader.h" +#include "device.h" +#include "render_resource_manager.h" +#include "helpers.h" +#include "image.h" +#include "pipeline_utils.h" + +constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv"; +constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv"; +constexpr cstr PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv"; +constexpr cstr BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv"; + +void +Environment::Destroy(RenderResourceManager *resourceManager) +{ + resourceManager->Release(Take(m_Skybox)); + resourceManager->Release(Take(m_Diffuse)); + resourceManager->Release(Take(m_Prefilter)); + resourceManager->Release(Take(m_BrdfLut)); +} + +Environment +CreateEnvironment(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv, + const cstr name) +{ + RenderResourceManager *resMan = assetLoader->m_ResourceManager; + const Device *pDevice = resMan->m_Device; + + vk::SamplerCreateInfo brdfLutSamplerCreateInfo = resMan->m_DefaultSamplerCreateInfo; + brdfLutSamplerCreateInfo.addressModeU = vk::SamplerAddressMode::eClampToEdge; + brdfLutSamplerCreateInfo.addressModeV = vk::SamplerAddressMode::eClampToEdge; + brdfLutSamplerCreateInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge; + + StorageTextureCube skybox; + StorageTextureCube diffuseIrradiance; + StorageTextureCube prefilterCube; + StorageTexture brdfLut; + SamplerHandle brdfLutSampler; + + skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox"); + TextureHandle skyboxHandle = resMan->CommitTexture(&skybox); + StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox); + + diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance"); + TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance); + StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance); + + prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter"); + TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us. + constexpr u32 prefilterMipCountMax = 6; + eastl::array prefilterStorageHandles; + // All non-owning copies. + for (u32 mipLevel = 0; auto &tex : prefilterStorageHandles) + { + vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = prefilterCube.m_Image, + .viewType = vk::ImageViewType::eCube, + .format = vk::Format::eR16G16B16A16Sfloat, + .components = vk::ComponentMapping{}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = mipLevel++, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 6, + }, + }; + AbortIfFailed(pDevice->m_Device.createImageView(&imageViewCreateInfo, nullptr, &prefilterCube.m_View)); + tex = resMan->CommitStorageTexture(&prefilterCube); + } + + brdfLut.Init(pDevice, {512, 512}, vk::Format::eR16G16Sfloat, true, "BRDF LUT"); + brdfLutSampler = resMan->CreateSampler(&brdfLutSamplerCreateInfo); + TextureHandle brdfLutHandle = resMan->CommitTexture(&brdfLut, brdfLutSampler); + StorageTextureHandle brdfLutStorageHandle = resMan->CommitStorageTexture(&brdfLut); + +#pragma region Dependencies and Copies + vk::ImageSubresourceRange cubeSubresRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 6, + }; + vk::ImageSubresourceRange lutSubresRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = { + .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, + .subresourceRange = cubeSubresRange, + }; + eastl::fixed_vector readyToWriteBarriers(4, readyToWriteBarrierTemplate); + readyToWriteBarriers[0].image = skybox.m_Image; + readyToWriteBarriers[1].image = diffuseIrradiance.m_Image; + readyToWriteBarriers[2].image = prefilterCube.m_Image; + readyToWriteBarriers[3].image = brdfLut.m_Image; + readyToWriteBarriers[3].subresourceRange = lutSubresRange; + + vk::DependencyInfo readyToWriteDependency = { + .imageMemoryBarrierCount = Cast(readyToWriteBarriers.size()), + .pImageMemoryBarriers = readyToWriteBarriers.data(), + }; + + vk::ImageMemoryBarrier2 readyToSampleBarrierTemplate = { + .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, + .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead, + .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe, + .oldLayout = vk::ImageLayout::eGeneral, + .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .subresourceRange = cubeSubresRange, + }; + auto skyboxToSampleBarrier = readyToSampleBarrierTemplate; + skyboxToSampleBarrier.image = skybox.m_Image; + + auto diffIrrToSampleBarrier = readyToSampleBarrierTemplate; + diffIrrToSampleBarrier.image = diffuseIrradiance.m_Image; + + auto prefilterToSampleBarrier = readyToSampleBarrierTemplate; + prefilterToSampleBarrier.image = prefilterCube.m_Image; + + auto brdfToSampleBarrier = readyToSampleBarrierTemplate; + prefilterToSampleBarrier.image = brdfLut.m_Image; + prefilterToSampleBarrier.subresourceRange = lutSubresRange; + + vk::DependencyInfo skyboxToSampleDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &skyboxToSampleBarrier, + }; + vk::DependencyInfo diffIrrToSampleDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &diffIrrToSampleBarrier, + }; + vk::DependencyInfo prefilterToSampleDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &prefilterToSampleBarrier, + }; + vk::DependencyInfo brdfToSampleDependency = { + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &brdfToSampleBarrier, + }; + +#pragma endregion + + struct SkyboxPushConstants + { + TextureHandle m_HdrEnvHandle; + StorageTextureHandle m_OutputTexture; + u32 m_CubeSide; + }; + struct DiffuseIrradiancePushConstants + { + TextureHandle m_SkyboxHandle; + StorageTextureHandle m_OutputTexture; + u32 m_CubeSide; + }; + struct PrefilterPushConstants + { + TextureHandle m_SkyboxHandle; + StorageTextureHandle m_OutputTexture; + u32 m_CubeSide; + f32 m_Roughness; + u32 m_EnvSide; + }; + struct BrdfLutPushConstants + { + StorageTextureHandle m_OutputTexture; + }; + +#pragma region Pipeline Creation etc + + vk::PushConstantRange pcr = { + .stageFlags = vk::ShaderStageFlagBits::eCompute, + .offset = 0, + .size = eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)), + eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))), + }; + + 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 auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); + const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); + const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE); + const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE); + eastl::array computePipelineCreateInfo = { + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = eqRectToCubeShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = diffuseRadianceShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = prefilterShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = brdfLutShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, + }; + + eastl::array pipelines; + AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(), + computePipelineCreateInfo.data(), nullptr, + pipelines.data())); + + vk::Pipeline eqRectToCubePipeline = pipelines[0]; + vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; + vk::Pipeline prefilterPipeline = pipelines[2]; + vk::Pipeline brdfLutPipeline = pipelines[3]; + + for (auto &createInfos : computePipelineCreateInfo) + { + pDevice->m_Device.destroy(createInfos.stage.module, nullptr); + } + +#pragma endregion + + SkyboxPushConstants skyboxPushConstant = { + .m_HdrEnvHandle = hdrEnv, + .m_OutputTexture = skyboxStorageHandle, + .m_CubeSide = cubeSide, + }; + DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = { + .m_SkyboxHandle = skyboxHandle, + .m_OutputTexture = diffuseIrradianceStorageHandle, + .m_CubeSide = diffuseIrradiance.m_Extent.width, + }; + PrefilterPushConstants prefilterPushConstants = { + .m_SkyboxHandle = skyboxHandle, + .m_EnvSide = cubeSide, + }; + BrdfLutPushConstants brdfLutPushConstants = { + .m_OutputTexture = brdfLutStorageHandle, + }; + + resMan->Update(); + + auto cmd = assetLoader->m_CommandBuffer; + constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; + AbortIfFailed(cmd.begin(&beginInfo)); + +#if !defined(ASTER_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, eqRectToCubePipeline); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, + &skyboxPushConstant); + assert(skybox.m_Extent.width % 16 == 0 && skybox.m_Extent.height % 16 == 0); + cmd.dispatch(skybox.m_Extent.width / 16, skybox.m_Extent.height / 16, 6); + + GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral, + vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader); + + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, + &diffuseIrradiancePushConstants); + assert(diffuseIrradiance.m_Extent.width % 16 == 0 && diffuseIrradiance.m_Extent.height % 16 == 0); + cmd.dispatch(diffuseIrradiance.m_Extent.width / 16, diffuseIrradiance.m_Extent.width / 16, 6); + + cmd.pipelineBarrier2(&diffIrrToSampleDependency); + + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline); + u32 mipSize = prefilterCube.m_Extent.width; + assert(mipSize % 16 == 0); + for (u32 mipCount = 0; auto &tex : prefilterStorageHandles) + { + prefilterPushConstants.m_OutputTexture = tex; + prefilterPushConstants.m_CubeSide = mipSize; + prefilterPushConstants.m_Roughness = Cast(mipCount) / Cast(prefilterMipCountMax); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof prefilterPushConstants, + &prefilterPushConstants); + u32 groupCount = eastl::max(mipSize / 16u, 1u); + cmd.dispatch(groupCount, groupCount, 6); + + ++mipCount; + mipSize = mipSize >> 1; + } + + cmd.pipelineBarrier2(&skyboxToSampleDependency); + cmd.pipelineBarrier2(&prefilterToSampleDependency); + + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, brdfLutPipeline); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof brdfLutPushConstants, + &brdfLutPushConstants); + assert(brdfLut.m_Extent.width % 16 == 0 && brdfLut.m_Extent.height % 16 == 0); + cmd.dispatch(brdfLut.m_Extent.width / 16, brdfLut.m_Extent.height / 16, 1); + +#if !defined(ASTER_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, {})); + + skybox = {}; + resMan->Release(skyboxStorageHandle); + resMan->Release(diffuseIrradianceStorageHandle); + resMan->Release(brdfLutStorageHandle); + for (auto &texHandles : prefilterStorageHandles) + { + StorageTextureCube st; + resMan->Release(&st, texHandles); + pDevice->m_Device.destroy(st.m_View, nullptr); + } + for (auto &pipeline : pipelines) + { + pDevice->m_Device.destroy(pipeline, nullptr); + } + pDevice->m_Device.destroy(pipelineLayout, nullptr); + + return { + .m_Skybox = skyboxHandle, + .m_Diffuse = diffuseIrradianceHandle, + .m_Prefilter = prefilterHandle, + .m_BrdfLut = brdfLutHandle, + }; +} \ No newline at end of file diff --git a/samples/04_scenes/ibl_helpers.h b/samples/04_scenes/ibl_helpers.h new file mode 100644 index 0000000..624b5df --- /dev/null +++ b/samples/04_scenes/ibl_helpers.h @@ -0,0 +1,28 @@ +// ============================================= +// Aster: ibl_helpers.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" +#include "render_resource_manager.h" + +struct Pipeline; +struct Texture; +struct TextureCube; +struct AssetLoader; + +struct Environment +{ + TextureHandle m_Skybox; + TextureHandle m_Diffuse; + TextureHandle m_Prefilter; + TextureHandle m_BrdfLut; + + void Destroy(RenderResourceManager *resourceManager); +}; + +Environment +CreateEnvironment(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, + cstr name = nullptr); \ No newline at end of file diff --git a/samples/04_scenes/image/photo_studio_loft_hall_4k.hdr b/samples/04_scenes/image/photo_studio_loft_hall_4k.hdr new file mode 100644 index 0000000..beea1dc --- /dev/null +++ b/samples/04_scenes/image/photo_studio_loft_hall_4k.hdr @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b1e43955efa60a24e8021994d3ab6e630ba142fbd02d1530a0cc0ab53ec41a2 +size 25245847 diff --git a/samples/04_scenes/light_manager.cpp b/samples/04_scenes/light_manager.cpp new file mode 100644 index 0000000..bbf9b0b --- /dev/null +++ b/samples/04_scenes/light_manager.cpp @@ -0,0 +1,303 @@ +// ============================================= +// Aster: light_manager.cpp +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#include "light_manager.h" + +#include "buffer.h" +#include "ibl_helpers.h" +#include "glm/ext/matrix_transform.hpp" + +struct Light +{ + union { + vec3 um_Position; + vec3 um_Direction; + }; + f32 m_Range; // < 0.0 for invalid + u32 m_Color_; // LSB is used for flags. (R G B Flags) + f32 m_Intensity; + + constexpr static u32 MAX_GEN = 0x40; + constexpr static u32 GEN_MASK = MAX_GEN - 1; + + constexpr static u32 TYPE_MASK = 0xC0; + constexpr static u32 TYPE_INVALID = 0x0; + constexpr static u32 TYPE_DIRECTIONAL = 1 << 6; + constexpr static u32 TYPE_POINT = 2 << 6; + constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused + + constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK); +}; + +// Static Checks + +// Ensure layouts are exact. +static_assert(offsetof(DirectionalLight, m_Direction) == offsetof(Light, um_Direction)); +static_assert(offsetof(DirectionalLight, m_Color_) == offsetof(Light, m_Color_)); +static_assert(offsetof(DirectionalLight, m_Intensity) == offsetof(Light, m_Intensity)); +static_assert(sizeof(DirectionalLight) <= sizeof(Light)); + +// Ensure layouts are exact. +static_assert(offsetof(PointLight, m_Position) == offsetof(Light, um_Position)); +static_assert(offsetof(PointLight, m_Range) == offsetof(Light, m_Range)); +static_assert(offsetof(PointLight, m_Color_) == offsetof(Light, m_Color_)); +static_assert(offsetof(PointLight, m_Intensity) == offsetof(Light, m_Intensity)); +static_assert(sizeof(PointLight) <= sizeof(Light)); + +// Ensure bitmask are in the right place. +static_assert((Light::TYPE_MASK & Light::TYPE_INVALID) == Light::TYPE_INVALID); +static_assert((Light::TYPE_MASK & Light::TYPE_DIRECTIONAL) == Light::TYPE_DIRECTIONAL); +static_assert((Light::TYPE_MASK & Light::TYPE_POINT) == Light::TYPE_POINT); +static_assert((Light::TYPE_MASK & Light::TYPE_SPOT) == Light::TYPE_SPOT); +static_assert(Light::COLOR_MASK == 0xFFFFFF00); + +inline u32 +ToColor32(const vec4 &col) +{ + const u32 r = Cast(eastl::min(col.r, 1.0f) * 255.99f); + const u32 g = Cast(eastl::min(col.g, 1.0f) * 255.99f); + const u32 b = Cast(eastl::min(col.b, 1.0f) * 255.99f); + const u32 a = Cast(eastl::min(col.a, 1.0f) * 255.99f); + + return r << 24 | g << 16 | b << 8 | a; +} + +inline u32 +ToColor32(const vec3 &col) +{ + const u32 r = Cast(eastl::min(col.r, 1.0f) * 255.99f); + const u32 g = Cast(eastl::min(col.g, 1.0f) * 255.99f); + const u32 b = Cast(eastl::min(col.b, 1.0f) * 255.99f); + constexpr u32 a = 255; + + return r << 24 | g << 16 | b << 8 | a; +} + +LightManager::LightManager(RenderResourceManager *resourceManager) + : m_ResourceManager{resourceManager} + , m_DirectionalLightCount{} + , m_PointLightCount{} + , m_MetaInfo{} + , m_GpuBufferCapacity_{0} +{ +} + +LightManager::~LightManager() +{ + m_ResourceManager->Release(m_MetaInfo.m_LightBuffer); + m_ResourceManager->Release(m_MetaInfo.m_Skybox); + m_ResourceManager->Release(m_MetaInfo.m_Diffuse); + m_ResourceManager->Release(m_MetaInfo.m_Prefilter); + m_ResourceManager->Release(m_MetaInfo.m_BrdfLut); +} + +LightManager::LightManager(LightManager &&other) noexcept + : m_ResourceManager(other.m_ResourceManager) + , m_Lights(std::move(other.m_Lights)) + , m_DirectionalLightCount(other.m_DirectionalLightCount) + , m_PointLightCount(other.m_PointLightCount) + , m_MetaInfo(other.m_MetaInfo) + , m_GpuBufferCapacity_(other.m_GpuBufferCapacity_) +{ + other.m_MetaInfo.m_LightBuffer = {}; +} + +LightManager & +LightManager::operator=(LightManager &&other) noexcept +{ + if (this == &other) + return *this; + m_ResourceManager = other.m_ResourceManager; + m_Lights = std::move(other.m_Lights); + m_DirectionalLightCount = other.m_DirectionalLightCount; + m_PointLightCount = other.m_PointLightCount; + m_MetaInfo = other.m_MetaInfo; + other.m_MetaInfo.m_LightBuffer = {}; + m_GpuBufferCapacity_ = other.m_GpuBufferCapacity_; + return *this; +} + +LightHandle +LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity) +{ + const vec3 normDirection = normalize(direction); + if (m_DirectionalLightCount < m_MetaInfo.m_DirectionalLightMaxCount) + { + u16 index = 0; + for (auto &light : m_Lights) + { + if (light.m_Range < 0) + { + const u8 gen = light.m_Color_ & Light::GEN_MASK; + + light.m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen; + light.m_Range = 1.0f; + light.um_Direction = normDirection; + light.m_Intensity = intensity; + + m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT; + + return {Light::TYPE_DIRECTIONAL, gen, index}; + } + ++index; + assert(index < m_MetaInfo.m_DirectionalLightMaxCount); // Gap not found. But must exist + } + } + + // In case we will end up intersecting, we move point lights away. (2 at a time) + if (m_DirectionalLightCount == m_MetaInfo.m_DirectionalLightMaxCount && + m_MetaInfo.m_DirectionalLightMaxCount == m_MetaInfo.m_PointLightOffset) + { + const u16 oldPointLightOffset = m_MetaInfo.m_PointLightOffset; + const u32 pointLightMaxCount = m_MetaInfo.m_PointLightMaxCount; + // Might cause a capacity increase, but I want to use that for my gpu buffer resize. + m_Lights.push_back(); + m_Lights.push_back(); + + if (m_MetaInfo.m_PointLightMaxCount > 0) // Edge Case: nullptr at size 0 + { + Light *oldPointStart = m_Lights.data() + oldPointLightOffset; + Light *oldPointEnd = oldPointStart + pointLightMaxCount; + Light *newPointEnd = oldPointEnd + 2; + + static_assert(std::is_trivially_copyable_v); + + // Overlaps since 0 -> 2, 1 -> 3, 2 -> 4 (old 2 is lost) + // Backward copy fixes this. + std::copy_backward(oldPointStart, oldPointEnd, newPointEnd); + } + + m_MetaInfo.m_PointLightOffset += 2; + } + + constexpr u8 gen = 0; // New light + m_Lights[m_DirectionalLightCount].m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen; + m_Lights[m_DirectionalLightCount].m_Range = 1.0f; + m_Lights[m_DirectionalLightCount].um_Direction = normDirection; + m_Lights[m_DirectionalLightCount].m_Intensity = intensity; + const u16 index = m_DirectionalLightCount; + + ++m_DirectionalLightCount; + ++m_MetaInfo.m_DirectionalLightMaxCount; + + return {Light::TYPE_DIRECTIONAL, gen, index}; +} + +LightHandle +LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius, f32 intensity) +{ + assert(m_PointLightCount <= m_MetaInfo.m_PointLightMaxCount); + assert(radius >= 0.0f); + if (m_PointLightCount < m_MetaInfo.m_PointLightMaxCount) + { + Light *light = m_Lights.data() + m_MetaInfo.m_PointLightOffset; + for (u32 index = 0; index < m_MetaInfo.m_PointLightMaxCount; ++index) + { + if (light->m_Range < 0) + { + const u8 gen = light->m_Color_ & Light::GEN_MASK; + + light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen; + light->m_Range = radius; + light->um_Position = position; + light->m_Intensity = intensity; + + m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT; + + return {Light::TYPE_POINT, gen, Cast(index)}; + } + ++light; + } + assert(false); // gap must exists. + return {}; + } + + m_Lights.push_back(); + const u16 index = m_PointLightCount; + + Light *light = &m_Lights[index + m_MetaInfo.m_PointLightOffset]; + constexpr u8 gen = 0; // New light + + light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen; + light->m_Range = radius; + light->um_Position = position; + light->m_Intensity = intensity; + + ++m_PointLightCount; + ++m_MetaInfo.m_PointLightMaxCount; + + m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT; + + return {Light::TYPE_POINT, gen, index}; +} + +void +LightManager::Update() +{ + const u16 requiredBufferCapacity = eastl::min(Cast(m_Lights.capacity()), MAX_LIGHTS); + if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity) + { + StorageBuffer newBuffer; + newBuffer.Init(m_ResourceManager->m_Device, requiredBufferCapacity * sizeof m_Lights[0], true, "Light Buffer"); + m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT; + + m_ResourceManager->Release(m_MetaInfo.m_LightBuffer); + m_MetaInfo.m_LightBuffer = m_ResourceManager->Commit(&newBuffer); + } + if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT) + { + m_ResourceManager->Write(m_MetaInfo.m_LightBuffer, 0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data()); + } +} + +void +LightManager::RemoveLight(const LightHandle handle) +{ + const u8 handleGen = handle.m_Generation; + + if (handle.m_Type == Light::TYPE_DIRECTIONAL) + { + Light *lightSlot = &m_Lights[handle.m_Index]; + const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK; + if (slotGen > handleGen) + { + WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen); + return; + } + + lightSlot->m_Range = -1.0f; + lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN; + --m_DirectionalLightCount; + } + + if (handle.m_Type == Light::TYPE_POINT) + { + Light *lightSlot = &m_Lights[handle.m_Index + m_MetaInfo.m_PointLightOffset]; + const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK; + if (slotGen > handleGen) + { + WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen); + return; + } + + lightSlot->m_Range = -1.0f; + lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN; + --m_PointLightCount; + } +} + +void +LightManager::SetEnvironment(Environment &&environment) +{ + m_ResourceManager->Release(m_MetaInfo.m_Skybox); + m_ResourceManager->Release(m_MetaInfo.m_Diffuse); + m_ResourceManager->Release(m_MetaInfo.m_Prefilter); + m_ResourceManager->Release(m_MetaInfo.m_BrdfLut); + + m_MetaInfo.m_Skybox = environment.m_Skybox; + m_MetaInfo.m_Diffuse = environment.m_Diffuse; + m_MetaInfo.m_Prefilter = environment.m_Prefilter; + m_MetaInfo.m_BrdfLut = environment.m_BrdfLut; +} \ No newline at end of file diff --git a/samples/04_scenes/light_manager.h b/samples/04_scenes/light_manager.h new file mode 100644 index 0000000..8e83940 --- /dev/null +++ b/samples/04_scenes/light_manager.h @@ -0,0 +1,91 @@ +// ============================================= +// Aster: light_manager.h +// Copyright (c) 2020-2024 Anish Bhobe +// ============================================= + +#pragma once + +#include "global.h" + +// TODO: Separate files so you only import handles. +#include "render_resource_manager.h" + +struct Environment; + +struct DirectionalLight +{ + vec3 m_Direction; + u32 m_UnusedPadding0_; + u32 m_Color_; // LSB is used for flags. (R G B Flags) + f32 m_Intensity; +}; + +struct PointLight +{ + vec3 m_Position; + f32 m_Range; + u32 m_Color_; // LSB is used for flags. (R G B Flags) + f32 m_Intensity; +}; + +struct LightHandle +{ + u8 m_Type; + u8 m_Generation; + u16 m_Index; +}; + +struct Light; + +struct LightManager +{ + constexpr static u16 MAX_LIGHTS = MaxValue; + struct LightMetaInfo + { + TextureHandle m_Skybox; // 04 04 + TextureHandle m_Diffuse; // 04 08 + TextureHandle m_Prefilter; // 04 12 + TextureHandle m_BrdfLut; // 04 16 + + // The number of directional lights is relatively low (1 - 2) and will almost never change in a scene. + // We can use that with Offset = 0, and point light at further offsets. + // This way we don't need to move point lights often. + BufferHandle m_LightBuffer; // 04 20 + u16 m_PointLightMaxCount; // 02 22 + u16 m_PointLightOffset; // 02 24 + u16 m_DirectionalLightMaxCount; // 02 26 + u16 m_UnusedPadding0 = 0; // 02 28 + }; + + RenderResourceManager *m_ResourceManager; + eastl::vector m_Lights; + + // We don't need a Directional Light free list. We will just brute force iterate. + u16 m_DirectionalLightCount; + // TODO: A point light free list. We will brute force until we have a lot (100+) of point lights. + u16 m_PointLightCount; + + LightMetaInfo m_MetaInfo; + // Using lower bit for flags. Use CAPACITY_MASK for value. + u16 m_GpuBufferCapacity_; + + // Using lower bit. Capacity can be directly a multiple of 2 + // Thus, range is up to MaxValue + constexpr static u16 UPDATE_REQUIRED_BIT = 1; + constexpr static u16 CAPACITY_MASK = ~(UPDATE_REQUIRED_BIT); + + LightHandle AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity); + LightHandle AddPoint(const vec3 &position, const vec3 &color, f32 radius, f32 intensity); + void Update(); + void RemoveLight(LightHandle handle); + + void SetEnvironment(Environment &&environment); + + explicit LightManager(RenderResourceManager *resourceManager); + ~LightManager(); + + LightManager(LightManager &&other) noexcept; + LightManager &operator=(LightManager &&other) noexcept; + + DISALLOW_COPY_AND_ASSIGN(LightManager); +}; \ No newline at end of file diff --git a/samples/04_scenes/main.cpp b/samples/04_scenes/main.cpp index db43aef..16bde5e 100644 --- a/samples/04_scenes/main.cpp +++ b/samples/04_scenes/main.cpp @@ -11,12 +11,14 @@ #include "swapchain.h" #include "window.h" +#include "light_manager.h" #include "asset_loader.h" #include "camera.h" #include "core_components.h" #include "ecs_adapter.h" #include "frame.h" +#include "ibl_helpers.h" #include "image.h" #include "pipeline.h" @@ -93,16 +95,30 @@ main(int, char *[]) AssetLoader assetLoader = {&resourceManager, ®istry, graphicsQueue, queueAllocation.m_Family, queueAllocation.m_Family}; + LightManager lightManager = LightManager{&resourceManager}; + + Texture environmentHdri; + assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE); + auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri); + + auto environment = CreateEnvironment(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env"); + + resourceManager.Release(envHdriHandle); + + lightManager.SetEnvironment(std::move(environment)); Model model = assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"); Model model2 = assetLoader.LoadModelToGpu(MODEL_FILE2, "Main Model 2"); registry.get(model2.m_RootEntity).m_Position.x += 1.0f; UniformBuffer ubo; - ubo.Init(&device, sizeof cameraController.m_Camera, "Desc1 UBO"); + constexpr usize uboTotalSize = sizeof cameraController.m_Camera + sizeof lightManager.m_MetaInfo; + ubo.Init(&device, uboTotalSize, "Desc1 UBO"); ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); + ubo.Write(&device, sizeof cameraController.m_Camera, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager); + Pipeline backgroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager); vk::DescriptorPool descriptorPool; vk::DescriptorSet perFrameDescriptor; @@ -127,10 +143,10 @@ main(int, char *[]) AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &perFrameDescriptor)); } - vk::DescriptorBufferInfo cameraBufferInfo = { + vk::DescriptorBufferInfo camLightBufferInfo = { .buffer = ubo.m_Buffer, .offset = 0, - .range = sizeof(Camera), + .range = uboTotalSize, }; eastl::array writeDescriptors = { vk::WriteDescriptorSet{ @@ -139,7 +155,7 @@ main(int, char *[]) .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = vk::DescriptorType::eUniformBuffer, - .pBufferInfo = &cameraBufferInfo, + .pBufferInfo = &camLightBufferInfo, }, }; device.m_Device.updateDescriptorSets(Cast(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr); @@ -438,7 +454,6 @@ main(int, char *[]) cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0, nullptr); - //TODO("Unify index buffers"); cmd.bindIndexBuffer(resourceManager.GetIndexBuffer(), 0, vk::IndexType::eUint32); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); @@ -452,6 +467,10 @@ main(int, char *[]) cmd.drawIndexed(node.m_IndexCount, 1, node.m_FirstIndex, 0, 0); } + cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backgroundPipeline.m_Pipeline); + + cmd.draw(3, 1, 0, 0); + cmd.endRendering(); cmd.pipelineBarrier2(&postRenderDependencies); diff --git a/samples/04_scenes/pipeline_utils.cpp b/samples/04_scenes/pipeline_utils.cpp index 50812b3..5604abd 100644 --- a/samples/04_scenes/pipeline_utils.cpp +++ b/samples/04_scenes/pipeline_utils.cpp @@ -43,18 +43,6 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe .descriptorCount = 1, .stageFlags = vk::ShaderStageFlagBits::eAll, }, - vk::DescriptorSetLayoutBinding{ - .binding = 1, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eAll, - }, - vk::DescriptorSetLayoutBinding{ - .binding = 2, - .descriptorType = vk::DescriptorType::eUniformBuffer, - .descriptorCount = 1, - .stageFlags = vk::ShaderStageFlagBits::eAll, - }, }; vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { .bindingCount = Cast(descriptorSetLayoutBindings.size()), @@ -67,7 +55,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe } vk::PushConstantRange pushConstantRange = { - .stageFlags = vk::ShaderStageFlagBits::eAll, + .stageFlags = vk::ShaderStageFlagBits::eAll, .offset = 0, .size = 96, }; @@ -80,7 +68,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe }; vk::PipelineLayout pipelineLayout; AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); - device->SetName(pipelineLayout, "Box Layout"); + device->SetName(pipelineLayout, "Scene Layout"); descriptorSetLayouts[0] = nullptr; // Not owned. @@ -172,6 +160,157 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)}; } + + +Pipeline +CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *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 = 96, + }; + + 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, "Scene BG 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/04_scenes/pipeline_utils.h b/samples/04_scenes/pipeline_utils.h index 01ac264..9459aaa 100644 --- a/samples/04_scenes/pipeline_utils.h +++ b/samples/04_scenes/pipeline_utils.h @@ -14,7 +14,12 @@ struct Device; constexpr auto VERTEX_SHADER_FILE = "shader/model.vert.glsl.spv"; constexpr auto FRAGMENT_SHADER_FILE = "shader/model.frag.glsl.spv"; +constexpr auto BACKGROUND_VERTEX_SHADER_FILE = "shader/background.vert.glsl.spv"; +constexpr auto BACKGROUND_FRAGMENT_SHADER_FILE = "shader/background.frag.glsl.spv"; vk::ShaderModule CreateShader(const Device *device, cstr shaderFile); Pipeline CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager); + +Pipeline +CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager); \ No newline at end of file diff --git a/samples/04_scenes/shader/background.frag.glsl b/samples/04_scenes/shader/background.frag.glsl new file mode 100644 index 0000000..ab4471b --- /dev/null +++ b/samples/04_scenes/shader/background.frag.glsl @@ -0,0 +1,35 @@ +#version 450 +#pragma shader_stage(fragment) +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference : require +#extension GL_EXT_nonuniform_qualifier : enable + +#include "bindless_structs.glsl" +#include "graphics_bindings.glsl" + +layout (location=0) in vec3 in_WorldPosition; + +layout (location=0) out vec4 out_Color; + +void main() +{ + vec3 direction = normalize(in_WorldPosition - camera.m_Position.xyz); +#ifndef _DEBUG + vec4 color = texture(textureCubes[lights.m_EnvCubeHandle], direction); +#else + vec4 color; +// if ((PushConstant.DebugFlags & SHOW_DIFFUSE_BIT) > 0) +// { +// Color = TextureCubes[Lights.DiffuseIrradianceHandle].Sample(ImmutableSamplers[Lights.DiffuseIrradianceHandle], Direction); +// } +// else if ((PushConstant.DebugFlags & SHOW_PREFILTER_BIT) > 0) +// { +// Color = TextureCubes[Lights.PrefilterHandle].Sample(ImmutableSamplers[Lights.PrefilterHandle], Direction); +// } +// else + { + color = texture(textureCubes[lights.m_EnvCubeHandle], direction); + } +#endif + out_Color = vec4(Uncharted2Tonemap(color.rgb), color.a); +} \ No newline at end of file diff --git a/samples/04_scenes/shader/background.vert.glsl b/samples/04_scenes/shader/background.vert.glsl new file mode 100644 index 0000000..fe98078 --- /dev/null +++ b/samples/04_scenes/shader/background.vert.glsl @@ -0,0 +1,24 @@ +#version 450 +#pragma shader_stage(vertex) +#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable +#extension GL_EXT_buffer_reference : require + +#include "graphics_bindings.glsl" + +layout (location = 0) out vec3 out_WorldPosition; + +void main() +{ + vec3 points[] = + { + vec3(-1.0f, -1.0f, 1.0f), + vec3(3.0f, -1.0f, 1.0f), + vec3(-1.0f, 3.0f, 1.0f), + }; + + gl_Position = vec4(points[gl_VertexIndex], 1.0f); + + vec4 clipSpace = camera.m_InvProjection * vec4(points[gl_VertexIndex], 1.0f); + vec4 worldSpace = camera.m_InvView * (clipSpace / clipSpace.wwww); + out_WorldPosition = worldSpace.xyz; +} \ No newline at end of file diff --git a/samples/04_scenes/shader/bindless_structs.glsl b/samples/04_scenes/shader/bindless_structs.glsl new file mode 100644 index 0000000..ce0ee91 --- /dev/null +++ b/samples/04_scenes/shader/bindless_structs.glsl @@ -0,0 +1,53 @@ + +struct VertexData { + vec4 Normal; + vec2 TexCoord0; + vec2 TexCoord1; + vec4 Color; +}; + +struct Material +{ + vec4 m_AlbedoFactor; // 16 16 + vec3 m_EmissionFactor; // 12 28 + float m_MetalFactor; // 04 32 + float m_RoughFactor; // 04 36 + uint m_AlbedoTex; // 04 40 + uint m_NormalTex; // 04 44 + uint m_MetalRoughTex; // 04 48 + uint m_OcclusionTex; // 04 52 + uint m_EmissionTex; // 04 56 + // We might be able to go upto 64 without pains. +}; + +// ---------- Bindless Bindings ---------- +#define INVALID_HANDLE 0xFFFFFFFF + +layout (set = 0, binding = 1) uniform sampler2D textures[]; +layout (set = 0, binding = 1) uniform samplerCube textureCubes[]; + + +// ---------- Buffer References ---------- +layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef { + vec4 Positions[]; +}; + +layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VDataRef { + VertexData Data[]; +}; + +layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer MaterialsRef { + Material materials[]; +}; + +layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer MaterialRef { + vec4 m_AlbedoFactor; // 16 16 + vec3 m_EmissionFactor; // 12 28 + float m_MetalFactor; // 04 32 + float m_RoughFactor; // 04 36 + uint m_AlbedoTex; // 04 40 + uint m_NormalTex; // 04 44 + uint m_MetalRoughTex; // 04 48 + uint m_OcclusionTex; // 04 52 + uint m_EmissionTex; // 04 56 +}; \ No newline at end of file diff --git a/samples/04_scenes/shader/bindless_structs.hlsli b/samples/04_scenes/shader/bindless_structs.hlsli new file mode 100644 index 0000000..3e9bf13 --- /dev/null +++ b/samples/04_scenes/shader/bindless_structs.hlsli @@ -0,0 +1,69 @@ + +struct VertexData +{ + float4 Normal; + float2 TexCoord0; + float2 TexCoord1; + float4 Color0; +}; + +struct TransformData +{ + float4x4 Transform; + float4x4 NormalTransform; +}; + +struct MaterialData +{ + float AlbedoFactor[4]; + float EmissionFactor[3]; + float MetalFactor; + float RoughFactor; + uint AlbedoTex; + uint NormalTex; + uint MetalRoughTex; + uint OcclusionTex; + uint EmissionTex; +}; + +struct PointLight +{ + float Position[3]; + float Range; + uint Color; + float Intensity; +}; + +struct DirectionalLight +{ + float Direction[3]; + float Validity_; + uint Color; + float Intensity; +}; + +// Little Endian storage. First short is least significant. +#define IndexerCount(Indexer) (Indexer & 0xFFFF) +#define IndexerOffset(Indexer) ((Indexer & 0xFFFF0000) >> 16); + +#define INVALID_HANDLE 0xFFFFFFFF + +static const float HALF_PI = 1.57079633f; +static const float PI = 3.14159265f; +static const float TAU = 6.28318530f; + +[[vk::binding(0, 0)]] StructuredBuffer VertexBuffer[]; +[[vk::binding(0, 0)]] StructuredBuffer VertexDataBuffer[]; +[[vk::binding(0, 0)]] StructuredBuffer MaterialsBuffer[]; +[[vk::binding(0, 0)]] StructuredBuffer NodeBuffer[]; +[[vk::binding(0, 0)]] StructuredBuffer PointLightBuffer[]; +[[vk::binding(0, 0)]] StructuredBuffer DirectionalLightBuffer[]; + +[[vk::binding(1, 0)]] Texture2D Textures[]; +[[vk::binding(1, 0)]] Texture2D TexturesRG[]; +[[vk::binding(1, 0)]] TextureCube TextureCubes[]; +[[vk::binding(1, 0)]] SamplerState ImmutableSamplers[]; + +[[vk::binding(2, 0)]] RWTexture2D StorageTextures[]; +[[vk::binding(2, 0)]] RWTexture2D StorageTexturesRG[]; +[[vk::binding(2, 0)]] RWTexture2DArray StorageTextureArrays[]; diff --git a/samples/04_scenes/shader/brdf_lut.cs.hlsl b/samples/04_scenes/shader/brdf_lut.cs.hlsl new file mode 100644 index 0000000..b515ed7 --- /dev/null +++ b/samples/04_scenes/shader/brdf_lut.cs.hlsl @@ -0,0 +1,82 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint OutputTextureHandle; +}; + +[[vk::push_constant]] +Block Pcb; + +float GeometrySchlickGGX(float NdotV, float Roughness) +{ + float R = Roughness; + // (Rough + 1)^2 / 8 for Punctual Lights + // Rough^2 / 2 for IBL + float K = (R * R) / 2.0; + + float Numerator = NdotV; + float Denominator = NdotV * (1.0f - K) + K; + + return Numerator / Denominator; +} + +float GeometrySmith(float NdotV, float NdotL, float Roughness) +{ + float GGX1 = GeometrySchlickGGX(NdotV, Roughness); + float GGX2 = GeometrySchlickGGX(NdotL, Roughness); + + return GGX1 * GGX2; +} + +float2 IntegrateBRDF(float NdotV, float Roughness) +{ + float3 ViewDir; + ViewDir.x = sqrt(1.0f - NdotV * NdotV); + ViewDir.y = 0.0f; + ViewDir.z = NdotV; + + float A = 0.0f; + float B = 0.0f; + + float3 Normal = float3(0.0f, 0.0f, 1.0f); + + const uint SAMPLE_COUNT = 1024u; + + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float2 Xi = Hammersley(i, SAMPLE_COUNT); + float3 Halfway = ImportanceSampleGGX(Xi, Normal, Roughness); + float3 LightDir = normalize(2.0f * dot(ViewDir, Halfway) * Halfway - ViewDir); + + float NdotL = max(LightDir.z, 0.0f); + float NdotH = max(Halfway.z, 0.0f); + float VdotH = max(dot(ViewDir, Halfway), 0.0f); + + if (NdotL > 0.0f) + { + float G = GeometrySmith(NdotV, NdotL, Roughness); + float G_Vis = (G * VdotH) / max((NdotH * NdotV), 0.0001f); + float Fc = pow(1.0f - VdotH, 5.0f); + + A += (1.0f - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + + return float2(A, B); +} + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float Width, Height; + StorageTexturesRG[Pcb.OutputTextureHandle].GetDimensions(Width, Height); + + float2 UV = GlobalInvocationID.xy / float2(Width, Height); + + float2 IntegratedBRDF = IntegrateBRDF(UV.x, UV.y); + StorageTexturesRG[Pcb.OutputTextureHandle][GlobalInvocationID.xy] = IntegratedBRDF; +} \ No newline at end of file diff --git a/samples/04_scenes/shader/diffuse_irradiance.cs.hlsl b/samples/04_scenes/shader/diffuse_irradiance.cs.hlsl new file mode 100644 index 0000000..21f3313 --- /dev/null +++ b/samples/04_scenes/shader/diffuse_irradiance.cs.hlsl @@ -0,0 +1,52 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint SkyboxHandle; + uint OutputTextureHandle; + int CubeSide; +}; + +[[vk::push_constant]] +Block pcb; + +/* +| 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 Forward, Up, Right; + + Forward = GetCubeDir(GlobalInvocationID, pcb.CubeSide); + Up = abs(Forward.y) < 1.0f ? float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f); // 0.01f offset to + Right = normalize(cross(Up, Forward)); + Up = normalize(cross(Forward, Right)); + + float3 Irradiance = 0.0f.xxx; + float3 IrrDirr = 0.0f.xxx; + float SampleStep = 0.005f; + float SampleCount = 0.0f; + + for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep) + { + for (float Zenith = 0.0f; Zenith < HALF_PI; Zenith += SampleStep) + { + float3 DirectionTanSpace = float3(sin(Zenith) * cos(Azimuth), sin(Zenith) * sin(Azimuth), cos(Zenith)); + float3 DirectionWorld = DirectionTanSpace.x * Right + DirectionTanSpace.y * Up + DirectionTanSpace.z * Forward; + + Irradiance += TextureCubes[pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[pcb.SkyboxHandle], DirectionWorld, 0).xyz * (cos(Zenith) * sin(Zenith)); + SampleCount++; + } + } + + StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f); +} \ No newline at end of file diff --git a/samples/04_scenes/shader/eqrect_to_cube.cs.hlsl b/samples/04_scenes/shader/eqrect_to_cube.cs.hlsl new file mode 100644 index 0000000..d80a0e3 --- /dev/null +++ b/samples/04_scenes/shader/eqrect_to_cube.cs.hlsl @@ -0,0 +1,29 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint HdrEnvHandle; + uint OutputTextureHandle; + int CubeSide; +}; + +[[vk::push_constant]] +Block pcb; + +float2 SampleSphericalMap(float3 v) +{ + const float2 InvTan = float2(0.1591f, 0.3183f); // (1/2PI, 1/PI) + float2 UV = float2(atan2(-v.x, v.z), asin(-v.y)); // (-PI, -PI/2) to (PI, PI/2) + UV *= InvTan; // (-1/2, -1/2) to (1/2, 1/2) + UV += 0.5f.xx; // (0, 0) to (1, 1) + return UV; +} + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float3 LocalDir = GetCubeDir(GlobalInvocationID, pcb.CubeSide); + + float2 UV = SampleSphericalMap(LocalDir); + StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0); +} \ No newline at end of file diff --git a/samples/04_scenes/shader/graphics_bindings.glsl b/samples/04_scenes/shader/graphics_bindings.glsl new file mode 100644 index 0000000..47f7fde --- /dev/null +++ b/samples/04_scenes/shader/graphics_bindings.glsl @@ -0,0 +1,42 @@ + +struct Camera { + mat4 m_View; // 64 + mat4 m_Projection; // 128 + mat4 m_InvView; // 192 + mat4 m_InvProjection; // 256 + vec4 m_Position; // 272 +}; + +struct Lighting +{ + uint m_EnvCubeHandle; // 4 + uint m_DiffuseIrradianceHandle; // 8 + uint m_PrefilterHandle; // 12 + uint m_BrdfLutHandle; // 16 + uint m_LightHandle; // 20 + uint m_PointLightIndexer; // 24 + uint m_DirectionalLightIndexer; // 28 +}; + +layout(set=1, binding=0) uniform Ubo { + Camera camera; + Lighting lights; +}; + +// Little Endian storage. First short is least significant. +#define IndexerCount(Indexer) (Indexer & 0xFFFF) +#define IndexerOffset(Indexer) ((Indexer & 0xFFFF0000) >> 16) + +#define PI 3.1415926535 + +vec3 Uncharted2Tonemap(vec3 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/04_scenes/shader/ibl_common.hlsli b/samples/04_scenes/shader/ibl_common.hlsli new file mode 100644 index 0000000..bec0adc --- /dev/null +++ b/samples/04_scenes/shader/ibl_common.hlsli @@ -0,0 +1,77 @@ +#include "bindless_structs.hlsli" + +/* + The Goal is simply to convert from a (0,0,0) to (CubeSide, CubeSide, FaceCount) Invocation ID space to a + (-1,-1,-1) to (1, 1, 1) space. + +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| -z | 4 | -y | +| +z | 5 | -y | +*/ +float3 GetCubeDir(uint3 GlobalInvocationID, float SideLength) +{ + float2 FaceUV = float2(GlobalInvocationID.xy) / SideLength; // (0, SideLength) -> (0, 1) + FaceUV = 2.0f * FaceUV - 1.0f; // (0, 1) -> (-1, 1) + + switch (GlobalInvocationID.z) + { + case 0: + return normalize(float3(1.0f, -FaceUV.y, -FaceUV.x)); // Face +X; x = 1, y = -v, z = -u + case 1: + return normalize(float3(-1.0f, -FaceUV.y, FaceUV.x)); // Face -X; x = -1, y = -v, z = u + case 2: + return normalize(float3(FaceUV.x, 1.0f, FaceUV.y)); // Face +Y; x = u, y = 1, z = v + case 3: + return normalize(float3(FaceUV.x, -1.0f, -FaceUV.y)); // Face -Y; x=u, y=-1, z=-v + case 4: + return normalize(float3(FaceUV.x, -FaceUV.y, 1.0f)); // Face +Z; x=u,y=-v, z=1 + case 5: + return normalize(float3(-FaceUV.x, -FaceUV.y, -1.0f)); // Face -Z; x=u,y=-v, z=-1 + default: + // Never reach here. + return 0.0f.xxx; + } +} + +float RadicalInverse_VdC(uint Bits) +{ + Bits = (Bits << 16u) | (Bits >> 16u); + Bits = ((Bits & 0x55555555u) << 1u) | ((Bits & 0xAAAAAAAAu) >> 1u); + Bits = ((Bits & 0x33333333u) << 2u) | ((Bits & 0xCCCCCCCCu) >> 2u); + Bits = ((Bits & 0x0F0F0F0Fu) << 4u) | ((Bits & 0xF0F0F0F0u) >> 4u); + Bits = ((Bits & 0x00FF00FFu) << 8u) | ((Bits & 0xFF00FF00u) >> 8u); + return float(Bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +float2 Hammersley(uint SampleIndex, uint SampleCount) +{ + return float2(float(SampleIndex) / float(SampleCount), RadicalInverse_VdC(SampleIndex)); +} + +float3 ImportanceSampleGGX(float2 Xi, float3 Normal, float Roughness) +{ + float A = Roughness * Roughness; + + float Phi = 2.0f * PI * Xi.x; + float CosTheta = sqrt((1.0f - Xi.y) / (1.0f + (A * A - 1.0f) * Xi.y)); + float SinTheta = sqrt(1.0f - CosTheta * CosTheta); + + // from spherical coordinates to cartesian coordinates + float3 H; + H.x = cos(Phi) * SinTheta; + H.y = sin(Phi) * SinTheta; + H.z = CosTheta; + + // from tangent-space vector to world-space sample vector + float3 Up = abs(Normal.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f); + float3 Tangent = normalize(cross(Up, Normal)); + float3 Bitangent = cross(Normal, Tangent); + + float3 SampleVec = Tangent * H.x + Bitangent * H.y + Normal * H.z; + return normalize(SampleVec); +} \ No newline at end of file diff --git a/samples/04_scenes/shader/model.frag.glsl b/samples/04_scenes/shader/model.frag.glsl index 3bec0da..5df242e 100644 --- a/samples/04_scenes/shader/model.frag.glsl +++ b/samples/04_scenes/shader/model.frag.glsl @@ -2,17 +2,299 @@ #pragma shader_stage(fragment) #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable #extension GL_EXT_buffer_reference : require +#extension GL_EXT_nonuniform_qualifier : enable + +#include "bindless_structs.glsl" +#include "graphics_bindings.glsl" + +layout (location = 0) in vec4 in_Position; +layout (location = 1) in vec4 in_Normal; +layout (location = 2) in vec4 in_Color0; +layout (location = 3) in vec2 in_TexCoord0; -layout (location = 2) in vec4 inColor; layout (location = 0) out vec4 outColor; -layout(push_constant) uniform Constants { +layout(push_constant) uniform Constants +{ mat4 globalTransform; uint64_t vertexPos; uint64_t vertexDat; - uint64_t materialIdx; -} pcb; + MaterialRef g_Material; +}; + +vec4 GetAlbedo(vec4 albedoFactor, uint texHandle, vec4 in_Color0, vec2 uv) +{ + return albedoFactor * in_Color0 * ((texHandle != INVALID_HANDLE) ? texture(textures[texHandle], uv) : vec4(1.0f)); +} + +float GetOcclusion(uint texHandle, vec2 uv) +{ + return texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).r : 1.0f; +} + +vec3 GetEmissive(vec3 emissionFactor, uint texHandle, vec2 uv) +{ + return emissionFactor * (texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).rgb : vec3(1.0f)); +} + +vec3 GetNormal(uint texHandle, vec3 position, vec3 normal, vec2 uv) +{ + vec3 N = normalize(normal); + + if (texHandle == INVALID_HANDLE) + { + return N; + } + + vec3 tanSpaceNormal = texture(textures[texHandle], uv).xyz * 2.0f - 1.0f; + + vec3 q1 = dFdx(position); + vec3 q2 = dFdy(position); + vec2 st1 = dFdx(uv); + vec2 st2 = dFdy(uv); + + vec3 T = normalize(q1 * st2.y - q2 * st1.y).xyz; + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); // Construction is Col by Col + + return normalize(TBN * tanSpaceNormal); +} + +vec2 GetMetalRough(float metalFactor, float roughFactor, uint texHandle, vec2 uv) +{ + return vec2(metalFactor, roughFactor) * (texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).bg : vec2(1.0f)); // Metal is B, Rough is G. +} + +vec3 SampleIrradiance(vec3 direction) +{ + uint texHandle = lights.m_DiffuseIrradianceHandle; + return ((texHandle != INVALID_HANDLE) ? texture(textureCubes[texHandle], direction).rgb : vec3(0.04f)); +} + +vec3 SamplePrefiltered(vec3 direction, float roughness) +{ + const float MAX_MIP_LEVEL = 5.0f; + float mipLevel = MAX_MIP_LEVEL * roughness; + + uint texHandle = lights.m_PrefilterHandle; + + return (texHandle != INVALID_HANDLE) ? textureLod(textureCubes[texHandle], direction, mipLevel).rgb : vec3(0.0f); +} + +vec2 SampleBrdfLut(float ndotv, float roughness) +{ + return lights.m_BrdfLutHandle != INVALID_HANDLE ? texture(textures[lights.m_BrdfLutHandle], vec2(ndotv, roughness)).rg : 0.0f.xx; +} + + +float TrowbridgeReitzGGX(vec3 normal, vec3 halfway, float roughness) +{ + float coeff = roughness * roughness; + float coeff2 = coeff * coeff; + float ndoth = max(dot(normal, halfway), 0.0f); + float ndoth2 = ndoth * ndoth; + + float numerator = coeff2; + float denominator = ndoth2 * (coeff2 - 1.0f) + 1.0f; + denominator = PI * denominator * denominator; + + return numerator / denominator; +} + +float GeometrySchlickGGX(float ndotv, float roughness) +{ + float r = roughness + 1.0f; + float k = (r * r) / 8.0f; + + float numerator = ndotv; + float denominator = ndotv * (1.0f - k) + k; + + return numerator / denominator; +} + +float GeometrySmith(float ndotv, float ndotl, float roughness) +{ + float ggx1 = GeometrySchlickGGX(ndotv, roughness); + float ggx2 = GeometrySchlickGGX(ndotl, roughness); + + return ggx1 * ggx2; +} + +// https://en.wikipedia.org/wiki/Schlick%27s_approximation +// TODO: Possibly needs fixing. unreal vs LearnOpenGL impl. +vec3 FresnelSchlick(float cosine, vec3 f0) +{ + return f0 + (1.0f - f0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. +} + +// Sebastian Lagarde +vec3 FresnelSchlickRoughness(float cosine, vec3 f0, float roughness) +{ + return f0 + (max((1.0f - roughness).xxx, f0) - f0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. +} + +vec3 GetPBRContrib(vec3 albedo, vec3 lightColor, vec3 viewDir, vec3 normal, float metallic, float roughness, vec3 f0, vec3 lightDir, float lightDistance) +{ + float attenuation = 1.0f / (lightDistance * lightDistance); // TODO: Controlled Attenuation + vec3 halfway = normalize(viewDir + lightDir); + + float cosineFactor = max(dot(halfway, viewDir), 0.0f); + float ndotv = max(dot(normal, viewDir), 0.0f); + float ndotl = max(dot(normal, lightDir), 0.0f); + + vec3 radiance = lightColor * attenuation; + + float normalDistribution = TrowbridgeReitzGGX(normal, halfway, roughness); + float geometry = GeometrySmith(ndotv, ndotl, roughness); + vec3 fresnel = FresnelSchlickRoughness(cosineFactor, f0, roughness); + + vec3 numerator = (normalDistribution * geometry) * fresnel; + float denominator = 4.0f * ndotv * ndotl; + vec3 specular = numerator / (denominator + 0.00001f); + + vec3 kSpecular = fresnel; + vec3 kDiffuse = 1.0f.xxx - kSpecular; + + kDiffuse *= 1.0f - metallic; + + return ndotl * radiance * (kDiffuse * albedo / PI + specular); +} +// +//vec3 GetPointLightInfluence(vec3 albedo, vec2 MetalRough, vec3 Position, vec3 Normal) +//{ +// if (lights.LightHandle == INVALID_HANDLE) +// return 0.0f.xxx; +// +// uint Offset = IndexerOffset(lights.PointLightIndexer); +// uint Count = IndexerCount(lights.PointLightIndexer); +// +// vec3 ViewDir = normalize(Camera.Position.xyz - Position); +// +// float Metallic = MetalRough.r; +// float Roughness = MetalRough.g; +// +// // Dielectric F_0 based on LearnOpenGL. +// // TODO: Cite +// vec3 F_0 = 0.04f.xxx; +// F_0 = lerp(F_0, albedo, Metallic); +// +// vec3 Contrib = 0.0f; +// for (uint i = 0; i < Count; ++i) +// { +// PointLight Light = PointLightBuffer[lights.LightHandle][i + Offset]; +// +// if (Light.Range < 0.0f) +// continue; +// +// vec3 LightDir = vec3(Light.Position) - Position; +// float LightDistance = length(LightDir); +// +// if (LightDistance > Light.Range) +// continue; +// +// LightDir /= LightDistance; // Normalization +// +// // Color Unpack +// float R = (Light.Color & 0xFF000000) >> 24; +// float G = (Light.Color & 0x00FF0000) >> 16; +// float B = (Light.Color & 0x0000FF00) >> 8; +// +// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 +// +// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance); +// } +// +// return Contrib; +//} +// +//vec3 GetDirectionalLightInfluence(vec3 albedo, float2 MetalRough, vec3 Position, vec3 Normal) +//{ +// if (lights.LightHandle == INVALID_HANDLE) +// return 0.0f.xxx; +// +// uint Count = IndexerCount(lights.DirectionalLightIndexer); +// +// vec3 ViewDir = normalize(Camera.Position.xyz - Position); +// +// float Metallic = MetalRough.r; +// float Roughness = MetalRough.g; +// +// // Dielectric F_0 based on LearnOpenGL. +// // TODO: Cite +// vec3 F_0 = 0.04f.xxx; +// F_0 = lerp(F_0, albedo, Metallic); +// +// vec3 Contrib = 0.0f; +// for (uint i = 0; i < Count; ++i) +// { +// DirectionalLight Light = DirectionalLightBuffer[lights.LightHandle][i]; +// +// if (Light.Validity_ < 0.0f) +// continue; +// +// vec3 LightDir = -normalize(vec3(Light.Direction)); +// +// // Color Unpack +// float R = (Light.Color & 0xFF000000) >> 24; +// float G = (Light.Color & 0x00FF0000) >> 16; +// float B = (Light.Color & 0x0000FF00) >> 8; +// +// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 +// +// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f); +// } +// +// return Contrib; +//} +// +vec3 GetAmbientInfluence(vec3 albedo, float metal, float roughness, vec3 Position, vec3 Normal, float Occlusion) +{ + vec3 ViewDir = normalize(camera.m_Position.xyz - Position); + float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient. + + vec3 F_0 = 0.04f.xxx; + F_0 = mix(F_0, albedo, metal); + vec3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, roughness); + vec3 K_Diffuse = 1.0f.xxx - K_Specular; + + K_Diffuse *= 1.0f - metal; // Metals don't have diffuse/refractions. + + vec3 ReflectionDir = reflect(-ViewDir, Normal); + + float NdotV = max(dot(Normal, ViewDir), 0.0f); + vec3 PrefilteredColor = SamplePrefiltered(ReflectionDir, roughness).rgb; + vec2 EnvBRDF = SampleBrdfLut(NdotV, roughness); + vec3 Specular = PrefilteredColor * (K_Specular * EnvBRDF.x + EnvBRDF.y); + + vec3 DiffuseIrradiance = albedo * SampleIrradiance(Normal); +//#ifdef _DEBUG +// if ((PushConstant.DebugFlags & USE_DIFFUSE_BIT) == 0) { +// DiffuseIrradiance = 0.0f.xxx; +// } +// if ((PushConstant.DebugFlags & USE_SPECULAR_BIT) == 0) { +// Specular = 0.0f.xxx; +// } +//#endif + + return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion; +} void main() { - outColor = vec4(inColor.rgb, 1.0f); + + vec4 albedoA = GetAlbedo(g_Material.m_AlbedoFactor, g_Material.m_AlbedoTex, in_Color0, in_TexCoord0); + vec3 albedo = albedoA.rgb; + float alpha = albedoA.a; + + vec3 normal = GetNormal(g_Material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0); + + vec3 emission = GetEmissive(g_Material.m_EmissionFactor, g_Material.m_EmissionTex, in_TexCoord0); + + vec2 metalRough = GetMetalRough(g_Material.m_MetalFactor, g_Material.m_RoughFactor, g_Material.m_MetalRoughTex, in_TexCoord0); + float occlusion = GetOcclusion(g_Material.m_OcclusionTex, in_TexCoord0); + + vec3 ambientLumin = GetAmbientInfluence(albedo, metalRough.r, metalRough.g, in_Position.xyz, normal, occlusion); + + outColor = vec4(Uncharted2Tonemap(ambientLumin + emission), alpha); + +// outColor = vec4(0.5f + 0.5f * normal, 1.0f); } \ No newline at end of file diff --git a/samples/04_scenes/shader/model.vert.glsl b/samples/04_scenes/shader/model.vert.glsl index 50083d8..5283fe2 100644 --- a/samples/04_scenes/shader/model.vert.glsl +++ b/samples/04_scenes/shader/model.vert.glsl @@ -3,40 +3,20 @@ #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable #extension GL_EXT_buffer_reference : require +#include "bindless_structs.glsl" +#include "graphics_bindings.glsl" + layout(location=0) out vec4 outWorldNormal; layout(location=1) out vec4 outWorldPosition; layout(location=2) out vec4 outColor; layout(location=3) out vec2 outUV0; -struct VertexData { - vec4 Normal; - vec2 TexCoord0; - vec2 TexCoord1; - vec4 Color; -}; - -layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef { - vec4 Positions[]; -}; - -layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VDataRef { - VertexData Data[]; -}; - -layout(set=1, binding=0) uniform Camera { - mat4 View; // 64 - mat4 Projection; // 128 - mat4 InvView; // 192 - mat4 InvProjection; // 256 - vec4 Position; // 272 -} camera; - layout(push_constant) uniform Constants { mat4 globalTransform; - VPositionRef vertexPos; - VDataRef vertexDat; + VPositionRef pVertexPosition; + VDataRef pVertexData; uint64_t materialIdx; -} pcb; +}; void main() { vec3 colors[] = { @@ -45,12 +25,10 @@ void main() { vec3( 0.0f, 0.0f, 1.0f ), }; - gl_Position = camera.Projection * camera.View * pcb.globalTransform * vec4(pcb.vertexPos.Positions[gl_VertexIndex].xyz, 1.0f); - outColor = vec4(pcb.vertexDat.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]); - - // TODO - // layout(location=0) out vec4 outWorldNormal; - // layout(location=1) out vec4 outWorldPosition; - // layout(location=2) out vec4 outColor; - // layout(location=3) out vec2 outUV0; + outWorldNormal = vec4(normalize(transpose(inverse(mat3(globalTransform))) * pVertexData.Data[gl_VertexIndex].Normal.xyz), 0.0f); + + outWorldPosition = globalTransform * vec4(pVertexPosition.Positions[gl_VertexIndex].xyz, 1.0f); + gl_Position = camera.m_Projection * camera.m_View * outWorldPosition; + outColor = vec4(pVertexData.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]); + outUV0 = pVertexData.Data[gl_VertexIndex].TexCoord0; } \ No newline at end of file diff --git a/samples/04_scenes/shader/model.vs.hlsl b/samples/04_scenes/shader/model.vs.hlsl deleted file mode 100644 index 0ac6b79..0000000 --- a/samples/04_scenes/shader/model.vs.hlsl +++ /dev/null @@ -1,62 +0,0 @@ - -struct VS_Input -{ - uint VertexIndex : SV_VertexID; -}; - -struct VS_Out { - float4 WorldNormal : NORMAL; - float4 WorldPosition : POSITION; - float4 Color : COLOR0; - float2 TexCoord0 : TEXCOORD0; -}; - -struct CameraData -{ - float4x4 View; // 64 - float4x4 Projection; // 128 - float4x4 InvView; // 192 - float4x4 InvProjection; // 256 - float4 Position; // 272 -}; - -[[vk::binding(0, 1)]] ConstantBuffer Camera; - -struct VertexData { - float4 Normal; - float2 TexCoord0; - float2 TexCoord1; - float4 Color; -}; - -[[vk::binding(0, 0)]] ByteAddressBuffer GeometryBuffer[]; - -layout(set=1, binding=0) uniform Camera { - float4x4 View; // 64 - float4x4 Projection; // 128 - float4x4 InvView; // 192 - float4x4 InvProjection; // 256 - float4 Position; // 272 -} Camera; - -layout(push_constant) uniform Constants { - mat4 globalTransform; - uint64 vertexPos; - VDataRef vertexDat; - uint64_t materialIdx; -} pcb; - -void main() { - VS_Output Output; - - float4 GlobalPosition = mul(globalTransform, GeometryBuffer[0].Load()); - float4 ClipSpace = mul(Camera.View, GlobalPosition); - - Output.VertexPosition = mul(Camera.Projection, ClipSpace); - Output.WorldPosition = GlobalPosition; - Output.UV0 = GetUV(StageInput.VertexIndex); - Output.VertexColor = GetColor(StageInput.VertexIndex); - - Output.WorldNormal = mul(GetNormalTransform(PushConstant.NodeIdx), GetNormal(StageInput.VertexIndex)); - return Output; -} \ No newline at end of file diff --git a/samples/04_scenes/shader/prefilter.cs.hlsl b/samples/04_scenes/shader/prefilter.cs.hlsl new file mode 100644 index 0000000..738d019 --- /dev/null +++ b/samples/04_scenes/shader/prefilter.cs.hlsl @@ -0,0 +1,68 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint SkyboxHandle; + uint OutputTextureHandle; + int CubeSide; + float Roughness; + uint EnvRes; +}; + +[[vk::push_constant]] +Block Pcb; + +float TrowbridgeReitzGGX(float NdotH, float Roughness) +{ + float Coeff = Roughness * Roughness; + float Alpha2 = Coeff * Coeff; + float NdotH2 = NdotH * NdotH; + + float Numerator = Alpha2; + float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f; + Denominator = PI * Denominator * Denominator; + + return Numerator / Denominator; +} + +float GetSampleMipLevel(float NdotH, float HdotV, float SampleCount) +{ + float D = TrowbridgeReitzGGX(NdotH, Pcb.Roughness); + float Pdf = (D * NdotH / (4.0f * HdotV)) + 0.0001f; + + float SurfAreaTexel = 4.0f * PI / (6.0f * Pcb.EnvRes * Pcb.EnvRes); + float SurfAreaSample = 1.0f / (SampleCount * Pdf + 0.0001f); + + return Pcb.Roughness == 0.0f ? 0.0f : 0.5f * log2(SurfAreaSample / SurfAreaTexel); +} + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float3 Normal = GetCubeDir(GlobalInvocationID, Pcb.CubeSide); + float3 ViewDir = Normal; + + const uint SAMPLE_COUNT = 2048u; + float TotalWeight = 0.0f; + float3 PrefilterColor = 0.0f.xxx; + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float2 Xi = Hammersley(i, SAMPLE_COUNT); + float3 Halfway = ImportanceSampleGGX(Xi, Normal, Pcb.Roughness); + float3 LightDir = normalize(2.0 * dot(ViewDir, Halfway) * Halfway - ViewDir); + + float NdotH = max(dot(Normal, Halfway), 0.0f); + + float MipLevel = GetSampleMipLevel(NdotH, NdotH /* N = V :: NdotH = HdotV */, SAMPLE_COUNT); + + float NdotL = max(dot(Normal, LightDir), 0.0); + if (NdotL > 0.0) + { + PrefilterColor += TextureCubes[Pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[Pcb.SkyboxHandle], LightDir, MipLevel).rgb * NdotL; + TotalWeight += NdotL; + } + } + PrefilterColor = PrefilterColor / TotalWeight; + + StorageTextureArrays[Pcb.OutputTextureHandle][GlobalInvocationID] = float4(PrefilterColor, 1.0f); +} \ No newline at end of file -- 2.43.1 From 98972bfc5966877c1d10d6d91064f111be57e558 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Tue, 10 Sep 2024 15:28:15 +0200 Subject: [PATCH 4/6] Multi-Draw Indirect complete. --- aster/buffer.cpp | 25 +++ aster/buffer.h | 8 + samples/03_model_render/model_render.cpp | 2 + samples/04_scenes/asset_loader.cpp | 1 + samples/04_scenes/asset_loader.h | 2 + samples/04_scenes/ecs_adapter.h | 3 + samples/04_scenes/light_manager.cpp | 10 +- samples/04_scenes/light_manager.h | 3 +- samples/04_scenes/main.cpp | 122 ++++++---- samples/04_scenes/render_resource_manager.cpp | 6 +- .../04_scenes/shader/bindless_structs.glsl | 50 ++++- samples/04_scenes/shader/model.frag.glsl | 212 +++++++++--------- samples/04_scenes/shader/model.vert.glsl | 34 ++- 13 files changed, 298 insertions(+), 180 deletions(-) diff --git a/aster/buffer.cpp b/aster/buffer.cpp index 2085cc5..d24b918 100644 --- a/aster/buffer.cpp +++ b/aster/buffer.cpp @@ -54,6 +54,13 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs device->SetName(m_Buffer, name); } +uptr +Buffer::GetDeviceAddress(const Device *device) +{ + vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer}; + return device->m_Device.getBufferAddress(&addressInfo); +} + void Buffer::Write(const Device *device, usize offset, usize size, const void *data) { @@ -143,6 +150,24 @@ StorageIndexBuffer::Init(const Device *device, usize size, bool hostVisible, boo } } +void +IndirectBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name) +{ + vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress; + if (hostVisible) + { + Allocate(device, size, usage, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT, + VMA_MEMORY_USAGE_AUTO, name); + } + else + { + usage |= vk::BufferUsageFlagBits::eTransferDst; + Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name); + } +} + void VertexBuffer::Init(const Device *device, usize size, cstr name) { diff --git a/aster/buffer.h b/aster/buffer.h index cee1edb..45075d3 100644 --- a/aster/buffer.h +++ b/aster/buffer.h @@ -31,6 +31,9 @@ struct Buffer void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage, VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name); + uptr + GetDeviceAddress(const Device *device); + // Buffer.size is used for bookkeeping // If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`. usize m_Size_ = 0; @@ -54,6 +57,11 @@ struct StorageBuffer : Buffer void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr); }; +struct IndirectBuffer : Buffer +{ + void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr); +}; + struct StorageIndexBuffer : StorageBuffer { void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr); diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 2296562..60830d8 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -157,6 +157,8 @@ main(int, char **) .descriptorBindingStorageBufferUpdateAfterBind = true, .descriptorBindingPartiallyBound = true, .runtimeDescriptorArray = true, + .bufferDeviceAddress = true, + .bufferDeviceAddressCaptureReplay = true, }, .m_Vulkan13Features = { diff --git a/samples/04_scenes/asset_loader.cpp b/samples/04_scenes/asset_loader.cpp index 4290083..d877329 100644 --- a/samples/04_scenes/asset_loader.cpp +++ b/samples/04_scenes/asset_loader.cpp @@ -879,6 +879,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) Entity modelRootEntity = m_Registry->create(); m_Registry->emplace(modelRootEntity); m_Registry->emplace(modelRootEntity); + m_Registry->emplace(modelRootEntity); entities.push_back(modelRootEntity); assert(model.defaultScene >= 0); diff --git a/samples/04_scenes/asset_loader.h b/samples/04_scenes/asset_loader.h index dc5ce9d..bd7564d 100644 --- a/samples/04_scenes/asset_loader.h +++ b/samples/04_scenes/asset_loader.h @@ -61,6 +61,8 @@ struct Model void Destroy(RenderResourceManager *resourceManager, EcsRegistry *registry); }; +struct CModel {}; + struct AssetLoader { RenderResourceManager *m_ResourceManager; diff --git a/samples/04_scenes/ecs_adapter.h b/samples/04_scenes/ecs_adapter.h index 3cc09bf..ced8d8d 100644 --- a/samples/04_scenes/ecs_adapter.h +++ b/samples/04_scenes/ecs_adapter.h @@ -12,6 +12,9 @@ using Entity = entt::entity; template using Without = entt::exclude_t; +template +using Get = entt::get_t; + [[nodiscard]] inline bool Exists(Entity entity) { diff --git a/samples/04_scenes/light_manager.cpp b/samples/04_scenes/light_manager.cpp index bbf9b0b..c2ff6f5 100644 --- a/samples/04_scenes/light_manager.cpp +++ b/samples/04_scenes/light_manager.cpp @@ -289,15 +289,15 @@ LightManager::RemoveLight(const LightHandle handle) } void -LightManager::SetEnvironment(Environment &&environment) +LightManager::SetEnvironment(Environment *environment) { m_ResourceManager->Release(m_MetaInfo.m_Skybox); m_ResourceManager->Release(m_MetaInfo.m_Diffuse); m_ResourceManager->Release(m_MetaInfo.m_Prefilter); m_ResourceManager->Release(m_MetaInfo.m_BrdfLut); - m_MetaInfo.m_Skybox = environment.m_Skybox; - m_MetaInfo.m_Diffuse = environment.m_Diffuse; - m_MetaInfo.m_Prefilter = environment.m_Prefilter; - m_MetaInfo.m_BrdfLut = environment.m_BrdfLut; + m_MetaInfo.m_Skybox = environment->m_Skybox; + m_MetaInfo.m_Diffuse = environment->m_Diffuse; + m_MetaInfo.m_Prefilter = environment->m_Prefilter; + m_MetaInfo.m_BrdfLut = environment->m_BrdfLut; } \ No newline at end of file diff --git a/samples/04_scenes/light_manager.h b/samples/04_scenes/light_manager.h index 8e83940..5a52e86 100644 --- a/samples/04_scenes/light_manager.h +++ b/samples/04_scenes/light_manager.h @@ -55,6 +55,7 @@ struct LightManager u16 m_PointLightOffset; // 02 24 u16 m_DirectionalLightMaxCount; // 02 26 u16 m_UnusedPadding0 = 0; // 02 28 + u32 m_UnusedPadding1 = 0; // 04 32 }; RenderResourceManager *m_ResourceManager; @@ -79,7 +80,7 @@ struct LightManager void Update(); void RemoveLight(LightHandle handle); - void SetEnvironment(Environment &&environment); + void SetEnvironment(Environment *environment); explicit LightManager(RenderResourceManager *resourceManager); ~LightManager(); diff --git a/samples/04_scenes/main.cpp b/samples/04_scenes/main.cpp index 16bde5e..f7ff544 100644 --- a/samples/04_scenes/main.cpp +++ b/samples/04_scenes/main.cpp @@ -11,10 +11,10 @@ #include "swapchain.h" #include "window.h" -#include "light_manager.h" #include "asset_loader.h" #include "camera.h" #include "core_components.h" +#include "light_manager.h" #include "ecs_adapter.h" #include "frame.h" @@ -43,12 +43,10 @@ main(int, char *[]) PhysicalDevices physicalDevices = {&window, &context}; PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices); - usize physicalDeviceOffsetAlignment = deviceToUse.m_DeviceProperties.limits.minUniformBufferOffsetAlignment; - vk::Extent2D internalResolution = {1920, 1080}; internalResolution.width = (internalResolution.height * INIT_WIDTH) / INIT_HEIGHT; - CameraController cameraController = {vec3{0.0f, 0.0f, 2.0f}, vec3{0.0f}, 70_deg, + CameraController cameraController = {vec3{0.0f, 2.0f, 4.0f}, vec3{0.0f, 0.0f, 0.0f}, 70_deg, Cast(internalResolution.width) / Cast(internalResolution.height)}; INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); @@ -56,9 +54,14 @@ main(int, char *[]) Features enabledDeviceFeatures = { .m_Vulkan10Features = { + .multiDrawIndirect = true, .samplerAnisotropy = true, .shaderInt64 = true, }, + .m_Vulkan11Features = + { + .shaderDrawParameters = true, + }, .m_Vulkan12Features = { .descriptorIndexing = true, @@ -105,17 +108,25 @@ main(int, char *[]) resourceManager.Release(envHdriHandle); - lightManager.SetEnvironment(std::move(environment)); + lightManager.SetEnvironment(&environment); - Model model = assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"); - Model model2 = assetLoader.LoadModelToGpu(MODEL_FILE2, "Main Model 2"); - registry.get(model2.m_RootEntity).m_Position.x += 1.0f; + eastl::vector models; + for (int i = -1; i <= 1; ++i) + { + for (int j = -1; j <= 1; ++j) + { + INFO("{}, {}", i, j); + auto &model = models.emplace_back(std::move(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"))); + registry.get(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j); + } + } UniformBuffer ubo; - constexpr usize uboTotalSize = sizeof cameraController.m_Camera + sizeof lightManager.m_MetaInfo; + constexpr usize uboLightManagerOffset = sizeof cameraController.m_Camera; + constexpr usize uboTotalSize = uboLightManagerOffset + sizeof lightManager.m_MetaInfo; ubo.Init(&device, uboTotalSize, "Desc1 UBO"); ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); - ubo.Write(&device, sizeof cameraController.m_Camera, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); + ubo.Write(&device, uboLightManagerOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager); Pipeline backgroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager); @@ -287,19 +298,23 @@ main(int, char *[]) uptr m_VertexPositionPtr; uptr m_VertexDataPtr; uptr m_MaterialPtr; - - // TODO: Remove - u32 m_FirstIndex; - u32 m_IndexCount; + u8 m_Padding_[8]; }; - eastl::fixed_vector, MAX_FRAMES_IN_FLIGHT> perFrameNodeData(frameManager.m_FramesInFlight); - eastl::fixed_vector perFrameNodeBuffer(frameManager.m_FramesInFlight); - for (auto &bufferHandle : perFrameNodeBuffer) + eastl::vector nodeData; + eastl::vector nodeDrawInfo; + + eastl::fixed_vector nodeBuffers; + eastl::fixed_vector nodeIndirectBuffers; + eastl::fixed_vector perFrameNodeBufferPtr; + + for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i) { - StorageBuffer buffer; - buffer.Init(&device, sizeof(NodeData) * 100'000, true); - bufferHandle = resourceManager.Commit(&buffer); + auto *buffer = &nodeBuffers.push_back(); + buffer->Init(&device, sizeof(NodeData) * 100'000, true, true, "Node Buffer"); + auto *indirect = &nodeIndirectBuffers.push_back(); + indirect->Init(&device, sizeof(vk::DrawIndexedIndirectCommand) * 100'000, true); + perFrameNodeBufferPtr.push_back(buffer->GetDeviceAddress(&device)); } swapchain.RegisterResizeCallback( @@ -324,19 +339,28 @@ main(int, char *[]) auto rootNodeUpdateView = registry.view(Without>{}); + auto rootModel = registry.group(Get{}); + auto nodeWithParentsUpdateView = registry.view, CGlobalTransform>(); nodeWithParentsUpdateView.use>(); auto renderableObjectsGroup = registry.group(); + lightManager.Update(); + ubo.Write(&device, uboLightManagerOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo); + resourceManager.Update(); while (window.Poll()) { Time::Update(); - auto *rot = ®istry.get(model.m_RootEntity).m_Rotation; - *rot = glm::rotate(*rot, Cast(30_deg * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f}); + u32 index = 0; + for (auto [entity, dynTrans] : rootModel.each()) + { + dynTrans.m_Rotation = + glm::rotate(dynTrans.m_Rotation, Cast(30_deg * (++index) * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f}); + } Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window); @@ -392,23 +416,31 @@ main(int, char *[]) registry.get(parent.m_ParentEntity).m_Transform * translation * rotation * scale; } - usize objectCount = renderableObjectsGroup.size(); - auto *nodeData = &perFrameNodeData[currentFrame->m_FrameIdx]; - nodeData->clear(); - nodeData->reserve(objectCount); + u32 objectCount = Cast(renderableObjectsGroup.size()); + nodeData.clear(); + nodeDrawInfo.clear(); + nodeData.reserve(objectCount); + nodeDrawInfo.reserve(objectCount); for (auto [entity, globalTransform, mesh, material] : renderableObjectsGroup.each()) { - nodeData->push_back({ + nodeData.push_back({ .m_Transform = globalTransform.m_Transform, .m_VertexPositionPtr = mesh.m_VertexPositionPtr, .m_VertexDataPtr = mesh.m_VertexDataPtr, .m_MaterialPtr = material.m_MaterialPtr, - .m_FirstIndex = mesh.m_FirstIndex, - .m_IndexCount = mesh.m_IndexCount, + }); + nodeDrawInfo.push_back({ + .indexCount = mesh.m_IndexCount, + .instanceCount = 1, + .firstIndex = mesh.m_FirstIndex, + .vertexOffset = 0, + .firstInstance = 0, }); } - resourceManager.Write(perFrameNodeBuffer[currentFrame->m_FrameIdx], 0, objectCount * sizeof(NodeData), - nodeData->data()); + + nodeBuffers[currentFrame->m_FrameIdx].Write(&device, 0, objectCount * sizeof(NodeData), nodeData.data()); + nodeIndirectBuffers[currentFrame->m_FrameIdx].Write(&device, 0, objectCount * sizeof nodeDrawInfo[0], + nodeDrawInfo.data()); vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit}; AbortIfFailed(cmd.begin(&beginInfo)); @@ -458,14 +490,12 @@ main(int, char *[]) cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline); - // TODO("Get the data to the GPU"); - // auto nodeHandle = perFrameNodeBuffer[currentFrame->m_FrameIdx]; - auto &nodeBuffer = perFrameNodeData[currentFrame->m_FrameIdx]; - for (auto &node : nodeBuffer) - { - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof node, &node); - cmd.drawIndexed(node.m_IndexCount, 1, node.m_FirstIndex, 0, 0); - } + auto nodeBufferAddr = perFrameNodeBufferPtr[currentFrame->m_FrameIdx]; + + cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, 0, sizeof nodeBufferAddr, &nodeBufferAddr); + + cmd.drawIndexedIndirect(nodeIndirectBuffers[currentFrame->m_FrameIdx].m_Buffer, 0, objectCount, + sizeof nodeDrawInfo[0]); cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backgroundPipeline.m_Pipeline); @@ -527,9 +557,13 @@ main(int, char *[]) device.WaitIdle(); - for (auto bufferHandle : perFrameNodeBuffer) + for (auto buffer : nodeIndirectBuffers) { - resourceManager.Release(bufferHandle); + buffer.Destroy(&device); + } + for (auto buffer : nodeBuffers) + { + buffer.Destroy(&device); } for (auto depthImage : depthImages) { @@ -543,6 +577,8 @@ main(int, char *[]) device.m_Device.destroy(descriptorPool, nullptr); - model.Destroy(&resourceManager, ®istry); - model2.Destroy(&resourceManager, ®istry); + for (auto &model : models) + { + model.Destroy(&resourceManager, ®istry); + } } \ No newline at end of file diff --git a/samples/04_scenes/render_resource_manager.cpp b/samples/04_scenes/render_resource_manager.cpp index aa3bc41..76f22a8 100644 --- a/samples/04_scenes/render_resource_manager.cpp +++ b/samples/04_scenes/render_resource_manager.cpp @@ -641,9 +641,9 @@ RenderResourceManager::RenderResourceManager(Device *device, u16 maxSize, bool u INFO("Max Texture Count: {}", texturesCount); INFO("Max Storage Texture Count: {}", storageTexturesCount); - m_Geometry.InitStorage(device, Megabyte(128u)); - m_Index.InitIndex(device, Megabyte(8u)); - m_Material.InitStorage(device, Kilobyte(560u)); + m_Geometry.InitStorage(device, Gigabyte(1u)); + m_Index.InitIndex(device, Megabyte(256u)); + m_Material.InitStorage(device, Gigabyte(1u)); m_BufferManager.Init(buffersCount); m_TextureManager.Init(texturesCount); m_StorageTextureManager.Init(storageTexturesCount); diff --git a/samples/04_scenes/shader/bindless_structs.glsl b/samples/04_scenes/shader/bindless_structs.glsl index ce0ee91..91e861e 100644 --- a/samples/04_scenes/shader/bindless_structs.glsl +++ b/samples/04_scenes/shader/bindless_structs.glsl @@ -20,13 +20,36 @@ struct Material // We might be able to go upto 64 without pains. }; +struct PointLight +{ + vec3 m_Position; + float m_Range; + uint m_Color; + float m_Intensity; +}; + +struct DirectionalLight +{ + vec3 m_Direction; + float m_Validity_; + uint m_Color; + float m_Intensity; +}; + // ---------- Bindless Bindings ---------- #define INVALID_HANDLE 0xFFFFFFFF +layout (std430, set = 0, binding = 0) readonly buffer PointLightBuffer { + PointLight lights[]; +} pointLightBuffers[]; + +layout (std430, set = 0, binding = 0) readonly buffer DirectionLightBuffer { + DirectionalLight lights[]; +} directionalLightBuffers[]; + layout (set = 0, binding = 1) uniform sampler2D textures[]; layout (set = 0, binding = 1) uniform samplerCube textureCubes[]; - // ---------- Buffer References ---------- layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef { vec4 Positions[]; @@ -41,13 +64,20 @@ layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer Mater }; layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer MaterialRef { - vec4 m_AlbedoFactor; // 16 16 - vec3 m_EmissionFactor; // 12 28 - float m_MetalFactor; // 04 32 - float m_RoughFactor; // 04 36 - uint m_AlbedoTex; // 04 40 - uint m_NormalTex; // 04 44 - uint m_MetalRoughTex; // 04 48 - uint m_OcclusionTex; // 04 52 - uint m_EmissionTex; // 04 56 + Material material; +}; + +// Node + +struct Node +{ + mat4 globalTransform; // 64 64 + VPositionRef pVertexPosition; // 08 72 + VDataRef pVertexData; // 08 80 + MaterialRef pMaterial; // 08 88 + // pad // 08 96 +}; + +layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer NodeRef { + Node nodes[]; }; \ No newline at end of file diff --git a/samples/04_scenes/shader/model.frag.glsl b/samples/04_scenes/shader/model.frag.glsl index 5df242e..8a12303 100644 --- a/samples/04_scenes/shader/model.frag.glsl +++ b/samples/04_scenes/shader/model.frag.glsl @@ -1,4 +1,4 @@ -#version 450 +#version 460 #pragma shader_stage(fragment) #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable #extension GL_EXT_buffer_reference : require @@ -11,15 +11,12 @@ layout (location = 0) in vec4 in_Position; layout (location = 1) in vec4 in_Normal; layout (location = 2) in vec4 in_Color0; layout (location = 3) in vec2 in_TexCoord0; +layout (location = 4) in flat uint in_DrawID; layout (location = 0) out vec4 outColor; -layout(push_constant) uniform Constants -{ - mat4 globalTransform; - uint64_t vertexPos; - uint64_t vertexDat; - MaterialRef g_Material; +layout (push_constant) uniform Const { + NodeRef nodes; }; vec4 GetAlbedo(vec4 albedoFactor, uint texHandle, vec4 in_Color0, vec2 uv) @@ -159,99 +156,99 @@ vec3 GetPBRContrib(vec3 albedo, vec3 lightColor, vec3 viewDir, vec3 normal, floa return ndotl * radiance * (kDiffuse * albedo / PI + specular); } -// -//vec3 GetPointLightInfluence(vec3 albedo, vec2 MetalRough, vec3 Position, vec3 Normal) -//{ -// if (lights.LightHandle == INVALID_HANDLE) -// return 0.0f.xxx; -// -// uint Offset = IndexerOffset(lights.PointLightIndexer); -// uint Count = IndexerCount(lights.PointLightIndexer); -// -// vec3 ViewDir = normalize(Camera.Position.xyz - Position); -// -// float Metallic = MetalRough.r; -// float Roughness = MetalRough.g; -// -// // Dielectric F_0 based on LearnOpenGL. -// // TODO: Cite -// vec3 F_0 = 0.04f.xxx; -// F_0 = lerp(F_0, albedo, Metallic); -// -// vec3 Contrib = 0.0f; -// for (uint i = 0; i < Count; ++i) -// { -// PointLight Light = PointLightBuffer[lights.LightHandle][i + Offset]; -// -// if (Light.Range < 0.0f) -// continue; -// -// vec3 LightDir = vec3(Light.Position) - Position; -// float LightDistance = length(LightDir); -// -// if (LightDistance > Light.Range) -// continue; -// -// LightDir /= LightDistance; // Normalization -// -// // Color Unpack -// float R = (Light.Color & 0xFF000000) >> 24; -// float G = (Light.Color & 0x00FF0000) >> 16; -// float B = (Light.Color & 0x0000FF00) >> 8; -// -// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 -// -// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance); -// } -// -// return Contrib; -//} -// -//vec3 GetDirectionalLightInfluence(vec3 albedo, float2 MetalRough, vec3 Position, vec3 Normal) -//{ -// if (lights.LightHandle == INVALID_HANDLE) -// return 0.0f.xxx; -// -// uint Count = IndexerCount(lights.DirectionalLightIndexer); -// -// vec3 ViewDir = normalize(Camera.Position.xyz - Position); -// -// float Metallic = MetalRough.r; -// float Roughness = MetalRough.g; -// -// // Dielectric F_0 based on LearnOpenGL. -// // TODO: Cite -// vec3 F_0 = 0.04f.xxx; -// F_0 = lerp(F_0, albedo, Metallic); -// -// vec3 Contrib = 0.0f; -// for (uint i = 0; i < Count; ++i) -// { -// DirectionalLight Light = DirectionalLightBuffer[lights.LightHandle][i]; -// -// if (Light.Validity_ < 0.0f) -// continue; -// -// vec3 LightDir = -normalize(vec3(Light.Direction)); -// -// // Color Unpack -// float R = (Light.Color & 0xFF000000) >> 24; -// float G = (Light.Color & 0x00FF0000) >> 16; -// float B = (Light.Color & 0x0000FF00) >> 8; -// -// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 -// -// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f); -// } -// -// return Contrib; -//} -// + +vec3 GetPointLightInfluence(vec3 albedo, vec2 metalRough, vec3 position, vec3 normal) +{ + if (lights.m_LightHandle == INVALID_HANDLE) + return 0.0f.xxx; + + uint offset = IndexerOffset(lights.m_PointLightIndexer); + uint count = IndexerCount(lights.m_PointLightIndexer); + + vec3 viewDir = normalize(camera.m_Position.xyz - position); + + float metallic = metalRough.r; + float roughness = metalRough.g; + + // Dielectric F_0 based on LearnOpenGL. + // TODO: Cite + vec3 f0 = vec3(0.04f); + f0 = mix(f0, albedo, metallic); + + vec3 contrib = vec3(0.0f); + for (uint i = 0; i < count; ++i) + { + PointLight light = pointLightBuffers[lights.m_LightHandle].lights[i + offset]; + + if (light.m_Range < 0.0f) + continue; + + vec3 lightDir = vec3(light.m_Position) - position; + float lightDistance = length(lightDir); + + if (lightDistance > light.m_Range) + continue; + + lightDir /= lightDistance; // Normalization + + // Color Unpack + float r = (light.m_Color & 0xFF000000) >> 24; + float g = (light.m_Color & 0x00FF0000) >> 16; + float b = (light.m_Color & 0x0000FF00) >> 8; + + vec3 lightColor = light.m_Intensity * vec3(r, g, b) * 0.00392156862f; // 0.00392156862 = 1/255 + + contrib += GetPBRContrib(albedo, lightColor, viewDir, normal, metallic, roughness, f0, lightDir, lightDistance); + } + + return contrib; +} + +vec3 GetDirectionalLightInfluence(vec3 albedo, vec2 metalRough, vec3 position, vec3 normal) +{ + if (lights.m_LightHandle == INVALID_HANDLE) + return 0.0f.xxx; + + uint count = IndexerCount(lights.m_DirectionalLightIndexer); + + vec3 viewDir = normalize(camera.m_Position.xyz - position); + + float metallic = metalRough.r; + float roughness = metalRough.g; + + // Dielectric F_0 based on LearnOpenGL. + // TODO: Cite + vec3 f0 = vec3(0.04f); + f0 = mix(f0, albedo, metallic); + + vec3 contrib = vec3(0.0f); + for (uint i = 0; i < count; ++i) + { + DirectionalLight light = directionalLightBuffers[lights.m_LightHandle].lights[i]; + + if (light.m_Validity_ < 0.0f) + continue; + + vec3 lightDir = -normalize(light.m_Direction); + + // Color Unpack + float r = (light.m_Color & 0xFF000000) >> 24; + float g = (light.m_Color & 0x00FF0000) >> 16; + float b = (light.m_Color & 0x0000FF00) >> 8; + + vec3 lightColor = light.m_Intensity * vec3(r, g, b) * 0.00392156862f; // 0.00392156862 = 1/255 + + contrib += GetPBRContrib(albedo, lightColor, viewDir, normal, metallic, roughness, f0, lightDir, 1.0f); + } + + return contrib; +} + vec3 GetAmbientInfluence(vec3 albedo, float metal, float roughness, vec3 Position, vec3 Normal, float Occlusion) { vec3 ViewDir = normalize(camera.m_Position.xyz - Position); float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient. - + vec3 F_0 = 0.04f.xxx; F_0 = mix(F_0, albedo, metal); vec3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, roughness); @@ -280,21 +277,24 @@ vec3 GetAmbientInfluence(vec3 albedo, float metal, float roughness, vec3 Positio } void main() { + + Material material = nodes.nodes[nonuniformEXT(in_DrawID)].pMaterial.material; - vec4 albedoA = GetAlbedo(g_Material.m_AlbedoFactor, g_Material.m_AlbedoTex, in_Color0, in_TexCoord0); + vec4 albedoA = GetAlbedo(material.m_AlbedoFactor, material.m_AlbedoTex, in_Color0, in_TexCoord0); vec3 albedo = albedoA.rgb; float alpha = albedoA.a; - vec3 normal = GetNormal(g_Material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0); + vec3 normal = GetNormal(material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0); - vec3 emission = GetEmissive(g_Material.m_EmissionFactor, g_Material.m_EmissionTex, in_TexCoord0); + vec3 emission = GetEmissive(material.m_EmissionFactor, material.m_EmissionTex, in_TexCoord0); - vec2 metalRough = GetMetalRough(g_Material.m_MetalFactor, g_Material.m_RoughFactor, g_Material.m_MetalRoughTex, in_TexCoord0); - float occlusion = GetOcclusion(g_Material.m_OcclusionTex, in_TexCoord0); + vec2 metalRough = GetMetalRough(material.m_MetalFactor, material.m_RoughFactor, material.m_MetalRoughTex, in_TexCoord0); + float occlusion = GetOcclusion(material.m_OcclusionTex, in_TexCoord0); + vec3 pointLumin = GetPointLightInfluence(albedo, metalRough, in_Position.xyz, normal); + vec3 directionalLumin = GetDirectionalLightInfluence(albedo, metalRough, in_Position.xyz, normal); vec3 ambientLumin = GetAmbientInfluence(albedo, metalRough.r, metalRough.g, in_Position.xyz, normal, occlusion); - - outColor = vec4(Uncharted2Tonemap(ambientLumin + emission), alpha); - -// outColor = vec4(0.5f + 0.5f * normal, 1.0f); -} \ No newline at end of file + + vec3 viewDir = normalize(camera.m_Position.xyz - in_Position.xyz); + outColor = vec4(Uncharted2Tonemap(pointLumin + directionalLumin + ambientLumin + emission), alpha); +} diff --git a/samples/04_scenes/shader/model.vert.glsl b/samples/04_scenes/shader/model.vert.glsl index 5283fe2..53cbe10 100644 --- a/samples/04_scenes/shader/model.vert.glsl +++ b/samples/04_scenes/shader/model.vert.glsl @@ -1,21 +1,27 @@ -#version 450 +#version 460 #pragma shader_stage(vertex) #extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable #extension GL_EXT_buffer_reference : require +#extension GL_EXT_nonuniform_qualifier : require #include "bindless_structs.glsl" #include "graphics_bindings.glsl" -layout(location=0) out vec4 outWorldNormal; -layout(location=1) out vec4 outWorldPosition; +layout(location=0) out vec4 outWorldPosition; +layout(location=1) out vec4 outWorldNormal; layout(location=2) out vec4 outColor; layout(location=3) out vec2 outUV0; +layout(location=4) out uint drawID; +// +//layout(push_constant) uniform Constants { +// mat4 globalTransform; +// VPositionRef pVertexPosition; +// VDataRef pVertexData; +// uint64_t materialIdx; +//}; -layout(push_constant) uniform Constants { - mat4 globalTransform; - VPositionRef pVertexPosition; - VDataRef pVertexData; - uint64_t materialIdx; +layout (push_constant) uniform Const { + NodeRef nodes; }; void main() { @@ -25,10 +31,14 @@ void main() { vec3( 0.0f, 0.0f, 1.0f ), }; - outWorldNormal = vec4(normalize(transpose(inverse(mat3(globalTransform))) * pVertexData.Data[gl_VertexIndex].Normal.xyz), 0.0f); + drawID = gl_DrawID; + Node n = nodes.nodes[gl_DrawID]; - outWorldPosition = globalTransform * vec4(pVertexPosition.Positions[gl_VertexIndex].xyz, 1.0f); + outWorldNormal = normalize(n.globalTransform * vec4(n.pVertexData.Data[gl_VertexIndex].Normal.xyz, 0.0f)); + + outWorldPosition = n.globalTransform * vec4(n.pVertexPosition.Positions[gl_VertexIndex].xyz, 1.0f); gl_Position = camera.m_Projection * camera.m_View * outWorldPosition; - outColor = vec4(pVertexData.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]); - outUV0 = pVertexData.Data[gl_VertexIndex].TexCoord0; + outColor = vec4(n.pVertexData.Data[gl_VertexIndex].Color.rgb, 1.0f); +// outColor = vec4(colors[gl_VertexIndex % 3], 1.0f); + outUV0 = n.pVertexData.Data[gl_VertexIndex].TexCoord0; } \ No newline at end of file -- 2.43.1 From 81541ec8427544759f0ad326a7fc7a088f4b05c6 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sun, 24 Nov 2024 18:50:57 +0100 Subject: [PATCH 5/6] MInor updates and TODOs in README. --- README.md | 42 +++++++++++++++++++++++++++----------- samples/04_scenes/main.cpp | 9 +++++++- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 50b1df7..95d428d 100644 --- a/README.md +++ b/README.md @@ -8,30 +8,35 @@ A Vulkan based renderer created with Vulkan 1.3 in C++. - [X] Load Vertex Data - [X] Load Material Data - [ ] Load Animation Data - - [ ] Load Camera - [ ] Support Specular Materials - [X] Bindless Descriptors - [X] Simplified Descriptor Creation Pipeline +- [X] Lighting Equations + - [X] Blinn-Phong + - [X] PBR / IBL - [ ] Debugging - [ ] Tracy Integration - [X] Dear ImGui Integration - [ ] Transparency - [ ] Sorted - - [ ] Order Independent -- [ ] Shadows v1 + - [ ] Order Independent (Depth Peeling) +- [ ] Shadows + - [ ] Shadow Mapping - [ ] Omnidirectional Cubemap Shadows - [ ] SpotLight Shadows - [ ] Directional Shadows - [ ] Cascaded Shadows - [ ] PCF -- [ ] Lighting / Shading Pipelines - - [ ] Blinn-Phong - - [ ] PBR - - [ ] IBL + - [ ] Omnidirectional Dual Paraboloid Shadows + - [ ] Shadow Masks + - [ ] Perspective Shadow Mapping + - [ ] RTX Shadows - [ ] Rendering Techniques - - [ ] Forward Rendering + - [X] Forward Rendering - [ ] Deferred Rendering - [ ] Clustered-Forward Rendering + - [ ] V-Buffer Rendering + - [ ] Light Prepass - [ ] Ambient Occlusion - [ ] SSAO - [ ] HBAO @@ -43,16 +48,29 @@ A Vulkan based renderer created with Vulkan 1.3 in C++. - [ ] Cubemap/Probe Reflection - [ ] Ray-Traced Reflection - [ ] Global Illumination + - [ ] Light Mapping + - [ ] Light Probes - [ ] Precomputed Radiance Transfer - [ ] Voxel Cone Tracing - [ ] SDFGI - [ ] RTXGI -- [ ] Shadows v2 - - [ ] Omnidirectional Dual Paraboloid Shadows - - [ ] Perspective Shadow Mapping - - [ ] RTX Shadows - [ ] Animation - [ ] Skeletal Animation - [ ] TBD - [ ] Particle Effects - [ ] Full Path Tracing +- [ ] Culling + - [ ] Frustrum Culling + - [ ] Depth based Occlusion Culling + - [ ] TBD +- [ ] Performance Techniques + - [ ] Instancing + - [ ] Batching + - [ ] Depth Prepass + - [ ] Level of Detail + - [ ] Shadow Atlases + - [X] Buffer sub-division + - [ ] Resource streaming +- [ ] Light Effects + - [ ] Volumetric Lighting + - [ ] Depth based Fog \ No newline at end of file diff --git a/samples/04_scenes/main.cpp b/samples/04_scenes/main.cpp index f7ff544..410fbd0 100644 --- a/samples/04_scenes/main.cpp +++ b/samples/04_scenes/main.cpp @@ -116,7 +116,7 @@ main(int, char *[]) for (int j = -1; j <= 1; ++j) { INFO("{}, {}", i, j); - auto &model = models.emplace_back(std::move(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"))); + auto &model = models.emplace_back(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model")); registry.get(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j); } } @@ -372,6 +372,7 @@ main(int, char *[]) DepthImage *currentDepthImage = &depthImages[currentFrame->m_FrameIdx]; AttachmentImage *currentAttachment = &attachmentImages[currentFrame->m_FrameIdx]; + // Resize outdated attachments if (currentAttachment->m_Extent.width != internalResolution.width || currentAttachment->m_Extent.height != internalResolution.height) { @@ -388,14 +389,17 @@ main(int, char *[]) vk::Image currentImage = currentAttachment->m_Image; vk::ImageView currentImageView = currentAttachment->m_View; + // Ready the barrier structs preRenderBarrier.image = currentImage; postRenderBarriers[0].image = currentImage; postRenderBarriers[1].image = currentSwapchainImage; transferDstToGuiRenderBarrier.image = currentSwapchainImage; prePresentBarrier.image = currentSwapchainImage; + // Write Camera ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera); + // Update all root dynamic object transforms. for (auto [entity, dynTransform, globalTransform] : rootNodeUpdateView.each()) { auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale); @@ -406,6 +410,7 @@ main(int, char *[]) } // Has been sorted and ordered by parent. + // Update all dynamic object transforms. for (auto [entity, dynTransform, parent, globalTransform] : nodeWithParentsUpdateView.each()) { auto scale = glm::scale(mat4{1.0f}, dynTransform.m_Scale); @@ -421,6 +426,8 @@ main(int, char *[]) nodeDrawInfo.clear(); nodeData.reserve(objectCount); nodeDrawInfo.reserve(objectCount); + + // Write all objects into the node data to be pushed. for (auto [entity, globalTransform, mesh, material] : renderableObjectsGroup.each()) { nodeData.push_back({ -- 2.43.1 From 14f4ac39beae03dac419a2379f0f7b03c1326456 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Sat, 30 Nov 2024 23:48:03 +0100 Subject: [PATCH 6/6] Added simple transparency. FIX: Possible problems based on rendering order. Ensure opaque then transparent, and back-to-front/front-to-back orders. --- samples/04_scenes/asset_loader.cpp | 15 ++++++++++- samples/04_scenes/asset_loader.h | 14 +++++++--- samples/04_scenes/main.cpp | 27 +++++++------------ samples/04_scenes/pipeline_utils.cpp | 6 ++--- .../04_scenes/shader/bindless_structs.glsl | 6 +++++ samples/04_scenes/shader/model.frag.glsl | 12 ++++++++- 6 files changed, 55 insertions(+), 25 deletions(-) diff --git a/samples/04_scenes/asset_loader.cpp b/samples/04_scenes/asset_loader.cpp index d877329..df1a889 100644 --- a/samples/04_scenes/asset_loader.cpp +++ b/samples/04_scenes/asset_loader.cpp @@ -540,7 +540,8 @@ AssetLoader::ProcessNode(tinygltf::Model *model, eastl::vector *vertexPosi perspective); dynamicTransform.m_Rotation = glm::conjugate(dynamicTransform.m_Rotation); } - else { + else + { dynamicTransform = { .m_Position = VectorToVec3(node->translation), .m_Rotation = VectorToQuat(node->rotation), @@ -861,6 +862,17 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) u32 index = Cast(materials.size()); auto *material = &model.materials[materialIdx]; + + f32 alphaBlendValue = -1; + if (material->alphaMode == "BLEND") + { + alphaBlendValue = 2; + } + else if (material->alphaMode == "MASK") + { + alphaBlendValue = material->alphaCutoff; + } + materials.push_back({ .m_AlbedoFactor = VectorToVec4(material->pbrMetallicRoughness.baseColorFactor), .m_EmissionFactor = VectorToVec3(material->emissiveFactor), @@ -871,6 +883,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name) .m_MetalRoughTex = getTextureHandle(material->pbrMetallicRoughness.metallicRoughnessTexture.index, false), .m_OcclusionTex = getTextureHandle(material->occlusionTexture.index, false), .m_EmissionTex = getTextureHandle(material->emissiveTexture.index, true), + .m_AlphaBlend = alphaBlendValue, }); materialsIndirection[materialIdx] = index; return index; diff --git a/samples/04_scenes/asset_loader.h b/samples/04_scenes/asset_loader.h index bd7564d..f842d48 100644 --- a/samples/04_scenes/asset_loader.h +++ b/samples/04_scenes/asset_loader.h @@ -35,9 +35,14 @@ struct Material TextureHandle m_MetalRoughTex; // 04 48 TextureHandle m_OcclusionTex; // 04 52 TextureHandle m_EmissionTex; // 04 56 + f32 m_AlphaBlend; /* 04 60 + AlphaBlend < 0 means Opaque + AlphaBlend > 1 means Blend + AlphaBlend in [0,1] means cutoff */ }; -static_assert(sizeof(Material) == 56); +// If this is changed, ensure the shaders are changed accordingly. +static_assert(sizeof(Material) == 60); struct VertexData { @@ -61,7 +66,9 @@ struct Model void Destroy(RenderResourceManager *resourceManager, EcsRegistry *registry); }; -struct CModel {}; +struct CModel +{ +}; struct AssetLoader { @@ -97,7 +104,8 @@ struct AssetLoader TextureHandle LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const; void ProcessNode(tinygltf::Model *model, eastl::vector *vertexPositions, eastl::vector *vertexData, - eastl::vector *indices, eastl::vector *entities, const std::function &loadMaterial, int current, Entity parent); + eastl::vector *indices, eastl::vector *entities, + const std::function &loadMaterial, int current, Entity parent); public: DISALLOW_COPY_AND_ASSIGN(AssetLoader); diff --git a/samples/04_scenes/main.cpp b/samples/04_scenes/main.cpp index 410fbd0..38cdc03 100644 --- a/samples/04_scenes/main.cpp +++ b/samples/04_scenes/main.cpp @@ -26,7 +26,7 @@ constexpr u32 MAX_FRAMES_IN_FLIGHT = 3; constexpr auto PIPELINE_CACHE_FILE = "PipelineCacheData.bin"; -constexpr auto MODEL_FILE = "model/DamagedHelmet.glb"; +constexpr auto MODEL_FILE = "model/AlphaBlendModeTest.glb"; constexpr auto MODEL_FILE2 = "model/Box.glb"; constexpr auto BACKDROP_FILE = "image/photo_studio_loft_hall_4k.hdr"; constexpr u32 INIT_WIDTH = 640; @@ -46,7 +46,7 @@ main(int, char *[]) vk::Extent2D internalResolution = {1920, 1080}; internalResolution.width = (internalResolution.height * INIT_WIDTH) / INIT_HEIGHT; - CameraController cameraController = {vec3{0.0f, 2.0f, 4.0f}, vec3{0.0f, 0.0f, 0.0f}, 70_deg, + CameraController cameraController = {vec3{0.0f, 1.0f, 4.0f}, vec3{0.0f, 0.0f, 0.0f}, 70_deg, Cast(internalResolution.width) / Cast(internalResolution.height)}; INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data()); @@ -111,15 +111,8 @@ main(int, char *[]) lightManager.SetEnvironment(&environment); eastl::vector models; - for (int i = -1; i <= 1; ++i) - { - for (int j = -1; j <= 1; ++j) - { - INFO("{}, {}", i, j); - auto &model = models.emplace_back(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model")); - registry.get(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j); - } - } + models.emplace_back(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model")); + //registry.get(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j); UniformBuffer ubo; constexpr usize uboLightManagerOffset = sizeof cameraController.m_Camera; @@ -355,12 +348,12 @@ main(int, char *[]) { Time::Update(); - u32 index = 0; - for (auto [entity, dynTrans] : rootModel.each()) - { - dynTrans.m_Rotation = - glm::rotate(dynTrans.m_Rotation, Cast(30_deg * (++index) * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f}); - } + //u32 index = 0; + //for (auto [entity, dynTrans] : rootModel.each()) + //{ + // dynTrans.m_Rotation = + // glm::rotate(dynTrans.m_Rotation, Cast(30_deg * (++index) * Time::m_Delta), vec3{0.0f, 1.0f, 0.0f}); + //} Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &window); diff --git a/samples/04_scenes/pipeline_utils.cpp b/samples/04_scenes/pipeline_utils.cpp index 5604abd..4ecd5aa 100644 --- a/samples/04_scenes/pipeline_utils.cpp +++ b/samples/04_scenes/pipeline_utils.cpp @@ -102,9 +102,9 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe .depthCompareOp = vk::CompareOp::eLess, }; vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = { - .blendEnable = false, - .srcColorBlendFactor = vk::BlendFactor::eSrcColor, - .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor, + .blendEnable = true, + .srcColorBlendFactor = vk::BlendFactor::eSrcAlpha, + .dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, .colorBlendOp = vk::BlendOp::eAdd, .srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha, .dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha, diff --git a/samples/04_scenes/shader/bindless_structs.glsl b/samples/04_scenes/shader/bindless_structs.glsl index 91e861e..3f9a823 100644 --- a/samples/04_scenes/shader/bindless_structs.glsl +++ b/samples/04_scenes/shader/bindless_structs.glsl @@ -17,6 +17,12 @@ struct Material uint m_MetalRoughTex; // 04 48 uint m_OcclusionTex; // 04 52 uint m_EmissionTex; // 04 56 + /* + AlphaBlend < 0 means Opaque + AlphaBlend > 1 means Blend + AlphaBlend in [0,1] means cutoff + */ + float AlphaBlend; // 04 60 // We might be able to go upto 64 without pains. }; diff --git a/samples/04_scenes/shader/model.frag.glsl b/samples/04_scenes/shader/model.frag.glsl index 8a12303..69ea493 100644 --- a/samples/04_scenes/shader/model.frag.glsl +++ b/samples/04_scenes/shader/model.frag.glsl @@ -276,13 +276,23 @@ vec3 GetAmbientInfluence(vec3 albedo, float metal, float roughness, vec3 Positio return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion; } +float processAlpha(float sourceAlpha, float alphaBlendValue) { + if (alphaBlendValue < 0) { + return 1.0; + } else if (alphaBlendValue > 1) { + return sourceAlpha; + } else { + return step(alphaBlendValue, sourceAlpha); + } +} + void main() { Material material = nodes.nodes[nonuniformEXT(in_DrawID)].pMaterial.material; vec4 albedoA = GetAlbedo(material.m_AlbedoFactor, material.m_AlbedoTex, in_Color0, in_TexCoord0); vec3 albedo = albedoA.rgb; - float alpha = albedoA.a; + float alpha = processAlpha(albedoA.a, material.AlphaBlend); vec3 normal = GetNormal(material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0); -- 2.43.1