render-by-index-meshid #8
42
README.md
42
README.md
|
|
@ -8,30 +8,35 @@ A Vulkan based renderer created with Vulkan 1.3 in C++.
|
||||||
- [X] Load Vertex Data
|
- [X] Load Vertex Data
|
||||||
- [X] Load Material Data
|
- [X] Load Material Data
|
||||||
- [ ] Load Animation Data
|
- [ ] Load Animation Data
|
||||||
- [ ] Load Camera
|
|
||||||
- [ ] Support Specular Materials
|
- [ ] Support Specular Materials
|
||||||
- [X] Bindless Descriptors
|
- [X] Bindless Descriptors
|
||||||
- [X] Simplified Descriptor Creation Pipeline
|
- [X] Simplified Descriptor Creation Pipeline
|
||||||
|
- [X] Lighting Equations
|
||||||
|
- [X] Blinn-Phong
|
||||||
|
- [X] PBR / IBL
|
||||||
- [ ] Debugging
|
- [ ] Debugging
|
||||||
- [ ] Tracy Integration
|
- [ ] Tracy Integration
|
||||||
- [X] Dear ImGui Integration
|
- [X] Dear ImGui Integration
|
||||||
- [ ] Transparency
|
- [ ] Transparency
|
||||||
- [ ] Sorted
|
- [ ] Sorted
|
||||||
- [ ] Order Independent
|
- [ ] Order Independent (Depth Peeling)
|
||||||
- [ ] Shadows v1
|
- [ ] Shadows
|
||||||
|
- [ ] Shadow Mapping
|
||||||
- [ ] Omnidirectional Cubemap Shadows
|
- [ ] Omnidirectional Cubemap Shadows
|
||||||
- [ ] SpotLight Shadows
|
- [ ] SpotLight Shadows
|
||||||
- [ ] Directional Shadows
|
- [ ] Directional Shadows
|
||||||
- [ ] Cascaded Shadows
|
- [ ] Cascaded Shadows
|
||||||
- [ ] PCF
|
- [ ] PCF
|
||||||
- [ ] Lighting / Shading Pipelines
|
- [ ] Omnidirectional Dual Paraboloid Shadows
|
||||||
- [ ] Blinn-Phong
|
- [ ] Shadow Masks
|
||||||
- [ ] PBR
|
- [ ] Perspective Shadow Mapping
|
||||||
- [ ] IBL
|
- [ ] RTX Shadows
|
||||||
- [ ] Rendering Techniques
|
- [ ] Rendering Techniques
|
||||||
- [ ] Forward Rendering
|
- [X] Forward Rendering
|
||||||
- [ ] Deferred Rendering
|
- [ ] Deferred Rendering
|
||||||
- [ ] Clustered-Forward Rendering
|
- [ ] Clustered-Forward Rendering
|
||||||
|
- [ ] V-Buffer Rendering
|
||||||
|
- [ ] Light Prepass
|
||||||
- [ ] Ambient Occlusion
|
- [ ] Ambient Occlusion
|
||||||
- [ ] SSAO
|
- [ ] SSAO
|
||||||
- [ ] HBAO
|
- [ ] HBAO
|
||||||
|
|
@ -43,16 +48,29 @@ A Vulkan based renderer created with Vulkan 1.3 in C++.
|
||||||
- [ ] Cubemap/Probe Reflection
|
- [ ] Cubemap/Probe Reflection
|
||||||
- [ ] Ray-Traced Reflection
|
- [ ] Ray-Traced Reflection
|
||||||
- [ ] Global Illumination
|
- [ ] Global Illumination
|
||||||
|
- [ ] Light Mapping
|
||||||
|
- [ ] Light Probes
|
||||||
- [ ] Precomputed Radiance Transfer
|
- [ ] Precomputed Radiance Transfer
|
||||||
- [ ] Voxel Cone Tracing
|
- [ ] Voxel Cone Tracing
|
||||||
- [ ] SDFGI
|
- [ ] SDFGI
|
||||||
- [ ] RTXGI
|
- [ ] RTXGI
|
||||||
- [ ] Shadows v2
|
|
||||||
- [ ] Omnidirectional Dual Paraboloid Shadows
|
|
||||||
- [ ] Perspective Shadow Mapping
|
|
||||||
- [ ] RTX Shadows
|
|
||||||
- [ ] Animation
|
- [ ] Animation
|
||||||
- [ ] Skeletal Animation
|
- [ ] Skeletal Animation
|
||||||
- [ ] TBD
|
- [ ] TBD
|
||||||
- [ ] Particle Effects
|
- [ ] Particle Effects
|
||||||
- [ ] Full Path Tracing
|
- [ ] 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
|
||||||
|
|
@ -33,4 +33,10 @@ function(add_shader TARGET SHADER)
|
||||||
# Make sure our build depends on this output.
|
# Make sure our build depends on this output.
|
||||||
set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE)
|
set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE)
|
||||||
target_sources(${TARGET} PRIVATE ${current-output-path})
|
target_sources(${TARGET} PRIVATE ${current-output-path})
|
||||||
endfunction(add_shader)
|
endfunction(add_shader)
|
||||||
|
|
||||||
|
function(add_shaders TARGET SHADERS)
|
||||||
|
foreach(shader IN ${SHADERS})
|
||||||
|
add_shader(TARGET ${shader})
|
||||||
|
endforeach()
|
||||||
|
endfunction(add_shaders)
|
||||||
|
|
@ -21,6 +21,7 @@ void
|
||||||
Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name)
|
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name)
|
||||||
{
|
{
|
||||||
|
assert(!IsValid());
|
||||||
assert(size <= SIZE_MASK);
|
assert(size <= SIZE_MASK);
|
||||||
|
|
||||||
vk::BufferCreateInfo bufferCreateInfo = {
|
vk::BufferCreateInfo bufferCreateInfo = {
|
||||||
|
|
@ -53,6 +54,13 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
|
||||||
device->SetName(m_Buffer, name);
|
device->SetName(m_Buffer, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uptr
|
||||||
|
Buffer::GetDeviceAddress(const Device *device)
|
||||||
|
{
|
||||||
|
vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer};
|
||||||
|
return device->m_Device.getBufferAddress(&addressInfo);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Buffer::Write(const Device *device, usize offset, usize size, const void *data)
|
Buffer::Write(const Device *device, usize offset, usize size, const void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -94,17 +102,69 @@ UniformBuffer::Init(const Device *device, const usize size, const cstr name)
|
||||||
void
|
void
|
||||||
StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
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)
|
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_SEQUENTIAL_WRITE_BIT |
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
VMA_MEMORY_USAGE_AUTO, name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
usage |= vk::BufferUsageFlagBits::eTransferDst;
|
||||||
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,14 +172,14 @@ void
|
||||||
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
||||||
{
|
{
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
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
|
void
|
||||||
IndexBuffer::Init(const Device *device, usize size, cstr name)
|
IndexBuffer::Init(const Device *device, usize size, cstr name)
|
||||||
{
|
{
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
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
|
void
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@
|
||||||
|
|
||||||
struct Device;
|
struct Device;
|
||||||
|
|
||||||
|
// TODO Refactor the Buffer Hierarchy
|
||||||
|
|
||||||
struct Buffer
|
struct Buffer
|
||||||
{
|
{
|
||||||
vk::Buffer m_Buffer = nullptr;
|
vk::Buffer m_Buffer = nullptr;
|
||||||
|
|
@ -29,6 +31,9 @@ struct Buffer
|
||||||
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
||||||
|
|
||||||
|
uptr
|
||||||
|
GetDeviceAddress(const Device *device);
|
||||||
|
|
||||||
// Buffer.size is used for bookkeeping
|
// Buffer.size is used for bookkeeping
|
||||||
// If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`.
|
// If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`.
|
||||||
usize m_Size_ = 0;
|
usize m_Size_ = 0;
|
||||||
|
|
@ -49,6 +54,17 @@ struct UniformBuffer : Buffer
|
||||||
struct StorageBuffer : 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, cstr name = nullptr);
|
||||||
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexBuffer : Buffer
|
struct VertexBuffer : Buffer
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#define GLM_FORCE_RADIANS
|
#define GLM_FORCE_RADIANS
|
||||||
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||||
#define GLM_FORCE_QUATERNIONS_XYZW
|
#define GLM_FORCE_QUATERNIONS_XYZW
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
||||||
#define GLFW_INCLUDE_VULKAN
|
#define GLFW_INCLUDE_VULKAN
|
||||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
using c8 = char;
|
using c8 = char;
|
||||||
using u8 = uint8_t;
|
using u8 = uint8_t;
|
||||||
|
|
@ -57,22 +58,26 @@ Recast(TFrom &&in)
|
||||||
return reinterpret_cast<TType>(std::forward<TFrom>(in));
|
return reinterpret_cast<TType>(std::forward<TFrom>(in));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32 operator""_deg(long double degrees)
|
constexpr f32
|
||||||
|
operator""_deg(long double degrees)
|
||||||
{
|
{
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
return glm::radians<f32>(Cast<f32>(degrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32 operator""_deg(unsigned long long int degrees)
|
constexpr f32
|
||||||
|
operator""_deg(unsigned long long int degrees)
|
||||||
{
|
{
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
return glm::radians<f32>(Cast<f32>(degrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32 operator""_rad(long double rads)
|
constexpr f32
|
||||||
|
operator""_rad(long double rads)
|
||||||
{
|
{
|
||||||
return Cast<f32>(rads);
|
return Cast<f32>(rads);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32 operator""_rad(unsigned long long int rads)
|
constexpr f32
|
||||||
|
operator""_rad(unsigned long long int rads)
|
||||||
{
|
{
|
||||||
return Cast<f32>(rads);
|
return Cast<f32>(rads);
|
||||||
}
|
}
|
||||||
|
|
@ -110,6 +115,36 @@ constexpr Version VERSION = {
|
||||||
.m_Patch = 0,
|
.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 <typename T>
|
template <typename T>
|
||||||
constexpr T MaxValue = std::numeric_limits<T>::max();
|
constexpr T MaxValue = std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
|
||||||
};
|
};
|
||||||
|
|
||||||
const VmaAllocatorCreateInfo allocatorCreateInfo = {
|
const VmaAllocatorCreateInfo allocatorCreateInfo = {
|
||||||
|
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
||||||
.physicalDevice = m_PhysicalDevice,
|
.physicalDevice = m_PhysicalDevice,
|
||||||
.device = m_Device,
|
.device = m_Device,
|
||||||
.pVulkanFunctions = &vmaVulkanFunctions,
|
.pVulkanFunctions = &vmaVulkanFunctions,
|
||||||
|
|
@ -142,6 +143,14 @@ Device::DumpPipelineCache() const
|
||||||
return pipelineCacheData;
|
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
|
Device::Device(Device &&other) noexcept
|
||||||
: m_Name(std::move(other.m_Name))
|
: m_Name(std::move(other.m_Name))
|
||||||
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,8 @@ struct Device final
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<u8> DumpPipelineCache() const;
|
[[nodiscard]] eastl::vector<u8> DumpPipelineCache() const;
|
||||||
|
|
||||||
|
void WaitIdle() const;
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ add_library(util_helper STATIC
|
||||||
helpers.cpp
|
helpers.cpp
|
||||||
frame.cpp
|
frame.cpp
|
||||||
frame.h
|
frame.h
|
||||||
gpu_resource_manager.cpp
|
|
||||||
gpu_resource_manager.h
|
|
||||||
gui.cpp
|
gui.cpp
|
||||||
gui.h)
|
gui.h)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
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.vert.glsl)
|
||||||
add_shader(triangle shader/triangle.frag.glsl)
|
add_shader(triangle shader/triangle.frag.glsl)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -331,8 +331,7 @@ main(int, char **)
|
||||||
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto result = device.m_Device.waitIdle();
|
device.WaitIdle();
|
||||||
ERROR_IF(Failed(result), "Wait idle failed. Cause: {}", result);
|
|
||||||
|
|
||||||
device.m_Device.destroy(copyPool, nullptr);
|
device.m_Device.destroy(copyPool, nullptr);
|
||||||
vbo.Destroy(&device);
|
vbo.Destroy(&device);
|
||||||
|
|
|
||||||
|
|
@ -531,7 +531,7 @@ main(int, char **)
|
||||||
currentFrame->Present(commandQueue, &swapchain, &window);
|
currentFrame->Present(commandQueue, &swapchain, &window);
|
||||||
}
|
}
|
||||||
|
|
||||||
AbortIfFailed(device.m_Device.waitIdle());
|
device.WaitIdle();
|
||||||
|
|
||||||
for (auto &depthImage : depthImages)
|
for (auto &depthImage : depthImages)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ add_executable(model_render model_render.cpp
|
||||||
asset_loader.h
|
asset_loader.h
|
||||||
light_manager.cpp
|
light_manager.cpp
|
||||||
light_manager.h
|
light_manager.h
|
||||||
|
gpu_resource_manager.cpp
|
||||||
|
gpu_resource_manager.h
|
||||||
nodes.cpp
|
nodes.cpp
|
||||||
nodes.h
|
nodes.h
|
||||||
ibl_helpers.cpp
|
ibl_helpers.cpp
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -133,8 +133,8 @@ main(int, char **)
|
||||||
{
|
{
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
||||||
|
|
||||||
Context context = {"ModelRender [WIP]", VERSION};
|
Context context = {"ModelRender", VERSION};
|
||||||
Window window = {"ModelRender [WIP] (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}};
|
Window window = {"ModelRender (Aster)", &context, {INIT_WIDTH, INIT_HEIGHT}};
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&window, &context};
|
PhysicalDevices physicalDevices = {&window, &context};
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
||||||
|
|
@ -157,6 +157,8 @@ main(int, char **)
|
||||||
.descriptorBindingStorageBufferUpdateAfterBind = true,
|
.descriptorBindingStorageBufferUpdateAfterBind = true,
|
||||||
.descriptorBindingPartiallyBound = true,
|
.descriptorBindingPartiallyBound = true,
|
||||||
.runtimeDescriptorArray = true,
|
.runtimeDescriptorArray = true,
|
||||||
|
.bufferDeviceAddress = true,
|
||||||
|
.bufferDeviceAddressCaptureReplay = true,
|
||||||
},
|
},
|
||||||
.m_Vulkan13Features =
|
.m_Vulkan13Features =
|
||||||
{
|
{
|
||||||
|
|
@ -584,8 +586,6 @@ main(int, char **)
|
||||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1,
|
||||||
&resourceManager.m_DescriptorSet, 0, nullptr);
|
&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,
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
|
|
@ -689,7 +689,7 @@ main(int, char **)
|
||||||
currentFrame->Present(graphicsQueue, &swapchain, &window);
|
currentFrame->Present(graphicsQueue, &swapchain, &window);
|
||||||
}
|
}
|
||||||
|
|
||||||
AbortIfFailed(device.m_Device.waitIdle());
|
device.WaitIdle();
|
||||||
|
|
||||||
environment.Destroy(&resourceManager);
|
environment.Destroy(&resourceManager);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
# 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
|
||||||
|
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/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)
|
||||||
|
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)
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,117 @@
|
||||||
|
// =============================================
|
||||||
|
// 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
|
||||||
|
f32 m_AlphaBlend; /* 04 60
|
||||||
|
AlphaBlend < 0 means Opaque
|
||||||
|
AlphaBlend > 1 means Blend
|
||||||
|
AlphaBlend in [0,1] means cutoff */
|
||||||
|
};
|
||||||
|
|
||||||
|
// If this is changed, ensure the shaders are changed accordingly.
|
||||||
|
static_assert(sizeof(Material) == 60);
|
||||||
|
|
||||||
|
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};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Model
|
||||||
|
{
|
||||||
|
eastl::vector<TextureHandle> m_Textures;
|
||||||
|
eastl::vector<Entity> m_Entities;
|
||||||
|
|
||||||
|
IndexHandle m_IndexHandle;
|
||||||
|
GeometryHandle m_VertexPositionHandle;
|
||||||
|
GeometryHandle m_VertexDataHandle;
|
||||||
|
MaterialHandle m_MaterialHandle;
|
||||||
|
Entity m_RootEntity;
|
||||||
|
|
||||||
|
void Destroy(RenderResourceManager *resourceManager, EcsRegistry *registry);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CModel
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
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<vec4> *vertexPositions, eastl::vector<VertexData> *vertexData,
|
||||||
|
eastl::vector<u32> *indices, eastl::vector<Entity> *entities,
|
||||||
|
const std::function<u32(i32)> &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);
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: core_components.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
template <typename TComponent>
|
||||||
|
struct CDirty
|
||||||
|
{
|
||||||
|
using RelatedComponentType = TComponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TComponent>
|
||||||
|
struct CParent
|
||||||
|
{
|
||||||
|
using RelatedComponentType = TComponent;
|
||||||
|
entt::entity m_ParentEntity = NULL_ENTITY;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CDynamicTransform
|
||||||
|
{
|
||||||
|
vec3 m_Position = vec3{0.0f};
|
||||||
|
quat m_Rotation = glm::identity<quat>();
|
||||||
|
vec3 m_Scale = vec3{1.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CStaticTransform
|
||||||
|
{};
|
||||||
|
|
||||||
|
struct CGlobalTransform
|
||||||
|
{
|
||||||
|
mat4 m_Transform = glm::identity<mat4>();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CMaterial
|
||||||
|
{
|
||||||
|
uptr m_MaterialPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CMesh
|
||||||
|
{
|
||||||
|
uptr m_VertexPositionPtr;
|
||||||
|
uptr m_VertexDataPtr;
|
||||||
|
u32 m_FirstIndex;
|
||||||
|
u32 m_IndexCount;
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: ecs_adapter.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
|
||||||
|
using EcsRegistry = entt::registry;
|
||||||
|
using Entity = entt::entity;
|
||||||
|
template <typename... T>
|
||||||
|
using Without = entt::exclude_t<T...>;
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
using Get = entt::get_t<T...>;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool Exists(Entity entity)
|
||||||
|
{
|
||||||
|
return entity != entt::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Entity NULL_ENTITY = entt::null;
|
||||||
|
|
@ -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<StorageTextureHandle, prefilterMipCountMax> 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<vk::ImageMemoryBarrier2, 4> 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<u32>(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<vk::Pipeline, computePipelineCreateInfo.size()> 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 : "<unknown env>";
|
||||||
|
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<f32>(mipCount) / Cast<f32>(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<u32>));
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
*.hdr filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.exr filter=lfs diff=lfs merge=lfs -text
|
||||||
Binary file not shown.
|
|
@ -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<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||||
|
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||||
|
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
||||||
|
const u32 a = Cast<u32>(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<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||||
|
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||||
|
const u32 b = Cast<u32>(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<Light>);
|
||||||
|
|
||||||
|
// 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<u16>(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<u16>(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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
// =============================================
|
||||||
|
// 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<u16>;
|
||||||
|
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
|
||||||
|
u32 m_UnusedPadding1 = 0; // 04 32
|
||||||
|
};
|
||||||
|
|
||||||
|
RenderResourceManager *m_ResourceManager;
|
||||||
|
eastl::vector<Light> 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<u16>
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,584 @@
|
||||||
|
// =============================================
|
||||||
|
// 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 "light_manager.h"
|
||||||
|
|
||||||
|
#include "ecs_adapter.h"
|
||||||
|
#include "frame.h"
|
||||||
|
#include "ibl_helpers.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/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;
|
||||||
|
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);
|
||||||
|
|
||||||
|
vk::Extent2D internalResolution = {1920, 1080};
|
||||||
|
internalResolution.width = (internalResolution.height * INIT_WIDTH) / INIT_HEIGHT;
|
||||||
|
|
||||||
|
CameraController cameraController = {vec3{0.0f, 1.0f, 4.0f}, vec3{0.0f, 0.0f, 0.0f}, 70_deg,
|
||||||
|
Cast<f32>(internalResolution.width) / Cast<f32>(internalResolution.height)};
|
||||||
|
|
||||||
|
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
||||||
|
|
||||||
|
Features enabledDeviceFeatures = {
|
||||||
|
.m_Vulkan10Features =
|
||||||
|
{
|
||||||
|
.multiDrawIndirect = true,
|
||||||
|
.samplerAnisotropy = true,
|
||||||
|
.shaderInt64 = true,
|
||||||
|
},
|
||||||
|
.m_Vulkan11Features =
|
||||||
|
{
|
||||||
|
.shaderDrawParameters = 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};
|
||||||
|
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(&environment);
|
||||||
|
|
||||||
|
eastl::vector<Model> models;
|
||||||
|
models.emplace_back(assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model"));
|
||||||
|
//registry.get<CDynamicTransform>(model.m_RootEntity).m_Position = vec3(2 * i, 0, 2 * j);
|
||||||
|
|
||||||
|
UniformBuffer ubo;
|
||||||
|
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, uboLightManagerOffset, 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;
|
||||||
|
|
||||||
|
{
|
||||||
|
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<u32>(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 camLightBufferInfo = {
|
||||||
|
.buffer = ubo.m_Buffer,
|
||||||
|
.offset = 0,
|
||||||
|
.range = uboTotalSize,
|
||||||
|
};
|
||||||
|
eastl::array writeDescriptors = {
|
||||||
|
vk::WriteDescriptorSet{
|
||||||
|
.dstSet = perFrameDescriptor,
|
||||||
|
.dstBinding = 0,
|
||||||
|
.dstArrayElement = 0,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.pBufferInfo = &camLightBufferInfo,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
|
||||||
|
|
||||||
|
// Persistent variables
|
||||||
|
vk::Viewport viewport = {
|
||||||
|
.x = 0,
|
||||||
|
.y = Cast<f32>(internalResolution.height),
|
||||||
|
.width = Cast<f32>(internalResolution.width),
|
||||||
|
.height = -Cast<f32>(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<u32>(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<DepthImage, MAX_FRAMES_IN_FLIGHT> depthImages(frameManager.m_FramesInFlight);
|
||||||
|
eastl::fixed_vector<AttachmentImage, MAX_FRAMES_IN_FLIGHT> 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;
|
||||||
|
u8 m_Padding_[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::vector<NodeData> nodeData;
|
||||||
|
eastl::vector<vk::DrawIndexedIndirectCommand> nodeDrawInfo;
|
||||||
|
|
||||||
|
eastl::fixed_vector<StorageBuffer, MAX_FRAMES_IN_FLIGHT> nodeBuffers;
|
||||||
|
eastl::fixed_vector<IndirectBuffer, MAX_FRAMES_IN_FLIGHT> nodeIndirectBuffers;
|
||||||
|
eastl::fixed_vector<uptr, MAX_FRAMES_IN_FLIGHT> perFrameNodeBufferPtr;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
|
||||||
|
{
|
||||||
|
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(
|
||||||
|
[&cameraController, &internalResolution, &viewport, &scissor](vk::Extent2D extent) {
|
||||||
|
cameraController.SetAspectRatio(Cast<f32>(extent.width) / Cast<f32>(extent.height));
|
||||||
|
|
||||||
|
internalResolution.width = Cast<u32>(Cast<f32>(internalResolution.height) * cameraController.m_AspectRatio);
|
||||||
|
|
||||||
|
viewport.y = Cast<f32>(internalResolution.height);
|
||||||
|
viewport.width = Cast<f32>(internalResolution.width);
|
||||||
|
viewport.height = -Cast<f32>(internalResolution.height);
|
||||||
|
scissor.extent = internalResolution;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto sortByParentHier = [®istry](Entity a, Entity b) {
|
||||||
|
const auto parent = registry.try_get<CParent<CDynamicTransform>>(b);
|
||||||
|
return parent && parent->m_ParentEntity == a;
|
||||||
|
};
|
||||||
|
registry.sort<CParent<CDynamicTransform>>(sortByParentHier);
|
||||||
|
|
||||||
|
Time::Init();
|
||||||
|
|
||||||
|
auto rootNodeUpdateView = registry.view<CDynamicTransform, CGlobalTransform>(Without<CParent<CDynamicTransform>>{});
|
||||||
|
|
||||||
|
auto rootModel = registry.group<CModel>(Get<CDynamicTransform>{});
|
||||||
|
|
||||||
|
auto nodeWithParentsUpdateView = registry.view<CDynamicTransform, CParent<CDynamicTransform>, CGlobalTransform>();
|
||||||
|
nodeWithParentsUpdateView.use<CParent<CDynamicTransform>>();
|
||||||
|
|
||||||
|
auto renderableObjectsGroup = registry.group<CGlobalTransform, CMesh, CMaterial>();
|
||||||
|
|
||||||
|
lightManager.Update();
|
||||||
|
ubo.Write(&device, uboLightManagerOffset, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo);
|
||||||
|
|
||||||
|
resourceManager.Update();
|
||||||
|
|
||||||
|
while (window.Poll())
|
||||||
|
{
|
||||||
|
Time::Update();
|
||||||
|
|
||||||
|
//u32 index = 0;
|
||||||
|
//for (auto [entity, dynTrans] : rootModel.each())
|
||||||
|
//{
|
||||||
|
// dynTrans.m_Rotation =
|
||||||
|
// glm::rotate(dynTrans.m_Rotation, Cast<f32>(30_deg * (++index) * 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];
|
||||||
|
|
||||||
|
// Resize outdated attachments
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
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.
|
||||||
|
// Update all dynamic object transforms.
|
||||||
|
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<CGlobalTransform>(parent.m_ParentEntity).m_Transform * translation * rotation * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 objectCount = Cast<u32>(renderableObjectsGroup.size());
|
||||||
|
nodeData.clear();
|
||||||
|
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({
|
||||||
|
.m_Transform = globalTransform.m_Transform,
|
||||||
|
.m_VertexPositionPtr = mesh.m_VertexPositionPtr,
|
||||||
|
.m_VertexDataPtr = mesh.m_VertexDataPtr,
|
||||||
|
.m_MaterialPtr = material.m_MaterialPtr,
|
||||||
|
});
|
||||||
|
nodeDrawInfo.push_back({
|
||||||
|
.indexCount = mesh.m_IndexCount,
|
||||||
|
.instanceCount = 1,
|
||||||
|
.firstIndex = mesh.m_FirstIndex,
|
||||||
|
.vertexOffset = 0,
|
||||||
|
.firstInstance = 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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<u32>(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);
|
||||||
|
|
||||||
|
cmd.bindIndexBuffer(resourceManager.GetIndexBuffer(), 0, vk::IndexType::eUint32);
|
||||||
|
|
||||||
|
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
cmd.draw(3, 1, 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<i32>(swapchain.m_Extent.width), Cast<i32>(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 buffer : nodeIndirectBuffers)
|
||||||
|
{
|
||||||
|
buffer.Destroy(&device);
|
||||||
|
}
|
||||||
|
for (auto buffer : nodeBuffers)
|
||||||
|
{
|
||||||
|
buffer.Destroy(&device);
|
||||||
|
}
|
||||||
|
for (auto depthImage : depthImages)
|
||||||
|
{
|
||||||
|
depthImage.Destroy(&device);
|
||||||
|
}
|
||||||
|
for (auto attachmentImage : attachmentImages)
|
||||||
|
{
|
||||||
|
attachmentImage.Destroy(&device);
|
||||||
|
}
|
||||||
|
ubo.Destroy(&device);
|
||||||
|
|
||||||
|
device.m_Device.destroy(descriptorPool, nullptr);
|
||||||
|
|
||||||
|
for (auto &model : models)
|
||||||
|
{
|
||||||
|
model.Destroy(&resourceManager, ®istry);
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg (Stored with Git LFS)
Normal file
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_diff_4k.jpg (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg (Stored with Git LFS)
Normal file
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_nor_gl_4k.jpg (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg (Stored with Git LFS)
Normal file
BIN
samples/04_scenes/model/MarbleBust/textures/marble_bust_01_rough_4k.jpg (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,328 @@
|
||||||
|
// =============================================
|
||||||
|
// 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 <EASTL/array.h>
|
||||||
|
|
||||||
|
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<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vertexShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fragmentShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> 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<u32>(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<u32>(descriptorSetLayouts.size()),
|
||||||
|
.pSetLayouts = descriptorSetLayouts.data(),
|
||||||
|
.pushConstantRangeCount = 1,
|
||||||
|
.pPushConstantRanges = &pushConstantRange,
|
||||||
|
};
|
||||||
|
vk::PipelineLayout pipelineLayout;
|
||||||
|
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
|
device->SetName(pipelineLayout, "Scene 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 = true,
|
||||||
|
.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha,
|
||||||
|
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
||||||
|
.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<u32>(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<u32>(shaderStages.size()),
|
||||||
|
.pStages = shaderStages.data(),
|
||||||
|
.pVertexInputState = &vertexInputStateCreateInfo,
|
||||||
|
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
||||||
|
.pViewportState = &viewportStateCreateInfo,
|
||||||
|
.pRasterizationState = &rasterizationStateCreateInfo,
|
||||||
|
.pMultisampleState = &multisampleStateCreateInfo,
|
||||||
|
.pDepthStencilState = &depthStencilStateCreateInfo,
|
||||||
|
.pColorBlendState = &colorBlendStateCreateInfo,
|
||||||
|
.pDynamicState = &dynamicStateCreateInfo,
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
};
|
||||||
|
vk::Pipeline pipeline;
|
||||||
|
AbortIfFailed(
|
||||||
|
device->m_Device.createGraphicsPipelines(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)};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||||
|
.module = vertexShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||||
|
.module = fragmentShaderModule,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
}};
|
||||||
|
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> 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<u32>(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<u32>(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<u32>(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<u32>(shaderStages.size()),
|
||||||
|
.pStages = shaderStages.data(),
|
||||||
|
.pVertexInputState = &vertexInputStateCreateInfo,
|
||||||
|
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
||||||
|
.pViewportState = &viewportStateCreateInfo,
|
||||||
|
.pRasterizationState = &rasterizationStateCreateInfo,
|
||||||
|
.pMultisampleState = &multisampleStateCreateInfo,
|
||||||
|
.pDepthStencilState = &depthStencilStateCreateInfo,
|
||||||
|
.pColorBlendState = &colorBlendStateCreateInfo,
|
||||||
|
.pDynamicState = &dynamicStateCreateInfo,
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
};
|
||||||
|
vk::Pipeline pipeline;
|
||||||
|
AbortIfFailed(
|
||||||
|
device->m_Device.createGraphicsPipelines(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)
|
||||||
|
{
|
||||||
|
eastl::vector<u32> shaderCode = ReadFile(shaderFile);
|
||||||
|
|
||||||
|
const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
|
||||||
|
.codeSize = shaderCode.size() * sizeof(u32),
|
||||||
|
.pCode = shaderCode.data(),
|
||||||
|
};
|
||||||
|
vk::ShaderModule shaderModule;
|
||||||
|
|
||||||
|
AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule),
|
||||||
|
"Shader {} could not be created.", shaderFile);
|
||||||
|
return shaderModule;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// =============================================
|
||||||
|
// 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";
|
||||||
|
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);
|
||||||
|
|
@ -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 <EASTL/array.h>
|
||||||
|
|
||||||
|
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<u32 *>(allocatedTexture);
|
||||||
|
|
||||||
|
// Ensure it is copyable.
|
||||||
|
static_assert(std::is_trivially_copyable_v<Texture>);
|
||||||
|
*allocatedTexture = *texture;
|
||||||
|
|
||||||
|
// Take ownership of the texture.
|
||||||
|
texture->m_Flags_ &= ~Texture::OWNED_BIT;
|
||||||
|
|
||||||
|
return {index};
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 index = Cast<u32>(m_Textures.size());
|
||||||
|
if (index < m_MaxCapacity)
|
||||||
|
{
|
||||||
|
Texture *allocatedTexture = &m_Textures.push_back();
|
||||||
|
|
||||||
|
// Ensure it is copyable.
|
||||||
|
static_assert(std::is_trivially_copyable_v<Texture>);
|
||||||
|
*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<u32 *>(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<u32 *>(allocatedBuffer);
|
||||||
|
|
||||||
|
// Ensure it is copyable.
|
||||||
|
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
|
||||||
|
*allocatedBuffer = *buffer;
|
||||||
|
|
||||||
|
// Take ownership of the buffer.
|
||||||
|
buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT;
|
||||||
|
|
||||||
|
return {index};
|
||||||
|
}
|
||||||
|
|
||||||
|
const u32 index = Cast<u32>(m_Buffers.size());
|
||||||
|
if (index < m_MaxCapacity)
|
||||||
|
{
|
||||||
|
StorageBuffer *allocatedBuffer = &m_Buffers.push_back();
|
||||||
|
|
||||||
|
// Ensure it is copyable.
|
||||||
|
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
|
||||||
|
*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<u32 *>(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<StorageTexture *>(&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<usize>(createInfo->mipLodBias * 1000))); // Resolution of 10^-3
|
||||||
|
hash = HashCombine(hash, HashAny(createInfo->anisotropyEnable));
|
||||||
|
hash = HashCombine(hash,
|
||||||
|
HashAny(Cast<usize>(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<usize>(createInfo->minLod * 1000))); // 0.001 resolution is enough.
|
||||||
|
hash = HashCombine(hash,
|
||||||
|
HashAny(Cast<usize>(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<u32>(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<StorageBuffer>();
|
||||||
|
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<vk::Result>(vmaCreateVirtualBlock(&virtualBlockCreateInfo, &m_Block)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
VirtualizedBufferPool::InitIndex(const Device *device, usize bufferMaxSize)
|
||||||
|
{
|
||||||
|
auto buffer = std::make_unique<StorageIndexBuffer>();
|
||||||
|
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<vk::Result>(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<u32 *>(allocVBuf);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
index = Cast<u32>(m_VirtualBuffers.size());
|
||||||
|
allocVBuf = &m_VirtualBuffers.push_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure it is copyable.
|
||||||
|
static_assert(std::is_trivially_copyable_v<Texture>);
|
||||||
|
*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<u32 *>(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<u32>(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<u32>(maxSize));
|
||||||
|
u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast<u32>(maxSize));
|
||||||
|
u32 storageTexturesCount =
|
||||||
|
eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize));
|
||||||
|
|
||||||
|
INFO("Max Buffer Count: {}", buffersCount);
|
||||||
|
INFO("Max Texture Count: {}", texturesCount);
|
||||||
|
INFO("Max Storage Texture Count: {}", storageTexturesCount);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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<u32>(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<u32>(layoutBindingFlags.size()),
|
||||||
|
.pBindingFlags = layoutBindingFlags.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::array descriptorLayoutBindings = {
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = BUFFER_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||||
|
.descriptorCount = Cast<u32>(buffersCount),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = TEXTURE_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.descriptorCount = Cast<u32>(texturesCount),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = STORAGE_TEXTURE_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.descriptorCount = Cast<u32>(storageTexturesCount),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
|
||||||
|
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
||||||
|
.pNext = &bindingFlagsCreateInfo,
|
||||||
|
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
|
||||||
|
.bindingCount = Cast<u32>(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<u32>(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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: render_resource_manager.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <EASTL/deque.h>
|
||||||
|
#include <EASTL/vector_map.h>
|
||||||
|
|
||||||
|
struct Device;
|
||||||
|
struct Texture;
|
||||||
|
struct StorageTexture;
|
||||||
|
struct StorageBuffer;
|
||||||
|
struct Buffer;
|
||||||
|
|
||||||
|
struct GpuResourceHandle
|
||||||
|
{
|
||||||
|
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
|
||||||
|
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<Texture> 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<StorageBuffer> 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<vk::Sampler> m_Samplers;
|
||||||
|
eastl::vector<usize> 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<StorageBuffer> m_BackingBuffer;
|
||||||
|
uptr m_BufferPtr;
|
||||||
|
VmaVirtualBlock m_Block;
|
||||||
|
|
||||||
|
struct VirtualBuffer
|
||||||
|
{
|
||||||
|
VmaVirtualAllocation m_Allocation;
|
||||||
|
usize m_Offset;
|
||||||
|
usize m_Size;
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::vector<VirtualBuffer> 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<HandleType, u32>;
|
||||||
|
|
||||||
|
eastl::deque<WriteInfo> m_WriteInfos;
|
||||||
|
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
||||||
|
eastl::vector<WriteOwner> 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);
|
||||||
|
};
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
|
||||||
|
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
|
||||||
|
/*
|
||||||
|
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.
|
||||||
|
};
|
||||||
|
|
||||||
|
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[];
|
||||||
|
};
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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[];
|
||||||
|
};
|
||||||
|
|
@ -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<float4> VertexBuffer[];
|
||||||
|
[[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[];
|
||||||
|
[[vk::binding(0, 0)]] StructuredBuffer<MaterialData> MaterialsBuffer[];
|
||||||
|
[[vk::binding(0, 0)]] StructuredBuffer<TransformData> NodeBuffer[];
|
||||||
|
[[vk::binding(0, 0)]] StructuredBuffer<PointLight> PointLightBuffer[];
|
||||||
|
[[vk::binding(0, 0)]] StructuredBuffer<DirectionalLight> DirectionalLightBuffer[];
|
||||||
|
|
||||||
|
[[vk::binding(1, 0)]] Texture2D<float4> Textures[];
|
||||||
|
[[vk::binding(1, 0)]] Texture2D<float2> TexturesRG[];
|
||||||
|
[[vk::binding(1, 0)]] TextureCube<float4> TextureCubes[];
|
||||||
|
[[vk::binding(1, 0)]] SamplerState ImmutableSamplers[];
|
||||||
|
|
||||||
|
[[vk::binding(2, 0)]] RWTexture2D<float4> StorageTextures[];
|
||||||
|
[[vk::binding(2, 0)]] RWTexture2D<float2> StorageTexturesRG[];
|
||||||
|
[[vk::binding(2, 0)]] RWTexture2DArray<float4> StorageTextureArrays[];
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,310 @@
|
||||||
|
#version 460
|
||||||
|
#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 = 4) in flat uint in_DrawID;
|
||||||
|
|
||||||
|
layout (location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout (push_constant) uniform Const {
|
||||||
|
NodeRef nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
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.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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = processAlpha(albedoA.a, material.AlphaBlend);
|
||||||
|
|
||||||
|
vec3 normal = GetNormal(material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0);
|
||||||
|
|
||||||
|
vec3 emission = GetEmissive(material.m_EmissionFactor, material.m_EmissionTex, 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);
|
||||||
|
|
||||||
|
vec3 viewDir = normalize(camera.m_Position.xyz - in_Position.xyz);
|
||||||
|
outColor = vec4(Uncharted2Tonemap(pointLumin + directionalLumin + ambientLumin + emission), alpha);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
#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 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 Const {
|
||||||
|
NodeRef nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 ),
|
||||||
|
};
|
||||||
|
|
||||||
|
drawID = gl_DrawID;
|
||||||
|
Node n = nodes.nodes[gl_DrawID];
|
||||||
|
|
||||||
|
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(n.pVertexData.Data[gl_VertexIndex].Color.rgb, 1.0f);
|
||||||
|
// outColor = vec4(colors[gl_VertexIndex % 3], 1.0f);
|
||||||
|
outUV0 = n.pVertexData.Data[gl_VertexIndex].TexCoord0;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -6,3 +6,4 @@ add_subdirectory("00_util")
|
||||||
add_subdirectory("01_triangle")
|
add_subdirectory("01_triangle")
|
||||||
add_subdirectory("02_box")
|
add_subdirectory("02_box")
|
||||||
add_subdirectory("03_model_render")
|
add_subdirectory("03_model_render")
|
||||||
|
add_subdirectory("04_scenes")
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@
|
||||||
{
|
{
|
||||||
"name": "imgui",
|
"name": "imgui",
|
||||||
"features": [
|
"features": [
|
||||||
|
"docking-experimental",
|
||||||
"glfw-binding",
|
"glfw-binding",
|
||||||
"vulkan-binding",
|
"vulkan-binding"
|
||||||
"docking-experimental"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"scottt-debugbreak",
|
"scottt-debugbreak",
|
||||||
"tinygltf",
|
"tinygltf",
|
||||||
"vulkan-memory-allocator"
|
"vulkan-memory-allocator",
|
||||||
|
"entt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue