Compare commits
8 Commits
6b5442527f
...
e82b37b2d9
| Author | SHA1 | Date |
|---|---|---|
|
|
e82b37b2d9 | |
|
|
b9d5ba56d4 | |
|
|
23cd7eae8b | |
|
|
8a865e2e49 | |
|
|
7ba132ec0c | |
|
|
ba2e21f52e | |
|
|
f735b63d7e | |
|
|
6d39576b5c |
|
|
@ -9,9 +9,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "/W4 /GR- /Zi")
|
||||
set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS}")
|
||||
add_compile_definitions(_HAS_EXCEPTIONS=0)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||
add_compile_definitions(${MSVC_DEFINES})
|
||||
else ()
|
||||
set(CMAKE_CXX_FLAGS "-Wall -g -fno-rtti -fno-exceptions")
|
||||
endif ()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": 2,
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "linux",
|
||||
|
|
@ -11,16 +11,83 @@
|
|||
"CMAKE_C_COMPILER": "/usr/bin/clang",
|
||||
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows",
|
||||
"name": "windows-debug",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"binaryDir": "${sourceDir}/build/debug/",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CMAKE_MAKE_PROGRAM": "ninja",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"DXC_SHADER_FLAGS": "-Zi",
|
||||
"GLSLC_SHADER_FLAGS": "-g"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-reldebug",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/reldebug/",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CMAKE_MAKE_PROGRAM": "ninja",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"DXC_SHADER_FLAGS": "-Zi",
|
||||
"GLSLC_SHADER_FLAGS": "-g"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-reldebug-nobreak",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/reldebug/",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CMAKE_MAKE_PROGRAM": "ninja",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"DXC_SHADER_FLAGS": "-Zi",
|
||||
"GLSLC_SHADER_FLAGS": "-g",
|
||||
"MSVC_DEFINES": "ASTER_NO_BREAK"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-release",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build/release/",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CMAKE_MAKE_PROGRAM": "ninja",
|
||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
|
||||
"MSVC_DEFINES": "ASTER_NDEBUG"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ function(add_shader TARGET SHADER)
|
|||
message("Marked as hlsl file. ${current-output-path}")
|
||||
add_custom_command(
|
||||
OUTPUT ${current-output-path}
|
||||
COMMAND Vulkan::dxc_exe -spirv -T "${shader-type}_6_0" -E main ${current-shader-path} -Fo ${current-output-path}
|
||||
COMMAND Vulkan::dxc_exe ${DXC_SHADER_FLAGS} -spirv -T "${shader-type}_6_0" -E main ${current-shader-path} -Fo ${current-output-path}
|
||||
DEPENDS ${current-shader-path}
|
||||
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
||||
VERBATIM)
|
||||
|
|
@ -24,7 +24,7 @@ function(add_shader TARGET SHADER)
|
|||
message("Marked as glsl file. ${current-output-path}")
|
||||
add_custom_command(
|
||||
OUTPUT ${current-output-path}
|
||||
COMMAND Vulkan::glslc -o ${current-output-path} ${current-shader-path}
|
||||
COMMAND Vulkan::glslc ${GLSLC_SHADER_FLAGS} -o ${current-output-path} ${current-shader-path}
|
||||
DEPENDS ${current-shader-path}
|
||||
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
||||
VERBATIM)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
#define EASTL_NO_EXCEPTIONS 1
|
||||
|
||||
#if defined(NDEBUG)
|
||||
#if defined(ASTER_NDEBUG)
|
||||
#define USE_OPTICK (0)
|
||||
#else
|
||||
#define USE_OPTICK (1)
|
||||
|
|
|
|||
|
|
@ -21,12 +21,18 @@ struct Context final
|
|||
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
||||
|
||||
// Ctor/Dtor
|
||||
Context(cstr appName, Version version, bool enableValidation = true);
|
||||
Context(cstr appName, Version version, bool enableValidation = ENABLE_VALIDATION_DEFAULT_VALUE);
|
||||
~Context();
|
||||
|
||||
// Move
|
||||
Context(Context &&other) noexcept;
|
||||
Context &operator=(Context &&other) noexcept;
|
||||
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = true;
|
||||
#else
|
||||
constexpr static bool ENABLE_VALIDATION_DEFAULT_VALUE = false;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Context);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
|
|||
NameString &&name)
|
||||
: m_Name(std::move(name))
|
||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
||||
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
|
||||
{
|
||||
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
||||
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct Device final
|
|||
vk::Device m_Device = nullptr;
|
||||
VmaAllocator m_Allocator = nullptr;
|
||||
vk::PipelineCache m_PipelineCache = nullptr;
|
||||
bool m_ValidationEnabled = true;
|
||||
|
||||
template <typename T>
|
||||
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const;
|
||||
|
|
@ -54,8 +55,9 @@ template <typename T>
|
|||
requires vk::isVulkanHandleType<T>::value void
|
||||
Device::SetName(const T &object, cstr name) const
|
||||
{
|
||||
if (!name)
|
||||
if (!m_ValidationEnabled || !name || !object)
|
||||
return;
|
||||
|
||||
auto handle = Recast<u64>(Cast<typename T::NativeType>(object));
|
||||
const vk::DebugUtilsObjectNameInfoEXT objectNameInfo = {
|
||||
.objectType = object.objectType,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@
|
|||
#undef min
|
||||
#endif
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
||||
#endif
|
||||
#include <EASTL/fixed_string.h>
|
||||
#include <EASTL/string.h>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
|
@ -51,7 +53,7 @@ Failed(const vk::Result result)
|
|||
using NameString = eastl::fixed_string<char, 32, false>;
|
||||
|
||||
template <typename TFlagBits>
|
||||
struct std::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
|
||||
struct eastl::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
|
||||
{
|
||||
[[nodiscard]] usize
|
||||
operator()(const vk::Flags<TFlagBits> &val)
|
||||
|
|
@ -64,7 +66,7 @@ template <typename T>
|
|||
[[nodiscard]] usize
|
||||
HashAny(const T &val)
|
||||
{
|
||||
return std::hash<std::remove_cvref_t<T>>()(val);
|
||||
return eastl::hash<std::remove_cvref_t<T>>()(val);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline usize
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ void
|
|||
Image::Destroy(const Device *device)
|
||||
{
|
||||
if (!IsValid() || !IsOwned())
|
||||
{
|
||||
m_MipLevels_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
device->m_Device.destroy(m_View, nullptr);
|
||||
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
|
||||
|
|
@ -280,7 +283,6 @@ void
|
|||
StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled,
|
||||
cstr name)
|
||||
{
|
||||
|
||||
// Reasoning:
|
||||
// Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
|
||||
// results.
|
||||
|
|
@ -342,3 +344,72 @@ StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format
|
|||
|
||||
device->SetName(m_Image, name);
|
||||
}
|
||||
|
||||
void
|
||||
StorageTextureCube::Init(const Device* device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped, cstr name)
|
||||
{
|
||||
// Reasoning:
|
||||
// Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
|
||||
// results.
|
||||
auto usage =
|
||||
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
||||
if (isSampled)
|
||||
{
|
||||
WARN_IF(!IsPowerOfTwo(cubeSide), "Image {1} is {0}x{0} (Non Power of Two)",
|
||||
cubeSide, name ? name : "<unnamed>");
|
||||
usage |= vk::ImageUsageFlagBits::eSampled;
|
||||
}
|
||||
|
||||
const u32 mipLevels = isMipMapped ? 1 + Cast<u32>(floor(log2(cubeSide))) : 1;
|
||||
assert(mipLevels <= MIP_MASK);
|
||||
|
||||
vk::ImageCreateInfo imageCreateInfo = {
|
||||
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
||||
.imageType = vk::ImageType::e2D,
|
||||
.format = imageFormat,
|
||||
.extent = {cubeSide, cubeSide, 1},
|
||||
.mipLevels = mipLevels,
|
||||
.arrayLayers = 6,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.tiling = vk::ImageTiling::eOptimal,
|
||||
.usage = usage,
|
||||
.sharingMode = vk::SharingMode::eExclusive,
|
||||
.initialLayout = vk::ImageLayout::eUndefined,
|
||||
};
|
||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||
.flags = {},
|
||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
||||
};
|
||||
|
||||
VkImage image;
|
||||
VmaAllocation allocation;
|
||||
auto result = Cast<vk::Result>(vmaCreateImage(device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||
|
||||
vk::ImageView view;
|
||||
const vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||
.image = image,
|
||||
.viewType = vk::ImageViewType::eCube,
|
||||
.format = imageFormat,
|
||||
.components = {},
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = mipLevels,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 6,
|
||||
},
|
||||
};
|
||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||
|
||||
m_Image = image;
|
||||
m_View = view;
|
||||
m_Allocation = allocation;
|
||||
m_Extent = imageCreateInfo.extent;
|
||||
m_MipLevels_ = mipLevels | OWNED_BIT | VALID_BIT;
|
||||
|
||||
device->SetName(m_Image, name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,27 +59,45 @@ struct Texture : Image
|
|||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(Texture) == sizeof(Image));
|
||||
|
||||
struct TextureCube : Texture
|
||||
{
|
||||
void
|
||||
Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped = false, cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(TextureCube) == sizeof(Image));
|
||||
|
||||
struct AttachmentImage : Image
|
||||
{
|
||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(AttachmentImage) == sizeof(Image));
|
||||
|
||||
struct DepthImage : Image
|
||||
{
|
||||
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(DepthImage) == sizeof(Image));
|
||||
|
||||
struct StorageTexture : Texture
|
||||
{
|
||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isSampled, cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(StorageTexture) == sizeof(Image));
|
||||
|
||||
struct StorageTextureCube : StorageTexture
|
||||
{
|
||||
void Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped = false,
|
||||
cstr name = nullptr);
|
||||
};
|
||||
|
||||
static_assert(sizeof(StorageTextureCube) == sizeof(Image));
|
||||
|
||||
inline bool
|
||||
Image::IsValid() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,6 +15,5 @@ void
|
|||
AssertionFailure(const char *af)
|
||||
{
|
||||
ERROR("{}", af);
|
||||
debug_break();
|
||||
}
|
||||
} // namespace eastl
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@ struct Logger
|
|||
fmt::println("{}{} {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), message.data(),
|
||||
ansi_color::Black, loc, line, ansi_color::Reset);
|
||||
}
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG) && !defined(ASTER_NO_BREAK)
|
||||
if constexpr (TLogLevel == LogType::eError)
|
||||
{
|
||||
debug_break();
|
||||
}
|
||||
#endif // !defined(NDEBUG)
|
||||
#endif
|
||||
}
|
||||
|
||||
template <LogType TLogLevel>
|
||||
|
|
@ -86,12 +86,12 @@ struct Logger
|
|||
fmt::println("{}{} ({}) {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), exprStr,
|
||||
message.data(), ansi_color::Black, loc, line, ansi_color::Reset);
|
||||
}
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG) && !defined(ASTER_NO_BREAK)
|
||||
if constexpr (TLogLevel == LogType::eError)
|
||||
{
|
||||
debug_break();
|
||||
}
|
||||
#endif // !defined(NDEBUG)
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ extern Logger g_Logger;
|
|||
; \
|
||||
else g_Logger.Log<Logger::LogType::eInfo>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||
|
||||
#if !defined(DEBUG_LOG_DISABLED) && !defined(NDEBUG)
|
||||
#if !defined(DEBUG_LOG_DISABLED) && !defined(ASTER_NDEBUG)
|
||||
|
||||
#define DEBUG(...) g_Logger.Log<Logger::LogType::eDebug>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||
#define DEBUG_IF(expr, ...) \
|
||||
|
|
@ -151,24 +151,26 @@ extern Logger g_Logger;
|
|||
|
||||
#else // !defined(DEBUG_LOG_DISABLED)
|
||||
|
||||
#define DEBUG(msg) \
|
||||
#define DEBUG(...) \
|
||||
{ \
|
||||
}
|
||||
#define DEBUG_IF(expr, msg) \
|
||||
if (expr) \
|
||||
(void)msg
|
||||
#define ELSE_IF_DEBUG(expr, msg) \
|
||||
#define DEBUG_IF(expr, ...) \
|
||||
if (false) \
|
||||
{ \
|
||||
}
|
||||
#define ELSE_IF_DEBUG(expr, ...) \
|
||||
; \
|
||||
if (expr) \
|
||||
(void)msg
|
||||
#define ELSE_DEBUG(msg) \
|
||||
if (false) \
|
||||
{ \
|
||||
}
|
||||
#define ELSE_DEBUG(...) \
|
||||
; \
|
||||
{ \
|
||||
}
|
||||
|
||||
#endif // !defined(DEBUG_LOG_DISABLED)
|
||||
|
||||
#if !defined(VERBOSE_LOG_DISABLED) && !defined(NDEBUG)
|
||||
#if !defined(VERBOSE_LOG_DISABLED) && !defined(ASTER_NDEBUG)
|
||||
|
||||
#define VERBOSE(...) g_Logger.Log<Logger::LogType::eVerbose>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||
#define VERBOSE_IF(expr, ...) \
|
||||
|
|
@ -184,17 +186,20 @@ extern Logger g_Logger;
|
|||
|
||||
#else // !defined(DEBUG_LOG_DISABLED)
|
||||
|
||||
#define VERBOSE(msg) \
|
||||
#define VERBOSE(...) \
|
||||
{ \
|
||||
}
|
||||
#define VERBOSE_IF(expr, msg) \
|
||||
if (expr) \
|
||||
(void)msg
|
||||
#define ELSE_IF_VERBOSE(expr, msg) \
|
||||
#define VERBOSE_IF(expr, ...) \
|
||||
if (false) \
|
||||
{ \
|
||||
} \
|
||||
}
|
||||
#define ELSE_IF_VERBOSE(expr, ...) \
|
||||
; \
|
||||
if (expr) \
|
||||
(void)msg
|
||||
#define ELSE_VERBOSE(msg) \
|
||||
if (false) \
|
||||
{ \
|
||||
}
|
||||
#define ELSE_VERBOSE(...) \
|
||||
; \
|
||||
{ \
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ TextureManager::Init(const u32 maxCapacity)
|
|||
TextureHandle
|
||||
TextureManager::Commit(Texture *texture)
|
||||
{
|
||||
ERROR_IF(!texture || !texture->IsValid() || !texture->IsOwned(), "Texture must be valid and owned for commital")
|
||||
ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for commital")
|
||||
THEN_ABORT(-1);
|
||||
|
||||
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)
|
||||
|
|
@ -193,6 +193,79 @@ StorageTextureManager::Release(const Device *device, const StorageTextureHandle
|
|||
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();
|
||||
}
|
||||
|
||||
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info)
|
||||
: uBufferInfo(info)
|
||||
{
|
||||
|
|
@ -230,7 +303,7 @@ GpuResourceManager::Commit(StorageBuffer *storageBuffer)
|
|||
|
||||
m_WriteOwner.emplace_back(HandleType::eBuffer, handle.m_Index);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
++m_CommitedBufferCount;
|
||||
#endif
|
||||
|
||||
|
|
@ -276,7 +349,7 @@ GpuResourceManager::Release(BufferHandle handle)
|
|||
|
||||
m_BufferManager.Release(m_Device, handle);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
--m_CommitedBufferCount;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -304,7 +377,7 @@ GpuResourceManager::Release(TextureHandle handle)
|
|||
|
||||
m_TextureManager.Release(m_Device, handle);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
--m_CommitedTextureCount;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -323,12 +396,14 @@ GpuResourceManager::Release(Texture *texture, TextureHandle handle)
|
|||
}
|
||||
|
||||
TextureHandle
|
||||
GpuResourceManager::Commit(Texture *texture)
|
||||
GpuResourceManager::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 = nullptr,
|
||||
.sampler = samplerImpl,
|
||||
.imageView = texture->m_View,
|
||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
});
|
||||
|
|
@ -344,7 +419,7 @@ GpuResourceManager::Commit(Texture *texture)
|
|||
|
||||
m_WriteOwner.emplace_back(HandleType::eTexture, handle.m_Index);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
++m_CommitedTextureCount;
|
||||
#endif
|
||||
|
||||
|
|
@ -352,12 +427,14 @@ GpuResourceManager::Commit(Texture *texture)
|
|||
}
|
||||
|
||||
StorageTextureHandle
|
||||
GpuResourceManager::Commit(StorageTexture *storageTexture)
|
||||
GpuResourceManager::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 = nullptr,
|
||||
.sampler = samplerImpl,
|
||||
.imageView = storageTexture->m_View,
|
||||
.imageLayout = vk::ImageLayout::eGeneral,
|
||||
});
|
||||
|
|
@ -373,7 +450,7 @@ GpuResourceManager::Commit(StorageTexture *storageTexture)
|
|||
|
||||
m_WriteOwner.emplace_back(HandleType::eStorageTexture, handle.m_Index);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
++m_CommitedStorageTextureCount;
|
||||
#endif
|
||||
|
||||
|
|
@ -390,7 +467,7 @@ GpuResourceManager::Release(StorageTextureHandle handle)
|
|||
|
||||
m_StorageTextureManager.Release(m_Device, handle);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
--m_CommitedStorageTextureCount;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -432,8 +509,16 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|||
u32 storageTexturesCount =
|
||||
eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize));
|
||||
|
||||
// TODO: Switch to bindless samplers / multiple sampler configurations
|
||||
vk::SamplerCreateInfo samplerCreateInfo = {
|
||||
INFO("Max Buffer Count: {}", buffersCount);
|
||||
INFO("Max Texture Count: {}", texturesCount);
|
||||
INFO("Max Storage Texture Count: {}", storageTexturesCount);
|
||||
|
||||
m_BufferManager.Init(buffersCount);
|
||||
m_TextureManager.Init(texturesCount);
|
||||
m_StorageTextureManager.Init(storageTexturesCount);
|
||||
m_SamplerManager.Init(storageTexturesCount);
|
||||
|
||||
m_DefaultSamplerCreateInfo = {
|
||||
.magFilter = vk::Filter::eLinear,
|
||||
.minFilter = vk::Filter::eLinear,
|
||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
||||
|
|
@ -449,15 +534,8 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|||
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
|
||||
.unnormalizedCoordinates = false,
|
||||
};
|
||||
AbortIfFailed(device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_ImmutableSampler));
|
||||
|
||||
INFO("Max Buffer Count: {}", buffersCount);
|
||||
INFO("Max Texture Count: {}", texturesCount);
|
||||
INFO("Max Storage Texture Count: {}", storageTexturesCount);
|
||||
|
||||
m_BufferManager.Init(buffersCount);
|
||||
m_TextureManager.Init(texturesCount);
|
||||
m_StorageTextureManager.Init(storageTexturesCount);
|
||||
m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo));
|
||||
|
||||
eastl::array poolSizes = {
|
||||
vk::DescriptorPoolSize{
|
||||
|
|
@ -495,7 +573,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|||
.pBindingFlags = layoutBindingFlags.data(),
|
||||
};
|
||||
|
||||
eastl::vector immutableSamplers(texturesCount, m_ImmutableSampler);
|
||||
eastl::array descriptorLayoutBindings = {
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = BUFFER_BINDING_INDEX,
|
||||
|
|
@ -508,7 +585,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||
.descriptorCount = Cast<u32>(texturesCount),
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.pImmutableSamplers = immutableSamplers.data(),
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = STORAGE_TEXTURE_BINDING_INDEX,
|
||||
|
|
@ -543,7 +619,7 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|||
|
||||
GpuResourceManager::~GpuResourceManager()
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
#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);
|
||||
|
|
@ -552,7 +628,7 @@ GpuResourceManager::~GpuResourceManager()
|
|||
m_BufferManager.Destroy(m_Device);
|
||||
m_TextureManager.Destroy(m_Device);
|
||||
m_StorageTextureManager.Destroy(m_Device);
|
||||
m_Device->m_Device.destroy(m_ImmutableSampler, nullptr);
|
||||
m_SamplerManager.Destroy(m_Device);
|
||||
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
|
||||
m_Device->m_Device.destroy(m_SetLayout, nullptr);
|
||||
}
|
||||
|
|
@ -561,16 +637,19 @@ GpuResourceManager::GpuResourceManager(GpuResourceManager &&other) noexcept
|
|||
: m_WriteInfos(std::move(other.m_WriteInfos))
|
||||
, m_Writes(std::move(other.m_Writes))
|
||||
, m_WriteOwner(std::move(other.m_WriteOwner))
|
||||
, m_ImmutableSampler(other.m_ImmutableSampler)
|
||||
, m_BufferManager(std::move(other.m_BufferManager))
|
||||
, m_TextureManager(std::move(other.m_TextureManager))
|
||||
, m_StorageTextureManager(std::move(other.m_StorageTextureManager))
|
||||
, m_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)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
, m_CommitedBufferCount(other.m_CommitedBufferCount)
|
||||
, m_CommitedTextureCount(other.m_CommitedTextureCount)
|
||||
, m_CommitedStorageTextureCount(other.m_CommitedStorageTextureCount)
|
||||
#endif
|
||||
{
|
||||
assert(!other.m_Device);
|
||||
}
|
||||
|
|
@ -583,17 +662,26 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept
|
|||
m_WriteInfos = std::move(other.m_WriteInfos);
|
||||
m_Writes = std::move(other.m_Writes);
|
||||
m_WriteOwner = std::move(other.m_WriteOwner);
|
||||
m_ImmutableSampler = other.m_ImmutableSampler;
|
||||
m_BufferManager = std::move(other.m_BufferManager);
|
||||
m_TextureManager = std::move(other.m_TextureManager);
|
||||
m_StorageTextureManager = std::move(other.m_StorageTextureManager);
|
||||
m_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;
|
||||
#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
|
||||
GpuResourceManager::CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo)
|
||||
{
|
||||
return m_SamplerManager.Create(m_Device, samplerCreateInfo);
|
||||
}
|
||||
|
|
@ -39,6 +39,10 @@ struct StorageTextureHandle : GpuResourceHandle
|
|||
{
|
||||
};
|
||||
|
||||
struct SamplerHandle : GpuResourceHandle
|
||||
{
|
||||
};
|
||||
|
||||
struct TextureManager
|
||||
{
|
||||
eastl::vector<Texture> m_Textures;
|
||||
|
|
@ -72,6 +76,18 @@ struct StorageTextureManager : TextureManager
|
|||
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 GpuResourceManager
|
||||
{
|
||||
private:
|
||||
|
|
@ -102,11 +118,12 @@ struct GpuResourceManager
|
|||
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
||||
eastl::vector<WriteOwner> m_WriteOwner;
|
||||
|
||||
vk::Sampler m_ImmutableSampler;
|
||||
vk::Sampler m_DefaultSampler;
|
||||
|
||||
BufferManager m_BufferManager;
|
||||
TextureManager m_TextureManager;
|
||||
StorageTextureManager m_StorageTextureManager;
|
||||
SamplerManager m_SamplerManager;
|
||||
|
||||
void EraseWrites(u32 handleIndex, HandleType handleType);
|
||||
|
||||
|
|
@ -117,6 +134,8 @@ struct GpuResourceManager
|
|||
constexpr static u32 TEXTURE_BINDING_INDEX = 1;
|
||||
constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2;
|
||||
|
||||
vk::SamplerCreateInfo m_DefaultSamplerCreateInfo;
|
||||
|
||||
vk::DescriptorPool m_DescriptorPool;
|
||||
vk::DescriptorSetLayout m_SetLayout;
|
||||
vk::DescriptorSet m_DescriptorSet;
|
||||
|
|
@ -126,13 +145,16 @@ struct GpuResourceManager
|
|||
void Release(BufferHandle handle); // Release and Destroy
|
||||
void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return
|
||||
|
||||
TextureHandle Commit(Texture *texture); // Commit to GPU and take Ownership
|
||||
void Release(TextureHandle handle); // Release and Destroy
|
||||
void Release(Texture *texture, TextureHandle handle); // Release and Return
|
||||
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 Commit(StorageTexture *storageTexture);
|
||||
void Release(StorageTextureHandle handle);
|
||||
void Release(StorageTexture *texture, StorageTextureHandle handle);
|
||||
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);
|
||||
|
||||
void Update(); // Update all the descriptors required.
|
||||
|
||||
|
|
@ -143,7 +165,7 @@ struct GpuResourceManager
|
|||
GpuResourceManager(GpuResourceManager &&other) noexcept;
|
||||
GpuResourceManager &operator=(GpuResourceManager &&other) noexcept;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
usize m_CommitedBufferCount = 0;
|
||||
usize m_CommitedTextureCount = 0;
|
||||
usize m_CommitedStorageTextureCount = 0;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk:
|
|||
{
|
||||
// OPTICK_EVENT();
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
constexpr vk::DebugUtilsLabelEXT label = {
|
||||
.pLabelName = "UI pass",
|
||||
.color = std::array{0.9f, 0.9f, 1.0f, 1.0f},
|
||||
|
|
@ -195,7 +195,7 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk:
|
|||
|
||||
commandBuffer.endRendering();
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
commandBuffer.endDebugUtilsLabelEXT();
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,12 @@ add_executable(model_render model_render.cpp
|
|||
|
||||
add_shader(model_render shader/model.vs.hlsl)
|
||||
add_shader(model_render shader/model.ps.hlsl)
|
||||
add_shader(model_render shader/eqrectToCube.cs.hlsl)
|
||||
add_shader(model_render shader/eqrect_to_cube.cs.hlsl)
|
||||
add_shader(model_render shader/background.vs.hlsl)
|
||||
add_shader(model_render shader/background.ps.hlsl)
|
||||
add_shader(model_render shader/diffuse_irradiance.cs.hlsl)
|
||||
add_shader(model_render shader/prefilter.cs.hlsl)
|
||||
add_shader(model_render shader/brdf_lut.cs.hlsl)
|
||||
|
||||
target_link_libraries(model_render PRIVATE aster_core)
|
||||
target_link_libraries(model_render PRIVATE util_helper)
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@
|
|||
|
||||
#include "asset_loader.h"
|
||||
|
||||
#include "EASTL/fixed_vector.h"
|
||||
#include "buffer.h"
|
||||
#include "device.h"
|
||||
#include "gpu_resource_manager.h"
|
||||
#include "helpers.h"
|
||||
#include "image.h"
|
||||
|
||||
#include <EASTL/array.h>
|
||||
#include <EASTL/hash_map.h>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
|||
|
||||
AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
StackString<128> loadActionName = "Load: ";
|
||||
loadActionName += name ? name : path;
|
||||
vk::DebugUtilsLabelEXT debugLabel = {
|
||||
|
|
@ -165,7 +165,7 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
|||
m_CommandBuffer.copyBufferToImage2(&stagingInfo);
|
||||
m_CommandBuffer.pipelineBarrier2(&postStagingDependency);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
||||
#endif
|
||||
|
||||
|
|
@ -190,6 +190,176 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
|||
stagingBuffer.Destroy(pDevice);
|
||||
}
|
||||
|
||||
void
|
||||
GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
|
||||
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage)
|
||||
{
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
vk::DebugUtilsLabelEXT label = {
|
||||
.pLabelName = "Generate Mipmap",
|
||||
.color = std::array{0.9f, 0.9f, 0.9f, 1.0f},
|
||||
};
|
||||
commandBuffer.beginDebugUtilsLabelEXT(&label);
|
||||
#endif
|
||||
|
||||
vk::ImageMemoryBarrier2 imageStartBarrier = {
|
||||
.srcStageMask = prevStage,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||
.oldLayout = initialLayout,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = texture->m_Image,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
vk::ImageMemoryBarrier2 mipsStartBarrier = imageStartBarrier;
|
||||
mipsStartBarrier.dstAccessMask = vk::AccessFlagBits2::eTransferWrite;
|
||||
mipsStartBarrier.oldLayout = vk::ImageLayout::eUndefined;
|
||||
mipsStartBarrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
mipsStartBarrier.subresourceRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 1,
|
||||
.levelCount = texture->GetMipLevels() - 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
eastl::fixed_vector<vk::ImageMemoryBarrier2, 2> startBarriers = {
|
||||
mipsStartBarrier,
|
||||
};
|
||||
if (initialLayout != imageStartBarrier.newLayout)
|
||||
{
|
||||
startBarriers.push_back(imageStartBarrier);
|
||||
}
|
||||
|
||||
vk::DependencyInfo imageStartDependency = {
|
||||
.imageMemoryBarrierCount = Cast<u32>(startBarriers.size()),
|
||||
.pImageMemoryBarriers = startBarriers.data(),
|
||||
};
|
||||
|
||||
vk::ImageMemoryBarrier2 nextMipBarrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = texture->m_Image,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
vk::DependencyInfo interMipDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &nextMipBarrier,
|
||||
};
|
||||
|
||||
vk::ImageMemoryBarrier2 imageReadyBarrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = finalStage,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.newLayout = finalLayout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = texture->m_Image,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = texture->GetMipLevels(),
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
vk::DependencyInfo imageReadyDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &imageReadyBarrier,
|
||||
};
|
||||
|
||||
vk::ImageBlit2 blitRegion = {
|
||||
.srcSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
vk::BlitImageInfo2 mipBlitInfo = {
|
||||
.srcImage = texture->m_Image,
|
||||
.srcImageLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.dstImage = texture->m_Image,
|
||||
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.regionCount = 1,
|
||||
.pRegions = &blitRegion,
|
||||
.filter = vk::Filter::eLinear,
|
||||
};
|
||||
|
||||
auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); };
|
||||
|
||||
// Mip Mapping
|
||||
|
||||
commandBuffer.pipelineBarrier2(&imageStartDependency);
|
||||
|
||||
i32 prevMipWidth = Cast<i32>(texture->m_Extent.width);
|
||||
i32 prevMipHeight = Cast<i32>(texture->m_Extent.height);
|
||||
|
||||
u32 maxPrevMip = texture->GetMipLevels() - 1;
|
||||
for (u32 prevMipLevel = 0; prevMipLevel < maxPrevMip; ++prevMipLevel)
|
||||
{
|
||||
i32 currentMipWidth = calcNextMip(prevMipWidth);
|
||||
i32 currentMipHeight = calcNextMip(prevMipHeight);
|
||||
u32 currentMipLevel = prevMipLevel + 1;
|
||||
|
||||
blitRegion.srcSubresource.mipLevel = prevMipLevel;
|
||||
blitRegion.srcOffsets = std::array{
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{prevMipWidth, prevMipHeight, 1},
|
||||
};
|
||||
blitRegion.dstSubresource.mipLevel = currentMipLevel;
|
||||
blitRegion.dstOffsets = std::array{
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{currentMipWidth, currentMipHeight, 1},
|
||||
};
|
||||
|
||||
nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel;
|
||||
|
||||
commandBuffer.blitImage2(&mipBlitInfo);
|
||||
commandBuffer.pipelineBarrier2(&interMipDependency);
|
||||
|
||||
prevMipHeight = currentMipHeight;
|
||||
prevMipWidth = currentMipWidth;
|
||||
}
|
||||
|
||||
commandBuffer.pipelineBarrier2(&imageReadyDependency);
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
commandBuffer.endDebugUtilsLabelEXT();
|
||||
#endif
|
||||
}
|
||||
|
||||
TextureHandle
|
||||
AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const
|
||||
{
|
||||
|
|
@ -209,7 +379,7 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
|||
stagingBuffer->Init(m_ResourceManager->m_Device, byteSize);
|
||||
stagingBuffer->Write(m_ResourceManager->m_Device, 0, byteSize, image->image.data());
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
StackString<128> loadActionName = "Load: ";
|
||||
loadActionName += image->name.empty() ? "<texture>" : image->name.c_str();
|
||||
vk::DebugUtilsLabelEXT debugLabel = {
|
||||
|
|
@ -235,7 +405,7 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
|||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = texture.GetMipLevels(),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
|
|
@ -247,15 +417,13 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
|||
.pImageMemoryBarriers = &imageStartBarrier,
|
||||
};
|
||||
|
||||
vk::ImageMemoryBarrier2 nextMipBarrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||
vk::ImageMemoryBarrier2 postStagingBarrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.srcQueueFamilyIndex = m_TransferQueueIndex,
|
||||
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
|
||||
.image = texture.m_Image,
|
||||
.subresourceRange =
|
||||
{
|
||||
|
|
@ -266,33 +434,11 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
|||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
vk::DependencyInfo interMipDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &nextMipBarrier,
|
||||
};
|
||||
;
|
||||
|
||||
vk::ImageMemoryBarrier2 imageReadyBarrier = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
.srcQueueFamilyIndex = m_TransferQueueIndex,
|
||||
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
|
||||
.image = texture.m_Image,
|
||||
.subresourceRange =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = texture.GetMipLevels(),
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
vk::DependencyInfo imageReadyDependency = {
|
||||
vk::DependencyInfo postStagingDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &imageReadyBarrier,
|
||||
.pImageMemoryBarriers = &postStagingBarrier,
|
||||
};
|
||||
|
||||
vk::BufferImageCopy2 imageCopy = {
|
||||
|
|
@ -317,78 +463,20 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
|||
.pRegions = &imageCopy,
|
||||
};
|
||||
|
||||
vk::ImageBlit2 blitRegion = {
|
||||
.srcSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
|
||||
vk::BlitImageInfo2 mipBlitInfo = {
|
||||
.srcImage = texture.m_Image,
|
||||
.srcImageLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.dstImage = texture.m_Image,
|
||||
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.regionCount = 1,
|
||||
.pRegions = &blitRegion,
|
||||
.filter = vk::Filter::eLinear,
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
m_CommandBuffer.pipelineBarrier2(&imageStartDependency);
|
||||
m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo);
|
||||
m_CommandBuffer.pipelineBarrier2(&interMipDependency);
|
||||
m_CommandBuffer.pipelineBarrier2(&postStagingDependency);
|
||||
|
||||
auto calcNextMip = [](i32 prev) { return eastl::max(prev / 2, 1); };
|
||||
GenerateMipMaps(m_CommandBuffer, &texture, vk::ImageLayout::eTransferSrcOptimal,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||
|
||||
// Mip Mapping
|
||||
|
||||
i32 prevMipWidth = Cast<i32>(texture.m_Extent.width);
|
||||
i32 prevMipHeight = Cast<i32>(texture.m_Extent.height);
|
||||
|
||||
u32 maxPrevMip = texture.GetMipLevels() - 1;
|
||||
for (u32 prevMipLevel = 0; prevMipLevel < maxPrevMip; ++prevMipLevel)
|
||||
{
|
||||
i32 currentMipWidth = calcNextMip(prevMipWidth);
|
||||
i32 currentMipHeight = calcNextMip(prevMipHeight);
|
||||
u32 currentMipLevel = prevMipLevel + 1;
|
||||
|
||||
blitRegion.srcSubresource.mipLevel = prevMipLevel;
|
||||
blitRegion.srcOffsets = std::array{
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{prevMipWidth, prevMipHeight, 1},
|
||||
};
|
||||
blitRegion.dstSubresource.mipLevel = currentMipLevel;
|
||||
blitRegion.dstOffsets = std::array{
|
||||
vk::Offset3D{0, 0, 0},
|
||||
vk::Offset3D{currentMipWidth, currentMipHeight, 1},
|
||||
};
|
||||
|
||||
nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel;
|
||||
|
||||
m_CommandBuffer.blitImage2(&mipBlitInfo);
|
||||
m_CommandBuffer.pipelineBarrier2(&interMipDependency);
|
||||
|
||||
prevMipHeight = currentMipHeight;
|
||||
prevMipWidth = currentMipWidth;
|
||||
}
|
||||
|
||||
m_CommandBuffer.pipelineBarrier2(&imageReadyDependency);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
||||
#endif
|
||||
|
||||
return m_ResourceManager->Commit(&texture);
|
||||
return m_ResourceManager->CommitTexture(&texture);
|
||||
}
|
||||
|
||||
Model
|
||||
|
|
@ -425,7 +513,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
|||
|
||||
AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
StackString<128> loadActionName = "Load: ";
|
||||
loadActionName += name ? name : path;
|
||||
vk::DebugUtilsLabelEXT debugLabel = {
|
||||
|
|
@ -808,7 +896,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
|||
|
||||
#pragma endregion
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
||||
#endif
|
||||
AbortIfFailed(m_CommandBuffer.end());
|
||||
|
|
|
|||
|
|
@ -118,3 +118,8 @@ struct AssetLoader
|
|||
|
||||
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);
|
||||
|
|
@ -5,36 +5,86 @@
|
|||
|
||||
#include "ibl_helpers.h"
|
||||
|
||||
#include "EASTL/fixed_vector.h"
|
||||
#include "EASTL/tuple.h"
|
||||
#include "asset_loader.h"
|
||||
#include "device.h"
|
||||
#include "gpu_resource_manager.h"
|
||||
#include "helpers.h"
|
||||
#include "image.h"
|
||||
#include "asset_loader.h"
|
||||
#include "pipeline_utils.h"
|
||||
|
||||
constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrectToCube.cs.hlsl.spv";
|
||||
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";
|
||||
|
||||
TextureHandle
|
||||
void
|
||||
Environment::Destroy(GpuResourceManager *resourceManager)
|
||||
{
|
||||
resourceManager->Release(Take(m_Skybox));
|
||||
resourceManager->Release(Take(m_Diffuse));
|
||||
resourceManager->Release(Take(m_Prefilter));
|
||||
resourceManager->Release(Take(m_BrdfLut));
|
||||
}
|
||||
|
||||
Environment
|
||||
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv,
|
||||
const cstr name)
|
||||
{
|
||||
GpuResourceManager *resMan = assetLoader->m_ResourceManager;
|
||||
const Device *pDevice = resMan->m_Device;
|
||||
|
||||
TextureCube cubeMap;
|
||||
cubeMap.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, false, name ? name : "Env CubeMap");
|
||||
vk::SamplerCreateInfo brdfLutSamplerCreateInfo = resMan->m_DefaultSamplerCreateInfo;
|
||||
brdfLutSamplerCreateInfo.addressModeU = vk::SamplerAddressMode::eClampToEdge;
|
||||
brdfLutSamplerCreateInfo.addressModeV = vk::SamplerAddressMode::eClampToEdge;
|
||||
brdfLutSamplerCreateInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge;
|
||||
|
||||
StorageTexture stagingTexture;
|
||||
stagingTexture.Init(pDevice, {cubeSide * 3, cubeSide * 2}, vk::Format::eR16G16B16A16Sfloat, false, "EnvStaging");
|
||||
auto envStagingHandle = resMan->Commit(&stagingTexture);
|
||||
StorageTextureCube skybox;
|
||||
StorageTextureCube diffuseIrradiance;
|
||||
StorageTextureCube prefilterCube;
|
||||
StorageTexture brdfLut;
|
||||
SamplerHandle brdfLutSampler;
|
||||
|
||||
vk::ImageSubresourceRange stagingSubresRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
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,
|
||||
|
|
@ -42,8 +92,15 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
.baseArrayLayer = 0,
|
||||
.layerCount = 6,
|
||||
};
|
||||
vk::ImageSubresourceRange lutSubresRange = {
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
vk::ImageMemoryBarrier2 readyToWriteBarrier = {
|
||||
vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
|
|
@ -52,117 +109,94 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
.newLayout = vk::ImageLayout::eGeneral,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = stagingTexture.m_Image,
|
||||
.subresourceRange = stagingSubresRange,
|
||||
};
|
||||
vk::DependencyInfo readyToWriteDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &readyToWriteBarrier,
|
||||
};
|
||||
vk::ImageMemoryBarrier2 stagingReadyForTransfer = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = stagingTexture.m_Image,
|
||||
.subresourceRange = stagingSubresRange,
|
||||
};
|
||||
vk::ImageMemoryBarrier2 cubeReadyForTransfer = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = cubeMap.m_Image,
|
||||
.subresourceRange = cubeSubresRange,
|
||||
};
|
||||
eastl::array transferBarriers = {stagingReadyForTransfer, cubeReadyForTransfer};
|
||||
vk::DependencyInfo preTransferDependency = {
|
||||
.imageMemoryBarrierCount = transferBarriers.size(),
|
||||
.pImageMemoryBarriers = transferBarriers.data(),
|
||||
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 cubemapToRead = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderSampledRead,
|
||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
|
||||
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,
|
||||
.image = cubeMap.m_Image,
|
||||
.subresourceRange = cubeSubresRange,
|
||||
};
|
||||
vk::DependencyInfo cubemapToReadDependency = {
|
||||
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 = &cubemapToRead,
|
||||
.pImageMemoryBarriers = &skyboxToSampleBarrier,
|
||||
};
|
||||
vk::DependencyInfo diffIrrToSampleDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &diffIrrToSampleBarrier,
|
||||
};
|
||||
vk::DependencyInfo prefilterToSampleDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &prefilterToSampleBarrier,
|
||||
};
|
||||
vk::DependencyInfo brdfToSampleDependency = {
|
||||
.imageMemoryBarrierCount = 1,
|
||||
.pImageMemoryBarriers = &brdfToSampleBarrier,
|
||||
};
|
||||
|
||||
eastl::array<vk::ImageCopy2, 6> imageCopies;
|
||||
for (i32 i = 0; i < 6; ++i)
|
||||
{
|
||||
imageCopies[i] = vk::ImageCopy2{
|
||||
.srcSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.srcOffset =
|
||||
{
|
||||
(i % 3) * Cast<i32>(cubeSide),
|
||||
(i / 3) * Cast<i32>(cubeSide),
|
||||
0,
|
||||
},
|
||||
.dstSubresource =
|
||||
{
|
||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||
.mipLevel = 0,
|
||||
.baseArrayLayer = Cast<u32>(i),
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstOffset = {0, 0, 0},
|
||||
.extent = {cubeSide, cubeSide, 1},
|
||||
};
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
vk::CopyImageInfo2 copyFlatToCube = {
|
||||
.srcImage = stagingTexture.m_Image,
|
||||
.srcImageLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||
.dstImage = cubeMap.m_Image,
|
||||
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||
.regionCount = imageCopies.size(),
|
||||
.pRegions = imageCopies.data(),
|
||||
};
|
||||
|
||||
struct WorkloadPushConstants
|
||||
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
|
||||
const auto shaderModule = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE);
|
||||
|
||||
const vk::PipelineShaderStageCreateInfo shaderStageCreateInfo = {
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = shaderModule,
|
||||
.pName = "main",
|
||||
};
|
||||
|
||||
vk::PushConstantRange pcr = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||
.offset = 0,
|
||||
.size = sizeof(WorkloadPushConstants),
|
||||
.size = eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
|
||||
eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))),
|
||||
};
|
||||
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
|
|
@ -174,21 +208,83 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
};
|
||||
AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout));
|
||||
|
||||
const vk::ComputePipelineCreateInfo computePipelineCreateInfo = {
|
||||
.stage = shaderStageCreateInfo,
|
||||
.layout = 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,
|
||||
},
|
||||
};
|
||||
|
||||
vk::Pipeline pipeline;
|
||||
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, 1, &computePipelineCreateInfo,
|
||||
nullptr, &pipeline));
|
||||
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
|
||||
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(),
|
||||
computePipelineCreateInfo.data(), nullptr,
|
||||
pipelines.data()));
|
||||
|
||||
pDevice->m_Device.destroy(shaderModule, nullptr);
|
||||
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
|
||||
|
||||
WorkloadPushConstants pushConstants = {
|
||||
.m_HdrEnvHandle = hdrEnv, .m_OutputTexture = envStagingHandle, .m_CubeSide = cubeSide};
|
||||
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();
|
||||
|
||||
|
|
@ -196,7 +292,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
AbortIfFailed(cmd.begin(&beginInfo));
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
StackString<128> labelName = "Eqrect -> Cubemap: ";
|
||||
labelName += name ? name : "<unknown env>";
|
||||
vk::DebugUtilsLabelEXT label = {
|
||||
|
|
@ -209,17 +305,50 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
cmd.pipelineBarrier2(&readyToWriteDependency);
|
||||
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr);
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
|
||||
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof pushConstants, &pushConstants);
|
||||
cmd.dispatch(cubeSide / 16, cubeSide / 16, 6);
|
||||
cmd.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);
|
||||
|
||||
cmd.pipelineBarrier2(&preTransferDependency);
|
||||
GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral,
|
||||
vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader);
|
||||
|
||||
cmd.copyImage2(©FlatToCube);
|
||||
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(&cubemapToReadDependency);
|
||||
cmd.pipelineBarrier2(&diffIrrToSampleDependency);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
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
|
||||
|
||||
|
|
@ -241,9 +370,26 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
|||
|
||||
AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {}));
|
||||
|
||||
resMan->Release(envStagingHandle);
|
||||
pDevice->m_Device.destroy(pipeline, nullptr);
|
||||
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 resMan->Commit(&cubeMap);
|
||||
return {
|
||||
.m_Skybox = skyboxHandle,
|
||||
.m_Diffuse = diffuseIrradianceHandle,
|
||||
.m_Prefilter = prefilterHandle,
|
||||
.m_BrdfLut = brdfLutHandle,
|
||||
};
|
||||
}
|
||||
|
|
@ -8,8 +8,23 @@
|
|||
#include "global.h"
|
||||
#include "gpu_resource_manager.h"
|
||||
|
||||
#include <EASTL/tuple.h>
|
||||
|
||||
struct Pipeline;
|
||||
struct Texture;
|
||||
struct TextureCube;
|
||||
struct AssetLoader;
|
||||
|
||||
TextureHandle CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, cstr name = nullptr);
|
||||
struct Environment
|
||||
{
|
||||
TextureHandle m_Skybox;
|
||||
TextureHandle m_Diffuse;
|
||||
TextureHandle m_Prefilter;
|
||||
TextureHandle m_BrdfLut;
|
||||
|
||||
void Destroy(GpuResourceManager *resourceManager);
|
||||
};
|
||||
|
||||
Environment
|
||||
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv,
|
||||
cstr name = nullptr);
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -18,10 +18,10 @@
|
|||
#include "helpers.h"
|
||||
#include "light_manager.h"
|
||||
|
||||
#include "asset_loader.h"
|
||||
#include "gpu_resource_manager.h"
|
||||
#include "gui.h"
|
||||
#include "ibl_helpers.h"
|
||||
#include "asset_loader.h"
|
||||
#include "pipeline_utils.h"
|
||||
|
||||
#include <EASTL/array.h>
|
||||
|
|
@ -176,13 +176,13 @@ main(int, char **)
|
|||
LightManager lightManager = LightManager{&resourceManager};
|
||||
|
||||
Model model = assetLoader.LoadModelToGpu(MODEL_FILE);
|
||||
Texture environment;
|
||||
assetLoader.LoadHdrImage(&environment, BACKDROP_FILE);
|
||||
auto envHandle = resourceManager.Commit(&environment);
|
||||
Texture environmentHdri;
|
||||
assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE);
|
||||
auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri);
|
||||
|
||||
TextureHandle texCube = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 1024, envHandle, "Cube Env");
|
||||
auto environment = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env");
|
||||
|
||||
resourceManager.Release(envHandle);
|
||||
resourceManager.Release(envHdriHandle);
|
||||
|
||||
vk::Format attachmentFormat = vk::Format::eR8G8B8A8Srgb;
|
||||
|
||||
|
|
@ -371,6 +371,11 @@ main(int, char **)
|
|||
queueAllocation.m_Family, graphicsQueue);
|
||||
bool rotating = false;
|
||||
bool lockToScreen = true;
|
||||
bool showDiffuse = false;
|
||||
bool useDiffuse = true;
|
||||
bool showPrefilter = false;
|
||||
bool useSpecular = true;
|
||||
|
||||
i32 height = Cast<i32>(internalResolution.height);
|
||||
f32 camPitch = glm::degrees(cameraController.m_Pitch);
|
||||
f32 camYaw = glm::degrees(cameraController.m_Yaw);
|
||||
|
|
@ -445,6 +450,13 @@ main(int, char **)
|
|||
{
|
||||
cameraController.SetPosition(camPosition);
|
||||
}
|
||||
gui::Separator();
|
||||
gui::Text("IBL");
|
||||
gui::Checkbox("Show DiffIrr", &showDiffuse);
|
||||
gui::Checkbox("Use DiffIrr", &useDiffuse);
|
||||
gui::Checkbox("Show Prefilter", &showPrefilter);
|
||||
gui::Checkbox("Use Specular", &useSpecular);
|
||||
gui::Separator();
|
||||
gui::Checkbox("Rotate", &rotating);
|
||||
gui::PopItemWidth();
|
||||
if (gui::Button("Exit"))
|
||||
|
|
@ -546,8 +558,32 @@ main(int, char **)
|
|||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles,
|
||||
&model.m_Handles);
|
||||
pcbOffset += sizeof model.m_Handles;
|
||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube);
|
||||
pcbOffset += sizeof texCube;
|
||||
|
||||
Environment thisFrameEnvBuffers = environment;
|
||||
if (showDiffuse)
|
||||
{
|
||||
thisFrameEnvBuffers.m_Skybox = environment.m_Diffuse;
|
||||
}
|
||||
else if (showPrefilter)
|
||||
{
|
||||
thisFrameEnvBuffers.m_Skybox = environment.m_Prefilter;
|
||||
}
|
||||
|
||||
if (!useDiffuse)
|
||||
{
|
||||
thisFrameEnvBuffers.m_Diffuse = {};
|
||||
}
|
||||
if (!useSpecular)
|
||||
{
|
||||
thisFrameEnvBuffers.m_BrdfLut = {};
|
||||
thisFrameEnvBuffers.m_Prefilter = {};
|
||||
}
|
||||
|
||||
assert(pcbOffset == 16);
|
||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof thisFrameEnvBuffers,
|
||||
&thisFrameEnvBuffers);
|
||||
pcbOffset += sizeof thisFrameEnvBuffers;
|
||||
|
||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof lightManager.m_MetaInfo,
|
||||
&lightManager.m_MetaInfo);
|
||||
pcbOffset += sizeof lightManager.m_MetaInfo;
|
||||
|
|
@ -625,7 +661,7 @@ main(int, char **)
|
|||
|
||||
AbortIfFailed(device.m_Device.waitIdle());
|
||||
|
||||
resourceManager.Release(texCube);
|
||||
environment.Destroy(&resourceManager);
|
||||
|
||||
pipelineCacheData = device.DumpPipelineCache();
|
||||
ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written");
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou
|
|||
vk::PushConstantRange pushConstantRange = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.offset = 0,
|
||||
.size = 40,
|
||||
.size = 52,
|
||||
};
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
||||
|
|
@ -206,7 +206,7 @@ CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, cons
|
|||
vk::PushConstantRange pushConstantRange = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.offset = 0,
|
||||
.size = 40,
|
||||
.size = 52,
|
||||
};
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ struct DirectionalLight
|
|||
|
||||
#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[];
|
||||
|
|
@ -58,7 +60,10 @@ static const float PI = 3.14159265f;
|
|||
[[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);
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
#include "bindless_structs.hlsli"
|
||||
|
||||
struct Block
|
||||
{
|
||||
uint HdrEnvHandle;
|
||||
uint OutputTextureHandle;
|
||||
int CubeSize;
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
Block pcb;
|
||||
|
||||
float2 SampleSphericalMap(float3 v)
|
||||
{
|
||||
const float2 invAtan = float2(0.1591f, 0.3183f);
|
||||
float2 uv = float2(atan2(v.x, v.z), asin(v.y));
|
||||
uv *= invAtan;
|
||||
uv += 0.5;
|
||||
return uv;
|
||||
}
|
||||
|
||||
/*
|
||||
| Axis | Layer | Up |
|
||||
|:----:|:-----:|:--:|
|
||||
| +x | 0 | -y |
|
||||
| -x | 1 | -y |
|
||||
| +y | 2 | +z |
|
||||
| -y | 3 | -z |
|
||||
| -z | 4 | -y |
|
||||
| +z | 5 | -y |
|
||||
*/
|
||||
|
||||
[numthreads(16, 16, 1)]
|
||||
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
|
||||
{
|
||||
float3 LocalDir;
|
||||
float HalfSide = pcb.CubeSize / 2;
|
||||
float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2));
|
||||
|
||||
if (GlobalInvocationID.z < 2)
|
||||
{
|
||||
LocalDir = float3(AxisSign * HalfSide, GlobalInvocationID.y - HalfSide, (GlobalInvocationID.x - HalfSide) * AxisSign);
|
||||
}
|
||||
else if (GlobalInvocationID.z < 4)
|
||||
{
|
||||
LocalDir = float3(GlobalInvocationID.x - HalfSide, -AxisSign * HalfSide, -AxisSign * (GlobalInvocationID.y - HalfSide));
|
||||
}
|
||||
else
|
||||
{
|
||||
LocalDir = float3((GlobalInvocationID.x - HalfSide) * AxisSign, GlobalInvocationID.y - HalfSide, -AxisSign * HalfSide);
|
||||
}
|
||||
|
||||
uint TileX = GlobalInvocationID.z % 3;
|
||||
uint TileY = GlobalInvocationID.z / 3;
|
||||
|
||||
float2 UV = SampleSphericalMap(normalize(LocalDir));
|
||||
StorageTextures[pcb.OutputTextureHandle][GlobalInvocationID.xy + uint2(TileX, TileY) * pcb.CubeSize] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -15,10 +15,16 @@ struct Block
|
|||
uint VertexDataHandle;
|
||||
uint MaterialBufferHandle;
|
||||
uint NodeBufferHandle;
|
||||
|
||||
uint EnvCubeHandle;
|
||||
uint DiffuseIrradianceHandle;
|
||||
uint PrefilterHandle;
|
||||
uint BrdfLutHandle;
|
||||
|
||||
uint LightHandle;
|
||||
uint PointLightIndexer;
|
||||
uint DirectionalLightIndexer;
|
||||
|
||||
int MaterialIdx;
|
||||
uint NodeIdx;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ struct FS_Input
|
|||
|
||||
struct FS_Output
|
||||
{
|
||||
float4 ColorTarget : SV_Target0;
|
||||
float4 ColorTarget : SV_Target0;
|
||||
};
|
||||
|
||||
float4 GetAlbedo(float2 UV, float4 InColor)
|
||||
|
|
@ -21,6 +21,13 @@ float4 GetAlbedo(float2 UV, float4 InColor)
|
|||
return AlbedoFactor * InColor * (AlbedoTexId != INVALID_HANDLE ? Textures[AlbedoTexId].Sample(ImmutableSamplers[AlbedoTexId], UV) : 1.0f.xxxx);
|
||||
}
|
||||
|
||||
float GetOcclusion(float2 UV)
|
||||
{
|
||||
uint OcclusionTexId = MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].OcclusionTex;
|
||||
|
||||
return OcclusionTexId != INVALID_HANDLE ? Textures[OcclusionTexId].Sample(ImmutableSamplers[OcclusionTexId], UV).r : 1.0f;
|
||||
}
|
||||
|
||||
float3 GetEmissive(float2 UV)
|
||||
{
|
||||
float3 EmissionFactor = (float3) MaterialsBuffer[PushConstant.MaterialBufferHandle][PushConstant.MaterialIdx].EmissionFactor;
|
||||
|
|
@ -61,6 +68,35 @@ float2 GetMetalRough(float2 UV)
|
|||
return MetalRoughFactors * (MetalRoughTexId != INVALID_HANDLE ? Textures[MetalRoughTexId].Sample(ImmutableSamplers[MetalRoughTexId], UV).bg : 1.0f.xx); // Metal is B, Rough is G.
|
||||
}
|
||||
|
||||
float3 SampleIrradiance(float3 Direction)
|
||||
{
|
||||
if (PushConstant.DiffuseIrradianceHandle != INVALID_HANDLE)
|
||||
{
|
||||
return TextureCubes[PushConstant.DiffuseIrradianceHandle].Sample(ImmutableSamplers[PushConstant.DiffuseIrradianceHandle], Direction).rgb;
|
||||
}
|
||||
return 0.04f.xxx;
|
||||
}
|
||||
|
||||
float3 SamplePrefiltered(float3 Direction, float Roughness)
|
||||
{
|
||||
const float MAX_MIP_LEVEL = 5.0f;
|
||||
float Mip = MAX_MIP_LEVEL * Roughness;
|
||||
if (PushConstant.PrefilterHandle != INVALID_HANDLE)
|
||||
{
|
||||
return TextureCubes[PushConstant.PrefilterHandle].SampleLevel(ImmutableSamplers[PushConstant.PrefilterHandle], Direction, Mip).rgb;
|
||||
}
|
||||
return 0.0f.xxx;
|
||||
}
|
||||
|
||||
float2 SampleBrdfLut(float NdotV, float Roughness)
|
||||
{
|
||||
if (PushConstant.BrdfLutHandle != INVALID_HANDLE)
|
||||
{
|
||||
return TexturesRG[PushConstant.BrdfLutHandle].Sample(ImmutableSamplers[PushConstant.BrdfLutHandle], float2(NdotV, Roughness));
|
||||
}
|
||||
return 0.0f.xx;
|
||||
}
|
||||
|
||||
float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness)
|
||||
{
|
||||
float Coeff = Roughness * Roughness;
|
||||
|
|
@ -100,6 +136,12 @@ float3 FresnelSchlick(float cosine, float3 F_0)
|
|||
return F_0 + (1.0f - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
|
||||
}
|
||||
|
||||
// Sebastian Lagarde
|
||||
float3 FresnelSchlickRoughness(float cosine, float3 F_0, float Roughness)
|
||||
{
|
||||
return F_0 + (max((1.0f - Roughness).xxx, F_0) - F_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
|
||||
}
|
||||
|
||||
float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 Normal, float Metallic, float Roughness, float3 F_0, float3 LightDir, float LightDistance)
|
||||
{
|
||||
float Attenuation = 1.0f / (LightDistance * LightDistance); // TODO: Controlled Attenuation
|
||||
|
|
@ -113,18 +155,18 @@ float3 GetPBRContrib(float3 Albedo, float3 LightColor, float3 ViewDir, float3 No
|
|||
|
||||
float NormalDistribution = TrowbridgeReitzGGX(Normal, Halfway, Roughness);
|
||||
float Geometry = GeometrySmith(NdotV, NdotL, Roughness);
|
||||
float3 Fresnel = FresnelSchlick(CosineFactor, F_0);
|
||||
float3 Fresnel = FresnelSchlickRoughness(CosineFactor, F_0, Roughness);
|
||||
|
||||
float3 Numerator = (NormalDistribution * Geometry) * Fresnel;
|
||||
float Denominator = 4.0f * NdotV * NdotL;
|
||||
float3 Specular = Numerator / max(Denominator, 0.00001);
|
||||
float3 Specular = Numerator / (Denominator + 0.00001f);
|
||||
|
||||
float3 K_Specular = Fresnel;
|
||||
float3 K_Diffuse = 1.0f.xxx - K_Specular;
|
||||
|
||||
K_Diffuse *= 1.0f - Metallic;
|
||||
|
||||
return NdotL * Radiance * ((K_Diffuse * Albedo / PI) + Specular);
|
||||
return NdotL * Radiance * (K_Diffuse * Albedo / PI + Specular);
|
||||
}
|
||||
|
||||
float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
|
||||
|
|
@ -144,7 +186,6 @@ float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position,
|
|||
// TODO: Cite
|
||||
float3 F_0 = 0.04f.xxx;
|
||||
F_0 = lerp(F_0, Albedo, Metallic);
|
||||
float3 LightAmp = float3(23.47f, 21.31f, 20.79f); // TODO: Get rid
|
||||
|
||||
float3 Contrib = 0.0f;
|
||||
for (uint i = 0; i < Count; ++i)
|
||||
|
|
@ -167,7 +208,7 @@ float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position,
|
|||
float G = (Light.Color & 0x00FF0000) >> 16;
|
||||
float B = (Light.Color & 0x0000FF00) >> 8;
|
||||
|
||||
float3 LightColor = LightAmp * Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
|
||||
Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance);
|
||||
}
|
||||
|
|
@ -175,7 +216,6 @@ float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position,
|
|||
return Contrib;
|
||||
}
|
||||
|
||||
|
||||
float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
|
||||
{
|
||||
if (PushConstant.LightHandle == INVALID_HANDLE)
|
||||
|
|
@ -192,7 +232,6 @@ float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Pos
|
|||
// TODO: Cite
|
||||
float3 F_0 = 0.04f.xxx;
|
||||
F_0 = lerp(F_0, Albedo, Metallic);
|
||||
float3 LightAmp = float3(23.47f, 21.31f, 20.79f); // TODO: Get rid
|
||||
|
||||
float3 Contrib = 0.0f;
|
||||
for (uint i = 0; i < Count; ++i)
|
||||
|
|
@ -209,7 +248,7 @@ float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Pos
|
|||
float G = (Light.Color & 0x00FF0000) >> 16;
|
||||
float B = (Light.Color & 0x0000FF00) >> 8;
|
||||
|
||||
float3 LightColor = LightAmp * Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
|
||||
Contrib += GetPBRContrib(Albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f);
|
||||
}
|
||||
|
|
@ -217,12 +256,37 @@ float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Pos
|
|||
return Contrib;
|
||||
}
|
||||
|
||||
float3 GetAmbientInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal, float Occlusion)
|
||||
{
|
||||
float3 ViewDir = normalize(Camera.Position.xyz - Position);
|
||||
float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient.
|
||||
|
||||
float Metal = MetalRough.r;
|
||||
float Roughness = MetalRough.g;
|
||||
|
||||
float3 F_0 = 0.04f.xxx;
|
||||
F_0 = lerp(F_0, Albedo, MetalRough.r);
|
||||
float3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, Roughness);
|
||||
float3 K_Diffuse = 1.0f.xxx - K_Specular;
|
||||
|
||||
K_Diffuse *= 1.0f - Metal; // Metals don't have diffuse/refractions.
|
||||
|
||||
float3 ReflectionDir = reflect(-ViewDir, Normal);
|
||||
|
||||
float NdotV = max(dot(Normal, ViewDir), 0.0f);
|
||||
float3 PrefilteredColor = SamplePrefiltered(ReflectionDir, Roughness).rgb;
|
||||
float2 EnvBRDF = SampleBrdfLut(NdotV, Roughness);
|
||||
float3 Specular = PrefilteredColor * (K_Specular * EnvBRDF.x + EnvBRDF.y);
|
||||
|
||||
float3 DiffuseIrradiance = Albedo * SampleIrradiance(Normal);
|
||||
|
||||
return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion;
|
||||
}
|
||||
|
||||
FS_Output main(FS_Input StageInput)
|
||||
{
|
||||
FS_Output Output;
|
||||
|
||||
float3 Ambient = float3(0.02f, 0.02f, 0.02f);
|
||||
|
||||
// TODO: This should be invalid on the CPU side.
|
||||
if (PushConstant.MaterialIdx < 0)
|
||||
{
|
||||
|
|
@ -238,7 +302,12 @@ FS_Output main(FS_Input StageInput)
|
|||
float Alpha = AlbedoAlpha.a;
|
||||
float2 MetalRough = GetMetalRough(StageInput.InUV0);
|
||||
float3 Emission = GetEmissive(StageInput.InUV0);
|
||||
float3 Color = GetDirectionalLightInfluence(Albedo, MetalRough, Position, Normal) + GetPointLightInfluence(Albedo, MetalRough, Position, Normal);
|
||||
float Occlusion = GetOcclusion(StageInput.InUV0);
|
||||
|
||||
float3 DirectionalLightLum = GetDirectionalLightInfluence(Albedo, MetalRough, Position, Normal);
|
||||
float3 PointLighLum = GetPointLightInfluence(Albedo, MetalRough, Position, Normal);
|
||||
float3 AmbientLum = GetAmbientInfluence(Albedo, MetalRough, Position, Normal, Occlusion);
|
||||
float3 Color = DirectionalLightLum + PointLighLum + AmbientLum;
|
||||
|
||||
Output.ColorTarget = float4(Uncharted2Tonemap(Color + Emission), Alpha);
|
||||
return Output;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
Loading…
Reference in New Issue