Compare commits
42 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
38b697f202 | |
|
|
19e3222460 | |
|
|
4cdb39c6ba | |
|
|
cc1fd12b64 | |
|
|
cfb76c7d78 | |
|
|
58edfef94d | |
|
|
3ca3beb1e4 | |
|
|
4f71df797c | |
|
|
befa36c7f1 | |
|
|
3b4ea52611 | |
|
|
8e2c77bcf1 | |
|
|
1f8f102ee1 | |
|
|
cc4cffe989 | |
|
|
adfa86ebe9 | |
|
|
41c91058b6 | |
|
|
3a7a2b4ab7 | |
|
|
63282c3587 | |
|
|
7351415ebf | |
|
|
1db942f1a9 | |
|
|
3dc6501246 | |
|
|
5d6ddbb158 | |
|
|
7507394af9 | |
|
|
2facb3e6c1 | |
|
|
d683de3181 | |
|
|
d82e81d104 | |
|
|
a790c26f1c | |
|
|
668189acb5 | |
|
|
b8b620a723 | |
|
|
703624eb86 | |
|
|
1748a48272 | |
|
|
d8770c1e06 | |
|
|
1bee73e46f | |
|
|
98660a11fa | |
|
|
8eb5a678fc | |
|
|
e5b002c8cc | |
|
|
ec6aeb6f3b | |
|
|
8f9b6d66be | |
|
|
aa729610cf | |
|
|
3ab9d838fa | |
|
|
73c96dc56b | |
|
|
afec1e3e32 | |
|
|
396810d203 |
|
|
@ -5,3 +5,4 @@ build/
|
||||||
.direnv/
|
.direnv/
|
||||||
.ccls-cache/
|
.ccls-cache/
|
||||||
*.user
|
*.user
|
||||||
|
/vcpkg_installed
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
project(Aster VERSION 0.1.0)
|
project(Aster VERSION 0.1.0)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS}")
|
set(CMAKE_CXX_FLAGS "/W4 /GR- ${MSVC_FLAGS} /utf-8")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "/O3")
|
set(CMAKE_CXX_FLAGS_RELEASE "/O3")
|
||||||
add_compile_definitions(_HAS_EXCEPTIONS=0)
|
add_compile_definitions(_HAS_EXCEPTIONS=0)
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,27 @@
|
||||||
function(add_shader TARGET SHADER)
|
function(add_shader TARGET SHADER)
|
||||||
find_package(Vulkan REQUIRED COMPONENTS dxc)
|
find_package(Vulkan REQUIRED COMPONENTS dxc)
|
||||||
|
|
||||||
|
get_filename_component(vulkan-bin-dir ${Vulkan_GLSLC_EXECUTABLE} DIRECTORY)
|
||||||
|
|
||||||
|
find_program(slangc_exe NAMES "slangc")
|
||||||
|
if (NOT slangc_exe STREQUAL "slangc_exe-NOTFOUND")
|
||||||
|
set(slangc_exe_FOUND true)
|
||||||
|
endif()
|
||||||
|
|
||||||
get_filename_component(shader-ext ${SHADER} LAST_EXT)
|
get_filename_component(shader-ext ${SHADER} LAST_EXT)
|
||||||
get_filename_component(shader-inner ${SHADER} NAME_WLE)
|
|
||||||
get_filename_component(shader-type ${shader-inner} LAST_EXT)
|
|
||||||
string(REPLACE "." "" shader-type ${shader-type})
|
|
||||||
|
|
||||||
set(current-shader-path ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER})
|
set(current-shader-path ${CMAKE_CURRENT_SOURCE_DIR}/${SHADER})
|
||||||
set(current-output-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.spv)
|
set(current-output-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.slang-module)
|
||||||
|
set(current-copy-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER})
|
||||||
|
|
||||||
get_filename_component(current-output-dir ${current-output-path} DIRECTORY)
|
get_filename_component(current-output-dir ${current-output-path} DIRECTORY)
|
||||||
file(MAKE_DIRECTORY ${current-output-dir})
|
file(MAKE_DIRECTORY ${current-output-dir})
|
||||||
|
|
||||||
if (Vulkan_dxc_exe_FOUND AND ${shader-ext} STREQUAL ".hlsl")
|
if (${shader-ext} STREQUAL ".slang")
|
||||||
message("Marked as hlsl file. ${current-output-path}")
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${current-output-path}
|
OUTPUT ${current-output-path} ${current-copy-path}
|
||||||
COMMAND Vulkan::dxc_exe ${DXC_SHADER_FLAGS} -spirv -T "${shader-type}_6_0" -E main ${current-shader-path} -Fo ${current-output-path}
|
COMMAND ${slangc_exe} ${current-shader-path} -o ${current-output-path}
|
||||||
DEPENDS ${current-shader-path}
|
COMMAND ${CMAKE_COMMAND} -E copy ${current-shader-path} ${current-copy-path}
|
||||||
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
|
||||||
VERBATIM)
|
|
||||||
elseif (Vulkan_glslc_FOUND AND ${shader-ext} STREQUAL ".glsl")
|
|
||||||
message("Marked as glsl file. ${current-output-path}")
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${current-output-path}
|
|
||||||
COMMAND Vulkan::glslc ${GLSLC_SHADER_FLAGS} -o ${current-output-path} ${current-shader-path}
|
|
||||||
DEPENDS ${current-shader-path}
|
DEPENDS ${current-shader-path}
|
||||||
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
||||||
VERBATIM)
|
VERBATIM)
|
||||||
|
|
@ -33,10 +30,4 @@ function(add_shader TARGET SHADER)
|
||||||
# Make sure our build depends on this output.
|
# Make sure our build depends on this output.
|
||||||
set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE)
|
set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE)
|
||||||
target_sources(${TARGET} PRIVATE ${current-output-path})
|
target_sources(${TARGET} PRIVATE ${current-output-path})
|
||||||
endfunction(add_shader)
|
endfunction(add_shader)
|
||||||
|
|
||||||
function(add_shaders TARGET SHADERS)
|
|
||||||
foreach(shader IN ${SHADERS})
|
|
||||||
add_shader(TARGET ${shader})
|
|
||||||
endforeach()
|
|
||||||
endfunction(add_shaders)
|
|
||||||
|
|
@ -9,13 +9,15 @@ find_package(Vulkan REQUIRED)
|
||||||
find_package(fmt CONFIG REQUIRED)
|
find_package(fmt CONFIG REQUIRED)
|
||||||
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
||||||
find_package(EASTL CONFIG REQUIRED)
|
find_package(EASTL CONFIG REQUIRED)
|
||||||
|
find_library(slang NAMES "slang" CONFIG REQUIRED)
|
||||||
|
find_package(foonathan_memory CONFIG REQUIRED)
|
||||||
|
|
||||||
add_library(aster_core STATIC)
|
add_library(aster_core STATIC)
|
||||||
|
|
||||||
add_subdirectory("include")
|
add_subdirectory("include")
|
||||||
add_subdirectory("src")
|
add_subdirectory("src")
|
||||||
|
|
||||||
set_property(TARGET aster_core PROPERTY CXX_STANDARD 20)
|
target_compile_features(aster_core PUBLIC cxx_std_23)
|
||||||
|
|
||||||
target_include_directories(aster_core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/aster")
|
target_include_directories(aster_core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/aster")
|
||||||
target_include_directories(aster_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(aster_core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
|
@ -26,4 +28,6 @@ target_include_directories(aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS})
|
||||||
target_link_libraries(aster_core PRIVATE fmt::fmt)
|
target_link_libraries(aster_core PRIVATE fmt::fmt)
|
||||||
target_link_libraries(aster_core PRIVATE EASTL)
|
target_link_libraries(aster_core PRIVATE EASTL)
|
||||||
target_link_libraries(aster_core PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
|
target_link_libraries(aster_core PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
|
||||||
|
target_link_libraries(aster_core PUBLIC ${slang})
|
||||||
|
target_link_libraries(aster_core PRIVATE foonathan_memory)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ INTERFACE
|
||||||
"global.h"
|
"global.h"
|
||||||
"constants.h"
|
"constants.h"
|
||||||
"config.h"
|
"config.h"
|
||||||
"context.h"
|
"instance.h"
|
||||||
"physical_device.h"
|
"physical_device.h"
|
||||||
"device.h"
|
"device.h"
|
||||||
"swapchain.h"
|
"swapchain.h"
|
||||||
|
|
@ -15,7 +15,9 @@ INTERFACE
|
||||||
"queue_allocation.h"
|
"queue_allocation.h"
|
||||||
"buffer.h"
|
"buffer.h"
|
||||||
"image.h"
|
"image.h"
|
||||||
|
"image_view.h"
|
||||||
"surface.h"
|
"surface.h"
|
||||||
"size.h"
|
"size.h"
|
||||||
"type_traits.h"
|
"type_traits.h"
|
||||||
"window.h")
|
"window.h"
|
||||||
|
"sampler.h")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: buffer.h
|
// Aster: buffer.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -9,125 +9,110 @@
|
||||||
|
|
||||||
struct Device;
|
struct Device;
|
||||||
|
|
||||||
// TODO Refactor the Buffer Hierarchy
|
/// A Vulkan buffer wrapper.
|
||||||
|
|
||||||
struct Buffer
|
struct Buffer
|
||||||
{
|
{
|
||||||
|
enum class FlagBits : u8
|
||||||
|
{
|
||||||
|
eNone = 0x0,
|
||||||
|
eStaging = 0x1,
|
||||||
|
eUniform = 0x2,
|
||||||
|
eStorage = 0x4,
|
||||||
|
eIndex = 0x8,
|
||||||
|
eVertex = 0x10,
|
||||||
|
eIndirect = 0x20,
|
||||||
|
};
|
||||||
|
|
||||||
|
using Flags = vk::Flags<FlagBits>;
|
||||||
|
constexpr static Flags FLAGS = {};
|
||||||
|
|
||||||
|
Device const *m_Device = nullptr;
|
||||||
vk::Buffer m_Buffer = nullptr;
|
vk::Buffer m_Buffer = nullptr;
|
||||||
VmaAllocation m_Allocation = nullptr;
|
VmaAllocation m_Allocation = nullptr;
|
||||||
|
u8 *m_Mapped = nullptr; ///< If the buffer is host visible, it should be (and stay) mapped.
|
||||||
|
uptr m_DeviceAddr = 0;
|
||||||
|
usize m_Size = 0;
|
||||||
|
Flags m_Flags = {};
|
||||||
|
|
||||||
// If the buffer is host visible, it should be (and stay) mapped.
|
/// @returns True if it is a valid vulkan buffer.
|
||||||
u8 *m_Mapped = nullptr;
|
[[nodiscard]] bool
|
||||||
|
IsValid() const
|
||||||
|
{
|
||||||
|
return m_Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] usize GetSize() const;
|
/// If the buffer is host visible, it should be (and stay) mapped.
|
||||||
[[nodiscard]] bool IsHostVisible() const;
|
/// @returns True if the buffer is host-visible and mapped.
|
||||||
[[nodiscard]] bool IsValid() const;
|
[[nodiscard]] bool
|
||||||
[[nodiscard]] bool IsMapped() const;
|
IsMapped() const
|
||||||
[[nodiscard]] bool IsOwned() const;
|
{
|
||||||
[[nodiscard]] bool IsCommitted() const;
|
return m_Mapped;
|
||||||
void SetCommitted(bool committed);
|
}
|
||||||
|
|
||||||
void Destroy(const Device *device);
|
/// Writes the data to the buffer.
|
||||||
void Write(const Device *device, usize offset, usize size, const void *data);
|
/// @note The buffer must be mapped.
|
||||||
|
void Write(usize offset, usize size, void const *data) const;
|
||||||
|
|
||||||
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
/// If Buffer Device Address is enabled,
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
/// Get a pointer.
|
||||||
|
[[nodiscard]] uptr GetDeviceAddress() const;
|
||||||
|
|
||||||
uptr
|
// Constructors
|
||||||
GetDeviceAddress(const Device *device);
|
|
||||||
|
|
||||||
// Buffer.size is used for bookkeeping
|
Buffer(Device const *device, usize size, vk::BufferUsageFlags bufferUsage, VmaAllocationCreateFlags allocationFlags,
|
||||||
// If the buffer is Invalid, the remaining data in Buffer is used intrusively by `GpuResourceManager`.
|
VmaMemoryUsage memoryUsage, cstr name);
|
||||||
usize m_Size_ = 0;
|
|
||||||
|
|
||||||
constexpr static usize VALID_BUFFER_BIT = Cast<usize>(1llu << 63);
|
Buffer(Buffer &&other) noexcept;
|
||||||
constexpr static usize OWNED_BIT = 1llu << 62;
|
Buffer &operator=(Buffer &&other) noexcept;
|
||||||
constexpr static usize COMMITTED_BIT = 1llu << 61;
|
|
||||||
constexpr static usize SIZE_MASK = ~(VALID_BUFFER_BIT | OWNED_BIT | COMMITTED_BIT);
|
~Buffer();
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
constexpr bool concepts::GpuResource<Buffer> = true;
|
|
||||||
|
|
||||||
// Ensure that m_Size doesn't get used intrusively since it manages the state.
|
|
||||||
static_assert(offsetof(Buffer, m_Size_) > sizeof(usize));
|
|
||||||
|
|
||||||
struct UniformBuffer : Buffer
|
struct UniformBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eUniform;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StorageBuffer : Buffer
|
struct StorageBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eStorage;
|
||||||
void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IndirectBuffer : Buffer
|
struct IndirectBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, bool hostVisible, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eIndirect;
|
||||||
};
|
|
||||||
|
|
||||||
struct StorageIndexBuffer : StorageBuffer
|
|
||||||
{
|
|
||||||
void Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexBuffer : Buffer
|
struct VertexBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eVertex;
|
||||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IndexBuffer : Buffer
|
struct IndexBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eIndex;
|
||||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StagingBuffer : Buffer
|
struct StagingBuffer : Buffer
|
||||||
{
|
{
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eStaging;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline usize
|
namespace concepts
|
||||||
Buffer::GetSize() const
|
|
||||||
{
|
{
|
||||||
return m_Size_ & SIZE_MASK;
|
template <typename T>
|
||||||
}
|
concept AnyBuffer = std::derived_from<T, Buffer>;
|
||||||
|
|
||||||
inline bool
|
template <typename T, typename TInto>
|
||||||
Buffer::IsHostVisible() const
|
concept BufferInto = std::derived_from<T, Buffer> and std::derived_from<TInto, Buffer> and
|
||||||
{
|
(static_cast<bool>(T::FLAGS & TInto::FLAGS) or std::same_as<Buffer, TInto>);
|
||||||
return IsMapped();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
template <typename T>
|
||||||
Buffer::IsValid() const
|
concept AnyBufferRef = Deref<T> and AnyBuffer<DerefType<T>>;
|
||||||
{
|
|
||||||
return m_Size_ & VALID_BUFFER_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
template <typename T, typename TTo>
|
||||||
Buffer::IsMapped() const
|
concept BufferRefTo = Deref<T> and BufferInto<DerefType<T>, TTo>;
|
||||||
{
|
|
||||||
return m_Mapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
} // namespace concepts
|
||||||
Buffer::IsOwned() const
|
|
||||||
{
|
|
||||||
return m_Size_ & OWNED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
Buffer::IsCommitted() const
|
|
||||||
{
|
|
||||||
return m_Size_ & COMMITTED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
|
||||||
Buffer::SetCommitted(const bool committed)
|
|
||||||
{
|
|
||||||
m_Size_ = committed ? (m_Size_ | COMMITTED_BIT) : (m_Size_ & ~COMMITTED_BIT);
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: config.h
|
// Aster: config.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
|
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
|
||||||
#define VULKAN_HPP_DISABLE_ENHANCED_MODE 1
|
#define VULKAN_HPP_DISABLE_ENHANCED_MODE 1
|
||||||
#define VULKAN_HPP_NO_EXCEPTIONS 1
|
#define VULKAN_HPP_NO_EXCEPTIONS 1
|
||||||
|
#define VULKAN_HPP_NO_SMART_HANDLE 1
|
||||||
|
#define VULKAN_HPP_NO_STRUCT_SETTERS 1
|
||||||
|
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
||||||
|
|
@ -25,4 +27,4 @@
|
||||||
#define USE_OPTICK (0)
|
#define USE_OPTICK (0)
|
||||||
#else
|
#else
|
||||||
#define USE_OPTICK (1)
|
#define USE_OPTICK (1)
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: constants.h
|
// Aster: constants.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -13,6 +13,8 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtx/quaternion.hpp>
|
#include <glm/gtx/quaternion.hpp>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
using c8 = char;
|
using c8 = char;
|
||||||
using u8 = uint8_t;
|
using u8 = uint8_t;
|
||||||
using u16 = uint16_t;
|
using u16 = uint16_t;
|
||||||
|
|
@ -28,8 +30,9 @@ using f128 = long double;
|
||||||
using b8 = bool;
|
using b8 = bool;
|
||||||
using b32 = u32;
|
using b32 = u32;
|
||||||
using usize = size_t;
|
using usize = size_t;
|
||||||
|
using isize = intptr_t;
|
||||||
using uptr = uintptr_t;
|
using uptr = uintptr_t;
|
||||||
using cstr = const char *;
|
using cstr = char const *;
|
||||||
|
|
||||||
namespace ansi_color
|
namespace ansi_color
|
||||||
{
|
{
|
||||||
|
|
@ -44,42 +47,28 @@ constexpr auto White = "\u001b[37m";
|
||||||
constexpr auto Reset = "\u001b[0m";
|
constexpr auto Reset = "\u001b[0m";
|
||||||
} // namespace ansi_color
|
} // namespace ansi_color
|
||||||
|
|
||||||
template <typename TType, typename TFrom>
|
|
||||||
constexpr auto
|
|
||||||
Cast(TFrom &&in)
|
|
||||||
{
|
|
||||||
return static_cast<TType>(std::forward<TFrom>(in));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TType, typename TFrom>
|
|
||||||
constexpr auto
|
|
||||||
Recast(TFrom &&in)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<TType>(std::forward<TFrom>(in));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr f32
|
constexpr f32
|
||||||
operator""_deg(long double degrees)
|
operator""_deg(long double degrees)
|
||||||
{
|
{
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
return glm::radians<f32>(static_cast<f32>(degrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32
|
constexpr f32
|
||||||
operator""_deg(unsigned long long int degrees)
|
operator""_deg(unsigned long long int degrees)
|
||||||
{
|
{
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
return glm::radians<f32>(static_cast<f32>(degrees));
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32
|
constexpr f32
|
||||||
operator""_rad(long double rads)
|
operator""_rad(long double rads)
|
||||||
{
|
{
|
||||||
return Cast<f32>(rads);
|
return static_cast<f32>(rads);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr f32
|
constexpr f32
|
||||||
operator""_rad(unsigned long long int rads)
|
operator""_rad(unsigned long long int rads)
|
||||||
{
|
{
|
||||||
return Cast<f32>(rads);
|
return static_cast<f32>(rads);
|
||||||
}
|
}
|
||||||
|
|
||||||
using glm::ivec2;
|
using glm::ivec2;
|
||||||
|
|
@ -116,31 +105,31 @@ constexpr Version VERSION = {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr u32
|
constexpr u32
|
||||||
Kilobyte(const u32 in)
|
Kilobyte(u32 const in)
|
||||||
{
|
{
|
||||||
return in * 1024;
|
return in * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr usize
|
constexpr usize
|
||||||
Kilobyte(const usize in)
|
Kilobyte(usize const in)
|
||||||
{
|
{
|
||||||
return in * 1024;
|
return in * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32
|
constexpr u32
|
||||||
Megabyte(const u32 in)
|
Megabyte(u32 const in)
|
||||||
{
|
{
|
||||||
return in * 1024 * 1024;
|
return in * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr usize
|
constexpr usize
|
||||||
Megabyte(const usize in)
|
Megabyte(usize const in)
|
||||||
{
|
{
|
||||||
return in * 1024 * 1024;
|
return in * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr usize
|
constexpr usize
|
||||||
Gigabyte(const usize in)
|
Gigabyte(usize const in)
|
||||||
{
|
{
|
||||||
return in * 1024 * 1024 * 1024;
|
return in * 1024 * 1024 * 1024;
|
||||||
}
|
}
|
||||||
|
|
@ -167,4 +156,4 @@ template <typename T>
|
||||||
constexpr T Qnan = std::numeric_limits<T>::quiet_NaN();
|
constexpr T Qnan = std::numeric_limits<T>::quiet_NaN();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
constexpr T Snan = std::numeric_limits<T>::signalling_NaN();
|
constexpr T Snan = std::numeric_limits<T>::signalling_NaN();
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: device.h
|
// Aster: device.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
#include <EASTL/vector.h>
|
|
||||||
#include <EASTL/span.h>
|
#include <EASTL/span.h>
|
||||||
|
#include <EASTL/vector.h>
|
||||||
|
|
||||||
struct QueueAllocation;
|
struct QueueAllocation;
|
||||||
struct Context;
|
struct Instance;
|
||||||
struct PhysicalDevice;
|
struct PhysicalDevice;
|
||||||
|
|
||||||
struct Features
|
struct Features
|
||||||
|
|
@ -31,19 +31,31 @@ struct Device final
|
||||||
vk::PipelineCache m_PipelineCache = nullptr;
|
vk::PipelineCache m_PipelineCache = nullptr;
|
||||||
bool m_ValidationEnabled = true;
|
bool m_ValidationEnabled = true;
|
||||||
|
|
||||||
template <typename T>
|
template <concepts::VkHandle T>
|
||||||
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const;
|
void SetName(T const &object, cstr name) const;
|
||||||
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
|
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<u8> DumpPipelineCache() const;
|
[[nodiscard]] eastl::vector<u8> DumpPipelineCache() const;
|
||||||
|
|
||||||
void WaitIdle() const;
|
void WaitIdle() const;
|
||||||
|
|
||||||
|
vk::Device *
|
||||||
|
operator->()
|
||||||
|
{
|
||||||
|
return &m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Device const *
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
Device() = default;
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
Device(Instance const &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||||
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
eastl::span<QueueAllocation> const &queueAllocations, eastl::span<u8> const &pipelineCacheData,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData, NameString &&name);
|
NameString &&name);
|
||||||
~Device();
|
~Device();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
@ -53,15 +65,15 @@ struct Device final
|
||||||
DISALLOW_COPY_AND_ASSIGN(Device);
|
DISALLOW_COPY_AND_ASSIGN(Device);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <concepts::VkHandle T>
|
||||||
requires vk::isVulkanHandleType<T>::value void
|
void
|
||||||
Device::SetName(const T &object, cstr name) const
|
Device::SetName(T const &object, cstr name) const
|
||||||
{
|
{
|
||||||
if (!m_ValidationEnabled || !name || !object)
|
if (!m_ValidationEnabled || !name || !object)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto handle = Recast<u64>(Cast<typename T::NativeType>(object));
|
auto handle = reinterpret_cast<u64>(static_cast<typename T::NativeType>(object));
|
||||||
const vk::DebugUtilsObjectNameInfoEXT objectNameInfo = {
|
vk::DebugUtilsObjectNameInfoEXT const objectNameInfo = {
|
||||||
.objectType = object.objectType,
|
.objectType = object.objectType,
|
||||||
.objectHandle = handle,
|
.objectHandle = handle,
|
||||||
.pObjectName = name,
|
.pObjectName = name,
|
||||||
|
|
@ -69,4 +81,4 @@ Device::SetName(const T &object, cstr name) const
|
||||||
vk::Result result = m_Device.setDebugUtilsObjectNameEXT(&objectNameInfo);
|
vk::Result result = m_Device.setDebugUtilsObjectNameEXT(&objectNameInfo);
|
||||||
WARN_IF(Failed(result), "Could not name {:x}: {} as {}. Cause: {}", handle, to_string(object.objectType), name,
|
WARN_IF(Failed(result), "Could not name {:x}: {} as {}. Cause: {}", handle, to_string(object.objectType), name,
|
||||||
result);
|
result);
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: global.h
|
// Aster: global.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "util/logger.h"
|
|
||||||
|
#include "aster/util/logger.h"
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
#include <EASTL/shared_ptr.h>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
// Macros that can collide with functions.
|
// Macros that can collide with functions.
|
||||||
|
|
@ -26,6 +29,8 @@
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "EASTL/intrusive_ptr.h"
|
||||||
#include "type_traits.h"
|
#include "type_traits.h"
|
||||||
|
|
||||||
#include <EASTL/fixed_string.h>
|
#include <EASTL/fixed_string.h>
|
||||||
|
|
@ -50,38 +55,70 @@ constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
||||||
|
|
||||||
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
|
#define Take(ELEMENT) eastl::exchange(ELEMENT, {})
|
||||||
|
|
||||||
#define TODO(MSG) assert(false && ("Unimplemented: " MSG))
|
#define TODO(...) assert(!("Unimplemented: " __VA_ARGS__))
|
||||||
|
#define FIX(...) static_assert(!("Unimplemented: " __VA_ARGS__))
|
||||||
|
#define UNREACHABLE(...) assert(!("Unreachable: " __VA_ARGS__))
|
||||||
|
|
||||||
|
#define AbortIfFailed(RESULT) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
vk::Result _checkResultValue_; \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
|
||||||
|
THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
vk::Result _checkResultValue_; \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, \
|
||||||
|
_checkResultValue_) \
|
||||||
|
THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define AbortIfFailedM(RESULT, MSG) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
auto _checkResultValue_ = static_cast<vk::Result>(RESULT); \
|
||||||
|
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
[[nodiscard]] inline bool
|
[[nodiscard]] inline bool
|
||||||
Failed(const vk::Result result)
|
Failed(vk::Result const result)
|
||||||
{
|
{
|
||||||
return result != vk::Result::eSuccess;
|
return result != vk::Result::eSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace concepts
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
concept VkHandle = vk::isVulkanHandleType<T>::value;
|
||||||
|
}
|
||||||
|
|
||||||
using NameString = eastl::fixed_string<char, 32, false>;
|
using NameString = eastl::fixed_string<char, 32, false>;
|
||||||
|
|
||||||
template <typename TFlagBits>
|
template <typename TFlagBits>
|
||||||
struct eastl::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
|
struct eastl::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
|
||||||
{
|
{
|
||||||
[[nodiscard]] usize
|
[[nodiscard]] usize
|
||||||
operator()(const vk::Flags<TFlagBits> &val)
|
operator()(vk::Flags<TFlagBits> const &val)
|
||||||
{
|
{
|
||||||
return std::hash<u32>()(Cast<u32>(val));
|
return std::hash<u32>()(static_cast<u32>(val));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[nodiscard]] usize
|
[[nodiscard]] usize
|
||||||
HashAny(const T &val)
|
HashAny(T const &val)
|
||||||
{
|
{
|
||||||
return eastl::hash<std::remove_cvref_t<T>>()(val);
|
return eastl::hash<std::remove_cvref_t<T>>()(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline usize
|
[[nodiscard]] inline usize
|
||||||
HashCombine(const usize hash0, const usize hash1)
|
HashCombine(usize const hash0, usize const hash1)
|
||||||
{
|
{
|
||||||
constexpr usize saltValue = 0x9e3779b9;
|
constexpr usize saltValue = 0x9e3779b9;
|
||||||
const usize tempVar = hash1 + saltValue + (hash0 << 6) + (hash0 >> 2);
|
usize const tempVar = hash1 + saltValue + (hash0 << 6) + (hash0 >> 2);
|
||||||
return hash0 ^ tempVar;
|
return hash0 ^ tempVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,32 +141,32 @@ struct Time
|
||||||
Update()
|
Update()
|
||||||
{
|
{
|
||||||
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
||||||
const auto newElapsed = glfwGetTime();
|
auto const newElapsed = glfwGetTime();
|
||||||
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, MAX_DELTA);
|
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, MAX_DELTA);
|
||||||
m_Elapsed = newElapsed;
|
m_Elapsed = newElapsed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] constexpr usize
|
[[nodiscard]] constexpr usize
|
||||||
ClosestMultiple(const usize val, const usize of)
|
ClosestMultiple(usize const val, usize const of)
|
||||||
{
|
{
|
||||||
return of * ((val + of - 1) / of);
|
return of * ((val + of - 1) / of);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr u32
|
[[nodiscard]] constexpr u32
|
||||||
ClosestMultiple(const u32 val, const u32 of)
|
ClosestMultiple(u32 const val, u32 const of)
|
||||||
{
|
{
|
||||||
return of * ((val + of - 1) / of);
|
return of * ((val + of - 1) / of);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool
|
[[nodiscard]] constexpr bool
|
||||||
IsPowerOfTwo(const usize val)
|
IsPowerOfTwo(usize const val)
|
||||||
{
|
{
|
||||||
return val && !(val & (val - 1));
|
return val && !(val & (val - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr bool
|
[[nodiscard]] constexpr bool
|
||||||
IsPowerOfTwo(const u32 val)
|
IsPowerOfTwo(u32 const val)
|
||||||
{
|
{
|
||||||
return val && !(val & (val - 1));
|
return val && !(val & (val - 1));
|
||||||
}
|
}
|
||||||
|
|
@ -149,10 +186,10 @@ ClosestLargerPowerOfTwo(usize val)
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr usize
|
[[nodiscard]] constexpr usize
|
||||||
ClosestPowerOfTwo(const usize val)
|
ClosestPowerOfTwo(usize const val)
|
||||||
{
|
{
|
||||||
const usize largerPo2 = ClosestLargerPowerOfTwo(val);
|
usize const largerPo2 = ClosestLargerPowerOfTwo(val);
|
||||||
const usize smallerPo2 = largerPo2 >> 1;
|
usize const smallerPo2 = largerPo2 >> 1;
|
||||||
return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2;
|
return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -170,10 +207,10 @@ ClosestLargerPowerOfTwo(u32 val)
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] constexpr u32
|
[[nodiscard]] constexpr u32
|
||||||
ClosestPowerOfTwo(const u32 val)
|
ClosestPowerOfTwo(u32 const val)
|
||||||
{
|
{
|
||||||
const u32 largerPo2 = ClosestLargerPowerOfTwo(val);
|
u32 const largerPo2 = ClosestLargerPowerOfTwo(val);
|
||||||
const u32 smallerPo2 = largerPo2 >> 1;
|
u32 const smallerPo2 = largerPo2 >> 1;
|
||||||
return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2;
|
return (smallerPo2 + largerPo2 <= (val << 1)) ? largerPo2 : smallerPo2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,15 +226,20 @@ GetMaskOffset(u32 val)
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <typename T>
|
||||||
struct fmt::formatter<vk::Result> : nested_formatter<std::string>
|
concept VkToString = requires(T a) {
|
||||||
|
{ vk::to_string(a) } -> std::convertible_to<std::string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <VkToString T>
|
||||||
|
struct fmt::formatter<T> : nested_formatter<std::string>
|
||||||
{
|
{
|
||||||
auto
|
auto
|
||||||
// ReSharper disable once CppInconsistentNaming
|
// ReSharper disable once CppInconsistentNaming
|
||||||
format(vk::Result result, format_context &ctx) const
|
format(T result, format_context &ctx) const
|
||||||
{
|
{
|
||||||
return write_padded(ctx,
|
return write_padded(ctx,
|
||||||
[this, result](auto out) { return v10::format_to(out, "{}", nested(to_string(result))); });
|
[this, result](auto out) { return fmt::format_to(out, "{}", nested(to_string(result))); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -206,8 +248,14 @@ struct fmt::formatter<eastl::fixed_string<TType, TCount, TOverflow>> : nested_fo
|
||||||
{
|
{
|
||||||
auto
|
auto
|
||||||
// ReSharper disable once CppInconsistentNaming
|
// ReSharper disable once CppInconsistentNaming
|
||||||
format(const eastl::fixed_string<TType, TCount, TOverflow> &str, format_context &ctx) const
|
format(eastl::fixed_string<TType, TCount, TOverflow> const &str, format_context &ctx) const
|
||||||
{
|
{
|
||||||
return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); });
|
return write_padded(ctx, [this, str](auto out) { return fmt::format_to(out, "{}", nested(str.c_str())); });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using Ref = eastl::shared_ptr<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using WeakRef = eastl::weak_ptr<T>;
|
||||||
|
|
@ -1,137 +1,132 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: image.h
|
// Aster: image.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
|
struct StorageTexture;
|
||||||
struct Device;
|
struct Device;
|
||||||
|
|
||||||
[[nodiscard]] inline vk::Extent2D
|
[[nodiscard]] inline vk::Extent2D
|
||||||
ToExtent2D(const vk::Extent3D &extent)
|
ToExtent2D(vk::Extent3D const &extent)
|
||||||
{
|
{
|
||||||
return {extent.width, extent.height};
|
return {extent.width, extent.height};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline vk::Extent3D
|
[[nodiscard]] inline vk::Extent3D
|
||||||
ToExtent3D(const vk::Extent2D &extent, const u32 depth)
|
ToExtent3D(vk::Extent2D const &extent, u32 const depth)
|
||||||
{
|
{
|
||||||
return {extent.width, extent.height, depth};
|
return {extent.width, extent.height, depth};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline vk::Offset2D
|
[[nodiscard]] inline vk::Offset2D
|
||||||
ToOffset2D(const vk::Extent3D &extent)
|
ToOffset2D(vk::Extent3D const &extent)
|
||||||
{
|
{
|
||||||
return {Cast<i32>(extent.width), Cast<i32>(extent.height)};
|
return {static_cast<i32>(extent.width), static_cast<i32>(extent.height)};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] inline vk::Offset3D
|
[[nodiscard]] inline vk::Offset3D
|
||||||
ToOffset3D(const vk::Extent3D &extent)
|
ToOffset3D(vk::Extent3D const &extent)
|
||||||
{
|
{
|
||||||
return {Cast<i32>(extent.width), Cast<i32>(extent.height), Cast<i32>(extent.depth)};
|
return {static_cast<i32>(extent.width), static_cast<i32>(extent.height), static_cast<i32>(extent.depth)};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Image
|
struct Image
|
||||||
{
|
{
|
||||||
|
enum class FlagBits : u8
|
||||||
|
{
|
||||||
|
eSampled = 0x1,
|
||||||
|
eStorage = 0x2,
|
||||||
|
eCube = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
using Flags = vk::Flags<FlagBits>;
|
||||||
|
constexpr static Flags FLAGS = {};
|
||||||
|
|
||||||
|
Device const *m_Device = nullptr;
|
||||||
vk::Image m_Image = nullptr;
|
vk::Image m_Image = nullptr;
|
||||||
vk::ImageView m_View = nullptr;
|
|
||||||
VmaAllocation m_Allocation = nullptr;
|
VmaAllocation m_Allocation = nullptr;
|
||||||
vk::Extent3D m_Extent;
|
vk::Extent3D m_Extent;
|
||||||
// Image.m_MipLevels_ is used for bookkeeping
|
vk::Format m_Format;
|
||||||
// If the image is Invalid, the remaining data in Image is used intrusively by `GpuResourceManager`.
|
|
||||||
u8 m_EmptyPadding_ = 0;
|
u8 m_EmptyPadding_ = 0;
|
||||||
u8 m_Flags_ = 0;
|
Flags m_Flags_ = {};
|
||||||
u8 m_LayerCount = 0;
|
u8 m_LayerCount = 0;
|
||||||
u8 m_MipLevels = 0;
|
u8 m_MipLevels = 0;
|
||||||
|
|
||||||
[[nodiscard]] bool IsValid() const;
|
[[nodiscard]] bool
|
||||||
[[nodiscard]] bool IsOwned() const;
|
IsValid() const
|
||||||
[[nodiscard]] u32 GetMipLevels() const;
|
{
|
||||||
[[nodiscard]] bool IsCommitted() const;
|
return m_Image;
|
||||||
void SetCommitted(bool committed);
|
}
|
||||||
|
|
||||||
void Destroy(const Device *device);
|
[[nodiscard]] u32
|
||||||
|
GetMipLevels() const
|
||||||
|
{
|
||||||
|
return m_MipLevels;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr static u8 VALID_BIT = 1u << 7;
|
void DestroyView(vk::ImageView imageView) const;
|
||||||
constexpr static u8 OWNED_BIT = 1u << 6;
|
|
||||||
constexpr static u8 COMMITTED_BIT = 1u << 5;
|
// Constructors.
|
||||||
|
|
||||||
|
explicit Image(Device const *device, vk::Image image, VmaAllocation allocation, vk::Extent3D extent,
|
||||||
|
vk::Format format, Flags flags, u8 layerCount, u8 mipLevels);
|
||||||
|
|
||||||
|
Image(Image &&other) noexcept;
|
||||||
|
Image &operator=(Image &&other) noexcept;
|
||||||
|
|
||||||
|
~Image();
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Image);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
|
||||||
constexpr bool concepts::GpuResource<Image> = true;
|
|
||||||
|
|
||||||
struct Texture : Image
|
struct Texture : Image
|
||||||
{
|
{
|
||||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isMipMapped, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eSampled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(Texture) == sizeof(Image));
|
struct ImageCube : Image
|
||||||
|
|
||||||
struct TextureCube : Texture
|
|
||||||
{
|
{
|
||||||
void
|
constexpr static Flags FLAGS = FlagBits::eCube;
|
||||||
Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped = false, cstr name = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(TextureCube) == sizeof(Image));
|
struct TextureCube : Image
|
||||||
|
|
||||||
struct AttachmentImage : Image
|
|
||||||
{
|
{
|
||||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name = nullptr);
|
constexpr static Flags FLAGS = Texture::FLAGS | ImageCube::FLAGS;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(AttachmentImage) == sizeof(Image));
|
struct StorageImage : Image
|
||||||
|
|
||||||
struct DepthImage : Image
|
|
||||||
{
|
{
|
||||||
void Init(const Device *device, vk::Extent2D extent, cstr name = nullptr);
|
constexpr static Flags FLAGS = FlagBits::eStorage;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(DepthImage) == sizeof(Image));
|
struct StorageTexture : StorageImage
|
||||||
|
|
||||||
struct StorageTexture : Texture
|
|
||||||
{
|
{
|
||||||
void Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, bool isSampled, cstr name = nullptr);
|
constexpr static Flags FLAGS = StorageImage::FLAGS | Texture::FLAGS;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(StorageTexture) == sizeof(Image));
|
struct StorageTextureCube : StorageImage
|
||||||
|
|
||||||
struct StorageTextureCube : StorageTexture
|
|
||||||
{
|
{
|
||||||
void Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isSampled, bool isMipMapped = false,
|
constexpr static Flags FLAGS = StorageImage::FLAGS | Texture::FLAGS | ImageCube::FLAGS;
|
||||||
cstr name = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(sizeof(StorageTextureCube) == sizeof(Image));
|
namespace concepts
|
||||||
|
|
||||||
inline bool
|
|
||||||
Image::IsValid() const
|
|
||||||
{
|
{
|
||||||
return m_Flags_ & VALID_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
template <typename T>
|
||||||
Image::IsOwned() const
|
concept AnyImage = std::derived_from<T, Image>;
|
||||||
{
|
|
||||||
return m_Flags_ & OWNED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline u32
|
template <typename T, typename TInto>
|
||||||
Image::GetMipLevels() const
|
concept ImageInto = std::derived_from<T, Image> and std::derived_from<TInto, Image> and
|
||||||
{
|
(static_cast<bool>(T::FLAGS & TInto::FLAGS) or std::same_as<Image, TInto>);
|
||||||
return m_MipLevels;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
template <typename T>
|
||||||
Image::IsCommitted() const
|
concept AnyImageRef = Deref<T> and AnyImage<DerefType<T>>;
|
||||||
{
|
|
||||||
return m_Flags_ & COMMITTED_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void
|
template <typename T, typename TTo>
|
||||||
Image::SetCommitted(const bool committed)
|
concept ImageRefTo = Deref<T> and ImageInto<DerefType<T>, TTo>;
|
||||||
{
|
|
||||||
m_Flags_ = committed ? (m_Flags_ | COMMITTED_BIT) : (m_Flags_ & ~COMMITTED_BIT);
|
} // namespace concepts
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: image_view.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "image.h"
|
||||||
|
|
||||||
|
template <concepts::AnyImage TImage>
|
||||||
|
struct View
|
||||||
|
{
|
||||||
|
using ImageType = TImage;
|
||||||
|
|
||||||
|
Ref<Image> m_Image;
|
||||||
|
vk::ImageView m_View = nullptr;
|
||||||
|
vk::Extent3D m_Extent;
|
||||||
|
u8 m_BaseLayer = 0;
|
||||||
|
u8 m_LayerCount = 0;
|
||||||
|
u8 m_BaseMipLevel = 0;
|
||||||
|
u8 m_MipLevelCount = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Image
|
||||||
|
GetImage() const
|
||||||
|
{
|
||||||
|
return m_Image->m_Image;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
IsValid() const
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_Image);
|
||||||
|
}
|
||||||
|
|
||||||
|
View(Ref<Image> image, vk::ImageView const view, vk::Extent3D const extent, u8 const baseLayer, u8 const layerCount,
|
||||||
|
u8 const baseMipLevel, u8 const mipLevelCount)
|
||||||
|
: m_Image{std::move(image)}
|
||||||
|
, m_View{view}
|
||||||
|
, m_Extent{extent}
|
||||||
|
, m_BaseLayer{baseLayer}
|
||||||
|
, m_LayerCount{layerCount}
|
||||||
|
, m_BaseMipLevel{baseMipLevel}
|
||||||
|
, m_MipLevelCount{mipLevelCount}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
View(View &&other) noexcept
|
||||||
|
: m_Image{std::move(other.m_Image)}
|
||||||
|
, m_View{Take(other.m_View)}
|
||||||
|
, m_Extent{std::move(other.m_Extent)}
|
||||||
|
, m_BaseLayer{other.m_BaseLayer}
|
||||||
|
, m_LayerCount{other.m_LayerCount}
|
||||||
|
, m_BaseMipLevel{other.m_BaseMipLevel}
|
||||||
|
, m_MipLevelCount{other.m_MipLevelCount}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
View &
|
||||||
|
operator=(View &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
using std::swap;
|
||||||
|
swap(m_Image, other.m_Image);
|
||||||
|
swap(m_View, other.m_View);
|
||||||
|
swap(m_Extent, other.m_Extent);
|
||||||
|
swap(m_BaseLayer, other.m_BaseLayer);
|
||||||
|
swap(m_LayerCount, other.m_LayerCount);
|
||||||
|
swap(m_BaseMipLevel, other.m_BaseMipLevel);
|
||||||
|
swap(m_MipLevelCount, other.m_MipLevelCount);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(View);
|
||||||
|
|
||||||
|
~View()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_Image->DestroyView(Take(m_View));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ImageView = View<Image>;
|
||||||
|
using ImageCubeView = View<ImageCube>;
|
||||||
|
using TextureView = View<Texture>;
|
||||||
|
using TextureCubeView = View<TextureCube>;
|
||||||
|
using StorageImageView = View<StorageImage>;
|
||||||
|
using StorageTextureView = View<StorageTexture>;
|
||||||
|
using StorageTextureCubeView = View<StorageTextureCube>;
|
||||||
|
|
||||||
|
namespace concepts
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
concept View = std::derived_from<T, View<typename T::ImageType>>;
|
||||||
|
|
||||||
|
template <typename T, typename TTo>
|
||||||
|
concept ViewTo = View<T> and ImageInto<typename T::ImageType, TTo>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept ViewRef = Deref<T> and View<DerefType<T>>;
|
||||||
|
|
||||||
|
template <typename T, typename TTo>
|
||||||
|
concept ViewRefTo = ViewRef<T> and ImageInto<typename DerefType<T>::ImageType, TTo>;
|
||||||
|
|
||||||
|
} // namespace concepts
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: context.h
|
// Aster: context.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -8,25 +8,26 @@
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Context
|
* @class Instance
|
||||||
*
|
*
|
||||||
* @brief Vulkan context to handle device initialization logic.
|
* @brief Vulkan context to handle device initialization logic.
|
||||||
*
|
*
|
||||||
* Handles the required hardware interactions.
|
* Handles the required hardware interactions.
|
||||||
*/
|
*/
|
||||||
struct Context final
|
struct Instance final
|
||||||
{
|
{
|
||||||
// Members
|
// Members
|
||||||
vk::Instance m_Instance = nullptr;
|
vk::Instance m_Instance = nullptr;
|
||||||
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Context(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
|
Instance() = default;
|
||||||
~Context();
|
Instance(cstr appName, Version version, bool enableValidation = ENABLE_LAYER_MESSAGES_DEFAULT_VALUE);
|
||||||
|
~Instance();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
Context(Context &&other) noexcept;
|
Instance(Instance &&other) noexcept;
|
||||||
Context &operator=(Context &&other) noexcept;
|
Instance &operator=(Instance &&other) noexcept;
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
#if !defined(ASTER_NDEBUG)
|
||||||
constexpr static bool ENABLE_LAYER_MESSAGES_DEFAULT_VALUE = true;
|
constexpr static bool ENABLE_LAYER_MESSAGES_DEFAULT_VALUE = true;
|
||||||
|
|
@ -34,5 +35,5 @@ struct Context final
|
||||||
constexpr static bool ENABLE_LAYER_MESSAGES_DEFAULT_VALUE = false;
|
constexpr static bool ENABLE_LAYER_MESSAGES_DEFAULT_VALUE = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Context);
|
DISALLOW_COPY_AND_ASSIGN(Instance);
|
||||||
};
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: physical_device.h
|
// Aster: physical_device.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -8,10 +8,12 @@
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
|
|
||||||
struct Window;
|
struct Window;
|
||||||
struct Context;
|
struct Instance;
|
||||||
|
|
||||||
enum class QueueSupportFlagBits
|
enum class QueueSupportFlagBits
|
||||||
{
|
{
|
||||||
|
|
@ -23,6 +25,31 @@ enum class QueueSupportFlagBits
|
||||||
|
|
||||||
using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>;
|
using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>;
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
// ReSharper disable once CppInconsistentNaming
|
||||||
|
format_as(QueueSupportFlags const &qfi)
|
||||||
|
{
|
||||||
|
std::stringstream sb;
|
||||||
|
if (qfi & QueueSupportFlagBits::eGraphics)
|
||||||
|
{
|
||||||
|
sb << "Graphics | ";
|
||||||
|
}
|
||||||
|
if (qfi & QueueSupportFlagBits::eTransfer)
|
||||||
|
{
|
||||||
|
sb << "Transfer | ";
|
||||||
|
}
|
||||||
|
if (qfi & QueueSupportFlagBits::eCompute)
|
||||||
|
{
|
||||||
|
sb << "Compute | ";
|
||||||
|
}
|
||||||
|
if (qfi & QueueSupportFlagBits::ePresent)
|
||||||
|
{
|
||||||
|
sb << "Present | ";
|
||||||
|
}
|
||||||
|
auto const sbv = sb.view();
|
||||||
|
return std::string(sbv.substr(0, sbv.size() - 3));
|
||||||
|
}
|
||||||
|
|
||||||
struct QueueFamilyInfo
|
struct QueueFamilyInfo
|
||||||
{
|
{
|
||||||
u32 m_Index;
|
u32 m_Index;
|
||||||
|
|
@ -30,6 +57,12 @@ struct QueueFamilyInfo
|
||||||
QueueSupportFlags m_Support;
|
QueueSupportFlags m_Support;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline std::string
|
||||||
|
format_as(QueueFamilyInfo const &qfi)
|
||||||
|
{
|
||||||
|
return fmt::format("Queue {}: Count={} Support={}", qfi.m_Index, qfi.m_Count, qfi.m_Support);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
||||||
GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
|
GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
|
||||||
|
|
||||||
|
|
@ -48,11 +81,12 @@ struct PhysicalDevice final
|
||||||
eastl::vector<vk::PresentModeKHR> m_PresentModes;
|
eastl::vector<vk::PresentModeKHR> m_PresentModes;
|
||||||
eastl::vector<QueueFamilyInfo> m_QueueFamilies;
|
eastl::vector<QueueFamilyInfo> m_QueueFamilies;
|
||||||
|
|
||||||
|
PhysicalDevice() = default;
|
||||||
PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice);
|
PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice);
|
||||||
};
|
};
|
||||||
|
|
||||||
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PhysicalDevices(const Surface *surface, const Context *context);
|
PhysicalDevices(Surface const &surface, Instance const &context);
|
||||||
};
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: pipeline.h
|
// Aster: pipeline.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -13,12 +13,45 @@ struct Device;
|
||||||
|
|
||||||
struct Pipeline
|
struct Pipeline
|
||||||
{
|
{
|
||||||
const Device *m_Device;
|
enum class Kind
|
||||||
vk::PipelineLayout m_Layout;
|
{
|
||||||
vk::Pipeline m_Pipeline;
|
eGraphics,
|
||||||
eastl::vector<vk::DescriptorSetLayout> m_SetLayouts;
|
eCompute,
|
||||||
|
};
|
||||||
|
|
||||||
Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
|
Device const *m_Device = nullptr;
|
||||||
eastl::vector<vk::DescriptorSetLayout> &&setLayouts);
|
vk::PipelineLayout m_Layout;
|
||||||
|
vk::Pipeline m_Pipeline = nullptr;
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> m_SetLayouts;
|
||||||
|
Kind m_Kind;
|
||||||
|
|
||||||
|
Pipeline() = default;
|
||||||
|
Pipeline(Device const *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> &&setLayouts, Kind kind);
|
||||||
~Pipeline();
|
~Pipeline();
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Pipeline);
|
||||||
|
|
||||||
|
Pipeline(Pipeline &&other) noexcept
|
||||||
|
: m_Device{other.m_Device}
|
||||||
|
, m_Layout{Take(other.m_Layout)}
|
||||||
|
, m_Pipeline{Take(other.m_Pipeline)}
|
||||||
|
, m_SetLayouts{std::move(other.m_SetLayouts)}
|
||||||
|
, m_Kind{other.m_Kind}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline &
|
||||||
|
operator=(Pipeline &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
using eastl::swap;
|
||||||
|
swap(m_Device, other.m_Device);
|
||||||
|
swap(m_Layout, other.m_Layout);
|
||||||
|
swap(m_Pipeline, other.m_Pipeline);
|
||||||
|
swap(m_SetLayouts, other.m_SetLayouts);
|
||||||
|
swap(m_Kind, other.m_Kind);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: queue_allocation.h
|
// Aster: queue_allocation.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: sampler.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
struct Device;
|
||||||
|
|
||||||
|
struct Sampler final
|
||||||
|
{
|
||||||
|
Device const *m_Device = nullptr;
|
||||||
|
vk::Sampler m_Sampler = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
IsValid() const
|
||||||
|
{
|
||||||
|
return m_Sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
Sampler(Device const *device, vk::SamplerCreateInfo const &samplerCreateInfo, cstr name);
|
||||||
|
~Sampler();
|
||||||
|
|
||||||
|
Sampler(Sampler &&other) noexcept;
|
||||||
|
Sampler &operator=(Sampler &&other) noexcept;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Sampler);
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: size.h
|
// Aster: size.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -9,12 +9,35 @@
|
||||||
|
|
||||||
struct Size2D
|
struct Size2D
|
||||||
{
|
{
|
||||||
u32 m_Width;
|
u32 m_Width = 0;
|
||||||
u32 m_Height;
|
u32 m_Height = 0;
|
||||||
|
|
||||||
|
Size2D() = default;
|
||||||
|
|
||||||
|
Size2D(u32 const width, u32 const height)
|
||||||
|
: m_Width{width}
|
||||||
|
, m_Height{height}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Size2D(vk::Extent2D const extent)
|
||||||
|
: m_Width{extent.width}
|
||||||
|
, m_Height{extent.height}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Size2D &
|
||||||
|
operator=(vk::Extent2D const other)
|
||||||
|
{
|
||||||
|
m_Height = other.height;
|
||||||
|
m_Width = other.width;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(Size2D const &) const = default;
|
||||||
|
|
||||||
explicit
|
|
||||||
operator vk::Extent2D() const
|
operator vk::Extent2D() const
|
||||||
{
|
{
|
||||||
return {m_Width, m_Height};
|
return {m_Width, m_Height};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: surface.h
|
// Aster: surface.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
struct Context;
|
struct Instance;
|
||||||
struct Window;
|
struct Window;
|
||||||
|
|
||||||
struct Surface
|
struct Surface
|
||||||
{
|
{
|
||||||
Context *m_Context;
|
Instance *m_Context;
|
||||||
vk::SurfaceKHR m_Surface;
|
vk::SurfaceKHR m_Surface;
|
||||||
NameString m_Name;
|
NameString m_Name;
|
||||||
|
|
||||||
// Ctor Dtor
|
// Ctor Dtor
|
||||||
Surface(Context *context, const Window *window, cstr name);
|
Surface() = default;
|
||||||
|
Surface(Instance &context, Window const &window);
|
||||||
~Surface();
|
~Surface();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/// =============================================
|
/// =============================================
|
||||||
// Aster: swapchain.h
|
// Aster: swapchain.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// ==============================================
|
// ==============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -19,9 +19,8 @@ struct Swapchain final
|
||||||
{
|
{
|
||||||
using FnResizeCallback = eastl::function<void(vk::Extent2D)>;
|
using FnResizeCallback = eastl::function<void(vk::Extent2D)>;
|
||||||
|
|
||||||
const Device *m_Device;
|
Device const *m_Device;
|
||||||
vk::SwapchainKHR m_Swapchain;
|
vk::SwapchainKHR m_Swapchain;
|
||||||
NameString m_Name;
|
|
||||||
vk::Extent2D m_Extent;
|
vk::Extent2D m_Extent;
|
||||||
vk::Format m_Format;
|
vk::Format m_Format;
|
||||||
eastl::fixed_vector<vk::Image, 4> m_Images;
|
eastl::fixed_vector<vk::Image, 4> m_Images;
|
||||||
|
|
@ -29,11 +28,12 @@ struct Swapchain final
|
||||||
|
|
||||||
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
|
eastl::vector<FnResizeCallback> m_ResizeCallbacks;
|
||||||
|
|
||||||
void Create(const Surface *window, Size2D size);
|
void Create(Surface const &surface, Size2D size);
|
||||||
void RegisterResizeCallback(FnResizeCallback &&callback);
|
void RegisterResizeCallback(FnResizeCallback &&callback);
|
||||||
|
|
||||||
// Ctor/Dtor
|
// Ctor/Dtor
|
||||||
Swapchain(const Surface *window, const Device *device, Size2D size, NameString &&name);
|
Swapchain() = default;
|
||||||
|
Swapchain(Surface const &surface, Device const &device, Size2D size);
|
||||||
~Swapchain();
|
~Swapchain();
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
|
|
@ -42,6 +42,6 @@ struct Swapchain final
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Swapchain);
|
DISALLOW_COPY_AND_ASSIGN(Swapchain);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
};
|
};
|
||||||
|
|
@ -1,36 +1,28 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: type_traits.h
|
// Aster: type_traits.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
|
||||||
struct Device;
|
struct Device;
|
||||||
|
struct Image;
|
||||||
|
|
||||||
namespace concepts
|
namespace concepts
|
||||||
{
|
{
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept DeviceDestructible = requires(T a, Device *p) {
|
concept Deref = requires(T a) {
|
||||||
{ a.Destroy(p) } -> std::convertible_to<void>;
|
{ *a };
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename TRef, typename TVal>
|
||||||
concept Committable = requires(T a, bool v) {
|
concept DerefTo = requires(TRef a) {
|
||||||
{ a.IsCommitted() } -> std::convertible_to<bool>;
|
{ *a } -> std::convertible_to<TVal>;
|
||||||
{ a.SetCommitted(v) } -> std::convertible_to<void>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <Deref T>
|
||||||
constexpr bool GpuResource = false;
|
using DerefType = std::remove_cvref_t<decltype(*std::declval<T>())>;
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
concept RenderResource = GpuResource<T> and std::is_default_constructible_v<T> and std::is_trivially_copyable_v<T> and
|
|
||||||
DeviceDestructible<T> and Committable<T>;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr bool IsHandle = false;
|
|
||||||
|
|
||||||
template <typename THandle>
|
|
||||||
concept HandleType = IsHandle<THandle> and RenderResource<typename THandle::Type>;
|
|
||||||
|
|
||||||
} // namespace concepts
|
} // namespace concepts
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: window.h
|
// Aster: window.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -20,6 +20,8 @@ struct Window final
|
||||||
|
|
||||||
static std::atomic_uint64_t m_WindowCount;
|
static std::atomic_uint64_t m_WindowCount;
|
||||||
static std::atomic_bool m_IsGlfwInit;
|
static std::atomic_bool m_IsGlfwInit;
|
||||||
|
static void SetupLibrary();
|
||||||
|
static cstr *GetInstanceExtensions(u32 *extensionCount);
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
|
|
@ -30,7 +32,7 @@ struct Window final
|
||||||
}
|
}
|
||||||
|
|
||||||
void RequestExit() const noexcept;
|
void RequestExit() const noexcept;
|
||||||
void SetWindowSize(const vk::Extent2D &extent) const noexcept;
|
void SetWindowSize(vk::Extent2D const &extent) const noexcept;
|
||||||
void SetWindowSize(u32 width, u32 height) const noexcept;
|
void SetWindowSize(u32 width, u32 height) const noexcept;
|
||||||
/// Actual size of the framebuffer being used for the window render.
|
/// Actual size of the framebuffer being used for the window render.
|
||||||
[[nodiscard]] Size2D GetSize() const;
|
[[nodiscard]] Size2D GetSize() const;
|
||||||
|
|
@ -44,4 +46,4 @@ struct Window final
|
||||||
Window &operator=(Window &&other) noexcept;
|
Window &operator=(Window &&other) noexcept;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Window);
|
DISALLOW_COPY_AND_ASSIGN(Window);
|
||||||
};
|
};
|
||||||
|
|
@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
INTERFACE
|
INTERFACE
|
||||||
"manager.h"
|
"rendering_device.h"
|
||||||
"buffer_manager.h"
|
"resource.h"
|
||||||
"image_manager.h"
|
"context.h"
|
||||||
"render_resource_manager.h")
|
"commit_manager.h")
|
||||||
|
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer_manager.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/buffer.h"
|
|
||||||
#include "manager.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
using BufferHandle = Handle<Buffer>;
|
|
||||||
|
|
||||||
class BufferManager final : public Manager<Buffer>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BufferManager(const Device *device, const u32 maxCount, const u8 binding);
|
|
||||||
|
|
||||||
[[nodiscard]] Handle CreateStorageBuffer(usize size, cstr name = nullptr);
|
|
||||||
[[nodiscard]] Handle CreateUniformBuffer(usize size, cstr name = nullptr);
|
|
||||||
};
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -0,0 +1,375 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: render_resource_manager.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aster/aster.h"
|
||||||
|
|
||||||
|
#include "aster/util/freelist.h"
|
||||||
|
|
||||||
|
#include "EASTL/deque.h"
|
||||||
|
#include "EASTL/intrusive_hash_map.h"
|
||||||
|
|
||||||
|
#include "EASTL/vector.h"
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
#include "aster/core/sampler.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class RenderingDevice;
|
||||||
|
|
||||||
|
class CommitManager
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
struct HandleMapper
|
||||||
|
{
|
||||||
|
using Type = T;
|
||||||
|
using Handle = Ref<Type>;
|
||||||
|
using Resource = ResId<Type>;
|
||||||
|
|
||||||
|
struct Entry : eastl::intrusive_hash_node_key<Handle>
|
||||||
|
{
|
||||||
|
std::atomic<u32> m_CommitCount;
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRef()
|
||||||
|
{
|
||||||
|
auto const rc = ++m_CommitCount;
|
||||||
|
assert(rc > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Release()
|
||||||
|
{
|
||||||
|
auto const rc = --m_CommitCount;
|
||||||
|
assert(rc < MaxValue<u32>);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32
|
||||||
|
IsReferenced() const
|
||||||
|
{
|
||||||
|
return m_CommitCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(Entry const &other) const
|
||||||
|
{
|
||||||
|
return this->mKey == other.mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry *
|
||||||
|
Next()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Entry *>(this->mpNext);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetNext(Entry &entry)
|
||||||
|
{
|
||||||
|
this->mpNext = &entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Hash
|
||||||
|
{
|
||||||
|
usize
|
||||||
|
operator()(Handle const &e)
|
||||||
|
{
|
||||||
|
return eastl::hash<Type *>()(e.get());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::vector<Entry> m_Data;
|
||||||
|
FreeList<Entry> m_FreeList;
|
||||||
|
eastl::intrusive_hash_map<typename Entry::key_type, Entry, 31, typename Entry::Hash> m_InUse;
|
||||||
|
std::array<FreeList<Entry>, 4> m_ToDelete;
|
||||||
|
u8 m_ToDeleteIndex = 0;
|
||||||
|
|
||||||
|
explicit HandleMapper(u32 const maxCount)
|
||||||
|
: m_Data{maxCount}
|
||||||
|
{
|
||||||
|
// Setup freelist
|
||||||
|
for (auto it = m_Data.rbegin(); it != m_Data.rend(); ++it)
|
||||||
|
{
|
||||||
|
m_FreeList.Push(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~HandleMapper()
|
||||||
|
{
|
||||||
|
for (auto &toDelete : m_ToDelete)
|
||||||
|
{
|
||||||
|
ClearEntries(toDelete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PIN_MEMORY(HandleMapper);
|
||||||
|
|
||||||
|
/// Returns a commit, and a bool signifying if it is a new commit.
|
||||||
|
std::tuple<Resource, bool>
|
||||||
|
Create(Handle const &object)
|
||||||
|
{
|
||||||
|
// Get-from freelist
|
||||||
|
assert(!m_FreeList.Empty());
|
||||||
|
|
||||||
|
auto it = m_InUse.find(object);
|
||||||
|
if (it != m_InUse.end())
|
||||||
|
{
|
||||||
|
it->AddRef();
|
||||||
|
auto i = GetIndex(*it);
|
||||||
|
return {Resource{i}, false};
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry &data = m_FreeList.Pop();
|
||||||
|
|
||||||
|
data.mKey = object;
|
||||||
|
data.m_CommitCount = 0;
|
||||||
|
|
||||||
|
m_InUse.insert(data);
|
||||||
|
|
||||||
|
auto i = GetIndex(data);
|
||||||
|
|
||||||
|
return {Resource{i}, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle
|
||||||
|
GetHandle(Resource const &res)
|
||||||
|
{
|
||||||
|
return m_Data[res.m_Index].mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRef(Resource const &commit)
|
||||||
|
{
|
||||||
|
m_Data.at(commit.m_Index).AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Release(Resource const &commit)
|
||||||
|
{
|
||||||
|
auto &entry = m_Data.at(commit.m_Index);
|
||||||
|
entry.Release();
|
||||||
|
if (!entry.IsReferenced())
|
||||||
|
{
|
||||||
|
QueueDelete(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sweeps through the delete queue.
|
||||||
|
* All freed items are cleared. (With a 3 frame delay)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
Update()
|
||||||
|
{
|
||||||
|
m_ToDeleteIndex = (m_ToDeleteIndex + 1) % m_ToDelete.size();
|
||||||
|
auto &list = m_ToDelete[m_ToDeleteIndex];
|
||||||
|
ClearEntries(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
u32
|
||||||
|
GetIndex(Entry const &entry)
|
||||||
|
{
|
||||||
|
return static_cast<u32>(&entry - m_Data.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QueueDelete(Entry &entry)
|
||||||
|
{
|
||||||
|
m_InUse.remove(entry);
|
||||||
|
m_ToDelete[m_ToDeleteIndex].Push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearEntries(FreeList<Entry> &entries)
|
||||||
|
{
|
||||||
|
while (!entries.Empty())
|
||||||
|
{
|
||||||
|
Entry &entry = entries.Pop();
|
||||||
|
entry.mKey.reset();
|
||||||
|
entry.m_CommitCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
union WriteInfo {
|
||||||
|
vk::DescriptorBufferInfo uBufferInfo;
|
||||||
|
vk::DescriptorImageInfo uImageInfo;
|
||||||
|
vk::BufferView uBufferView;
|
||||||
|
|
||||||
|
explicit WriteInfo(vk::DescriptorBufferInfo const &info);
|
||||||
|
explicit WriteInfo(vk::DescriptorImageInfo const &info);
|
||||||
|
explicit WriteInfo(vk::BufferView const &info);
|
||||||
|
};
|
||||||
|
|
||||||
|
using WriteCommand = vk::WriteDescriptorSet;
|
||||||
|
|
||||||
|
// using WriteOwner = std::variant<Handle<Buffer>, Handle<Image>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenderingDevice const *m_Device;
|
||||||
|
|
||||||
|
CommitManager(RenderingDevice const *device, u32 maxBuffers, u32 maxImages, u32 maxStorageImages,
|
||||||
|
Ref<Sampler> defaultSampler);
|
||||||
|
~CommitManager();
|
||||||
|
|
||||||
|
PIN_MEMORY(CommitManager);
|
||||||
|
|
||||||
|
// Commit Buffer
|
||||||
|
private:
|
||||||
|
ResId<Buffer> CommitBuffer(Ref<Buffer> const &buffer);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Commit Storage Images
|
||||||
|
ResId<StorageImageView>
|
||||||
|
CommitStorageImage(concepts::ViewRefTo<StorageImage> auto const &image)
|
||||||
|
{
|
||||||
|
return CommitStorageImage(CastView<StorageImageView>(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<StorageImageView> CommitStorageImage(Ref<StorageImageView> const &image);
|
||||||
|
|
||||||
|
// Sampled Images
|
||||||
|
ResId<TextureView>
|
||||||
|
CommitTexture(concepts::ViewRefTo<Texture> auto const &image, Ref<Sampler> const &sampler)
|
||||||
|
{
|
||||||
|
return CommitTexture(CastView<TextureView>(image), sampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<TextureView>
|
||||||
|
CommitTexture(concepts::ViewRefTo<Texture> auto const &image)
|
||||||
|
{
|
||||||
|
return CommitTexture(CastView<TextureView>(image));
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<TextureView> CommitTexture(Ref<TextureView> const &handle);
|
||||||
|
ResId<TextureView> CommitTexture(Ref<TextureView> const &image, Ref<Sampler> const &sampler);
|
||||||
|
|
||||||
|
void Update();
|
||||||
|
|
||||||
|
Ref<Buffer>
|
||||||
|
FetchHandle(ResId<Buffer> const &id)
|
||||||
|
{
|
||||||
|
return m_Buffers.GetHandle(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<TextureView>
|
||||||
|
FetchHandle(ResId<TextureView> const &id)
|
||||||
|
{
|
||||||
|
return m_Images.GetHandle(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<StorageImageView>
|
||||||
|
FetchHandle(ResId<StorageImageView> const &id)
|
||||||
|
{
|
||||||
|
return m_StorageImages.GetHandle(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::DescriptorSetLayout const &
|
||||||
|
GetDescriptorSetLayout() const
|
||||||
|
{
|
||||||
|
return m_SetLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::DescriptorSet const &
|
||||||
|
GetDescriptorSet() const
|
||||||
|
{
|
||||||
|
return m_DescriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CommitManager &
|
||||||
|
Instance()
|
||||||
|
{
|
||||||
|
assert(m_Instance);
|
||||||
|
return *m_Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
IsInit()
|
||||||
|
{
|
||||||
|
return static_cast<bool>(m_Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
vk::DescriptorPool m_DescriptorPool;
|
||||||
|
vk::DescriptorSetLayout m_SetLayout;
|
||||||
|
vk::DescriptorSet m_DescriptorSet;
|
||||||
|
|
||||||
|
constexpr static u8 BUFFER_BINDING_INDEX = 0x0;
|
||||||
|
constexpr static u8 IMAGE_BINDING_INDEX = 0x1;
|
||||||
|
constexpr static u8 STORAGE_IMAGE_BINDING_INDEX = 0x2;
|
||||||
|
|
||||||
|
HandleMapper<Buffer> m_Buffers;
|
||||||
|
HandleMapper<TextureView> m_Images;
|
||||||
|
HandleMapper<StorageImageView> m_StorageImages;
|
||||||
|
|
||||||
|
Ref<Sampler> m_DefaultSampler;
|
||||||
|
|
||||||
|
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
||||||
|
eastl::deque<WriteInfo> m_WriteInfos;
|
||||||
|
// eastl::vector<WriteOwner> m_WriteOwner;
|
||||||
|
|
||||||
|
static CommitManager *m_Instance;
|
||||||
|
friend ResId<Buffer>;
|
||||||
|
friend ResId<TextureView>;
|
||||||
|
friend ResId<StorageImageView>;
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRef(ResId<Buffer> const &handle)
|
||||||
|
{
|
||||||
|
m_Buffers.AddRef(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRef(ResId<TextureView> const &handle)
|
||||||
|
{
|
||||||
|
m_Images.AddRef(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AddRef(ResId<StorageImageView> const &handle)
|
||||||
|
{
|
||||||
|
m_StorageImages.AddRef(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Release(ResId<Buffer> const &handle)
|
||||||
|
{
|
||||||
|
m_Buffers.Release(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Release(ResId<TextureView> const &handle)
|
||||||
|
{
|
||||||
|
m_Images.Release(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Release(ResId<StorageImageView> const &handle)
|
||||||
|
{
|
||||||
|
m_StorageImages.Release(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
ResId<T>::AddRef() const
|
||||||
|
{
|
||||||
|
if (m_Index != INVALID)
|
||||||
|
CommitManager::Instance().AddRef(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
ResId<T>::Release() const
|
||||||
|
{
|
||||||
|
if (m_Index != INVALID)
|
||||||
|
CommitManager::Instance().Release(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -0,0 +1,471 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: context_pool.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "EASTL/span.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
#include <aster/aster.h>
|
||||||
|
|
||||||
|
#include <aster/core/buffer.h>
|
||||||
|
#include <aster/core/image.h>
|
||||||
|
#include <aster/core/image_view.h>
|
||||||
|
#include <aster/core/physical_device.h>
|
||||||
|
#include <aster/core/pipeline.h>
|
||||||
|
|
||||||
|
#include <EASTL/intrusive_list.h>
|
||||||
|
#include <EASTL/optional.h>
|
||||||
|
#include <EASTL/vector.h>
|
||||||
|
|
||||||
|
#include <foonathan/memory/memory_pool.hpp>
|
||||||
|
#include <foonathan/memory/namespace_alias.hpp>
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
|
||||||
|
class RenderingDevice;
|
||||||
|
struct Frame;
|
||||||
|
|
||||||
|
namespace _internal
|
||||||
|
{
|
||||||
|
class ComputeContextPool;
|
||||||
|
class GraphicsContextPool;
|
||||||
|
class TransferContextPool;
|
||||||
|
class ContextPool;
|
||||||
|
} // namespace _internal
|
||||||
|
|
||||||
|
#define DEPRECATE_RAW_CALLS
|
||||||
|
|
||||||
|
class Context
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
_internal::ContextPool *m_Pool;
|
||||||
|
vk::CommandBuffer m_Cmd;
|
||||||
|
|
||||||
|
friend RenderingDevice;
|
||||||
|
friend _internal::ContextPool;
|
||||||
|
|
||||||
|
explicit Context(_internal::ContextPool &pool, vk::CommandBuffer const cmd)
|
||||||
|
: m_Pool{&pool}
|
||||||
|
, m_Cmd{cmd}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<Buffer> const &buffer);
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<Image> const &image);
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<ImageView> const &view);
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEPRECATE_RAW_CALLS void Dependency(vk::DependencyInfo const &dependencyInfo);
|
||||||
|
|
||||||
|
void Begin();
|
||||||
|
void End();
|
||||||
|
|
||||||
|
void BeginDebugRegion(cstr name, vec4 color = {});
|
||||||
|
void EndDebugRegion();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Inline the no-op if not debug.
|
||||||
|
#if defined(ASTER_NDEBUG)
|
||||||
|
inline void
|
||||||
|
Context::BeginDebugRegion(cstr name, vec4 color)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
Context::EndDebugRegion()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TransferContext : public Context
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
friend RenderingDevice;
|
||||||
|
friend _internal::TransferContextPool;
|
||||||
|
|
||||||
|
explicit TransferContext(_internal::ContextPool &pool, vk::CommandBuffer const cmd)
|
||||||
|
: Context{pool, cmd}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UploadBuffer(Ref<Buffer> const &buffer, usize size, void const *data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void UploadTexture(Ref<Image> const &image, eastl::span<u8> const &data);
|
||||||
|
|
||||||
|
void
|
||||||
|
UploadBuffer(Ref<Buffer> const &buffer, std::ranges::range auto const &data)
|
||||||
|
{
|
||||||
|
auto const span = eastl::span{data.begin(), data.end()};
|
||||||
|
UploadBuffer(buffer, span.size_bytes(), span.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
DEPRECATE_RAW_CALLS void Blit(vk::BlitImageInfo2 const &mipBlitInfo);
|
||||||
|
|
||||||
|
TransferContext(TransferContext &&other) noexcept;
|
||||||
|
TransferContext &operator=(TransferContext &&other) noexcept;
|
||||||
|
|
||||||
|
~TransferContext() = default;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TransferContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComputeContext : public TransferContext
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
friend RenderingDevice;
|
||||||
|
friend _internal::ComputeContextPool;
|
||||||
|
|
||||||
|
Pipeline const *m_PipelineInUse;
|
||||||
|
|
||||||
|
explicit ComputeContext(_internal::ContextPool &pool, vk::CommandBuffer const cmd)
|
||||||
|
: TransferContext{pool, cmd}
|
||||||
|
, m_PipelineInUse{nullptr}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PushConstantBlock(usize offset, usize size, void const *data);
|
||||||
|
|
||||||
|
void Dispatch(Pipeline const &pipeline, u32 x, u32 y, u32 z, usize size, void *data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void BindPipeline(Pipeline const &pipeline);
|
||||||
|
|
||||||
|
void
|
||||||
|
PushConstantBlock(auto const &block)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof block > 128)
|
||||||
|
WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}", sizeof block);
|
||||||
|
PushConstantBlock(0, sizeof block, &block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PushConstantBlock(usize const offset, auto const &block)
|
||||||
|
{
|
||||||
|
if (offset + sizeof block > 128)
|
||||||
|
WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}, at offset {}", sizeof block,
|
||||||
|
offset);
|
||||||
|
PushConstantBlock(offset, sizeof block, &block);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Dispatch(Pipeline const &pipeline, u32 const x, u32 const y, u32 const z, auto &pushConstantBlock)
|
||||||
|
{
|
||||||
|
if constexpr (sizeof pushConstantBlock > 128)
|
||||||
|
WARN("Vulkan only guarantees 128 bytes of Push Constants. Size of PCB is {}", sizeof pushConstantBlock);
|
||||||
|
Dispatch(pipeline, x, y, z, sizeof pushConstantBlock, &pushConstantBlock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class GraphicsContext : public ComputeContext
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
friend RenderingDevice;
|
||||||
|
friend _internal::GraphicsContextPool;
|
||||||
|
|
||||||
|
explicit GraphicsContext(_internal::ContextPool &pool, vk::CommandBuffer const cmd)
|
||||||
|
: ComputeContext{pool, cmd}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
DEPRECATE_RAW_CALLS void SetViewport(vk::Viewport const &viewport);
|
||||||
|
void BindVertexBuffer(Ref<VertexBuffer> const &vertexBuffer);
|
||||||
|
void BindIndexBuffer(Ref<IndexBuffer> const &indexBuffer);
|
||||||
|
void Draw(usize vertexCount);
|
||||||
|
void DrawIndexed(usize indexCount);
|
||||||
|
void DrawIndexed(usize indexCount, usize firstIndex, usize firstVertex);
|
||||||
|
|
||||||
|
DEPRECATE_RAW_CALLS void BeginRendering(vk::RenderingInfo const &renderingInfo);
|
||||||
|
void EndRendering();
|
||||||
|
|
||||||
|
DEPRECATE_RAW_CALLS vk::CommandBuffer
|
||||||
|
GetCommandBuffer() const
|
||||||
|
{
|
||||||
|
return m_Cmd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace _internal
|
||||||
|
{
|
||||||
|
|
||||||
|
class ContextPool
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
RenderingDevice *m_Device;
|
||||||
|
vk::CommandPool m_Pool;
|
||||||
|
eastl::vector<vk::CommandBuffer> m_CommandBuffers;
|
||||||
|
u32 m_BuffersAllocated;
|
||||||
|
|
||||||
|
public:
|
||||||
|
u16 m_ExtraData;
|
||||||
|
|
||||||
|
enum class ManagedBy : u8
|
||||||
|
{
|
||||||
|
eFrame,
|
||||||
|
eDevice,
|
||||||
|
} m_ManagedBy;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
eastl::vector<Ref<Buffer>> m_OwnedBuffers;
|
||||||
|
eastl::vector<Ref<Image>> m_OwnedImages;
|
||||||
|
eastl::vector<Ref<ImageView>> m_OwnedImageViews;
|
||||||
|
|
||||||
|
vk::CommandBuffer AllocateCommandBuffer();
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] RenderingDevice &
|
||||||
|
GetDevice() const
|
||||||
|
{
|
||||||
|
assert(m_Device);
|
||||||
|
return *m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
eastl::function<void(ContextPool &)> m_ResetCallback;
|
||||||
|
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<Buffer> const &buffer);
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<Image> const &image);
|
||||||
|
/// Keep the resource alive while the command buffers are acting.
|
||||||
|
void KeepAlive(Ref<ImageView> const &view);
|
||||||
|
|
||||||
|
Context CreateContext();
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
ContextPool() = default;
|
||||||
|
ContextPool(RenderingDevice &device, u32 queueFamilyIndex, ManagedBy managedBy);
|
||||||
|
|
||||||
|
ContextPool(ContextPool &&other) noexcept;
|
||||||
|
ContextPool &operator=(ContextPool &&other) noexcept;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(ContextPool const &other) const
|
||||||
|
{
|
||||||
|
return m_Pool == other.m_Pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ContextPool();
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ContextPool);
|
||||||
|
};
|
||||||
|
|
||||||
|
class TransferContextPool : public ContextPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TransferContext CreateTransferContext();
|
||||||
|
|
||||||
|
TransferContextPool() = default;
|
||||||
|
|
||||||
|
TransferContextPool(RenderingDevice &device, u32 const queueFamilyIndex, ManagedBy const managedBy)
|
||||||
|
: ContextPool{device, queueFamilyIndex, managedBy}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferContextPool(TransferContextPool &&other) noexcept = default;
|
||||||
|
TransferContextPool &operator=(TransferContextPool &&other) noexcept = default;
|
||||||
|
|
||||||
|
~TransferContextPool() = default;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(TransferContextPool);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComputeContextPool : public TransferContextPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ComputeContext CreateComputeContext();
|
||||||
|
|
||||||
|
ComputeContextPool() = default;
|
||||||
|
|
||||||
|
ComputeContextPool(RenderingDevice &device, u32 const queueFamilyIndex, ManagedBy const managedBy)
|
||||||
|
: TransferContextPool{device, queueFamilyIndex, managedBy}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeContextPool(ComputeContextPool &&other) noexcept = default;
|
||||||
|
ComputeContextPool &operator=(ComputeContextPool &&other) noexcept = default;
|
||||||
|
|
||||||
|
~ComputeContextPool() = default;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(ComputeContextPool);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GraphicsContextPool : public ComputeContextPool
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GraphicsContext CreateGraphicsContext();
|
||||||
|
|
||||||
|
GraphicsContextPool() = default;
|
||||||
|
|
||||||
|
GraphicsContextPool(RenderingDevice &device, u32 const queueFamilyIndex, ManagedBy const managedBy)
|
||||||
|
: ComputeContextPool{device, queueFamilyIndex, managedBy}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsContextPool(GraphicsContextPool &&other) noexcept = default;
|
||||||
|
GraphicsContextPool &operator=(GraphicsContextPool &&other) noexcept = default;
|
||||||
|
|
||||||
|
~GraphicsContextPool() = default;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(GraphicsContextPool);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::derived_from<ContextPool> TContextPool>
|
||||||
|
class OrderlessContextPool
|
||||||
|
{
|
||||||
|
using ContextPoolType = TContextPool;
|
||||||
|
|
||||||
|
struct ContextListEntry : eastl::intrusive_list_node
|
||||||
|
{
|
||||||
|
ContextPoolType m_Pool;
|
||||||
|
|
||||||
|
bool
|
||||||
|
Contains(ContextPool const &other) const
|
||||||
|
{
|
||||||
|
return m_Pool == other;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ContextListType = eastl::intrusive_list<ContextListEntry>;
|
||||||
|
|
||||||
|
RenderingDevice *m_Device;
|
||||||
|
memory::memory_pool<> m_ContextPoolEntryMemory;
|
||||||
|
ContextListType m_FreeContextPools;
|
||||||
|
ContextListType m_UsedContextPools;
|
||||||
|
u32 m_QueueFamilyIndex;
|
||||||
|
|
||||||
|
constexpr static usize ENTRY_SIZE = sizeof(ContextListEntry);
|
||||||
|
constexpr static usize ENTRIES_PER_BLOCK = 5;
|
||||||
|
constexpr static usize BLOCK_SIZE = ENTRIES_PER_BLOCK * ENTRY_SIZE;
|
||||||
|
|
||||||
|
public:
|
||||||
|
OrderlessContextPool()
|
||||||
|
: m_Device{nullptr}
|
||||||
|
, m_ContextPoolEntryMemory{ENTRY_SIZE, BLOCK_SIZE}
|
||||||
|
, m_QueueFamilyIndex{0}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Init(RenderingDevice &device, u32 const queueFamilyIndex)
|
||||||
|
{
|
||||||
|
m_Device = &device;
|
||||||
|
m_QueueFamilyIndex = queueFamilyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransferContext
|
||||||
|
CreateTransferContext()
|
||||||
|
requires std::derived_from<TContextPool, TransferContextPool>
|
||||||
|
{
|
||||||
|
if (!m_FreeContextPools.empty())
|
||||||
|
{
|
||||||
|
ContextListEntry &entry = m_FreeContextPools.back();
|
||||||
|
m_FreeContextPools.pop_back();
|
||||||
|
m_UsedContextPools.push_back(entry);
|
||||||
|
|
||||||
|
return entry.m_Pool.CreateTransferContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextListEntry &entry = *static_cast<ContextListEntry *>(m_ContextPoolEntryMemory.allocate_node());
|
||||||
|
auto pool = ContextPoolType{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice};
|
||||||
|
pool.m_ResetCallback = [this](ContextPool &resetPool) { this->ReleasePool(resetPool); };
|
||||||
|
new (&entry) ContextListEntry{
|
||||||
|
.m_Pool = eastl::move(pool),
|
||||||
|
};
|
||||||
|
m_UsedContextPools.push_back(entry);
|
||||||
|
|
||||||
|
return entry.m_Pool.CreateTransferContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComputeContext
|
||||||
|
CreateComputeContext()
|
||||||
|
requires std::derived_from<TContextPool, ComputeContextPool>
|
||||||
|
{
|
||||||
|
if (!m_FreeContextPools.empty())
|
||||||
|
{
|
||||||
|
ContextListEntry &entry = m_FreeContextPools.back();
|
||||||
|
m_FreeContextPools.pop_back();
|
||||||
|
m_UsedContextPools.push_back(entry);
|
||||||
|
|
||||||
|
return entry.m_Pool.CreateComputeContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextListEntry &entry = *static_cast<ContextListEntry *>(m_ContextPoolEntryMemory.allocate_node());
|
||||||
|
auto pool = ContextPoolType{*m_Device, m_QueueFamilyIndex, ContextPool::ManagedBy::eDevice};
|
||||||
|
pool.m_ResetCallback = [this](ContextPool &resetPool) { this->ReleasePool(resetPool); };
|
||||||
|
new (&entry) ContextListEntry{
|
||||||
|
.m_Pool = eastl::move(pool),
|
||||||
|
};
|
||||||
|
m_UsedContextPools.push_back(entry);
|
||||||
|
|
||||||
|
return entry.m_Pool.CreateComputeContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReleasePool(ContextPool &pool)
|
||||||
|
{
|
||||||
|
auto const found = eastl::find_if(m_UsedContextPools.begin(), m_UsedContextPools.end(),
|
||||||
|
[&pool](ContextListEntry const &v) { return v.Contains(pool); });
|
||||||
|
auto &v = *found;
|
||||||
|
ContextListType::remove(v);
|
||||||
|
|
||||||
|
pool.Reset();
|
||||||
|
|
||||||
|
m_FreeContextPools.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderlessContextPool(OrderlessContextPool &&other) noexcept
|
||||||
|
: m_Device{other.m_Device}
|
||||||
|
, m_ContextPoolEntryMemory{std::move(other.m_ContextPoolEntryMemory)}
|
||||||
|
, m_FreeContextPools{other.m_FreeContextPools}
|
||||||
|
, m_UsedContextPools{other.m_UsedContextPools}
|
||||||
|
, m_QueueFamilyIndex{other.m_QueueFamilyIndex}
|
||||||
|
{
|
||||||
|
other.m_FreeContextPools.clear();
|
||||||
|
other.m_UsedContextPools.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
OrderlessContextPool &
|
||||||
|
operator=(OrderlessContextPool &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
m_Device = other.m_Device;
|
||||||
|
m_ContextPoolEntryMemory = std::move(other.m_ContextPoolEntryMemory);
|
||||||
|
m_FreeContextPools = other.m_FreeContextPools;
|
||||||
|
other.m_FreeContextPools.clear();
|
||||||
|
m_UsedContextPools = other.m_UsedContextPools;
|
||||||
|
other.m_UsedContextPools.clear();
|
||||||
|
m_QueueFamilyIndex = other.m_QueueFamilyIndex;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~OrderlessContextPool()
|
||||||
|
{
|
||||||
|
for (auto &entry : m_FreeContextPools)
|
||||||
|
{
|
||||||
|
entry.m_Pool.~ContextPoolType();
|
||||||
|
}
|
||||||
|
for (auto &entry : m_UsedContextPools)
|
||||||
|
{
|
||||||
|
entry.m_Pool.~ContextPoolType();
|
||||||
|
}
|
||||||
|
// The allocations will 'wink' away.
|
||||||
|
}
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(OrderlessContextPool);
|
||||||
|
};
|
||||||
|
|
||||||
|
using OrderlessTransferContextPool = OrderlessContextPool<TransferContextPool>;
|
||||||
|
using OrderlessComputeContextPool = OrderlessContextPool<ComputeContextPool>;
|
||||||
|
|
||||||
|
} // namespace _internal
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: image_manager.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/image.h"
|
|
||||||
#include "manager.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
|
|
||||||
struct Texture2DCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
bool m_IsSampled = true;
|
|
||||||
bool m_IsMipMapped = false;
|
|
||||||
bool m_IsStorage = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureCubeCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
u32 m_Side = 0;
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
bool m_IsSampled = true;
|
|
||||||
bool m_IsMipMapped = false;
|
|
||||||
bool m_IsStorage = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AttachmentCreateInfo
|
|
||||||
{
|
|
||||||
vk::Format m_Format = vk::Format::eUndefined;
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DepthStencilImageCreateInfo
|
|
||||||
{
|
|
||||||
vk::Extent2D m_Extent = {};
|
|
||||||
cstr m_Name = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
using ImageHandle = Handle<Image>;
|
|
||||||
|
|
||||||
class ImageManager final : public Manager<Image>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ImageManager(const Device *device, const u32 maxCount, const u8 binding);
|
|
||||||
|
|
||||||
[[nodiscard]] Handle CreateTexture2D(const Texture2DCreateInfo &createInfo);
|
|
||||||
[[nodiscard]] Handle CreateTextureCube(const TextureCubeCreateInfo &createInfo);
|
|
||||||
[[nodiscard]] Handle CreateAttachment(const AttachmentCreateInfo &createInfo);
|
|
||||||
[[nodiscard]] Handle CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo);
|
|
||||||
};
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: manager.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "aster/core/type_traits.h"
|
|
||||||
|
|
||||||
struct Device;
|
|
||||||
|
|
||||||
template <concepts::RenderResource T>
|
|
||||||
class Handle;
|
|
||||||
|
|
||||||
template <concepts::RenderResource T>
|
|
||||||
class Manager
|
|
||||||
{
|
|
||||||
friend Handle<T>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using Type = T;
|
|
||||||
using Handle = Handle<Type>;
|
|
||||||
static_assert(sizeof(Handle) == sizeof(u32));
|
|
||||||
constexpr static u32 MAX_HANDLES = Handle::INDEX_MASK + 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the Manager class template.
|
|
||||||
* @param device Device with which resources are created.
|
|
||||||
* @param maxCount Max number of resources that can be created (maxCount <= Handle::INDEX_MASK)
|
|
||||||
* @param binding The shader binding at which this manager will bind its resources.
|
|
||||||
*/
|
|
||||||
explicit Manager(const Device *device, const u32 maxCount, const u8 binding)
|
|
||||||
: m_MaxCount{maxCount}
|
|
||||||
, m_Binding{binding}
|
|
||||||
, m_Device{device}
|
|
||||||
{
|
|
||||||
assert(!m_Instance);
|
|
||||||
assert(maxCount <= MAX_HANDLES);
|
|
||||||
|
|
||||||
m_Data = new Type[m_MaxCount];
|
|
||||||
m_RefCount = new std::atomic<u32>[m_MaxCount];
|
|
||||||
|
|
||||||
for (u32 i = 0; i < m_MaxCount; ++i)
|
|
||||||
{
|
|
||||||
*Recast<u32 *>(&m_Data[i]) = (i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Manager()
|
|
||||||
{
|
|
||||||
if (!m_Data)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < m_MaxCount; ++i)
|
|
||||||
{
|
|
||||||
m_Data[i].Destroy(m_Device);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete[] m_Data;
|
|
||||||
delete[] m_RefCount;
|
|
||||||
m_Data = nullptr;
|
|
||||||
m_RefCount = nullptr;
|
|
||||||
m_MaxCount = 0;
|
|
||||||
m_FreeHead = 0;
|
|
||||||
m_Device = nullptr;
|
|
||||||
|
|
||||||
m_Instance = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @warning only to be used internally.
|
|
||||||
* @return The only constructed instance of this manager.
|
|
||||||
*/
|
|
||||||
static Manager *
|
|
||||||
Instance()
|
|
||||||
{
|
|
||||||
assert(m_Instance);
|
|
||||||
return m_Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
PIN_MEMORY(Manager);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Type *m_Data = nullptr; // Data also keeps the freelist during 'not use'.
|
|
||||||
std::atomic<u32> *m_RefCount = nullptr; // Associated reference count for each of the instances in Data.
|
|
||||||
u32 m_MaxCount = 0; // Max number of resources supported.
|
|
||||||
u32 m_FreeHead = 0;
|
|
||||||
u8 m_Binding = 0;
|
|
||||||
|
|
||||||
static Manager *m_Instance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User is expected to type-check.
|
|
||||||
* @param index Actual index of the resource in the m_Data array. Not type checked.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
AddRef(const u32 index)
|
|
||||||
{
|
|
||||||
assert(index < m_MaxCount);
|
|
||||||
++m_RefCount[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User is expected to type-check.
|
|
||||||
* @param index Actual index of the resource in the m_Data array. Not type checked.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
Release(const u32 index)
|
|
||||||
{
|
|
||||||
assert(index < m_MaxCount);
|
|
||||||
const u32 rc = --m_RefCount[index];
|
|
||||||
assert(rc != MaxValue<u32>);
|
|
||||||
if (rc == 0)
|
|
||||||
{
|
|
||||||
// TODO: Don't destroy here. Separate out to a cleanup routine.
|
|
||||||
m_Data[index].Destroy(m_Device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User is expected to type-check.
|
|
||||||
* @param index Actual index of the resource in the m_Data array. Not type checked.
|
|
||||||
* @return Pointer to the resource at the index.
|
|
||||||
*/
|
|
||||||
Type *
|
|
||||||
Fetch(const u32 index)
|
|
||||||
{
|
|
||||||
assert(index < m_MaxCount);
|
|
||||||
return &m_Data[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const Device *m_Device;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal Method to Allocate a resource on the manager.
|
|
||||||
* @return [Handle, Type*] Where Type* is available to initialize the resource.
|
|
||||||
*/
|
|
||||||
[[nodiscard]] std::pair<Handle, Type *>
|
|
||||||
Alloc()
|
|
||||||
{
|
|
||||||
ERROR_IF(m_FreeHead >= m_MaxCount, "Max buffers allocated.") THEN_ABORT(-1);
|
|
||||||
|
|
||||||
const auto index = m_FreeHead;
|
|
||||||
Type *pAlloc = &m_Data[index];
|
|
||||||
m_FreeHead = *Recast<u32 *>(pAlloc);
|
|
||||||
return {Handle{index, m_Binding}, pAlloc};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <concepts::RenderResource T>
|
|
||||||
class Ref
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Type = T;
|
|
||||||
using Handle = Handle<Type>;
|
|
||||||
using Manager = Manager<Type>;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
Handle m_Handle;
|
|
||||||
Type *m_Pointer = nullptr;
|
|
||||||
|
|
||||||
friend Handle;
|
|
||||||
|
|
||||||
void
|
|
||||||
InitPtr()
|
|
||||||
{
|
|
||||||
m_Pointer = m_Handle.Fetch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Type *
|
|
||||||
Get()
|
|
||||||
{
|
|
||||||
assert(m_Pointer);
|
|
||||||
return m_Pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type *
|
|
||||||
Get() const
|
|
||||||
{
|
|
||||||
assert(m_Pointer);
|
|
||||||
return m_Pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type *
|
|
||||||
operator->()
|
|
||||||
{
|
|
||||||
return Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type *
|
|
||||||
operator->() const
|
|
||||||
{
|
|
||||||
return Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Type &
|
|
||||||
operator*()
|
|
||||||
{
|
|
||||||
return *Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const Type &
|
|
||||||
operator*() const
|
|
||||||
{
|
|
||||||
return Get();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only constructor requires a valid construction.
|
|
||||||
explicit Ref(Handle &&handle)
|
|
||||||
: m_Handle{std::forward<Handle>(handle)}
|
|
||||||
{
|
|
||||||
InitPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The only constructor requires a valid construction.
|
|
||||||
explicit Ref(const Handle &&handle)
|
|
||||||
: m_Handle{handle}
|
|
||||||
{
|
|
||||||
InitPtr();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref(const Ref &other) = default;
|
|
||||||
Ref(Ref &&other) noexcept = default;
|
|
||||||
Ref &operator=(const Ref &other) = default;
|
|
||||||
Ref &operator=(Ref &&other) noexcept = default;
|
|
||||||
~Ref() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RawHandle
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
|
|
||||||
constexpr static u32 INDEX_MASK = 0x0FFFFFFF;
|
|
||||||
constexpr static u32 TYPE_MASK = ~INDEX_MASK;
|
|
||||||
constexpr static u32 TYPE_OFFSET = GetMaskOffset(TYPE_MASK);
|
|
||||||
u32 m_Internal = INVALID_HANDLE;
|
|
||||||
|
|
||||||
RawHandle(const u32 index, const u8 typeId)
|
|
||||||
: m_Internal{(index & INDEX_MASK) | (typeId & TYPE_MASK)}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit RawHandle(const u32 internal)
|
|
||||||
: m_Internal{internal}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
[[nodiscard]] bool
|
|
||||||
IsValid() const
|
|
||||||
{
|
|
||||||
return m_Internal != INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32
|
|
||||||
GetIndex() const
|
|
||||||
{
|
|
||||||
return m_Internal & INDEX_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] u32
|
|
||||||
GetType() const
|
|
||||||
{
|
|
||||||
return (m_Internal & TYPE_MASK) >> TYPE_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
operator==(const RawHandle &other) const
|
|
||||||
{
|
|
||||||
return m_Internal == other.m_Internal;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <concepts::RenderResource T>
|
|
||||||
class Handle : public RawHandle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Type = T;
|
|
||||||
using Manager = Manager<Type>;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The only constructor requires a valid construction.
|
|
||||||
Handle(const u32 index, const u8 typeId)
|
|
||||||
: RawHandle{index, typeId}
|
|
||||||
{
|
|
||||||
AddRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
friend Manager;
|
|
||||||
friend Ref<T>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Handle(const Handle &other)
|
|
||||||
: RawHandle{other}
|
|
||||||
{
|
|
||||||
AddRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle(Handle &&other) noexcept
|
|
||||||
: RawHandle{std::exchange(other.m_Internal, m_Internal)}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Ref<T>
|
|
||||||
ToPointer()
|
|
||||||
{
|
|
||||||
return Ref{std::move(*this)};
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] Type *
|
|
||||||
Fetch() const
|
|
||||||
{
|
|
||||||
return Manager::Instance()->Fetch(m_Internal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle &
|
|
||||||
operator=(const Handle &other)
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
m_Internal = other.m_Internal;
|
|
||||||
AddRef();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle &
|
|
||||||
operator=(Handle &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
std::swap(m_Internal, other.m_Internal);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Handle()
|
|
||||||
{
|
|
||||||
if (m_Internal != INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
Release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void
|
|
||||||
AddRef()
|
|
||||||
{
|
|
||||||
Manager::Instance()->AddRef(GetIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Release()
|
|
||||||
{
|
|
||||||
Manager::Instance()->Release(GetIndex());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: pipeline_helpers.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <aster/aster.h>
|
||||||
|
|
||||||
|
#include <EASTL/vector.h>
|
||||||
|
#include <slang.h>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class RenderingDevice;
|
||||||
|
|
||||||
|
struct PipelineCreationError
|
||||||
|
{
|
||||||
|
std::variant<std::monostate, vk::Result, SlangResult> m_Data;
|
||||||
|
|
||||||
|
std::string What();
|
||||||
|
i32 Value();
|
||||||
|
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
PipelineCreationError(vk::Result res);
|
||||||
|
PipelineCreationError(SlangResult res);
|
||||||
|
PipelineCreationError();
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::ShaderStageFlagBits SlangToVulkanShaderStage(SlangStage stage);
|
||||||
|
|
||||||
|
namespace _internal
|
||||||
|
{
|
||||||
|
struct PipelineLayoutBuilder
|
||||||
|
{
|
||||||
|
RenderingDevice *m_Device;
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> m_DescriptorSetLayouts;
|
||||||
|
eastl::vector<vk::PushConstantRange> m_PushConstants;
|
||||||
|
vk::ShaderStageFlags m_Stage;
|
||||||
|
|
||||||
|
explicit PipelineLayoutBuilder(RenderingDevice *device, vk::DescriptorSetLayout bindlessLayout = {});
|
||||||
|
|
||||||
|
[[nodiscard]] vk::PipelineLayout Build();
|
||||||
|
|
||||||
|
[[nodiscard]] vk::DescriptorSetLayout
|
||||||
|
CreateDescriptorSetLayout(vk::DescriptorSetLayoutCreateInfo const &createInfo) const;
|
||||||
|
void AddDescriptorSetForParameterBlock(slang::TypeLayoutReflection *layout);
|
||||||
|
void AddPushConstantRangeForConstantBuffer(slang::TypeLayoutReflection *layout);
|
||||||
|
void AddSubObjectRange(slang::TypeLayoutReflection *layout, i64 subObjectRangeIndex);
|
||||||
|
void AddSubObjectRanges(slang::TypeLayoutReflection *layout);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DescriptorLayoutBuilder
|
||||||
|
{
|
||||||
|
PipelineLayoutBuilder *m_PipelineLayoutBuilder;
|
||||||
|
eastl::vector<vk::DescriptorSetLayoutBinding> m_LayoutBindings;
|
||||||
|
u32 m_SetIndex;
|
||||||
|
|
||||||
|
vk::ShaderStageFlags &Stage() const;
|
||||||
|
explicit DescriptorLayoutBuilder(PipelineLayoutBuilder *pipelineLayoutBuilder);
|
||||||
|
|
||||||
|
void AddGlobalScopeParameters(slang::ProgramLayout *layout);
|
||||||
|
void AddEntryPointParameters(slang::ProgramLayout *layout);
|
||||||
|
void AddEntryPointParameters(slang::EntryPointLayout *layout);
|
||||||
|
void AddAutomaticallyIntroducedUniformBuffer();
|
||||||
|
void AddRanges(slang::TypeLayoutReflection *layout);
|
||||||
|
void AddRangesForParamBlockElement(slang::TypeLayoutReflection *layout);
|
||||||
|
void AddDescriptorRange(slang::TypeLayoutReflection *layout, i64 relativeSetIndex, i64 rangeIndex);
|
||||||
|
void AddDescriptorRanges(slang::TypeLayoutReflection *layout);
|
||||||
|
void Build();
|
||||||
|
};
|
||||||
|
} // namespace _internal
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -1,157 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: render_resource_manager.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
#include "buffer_manager.h"
|
|
||||||
#include "image_manager.h"
|
|
||||||
|
|
||||||
#include "EASTL/deque.h"
|
|
||||||
#include "EASTL/vector.h"
|
|
||||||
|
|
||||||
namespace systems
|
|
||||||
{
|
|
||||||
|
|
||||||
class RenderResourceManager
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
union WriteInfo {
|
|
||||||
vk::DescriptorBufferInfo uBufferInfo;
|
|
||||||
vk::DescriptorImageInfo uImageInfo;
|
|
||||||
vk::BufferView uBufferView;
|
|
||||||
|
|
||||||
explicit WriteInfo(const vk::DescriptorBufferInfo &info);
|
|
||||||
explicit WriteInfo(const vk::DescriptorImageInfo &info);
|
|
||||||
explicit WriteInfo(const vk::BufferView &info);
|
|
||||||
};
|
|
||||||
|
|
||||||
using WriteCommand = vk::WriteDescriptorSet;
|
|
||||||
|
|
||||||
union WriteOwner {
|
|
||||||
Handle<Buffer> uBufferHandle;
|
|
||||||
Handle<Image> uImageHandle;
|
|
||||||
|
|
||||||
explicit WriteOwner(const Handle<Buffer> &handle);
|
|
||||||
explicit WriteOwner(const Handle<Image> &handle);
|
|
||||||
|
|
||||||
WriteOwner(const WriteOwner &other)
|
|
||||||
{
|
|
||||||
switch (uRawHandle.GetType())
|
|
||||||
{
|
|
||||||
case BUFFER_BINDING_INDEX:
|
|
||||||
uBufferHandle = other.uBufferHandle;
|
|
||||||
break;
|
|
||||||
case IMAGE_BINDING_INDEX:
|
|
||||||
uImageHandle = other.uImageHandle;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR("Invalid Handle type.") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOwner(WriteOwner &&other) noexcept
|
|
||||||
{
|
|
||||||
switch (uRawHandle.GetType())
|
|
||||||
{
|
|
||||||
case BUFFER_BINDING_INDEX:
|
|
||||||
uBufferHandle = std::move(other.uBufferHandle);
|
|
||||||
break;
|
|
||||||
case IMAGE_BINDING_INDEX:
|
|
||||||
uImageHandle = std::move(other.uImageHandle);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR("Invalid Handle type.") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOwner &
|
|
||||||
operator=(const WriteOwner &other)
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
switch (uRawHandle.GetType())
|
|
||||||
{
|
|
||||||
case BUFFER_BINDING_INDEX:
|
|
||||||
uBufferHandle = other.uBufferHandle;
|
|
||||||
break;
|
|
||||||
case IMAGE_BINDING_INDEX:
|
|
||||||
uImageHandle = other.uImageHandle;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR("Invalid Handle type.") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteOwner &
|
|
||||||
operator=(WriteOwner &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
|
|
||||||
switch (uRawHandle.GetType())
|
|
||||||
{
|
|
||||||
case BUFFER_BINDING_INDEX:
|
|
||||||
uBufferHandle = std::move(other.uBufferHandle);
|
|
||||||
break;
|
|
||||||
case IMAGE_BINDING_INDEX:
|
|
||||||
uImageHandle = std::move(other.uImageHandle);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ERROR("Invalid Handle type.") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~WriteOwner()
|
|
||||||
{
|
|
||||||
switch (uRawHandle.GetType())
|
|
||||||
{
|
|
||||||
case BUFFER_BINDING_INDEX:
|
|
||||||
uBufferHandle.~Handle();
|
|
||||||
return;
|
|
||||||
case IMAGE_BINDING_INDEX:
|
|
||||||
uImageHandle.~Handle();
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
ERROR("Invalid Handle type.") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
RawHandle uRawHandle;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
RenderResourceManager(const Device *device, u32 maxBuffers, u32 maxImages);
|
|
||||||
|
|
||||||
void Commit(concepts::HandleType auto &handle);
|
|
||||||
|
|
||||||
private:
|
|
||||||
BufferManager m_BufferManager;
|
|
||||||
ImageManager m_ImageManager;
|
|
||||||
|
|
||||||
vk::DescriptorPool m_DescriptorPool;
|
|
||||||
vk::DescriptorSetLayout m_SetLayout;
|
|
||||||
vk::DescriptorSet m_DescriptorSet;
|
|
||||||
|
|
||||||
constexpr static u8 BUFFER_BINDING_INDEX = 0;
|
|
||||||
constexpr static u8 IMAGE_BINDING_INDEX = 1;
|
|
||||||
|
|
||||||
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
|
||||||
eastl::deque<WriteInfo> m_WriteInfos;
|
|
||||||
eastl::vector<WriteOwner> m_WriteOwner;
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
usize m_CommitedBufferCount = 0;
|
|
||||||
usize m_CommitedTextureCount = 0;
|
|
||||||
usize m_CommitedStorageTextureCount = 0;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace systems
|
|
||||||
|
|
@ -0,0 +1,646 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: rendering_device.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "pipeline_helpers.h"
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
#include "aster/aster.h"
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/device.h"
|
||||||
|
#include "aster/core/image.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
#include "aster/core/instance.h"
|
||||||
|
#include "aster/core/physical_device.h"
|
||||||
|
#include "aster/core/pipeline.h"
|
||||||
|
#include "aster/core/sampler.h"
|
||||||
|
#include "aster/core/size.h"
|
||||||
|
#include "aster/core/swapchain.h"
|
||||||
|
|
||||||
|
#include <EASTL/hash_map.h>
|
||||||
|
#include <EASTL/optional.h>
|
||||||
|
#include <EASTL/variant.h>
|
||||||
|
|
||||||
|
#include <slang-com-ptr.h>
|
||||||
|
#include <slang.h>
|
||||||
|
|
||||||
|
constexpr static u32 MAX_FRAMES_IN_FLIGHT = 3;
|
||||||
|
|
||||||
|
struct Window;
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct eastl::hash<vk::SamplerCreateInfo>
|
||||||
|
{
|
||||||
|
usize
|
||||||
|
operator()(vk::SamplerCreateInfo const &createInfo) const noexcept
|
||||||
|
{
|
||||||
|
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(static_cast<usize>(createInfo.mipLodBias * 1000))); // Resolution of 10^-3
|
||||||
|
hash = HashCombine(hash, HashAny(createInfo.anisotropyEnable));
|
||||||
|
hash = HashCombine(
|
||||||
|
hash,
|
||||||
|
HashAny(static_cast<usize>(createInfo.maxAnisotropy * 0x20))); // 32:1 Anisotropy is enough resolution
|
||||||
|
hash = HashCombine(hash, HashAny(createInfo.compareEnable));
|
||||||
|
hash = HashCombine(hash, HashAny(createInfo.compareOp));
|
||||||
|
hash = HashCombine(hash, HashAny(static_cast<usize>(createInfo.minLod * 1000))); // 0.001 resolution is enough.
|
||||||
|
hash = HashCombine(
|
||||||
|
hash,
|
||||||
|
HashAny(static_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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Creation Structs
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Image
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct Texture2DCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
bool m_IsSampled = true;
|
||||||
|
bool m_IsMipMapped = false;
|
||||||
|
bool m_IsStorage = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextureCubeCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
u32 m_Side = 0;
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
bool m_IsSampled = true;
|
||||||
|
bool m_IsMipMapped = false;
|
||||||
|
bool m_IsStorage = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AttachmentCreateInfo
|
||||||
|
{
|
||||||
|
vk::Format m_Format = vk::Format::eUndefined;
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DepthStencilImageCreateInfo
|
||||||
|
{
|
||||||
|
vk::Extent2D m_Extent = {};
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region View
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::AnyImage TImage>
|
||||||
|
struct ViewCreateInfo
|
||||||
|
{
|
||||||
|
using ImageType = TImage;
|
||||||
|
|
||||||
|
Ref<ImageType> m_Image;
|
||||||
|
cstr m_Name;
|
||||||
|
vk::ImageViewType m_ViewType = vk::ImageViewType::e2D;
|
||||||
|
vk::ComponentMapping m_Components = {};
|
||||||
|
vk::ImageAspectFlags m_AspectMask = {};
|
||||||
|
eastl::optional<u8> m_MipLevelCount = eastl::nullopt;
|
||||||
|
eastl::optional<u8> m_LayerCount = eastl::nullopt;
|
||||||
|
u8 m_BaseMipLevel = 0;
|
||||||
|
u8 m_BaseLayer = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] u8
|
||||||
|
GetMipLevelCount() const
|
||||||
|
{
|
||||||
|
return m_MipLevelCount.value_or(m_Image->m_MipLevels - m_BaseMipLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] u8
|
||||||
|
GetLayerCount() const
|
||||||
|
{
|
||||||
|
return m_LayerCount.value_or(m_Image->m_LayerCount - m_BaseLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator vk::ImageViewCreateInfo() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.image = m_Image->m_Image,
|
||||||
|
.viewType = m_ViewType,
|
||||||
|
.format = m_Image->m_Format,
|
||||||
|
.components = m_Components,
|
||||||
|
.subresourceRange =
|
||||||
|
{
|
||||||
|
.aspectMask = m_AspectMask,
|
||||||
|
.baseMipLevel = m_BaseMipLevel,
|
||||||
|
.levelCount = GetMipLevelCount(),
|
||||||
|
.baseArrayLayer = m_BaseLayer,
|
||||||
|
.layerCount = GetLayerCount(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator ViewCreateInfo<Image>() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.m_Image = CastImage<Image>(m_Image),
|
||||||
|
.m_Name = m_Name,
|
||||||
|
.m_ViewType = m_ViewType,
|
||||||
|
.m_Components = m_Components,
|
||||||
|
.m_AspectMask = m_AspectMask,
|
||||||
|
.m_MipLevelCount = m_MipLevelCount,
|
||||||
|
.m_LayerCount = m_LayerCount,
|
||||||
|
.m_BaseMipLevel = m_BaseMipLevel,
|
||||||
|
.m_BaseLayer = m_BaseLayer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Sampler
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct SamplerCreateInfo
|
||||||
|
{
|
||||||
|
cstr m_Name = nullptr;
|
||||||
|
vk::SamplerCreateFlags m_Flags = {};
|
||||||
|
vk::Filter m_MagFilter = vk::Filter::eLinear;
|
||||||
|
vk::Filter m_MinFilter = vk::Filter::eLinear;
|
||||||
|
vk::SamplerMipmapMode m_MipmapMode = vk::SamplerMipmapMode::eLinear;
|
||||||
|
vk::SamplerAddressMode m_AddressModeU = vk::SamplerAddressMode::eRepeat;
|
||||||
|
vk::SamplerAddressMode m_AddressModeV = vk::SamplerAddressMode::eRepeat;
|
||||||
|
vk::SamplerAddressMode m_AddressModeW = vk::SamplerAddressMode::eRepeat;
|
||||||
|
vk::BorderColor m_BorderColor = vk::BorderColor::eFloatOpaqueBlack;
|
||||||
|
vk::CompareOp m_CompareOp = vk::CompareOp::eNever;
|
||||||
|
f32 m_MipLodBias = 0.0f;
|
||||||
|
f32 m_MaxAnisotropy = 16.0f;
|
||||||
|
f32 m_MinLod = 0;
|
||||||
|
f32 m_MaxLod = VK_LOD_CLAMP_NONE;
|
||||||
|
bool m_AnisotropyEnable = true;
|
||||||
|
bool m_CompareEnable = false;
|
||||||
|
bool m_NormalizedCoordinates = true;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
operator vk::SamplerCreateInfo() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.flags = m_Flags,
|
||||||
|
.magFilter = m_MagFilter,
|
||||||
|
.minFilter = m_MinFilter,
|
||||||
|
.mipmapMode = m_MipmapMode,
|
||||||
|
.addressModeU = m_AddressModeU,
|
||||||
|
.addressModeV = m_AddressModeV,
|
||||||
|
.addressModeW = m_AddressModeW,
|
||||||
|
.mipLodBias = m_MipLodBias,
|
||||||
|
.anisotropyEnable = m_AnisotropyEnable,
|
||||||
|
.maxAnisotropy = m_MaxAnisotropy,
|
||||||
|
.compareEnable = m_CompareEnable,
|
||||||
|
.compareOp = m_CompareOp,
|
||||||
|
.minLod = m_MinLod,
|
||||||
|
.maxLod = m_MaxLod,
|
||||||
|
.borderColor = m_BorderColor,
|
||||||
|
.unnormalizedCoordinates = !m_NormalizedCoordinates,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Pipeline
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct AttributeInfo
|
||||||
|
{
|
||||||
|
u32 m_Location;
|
||||||
|
u32 m_Offset;
|
||||||
|
|
||||||
|
enum class Format
|
||||||
|
{
|
||||||
|
eFloat32X4,
|
||||||
|
eFloat32X3,
|
||||||
|
eFloat32X2,
|
||||||
|
eFloat32,
|
||||||
|
} m_Format;
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Format
|
||||||
|
GetFormat() const
|
||||||
|
{
|
||||||
|
switch (m_Format)
|
||||||
|
{
|
||||||
|
case Format::eFloat32X4:
|
||||||
|
return vk::Format::eR32G32B32A32Sfloat;
|
||||||
|
case Format::eFloat32X3:
|
||||||
|
return vk::Format::eR32G32B32Sfloat;
|
||||||
|
case Format::eFloat32X2:
|
||||||
|
return vk::Format::eR32G32Sfloat;
|
||||||
|
case Format::eFloat32:
|
||||||
|
return vk::Format::eR32Sfloat;
|
||||||
|
}
|
||||||
|
return vk::Format::eUndefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VertexInput
|
||||||
|
{
|
||||||
|
eastl::vector<AttributeInfo> m_Attribute;
|
||||||
|
u32 m_Stride;
|
||||||
|
bool m_IsPerInstance;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ShaderType
|
||||||
|
{
|
||||||
|
eInvalid = 0,
|
||||||
|
eVertex = VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
eTesselationControl = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
|
||||||
|
eTesselationEvaluation = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
|
||||||
|
eGeometry = VK_SHADER_STAGE_GEOMETRY_BIT,
|
||||||
|
eFragment = VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
eCompute = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||||
|
eTask = VK_SHADER_STAGE_TASK_BIT_EXT,
|
||||||
|
eMesh = VK_SHADER_STAGE_MESH_BIT_EXT,
|
||||||
|
eMax,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static u32 ShaderTypeCount = 8;
|
||||||
|
|
||||||
|
static_assert(static_cast<u32>(ShaderType::eMax) == 1 + (1 << (ShaderTypeCount - 1)));
|
||||||
|
|
||||||
|
struct ShaderInfo
|
||||||
|
{
|
||||||
|
std::string_view m_ShaderFile;
|
||||||
|
eastl::vector<std::string_view> m_EntryPoints;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GraphicsPipelineCreateInfo
|
||||||
|
{
|
||||||
|
enum class DepthTest
|
||||||
|
{
|
||||||
|
eEnabled,
|
||||||
|
eReadOnly,
|
||||||
|
eDisabled,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CompareOp
|
||||||
|
{
|
||||||
|
eNever = 0x0,
|
||||||
|
eLessThan = 0x1,
|
||||||
|
eEqualTo = 0x2,
|
||||||
|
eGreaterThan = 0x4,
|
||||||
|
eLessThanOrEqualTo = eLessThan | eEqualTo,
|
||||||
|
eGreaterThanOrEqualTo = eGreaterThan | eEqualTo,
|
||||||
|
eNotEqualTo = eLessThan | eGreaterThan,
|
||||||
|
eAlways = eLessThan | eEqualTo | eGreaterThan,
|
||||||
|
};
|
||||||
|
|
||||||
|
eastl::fixed_vector<VertexInput, 4, false> m_VertexInputs;
|
||||||
|
eastl::fixed_vector<ShaderInfo, 4, false> m_Shaders;
|
||||||
|
|
||||||
|
DepthTest m_DepthTest = DepthTest::eEnabled;
|
||||||
|
CompareOp m_DepthOp = CompareOp::eLessThan;
|
||||||
|
|
||||||
|
cstr m_Name;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend RenderingDevice;
|
||||||
|
[[nodiscard]] vk::PipelineDepthStencilStateCreateInfo GetDepthStencilStateCreateInfo() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ComputePipelineCreateInfo
|
||||||
|
{
|
||||||
|
ShaderInfo m_Shader;
|
||||||
|
cstr m_Name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
#pragma region Device
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PhysicalDevice DefaultPhysicalDeviceSelector(PhysicalDevices const &physicalDevices);
|
||||||
|
|
||||||
|
using PhysicalDeviceSelectorFn = PhysicalDevice (*)(PhysicalDevices const &);
|
||||||
|
static_assert(std::convertible_to<decltype(DefaultPhysicalDeviceSelector), PhysicalDeviceSelectorFn>);
|
||||||
|
|
||||||
|
struct DeviceCreateInfo
|
||||||
|
{
|
||||||
|
std::reference_wrapper<Window> m_Window;
|
||||||
|
Features m_Features;
|
||||||
|
cstr m_AppName = "Aster App";
|
||||||
|
Version m_AppVersion = {0, 1, 0};
|
||||||
|
PhysicalDeviceSelectorFn m_PhysicalDeviceSelector = DefaultPhysicalDeviceSelector;
|
||||||
|
std::span<u8> m_PipelineCacheData = {};
|
||||||
|
eastl::vector<cstr> m_ShaderSearchPaths;
|
||||||
|
bool m_UseBindless = true;
|
||||||
|
cstr m_Name = "Primary";
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
namespace _internal
|
||||||
|
{
|
||||||
|
class SyncServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Receipt
|
||||||
|
{
|
||||||
|
void *m_Opaque;
|
||||||
|
|
||||||
|
explicit Receipt(void *opaque)
|
||||||
|
: m_Opaque{opaque}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
friend _internal::SyncServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Frame
|
||||||
|
{
|
||||||
|
// Persistent
|
||||||
|
RenderingDevice *m_Device;
|
||||||
|
|
||||||
|
// TODO: ThreadSafe
|
||||||
|
_internal::GraphicsContextPool m_PrimaryPool;
|
||||||
|
_internal::TransferContextPool m_AsyncTransferPool;
|
||||||
|
_internal::ComputeContextPool m_AsyncComputePool;
|
||||||
|
|
||||||
|
vk::Fence m_FrameAvailableFence;
|
||||||
|
vk::Semaphore m_ImageAcquireSem;
|
||||||
|
vk::Semaphore m_RenderFinishSem;
|
||||||
|
u32 m_FrameIdx;
|
||||||
|
|
||||||
|
// Transient
|
||||||
|
vk::Image m_SwapchainImage;
|
||||||
|
vk::ImageView m_SwapchainImageView;
|
||||||
|
Size2D m_SwapchainSize;
|
||||||
|
u32 m_ImageIdx;
|
||||||
|
|
||||||
|
void Reset(u32 imageIdx, vk::Image swapchainImage, vk::ImageView swapchainImageView, Size2D swapchainSize);
|
||||||
|
GraphicsContext CreateGraphicsContext();
|
||||||
|
TransferContext CreateAsyncTransferContext();
|
||||||
|
ComputeContext CreateAsyncComputeContext();
|
||||||
|
void WaitUntilReady();
|
||||||
|
|
||||||
|
Frame() = default;
|
||||||
|
Frame(RenderingDevice &device, u32 frameIndex, u32 primaryQueueFamily, u32 asyncTransferQueue,
|
||||||
|
u32 asyncComputeQueue);
|
||||||
|
Frame(Frame &&other) noexcept;
|
||||||
|
|
||||||
|
Frame &operator=(Frame &&other) noexcept;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Frame);
|
||||||
|
|
||||||
|
~Frame() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CommitManager;
|
||||||
|
|
||||||
|
class RenderingDevice final
|
||||||
|
{
|
||||||
|
public: // TODO: Temp
|
||||||
|
std::reference_wrapper<Window> m_Window;
|
||||||
|
Instance m_Instance;
|
||||||
|
Surface m_Surface;
|
||||||
|
Device m_Device;
|
||||||
|
Swapchain m_Swapchain;
|
||||||
|
std::unique_ptr<CommitManager> m_CommitManager;
|
||||||
|
|
||||||
|
// TODO: This is single-threaded.
|
||||||
|
vk::Queue m_PrimaryQueue;
|
||||||
|
u32 m_PrimaryQueueFamily;
|
||||||
|
|
||||||
|
vk::Queue m_TransferQueue;
|
||||||
|
u32 m_TransferQueueFamily;
|
||||||
|
|
||||||
|
vk::Queue m_ComputeQueue;
|
||||||
|
u32 m_ComputeQueueFamily;
|
||||||
|
|
||||||
|
_internal::OrderlessTransferContextPool m_TransferContextPool;
|
||||||
|
_internal::OrderlessComputeContextPool m_ComputeContextPool;
|
||||||
|
|
||||||
|
std::array<Frame, MAX_FRAMES_IN_FLIGHT> m_Frames;
|
||||||
|
u32 m_CurrentFrameIdx = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// ====================================================================================================
|
||||||
|
// Resource Management
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
//
|
||||||
|
// Buffer Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<StorageBuffer> CreateStorageBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<IndexBuffer> CreateIndexBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<UniformBuffer> CreateUniformBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<StagingBuffer> CreateStagingBuffer(usize size, cstr name = nullptr);
|
||||||
|
[[nodiscard]] Ref<VertexBuffer> CreateVertexBuffer(usize size, cstr name = nullptr);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Image Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::ImageInto<Texture> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTexture2D(Texture2DCreateInfo const &createInfo)
|
||||||
|
{
|
||||||
|
return CastImage<T>(CreateTexture2D(createInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::ImageInto<TextureCube> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTextureCube(TextureCubeCreateInfo const &createInfo)
|
||||||
|
{
|
||||||
|
return CastImage<T>(CreateTextureCube(createInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<Image> CreateTexture2D(Texture2DCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageCube> CreateTextureCube(TextureCubeCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<Image> CreateAttachment(AttachmentCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<Image> CreateDepthStencilImage(DepthStencilImageCreateInfo const &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// View Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::View TImageView>
|
||||||
|
Ref<TImageView>
|
||||||
|
CreateView(ViewCreateInfo<typename TImageView::ImageType> const &createInfo)
|
||||||
|
{
|
||||||
|
return CastView<TImageView>(CreateView(ViewCreateInfo<Image>(createInfo)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateView(ViewCreateInfo<Image> const &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Image - View Combined Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::ViewTo<Image> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTexture2DWithView(Texture2DCreateInfo const &createInfo)
|
||||||
|
{
|
||||||
|
auto handle = CreateTexture2DWithView(createInfo);
|
||||||
|
return CastView<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <concepts::ViewTo<ImageCube> T>
|
||||||
|
[[nodiscard]] Ref<T>
|
||||||
|
CreateTextureCubeWithView(TextureCubeCreateInfo const &createInfo)
|
||||||
|
{
|
||||||
|
auto handle = CreateTextureCubeWithView(createInfo);
|
||||||
|
return CastView<T>(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Ref<TextureView> CreateTexture2DWithView(Texture2DCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageCubeView> CreateTextureCubeWithView(TextureCubeCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateAttachmentWithView(AttachmentCreateInfo const &createInfo);
|
||||||
|
[[nodiscard]] Ref<ImageView> CreateDepthStencilImageWithView(DepthStencilImageCreateInfo const &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sampler Management
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private:
|
||||||
|
eastl::hash_map<vk::SamplerCreateInfo, WeakRef<Sampler>> m_HashToSamplerIdx;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Ref<Sampler> CreateSampler(SamplerCreateInfo const &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Pipeline
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// TODO: Cache shader modules for reuse. Time to move to `slang`
|
||||||
|
private:
|
||||||
|
Slang::ComPtr<slang::IGlobalSession> m_GlobalSlangSession;
|
||||||
|
Slang::ComPtr<slang::ISession> m_SlangSession;
|
||||||
|
|
||||||
|
PipelineCreationError
|
||||||
|
CreateShaders(eastl::fixed_vector<vk::PipelineShaderStageCreateInfo, ShaderTypeCount, false> &shadersOut,
|
||||||
|
Slang::ComPtr<slang::IComponentType> &program, std::span<ShaderInfo const> const &shaders);
|
||||||
|
PipelineCreationError
|
||||||
|
CreatePipelineLayout(vk::PipelineLayout &pipelineLayout, Slang::ComPtr<slang::IComponentType> const &program);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Pipelines, unlike the other resources, are not ref-counted.
|
||||||
|
PipelineCreationError CreateGraphicsPipeline(Pipeline &pipeline, GraphicsPipelineCreateInfo const &createInfo);
|
||||||
|
PipelineCreationError CreateComputePipeline(Pipeline &pipeline, ComputePipelineCreateInfo const &createInfo);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Frames
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public:
|
||||||
|
Frame &GetNextFrame();
|
||||||
|
|
||||||
|
Size2D
|
||||||
|
GetSwapchainSize() const
|
||||||
|
{
|
||||||
|
return {m_Swapchain.m_Extent.width, m_Swapchain.m_Extent.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RegisterResizeCallback(Swapchain::FnResizeCallback &&callback)
|
||||||
|
{
|
||||||
|
m_Swapchain.RegisterResizeCallback(eastl::forward<Swapchain::FnResizeCallback>(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Present(Frame &frame, GraphicsContext &graphicsContext);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Context
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
friend Context;
|
||||||
|
friend GraphicsContext;
|
||||||
|
friend TransferContext;
|
||||||
|
|
||||||
|
TransferContext CreateTransferContext();
|
||||||
|
ComputeContext CreateComputeContext();
|
||||||
|
Receipt Submit(Context &context);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sync
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
std::unique_ptr<_internal::SyncServer> m_SyncServer;
|
||||||
|
void WaitOn(Receipt recpt);
|
||||||
|
|
||||||
|
//
|
||||||
|
// RenderingDevice Methods
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::VkHandle T>
|
||||||
|
void
|
||||||
|
SetName(T const &object, cstr name) const
|
||||||
|
{
|
||||||
|
m_Device.SetName(object, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Queue
|
||||||
|
GetQueue(u32 const familyIndex, u32 const queueIndex) const
|
||||||
|
{
|
||||||
|
return m_Device.GetQueue(familyIndex, queueIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] eastl::vector<u8>
|
||||||
|
DumpPipelineCache() const
|
||||||
|
{
|
||||||
|
return m_Device.DumpPipelineCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WaitIdle() const
|
||||||
|
{
|
||||||
|
m_Device.WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inner
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[[nodiscard]] Device &
|
||||||
|
GetInner()
|
||||||
|
{
|
||||||
|
return m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vk::Device &
|
||||||
|
GetHandle()
|
||||||
|
{
|
||||||
|
return m_Device.m_Device;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctor/Dtor
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
explicit RenderingDevice(DeviceCreateInfo const &createInfo);
|
||||||
|
|
||||||
|
~RenderingDevice();
|
||||||
|
|
||||||
|
PIN_MEMORY(RenderingDevice);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: resource.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/core/image.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
|
||||||
|
#include <EASTL/intrusive_ptr.h>
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
|
||||||
|
// ====================================================================================================
|
||||||
|
#pragma region Util Methods
|
||||||
|
// ====================================================================================================
|
||||||
|
|
||||||
|
#pragma region Buffer
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <std::derived_from<Buffer> TTo, std::derived_from<Buffer> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastBuffer(Ref<TFrom> const &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::BufferInto<TFrom, TTo>)
|
||||||
|
assert(TTo::FLAGS & from->m_Flags);
|
||||||
|
return eastl::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region Image
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <std::derived_from<Image> TTo, std::derived_from<Image> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastImage(Ref<TFrom> const &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::ImageInto<TFrom, TTo>)
|
||||||
|
assert(TTo::FLAGS & from->m_Flags_);
|
||||||
|
return eastl::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region View
|
||||||
|
// ----------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <concepts::View TTo, std::derived_from<Image> TFrom>
|
||||||
|
static Ref<TTo>
|
||||||
|
CastView(Ref<View<TFrom>> const &from)
|
||||||
|
{
|
||||||
|
if constexpr (not concepts::ImageInto<TFrom, typename TTo::ImageType>)
|
||||||
|
assert(TTo::ImageType::FLAGS & from->m_Image->m_Flags_);
|
||||||
|
return eastl::reinterpret_pointer_cast<TTo>(from);
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ResId manages the lifetime of the committed resource.
|
||||||
|
* @tparam T Type of the committed resource.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
class ResId
|
||||||
|
{
|
||||||
|
using IdType = u32;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr static IdType INVALID = MaxValue<IdType>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IdType m_Index;
|
||||||
|
u32 m_Padding = 0; //< Slang DescriptorHandle are a pair of u32. TODO: Use as validation.
|
||||||
|
|
||||||
|
explicit ResId(IdType const index)
|
||||||
|
: m_Index{index}
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class CommitManager;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ResId
|
||||||
|
Null()
|
||||||
|
{
|
||||||
|
return ResId{INVALID};
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId(ResId const &other)
|
||||||
|
: m_Index{other.m_Index}
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId(ResId &&other) noexcept
|
||||||
|
: m_Index{other.m_Index}
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId &
|
||||||
|
operator=(ResId const &other)
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
m_Index = other.m_Index;
|
||||||
|
AddRef();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId &
|
||||||
|
operator=(ResId &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
m_Index = other.m_Index;
|
||||||
|
AddRef();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ResId()
|
||||||
|
{
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AddRef() const; ///< Increases the refcount in the CommitManager.
|
||||||
|
void Release() const; ///< Decreases the refcount in the CommitManager.
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NullId
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
operator ResId<T>()
|
||||||
|
{
|
||||||
|
return ResId<T>::Null();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace systems
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: sync_server.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "aster/aster.h"
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
#include <EASTL/deque.h>
|
||||||
|
#include <EASTL/intrusive_list.h>
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class Receipt;
|
||||||
|
class RenderingDevice;
|
||||||
|
} // namespace systems
|
||||||
|
|
||||||
|
namespace systems::_internal
|
||||||
|
{
|
||||||
|
struct TimelinePoint
|
||||||
|
{
|
||||||
|
u64 m_WaitValue;
|
||||||
|
u64 m_NextValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SyncServer
|
||||||
|
{
|
||||||
|
struct Entry : eastl::intrusive_list_node
|
||||||
|
{
|
||||||
|
vk::Semaphore m_Semaphore;
|
||||||
|
TimelinePoint m_CurrentPoint;
|
||||||
|
ContextPool *m_AttachedPool;
|
||||||
|
|
||||||
|
explicit Entry(RenderingDevice &device);
|
||||||
|
void Destroy(RenderingDevice &device);
|
||||||
|
void Wait(RenderingDevice &device);
|
||||||
|
void Next();
|
||||||
|
void AttachPool(ContextPool *pool);
|
||||||
|
|
||||||
|
Entry(Entry &&) = default;
|
||||||
|
Entry &operator=(Entry &&) = default;
|
||||||
|
|
||||||
|
~Entry() = default;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(Entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
RenderingDevice *m_Device;
|
||||||
|
eastl::deque<Entry> m_Allocations;
|
||||||
|
eastl::intrusive_list<Entry> m_FreeList;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Receipt Allocate();
|
||||||
|
void Free(Receipt);
|
||||||
|
|
||||||
|
void WaitOn(Receipt);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Entry &GetEntry(Receipt receipt);
|
||||||
|
|
||||||
|
// Inner Alloc/Free functions.
|
||||||
|
Entry &AllocateEntry();
|
||||||
|
void FreeEntry(Entry &entry);
|
||||||
|
|
||||||
|
// Constructor/Destructor
|
||||||
|
explicit SyncServer(RenderingDevice &device);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~SyncServer();
|
||||||
|
|
||||||
|
// Move Constructors.
|
||||||
|
SyncServer(SyncServer &&other) noexcept;
|
||||||
|
SyncServer &operator=(SyncServer &&other) noexcept;
|
||||||
|
|
||||||
|
friend RenderingDevice;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(SyncServer);
|
||||||
|
};
|
||||||
|
} // namespace systems::_internal
|
||||||
|
|
@ -3,4 +3,7 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
INTERFACE "logger.h")
|
INTERFACE
|
||||||
|
"logger.h"
|
||||||
|
"freelist.h"
|
||||||
|
"files.h")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: files.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "aster/core/constants.h"
|
||||||
|
|
||||||
|
#include <EASTL/span.h>
|
||||||
|
#include <EASTL/vector.h>
|
||||||
|
|
||||||
|
eastl::vector<u32> ReadFile(std::string_view fileName);
|
||||||
|
eastl::vector<u8> ReadFileBytes(std::string_view fileName, bool errorOnFail = true);
|
||||||
|
bool WriteFileBytes(std::string_view fileName, eastl::span<u8> data);
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: freelist.h
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
struct FreeListNode
|
||||||
|
{
|
||||||
|
FreeListNode *m_Next;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept FreeListCapable = sizeof(T) >= sizeof(FreeListNode);
|
||||||
|
|
||||||
|
template <FreeListCapable T>
|
||||||
|
struct FreeList
|
||||||
|
{
|
||||||
|
using Value = T;
|
||||||
|
using Reference = T &;
|
||||||
|
using ConstReference = T const &;
|
||||||
|
using Pointer = T *;
|
||||||
|
|
||||||
|
FreeListNode *m_Top;
|
||||||
|
|
||||||
|
FreeList()
|
||||||
|
: m_Top{nullptr}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeList(FreeList &&other) noexcept
|
||||||
|
: m_Top{Take(other.m_Top)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FreeList &
|
||||||
|
operator=(FreeList &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
m_Top = Take(other.m_Top);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(FreeList);
|
||||||
|
|
||||||
|
~FreeList()
|
||||||
|
{
|
||||||
|
m_Top = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool
|
||||||
|
Empty() const
|
||||||
|
{
|
||||||
|
return !m_Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Reference
|
||||||
|
Pop()
|
||||||
|
{
|
||||||
|
assert(m_Top);
|
||||||
|
Reference ref = *reinterpret_cast<Pointer>(m_Top);
|
||||||
|
m_Top = m_Top->m_Next;
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Push(Reference ref)
|
||||||
|
{
|
||||||
|
auto next = reinterpret_cast<FreeListNode *>(&ref);
|
||||||
|
next->m_Next = m_Top;
|
||||||
|
m_Top = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ConstReference
|
||||||
|
Peek() const
|
||||||
|
{
|
||||||
|
assert(m_Top);
|
||||||
|
return *m_Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Reference
|
||||||
|
Peek()
|
||||||
|
{
|
||||||
|
assert(m_Top);
|
||||||
|
return *m_Top;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Clear()
|
||||||
|
{
|
||||||
|
m_Top = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -20,16 +20,16 @@ struct Logger
|
||||||
eVerbose,
|
eVerbose,
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 m_MinimumLoggingLevel{Cast<u32>(LogType::eDebug)};
|
u32 m_MinimumLoggingLevel{static_cast<u32>(LogType::eDebug)};
|
||||||
|
|
||||||
void
|
void
|
||||||
SetMinimumLoggingLevel(LogType logType)
|
SetMinimumLoggingLevel(LogType logType)
|
||||||
{
|
{
|
||||||
m_MinimumLoggingLevel = Cast<u32>(logType);
|
m_MinimumLoggingLevel = static_cast<u32>(logType);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
constexpr static const char *
|
constexpr static char const *
|
||||||
ToCstr()
|
ToCstr()
|
||||||
{
|
{
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
if constexpr (TLogLevel == LogType::eError)
|
||||||
|
|
@ -45,7 +45,7 @@ struct Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
constexpr static const char *
|
constexpr static char const *
|
||||||
ToColorCstr()
|
ToColorCstr()
|
||||||
{
|
{
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
if constexpr (TLogLevel == LogType::eError)
|
||||||
|
|
@ -62,9 +62,9 @@ struct Logger
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
void
|
void
|
||||||
Log(const std::string_view &message, const char *loc, u32 line) const
|
Log(std::string_view const &message, char const *loc, u32 line) const
|
||||||
{
|
{
|
||||||
if (Cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
if (static_cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
||||||
{
|
{
|
||||||
fmt::println("{}{} {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), message.data(),
|
fmt::println("{}{} {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), message.data(),
|
||||||
ansi_color::Black, loc, line, ansi_color::Reset);
|
ansi_color::Black, loc, line, ansi_color::Reset);
|
||||||
|
|
@ -79,9 +79,9 @@ struct Logger
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
template <LogType TLogLevel>
|
||||||
void
|
void
|
||||||
LogCond(const char *exprStr, const std::string_view &message, const char *loc, u32 line) const
|
LogCond(char const *exprStr, std::string_view const &message, char const *loc, u32 line) const
|
||||||
{
|
{
|
||||||
if (Cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
if (static_cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
||||||
{
|
{
|
||||||
fmt::println("{}{} ({}) {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), exprStr,
|
fmt::println("{}{} ({}) {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), exprStr,
|
||||||
message.data(), ansi_color::Black, loc, line, ansi_color::Reset);
|
message.data(), ansi_color::Black, loc, line, ansi_color::Reset);
|
||||||
|
|
@ -103,26 +103,26 @@ extern Logger g_Logger;
|
||||||
#define INFO(...) g_Logger.Log<Logger::LogType::eInfo>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
#define INFO(...) g_Logger.Log<Logger::LogType::eInfo>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
|
|
||||||
#define ERROR_IF(expr, ...) \
|
#define ERROR_IF(expr, ...) \
|
||||||
if (Cast<bool>(expr)) [[unlikely]] \
|
if (static_cast<bool>(expr)) [[unlikely]] \
|
||||||
g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define WARN_IF(expr, ...) \
|
#define WARN_IF(expr, ...) \
|
||||||
if (Cast<bool>(expr)) [[unlikely]] \
|
if (static_cast<bool>(expr)) [[unlikely]] \
|
||||||
g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define INFO_IF(expr, ...) \
|
#define INFO_IF(expr, ...) \
|
||||||
if (Cast<bool>(expr)) \
|
if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
|
|
||||||
#define ELSE_IF_ERROR(expr, ...) \
|
#define ELSE_IF_ERROR(expr, ...) \
|
||||||
; \
|
; \
|
||||||
else if (Cast<bool>(expr)) \
|
else if (static_cast<bool>(expr)) \
|
||||||
[[unlikely]] g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
[[unlikely]] g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_IF_WARN(expr, ...) \
|
#define ELSE_IF_WARN(expr, ...) \
|
||||||
; \
|
; \
|
||||||
else if (Cast<bool>(expr)) \
|
else if (static_cast<bool>(expr)) \
|
||||||
[[unlikely]] g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
[[unlikely]] g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_IF_INFO(expr, ...) \
|
#define ELSE_IF_INFO(expr, ...) \
|
||||||
; \
|
; \
|
||||||
else if (Cast<bool>(expr)) \
|
else if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
|
|
||||||
#define ELSE_ERROR(...) \
|
#define ELSE_ERROR(...) \
|
||||||
|
|
@ -139,11 +139,11 @@ extern Logger g_Logger;
|
||||||
|
|
||||||
#define DEBUG(...) g_Logger.Log<Logger::LogType::eDebug>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
#define DEBUG(...) g_Logger.Log<Logger::LogType::eDebug>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define DEBUG_IF(expr, ...) \
|
#define DEBUG_IF(expr, ...) \
|
||||||
if (Cast<bool>(expr)) \
|
if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_IF_DEBUG(expr, ...) \
|
#define ELSE_IF_DEBUG(expr, ...) \
|
||||||
; \
|
; \
|
||||||
else if (Cast<bool>(expr)) \
|
else if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_DEBUG(...) \
|
#define ELSE_DEBUG(...) \
|
||||||
; \
|
; \
|
||||||
|
|
@ -174,11 +174,11 @@ extern Logger g_Logger;
|
||||||
|
|
||||||
#define VERBOSE(...) g_Logger.Log<Logger::LogType::eVerbose>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
#define VERBOSE(...) g_Logger.Log<Logger::LogType::eVerbose>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define VERBOSE_IF(expr, ...) \
|
#define VERBOSE_IF(expr, ...) \
|
||||||
if (Cast<bool>(expr)) \
|
if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_IF_VERBOSE(expr, ...) \
|
#define ELSE_IF_VERBOSE(expr, ...) \
|
||||||
; \
|
; \
|
||||||
else if (Cast<bool>(expr)) \
|
else if (static_cast<bool>(expr)) \
|
||||||
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
||||||
#define ELSE_VERBOSE(...) \
|
#define ELSE_VERBOSE(...) \
|
||||||
; \
|
; \
|
||||||
|
|
@ -207,5 +207,5 @@ extern Logger g_Logger;
|
||||||
#endif // !defined(VERBOSE_LOG_DISABLED)
|
#endif // !defined(VERBOSE_LOG_DISABLED)
|
||||||
|
|
||||||
#define DO(code) , code
|
#define DO(code) , code
|
||||||
#define ABORT(code) exit(Cast<i32>(code))
|
#define ABORT(code) exit(static_cast<i32>(code))
|
||||||
#define THEN_ABORT(code) , ABORT(code)
|
#define THEN_ABORT(code) , ABORT(code)
|
||||||
|
|
@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"global.cpp"
|
"global.cpp"
|
||||||
"context.cpp"
|
"instance.cpp"
|
||||||
"physical_device.cpp"
|
"physical_device.cpp"
|
||||||
"device.cpp"
|
"device.cpp"
|
||||||
"swapchain.cpp"
|
"swapchain.cpp"
|
||||||
|
|
@ -13,4 +13,5 @@ PRIVATE
|
||||||
"buffer.cpp"
|
"buffer.cpp"
|
||||||
"image.cpp"
|
"image.cpp"
|
||||||
"surface.cpp"
|
"surface.cpp"
|
||||||
"window.cpp")
|
"window.cpp"
|
||||||
|
"sampler.cpp")
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,25 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: buffer.cpp
|
// Aster: buffer.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/buffer.h"
|
#include "core/buffer.h"
|
||||||
|
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
|
|
||||||
void
|
Buffer::Buffer(Device const *device, usize const size, vk::BufferUsageFlags const bufferUsage,
|
||||||
Buffer::Destroy(const Device *device)
|
VmaAllocationCreateFlags const allocationFlags, VmaMemoryUsage const memoryUsage, cstr const name)
|
||||||
{
|
{
|
||||||
if (!IsValid() || !IsOwned())
|
assert(!m_Buffer);
|
||||||
return;
|
|
||||||
|
|
||||||
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
|
m_Device = device;
|
||||||
m_Size_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name)
|
|
||||||
{
|
|
||||||
assert(!IsValid());
|
|
||||||
assert(size <= SIZE_MASK);
|
|
||||||
|
|
||||||
vk::BufferCreateInfo bufferCreateInfo = {
|
vk::BufferCreateInfo bufferCreateInfo = {
|
||||||
.size = size,
|
.size = size,
|
||||||
.usage = bufferUsage,
|
.usage = bufferUsage | vk::BufferUsageFlagBits::eShaderDeviceAddress,
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
.sharingMode = vk::SharingMode::eExclusive,
|
||||||
};
|
};
|
||||||
const VmaAllocationCreateInfo allocationCreateInfo = {
|
VmaAllocationCreateInfo const allocationCreateInfo = {
|
||||||
.flags = allocationFlags,
|
.flags = allocationFlags,
|
||||||
.usage = memoryUsage,
|
.usage = memoryUsage,
|
||||||
};
|
};
|
||||||
|
|
@ -37,156 +27,88 @@ Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUs
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VmaAllocation allocation;
|
VmaAllocation allocation;
|
||||||
VmaAllocationInfo allocationInfo;
|
VmaAllocationInfo allocationInfo;
|
||||||
auto result = Cast<vk::Result>(vmaCreateBuffer(device->m_Allocator, Recast<VkBufferCreateInfo *>(&bufferCreateInfo),
|
auto result = static_cast<vk::Result>(
|
||||||
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
vmaCreateBuffer(device->m_Allocator, reinterpret_cast<VkBufferCreateInfo *>(&bufferCreateInfo),
|
||||||
|
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
||||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
||||||
|
|
||||||
vk::MemoryPropertyFlags memoryPropertyFlags;
|
// vk::MemoryPropertyFlags memoryPropertyFlags;
|
||||||
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
|
// vmaGetAllocationMemoryProperties(device->m_Allocator, allocation, Recast<VkMemoryPropertyFlags
|
||||||
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
|
// *>(&memoryPropertyFlags));
|
||||||
// TODO: Actually track Host Access
|
// TODO: Actually track Host Access
|
||||||
// bool hostAccessible = Cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
|
// bool hostAccessible = static_cast<bool>(memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible);
|
||||||
|
|
||||||
m_Buffer = buffer;
|
m_Buffer = buffer;
|
||||||
m_Size_ = size | VALID_BUFFER_BIT | OWNED_BIT;
|
m_Size = size;
|
||||||
m_Allocation = allocation;
|
m_Allocation = allocation;
|
||||||
m_Mapped = Cast<u8 *>(allocationInfo.pMappedData);
|
m_Mapped = static_cast<u8 *>(allocationInfo.pMappedData);
|
||||||
|
m_Flags = {};
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eTransferSrc)
|
||||||
|
m_Flags |= FlagBits::eStaging;
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eIndexBuffer)
|
||||||
|
m_Flags |= FlagBits::eIndex;
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eIndirectBuffer)
|
||||||
|
m_Flags |= FlagBits::eIndirect;
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eVertexBuffer)
|
||||||
|
m_Flags |= FlagBits::eVertex;
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eUniformBuffer)
|
||||||
|
m_Flags |= FlagBits::eUniform;
|
||||||
|
if (bufferUsage & vk::BufferUsageFlagBits::eStorageBuffer)
|
||||||
|
m_Flags |= FlagBits::eStorage;
|
||||||
|
|
||||||
|
vk::BufferDeviceAddressInfo const addressInfo = {.buffer = m_Buffer};
|
||||||
|
m_DeviceAddr = m_Device->m_Device.getBufferAddress(&addressInfo);
|
||||||
|
|
||||||
device->SetName(m_Buffer, name);
|
device->SetName(m_Buffer, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
uptr
|
Buffer::Buffer(Buffer &&other) noexcept
|
||||||
Buffer::GetDeviceAddress(const Device *device)
|
: m_Device{Take(other.m_Device)}
|
||||||
|
, m_Buffer{Take(other.m_Buffer)}
|
||||||
|
, m_Allocation{Take(other.m_Allocation)}
|
||||||
|
, m_Mapped{Take(other.m_Mapped)}
|
||||||
|
, m_DeviceAddr{Take(other.m_DeviceAddr)}
|
||||||
|
, m_Size{Take(other.m_Size)}
|
||||||
{
|
{
|
||||||
vk::BufferDeviceAddressInfo addressInfo = {.buffer = m_Buffer};
|
}
|
||||||
return device->m_Device.getBufferAddress(&addressInfo);
|
|
||||||
|
Buffer &
|
||||||
|
Buffer::operator=(Buffer &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
using std::swap;
|
||||||
|
swap(m_Device, other.m_Device);
|
||||||
|
swap(m_Buffer, other.m_Buffer);
|
||||||
|
swap(m_Allocation, other.m_Allocation);
|
||||||
|
swap(m_Mapped, other.m_Mapped);
|
||||||
|
swap(m_DeviceAddr, other.m_DeviceAddr);
|
||||||
|
swap(m_Size, other.m_Size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer::~Buffer()
|
||||||
|
{
|
||||||
|
if (!m_Buffer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vmaDestroyBuffer(m_Device->m_Allocator, Take(m_Buffer), m_Allocation);
|
||||||
|
m_Size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uptr
|
||||||
|
Buffer::GetDeviceAddress() const
|
||||||
|
{
|
||||||
|
return m_DeviceAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Buffer::Write(const Device *device, usize offset, usize size, const void *data)
|
Buffer::Write(usize const offset, usize const size, void const *data) const
|
||||||
{
|
{
|
||||||
assert(IsHostVisible());
|
assert(IsMapped());
|
||||||
|
memcpy(m_Mapped + offset, data, size);
|
||||||
if (!IsMapped())
|
|
||||||
{
|
|
||||||
void *mapped;
|
|
||||||
auto result = Cast<vk::Result>(vmaMapMemory(device->m_Allocator, m_Allocation, &mapped));
|
|
||||||
ERROR_IF(Failed(result), "Memory mapping failed. Cause: {}", result);
|
|
||||||
if (!Failed(result))
|
|
||||||
{
|
|
||||||
m_Mapped = Cast<u8 *>(mapped);
|
|
||||||
memcpy(m_Mapped + offset, data, size);
|
|
||||||
|
|
||||||
vmaUnmapMemory(device->m_Allocator, m_Allocation);
|
|
||||||
m_Mapped = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(m_Mapped + offset, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Debug this.
|
// TODO: Debug this.
|
||||||
// auto result = Cast<vk::Result>(vmaCopyMemoryToAllocation(device->m_Allocator, &data, m_Allocation, 0, size));
|
// auto result = static_cast<vk::Result>(vmaCopyMemoryToAllocation(device->m_Allocator, &data, m_Allocation, 0,
|
||||||
// ERROR_IF(Failed(result), "Writing to buffer failed. Cause: {}", result) THEN_ABORT(result);
|
// size)); ERROR_IF(Failed(result), "Writing to buffer failed. Cause: {}", result) THEN_ABORT(result);
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UniformBuffer::Init(const Device *device, const usize size, const cstr name)
|
|
||||||
{
|
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eUniformBuffer,
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StorageBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
|
||||||
{
|
|
||||||
Init(device, size, hostVisible, false, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StorageBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
|
|
||||||
{
|
|
||||||
vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer;
|
|
||||||
if (deviceAddress)
|
|
||||||
{
|
|
||||||
usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
}
|
|
||||||
if (hostVisible)
|
|
||||||
{
|
|
||||||
Allocate(device, size, usage,
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
Allocate(device, size, usage, 0,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StorageIndexBuffer::Init(const Device *device, usize size, bool hostVisible, bool deviceAddress, cstr name)
|
|
||||||
{
|
|
||||||
vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer;
|
|
||||||
if (deviceAddress)
|
|
||||||
{
|
|
||||||
usage |= vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
}
|
|
||||||
if (hostVisible)
|
|
||||||
{
|
|
||||||
Allocate(device, size, usage,
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IndirectBuffer::Init(const Device *device, usize size, bool hostVisible, cstr name)
|
|
||||||
{
|
|
||||||
vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndirectBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
if (hostVisible)
|
|
||||||
{
|
|
||||||
Allocate(device, size, usage,
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage |= vk::BufferUsageFlagBits::eTransferDst;
|
|
||||||
Allocate(device, size, usage, 0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
{
|
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
|
||||||
0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
IndexBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
{
|
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
|
||||||
0, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StagingBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
{
|
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eTransferSrc,
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT,
|
|
||||||
VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: device.cpp
|
// Aster: device.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
|
|
||||||
#include "core/context.h"
|
#include "core/instance.h"
|
||||||
#include "core/physical_device.h"
|
#include "core/physical_device.h"
|
||||||
#include "core/queue_allocation.h"
|
#include "core/queue_allocation.h"
|
||||||
|
|
||||||
|
|
@ -17,18 +17,12 @@ constexpr eastl::array DEVICE_EXTENSIONS = {
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
Device::Device(Instance const &context, PhysicalDevice &physicalDevice, Features &enabledFeatures,
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
eastl::span<QueueAllocation> const &queueAllocations, eastl::span<u8> const &pipelineCacheData,
|
||||||
: Device(context, physicalDevice, enabledFeatures, queueAllocations, {}, std::move(name))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, eastl::span<u8> &&pipelineCacheData,
|
|
||||||
NameString &&name)
|
NameString &&name)
|
||||||
: m_Name(std::move(name))
|
: m_Name(std::move(name))
|
||||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
, m_PhysicalDevice(physicalDevice.m_PhysicalDevice)
|
||||||
, m_ValidationEnabled(context->m_DebugMessenger != nullptr)
|
, m_ValidationEnabled(context.m_DebugMessenger != nullptr)
|
||||||
{
|
{
|
||||||
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
||||||
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
||||||
|
|
@ -51,19 +45,19 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures->m_Vulkan10Features;
|
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures.m_Vulkan10Features;
|
||||||
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features;
|
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures.m_Vulkan11Features;
|
||||||
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features;
|
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures.m_Vulkan12Features;
|
||||||
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features;
|
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures.m_Vulkan13Features;
|
||||||
|
|
||||||
vulkan11Features->pNext = vulkan12Features;
|
vulkan11Features->pNext = vulkan12Features;
|
||||||
vulkan12Features->pNext = vulkan13Features;
|
vulkan12Features->pNext = vulkan13Features;
|
||||||
|
|
||||||
vk::DeviceCreateInfo deviceCreateInfo = {
|
vk::DeviceCreateInfo deviceCreateInfo = {
|
||||||
.pNext = vulkan11Features,
|
.pNext = vulkan11Features,
|
||||||
.queueCreateInfoCount = Cast<u32>(deviceQueueCreateInfos.size()),
|
.queueCreateInfoCount = static_cast<u32>(deviceQueueCreateInfos.size()),
|
||||||
.pQueueCreateInfos = deviceQueueCreateInfos.data(),
|
.pQueueCreateInfos = deviceQueueCreateInfos.data(),
|
||||||
.enabledExtensionCount = Cast<u32>(DEVICE_EXTENSIONS.size()),
|
.enabledExtensionCount = static_cast<u32>(DEVICE_EXTENSIONS.size()),
|
||||||
.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(),
|
.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(),
|
||||||
.pEnabledFeatures = deviceFeatures,
|
.pEnabledFeatures = deviceFeatures,
|
||||||
};
|
};
|
||||||
|
|
@ -71,25 +65,25 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
|
||||||
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
||||||
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
|
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data());
|
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice.m_DeviceProperties.deviceName.data());
|
||||||
|
|
||||||
SetName(m_Device, m_Name.data());
|
SetName(m_Device, m_Name.data());
|
||||||
|
|
||||||
VmaVulkanFunctions vmaVulkanFunctions = {
|
VmaVulkanFunctions vmaVulkanFunctions = {
|
||||||
.vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr,
|
.vkGetInstanceProcAddr = vk::detail::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr,
|
||||||
.vkGetDeviceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr,
|
.vkGetDeviceProcAddr = vk::detail::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
const VmaAllocatorCreateInfo allocatorCreateInfo = {
|
VmaAllocatorCreateInfo const allocatorCreateInfo = {
|
||||||
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT,
|
||||||
.physicalDevice = m_PhysicalDevice,
|
.physicalDevice = m_PhysicalDevice,
|
||||||
.device = m_Device,
|
.device = m_Device,
|
||||||
.pVulkanFunctions = &vmaVulkanFunctions,
|
.pVulkanFunctions = &vmaVulkanFunctions,
|
||||||
.instance = context->m_Instance,
|
.instance = context.m_Instance,
|
||||||
.vulkanApiVersion = ASTER_API_VERSION,
|
.vulkanApiVersion = ASTER_API_VERSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
result = Cast<vk::Result>(vmaCreateAllocator(&allocatorCreateInfo, &m_Allocator));
|
result = static_cast<vk::Result>(vmaCreateAllocator(&allocatorCreateInfo, &m_Allocator));
|
||||||
ERROR_IF(Failed(result), "Memory allocator creation failed. Cause: {}", result)
|
ERROR_IF(Failed(result), "Memory allocator creation failed. Cause: {}", result)
|
||||||
DO(m_Device.destroy(nullptr))
|
DO(m_Device.destroy(nullptr))
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
|
|
@ -110,6 +104,9 @@ Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
|
if (!m_Device)
|
||||||
|
return;
|
||||||
|
|
||||||
m_Device.destroy(m_PipelineCache, nullptr);
|
m_Device.destroy(m_PipelineCache, nullptr);
|
||||||
if (m_Allocator)
|
if (m_Allocator)
|
||||||
{
|
{
|
||||||
|
|
@ -123,7 +120,7 @@ Device::~Device()
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Queue
|
vk::Queue
|
||||||
Device::GetQueue(const u32 familyIndex, const u32 queueIndex) const
|
Device::GetQueue(u32 const familyIndex, u32 const queueIndex) const
|
||||||
{
|
{
|
||||||
vk::Queue queue;
|
vk::Queue queue;
|
||||||
m_Device.getQueue(familyIndex, queueIndex, &queue);
|
m_Device.getQueue(familyIndex, queueIndex, &queue);
|
||||||
|
|
@ -156,6 +153,7 @@ Device::Device(Device &&other) noexcept
|
||||||
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
, m_PhysicalDevice(Take(other.m_PhysicalDevice))
|
||||||
, m_Device(Take(other.m_Device))
|
, m_Device(Take(other.m_Device))
|
||||||
, m_Allocator(Take(other.m_Allocator))
|
, m_Allocator(Take(other.m_Allocator))
|
||||||
|
, m_PipelineCache(Take(other.m_PipelineCache))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,5 +166,6 @@ Device::operator=(Device &&other) noexcept
|
||||||
m_PhysicalDevice = Take(other.m_PhysicalDevice);
|
m_PhysicalDevice = Take(other.m_PhysicalDevice);
|
||||||
m_Device = Take(other.m_Device);
|
m_Device = Take(other.m_Device);
|
||||||
m_Allocator = Take(other.m_Allocator);
|
m_Allocator = Take(other.m_Allocator);
|
||||||
|
m_PipelineCache = Take(other.m_PipelineCache);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: global.cpp
|
// Aster: global.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/global.h"
|
#include "core/global.h"
|
||||||
|
|
@ -26,11 +26,11 @@ struct MemorySize
|
||||||
{
|
{
|
||||||
usize totalBytes = bytes + m_Bytes;
|
usize totalBytes = bytes + m_Bytes;
|
||||||
m_Bytes = totalBytes % 1024;
|
m_Bytes = totalBytes % 1024;
|
||||||
const usize totalKb = m_Kilobytes + totalBytes / 1024;
|
usize const totalKb = m_Kilobytes + totalBytes / 1024;
|
||||||
m_Kilobytes = totalKb % 1024;
|
m_Kilobytes = totalKb % 1024;
|
||||||
const usize totalMb = m_Megabytes + totalKb / 1024;
|
usize const totalMb = m_Megabytes + totalKb / 1024;
|
||||||
m_Megabytes = totalMb % 1024;
|
m_Megabytes = totalMb % 1024;
|
||||||
m_Gigabytes += Cast<u16>(totalMb / 1024);
|
m_Gigabytes += static_cast<u16>(totalMb / 1024);
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -56,23 +56,23 @@ struct fmt::formatter<MemorySize>
|
||||||
// return format_to(ctx.out(), "({}, {})", foo.a, foo.b); // --== KEY LINE ==--
|
// return format_to(ctx.out(), "({}, {})", foo.a, foo.b); // --== KEY LINE ==--
|
||||||
if (mem.m_Gigabytes > 0)
|
if (mem.m_Gigabytes > 0)
|
||||||
{
|
{
|
||||||
return v10::format_to(ctx.out(), "{}.{} GB", mem.m_Gigabytes, Cast<u16>(mem.m_Megabytes / 1024.0));
|
return fmt::format_to(ctx.out(), "{}.{} GB", mem.m_Gigabytes, static_cast<u16>(mem.m_Megabytes / 1024.0));
|
||||||
}
|
}
|
||||||
if (mem.m_Megabytes > 0)
|
if (mem.m_Megabytes > 0)
|
||||||
{
|
{
|
||||||
return v10::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, Cast<u16>(mem.m_Kilobytes / 1024.0));
|
return fmt::format_to(ctx.out(), "{}.{} MB", mem.m_Megabytes, static_cast<u16>(mem.m_Kilobytes / 1024.0));
|
||||||
}
|
}
|
||||||
if (mem.m_Kilobytes > 0)
|
if (mem.m_Kilobytes > 0)
|
||||||
{
|
{
|
||||||
return v10::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, Cast<u16>(mem.m_Bytes / 1024.0));
|
return fmt::format_to(ctx.out(), "{}.{} KB", mem.m_Kilobytes, static_cast<u16>(mem.m_Bytes / 1024.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
return v10::format_to(ctx.out(), "{} Bytes", mem.m_Bytes);
|
return fmt::format_to(ctx.out(), "{} Bytes", mem.m_Bytes);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void *
|
void *
|
||||||
operator new[](size_t size, const char * /*pName*/, int flags, unsigned /*debugFlags*/, const char * /*file*/,
|
operator new[](size_t size, char const * /*pName*/, int flags, unsigned /*debugFlags*/, char const * /*file*/,
|
||||||
int /*line*/)
|
int /*line*/)
|
||||||
{
|
{
|
||||||
g_TotalAlloc += size;
|
g_TotalAlloc += size;
|
||||||
|
|
@ -82,8 +82,8 @@ operator new[](size_t size, const char * /*pName*/, int flags, unsigned /*debugF
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char * /*pName*/, int flags,
|
operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, char const * /*pName*/, int flags,
|
||||||
unsigned /*debugFlags*/, const char * /*file*/, int /*line*/)
|
unsigned /*debugFlags*/, char const * /*file*/, int /*line*/)
|
||||||
{
|
{
|
||||||
g_TotalAlloc += size;
|
g_TotalAlloc += size;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,425 +1,482 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: image.cpp
|
// Aster: image.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/image.h"
|
#include "core/image.h"
|
||||||
|
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
|
|
||||||
void
|
Image &
|
||||||
Image::Destroy(const Device *device)
|
Image::operator=(Image &&other) noexcept
|
||||||
{
|
{
|
||||||
if (!IsValid() || !IsOwned())
|
if (this == &other)
|
||||||
{
|
return *this;
|
||||||
m_Flags_ = 0;
|
using std::swap;
|
||||||
|
swap(m_Device, other.m_Device);
|
||||||
|
swap(m_Image, other.m_Image);
|
||||||
|
swap(m_Allocation, other.m_Allocation);
|
||||||
|
swap(m_Extent, other.m_Extent);
|
||||||
|
swap(m_Format, other.m_Format);
|
||||||
|
swap(m_EmptyPadding_, other.m_EmptyPadding_);
|
||||||
|
swap(m_Flags_, other.m_Flags_);
|
||||||
|
swap(m_LayerCount, other.m_LayerCount);
|
||||||
|
swap(m_MipLevels, other.m_MipLevels);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image::~Image()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
device->m_Device.destroy(m_View, nullptr);
|
vmaDestroyImage(m_Device->m_Allocator, Take(m_Image), m_Allocation);
|
||||||
vmaDestroyImage(device->m_Allocator, m_Image, m_Allocation);
|
m_Flags_ = {};
|
||||||
m_Flags_ = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipMapped,
|
Image::DestroyView(vk::ImageView const imageView) const
|
||||||
const cstr name)
|
|
||||||
{
|
{
|
||||||
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
|
m_Device->m_Device.destroy(imageView, nullptr);
|
||||||
extent.width, extent.height, name ? name : "<unnamed>");
|
|
||||||
|
|
||||||
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
|
|
||||||
|
|
||||||
auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
if (isMipMapped)
|
|
||||||
{
|
|
||||||
usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eOptimal,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
auto result = Cast<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;
|
|
||||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Image = image;
|
|
||||||
m_View = view;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Extent = imageCreateInfo.extent;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 1;
|
|
||||||
m_MipLevels = mipLevels;
|
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
//
|
||||||
Cube map Faces info.
|
// void
|
||||||
|
// Texture::Init(const Device *device, const vk::Extent2D extent, vk::Format imageFormat, const bool isMipMapped,
|
||||||
TODO: Correct this based on the actual layout for upside down viewport.
|
// const cstr name)
|
||||||
|
//{
|
||||||
| Axis | Layer | Up |
|
// WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
|
||||||
|:----:|:-----:|:--:|
|
// extent.width, extent.height, name ? name : "<unnamed>");
|
||||||
| +x | 0 | -y |
|
//
|
||||||
| -x | 1 | -y |
|
// const u8 mipLevels = isMipMapped ? 1 + static_cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
|
||||||
| +y | 2 | +z |
|
//
|
||||||
| -y | 3 | -z |
|
// auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
| +z | 4 | -y |
|
// if (isMipMapped)
|
||||||
| -z | 5 | -y |
|
// {
|
||||||
|
// usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||||
Remember, we use upside down viewport.
|
// }
|
||||||
|
//
|
||||||
*/
|
// vk::ImageCreateInfo imageCreateInfo = {
|
||||||
|
// .imageType = vk::ImageType::e2D,
|
||||||
void
|
// .format = imageFormat,
|
||||||
TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name)
|
// .extent = ToExtent3D(extent, 1),
|
||||||
|
// .mipLevels = mipLevels,
|
||||||
|
// .arrayLayers = 1,
|
||||||
|
// .samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
// .usage = usage,
|
||||||
|
// .sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
// .initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
// };
|
||||||
|
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
// .flags = {},
|
||||||
|
// .usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// VkImage image;
|
||||||
|
// VmaAllocation allocation;
|
||||||
|
// auto result = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<VkImageCreateInfo
|
||||||
|
// *>(&imageCreateInfo),
|
||||||
|
// &allocationCreateInfo, &image, &allocation, nullptr));
|
||||||
|
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// vk::ImageView view;
|
||||||
|
// vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||||
|
// .image = image,
|
||||||
|
// .viewType = vk::ImageViewType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .components = {},
|
||||||
|
// .subresourceRange =
|
||||||
|
// {
|
||||||
|
// .aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
// .baseMipLevel = 0,
|
||||||
|
// .levelCount = mipLevels,
|
||||||
|
// .baseArrayLayer = 0,
|
||||||
|
// .layerCount = 1,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// m_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = imageCreateInfo.extent;
|
||||||
|
// m_LayerCount = 1;
|
||||||
|
// m_MipLevels = mipLevels;
|
||||||
|
//
|
||||||
|
// device->SetName(m_Image, name);
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
///*
|
||||||
|
// Cube map Faces info.
|
||||||
|
//
|
||||||
|
// TODO: Correct this based on the actual layout for upside down viewport.
|
||||||
|
//
|
||||||
|
//| Axis | Layer | Up |
|
||||||
|
//|:----:|:-----:|:--:|
|
||||||
|
//| +x | 0 | -y |
|
||||||
|
//| -x | 1 | -y |
|
||||||
|
//| +y | 2 | +z |
|
||||||
|
//| -y | 3 | -z |
|
||||||
|
//| +z | 4 | -y |
|
||||||
|
//| -z | 5 | -y |
|
||||||
|
//
|
||||||
|
// Remember, we use upside down viewport.
|
||||||
|
//
|
||||||
|
//*/
|
||||||
|
//
|
||||||
|
// void
|
||||||
|
// TextureCube::Init(const Device *device, u32 cubeSide, vk::Format imageFormat, bool isMipMapped, cstr name)
|
||||||
|
//{
|
||||||
|
// WARN_IF(!IsPowerOfTwo(cubeSide), "Image Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide,
|
||||||
|
// name ? name : "<unnamed>");
|
||||||
|
//
|
||||||
|
// const u8 mipLevels = isMipMapped ? 1 + static_cast<u8>(floor(log2(cubeSide))) : 1;
|
||||||
|
//
|
||||||
|
// auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
||||||
|
// if (isMipMapped)
|
||||||
|
// {
|
||||||
|
// usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
|
||||||
|
//
|
||||||
|
// vk::ImageCreateInfo imageCreateInfo = {
|
||||||
|
// .flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
||||||
|
// .imageType = vk::ImageType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .extent = extent,
|
||||||
|
// .mipLevels = mipLevels,
|
||||||
|
// .arrayLayers = 6,
|
||||||
|
// .samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
// .usage = usage,
|
||||||
|
// .sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
// .initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
// };
|
||||||
|
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
// .flags = {},
|
||||||
|
// .usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// VkImage image;
|
||||||
|
// VmaAllocation allocation;
|
||||||
|
// auto result = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<VkImageCreateInfo
|
||||||
|
// *>(&imageCreateInfo),
|
||||||
|
// &allocationCreateInfo, &image, &allocation, nullptr));
|
||||||
|
// ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// vk::ImageView view;
|
||||||
|
// vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||||
|
// .image = image,
|
||||||
|
// .viewType = vk::ImageViewType::eCube,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .components = {},
|
||||||
|
// .subresourceRange =
|
||||||
|
// {
|
||||||
|
// .aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
// .baseMipLevel = 0,
|
||||||
|
// .levelCount = mipLevels,
|
||||||
|
// .baseArrayLayer = 0,
|
||||||
|
// .layerCount = 6,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// m_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = extent;
|
||||||
|
// m_MipLevels = mipLevels;
|
||||||
|
// m_LayerCount = 6;
|
||||||
|
//
|
||||||
|
// device->SetName(m_Image, name);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void
|
||||||
|
// AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name)
|
||||||
|
//{
|
||||||
|
// vk::ImageCreateInfo imageCreateInfo = {
|
||||||
|
// .imageType = vk::ImageType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .extent = ToExtent3D(extent, 1),
|
||||||
|
// .mipLevels = 1,
|
||||||
|
// .arrayLayers = 1,
|
||||||
|
// .samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
// .usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc,
|
||||||
|
// .sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
// .initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
// };
|
||||||
|
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
// .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
||||||
|
// .usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// VkImage image;
|
||||||
|
// VmaAllocation allocation;
|
||||||
|
// auto result = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<VkImageCreateInfo
|
||||||
|
// *>(&imageCreateInfo),
|
||||||
|
// &allocationCreateInfo, &image, &allocation, nullptr));
|
||||||
|
// ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// vk::ImageView view;
|
||||||
|
// vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||||
|
// .image = image,
|
||||||
|
// .viewType = vk::ImageViewType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .components = {},
|
||||||
|
// .subresourceRange =
|
||||||
|
// {
|
||||||
|
// .aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
// .baseMipLevel = 0,
|
||||||
|
// .levelCount = 1,
|
||||||
|
// .baseArrayLayer = 0,
|
||||||
|
// .layerCount = 1,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
// ERROR_IF(Failed(result), "Could not create attachment image view {}. Cause: {}", name, result)
|
||||||
|
// THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// m_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = imageCreateInfo.extent;
|
||||||
|
// m_MipLevels = 1;
|
||||||
|
// m_LayerCount = 1;
|
||||||
|
//
|
||||||
|
// device->SetName(m_Image, name);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void
|
||||||
|
// DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
||||||
|
//{
|
||||||
|
// constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
|
||||||
|
// vk::ImageCreateInfo imageCreateInfo = {
|
||||||
|
// .imageType = vk::ImageType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .extent = ToExtent3D(extent, 1),
|
||||||
|
// .mipLevels = 1,
|
||||||
|
// .arrayLayers = 1,
|
||||||
|
// .samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
// .usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
|
||||||
|
// .sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
// .initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
// };
|
||||||
|
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
// .flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
||||||
|
// .usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// VkImage image;
|
||||||
|
// VmaAllocation allocation;
|
||||||
|
// auto result = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<VkImageCreateInfo
|
||||||
|
// *>(&imageCreateInfo),
|
||||||
|
// &allocationCreateInfo, &image, &allocation, nullptr));
|
||||||
|
// ERROR_IF(Failed(result), "Could not allocate depth buffer. Cause: {}", result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// vk::ImageView view;
|
||||||
|
// vk::ImageViewCreateInfo imageViewCreateInfo = {
|
||||||
|
// .image = image,
|
||||||
|
// .viewType = vk::ImageViewType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .components = {},
|
||||||
|
// .subresourceRange =
|
||||||
|
// {
|
||||||
|
// .aspectMask = vk::ImageAspectFlagBits::eDepth,
|
||||||
|
// .baseMipLevel = 0,
|
||||||
|
// .levelCount = 1,
|
||||||
|
// .baseArrayLayer = 0,
|
||||||
|
// .layerCount = 1,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
// ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// m_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = imageCreateInfo.extent;
|
||||||
|
// m_MipLevels = 1;
|
||||||
|
// m_LayerCount = 1;
|
||||||
|
//
|
||||||
|
// device->SetName(m_Image, name);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// void
|
||||||
|
// StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled,
|
||||||
|
// cstr name)
|
||||||
|
//{
|
||||||
|
// // Reasoning:
|
||||||
|
// // Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
|
||||||
|
// // results.
|
||||||
|
// auto usage =
|
||||||
|
// vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc |
|
||||||
|
// vk::ImageUsageFlagBits::eTransferDst;
|
||||||
|
// if (isSampled)
|
||||||
|
// {
|
||||||
|
// WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of
|
||||||
|
// Two)",
|
||||||
|
// extent.width, extent.height, name ? name : "<unnamed>");
|
||||||
|
// usage |= vk::ImageUsageFlagBits::eSampled;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// vk::ImageCreateInfo imageCreateInfo = {
|
||||||
|
// .imageType = vk::ImageType::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .extent = ToExtent3D(extent, 1),
|
||||||
|
// .mipLevels = 1,
|
||||||
|
// .arrayLayers = 1,
|
||||||
|
// .samples = vk::SampleCountFlagBits::e1,
|
||||||
|
// .tiling = vk::ImageTiling::eOptimal,
|
||||||
|
// .usage = usage,
|
||||||
|
// .sharingMode = vk::SharingMode::eExclusive,
|
||||||
|
// .initialLayout = vk::ImageLayout::eUndefined,
|
||||||
|
// };
|
||||||
|
// constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
||||||
|
// .flags = {},
|
||||||
|
// .usage = VMA_MEMORY_USAGE_AUTO,
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// VkImage image;
|
||||||
|
// VmaAllocation allocation;
|
||||||
|
// auto result = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<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::e2D,
|
||||||
|
// .format = imageFormat,
|
||||||
|
// .components = {},
|
||||||
|
// .subresourceRange =
|
||||||
|
// {
|
||||||
|
// .aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
// .baseMipLevel = 0,
|
||||||
|
// .levelCount = 1,
|
||||||
|
// .baseArrayLayer = 0,
|
||||||
|
// .layerCount = 1,
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
// result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
||||||
|
// ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
||||||
|
//
|
||||||
|
// m_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = imageCreateInfo.extent;
|
||||||
|
// m_MipLevels = 1;
|
||||||
|
// m_LayerCount = 1;
|
||||||
|
//
|
||||||
|
// 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 u8 mipLevels = isMipMapped ? 1 + static_cast<u8>(floor(log2(cubeSide))) : 1;
|
||||||
|
//
|
||||||
|
// 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 = static_cast<vk::Result>(vmaCreateImage(device->m_Allocator, reinterpret_cast<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_Device = device;
|
||||||
|
// m_Image = image;
|
||||||
|
// m_View = view;
|
||||||
|
// m_Allocation = allocation;
|
||||||
|
// m_Extent = imageCreateInfo.extent;
|
||||||
|
// m_MipLevels = mipLevels;
|
||||||
|
// m_LayerCount = 6;
|
||||||
|
//
|
||||||
|
// device->SetName(m_Image, name);
|
||||||
|
// }
|
||||||
|
Image::Image(Image &&other) noexcept
|
||||||
|
: m_Device{Take(other.m_Device)}
|
||||||
|
, m_Image{Take(other.m_Image)}
|
||||||
|
, m_Allocation{Take(other.m_Allocation)}
|
||||||
|
, m_Extent{other.m_Extent}
|
||||||
|
, m_Format{other.m_Format}
|
||||||
|
, m_EmptyPadding_{other.m_EmptyPadding_}
|
||||||
|
, m_Flags_{other.m_Flags_}
|
||||||
|
, m_LayerCount{other.m_LayerCount}
|
||||||
|
, m_MipLevels{other.m_MipLevels}
|
||||||
{
|
{
|
||||||
WARN_IF(!IsPowerOfTwo(cubeSide), "Image Cube {1} has side {0}x{0} (Non Power of Two)", cubeSide, name ? name : "<unnamed>");
|
|
||||||
|
|
||||||
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
|
|
||||||
|
|
||||||
auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
if (isMipMapped)
|
|
||||||
{
|
|
||||||
usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
}
|
|
||||||
|
|
||||||
const vk::Extent3D extent = {.width = cubeSide, .height = cubeSide, .depth = 1};
|
|
||||||
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
|
||||||
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.extent = extent,
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 6,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eOptimal,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
auto result = Cast<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;
|
|
||||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::eCube,
|
|
||||||
.format = imageFormat,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 6,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Image = image;
|
|
||||||
m_View = view;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Extent = extent;
|
|
||||||
m_MipLevels = mipLevels;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 6;
|
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Image::Image(Device const *device, vk::Image const image, VmaAllocation const allocation, vk::Extent3D const extent,
|
||||||
AttachmentImage::Init(const Device *device, vk::Extent2D extent, vk::Format imageFormat, cstr name)
|
vk::Format const format, Flags const flags, u8 const layerCount, u8 const mipLevels)
|
||||||
|
: m_Device{device}
|
||||||
|
, m_Image{image}
|
||||||
|
, m_Allocation{allocation}
|
||||||
|
, m_Extent{extent}
|
||||||
|
, m_Format{format}
|
||||||
|
, m_Flags_{flags}
|
||||||
|
, m_LayerCount{layerCount}
|
||||||
|
, m_MipLevels{mipLevels}
|
||||||
{
|
{
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
}
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eOptimal,
|
|
||||||
.usage = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.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 depth buffer. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create attachment image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Image = image;
|
|
||||||
m_View = view;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Extent = imageCreateInfo.extent;
|
|
||||||
m_MipLevels = 1;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 1;
|
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
DepthImage::Init(const Device *device, vk::Extent2D extent, cstr name)
|
|
||||||
{
|
|
||||||
constexpr vk::Format imageFormat = vk::Format::eD24UnormS8Uint;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eOptimal,
|
|
||||||
.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.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 depth buffer. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eDepth,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create depth image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Image = image;
|
|
||||||
m_View = view;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Extent = imageCreateInfo.extent;
|
|
||||||
m_MipLevels = 1;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 1;
|
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StorageTexture::Init(const Device *device, vk::Extent2D extent, const vk::Format imageFormat, const bool isSampled,
|
|
||||||
cstr name)
|
|
||||||
{
|
|
||||||
// Reasoning:
|
|
||||||
// Transfer Src and Dst to copy to and from the buffer since Storage will often be loaded with info, and read for
|
|
||||||
// results.
|
|
||||||
auto usage =
|
|
||||||
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
if (isSampled)
|
|
||||||
{
|
|
||||||
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
|
|
||||||
extent.width, extent.height, name ? name : "<unnamed>");
|
|
||||||
usage |= vk::ImageUsageFlagBits::eSampled;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.samples = vk::SampleCountFlagBits::e1,
|
|
||||||
.tiling = vk::ImageTiling::eOptimal,
|
|
||||||
.usage = usage,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.initialLayout = vk::ImageLayout::eUndefined,
|
|
||||||
};
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
auto result = Cast<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::e2D,
|
|
||||||
.format = imageFormat,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Image = image;
|
|
||||||
m_View = view;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Extent = imageCreateInfo.extent;
|
|
||||||
m_MipLevels = 1;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 1;
|
|
||||||
|
|
||||||
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 u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(cubeSide))) : 1;
|
|
||||||
|
|
||||||
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;
|
|
||||||
m_Flags_ = OWNED_BIT | VALID_BIT;
|
|
||||||
m_LayerCount = 6;
|
|
||||||
|
|
||||||
device->SetName(m_Image, name);
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +1,46 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: context.cpp
|
// Aster: context.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/context.h"
|
#include "core/instance.h"
|
||||||
|
|
||||||
|
#include "core/window.h"
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
|
|
||||||
VKAPI_ATTR b32 VKAPI_CALL
|
VKAPI_ATTR b32 VKAPI_CALL
|
||||||
DebugCallback(const VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
DebugCallback(vk::DebugUtilsMessageSeverityFlagBitsEXT const messageSeverity,
|
||||||
const VkDebugUtilsMessageTypeFlagsEXT messageType,
|
vk::DebugUtilsMessageTypeFlagsEXT const messageType,
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT *callbackData, [[maybe_unused]] void *userData)
|
vk::DebugUtilsMessengerCallbackDataEXT const *callbackData, [[maybe_unused]] void *userData)
|
||||||
{
|
{
|
||||||
using Severity = vk::DebugUtilsMessageSeverityFlagsEXT;
|
|
||||||
using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT;
|
using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT;
|
||||||
using MessageType = vk::DebugUtilsMessageTypeFlagsEXT;
|
|
||||||
using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT;
|
using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT;
|
||||||
|
|
||||||
const auto severity = Severity(messageSeverity);
|
if (messageType & MessageTypeBits::eValidation)
|
||||||
|
|
||||||
if (MessageType(messageType) & MessageTypeBits::eValidation)
|
|
||||||
{
|
{
|
||||||
if (severity & SeverityBits::eError)
|
if (messageSeverity & SeverityBits::eError)
|
||||||
ERROR("{}", callbackData->pMessage);
|
ERROR("{}", callbackData->pMessage);
|
||||||
if (severity & SeverityBits::eWarning)
|
if (messageSeverity & SeverityBits::eWarning)
|
||||||
WARN("{}", callbackData->pMessage);
|
WARN("{}", callbackData->pMessage);
|
||||||
if (severity & SeverityBits::eInfo)
|
if (messageSeverity & SeverityBits::eInfo)
|
||||||
INFO("{}", callbackData->pMessage);
|
INFO("{}", callbackData->pMessage);
|
||||||
if (severity & SeverityBits::eVerbose)
|
if (messageSeverity & SeverityBits::eVerbose)
|
||||||
VERBOSE("{}", callbackData->pMessage);
|
VERBOSE("{}", callbackData->pMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Context(const cstr appName, const Version version, bool enableValidation)
|
Instance::Instance(cstr const appName, Version const version, bool enableValidation)
|
||||||
{
|
{
|
||||||
INFO_IF(enableValidation, "Validation Layers enabled");
|
INFO_IF(enableValidation, "Validation Layers enabled");
|
||||||
|
|
||||||
// TODO Get/Check API Version
|
// TODO Get/Check API Version
|
||||||
|
|
||||||
// Creating Instance
|
// Creating Instance
|
||||||
const vk::ApplicationInfo appInfo = {
|
vk::ApplicationInfo const appInfo = {
|
||||||
.pApplicationName = appName,
|
.pApplicationName = appName,
|
||||||
.applicationVersion = version.GetVkVersion(),
|
.applicationVersion = version.GetVkVersion(),
|
||||||
.pEngineName = PROJECT_NAME,
|
.pEngineName = PROJECT_NAME,
|
||||||
|
|
@ -50,7 +48,7 @@ Context::Context(const cstr appName, const Version version, bool enableValidatio
|
||||||
.apiVersion = ASTER_API_VERSION,
|
.apiVersion = ASTER_API_VERSION,
|
||||||
};
|
};
|
||||||
|
|
||||||
const vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
|
vk::DebugUtilsMessengerCreateInfoEXT const debugUtilsMessengerCreateInfo = {
|
||||||
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo,
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo,
|
||||||
|
|
@ -61,23 +59,23 @@ Context::Context(const cstr appName, const Version version, bool enableValidatio
|
||||||
.pUserData = nullptr,
|
.pUserData = nullptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
u32 glfwExtensionCount = 0;
|
u32 windowExtensionCount = 0;
|
||||||
cstr *glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
cstr *windowExtensions = Window::GetInstanceExtensions(&windowExtensionCount);
|
||||||
eastl::fixed_vector<cstr, 3> instanceExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
|
eastl::fixed_vector<cstr, 3> instanceExtensions(windowExtensions, windowExtensions + windowExtensionCount);
|
||||||
if (enableValidation)
|
if (enableValidation)
|
||||||
{
|
{
|
||||||
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
const vk::DynamicLoader dl;
|
vk::detail::DynamicLoader const dl;
|
||||||
// ReSharper disable once CppInconsistentNaming
|
// ReSharper disable once CppInconsistentNaming
|
||||||
const auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
auto const vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||||
|
|
||||||
const auto instanceCreateInfo = vk::InstanceCreateInfo{
|
auto const instanceCreateInfo = vk::InstanceCreateInfo{
|
||||||
.pNext = enableValidation ? &debugUtilsMessengerCreateInfo : nullptr,
|
.pNext = enableValidation ? &debugUtilsMessengerCreateInfo : nullptr,
|
||||||
.pApplicationInfo = &appInfo,
|
.pApplicationInfo = &appInfo,
|
||||||
.enabledExtensionCount = Cast<u32>(instanceExtensions.size()),
|
.enabledExtensionCount = static_cast<u32>(instanceExtensions.size()),
|
||||||
.ppEnabledExtensionNames = instanceExtensions.data(),
|
.ppEnabledExtensionNames = instanceExtensions.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -97,8 +95,11 @@ Context::Context(const cstr appName, const Version version, bool enableValidatio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::~Context()
|
Instance::~Instance()
|
||||||
{
|
{
|
||||||
|
if (!m_Instance)
|
||||||
|
return;
|
||||||
|
|
||||||
if (m_DebugMessenger)
|
if (m_DebugMessenger)
|
||||||
{
|
{
|
||||||
m_Instance.destroy(m_DebugMessenger, nullptr);
|
m_Instance.destroy(m_DebugMessenger, nullptr);
|
||||||
|
|
@ -108,18 +109,18 @@ Context::~Context()
|
||||||
DEBUG("Instance destroyed");
|
DEBUG("Instance destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::Context(Context &&other) noexcept
|
Instance::Instance(Instance &&other) noexcept
|
||||||
: m_Instance(Take(other.m_Instance))
|
: m_Instance(Take(other.m_Instance))
|
||||||
, m_DebugMessenger(Take(other.m_DebugMessenger))
|
, m_DebugMessenger(Take(other.m_DebugMessenger))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Context &
|
Instance &
|
||||||
Context::operator=(Context &&other) noexcept
|
Instance::operator=(Instance &&other) noexcept
|
||||||
{
|
{
|
||||||
if (this == &other)
|
if (this == &other)
|
||||||
return *this;
|
return *this;
|
||||||
m_Instance = Take(other.m_Instance);
|
m_Instance = Take(other.m_Instance);
|
||||||
m_DebugMessenger = Take(other.m_DebugMessenger);
|
m_DebugMessenger = Take(other.m_DebugMessenger);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: physical_device.cpp
|
// Aster: physical_device.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/physical_device.h"
|
#include "core/physical_device.h"
|
||||||
|
|
||||||
#include "core/context.h"
|
#include "core/instance.h"
|
||||||
#include "core/surface.h"
|
#include "core/surface.h"
|
||||||
|
|
||||||
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
||||||
GetSurfaceCapabilities(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
GetSurfaceCapabilities(vk::PhysicalDevice const physicalDevice, vk::SurfaceKHR const surface)
|
||||||
{
|
{
|
||||||
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
|
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ GetSurfaceCapabilities(const vk::PhysicalDevice physicalDevice, const vk::Surfac
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::SurfaceFormatKHR>
|
[[nodiscard]] eastl::vector<vk::SurfaceFormatKHR>
|
||||||
GetSurfaceFormats(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
GetSurfaceFormats(vk::PhysicalDevice const physicalDevice, vk::SurfaceKHR const surface)
|
||||||
{
|
{
|
||||||
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
|
@ -38,7 +38,7 @@ GetSurfaceFormats(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::PresentModeKHR>
|
[[nodiscard]] eastl::vector<vk::PresentModeKHR>
|
||||||
GetSurfacePresentModes(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
GetSurfacePresentModes(vk::PhysicalDevice const physicalDevice, vk::SurfaceKHR const surface)
|
||||||
{
|
{
|
||||||
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
|
@ -55,11 +55,11 @@ GetSurfacePresentModes(const vk::PhysicalDevice physicalDevice, const vk::Surfac
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool
|
[[nodiscard]] bool
|
||||||
GetQueuePresentSupport(const u32 queueFamilyIndex, const vk::SurfaceKHR surface,
|
GetQueuePresentSupport(u32 const queueFamilyIndex, vk::SurfaceKHR const surface,
|
||||||
const vk::PhysicalDevice physicalDevice)
|
vk::PhysicalDevice const physicalDevice)
|
||||||
{
|
{
|
||||||
b32 supported = false;
|
b32 supported = false;
|
||||||
const vk::Result result = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface, &supported);
|
vk::Result const result = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface, &supported);
|
||||||
ERROR_IF(Failed(result), "Could not get queue family surface support. Cause: {}", result)
|
ERROR_IF(Failed(result), "Could not get queue family surface support. Cause: {}", result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ GetQueuePresentSupport(const u32 queueFamilyIndex, const vk::SurfaceKHR surface,
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] eastl::fixed_vector<vk::QueueFamilyProperties, 32>
|
[[nodiscard]] eastl::fixed_vector<vk::QueueFamilyProperties, 32>
|
||||||
GetQueueFamilyProperties(const vk::PhysicalDevice physicalDevice)
|
GetQueueFamilyProperties(vk::PhysicalDevice const physicalDevice)
|
||||||
{
|
{
|
||||||
// Devices rarely have more than 32 queue families. Thus fixed vector
|
// Devices rarely have more than 32 queue families. Thus fixed vector
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
|
|
@ -81,7 +81,7 @@ GetQueueFamilyProperties(const vk::PhysicalDevice physicalDevice)
|
||||||
|
|
||||||
// Size 384 return.
|
// Size 384 return.
|
||||||
[[nodiscard]] eastl::vector<QueueFamilyInfo>
|
[[nodiscard]] eastl::vector<QueueFamilyInfo>
|
||||||
GetQueueFamilies(const vk::SurfaceKHR surface, const vk::PhysicalDevice physicalDevice)
|
GetQueueFamilies(vk::SurfaceKHR const surface, vk::PhysicalDevice const physicalDevice)
|
||||||
{
|
{
|
||||||
|
|
||||||
auto queueFamilyProperties = GetQueueFamilyProperties(physicalDevice);
|
auto queueFamilyProperties = GetQueueFamilyProperties(physicalDevice);
|
||||||
|
|
@ -126,7 +126,7 @@ GetQueueFamilies(const vk::SurfaceKHR surface, const vk::PhysicalDevice physical
|
||||||
return queueFamilyInfos;
|
return queueFamilyInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDevice::PhysicalDevice(const vk::SurfaceKHR surface, const vk::PhysicalDevice physicalDevice)
|
PhysicalDevice::PhysicalDevice(vk::SurfaceKHR const surface, vk::PhysicalDevice const physicalDevice)
|
||||||
{
|
{
|
||||||
physicalDevice.getProperties(&m_DeviceProperties);
|
physicalDevice.getProperties(&m_DeviceProperties);
|
||||||
physicalDevice.getFeatures(&m_DeviceFeatures);
|
physicalDevice.getFeatures(&m_DeviceFeatures);
|
||||||
|
|
@ -139,7 +139,7 @@ PhysicalDevice::PhysicalDevice(const vk::SurfaceKHR surface, const vk::PhysicalD
|
||||||
}
|
}
|
||||||
|
|
||||||
eastl::fixed_vector<vk::PhysicalDevice, 8>
|
eastl::fixed_vector<vk::PhysicalDevice, 8>
|
||||||
EnumeratePhysicalDevices(const vk::Instance instance)
|
EnumeratePhysicalDevices(vk::Instance const instance)
|
||||||
{
|
{
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
vk::Result result = instance.enumeratePhysicalDevices(&count, nullptr);
|
vk::Result result = instance.enumeratePhysicalDevices(&count, nullptr);
|
||||||
|
|
@ -154,11 +154,10 @@ EnumeratePhysicalDevices(const vk::Instance instance)
|
||||||
return physicalDevices;
|
return physicalDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDevices::PhysicalDevices(const Surface *surface, const Context *context)
|
PhysicalDevices::PhysicalDevices(Surface const &surface, Instance const &context)
|
||||||
{
|
{
|
||||||
auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance);
|
for (auto physicalDevices = EnumeratePhysicalDevices(context.m_Instance); auto physicalDevice : physicalDevices)
|
||||||
for (auto physicalDevice : physicalDevices)
|
|
||||||
{
|
{
|
||||||
this->emplace_back(surface->m_Surface, physicalDevice);
|
this->emplace_back(surface.m_Surface, physicalDevice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,24 +1,28 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: pipeline.cpp
|
// Aster: pipeline.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/pipeline.h"
|
#include "core/pipeline.h"
|
||||||
|
|
||||||
#include "core/device.h"
|
#include "core/device.h"
|
||||||
|
|
||||||
Pipeline::Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline,
|
Pipeline::Pipeline(Device const *device, vk::PipelineLayout const layout, vk::Pipeline const pipeline,
|
||||||
eastl::vector<vk::DescriptorSetLayout> &&setLayouts)
|
eastl::vector<vk::DescriptorSetLayout> &&setLayouts, Kind const kind)
|
||||||
: m_Device(device)
|
: m_Device{device}
|
||||||
, m_Layout(layout)
|
, m_Layout{layout}
|
||||||
, m_Pipeline(pipeline)
|
, m_Pipeline{pipeline}
|
||||||
, m_SetLayouts(std::move(setLayouts))
|
, m_SetLayouts{std::move(setLayouts)}
|
||||||
|
, m_Kind{kind}
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Pipeline::~Pipeline()
|
Pipeline::~Pipeline()
|
||||||
{
|
{
|
||||||
for (const auto setLayout : m_SetLayouts)
|
if (!m_Device || !m_Pipeline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto const setLayout : m_SetLayouts)
|
||||||
{
|
{
|
||||||
m_Device->m_Device.destroy(setLayout, nullptr);
|
m_Device->m_Device.destroy(setLayout, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: sampler.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "core/sampler.h"
|
||||||
|
|
||||||
|
#include "core/device.h"
|
||||||
|
|
||||||
|
Sampler::~Sampler()
|
||||||
|
{
|
||||||
|
if (!IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_Device->m_Device.destroy(Take(m_Sampler), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler::Sampler(Device const *device, vk::SamplerCreateInfo const &samplerCreateInfo, cstr name)
|
||||||
|
{
|
||||||
|
m_Device = device;
|
||||||
|
auto const result = device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_Sampler);
|
||||||
|
ERROR_IF(Failed(result), "Could not create a sampler {}", name ? name : "<unnamed>") THEN_ABORT(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler &
|
||||||
|
Sampler::operator=(Sampler &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
using std::swap;
|
||||||
|
swap(m_Device, other.m_Device);
|
||||||
|
swap(m_Sampler, other.m_Sampler);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler::Sampler(Sampler &&other) noexcept
|
||||||
|
: m_Device{other.m_Device}
|
||||||
|
, m_Sampler{Take(other.m_Sampler)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -1,20 +1,19 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: surface.cpp
|
// Aster: surface.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/surface.h"
|
#include "core/surface.h"
|
||||||
|
|
||||||
#include "core/context.h"
|
#include "core/instance.h"
|
||||||
#include "core/window.h"
|
#include "core/window.h"
|
||||||
|
|
||||||
Surface::Surface(Context *context, const Window *window, cstr name)
|
Surface::Surface(Instance &context, Window const &window)
|
||||||
: m_Context(context)
|
: m_Context(&context)
|
||||||
, m_Name(name)
|
|
||||||
{
|
{
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
auto result = Cast<vk::Result>(
|
auto result = static_cast<vk::Result>(
|
||||||
glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), window->m_Window, nullptr, &surface));
|
glfwCreateWindowSurface(static_cast<VkInstance>(m_Context->m_Instance), window.m_Window, nullptr, &surface));
|
||||||
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
|
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("Surface {} Created", m_Name);
|
ELSE_DEBUG("Surface {} Created", m_Name);
|
||||||
|
|
@ -23,14 +22,14 @@ Surface::Surface(Context *context, const Window *window, cstr name)
|
||||||
|
|
||||||
Surface::~Surface()
|
Surface::~Surface()
|
||||||
{
|
{
|
||||||
if (m_Context && m_Surface)
|
if (!m_Context || !m_Context->m_Instance || !m_Surface)
|
||||||
{
|
return;
|
||||||
m_Context->m_Instance.destroy(m_Surface, nullptr);
|
|
||||||
DEBUG("Surface Destroyed");
|
|
||||||
|
|
||||||
m_Surface = nullptr;
|
m_Context->m_Instance.destroy(m_Surface, nullptr);
|
||||||
m_Context = nullptr;
|
DEBUG("Surface Destroyed");
|
||||||
}
|
|
||||||
|
m_Surface = nullptr;
|
||||||
|
m_Context = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface::Surface(Surface &&other) noexcept
|
Surface::Surface(Surface &&other) noexcept
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/// =============================================
|
/// =============================================
|
||||||
// Aster: swapchain.cpp
|
// Aster: swapchain.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// ==============================================
|
// ==============================================
|
||||||
|
|
||||||
#include "core/swapchain.h"
|
#include "core/swapchain.h"
|
||||||
|
|
@ -11,9 +11,8 @@
|
||||||
|
|
||||||
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
[[nodiscard]] vk::Extent2D GetExtent(Size2D size, vk::SurfaceCapabilitiesKHR *surfaceCapabilities);
|
||||||
|
|
||||||
Swapchain::Swapchain(const Surface *surface, const Device *device, Size2D size, NameString &&name)
|
Swapchain::Swapchain(Surface const &surface, Device const &device, Size2D size)
|
||||||
: m_Device(device)
|
: m_Device(&device)
|
||||||
, m_Name(std::move(name))
|
|
||||||
, m_Format(vk::Format::eUndefined)
|
, m_Format(vk::Format::eUndefined)
|
||||||
{
|
{
|
||||||
this->Create(surface, size);
|
this->Create(surface, size);
|
||||||
|
|
@ -27,11 +26,11 @@ Swapchain::~Swapchain()
|
||||||
Swapchain::Swapchain(Swapchain &&other) noexcept
|
Swapchain::Swapchain(Swapchain &&other) noexcept
|
||||||
: m_Device(other.m_Device)
|
: m_Device(other.m_Device)
|
||||||
, m_Swapchain(Take(other.m_Swapchain))
|
, m_Swapchain(Take(other.m_Swapchain))
|
||||||
, m_Name(std::move(other.m_Name))
|
|
||||||
, m_Extent(other.m_Extent)
|
, m_Extent(other.m_Extent)
|
||||||
, m_Format(other.m_Format)
|
, m_Format(other.m_Format)
|
||||||
, m_Images(std::move(other.m_Images))
|
, m_Images(std::move(other.m_Images))
|
||||||
, m_ImageViews(std::move(other.m_ImageViews))
|
, m_ImageViews(std::move(other.m_ImageViews))
|
||||||
|
, m_ResizeCallbacks(std::move(other.m_ResizeCallbacks))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,32 +41,32 @@ Swapchain::operator=(Swapchain &&other) noexcept
|
||||||
return *this;
|
return *this;
|
||||||
m_Device = other.m_Device;
|
m_Device = other.m_Device;
|
||||||
m_Swapchain = Take(other.m_Swapchain);
|
m_Swapchain = Take(other.m_Swapchain);
|
||||||
m_Name = std::move(other.m_Name);
|
|
||||||
m_Extent = other.m_Extent;
|
m_Extent = other.m_Extent;
|
||||||
m_Format = other.m_Format;
|
m_Format = other.m_Format;
|
||||||
m_Images = std::move(other.m_Images);
|
m_Images = std::move(other.m_Images);
|
||||||
m_ImageViews = std::move(other.m_ImageViews);
|
m_ImageViews = std::move(other.m_ImageViews);
|
||||||
|
m_ResizeCallbacks = std::move(other.m_ResizeCallbacks);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Swapchain::Create(const Surface *surface, Size2D size)
|
Swapchain::Create(Surface const &surface, Size2D size)
|
||||||
{
|
{
|
||||||
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
m_Extent = GetExtent(size, &surfaceCapabilities);
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
||||||
|
|
||||||
while (m_Extent.width == 0 || m_Extent.height == 0)
|
while (m_Extent.width == 0 || m_Extent.height == 0)
|
||||||
{
|
{
|
||||||
glfwWaitEvents();
|
glfwWaitEvents();
|
||||||
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface->m_Surface);
|
surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
m_Extent = GetExtent(size, &surfaceCapabilities);
|
m_Extent = GetExtent(size, &surfaceCapabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface->m_Surface);
|
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, surface.m_Surface);
|
||||||
|
|
||||||
m_Format = vk::Format::eUndefined;
|
m_Format = vk::Format::eUndefined;
|
||||||
vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
auto swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
||||||
for (auto [format, colorSpace] : surfaceFormats)
|
for (auto [format, colorSpace] : surfaceFormats)
|
||||||
{
|
{
|
||||||
if (format == vk::Format::eB8G8R8A8Srgb && colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
if (format == vk::Format::eB8G8R8A8Srgb && colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
||||||
|
|
@ -84,8 +83,8 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
swapchainColorSpace = colorSpace;
|
swapchainColorSpace = colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
auto swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
||||||
for (const auto presentMode : presentModes)
|
for (auto const presentMode : presentModes)
|
||||||
{
|
{
|
||||||
if (presentMode == vk::PresentModeKHR::eMailbox)
|
if (presentMode == vk::PresentModeKHR::eMailbox)
|
||||||
{
|
{
|
||||||
|
|
@ -95,16 +94,14 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 swapchainImageCount = 3;
|
u32 swapchainImageCount = 3;
|
||||||
if (surfaceCapabilities.maxImageCount > 0)
|
u32 maxImageCount =
|
||||||
{
|
glm::max(swapchainImageCount, glm::max(surfaceCapabilities.maxImageCount, surfaceCapabilities.minImageCount));
|
||||||
swapchainImageCount =
|
swapchainImageCount = glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, maxImageCount);
|
||||||
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Note that different queues might need the images to be shared.
|
// TODO: Note that different queues might need the images to be shared.
|
||||||
|
|
||||||
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
vk::SwapchainCreateInfoKHR const swapchainCreateInfo = {
|
||||||
.surface = surface->m_Surface,
|
.surface = surface.m_Surface,
|
||||||
.minImageCount = swapchainImageCount,
|
.minImageCount = swapchainImageCount,
|
||||||
.imageFormat = m_Format,
|
.imageFormat = m_Format,
|
||||||
.imageColorSpace = swapchainColorSpace,
|
.imageColorSpace = swapchainColorSpace,
|
||||||
|
|
@ -120,28 +117,30 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::Device device = m_Device->m_Device;
|
vk::Device device = m_Device->m_Device;
|
||||||
|
NameString name = "Swapchain of ";
|
||||||
|
name += m_Device->m_Name;
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
vk::SwapchainKHR swapchain;
|
||||||
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
||||||
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "'{}' creation failed. Cause {}", name, result)
|
||||||
THEN_ABORT(result)
|
THEN_ABORT(result)
|
||||||
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
ELSE_DEBUG("Created '{}'", name);
|
||||||
|
|
||||||
// Irrelevant on the first run. Required for re-creation.
|
// Irrelevant on the first run. Required for re-creation.
|
||||||
Cleanup();
|
Cleanup();
|
||||||
|
|
||||||
m_Swapchain = swapchain;
|
m_Swapchain = swapchain;
|
||||||
|
|
||||||
m_Device->SetName(m_Swapchain, m_Name.data());
|
m_Device->SetName(m_Swapchain, m_Device->m_Name.data());
|
||||||
|
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
// Managed by the Swapchain.
|
// Managed by the Swapchain.
|
||||||
m_Images.resize(swapchainImageCount);
|
m_Images.resize(swapchainImageCount, nullptr);
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
ERROR_IF(Failed(result), "Failed getting {}'s images. Cause {}", name, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
vk::ImageViewCreateInfo viewCreateInfo = {
|
vk::ImageViewCreateInfo viewCreateInfo = {
|
||||||
|
|
@ -165,7 +164,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
|
|
||||||
vk::ImageView imageView;
|
vk::ImageView imageView;
|
||||||
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
||||||
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", m_Name, index, result)
|
ERROR_IF(Failed(result), "Failed creating {}'s image view [{}]. Cause {}", name, index, result)
|
||||||
THEN_ABORT(result);
|
THEN_ABORT(result);
|
||||||
|
|
||||||
m_ImageViews.push_back(imageView);
|
m_ImageViews.push_back(imageView);
|
||||||
|
|
@ -173,7 +172,7 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
++index;
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG("Swapchain {} Image Views created.", m_Name);
|
DEBUG("{} Image Views created.", name);
|
||||||
|
|
||||||
for (auto &callback : m_ResizeCallbacks)
|
for (auto &callback : m_ResizeCallbacks)
|
||||||
{
|
{
|
||||||
|
|
@ -184,24 +183,31 @@ Swapchain::Create(const Surface *surface, Size2D size)
|
||||||
void
|
void
|
||||||
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
Swapchain::RegisterResizeCallback(FnResizeCallback &&callback)
|
||||||
{
|
{
|
||||||
m_ResizeCallbacks.emplace_back(callback);
|
m_ResizeCallbacks.emplace_back(std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Swapchain::Cleanup()
|
Swapchain::Cleanup()
|
||||||
{
|
{
|
||||||
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
if (!m_Swapchain)
|
||||||
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
|
return;
|
||||||
for (const auto imageView : m_ImageViews)
|
|
||||||
|
NameString name = "Swapchain of ";
|
||||||
|
name += m_Device->m_Name;
|
||||||
|
|
||||||
|
for (auto const imageView : m_ImageViews)
|
||||||
{
|
{
|
||||||
m_Device->m_Device.destroy(imageView, nullptr);
|
m_Device->m_Device.destroy(imageView, nullptr);
|
||||||
}
|
}
|
||||||
|
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
||||||
|
DEBUG("Swapchain {} Image Views destroyed.", name);
|
||||||
m_ImageViews.clear();
|
m_ImageViews.clear();
|
||||||
|
m_Images.clear();
|
||||||
if (m_Swapchain)
|
if (m_Swapchain)
|
||||||
{
|
{
|
||||||
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
||||||
m_Swapchain = nullptr;
|
m_Swapchain = nullptr;
|
||||||
DEBUG("Swapchain '{}' destroyed.", m_Name);
|
DEBUG("Swapchain '{}' destroyed.", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,40 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: window.cpp
|
// Aster: window.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "core/window.h"
|
#include "core/window.h"
|
||||||
|
|
||||||
#include "core/context.h"
|
#include "core/instance.h"
|
||||||
#include "util/logger.h"
|
#include "util/logger.h"
|
||||||
|
|
||||||
std::atomic_uint64_t Window::m_WindowCount = 0;
|
std::atomic_uint64_t Window::m_WindowCount = 0;
|
||||||
std::atomic_bool Window::m_IsGlfwInit = false;
|
std::atomic_bool Window::m_IsGlfwInit = false;
|
||||||
|
|
||||||
|
void
|
||||||
|
Window::SetupLibrary()
|
||||||
|
{
|
||||||
|
if (!m_IsGlfwInit)
|
||||||
|
{
|
||||||
|
if (!glfwInit())
|
||||||
|
{
|
||||||
|
char const *error = nullptr;
|
||||||
|
auto const code = glfwGetError(&error);
|
||||||
|
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
|
||||||
|
THEN_ABORT(code);
|
||||||
|
}
|
||||||
|
m_WindowCount = 0;
|
||||||
|
m_IsGlfwInit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr *
|
||||||
|
Window::GetInstanceExtensions(u32 *extensionCount)
|
||||||
|
{
|
||||||
|
SetupLibrary();
|
||||||
|
return glfwGetRequiredInstanceExtensions(extensionCount);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Window::RequestExit() const noexcept
|
Window::RequestExit() const noexcept
|
||||||
{
|
{
|
||||||
|
|
@ -18,15 +42,15 @@ Window::RequestExit() const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Window::SetWindowSize(const vk::Extent2D &extent) const noexcept
|
Window::SetWindowSize(vk::Extent2D const &extent) const noexcept
|
||||||
{
|
{
|
||||||
SetWindowSize(extent.width, extent.height);
|
SetWindowSize(extent.width, extent.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Window::SetWindowSize(const u32 width, const u32 height) const noexcept
|
Window::SetWindowSize(u32 const width, u32 const height) const noexcept
|
||||||
{
|
{
|
||||||
glfwSetWindowSize(m_Window, Cast<i32>(width), Cast<i32>(height));
|
glfwSetWindowSize(m_Window, static_cast<i32>(width), static_cast<i32>(height));
|
||||||
}
|
}
|
||||||
|
|
||||||
Size2D
|
Size2D
|
||||||
|
|
@ -35,25 +59,14 @@ Window::GetSize() const
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
glfwGetFramebufferSize(m_Window, &width, &height);
|
glfwGetFramebufferSize(m_Window, &width, &height);
|
||||||
return {Cast<u32>(width), Cast<u32>(height)};
|
return {static_cast<u32>(width), static_cast<u32>(height)};
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::Window(const cstr title, Size2D extent, const b8 isFullScreen)
|
Window::Window(cstr const title, Size2D extent, b8 const isFullScreen)
|
||||||
{
|
{
|
||||||
m_Name = title;
|
m_Name = title;
|
||||||
|
|
||||||
if (!m_IsGlfwInit)
|
SetupLibrary();
|
||||||
{
|
|
||||||
if (!glfwInit())
|
|
||||||
{
|
|
||||||
const char *error = nullptr;
|
|
||||||
const auto code = glfwGetError(&error);
|
|
||||||
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
|
|
||||||
THEN_ABORT(code);
|
|
||||||
}
|
|
||||||
m_WindowCount = 0;
|
|
||||||
m_IsGlfwInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
||||||
ERROR_IF(!monitor, "No monitor found");
|
ERROR_IF(!monitor, "No monitor found");
|
||||||
|
|
@ -64,22 +77,22 @@ Window::Window(const cstr title, Size2D extent, const b8 isFullScreen)
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
|
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
|
||||||
|
|
||||||
m_Window = glfwCreateWindow(Cast<i32>(extent.m_Width), Cast<i32>(extent.m_Height), m_Name.c_str(),
|
m_Window = glfwCreateWindow(static_cast<i32>(extent.m_Width), static_cast<i32>(extent.m_Height), m_Name.c_str(),
|
||||||
isFullScreen ? monitor : nullptr, nullptr);
|
isFullScreen ? monitor : nullptr, nullptr);
|
||||||
ERROR_IF(m_Window == nullptr, "Window creation failed")
|
ERROR_IF(m_Window == nullptr, "Window creation failed")
|
||||||
ELSE_DEBUG("Window '{}' created with resolution '{}x{}'", m_Name, extent.m_Width, extent.m_Height);
|
ELSE_DEBUG("Window '{}' created with resolution '{}x{}'", m_Name, extent.m_Width, extent.m_Height);
|
||||||
if (m_Window == nullptr)
|
if (m_Window == nullptr)
|
||||||
{
|
{
|
||||||
const char *error = nullptr;
|
char const *error = nullptr;
|
||||||
const auto code = glfwGetError(&error);
|
auto const code = glfwGetError(&error);
|
||||||
ERROR("GLFW Window Creation failed. Cause: ({}) {}", code, error)
|
ERROR("GLFW Window Creation failed. Cause: ({}) {}", code, error)
|
||||||
THEN_ABORT(code);
|
THEN_ABORT(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isFullScreen == false)
|
if (isFullScreen == false)
|
||||||
{
|
{
|
||||||
glfwSetWindowPos(m_Window, Cast<i32>(windowWidth - extent.m_Width) / 2,
|
glfwSetWindowPos(m_Window, static_cast<i32>(windowWidth - extent.m_Width) / 2,
|
||||||
Cast<i32>(windowHeight - extent.m_Height) / 2);
|
static_cast<i32>(windowHeight - extent.m_Height) / 2);
|
||||||
}
|
}
|
||||||
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
|
|
||||||
|
|
@ -96,7 +109,7 @@ Window::~Window()
|
||||||
--m_WindowCount;
|
--m_WindowCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_WindowCount== 0 && m_IsGlfwInit)
|
if (m_WindowCount == 0 && m_IsGlfwInit)
|
||||||
{
|
{
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
m_IsGlfwInit = false;
|
m_IsGlfwInit = false;
|
||||||
|
|
@ -119,4 +132,4 @@ Window::operator=(Window &&other) noexcept
|
||||||
m_Window = Take(other.m_Window);
|
m_Window = Take(other.m_Window);
|
||||||
m_Name = Take(other.m_Name);
|
m_Name = Take(other.m_Name);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,8 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core
|
target_sources(aster_core
|
||||||
PRIVATE
|
PRIVATE
|
||||||
"manager.cpp"
|
"rendering_device.cpp"
|
||||||
"buffer_manager.cpp"
|
"commit_manager.cpp"
|
||||||
"image_manager.cpp"
|
"pipeline_helpers.cpp"
|
||||||
"render_resource_manager.cpp")
|
"context.cpp"
|
||||||
|
"sync_server.cpp")
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/buffer_manager.h"
|
|
||||||
|
|
||||||
Manager<Buffer> *Manager<Buffer>::m_Instance = nullptr;
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
BufferHandle
|
|
||||||
BufferManager::CreateStorageBuffer(const usize size, const cstr name)
|
|
||||||
{
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
|
|
||||||
// TODO: Storage and Index buffer are set.
|
|
||||||
// This is hacky and should be improved.
|
|
||||||
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eIndexBuffer |
|
|
||||||
vk::BufferUsageFlagBits::eShaderDeviceAddress;
|
|
||||||
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
||||||
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
|
||||||
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
|
|
||||||
|
|
||||||
return std::move(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
Manager<Buffer>::Handle
|
|
||||||
BufferManager::CreateUniformBuffer(const usize size, const cstr name)
|
|
||||||
{
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
|
|
||||||
constexpr vk::BufferUsageFlags usage = vk::BufferUsageFlagBits::eUniformBuffer;
|
|
||||||
constexpr VmaAllocationCreateFlags createFlags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |
|
|
||||||
VMA_ALLOCATION_CREATE_MAPPED_BIT;
|
|
||||||
constexpr VmaMemoryUsage memoryUsage = VMA_MEMORY_USAGE_AUTO;
|
|
||||||
object->Allocate(m_Device, size, usage, createFlags, memoryUsage, name);
|
|
||||||
|
|
||||||
return std::move(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferManager::BufferManager(const Device *device, const u32 maxCount, const u8 binding)
|
|
||||||
: Manager{device, maxCount, binding}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,239 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: render_resource_manager.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "systems/commit_manager.h"
|
||||||
|
|
||||||
|
#include "EASTL/array.h"
|
||||||
|
#include "core/device.h"
|
||||||
|
#include "core/image_view.h"
|
||||||
|
#include "systems/rendering_device.h"
|
||||||
|
|
||||||
|
using namespace systems;
|
||||||
|
|
||||||
|
CommitManager *CommitManager::m_Instance = nullptr;
|
||||||
|
|
||||||
|
CommitManager::CommitManager(RenderingDevice const *device, u32 const maxBuffers, u32 const maxImages,
|
||||||
|
u32 const maxStorageImages, Ref<Sampler> defaultSampler)
|
||||||
|
: m_Device{device}
|
||||||
|
, m_Buffers{maxBuffers}
|
||||||
|
, m_Images{maxImages}
|
||||||
|
, m_StorageImages{maxStorageImages}
|
||||||
|
, m_DefaultSampler{std::move(defaultSampler)}
|
||||||
|
{
|
||||||
|
assert(!m_Instance);
|
||||||
|
|
||||||
|
eastl::array poolSizes = {
|
||||||
|
vk::DescriptorPoolSize{
|
||||||
|
.type = vk::DescriptorType::eStorageBuffer,
|
||||||
|
.descriptorCount = maxBuffers,
|
||||||
|
},
|
||||||
|
vk::DescriptorPoolSize{
|
||||||
|
.type = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.descriptorCount = maxImages,
|
||||||
|
},
|
||||||
|
vk::DescriptorPoolSize{
|
||||||
|
.type = vk::DescriptorType::eStorageImage,
|
||||||
|
.descriptorCount = maxStorageImages,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorPoolCreateInfo const poolCreateInfo = {
|
||||||
|
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
|
||||||
|
.maxSets = 1,
|
||||||
|
.poolSizeCount = static_cast<u32>(poolSizes.size()),
|
||||||
|
.pPoolSizes = poolSizes.data(),
|
||||||
|
};
|
||||||
|
AbortIfFailed(device->m_Device->createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
|
||||||
|
|
||||||
|
eastl::array descriptorLayoutBindings = {
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = BUFFER_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||||
|
.descriptorCount = static_cast<u32>(maxBuffers),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = IMAGE_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.descriptorCount = static_cast<u32>(maxImages),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
vk::DescriptorSetLayoutBinding{
|
||||||
|
.binding = STORAGE_IMAGE_BINDING_INDEX,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.descriptorCount = static_cast<u32>(maxStorageImages),
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorBindingFlags bindingFlags =
|
||||||
|
vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind;
|
||||||
|
|
||||||
|
eastl::array<vk::DescriptorBindingFlags, descriptorLayoutBindings.size()> layoutBindingFlags;
|
||||||
|
layoutBindingFlags.fill(bindingFlags);
|
||||||
|
|
||||||
|
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {
|
||||||
|
.bindingCount = static_cast<u32>(layoutBindingFlags.size()),
|
||||||
|
.pBindingFlags = layoutBindingFlags.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
|
||||||
|
vk::DescriptorSetLayoutCreateInfo const descriptorSetLayoutCreateInfo = {
|
||||||
|
.pNext = &bindingFlagsCreateInfo,
|
||||||
|
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
|
||||||
|
.bindingCount = static_cast<u32>(descriptorLayoutBindings.size()),
|
||||||
|
.pBindings = descriptorLayoutBindings.data(),
|
||||||
|
};
|
||||||
|
AbortIfFailed(device->m_Device->createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
|
||||||
|
|
||||||
|
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
|
||||||
|
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
|
||||||
|
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
|
||||||
|
vk::DescriptorSetAllocateInfo const descriptorSetAllocateInfo = {
|
||||||
|
.descriptorPool = m_DescriptorPool,
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
.pSetLayouts = &m_SetLayout,
|
||||||
|
};
|
||||||
|
AbortIfFailed(device->m_Device->allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
|
||||||
|
|
||||||
|
device->SetName(m_SetLayout, "Bindless Layout");
|
||||||
|
device->SetName(m_DescriptorPool, "Bindless Pool");
|
||||||
|
device->SetName(m_DescriptorSet, "Bindless Set");
|
||||||
|
|
||||||
|
m_Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitManager::~CommitManager()
|
||||||
|
{
|
||||||
|
m_Device->m_Device->destroy(m_SetLayout, nullptr);
|
||||||
|
m_Device->m_Device->destroy(m_DescriptorPool, nullptr);
|
||||||
|
|
||||||
|
#if !defined(ASTER_NDEBUG)
|
||||||
|
u32 bufferCount = 0;
|
||||||
|
for (auto const &entry : m_Buffers.m_Data)
|
||||||
|
{
|
||||||
|
bufferCount += entry.m_CommitCount;
|
||||||
|
}
|
||||||
|
u32 imageCount = 0;
|
||||||
|
for (auto const &entry : m_Images.m_Data)
|
||||||
|
{
|
||||||
|
imageCount += entry.m_CommitCount;
|
||||||
|
}
|
||||||
|
if (bufferCount > 0 || imageCount > 0)
|
||||||
|
{
|
||||||
|
WARN("Committed resources at destruction. Buffers: {}, Images: {}", bufferCount, imageCount);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<Buffer>
|
||||||
|
CommitManager::CommitBuffer(Ref<Buffer> const &buffer)
|
||||||
|
{
|
||||||
|
auto [commit, isNew] = m_Buffers.Create(buffer);
|
||||||
|
|
||||||
|
if (!isNew)
|
||||||
|
return commit;
|
||||||
|
|
||||||
|
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
|
||||||
|
.buffer = buffer->m_Buffer,
|
||||||
|
.offset = 0,
|
||||||
|
.range = buffer->m_Size,
|
||||||
|
});
|
||||||
|
m_Writes.push_back({
|
||||||
|
.dstSet = m_DescriptorSet,
|
||||||
|
.dstBinding = BUFFER_BINDING_INDEX,
|
||||||
|
.dstArrayElement = commit.m_Index,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
||||||
|
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<StorageImageView>
|
||||||
|
CommitManager::CommitStorageImage(Ref<StorageImageView> const &image)
|
||||||
|
{
|
||||||
|
auto [commit, isNew] = m_StorageImages.Create(image);
|
||||||
|
if (!isNew)
|
||||||
|
return commit;
|
||||||
|
|
||||||
|
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
|
||||||
|
.sampler = nullptr,
|
||||||
|
.imageView = image->m_View,
|
||||||
|
.imageLayout = vk::ImageLayout::eGeneral,
|
||||||
|
});
|
||||||
|
m_Writes.push_back({
|
||||||
|
.dstSet = m_DescriptorSet,
|
||||||
|
.dstBinding = STORAGE_IMAGE_BINDING_INDEX,
|
||||||
|
.dstArrayElement = commit.m_Index,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eStorageImage,
|
||||||
|
.pImageInfo = &m_WriteInfos.back().uImageInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<TextureView>
|
||||||
|
CommitManager::CommitTexture(Ref<TextureView> const &handle)
|
||||||
|
{
|
||||||
|
return CommitTexture(handle, m_DefaultSampler);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResId<TextureView>
|
||||||
|
CommitManager::CommitTexture(Ref<TextureView> const &image, Ref<Sampler> const &sampler)
|
||||||
|
{
|
||||||
|
auto [commit, isNew] = m_Images.Create(image);
|
||||||
|
if (!isNew)
|
||||||
|
return commit;
|
||||||
|
|
||||||
|
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
|
||||||
|
.sampler = sampler->m_Sampler,
|
||||||
|
.imageView = image->m_View,
|
||||||
|
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
|
});
|
||||||
|
m_Writes.push_back({
|
||||||
|
.dstSet = m_DescriptorSet,
|
||||||
|
.dstBinding = IMAGE_BINDING_INDEX,
|
||||||
|
.dstArrayElement = commit.m_Index,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
||||||
|
.pImageInfo = &m_WriteInfos.back().uImageInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
return commit;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo const &info)
|
||||||
|
: uBufferInfo{info}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo const &info)
|
||||||
|
: uImageInfo{info}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CommitManager::WriteInfo::WriteInfo(vk::BufferView const &info)
|
||||||
|
: uBufferView{info}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommitManager::Update()
|
||||||
|
{
|
||||||
|
// Descriptor Updates
|
||||||
|
if (!m_Writes.empty())
|
||||||
|
{
|
||||||
|
m_Device->m_Device->updateDescriptorSets(static_cast<u32>(m_Writes.size()), m_Writes.data(), 0, nullptr);
|
||||||
|
|
||||||
|
m_Writes.clear();
|
||||||
|
m_WriteInfos.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Buffers.Update();
|
||||||
|
m_Images.Update();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,506 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: context.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "aster/systems/context.h"
|
||||||
|
|
||||||
|
#include "aster/systems/commit_manager.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
|
||||||
|
constexpr static u32
|
||||||
|
GetFormatSize(vk::Format const format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case vk::Format::eUndefined:
|
||||||
|
return 0;
|
||||||
|
case vk::Format::eR8Unorm:
|
||||||
|
case vk::Format::eR8Snorm:
|
||||||
|
case vk::Format::eR8Uscaled:
|
||||||
|
case vk::Format::eR8Sscaled:
|
||||||
|
case vk::Format::eR8Uint:
|
||||||
|
case vk::Format::eR8Sint:
|
||||||
|
case vk::Format::eR8Srgb:
|
||||||
|
return 1;
|
||||||
|
case vk::Format::eR8G8Unorm:
|
||||||
|
case vk::Format::eR8G8Snorm:
|
||||||
|
case vk::Format::eR8G8Uscaled:
|
||||||
|
case vk::Format::eR8G8Sscaled:
|
||||||
|
case vk::Format::eR8G8Uint:
|
||||||
|
case vk::Format::eR8G8Sint:
|
||||||
|
case vk::Format::eR8G8Srgb:
|
||||||
|
return 2;
|
||||||
|
case vk::Format::eR8G8B8Unorm:
|
||||||
|
case vk::Format::eR8G8B8Snorm:
|
||||||
|
case vk::Format::eR8G8B8Uscaled:
|
||||||
|
case vk::Format::eR8G8B8Sscaled:
|
||||||
|
case vk::Format::eR8G8B8Uint:
|
||||||
|
case vk::Format::eR8G8B8Sint:
|
||||||
|
case vk::Format::eR8G8B8Srgb:
|
||||||
|
case vk::Format::eB8G8R8Unorm:
|
||||||
|
case vk::Format::eB8G8R8Snorm:
|
||||||
|
case vk::Format::eB8G8R8Uscaled:
|
||||||
|
case vk::Format::eB8G8R8Sscaled:
|
||||||
|
case vk::Format::eB8G8R8Uint:
|
||||||
|
case vk::Format::eB8G8R8Sint:
|
||||||
|
case vk::Format::eB8G8R8Srgb:
|
||||||
|
return 3;
|
||||||
|
case vk::Format::eR8G8B8A8Unorm:
|
||||||
|
case vk::Format::eR8G8B8A8Snorm:
|
||||||
|
case vk::Format::eR8G8B8A8Uscaled:
|
||||||
|
case vk::Format::eR8G8B8A8Sscaled:
|
||||||
|
case vk::Format::eR8G8B8A8Uint:
|
||||||
|
case vk::Format::eR8G8B8A8Sint:
|
||||||
|
case vk::Format::eR8G8B8A8Srgb:
|
||||||
|
case vk::Format::eB8G8R8A8Unorm:
|
||||||
|
case vk::Format::eB8G8R8A8Snorm:
|
||||||
|
case vk::Format::eB8G8R8A8Uscaled:
|
||||||
|
case vk::Format::eB8G8R8A8Sscaled:
|
||||||
|
case vk::Format::eB8G8R8A8Uint:
|
||||||
|
case vk::Format::eB8G8R8A8Sint:
|
||||||
|
case vk::Format::eB8G8R8A8Srgb:
|
||||||
|
return 4;
|
||||||
|
case vk::Format::eR16Unorm:
|
||||||
|
case vk::Format::eR16Snorm:
|
||||||
|
case vk::Format::eR16Uscaled:
|
||||||
|
case vk::Format::eR16Sscaled:
|
||||||
|
case vk::Format::eR16Uint:
|
||||||
|
case vk::Format::eR16Sint:
|
||||||
|
case vk::Format::eR16Sfloat:
|
||||||
|
return 2;
|
||||||
|
case vk::Format::eR16G16Unorm:
|
||||||
|
case vk::Format::eR16G16Snorm:
|
||||||
|
case vk::Format::eR16G16Uscaled:
|
||||||
|
case vk::Format::eR16G16Sscaled:
|
||||||
|
case vk::Format::eR16G16Uint:
|
||||||
|
case vk::Format::eR16G16Sint:
|
||||||
|
case vk::Format::eR16G16Sfloat:
|
||||||
|
return 4;
|
||||||
|
case vk::Format::eR16G16B16Unorm:
|
||||||
|
case vk::Format::eR16G16B16Snorm:
|
||||||
|
case vk::Format::eR16G16B16Uscaled:
|
||||||
|
case vk::Format::eR16G16B16Sscaled:
|
||||||
|
case vk::Format::eR16G16B16Uint:
|
||||||
|
case vk::Format::eR16G16B16Sint:
|
||||||
|
case vk::Format::eR16G16B16Sfloat:
|
||||||
|
return 6;
|
||||||
|
case vk::Format::eR16G16B16A16Unorm:
|
||||||
|
case vk::Format::eR16G16B16A16Snorm:
|
||||||
|
case vk::Format::eR16G16B16A16Uscaled:
|
||||||
|
case vk::Format::eR16G16B16A16Sscaled:
|
||||||
|
case vk::Format::eR16G16B16A16Uint:
|
||||||
|
case vk::Format::eR16G16B16A16Sint:
|
||||||
|
case vk::Format::eR16G16B16A16Sfloat:
|
||||||
|
return 8;
|
||||||
|
case vk::Format::eR32Uint:
|
||||||
|
case vk::Format::eR32Sint:
|
||||||
|
case vk::Format::eR32Sfloat:
|
||||||
|
return 4;
|
||||||
|
case vk::Format::eR32G32Uint:
|
||||||
|
case vk::Format::eR32G32Sint:
|
||||||
|
case vk::Format::eR32G32Sfloat:
|
||||||
|
return 8;
|
||||||
|
case vk::Format::eR32G32B32Uint:
|
||||||
|
case vk::Format::eR32G32B32Sint:
|
||||||
|
case vk::Format::eR32G32B32Sfloat:
|
||||||
|
return 12;
|
||||||
|
case vk::Format::eR32G32B32A32Uint:
|
||||||
|
case vk::Format::eR32G32B32A32Sint:
|
||||||
|
case vk::Format::eR32G32B32A32Sfloat:
|
||||||
|
return 16;
|
||||||
|
case vk::Format::eD16Unorm:
|
||||||
|
return 2;
|
||||||
|
case vk::Format::eD32Sfloat:
|
||||||
|
return 4;
|
||||||
|
case vk::Format::eS8Uint:
|
||||||
|
return 1;
|
||||||
|
case vk::Format::eD16UnormS8Uint:
|
||||||
|
return 6;
|
||||||
|
case vk::Format::eD24UnormS8Uint:
|
||||||
|
return 4;
|
||||||
|
case vk::Format::eD32SfloatS8Uint:
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
TODO("Esoteric Formats");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::KeepAlive(Ref<Buffer> const &buffer)
|
||||||
|
{
|
||||||
|
assert(m_Pool);
|
||||||
|
m_Pool->KeepAlive(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::KeepAlive(Ref<Image> const &image)
|
||||||
|
{
|
||||||
|
assert(m_Pool);
|
||||||
|
m_Pool->KeepAlive(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::KeepAlive(Ref<ImageView> const &view)
|
||||||
|
{
|
||||||
|
assert(m_Pool);
|
||||||
|
m_Pool->KeepAlive(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::Dependency(vk::DependencyInfo const &dependencyInfo)
|
||||||
|
{
|
||||||
|
m_Cmd.pipelineBarrier2(&dependencyInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::Begin()
|
||||||
|
{
|
||||||
|
vk::CommandBufferBeginInfo commandBufferBeginInfo = {
|
||||||
|
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto result = m_Cmd.begin(&commandBufferBeginInfo);
|
||||||
|
ERROR_IF(Failed(result), "Could not begin context") THEN_ABORT(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release versions inline 'no-op'.
|
||||||
|
#if !defined(ASTER_NDEBUG)
|
||||||
|
void
|
||||||
|
systems::Context::BeginDebugRegion(cstr const name, vec4 const color)
|
||||||
|
{
|
||||||
|
vk::DebugUtilsLabelEXT const label = {
|
||||||
|
.pLabelName = name,
|
||||||
|
.color = std::array{color.r, color.g, color.b, color.a},
|
||||||
|
};
|
||||||
|
m_Cmd.beginDebugUtilsLabelEXT(&label);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::EndDebugRegion()
|
||||||
|
{
|
||||||
|
m_Cmd.endDebugUtilsLabelEXT();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::Context::End()
|
||||||
|
{
|
||||||
|
auto result = m_Cmd.end();
|
||||||
|
ERROR_IF(Failed(result), "Could not end context") THEN_ABORT(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::ComputeContext::Dispatch(Pipeline const &pipeline, u32 x, u32 y, u32 z, usize size, void *data)
|
||||||
|
{
|
||||||
|
BindPipeline(pipeline);
|
||||||
|
PushConstantBlock(0, size, data);
|
||||||
|
|
||||||
|
m_Cmd.dispatch(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::ComputeContext::BindPipeline(Pipeline const &pipeline)
|
||||||
|
{
|
||||||
|
auto bindPoint = vk::PipelineBindPoint::eGraphics;
|
||||||
|
switch (pipeline.m_Kind)
|
||||||
|
{
|
||||||
|
case Pipeline::Kind::eGraphics:
|
||||||
|
bindPoint = vk::PipelineBindPoint::eGraphics;
|
||||||
|
break;
|
||||||
|
case Pipeline::Kind::eCompute:
|
||||||
|
bindPoint = vk::PipelineBindPoint::eCompute;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE("No additional bind points");
|
||||||
|
}
|
||||||
|
m_Cmd.bindPipeline(bindPoint, pipeline.m_Pipeline);
|
||||||
|
|
||||||
|
// TODO: Maybe find a smarter place to host this.
|
||||||
|
if (CommitManager::IsInit())
|
||||||
|
{
|
||||||
|
m_Cmd.bindDescriptorSets(bindPoint, pipeline.m_Layout, 0, 1, &CommitManager::Instance().GetDescriptorSet(), 0,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_PipelineInUse = &pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::SetViewport(vk::Viewport const &viewport)
|
||||||
|
{
|
||||||
|
m_Cmd.setViewport(0, 1, &viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::BindVertexBuffer(Ref<VertexBuffer> const &vertexBuffer)
|
||||||
|
{
|
||||||
|
constexpr vk::DeviceSize offset = 0;
|
||||||
|
m_Cmd.bindVertexBuffers(0, 1, &vertexBuffer->m_Buffer, &offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::BindIndexBuffer(Ref<IndexBuffer> const &indexBuffer)
|
||||||
|
{
|
||||||
|
m_Cmd.bindIndexBuffer(indexBuffer->m_Buffer, 0, vk::IndexType::eUint32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::Draw(usize const vertexCount)
|
||||||
|
{
|
||||||
|
m_Cmd.draw(static_cast<u32>(vertexCount), 1, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::DrawIndexed(usize indexCount)
|
||||||
|
{
|
||||||
|
m_Cmd.drawIndexed(static_cast<u32>(indexCount), 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::DrawIndexed(usize const indexCount, usize const firstIndex, usize const firstVertex)
|
||||||
|
{
|
||||||
|
m_Cmd.drawIndexed(static_cast<u32>(indexCount), 1, static_cast<u32>(firstIndex), static_cast<i32>(firstVertex), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::BeginRendering(vk::RenderingInfo const &renderingInfo)
|
||||||
|
{
|
||||||
|
m_Cmd.beginRendering(&renderingInfo);
|
||||||
|
m_Cmd.setScissor(0, 1, &renderingInfo.renderArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::GraphicsContext::EndRendering()
|
||||||
|
{
|
||||||
|
m_Cmd.endRendering();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::TransferContext::UploadTexture(Ref<Image> const &image, eastl::span<u8> const &data)
|
||||||
|
{
|
||||||
|
ERROR_IF(not(image and image->IsValid()), "Invalid image");
|
||||||
|
|
||||||
|
auto [w, h, d] = image->m_Extent;
|
||||||
|
auto formatSize = GetFormatSize(image->m_Format);
|
||||||
|
auto expectedByteSize = static_cast<u64>(w) * static_cast<u64>(h) * static_cast<u64>(d) * formatSize;
|
||||||
|
ERROR_IF(expectedByteSize != data.size_bytes(), "Mismatch in data size {} vs image size {} ({}x{}x{}x{})",
|
||||||
|
data.size_bytes(), expectedByteSize, w, h, d, formatSize);
|
||||||
|
|
||||||
|
Ref<StagingBuffer> const stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(data.size_bytes());
|
||||||
|
stagingBuffer->Write(0, data.size_bytes(), data.data());
|
||||||
|
|
||||||
|
vk::BufferImageCopy const bufferImageCopy = {
|
||||||
|
.bufferOffset = 0,
|
||||||
|
.bufferRowLength = w,
|
||||||
|
.bufferImageHeight = h,
|
||||||
|
.imageSubresource =
|
||||||
|
{
|
||||||
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
.mipLevel = 0,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.layerCount = 1,
|
||||||
|
},
|
||||||
|
.imageOffset = {},
|
||||||
|
.imageExtent = image->m_Extent,
|
||||||
|
};
|
||||||
|
m_Cmd.copyBufferToImage(stagingBuffer->m_Buffer, image->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
|
||||||
|
&bufferImageCopy);
|
||||||
|
|
||||||
|
KeepAlive(stagingBuffer);
|
||||||
|
KeepAlive(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::TransferContext::UploadBuffer(Ref<Buffer> const &buffer, usize size, void const *data)
|
||||||
|
{
|
||||||
|
ERROR_IF(not(buffer and buffer->IsValid()), "Invalid buffer");
|
||||||
|
|
||||||
|
auto expectedByteSize = buffer->m_Size;
|
||||||
|
ERROR_IF(expectedByteSize != size, "Mismatch in data size {} vs buffer size {}", size, expectedByteSize);
|
||||||
|
|
||||||
|
Ref<StagingBuffer> const stagingBuffer = m_Pool->GetDevice().CreateStagingBuffer(size);
|
||||||
|
stagingBuffer->Write(0, size, data);
|
||||||
|
|
||||||
|
vk::BufferCopy const bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = expectedByteSize};
|
||||||
|
|
||||||
|
m_Cmd.copyBuffer(stagingBuffer->m_Buffer, buffer->m_Buffer, 1, &bufferCopy);
|
||||||
|
|
||||||
|
KeepAlive(stagingBuffer);
|
||||||
|
KeepAlive(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::TransferContext::Blit(vk::BlitImageInfo2 const &mipBlitInfo)
|
||||||
|
{
|
||||||
|
m_Cmd.blitImage2(&mipBlitInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::TransferContext::TransferContext(TransferContext &&other) noexcept
|
||||||
|
: Context{std::move(other)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::TransferContext &
|
||||||
|
systems::TransferContext::operator=(TransferContext &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
Context::operator=(std::move(other));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
systems::ComputeContext::PushConstantBlock(usize const offset, usize const size, void const *data)
|
||||||
|
{
|
||||||
|
assert(m_PipelineInUse);
|
||||||
|
|
||||||
|
vk::ShaderStageFlags stage;
|
||||||
|
switch (m_PipelineInUse->m_Kind)
|
||||||
|
{
|
||||||
|
case Pipeline::Kind::eGraphics:
|
||||||
|
stage = vk::ShaderStageFlagBits::eAll;
|
||||||
|
break;
|
||||||
|
case Pipeline::Kind::eCompute:
|
||||||
|
stage = vk::ShaderStageFlagBits::eCompute;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Cmd.pushConstants(m_PipelineInUse->m_Layout, stage, static_cast<u32>(offset), static_cast<u32>(size), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace systems::_internal;
|
||||||
|
|
||||||
|
ContextPool::ContextPool(RenderingDevice &device, u32 const queueFamilyIndex, ManagedBy const managedBy)
|
||||||
|
: m_Device{&device}
|
||||||
|
, m_BuffersAllocated{0}
|
||||||
|
, m_ExtraData{0}
|
||||||
|
, m_ManagedBy{managedBy}
|
||||||
|
, m_ResetCallback{}
|
||||||
|
{
|
||||||
|
vk::CommandPoolCreateInfo const commandPoolCreateInfo = {
|
||||||
|
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
||||||
|
.queueFamilyIndex = queueFamilyIndex,
|
||||||
|
};
|
||||||
|
AbortIfFailed(device.m_Device->createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextPool::ContextPool(ContextPool &&other) noexcept
|
||||||
|
: m_Device{other.m_Device}
|
||||||
|
, m_Pool{Take(other.m_Pool)}
|
||||||
|
, m_CommandBuffers{std::move(other.m_CommandBuffers)}
|
||||||
|
, m_BuffersAllocated{other.m_BuffersAllocated}
|
||||||
|
, m_ExtraData{other.m_ExtraData}
|
||||||
|
, m_ManagedBy{other.m_ManagedBy}
|
||||||
|
, m_OwnedBuffers{std::move(other.m_OwnedBuffers)}
|
||||||
|
, m_OwnedImages{std::move(other.m_OwnedImages)}
|
||||||
|
, m_OwnedImageViews{std::move(other.m_OwnedImageViews)}
|
||||||
|
, m_ResetCallback{std::move(other.m_ResetCallback)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextPool &
|
||||||
|
ContextPool::operator=(ContextPool &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
using eastl::swap;
|
||||||
|
swap(m_Device, other.m_Device);
|
||||||
|
swap(m_Pool, other.m_Pool);
|
||||||
|
swap(m_CommandBuffers, other.m_CommandBuffers);
|
||||||
|
swap(m_ExtraData, other.m_ExtraData);
|
||||||
|
swap(m_ManagedBy, other.m_ManagedBy);
|
||||||
|
swap(m_BuffersAllocated, other.m_BuffersAllocated);
|
||||||
|
swap(m_OwnedBuffers, other.m_OwnedBuffers);
|
||||||
|
swap(m_OwnedImages, other.m_OwnedImages);
|
||||||
|
swap(m_OwnedImageViews, other.m_OwnedImageViews);
|
||||||
|
swap(m_ResetCallback, other.m_ResetCallback);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContextPool::~ContextPool()
|
||||||
|
{
|
||||||
|
if (!m_Pool)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_Device->m_Device->destroy(Take(m_Pool), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ContextPool::KeepAlive(Ref<Buffer> const &buffer)
|
||||||
|
{
|
||||||
|
m_OwnedBuffers.push_back(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ContextPool::KeepAlive(Ref<Image> const &image)
|
||||||
|
{
|
||||||
|
m_OwnedImages.push_back(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ContextPool::KeepAlive(Ref<ImageView> const &view)
|
||||||
|
{
|
||||||
|
m_OwnedImageViews.push_back(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::CommandBuffer
|
||||||
|
ContextPool::AllocateCommandBuffer()
|
||||||
|
{
|
||||||
|
// Buffers are available.
|
||||||
|
if (m_BuffersAllocated < m_CommandBuffers.size())
|
||||||
|
{
|
||||||
|
return m_CommandBuffers[m_BuffersAllocated++];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate New Buffer.
|
||||||
|
vk::CommandBufferAllocateInfo const allocateInfo = {
|
||||||
|
.commandPool = m_Pool,
|
||||||
|
.level = vk::CommandBufferLevel::ePrimary,
|
||||||
|
.commandBufferCount = 1,
|
||||||
|
};
|
||||||
|
vk::CommandBuffer &cmd = m_CommandBuffers.emplace_back();
|
||||||
|
AbortIfFailed(m_Device->m_Device->allocateCommandBuffers(&allocateInfo, &cmd));
|
||||||
|
++m_BuffersAllocated;
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Context
|
||||||
|
ContextPool::CreateContext()
|
||||||
|
{
|
||||||
|
return Context{*this, AllocateCommandBuffer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ContextPool::Reset()
|
||||||
|
{
|
||||||
|
assert(m_Pool);
|
||||||
|
|
||||||
|
AbortIfFailed(m_Device->m_Device->resetCommandPool(m_Pool, {}));
|
||||||
|
m_BuffersAllocated = 0;
|
||||||
|
|
||||||
|
m_OwnedBuffers.clear();
|
||||||
|
m_OwnedImages.clear();
|
||||||
|
m_OwnedImageViews.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::TransferContext
|
||||||
|
TransferContextPool::CreateTransferContext()
|
||||||
|
{
|
||||||
|
return TransferContext{*this, AllocateCommandBuffer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::ComputeContext
|
||||||
|
ComputeContextPool::CreateComputeContext()
|
||||||
|
{
|
||||||
|
return ComputeContext{*this, AllocateCommandBuffer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::GraphicsContext
|
||||||
|
GraphicsContextPool::CreateGraphicsContext()
|
||||||
|
{
|
||||||
|
return GraphicsContext{*this, AllocateCommandBuffer()};
|
||||||
|
}
|
||||||
|
|
@ -1,316 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/image_manager.h"
|
|
||||||
|
|
||||||
#include "core/device.h"
|
|
||||||
|
|
||||||
Manager<Image> *Manager<Image>::m_Instance = nullptr;
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const Texture2DCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const TextureCubeCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const AttachmentCreateInfo &createInfo);
|
|
||||||
vk::ImageCreateInfo ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo);
|
|
||||||
|
|
||||||
namespace usage_flags
|
|
||||||
{
|
|
||||||
constexpr vk::ImageUsageFlags MIPMAP = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
constexpr vk::ImageUsageFlags SAMPLE = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
|
|
||||||
constexpr vk::ImageUsageFlags STORAGE =
|
|
||||||
vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
constexpr vk::ImageUsageFlags COLOR_ATTACHMENT =
|
|
||||||
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc;
|
|
||||||
constexpr vk::ImageUsageFlags DEPTH_STENCIL_ATTACHMENT = vk::ImageUsageFlagBits::eDepthStencilAttachment;
|
|
||||||
} // namespace usage_flags
|
|
||||||
|
|
||||||
ImageHandle
|
|
||||||
ImageManager::CreateTexture2D(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
const vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageCreateInfo.format,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = imageCreateInfo.mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = imageCreateInfo.arrayLayers,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
object->m_Image = image;
|
|
||||||
object->m_View = view;
|
|
||||||
object->m_Allocation = allocation;
|
|
||||||
object->m_Extent = imageCreateInfo.extent;
|
|
||||||
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
|
|
||||||
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(object->m_Image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageHandle
|
|
||||||
ImageManager::CreateTextureCube(const TextureCubeCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = {},
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
const vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::eCube,
|
|
||||||
.format = imageCreateInfo.format,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = imageCreateInfo.mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = imageCreateInfo.arrayLayers,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
object->m_Image = image;
|
|
||||||
object->m_View = view;
|
|
||||||
object->m_Allocation = allocation;
|
|
||||||
object->m_Extent = imageCreateInfo.extent;
|
|
||||||
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
|
|
||||||
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(object->m_Image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageHandle
|
|
||||||
ImageManager::CreateAttachment(const AttachmentCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
const vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageCreateInfo.format,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = imageCreateInfo.mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = imageCreateInfo.arrayLayers,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
object->m_Image = image;
|
|
||||||
object->m_View = view;
|
|
||||||
object->m_Allocation = allocation;
|
|
||||||
object->m_Extent = imageCreateInfo.extent;
|
|
||||||
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
|
|
||||||
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(object->m_Image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageHandle
|
|
||||||
ImageManager::CreateDepthStencilImage(const DepthStencilImageCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
constexpr VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,
|
|
||||||
.usage = VMA_MEMORY_USAGE_AUTO,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkImage image;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
vk::ImageCreateInfo imageCreateInfo = ToImageCreateInfo(createInfo);
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateImage(m_Device->m_Allocator, Recast<VkImageCreateInfo *>(&imageCreateInfo),
|
|
||||||
&allocationCreateInfo, &image, &allocation, nullptr));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate image {}. Cause: {}", createInfo.m_Name, result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView view;
|
|
||||||
const vk::ImageViewCreateInfo imageViewCreateInfo = {
|
|
||||||
.image = image,
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = imageCreateInfo.format,
|
|
||||||
.components = {},
|
|
||||||
.subresourceRange =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = imageCreateInfo.mipLevels,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = imageCreateInfo.arrayLayers,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
result = m_Device->m_Device.createImageView(&imageViewCreateInfo, nullptr, &view);
|
|
||||||
ERROR_IF(Failed(result), "Could not create image view {}. Cause: {}", createInfo.m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
auto [handle, object] = Alloc();
|
|
||||||
object->m_Image = image;
|
|
||||||
object->m_View = view;
|
|
||||||
object->m_Allocation = allocation;
|
|
||||||
object->m_Extent = imageCreateInfo.extent;
|
|
||||||
object->m_Flags_ = Image::OWNED_BIT | Image::VALID_BIT;
|
|
||||||
object->m_LayerCount = Cast<u8>(imageCreateInfo.arrayLayers);
|
|
||||||
object->m_MipLevels = Cast<u8>(imageCreateInfo.mipLevels);
|
|
||||||
|
|
||||||
m_Device->SetName(object->m_Image, createInfo.m_Name);
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const Texture2DCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, extent, name, isSampled, isMipMapped, isStorage] = createInfo;
|
|
||||||
|
|
||||||
WARN_IF(!IsPowerOfTwo(extent.width) || !IsPowerOfTwo(extent.width), "Image {2} is {0}x{1} (Non Power of Two)",
|
|
||||||
extent.width, extent.height, name ? name : "<unnamed>");
|
|
||||||
|
|
||||||
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(eastl::max(extent.width, extent.height)))) : 1;
|
|
||||||
|
|
||||||
auto usage = vk::ImageUsageFlags{};
|
|
||||||
if (isSampled)
|
|
||||||
usage |= usage_flags::SAMPLE;
|
|
||||||
if (isMipMapped)
|
|
||||||
usage |= usage_flags::MIPMAP;
|
|
||||||
if (isStorage)
|
|
||||||
usage |= usage_flags::STORAGE;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const TextureCubeCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, side, name, isSampled, isMipMapped, isStorage] = createInfo;
|
|
||||||
|
|
||||||
WARN_IF(!IsPowerOfTwo(side), "ImageCube {1} is {0}x{0} (Non Power of Two)", side, name ? name : "<unnamed>");
|
|
||||||
|
|
||||||
const u8 mipLevels = isMipMapped ? 1 + Cast<u8>(floor(log2(side))) : 1;
|
|
||||||
|
|
||||||
auto usage = vk::ImageUsageFlags{};
|
|
||||||
if (isSampled)
|
|
||||||
usage |= usage_flags::SAMPLE;
|
|
||||||
if (isMipMapped)
|
|
||||||
usage |= usage_flags::MIPMAP;
|
|
||||||
if (isStorage)
|
|
||||||
usage |= usage_flags::STORAGE;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.flags = vk::ImageCreateFlagBits::eCubeCompatible,
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = {side, side, 1},
|
|
||||||
.mipLevels = mipLevels,
|
|
||||||
.arrayLayers = 6,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const AttachmentCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[format, extent, name] = createInfo;
|
|
||||||
|
|
||||||
constexpr auto usage = usage_flags::COLOR_ATTACHMENT;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ImageCreateInfo
|
|
||||||
ToImageCreateInfo(const DepthStencilImageCreateInfo &createInfo)
|
|
||||||
{
|
|
||||||
auto &[extent, name] = createInfo;
|
|
||||||
|
|
||||||
constexpr vk::Format format = vk::Format::eD24UnormS8Uint;
|
|
||||||
constexpr auto usage = usage_flags::DEPTH_STENCIL_ATTACHMENT;
|
|
||||||
|
|
||||||
return {
|
|
||||||
.imageType = vk::ImageType::e2D,
|
|
||||||
.format = format,
|
|
||||||
.extent = ToExtent3D(extent, 1),
|
|
||||||
.mipLevels = 1,
|
|
||||||
.arrayLayers = 1,
|
|
||||||
.usage = usage,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageManager::ImageManager(const Device *device, const u32 maxCount, const u8 binding)
|
|
||||||
: Manager{device, maxCount, binding}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/manager.h"
|
|
||||||
|
|
@ -0,0 +1,374 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: pipeline_helpers.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "systems/rendering_device.h"
|
||||||
|
|
||||||
|
#include <aster/systems/pipeline_helpers.h>
|
||||||
|
|
||||||
|
using namespace systems::_internal;
|
||||||
|
|
||||||
|
struct WhatVisitor
|
||||||
|
{
|
||||||
|
std::string
|
||||||
|
operator()(std::monostate) const
|
||||||
|
{
|
||||||
|
return "No Error";
|
||||||
|
}
|
||||||
|
std::string
|
||||||
|
operator()(vk::Result result) const
|
||||||
|
{
|
||||||
|
return fmt::format("Vulkan Error: {}", result);
|
||||||
|
}
|
||||||
|
std::string
|
||||||
|
operator()(SlangResult result) const
|
||||||
|
{
|
||||||
|
return fmt::format("Slang Error: {}", result);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ValueVisitor
|
||||||
|
{
|
||||||
|
i32
|
||||||
|
operator()(std::monostate) const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
i32
|
||||||
|
operator()(vk::Result result) const
|
||||||
|
{
|
||||||
|
return static_cast<i32>(result);
|
||||||
|
}
|
||||||
|
i32
|
||||||
|
operator()(SlangResult result) const
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i32
|
||||||
|
systems::PipelineCreationError::Value()
|
||||||
|
{
|
||||||
|
return std::visit(ValueVisitor{}, m_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::PipelineCreationError::PipelineCreationError(vk::Result res)
|
||||||
|
: m_Data{res}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::PipelineCreationError::PipelineCreationError(SlangResult res)
|
||||||
|
: m_Data{res}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::PipelineCreationError::PipelineCreationError()
|
||||||
|
: m_Data{std::monostate{}}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::PipelineCreationError::operator bool() const
|
||||||
|
{
|
||||||
|
return not std::holds_alternative<std::monostate>(m_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
systems::PipelineCreationError::What()
|
||||||
|
{
|
||||||
|
return std::visit(WhatVisitor{}, m_Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ShaderStageFlagBits
|
||||||
|
systems::SlangToVulkanShaderStage(SlangStage const stage)
|
||||||
|
{
|
||||||
|
switch (stage)
|
||||||
|
{
|
||||||
|
case SLANG_STAGE_VERTEX:
|
||||||
|
return vk::ShaderStageFlagBits::eVertex;
|
||||||
|
case SLANG_STAGE_HULL:
|
||||||
|
return vk::ShaderStageFlagBits::eTessellationControl;
|
||||||
|
case SLANG_STAGE_DOMAIN:
|
||||||
|
return vk::ShaderStageFlagBits::eTessellationEvaluation;
|
||||||
|
case SLANG_STAGE_GEOMETRY:
|
||||||
|
return vk::ShaderStageFlagBits::eGeometry;
|
||||||
|
case SLANG_STAGE_FRAGMENT:
|
||||||
|
return vk::ShaderStageFlagBits::eFragment;
|
||||||
|
case SLANG_STAGE_COMPUTE:
|
||||||
|
return vk::ShaderStageFlagBits::eCompute;
|
||||||
|
case SLANG_STAGE_RAY_GENERATION:
|
||||||
|
return vk::ShaderStageFlagBits::eRaygenKHR;
|
||||||
|
case SLANG_STAGE_INTERSECTION:
|
||||||
|
return vk::ShaderStageFlagBits::eIntersectionKHR;
|
||||||
|
case SLANG_STAGE_ANY_HIT:
|
||||||
|
return vk::ShaderStageFlagBits::eAnyHitKHR;
|
||||||
|
case SLANG_STAGE_CLOSEST_HIT:
|
||||||
|
return vk::ShaderStageFlagBits::eClosestHitKHR;
|
||||||
|
case SLANG_STAGE_MISS:
|
||||||
|
return vk::ShaderStageFlagBits::eMissKHR;
|
||||||
|
case SLANG_STAGE_CALLABLE:
|
||||||
|
return vk::ShaderStageFlagBits::eCallableKHR;
|
||||||
|
case SLANG_STAGE_MESH:
|
||||||
|
return vk::ShaderStageFlagBits::eMeshEXT;
|
||||||
|
case SLANG_STAGE_AMPLIFICATION:
|
||||||
|
return vk::ShaderStageFlagBits::eTaskEXT;
|
||||||
|
case SLANG_STAGE_NONE:
|
||||||
|
case SLANG_STAGE_COUNT:
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
PipelineLayoutBuilder::PipelineLayoutBuilder(RenderingDevice *device, vk::DescriptorSetLayout bindlessLayout)
|
||||||
|
: m_Device{device}
|
||||||
|
, m_DescriptorSetLayouts{bindlessLayout} // if `null` will be filtered out during build.
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::PipelineLayout
|
||||||
|
PipelineLayoutBuilder::Build()
|
||||||
|
{
|
||||||
|
vk::PipelineLayout pipelineLayout;
|
||||||
|
|
||||||
|
eastl::vector<vk::DescriptorSetLayout> filteredDescriptorSetLayouts;
|
||||||
|
filteredDescriptorSetLayouts.reserve(m_DescriptorSetLayouts.size());
|
||||||
|
for (auto dsl : m_DescriptorSetLayouts)
|
||||||
|
{
|
||||||
|
if (dsl)
|
||||||
|
{
|
||||||
|
filteredDescriptorSetLayouts.push_back(dsl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::PipelineLayoutCreateInfo const createInfo = {
|
||||||
|
.setLayoutCount = static_cast<u32>(filteredDescriptorSetLayouts.size()),
|
||||||
|
.pSetLayouts = filteredDescriptorSetLayouts.data(),
|
||||||
|
.pushConstantRangeCount = static_cast<u32>(m_PushConstants.size()),
|
||||||
|
.pPushConstantRanges = m_PushConstants.data(),
|
||||||
|
};
|
||||||
|
AbortIfFailed(m_Device->m_Device->createPipelineLayout(&createInfo, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
|
return pipelineLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::DescriptorSetLayout
|
||||||
|
PipelineLayoutBuilder::CreateDescriptorSetLayout(vk::DescriptorSetLayoutCreateInfo const &createInfo) const
|
||||||
|
{
|
||||||
|
vk::DescriptorSetLayout dsl;
|
||||||
|
// Failure Cases are OoM errors. No recovery.
|
||||||
|
AbortIfFailed(m_Device->m_Device->createDescriptorSetLayout(&createInfo, nullptr, &dsl));
|
||||||
|
return dsl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PipelineLayoutBuilder::AddDescriptorSetForParameterBlock(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
DescriptorLayoutBuilder descriptorLayoutBuilder{this};
|
||||||
|
descriptorLayoutBuilder.AddRangesForParamBlockElement(layout->getElementTypeLayout());
|
||||||
|
descriptorLayoutBuilder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PipelineLayoutBuilder::AddPushConstantRangeForConstantBuffer(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
auto const elementTypeLayout = layout->getElementTypeLayout();
|
||||||
|
auto const elementSize = elementTypeLayout->getSize();
|
||||||
|
|
||||||
|
if (elementSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_PushConstants.push_back({
|
||||||
|
.stageFlags = m_Stage,
|
||||||
|
.offset = 0,
|
||||||
|
.size = static_cast<u32>(elementSize),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PipelineLayoutBuilder::AddSubObjectRange(slang::TypeLayoutReflection *layout, i64 subObjectRangeIndex)
|
||||||
|
{
|
||||||
|
auto bindingRangeIndex = layout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
|
||||||
|
switch (layout->getBindingRangeType(bindingRangeIndex))
|
||||||
|
{
|
||||||
|
case slang::BindingType::ParameterBlock: {
|
||||||
|
auto const parameterBlockTypeLayout = layout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
|
||||||
|
|
||||||
|
AddDescriptorSetForParameterBlock(parameterBlockTypeLayout);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case slang::BindingType::PushConstant: {
|
||||||
|
auto const constantBufferTypeLayout = layout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
|
||||||
|
AddPushConstantRangeForConstantBuffer(constantBufferTypeLayout);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE("Unexpected types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::DescriptorType
|
||||||
|
BindingTypeToDescriptorType(slang::BindingType binding)
|
||||||
|
{
|
||||||
|
using vk::DescriptorType;
|
||||||
|
switch (binding)
|
||||||
|
{
|
||||||
|
case slang::BindingType::Sampler:
|
||||||
|
return DescriptorType::eSampler;
|
||||||
|
case slang::BindingType::Texture:
|
||||||
|
return DescriptorType::eSampledImage;
|
||||||
|
case slang::BindingType::ConstantBuffer:
|
||||||
|
return DescriptorType::eUniformBuffer;
|
||||||
|
case slang::BindingType::TypedBuffer:
|
||||||
|
return DescriptorType::eStorageBuffer;
|
||||||
|
case slang::BindingType::RawBuffer:
|
||||||
|
return DescriptorType::eStorageBuffer;
|
||||||
|
case slang::BindingType::CombinedTextureSampler:
|
||||||
|
return DescriptorType::eCombinedImageSampler;
|
||||||
|
case slang::BindingType::InlineUniformData:
|
||||||
|
return DescriptorType::eInlineUniformBlock;
|
||||||
|
case slang::BindingType::RayTracingAccelerationStructure:
|
||||||
|
return DescriptorType::eAccelerationStructureKHR;
|
||||||
|
case slang::BindingType::MutableTexture:
|
||||||
|
return DescriptorType::eStorageImage;
|
||||||
|
case slang::BindingType::MutableTypedBuffer:
|
||||||
|
return DescriptorType::eStorageBuffer;
|
||||||
|
case slang::BindingType::MutableRawBuffer:
|
||||||
|
return DescriptorType::eStorageBuffer;
|
||||||
|
default:
|
||||||
|
UNREACHABLE("Unsupported Types");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ShaderStageFlags &
|
||||||
|
DescriptorLayoutBuilder::Stage() const
|
||||||
|
{
|
||||||
|
return m_PipelineLayoutBuilder->m_Stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorLayoutBuilder::DescriptorLayoutBuilder(PipelineLayoutBuilder *pipelineLayoutBuilder)
|
||||||
|
: m_PipelineLayoutBuilder{pipelineLayoutBuilder}
|
||||||
|
, m_SetIndex{static_cast<u32>(pipelineLayoutBuilder->m_DescriptorSetLayouts.size())}
|
||||||
|
{
|
||||||
|
m_PipelineLayoutBuilder->m_DescriptorSetLayouts.push_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddDescriptorRange(slang::TypeLayoutReflection *layout, i64 const relativeSetIndex,
|
||||||
|
i64 const rangeIndex)
|
||||||
|
{
|
||||||
|
auto const bindingType = layout->getDescriptorSetDescriptorRangeType(relativeSetIndex, rangeIndex);
|
||||||
|
|
||||||
|
if (bindingType == slang::BindingType::PushConstant)
|
||||||
|
return;
|
||||||
|
|
||||||
|
u32 const descriptorCount =
|
||||||
|
static_cast<u32>(layout->getDescriptorSetDescriptorRangeDescriptorCount(relativeSetIndex, rangeIndex));
|
||||||
|
|
||||||
|
u32 const bindingIndex = static_cast<u32>(m_LayoutBindings.size());
|
||||||
|
auto const vkBindingType = BindingTypeToDescriptorType(bindingType);
|
||||||
|
|
||||||
|
m_LayoutBindings.push_back({
|
||||||
|
.binding = bindingIndex,
|
||||||
|
.descriptorType = vkBindingType,
|
||||||
|
.descriptorCount = descriptorCount,
|
||||||
|
.stageFlags = Stage(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddDescriptorRanges(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
i64 nSets = layout->getDescriptorSetCount();
|
||||||
|
for (i64 relativeSetIndex = 0; relativeSetIndex < nSets; ++relativeSetIndex)
|
||||||
|
{
|
||||||
|
i64 rangeCount = layout->getDescriptorSetDescriptorRangeCount(relativeSetIndex);
|
||||||
|
|
||||||
|
for (i64 rangeIndex = 0; rangeIndex < rangeCount; ++rangeIndex)
|
||||||
|
{
|
||||||
|
AddDescriptorRange(layout, relativeSetIndex, rangeIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::Build()
|
||||||
|
{
|
||||||
|
if (m_LayoutBindings.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto const dsl = m_PipelineLayoutBuilder->CreateDescriptorSetLayout({
|
||||||
|
.bindingCount = static_cast<u32>(m_LayoutBindings.size()),
|
||||||
|
.pBindings = m_LayoutBindings.data(),
|
||||||
|
});
|
||||||
|
m_PipelineLayoutBuilder->m_DescriptorSetLayouts[m_SetIndex] = dsl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddAutomaticallyIntroducedUniformBuffer()
|
||||||
|
{
|
||||||
|
auto const vulkanBindingIndex = static_cast<u32>(m_LayoutBindings.size());
|
||||||
|
|
||||||
|
m_LayoutBindings.push_back({
|
||||||
|
.binding = vulkanBindingIndex,
|
||||||
|
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||||
|
.descriptorCount = 1,
|
||||||
|
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddRanges(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
AddDescriptorRanges(layout);
|
||||||
|
m_PipelineLayoutBuilder->AddSubObjectRanges(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddRangesForParamBlockElement(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
if (layout->getSize() > 0)
|
||||||
|
{
|
||||||
|
AddAutomaticallyIntroducedUniformBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRanges(layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddGlobalScopeParameters(slang::ProgramLayout *layout)
|
||||||
|
{
|
||||||
|
Stage() = vk::ShaderStageFlagBits::eAll;
|
||||||
|
AddRangesForParamBlockElement(layout->getGlobalParamsTypeLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddEntryPointParameters(slang::ProgramLayout *layout)
|
||||||
|
{
|
||||||
|
u64 entryPointCount = layout->getEntryPointCount();
|
||||||
|
for (u64 i = 0; i < entryPointCount; ++i)
|
||||||
|
{
|
||||||
|
auto *entryPoint = layout->getEntryPointByIndex(i);
|
||||||
|
AddEntryPointParameters(entryPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DescriptorLayoutBuilder::AddEntryPointParameters(slang::EntryPointLayout *layout)
|
||||||
|
{
|
||||||
|
Stage() = SlangToVulkanShaderStage(layout->getStage());
|
||||||
|
|
||||||
|
AddRangesForParamBlockElement(layout->getTypeLayout());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PipelineLayoutBuilder::AddSubObjectRanges(slang::TypeLayoutReflection *layout)
|
||||||
|
{
|
||||||
|
i64 subObjectRangeCount = layout->getSubObjectRangeCount();
|
||||||
|
for (i64 subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; ++subObjectRangeIndex)
|
||||||
|
{
|
||||||
|
AddSubObjectRange(layout, subObjectRangeIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: render_resource_manager.cpp
|
|
||||||
// Copyright (c) 2020-2025 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "systems/render_resource_manager.h"
|
|
||||||
|
|
||||||
#include "EASTL/array.h"
|
|
||||||
#include "core/device.h"
|
|
||||||
|
|
||||||
#define AbortIfFailed(RESULT) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
vk::Result _checkResultValue_; \
|
|
||||||
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
|
|
||||||
THEN_ABORT(_checkResultValue_); \
|
|
||||||
} while (false)
|
|
||||||
|
|
||||||
#define AbortIfFailedMV(RESULT, MSG, EXTRA) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
vk::Result _checkResultValue_; \
|
|
||||||
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
|
|
||||||
THEN_ABORT(_checkResultValue_); \
|
|
||||||
} while (false)
|
|
||||||
|
|
||||||
#define AbortIfFailedM(RESULT, MSG) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
|
|
||||||
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
|
|
||||||
} while (false)
|
|
||||||
|
|
||||||
|
|
||||||
using namespace systems;
|
|
||||||
|
|
||||||
u32
|
|
||||||
GetHandleInternal(concepts::HandleType auto &handle)
|
|
||||||
{
|
|
||||||
return *Recast<u32 *>(&handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Buffer> &handle)
|
|
||||||
: uBufferHandle(handle)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::WriteOwner::WriteOwner(const Handle<Image> &handle)
|
|
||||||
: uImageHandle(handle)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::RenderResourceManager(const Device *device, u32 const maxBuffers, const u32 maxImages)
|
|
||||||
: m_BufferManager{device, maxBuffers, BUFFER_BINDING_INDEX}
|
|
||||||
, m_ImageManager{device, maxImages, IMAGE_BINDING_INDEX}
|
|
||||||
{
|
|
||||||
eastl::array poolSizes = {
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = maxBuffers,
|
|
||||||
},
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = maxImages,
|
|
||||||
},
|
|
||||||
//vk::DescriptorPoolSize{
|
|
||||||
// .type = vk::DescriptorType::eStorageImage,
|
|
||||||
// .descriptorCount = storageTexturesCount,
|
|
||||||
//},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::DescriptorPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
|
|
||||||
.maxSets = 1,
|
|
||||||
.poolSizeCount = Cast<u32>(poolSizes.size()),
|
|
||||||
.pPoolSizes = poolSizes.data(),
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
|
|
||||||
|
|
||||||
eastl::array descriptorLayoutBindings = {
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = BUFFER_BINDING_INDEX,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = Cast<u32>(maxBuffers),
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
},
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = IMAGE_BINDING_INDEX,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = Cast<u32>(maxImages),
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
},
|
|
||||||
//vk::DescriptorSetLayoutBinding{
|
|
||||||
// .binding = STORAGE_TEXTURE_BINDING_INDEX,
|
|
||||||
// .descriptorType = vk::DescriptorType::eStorageImage,
|
|
||||||
// .descriptorCount = Cast<u32>(storageTexturesCount),
|
|
||||||
// .stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
//},
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::DescriptorBindingFlags bindingFlags =
|
|
||||||
vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind;
|
|
||||||
|
|
||||||
eastl::array<vk::DescriptorBindingFlags, decltype(descriptorLayoutBindings)::count> layoutBindingFlags;
|
|
||||||
layoutBindingFlags.fill(bindingFlags);
|
|
||||||
|
|
||||||
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {
|
|
||||||
.bindingCount = Cast<u32>(layoutBindingFlags.size()),
|
|
||||||
.pBindingFlags = layoutBindingFlags.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
|
|
||||||
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
|
||||||
.pNext = &bindingFlagsCreateInfo,
|
|
||||||
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
|
|
||||||
.bindingCount = Cast<u32>(descriptorLayoutBindings.size()),
|
|
||||||
.pBindings = descriptorLayoutBindings.data(),
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
|
|
||||||
|
|
||||||
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
|
|
||||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
|
|
||||||
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
|
|
||||||
const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
|
|
||||||
.descriptorPool = m_DescriptorPool,
|
|
||||||
.descriptorSetCount = 1,
|
|
||||||
.pSetLayouts = &m_SetLayout,
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
|
|
||||||
|
|
||||||
device->SetName(m_SetLayout, "Bindless Layout");
|
|
||||||
device->SetName(m_DescriptorPool, "Bindless Pool");
|
|
||||||
device->SetName(m_DescriptorSet, "Bindless Set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
systems::RenderResourceManager::Commit(concepts::HandleType auto &handle)
|
|
||||||
{
|
|
||||||
using HandleType = decltype(handle)::Type;
|
|
||||||
if constexpr (std::is_same_v<HandleType, Buffer>)
|
|
||||||
{
|
|
||||||
const Buffer *buffer = handle.Fetch();
|
|
||||||
|
|
||||||
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
|
|
||||||
.buffer = buffer->m_Buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.range = buffer->GetSize(),
|
|
||||||
});
|
|
||||||
m_Writes.push_back({
|
|
||||||
.dstSet = m_DescriptorSet,
|
|
||||||
.dstBinding = BUFFER_BINDING_INDEX,
|
|
||||||
.dstArrayElement = handle.GetIndex(),
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if constexpr (std::is_same_v<HandleType, Image>)
|
|
||||||
{
|
|
||||||
const Image *image = handle.Fetch();
|
|
||||||
|
|
||||||
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
|
|
||||||
.sampler = nullptr /* TODO Sampler */,
|
|
||||||
.imageView = image->m_View,
|
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
});
|
|
||||||
m_Writes.push_back({
|
|
||||||
.dstSet = m_DescriptorSet,
|
|
||||||
.dstBinding = IMAGE_BINDING_INDEX,
|
|
||||||
.dstArrayElement = handle.GetIndex(),
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eSampledImage,
|
|
||||||
.pImageInfo = &m_WriteInfos.back().uImageInfo,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
static_assert(false && "Type is currently unsupported");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_WriteOwner.emplace_back(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorBufferInfo &info)
|
|
||||||
: uBufferInfo{info}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::WriteInfo::WriteInfo(const vk::DescriptorImageInfo &info)
|
|
||||||
: uImageInfo{info}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderResourceManager::WriteInfo::WriteInfo(const vk::BufferView &info)
|
|
||||||
: uBufferView{info}
|
|
||||||
{
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,150 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: sync_server.cpp
|
||||||
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "aster/systems/sync_server.h"
|
||||||
|
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
|
||||||
|
using namespace systems::_internal;
|
||||||
|
|
||||||
|
SyncServer::Entry::Entry(RenderingDevice &device)
|
||||||
|
: m_CurrentPoint{0, 1}
|
||||||
|
, m_AttachedPool{nullptr}
|
||||||
|
{
|
||||||
|
constexpr static vk::SemaphoreTypeCreateInfo TYPE_CREATE_INFO = {
|
||||||
|
.semaphoreType = vk::SemaphoreType::eTimeline,
|
||||||
|
.initialValue = 0,
|
||||||
|
};
|
||||||
|
constexpr static vk::SemaphoreCreateInfo SEMAPHORE_CREATE_INFO = {.pNext = &TYPE_CREATE_INFO};
|
||||||
|
|
||||||
|
AbortIfFailed(device.m_Device->createSemaphore(&SEMAPHORE_CREATE_INFO, nullptr, &m_Semaphore));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::Entry::Destroy(RenderingDevice &device)
|
||||||
|
{
|
||||||
|
if (m_Semaphore)
|
||||||
|
{
|
||||||
|
device.m_Device->destroy(Take(m_Semaphore), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::Entry::Wait(RenderingDevice &device)
|
||||||
|
{
|
||||||
|
vk::SemaphoreWaitInfo const waitInfo = {
|
||||||
|
.semaphoreCount = 1,
|
||||||
|
.pSemaphores = &m_Semaphore,
|
||||||
|
.pValues = &m_CurrentPoint.m_NextValue,
|
||||||
|
};
|
||||||
|
// This blocks.
|
||||||
|
// So `m_NextValue` is not modified while we wait for the signal.
|
||||||
|
AbortIfFailed(device.m_Device->waitSemaphores(&waitInfo, MaxValue<u64>));
|
||||||
|
|
||||||
|
// Thus, this is safe.
|
||||||
|
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue;
|
||||||
|
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue + 1;
|
||||||
|
if (m_AttachedPool)
|
||||||
|
{
|
||||||
|
m_AttachedPool->Reset();
|
||||||
|
m_AttachedPool = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::Entry::Next()
|
||||||
|
{
|
||||||
|
m_CurrentPoint.m_WaitValue = m_CurrentPoint.m_NextValue;
|
||||||
|
++m_CurrentPoint.m_NextValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::Entry::AttachPool(ContextPool *pool)
|
||||||
|
{
|
||||||
|
assert(!m_AttachedPool);
|
||||||
|
m_AttachedPool = pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
systems::Receipt
|
||||||
|
SyncServer::Allocate()
|
||||||
|
{
|
||||||
|
auto &entry = AllocateEntry();
|
||||||
|
return Receipt{&entry};
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::Free(Receipt const receipt)
|
||||||
|
{
|
||||||
|
FreeEntry(GetEntry(receipt));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::WaitOn(Receipt const receipt)
|
||||||
|
{
|
||||||
|
auto &entry = GetEntry(receipt);
|
||||||
|
entry.Wait(*m_Device);
|
||||||
|
FreeEntry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer::Entry &
|
||||||
|
SyncServer::AllocateEntry()
|
||||||
|
{
|
||||||
|
if (not m_FreeList.empty())
|
||||||
|
{
|
||||||
|
auto &alloc = m_FreeList.back();
|
||||||
|
m_FreeList.pop_back();
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_Allocations.emplace_back(*m_Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SyncServer::FreeEntry(Entry &entry)
|
||||||
|
{
|
||||||
|
entry.Next();
|
||||||
|
m_FreeList.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer::Entry &
|
||||||
|
SyncServer::GetEntry(Receipt receipt)
|
||||||
|
{
|
||||||
|
return *static_cast<Entry *>(receipt.m_Opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer::SyncServer(RenderingDevice &device)
|
||||||
|
: m_Device{&device}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer::~SyncServer()
|
||||||
|
{
|
||||||
|
if (m_Device && !m_Allocations.empty())
|
||||||
|
{
|
||||||
|
for (auto &entry : m_Allocations)
|
||||||
|
{
|
||||||
|
entry.Destroy(*m_Device);
|
||||||
|
}
|
||||||
|
m_Device = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer::SyncServer(SyncServer &&other) noexcept
|
||||||
|
: m_Device{Take(other.m_Device)}
|
||||||
|
, m_Allocations{std::move(other.m_Allocations)}
|
||||||
|
, m_FreeList{Take(other.m_FreeList)}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncServer &
|
||||||
|
SyncServer::operator=(SyncServer &&other) noexcept
|
||||||
|
{
|
||||||
|
if (this == &other)
|
||||||
|
return *this;
|
||||||
|
m_Device = Take(other.m_Device);
|
||||||
|
m_Allocations = std::move(other.m_Allocations);
|
||||||
|
m_FreeList = Take(other.m_FreeList);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
@ -2,4 +2,4 @@
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
target_sources(aster_core PRIVATE "logger.cpp")
|
target_sources(aster_core PRIVATE "logger.cpp" "files.cpp")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: files.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "aster/util/files.h"
|
||||||
|
|
||||||
|
eastl::vector<u32>
|
||||||
|
ReadFile(std::string_view fileName)
|
||||||
|
{
|
||||||
|
FILE *filePtr = fopen(fileName.data(), "rb");
|
||||||
|
|
||||||
|
if (!filePtr)
|
||||||
|
{
|
||||||
|
ERROR("Invalid read of {}", fileName) THEN_ABORT(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
eastl::vector<u32> outputVec;
|
||||||
|
eastl::array<u32, 1024> buffer{};
|
||||||
|
usize totalRead = 0;
|
||||||
|
usize readCount;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
readCount = fread(buffer.data(), sizeof(u32), buffer.size(), filePtr);
|
||||||
|
auto const nextSize = totalRead + readCount;
|
||||||
|
outputVec.resize(nextSize);
|
||||||
|
memcpy(outputVec.data() + totalRead, buffer.data(), readCount * sizeof *buffer.data());
|
||||||
|
totalRead = nextSize;
|
||||||
|
} while (readCount == buffer.size());
|
||||||
|
|
||||||
|
return outputVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
eastl::vector<u8>
|
||||||
|
ReadFileBytes(std::string_view fileName, bool errorOnFail)
|
||||||
|
{
|
||||||
|
FILE *filePtr = fopen(fileName.data(), "rb");
|
||||||
|
|
||||||
|
if (!filePtr)
|
||||||
|
{
|
||||||
|
ERROR_IF(errorOnFail, "Invalid open (r) of {}. Cause: {}", fileName, errno);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
eastl::vector<u8> outputVec;
|
||||||
|
eastl::array<u8, 4096> buffer{};
|
||||||
|
usize totalRead = 0;
|
||||||
|
usize readCount;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
readCount = fread(buffer.data(), sizeof(u8), buffer.size(), filePtr);
|
||||||
|
auto const nextSize = totalRead + readCount;
|
||||||
|
outputVec.resize(nextSize);
|
||||||
|
memcpy(outputVec.data() + totalRead, buffer.data(), readCount * sizeof *buffer.data());
|
||||||
|
totalRead = nextSize;
|
||||||
|
} while (readCount == buffer.size());
|
||||||
|
|
||||||
|
(void)fclose(filePtr);
|
||||||
|
|
||||||
|
return outputVec;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WriteFileBytes(std::string_view fileName, eastl::span<u8> const data)
|
||||||
|
{
|
||||||
|
FILE *filePtr = fopen(fileName.data(), "wb");
|
||||||
|
|
||||||
|
if (!filePtr)
|
||||||
|
{
|
||||||
|
ERROR("Invalid open (w) of {}. Cause: {}", fileName, errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
usize const written = fwrite(data.data(), sizeof(u8), data.size(), filePtr);
|
||||||
|
|
||||||
|
(void)fclose(filePtr);
|
||||||
|
|
||||||
|
return written == data.size();
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: logger.cpp
|
// Aster: logger.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "util/logger.h"
|
#include "util/logger.h"
|
||||||
|
|
||||||
Logger g_Logger = Logger();
|
auto g_Logger = Logger();
|
||||||
// ReSharper disable once CppInconsistentNaming
|
// ReSharper disable once CppInconsistentNaming
|
||||||
|
|
||||||
/* Credits to Const-me */
|
/* Credits to Const-me */
|
||||||
namespace eastl
|
namespace eastl
|
||||||
{
|
{
|
||||||
void
|
void
|
||||||
AssertionFailure(const char *af)
|
AssertionFailure(char const *af)
|
||||||
{
|
{
|
||||||
ERROR("{}", af);
|
ERROR("{}", af);
|
||||||
}
|
}
|
||||||
} // namespace eastl
|
} // namespace eastl
|
||||||
31
build.sh
31
build.sh
|
|
@ -1,31 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
echo "Running CMake"
|
|
||||||
if grep 'NAME=NixOS' /etc/os-release
|
|
||||||
then
|
|
||||||
cmake --preset nixos
|
|
||||||
else
|
|
||||||
cmake --preset linux
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Running Ninja"
|
|
||||||
if echo "$@" | grep -e "clean" -q
|
|
||||||
then
|
|
||||||
cmake --build build --target clean
|
|
||||||
elif echo "$@" | grep -e "rebuild" -q
|
|
||||||
then
|
|
||||||
cmake --build build --clean-first
|
|
||||||
else
|
|
||||||
cmake --build build
|
|
||||||
fi
|
|
||||||
|
|
||||||
if echo "$@" | grep -e "docs" -q
|
|
||||||
then
|
|
||||||
if echo "$@" | grep -e "-v" -q
|
|
||||||
then
|
|
||||||
doxygen
|
|
||||||
else
|
|
||||||
doxygen > /dev/null || echo "Doxygen Failed"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
12
flake.lock
12
flake.lock
|
|
@ -20,16 +20,16 @@
|
||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1738734093,
|
"lastModified": 1742976680,
|
||||||
"narHash": "sha256-UEYOKfXXKU49fR7dGB05As0s2pGbLK4xDo48Qtdm7xs=",
|
"narHash": "sha256-Lcyi6YyR0PgN5rOrmM6mM/1MJIYhGi6rrq0+eiqvUb4=",
|
||||||
"owner": "NixOS",
|
"owner": "kidrigger",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "5b2753b0356d1c951d7a3ef1d086ba5a71fff43c",
|
"rev": "51cf54bdbd9c1a0a2f833cced82451df0d9c25bd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "kidrigger",
|
||||||
"ref": "nixpkgs-unstable",
|
"ref": "imgui-docking",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:kidrigger/nixpkgs/imgui-docking";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
outputs = {self, nixpkgs, flake-utils }:
|
outputs = {self, nixpkgs, flake-utils }:
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
with pkgs;
|
with pkgs;
|
||||||
{
|
{
|
||||||
devShells.default = clangStdenv.mkDerivation {
|
devShells.default = clangStdenv.mkDerivation {
|
||||||
name = "BlazeEnv";
|
name = "Aster-Env";
|
||||||
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
|
|
@ -26,11 +26,10 @@
|
||||||
ccls
|
ccls
|
||||||
clang-tools
|
clang-tools
|
||||||
lldb
|
lldb
|
||||||
(imgui.override {IMGUI_BUILD_VULKAN_BINDING = true; IMGUI_BUILD_GLFW_BINDING=true; })
|
(imgui.override {IMGUI_BUILD_VULKAN_BINDING = true; IMGUI_BUILD_GLFW_BINDING=true; IMGUI_EXPERIMENTAL_DOCKING = true; })
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
buildInputs = [
|
||||||
sdl3
|
|
||||||
glm
|
glm
|
||||||
glfw3
|
glfw3
|
||||||
eastl
|
eastl
|
||||||
|
|
@ -50,6 +49,7 @@
|
||||||
directx-shader-compiler
|
directx-shader-compiler
|
||||||
glslang
|
glslang
|
||||||
shaderc
|
shaderc
|
||||||
|
shader-slang
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
run.sh
14
run.sh
|
|
@ -1,14 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [ -d "build" ]; then
|
|
||||||
pushd ./build/samples/04_scenes/ > /dev/null || exit
|
|
||||||
if echo "$@" | grep -e "debug" -q
|
|
||||||
then
|
|
||||||
lldb ./scene_render
|
|
||||||
else
|
|
||||||
./scene_render
|
|
||||||
fi
|
|
||||||
popd > /dev/null || exit
|
|
||||||
else
|
|
||||||
echo "Build Aster first."
|
|
||||||
fi
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: frame.cpp
|
// Aster: frame.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
|
|
@ -17,7 +17,7 @@ Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCo
|
||||||
m_Device = device;
|
m_Device = device;
|
||||||
|
|
||||||
eastl::fixed_string<char, 50, false> name = "Frame ";
|
eastl::fixed_string<char, 50, false> name = "Frame ";
|
||||||
name += Cast<char>('0' + frameCount);
|
name += static_cast<char>('0' + frameCount);
|
||||||
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
|
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
||||||
.queueFamilyIndex = queueFamilyIndex,
|
.queueFamilyIndex = queueFamilyIndex,
|
||||||
|
|
@ -71,7 +71,7 @@ Frame::Present(const vk::Queue commandQueue, Swapchain *swapchain, const Surface
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
case vk::Result::eSuboptimalKHR:
|
case vk::Result::eSuboptimalKHR:
|
||||||
DEBUG("Recreating Swapchain. Cause: {}", result);
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
swapchain->Create(surface, size);
|
swapchain->Create(*surface, size);
|
||||||
break; // Present failed. We do nothing. Frame is skipped.
|
break; // Present failed. We do nothing. Frame is skipped.
|
||||||
default:
|
default:
|
||||||
AbortIfFailedM(result, "Swapchain Present failed.");
|
AbortIfFailedM(result, "Swapchain Present failed.");
|
||||||
|
|
@ -154,7 +154,7 @@ FrameManager::GetNextFrame(Swapchain *swapchain, const Surface *surface, Size2D
|
||||||
break; // Image acquired. Break out of loop.
|
break; // Image acquired. Break out of loop.
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
case vk::Result::eErrorOutOfDateKHR:
|
||||||
DEBUG("Recreating Swapchain. Cause: {}", result);
|
DEBUG("Recreating Swapchain. Cause: {}", result);
|
||||||
swapchain->Create(surface, size);
|
swapchain->Create(*surface, size);
|
||||||
break; // Image acquire has failed. We move to the next frame.
|
break; // Image acquire has failed. We move to the next frame.
|
||||||
default:
|
default:
|
||||||
AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex);
|
AbortIfFailedMV(result, "Waiting for swapchain image {} failed.", frameIndex);
|
||||||
|
|
@ -171,4 +171,4 @@ FrameManager::GetNextFrame(Swapchain *swapchain, const Surface *surface, Size2D
|
||||||
currentFrame->m_ImageIdx = imageIndex;
|
currentFrame->m_ImageIdx = imageIndex;
|
||||||
|
|
||||||
return currentFrame;
|
return currentFrame;
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: frame.h
|
// Aster: frame.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -31,7 +31,7 @@ struct Frame
|
||||||
// Transient
|
// Transient
|
||||||
u32 m_ImageIdx;
|
u32 m_ImageIdx;
|
||||||
|
|
||||||
void Present(const vk::Queue commandQueue, Swapchain* swapchain, const Surface* surface, Size2D size);
|
void Present(vk::Queue commandQueue, Swapchain *swapchain, const Surface *surface, Size2D size);
|
||||||
|
|
||||||
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
||||||
~Frame();
|
~Frame();
|
||||||
|
|
@ -52,4 +52,4 @@ struct FrameManager
|
||||||
FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight);
|
FrameManager(const Device *device, u32 queueFamilyIndex, u32 framesInFlight);
|
||||||
|
|
||||||
Frame *GetNextFrame(Swapchain *swapchain, const Surface *surface, Size2D size);
|
Frame *GetNextFrame(Swapchain *swapchain, const Surface *surface, Size2D size);
|
||||||
};
|
};
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: gui.cpp
|
// Aster: gui.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "gui.h"
|
#include "gui.h"
|
||||||
|
|
||||||
#include "aster/core/context.h"
|
|
||||||
#include "aster/core/device.h"
|
#include "aster/core/device.h"
|
||||||
|
#include "aster/core/instance.h"
|
||||||
#include "aster/core/window.h"
|
#include "aster/core/window.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
#include <imgui_impl_glfw.h>
|
#include <imgui_impl_glfw.h>
|
||||||
|
|
@ -26,8 +27,74 @@ VulkanAssert(VkResult result)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Init(const Context *context, const Device *device, const Window *window, vk::Format attachmentFormat,
|
Init(systems::RenderingDevice &device, Window &window)
|
||||||
const u32 imageCount, const u32 queueFamily, const vk::Queue queue)
|
{
|
||||||
|
g_AttachmentFormat = device.m_Swapchain.m_Format;
|
||||||
|
|
||||||
|
eastl::vector<vk::DescriptorPoolSize> poolSizes = {
|
||||||
|
{vk::DescriptorType::eSampler, 1000},
|
||||||
|
{vk::DescriptorType::eCombinedImageSampler, 1000},
|
||||||
|
{vk::DescriptorType::eSampledImage, 1000},
|
||||||
|
{vk::DescriptorType::eStorageImage, 1000},
|
||||||
|
{vk::DescriptorType::eUniformTexelBuffer, 1000},
|
||||||
|
{vk::DescriptorType::eStorageTexelBuffer, 1000},
|
||||||
|
{vk::DescriptorType::eUniformBuffer, 1000},
|
||||||
|
{vk::DescriptorType::eStorageBuffer, 1000},
|
||||||
|
{vk::DescriptorType::eUniformBufferDynamic, 1000},
|
||||||
|
{vk::DescriptorType::eStorageBufferDynamic, 1000},
|
||||||
|
{vk::DescriptorType::eInputAttachment, 1000},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DescriptorPoolCreateInfo const descriptorPoolCreateInfo = {
|
||||||
|
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
|
||||||
|
.maxSets = 1000,
|
||||||
|
.poolSizeCount = static_cast<u32>(poolSizes.size()),
|
||||||
|
.pPoolSizes = poolSizes.data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
AbortIfFailed(device.m_Device->createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &g_DescriptorPool));
|
||||||
|
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
CreateContext();
|
||||||
|
ImGuiIO &io = GetIO();
|
||||||
|
(void)io;
|
||||||
|
// io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
||||||
|
// io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Viewports bad
|
||||||
|
|
||||||
|
StyleColorsDark();
|
||||||
|
|
||||||
|
ImGui_ImplGlfw_InitForVulkan(window.m_Window, true);
|
||||||
|
|
||||||
|
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachmentFormats = &g_AttachmentFormat,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Switch this into being managed by RenderingDevice.
|
||||||
|
// m_Instance etc should private.
|
||||||
|
ImGui_ImplVulkan_InitInfo imguiVulkanInitInfo = {
|
||||||
|
.Instance = device.m_Instance.m_Instance,
|
||||||
|
.PhysicalDevice = device.m_Device.m_PhysicalDevice,
|
||||||
|
.Device = device.m_Device.m_Device,
|
||||||
|
.QueueFamily = device.m_PrimaryQueueFamily,
|
||||||
|
.Queue = device.m_PrimaryQueue,
|
||||||
|
.DescriptorPool = g_DescriptorPool,
|
||||||
|
.MinImageCount = static_cast<u32>(device.m_Swapchain.m_Images.size()),
|
||||||
|
.ImageCount = static_cast<u32>(device.m_Swapchain.m_Images.size()),
|
||||||
|
.PipelineCache = nullptr,
|
||||||
|
.UseDynamicRendering = true,
|
||||||
|
.PipelineRenderingCreateInfo = renderingCreateInfo,
|
||||||
|
.Allocator = nullptr,
|
||||||
|
.CheckVkResultFn = VulkanAssert,
|
||||||
|
};
|
||||||
|
ImGui_ImplVulkan_Init(&imguiVulkanInitInfo);
|
||||||
|
|
||||||
|
ImGui_ImplVulkan_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Init(Instance const *context, Device const *device, Window const *window, vk::Format attachmentFormat,
|
||||||
|
u32 const imageCount, u32 const queueFamily, vk::Queue const queue)
|
||||||
{
|
{
|
||||||
g_AttachmentFormat = attachmentFormat;
|
g_AttachmentFormat = attachmentFormat;
|
||||||
|
|
||||||
|
|
@ -45,10 +112,10 @@ Init(const Context *context, const Device *device, const Window *window, vk::For
|
||||||
{vk::DescriptorType::eInputAttachment, 1000},
|
{vk::DescriptorType::eInputAttachment, 1000},
|
||||||
};
|
};
|
||||||
|
|
||||||
const vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
|
vk::DescriptorPoolCreateInfo const descriptorPoolCreateInfo = {
|
||||||
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
|
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
|
||||||
.maxSets = 1000,
|
.maxSets = 1000,
|
||||||
.poolSizeCount = Cast<u32>(poolSizes.size()),
|
.poolSizeCount = static_cast<u32>(poolSizes.size()),
|
||||||
.pPoolSizes = poolSizes.data(),
|
.pPoolSizes = poolSizes.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -91,9 +158,18 @@ Init(const Context *context, const Device *device, const Window *window, vk::For
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Destroy(const Device *device)
|
Destroy(systems::RenderingDevice const &device)
|
||||||
{
|
{
|
||||||
|
ImGui_ImplVulkan_Shutdown();
|
||||||
|
ImGui_ImplGlfw_Shutdown();
|
||||||
|
DestroyContext();
|
||||||
|
|
||||||
|
device.m_Device->destroy(Take(g_DescriptorPool), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Destroy(Device const *device)
|
||||||
|
{
|
||||||
ImGui_ImplVulkan_Shutdown();
|
ImGui_ImplVulkan_Shutdown();
|
||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui_ImplGlfw_Shutdown();
|
||||||
DestroyContext();
|
DestroyContext();
|
||||||
|
|
@ -108,13 +184,13 @@ StartBuild()
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
NewFrame();
|
NewFrame();
|
||||||
|
|
||||||
static ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_None | ImGuiDockNodeFlags_PassthruCentralNode;
|
static ImGuiDockNodeFlags dockspaceFlags = ImGuiDockNodeFlags_None | ImGuiDockNodeFlags_PassthruCentralNode;
|
||||||
|
|
||||||
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
|
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
|
||||||
// because it would be confusing to have two docking targets within each others.
|
// because it would be confusing to have two docking targets within each others.
|
||||||
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_None | ImGuiWindowFlags_NoDocking;
|
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_None | ImGuiWindowFlags_NoDocking;
|
||||||
|
|
||||||
const ImGuiViewport *viewport = GetMainViewport();
|
ImGuiViewport const *viewport = GetMainViewport();
|
||||||
SetNextWindowPos(viewport->WorkPos);
|
SetNextWindowPos(viewport->WorkPos);
|
||||||
SetNextWindowSize(viewport->WorkSize);
|
SetNextWindowSize(viewport->WorkSize);
|
||||||
// SetNextWindowViewport(viewport->ID);
|
// SetNextWindowViewport(viewport->ID);
|
||||||
|
|
@ -130,18 +206,18 @@ StartBuild()
|
||||||
// all active windows docked into it will lose their parent and become undocked.
|
// all active windows docked into it will lose their parent and become undocked.
|
||||||
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
|
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
|
||||||
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
|
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
|
||||||
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||||
Begin("DockSpace Demo", nullptr, windowFlags);
|
Begin("DockSpace Demo", nullptr, windowFlags);
|
||||||
PopStyleVar();
|
PopStyleVar();
|
||||||
|
|
||||||
PopStyleVar(2);
|
PopStyleVar(2);
|
||||||
|
|
||||||
// DockSpace
|
// DockSpace
|
||||||
if (GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
if (GetIO().ConfigFlags & ImGuiConfigFlags_DockingEnable)
|
||||||
{
|
{
|
||||||
const ImGuiID dockspaceId = GetID("MyDockSpace");
|
ImGuiID const dockspaceId = GetID("MyDockSpace");
|
||||||
DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockspaceFlags);
|
DockSpace(dockspaceId, ImVec2(0.0f, 0.0f), dockspaceFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -161,7 +237,7 @@ EndBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk::ImageView view)
|
Draw(vk::CommandBuffer const commandBuffer, vk::Extent2D const extent, vk::ImageView const view)
|
||||||
{
|
{
|
||||||
// OPTICK_EVENT();
|
// OPTICK_EVENT();
|
||||||
|
|
||||||
|
|
@ -181,7 +257,7 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk:
|
||||||
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
|
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
};
|
};
|
||||||
|
|
||||||
const vk::RenderingInfo renderingInfo = {
|
vk::RenderingInfo const renderingInfo = {
|
||||||
.renderArea = {.extent = extent},
|
.renderArea = {.extent = extent},
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
.colorAttachmentCount = 1,
|
.colorAttachmentCount = 1,
|
||||||
|
|
@ -200,6 +276,36 @@ Draw(const vk::CommandBuffer commandBuffer, const vk::Extent2D extent, const vk:
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Draw(systems::Frame &frame, systems::GraphicsContext &context)
|
||||||
|
{
|
||||||
|
context.BeginDebugRegion("UI Pass", {0.9f, 0.9f, 1.0f, 1.0f});
|
||||||
|
|
||||||
|
vk::RenderingAttachmentInfo attachmentInfo = {
|
||||||
|
.imageView = frame.m_SwapchainImageView,
|
||||||
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
|
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
||||||
|
.loadOp = vk::AttachmentLoadOp::eLoad,
|
||||||
|
.storeOp = vk::AttachmentStoreOp::eStore,
|
||||||
|
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::RenderingInfo const renderingInfo = {
|
||||||
|
.renderArea = {.extent = frame.m_SwapchainSize},
|
||||||
|
.layerCount = 1,
|
||||||
|
.colorAttachmentCount = 1,
|
||||||
|
.pColorAttachments = &attachmentInfo,
|
||||||
|
.pDepthAttachment = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
context.BeginRendering(renderingInfo);
|
||||||
|
|
||||||
|
ImGui_ImplVulkan_RenderDrawData(GetDrawData(), context.GetCommandBuffer());
|
||||||
|
|
||||||
|
context.EndRendering();
|
||||||
|
context.EndDebugRegion();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PushDisable()
|
PushDisable()
|
||||||
{
|
{
|
||||||
|
|
@ -213,4 +319,4 @@ PopDisable()
|
||||||
PopStyleVar();
|
PopStyleVar();
|
||||||
PopItemFlag();
|
PopItemFlag();
|
||||||
}
|
}
|
||||||
} // namespace ImGui
|
} // namespace ImGui
|
||||||
|
|
@ -1,31 +1,41 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: gui.h
|
// Aster: gui.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
|
#include "aster/core/device.h"
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
struct AttachmentImage;
|
|
||||||
struct Device;
|
struct Device;
|
||||||
struct Context;
|
struct Instance;
|
||||||
struct Window;
|
struct Window;
|
||||||
struct Swapchain;
|
struct Swapchain;
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class RenderingDevice;
|
||||||
|
class GraphicsContext;
|
||||||
|
struct Frame;
|
||||||
|
}
|
||||||
|
|
||||||
// ReSharper disable once CppInconsistentNaming
|
// ReSharper disable once CppInconsistentNaming
|
||||||
namespace ImGui
|
namespace ImGui
|
||||||
{
|
{
|
||||||
void Init(const Context *context, const Device *device, const Window *window, vk::Format attachmentFormat,
|
void Init(systems::RenderingDevice &device, Window &window);
|
||||||
|
void Init(const Instance *context, const Device *device, const Window *window, vk::Format attachmentFormat,
|
||||||
u32 imageCount, u32 queueFamily, vk::Queue queue);
|
u32 imageCount, u32 queueFamily, vk::Queue queue);
|
||||||
|
void Destroy(const systems::RenderingDevice &device);
|
||||||
void Destroy(const Device *device);
|
void Destroy(const Device *device);
|
||||||
|
|
||||||
void Recreate();
|
void Recreate();
|
||||||
void StartBuild();
|
void StartBuild();
|
||||||
void EndBuild();
|
void EndBuild();
|
||||||
void Draw(vk::CommandBuffer commandBuffer, vk::Extent2D extent, vk::ImageView view);
|
void Draw(vk::CommandBuffer commandBuffer, vk::Extent2D extent, vk::ImageView view);
|
||||||
|
void Draw(systems::Frame &frame, systems::GraphicsContext &context);
|
||||||
|
|
||||||
void PushDisable();
|
void PushDisable();
|
||||||
void PopDisable();
|
void PopDisable();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: helpers.cpp
|
// Aster: helpers.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
@ -15,24 +15,24 @@ constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | Queue
|
||||||
QueueSupportFlagBits::eTransfer;
|
QueueSupportFlagBits::eTransfer;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IsSuitableDevice(const PhysicalDevice *physicalDevice)
|
IsSuitableDevice(PhysicalDevice const *physicalDevice)
|
||||||
{
|
{
|
||||||
const bool hasAllRequiredQueues =
|
bool const hasAllRequiredQueues =
|
||||||
std::ranges::any_of(physicalDevice->m_QueueFamilies, [](const auto &queueFamilyProp) {
|
std::ranges::any_of(physicalDevice->m_QueueFamilies, [](auto const &queueFamilyProp) {
|
||||||
return (queueFamilyProp.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT;
|
return (queueFamilyProp.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT;
|
||||||
});
|
});
|
||||||
|
|
||||||
const bool isNotCpu = physicalDevice->m_DeviceProperties.deviceType != vk::PhysicalDeviceType::eCpu;
|
bool const isNotCpu = physicalDevice->m_DeviceProperties.deviceType != vk::PhysicalDeviceType::eCpu;
|
||||||
|
|
||||||
const bool hasPresentMode = !physicalDevice->m_PresentModes.empty();
|
bool const hasPresentMode = !physicalDevice->m_PresentModes.empty();
|
||||||
|
|
||||||
const bool hasSurfaceFormat = !physicalDevice->m_SurfaceFormats.empty();
|
bool const hasSurfaceFormat = !physicalDevice->m_SurfaceFormats.empty();
|
||||||
|
|
||||||
return hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues;
|
return hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalDevice
|
PhysicalDevice
|
||||||
FindSuitableDevice(const PhysicalDevices &physicalDevices)
|
FindSuitableDevice(PhysicalDevices const &physicalDevices)
|
||||||
{
|
{
|
||||||
for (auto &physicalDevice : physicalDevices)
|
for (auto &physicalDevice : physicalDevices)
|
||||||
{
|
{
|
||||||
|
|
@ -47,7 +47,7 @@ FindSuitableDevice(const PhysicalDevices &physicalDevices)
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueAllocation
|
QueueAllocation
|
||||||
FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice)
|
FindAppropriateQueueAllocation(PhysicalDevice const *physicalDevice)
|
||||||
{
|
{
|
||||||
for (auto &queueFamilyInfo : physicalDevice->m_QueueFamilies)
|
for (auto &queueFamilyInfo : physicalDevice->m_QueueFamilies)
|
||||||
{
|
{
|
||||||
|
|
@ -61,77 +61,4 @@ FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice)
|
||||||
}
|
}
|
||||||
ERROR("No suitable queue family on the GPU.")
|
ERROR("No suitable queue family on the GPU.")
|
||||||
THEN_ABORT(vk::Result::eErrorUnknown);
|
THEN_ABORT(vk::Result::eErrorUnknown);
|
||||||
}
|
|
||||||
|
|
||||||
eastl::vector<u32>
|
|
||||||
ReadFile(cstr fileName)
|
|
||||||
{
|
|
||||||
FILE *filePtr = fopen(fileName, "rb");
|
|
||||||
|
|
||||||
if (!filePtr)
|
|
||||||
{
|
|
||||||
ERROR("Invalid read of {}", fileName) THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
eastl::vector<u32> outputVec;
|
|
||||||
eastl::array<u32, 1024> buffer{};
|
|
||||||
usize totalRead = 0;
|
|
||||||
usize readCount;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
readCount = fread(buffer.data(), sizeof(u32), buffer.size(), filePtr);
|
|
||||||
const auto nextSize = totalRead + readCount;
|
|
||||||
outputVec.resize(nextSize);
|
|
||||||
memcpy(outputVec.data() + totalRead, buffer.data(), readCount * sizeof *buffer.data());
|
|
||||||
totalRead = nextSize;
|
|
||||||
} while (readCount == buffer.size());
|
|
||||||
|
|
||||||
return outputVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
eastl::vector<u8>
|
|
||||||
ReadFileBytes(cstr fileName, bool errorOnFail)
|
|
||||||
{
|
|
||||||
FILE *filePtr = fopen(fileName, "rb");
|
|
||||||
|
|
||||||
if (!filePtr)
|
|
||||||
{
|
|
||||||
ERROR_IF(errorOnFail, "Invalid open (r) of {}. Cause: {}", fileName, errno);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
eastl::vector<u8> outputVec;
|
|
||||||
eastl::array<u8, 4096> buffer{};
|
|
||||||
usize totalRead = 0;
|
|
||||||
usize readCount;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
readCount = fread(buffer.data(), sizeof(u8), buffer.size(), filePtr);
|
|
||||||
const auto nextSize = totalRead + readCount;
|
|
||||||
outputVec.resize(nextSize);
|
|
||||||
memcpy(outputVec.data() + totalRead, buffer.data(), readCount * sizeof *buffer.data());
|
|
||||||
totalRead = nextSize;
|
|
||||||
} while (readCount == buffer.size());
|
|
||||||
|
|
||||||
(void)fclose(filePtr);
|
|
||||||
|
|
||||||
return outputVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
WriteFileBytes(cstr fileName, eastl::span<u8> data)
|
|
||||||
{
|
|
||||||
FILE *filePtr = fopen(fileName, "wb");
|
|
||||||
|
|
||||||
if (!filePtr)
|
|
||||||
{
|
|
||||||
ERROR("Invalid open (w) of {}. Cause: {}", fileName, errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const usize written = fwrite(data.data(), sizeof(u8), data.size(), filePtr);
|
|
||||||
|
|
||||||
(void)fclose(filePtr);
|
|
||||||
|
|
||||||
return written == data.size();
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: helpers.h
|
// Aster: helpers.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -18,9 +18,6 @@ class PhysicalDevices;
|
||||||
|
|
||||||
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
|
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
|
||||||
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
|
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
|
||||||
eastl::vector<u32> ReadFile(cstr fileName);
|
|
||||||
eastl::vector<u8> ReadFileBytes(cstr fileName, bool errorOnFail = true);
|
|
||||||
bool WriteFileBytes(cstr fileName, eastl::span<u8> data);
|
|
||||||
|
|
||||||
template <usize TSize>
|
template <usize TSize>
|
||||||
using StackString = eastl::fixed_string<char, TSize, false>;
|
using StackString = eastl::fixed_string<char, TSize, false>;
|
||||||
|
|
@ -29,7 +26,7 @@ using StackString = eastl::fixed_string<char, TSize, false>;
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
vk::Result _checkResultValue_; \
|
vk::Result _checkResultValue_; \
|
||||||
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
|
ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), "Cause: {}", _checkResultValue_) \
|
||||||
THEN_ABORT(_checkResultValue_); \
|
THEN_ABORT(_checkResultValue_); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
|
|
@ -37,13 +34,13 @@ using StackString = eastl::fixed_string<char, TSize, false>;
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
vk::Result _checkResultValue_; \
|
vk::Result _checkResultValue_; \
|
||||||
ERROR_IF(Failed(_checkResultValue_ = Cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
|
ERROR_IF(Failed(_checkResultValue_ = static_cast<vk::Result>(RESULT)), MSG " Cause: {}", EXTRA, _checkResultValue_) \
|
||||||
THEN_ABORT(_checkResultValue_); \
|
THEN_ABORT(_checkResultValue_); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
#define AbortIfFailedM(RESULT, MSG) \
|
#define AbortIfFailedM(RESULT, MSG) \
|
||||||
do \
|
do \
|
||||||
{ \
|
{ \
|
||||||
auto _checkResultValue_ = Cast<vk::Result>(RESULT); \
|
auto _checkResultValue_ = static_cast<vk::Result>(RESULT); \
|
||||||
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
|
ERROR_IF(Failed(_checkResultValue_), MSG " Cause: {}", _checkResultValue_) THEN_ABORT(_checkResultValue_); \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
add_executable(triangle "triangle.cpp")
|
add_executable(triangle "triangle.cpp")
|
||||||
add_shader(triangle "shader/triangle.vert.glsl")
|
add_shader(triangle "shader/triangle.slang")
|
||||||
add_shader(triangle "shader/triangle.frag.glsl")
|
add_resource_dir(triangle "shader")
|
||||||
|
|
||||||
target_link_libraries(triangle PRIVATE aster_core)
|
target_link_libraries(triangle PRIVATE aster_core)
|
||||||
target_link_libraries(triangle PRIVATE util_helper)
|
target_link_libraries(triangle PRIVATE util_helper)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#version 450
|
|
||||||
#pragma shader_stage(fragment)
|
|
||||||
|
|
||||||
layout (location = 0) in vec3 inColor;
|
|
||||||
layout (location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
outColor = vec4(inColor, 1.0);
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
float3 point;
|
||||||
|
float3 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSIn {
|
||||||
|
Vertex vertex;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSOut
|
||||||
|
{
|
||||||
|
float4 Pos : SV_POSITION;
|
||||||
|
float3 Color : COLOR0;
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
VSOut vsmain(VSIn input) {
|
||||||
|
VSOut output;
|
||||||
|
output.Pos = float4(input.vertex.point, 1.0f);
|
||||||
|
output.Color = input.vertex.color;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FSOut {
|
||||||
|
float4 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
FSOut fsmain(VSOut input) {
|
||||||
|
FSOut outp;
|
||||||
|
outp.Color = float4(input.Color, 1.0);
|
||||||
|
return outp;
|
||||||
|
}
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
#version 450
|
|
||||||
#pragma shader_stage(vertex)
|
|
||||||
|
|
||||||
layout(location=0) in vec4 position;
|
|
||||||
layout(location=1) in vec4 color;
|
|
||||||
|
|
||||||
layout(location=0) out vec3 outColor;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
/*
|
|
||||||
vec3 points[] = {
|
|
||||||
vec3(-0.5f, -0.5f, 0.0f),
|
|
||||||
vec3(0.5f, -0.5f, 0.0f),
|
|
||||||
vec3(0.0f, 0.5f, 0.0f)
|
|
||||||
};
|
|
||||||
vec3 colors[] = {
|
|
||||||
vec3( 1.0f, 0.0f, 0.0f ),
|
|
||||||
vec3( 0.0f, 1.0f, 0.0f ),
|
|
||||||
vec3( 0.0f, 0.0f, 1.0f ),
|
|
||||||
};
|
|
||||||
|
|
||||||
gl_Position = vec4(points[gl_VertexIndex], 1.0f);
|
|
||||||
outColor = vec3(colors[gl_VertexIndex]); //*/
|
|
||||||
//*
|
|
||||||
gl_Position = vec4(position.xyz, 1.0f);
|
|
||||||
outColor = vec3(color.rgb); //*/
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
struct VSIn {
|
|
||||||
int idx : SV_VERTEXID;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VSOut
|
|
||||||
{
|
|
||||||
float4 Pos : SV_POSITION;
|
|
||||||
[[vk::location(0)]] float3 Color : COLOR0;
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOut main(VSIn input) {
|
|
||||||
float3 points[] = {
|
|
||||||
float3(-0.5f, -0.5f, 0.0f),
|
|
||||||
float3(0.5f, -0.5f, 0.0f),
|
|
||||||
float3(0.0f, 0.5f, 0.0f)
|
|
||||||
};
|
|
||||||
float3 colors[] = {
|
|
||||||
float3( 1.0f, 0.0f, 0.0f ),
|
|
||||||
float3( 0.0f, 1.0f, 0.0f ),
|
|
||||||
float3( 0.0f, 0.0f, 1.0f ),
|
|
||||||
};
|
|
||||||
|
|
||||||
VSOut output;
|
|
||||||
output.Pos = float4(points[input.idx], 1.0f);
|
|
||||||
output.Color = colors[input.idx];
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -1,115 +1,78 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: triangle.cpp
|
// Aster: triangle.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
#include "aster/core/constants.h"
|
#include "aster/core/constants.h"
|
||||||
#include "aster/core/context.h"
|
#include "aster/core/instance.h"
|
||||||
#include "aster/core/device.h"
|
|
||||||
#include "aster/core/physical_device.h"
|
#include "aster/core/physical_device.h"
|
||||||
#include "aster/core/pipeline.h"
|
#include "aster/core/pipeline.h"
|
||||||
#include "aster/core/swapchain.h"
|
#include "aster/core/swapchain.h"
|
||||||
#include "aster/core/window.h"
|
#include "aster/core/window.h"
|
||||||
|
|
||||||
|
#include "aster/core/pipeline.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
#include "aster/util/files.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
|
|
||||||
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
constexpr auto SHADER_MODULE = "triangle.slang";
|
||||||
constexpr auto VERTEX_SHADER_FILE = "shader/triangle.vert.glsl.spv";
|
|
||||||
constexpr auto FRAGMENT_SHADER_FILE = "shader/triangle.frag.glsl.spv";
|
|
||||||
|
|
||||||
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
|
|
||||||
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
|
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
vec3 m_Position;
|
vec3 m_Position;
|
||||||
vec3 m_Color;
|
vec3 m_Color;
|
||||||
|
|
||||||
constexpr static vk::VertexInputBindingDescription
|
static eastl::vector<systems::AttributeInfo>
|
||||||
GetBinding(const u32 binding)
|
GetAttributes()
|
||||||
{
|
|
||||||
return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr static eastl::array<vk::VertexInputAttributeDescription, 2>
|
|
||||||
GetAttributes(const u32 binding)
|
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
vk::VertexInputAttributeDescription{
|
{
|
||||||
.location = 0,
|
.m_Location = 0,
|
||||||
.binding = binding,
|
.m_Offset = offsetof(Vertex, m_Position),
|
||||||
.format = vk::Format::eR32G32B32Sfloat,
|
.m_Format = systems::AttributeInfo::Format::eFloat32X3,
|
||||||
.offset = offsetof(Vertex, m_Position),
|
|
||||||
},
|
},
|
||||||
vk::VertexInputAttributeDescription{
|
{
|
||||||
.location = 1,
|
.m_Location = 1,
|
||||||
.binding = binding,
|
.m_Offset = offsetof(Vertex, m_Color),
|
||||||
.format = vk::Format::eR32G32B32Sfloat,
|
.m_Format = systems::AttributeInfo::Format::eFloat32X3,
|
||||||
.offset = offsetof(Vertex, m_Color),
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Frame
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
vk::CommandPool m_Pool;
|
|
||||||
vk::CommandBuffer m_CommandBuffer;
|
|
||||||
vk::Fence m_FrameAvailableFence;
|
|
||||||
vk::Semaphore m_ImageAcquireSem;
|
|
||||||
vk::Semaphore m_RenderFinishSem;
|
|
||||||
|
|
||||||
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
|
||||||
~Frame();
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int, char **)
|
main(int, char **)
|
||||||
{
|
{
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
||||||
|
|
||||||
Window window = {"Triangle (Aster)", {640, 480}};
|
Window window = {"Triangle (Aster)", {640, 480}};
|
||||||
Context context = {"Triangle", VERSION};
|
systems::RenderingDevice device{{
|
||||||
Surface surface = {&context, &window, "Primary"};
|
.m_Window = window,
|
||||||
|
.m_Features = {.m_Vulkan12Features = {.bufferDeviceAddress = true},
|
||||||
|
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true}},
|
||||||
|
.m_AppName = "Triangle",
|
||||||
|
.m_ShaderSearchPaths = {"shader/"},
|
||||||
|
.m_UseBindless = false,
|
||||||
|
.m_Name = "Primary",
|
||||||
|
}};
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&surface, &context};
|
Pipeline pipeline;
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
auto pipelineError = device.CreateGraphicsPipeline(pipeline, {
|
||||||
|
.m_VertexInputs = {{
|
||||||
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
.m_Attribute = Vertex::GetAttributes(),
|
||||||
|
.m_Stride = sizeof(Vertex),
|
||||||
Features enabledDeviceFeatures = {
|
}},
|
||||||
.m_Vulkan12Features = {.bufferDeviceAddress = true},
|
.m_Shaders = {{
|
||||||
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
|
.m_ShaderFile = SHADER_MODULE,
|
||||||
};
|
.m_EntryPoints = {"vsmain", "fsmain"},
|
||||||
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
|
}},
|
||||||
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
|
});
|
||||||
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
|
ERROR_IF(pipelineError, "Error creating pipeline. Cause: {}", pipelineError.What());
|
||||||
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
|
|
||||||
Pipeline pipeline = CreatePipeline(&device, &swapchain);
|
|
||||||
|
|
||||||
vk::CommandPool copyPool;
|
|
||||||
vk::CommandBuffer copyBuffer;
|
|
||||||
{
|
|
||||||
vk::CommandPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
};
|
|
||||||
auto result = device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool);
|
|
||||||
ERROR_IF(Failed(result), "Copy command pool creation failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
|
|
||||||
.commandPool = copyPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
result = device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer);
|
|
||||||
ERROR_IF(Failed(result), "Copy command buffer allocation failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eastl::array<Vertex, 3> vertices{};
|
// eastl::array<Vertex, 3> vertices{};
|
||||||
eastl::array vertices = {
|
eastl::array vertices = {
|
||||||
|
|
@ -117,60 +80,10 @@ main(int, char **)
|
||||||
Vertex{.m_Position = {0.5f, -0.5f, 0.0f}, .m_Color = {0.0f, 1.0f, 0.0f}},
|
Vertex{.m_Position = {0.5f, -0.5f, 0.0f}, .m_Color = {0.0f, 1.0f, 0.0f}},
|
||||||
Vertex{.m_Position = {0.0f, 0.5f, 0.0f}, .m_Color = {0.0f, 0.0f, 1.0f}},
|
Vertex{.m_Position = {0.0f, 0.5f, 0.0f}, .m_Color = {0.0f, 0.0f, 1.0f}},
|
||||||
};
|
};
|
||||||
VertexBuffer vbo;
|
auto vbo = device.CreateVertexBuffer(vertices.size() * sizeof vertices[0], "VBO");
|
||||||
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
|
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
|
||||||
{
|
|
||||||
StagingBuffer staging;
|
|
||||||
staging.Init(&device, vertices.size() * sizeof vertices[0], "Staging");
|
|
||||||
staging.Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
|
|
||||||
|
|
||||||
vk::Fence fence;
|
|
||||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
|
||||||
auto result = device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence);
|
|
||||||
ERROR_IF(Failed(result), "Fence creation failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
|
||||||
result = copyBuffer.begin(&beginInfo);
|
|
||||||
ERROR_IF(Failed(result), "Copy begin failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = staging.GetSize()};
|
|
||||||
copyBuffer.copyBuffer(staging.m_Buffer, vbo.m_Buffer, 1, &bufferCopy);
|
|
||||||
|
|
||||||
result = copyBuffer.end();
|
|
||||||
ERROR_IF(Failed(result), "Copy end failed. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = ©Buffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
result = commandQueue.submit(1, &submitInfo, fence);
|
|
||||||
ERROR_IF(Failed(result), "Submit failed. Cause: {}", result) THEN_ABORT(result) ELSE_INFO("Submit copy");
|
|
||||||
|
|
||||||
result = device.m_Device.waitForFences(1, &fence, true, MaxValue<u64>);
|
|
||||||
ERROR_IF(Failed(result), "Fence wait failed. Cause: {}", result) THEN_ABORT(result) ELSE_INFO("Fence wait");
|
|
||||||
|
|
||||||
result = device.m_Device.resetCommandPool(copyPool, {});
|
|
||||||
ERROR_IF(Failed(result), "Couldn't reset command pool. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
device.m_Device.destroy(fence, nullptr);
|
|
||||||
staging.Destroy(&device);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Persistent variables
|
// Persistent variables
|
||||||
vk::Viewport viewport = {
|
|
||||||
.x = 0,
|
|
||||||
.y = Cast<f32>(swapchain.m_Extent.height),
|
|
||||||
.width = Cast<f32>(swapchain.m_Extent.width),
|
|
||||||
.height = -Cast<f32>(swapchain.m_Extent.height),
|
|
||||||
.minDepth = 0.0,
|
|
||||||
.maxDepth = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Rect2D scissor = {
|
|
||||||
.offset = {0, 0},
|
|
||||||
.extent = swapchain.m_Extent,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::ImageSubresourceRange subresourceRange = {
|
vk::ImageSubresourceRange subresourceRange = {
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -186,8 +99,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo topOfThePipeDependency = {
|
vk::DependencyInfo topOfThePipeDependency = {
|
||||||
|
|
@ -201,8 +114,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo renderToPresentDependency = {
|
vk::DependencyInfo renderToPresentDependency = {
|
||||||
|
|
@ -210,70 +123,39 @@ main(int, char **)
|
||||||
.pImageMemoryBarriers = &renderToPresentBarrier,
|
.pImageMemoryBarriers = &renderToPresentBarrier,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Frames
|
|
||||||
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
|
|
||||||
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
|
|
||||||
{
|
|
||||||
frames.emplace_back(&device, queueAllocation.m_Family, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO("Starting loop");
|
INFO("Starting loop");
|
||||||
u32 frameIndex = 0;
|
|
||||||
while (window.Poll())
|
while (window.Poll())
|
||||||
{
|
{
|
||||||
Frame *currentFrame = &frames[frameIndex];
|
systems::Frame ¤tFrame = device.GetNextFrame();
|
||||||
|
|
||||||
auto result = device.m_Device.waitForFences(1, ¤tFrame->m_FrameAvailableFence, true, MaxValue<u64>);
|
Size2D swapchainSize = currentFrame.m_SwapchainSize;
|
||||||
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
u32 imageIndex;
|
vk::Viewport viewport = {
|
||||||
result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
|
.x = 0,
|
||||||
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
|
.y = static_cast<f32>(swapchainSize.m_Height),
|
||||||
if (Failed(result))
|
.width = static_cast<f32>(swapchainSize.m_Width),
|
||||||
{
|
.height = -static_cast<f32>(swapchainSize.m_Height),
|
||||||
switch (result)
|
.minDepth = 0.0,
|
||||||
{
|
.maxDepth = 1.0,
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
};
|
||||||
case vk::Result::eSuboptimalKHR:
|
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
|
||||||
swapchain.Create(&surface, window.GetSize());
|
|
||||||
viewport.y = Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
viewport.width = Cast<f32>(swapchain.m_Extent.width);
|
|
||||||
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
scissor.extent = swapchain.m_Extent;
|
|
||||||
continue; // Image acquire has failed. We move to the next frame.
|
|
||||||
default:
|
|
||||||
ERROR("Waiting for swapchain image {} failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Reset fences here. In case swapchain was out of date, we leave the fences signalled.
|
|
||||||
result = device.m_Device.resetFences(1, ¤tFrame->m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Fence {} reset failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
result = device.m_Device.resetCommandPool(currentFrame->m_Pool, {});
|
vk::Rect2D scissor = {
|
||||||
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
|
.offset = {0, 0},
|
||||||
THEN_ABORT(result);
|
.extent = static_cast<vk::Extent2D>(swapchainSize),
|
||||||
|
};
|
||||||
|
|
||||||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
auto context = currentFrame.CreateGraphicsContext();
|
||||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
|
||||||
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
|
|
||||||
|
|
||||||
topOfThePipeBarrier.image = currentImage;
|
topOfThePipeBarrier.image = currentFrame.m_SwapchainImage;
|
||||||
renderToPresentBarrier.image = currentImage;
|
renderToPresentBarrier.image = currentFrame.m_SwapchainImage;
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
context.Begin();
|
||||||
result = cmd.begin(&beginInfo);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&topOfThePipeDependency);
|
context.Dependency(topOfThePipeDependency);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
vk::RenderingAttachmentInfo attachmentInfo = {
|
vk::RenderingAttachmentInfo attachmentInfo = {
|
||||||
.imageView = currentImageView,
|
.imageView = currentFrame.m_SwapchainImageView,
|
||||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
||||||
.loadOp = vk::AttachmentLoadOp::eClear,
|
.loadOp = vk::AttachmentLoadOp::eClear,
|
||||||
|
|
@ -282,265 +164,29 @@ main(int, char **)
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::RenderingInfo renderingInfo = {
|
vk::RenderingInfo renderingInfo = {
|
||||||
.renderArea = {.extent = swapchain.m_Extent},
|
.renderArea = scissor,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
.colorAttachmentCount = 1,
|
.colorAttachmentCount = 1,
|
||||||
.pColorAttachments = &attachmentInfo,
|
.pColorAttachments = &attachmentInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd.beginRendering(&renderingInfo);
|
context.BeginRendering(renderingInfo);
|
||||||
|
|
||||||
cmd.setViewport(0, 1, &viewport);
|
context.SetViewport(viewport);
|
||||||
cmd.setScissor(0, 1, &scissor);
|
context.BindPipeline(pipeline);
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
context.BindVertexBuffer(vbo);
|
||||||
usize offsets = 0;
|
context.Draw(3);
|
||||||
cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets);
|
|
||||||
cmd.draw(3, 1, 0, 0);
|
|
||||||
|
|
||||||
cmd.endRendering();
|
context.EndRendering();
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&renderToPresentDependency);
|
context.Dependency(renderToPresentDependency);
|
||||||
|
|
||||||
result = cmd.end();
|
context.End();
|
||||||
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
device.Present(currentFrame, context);
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_ImageAcquireSem,
|
|
||||||
.pWaitDstStageMask = &waitDstStage,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &cmd,
|
|
||||||
.signalSemaphoreCount = 1,
|
|
||||||
.pSignalSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
};
|
|
||||||
result = commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Command queue submit failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::PresentInfoKHR presentInfo = {
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
.swapchainCount = 1,
|
|
||||||
.pSwapchains = &swapchain.m_Swapchain,
|
|
||||||
.pImageIndices = &imageIndex,
|
|
||||||
.pResults = nullptr,
|
|
||||||
};
|
|
||||||
result = commandQueue.presentKHR(&presentInfo);
|
|
||||||
if (Failed(result))
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
|
||||||
case vk::Result::eSuboptimalKHR:
|
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
|
||||||
swapchain.Create(&surface, window.GetSize());
|
|
||||||
viewport.y = Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
viewport.width = Cast<f32>(swapchain.m_Extent.width);
|
|
||||||
viewport.height = -Cast<f32>(swapchain.m_Extent.height);
|
|
||||||
scissor.extent = swapchain.m_Extent;
|
|
||||||
break; // Present failed. We redo the frame.
|
|
||||||
default:
|
|
||||||
ERROR("Command queue present failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
frameIndex = (frameIndex + 1) % MAX_FRAMES_IN_FLIGHT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.WaitIdle();
|
device.WaitIdle();
|
||||||
|
|
||||||
device.m_Device.destroy(copyPool, nullptr);
|
|
||||||
vbo.Destroy(&device);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
Frame::Frame(const Device *device, const u32 queueFamilyIndex, const u32 frameCount)
|
|
||||||
{
|
|
||||||
m_Device = device;
|
|
||||||
|
|
||||||
const vk::CommandPoolCreateInfo commandPoolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = queueFamilyIndex,
|
|
||||||
};
|
|
||||||
vk::Result result = device->m_Device.createCommandPool(&commandPoolCreateInfo, nullptr, &m_Pool);
|
|
||||||
ERROR_IF(Failed(result), "Could not command pool for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
constexpr vk::FenceCreateInfo fenceCreateInfo = {.flags = vk::FenceCreateFlagBits::eSignaled};
|
|
||||||
result = device->m_Device.createFence(&fenceCreateInfo, nullptr, &m_FrameAvailableFence);
|
|
||||||
ERROR_IF(Failed(result), "Could not create a fence for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo = {};
|
|
||||||
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_ImageAcquireSem);
|
|
||||||
ERROR_IF(Failed(result), "Could not create IA semaphore for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
result = device->m_Device.createSemaphore(&semaphoreCreateInfo, nullptr, &m_RenderFinishSem);
|
|
||||||
ERROR_IF(Failed(result), "Could not create RF semaphore for frame {}. Cause: {}", frameCount, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
const vk::CommandBufferAllocateInfo allocateInfo = {
|
|
||||||
.commandPool = m_Pool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
|
|
||||||
|
|
||||||
result = m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &m_CommandBuffer);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer allocation failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
DEBUG("Frame {} created successfully.", frameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline
|
|
||||||
CreatePipeline(const Device *device, const Swapchain *swapchain)
|
|
||||||
{
|
|
||||||
// Pipeline Setup
|
|
||||||
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
|
|
||||||
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
|
|
||||||
|
|
||||||
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
|
||||||
{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
|
||||||
.module = vertexShaderModule,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
.module = fragmentShaderModule,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
|
||||||
.setLayoutCount = 0,
|
|
||||||
.pSetLayouts = nullptr,
|
|
||||||
.pushConstantRangeCount = 0,
|
|
||||||
.pPushConstantRanges = nullptr,
|
|
||||||
};
|
|
||||||
vk::PipelineLayout pipelineLayout;
|
|
||||||
vk::Result result = device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout);
|
|
||||||
ERROR_IF(Failed(result), "Could not create a pipeline layout. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
device->SetName(pipelineLayout, "Triangle Layout");
|
|
||||||
|
|
||||||
vk::VertexInputBindingDescription inputBindingDescription = Vertex::GetBinding(0);
|
|
||||||
auto inputAttributeDescription = Vertex::GetAttributes(0);
|
|
||||||
|
|
||||||
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {
|
|
||||||
.vertexBindingDescriptionCount = 1,
|
|
||||||
.pVertexBindingDescriptions = &inputBindingDescription,
|
|
||||||
.vertexAttributeDescriptionCount = Cast<u32>(inputAttributeDescription.size()),
|
|
||||||
.pVertexAttributeDescriptions = inputAttributeDescription.data(),
|
|
||||||
};
|
|
||||||
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
|
|
||||||
.topology = vk::PrimitiveTopology::eTriangleList,
|
|
||||||
.primitiveRestartEnable = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
|
|
||||||
.viewportCount = 1,
|
|
||||||
.scissorCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
|
|
||||||
.depthClampEnable = false,
|
|
||||||
.rasterizerDiscardEnable = false,
|
|
||||||
.polygonMode = vk::PolygonMode::eFill,
|
|
||||||
.cullMode = vk::CullModeFlagBits::eNone,
|
|
||||||
.frontFace = vk::FrontFace::eCounterClockwise,
|
|
||||||
.depthBiasEnable = false,
|
|
||||||
.lineWidth = 1.0,
|
|
||||||
};
|
|
||||||
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
|
|
||||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
|
||||||
.sampleShadingEnable = false,
|
|
||||||
};
|
|
||||||
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
|
|
||||||
.depthTestEnable = false,
|
|
||||||
.depthWriteEnable = false,
|
|
||||||
};
|
|
||||||
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
|
|
||||||
.blendEnable = false,
|
|
||||||
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
|
|
||||||
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
|
|
||||||
.colorBlendOp = vk::BlendOp::eAdd,
|
|
||||||
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
|
|
||||||
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
|
||||||
.alphaBlendOp = vk::BlendOp::eAdd,
|
|
||||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
|
||||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
|
||||||
};
|
|
||||||
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
|
|
||||||
.logicOpEnable = false,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = &colorBlendAttachmentState,
|
|
||||||
};
|
|
||||||
|
|
||||||
eastl::array dynamicStates = {
|
|
||||||
vk::DynamicState::eScissor,
|
|
||||||
vk::DynamicState::eViewport,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
|
||||||
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
|
|
||||||
.pDynamicStates = dynamicStates.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
|
|
||||||
.viewMask = 0,
|
|
||||||
.colorAttachmentCount = 1,
|
|
||||||
.pColorAttachmentFormats = &swapchain->m_Format,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
|
|
||||||
.pNext = &renderingCreateInfo,
|
|
||||||
.stageCount = Cast<u32>(shaderStages.size()),
|
|
||||||
.pStages = shaderStages.data(),
|
|
||||||
.pVertexInputState = &vertexInputStateCreateInfo,
|
|
||||||
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
|
||||||
.pViewportState = &viewportStateCreateInfo,
|
|
||||||
.pRasterizationState = &rasterizationStateCreateInfo,
|
|
||||||
.pMultisampleState = &multisampleStateCreateInfo,
|
|
||||||
.pDepthStencilState = &depthStencilStateCreateInfo,
|
|
||||||
.pColorBlendState = &colorBlendStateCreateInfo,
|
|
||||||
.pDynamicState = &dynamicStateCreateInfo,
|
|
||||||
.layout = pipelineLayout,
|
|
||||||
};
|
|
||||||
vk::Pipeline pipeline;
|
|
||||||
result = device->m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline);
|
|
||||||
ERROR_IF(Failed(result), "Could not create a graphics pipeline. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
device->SetName(pipeline, "Triangle Pipeline");
|
|
||||||
|
|
||||||
device->m_Device.destroy(vertexShaderModule, nullptr);
|
|
||||||
device->m_Device.destroy(fragmentShaderModule, nullptr);
|
|
||||||
|
|
||||||
return {device, pipelineLayout, pipeline, {}};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ShaderModule
|
|
||||||
CreateShader(const Device *device, cstr shaderFile)
|
|
||||||
{
|
|
||||||
eastl::vector<u32> shaderCode = ReadFile(shaderFile);
|
|
||||||
|
|
||||||
const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
|
|
||||||
.codeSize = shaderCode.size() * sizeof(u32),
|
|
||||||
.pCode = shaderCode.data(),
|
|
||||||
};
|
|
||||||
vk::ShaderModule shaderModule;
|
|
||||||
|
|
||||||
vk::Result result = device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule);
|
|
||||||
ERROR_IF(Failed(result), "Shader {} could not be created. Cause: {}", shaderFile, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
return shaderModule;
|
|
||||||
}
|
|
||||||
|
|
||||||
Frame::~Frame()
|
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(m_RenderFinishSem, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_ImageAcquireSem, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_FrameAvailableFence, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_Pool, nullptr);
|
|
||||||
|
|
||||||
DEBUG("Destoryed Frame");
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,10 +5,8 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
|
||||||
|
|
||||||
add_executable(box "box.cpp" "stb_image.h")
|
add_executable(box "box.cpp" "stb_image.h")
|
||||||
add_shader(box "shader/box.vert.glsl")
|
add_shader(box "shader/box.slang")
|
||||||
add_shader(box "shader/box.frag.glsl")
|
add_resource_dir(box "shader/")
|
||||||
add_shader(box "shader/box.vs.hlsl")
|
|
||||||
add_shader(box "shader/box.ps.hlsl")
|
|
||||||
|
|
||||||
target_link_libraries(box PRIVATE aster_core)
|
target_link_libraries(box PRIVATE aster_core)
|
||||||
target_link_libraries(box PRIVATE util_helper)
|
target_link_libraries(box PRIVATE util_helper)
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,29 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: box.cpp
|
// Aster: box.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
#include "aster/core/constants.h"
|
#include "aster/core/constants.h"
|
||||||
#include "aster/core/context.h"
|
|
||||||
#include "aster/core/device.h"
|
|
||||||
#include "aster/core/image.h"
|
#include "aster/core/image.h"
|
||||||
#include "aster/core/physical_device.h"
|
#include "aster/core/physical_device.h"
|
||||||
#include "aster/core/pipeline.h"
|
#include "aster/core/pipeline.h"
|
||||||
#include "aster/core/swapchain.h"
|
#include "aster/core/swapchain.h"
|
||||||
#include "aster/core/window.h"
|
#include "aster/core/window.h"
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "aster/systems/buffer_manager.h"
|
#include "aster/systems/commit_manager.h"
|
||||||
#include "aster/systems/image_manager.h"
|
#include "aster/systems/rendering_device.h"
|
||||||
#include "frame.h"
|
#include "aster/util/files.h"
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
#include <EASTL/array.h>
|
||||||
|
|
||||||
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
|
||||||
constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv";
|
constexpr auto VERTEX_SHADER_FILE = "shader/box.vs.hlsl.spv";
|
||||||
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv";
|
constexpr auto FRAGMENT_SHADER_FILE = "shader/box.ps.hlsl.spv";
|
||||||
|
constexpr auto SHADER_FILE = "box";
|
||||||
|
|
||||||
struct ImageFile
|
struct ImageFile
|
||||||
{
|
{
|
||||||
|
|
@ -38,6 +34,12 @@ struct ImageFile
|
||||||
|
|
||||||
bool Load(cstr fileName);
|
bool Load(cstr fileName);
|
||||||
[[nodiscard]] usize GetSize() const;
|
[[nodiscard]] usize GetSize() const;
|
||||||
|
|
||||||
|
operator eastl::span<u8>() const
|
||||||
|
{
|
||||||
|
return {static_cast<u8 *>(m_Data), GetSize()};
|
||||||
|
}
|
||||||
|
|
||||||
~ImageFile();
|
~ImageFile();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -63,7 +65,7 @@ ImageFile::Load(cstr fileName)
|
||||||
usize
|
usize
|
||||||
ImageFile::GetSize() const
|
ImageFile::GetSize() const
|
||||||
{
|
{
|
||||||
return Cast<usize>(m_Width) * m_Height * m_NumChannels;
|
return static_cast<usize>(m_Width) * m_Height * m_NumChannels;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageFile::~ImageFile()
|
ImageFile::~ImageFile()
|
||||||
|
|
@ -72,9 +74,6 @@ ImageFile::~ImageFile()
|
||||||
m_Data = nullptr;
|
m_Data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
|
|
||||||
Pipeline CreatePipeline(const Device *device, const Swapchain *swapchain);
|
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex
|
||||||
{
|
{
|
||||||
vec3 m_Position;
|
vec3 m_Position;
|
||||||
|
|
@ -93,86 +92,55 @@ struct Camera
|
||||||
int
|
int
|
||||||
main(int, char **)
|
main(int, char **)
|
||||||
{
|
{
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
MIN_LOG_LEVEL(Logger::LogType::eDebug);
|
||||||
|
|
||||||
Window window = {"Box (Aster)", {640, 480}};
|
Window window = {"Box (Aster)", {640, 480}};
|
||||||
Context context = {"Box", VERSION};
|
|
||||||
Surface surface = {&context, &window, "Primary"};
|
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&surface, &context};
|
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
|
||||||
|
|
||||||
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
|
||||||
|
|
||||||
Features enabledDeviceFeatures = {
|
Features enabledDeviceFeatures = {
|
||||||
.m_Vulkan10Features = {.samplerAnisotropy = true},
|
.m_Vulkan10Features = {.samplerAnisotropy = true},
|
||||||
.m_Vulkan12Features = {.bufferDeviceAddress = true},
|
.m_Vulkan12Features =
|
||||||
|
{
|
||||||
|
.descriptorIndexing = true,
|
||||||
|
.shaderSampledImageArrayNonUniformIndexing = true,
|
||||||
|
.shaderStorageBufferArrayNonUniformIndexing = true,
|
||||||
|
.shaderStorageImageArrayNonUniformIndexing = true,
|
||||||
|
.descriptorBindingUniformBufferUpdateAfterBind = true, // Not related to Bindless
|
||||||
|
.descriptorBindingSampledImageUpdateAfterBind = true,
|
||||||
|
.descriptorBindingStorageImageUpdateAfterBind = true,
|
||||||
|
.descriptorBindingStorageBufferUpdateAfterBind = true,
|
||||||
|
.descriptorBindingPartiallyBound = true,
|
||||||
|
.runtimeDescriptorArray = true,
|
||||||
|
.timelineSemaphore = true,
|
||||||
|
.bufferDeviceAddress = true,
|
||||||
|
.bufferDeviceAddressCaptureReplay = true,
|
||||||
|
},
|
||||||
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
|
.m_Vulkan13Features = {.synchronization2 = true, .dynamicRendering = true},
|
||||||
};
|
};
|
||||||
QueueAllocation queueAllocation = FindAppropriateQueueAllocation(&deviceToUse);
|
|
||||||
Device device = {&context, &deviceToUse, &enabledDeviceFeatures, {queueAllocation}, "Primary Device"};
|
|
||||||
vk::Queue commandQueue = device.GetQueue(queueAllocation.m_Family, 0);
|
|
||||||
Swapchain swapchain = {&surface, &device, window.GetSize(), "Primary Chain"};
|
|
||||||
Pipeline pipeline = CreatePipeline(&device, &swapchain);
|
|
||||||
|
|
||||||
systems::BufferManager bufferManager{&device, 12, 0};
|
systems::RenderingDevice device{{
|
||||||
systems::ImageManager imageManager{&device, 12, 1};
|
.m_Window = window,
|
||||||
|
.m_Features = enabledDeviceFeatures,
|
||||||
|
.m_AppName = "Box",
|
||||||
|
.m_AppVersion = VERSION,
|
||||||
|
.m_ShaderSearchPaths = {"shader/"},
|
||||||
|
}};
|
||||||
|
|
||||||
|
Pipeline pipeline;
|
||||||
|
auto pipelineResult =
|
||||||
|
device.CreateGraphicsPipeline(pipeline, {.m_Shaders = {
|
||||||
|
{.m_ShaderFile = SHADER_FILE, .m_EntryPoints = {"vsmain", "fsmain"}},
|
||||||
|
}});
|
||||||
|
ERROR_IF(pipelineResult, "Could not create pipeline. Cause: {}", pipelineResult.What())
|
||||||
|
THEN_ABORT(pipelineResult.Value());
|
||||||
|
|
||||||
|
auto swapchainSize = device.GetSwapchainSize();
|
||||||
Camera camera = {
|
Camera camera = {
|
||||||
.m_Model = {1.0f},
|
.m_Model = {1.0f},
|
||||||
.m_View = glm::lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)),
|
.m_View = lookAt(vec3(0.0f, 2.0f, 2.0f), vec3(0.0f), vec3(0.0f, 1.0f, 0.0f)),
|
||||||
.m_Perspective = glm::perspective(
|
.m_Perspective = glm::perspective(
|
||||||
70_deg, Cast<f32>(swapchain.m_Extent.width) / Cast<f32>(swapchain.m_Extent.height), 0.1f, 100.0f),
|
70_deg, static_cast<f32>(swapchainSize.m_Width) / static_cast<f32>(swapchainSize.m_Height), 0.1f, 100.0f),
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::DescriptorPool descriptorPool;
|
|
||||||
vk::DescriptorSet descriptorSet;
|
|
||||||
{
|
|
||||||
vk::DescriptorSetLayout descriptorSetLayout = pipeline.m_SetLayouts.front();
|
|
||||||
eastl::array poolSizes = {
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eUniformBuffer,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
},
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
},
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
vk::DescriptorPoolCreateInfo descriptorPoolCreateInfo = {
|
|
||||||
.maxSets = 1, .poolSizeCount = Cast<u32>(poolSizes.size()), .pPoolSizes = poolSizes.data()};
|
|
||||||
AbortIfFailed(device.m_Device.createDescriptorPool(&descriptorPoolCreateInfo, nullptr, &descriptorPool));
|
|
||||||
|
|
||||||
vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
|
|
||||||
.descriptorPool = descriptorPool,
|
|
||||||
.descriptorSetCount = 1,
|
|
||||||
.pSetLayouts = &descriptorSetLayout,
|
|
||||||
};
|
|
||||||
AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &descriptorSet));
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::CommandPool copyPool;
|
|
||||||
vk::CommandBuffer copyBuffer;
|
|
||||||
{
|
|
||||||
vk::CommandPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
};
|
|
||||||
AbortIfFailedM(device.m_Device.createCommandPool(&poolCreateInfo, nullptr, ©Pool),
|
|
||||||
"Copy command pool creation failed.");
|
|
||||||
vk::CommandBufferAllocateInfo bufferAllocateInfo = {
|
|
||||||
.commandPool = copyPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
AbortIfFailedM(device.m_Device.allocateCommandBuffers(&bufferAllocateInfo, ©Buffer),
|
|
||||||
"Copy command buffer allocation failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
eastl::array vertices = {
|
eastl::array vertices = {
|
||||||
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
Vertex{.m_Position = vec3(0.5f, 0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 1.0f)},
|
||||||
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
Vertex{.m_Position = vec3(0.5f, -0.5f, -0.5f), .m_TexCoord0 = vec2(1.0f, 0.0f)},
|
||||||
|
|
@ -222,21 +190,19 @@ main(int, char **)
|
||||||
assert(loaded);
|
assert(loaded);
|
||||||
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
|
INFO("Image {}x{} : {} channels", imageFile.m_Width, imageFile.m_Height, imageFile.m_NumChannels);
|
||||||
|
|
||||||
auto vbo = bufferManager.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer").ToPointer();
|
auto vbo = device.CreateStorageBuffer(vertices.size() * sizeof vertices[0], "Vertex Buffer");
|
||||||
auto crate = imageManager
|
vbo->Write(0, vertices.size() * sizeof vertices[0], vertices.data());
|
||||||
.CreateTexture2D({
|
|
||||||
.m_Format = vk::Format::eR8G8B8A8Srgb,
|
auto crate = device.CreateTexture2DWithView({
|
||||||
.m_Extent = {imageFile.m_Width, imageFile.m_Height},
|
.m_Format = vk::Format::eR8G8B8A8Srgb,
|
||||||
.m_Name = "Crate Texture",
|
.m_Extent = {imageFile.m_Width, imageFile.m_Height},
|
||||||
})
|
.m_Name = "Crate Texture",
|
||||||
.ToPointer();
|
});
|
||||||
vbo->Write(&device, 0, vertices.size() * sizeof vertices[0], vertices.data());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
StagingBuffer imageStaging;
|
|
||||||
|
|
||||||
imageStaging.Init(&device, imageFile.GetSize(), "Image Staging");
|
auto imageStaging = device.CreateStagingBuffer(imageFile.GetSize(), "Image Staging");
|
||||||
imageStaging.Write(&device, 0, imageFile.GetSize(), imageFile.m_Data);
|
imageStaging->Write(0, imageFile.GetSize(), imageFile.m_Data);
|
||||||
|
|
||||||
vk::ImageMemoryBarrier2 imageReadyToWrite = {
|
vk::ImageMemoryBarrier2 imageReadyToWrite = {
|
||||||
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||||
|
|
@ -245,9 +211,9 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.image = crate->m_Image,
|
.image = crate->GetImage(),
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -269,9 +235,9 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = device.m_TransferQueueFamily,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = device.m_PrimaryQueueFamily,
|
||||||
.image = crate->m_Image,
|
.image = crate->GetImage(),
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -286,140 +252,25 @@ main(int, char **)
|
||||||
.pImageMemoryBarriers = &imageReadyToRead,
|
.pImageMemoryBarriers = &imageReadyToRead,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::Fence fence;
|
auto context = device.CreateTransferContext();
|
||||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
context.Begin();
|
||||||
AbortIfFailed(device.m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
|
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
context.Dependency(imageReadyToWriteDependency);
|
||||||
AbortIfFailed(copyBuffer.begin(&beginInfo));
|
|
||||||
|
|
||||||
copyBuffer.pipelineBarrier2(&imageReadyToWriteDependency);
|
context.UploadTexture(crate->m_Image, imageFile);
|
||||||
|
|
||||||
vk::BufferImageCopy imageCopy = {
|
context.Dependency(imageReadyToReadDependency);
|
||||||
.bufferOffset = 0,
|
|
||||||
.bufferRowLength = imageFile.m_Width,
|
|
||||||
.bufferImageHeight = imageFile.m_Height,
|
|
||||||
.imageSubresource =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.imageOffset = {},
|
|
||||||
.imageExtent = {imageFile.m_Width, imageFile.m_Height, 1},
|
|
||||||
};
|
|
||||||
copyBuffer.copyBufferToImage(imageStaging.m_Buffer, crate->m_Image, vk::ImageLayout::eTransferDstOptimal, 1,
|
|
||||||
&imageCopy);
|
|
||||||
|
|
||||||
copyBuffer.pipelineBarrier2(&imageReadyToReadDependency);
|
context.End();
|
||||||
|
|
||||||
AbortIfFailed(copyBuffer.end());
|
auto recpt = device.Submit(context);
|
||||||
|
device.WaitOn(recpt);
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = ©Buffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
AbortIfFailed(commandQueue.submit(1, &submitInfo, fence));
|
|
||||||
INFO("Submit copy");
|
|
||||||
|
|
||||||
AbortIfFailed(device.m_Device.waitForFences(1, &fence, true, MaxValue<u64>));
|
|
||||||
INFO("Fence wait");
|
|
||||||
|
|
||||||
AbortIfFailedM(device.m_Device.resetCommandPool(copyPool, {}), "Couldn't reset command pool.");
|
|
||||||
|
|
||||||
device.m_Device.destroy(fence, nullptr);
|
|
||||||
imageStaging.Destroy(&device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::Sampler sampler;
|
auto ubo = device.CreateStorageBuffer(sizeof camera, "Camera UBO");
|
||||||
{
|
ubo->Write(0, sizeof camera, &camera);
|
||||||
vk::SamplerCreateInfo samplerCreateInfo = {
|
|
||||||
.magFilter = vk::Filter::eLinear,
|
|
||||||
.minFilter = vk::Filter::eLinear,
|
|
||||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
|
||||||
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.mipLodBias = 0.2f,
|
|
||||||
.anisotropyEnable = true,
|
|
||||||
.maxAnisotropy = 1.0f,
|
|
||||||
.compareEnable = false,
|
|
||||||
.minLod = 0,
|
|
||||||
.maxLod = 4,
|
|
||||||
.unnormalizedCoordinates = false,
|
|
||||||
};
|
|
||||||
AbortIfFailed(device.m_Device.createSampler(&samplerCreateInfo, nullptr, &sampler));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ubo = bufferManager.CreateUniformBuffer(sizeof camera, "Camera UBO").ToPointer();
|
|
||||||
ubo->Write(&device, 0, sizeof camera, &camera);
|
|
||||||
vk::DescriptorBufferInfo descriptorBufferInfo = {
|
|
||||||
.buffer = ubo->m_Buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.range = ubo->GetSize(),
|
|
||||||
};
|
|
||||||
vk::DescriptorImageInfo descriptorImageInfo = {
|
|
||||||
.sampler = sampler,
|
|
||||||
.imageView = crate->m_View,
|
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
};
|
|
||||||
vk::DescriptorBufferInfo descriptorStorageBufferInfo = {
|
|
||||||
.buffer = vbo->m_Buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.range = vbo->GetSize(),
|
|
||||||
};
|
|
||||||
eastl::array writeDescriptors = {
|
|
||||||
vk::WriteDescriptorSet{
|
|
||||||
.dstSet = descriptorSet,
|
|
||||||
.dstBinding = 0,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
||||||
.pBufferInfo = &descriptorBufferInfo,
|
|
||||||
},
|
|
||||||
vk::WriteDescriptorSet{
|
|
||||||
.dstSet = descriptorSet,
|
|
||||||
.dstBinding = 1,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.pImageInfo = &descriptorImageInfo,
|
|
||||||
},
|
|
||||||
vk::WriteDescriptorSet{
|
|
||||||
.dstSet = descriptorSet,
|
|
||||||
.dstBinding = 2,
|
|
||||||
.dstArrayElement = 0,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.pBufferInfo = &descriptorStorageBufferInfo,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
|
|
||||||
|
|
||||||
// Persistent variables
|
// Persistent variables
|
||||||
vk::Viewport viewport = {
|
|
||||||
.x = 0,
|
|
||||||
.y = Cast<f32>(swapchain.m_Extent.height),
|
|
||||||
.width = Cast<f32>(swapchain.m_Extent.width),
|
|
||||||
.height = -Cast<f32>(swapchain.m_Extent.height),
|
|
||||||
.minDepth = 0.0,
|
|
||||||
.maxDepth = 1.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Rect2D scissor = {
|
|
||||||
.offset = {0, 0},
|
|
||||||
.extent = swapchain.m_Extent,
|
|
||||||
};
|
|
||||||
|
|
||||||
auto resizeViewportScissor = [&viewport, &scissor](vk::Extent2D extent) {
|
|
||||||
viewport.y = Cast<f32>(extent.height);
|
|
||||||
viewport.width = Cast<f32>(extent.width);
|
|
||||||
viewport.height = -Cast<f32>(extent.height);
|
|
||||||
scissor.extent = extent;
|
|
||||||
};
|
|
||||||
swapchain.RegisterResizeCallback(resizeViewportScissor);
|
|
||||||
|
|
||||||
vk::ImageSubresourceRange subresourceRange = {
|
vk::ImageSubresourceRange subresourceRange = {
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -437,8 +288,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
.dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite,
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
.oldLayout = vk::ImageLayout::eUndefined,
|
||||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo topOfThePipeDependency = {
|
vk::DependencyInfo topOfThePipeDependency = {
|
||||||
|
|
@ -452,8 +303,8 @@ main(int, char **)
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
.dstAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
||||||
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
.srcQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
.dstQueueFamilyIndex = vk::QueueFamilyIgnored,
|
||||||
.subresourceRange = subresourceRange,
|
.subresourceRange = subresourceRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo renderToPresentDependency = {
|
vk::DependencyInfo renderToPresentDependency = {
|
||||||
|
|
@ -461,50 +312,85 @@ main(int, char **)
|
||||||
.pImageMemoryBarriers = &renderToPresentBarrier,
|
.pImageMemoryBarriers = &renderToPresentBarrier,
|
||||||
};
|
};
|
||||||
|
|
||||||
FrameManager frameManager = {&device, queueAllocation.m_Family, MAX_FRAMES_IN_FLIGHT};
|
eastl::fixed_vector<Ref<ImageView>, MAX_FRAMES_IN_FLIGHT> depthImages;
|
||||||
eastl::fixed_vector<Ref<Image>, MAX_FRAMES_IN_FLIGHT> depthImages;
|
|
||||||
|
|
||||||
auto initDepthImages = [&imageManager, &depthImages, &frameManager] (const vk::Extent2D extent) {
|
auto initDepthImages = [&depthImages, &device](vk::Extent2D const extent) {
|
||||||
for (u32 i = 0; i < frameManager.m_FramesInFlight; ++i)
|
for (u32 i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
|
||||||
{
|
{
|
||||||
depthImages.push_back(
|
depthImages.push_back(device.CreateDepthStencilImageWithView({.m_Extent = extent, .m_Name = "Depth"}));
|
||||||
imageManager.CreateDepthStencilImage({.m_Extent = extent, .m_Name = "Depth"}).ToPointer());
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
initDepthImages(swapchain.m_Extent);
|
initDepthImages(swapchainSize);
|
||||||
|
|
||||||
auto recreateDepthBuffers = [&depthImages, &initDepthImages](const vk::Extent2D extent) {
|
auto recreateDepthBuffers = [&depthImages, &initDepthImages](vk::Extent2D const extent) {
|
||||||
depthImages.clear();
|
depthImages.clear();
|
||||||
initDepthImages(extent);
|
initDepthImages(extent);
|
||||||
};
|
};
|
||||||
swapchain.RegisterResizeCallback(recreateDepthBuffers);
|
|
||||||
|
struct PCB
|
||||||
|
{
|
||||||
|
uptr m_VertexBuffer;
|
||||||
|
uptr m_Camera;
|
||||||
|
systems::ResId<TextureView> m_Texture;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(PCB) == 24);
|
||||||
|
|
||||||
|
auto &commitManager = systems::CommitManager::Instance();
|
||||||
|
|
||||||
|
PCB pcb = {
|
||||||
|
.m_VertexBuffer = vbo->GetDeviceAddress(),
|
||||||
|
.m_Camera = ubo->GetDeviceAddress(),
|
||||||
|
.m_Texture = commitManager.CommitTexture(crate),
|
||||||
|
};
|
||||||
|
|
||||||
Time::Init();
|
Time::Init();
|
||||||
|
|
||||||
|
auto prevSwapchainSize = swapchainSize;
|
||||||
|
|
||||||
INFO("Starting loop");
|
INFO("Starting loop");
|
||||||
while (window.Poll())
|
while (window.Poll())
|
||||||
{
|
{
|
||||||
Time::Update();
|
Time::Update();
|
||||||
|
|
||||||
camera.m_Model *= rotate(mat4{1.0f}, Cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
|
camera.m_Model *= rotate(mat4{1.0f}, static_cast<f32>(45.0_deg * Time::m_Delta), vec3(0.0f, 1.0f, 0.0f));
|
||||||
ubo->Write(&device, 0, sizeof camera, &camera);
|
ubo->Write(0, sizeof camera, &camera);
|
||||||
|
|
||||||
Frame *currentFrame = frameManager.GetNextFrame(&swapchain, &surface, window.GetSize());
|
auto ¤tFrame = device.GetNextFrame();
|
||||||
|
|
||||||
u32 imageIndex = currentFrame->m_ImageIdx;
|
prevSwapchainSize = swapchainSize;
|
||||||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
swapchainSize = currentFrame.m_SwapchainSize;
|
||||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
if (swapchainSize != prevSwapchainSize)
|
||||||
vk::CommandBuffer cmd = currentFrame->m_CommandBuffer;
|
{
|
||||||
vk::ImageView currentDepthImageView = depthImages[currentFrame->m_FrameIdx]->m_View;
|
recreateDepthBuffers(swapchainSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Viewport viewport = {
|
||||||
|
.x = 0,
|
||||||
|
.y = static_cast<f32>(swapchainSize.m_Height),
|
||||||
|
.width = static_cast<f32>(swapchainSize.m_Width),
|
||||||
|
.height = -static_cast<f32>(swapchainSize.m_Height),
|
||||||
|
.minDepth = 0.0,
|
||||||
|
.maxDepth = 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::Rect2D scissor = {
|
||||||
|
.offset = {0, 0},
|
||||||
|
.extent = static_cast<vk::Extent2D>(swapchainSize),
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::ImageView currentImageView = currentFrame.m_SwapchainImageView;
|
||||||
|
vk::Image currentImage = currentFrame.m_SwapchainImage;
|
||||||
|
vk::ImageView currentDepthImageView = depthImages[currentFrame.m_FrameIdx]->m_View;
|
||||||
|
|
||||||
topOfThePipeBarrier.image = currentImage;
|
topOfThePipeBarrier.image = currentImage;
|
||||||
renderToPresentBarrier.image = currentImage;
|
renderToPresentBarrier.image = currentImage;
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
auto context = currentFrame.CreateGraphicsContext();
|
||||||
AbortIfFailed(cmd.begin(&beginInfo));
|
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&topOfThePipeDependency);
|
context.Begin();
|
||||||
|
|
||||||
|
context.Dependency(topOfThePipeDependency);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
eastl::array attachmentInfos = {
|
eastl::array attachmentInfos = {
|
||||||
|
|
@ -528,207 +414,30 @@ main(int, char **)
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::RenderingInfo renderingInfo = {
|
vk::RenderingInfo renderingInfo = {
|
||||||
.renderArea = {.extent = swapchain.m_Extent},
|
.renderArea = scissor,
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
.colorAttachmentCount = Cast<u32>(attachmentInfos.size()),
|
.colorAttachmentCount = static_cast<u32>(attachmentInfos.size()),
|
||||||
.pColorAttachments = attachmentInfos.data(),
|
.pColorAttachments = attachmentInfos.data(),
|
||||||
.pDepthAttachment = &depthAttachment,
|
.pDepthAttachment = &depthAttachment,
|
||||||
};
|
};
|
||||||
|
|
||||||
cmd.beginRendering(&renderingInfo);
|
context.BeginRendering(renderingInfo);
|
||||||
|
|
||||||
cmd.setViewport(0, 1, &viewport);
|
context.SetViewport(viewport);
|
||||||
cmd.setScissor(0, 1, &scissor);
|
context.BindPipeline(pipeline);
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
context.PushConstantBlock(pcb);
|
||||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 0, 1, &descriptorSet, 0, nullptr);
|
context.Draw(vertices.size());
|
||||||
cmd.draw(Cast<u32>(vertices.size()), 1, 0, 0);
|
|
||||||
|
|
||||||
cmd.endRendering();
|
context.EndRendering();
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&renderToPresentDependency);
|
context.Dependency(renderToPresentDependency);
|
||||||
|
|
||||||
AbortIfFailed(cmd.end());
|
context.End();
|
||||||
|
|
||||||
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
device.Present(currentFrame, context);
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_ImageAcquireSem,
|
|
||||||
.pWaitDstStageMask = &waitDstStage,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &cmd,
|
|
||||||
.signalSemaphoreCount = 1,
|
|
||||||
.pSignalSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
};
|
|
||||||
AbortIfFailed(commandQueue.submit(1, &submitInfo, currentFrame->m_FrameAvailableFence));
|
|
||||||
|
|
||||||
currentFrame->Present(commandQueue, &swapchain, &surface, window.GetSize());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
device.WaitIdle();
|
device.WaitIdle();
|
||||||
device.m_Device.destroy(sampler, nullptr);
|
|
||||||
device.m_Device.destroy(descriptorPool, nullptr);
|
|
||||||
device.m_Device.destroy(copyPool, nullptr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline
|
|
||||||
CreatePipeline(const Device *device, const Swapchain *swapchain)
|
|
||||||
{
|
|
||||||
// Pipeline Setup
|
|
||||||
auto vertexShaderModule = CreateShader(device, VERTEX_SHADER_FILE);
|
|
||||||
auto fragmentShaderModule = CreateShader(device, FRAGMENT_SHADER_FILE);
|
|
||||||
|
|
||||||
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
|
||||||
{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
|
||||||
.module = vertexShaderModule,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
.module = fragmentShaderModule,
|
|
||||||
.pName = "main",
|
|
||||||
},
|
|
||||||
}};
|
|
||||||
|
|
||||||
eastl::array descriptorSetLayoutBinding = {
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = 0,
|
|
||||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
|
||||||
},
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eFragment,
|
|
||||||
},
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = 2,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eVertex,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
|
||||||
.bindingCount = Cast<u32>(descriptorSetLayoutBinding.size()),
|
|
||||||
.pBindings = descriptorSetLayoutBinding.data(),
|
|
||||||
};
|
|
||||||
vk::DescriptorSetLayout descriptorSetLayout;
|
|
||||||
AbortIfFailed(
|
|
||||||
device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout));
|
|
||||||
|
|
||||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
|
||||||
.setLayoutCount = 1,
|
|
||||||
.pSetLayouts = &descriptorSetLayout,
|
|
||||||
.pushConstantRangeCount = 0,
|
|
||||||
.pPushConstantRanges = nullptr,
|
|
||||||
};
|
|
||||||
vk::PipelineLayout pipelineLayout;
|
|
||||||
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
|
||||||
device->SetName(pipelineLayout, "Box Layout");
|
|
||||||
|
|
||||||
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {};
|
|
||||||
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
|
|
||||||
.topology = vk::PrimitiveTopology::eTriangleList,
|
|
||||||
.primitiveRestartEnable = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
|
|
||||||
.viewportCount = 1,
|
|
||||||
.scissorCount = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
|
|
||||||
.depthClampEnable = false,
|
|
||||||
.rasterizerDiscardEnable = false,
|
|
||||||
.polygonMode = vk::PolygonMode::eFill,
|
|
||||||
.cullMode = vk::CullModeFlagBits::eNone,
|
|
||||||
.frontFace = vk::FrontFace::eCounterClockwise,
|
|
||||||
.depthBiasEnable = false,
|
|
||||||
.lineWidth = 1.0,
|
|
||||||
};
|
|
||||||
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
|
|
||||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
|
||||||
.sampleShadingEnable = false,
|
|
||||||
};
|
|
||||||
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
|
|
||||||
.depthTestEnable = true,
|
|
||||||
.depthWriteEnable = true,
|
|
||||||
.depthCompareOp = vk::CompareOp::eLess,
|
|
||||||
};
|
|
||||||
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
|
|
||||||
.blendEnable = false,
|
|
||||||
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
|
|
||||||
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
|
|
||||||
.colorBlendOp = vk::BlendOp::eAdd,
|
|
||||||
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
|
|
||||||
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
|
||||||
.alphaBlendOp = vk::BlendOp::eAdd,
|
|
||||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
|
||||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
|
||||||
};
|
|
||||||
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
|
|
||||||
.logicOpEnable = false,
|
|
||||||
.attachmentCount = 1,
|
|
||||||
.pAttachments = &colorBlendAttachmentState,
|
|
||||||
};
|
|
||||||
|
|
||||||
eastl::array dynamicStates = {
|
|
||||||
vk::DynamicState::eScissor,
|
|
||||||
vk::DynamicState::eViewport,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
|
||||||
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
|
|
||||||
.pDynamicStates = dynamicStates.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
|
|
||||||
.viewMask = 0,
|
|
||||||
.colorAttachmentCount = 1,
|
|
||||||
.pColorAttachmentFormats = &swapchain->m_Format,
|
|
||||||
.depthAttachmentFormat = vk::Format::eD24UnormS8Uint,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
|
|
||||||
.pNext = &renderingCreateInfo,
|
|
||||||
.stageCount = Cast<u32>(shaderStages.size()),
|
|
||||||
.pStages = shaderStages.data(),
|
|
||||||
.pVertexInputState = &vertexInputStateCreateInfo,
|
|
||||||
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
|
||||||
.pViewportState = &viewportStateCreateInfo,
|
|
||||||
.pRasterizationState = &rasterizationStateCreateInfo,
|
|
||||||
.pMultisampleState = &multisampleStateCreateInfo,
|
|
||||||
.pDepthStencilState = &depthStencilStateCreateInfo,
|
|
||||||
.pColorBlendState = &colorBlendStateCreateInfo,
|
|
||||||
.pDynamicState = &dynamicStateCreateInfo,
|
|
||||||
.layout = pipelineLayout,
|
|
||||||
};
|
|
||||||
vk::Pipeline pipeline;
|
|
||||||
AbortIfFailed(device->m_Device.createGraphicsPipelines(nullptr, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
|
||||||
device->SetName(pipeline, "Box Pipeline");
|
|
||||||
|
|
||||||
device->m_Device.destroy(vertexShaderModule, nullptr);
|
|
||||||
device->m_Device.destroy(fragmentShaderModule, nullptr);
|
|
||||||
|
|
||||||
return {device, pipelineLayout, pipeline, {descriptorSetLayout}};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::ShaderModule
|
|
||||||
CreateShader(const Device *device, cstr shaderFile)
|
|
||||||
{
|
|
||||||
eastl::vector<u32> shaderCode = ReadFile(shaderFile);
|
|
||||||
|
|
||||||
const vk::ShaderModuleCreateInfo shaderModuleCreateInfo = {
|
|
||||||
.codeSize = shaderCode.size() * sizeof(u32),
|
|
||||||
.pCode = shaderCode.data(),
|
|
||||||
};
|
|
||||||
vk::ShaderModule shaderModule;
|
|
||||||
|
|
||||||
AbortIfFailedMV(device->m_Device.createShaderModule(&shaderModuleCreateInfo, nullptr, &shaderModule),
|
|
||||||
"Shader {} could not be created.", shaderFile);
|
|
||||||
return shaderModule;
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
[vk::binding(0, 0)] __DynamicResource<__DynamicResourceKind.General> gBuffers[];
|
||||||
|
[vk::binding(1, 0)] __DynamicResource<__DynamicResourceKind.Sampler> gSamplers[];
|
||||||
|
[vk::binding(2, 0)] __DynamicResource<__DynamicResourceKind.General> gStorageTextures[];
|
||||||
|
|
||||||
|
export T getDescriptorFromHandle<T>(DescriptorHandle<T> handle) where T : IOpaqueDescriptor
|
||||||
|
{
|
||||||
|
__target_switch
|
||||||
|
{
|
||||||
|
case spirv:
|
||||||
|
switch (T.kind) {
|
||||||
|
case DescriptorKind.Buffer:
|
||||||
|
return gBuffers[((uint2)handle).x].asOpaqueDescriptor<T>();
|
||||||
|
case DescriptorKind.CombinedTextureSampler:
|
||||||
|
return gSamplers[((uint2)handle).x].asOpaqueDescriptor<T>();
|
||||||
|
case DescriptorKind.Texture:
|
||||||
|
return gStorageTextures[((uint2)handle).x].asOpaqueDescriptor<T>();
|
||||||
|
default:
|
||||||
|
return defaultGetDescriptorFromHandle(handle);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return defaultGetDescriptorFromHandle(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
#version 450
|
|
||||||
#pragma shader_stage(fragment)
|
|
||||||
|
|
||||||
layout (location = 0) in vec2 inUV;
|
|
||||||
layout (location = 0) out vec4 outColor;
|
|
||||||
|
|
||||||
layout(binding = 1) uniform sampler2D tex;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
outColor = vec4(texture(tex, inUV).rgb, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
struct FS_Input {
|
|
||||||
float2 UV0 : TEXCOORD0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FS_Output
|
|
||||||
{
|
|
||||||
float4 ColorTarget : SV_Target0;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[vk::binding(1, 0)]] Texture2D<float4> Texture;
|
|
||||||
[[vk::binding(1, 0)]] SamplerState Sampler;
|
|
||||||
|
|
||||||
FS_Output main(FS_Input StageInput) {
|
|
||||||
FS_Output output;
|
|
||||||
|
|
||||||
output.ColorTarget = float4(Texture.Sample(Sampler, StageInput.UV0).rgb, 1.0);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
import bindless;
|
||||||
|
|
||||||
|
struct VertexData
|
||||||
|
{
|
||||||
|
float4 position;
|
||||||
|
float2 texCoord0;
|
||||||
|
float2 _pad0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraData
|
||||||
|
{
|
||||||
|
float4x4 model;
|
||||||
|
float4x4 view;
|
||||||
|
float4x4 projection;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PCB {
|
||||||
|
VertexData* vertexBuffer;
|
||||||
|
CameraData* cameraBuffer;
|
||||||
|
Sampler2D.Handle texture;
|
||||||
|
};
|
||||||
|
|
||||||
|
[vk::push_constant]
|
||||||
|
uniform PCB pcb;
|
||||||
|
|
||||||
|
struct VSIn {
|
||||||
|
uint vertexIndex : SV_VertexID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSOut
|
||||||
|
{
|
||||||
|
float4 position : SV_POSITION;
|
||||||
|
float2 texCoord0 : TEXCOORD0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FSOut {
|
||||||
|
float4 Color;
|
||||||
|
};
|
||||||
|
|
||||||
|
[shader("vertex")]
|
||||||
|
func vsmain(VSIn input) -> VSOut {
|
||||||
|
VSOut output;
|
||||||
|
|
||||||
|
VertexData vd = pcb.vertexBuffer[input.vertexIndex];
|
||||||
|
|
||||||
|
output.position = mul(mul(mul(float4(vd.position.xyz, 1.0f), pcb.cameraBuffer->model), pcb.cameraBuffer->view), pcb.cameraBuffer->projection);
|
||||||
|
output.texCoord0 = vd.texCoord0;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
[shader("fragment")]
|
||||||
|
func fsmain(VSOut input) -> FSOut {
|
||||||
|
FSOut outp;
|
||||||
|
outp.Color = float4(pcb.texture.Sample(input.texCoord0).rgb, 1.0);
|
||||||
|
return outp;
|
||||||
|
}
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
#version 450
|
|
||||||
#pragma shader_stage(vertex)
|
|
||||||
|
|
||||||
layout(location=0) in vec4 position;
|
|
||||||
layout(location=1) in vec2 uv0;
|
|
||||||
|
|
||||||
layout(location=0) out vec2 outUV;
|
|
||||||
|
|
||||||
layout(binding=0) uniform Camera {
|
|
||||||
mat4 model;
|
|
||||||
mat4 view;
|
|
||||||
mat4 proj;
|
|
||||||
} ubo;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
outUV = uv0;
|
|
||||||
gl_Position = ubo.proj * ubo.view * ubo.model * vec4(position.xyz, 1.0f);
|
|
||||||
// outColor = vec3(0.5f, 0.3f, 0.1f);
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
struct VS_Input
|
|
||||||
{
|
|
||||||
uint VertexIndex : SV_VertexID;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VS_Output
|
|
||||||
{
|
|
||||||
float2 UV0 : TEXCOORD0;
|
|
||||||
float4 VertexPosition : SV_Position;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CameraData {
|
|
||||||
float4x4 Model;
|
|
||||||
float4x4 View;
|
|
||||||
float4x4 Projection;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexData {
|
|
||||||
float4 Position;
|
|
||||||
float2 UV0;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[vk::binding(0, 0)]] ConstantBuffer<CameraData> Camera;
|
|
||||||
[[vk::binding(2, 0)]] StructuredBuffer<VertexData> Vertices;
|
|
||||||
|
|
||||||
VS_Output main(VS_Input StageInput) {
|
|
||||||
VS_Output output;
|
|
||||||
|
|
||||||
output.UV0 = Vertices[StageInput.VertexIndex].UV0;
|
|
||||||
|
|
||||||
float4 position = Vertices[StageInput.VertexIndex].Position;
|
|
||||||
|
|
||||||
output.VertexPosition = mul(Camera.Projection, mul(Camera.View, mul(Camera.Model, position)));
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
@ -5,28 +5,25 @@ cmake_minimum_required(VERSION 3.13)
|
||||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fsanitize=address")
|
||||||
find_path(TINYGLTF_INCLUDE_DIRS "tiny_gltf.h")
|
find_path(TINYGLTF_INCLUDE_DIRS "tiny_gltf.h")
|
||||||
|
|
||||||
add_executable(model_render "model_render.cpp"
|
add_executable(model_render
|
||||||
"pipeline_utils.cpp"
|
"model_render.cpp"
|
||||||
"pipeline_utils.h"
|
"asset_loader.cpp"
|
||||||
"asset_loader.cpp"
|
"asset_loader.h"
|
||||||
"asset_loader.h"
|
"light_manager.cpp"
|
||||||
"light_manager.cpp"
|
"light_manager.h"
|
||||||
"light_manager.h"
|
"nodes.cpp"
|
||||||
"gpu_resource_manager.cpp"
|
"nodes.h"
|
||||||
"gpu_resource_manager.h"
|
"ibl_helpers.cpp"
|
||||||
"nodes.cpp"
|
"ibl_helpers.h"
|
||||||
"nodes.h"
|
"tiny_gltf_setup.cpp")
|
||||||
"ibl_helpers.cpp"
|
|
||||||
"ibl_helpers.h")
|
|
||||||
|
|
||||||
add_shader(model_render "shader/model.vs.hlsl")
|
add_shader(model_render "shader/background.slang")
|
||||||
add_shader(model_render "shader/model.ps.hlsl")
|
add_shader(model_render "shader/bindless.slang")
|
||||||
add_shader(model_render "shader/eqrect_to_cube.cs.hlsl")
|
add_shader(model_render "shader/common_structs.slang")
|
||||||
add_shader(model_render "shader/background.vs.hlsl")
|
add_shader(model_render "shader/environment.slang")
|
||||||
add_shader(model_render "shader/background.ps.hlsl")
|
add_shader(model_render "shader/eqrect_to_cube.slang")
|
||||||
add_shader(model_render "shader/diffuse_irradiance.cs.hlsl")
|
add_shader(model_render "shader/ibl_common.slang")
|
||||||
add_shader(model_render "shader/prefilter.cs.hlsl")
|
add_shader(model_render "shader/model.slang")
|
||||||
add_shader(model_render "shader/brdf_lut.cs.hlsl")
|
|
||||||
|
|
||||||
target_link_libraries(model_render PRIVATE aster_core)
|
target_link_libraries(model_render PRIVATE aster_core)
|
||||||
target_link_libraries(model_render PRIVATE util_helper)
|
target_link_libraries(model_render PRIVATE util_helper)
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,26 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: asset_loader.cpp
|
// Aster: asset_loader.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#define TINYGLTF_NOEXCEPTION
|
|
||||||
#define JSON_NOEXCEPTION
|
|
||||||
|
|
||||||
#define TINYGLTF_IMPLEMENTATION
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
#include "aster/core/device.h"
|
#include "aster/core/device.h"
|
||||||
#include "aster/core/image.h"
|
#include "aster/core/image.h"
|
||||||
|
|
||||||
#include "gpu_resource_manager.h"
|
|
||||||
#include "helpers.h"
|
|
||||||
#include "asset_loader.h"
|
#include "asset_loader.h"
|
||||||
|
#include "helpers.h"
|
||||||
|
|
||||||
|
#include "aster/systems/commit_manager.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
#include <EASTL/hash_map.h>
|
#include <EASTL/hash_map.h>
|
||||||
|
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <stb_image.h>
|
||||||
#include <tiny_gltf.h>
|
#include <tiny_gltf.h>
|
||||||
|
|
||||||
#if defined(LoadImage)
|
#if defined(LoadImage)
|
||||||
|
|
@ -31,19 +30,29 @@
|
||||||
constexpr vk::CommandBufferBeginInfo OneTimeCmdBeginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
constexpr vk::CommandBufferBeginInfo OneTimeCmdBeginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||||
|
|
||||||
vec4
|
vec4
|
||||||
VectorToVec4(const std::vector<double> &vec)
|
VectorToVec4(std::vector<double> const &vec)
|
||||||
{
|
{
|
||||||
if (vec.empty())
|
if (vec.empty())
|
||||||
{
|
{
|
||||||
return vec4{0.0f};
|
return vec4{0.0f};
|
||||||
}
|
}
|
||||||
assert(vec.size() == 4);
|
assert(vec.size() == 4);
|
||||||
|
|
||||||
return {vec[0], vec[1], vec[2], vec[3]};
|
return {vec[0], vec[1], vec[2], vec[3]};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vec4
|
||||||
|
VectorToVec4(std::vector<double> const &vec, float w)
|
||||||
|
{
|
||||||
|
if (vec.empty())
|
||||||
|
{
|
||||||
|
return vec4{0.0f};
|
||||||
|
}
|
||||||
|
assert(vec.size() == 3);
|
||||||
|
return {vec[0], vec[1], vec[2], w};
|
||||||
|
}
|
||||||
|
|
||||||
vec3
|
vec3
|
||||||
VectorToVec3(const std::vector<double> &vec)
|
VectorToVec3(std::vector<double> const &vec)
|
||||||
{
|
{
|
||||||
if (vec.empty())
|
if (vec.empty())
|
||||||
{
|
{
|
||||||
|
|
@ -54,51 +63,28 @@ VectorToVec3(const std::vector<double> &vec)
|
||||||
return {vec[0], vec[1], vec[2]};
|
return {vec[0], vec[1], vec[2]};
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Ref<TextureView>
|
||||||
AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
AssetLoader::LoadHdrImage(cstr path, cstr name) const
|
||||||
{
|
{
|
||||||
const Device *pDevice = m_ResourceManager->m_Device;
|
|
||||||
ERROR_IF(texture->IsValid(), "Expected invalid image.") THEN_ABORT(-1);
|
|
||||||
|
|
||||||
i32 x, y, nChannels;
|
i32 x, y, nChannels;
|
||||||
f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4);
|
f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4);
|
||||||
assert(nChannels == 3);
|
assert(nChannels == 3);
|
||||||
|
|
||||||
ERROR_IF(!data, "Could not load {}", path) THEN_ABORT(-1);
|
ERROR_IF(!data, "Could not load {}", path) THEN_ABORT(-1);
|
||||||
|
|
||||||
u32 width = Cast<u32>(x);
|
u32 width = static_cast<u32>(x);
|
||||||
u32 height = Cast<u32>(y);
|
u32 height = static_cast<u32>(y);
|
||||||
|
|
||||||
StagingBuffer stagingBuffer;
|
auto texture = m_Device->CreateTexture2DWithView({
|
||||||
texture->Init(m_ResourceManager->m_Device, {width, height}, vk::Format::eR32G32B32A32Sfloat, false, path);
|
.m_Format = vk::Format::eR32G32B32A32Sfloat,
|
||||||
assert(texture->IsValid());
|
.m_Extent = {width, height},
|
||||||
stagingBuffer.Init(m_ResourceManager->m_Device, (sizeof *data) * x * y * 4, "HDR Staging Buffer");
|
.m_Name = path,
|
||||||
stagingBuffer.Write(m_ResourceManager->m_Device, 0, stagingBuffer.GetSize(), data);
|
.m_IsSampled = true,
|
||||||
|
.m_IsMipMapped = false,
|
||||||
stbi_image_free(data);
|
.m_IsStorage = false,
|
||||||
|
});
|
||||||
|
|
||||||
#pragma region Setup Copy/Sync primitives
|
#pragma region Setup Copy/Sync primitives
|
||||||
vk::BufferImageCopy2 copyRegion = {
|
|
||||||
.bufferOffset = 0,
|
|
||||||
.bufferRowLength = width,
|
|
||||||
.bufferImageHeight = height,
|
|
||||||
.imageSubresource =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.imageOffset = {0, 0, 0},
|
|
||||||
.imageExtent = texture->m_Extent,
|
|
||||||
};
|
|
||||||
vk::CopyBufferToImageInfo2 stagingInfo = {
|
|
||||||
.srcBuffer = stagingBuffer.m_Buffer,
|
|
||||||
.dstImage = texture->m_Image,
|
|
||||||
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
|
|
||||||
.regionCount = 1,
|
|
||||||
.pRegions = ©Region,
|
|
||||||
};
|
|
||||||
vk::ImageMemoryBarrier2 readyToStageBarrier = {
|
vk::ImageMemoryBarrier2 readyToStageBarrier = {
|
||||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
||||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
|
|
@ -108,7 +94,7 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
||||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.image = texture->m_Image,
|
.image = texture->GetImage(),
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -131,9 +117,9 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
.dstAccessMask = vk::AccessFlagBits2::eShaderRead,
|
||||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||||
.srcQueueFamilyIndex = m_TransferQueueIndex,
|
.srcQueueFamilyIndex = m_Device->m_TransferQueueFamily,
|
||||||
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
|
.dstQueueFamilyIndex = m_Device->m_PrimaryQueueFamily,
|
||||||
.image = texture->m_Image,
|
.image = texture->GetImage(),
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -151,49 +137,31 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
||||||
};
|
};
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo));
|
auto context = m_Device->CreateTransferContext();
|
||||||
|
context.Begin();
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
StackString<128> loadActionName = "Load: ";
|
StackString<128> loadActionName = "Load: ";
|
||||||
loadActionName += name ? name : path;
|
loadActionName += name ? name : path;
|
||||||
vk::DebugUtilsLabelEXT debugLabel = {
|
context.BeginDebugRegion(loadActionName.c_str());
|
||||||
.pLabelName = loadActionName.c_str(),
|
|
||||||
.color = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
|
||||||
m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_CommandBuffer.pipelineBarrier2(&readyToStageDependency);
|
context.Dependency(readyToStageDependency);
|
||||||
m_CommandBuffer.copyBufferToImage2(&stagingInfo);
|
context.UploadTexture(texture->m_Image, {reinterpret_cast<u8 *>(data), (sizeof *data) * x * y * 4});
|
||||||
m_CommandBuffer.pipelineBarrier2(&postStagingDependency);
|
context.Dependency(postStagingDependency);
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
context.EndDebugRegion();
|
||||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AbortIfFailed(m_CommandBuffer.end());
|
context.End();
|
||||||
|
|
||||||
vk::SubmitInfo submitInfo = {
|
auto rcpt = m_Device->Submit(context);
|
||||||
.waitSemaphoreCount = 0,
|
stbi_image_free(data);
|
||||||
.pWaitDstStageMask = nullptr,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &m_CommandBuffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Fence fence;
|
m_Device->WaitOn(rcpt);
|
||||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
|
||||||
AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
|
|
||||||
AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence));
|
|
||||||
AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue<u32>));
|
|
||||||
pDevice->m_Device.destroy(fence, nullptr);
|
|
||||||
|
|
||||||
AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {}));
|
return texture;
|
||||||
|
|
||||||
stagingBuffer.Destroy(pDevice);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
|
GenerateMipMaps(systems::TransferContext &context, Ref<Texture> const &texture, vk::ImageLayout initialLayout,
|
||||||
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage)
|
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage)
|
||||||
{
|
{
|
||||||
#if !defined(ASTER_NDEBUG)
|
#if !defined(ASTER_NDEBUG)
|
||||||
|
|
@ -201,7 +169,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
|
||||||
.pLabelName = "Generate Mipmap",
|
.pLabelName = "Generate Mipmap",
|
||||||
.color = std::array{0.9f, 0.9f, 0.9f, 1.0f},
|
.color = std::array{0.9f, 0.9f, 0.9f, 1.0f},
|
||||||
};
|
};
|
||||||
commandBuffer.beginDebugUtilsLabelEXT(&label);
|
context.BeginDebugRegion("Generate MipMap", {0.9, 0.9, 0.9, 1.0});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
vk::ImageMemoryBarrier2 imageStartBarrier = {
|
vk::ImageMemoryBarrier2 imageStartBarrier = {
|
||||||
|
|
@ -243,7 +211,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
|
||||||
}
|
}
|
||||||
|
|
||||||
vk::DependencyInfo imageStartDependency = {
|
vk::DependencyInfo imageStartDependency = {
|
||||||
.imageMemoryBarrierCount = Cast<u32>(startBarriers.size()),
|
.imageMemoryBarrierCount = static_cast<u32>(startBarriers.size()),
|
||||||
.pImageMemoryBarriers = startBarriers.data(),
|
.pImageMemoryBarriers = startBarriers.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -324,10 +292,10 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
|
||||||
|
|
||||||
// Mip Mapping
|
// Mip Mapping
|
||||||
|
|
||||||
commandBuffer.pipelineBarrier2(&imageStartDependency);
|
context.Dependency(imageStartDependency);
|
||||||
|
|
||||||
i32 prevMipWidth = Cast<i32>(texture->m_Extent.width);
|
i32 prevMipWidth = static_cast<i32>(texture->m_Extent.width);
|
||||||
i32 prevMipHeight = Cast<i32>(texture->m_Extent.height);
|
i32 prevMipHeight = static_cast<i32>(texture->m_Extent.height);
|
||||||
|
|
||||||
u32 maxPrevMip = texture->GetMipLevels() - 1;
|
u32 maxPrevMip = texture->GetMipLevels() - 1;
|
||||||
for (u32 prevMipLevel = 0; prevMipLevel < maxPrevMip; ++prevMipLevel)
|
for (u32 prevMipLevel = 0; prevMipLevel < maxPrevMip; ++prevMipLevel)
|
||||||
|
|
@ -349,47 +317,50 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
|
||||||
|
|
||||||
nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel;
|
nextMipBarrier.subresourceRange.baseMipLevel = currentMipLevel;
|
||||||
|
|
||||||
commandBuffer.blitImage2(&mipBlitInfo);
|
context.Blit(mipBlitInfo);
|
||||||
commandBuffer.pipelineBarrier2(&interMipDependency);
|
context.Dependency(interMipDependency);
|
||||||
|
|
||||||
prevMipHeight = currentMipHeight;
|
prevMipHeight = currentMipHeight;
|
||||||
prevMipWidth = currentMipWidth;
|
prevMipWidth = currentMipWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
commandBuffer.pipelineBarrier2(&imageReadyDependency);
|
context.Dependency(imageReadyDependency);
|
||||||
#if !defined(ASTER_NDEBUG)
|
#if !defined(ASTER_NDEBUG)
|
||||||
commandBuffer.endDebugUtilsLabelEXT();
|
context.EndDebugRegion();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle
|
systems::ResId<TextureView>
|
||||||
AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const
|
AssetLoader::LoadImageToGpu(systems::TransferContext &context, tinygltf::Image *image, bool isSrgb, cstr name) const
|
||||||
{
|
{
|
||||||
|
// TODO(Something not loading properly).
|
||||||
|
|
||||||
assert(image->component == 4);
|
assert(image->component == 4);
|
||||||
assert(image->height > 0 && image->width > 0);
|
assert(image->height > 0 && image->width > 0);
|
||||||
|
|
||||||
u32 height = Cast<u32>(image->height);
|
#if !defined(ASTER_NDEBUG)
|
||||||
u32 width = Cast<u32>(image->width);
|
auto assignedName = name ? name : image->name.empty() ? image->uri.c_str() : image->name.c_str();
|
||||||
|
#else
|
||||||
|
auto assignedName = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u32 height = static_cast<u32>(image->height);
|
||||||
|
u32 width = static_cast<u32>(image->width);
|
||||||
|
|
||||||
vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm;
|
vk::Format imageFormat = isSrgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm;
|
||||||
|
|
||||||
Texture texture;
|
auto texture = m_Device->CreateTexture2D<Texture>({
|
||||||
|
.m_Format = imageFormat,
|
||||||
|
.m_Extent = {width, height},
|
||||||
|
.m_Name = assignedName,
|
||||||
|
.m_IsSampled = true,
|
||||||
|
.m_IsMipMapped = true,
|
||||||
|
.m_IsStorage = false,
|
||||||
|
});
|
||||||
|
|
||||||
usize byteSize = image->image.size();
|
|
||||||
texture.Init(m_ResourceManager->m_Device, {.width = width, .height = height}, imageFormat, true,
|
|
||||||
image->name.data());
|
|
||||||
stagingBuffer->Init(m_ResourceManager->m_Device, byteSize);
|
|
||||||
stagingBuffer->Write(m_ResourceManager->m_Device, 0, byteSize, image->image.data());
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
StackString<128> loadActionName = "Load: ";
|
StackString<128> loadActionName = "Load: ";
|
||||||
loadActionName += image->name.empty() ? "<texture>" : image->name.c_str();
|
loadActionName += assignedName;
|
||||||
vk::DebugUtilsLabelEXT debugLabel = {
|
context.BeginDebugRegion(loadActionName.c_str());
|
||||||
.pLabelName = loadActionName.c_str(),
|
|
||||||
.color = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
|
||||||
m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma region Barriers and Blits
|
#pragma region Barriers and Blits
|
||||||
|
|
||||||
|
|
@ -402,7 +373,7 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
||||||
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
.newLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||||
.image = texture.m_Image,
|
.image = texture->m_Image,
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -421,12 +392,14 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
||||||
|
|
||||||
vk::ImageMemoryBarrier2 postStagingBarrier = {
|
vk::ImageMemoryBarrier2 postStagingBarrier = {
|
||||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
.srcStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||||
|
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
|
||||||
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
.dstStageMask = vk::PipelineStageFlagBits2::eAllTransfer,
|
||||||
|
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||||
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
|
||||||
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
|
||||||
.srcQueueFamilyIndex = m_TransferQueueIndex,
|
.srcQueueFamilyIndex = m_Device->m_TransferQueueFamily,
|
||||||
.dstQueueFamilyIndex = m_GraphicsQueueIndex,
|
.dstQueueFamilyIndex = m_Device->m_PrimaryQueueFamily,
|
||||||
.image = texture.m_Image,
|
.image = texture->m_Image,
|
||||||
.subresourceRange =
|
.subresourceRange =
|
||||||
{
|
{
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -436,49 +409,28 @@ AssetLoader::LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image
|
||||||
.layerCount = 1,
|
.layerCount = 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
;
|
|
||||||
|
|
||||||
vk::DependencyInfo postStagingDependency = {
|
vk::DependencyInfo postStagingDependency = {
|
||||||
.imageMemoryBarrierCount = 1,
|
.imageMemoryBarrierCount = 1,
|
||||||
.pImageMemoryBarriers = &postStagingBarrier,
|
.pImageMemoryBarriers = &postStagingBarrier,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::BufferImageCopy2 imageCopy = {
|
|
||||||
.bufferOffset = 0,
|
|
||||||
.bufferRowLength = Cast<u32>(image->width),
|
|
||||||
.bufferImageHeight = Cast<u32>(image->height),
|
|
||||||
.imageSubresource =
|
|
||||||
{
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.mipLevel = 0,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
},
|
|
||||||
.imageOffset = {},
|
|
||||||
.imageExtent = texture.m_Extent,
|
|
||||||
};
|
|
||||||
vk::CopyBufferToImageInfo2 stagingCopyInfo = {
|
|
||||||
.srcBuffer = stagingBuffer->m_Buffer,
|
|
||||||
.dstImage = texture.m_Image,
|
|
||||||
.dstImageLayout = vk::ImageLayout::eTransferDstOptimal,
|
|
||||||
.regionCount = 1,
|
|
||||||
.pRegions = &imageCopy,
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
m_CommandBuffer.pipelineBarrier2(&imageStartDependency);
|
context.Dependency(imageStartDependency);
|
||||||
m_CommandBuffer.copyBufferToImage2(&stagingCopyInfo);
|
context.UploadTexture(texture, {image->image.data(), image->image.size()});
|
||||||
m_CommandBuffer.pipelineBarrier2(&postStagingDependency);
|
context.Dependency(postStagingDependency);
|
||||||
|
|
||||||
GenerateMipMaps(m_CommandBuffer, &texture, vk::ImageLayout::eTransferSrcOptimal,
|
GenerateMipMaps(context, texture, vk::ImageLayout::eTransferSrcOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
|
||||||
vk::ImageLayout::eShaderReadOnlyOptimal);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
#if !defined(ASTER_NDEBUG)
|
||||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
context.EndDebugRegion();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return m_ResourceManager->CommitTexture(&texture);
|
auto textureView = m_Device->CreateView<TextureView>(
|
||||||
|
{.m_Image = texture, .m_Name = image->name.data(), .m_AspectMask = vk::ImageAspectFlagBits::eColor});
|
||||||
|
|
||||||
|
return m_Device->m_CommitManager->CommitTexture(textureView);
|
||||||
}
|
}
|
||||||
|
|
||||||
Model
|
Model
|
||||||
|
|
@ -488,10 +440,8 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
tinygltf::Model model;
|
tinygltf::Model model;
|
||||||
tinygltf::TinyGLTF loader;
|
tinygltf::TinyGLTF loader;
|
||||||
|
|
||||||
const Device *pDevice = m_ResourceManager->m_Device;
|
auto const fsPath = fs::absolute(path);
|
||||||
|
auto const ext = fsPath.extension();
|
||||||
const auto fsPath = fs::absolute(path);
|
|
||||||
const auto ext = fsPath.extension();
|
|
||||||
if (ext == GLTF_ASCII_FILE_EXTENSION)
|
if (ext == GLTF_ASCII_FILE_EXTENSION)
|
||||||
{
|
{
|
||||||
std::string err;
|
std::string err;
|
||||||
|
|
@ -513,42 +463,36 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AbortIfFailed(m_CommandBuffer.begin(&OneTimeCmdBeginInfo));
|
auto context = m_Device->CreateTransferContext();
|
||||||
|
|
||||||
|
context.Begin();
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
StackString<128> loadActionName = "Load: ";
|
StackString<128> loadActionName = "Load: ";
|
||||||
loadActionName += name ? name : path;
|
loadActionName += name ? name : path;
|
||||||
vk::DebugUtilsLabelEXT debugLabel = {
|
context.BeginDebugRegion(loadActionName.c_str());
|
||||||
.pLabelName = loadActionName.c_str(),
|
|
||||||
.color = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
|
||||||
m_CommandBuffer.beginDebugUtilsLabelEXT(&debugLabel);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
eastl::vector<StagingBuffer> stagingBuffers;
|
eastl::hash_map<i32, systems::ResId<TextureView>> textureHandleMap;
|
||||||
|
|
||||||
eastl::hash_map<i32, TextureHandle> textureHandleMap;
|
|
||||||
|
|
||||||
eastl::vector<Material> materials;
|
eastl::vector<Material> materials;
|
||||||
StorageBuffer materialsBuffer;
|
Ref<Buffer> materialsBuffer;
|
||||||
BufferHandle materialsHandle;
|
|
||||||
|
|
||||||
if (!model.materials.empty())
|
if (!model.materials.empty())
|
||||||
{
|
{
|
||||||
auto getTextureHandle = [this, &textureHandleMap, &stagingBuffers, &model](i32 index,
|
// TODO("Something broken on load here.");
|
||||||
bool isSrgb) -> TextureHandle {
|
auto getTextureHandle = [this, &context, &textureHandleMap,
|
||||||
|
&model](i32 index, bool const isSrgb) -> systems::ResId<TextureView> {
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
{
|
{
|
||||||
return {};
|
return systems::NullId{};
|
||||||
}
|
}
|
||||||
const auto iter = textureHandleMap.find(index);
|
if (auto const iter = textureHandleMap.find(index); iter != textureHandleMap.end())
|
||||||
if (iter != textureHandleMap.end())
|
|
||||||
{
|
{
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto *image = &model.images[index];
|
auto const &texture = model.textures[index];
|
||||||
TextureHandle handle = LoadImageToGpu(&stagingBuffers.push_back(), image, isSrgb);
|
auto *image = &model.images[texture.source];
|
||||||
|
auto handle = LoadImageToGpu(context, image, isSrgb, texture.name.empty() ? nullptr : texture.name.c_str());
|
||||||
textureHandleMap.emplace(index, handle);
|
textureHandleMap.emplace(index, handle);
|
||||||
return handle;
|
return handle;
|
||||||
};
|
};
|
||||||
|
|
@ -558,28 +502,22 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
{
|
{
|
||||||
materials.push_back({
|
materials.push_back({
|
||||||
.m_AlbedoFactor = VectorToVec4(material.pbrMetallicRoughness.baseColorFactor),
|
.m_AlbedoFactor = VectorToVec4(material.pbrMetallicRoughness.baseColorFactor),
|
||||||
.m_EmissionFactor = VectorToVec3(material.emissiveFactor),
|
.m_EmissionFactor = VectorToVec4(material.emissiveFactor, 0.0f),
|
||||||
.m_MetalFactor = Cast<f32>(material.pbrMetallicRoughness.metallicFactor),
|
|
||||||
.m_RoughFactor = Cast<f32>(material.pbrMetallicRoughness.roughnessFactor),
|
|
||||||
.m_AlbedoTex = getTextureHandle(material.pbrMetallicRoughness.baseColorTexture.index, true),
|
.m_AlbedoTex = getTextureHandle(material.pbrMetallicRoughness.baseColorTexture.index, true),
|
||||||
.m_NormalTex = getTextureHandle(material.normalTexture.index, false),
|
.m_NormalTex = getTextureHandle(material.normalTexture.index, false),
|
||||||
.m_MetalRoughTex =
|
.m_MetalRoughTex =
|
||||||
getTextureHandle(material.pbrMetallicRoughness.metallicRoughnessTexture.index, false),
|
getTextureHandle(material.pbrMetallicRoughness.metallicRoughnessTexture.index, false),
|
||||||
.m_OcclusionTex = getTextureHandle(material.occlusionTexture.index, false),
|
.m_OcclusionTex = getTextureHandle(material.occlusionTexture.index, false),
|
||||||
.m_EmissionTex = getTextureHandle(material.emissiveTexture.index, true),
|
.m_EmissionTex = getTextureHandle(material.emissiveTexture.index, true),
|
||||||
|
.m_MetalFactor = static_cast<f32>(material.pbrMetallicRoughness.metallicFactor),
|
||||||
|
.m_RoughFactor = static_cast<f32>(material.pbrMetallicRoughness.roughnessFactor),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
usize materialsByteSize = materials.size() * sizeof materials[0];
|
usize materialsByteSize = materials.size() * sizeof materials[0];
|
||||||
materialsBuffer.Init(pDevice, materialsByteSize, false, name);
|
materialsBuffer = m_Device->CreateStorageBuffer(materialsByteSize, name);
|
||||||
materialsHandle = m_ResourceManager->Commit(&materialsBuffer);
|
|
||||||
|
|
||||||
StagingBuffer &materialStaging = stagingBuffers.push_back();
|
context.UploadBuffer(materialsBuffer, materials);
|
||||||
materialStaging.Init(pDevice, materialsByteSize);
|
|
||||||
materialStaging.Write(pDevice, 0, materialsByteSize, materials.data());
|
|
||||||
|
|
||||||
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = materialsByteSize};
|
|
||||||
m_CommandBuffer.copyBuffer(materialStaging.m_Buffer, materialsBuffer.m_Buffer, 1, &bufferCopy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Mesh reordering based on nodes AND OR meshoptimizer
|
// TODO: Mesh reordering based on nodes AND OR meshoptimizer
|
||||||
|
|
@ -618,17 +556,17 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
tinygltf::Buffer *posBuffer = &model.buffers[posBufferView->buffer];
|
tinygltf::Buffer *posBuffer = &model.buffers[posBufferView->buffer];
|
||||||
usize byteOffset = (posAccessor->byteOffset + posBufferView->byteOffset);
|
usize byteOffset = (posAccessor->byteOffset + posBufferView->byteOffset);
|
||||||
|
|
||||||
vertexCount = Cast<u32>(posAccessor->count);
|
vertexCount = static_cast<u32>(posAccessor->count);
|
||||||
vertexPositions.reserve(vertexOffset + vertexCount);
|
vertexPositions.reserve(vertexOffset + vertexCount);
|
||||||
|
|
||||||
if (posAccessor->type == TINYGLTF_TYPE_VEC4)
|
if (posAccessor->type == TINYGLTF_TYPE_VEC4)
|
||||||
{
|
{
|
||||||
vec4 *data = Recast<vec4 *>(posBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec4 *>(posBuffer->data.data() + byteOffset);
|
||||||
vertexPositions.insert(vertexPositions.end(), data, data + vertexCount);
|
vertexPositions.insert(vertexPositions.end(), data, data + vertexCount);
|
||||||
}
|
}
|
||||||
else if (posAccessor->type == TINYGLTF_TYPE_VEC3)
|
else if (posAccessor->type == TINYGLTF_TYPE_VEC3)
|
||||||
{
|
{
|
||||||
vec3 *data = Recast<vec3 *>(posBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec3 *>(posBuffer->data.data() + byteOffset);
|
||||||
for (u32 i = 0; i < vertexCount; ++i)
|
for (u32 i = 0; i < vertexCount; ++i)
|
||||||
{
|
{
|
||||||
vertexPositions.push_back(vec4(data[i], 1.0f));
|
vertexPositions.push_back(vec4(data[i], 1.0f));
|
||||||
|
|
@ -636,7 +574,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
else if (posAccessor->type == TINYGLTF_TYPE_VEC2)
|
else if (posAccessor->type == TINYGLTF_TYPE_VEC2)
|
||||||
{
|
{
|
||||||
vec2 *data = Recast<vec2 *>(posBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec2 *>(posBuffer->data.data() + byteOffset);
|
||||||
for (u32 i = 0; i < vertexCount; ++i)
|
for (u32 i = 0; i < vertexCount; ++i)
|
||||||
{
|
{
|
||||||
vertexPositions.push_back(vec4(data[i], 0.0f, 1.0f));
|
vertexPositions.push_back(vec4(data[i], 0.0f, 1.0f));
|
||||||
|
|
@ -660,7 +598,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
|
|
||||||
if (normAccessor->type == TINYGLTF_TYPE_VEC4)
|
if (normAccessor->type == TINYGLTF_TYPE_VEC4)
|
||||||
{
|
{
|
||||||
vec4 *data = Recast<vec4 *>(normBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec4 *>(normBuffer->data.data() + byteOffset);
|
||||||
|
|
||||||
vec4 *end = data + vertexCount;
|
vec4 *end = data + vertexCount;
|
||||||
u32 idx = vertexOffset;
|
u32 idx = vertexOffset;
|
||||||
|
|
@ -672,7 +610,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
else if (normAccessor->type == TINYGLTF_TYPE_VEC3)
|
else if (normAccessor->type == TINYGLTF_TYPE_VEC3)
|
||||||
{
|
{
|
||||||
vec3 *data = Recast<vec3 *>(normBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec3 *>(normBuffer->data.data() + byteOffset);
|
||||||
for (u32 i = 0; i < vertexCount; ++i)
|
for (u32 i = 0; i < vertexCount; ++i)
|
||||||
{
|
{
|
||||||
auto norm = vec4(data[i], 0.0f);
|
auto norm = vec4(data[i], 0.0f);
|
||||||
|
|
@ -681,7 +619,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
else if (normAccessor->type == TINYGLTF_TYPE_VEC2)
|
else if (normAccessor->type == TINYGLTF_TYPE_VEC2)
|
||||||
{
|
{
|
||||||
vec2 *data = Recast<vec2 *>(normBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec2 *>(normBuffer->data.data() + byteOffset);
|
||||||
for (u32 i = 0; i < vertexCount; ++i)
|
for (u32 i = 0; i < vertexCount; ++i)
|
||||||
{
|
{
|
||||||
auto norm = vec4(data[i], 0.0f, 0.0f);
|
auto norm = vec4(data[i], 0.0f, 0.0f);
|
||||||
|
|
@ -704,7 +642,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
assert(uvAccessor->type == TINYGLTF_TYPE_VEC2 &&
|
assert(uvAccessor->type == TINYGLTF_TYPE_VEC2 &&
|
||||||
uvAccessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
|
uvAccessor->componentType == TINYGLTF_COMPONENT_TYPE_FLOAT);
|
||||||
{
|
{
|
||||||
vec2 *data = Recast<vec2 *>(uvBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec2 *>(uvBuffer->data.data() + byteOffset);
|
||||||
vec2 *end = data + vertexCount;
|
vec2 *end = data + vertexCount;
|
||||||
u32 idx = vertexOffset;
|
u32 idx = vertexOffset;
|
||||||
vec2 *it = data;
|
vec2 *it = data;
|
||||||
|
|
@ -727,7 +665,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
|
|
||||||
if (colorAccessor->type == TINYGLTF_TYPE_VEC4)
|
if (colorAccessor->type == TINYGLTF_TYPE_VEC4)
|
||||||
{
|
{
|
||||||
vec4 *data = Recast<vec4 *>(colorBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec4 *>(colorBuffer->data.data() + byteOffset);
|
||||||
|
|
||||||
vec4 *end = data + vertexCount;
|
vec4 *end = data + vertexCount;
|
||||||
u32 idx = vertexOffset;
|
u32 idx = vertexOffset;
|
||||||
|
|
@ -739,7 +677,7 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
else if (colorAccessor->type == TINYGLTF_TYPE_VEC3)
|
else if (colorAccessor->type == TINYGLTF_TYPE_VEC3)
|
||||||
{
|
{
|
||||||
vec3 *data = Recast<vec3 *>(colorBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<vec3 *>(colorBuffer->data.data() + byteOffset);
|
||||||
for (u32 i = 0; i < vertexCount; ++i)
|
for (u32 i = 0; i < vertexCount; ++i)
|
||||||
{
|
{
|
||||||
auto color = vec4(data[i], 1.0f);
|
auto color = vec4(data[i], 1.0f);
|
||||||
|
|
@ -760,22 +698,22 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
tinygltf::Buffer *indexBuffer = &model.buffers[indexBufferView->buffer];
|
tinygltf::Buffer *indexBuffer = &model.buffers[indexBufferView->buffer];
|
||||||
usize byteOffset = (indexAccessor->byteOffset + indexBufferView->byteOffset);
|
usize byteOffset = (indexAccessor->byteOffset + indexBufferView->byteOffset);
|
||||||
|
|
||||||
indexCount = Cast<u32>(indexAccessor->count);
|
indexCount = static_cast<u32>(indexAccessor->count);
|
||||||
indices.reserve(indexOffset + indexCount);
|
indices.reserve(indexOffset + indexCount);
|
||||||
|
|
||||||
if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
|
if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT)
|
||||||
{
|
{
|
||||||
u32 *data = Recast<u32 *>(indexBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<u32 *>(indexBuffer->data.data() + byteOffset);
|
||||||
indices.insert(indices.end(), data, data + indexCount);
|
indices.insert(indices.end(), data, data + indexCount);
|
||||||
}
|
}
|
||||||
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
|
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT)
|
||||||
{
|
{
|
||||||
u16 *data = Recast<u16 *>(indexBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<u16 *>(indexBuffer->data.data() + byteOffset);
|
||||||
indices.insert(indices.end(), data, data + indexCount);
|
indices.insert(indices.end(), data, data + indexCount);
|
||||||
}
|
}
|
||||||
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
|
else if (indexAccessor->componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)
|
||||||
{
|
{
|
||||||
u8 *data = Recast<u8 *>(indexBuffer->data.data() + byteOffset);
|
auto data = reinterpret_cast<u8 *>(indexBuffer->data.data() + byteOffset);
|
||||||
indices.insert(indices.end(), data, data + indexCount);
|
indices.insert(indices.end(), data, data + indexCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -810,12 +748,12 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
{
|
{
|
||||||
eastl::function<void(i32, i32)> processNode = [&processNode, &model, &nodes, &meshPrimRanges,
|
eastl::function<void(i32, i32)> processNode = [&processNode, &model, &nodes, &meshPrimRanges,
|
||||||
&meshPrimitives](i32 idx, i32 parent) -> void {
|
&meshPrimitives](i32 idx, i32 parent) -> void {
|
||||||
const auto *node = &model.nodes[idx];
|
auto const *node = &model.nodes[idx];
|
||||||
|
|
||||||
vec3 nodeTranslation = vec3{0.0f};
|
auto nodeTranslation = vec3{0.0f};
|
||||||
quat nodeRotation = quat{1.0f, 0.0f, 0.0f, 0.0f};
|
auto nodeRotation = quat{1.0f, 0.0f, 0.0f, 0.0f};
|
||||||
vec3 nodeScale = vec3{1.0f};
|
auto nodeScale = vec3{1.0f};
|
||||||
mat4 nodeMatrix = mat4{1.0f};
|
auto nodeMatrix = mat4{1.0f};
|
||||||
if (node->translation.size() == 3)
|
if (node->translation.size() == 3)
|
||||||
{
|
{
|
||||||
nodeTranslation = glm::make_vec3(node->translation.data());
|
nodeTranslation = glm::make_vec3(node->translation.data());
|
||||||
|
|
@ -833,21 +771,21 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
{
|
{
|
||||||
nodeMatrix = glm::make_mat4(node->matrix.data());
|
nodeMatrix = glm::make_mat4(node->matrix.data());
|
||||||
}
|
}
|
||||||
const mat4 transform = translate(mat4(1.0f), nodeTranslation) * mat4_cast(nodeRotation) *
|
mat4 const transform = translate(mat4(1.0f), nodeTranslation) * mat4_cast(nodeRotation) *
|
||||||
scale(mat4(1.0f), nodeScale) * nodeMatrix;
|
scale(mat4(1.0f), nodeScale) * nodeMatrix;
|
||||||
|
|
||||||
const i32 nodeArrayIndex = Cast<i32>(nodes.Add(transform, parent));
|
i32 const nodeArrayIndex = static_cast<i32>(nodes.Add(transform, parent));
|
||||||
if (node->mesh >= 0)
|
if (node->mesh >= 0)
|
||||||
{
|
{
|
||||||
auto [start, count] = meshPrimRanges[node->mesh];
|
auto [start, count] = meshPrimRanges[node->mesh];
|
||||||
const auto end = start + count;
|
auto const end = start + count;
|
||||||
for (usize i = start; i != end; ++i)
|
for (usize i = start; i != end; ++i)
|
||||||
{
|
{
|
||||||
meshPrimitives[i].m_TransformIdx = nodeArrayIndex;
|
meshPrimitives[i].m_TransformIdx = nodeArrayIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const i32 child : node->children)
|
for (i32 const child : node->children)
|
||||||
{
|
{
|
||||||
processNode(child, nodeArrayIndex);
|
processNode(child, nodeArrayIndex);
|
||||||
}
|
}
|
||||||
|
|
@ -862,76 +800,46 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
|
|
||||||
nodes.Update();
|
nodes.Update();
|
||||||
|
|
||||||
StorageBuffer nodeBuffer;
|
auto nodeBuffer = m_Device->CreateStorageBuffer(nodes.GetGlobalTransformByteSize());
|
||||||
nodeBuffer.Init(pDevice, nodes.GetGlobalTransformByteSize(), true);
|
nodeBuffer->Write(0, nodes.GetGlobalTransformByteSize(), nodes.GetGlobalTransformPtr());
|
||||||
nodeBuffer.Write(pDevice, 0, nodes.GetGlobalTransformByteSize(), nodes.GetGlobalTransformPtr());
|
|
||||||
BufferHandle nodeHandle = m_ResourceManager->Commit(&nodeBuffer);
|
|
||||||
|
|
||||||
#pragma region Staging / Transfer / Uploads
|
#pragma region Staging / Transfer / Uploads
|
||||||
BufferHandle positionBufferHandle;
|
systems::ResId<Buffer> positionBufferHandle = systems::ResId<Buffer>::Null();
|
||||||
BufferHandle vertexDataHandle;
|
systems::ResId<Buffer> vertexDataHandle = systems::ResId<Buffer>::Null();
|
||||||
IndexBuffer indexBuffer;
|
Ref<IndexBuffer> indexBuffer;
|
||||||
|
|
||||||
{
|
auto positionBuffer = m_Device->CreateStorageBuffer(vertexPositions.size() * sizeof vertexPositions[0]);
|
||||||
auto uploadBufferData = [cmd = this->m_CommandBuffer, &stagingBuffers, pDevice](const Buffer *buffer,
|
context.UploadBuffer(positionBuffer, vertexPositions);
|
||||||
const void *data) {
|
|
||||||
vk::BufferCopy bufferCopy = {.srcOffset = 0, .dstOffset = 0, .size = buffer->GetSize()};
|
|
||||||
StagingBuffer &stagingBuffer = stagingBuffers.push_back();
|
|
||||||
stagingBuffer.Init(pDevice, bufferCopy.size);
|
|
||||||
stagingBuffer.Write(pDevice, 0, bufferCopy.size, data);
|
|
||||||
cmd.copyBuffer(stagingBuffer.m_Buffer, buffer->m_Buffer, 1, &bufferCopy);
|
|
||||||
};
|
|
||||||
|
|
||||||
StorageBuffer positionBuffer;
|
auto vertexDataBuffer = m_Device->CreateStorageBuffer(vertexData.size() * sizeof vertexData[0]);
|
||||||
positionBuffer.Init(pDevice, vertexPositions.size() * sizeof vertexPositions[0], false);
|
context.UploadBuffer(vertexDataBuffer, vertexData);
|
||||||
positionBufferHandle = m_ResourceManager->Commit(&positionBuffer);
|
|
||||||
uploadBufferData(&positionBuffer, vertexPositions.data());
|
|
||||||
|
|
||||||
StorageBuffer vertexDataBuffer;
|
// TODO: Index buffer needs to be separated.
|
||||||
vertexDataBuffer.Init(pDevice, vertexData.size() * sizeof vertexData[0], false);
|
indexBuffer = systems::CastBuffer<IndexBuffer>(
|
||||||
vertexDataHandle = m_ResourceManager->Commit(&vertexDataBuffer);
|
m_Device->CreateIndexBuffer(indices.size() * sizeof indices[0], "Index Buffer"));
|
||||||
uploadBufferData(&vertexDataBuffer, vertexData.data());
|
context.UploadBuffer(indexBuffer, indices);
|
||||||
|
|
||||||
indexBuffer.Init(pDevice, indices.size() * sizeof indices[0]);
|
|
||||||
uploadBufferData(&indexBuffer, indices.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
#if !defined(ASTER_NDEBUG)
|
||||||
m_CommandBuffer.endDebugUtilsLabelEXT();
|
context.EndDebugRegion();
|
||||||
#endif
|
#endif
|
||||||
AbortIfFailed(m_CommandBuffer.end());
|
context.End();
|
||||||
|
|
||||||
vk::SubmitInfo submitInfo = {
|
auto rcpt = m_Device->Submit(context);
|
||||||
.waitSemaphoreCount = 0,
|
m_Device->WaitOn(rcpt);
|
||||||
.pWaitDstStageMask = nullptr,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &m_CommandBuffer,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Fence fence;
|
|
||||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
|
||||||
AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
|
|
||||||
AbortIfFailed(m_TransferQueue.submit(1, &submitInfo, fence));
|
|
||||||
AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue<u32>));
|
|
||||||
pDevice->m_Device.destroy(fence, nullptr);
|
|
||||||
|
|
||||||
AbortIfFailed(pDevice->m_Device.resetCommandPool(m_CommandPool, {}));
|
|
||||||
|
|
||||||
for (auto &buffer : stagingBuffers)
|
|
||||||
{
|
|
||||||
buffer.Destroy(pDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
Model::ModelHandles handles = {
|
Model::ModelHandles handles = {
|
||||||
.m_VertexPositionHandle = positionBufferHandle,
|
.m_VertexPositionHandle = positionBuffer,
|
||||||
.m_VertexDataHandle = vertexDataHandle,
|
.m_VertexDataHandle = vertexDataBuffer,
|
||||||
.m_MaterialsHandle = materialsHandle,
|
.m_MaterialsHandle = materialsBuffer,
|
||||||
.m_NodeHandle = nodeHandle,
|
.m_NodeHandle = nodeBuffer,
|
||||||
};
|
};
|
||||||
|
Model::ModelHandlesData handlesData = handles;
|
||||||
|
auto handlesBuffer = m_Device->CreateStorageBuffer(sizeof handlesData, "Materials");
|
||||||
|
handlesBuffer->Write(0, sizeof handlesData, &handlesData);
|
||||||
|
|
||||||
eastl::vector<TextureHandle> textureHandles;
|
eastl::vector<systems::ResId<TextureView>> textureHandles;
|
||||||
textureHandles.reserve(textureHandleMap.size());
|
textureHandles.reserve(textureHandleMap.size());
|
||||||
|
|
||||||
for (auto &[key, val] : textureHandleMap)
|
for (auto &[key, val] : textureHandleMap)
|
||||||
|
|
@ -940,139 +848,45 @@ AssetLoader::LoadModelToGpu(cstr path, cstr name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Model{
|
return Model{
|
||||||
m_ResourceManager, std::move(textureHandles), std::move(nodes), handles, indexBuffer, meshPrimitives,
|
textureHandles, std::move(nodes), nodeBuffer, handles, handlesBuffer, indexBuffer, meshPrimitives,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Model::Model(GpuResourceManager *resourceManager, eastl::vector<TextureHandle> &&textureHandles, Nodes &&nodes,
|
Model::Model(eastl::vector<systems::ResId<TextureView>> &textureHandles, Nodes &&nodes, Ref<Buffer> nodeBuffer,
|
||||||
const ModelHandles &handles, const IndexBuffer &indexBuffer,
|
ModelHandles &handles, Ref<Buffer> modelHandlesBuffer, Ref<IndexBuffer> indexBuffer,
|
||||||
const eastl::vector<MeshPrimitive> &meshPrimitives)
|
eastl::vector<MeshPrimitive> const &meshPrimitives)
|
||||||
: m_ResourceManager(resourceManager)
|
: m_TextureHandles(std::move(textureHandles))
|
||||||
, m_TextureHandles(std::move(textureHandles))
|
|
||||||
, m_Nodes(std::move(nodes))
|
, m_Nodes(std::move(nodes))
|
||||||
, m_Handles(handles)
|
, m_Handles(std::move(handles))
|
||||||
, m_IndexBuffer(indexBuffer)
|
, m_NodeBuffer(std::move(nodeBuffer))
|
||||||
|
, m_IndexBuffer(std::move(indexBuffer))
|
||||||
|
, m_ModelHandlesBuffer(std::move(modelHandlesBuffer))
|
||||||
, m_MeshPrimitives(meshPrimitives)
|
, m_MeshPrimitives(meshPrimitives)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Model::Model(Model &&other) noexcept
|
mat4 const &
|
||||||
: m_ResourceManager(Take(other.m_ResourceManager))
|
|
||||||
, m_TextureHandles(std::move(other.m_TextureHandles))
|
|
||||||
, m_Handles(other.m_Handles)
|
|
||||||
, m_IndexBuffer(other.m_IndexBuffer)
|
|
||||||
, m_MeshPrimitives(std::move(other.m_MeshPrimitives))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Model &
|
|
||||||
Model::operator=(Model &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
m_ResourceManager = Take(other.m_ResourceManager);
|
|
||||||
m_TextureHandles = std::move(other.m_TextureHandles);
|
|
||||||
m_Handles = other.m_Handles;
|
|
||||||
m_IndexBuffer = other.m_IndexBuffer;
|
|
||||||
m_MeshPrimitives = std::move(other.m_MeshPrimitives);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const mat4 &
|
|
||||||
Model::GetModelTransform() const
|
Model::GetModelTransform() const
|
||||||
{
|
{
|
||||||
return m_Nodes[0];
|
return m_Nodes[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Model::SetModelTransform(const mat4 &transform)
|
Model::SetModelTransform(mat4 const &transform)
|
||||||
{
|
{
|
||||||
m_Nodes.Set(0, transform);
|
m_Nodes.Set(0, transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Model::~Model()
|
|
||||||
{
|
|
||||||
if (!m_ResourceManager)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_IndexBuffer.Destroy(m_ResourceManager->m_Device);
|
|
||||||
|
|
||||||
m_ResourceManager->Release(m_Handles.m_VertexDataHandle);
|
|
||||||
m_ResourceManager->Release(m_Handles.m_NodeHandle);
|
|
||||||
m_ResourceManager->Release(m_Handles.m_VertexPositionHandle);
|
|
||||||
m_ResourceManager->Release(m_Handles.m_MaterialsHandle);
|
|
||||||
|
|
||||||
for (const TextureHandle &handle : m_TextureHandles)
|
|
||||||
{
|
|
||||||
m_ResourceManager->Release(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Model::Update()
|
Model::Update()
|
||||||
{
|
{
|
||||||
if (m_Nodes.Update())
|
if (m_Nodes.Update())
|
||||||
{
|
{
|
||||||
m_ResourceManager->Write(m_Handles.m_NodeHandle, 0, m_Nodes.GetGlobalTransformByteSize(),
|
m_NodeBuffer->Write(0, m_Nodes.GetGlobalTransformByteSize(), m_Nodes.GetGlobalTransformPtr());
|
||||||
m_Nodes.GetGlobalTransformPtr());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetLoader::AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex,
|
AssetLoader::AssetLoader(systems::RenderingDevice &device)
|
||||||
u32 graphicsQueueIndex)
|
: m_Device{&device}
|
||||||
: m_ResourceManager(resourceManager)
|
|
||||||
, m_TransferQueue(transferQueue)
|
|
||||||
, m_TransferQueueIndex(transferQueueIndex)
|
|
||||||
, m_GraphicsQueueIndex(graphicsQueueIndex)
|
|
||||||
{
|
{
|
||||||
const Device *pDevice = resourceManager->m_Device;
|
|
||||||
const vk::CommandPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::CommandPoolCreateFlagBits::eTransient,
|
|
||||||
.queueFamilyIndex = transferQueueIndex,
|
|
||||||
};
|
|
||||||
AbortIfFailedM(pDevice->m_Device.createCommandPool(&poolCreateInfo, nullptr, &m_CommandPool),
|
|
||||||
"Transfer command pool creation failed.");
|
|
||||||
|
|
||||||
pDevice->SetName(m_CommandPool, "Asset Loader Command Pool");
|
|
||||||
|
|
||||||
const vk::CommandBufferAllocateInfo commandBufferAllocateInfo = {
|
|
||||||
.commandPool = m_CommandPool,
|
|
||||||
.level = vk::CommandBufferLevel::ePrimary,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
};
|
|
||||||
AbortIfFailed(pDevice->m_Device.allocateCommandBuffers(&commandBufferAllocateInfo, &m_CommandBuffer));
|
|
||||||
|
|
||||||
pDevice->SetName(m_CommandBuffer, "Asset Loader Command Buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetLoader::~AssetLoader()
|
|
||||||
{
|
|
||||||
if (m_ResourceManager)
|
|
||||||
{
|
|
||||||
m_ResourceManager->m_Device->m_Device.destroy(m_CommandPool, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetLoader::AssetLoader(AssetLoader &&other) noexcept
|
|
||||||
: m_ResourceManager(Take(other.m_ResourceManager))
|
|
||||||
, m_CommandPool(other.m_CommandPool)
|
|
||||||
, m_CommandBuffer(other.m_CommandBuffer)
|
|
||||||
, m_TransferQueue(other.m_TransferQueue)
|
|
||||||
, m_TransferQueueIndex(other.m_TransferQueueIndex)
|
|
||||||
, m_GraphicsQueueIndex(other.m_GraphicsQueueIndex)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AssetLoader &
|
|
||||||
AssetLoader::operator=(AssetLoader &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
m_ResourceManager = Take(other.m_ResourceManager);
|
|
||||||
m_CommandPool = other.m_CommandPool;
|
|
||||||
m_CommandBuffer = other.m_CommandBuffer;
|
|
||||||
m_TransferQueue = other.m_TransferQueue;
|
|
||||||
m_TransferQueueIndex = other.m_TransferQueueIndex;
|
|
||||||
m_GraphicsQueueIndex = other.m_GraphicsQueueIndex;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: asset_loader.h
|
// Aster: asset_loader.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -9,8 +9,24 @@
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
|
|
||||||
#include "gpu_resource_manager.h"
|
#include "aster/systems/resource.h"
|
||||||
#include "nodes.h"
|
#include "nodes.h"
|
||||||
|
#include "tiny_gltf.h"
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class TransferContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class RenderingDevice;
|
||||||
|
class ResourceManager;
|
||||||
|
class SamplerManager;
|
||||||
|
class BufferManager;
|
||||||
|
class ImageManager;
|
||||||
|
class CommitManager;
|
||||||
|
} // namespace systems
|
||||||
|
|
||||||
namespace tinygltf
|
namespace tinygltf
|
||||||
{
|
{
|
||||||
|
|
@ -18,7 +34,6 @@ struct Image;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Image;
|
struct Image;
|
||||||
struct TextureHandle;
|
|
||||||
struct Texture;
|
struct Texture;
|
||||||
|
|
||||||
constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf";
|
constexpr auto GLTF_ASCII_FILE_EXTENSION = ".gltf";
|
||||||
|
|
@ -35,15 +50,15 @@ struct MeshPrimitive
|
||||||
|
|
||||||
struct Material
|
struct Material
|
||||||
{
|
{
|
||||||
vec4 m_AlbedoFactor; // 16 16
|
vec4 m_AlbedoFactor; // 16 16
|
||||||
vec3 m_EmissionFactor; // 12 28
|
vec4 m_EmissionFactor; // 16 32
|
||||||
f32 m_MetalFactor; // 04 32
|
systems::ResId<TextureView> m_AlbedoTex; // 08 40
|
||||||
f32 m_RoughFactor; // 04 36
|
systems::ResId<TextureView> m_NormalTex; // 08 48
|
||||||
TextureHandle m_AlbedoTex; // 04 40
|
systems::ResId<TextureView> m_MetalRoughTex; // 08 56
|
||||||
TextureHandle m_NormalTex; // 04 44
|
systems::ResId<TextureView> m_OcclusionTex; // 08 64
|
||||||
TextureHandle m_MetalRoughTex; // 04 48
|
systems::ResId<TextureView> m_EmissionTex; // 08 72
|
||||||
TextureHandle m_OcclusionTex; // 04 52
|
f32 m_MetalFactor; // 04 76
|
||||||
TextureHandle m_EmissionTex; // 04 56
|
f32 m_RoughFactor; // 04 80
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VertexData
|
struct VertexData
|
||||||
|
|
@ -56,49 +71,61 @@ struct VertexData
|
||||||
|
|
||||||
struct Model
|
struct Model
|
||||||
{
|
{
|
||||||
GpuResourceManager *m_ResourceManager;
|
eastl::vector<systems::ResId<TextureView>> m_TextureHandles;
|
||||||
|
|
||||||
eastl::vector<TextureHandle> m_TextureHandles;
|
|
||||||
Nodes m_Nodes;
|
Nodes m_Nodes;
|
||||||
|
|
||||||
|
struct ModelHandlesData
|
||||||
|
{
|
||||||
|
uptr m_VertexPositionHandle;
|
||||||
|
uptr m_VertexDataHandle;
|
||||||
|
uptr m_MaterialsHandle;
|
||||||
|
uptr m_NodeHandle;
|
||||||
|
};
|
||||||
|
|
||||||
struct ModelHandles
|
struct ModelHandles
|
||||||
{
|
{
|
||||||
BufferHandle m_VertexPositionHandle;
|
Ref<Buffer> m_VertexPositionHandle;
|
||||||
BufferHandle m_VertexDataHandle;
|
Ref<Buffer> m_VertexDataHandle;
|
||||||
BufferHandle m_MaterialsHandle;
|
Ref<Buffer> m_MaterialsHandle;
|
||||||
BufferHandle m_NodeHandle;
|
Ref<Buffer> m_NodeHandle;
|
||||||
|
|
||||||
|
operator ModelHandlesData() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.m_VertexPositionHandle = m_VertexPositionHandle->GetDeviceAddress(),
|
||||||
|
.m_VertexDataHandle = m_VertexDataHandle->GetDeviceAddress(),
|
||||||
|
.m_MaterialsHandle = m_MaterialsHandle->GetDeviceAddress(),
|
||||||
|
.m_NodeHandle = m_NodeHandle->GetDeviceAddress(),
|
||||||
|
};
|
||||||
|
}
|
||||||
} m_Handles;
|
} m_Handles;
|
||||||
|
|
||||||
IndexBuffer m_IndexBuffer;
|
Ref<Buffer> m_NodeBuffer;
|
||||||
|
Ref<IndexBuffer> m_IndexBuffer;
|
||||||
|
Ref<Buffer> m_ModelHandlesBuffer;
|
||||||
eastl::vector<MeshPrimitive> m_MeshPrimitives;
|
eastl::vector<MeshPrimitive> m_MeshPrimitives;
|
||||||
|
|
||||||
[[nodiscard]] const mat4 &GetModelTransform() const;
|
[[nodiscard]] mat4 const &GetModelTransform() const;
|
||||||
void SetModelTransform(const mat4 &transform);
|
void SetModelTransform(mat4 const &transform);
|
||||||
void Update();
|
void Update();
|
||||||
|
|
||||||
Model(GpuResourceManager *resourceManager, eastl::vector<TextureHandle> &&textureHandles, Nodes &&nodes,
|
Model(eastl::vector<systems::ResId<TextureView>> &textureHandles, Nodes &&nodes, Ref<Buffer> nodeBuffer,
|
||||||
const ModelHandles &handles, const IndexBuffer &indexBuffer,
|
ModelHandles &handles, Ref<Buffer> modelHandlesBuffer, Ref<IndexBuffer> indexBuffer,
|
||||||
const eastl::vector<MeshPrimitive> &meshPrimitives);
|
eastl::vector<MeshPrimitive> const &meshPrimitives);
|
||||||
~Model();
|
~Model() = default;
|
||||||
|
|
||||||
Model(Model &&other) noexcept;
|
Model(Model &&other) noexcept = default;
|
||||||
Model &operator=(Model &&other) noexcept;
|
Model &operator=(Model &&other) noexcept = default;
|
||||||
|
|
||||||
Model(const Model &) = delete;
|
Model(Model const &) = delete;
|
||||||
const Model &operator=(const Model &) = delete;
|
Model const &operator=(Model const &) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AssetLoader
|
struct AssetLoader
|
||||||
{
|
{
|
||||||
GpuResourceManager *m_ResourceManager;
|
systems::RenderingDevice *m_Device;
|
||||||
vk::CommandPool m_CommandPool;
|
|
||||||
vk::CommandBuffer m_CommandBuffer;
|
|
||||||
vk::Queue m_TransferQueue;
|
|
||||||
u32 m_TransferQueueIndex;
|
|
||||||
u32 m_GraphicsQueueIndex;
|
|
||||||
|
|
||||||
void LoadHdrImage(Texture *texture, cstr path, cstr name = nullptr) const;
|
Ref<TextureView> LoadHdrImage(cstr path, cstr name = nullptr) const;
|
||||||
TextureHandle LoadImageToGpu(StagingBuffer *stagingBuffer, tinygltf::Image *image, bool isSrgb) const;
|
|
||||||
Model LoadModelToGpu(cstr path, cstr name = nullptr);
|
Model LoadModelToGpu(cstr path, cstr name = nullptr);
|
||||||
|
|
||||||
constexpr static auto ANormal = "NORMAL";
|
constexpr static auto ANormal = "NORMAL";
|
||||||
|
|
@ -110,17 +137,32 @@ struct AssetLoader
|
||||||
constexpr static auto AJoints0 = "JOINTS_0";
|
constexpr static auto AJoints0 = "JOINTS_0";
|
||||||
constexpr static auto AWeights0 = "WEIGHTS_0";
|
constexpr static auto AWeights0 = "WEIGHTS_0";
|
||||||
|
|
||||||
AssetLoader(GpuResourceManager *resourceManager, vk::Queue transferQueue, u32 transferQueueIndex,
|
explicit AssetLoader(systems::RenderingDevice &device);
|
||||||
u32 graphicsQueueIndex);
|
|
||||||
~AssetLoader();
|
|
||||||
|
|
||||||
AssetLoader(AssetLoader &&other) noexcept;
|
private:
|
||||||
AssetLoader &operator=(AssetLoader &&other) noexcept;
|
systems::ResId<TextureView>
|
||||||
|
LoadImageToGpu(systems::TransferContext &context, tinygltf::Image *image, bool isSrgb, cstr name = nullptr) const;
|
||||||
DISALLOW_COPY_AND_ASSIGN(AssetLoader);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
|
void
|
||||||
vk::ImageLayout finalLayout,
|
GenerateMipMaps(systems::TransferContext &context, Ref<Texture> const &textureView, vk::ImageLayout initialLayout,
|
||||||
vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands,
|
vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage);
|
||||||
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands);
|
|
||||||
|
void
|
||||||
|
GenerateMipMaps(systems::TransferContext &context, concepts::ImageRefTo<Texture> auto &texture,
|
||||||
|
vk::ImageLayout initialLayout, vk::ImageLayout finalLayout,
|
||||||
|
vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands,
|
||||||
|
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands)
|
||||||
|
{
|
||||||
|
GenerateMipMaps(context, systems::CastImage<Texture>(texture), initialLayout, finalLayout, prevStage, finalStage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GenerateMipMaps(systems::TransferContext &context, concepts::ViewRefTo<Texture> auto &texture,
|
||||||
|
vk::ImageLayout initialLayout, vk::ImageLayout finalLayout,
|
||||||
|
vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands,
|
||||||
|
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands)
|
||||||
|
{
|
||||||
|
GenerateMipMaps(context, systems::CastImage<Texture>(texture->m_Image), initialLayout, finalLayout, prevStage,
|
||||||
|
finalStage);
|
||||||
|
}
|
||||||
|
|
@ -1,688 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: gpu_resource_manager.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "gpu_resource_manager.h"
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
|
||||||
#include "aster/core/device.h"
|
|
||||||
#include "aster/core/image.h"
|
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
|
||||||
|
|
||||||
void
|
|
||||||
TextureManager::Init(const u32 maxCapacity)
|
|
||||||
{
|
|
||||||
m_MaxCapacity = maxCapacity;
|
|
||||||
m_FreeHead = GpuResourceHandle::INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureHandle
|
|
||||||
TextureManager::Commit(Texture *texture)
|
|
||||||
{
|
|
||||||
ERROR_IF(!texture || !texture->IsValid(), "Texture must be valid for committal")
|
|
||||||
THEN_ABORT(-1);
|
|
||||||
|
|
||||||
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
const u32 index = m_FreeHead;
|
|
||||||
|
|
||||||
Texture *allocatedTexture = &m_Textures[index];
|
|
||||||
|
|
||||||
assert(!allocatedTexture->IsValid());
|
|
||||||
m_FreeHead = *Recast<u32 *>(allocatedTexture);
|
|
||||||
|
|
||||||
// Ensure it is copyable.
|
|
||||||
static_assert(std::is_trivially_copyable_v<Texture>);
|
|
||||||
*allocatedTexture = *texture;
|
|
||||||
|
|
||||||
// Take ownership of the texture.
|
|
||||||
texture->m_Flags_ &= ~Texture::OWNED_BIT;
|
|
||||||
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 index = Cast<u32>(m_Textures.size());
|
|
||||||
if (index < m_MaxCapacity)
|
|
||||||
{
|
|
||||||
Texture *allocatedTexture = &m_Textures.push_back();
|
|
||||||
|
|
||||||
// Ensure it is copyable.
|
|
||||||
static_assert(std::is_trivially_copyable_v<Texture>);
|
|
||||||
*allocatedTexture = *texture;
|
|
||||||
|
|
||||||
texture->m_Flags_ &= ~Texture::OWNED_BIT;
|
|
||||||
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
|
|
||||||
ERROR("Out of Buffers") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Texture *
|
|
||||||
TextureManager::Fetch(const TextureHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
return &m_Textures[handle.m_Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TextureManager::Release(const Device *device, const TextureHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
Texture *allocatedTexture = &m_Textures[handle.m_Index];
|
|
||||||
allocatedTexture->Destroy(device);
|
|
||||||
|
|
||||||
assert(!allocatedTexture->IsValid());
|
|
||||||
*Recast<u32 *>(allocatedTexture) = m_FreeHead;
|
|
||||||
|
|
||||||
m_FreeHead = handle.m_Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
TextureManager::Destroy(const Device *device)
|
|
||||||
{
|
|
||||||
for (auto &texture : m_Textures)
|
|
||||||
{
|
|
||||||
texture.Destroy(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
BufferManager::Init(const u32 maxCapacity)
|
|
||||||
{
|
|
||||||
m_MaxCapacity = maxCapacity;
|
|
||||||
m_FreeHead = GpuResourceHandle::INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferHandle
|
|
||||||
BufferManager::Commit(StorageBuffer *buffer)
|
|
||||||
{
|
|
||||||
ERROR_IF(!buffer || !buffer->IsValid() || !buffer->IsOwned(), "Buffer must be valid and owned for commital")
|
|
||||||
THEN_ABORT(-1);
|
|
||||||
|
|
||||||
if (m_FreeHead != GpuResourceHandle::INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
const u32 index = m_FreeHead;
|
|
||||||
|
|
||||||
StorageBuffer *allocatedBuffer = &m_Buffers[index];
|
|
||||||
|
|
||||||
assert(!allocatedBuffer->IsValid());
|
|
||||||
m_FreeHead = *Recast<u32 *>(allocatedBuffer);
|
|
||||||
|
|
||||||
// Ensure it is copyable.
|
|
||||||
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
|
|
||||||
*allocatedBuffer = *buffer;
|
|
||||||
|
|
||||||
// Take ownership of the buffer.
|
|
||||||
buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT;
|
|
||||||
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 index = Cast<u32>(m_Buffers.size());
|
|
||||||
if (index < m_MaxCapacity)
|
|
||||||
{
|
|
||||||
StorageBuffer *allocatedBuffer = &m_Buffers.push_back();
|
|
||||||
|
|
||||||
// Ensure it is copyable.
|
|
||||||
static_assert(std::is_trivially_copyable_v<StorageBuffer>);
|
|
||||||
*allocatedBuffer = *buffer;
|
|
||||||
|
|
||||||
buffer->m_Size_ &= ~StorageBuffer::OWNED_BIT;
|
|
||||||
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
|
|
||||||
ERROR("Out of Buffers") THEN_ABORT(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageBuffer *
|
|
||||||
BufferManager::Fetch(const BufferHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
return &m_Buffers[handle.m_Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
BufferManager::Release(const Device *device, const BufferHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
StorageBuffer *allocatedBuffer = &m_Buffers[handle.m_Index];
|
|
||||||
allocatedBuffer->Destroy(device);
|
|
||||||
|
|
||||||
assert(!allocatedBuffer->IsValid());
|
|
||||||
*Recast<u32 *>(allocatedBuffer) = m_FreeHead;
|
|
||||||
|
|
||||||
m_FreeHead = handle.m_Index;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
BufferManager::Destroy(const Device *device)
|
|
||||||
{
|
|
||||||
for (auto &buffer : m_Buffers)
|
|
||||||
{
|
|
||||||
buffer.Destroy(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageTextureHandle
|
|
||||||
StorageTextureManager::Commit(StorageTexture *texture)
|
|
||||||
{
|
|
||||||
const TextureHandle tx = TextureManager::Commit(texture);
|
|
||||||
return {tx.m_Index};
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageTexture *
|
|
||||||
StorageTextureManager::Fetch(const StorageTextureHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
return Recast<StorageTexture *>(&m_Textures[handle.m_Index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
StorageTextureManager::Release(const Device *device, const StorageTextureHandle handle)
|
|
||||||
{
|
|
||||||
TextureManager::Release(device, {handle.m_Index});
|
|
||||||
}
|
|
||||||
|
|
||||||
usize
|
|
||||||
HashSamplerCreateInfo(const vk::SamplerCreateInfo *createInfo)
|
|
||||||
{
|
|
||||||
usize hash = HashAny(createInfo->flags);
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->magFilter));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->minFilter));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->mipmapMode));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->addressModeU));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->addressModeV));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->addressModeW));
|
|
||||||
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo->mipLodBias * 1000))); // Resolution of 10^-3
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->anisotropyEnable));
|
|
||||||
hash = HashCombine(hash,
|
|
||||||
HashAny(Cast<usize>(createInfo->maxAnisotropy * 0x10))); // 16:1 Anisotropy is enough resolution
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->compareEnable));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->compareOp));
|
|
||||||
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo->minLod * 1000))); // 0.001 resolution is enough.
|
|
||||||
hash = HashCombine(hash,
|
|
||||||
HashAny(Cast<usize>(createInfo->maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->borderColor));
|
|
||||||
hash = HashCombine(hash, HashAny(createInfo->unnormalizedCoordinates));
|
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SamplerManager::Init(usize size)
|
|
||||||
{
|
|
||||||
m_Samplers.reserve(size);
|
|
||||||
m_SamplerHashes.reserve(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
SamplerHandle
|
|
||||||
SamplerManager::Create(const Device *device, const vk::SamplerCreateInfo *createInfo)
|
|
||||||
{
|
|
||||||
const usize hash = HashSamplerCreateInfo(createInfo);
|
|
||||||
|
|
||||||
for (u32 index = 0; usize samplerHash : m_SamplerHashes)
|
|
||||||
{
|
|
||||||
if (samplerHash == hash)
|
|
||||||
{
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Sampler sampler;
|
|
||||||
AbortIfFailed(device->m_Device.createSampler(createInfo, nullptr, &sampler));
|
|
||||||
const u32 index = Cast<u32>(m_SamplerHashes.size());
|
|
||||||
m_SamplerHashes.push_back(hash);
|
|
||||||
m_Samplers.push_back(sampler);
|
|
||||||
return {index};
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Sampler
|
|
||||||
SamplerManager::Fetch(const SamplerHandle handle)
|
|
||||||
{
|
|
||||||
assert(!handle.IsInvalid());
|
|
||||||
|
|
||||||
return m_Samplers[handle.m_Index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SamplerManager::Destroy(const Device *device)
|
|
||||||
{
|
|
||||||
for (const auto &sampler : m_Samplers)
|
|
||||||
{
|
|
||||||
device->m_Device.destroy(sampler, nullptr);
|
|
||||||
}
|
|
||||||
m_Samplers.clear();
|
|
||||||
m_SamplerHashes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info)
|
|
||||||
: uBufferInfo(info)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorImageInfo info)
|
|
||||||
: uImageInfo(info)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager::WriteInfo::WriteInfo(vk::BufferView info)
|
|
||||||
: uBufferView(info)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferHandle
|
|
||||||
GpuResourceManager::Commit(StorageBuffer *storageBuffer)
|
|
||||||
{
|
|
||||||
const BufferHandle handle = m_BufferManager.Commit(storageBuffer);
|
|
||||||
|
|
||||||
m_WriteInfos.emplace_back(vk::DescriptorBufferInfo{
|
|
||||||
.buffer = storageBuffer->m_Buffer,
|
|
||||||
.offset = 0,
|
|
||||||
.range = storageBuffer->GetSize(),
|
|
||||||
});
|
|
||||||
|
|
||||||
m_Writes.push_back({
|
|
||||||
.dstSet = m_DescriptorSet,
|
|
||||||
.dstBinding = BUFFER_BINDING_INDEX,
|
|
||||||
.dstArrayElement = handle.m_Index,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.pBufferInfo = &m_WriteInfos.back().uBufferInfo,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_WriteOwner.emplace_back(HandleType::eBuffer, handle.m_Index);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
++m_CommitedBufferCount;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Write(const BufferHandle handle, const usize offset, const usize size, const void *data)
|
|
||||||
{
|
|
||||||
m_BufferManager.Fetch(handle)->Write(m_Device, offset, size, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::EraseWrites(u32 handleIndex, HandleType handleType)
|
|
||||||
{
|
|
||||||
auto writeIter = m_Writes.begin();
|
|
||||||
auto ownerIter = m_WriteOwner.begin();
|
|
||||||
const auto ownerEnd = m_WriteOwner.end();
|
|
||||||
|
|
||||||
while (ownerIter != ownerEnd)
|
|
||||||
{
|
|
||||||
if (ownerIter->first == handleType && ownerIter->second == handleIndex)
|
|
||||||
{
|
|
||||||
*writeIter = m_Writes.back();
|
|
||||||
*ownerIter = m_WriteOwner.back();
|
|
||||||
m_Writes.pop_back();
|
|
||||||
m_WriteOwner.pop_back();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++ownerIter;
|
|
||||||
++writeIter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(BufferHandle handle)
|
|
||||||
{
|
|
||||||
if (handle.IsInvalid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
EraseWrites(handle.m_Index, HandleType::eBuffer);
|
|
||||||
|
|
||||||
m_BufferManager.Release(m_Device, handle);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
--m_CommitedBufferCount;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(StorageBuffer *storageBuffer, const BufferHandle handle)
|
|
||||||
{
|
|
||||||
assert(storageBuffer);
|
|
||||||
assert(!storageBuffer->IsValid());
|
|
||||||
|
|
||||||
StorageBuffer *internal = m_BufferManager.Fetch(handle);
|
|
||||||
*storageBuffer = *internal;
|
|
||||||
internal->m_Size_ &= ~StorageBuffer::OWNED_BIT;
|
|
||||||
|
|
||||||
Release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(TextureHandle handle)
|
|
||||||
{
|
|
||||||
if (handle.IsInvalid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
EraseWrites(handle.m_Index, HandleType::eTexture);
|
|
||||||
|
|
||||||
m_TextureManager.Release(m_Device, handle);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
--m_CommitedTextureCount;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(Texture *texture, TextureHandle handle)
|
|
||||||
{
|
|
||||||
assert(texture);
|
|
||||||
assert(!texture->IsValid());
|
|
||||||
|
|
||||||
Texture *internal = m_TextureManager.Fetch(handle);
|
|
||||||
*texture = *internal;
|
|
||||||
internal->m_Flags_ &= ~Texture::OWNED_BIT;
|
|
||||||
|
|
||||||
Release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureHandle
|
|
||||||
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 = samplerImpl,
|
|
||||||
.imageView = texture->m_View,
|
|
||||||
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_Writes.push_back({
|
|
||||||
.dstSet = m_DescriptorSet,
|
|
||||||
.dstBinding = TEXTURE_BINDING_INDEX,
|
|
||||||
.dstArrayElement = handle.m_Index,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.pImageInfo = &m_WriteInfos.back().uImageInfo,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_WriteOwner.emplace_back(HandleType::eTexture, handle.m_Index);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
++m_CommitedTextureCount;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return {handle};
|
|
||||||
}
|
|
||||||
|
|
||||||
StorageTextureHandle
|
|
||||||
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 = samplerImpl,
|
|
||||||
.imageView = storageTexture->m_View,
|
|
||||||
.imageLayout = vk::ImageLayout::eGeneral,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_Writes.push_back({
|
|
||||||
.dstSet = m_DescriptorSet,
|
|
||||||
.dstBinding = STORAGE_TEXTURE_BINDING_INDEX,
|
|
||||||
.dstArrayElement = handle.m_Index,
|
|
||||||
.descriptorCount = 1,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageImage,
|
|
||||||
.pImageInfo = &m_WriteInfos.back().uImageInfo,
|
|
||||||
});
|
|
||||||
|
|
||||||
m_WriteOwner.emplace_back(HandleType::eStorageTexture, handle.m_Index);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
++m_CommitedStorageTextureCount;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return {handle};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(StorageTextureHandle handle)
|
|
||||||
{
|
|
||||||
if (handle.IsInvalid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
EraseWrites(handle.m_Index, HandleType::eTexture);
|
|
||||||
|
|
||||||
m_StorageTextureManager.Release(m_Device, handle);
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
--m_CommitedStorageTextureCount;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Release(StorageTexture *texture, const StorageTextureHandle handle)
|
|
||||||
{
|
|
||||||
assert(texture);
|
|
||||||
assert(!texture->IsValid());
|
|
||||||
|
|
||||||
StorageTexture *internal = m_StorageTextureManager.Fetch(handle);
|
|
||||||
*texture = *internal;
|
|
||||||
internal->m_Flags_ &= ~StorageTexture::OWNED_BIT;
|
|
||||||
|
|
||||||
Release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GpuResourceManager::Update()
|
|
||||||
{
|
|
||||||
if (m_Writes.empty() || m_WriteInfos.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_Device->m_Device.updateDescriptorSets(Cast<u32>(m_Writes.size()), m_Writes.data(), 0, nullptr);
|
|
||||||
|
|
||||||
m_Writes.clear();
|
|
||||||
m_WriteInfos.clear();
|
|
||||||
m_WriteOwner.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
|
|
||||||
: m_Device(device)
|
|
||||||
{
|
|
||||||
vk::PhysicalDeviceProperties properties;
|
|
||||||
m_Device->m_PhysicalDevice.getProperties(&properties);
|
|
||||||
|
|
||||||
u32 buffersCount = eastl::min(properties.limits.maxPerStageDescriptorStorageBuffers - 1024, Cast<u32>(maxSize));
|
|
||||||
u32 texturesCount = eastl::min(properties.limits.maxPerStageDescriptorSampledImages - 1024, Cast<u32>(maxSize));
|
|
||||||
u32 storageTexturesCount =
|
|
||||||
eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize));
|
|
||||||
|
|
||||||
INFO("Max Buffer Count: {}", buffersCount);
|
|
||||||
INFO("Max Texture Count: {}", texturesCount);
|
|
||||||
INFO("Max Storage Texture Count: {}", storageTexturesCount);
|
|
||||||
|
|
||||||
m_BufferManager.Init(buffersCount);
|
|
||||||
m_TextureManager.Init(texturesCount);
|
|
||||||
m_StorageTextureManager.Init(storageTexturesCount);
|
|
||||||
m_SamplerManager.Init(storageTexturesCount);
|
|
||||||
|
|
||||||
m_DefaultSamplerCreateInfo = {
|
|
||||||
.magFilter = vk::Filter::eLinear,
|
|
||||||
.minFilter = vk::Filter::eLinear,
|
|
||||||
.mipmapMode = vk::SamplerMipmapMode::eLinear,
|
|
||||||
.addressModeU = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.addressModeV = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.addressModeW = vk::SamplerAddressMode::eRepeat,
|
|
||||||
.mipLodBias = 0.0f,
|
|
||||||
.anisotropyEnable = true,
|
|
||||||
.maxAnisotropy = properties.limits.maxSamplerAnisotropy,
|
|
||||||
.compareEnable = false,
|
|
||||||
.minLod = 0,
|
|
||||||
.maxLod = VK_LOD_CLAMP_NONE,
|
|
||||||
.borderColor = vk::BorderColor::eFloatOpaqueBlack,
|
|
||||||
.unnormalizedCoordinates = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo));
|
|
||||||
|
|
||||||
eastl::array poolSizes = {
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = buffersCount,
|
|
||||||
},
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = texturesCount,
|
|
||||||
},
|
|
||||||
vk::DescriptorPoolSize{
|
|
||||||
.type = vk::DescriptorType::eStorageImage,
|
|
||||||
.descriptorCount = storageTexturesCount,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::DescriptorPoolCreateInfo poolCreateInfo = {
|
|
||||||
.flags = vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind,
|
|
||||||
.maxSets = 1,
|
|
||||||
.poolSizeCount = Cast<u32>(poolSizes.size()),
|
|
||||||
.pPoolSizes = poolSizes.data(),
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.createDescriptorPool(&poolCreateInfo, nullptr, &m_DescriptorPool));
|
|
||||||
|
|
||||||
vk::DescriptorBindingFlags bindingFlags =
|
|
||||||
vk::DescriptorBindingFlagBits::ePartiallyBound | vk::DescriptorBindingFlagBits::eUpdateAfterBind;
|
|
||||||
eastl::array layoutBindingFlags = {
|
|
||||||
bindingFlags,
|
|
||||||
bindingFlags,
|
|
||||||
bindingFlags,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::DescriptorSetLayoutBindingFlagsCreateInfo bindingFlagsCreateInfo = {
|
|
||||||
.bindingCount = Cast<u32>(layoutBindingFlags.size()),
|
|
||||||
.pBindingFlags = layoutBindingFlags.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
eastl::array descriptorLayoutBindings = {
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = BUFFER_BINDING_INDEX,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageBuffer,
|
|
||||||
.descriptorCount = Cast<u32>(buffersCount),
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
},
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = TEXTURE_BINDING_INDEX,
|
|
||||||
.descriptorType = vk::DescriptorType::eCombinedImageSampler,
|
|
||||||
.descriptorCount = Cast<u32>(texturesCount),
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
},
|
|
||||||
vk::DescriptorSetLayoutBinding{
|
|
||||||
.binding = STORAGE_TEXTURE_BINDING_INDEX,
|
|
||||||
.descriptorType = vk::DescriptorType::eStorageImage,
|
|
||||||
.descriptorCount = Cast<u32>(storageTexturesCount),
|
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
static_assert(layoutBindingFlags.size() == descriptorLayoutBindings.size());
|
|
||||||
const vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
|
||||||
.pNext = &bindingFlagsCreateInfo,
|
|
||||||
.flags = vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
|
|
||||||
.bindingCount = Cast<u32>(descriptorLayoutBindings.size()),
|
|
||||||
.pBindings = descriptorLayoutBindings.data(),
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &m_SetLayout));
|
|
||||||
|
|
||||||
// One descriptor is enough. Updating it at any time is safe. (Update until submit, data held when pending)
|
|
||||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_descriptor_indexing.html
|
|
||||||
// https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/extensions/VK_EXT_descriptor_indexing.adoc
|
|
||||||
const vk::DescriptorSetAllocateInfo descriptorSetAllocateInfo = {
|
|
||||||
.descriptorPool = m_DescriptorPool,
|
|
||||||
.descriptorSetCount = 1,
|
|
||||||
.pSetLayouts = &m_SetLayout,
|
|
||||||
};
|
|
||||||
AbortIfFailed(device->m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &m_DescriptorSet));
|
|
||||||
|
|
||||||
m_Device->SetName(m_SetLayout, "Bindless Layout");
|
|
||||||
m_Device->SetName(m_DescriptorPool, "Bindless Pool");
|
|
||||||
m_Device->SetName(m_DescriptorSet, "Bindless Set");
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager::~GpuResourceManager()
|
|
||||||
{
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
WARN_IF(m_CommitedBufferCount > 0 || m_CommitedTextureCount > 0 || m_CommitedStorageTextureCount > 0,
|
|
||||||
"Resources alive: SSBO = {}, Textures = {}, RWTexture = {}", m_CommitedBufferCount, m_CommitedTextureCount,
|
|
||||||
m_CommitedStorageTextureCount);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_BufferManager.Destroy(m_Device);
|
|
||||||
m_TextureManager.Destroy(m_Device);
|
|
||||||
m_StorageTextureManager.Destroy(m_Device);
|
|
||||||
m_SamplerManager.Destroy(m_Device);
|
|
||||||
m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_SetLayout, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
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_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
GpuResourceManager &
|
|
||||||
GpuResourceManager::operator=(GpuResourceManager &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
m_WriteInfos = std::move(other.m_WriteInfos);
|
|
||||||
m_Writes = std::move(other.m_Writes);
|
|
||||||
m_WriteOwner = std::move(other.m_WriteOwner);
|
|
||||||
m_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);
|
|
||||||
}
|
|
||||||
|
|
@ -1,175 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: gpu_resource_manager.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "aster/aster.h"
|
|
||||||
|
|
||||||
#include <EASTL/deque.h>
|
|
||||||
#include <EASTL/vector_map.h>
|
|
||||||
|
|
||||||
struct Device;
|
|
||||||
struct Texture;
|
|
||||||
struct StorageTexture;
|
|
||||||
struct StorageBuffer;
|
|
||||||
|
|
||||||
struct GpuResourceHandle
|
|
||||||
{
|
|
||||||
constexpr static u32 INVALID_HANDLE = MaxValue<u32>;
|
|
||||||
u32 m_Index = INVALID_HANDLE; // Default = invalid
|
|
||||||
|
|
||||||
[[nodiscard]] bool
|
|
||||||
IsInvalid() const
|
|
||||||
{
|
|
||||||
return m_Index == INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BufferHandle : GpuResourceHandle
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureHandle : GpuResourceHandle
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StorageTextureHandle : GpuResourceHandle
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SamplerHandle : GpuResourceHandle
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextureManager
|
|
||||||
{
|
|
||||||
eastl::vector<Texture> m_Textures;
|
|
||||||
u32 m_MaxCapacity;
|
|
||||||
u32 m_FreeHead;
|
|
||||||
|
|
||||||
void Init(u32 maxCapacity);
|
|
||||||
TextureHandle Commit(Texture *texture);
|
|
||||||
Texture *Fetch(TextureHandle handle);
|
|
||||||
void Release(const Device *device, TextureHandle handle);
|
|
||||||
void Destroy(const Device *device);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BufferManager
|
|
||||||
{
|
|
||||||
eastl::vector<StorageBuffer> m_Buffers;
|
|
||||||
u32 m_MaxCapacity;
|
|
||||||
u32 m_FreeHead;
|
|
||||||
|
|
||||||
void Init(u32 maxCapacity);
|
|
||||||
BufferHandle Commit(StorageBuffer *buffer);
|
|
||||||
StorageBuffer *Fetch(BufferHandle handle);
|
|
||||||
void Release(const Device *device, BufferHandle handle);
|
|
||||||
void Destroy(const Device *device);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StorageTextureManager : TextureManager
|
|
||||||
{
|
|
||||||
StorageTextureHandle Commit(StorageTexture *texture);
|
|
||||||
StorageTexture *Fetch(StorageTextureHandle handle);
|
|
||||||
void Release(const Device *device, StorageTextureHandle handle);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SamplerManager
|
|
||||||
{
|
|
||||||
// There can only be so many samplers.
|
|
||||||
eastl::vector<vk::Sampler> m_Samplers;
|
|
||||||
eastl::vector<usize> m_SamplerHashes;
|
|
||||||
|
|
||||||
void Init(usize size);
|
|
||||||
SamplerHandle Create(const Device *device, const vk::SamplerCreateInfo *createInfo);
|
|
||||||
vk::Sampler Fetch(SamplerHandle handle);
|
|
||||||
void Destroy(const Device *device);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GpuResourceManager
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
union WriteInfo {
|
|
||||||
vk::DescriptorBufferInfo uBufferInfo;
|
|
||||||
vk::DescriptorImageInfo uImageInfo;
|
|
||||||
vk::BufferView uBufferView;
|
|
||||||
|
|
||||||
WriteInfo()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit WriteInfo(vk::DescriptorBufferInfo info);
|
|
||||||
explicit WriteInfo(vk::DescriptorImageInfo info);
|
|
||||||
explicit WriteInfo(vk::BufferView info);
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class HandleType
|
|
||||||
{
|
|
||||||
eBuffer,
|
|
||||||
eTexture,
|
|
||||||
eStorageTexture,
|
|
||||||
};
|
|
||||||
|
|
||||||
using WriteOwner = eastl::pair<HandleType, u32>;
|
|
||||||
|
|
||||||
eastl::deque<WriteInfo> m_WriteInfos;
|
|
||||||
eastl::vector<vk::WriteDescriptorSet> m_Writes;
|
|
||||||
eastl::vector<WriteOwner> m_WriteOwner;
|
|
||||||
|
|
||||||
vk::Sampler m_DefaultSampler;
|
|
||||||
|
|
||||||
BufferManager m_BufferManager;
|
|
||||||
TextureManager m_TextureManager;
|
|
||||||
StorageTextureManager m_StorageTextureManager;
|
|
||||||
SamplerManager m_SamplerManager;
|
|
||||||
|
|
||||||
void EraseWrites(u32 handleIndex, HandleType handleType);
|
|
||||||
|
|
||||||
public:
|
|
||||||
Device *m_Device;
|
|
||||||
|
|
||||||
constexpr static u32 BUFFER_BINDING_INDEX = 0;
|
|
||||||
constexpr static u32 TEXTURE_BINDING_INDEX = 1;
|
|
||||||
constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2;
|
|
||||||
|
|
||||||
vk::SamplerCreateInfo m_DefaultSamplerCreateInfo;
|
|
||||||
|
|
||||||
vk::DescriptorPool m_DescriptorPool;
|
|
||||||
vk::DescriptorSetLayout m_SetLayout;
|
|
||||||
vk::DescriptorSet m_DescriptorSet;
|
|
||||||
|
|
||||||
BufferHandle Commit(StorageBuffer *storageBuffer); // Commit to GPU and take Ownership
|
|
||||||
void Write(BufferHandle handle, usize offset, usize size, const void *data); // Write to buffer
|
|
||||||
void Release(BufferHandle handle); // Release and Destroy
|
|
||||||
void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return
|
|
||||||
|
|
||||||
TextureHandle CommitTexture(Texture *texture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
|
|
||||||
void Release(TextureHandle handle); // Release and Destroy
|
|
||||||
void Release(Texture *texture, TextureHandle handle); // Release and Return
|
|
||||||
|
|
||||||
StorageTextureHandle
|
|
||||||
CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
|
|
||||||
void Release(StorageTextureHandle handle); // Release and Destroy
|
|
||||||
void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return
|
|
||||||
|
|
||||||
SamplerHandle CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo);
|
|
||||||
|
|
||||||
void Update(); // Update all the descriptors required.
|
|
||||||
|
|
||||||
// Ctor/Dtor
|
|
||||||
GpuResourceManager(Device *device, u16 maxSize);
|
|
||||||
~GpuResourceManager();
|
|
||||||
|
|
||||||
GpuResourceManager(GpuResourceManager &&other) noexcept;
|
|
||||||
GpuResourceManager &operator=(GpuResourceManager &&other) noexcept;
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
|
||||||
usize m_CommitedBufferCount = 0;
|
|
||||||
usize m_CommitedTextureCount = 0;
|
|
||||||
usize m_CommitedStorageTextureCount = 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(GpuResourceManager);
|
|
||||||
};
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: ibl_helpers.cpp
|
// Aster: ibl_helpers.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "ibl_helpers.h"
|
#include "ibl_helpers.h"
|
||||||
|
|
@ -9,82 +9,91 @@
|
||||||
#include "aster/core/image.h"
|
#include "aster/core/image.h"
|
||||||
|
|
||||||
#include "asset_loader.h"
|
#include "asset_loader.h"
|
||||||
#include "gpu_resource_manager.h"
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "pipeline_utils.h"
|
|
||||||
|
#include "aster/systems/commit_manager.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
|
||||||
#include <EASTL/fixed_vector.h>
|
#include <EASTL/fixed_vector.h>
|
||||||
#include <EASTL/tuple.h>
|
#include <EASTL/tuple.h>
|
||||||
|
|
||||||
constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv";
|
constexpr auto EQUIRECT_TO_CUBE_SHADER_FILE = "eqrect_to_cube";
|
||||||
constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv";
|
constexpr auto ENVIRONMENT_SHADER_FILE = "environment";
|
||||||
constexpr cstr PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv";
|
constexpr auto DIFFUSE_IRRADIANCE_ENTRY = "diffuseIrradiance";
|
||||||
constexpr cstr BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv";
|
constexpr auto PREFILTER_ENTRY = "prefilter";
|
||||||
|
constexpr auto BRDF_LUT_ENTRY = "brdfLut";
|
||||||
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
|
Environment
|
||||||
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv,
|
CreateCubeFromHdrEnv(AssetLoader &assetLoader, u32 const cubeSide, systems::ResId<TextureView> hdrEnv)
|
||||||
const cstr name)
|
|
||||||
{
|
{
|
||||||
GpuResourceManager *resMan = assetLoader->m_ResourceManager;
|
systems::RenderingDevice &device = *assetLoader.m_Device;
|
||||||
const Device *pDevice = resMan->m_Device;
|
auto *commitManager = device.m_CommitManager.get();
|
||||||
|
|
||||||
vk::SamplerCreateInfo brdfLutSamplerCreateInfo = resMan->m_DefaultSamplerCreateInfo;
|
auto skybox = device.CreateTextureCubeWithView<StorageTextureCubeView>({
|
||||||
brdfLutSamplerCreateInfo.addressModeU = vk::SamplerAddressMode::eClampToEdge;
|
.m_Format = vk::Format::eR16G16B16A16Sfloat,
|
||||||
brdfLutSamplerCreateInfo.addressModeV = vk::SamplerAddressMode::eClampToEdge;
|
.m_Side = cubeSide,
|
||||||
brdfLutSamplerCreateInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge;
|
.m_Name = "Skybox",
|
||||||
|
.m_IsSampled = true,
|
||||||
|
.m_IsMipMapped = true,
|
||||||
|
.m_IsStorage = true,
|
||||||
|
});
|
||||||
|
|
||||||
StorageTextureCube skybox;
|
auto skyboxHandle = commitManager->CommitTexture(skybox);
|
||||||
StorageTextureCube diffuseIrradiance;
|
auto skyboxStorageHandle = commitManager->CommitStorageImage(skybox);
|
||||||
StorageTextureCube prefilterCube;
|
|
||||||
StorageTexture brdfLut;
|
|
||||||
SamplerHandle brdfLutSampler;
|
|
||||||
|
|
||||||
skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox");
|
auto diffuseIrradiance = device.CreateTextureCubeWithView<StorageTextureCubeView>({
|
||||||
TextureHandle skyboxHandle = resMan->CommitTexture(&skybox);
|
.m_Format = vk::Format::eR16G16B16A16Sfloat,
|
||||||
StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox);
|
.m_Side = 64,
|
||||||
|
.m_Name = "Diffuse Irradiance",
|
||||||
|
.m_IsSampled = true,
|
||||||
|
.m_IsMipMapped = false,
|
||||||
|
.m_IsStorage = true,
|
||||||
|
});
|
||||||
|
auto diffuseIrradianceHandle = commitManager->CommitTexture(diffuseIrradiance);
|
||||||
|
auto diffuseIrradianceStorageHandle = commitManager->CommitStorageImage(diffuseIrradiance);
|
||||||
|
|
||||||
diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance");
|
auto prefilterCube = device.CreateTextureCubeWithView<StorageTextureCubeView>({
|
||||||
TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance);
|
.m_Format = vk::Format::eR16G16B16A16Sfloat,
|
||||||
StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance);
|
.m_Side = cubeSide,
|
||||||
|
.m_Name = "Prefilter",
|
||||||
prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter");
|
.m_IsSampled = true,
|
||||||
TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us.
|
.m_IsMipMapped = true,
|
||||||
|
.m_IsStorage = true,
|
||||||
|
});
|
||||||
|
auto prefilterHandle = commitManager->CommitTexture(prefilterCube); // This stores the original view for us.
|
||||||
constexpr u32 prefilterMipCountMax = 6;
|
constexpr u32 prefilterMipCountMax = 6;
|
||||||
eastl::array<StorageTextureHandle, prefilterMipCountMax> prefilterStorageHandles;
|
eastl::fixed_vector<systems::ResId<StorageImageView>, prefilterMipCountMax> prefilterStorageHandles;
|
||||||
// All non-owning copies.
|
// All non-owning copies.
|
||||||
for (u32 mipLevel = 0; auto &tex : prefilterStorageHandles)
|
for (u8 mipLevel = 0; mipLevel < prefilterMipCountMax; ++mipLevel)
|
||||||
{
|
{
|
||||||
vk::ImageViewCreateInfo imageViewCreateInfo = {
|
auto view = device.CreateView<StorageTextureCubeView>({
|
||||||
.image = prefilterCube.m_Image,
|
.m_Image = systems::CastImage<StorageTextureCube>(prefilterCube->m_Image),
|
||||||
.viewType = vk::ImageViewType::eCube,
|
.m_ViewType = vk::ImageViewType::eCube,
|
||||||
.format = vk::Format::eR16G16B16A16Sfloat,
|
.m_AspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
.components = vk::ComponentMapping{},
|
.m_MipLevelCount = 1,
|
||||||
.subresourceRange =
|
.m_LayerCount = 6,
|
||||||
{
|
.m_BaseMipLevel = mipLevel,
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.m_BaseLayer = 0,
|
||||||
.baseMipLevel = mipLevel++,
|
});
|
||||||
.levelCount = 1,
|
prefilterStorageHandles.push_back(commitManager->CommitStorageImage(view));
|
||||||
.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");
|
auto brdfLut = device.CreateTexture2DWithView<StorageTextureView>({
|
||||||
brdfLutSampler = resMan->CreateSampler(&brdfLutSamplerCreateInfo);
|
.m_Format = vk::Format::eR16G16Sfloat,
|
||||||
TextureHandle brdfLutHandle = resMan->CommitTexture(&brdfLut, brdfLutSampler);
|
.m_Extent = {512, 512},
|
||||||
StorageTextureHandle brdfLutStorageHandle = resMan->CommitStorageTexture(&brdfLut);
|
.m_Name = "BRDF LUT",
|
||||||
|
.m_IsSampled = true,
|
||||||
|
.m_IsMipMapped = false,
|
||||||
|
.m_IsStorage = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
auto brdfLutSampler = device.CreateSampler({
|
||||||
|
.m_AddressModeU = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.m_AddressModeV = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
.m_AddressModeW = vk::SamplerAddressMode::eClampToEdge,
|
||||||
|
});
|
||||||
|
auto brdfLutHandle = commitManager->CommitTexture(brdfLut, brdfLutSampler);
|
||||||
|
auto brdfLutStorageHandle = commitManager->CommitStorageImage(brdfLut);
|
||||||
|
|
||||||
#pragma region Dependencies and Copies
|
#pragma region Dependencies and Copies
|
||||||
vk::ImageSubresourceRange cubeSubresRange = {
|
vk::ImageSubresourceRange cubeSubresRange = {
|
||||||
|
|
@ -114,14 +123,14 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
.subresourceRange = cubeSubresRange,
|
.subresourceRange = cubeSubresRange,
|
||||||
};
|
};
|
||||||
eastl::fixed_vector<vk::ImageMemoryBarrier2, 4> readyToWriteBarriers(4, readyToWriteBarrierTemplate);
|
eastl::fixed_vector<vk::ImageMemoryBarrier2, 4> readyToWriteBarriers(4, readyToWriteBarrierTemplate);
|
||||||
readyToWriteBarriers[0].image = skybox.m_Image;
|
readyToWriteBarriers[0].image = skybox->GetImage();
|
||||||
readyToWriteBarriers[1].image = diffuseIrradiance.m_Image;
|
readyToWriteBarriers[1].image = diffuseIrradiance->GetImage();
|
||||||
readyToWriteBarriers[2].image = prefilterCube.m_Image;
|
readyToWriteBarriers[2].image = prefilterCube->GetImage();
|
||||||
readyToWriteBarriers[3].image = brdfLut.m_Image;
|
readyToWriteBarriers[3].image = brdfLut->GetImage();
|
||||||
readyToWriteBarriers[3].subresourceRange = lutSubresRange;
|
readyToWriteBarriers[3].subresourceRange = lutSubresRange;
|
||||||
|
|
||||||
vk::DependencyInfo readyToWriteDependency = {
|
vk::DependencyInfo readyToWriteDependency = {
|
||||||
.imageMemoryBarrierCount = Cast<u32>(readyToWriteBarriers.size()),
|
.imageMemoryBarrierCount = static_cast<u32>(readyToWriteBarriers.size()),
|
||||||
.pImageMemoryBarriers = readyToWriteBarriers.data(),
|
.pImageMemoryBarriers = readyToWriteBarriers.data(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -136,16 +145,16 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
.subresourceRange = cubeSubresRange,
|
.subresourceRange = cubeSubresRange,
|
||||||
};
|
};
|
||||||
auto skyboxToSampleBarrier = readyToSampleBarrierTemplate;
|
auto skyboxToSampleBarrier = readyToSampleBarrierTemplate;
|
||||||
skyboxToSampleBarrier.image = skybox.m_Image;
|
skyboxToSampleBarrier.image = skybox->GetImage();
|
||||||
|
|
||||||
auto diffIrrToSampleBarrier = readyToSampleBarrierTemplate;
|
auto diffIrrToSampleBarrier = readyToSampleBarrierTemplate;
|
||||||
diffIrrToSampleBarrier.image = diffuseIrradiance.m_Image;
|
diffIrrToSampleBarrier.image = diffuseIrradiance->GetImage();
|
||||||
|
|
||||||
auto prefilterToSampleBarrier = readyToSampleBarrierTemplate;
|
auto prefilterToSampleBarrier = readyToSampleBarrierTemplate;
|
||||||
prefilterToSampleBarrier.image = prefilterCube.m_Image;
|
prefilterToSampleBarrier.image = prefilterCube->GetImage();
|
||||||
|
|
||||||
auto brdfToSampleBarrier = readyToSampleBarrierTemplate;
|
auto brdfToSampleBarrier = readyToSampleBarrierTemplate;
|
||||||
prefilterToSampleBarrier.image = brdfLut.m_Image;
|
prefilterToSampleBarrier.image = brdfLut->GetImage();
|
||||||
prefilterToSampleBarrier.subresourceRange = lutSubresRange;
|
prefilterToSampleBarrier.subresourceRange = lutSubresRange;
|
||||||
|
|
||||||
vk::DependencyInfo skyboxToSampleDependency = {
|
vk::DependencyInfo skyboxToSampleDependency = {
|
||||||
|
|
@ -169,103 +178,144 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
|
|
||||||
struct SkyboxPushConstants
|
struct SkyboxPushConstants
|
||||||
{
|
{
|
||||||
TextureHandle m_HdrEnvHandle;
|
systems::ResId<TextureView> m_HdrEnvHandle;
|
||||||
StorageTextureHandle m_OutputTexture;
|
systems::ResId<StorageImageView> m_OutputTexture;
|
||||||
u32 m_CubeSide;
|
u32 m_CubeSide;
|
||||||
};
|
};
|
||||||
struct DiffuseIrradiancePushConstants
|
struct DiffuseIrradiancePushConstants
|
||||||
{
|
{
|
||||||
TextureHandle m_SkyboxHandle;
|
systems::ResId<TextureView> m_SkyboxHandle;
|
||||||
StorageTextureHandle m_OutputTexture;
|
systems::ResId<StorageImageView> m_OutputTexture;
|
||||||
u32 m_CubeSide;
|
u32 m_CubeSide;
|
||||||
};
|
};
|
||||||
struct PrefilterPushConstants
|
struct PrefilterPushConstants
|
||||||
{
|
{
|
||||||
TextureHandle m_SkyboxHandle;
|
systems::ResId<TextureView> m_SkyboxHandle;
|
||||||
StorageTextureHandle m_OutputTexture;
|
systems::ResId<StorageImageView> m_OutputTexture;
|
||||||
u32 m_CubeSide;
|
u32 m_CubeSide;
|
||||||
f32 m_Roughness;
|
f32 m_Roughness;
|
||||||
u32 m_EnvSide;
|
u32 m_EnvSide;
|
||||||
};
|
};
|
||||||
struct BrdfLutPushConstants
|
struct BrdfLutPushConstants
|
||||||
{
|
{
|
||||||
StorageTextureHandle m_OutputTexture;
|
systems::ResId<StorageImageView> m_OutputTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma region Pipeline Creation etc
|
#pragma region Pipeline Creation etc
|
||||||
|
|
||||||
vk::PushConstantRange pcr = {
|
// vk::PushConstantRange pcr = {
|
||||||
.stageFlags = vk::ShaderStageFlagBits::eCompute,
|
// .stageFlags = vk::ShaderStageFlagBits::eCompute,
|
||||||
.offset = 0,
|
// .offset = 0,
|
||||||
.size = Cast<u32>(eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
|
// .size = static_cast<u32>(
|
||||||
eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants)))),
|
// eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
|
||||||
};
|
// eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants)))),
|
||||||
|
// };
|
||||||
|
|
||||||
vk::PipelineLayout pipelineLayout;
|
// vk::PipelineLayout pipelineLayout;
|
||||||
const vk::PipelineLayoutCreateInfo layoutCreateInfo = {
|
// const vk::PipelineLayoutCreateInfo layoutCreateInfo = {
|
||||||
.setLayoutCount = 1,
|
// .setLayoutCount = 1,
|
||||||
.pSetLayouts = &resMan->m_SetLayout,
|
// .pSetLayouts = &commitManager->GetDescriptorSetLayout(),
|
||||||
.pushConstantRangeCount = 1,
|
// .pushConstantRangeCount = 1,
|
||||||
.pPushConstantRanges = &pcr,
|
// .pPushConstantRanges = &pcr,
|
||||||
};
|
// };
|
||||||
AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout));
|
// AbortIfFailed(device.m_Device->createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout));
|
||||||
|
|
||||||
const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE);
|
// const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE);
|
||||||
const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE);
|
// const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE);
|
||||||
const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE);
|
// const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE);
|
||||||
const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE);
|
// const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE);
|
||||||
eastl::array computePipelineCreateInfo = {
|
// eastl::array computePipelineCreateInfo = {
|
||||||
vk::ComputePipelineCreateInfo{
|
// vk::ComputePipelineCreateInfo{
|
||||||
.stage =
|
// .stage =
|
||||||
{
|
// {
|
||||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
// .stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
.module = eqRectToCubeShader,
|
// .module = eqRectToCubeShader,
|
||||||
.pName = "main",
|
// .pName = "main",
|
||||||
},
|
// },
|
||||||
.layout = pipelineLayout,
|
// .layout = pipelineLayout,
|
||||||
},
|
// },
|
||||||
vk::ComputePipelineCreateInfo{
|
// vk::ComputePipelineCreateInfo{
|
||||||
.stage =
|
// .stage =
|
||||||
{
|
// {
|
||||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
// .stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
.module = diffuseRadianceShader,
|
// .module = diffuseRadianceShader,
|
||||||
.pName = "main",
|
// .pName = "main",
|
||||||
},
|
// },
|
||||||
.layout = pipelineLayout,
|
// .layout = pipelineLayout,
|
||||||
},
|
// },
|
||||||
vk::ComputePipelineCreateInfo{
|
// vk::ComputePipelineCreateInfo{
|
||||||
.stage =
|
// .stage =
|
||||||
{
|
// {
|
||||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
// .stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
.module = prefilterShader,
|
// .module = prefilterShader,
|
||||||
.pName = "main",
|
// .pName = "main",
|
||||||
},
|
// },
|
||||||
.layout = pipelineLayout,
|
// .layout = pipelineLayout,
|
||||||
},
|
// },
|
||||||
vk::ComputePipelineCreateInfo{
|
// vk::ComputePipelineCreateInfo{
|
||||||
.stage =
|
// .stage =
|
||||||
{
|
// {
|
||||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
// .stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
.module = brdfLutShader,
|
// .module = brdfLutShader,
|
||||||
.pName = "main",
|
// .pName = "main",
|
||||||
},
|
// },
|
||||||
.layout = pipelineLayout,
|
// .layout = pipelineLayout,
|
||||||
},
|
// },
|
||||||
};
|
// };
|
||||||
|
|
||||||
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
|
// eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
|
||||||
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, Cast<u32>(computePipelineCreateInfo.size()),
|
// AbortIfFailed(pDevice->m_Device.createComputePipelines(
|
||||||
computePipelineCreateInfo.data(), nullptr,
|
// pDevice->m_PipelineCache, static_cast<u32>(computePipelineCreateInfo.size()),
|
||||||
pipelines.data()));
|
// computePipelineCreateInfo.data(), nullptr, pipelines.data()));
|
||||||
|
|
||||||
vk::Pipeline eqRectToCubePipeline = pipelines[0];
|
// vk::Pipeline eqRectToCubePipeline = pipelines[0];
|
||||||
vk::Pipeline diffuseIrradiancePipeline = pipelines[1];
|
// vk::Pipeline diffuseIrradiancePipeline = pipelines[1];
|
||||||
vk::Pipeline prefilterPipeline = pipelines[2];
|
// vk::Pipeline prefilterPipeline = pipelines[2];
|
||||||
vk::Pipeline brdfLutPipeline = pipelines[3];
|
// vk::Pipeline brdfLutPipeline = pipelines[3];
|
||||||
|
|
||||||
for (auto &createInfos : computePipelineCreateInfo)
|
Pipeline eqRectToCubePipeline;
|
||||||
|
|
||||||
|
if (auto result =
|
||||||
|
device.CreateComputePipeline(eqRectToCubePipeline, {
|
||||||
|
.m_Shader =
|
||||||
|
{
|
||||||
|
.m_ShaderFile = EQUIRECT_TO_CUBE_SHADER_FILE,
|
||||||
|
.m_EntryPoints = {"main"},
|
||||||
|
},
|
||||||
|
.m_Name = "EqRect -> Cubemap",
|
||||||
|
}))
|
||||||
{
|
{
|
||||||
pDevice->m_Device.destroy(createInfos.stage.module, nullptr);
|
ERROR("EqRect -> Cubemap Pipeline Creation failed. Cause: {}", result.What()) THEN_ABORT(result.Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline diffuseIrradiancePipeline;
|
||||||
|
|
||||||
|
if (auto result = device.CreateComputePipeline(
|
||||||
|
diffuseIrradiancePipeline,
|
||||||
|
{{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {DIFFUSE_IRRADIANCE_ENTRY}},
|
||||||
|
"DiffuseIrradiance"}))
|
||||||
|
{
|
||||||
|
ERROR("Diffuse Irradiance compute pipeline creation failed. Cause: {}", result.What())
|
||||||
|
THEN_ABORT(result.Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline prefilterPipeline;
|
||||||
|
|
||||||
|
if (auto result = device.CreateComputePipeline(
|
||||||
|
prefilterPipeline,
|
||||||
|
{{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {PREFILTER_ENTRY}}, "Prefilter"}))
|
||||||
|
{
|
||||||
|
ERROR("Prefilter compute pipeline creation failed. Cause: {}", result.What())
|
||||||
|
THEN_ABORT(result.Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
Pipeline brdfLutPipeline;
|
||||||
|
|
||||||
|
if (auto result = device.CreateComputePipeline(
|
||||||
|
brdfLutPipeline, {{.m_ShaderFile = ENVIRONMENT_SHADER_FILE, .m_EntryPoints = {BRDF_LUT_ENTRY}}, "BRDF"}))
|
||||||
|
{
|
||||||
|
ERROR("BRDF LUT compute pipeline creation failed. Cause: {}", result.What())
|
||||||
|
THEN_ABORT(result.Value());
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
@ -278,115 +328,68 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = {
|
DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = {
|
||||||
.m_SkyboxHandle = skyboxHandle,
|
.m_SkyboxHandle = skyboxHandle,
|
||||||
.m_OutputTexture = diffuseIrradianceStorageHandle,
|
.m_OutputTexture = diffuseIrradianceStorageHandle,
|
||||||
.m_CubeSide = diffuseIrradiance.m_Extent.width,
|
.m_CubeSide = diffuseIrradiance->m_Extent.width,
|
||||||
};
|
};
|
||||||
PrefilterPushConstants prefilterPushConstants = {
|
PrefilterPushConstants prefilterPushConstants = {
|
||||||
.m_SkyboxHandle = skyboxHandle,
|
.m_SkyboxHandle = skyboxHandle,
|
||||||
|
.m_OutputTexture = systems::NullId{},
|
||||||
.m_EnvSide = cubeSide,
|
.m_EnvSide = cubeSide,
|
||||||
};
|
};
|
||||||
BrdfLutPushConstants brdfLutPushConstants = {
|
BrdfLutPushConstants brdfLutPushConstants = {
|
||||||
.m_OutputTexture = brdfLutStorageHandle,
|
.m_OutputTexture = brdfLutStorageHandle,
|
||||||
};
|
};
|
||||||
|
|
||||||
resMan->Update();
|
commitManager->Update();
|
||||||
|
|
||||||
auto cmd = assetLoader->m_CommandBuffer;
|
auto context = assetLoader.m_Device->CreateComputeContext();
|
||||||
constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
|
||||||
AbortIfFailed(cmd.begin(&beginInfo));
|
|
||||||
|
|
||||||
#if !defined(ASTER_NDEBUG)
|
context.Begin();
|
||||||
StackString<128> labelName = "Eqrect -> Cubemap: ";
|
|
||||||
labelName += name ? name : "<unknown env>";
|
|
||||||
vk::DebugUtilsLabelEXT label = {
|
|
||||||
.pLabelName = labelName.c_str(),
|
|
||||||
.color = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
|
||||||
};
|
|
||||||
cmd.beginDebugUtilsLabelEXT(&label);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&readyToWriteDependency);
|
context.BeginDebugRegion("Eqrect -> Cubemap");
|
||||||
|
|
||||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr);
|
context.Dependency(readyToWriteDependency);
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline);
|
|
||||||
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant,
|
|
||||||
&skyboxPushConstant);
|
|
||||||
assert(skybox.m_Extent.width % 16 == 0 && skybox.m_Extent.height % 16 == 0);
|
|
||||||
cmd.dispatch(skybox.m_Extent.width / 16, skybox.m_Extent.height / 16, 6);
|
|
||||||
|
|
||||||
GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral,
|
assert(skybox->m_Extent.width % 16 == 0 && skybox->m_Extent.height % 16 == 0);
|
||||||
|
context.Dispatch(eqRectToCubePipeline, skybox->m_Extent.width / 16, skybox->m_Extent.height / 16, 6,
|
||||||
|
skyboxPushConstant);
|
||||||
|
|
||||||
|
GenerateMipMaps(context, skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral,
|
||||||
vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader);
|
vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader);
|
||||||
|
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline);
|
assert(diffuseIrradiance->m_Extent.width % 16 == 0 && diffuseIrradiance->m_Extent.height % 16 == 0);
|
||||||
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant,
|
context.Dispatch(diffuseIrradiancePipeline, diffuseIrradiance->m_Extent.width / 16,
|
||||||
&diffuseIrradiancePushConstants);
|
diffuseIrradiance->m_Extent.width / 16, 6, diffuseIrradiancePushConstants);
|
||||||
assert(diffuseIrradiance.m_Extent.width % 16 == 0 && diffuseIrradiance.m_Extent.height % 16 == 0);
|
|
||||||
cmd.dispatch(diffuseIrradiance.m_Extent.width / 16, diffuseIrradiance.m_Extent.width / 16, 6);
|
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&diffIrrToSampleDependency);
|
context.Dependency(diffIrrToSampleDependency);
|
||||||
|
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline);
|
u32 mipSize = prefilterCube->m_Extent.width;
|
||||||
u32 mipSize = prefilterCube.m_Extent.width;
|
|
||||||
assert(mipSize % 16 == 0);
|
assert(mipSize % 16 == 0);
|
||||||
for (u32 mipCount = 0; auto &tex : prefilterStorageHandles)
|
for (u32 mipCount = 0; auto &tex : prefilterStorageHandles)
|
||||||
{
|
{
|
||||||
prefilterPushConstants.m_OutputTexture = tex;
|
prefilterPushConstants.m_OutputTexture = tex;
|
||||||
prefilterPushConstants.m_CubeSide = mipSize;
|
prefilterPushConstants.m_CubeSide = mipSize;
|
||||||
prefilterPushConstants.m_Roughness = Cast<f32>(mipCount) / Cast<f32>(prefilterMipCountMax);
|
prefilterPushConstants.m_Roughness = static_cast<f32>(mipCount) / static_cast<f32>(prefilterMipCountMax - 1);
|
||||||
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof prefilterPushConstants,
|
|
||||||
&prefilterPushConstants);
|
|
||||||
u32 groupCount = eastl::max(mipSize / 16u, 1u);
|
u32 groupCount = eastl::max(mipSize / 16u, 1u);
|
||||||
cmd.dispatch(groupCount, groupCount, 6);
|
context.Dispatch(prefilterPipeline, groupCount, groupCount, 6, prefilterPushConstants);
|
||||||
|
|
||||||
++mipCount;
|
++mipCount;
|
||||||
mipSize = mipSize >> 1;
|
mipSize = mipSize >> 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.pipelineBarrier2(&skyboxToSampleDependency);
|
context.Dependency(skyboxToSampleDependency);
|
||||||
cmd.pipelineBarrier2(&prefilterToSampleDependency);
|
context.Dependency(prefilterToSampleDependency);
|
||||||
|
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, brdfLutPipeline);
|
assert(brdfLut->m_Extent.width % 16 == 0 && brdfLut->m_Extent.height % 16 == 0);
|
||||||
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof brdfLutPushConstants,
|
context.Dispatch(brdfLutPipeline, brdfLut->m_Extent.width / 16, brdfLut->m_Extent.height / 16, 1,
|
||||||
&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)
|
context.EndDebugRegion();
|
||||||
cmd.endDebugUtilsLabelEXT();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
AbortIfFailed(cmd.end());
|
context.End();
|
||||||
|
|
||||||
vk::SubmitInfo submitInfo = {
|
auto receipt = device.Submit(context);
|
||||||
.waitSemaphoreCount = 0,
|
device.WaitOn(receipt);
|
||||||
.pWaitDstStageMask = nullptr,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &cmd,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Fence fence;
|
|
||||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
|
||||||
AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
|
|
||||||
AbortIfFailed(computeQueue.submit(1, &submitInfo, fence));
|
|
||||||
AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue<u32>));
|
|
||||||
pDevice->m_Device.destroy(fence, nullptr);
|
|
||||||
|
|
||||||
AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {}));
|
|
||||||
|
|
||||||
skybox = {};
|
|
||||||
resMan->Release(skyboxStorageHandle);
|
|
||||||
resMan->Release(diffuseIrradianceStorageHandle);
|
|
||||||
resMan->Release(brdfLutStorageHandle);
|
|
||||||
for (auto &texHandles : prefilterStorageHandles)
|
|
||||||
{
|
|
||||||
StorageTextureCube st;
|
|
||||||
resMan->Release(&st, texHandles);
|
|
||||||
pDevice->m_Device.destroy(st.m_View, nullptr);
|
|
||||||
}
|
|
||||||
for (auto &pipeline : pipelines)
|
|
||||||
{
|
|
||||||
pDevice->m_Device.destroy(pipeline, nullptr);
|
|
||||||
}
|
|
||||||
pDevice->m_Device.destroy(pipelineLayout, nullptr);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
.m_Skybox = skyboxHandle,
|
.m_Skybox = skyboxHandle,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: ibl_helpers.h
|
// Aster: ibl_helpers.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
#include "gpu_resource_manager.h"
|
#include "aster/core/image.h"
|
||||||
|
#include "aster/core/image_view.h"
|
||||||
|
#include "aster/systems/resource.h"
|
||||||
|
|
||||||
struct Pipeline;
|
struct Pipeline;
|
||||||
struct Texture;
|
struct Texture;
|
||||||
|
|
@ -15,14 +17,10 @@ struct AssetLoader;
|
||||||
|
|
||||||
struct Environment
|
struct Environment
|
||||||
{
|
{
|
||||||
TextureHandle m_Skybox;
|
systems::ResId<TextureView> m_Skybox;
|
||||||
TextureHandle m_Diffuse;
|
systems::ResId<TextureView> m_Diffuse;
|
||||||
TextureHandle m_Prefilter;
|
systems::ResId<TextureView> m_Prefilter;
|
||||||
TextureHandle m_BrdfLut;
|
systems::ResId<TextureView> m_BrdfLut;
|
||||||
|
|
||||||
void Destroy(GpuResourceManager *resourceManager);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Environment
|
Environment CreateCubeFromHdrEnv(AssetLoader &assetLoader, u32 cubeSide, systems::ResId<TextureView> hdrEnv);
|
||||||
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv,
|
|
||||||
cstr name = nullptr);
|
|
||||||
|
|
@ -1,35 +1,16 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: light_manager.cpp
|
// Aster: light_manager.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#include "light_manager.h"
|
#include "light_manager.h"
|
||||||
|
|
||||||
#include "aster/core/buffer.h"
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/systems/commit_manager.h"
|
||||||
|
#include "aster/systems/rendering_device.h"
|
||||||
|
#include "aster/systems/resource.h"
|
||||||
#include "glm/ext/matrix_transform.hpp"
|
#include "glm/ext/matrix_transform.hpp"
|
||||||
|
|
||||||
struct Light
|
|
||||||
{
|
|
||||||
union {
|
|
||||||
vec3 um_Position;
|
|
||||||
vec3 um_Direction;
|
|
||||||
};
|
|
||||||
f32 m_Range; // < 0.0 for invalid
|
|
||||||
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
|
||||||
f32 m_Intensity;
|
|
||||||
|
|
||||||
constexpr static u32 MAX_GEN = 0x40;
|
|
||||||
constexpr static u32 GEN_MASK = MAX_GEN - 1;
|
|
||||||
|
|
||||||
constexpr static u32 TYPE_MASK = 0xC0;
|
|
||||||
constexpr static u32 TYPE_INVALID = 0x0;
|
|
||||||
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
|
|
||||||
constexpr static u32 TYPE_POINT = 2 << 6;
|
|
||||||
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
|
|
||||||
|
|
||||||
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Static Checks
|
// Static Checks
|
||||||
|
|
||||||
// Ensure layouts are exact.
|
// Ensure layouts are exact.
|
||||||
|
|
@ -53,29 +34,29 @@ static_assert((Light::TYPE_MASK & Light::TYPE_SPOT) == Light::TYPE_SPOT);
|
||||||
static_assert(Light::COLOR_MASK == 0xFFFFFF00);
|
static_assert(Light::COLOR_MASK == 0xFFFFFF00);
|
||||||
|
|
||||||
inline u32
|
inline u32
|
||||||
ToColor32(const vec4 &col)
|
ToColor32(vec4 const &col)
|
||||||
{
|
{
|
||||||
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
u32 const r = static_cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||||
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
u32 const g = static_cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||||
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
u32 const b = static_cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
||||||
const u32 a = Cast<u32>(eastl::min(col.a, 1.0f) * 255.99f);
|
u32 const a = static_cast<u32>(eastl::min(col.a, 1.0f) * 255.99f);
|
||||||
|
|
||||||
return r << 24 | g << 16 | b << 8 | a;
|
return r << 24 | g << 16 | b << 8 | a;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u32
|
inline u32
|
||||||
ToColor32(const vec3 &col)
|
ToColor32(vec3 const &col)
|
||||||
{
|
{
|
||||||
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
u32 const r = static_cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||||
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
u32 const g = static_cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||||
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
u32 const b = static_cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
||||||
constexpr u32 a = 255;
|
constexpr u32 a = 255;
|
||||||
|
|
||||||
return r << 24 | g << 16 | b << 8 | a;
|
return r << 24 | g << 16 | b << 8 | a;
|
||||||
}
|
}
|
||||||
|
|
||||||
LightManager::LightManager(GpuResourceManager *resourceManager)
|
LightManager::LightManager(systems::RenderingDevice &device)
|
||||||
: m_ResourceManager{resourceManager}
|
: m_Device{&device}
|
||||||
, m_DirectionalLightCount{}
|
, m_DirectionalLightCount{}
|
||||||
, m_PointLightCount{}
|
, m_PointLightCount{}
|
||||||
, m_MetaInfo{}
|
, m_MetaInfo{}
|
||||||
|
|
@ -83,41 +64,10 @@ LightManager::LightManager(GpuResourceManager *resourceManager)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
LightManager::~LightManager()
|
|
||||||
{
|
|
||||||
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
LightManager::LightManager(LightManager &&other) noexcept
|
|
||||||
: m_ResourceManager(other.m_ResourceManager)
|
|
||||||
, m_Lights(std::move(other.m_Lights))
|
|
||||||
, m_DirectionalLightCount(other.m_DirectionalLightCount)
|
|
||||||
, m_PointLightCount(other.m_PointLightCount)
|
|
||||||
, m_MetaInfo(other.m_MetaInfo)
|
|
||||||
, m_GpuBufferCapacity_(other.m_GpuBufferCapacity_)
|
|
||||||
{
|
|
||||||
other.m_MetaInfo.m_LightBuffer = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
LightManager &
|
|
||||||
LightManager::operator=(LightManager &&other) noexcept
|
|
||||||
{
|
|
||||||
if (this == &other)
|
|
||||||
return *this;
|
|
||||||
m_ResourceManager = other.m_ResourceManager;
|
|
||||||
m_Lights = std::move(other.m_Lights);
|
|
||||||
m_DirectionalLightCount = other.m_DirectionalLightCount;
|
|
||||||
m_PointLightCount = other.m_PointLightCount;
|
|
||||||
m_MetaInfo = other.m_MetaInfo;
|
|
||||||
other.m_MetaInfo.m_LightBuffer = {};
|
|
||||||
m_GpuBufferCapacity_ = other.m_GpuBufferCapacity_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
LightHandle
|
LightHandle
|
||||||
LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity)
|
LightManager::AddDirectional(vec3 const &direction, vec3 const &color, f32 intensity)
|
||||||
{
|
{
|
||||||
const vec3 normDirection = normalize(direction);
|
vec3 const normDirection = normalize(direction);
|
||||||
if (m_DirectionalLightCount < m_MetaInfo.m_DirectionalLightMaxCount)
|
if (m_DirectionalLightCount < m_MetaInfo.m_DirectionalLightMaxCount)
|
||||||
{
|
{
|
||||||
u16 index = 0;
|
u16 index = 0;
|
||||||
|
|
@ -125,7 +75,7 @@ LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 inten
|
||||||
{
|
{
|
||||||
if (light.m_Range < 0)
|
if (light.m_Range < 0)
|
||||||
{
|
{
|
||||||
const u8 gen = light.m_Color_ & Light::GEN_MASK;
|
u8 const gen = light.m_Color_ & Light::GEN_MASK;
|
||||||
|
|
||||||
light.m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
light.m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
||||||
light.m_Range = 1.0f;
|
light.m_Range = 1.0f;
|
||||||
|
|
@ -145,8 +95,8 @@ LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 inten
|
||||||
if (m_DirectionalLightCount == m_MetaInfo.m_DirectionalLightMaxCount &&
|
if (m_DirectionalLightCount == m_MetaInfo.m_DirectionalLightMaxCount &&
|
||||||
m_MetaInfo.m_DirectionalLightMaxCount == m_MetaInfo.m_PointLightOffset)
|
m_MetaInfo.m_DirectionalLightMaxCount == m_MetaInfo.m_PointLightOffset)
|
||||||
{
|
{
|
||||||
const u16 oldPointLightOffset = m_MetaInfo.m_PointLightOffset;
|
u16 const oldPointLightOffset = m_MetaInfo.m_PointLightOffset;
|
||||||
const u32 pointLightMaxCount = m_MetaInfo.m_PointLightMaxCount;
|
u32 const pointLightMaxCount = m_MetaInfo.m_PointLightMaxCount;
|
||||||
// Might cause a capacity increase, but I want to use that for my gpu buffer resize.
|
// Might cause a capacity increase, but I want to use that for my gpu buffer resize.
|
||||||
m_Lights.push_back();
|
m_Lights.push_back();
|
||||||
m_Lights.push_back();
|
m_Lights.push_back();
|
||||||
|
|
@ -172,7 +122,7 @@ LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 inten
|
||||||
m_Lights[m_DirectionalLightCount].m_Range = 1.0f;
|
m_Lights[m_DirectionalLightCount].m_Range = 1.0f;
|
||||||
m_Lights[m_DirectionalLightCount].um_Direction = normDirection;
|
m_Lights[m_DirectionalLightCount].um_Direction = normDirection;
|
||||||
m_Lights[m_DirectionalLightCount].m_Intensity = intensity;
|
m_Lights[m_DirectionalLightCount].m_Intensity = intensity;
|
||||||
const u16 index = m_DirectionalLightCount;
|
u16 const index = m_DirectionalLightCount;
|
||||||
|
|
||||||
++m_DirectionalLightCount;
|
++m_DirectionalLightCount;
|
||||||
++m_MetaInfo.m_DirectionalLightMaxCount;
|
++m_MetaInfo.m_DirectionalLightMaxCount;
|
||||||
|
|
@ -181,7 +131,7 @@ LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 inten
|
||||||
}
|
}
|
||||||
|
|
||||||
LightHandle
|
LightHandle
|
||||||
LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius, f32 intensity)
|
LightManager::AddPoint(vec3 const &position, vec3 const &color, f32 const radius, f32 intensity)
|
||||||
{
|
{
|
||||||
assert(m_PointLightCount <= m_MetaInfo.m_PointLightMaxCount);
|
assert(m_PointLightCount <= m_MetaInfo.m_PointLightMaxCount);
|
||||||
assert(radius >= 0.0f);
|
assert(radius >= 0.0f);
|
||||||
|
|
@ -192,7 +142,7 @@ LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius
|
||||||
{
|
{
|
||||||
if (light->m_Range < 0)
|
if (light->m_Range < 0)
|
||||||
{
|
{
|
||||||
const u8 gen = light->m_Color_ & Light::GEN_MASK;
|
u8 const gen = light->m_Color_ & Light::GEN_MASK;
|
||||||
|
|
||||||
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
||||||
light->m_Range = radius;
|
light->m_Range = radius;
|
||||||
|
|
@ -201,7 +151,7 @@ LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius
|
||||||
|
|
||||||
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
||||||
|
|
||||||
return {Light::TYPE_POINT, gen, Cast<u16>(index)};
|
return {Light::TYPE_POINT, gen, static_cast<u16>(index)};
|
||||||
}
|
}
|
||||||
++light;
|
++light;
|
||||||
}
|
}
|
||||||
|
|
@ -210,7 +160,7 @@ LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius
|
||||||
}
|
}
|
||||||
|
|
||||||
m_Lights.push_back();
|
m_Lights.push_back();
|
||||||
const u16 index = m_PointLightCount;
|
u16 const index = m_PointLightCount;
|
||||||
|
|
||||||
Light *light = &m_Lights[index + m_MetaInfo.m_PointLightOffset];
|
Light *light = &m_Lights[index + m_MetaInfo.m_PointLightOffset];
|
||||||
constexpr u8 gen = 0; // New light
|
constexpr u8 gen = 0; // New light
|
||||||
|
|
@ -231,31 +181,29 @@ LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius
|
||||||
void
|
void
|
||||||
LightManager::Update()
|
LightManager::Update()
|
||||||
{
|
{
|
||||||
const u16 requiredBufferCapacity = eastl::min(Cast<u16>(m_Lights.capacity()), MAX_LIGHTS);
|
u16 const requiredBufferCapacity = eastl::min(static_cast<u16>(m_Lights.capacity()), MAX_LIGHTS);
|
||||||
if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity)
|
if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity)
|
||||||
{
|
{
|
||||||
StorageBuffer newBuffer;
|
m_LightBuffer = m_Device->CreateStorageBuffer(requiredBufferCapacity * sizeof m_Lights[0], "Light Buffer");
|
||||||
newBuffer.Init(m_ResourceManager->m_Device, requiredBufferCapacity * sizeof m_Lights[0], true, "Light Buffer");
|
|
||||||
m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT;
|
m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT;
|
||||||
|
|
||||||
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
m_MetaInfo.m_LightBuffer = m_LightBuffer->GetDeviceAddress();
|
||||||
m_MetaInfo.m_LightBuffer = m_ResourceManager->Commit(&newBuffer);
|
|
||||||
}
|
}
|
||||||
if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT)
|
if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT)
|
||||||
{
|
{
|
||||||
m_ResourceManager->Write(m_MetaInfo.m_LightBuffer, 0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
|
m_LightBuffer->Write(0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LightManager::RemoveLight(const LightHandle handle)
|
LightManager::RemoveLight(LightHandle const handle)
|
||||||
{
|
{
|
||||||
const u8 handleGen = handle.m_Generation;
|
u8 const handleGen = handle.m_Generation;
|
||||||
|
|
||||||
if (handle.m_Type == Light::TYPE_DIRECTIONAL)
|
if (handle.m_Type == Light::TYPE_DIRECTIONAL)
|
||||||
{
|
{
|
||||||
Light *lightSlot = &m_Lights[handle.m_Index];
|
Light *lightSlot = &m_Lights[handle.m_Index];
|
||||||
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
u8 const slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
||||||
if (slotGen > handleGen)
|
if (slotGen > handleGen)
|
||||||
{
|
{
|
||||||
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
||||||
|
|
@ -270,7 +218,7 @@ LightManager::RemoveLight(const LightHandle handle)
|
||||||
if (handle.m_Type == Light::TYPE_POINT)
|
if (handle.m_Type == Light::TYPE_POINT)
|
||||||
{
|
{
|
||||||
Light *lightSlot = &m_Lights[handle.m_Index + m_MetaInfo.m_PointLightOffset];
|
Light *lightSlot = &m_Lights[handle.m_Index + m_MetaInfo.m_PointLightOffset];
|
||||||
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
u8 const slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
||||||
if (slotGen > handleGen)
|
if (slotGen > handleGen)
|
||||||
{
|
{
|
||||||
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: light_manager.h
|
// Aster: light_manager.h
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2025 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
@ -8,7 +8,21 @@
|
||||||
#include "aster/aster.h"
|
#include "aster/aster.h"
|
||||||
|
|
||||||
// TODO: Separate files so you only import handles.
|
// TODO: Separate files so you only import handles.
|
||||||
#include "gpu_resource_manager.h"
|
#include "aster/core/buffer.h"
|
||||||
|
#include "aster/systems/resource.h"
|
||||||
|
|
||||||
|
#include <EASTL/vector.h>
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class RenderingDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace systems
|
||||||
|
{
|
||||||
|
class ResourceManager;
|
||||||
|
class CommitManager;
|
||||||
|
} // namespace systems
|
||||||
|
|
||||||
struct DirectionalLight
|
struct DirectionalLight
|
||||||
{
|
{
|
||||||
|
|
@ -33,26 +47,51 @@ struct LightHandle
|
||||||
u16 m_Index;
|
u16 m_Index;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Light;
|
struct Light
|
||||||
|
{
|
||||||
|
union {
|
||||||
|
vec3 um_Position;
|
||||||
|
vec3 um_Direction;
|
||||||
|
};
|
||||||
|
|
||||||
|
f32 m_Range; // < 0.0 for invalid
|
||||||
|
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
||||||
|
f32 m_Intensity;
|
||||||
|
u32 m_Pad0;
|
||||||
|
u32 m_Pad1;
|
||||||
|
|
||||||
|
constexpr static u32 MAX_GEN = 0x40;
|
||||||
|
constexpr static u32 GEN_MASK = MAX_GEN - 1;
|
||||||
|
|
||||||
|
constexpr static u32 TYPE_MASK = 0xC0;
|
||||||
|
constexpr static u32 TYPE_INVALID = 0x0;
|
||||||
|
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
|
||||||
|
constexpr static u32 TYPE_POINT = 2 << 6;
|
||||||
|
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
|
||||||
|
|
||||||
|
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
|
||||||
|
};
|
||||||
|
|
||||||
struct LightManager
|
struct LightManager
|
||||||
{
|
{
|
||||||
constexpr static u16 MAX_LIGHTS = MaxValue<u16>;
|
constexpr static u16 MAX_LIGHTS = MaxValue<u16>;
|
||||||
|
|
||||||
struct LightMetaInfo
|
struct LightMetaInfo
|
||||||
{
|
{
|
||||||
// The number of directional lights is relatively low (1 - 2) and will almost never change in a scene.
|
// The number of directional lights is relatively low (1 - 2) and will almost never change in a scene.
|
||||||
// We can use that with Offset = 0, and point light at further offsets.
|
// We can use that with Offset = 0, and point light at further offsets.
|
||||||
// This way we don't need to move point lights often.
|
// This way we don't need to move point lights often.
|
||||||
|
|
||||||
BufferHandle m_LightBuffer; // 04 04
|
uptr m_LightBuffer; // 08 08
|
||||||
u16 m_PointLightMaxCount; // 02 06
|
u16 m_PointLightMaxCount; // 02 10
|
||||||
u16 m_PointLightOffset; // 02 08
|
u16 m_PointLightOffset; // 02 12
|
||||||
u16 m_DirectionalLightMaxCount; // 02 10
|
u16 m_DirectionalLightMaxCount; // 02 14
|
||||||
u16 m_UnusedPadding0 = 0; // 02 12
|
u16 m_UnusedPadding0 = 0; // 02 16
|
||||||
};
|
};
|
||||||
|
|
||||||
GpuResourceManager *m_ResourceManager;
|
systems::RenderingDevice *m_Device;
|
||||||
eastl::vector<Light> m_Lights;
|
eastl::vector<Light> m_Lights;
|
||||||
|
Ref<Buffer> m_LightBuffer;
|
||||||
|
|
||||||
// We don't need a Directional Light free list. We will just brute force iterate.
|
// We don't need a Directional Light free list. We will just brute force iterate.
|
||||||
u16 m_DirectionalLightCount;
|
u16 m_DirectionalLightCount;
|
||||||
|
|
@ -66,18 +105,18 @@ struct LightManager
|
||||||
// Using lower bit. Capacity can be directly a multiple of 2
|
// Using lower bit. Capacity can be directly a multiple of 2
|
||||||
// Thus, range is up to MaxValue<u16>
|
// Thus, range is up to MaxValue<u16>
|
||||||
constexpr static u16 UPDATE_REQUIRED_BIT = 1;
|
constexpr static u16 UPDATE_REQUIRED_BIT = 1;
|
||||||
constexpr static u16 CAPACITY_MASK = Cast<u16>(~UPDATE_REQUIRED_BIT);
|
constexpr static u16 CAPACITY_MASK = static_cast<u16>(~UPDATE_REQUIRED_BIT);
|
||||||
|
|
||||||
LightHandle AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity);
|
LightHandle AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity);
|
||||||
LightHandle AddPoint(const vec3 &position, const vec3 &color, f32 radius, f32 intensity);
|
LightHandle AddPoint(const vec3 &position, const vec3 &color, f32 radius, f32 intensity);
|
||||||
void Update();
|
void Update();
|
||||||
void RemoveLight(LightHandle handle);
|
void RemoveLight(LightHandle handle);
|
||||||
|
|
||||||
explicit LightManager(GpuResourceManager *resourceManager);
|
~LightManager() = default;
|
||||||
~LightManager();
|
|
||||||
|
|
||||||
LightManager(LightManager &&other) noexcept;
|
explicit LightManager(systems::RenderingDevice &device);
|
||||||
LightManager &operator=(LightManager &&other) noexcept;
|
LightManager(LightManager &&other) noexcept = default;
|
||||||
|
LightManager &operator=(LightManager &&other) noexcept = default;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(LightManager);
|
DISALLOW_COPY_AND_ASSIGN(LightManager);
|
||||||
};
|
};
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue