Compare commits
9 Commits
1a8f113323
...
31efa504a3
| Author | SHA1 | Date |
|---|---|---|
|
|
31efa504a3 | |
|
|
7f8e18ff97 | |
|
|
60a7609963 | |
|
|
131a55c868 | |
|
|
688b34c926 | |
|
|
6e8e2c6c80 | |
|
|
3032b9a43d | |
|
|
8a4138eec3 | |
|
|
cd8ab16ad2 |
196
.clang-format
196
.clang-format
|
|
@ -3,14 +3,86 @@
|
||||||
# chosen value in case the base style changes (last sync: Clang 14.0).
|
# chosen value in case the base style changes (last sync: Clang 14.0).
|
||||||
---
|
---
|
||||||
### General config, applies to all languages ###
|
### General config, applies to all languages ###
|
||||||
BasedOnStyle: Microsoft
|
BasedOnStyle: LLVM
|
||||||
#AccessModifierOffset: -2
|
AccessModifierOffset: -4
|
||||||
AlwaysBreakAfterReturnType: AllDefinitions
|
AlignAfterOpenBracket: DontAlign
|
||||||
BreakConstructorInitializers: BeforeComma
|
# AlignArrayOfStructures: None
|
||||||
#ColumnLimit: 0
|
# AlignConsecutiveMacros: None
|
||||||
#ConstructorInitializerIndentWidth: 4
|
# AlignConsecutiveAssignments: None
|
||||||
#ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
# AlignConsecutiveBitFields: None
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
# AlignConsecutiveDeclarations: None
|
||||||
|
# AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: DontAlign
|
||||||
|
AlignTrailingComments: false
|
||||||
|
# AllowAllArgumentsOnNextLine: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: false
|
||||||
|
# AllowShortEnumsOnASingleLine: true
|
||||||
|
# AllowShortBlocksOnASingleLine: Never
|
||||||
|
# AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
# AllowShortFunctionsOnASingleLine: All
|
||||||
|
# AllowShortLambdasOnASingleLine: All
|
||||||
|
# AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
# AllowShortLoopsOnASingleLine: false
|
||||||
|
# AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
# AlwaysBreakAfterReturnType: None
|
||||||
|
# AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
# AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
|
# AttributeMacros:
|
||||||
|
# - __capability
|
||||||
|
# BinPackArguments: true
|
||||||
|
# BinPackParameters: true
|
||||||
|
# BraceWrapping:
|
||||||
|
# AfterCaseLabel: false
|
||||||
|
# AfterClass: false
|
||||||
|
# AfterControlStatement: Never
|
||||||
|
# AfterEnum: false
|
||||||
|
# AfterFunction: false
|
||||||
|
# AfterNamespace: false
|
||||||
|
# AfterObjCDeclaration: false
|
||||||
|
# AfterStruct: false
|
||||||
|
# AfterUnion: false
|
||||||
|
# AfterExternBlock: false
|
||||||
|
# BeforeCatch: false
|
||||||
|
# BeforeElse: false
|
||||||
|
# BeforeLambdaBody: false
|
||||||
|
# BeforeWhile: false
|
||||||
|
# IndentBraces: false
|
||||||
|
# SplitEmptyFunction: true
|
||||||
|
# SplitEmptyRecord: true
|
||||||
|
# SplitEmptyNamespace: true
|
||||||
|
# BreakBeforeBinaryOperators: None
|
||||||
|
# BreakBeforeConceptDeclarations: true
|
||||||
|
# BreakBeforeBraces: Attach
|
||||||
|
# BreakBeforeInheritanceComma: false
|
||||||
|
# BreakInheritanceList: BeforeColon
|
||||||
|
# BreakBeforeTernaryOperators: true
|
||||||
|
# BreakConstructorInitializersBeforeComma: false
|
||||||
|
BreakConstructorInitializers: AfterColon
|
||||||
|
# BreakStringLiterals: true
|
||||||
|
ColumnLimit: 0
|
||||||
|
# CommentPragmas: '^ IWYU pragma:'
|
||||||
|
# QualifierAlignment: Leave
|
||||||
|
# CompactNamespaces: false
|
||||||
|
ConstructorInitializerIndentWidth: 8
|
||||||
|
ContinuationIndentWidth: 8
|
||||||
|
Cpp11BracedListStyle: false
|
||||||
|
# DeriveLineEnding: true
|
||||||
|
# DerivePointerAlignment: false
|
||||||
|
# DisableFormat: false
|
||||||
|
# EmptyLineAfterAccessModifier: Never
|
||||||
|
# EmptyLineBeforeAccessModifier: LogicalBlock
|
||||||
|
# ExperimentalAutoDetectBinPacking: false
|
||||||
|
# PackConstructorInitializers: BinPack
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
# AllowAllConstructorInitializersOnNextLine: true
|
||||||
|
# FixNamespaceComments: true
|
||||||
|
# ForEachMacros:
|
||||||
|
# - foreach
|
||||||
|
# - Q_FOREACH
|
||||||
|
# - BOOST_FOREACH
|
||||||
|
# IfMacros:
|
||||||
|
# - KJ_IF_MAYBE
|
||||||
|
# IncludeBlocks: Preserve
|
||||||
IncludeCategories:
|
IncludeCategories:
|
||||||
- Regex: '".*"'
|
- Regex: '".*"'
|
||||||
Priority: 1
|
Priority: 1
|
||||||
|
|
@ -18,10 +90,110 @@ IncludeCategories:
|
||||||
Priority: 2
|
Priority: 2
|
||||||
- Regex: '^<.*'
|
- Regex: '^<.*'
|
||||||
Priority: 3
|
Priority: 3
|
||||||
#IndentCaseLabels: true
|
# IncludeIsMainRegex: '(Test)?$'
|
||||||
|
# IncludeIsMainSourceRegex: ''
|
||||||
|
# IndentAccessModifiers: false
|
||||||
|
IndentCaseLabels: true
|
||||||
|
# IndentCaseBlocks: false
|
||||||
|
# IndentGotoLabels: true
|
||||||
|
# IndentPPDirectives: None
|
||||||
|
# IndentExternBlock: AfterExternBlock
|
||||||
|
# IndentRequires: false
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
#KeepEmptyLinesAtTheStartOfBlocks: false
|
# IndentWrappedFunctionNames: false
|
||||||
PenaltyReturnTypeOnItsOwnLine: 0
|
# InsertTrailingCommas: None
|
||||||
|
# JavaScriptQuotes: Leave
|
||||||
|
# JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
# LambdaBodyIndentation: Signature
|
||||||
|
# MacroBlockBegin: ''
|
||||||
|
# MacroBlockEnd: ''
|
||||||
|
# MaxEmptyLinesToKeep: 1
|
||||||
|
# NamespaceIndentation: None
|
||||||
|
# PenaltyBreakAssignment: 2
|
||||||
|
# PenaltyBreakBeforeFirstCallParameter: 19
|
||||||
|
# PenaltyBreakComment: 300
|
||||||
|
# PenaltyBreakFirstLessLess: 120
|
||||||
|
# PenaltyBreakOpenParenthesis: 0
|
||||||
|
# PenaltyBreakString: 1000
|
||||||
|
# PenaltyBreakTemplateDeclaration: 10
|
||||||
|
# PenaltyExcessCharacter: 1000000
|
||||||
|
# PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
|
# PenaltyIndentedWhitespace: 0
|
||||||
|
# PointerAlignment: Right
|
||||||
|
# PPIndentWidth: -1
|
||||||
|
# ReferenceAlignment: Pointer
|
||||||
|
# ReflowComments: true
|
||||||
|
# RemoveBracesLLVM: false
|
||||||
|
# SeparateDefinitionBlocks: Leave
|
||||||
|
# ShortNamespaceLines: 1
|
||||||
|
# SortIncludes: CaseSensitive
|
||||||
|
# SortJavaStaticImport: Before
|
||||||
|
# SortUsingDeclarations: true
|
||||||
|
# SpaceAfterCStyleCast: false
|
||||||
|
# SpaceAfterLogicalNot: false
|
||||||
|
# SpaceAfterTemplateKeyword: true
|
||||||
|
# SpaceBeforeAssignmentOperators: true
|
||||||
|
# SpaceBeforeCaseColon: false
|
||||||
|
# SpaceBeforeCpp11BracedList: false
|
||||||
|
# SpaceBeforeCtorInitializerColon: true
|
||||||
|
# SpaceBeforeInheritanceColon: true
|
||||||
|
# SpaceBeforeParens: ControlStatements
|
||||||
|
# SpaceBeforeParensOptions:
|
||||||
|
# AfterControlStatements: true
|
||||||
|
# AfterForeachMacros: true
|
||||||
|
# AfterFunctionDefinitionName: false
|
||||||
|
# AfterFunctionDeclarationName: false
|
||||||
|
# AfterIfMacros: true
|
||||||
|
# AfterOverloadedOperator: false
|
||||||
|
# BeforeNonEmptyParentheses: false
|
||||||
|
# SpaceAroundPointerQualifiers: Default
|
||||||
|
# SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
# SpaceInEmptyBlock: false
|
||||||
|
# SpaceInEmptyParentheses: false
|
||||||
|
# SpacesBeforeTrailingComments: 1
|
||||||
|
# SpacesInAngles: Never
|
||||||
|
# SpacesInConditionalStatement: false
|
||||||
|
# SpacesInContainerLiterals: true
|
||||||
|
# SpacesInCStyleCastParentheses: false
|
||||||
|
## Godot TODO: We'll want to use a min of 1, but we need to see how to fix
|
||||||
|
## our comment capitalization at the same time.
|
||||||
|
SpacesInLineCommentPrefix:
|
||||||
|
Minimum: 0
|
||||||
|
Maximum: -1
|
||||||
|
# SpacesInParentheses: false
|
||||||
|
# SpacesInSquareBrackets: false
|
||||||
|
# SpaceBeforeSquareBrackets: false
|
||||||
|
# BitFieldColonSpacing: Both
|
||||||
|
# StatementAttributeLikeMacros:
|
||||||
|
# - Q_EMIT
|
||||||
|
# StatementMacros:
|
||||||
|
# - Q_UNUSED
|
||||||
|
# - QT_REQUIRE_VERSION
|
||||||
TabWidth: 4
|
TabWidth: 4
|
||||||
|
# UseCRLF: false
|
||||||
|
UseTab: Always
|
||||||
|
# WhitespaceSensitiveMacros:
|
||||||
|
# - STRINGIZE
|
||||||
|
# - PP_STRINGIZE
|
||||||
|
# - BOOST_PP_STRINGIZE
|
||||||
|
# - NS_SWIFT_NAME
|
||||||
|
# - CF_SWIFT_NAME
|
||||||
|
---
|
||||||
|
### C++ specific config ###
|
||||||
Language: Cpp
|
Language: Cpp
|
||||||
Standard: c++20
|
Standard: c++17
|
||||||
|
---
|
||||||
|
### ObjC specific config ###
|
||||||
|
Language: ObjC
|
||||||
|
# ObjCBinPackProtocolList: Auto
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
# ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
# ObjCSpaceAfterProperty: false
|
||||||
|
# ObjCSpaceBeforeProtocolList: true
|
||||||
|
---
|
||||||
|
### Java specific config ###
|
||||||
|
Language: Java
|
||||||
|
# BreakAfterJavaFieldAnnotations: false
|
||||||
|
JavaImportGroups: ['org.godotengine', 'android', 'androidx', 'com.android', 'com.google', 'java', 'javax']
|
||||||
|
...
|
||||||
|
|
|
||||||
45
.clang-tidy
45
.clang-tidy
|
|
@ -1,45 +0,0 @@
|
||||||
---
|
|
||||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,-*,cppcoreguidelines-pro-type-member-init,modernize-redundant-void-arg,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-nullptr,readability-braces-around-statements,readability-redundant-member-init'
|
|
||||||
WarningsAsErrors: ''
|
|
||||||
HeaderFilterRegex: ''
|
|
||||||
FormatStyle: none
|
|
||||||
CheckOptions:
|
|
||||||
- key: cert-dcl16-c.NewSuffixes
|
|
||||||
value: 'L;LL;LU;LLU'
|
|
||||||
- key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
|
|
||||||
value: '0'
|
|
||||||
- key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
|
|
||||||
value: '1'
|
|
||||||
- key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
|
||||||
value: '1'
|
|
||||||
- key: cppcoreguidelines-pro-type-member-init.IgnoreArrays
|
|
||||||
value: '1'
|
|
||||||
- key: cppcoreguidelines-pro-type-member-init.UseAssignment
|
|
||||||
value: '1'
|
|
||||||
- key: google-readability-function-size.StatementThreshold
|
|
||||||
value: '800'
|
|
||||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
|
||||||
value: '10'
|
|
||||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
|
||||||
value: '2'
|
|
||||||
- key: modernize-loop-convert.MaxCopySize
|
|
||||||
value: '16'
|
|
||||||
- key: modernize-loop-convert.MinConfidence
|
|
||||||
value: reasonable
|
|
||||||
- key: modernize-loop-convert.NamingStyle
|
|
||||||
value: CamelCase
|
|
||||||
- key: modernize-pass-by-value.IncludeStyle
|
|
||||||
value: microsoft
|
|
||||||
- key: modernize-replace-auto-ptr.IncludeStyle
|
|
||||||
value: microsoft
|
|
||||||
- key: modernize-use-bool-literals.IgnoreMacros
|
|
||||||
value: '0'
|
|
||||||
- key: modernize-use-default-member-init.IgnoreMacros
|
|
||||||
value: '0'
|
|
||||||
- key: modernize-use-default-member-init.UseAssignment
|
|
||||||
value: '1'
|
|
||||||
- key: modernize-use-nullptr.NullMacros
|
|
||||||
value: 'NULL'
|
|
||||||
- key: readability-braces-around-statements.ShortStatementLines
|
|
||||||
value: '0'
|
|
||||||
...
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
build/
|
build/
|
||||||
.vs/
|
|
||||||
|
|
@ -4,19 +4,10 @@ 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)
|
set(CMAKE_CXX_FLAGS -Wall)
|
||||||
set(CMAKE_CXX_FLAGS "/W4 /GR- /Zi")
|
|
||||||
add_compile_definitions(_HAS_EXCEPTIONS=1)
|
|
||||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
|
|
||||||
else ()
|
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -g -fno-rtti -fno-exceptions")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include(add_shader.cmake)
|
add_subdirectory("aster_core")
|
||||||
|
|
||||||
add_subdirectory("aster")
|
|
||||||
add_subdirectory("samples")
|
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,6 @@
|
||||||
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
||||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "windows",
|
|
||||||
"generator": "Ninja",
|
|
||||||
"binaryDir": "${sourceDir}/build",
|
|
||||||
"cacheVariables": {
|
|
||||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
|
||||||
"CMAKE_MAKE_PROGRAM": "ninja",
|
|
||||||
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
function(add_shader TARGET SHADER)
|
|
||||||
find_package(Vulkan REQUIRED COMPONENTS dxc)
|
|
||||||
|
|
||||||
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-output-path ${CMAKE_CURRENT_BINARY_DIR}/${SHADER}.spv)
|
|
||||||
|
|
||||||
get_filename_component(current-output-dir ${current-output-path} DIRECTORY)
|
|
||||||
file(MAKE_DIRECTORY ${current-output-dir})
|
|
||||||
|
|
||||||
if (Vulkan_dxc_exe_FOUND AND ${shader-ext} STREQUAL ".hlsl")
|
|
||||||
message("Marked as hlsl file. ${current-output-path}")
|
|
||||||
add_custom_command(
|
|
||||||
OUTPUT ${current-output-path}
|
|
||||||
COMMAND Vulkan::dxc_exe -spirv -T "${shader-type}_6_0" -E main ${current-shader-path} -Fo ${current-output-path}
|
|
||||||
DEPENDS ${current-shader-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 -o ${current-output-path} ${current-shader-path}
|
|
||||||
DEPENDS ${current-shader-path}
|
|
||||||
IMPLICIT_DEPENDS CXX ${current-shader-path}
|
|
||||||
VERBATIM)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Make sure our build depends on this output.
|
|
||||||
set_source_files_properties(${current-output-path} PROPERTIES GENERATED TRUE)
|
|
||||||
target_sources(${TARGET} PRIVATE ${current-output-path})
|
|
||||||
endfunction(add_shader)
|
|
||||||
108
aster/buffer.cpp
108
aster/buffer.cpp
|
|
@ -1,108 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "buffer.h"
|
|
||||||
|
|
||||||
#include <ranges>
|
|
||||||
|
|
||||||
void
|
|
||||||
Buffer::Destroy(const Device *device)
|
|
||||||
{
|
|
||||||
if (!m_Buffer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vmaDestroyBuffer(device->m_Allocator, m_Buffer, m_Allocation);
|
|
||||||
m_Buffer = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Buffer::Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name)
|
|
||||||
{
|
|
||||||
assert(size <= SIZE_MASK);
|
|
||||||
|
|
||||||
vk::BufferCreateInfo bufferCreateInfo = {
|
|
||||||
.size = size,
|
|
||||||
.usage = bufferUsage,
|
|
||||||
.sharingMode = vk::SharingMode::eExclusive,
|
|
||||||
};
|
|
||||||
const VmaAllocationCreateInfo allocationCreateInfo = {
|
|
||||||
.flags = allocationFlags,
|
|
||||||
.usage = memoryUsage,
|
|
||||||
};
|
|
||||||
|
|
||||||
VkBuffer buffer;
|
|
||||||
VmaAllocation allocation;
|
|
||||||
VmaAllocationInfo allocationInfo;
|
|
||||||
auto result = Cast<vk::Result>(vmaCreateBuffer(device->m_Allocator, Recast<VkBufferCreateInfo *>(&bufferCreateInfo),
|
|
||||||
&allocationCreateInfo, &buffer, &allocation, &allocationInfo));
|
|
||||||
ERROR_IF(Failed(result), "Could not allocate buffer. Cause: {}", result) THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_Buffer = buffer;
|
|
||||||
m_Size = size & SIZE_MASK;
|
|
||||||
m_Allocation = allocation;
|
|
||||||
m_Mapped = allocationInfo.pMappedData;
|
|
||||||
|
|
||||||
vk::MemoryPropertyFlags memoryPropertyFlags;
|
|
||||||
vmaGetAllocationMemoryProperties(device->m_Allocator, allocation,
|
|
||||||
Recast<VkMemoryPropertyFlags *>(&memoryPropertyFlags));
|
|
||||||
if (memoryPropertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)
|
|
||||||
{
|
|
||||||
m_Size |= HOST_ACCESSIBLE_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
device->SetName(m_Buffer, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Buffer::Write(const Device *device, usize offset, usize size, void *data)
|
|
||||||
{
|
|
||||||
assert(IsHostVisible());
|
|
||||||
|
|
||||||
if (!IsMapped())
|
|
||||||
{
|
|
||||||
auto result = Cast<vk::Result>(vmaMapMemory(device->m_Allocator, m_Allocation, &m_Mapped));
|
|
||||||
ERROR_IF(Failed(result), "Memory mapping failed. Cause: {}", result);
|
|
||||||
if (!Failed(result))
|
|
||||||
{
|
|
||||||
memcpy(m_Mapped, data, size);
|
|
||||||
|
|
||||||
vmaUnmapMemory(device->m_Allocator, m_Allocation);
|
|
||||||
m_Mapped = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memcpy(m_Mapped, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Debug this.
|
|
||||||
// auto result = Cast<vk::Result>(vmaCopyMemoryToAllocation(device->m_Allocator, &data, m_Allocation, 0, 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
|
|
||||||
VertexBuffer::Init(const Device *device, usize size, cstr name)
|
|
||||||
{
|
|
||||||
Allocate(device, size, vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst,
|
|
||||||
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, VMA_MEMORY_USAGE_AUTO, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,68 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: buffer.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "device.h"
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
struct Device;
|
|
||||||
|
|
||||||
struct Buffer
|
|
||||||
{
|
|
||||||
vk::Buffer m_Buffer = nullptr;
|
|
||||||
VmaAllocation m_Allocation = nullptr;
|
|
||||||
void *m_Mapped = nullptr;
|
|
||||||
|
|
||||||
[[nodiscard]] usize GetSize() const;
|
|
||||||
[[nodiscard]] bool IsHostVisible() const;
|
|
||||||
[[nodiscard]] bool IsMapped() const;
|
|
||||||
|
|
||||||
void Destroy(const Device *device);
|
|
||||||
void Write(const Device *device, usize offset, usize size, void *data);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void Allocate(const Device *device, usize size, vk::BufferUsageFlags bufferUsage,
|
|
||||||
VmaAllocationCreateFlags allocationFlags, VmaMemoryUsage memoryUsage, cstr name);
|
|
||||||
|
|
||||||
usize m_Size = 0;
|
|
||||||
|
|
||||||
constexpr static usize HOST_ACCESSIBLE_BIT = 1llu << 63;
|
|
||||||
constexpr static usize SIZE_MASK = ~HOST_ACCESSIBLE_BIT;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UniformBuffer : Buffer
|
|
||||||
{
|
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexBuffer : Buffer
|
|
||||||
{
|
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
|
||||||
void Write(const Device *device, void *data, usize size, usize offset) const = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StagingBuffer : Buffer
|
|
||||||
{
|
|
||||||
void Init(const Device *device, usize size, cstr name = nullptr);
|
|
||||||
};
|
|
||||||
|
|
||||||
inline usize
|
|
||||||
Buffer::GetSize() const
|
|
||||||
{
|
|
||||||
return m_Size & SIZE_MASK;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
Buffer::IsHostVisible() const
|
|
||||||
{
|
|
||||||
return m_Size & HOST_ACCESSIBLE_BIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
Buffer::IsMapped() const
|
|
||||||
{
|
|
||||||
return m_Mapped;
|
|
||||||
}
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: constants.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vulkan/vulkan_core.h>
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
using c8 = char;
|
|
||||||
using u8 = uint8_t;
|
|
||||||
using u16 = uint16_t;
|
|
||||||
using u32 = uint32_t;
|
|
||||||
using u64 = uint64_t;
|
|
||||||
using i8 = int8_t;
|
|
||||||
using i16 = int16_t;
|
|
||||||
using i32 = int32_t;
|
|
||||||
using i64 = int64_t;
|
|
||||||
using f32 = float;
|
|
||||||
using f64 = double;
|
|
||||||
using f128 = long double;
|
|
||||||
using b8 = bool;
|
|
||||||
using b32 = u32;
|
|
||||||
using usize = size_t;
|
|
||||||
using p64 = intptr_t;
|
|
||||||
using cstr = const char *;
|
|
||||||
|
|
||||||
namespace ansi_color
|
|
||||||
{
|
|
||||||
constexpr auto Black = "\u001b[30m";
|
|
||||||
constexpr auto Red = "\u001b[31m";
|
|
||||||
constexpr auto Green = "\u001b[32m";
|
|
||||||
constexpr auto Yellow = "\u001b[33m";
|
|
||||||
constexpr auto Blue = "\u001b[34m";
|
|
||||||
constexpr auto Magenta = "\u001b[35m";
|
|
||||||
constexpr auto Cyan = "\u001b[36m";
|
|
||||||
constexpr auto White = "\u001b[37m";
|
|
||||||
constexpr auto Reset = "\u001b[0m";
|
|
||||||
} // 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
|
|
||||||
operator""_deg(long double degrees)
|
|
||||||
{
|
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr f32
|
|
||||||
operator""_deg(unsigned long long int degrees)
|
|
||||||
{
|
|
||||||
return glm::radians<f32>(Cast<f32>(degrees));
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr f32
|
|
||||||
operator""_rad(long double rads)
|
|
||||||
{
|
|
||||||
return Cast<f32>(rads);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr f32
|
|
||||||
operator""_rad(unsigned long long int rads)
|
|
||||||
{
|
|
||||||
return Cast<f32>(rads);
|
|
||||||
}
|
|
||||||
|
|
||||||
using glm::ivec2;
|
|
||||||
using glm::ivec3;
|
|
||||||
using glm::ivec4;
|
|
||||||
using glm::vec2;
|
|
||||||
using glm::vec3;
|
|
||||||
using glm::vec4;
|
|
||||||
|
|
||||||
using glm::mat2;
|
|
||||||
using glm::mat3;
|
|
||||||
using glm::mat4;
|
|
||||||
|
|
||||||
constexpr auto *PROJECT_NAME = "Aster";
|
|
||||||
|
|
||||||
struct Version
|
|
||||||
{
|
|
||||||
u8 m_Major;
|
|
||||||
u8 m_Minor;
|
|
||||||
u8 m_Patch;
|
|
||||||
|
|
||||||
[[nodiscard]] u32
|
|
||||||
GetVkVersion() const
|
|
||||||
{
|
|
||||||
return VK_MAKE_API_VERSION(0, m_Major, m_Minor, m_Patch);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr Version VERSION = {
|
|
||||||
.m_Major = 0,
|
|
||||||
.m_Minor = 1,
|
|
||||||
.m_Patch = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T MaxValue = std::numeric_limits<T>::max();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T MinValue = std::numeric_limits<T>::min();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T LowestValue = std::numeric_limits<T>::lowest();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T ErrEpsilon = std::numeric_limits<T>::epsilon();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T PositiveInf = std::numeric_limits<T>::infinity();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T NegativeInf = -std::numeric_limits<T>::infinity();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T Qnan = std::numeric_limits<T>::quiet_NaN();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr T Snan = std::numeric_limits<T>::signalling_NaN();
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: context.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "context.h"
|
|
||||||
#include <EASTL/array.h>
|
|
||||||
#include <EASTL/fixed_vector.h>
|
|
||||||
|
|
||||||
constexpr eastl::array VALIDATION_LAYERS = {
|
|
||||||
"VK_LAYER_KHRONOS_validation",
|
|
||||||
};
|
|
||||||
|
|
||||||
VKAPI_ATTR b32 VKAPI_CALL
|
|
||||||
DebugCallback(const VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
||||||
const VkDebugUtilsMessageTypeFlagsEXT messageType,
|
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT *callbackData, [[maybe_unused]] void *userData)
|
|
||||||
{
|
|
||||||
using Severity = vk::DebugUtilsMessageSeverityFlagsEXT;
|
|
||||||
using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT;
|
|
||||||
using MessageType = vk::DebugUtilsMessageTypeFlagsEXT;
|
|
||||||
using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT;
|
|
||||||
|
|
||||||
const auto severity = Severity(messageSeverity);
|
|
||||||
|
|
||||||
if (MessageType(messageType) & MessageTypeBits::eValidation)
|
|
||||||
{
|
|
||||||
if (severity & SeverityBits::eError)
|
|
||||||
ERROR("{}", callbackData->pMessage);
|
|
||||||
if (severity & SeverityBits::eWarning)
|
|
||||||
WARN("{}", callbackData->pMessage);
|
|
||||||
if (severity & SeverityBits::eInfo)
|
|
||||||
INFO("{}", callbackData->pMessage);
|
|
||||||
if (severity & SeverityBits::eVerbose)
|
|
||||||
VERBOSE("{}", callbackData->pMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::Context(const cstr appName, const Version version, bool enableValidation)
|
|
||||||
{
|
|
||||||
INFO_IF(enableValidation, "Validation Layers enabled");
|
|
||||||
|
|
||||||
if (!glfwInit())
|
|
||||||
{
|
|
||||||
const char *error = nullptr;
|
|
||||||
const auto code = glfwGetError(&error);
|
|
||||||
ERROR("GLFW Init failed. Cause: ({}) {}", code, error)
|
|
||||||
THEN_ABORT(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Get/Check API Version
|
|
||||||
|
|
||||||
// Creating Instance
|
|
||||||
const vk::ApplicationInfo appInfo = {
|
|
||||||
.pApplicationName = appName,
|
|
||||||
.applicationVersion = version.GetVkVersion(),
|
|
||||||
.pEngineName = PROJECT_NAME,
|
|
||||||
.engineVersion = version.GetVkVersion(),
|
|
||||||
.apiVersion = ASTER_API_VERSION,
|
|
||||||
};
|
|
||||||
|
|
||||||
const vk::DebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {
|
|
||||||
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
|
||||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo,
|
|
||||||
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
|
||||||
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance |
|
|
||||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation,
|
|
||||||
.pfnUserCallback = DebugCallback,
|
|
||||||
.pUserData = nullptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 glfwExtensionCount = 0;
|
|
||||||
cstr *glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
|
||||||
eastl::fixed_vector<cstr, 3> instanceExtensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
|
|
||||||
if (enableValidation)
|
|
||||||
{
|
|
||||||
instanceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
const vk::DynamicLoader dl;
|
|
||||||
// ReSharper disable once CppInconsistentNaming
|
|
||||||
const auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
|
||||||
|
|
||||||
const auto instanceCreateInfo = vk::InstanceCreateInfo{
|
|
||||||
.pNext = enableValidation ? &debugUtilsMessengerCreateInfo : nullptr,
|
|
||||||
.pApplicationInfo = &appInfo,
|
|
||||||
.enabledLayerCount = enableValidation ? Cast<u32>(VALIDATION_LAYERS.size()) : 0,
|
|
||||||
.ppEnabledLayerNames = enableValidation ? VALIDATION_LAYERS.data() : nullptr,
|
|
||||||
.enabledExtensionCount = Cast<u32>(instanceExtensions.size()),
|
|
||||||
.ppEnabledExtensionNames = instanceExtensions.data(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// May throw. Irrecoverable.
|
|
||||||
vk::Result result = createInstance(&instanceCreateInfo, nullptr, &m_Instance);
|
|
||||||
ERROR_IF(result, "Instance creation failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result)
|
|
||||||
ELSE_DEBUG("Instance Created.");
|
|
||||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_Instance);
|
|
||||||
|
|
||||||
// Debug Messenger
|
|
||||||
if (enableValidation)
|
|
||||||
{
|
|
||||||
result = m_Instance.createDebugUtilsMessengerEXT(&debugUtilsMessengerCreateInfo, nullptr, &m_DebugMessenger);
|
|
||||||
ERROR_IF(result, "Debug Messenger creation failed. Cause: {}", result) // Non-critical. Continue.
|
|
||||||
ELSE_DEBUG("Debug Messenger Created.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context::~Context()
|
|
||||||
{
|
|
||||||
if (m_DebugMessenger)
|
|
||||||
{
|
|
||||||
m_Instance.destroy(m_DebugMessenger, nullptr);
|
|
||||||
DEBUG("Debug Messenger destroyed");
|
|
||||||
}
|
|
||||||
m_Instance.destroy(nullptr);
|
|
||||||
DEBUG("Instance destroyed");
|
|
||||||
|
|
||||||
glfwTerminate();
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: context.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class Context
|
|
||||||
*
|
|
||||||
* @brief Vulkan context to handle device initialization logic.
|
|
||||||
*
|
|
||||||
* Handles the required hardware interactions.
|
|
||||||
*/
|
|
||||||
struct Context final
|
|
||||||
{
|
|
||||||
// Members
|
|
||||||
vk::Instance m_Instance = nullptr;
|
|
||||||
vk::DebugUtilsMessengerEXT m_DebugMessenger = nullptr;
|
|
||||||
|
|
||||||
// Ctor/Dtor
|
|
||||||
Context(cstr appName, Version version, bool enableValidation = true);
|
|
||||||
~Context();
|
|
||||||
};
|
|
||||||
111
aster/device.cpp
111
aster/device.cpp
|
|
@ -1,111 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: device.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "device.h"
|
|
||||||
|
|
||||||
#include "context.h"
|
|
||||||
#include "physical_device.h"
|
|
||||||
#include "queue_allocation.h"
|
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
|
||||||
#include <EASTL/fixed_vector.h>
|
|
||||||
|
|
||||||
// TODO: This will need to be flexible for devices that don't support some of the extensions.
|
|
||||||
constexpr eastl::array DEVICE_EXTENSIONS = {
|
|
||||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
||||||
};
|
|
||||||
|
|
||||||
Device::Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name)
|
|
||||||
: m_Name(std::move(name))
|
|
||||||
, m_PhysicalDevice(physicalDevice->m_PhysicalDevice)
|
|
||||||
{
|
|
||||||
// Shouldn't have more than 4 deviceQueueFamilies in use anyway. Else we can heap
|
|
||||||
eastl::fixed_vector<vk::DeviceQueueCreateInfo, 4> deviceQueueCreateInfos;
|
|
||||||
deviceQueueCreateInfos.reserve(queueAllocations.size());
|
|
||||||
|
|
||||||
u32 numPriorities = 0;
|
|
||||||
for (auto [_, count] : queueAllocations)
|
|
||||||
{
|
|
||||||
numPriorities = std::max(count, numPriorities);
|
|
||||||
}
|
|
||||||
// Shouldn't have more than 4 queues either.
|
|
||||||
eastl::fixed_vector<f32, 4> priorities(numPriorities, 1.0f);
|
|
||||||
|
|
||||||
for (auto [family, count] : queueAllocations)
|
|
||||||
{
|
|
||||||
deviceQueueCreateInfos.push_back({
|
|
||||||
.queueFamilyIndex = family,
|
|
||||||
.queueCount = count,
|
|
||||||
.pQueuePriorities = priorities.data(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::PhysicalDeviceFeatures *deviceFeatures = &enabledFeatures->m_Vulkan10Features;
|
|
||||||
vk::PhysicalDeviceVulkan11Features *vulkan11Features = &enabledFeatures->m_Vulkan11Features;
|
|
||||||
vk::PhysicalDeviceVulkan12Features *vulkan12Features = &enabledFeatures->m_Vulkan12Features;
|
|
||||||
vk::PhysicalDeviceVulkan13Features *vulkan13Features = &enabledFeatures->m_Vulkan13Features;
|
|
||||||
|
|
||||||
vulkan11Features->pNext = vulkan12Features;
|
|
||||||
vulkan12Features->pNext = vulkan13Features;
|
|
||||||
|
|
||||||
vk::DeviceCreateInfo deviceCreateInfo = {
|
|
||||||
.pNext = vulkan11Features,
|
|
||||||
.queueCreateInfoCount = Cast<u32>(deviceQueueCreateInfos.size()),
|
|
||||||
.pQueueCreateInfos = deviceQueueCreateInfos.data(),
|
|
||||||
.enabledExtensionCount = Cast<u32>(DEVICE_EXTENSIONS.size()),
|
|
||||||
.ppEnabledExtensionNames = DEVICE_EXTENSIONS.data(),
|
|
||||||
.pEnabledFeatures = deviceFeatures,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Result result = m_PhysicalDevice.createDevice(&deviceCreateInfo, nullptr, &m_Device);
|
|
||||||
ERROR_IF(Failed(result), "Could not initialize Vulkan Device. Cause: {}", result)
|
|
||||||
THEN_ABORT(result)
|
|
||||||
ELSE_DEBUG("{} ({}) Initialized.", m_Name, physicalDevice->m_DeviceProperties.deviceName.data());
|
|
||||||
|
|
||||||
SetName(m_Device, m_Name.data());
|
|
||||||
|
|
||||||
VmaVulkanFunctions vmaVulkanFunctions = {
|
|
||||||
.vkGetInstanceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetInstanceProcAddr,
|
|
||||||
.vkGetDeviceProcAddr = vk::defaultDispatchLoaderDynamic.vkGetDeviceProcAddr,
|
|
||||||
};
|
|
||||||
|
|
||||||
const VmaAllocatorCreateInfo allocatorCreateInfo = {
|
|
||||||
.physicalDevice = m_PhysicalDevice,
|
|
||||||
.device = m_Device,
|
|
||||||
.pVulkanFunctions = &vmaVulkanFunctions,
|
|
||||||
.instance = context->m_Instance,
|
|
||||||
.vulkanApiVersion = ASTER_API_VERSION,
|
|
||||||
};
|
|
||||||
|
|
||||||
result = Cast<vk::Result>(vmaCreateAllocator(&allocatorCreateInfo, &m_Allocator));
|
|
||||||
ERROR_IF(Failed(result), "Memory allocator creation failed. Cause: {}", result)
|
|
||||||
DO(m_Device.destroy(nullptr))
|
|
||||||
THEN_ABORT(result)
|
|
||||||
ELSE_VERBOSE("Memory Allocator Created");
|
|
||||||
|
|
||||||
DEBUG("Created '{}' Successfully", m_Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::~Device()
|
|
||||||
{
|
|
||||||
if (m_Allocator)
|
|
||||||
{
|
|
||||||
vmaDestroyAllocator(m_Allocator);
|
|
||||||
m_Allocator = nullptr;
|
|
||||||
DEBUG("Memory Allocator Destroyed");
|
|
||||||
}
|
|
||||||
m_Device.destroy(nullptr);
|
|
||||||
DEBUG("Device '{}' Destroyed", m_Name);
|
|
||||||
m_PhysicalDevice = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Queue
|
|
||||||
Device::GetQueue(const u32 familyIndex, const u32 queueIndex) const
|
|
||||||
{
|
|
||||||
vk::Queue queue;
|
|
||||||
m_Device.getQueue(familyIndex, queueIndex, &queue);
|
|
||||||
return queue;
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: device.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#include <EASTL/vector.h>
|
|
||||||
|
|
||||||
struct QueueAllocation;
|
|
||||||
struct Context;
|
|
||||||
struct PhysicalDevice;
|
|
||||||
|
|
||||||
struct Features
|
|
||||||
{
|
|
||||||
vk::PhysicalDeviceFeatures m_Vulkan10Features;
|
|
||||||
vk::PhysicalDeviceVulkan11Features m_Vulkan11Features;
|
|
||||||
vk::PhysicalDeviceVulkan12Features m_Vulkan12Features;
|
|
||||||
vk::PhysicalDeviceVulkan13Features m_Vulkan13Features;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Device final
|
|
||||||
{
|
|
||||||
NameString m_Name;
|
|
||||||
vk::PhysicalDevice m_PhysicalDevice = nullptr;
|
|
||||||
vk::Device m_Device = nullptr;
|
|
||||||
VmaAllocator m_Allocator = nullptr;
|
|
||||||
|
|
||||||
Device(const Context *context, PhysicalDevice *physicalDevice, Features *enabledFeatures,
|
|
||||||
const eastl::vector<QueueAllocation> &queueAllocations, NameString &&name);
|
|
||||||
~Device();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires vk::isVulkanHandleType<T>::value void SetName(const T &object, cstr name) const;
|
|
||||||
[[nodiscard]] vk::Queue GetQueue(u32 familyIndex, u32 queueIndex) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
requires vk::isVulkanHandleType<T>::value void
|
|
||||||
Device::SetName(const T &object, cstr name) const
|
|
||||||
{
|
|
||||||
if (!name)
|
|
||||||
return;
|
|
||||||
auto handle = Recast<u64>(Cast<typename T::NativeType>(object));
|
|
||||||
const vk::DebugUtilsObjectNameInfoEXT objectNameInfo = {
|
|
||||||
.objectType = object.objectType,
|
|
||||||
.objectHandle = handle,
|
|
||||||
.pObjectName = name,
|
|
||||||
};
|
|
||||||
vk::Result result = m_Device.setDebugUtilsObjectNameEXT(&objectNameInfo);
|
|
||||||
WARN_IF(Failed(result), "Could not name {:x}: {} as {}. Cause: {}", handle, to_string(object.objectType), name,
|
|
||||||
result);
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: global.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
#define VMA_IMPLEMENTATION
|
|
||||||
#include <vk_mem_alloc.h>
|
|
||||||
|
|
||||||
// NOTE: Vulkan Dispatch Loader Storage - Should only appear once.
|
|
||||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
|
||||||
|
|
||||||
void *
|
|
||||||
operator new[](size_t size, const char * /*pName*/, int /*flags*/, unsigned /*debugFlags*/, const char * /*file*/,
|
|
||||||
int /*line*/)
|
|
||||||
{
|
|
||||||
return new u8[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
void *
|
|
||||||
operator new[](size_t size, size_t /*alignment*/, size_t /*alignmentOffset*/, const char * /*pName*/, int /*flags*/,
|
|
||||||
unsigned /*debugFlags*/, const char * /*file*/, int /*line*/)
|
|
||||||
{
|
|
||||||
return new u8[size];
|
|
||||||
}
|
|
||||||
124
aster/global.h
124
aster/global.h
|
|
@ -1,124 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: global.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
#include <GLFW/glfw3.h>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
// Macros that can collide with functions.
|
|
||||||
#if defined(max)
|
|
||||||
#undef max
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(min)
|
|
||||||
#undef min
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
|
||||||
#include <EASTL/fixed_string.h>
|
|
||||||
#include <EASTL/string.h>
|
|
||||||
#include <vk_mem_alloc.h>
|
|
||||||
|
|
||||||
#include <vulkan/vulkan.hpp>
|
|
||||||
|
|
||||||
constexpr u32 ASTER_API_VERSION = VK_API_VERSION_1_3;
|
|
||||||
|
|
||||||
#define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__)
|
|
||||||
|
|
||||||
#define FORCE_TODO static_assert(false)
|
|
||||||
|
|
||||||
[[nodiscard]] inline bool
|
|
||||||
Failed(const vk::Result result)
|
|
||||||
{
|
|
||||||
return result != vk::Result::eSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
using NameString = eastl::fixed_string<char, 32, false>;
|
|
||||||
|
|
||||||
template <typename TFlagBits>
|
|
||||||
struct std::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
|
|
||||||
{
|
|
||||||
[[nodiscard]] usize
|
|
||||||
operator()(const vk::Flags<TFlagBits> &val)
|
|
||||||
{
|
|
||||||
return std::hash<u32>()(Cast<u32>(val));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] usize
|
|
||||||
HashAny(const T &val)
|
|
||||||
{
|
|
||||||
return std::hash<std::remove_cvref_t<T>>()(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] inline usize
|
|
||||||
HashCombine(const usize hash0, const usize hash1)
|
|
||||||
{
|
|
||||||
constexpr usize saltValue = 0x9e3779b9;
|
|
||||||
const usize tempVar = hash1 + saltValue + (hash0 << 6) + (hash0 >> 2);
|
|
||||||
return hash0 ^ tempVar;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Time
|
|
||||||
{
|
|
||||||
static constexpr f64 cMaxDelta = 0.1;
|
|
||||||
|
|
||||||
inline static f64 m_Elapsed{Qnan<f64>};
|
|
||||||
inline static f64 m_Delta{Qnan<f64>};
|
|
||||||
|
|
||||||
static void
|
|
||||||
Init()
|
|
||||||
{
|
|
||||||
WARN_IF(!std::isnan(m_Elapsed), "Time already init.");
|
|
||||||
m_Elapsed = glfwGetTime();
|
|
||||||
m_Delta = 1.0 / 60.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
Update()
|
|
||||||
{
|
|
||||||
ERROR_IF(std::isnan(m_Elapsed), "Time not init.");
|
|
||||||
const auto newElapsed = glfwGetTime();
|
|
||||||
m_Delta = std::clamp(newElapsed - m_Elapsed, 0.0, cMaxDelta);
|
|
||||||
m_Elapsed = newElapsed;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] inline usize
|
|
||||||
ClosestMultiple(const usize val, const usize of)
|
|
||||||
{
|
|
||||||
return of * ((val + of - 1) / of);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<vk::Result> : nested_formatter<std::string>
|
|
||||||
{
|
|
||||||
auto
|
|
||||||
// ReSharper disable once CppInconsistentNaming
|
|
||||||
format(vk::Result result, format_context &ctx) const
|
|
||||||
{
|
|
||||||
return write_padded(ctx,
|
|
||||||
[this, result](auto out) { return v10::format_to(out, "{}", nested(to_string(result))); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TType, usize TCount, bool TOverflow>
|
|
||||||
struct fmt::formatter<eastl::fixed_string<TType, TCount, TOverflow>> : nested_formatter<cstr>
|
|
||||||
{
|
|
||||||
auto
|
|
||||||
// ReSharper disable once CppInconsistentNaming
|
|
||||||
format(const eastl::fixed_string<TType, TCount, TOverflow> &str, format_context &ctx) const
|
|
||||||
{
|
|
||||||
return write_padded(ctx, [this, str](auto out) { return v10::format_to(out, "{}", nested(str.c_str())); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: logger.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
Logger g_Logger = Logger();
|
|
||||||
// ReSharper disable once CppInconsistentNaming
|
|
||||||
|
|
||||||
/* Credits to Const-me */
|
|
||||||
namespace eastl
|
|
||||||
{
|
|
||||||
void
|
|
||||||
AssertionFailure(const char *af)
|
|
||||||
{
|
|
||||||
ERROR("{}", af);
|
|
||||||
debug_break();
|
|
||||||
}
|
|
||||||
} // namespace eastl
|
|
||||||
206
aster/logger.h
206
aster/logger.h
|
|
@ -1,206 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: logger.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "constants.h"
|
|
||||||
#include <debugbreak.h>
|
|
||||||
#include <fmt/core.h>
|
|
||||||
|
|
||||||
struct Logger
|
|
||||||
{
|
|
||||||
enum class LogType : u32
|
|
||||||
{
|
|
||||||
eError,
|
|
||||||
eWarning,
|
|
||||||
eInfo,
|
|
||||||
eDebug,
|
|
||||||
eVerbose,
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 m_MinimumLoggingLevel{Cast<u32>(LogType::eDebug)};
|
|
||||||
|
|
||||||
void
|
|
||||||
SetMinimumLoggingLevel(LogType logType)
|
|
||||||
{
|
|
||||||
m_MinimumLoggingLevel = Cast<u32>(logType);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
|
||||||
constexpr static const char *
|
|
||||||
ToCstr()
|
|
||||||
{
|
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
|
||||||
return "[ERROR]:";
|
|
||||||
if constexpr (TLogLevel == LogType::eWarning)
|
|
||||||
return "[WARN]: ";
|
|
||||||
if constexpr (TLogLevel == LogType::eInfo)
|
|
||||||
return "[INFO]: ";
|
|
||||||
if constexpr (TLogLevel == LogType::eDebug)
|
|
||||||
return "[DEBUG]:";
|
|
||||||
if constexpr (TLogLevel == LogType::eVerbose)
|
|
||||||
return "[VERB]: ";
|
|
||||||
}
|
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
|
||||||
constexpr static const char *
|
|
||||||
ToColorCstr()
|
|
||||||
{
|
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
|
||||||
return ansi_color::Red;
|
|
||||||
if constexpr (TLogLevel == LogType::eWarning)
|
|
||||||
return ansi_color::Yellow;
|
|
||||||
if constexpr (TLogLevel == LogType::eInfo)
|
|
||||||
return ansi_color::Green;
|
|
||||||
if constexpr (TLogLevel == LogType::eDebug)
|
|
||||||
return ansi_color::White;
|
|
||||||
if constexpr (TLogLevel == LogType::eVerbose)
|
|
||||||
return ansi_color::Blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
|
||||||
void
|
|
||||||
Log(const std::string_view &message, const char *loc, u32 line) const
|
|
||||||
{
|
|
||||||
if (Cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
|
||||||
{
|
|
||||||
fmt::println("{}{} {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), message.data(),
|
|
||||||
ansi_color::Black, loc, line, ansi_color::Reset);
|
|
||||||
}
|
|
||||||
#if !defined(NDEBUG)
|
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
|
||||||
{
|
|
||||||
debug_break();
|
|
||||||
}
|
|
||||||
#endif // !defined(NDEBUG)
|
|
||||||
}
|
|
||||||
|
|
||||||
template <LogType TLogLevel>
|
|
||||||
void
|
|
||||||
LogCond(const char *exprStr, const std::string_view &message, const char *loc, u32 line) const
|
|
||||||
{
|
|
||||||
if (Cast<u32>(TLogLevel) <= m_MinimumLoggingLevel)
|
|
||||||
{
|
|
||||||
fmt::println("{}{} ({}) {} {} at {}:{}{}", ToColorCstr<TLogLevel>(), ToCstr<TLogLevel>(), exprStr,
|
|
||||||
message.data(), ansi_color::Black, loc, line, ansi_color::Reset);
|
|
||||||
}
|
|
||||||
#if !defined(NDEBUG)
|
|
||||||
if constexpr (TLogLevel == LogType::eError)
|
|
||||||
{
|
|
||||||
debug_break();
|
|
||||||
}
|
|
||||||
#endif // !defined(NDEBUG)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Logger g_Logger;
|
|
||||||
|
|
||||||
#define MIN_LOG_LEVEL(LOG_LVL) g_Logger.SetMinimumLoggingLevel(LOG_LVL)
|
|
||||||
#define ERROR(...) g_Logger.Log<Logger::LogType::eError>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define WARN(...) g_Logger.Log<Logger::LogType::eWarning>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define INFO(...) g_Logger.Log<Logger::LogType::eInfo>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#define ERROR_IF(expr, ...) \
|
|
||||||
if (Cast<bool>(expr)) [[unlikely]] \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define WARN_IF(expr, ...) \
|
|
||||||
if (Cast<bool>(expr)) [[unlikely]] \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define INFO_IF(expr, ...) \
|
|
||||||
if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#define ELSE_IF_ERROR(expr, ...) \
|
|
||||||
; \
|
|
||||||
else if (Cast<bool>(expr)) \
|
|
||||||
[[unlikely]] g_Logger.LogCond<Logger::LogType::eError>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_IF_WARN(expr, ...) \
|
|
||||||
; \
|
|
||||||
else if (Cast<bool>(expr)) \
|
|
||||||
[[unlikely]] g_Logger.LogCond<Logger::LogType::eWarning>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_IF_INFO(expr, ...) \
|
|
||||||
; \
|
|
||||||
else if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eInfo>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#define ELSE_ERROR(...) \
|
|
||||||
; \
|
|
||||||
else [[unlikely]] g_Logger.Log<Logger::LogType::eError>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_WARN(...) \
|
|
||||||
; \
|
|
||||||
else [[unlikely]] g_Logger.Log<Logger::LogType::eWarning>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_INFO(...) \
|
|
||||||
; \
|
|
||||||
else g_Logger.Log<Logger::LogType::eInfo>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#if !defined(DEBUG_LOG_DISABLED) && !defined(NDEBUG)
|
|
||||||
|
|
||||||
#define DEBUG(...) g_Logger.Log<Logger::LogType::eDebug>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define DEBUG_IF(expr, ...) \
|
|
||||||
if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_IF_DEBUG(expr, ...) \
|
|
||||||
; \
|
|
||||||
else if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eDebug>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_DEBUG(...) \
|
|
||||||
; \
|
|
||||||
else g_Logger.Log<Logger::LogType::eDebug>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#else // !defined(DEBUG_LOG_DISABLED)
|
|
||||||
|
|
||||||
#define DEBUG(msg) \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
#define DEBUG_IF(expr, msg) \
|
|
||||||
if (expr) \
|
|
||||||
(void)msg
|
|
||||||
#define ELSE_IF_DEBUG(expr, msg) \
|
|
||||||
; \
|
|
||||||
if (expr) \
|
|
||||||
(void)msg
|
|
||||||
#define ELSE_DEBUG(msg) \
|
|
||||||
; \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(DEBUG_LOG_DISABLED)
|
|
||||||
|
|
||||||
#if !defined(VERBOSE_LOG_DISABLED) && !defined(NDEBUG)
|
|
||||||
|
|
||||||
#define VERBOSE(...) g_Logger.Log<Logger::LogType::eVerbose>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define VERBOSE_IF(expr, ...) \
|
|
||||||
if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_IF_VERBOSE(expr, ...) \
|
|
||||||
; \
|
|
||||||
else if (Cast<bool>(expr)) \
|
|
||||||
g_Logger.LogCond<Logger::LogType::eVerbose>(#expr, fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
#define ELSE_VERBOSE(...) \
|
|
||||||
; \
|
|
||||||
else g_Logger.Log<Logger::LogType::eVerbose>(fmt::format(__VA_ARGS__), __FILE__, __LINE__)
|
|
||||||
|
|
||||||
#else // !defined(DEBUG_LOG_DISABLED)
|
|
||||||
|
|
||||||
#define VERBOSE(msg) \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
#define VERBOSE_IF(expr, msg) \
|
|
||||||
if (expr) \
|
|
||||||
(void)msg
|
|
||||||
#define ELSE_IF_VERBOSE(expr, msg) \
|
|
||||||
; \
|
|
||||||
if (expr) \
|
|
||||||
(void)msg
|
|
||||||
#define ELSE_VERBOSE(msg) \
|
|
||||||
; \
|
|
||||||
{ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !defined(VERBOSE_LOG_DISABLED)
|
|
||||||
|
|
||||||
#define DO(code) , code
|
|
||||||
#define ABORT(code) exit(Cast<i32>(code))
|
|
||||||
#define THEN_ABORT(code) , ABORT(code)
|
|
||||||
|
|
@ -1,165 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: physical_device.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "physical_device.h"
|
|
||||||
|
|
||||||
#include "context.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
|
||||||
GetSurfaceCapabilities(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
|
||||||
{
|
|
||||||
vk::SurfaceCapabilitiesKHR surfaceCapabilities;
|
|
||||||
|
|
||||||
vk::Result result = physicalDevice.getSurfaceCapabilitiesKHR(surface, &surfaceCapabilities);
|
|
||||||
ERROR_IF(Failed(result), "Fetching surface capabilities failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return surfaceCapabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::SurfaceFormatKHR>
|
|
||||||
GetSurfaceFormats(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
|
||||||
{
|
|
||||||
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
|
||||||
u32 count = 0;
|
|
||||||
vk::Result result = physicalDevice.getSurfaceFormatsKHR(surface, &count, nullptr);
|
|
||||||
ERROR_IF(Failed(result), "Could not get surface formats. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
eastl::vector<vk::SurfaceFormatKHR> surfaceFormats(count);
|
|
||||||
result = physicalDevice.getSurfaceFormatsKHR(surface, &count, surfaceFormats.data());
|
|
||||||
ERROR_IF(Failed(result), "Could not get surface formats. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return surfaceFormats;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::PresentModeKHR>
|
|
||||||
GetSurfacePresentModes(const vk::PhysicalDevice physicalDevice, const vk::SurfaceKHR surface)
|
|
||||||
{
|
|
||||||
// vk::Result::eIncomplete should not occur in this function. The rest are errors. Thus, abort is allowed.
|
|
||||||
u32 count = 0;
|
|
||||||
vk::Result result = physicalDevice.getSurfacePresentModesKHR(surface, &count, nullptr);
|
|
||||||
ERROR_IF(Failed(result), "Could not get present modes. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
eastl::vector<vk::PresentModeKHR> presentModes(count);
|
|
||||||
result = physicalDevice.getSurfacePresentModesKHR(surface, &count, presentModes.data());
|
|
||||||
ERROR_IF(Failed(result), "Could not get present modes. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return presentModes;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] bool
|
|
||||||
GetQueuePresentSupport(const u32 queueFamilyIndex, const vk::SurfaceKHR surface,
|
|
||||||
const vk::PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
b32 supported = false;
|
|
||||||
const vk::Result result = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface, &supported);
|
|
||||||
ERROR_IF(Failed(result), "Could not get queue family surface support. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return supported;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] eastl::fixed_vector<vk::QueueFamilyProperties, 32>
|
|
||||||
GetQueueFamilyProperties(const vk::PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
// Devices rarely have more than 32 queue families. Thus fixed vector
|
|
||||||
u32 count = 0;
|
|
||||||
physicalDevice.getQueueFamilyProperties(&count, nullptr);
|
|
||||||
|
|
||||||
eastl::fixed_vector<vk::QueueFamilyProperties, 32> queueFamilyProperties(count);
|
|
||||||
physicalDevice.getQueueFamilyProperties(&count, queueFamilyProperties.data());
|
|
||||||
|
|
||||||
return queueFamilyProperties;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size 384 return.
|
|
||||||
[[nodiscard]] eastl::vector<QueueFamilyInfo>
|
|
||||||
GetQueueFamilies(const vk::SurfaceKHR surface, const vk::PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
|
|
||||||
auto queueFamilyProperties = GetQueueFamilyProperties(physicalDevice);
|
|
||||||
|
|
||||||
eastl::vector<QueueFamilyInfo> queueFamilyInfos;
|
|
||||||
queueFamilyInfos.reserve(queueFamilyProperties.size());
|
|
||||||
|
|
||||||
u32 familyIndex = 0;
|
|
||||||
for (auto qfp : queueFamilyProperties)
|
|
||||||
{
|
|
||||||
QueueSupportFlags support = {};
|
|
||||||
|
|
||||||
if (qfp.queueFlags | vk::QueueFlagBits::eGraphics)
|
|
||||||
{
|
|
||||||
support |= QueueSupportFlagBits::eGraphics;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qfp.queueFlags | vk::QueueFlagBits::eTransfer)
|
|
||||||
{
|
|
||||||
support |= QueueSupportFlagBits::eTransfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qfp.queueFlags | vk::QueueFlagBits::eCompute)
|
|
||||||
{
|
|
||||||
support |= QueueSupportFlagBits::eCompute;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetQueuePresentSupport(familyIndex, surface, physicalDevice))
|
|
||||||
{
|
|
||||||
support |= QueueSupportFlagBits::ePresent;
|
|
||||||
}
|
|
||||||
|
|
||||||
queueFamilyInfos.push_back({
|
|
||||||
.m_Index = familyIndex,
|
|
||||||
.m_Count = qfp.queueCount,
|
|
||||||
.m_Support = support,
|
|
||||||
});
|
|
||||||
|
|
||||||
familyIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return queueFamilyInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDevice::PhysicalDevice(const vk::SurfaceKHR surface, const vk::PhysicalDevice physicalDevice)
|
|
||||||
{
|
|
||||||
physicalDevice.getProperties(&m_DeviceProperties);
|
|
||||||
physicalDevice.getFeatures(&m_DeviceFeatures);
|
|
||||||
|
|
||||||
m_SurfaceFormats = GetSurfaceFormats(physicalDevice, surface);
|
|
||||||
m_PresentModes = GetSurfacePresentModes(physicalDevice, surface);
|
|
||||||
m_QueueFamilies = GetQueueFamilies(surface, physicalDevice);
|
|
||||||
|
|
||||||
m_PhysicalDevice = physicalDevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
eastl::fixed_vector<vk::PhysicalDevice, 8>
|
|
||||||
EnumeratePhysicalDevices(const vk::Instance instance)
|
|
||||||
{
|
|
||||||
u32 count = 0;
|
|
||||||
vk::Result result = instance.enumeratePhysicalDevices(&count, nullptr);
|
|
||||||
ERROR_IF(Failed(result), "Could not fetch vulkan devices. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
eastl::fixed_vector<vk::PhysicalDevice, 8> physicalDevices(count);
|
|
||||||
result = instance.enumeratePhysicalDevices(&count, physicalDevices.data());
|
|
||||||
ERROR_IF(Failed(result), "Could not fetch vulkan devices. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return physicalDevices;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDevices::PhysicalDevices(const Window *window, const Context *context)
|
|
||||||
{
|
|
||||||
auto surface = window->m_Surface;
|
|
||||||
auto physicalDevices = EnumeratePhysicalDevices(context->m_Instance);
|
|
||||||
for (auto physicalDevice : physicalDevices)
|
|
||||||
{
|
|
||||||
this->emplace_back(surface, physicalDevice);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: physical_device.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
#include <EASTL/fixed_vector.h>
|
|
||||||
|
|
||||||
struct Window;
|
|
||||||
struct Context;
|
|
||||||
|
|
||||||
enum class QueueSupportFlagBits
|
|
||||||
{
|
|
||||||
eGraphics = 0b0001,
|
|
||||||
eTransfer = 0b0010,
|
|
||||||
eCompute = 0b0100,
|
|
||||||
ePresent = 0b1000,
|
|
||||||
};
|
|
||||||
|
|
||||||
using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>;
|
|
||||||
|
|
||||||
struct QueueFamilyInfo
|
|
||||||
{
|
|
||||||
u32 m_Index;
|
|
||||||
u32 m_Count;
|
|
||||||
QueueSupportFlags m_Support;
|
|
||||||
};
|
|
||||||
|
|
||||||
[[nodiscard]] vk::SurfaceCapabilitiesKHR
|
|
||||||
GetSurfaceCapabilities(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
|
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::SurfaceFormatKHR>
|
|
||||||
GetSurfaceFormats(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
|
|
||||||
|
|
||||||
[[nodiscard]] eastl::vector<vk::PresentModeKHR>
|
|
||||||
GetSurfacePresentModes(vk::PhysicalDevice physicalDevice, vk::SurfaceKHR surface);
|
|
||||||
|
|
||||||
struct PhysicalDevice
|
|
||||||
{
|
|
||||||
vk::PhysicalDevice m_PhysicalDevice;
|
|
||||||
vk::PhysicalDeviceProperties m_DeviceProperties;
|
|
||||||
vk::PhysicalDeviceFeatures m_DeviceFeatures;
|
|
||||||
eastl::vector<vk::SurfaceFormatKHR> m_SurfaceFormats;
|
|
||||||
eastl::vector<vk::PresentModeKHR> m_PresentModes;
|
|
||||||
eastl::vector<QueueFamilyInfo> m_QueueFamilies;
|
|
||||||
|
|
||||||
PhysicalDevice(vk::SurfaceKHR surface, vk::PhysicalDevice physicalDevice);
|
|
||||||
};
|
|
||||||
|
|
||||||
class PhysicalDevices : public eastl::fixed_vector<PhysicalDevice, 4>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
PhysicalDevices(const Window *window, const Context *context);
|
|
||||||
};
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: pipeline.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "pipeline.h"
|
|
||||||
|
|
||||||
#include "device.h"
|
|
||||||
|
|
||||||
Pipeline::Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline)
|
|
||||||
: m_Device(device)
|
|
||||||
, m_Layout(layout)
|
|
||||||
, m_Pipeline(pipeline)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Pipeline::~Pipeline()
|
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(m_Pipeline, nullptr);
|
|
||||||
m_Device->m_Device.destroy(m_Layout, nullptr);
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: pipeline.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
struct Device;
|
|
||||||
|
|
||||||
struct Pipeline
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
vk::PipelineLayout m_Layout;
|
|
||||||
vk::Pipeline m_Pipeline;
|
|
||||||
|
|
||||||
Pipeline(const Device *device, vk::PipelineLayout layout, vk::Pipeline pipeline);
|
|
||||||
~Pipeline();
|
|
||||||
};
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
/// =============================================
|
|
||||||
// Aster: swapchain.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// ==============================================
|
|
||||||
|
|
||||||
#include "swapchain.h"
|
|
||||||
|
|
||||||
#include "device.h"
|
|
||||||
#include "physical_device.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
Swapchain::Swapchain(const Window *window, const Device *device, NameString &&name)
|
|
||||||
: m_Device(device)
|
|
||||||
, m_Name(std::move(name))
|
|
||||||
{
|
|
||||||
this->Create(window);
|
|
||||||
}
|
|
||||||
|
|
||||||
Swapchain::~Swapchain()
|
|
||||||
{
|
|
||||||
this->Cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Swapchain::Create(const Window *window)
|
|
||||||
{
|
|
||||||
auto surfaceCapabilities = GetSurfaceCapabilities(m_Device->m_PhysicalDevice, window->m_Surface);
|
|
||||||
auto surfaceFormats = GetSurfaceFormats(m_Device->m_PhysicalDevice, window->m_Surface);
|
|
||||||
auto presentModes = GetSurfacePresentModes(m_Device->m_PhysicalDevice, window->m_Surface);
|
|
||||||
|
|
||||||
m_Format = vk::Format::eUndefined;
|
|
||||||
vk::ColorSpaceKHR swapchainColorSpace = vk::ColorSpaceKHR::eSrgbNonlinear;
|
|
||||||
for (auto [format, colorSpace] : surfaceFormats)
|
|
||||||
{
|
|
||||||
if (format == vk::Format::eB8G8R8A8Srgb && colorSpace == vk::ColorSpaceKHR::eSrgbNonlinear)
|
|
||||||
{
|
|
||||||
m_Format = format;
|
|
||||||
swapchainColorSpace = colorSpace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (m_Format == vk::Format::eUndefined)
|
|
||||||
{
|
|
||||||
WARN("Preferred Swapchain format not found. Falling back.");
|
|
||||||
auto [format, colorSpace] = surfaceFormats.front();
|
|
||||||
m_Format = format;
|
|
||||||
swapchainColorSpace = colorSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::PresentModeKHR swapchainPresentMode = vk::PresentModeKHR::eFifo;
|
|
||||||
for (const auto presentMode : presentModes)
|
|
||||||
{
|
|
||||||
if (presentMode == vk::PresentModeKHR::eMailbox)
|
|
||||||
{
|
|
||||||
swapchainPresentMode = presentMode;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (surfaceCapabilities.currentExtent.width != MaxValue<u32>)
|
|
||||||
{
|
|
||||||
m_Extent = surfaceCapabilities.currentExtent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto [width, height] = window->GetSize();
|
|
||||||
auto [minWidth, minHeight] = surfaceCapabilities.minImageExtent;
|
|
||||||
auto [maxWidth, maxHeight] = surfaceCapabilities.maxImageExtent;
|
|
||||||
|
|
||||||
m_Extent.width = glm::clamp(width, minWidth, maxWidth);
|
|
||||||
m_Extent.height = glm::clamp(height, minHeight, maxHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 swapchainImageCount = 3;
|
|
||||||
if (surfaceCapabilities.maxImageCount > 0)
|
|
||||||
{
|
|
||||||
swapchainImageCount =
|
|
||||||
glm::clamp(swapchainImageCount, surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Note that different queues might need the images to be shared.
|
|
||||||
|
|
||||||
const vk::SwapchainCreateInfoKHR swapchainCreateInfo = {
|
|
||||||
.surface = window->m_Surface,
|
|
||||||
.minImageCount = swapchainImageCount,
|
|
||||||
.imageFormat = m_Format,
|
|
||||||
.imageColorSpace = swapchainColorSpace,
|
|
||||||
.imageExtent = m_Extent,
|
|
||||||
.imageArrayLayers = 1,
|
|
||||||
.imageUsage = vk::ImageUsageFlagBits::eColorAttachment,
|
|
||||||
.imageSharingMode = vk::SharingMode::eExclusive,
|
|
||||||
.preTransform = surfaceCapabilities.currentTransform,
|
|
||||||
.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque,
|
|
||||||
.presentMode = swapchainPresentMode,
|
|
||||||
.clipped = true,
|
|
||||||
.oldSwapchain = m_Swapchain,
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::Device device = m_Device->m_Device;
|
|
||||||
|
|
||||||
vk::SwapchainKHR swapchain;
|
|
||||||
vk::Result result = device.createSwapchainKHR(&swapchainCreateInfo, nullptr, &swapchain);
|
|
||||||
ERROR_IF(Failed(result), "Swapchain {} creation failed. Cause {}", m_Name, result)
|
|
||||||
THEN_ABORT(result)
|
|
||||||
ELSE_DEBUG("Created Swapchain '{}'", m_Name);
|
|
||||||
|
|
||||||
// Irrelevant on the first run. Required for re-creation.
|
|
||||||
Cleanup();
|
|
||||||
|
|
||||||
m_Swapchain = swapchain;
|
|
||||||
|
|
||||||
m_Device->SetName(m_Swapchain, m_Name.data());
|
|
||||||
|
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, nullptr);
|
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
// Managed by the Swapchain.
|
|
||||||
m_Images.resize(swapchainImageCount);
|
|
||||||
result = device.getSwapchainImagesKHR(m_Swapchain, &swapchainImageCount, m_Images.data());
|
|
||||||
ERROR_IF(Failed(result), "Failed getting swapchain {}'s images. Cause {}", m_Name, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageViewCreateInfo viewCreateInfo = {
|
|
||||||
.viewType = vk::ImageViewType::e2D,
|
|
||||||
.format = m_Format,
|
|
||||||
.components = {.r = vk::ComponentSwizzle::eIdentity,
|
|
||||||
.g = vk::ComponentSwizzle::eIdentity,
|
|
||||||
.b = vk::ComponentSwizzle::eIdentity,
|
|
||||||
.a = vk::ComponentSwizzle::eIdentity},
|
|
||||||
.subresourceRange = {.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1},
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 index = 0;
|
|
||||||
for (auto image : m_Images)
|
|
||||||
{
|
|
||||||
viewCreateInfo.image = image;
|
|
||||||
|
|
||||||
vk::ImageView imageView;
|
|
||||||
result = device.createImageView(&viewCreateInfo, nullptr, &imageView);
|
|
||||||
ERROR_IF(Failed(result), "Failed creating swapchain {}'s image view [{}]. Cause {}", m_Name, index, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
m_ImageViews.push_back(imageView);
|
|
||||||
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Swapchain {} Image Views created.", m_Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Swapchain::Cleanup()
|
|
||||||
{
|
|
||||||
if (!m_ImageViews.empty()) // Don't want the condition in the logs.
|
|
||||||
DEBUG("Swapchain {} Image Views destroyed.", m_Name);
|
|
||||||
for (const auto imageView : m_ImageViews)
|
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(imageView, nullptr);
|
|
||||||
}
|
|
||||||
m_ImageViews.clear();
|
|
||||||
if (m_Swapchain)
|
|
||||||
{
|
|
||||||
m_Device->m_Device.destroy(m_Swapchain, nullptr);
|
|
||||||
m_Swapchain = nullptr;
|
|
||||||
DEBUG("Swapchain '{}' destroyed.", m_Name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
/// =============================================
|
|
||||||
// Aster: swapchain.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// ==============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#include <EASTL/fixed_vector.h>
|
|
||||||
|
|
||||||
struct PhysicalDevice;
|
|
||||||
struct Window;
|
|
||||||
struct Device;
|
|
||||||
|
|
||||||
struct Swapchain final
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
vk::SwapchainKHR m_Swapchain;
|
|
||||||
NameString m_Name;
|
|
||||||
vk::Extent2D m_Extent;
|
|
||||||
vk::Format m_Format;
|
|
||||||
eastl::fixed_vector<vk::Image, 4> m_Images;
|
|
||||||
eastl::fixed_vector<vk::ImageView, 4> m_ImageViews;
|
|
||||||
|
|
||||||
Swapchain(const Window *window, const Device *device, NameString &&name);
|
|
||||||
~Swapchain();
|
|
||||||
void Create(const Window *window);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Cleanup();
|
|
||||||
};
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: window.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
#include "context.h"
|
|
||||||
#include "logger.h"
|
|
||||||
|
|
||||||
void
|
|
||||||
Window::SetWindowSize(const vk::Extent2D &extent) const noexcept
|
|
||||||
{
|
|
||||||
SetWindowSize(extent.width, extent.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Window::SetWindowSize(const u32 width, const u32 height) const noexcept
|
|
||||||
{
|
|
||||||
glfwSetWindowSize(m_Window, Cast<i32>(width), Cast<i32>(height));
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::Extent2D
|
|
||||||
Window::GetSize() const
|
|
||||||
{
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
glfwGetWindowSize(m_Window, &width, &height);
|
|
||||||
return {Cast<u32>(width), Cast<u32>(height)};
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::Window(const cstr title, Context *context, vk::Extent2D extent, const b8 isFullScreen)
|
|
||||||
{
|
|
||||||
m_Context = context;
|
|
||||||
m_Name = title;
|
|
||||||
|
|
||||||
GLFWmonitor *monitor = glfwGetPrimaryMonitor();
|
|
||||||
ERROR_IF(!monitor, "No monitor found");
|
|
||||||
|
|
||||||
i32 windowX, windowY, windowWidth, windowHeight;
|
|
||||||
glfwGetMonitorWorkarea(monitor, &windowX, &windowY, &windowWidth, &windowHeight);
|
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
|
||||||
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
|
|
||||||
|
|
||||||
m_Window = glfwCreateWindow(Cast<i32>(extent.width), Cast<i32>(extent.height), m_Name.c_str(),
|
|
||||||
isFullScreen ? monitor : nullptr, nullptr);
|
|
||||||
ERROR_IF(m_Window == nullptr, "Window creation failed")
|
|
||||||
ELSE_DEBUG("Window '{}' created with resolution '{}x{}'", m_Name, extent.width, extent.height);
|
|
||||||
if (m_Window == nullptr)
|
|
||||||
{
|
|
||||||
const char *error = nullptr;
|
|
||||||
const auto code = glfwGetError(&error);
|
|
||||||
ERROR("GLFW Window Creation failed. Cause: ({}) {}", code, error)
|
|
||||||
THEN_ABORT(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFullScreen == false)
|
|
||||||
{
|
|
||||||
glfwSetWindowPos(m_Window, Cast<i32>(windowWidth - extent.width) / 2,
|
|
||||||
Cast<i32>(windowHeight - extent.height) / 2);
|
|
||||||
}
|
|
||||||
glfwSetInputMode(m_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
|
||||||
|
|
||||||
VkSurfaceKHR surface;
|
|
||||||
auto result =
|
|
||||||
Cast<vk::Result>(glfwCreateWindowSurface(Cast<VkInstance>(m_Context->m_Instance), m_Window, nullptr, &surface));
|
|
||||||
ERROR_IF(Failed(result), "Failed to create Surface with {}", result)
|
|
||||||
THEN_ABORT(result)
|
|
||||||
ELSE_DEBUG("Surface {} Created", m_Name);
|
|
||||||
m_Surface = vk::SurfaceKHR(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
Window::~Window()
|
|
||||||
{
|
|
||||||
if (m_Context && m_Surface)
|
|
||||||
{
|
|
||||||
m_Context->m_Instance.destroy(m_Surface, nullptr);
|
|
||||||
DEBUG("Surface Destroyed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Window != nullptr)
|
|
||||||
{
|
|
||||||
glfwDestroyWindow(m_Window);
|
|
||||||
m_Window = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG("Window '{}' Destroyed", m_Name);
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: window.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
#include <EASTL/fixed_string.h>
|
|
||||||
|
|
||||||
struct Context;
|
|
||||||
|
|
||||||
struct Window final
|
|
||||||
{
|
|
||||||
// fields
|
|
||||||
Context *m_Context{};
|
|
||||||
|
|
||||||
GLFWwindow *m_Window = nullptr;
|
|
||||||
vk::SurfaceKHR m_Surface;
|
|
||||||
eastl::fixed_string<char, 32> m_Name;
|
|
||||||
|
|
||||||
// Methods
|
|
||||||
[[nodiscard]] bool
|
|
||||||
Poll() const noexcept
|
|
||||||
{
|
|
||||||
glfwPollEvents();
|
|
||||||
return !glfwWindowShouldClose(m_Window);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetWindowSize(const vk::Extent2D &extent) const noexcept;
|
|
||||||
void SetWindowSize(u32 width, u32 height) const noexcept;
|
|
||||||
[[nodiscard]] vk::Extent2D GetSize() const;
|
|
||||||
|
|
||||||
// Ctor/Dtor
|
|
||||||
Window(cstr title, Context *context, vk::Extent2D extent, b8 isFullScreen = false);
|
|
||||||
~Window();
|
|
||||||
};
|
|
||||||
|
|
@ -6,9 +6,9 @@ find_package(glm CONFIG REQUIRED)
|
||||||
find_package(glfw3 CONFIG REQUIRED)
|
find_package(glfw3 CONFIG REQUIRED)
|
||||||
find_path(SCOTTT_DEBUGBREAK_INCLUDE_DIRS "debugbreak.h")
|
find_path(SCOTTT_DEBUGBREAK_INCLUDE_DIRS "debugbreak.h")
|
||||||
find_package(Vulkan REQUIRED)
|
find_package(Vulkan REQUIRED)
|
||||||
find_package(fmt CONFIG REQUIRED)
|
# find_package( VulkanHeaders CONFIG REQUIRED )
|
||||||
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
||||||
find_package(EASTL CONFIG REQUIRED)
|
find_package(fmt CONFIG REQUIRED)
|
||||||
|
|
||||||
set(HEADER_FILES
|
set(HEADER_FILES
|
||||||
constants.h
|
constants.h
|
||||||
|
|
@ -17,35 +17,26 @@ set(HEADER_FILES
|
||||||
global.h
|
global.h
|
||||||
context.h
|
context.h
|
||||||
window.h
|
window.h
|
||||||
physical_device.h
|
|
||||||
device.h
|
device.h
|
||||||
swapchain.h
|
physical_device.h)
|
||||||
"pipeline.h"
|
|
||||||
queue_allocation.h
|
|
||||||
buffer.h)
|
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
logger.cpp
|
logger.cpp
|
||||||
global.cpp
|
global.cpp
|
||||||
context.cpp
|
context.cpp
|
||||||
window.cpp
|
window.cpp
|
||||||
physical_device.cpp
|
|
||||||
device.cpp
|
device.cpp
|
||||||
swapchain.cpp
|
physical_device.cpp)
|
||||||
pipeline.cpp
|
|
||||||
buffer.cpp)
|
|
||||||
|
|
||||||
add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES})
|
add_library(aster_core ${SOURCE_FILES} ${HEADER_FILES})
|
||||||
set_property(TARGET aster_core PROPERTY CXX_STANDARD 20)
|
set_property(TARGET aster_core PROPERTY CXX_STANDARD 23)
|
||||||
|
|
||||||
target_precompile_headers(aster_core PUBLIC global.h)
|
target_link_libraries(aster_core PRIVATE glm::glm-header-only)
|
||||||
|
|
||||||
target_include_directories(aster_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
|
|
||||||
target_link_libraries(aster_core PUBLIC glm::glm-header-only)
|
|
||||||
target_link_libraries(aster_core PRIVATE glfw)
|
target_link_libraries(aster_core PRIVATE glfw)
|
||||||
target_include_directories(aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS})
|
target_include_directories(aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(aster_core PRIVATE Vulkan::Vulkan Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
|
||||||
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 PUBLIC Vulkan::Headers GPUOpen::VulkanMemoryAllocator)
|
|
||||||
|
|
||||||
|
add_executable(aster_exe "aster.cpp")
|
||||||
|
target_link_libraries(aster_exe PRIVATE aster_core)
|
||||||
|
target_link_libraries(aster_exe PRIVATE glm::glm-header-only)
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#include "constants.h"
|
||||||
|
#include "glfw_context.h"
|
||||||
|
#include "physical_device.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
bool suitable_device(const PhysicalDevice *physical_device) {
|
||||||
|
constexpr auto required_support = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer;
|
||||||
|
|
||||||
|
return physical_device->properties.deviceType != vk::PhysicalDeviceType::eCpu &&
|
||||||
|
!physical_device->surface_formats.empty() &&
|
||||||
|
!physical_device->present_modes.empty() &&
|
||||||
|
cast<bool>(physical_device->queue_support & required_support);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalDevice find_suitable_device(PhysicalDevices &&_physical_devices) {
|
||||||
|
for (auto physdev : _physical_devices) {
|
||||||
|
VERBOSE(fmt::format("Checking device: {}", physdev.properties.deviceName.data()));
|
||||||
|
if (suitable_device(&physdev)) {
|
||||||
|
VERBOSE(fmt::format("Found suitable device {}.", physdev.properties.deviceName.data()));
|
||||||
|
return physdev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("No suitable physical device found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char **) {
|
||||||
|
GlfwContext glfw = {};
|
||||||
|
Context context = { "Aster", VERSION };
|
||||||
|
Window window = { "Aster1", &context, { 640, 480 } };
|
||||||
|
|
||||||
|
PhysicalDevice physical_device = find_suitable_device({ &window, &context });
|
||||||
|
INFO(fmt::format("Using Device {}", physical_device.properties.deviceName.data()));
|
||||||
|
|
||||||
|
while (window.poll()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -11,12 +11,6 @@
|
||||||
#define GLFW_INCLUDE_VULKAN
|
#define GLFW_INCLUDE_VULKAN
|
||||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||||
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
|
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
|
||||||
#define VULKAN_HPP_DISABLE_ENHANCED_MODE 1
|
|
||||||
|
|
||||||
#define VMA_STATIC_VULKAN_FUNCTIONS 0
|
|
||||||
#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1
|
|
||||||
|
|
||||||
#define EASTL_NO_EXCEPTIONS 1
|
|
||||||
|
|
||||||
#if defined(NDEBUG)
|
#if defined(NDEBUG)
|
||||||
#define USE_OPTICK (0)
|
#define USE_OPTICK (0)
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: constants.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <optional>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
using c8 = char;
|
||||||
|
using u8 = uint8_t;
|
||||||
|
using u16 = uint16_t;
|
||||||
|
using u32 = uint32_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
using i8 = int8_t;
|
||||||
|
using i16 = int16_t;
|
||||||
|
using i32 = int32_t;
|
||||||
|
using i64 = int64_t;
|
||||||
|
using f32 = float;
|
||||||
|
using f64 = double;
|
||||||
|
using f128 = long double;
|
||||||
|
using b8 = bool;
|
||||||
|
using b32 = u32;
|
||||||
|
using usize = size_t;
|
||||||
|
using p64 = intptr_t;
|
||||||
|
|
||||||
|
constexpr usize strlen_c(const char *s) {
|
||||||
|
usize len = 0;
|
||||||
|
char c = '\0';
|
||||||
|
do {
|
||||||
|
c = s[len];
|
||||||
|
len++;
|
||||||
|
} while (c != '\0');
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto ANSI_Black = "\u001b[30m";
|
||||||
|
constexpr auto ANSI_Red = "\u001b[31m";
|
||||||
|
constexpr auto ANSI_Green = "\u001b[32m";
|
||||||
|
constexpr auto ANSI_Yellow = "\u001b[33m";
|
||||||
|
constexpr auto ANSI_Blue = "\u001b[34m";
|
||||||
|
constexpr auto ANSI_Magenta = "\u001b[35m";
|
||||||
|
constexpr auto ANSI_Cyan = "\u001b[36m";
|
||||||
|
constexpr auto ANSI_White = "\u001b[37m";
|
||||||
|
constexpr auto ANSI_Reset = "\u001b[0m";
|
||||||
|
|
||||||
|
using std::forward;
|
||||||
|
using std::move;
|
||||||
|
using std::tie;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using Option = std::optional<T>;
|
||||||
|
|
||||||
|
template <typename type_t, typename from_t>
|
||||||
|
constexpr auto cast(from_t &&_in) {
|
||||||
|
return static_cast<type_t>(forward<from_t>(_in));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename type_t, typename from_t>
|
||||||
|
constexpr auto recast(from_t &&_in) {
|
||||||
|
return reinterpret_cast<type_t>(forward<from_t>(_in));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr f32 operator""_deg(long double degrees) {
|
||||||
|
return glm::radians<f32>(cast<f32>(degrees));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr f32 operator""_deg(unsigned long long int degrees) {
|
||||||
|
return glm::radians<f32>(cast<f32>(degrees));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr f32 operator""_rad(long double rads) {
|
||||||
|
return cast<f32>(rads);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr f32 operator""_rad(unsigned long long int rads) {
|
||||||
|
return cast<f32>(rads);
|
||||||
|
}
|
||||||
|
|
||||||
|
using glm::ivec2;
|
||||||
|
using glm::ivec3;
|
||||||
|
using glm::ivec4;
|
||||||
|
using glm::vec2;
|
||||||
|
using glm::vec3;
|
||||||
|
using glm::vec4;
|
||||||
|
|
||||||
|
using glm::mat2;
|
||||||
|
using glm::mat3;
|
||||||
|
using glm::mat4;
|
||||||
|
|
||||||
|
constexpr const char *PROJECT_NAME = "Aster";
|
||||||
|
|
||||||
|
struct Version {
|
||||||
|
u32 major;
|
||||||
|
u32 minor;
|
||||||
|
u32 patch;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr Version VERSION = {
|
||||||
|
.major = 0,
|
||||||
|
.minor = 0,
|
||||||
|
.patch = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Error {
|
||||||
|
eUnknown = 1000,
|
||||||
|
eNoDevices = 1001,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T max_value = std::numeric_limits<T>::max();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T min_value = std::numeric_limits<T>::min();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T lowest_value = std::numeric_limits<T>::lowest();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T err_epsilon = std::numeric_limits<T>::epsilon();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T positive_inf = std::numeric_limits<T>::infinity();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T negative_inf = -std::numeric_limits<T>::infinity();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T qnan = std::numeric_limits<T>::quiet_NaN();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr T snan = std::numeric_limits<T>::signalling_NaN();
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: context.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
VKAPI_ATTR b32 VKAPI_CALL Context::debug_callback(const VkDebugUtilsMessageSeverityFlagBitsEXT _message_severity, const VkDebugUtilsMessageTypeFlagsEXT _message_type, const VkDebugUtilsMessengerCallbackDataEXT *_callback_data, [[maybe_unused]] void *_user_data) {
|
||||||
|
using Severity = vk::DebugUtilsMessageSeverityFlagsEXT;
|
||||||
|
using SeverityBits = vk::DebugUtilsMessageSeverityFlagBitsEXT;
|
||||||
|
using MessageType = vk::DebugUtilsMessageTypeFlagsEXT;
|
||||||
|
using MessageTypeBits = vk::DebugUtilsMessageTypeFlagBitsEXT;
|
||||||
|
|
||||||
|
const auto severity = Severity(_message_severity);
|
||||||
|
const auto message_type = MessageType(_message_type);
|
||||||
|
|
||||||
|
if (message_type & MessageTypeBits::eValidation) {
|
||||||
|
if (severity & SeverityBits::eError)
|
||||||
|
ERROR(_callback_data->pMessage);
|
||||||
|
if (severity & SeverityBits::eWarning)
|
||||||
|
WARN(_callback_data->pMessage);
|
||||||
|
if (severity & SeverityBits::eInfo)
|
||||||
|
INFO(_callback_data->pMessage);
|
||||||
|
if (severity & SeverityBits::eVerbose)
|
||||||
|
VERBOSE(_callback_data->pMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::init(const std::string_view &_app_name, const Version &_app_version) {
|
||||||
|
INFO_IF(enable_validation_layers, "Validation Layers enabled");
|
||||||
|
|
||||||
|
// Creating Instance
|
||||||
|
vk::ApplicationInfo app_info = {
|
||||||
|
.pApplicationName = _app_name.data(),
|
||||||
|
.applicationVersion = VK_MAKE_VERSION(_app_version.major, _app_version.minor, _app_version.patch),
|
||||||
|
.pEngineName = PROJECT_NAME,
|
||||||
|
.engineVersion = VK_MAKE_VERSION(VERSION.major, VERSION.minor, VERSION.patch),
|
||||||
|
.apiVersion = VK_API_VERSION_1_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::DebugUtilsMessengerCreateInfoEXT debug_messenger_create_info = {
|
||||||
|
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
||||||
|
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo,
|
||||||
|
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
||||||
|
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance |
|
||||||
|
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation,
|
||||||
|
.pfnUserCallback = debug_callback,
|
||||||
|
.pUserData = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 glfw_extension_count = 0;
|
||||||
|
const char **glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count);
|
||||||
|
std::vector<const char *> vulkan_extensions(glfw_extensions, glfw_extensions + glfw_extension_count);
|
||||||
|
if (enable_validation_layers) {
|
||||||
|
vulkan_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vk::DynamicLoader dl;
|
||||||
|
// ReSharper disable once CppInconsistentNaming
|
||||||
|
const auto vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||||
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||||
|
|
||||||
|
try {
|
||||||
|
instance = vk::raii::Instance{ raii_context, {
|
||||||
|
.pNext = enable_validation_layers ? &debug_messenger_create_info : nullptr,
|
||||||
|
.pApplicationInfo = &app_info,
|
||||||
|
.enabledLayerCount = enable_validation_layers ? cast<u32>(validation_layers.size()) : 0,
|
||||||
|
.ppEnabledLayerNames = enable_validation_layers ? validation_layers.data() : nullptr,
|
||||||
|
.enabledExtensionCount = cast<u32>(vulkan_extensions.size()),
|
||||||
|
.ppEnabledExtensionNames = vulkan_extensions.data(),
|
||||||
|
} };
|
||||||
|
} catch (const std::exception &err) {
|
||||||
|
ERROR("Failed to create Vulkan instance with "s + err.what());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
INFO("Instance Created.");
|
||||||
|
|
||||||
|
// Debug Messenger
|
||||||
|
if (enable_validation_layers) {
|
||||||
|
try {
|
||||||
|
debug_messenger = vk::raii::DebugUtilsMessengerEXT{ instance, debug_messenger_create_info };
|
||||||
|
} catch (const std::exception &err) {
|
||||||
|
ERROR("Debug Messenger creation failed with "s + err.what());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
INFO("Debug Messenger Created.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Context::~Context() = default;
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: context.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Context
|
||||||
|
*
|
||||||
|
* @brief Vulkan context to handle device initialization logic.
|
||||||
|
*
|
||||||
|
* Handles the required hardware interactions.
|
||||||
|
*/
|
||||||
|
class Context final {
|
||||||
|
public:
|
||||||
|
Context(const std::string_view &_app_name, const Version &_app_version, const b8 _enable_validation = true) :
|
||||||
|
enable_validation_layers{ _enable_validation } {
|
||||||
|
init(_app_name, _app_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context(const std::string_view &_app_name, const Version &_app_version, const std::vector<const char *> &_additional_device_extensions, const b8 _enable_validation = true) :
|
||||||
|
enable_validation_layers{ _enable_validation } {
|
||||||
|
device_extensions.reserve(device_extensions.size() + _additional_device_extensions.size());
|
||||||
|
device_extensions.insert(device_extensions.end(), _additional_device_extensions.begin(), _additional_device_extensions.end());
|
||||||
|
|
||||||
|
init(_app_name, _app_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context(const std::string_view &_app_name, const Version &_app_version, const std::vector<const char *> &_additional_device_extensions, const std::vector<const char *> &_additional_validation_layers) {
|
||||||
|
device_extensions.reserve(device_extensions.size() + _additional_device_extensions.size());
|
||||||
|
device_extensions.insert(device_extensions.end(), _additional_device_extensions.begin(), _additional_device_extensions.end());
|
||||||
|
|
||||||
|
validation_layers.reserve(validation_layers.size() + _additional_validation_layers.size());
|
||||||
|
validation_layers.insert(validation_layers.end(), _additional_validation_layers.begin(), _additional_validation_layers.end());
|
||||||
|
|
||||||
|
init(_app_name, _app_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
Context(const Context &_other) = delete;
|
||||||
|
|
||||||
|
Context(Context &&_other) noexcept :
|
||||||
|
enable_validation_layers{ _other.enable_validation_layers }, validation_layers{ std::move(_other.validation_layers) }, device_extensions{ std::move(_other.device_extensions) }, instance{ std::exchange(_other.instance, nullptr) }, debug_messenger{ std::exchange(_other.debug_messenger, nullptr) } {}
|
||||||
|
|
||||||
|
Context &operator=(const Context &_other) = delete;
|
||||||
|
|
||||||
|
Context &operator=(Context &&_other) noexcept {
|
||||||
|
if (this == &_other)
|
||||||
|
return *this;
|
||||||
|
enable_validation_layers = _other.enable_validation_layers;
|
||||||
|
validation_layers = std::move(_other.validation_layers);
|
||||||
|
device_extensions = std::move(_other.device_extensions);
|
||||||
|
instance = std::exchange(_other.instance, nullptr);
|
||||||
|
debug_messenger = std::exchange(_other.debug_messenger, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Context();
|
||||||
|
|
||||||
|
// Fields
|
||||||
|
bool enable_validation_layers{ true };
|
||||||
|
|
||||||
|
std::vector<const char *> validation_layers = {
|
||||||
|
"VK_LAYER_KHRONOS_validation",
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<const char *> device_extensions = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
VK_KHR_MULTIVIEW_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
vk::raii::Context raii_context;
|
||||||
|
vk::raii::Instance instance{ nullptr };
|
||||||
|
vk::raii::DebugUtilsMessengerEXT debug_messenger{ nullptr };
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init(const std::string_view &_app_name, const Version &_app_version);
|
||||||
|
|
||||||
|
static VKAPI_ATTR b32 VKAPI_CALL debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT _message_severity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT _message_type,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT *_callback_data,
|
||||||
|
void *_user_data);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: device.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
Device::Device(Device &&_other) noexcept :
|
||||||
|
physical_device{ std::move(_other.physical_device) },
|
||||||
|
device{ std::exchange(_other.device, nullptr) },
|
||||||
|
allocator{ std::exchange(_other.allocator, nullptr) },
|
||||||
|
name{ std::move(_other.name) } {}
|
||||||
|
|
||||||
|
Device &Device::operator=(Device &&_other) noexcept {
|
||||||
|
if (this == &_other)
|
||||||
|
return *this;
|
||||||
|
physical_device = std::move(_other.physical_device);
|
||||||
|
device = std::exchange(_other.device, nullptr);
|
||||||
|
allocator = std::exchange(_other.allocator, nullptr);
|
||||||
|
name = std::move(_other.name);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features, std::set<QueueAllocation> &&_enabled_queues) :
|
||||||
|
physical_device{ _physical_device_info },
|
||||||
|
name{ _name } {
|
||||||
|
const auto &physical_device = _physical_device_info.device;
|
||||||
|
|
||||||
|
// Logical Device
|
||||||
|
std::vector queue_priority(_enabled_queues.size(), 1.0f);
|
||||||
|
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
|
||||||
|
queue_create_infos.reserve(_enabled_queues.size());
|
||||||
|
for (auto &[family_, count_] : _enabled_queues) {
|
||||||
|
queue_create_infos.push_back({
|
||||||
|
.queueFamilyIndex = family_,
|
||||||
|
.queueCount = count_,
|
||||||
|
.pQueuePriorities = queue_priority.data(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
device = physical_device.createDevice({
|
||||||
|
.queueCreateInfoCount = cast<u32>(queue_create_infos.size()),
|
||||||
|
.pQueueCreateInfos = queue_create_infos.data(),
|
||||||
|
.enabledLayerCount = _context->enable_validation_layers ? cast<u32>(_context->validation_layers.size()) : 0,
|
||||||
|
.ppEnabledLayerNames = _context->enable_validation_layers ? _context->validation_layers.data() : nullptr,
|
||||||
|
.enabledExtensionCount = cast<u32>(_context->device_extensions.size()),
|
||||||
|
.ppEnabledExtensionNames = _context->device_extensions.data(),
|
||||||
|
.pEnabledFeatures = &_enabled_features,
|
||||||
|
});
|
||||||
|
} catch (const std::exception &err) {
|
||||||
|
ERROR("Failed to create a logical device with "s + err.what());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
INFO("Logical Device Created!");
|
||||||
|
|
||||||
|
const VmaAllocatorCreateInfo allocator_create_info = {
|
||||||
|
.physicalDevice = *physical_device,
|
||||||
|
.device = *device,
|
||||||
|
.instance = *_context->instance,
|
||||||
|
};
|
||||||
|
|
||||||
|
allocator = nullptr;
|
||||||
|
const auto result = cast<vk::Result>(vmaCreateAllocator(&allocator_create_info, &allocator));
|
||||||
|
if (failed(result)) {
|
||||||
|
ERROR("Memory allocator creation failed with "s + vk::to_string(result));
|
||||||
|
throw std::runtime_error("Memory allocator creation failed with "s + vk::to_string(result));
|
||||||
|
}
|
||||||
|
VERBOSE("Memory Allocator Created");
|
||||||
|
|
||||||
|
INFO(fmt::format("Created Device '{}' Successfully", _name));
|
||||||
|
|
||||||
|
set_name(_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Device::~Device() {
|
||||||
|
if (allocator) {
|
||||||
|
vmaDestroyAllocator(allocator);
|
||||||
|
}
|
||||||
|
INFO("Device '" + name + "' Destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Device::set_name(const std::string_view &_name) {
|
||||||
|
VERBOSE(fmt::format("Device {} -> {}", name.data(), _name.data()));
|
||||||
|
name = _name;
|
||||||
|
set_object_name(*physical_device.device, fmt::format("{} GPU", _name.data()));
|
||||||
|
set_object_name(*device, fmt::format("{} Device", _name.data()));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: device.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
#include "global.h"
|
||||||
|
#include "physical_device.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <set>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
struct QueueAllocation {
|
||||||
|
u32 family;
|
||||||
|
u32 count;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Device {
|
||||||
|
public:
|
||||||
|
Device(const Device &_other) = delete;
|
||||||
|
Device(Device &&_other) noexcept;
|
||||||
|
Device &operator=(const Device &_other) = delete;
|
||||||
|
Device &operator=(Device &&_other) noexcept;
|
||||||
|
|
||||||
|
Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features, std::set<QueueAllocation> &&_enabled_queues);
|
||||||
|
|
||||||
|
~Device();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires vk::isVulkanHandleType<T>::value void set_object_name(const T &_obj, const std::string_view &_name) const {
|
||||||
|
try {
|
||||||
|
device.setDebugUtilsObjectNameEXT({
|
||||||
|
.objectType = _obj.objectType,
|
||||||
|
.objectHandle = get_vk_handle(_obj),
|
||||||
|
.pObjectName = _name.data(),
|
||||||
|
});
|
||||||
|
} catch (const std::exception &err) {
|
||||||
|
WARN("Debug Utils name setting failed with "s + err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fields
|
||||||
|
PhysicalDevice physical_device;
|
||||||
|
vk::raii::Device device{ nullptr };
|
||||||
|
VmaAllocator allocator;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_name(const std::string_view &_name);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: glfw_context.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
struct GlfwContext {
|
||||||
|
static i32 post_error() noexcept {
|
||||||
|
static const char *error_ = nullptr;
|
||||||
|
const auto code = glfwGetError(&error_);
|
||||||
|
ERROR("GLFW "s + error_);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static u32 count = 0;
|
||||||
|
|
||||||
|
GlfwContext() {
|
||||||
|
if (count++ > 0)
|
||||||
|
return;
|
||||||
|
if (glfwInit() == GLFW_FALSE) {
|
||||||
|
CRASH(post_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~GlfwContext() {
|
||||||
|
if (--count == 0) {
|
||||||
|
glfwTerminate();
|
||||||
|
}
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: global.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#define VMA_IMPLEMENTATION
|
||||||
|
#include <vma/vk_mem_alloc.h>
|
||||||
|
|
||||||
|
// NOTE: Vulkan Dispatch Loader Storage - Should only appear once.
|
||||||
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: global.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "constants.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
||||||
|
#include <vk_mem_alloc.h>
|
||||||
|
#include <vulkan/vulkan_raii.hpp>
|
||||||
|
|
||||||
|
#define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__)
|
||||||
|
[[nodiscard]] inline bool failed(const vk::Result _result) {
|
||||||
|
return _result != vk::Result::eSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept IsVkEnum = requires(T _t) {
|
||||||
|
{ std::is_enum_v<T> };
|
||||||
|
{ vk::to_string(_t) } -> std::same_as<std::string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires IsVkEnum<T> [[nodiscard]] const char *to_cstr(const T &_val) {
|
||||||
|
static std::string buffer;
|
||||||
|
buffer = vk::to_string(_val);
|
||||||
|
return buffer.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check why inline namespaces aren't working in MSVC 19.27.29110
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
using namespace std::literals::string_view_literals;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires vk::isVulkanHandleType<T>::value [[nodiscard]] constexpr u64
|
||||||
|
get_vk_handle(const T &_vk_handle) noexcept {
|
||||||
|
return reinterpret_cast<u64>(cast<typename T::CType>(_vk_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
struct std::hash<vk::Flags<F>> {
|
||||||
|
[[nodiscard]] usize operator()(const vk::Flags<F> &_val) {
|
||||||
|
return std::hash<u32>()(cast<u32>(_val));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] usize hash_any(const T &_val) {
|
||||||
|
return std::hash<std::remove_cvref_t<T>>()(_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline usize hash_combine(const usize _hash0,
|
||||||
|
const usize _hash1) {
|
||||||
|
constexpr usize salt_value = 0x9e3779b9;
|
||||||
|
return _hash0 ^ (_hash1 + salt_value + (_hash0 << 6) + (_hash0 >> 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Time {
|
||||||
|
static constexpr f64 max_delta = 0.1;
|
||||||
|
|
||||||
|
inline static f64 elapsed{ qnan<f64> };
|
||||||
|
inline static f64 delta{ qnan<f64> };
|
||||||
|
|
||||||
|
static void init() {
|
||||||
|
WARN_IF(!std::isnan(elapsed), "Time already init.");
|
||||||
|
elapsed = glfwGetTime();
|
||||||
|
delta = 1.0 / 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update() {
|
||||||
|
ERROR_IF(std::isnan(elapsed), "Time not init.");
|
||||||
|
const auto new_elapsed = glfwGetTime();
|
||||||
|
delta = std::clamp(new_elapsed - elapsed, 0.0, max_delta);
|
||||||
|
elapsed = new_elapsed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline usize closest_multiple(const usize _val, const usize _of) {
|
||||||
|
return _of * ((_val + _of - 1) / _of);
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,8 @@
|
||||||
// =============================================
|
// =============================================
|
||||||
// Aster: queue_allocation.h
|
// Aster: logger.cpp
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
// =============================================
|
// =============================================
|
||||||
|
|
||||||
#pragma once
|
#include "logger.h"
|
||||||
|
|
||||||
#include "global.h"
|
auto g_logger = Logger();
|
||||||
|
|
||||||
struct QueueAllocation
|
|
||||||
{
|
|
||||||
u32 m_Family;
|
|
||||||
u32 m_Count;
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: logger.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
#include <debugbreak.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct Logger {
|
||||||
|
enum class LogType : u32 {
|
||||||
|
eError,
|
||||||
|
eWarning,
|
||||||
|
eInfo,
|
||||||
|
eDebug,
|
||||||
|
eVerbose,
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 minimum_logging_level{ cast<u32>(LogType::eDebug) };
|
||||||
|
|
||||||
|
void set_minimum_logging_level(LogType _log_type) {
|
||||||
|
minimum_logging_level = cast<u32>(_log_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <LogType LogLevel>
|
||||||
|
constexpr static const char *to_cstr() {
|
||||||
|
if constexpr (LogLevel == LogType::eError)
|
||||||
|
return "[ERROR]:";
|
||||||
|
if constexpr (LogLevel == LogType::eWarning)
|
||||||
|
return "[WARN]: ";
|
||||||
|
if constexpr (LogLevel == LogType::eInfo)
|
||||||
|
return "[INFO]: ";
|
||||||
|
if constexpr (LogLevel == LogType::eDebug)
|
||||||
|
return "[DEBUG]:";
|
||||||
|
if constexpr (LogLevel == LogType::eVerbose)
|
||||||
|
return "[VERB]: ";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <LogType LogLevel>
|
||||||
|
constexpr static const char *to_color_cstr() {
|
||||||
|
if constexpr (LogLevel == LogType::eError)
|
||||||
|
return ANSI_Red;
|
||||||
|
if constexpr (LogLevel == LogType::eWarning)
|
||||||
|
return ANSI_Yellow;
|
||||||
|
if constexpr (LogLevel == LogType::eInfo)
|
||||||
|
return ANSI_Green;
|
||||||
|
if constexpr (LogLevel == LogType::eDebug)
|
||||||
|
return ANSI_White;
|
||||||
|
if constexpr (LogLevel == LogType::eVerbose)
|
||||||
|
return ANSI_Blue;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <LogType LogLevel>
|
||||||
|
void log(const std::string_view &_message, const char *_loc, u32 _line) const {
|
||||||
|
if (cast<u32>(LogLevel) <= minimum_logging_level) {
|
||||||
|
printf("%s%s %s%s| at %s:%u%s\n", to_color_cstr<LogLevel>(), to_cstr<LogLevel>(), _message.data(), ANSI_Black, _loc, _line, ANSI_Reset);
|
||||||
|
}
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
if constexpr (LogLevel == LogType::eError) {
|
||||||
|
debug_break();
|
||||||
|
}
|
||||||
|
#endif // !defined(NDEBUG)
|
||||||
|
}
|
||||||
|
|
||||||
|
template <LogType LogLevel>
|
||||||
|
void log_cond(const char *_expr_str, const std::string_view &_message, const char *_loc, const u32 _line) const {
|
||||||
|
if (cast<u32>(LogLevel) <= minimum_logging_level) {
|
||||||
|
printf("%s%s (%s) %s%s| at %s:%u%s\n", to_color_cstr<LogLevel>(), to_cstr<LogLevel>(), _expr_str, _message.data(), ANSI_Black, _loc, _line, ANSI_Reset);
|
||||||
|
}
|
||||||
|
#if !defined(NDEBUG)
|
||||||
|
if constexpr (LogLevel == LogType::eError) {
|
||||||
|
debug_break();
|
||||||
|
}
|
||||||
|
#endif // !defined(NDEBUG)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Logger g_logger;
|
||||||
|
|
||||||
|
#define ERROR(msg) g_logger.log<Logger::LogType::eError>(msg, __FILE__, __LINE__)
|
||||||
|
#define WARN(msg) g_logger.log<Logger::LogType::eWarning>(msg, __FILE__, __LINE__)
|
||||||
|
#define INFO(msg) g_logger.log<Logger::LogType::eInfo>(msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#define ERROR_IF(expr, msg) \
|
||||||
|
if (cast<bool>(expr)) [[unlikely]] \
|
||||||
|
g_logger.log_cond<Logger::LogType::eError>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define WARN_IF(expr, msg) \
|
||||||
|
if (cast<bool>(expr)) [[unlikely]] \
|
||||||
|
g_logger.log_cond<Logger::LogType::eWarning>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define INFO_IF(expr, msg) \
|
||||||
|
if (cast<bool>(expr)) \
|
||||||
|
g_logger.log_cond<Logger::LogType::eInfo>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#define ELSE_IF_ERROR(expr, msg) \
|
||||||
|
; \
|
||||||
|
else if (cast<bool>(expr)) [[unlikely]] g_logger.log_cond<Logger::LogType::eError>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_IF_WARN(expr, msg) \
|
||||||
|
; \
|
||||||
|
else if (cast<bool>(expr)) [[unlikely]] g_logger.log_cond<Logger::LogType::eWarning>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_IF_INFO(expr, msg) \
|
||||||
|
; \
|
||||||
|
else if (cast<bool>(expr)) g_logger.log_cond<Logger::LogType::eInfo>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#define ELSE_ERROR(msg) \
|
||||||
|
; \
|
||||||
|
else [[unlikely]] g_logger.log<Logger::LogType::eError>(msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_WARN(msg) \
|
||||||
|
; \
|
||||||
|
else [[unlikely]] g_logger.log<Logger::LogType::eWarning>(msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_INFO(msg) \
|
||||||
|
; \
|
||||||
|
else g_logger.log<Logger::LogType::eInfo>(msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#if !defined(DEBUG_LOG_DISABLED) && !defined(NDEBUG)
|
||||||
|
|
||||||
|
#define DEBUG(msg) g_logger.log<Logger::LogType::eDebug>(msg, __FILE__, __LINE__)
|
||||||
|
#define DEBUG_IF(expr, msg) \
|
||||||
|
if (cast<bool>(expr)) \
|
||||||
|
g_logger.log_cond<Logger::LogType::eDebug>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_IF_DEBUG(expr, msg) \
|
||||||
|
; \
|
||||||
|
else if (cast<bool>(expr)) g_logger.log_cond<Logger::LogType::eDebug>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_DEBUG(msg) \
|
||||||
|
; \
|
||||||
|
else [[unlikely]] g_logger.log<Logger::LogType::eDebug>(msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#else // !defined(DEBUG_LOG_DISABLED)
|
||||||
|
|
||||||
|
#define DEBUG(msg) \
|
||||||
|
{}
|
||||||
|
#define DEBUG_IF(expr, msg) \
|
||||||
|
if (expr) \
|
||||||
|
(void)msg
|
||||||
|
#define ELSE_IF_DEBUG(expr, msg) \
|
||||||
|
; \
|
||||||
|
if (expr) \
|
||||||
|
(void)msg
|
||||||
|
#define ELSE_DEBUG(msg) \
|
||||||
|
; \
|
||||||
|
{}
|
||||||
|
|
||||||
|
#endif // !defined(DEBUG_LOG_DISABLED)
|
||||||
|
|
||||||
|
#if !defined(VERBOSE_LOG_DISABLED) && !defined(NDEBUG)
|
||||||
|
|
||||||
|
#define VERBOSE(msg) g_logger.log<Logger::LogType::eVerbose>(msg, __FILE__, __LINE__)
|
||||||
|
#define VERBOSE_IF(expr, msg) \
|
||||||
|
if (cast<bool>(expr)) \
|
||||||
|
g_logger.log_cond<Logger::LogType::eVerbose>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_IF_VERBOSE(expr, msg) \
|
||||||
|
; \
|
||||||
|
else if (cast<bool>(expr)) g_logger.log_cond<Logger::LogType::eVerbose>(#expr, msg, __FILE__, __LINE__)
|
||||||
|
#define ELSE_VERBOSE(msg) \
|
||||||
|
; \
|
||||||
|
else g_logger.log<Logger::LogType::eVerbose>(msg, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
#else // !defined(DEBUG_LOG_DISABLED)
|
||||||
|
|
||||||
|
#define VERBOSE(msg) \
|
||||||
|
{}
|
||||||
|
#define VERBOSE_IF(expr, msg) \
|
||||||
|
if (expr) \
|
||||||
|
(void)msg
|
||||||
|
#define ELSE_IF_VERBOSE(expr, msg) \
|
||||||
|
; \
|
||||||
|
if (expr) \
|
||||||
|
(void)msg
|
||||||
|
#define ELSE_VERBOSE(msg) \
|
||||||
|
; \
|
||||||
|
{}
|
||||||
|
|
||||||
|
#endif // !defined(VERBOSE_LOG_DISABLED)
|
||||||
|
|
||||||
|
#define DO(code) , code
|
||||||
|
#define CRASH(code) exit(cast<i32>(code))
|
||||||
|
#define THEN_CRASH(code) , CRASH(code)
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: physical_device.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "physical_device.h"
|
||||||
|
|
||||||
|
std::vector<QueueFamilyInfo> PhysicalDevice::get_queue_families(const Window *_window) const {
|
||||||
|
auto queue_families_ = device.getQueueFamilyProperties();
|
||||||
|
|
||||||
|
std::vector<QueueFamilyInfo> queue_family_infos;
|
||||||
|
queue_family_infos.reserve(queue_families_.size());
|
||||||
|
|
||||||
|
u32 family_index = 0;
|
||||||
|
for (const auto &queue_family : queue_families_) {
|
||||||
|
QueueSupportFlags support;
|
||||||
|
|
||||||
|
if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
|
||||||
|
support |= QueueSupportFlagBits::eGraphics;
|
||||||
|
}
|
||||||
|
if (queue_family.queueFlags & vk::QueueFlagBits::eCompute) {
|
||||||
|
support |= QueueSupportFlagBits::eCompute;
|
||||||
|
}
|
||||||
|
if (queue_family.queueFlags & vk::QueueFlagBits::eTransfer) {
|
||||||
|
support |= QueueSupportFlagBits::eTransfer;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (device.getSurfaceSupportKHR(family_index, *_window->surface)) {
|
||||||
|
support |= QueueSupportFlagBits::ePresent;
|
||||||
|
}
|
||||||
|
} catch (const std::exception &err) {
|
||||||
|
ERROR("Failure in finding surface support, all possibilities fatal. Failed with "s + err.what());
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
VERBOSE(fmt::format("Queue({}): {}", family_index, to_string(support)));
|
||||||
|
|
||||||
|
queue_family_infos.push_back({
|
||||||
|
.index = family_index,
|
||||||
|
.count = queue_family.queueCount,
|
||||||
|
.queue_support = support,
|
||||||
|
});
|
||||||
|
|
||||||
|
++family_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return queue_family_infos;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: physical_device.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
enum class QueueSupportFlagBits {
|
||||||
|
eGraphics = 0b0000'0001,
|
||||||
|
eTransfer = 0b0000'0010,
|
||||||
|
eCompute = 0b0000'0100,
|
||||||
|
ePresent = 0b0000'1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
using QueueSupportFlags = vk::Flags<QueueSupportFlagBits>;
|
||||||
|
|
||||||
|
inline std::string to_string(QueueSupportFlags queue_support) {
|
||||||
|
if (! queue_support)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
std::string result = "";
|
||||||
|
if (queue_support & QueueSupportFlagBits::eGraphics) {
|
||||||
|
result += "Graphics | ";
|
||||||
|
}
|
||||||
|
if (queue_support & QueueSupportFlagBits::eTransfer) {
|
||||||
|
result += "Transfer | ";
|
||||||
|
}
|
||||||
|
if (queue_support & QueueSupportFlagBits::eCompute) {
|
||||||
|
result += "Compute | ";
|
||||||
|
}
|
||||||
|
if (queue_support & QueueSupportFlagBits::ePresent) {
|
||||||
|
result += "Present | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{ " + result.substr( 0, result.size() - 3 ) + " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QueueFamilyInfo {
|
||||||
|
u32 index;
|
||||||
|
u32 count;
|
||||||
|
QueueSupportFlags queue_support;
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_support(const QueueSupportFlags &support) const {
|
||||||
|
return cast<bool>(queue_support & support);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PhysicalDevice {
|
||||||
|
vk::raii::PhysicalDevice device;
|
||||||
|
vk::PhysicalDeviceProperties properties;
|
||||||
|
vk::PhysicalDeviceFeatures features;
|
||||||
|
std::vector<vk::SurfaceFormatKHR> surface_formats;
|
||||||
|
std::vector<vk::PresentModeKHR> present_modes;
|
||||||
|
std::vector<QueueFamilyInfo> queue_family_infos;
|
||||||
|
QueueSupportFlags queue_support;
|
||||||
|
|
||||||
|
PhysicalDevice(const Window *const _window, vk::raii::PhysicalDevice &&_device) :
|
||||||
|
device{ std::move(_device) } {
|
||||||
|
properties = device.getProperties();
|
||||||
|
features = device.getFeatures();
|
||||||
|
surface_formats = device.getSurfaceFormatsKHR(*_window->surface);
|
||||||
|
present_modes = device.getSurfacePresentModesKHR(*_window->surface);
|
||||||
|
queue_family_infos = get_queue_families(_window);
|
||||||
|
for (auto [family, count, support] : queue_family_infos) {
|
||||||
|
queue_support |= support;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<QueueFamilyInfo> get_queue_families(const Window *_window) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PhysicalDevices : std::vector<PhysicalDevice> {
|
||||||
|
PhysicalDevices(const Window *const _window, const Context *_context) {
|
||||||
|
auto phys_devs = vk::raii::PhysicalDevices(_context->instance);
|
||||||
|
reserve(phys_devs.size());
|
||||||
|
|
||||||
|
for (auto physdev : phys_devs) {
|
||||||
|
emplace_back(_window, std::move(physdev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: window.cpp
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#include "window.h"
|
||||||
|
#include "context.h"
|
||||||
|
#include "glfw_context.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
Window::Window(const std::string_view &_title, Context *_context, const vk::Extent2D _extent, const bool _full_screen) :
|
||||||
|
extent{ _extent }, name{ _title } {
|
||||||
|
full_screen = _full_screen;
|
||||||
|
monitor = glfwGetPrimaryMonitor();
|
||||||
|
ERROR_IF(monitor == nullptr, "No monitor found");
|
||||||
|
|
||||||
|
i32 x_, y_, w_, h_;
|
||||||
|
glfwGetMonitorWorkarea(monitor, &x_, &y_, &w_, &h_);
|
||||||
|
|
||||||
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
glfwWindowHint(GLFW_CENTER_CURSOR, GLFW_TRUE);
|
||||||
|
|
||||||
|
window = glfwCreateWindow(cast<i32>(extent.width), cast<i32>(extent.height), name.c_str(), (_full_screen ? monitor : nullptr), nullptr);
|
||||||
|
ERROR_IF(window == nullptr, "Window creation failed")
|
||||||
|
ELSE_INFO(fmt::format("Window '{}' created with resolution '{}x{}'", name.data(), extent.width, extent.height));
|
||||||
|
if (window == nullptr) {
|
||||||
|
auto code = GlfwContext::post_error();
|
||||||
|
glfwTerminate();
|
||||||
|
CRASH(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_screen == false) {
|
||||||
|
glfwSetWindowPos(window, cast<i32>(w_ - extent.width) / 2, cast<i32>(h_ - extent.height) / 2);
|
||||||
|
}
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||||
|
|
||||||
|
VkSurfaceKHR surface_;
|
||||||
|
auto result = cast<vk::Result>(glfwCreateWindowSurface(cast<VkInstance>(*_context->instance), window, nullptr, &surface_));
|
||||||
|
if (failed(result)) {
|
||||||
|
ERROR("Failed to create Surface with "s + to_string(result));
|
||||||
|
throw std::runtime_error("Failed to create Surface with "s + to_string(result));
|
||||||
|
}
|
||||||
|
INFO("Surface Created");
|
||||||
|
surface = vk::raii::SurfaceKHR{ _context->instance, surface_ };
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::Window(Window &&_other) noexcept :
|
||||||
|
window{ std::exchange(_other.window, nullptr) },
|
||||||
|
monitor{ _other.monitor },
|
||||||
|
surface{ std::exchange(_other.surface, nullptr) },
|
||||||
|
extent{ _other.extent },
|
||||||
|
name{ std::move(_other.name) },
|
||||||
|
full_screen{ _other.full_screen } {}
|
||||||
|
|
||||||
|
Window &Window::operator=(Window &&_other) noexcept {
|
||||||
|
if (this == &_other)
|
||||||
|
return *this;
|
||||||
|
window = _other.window;
|
||||||
|
monitor = _other.monitor;
|
||||||
|
surface = std::exchange(_other.surface, nullptr);
|
||||||
|
extent = _other.extent;
|
||||||
|
name = std::move(_other.name);
|
||||||
|
full_screen = _other.full_screen;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::~Window() {
|
||||||
|
if (window != nullptr) {
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
window = nullptr;
|
||||||
|
}
|
||||||
|
monitor = nullptr;
|
||||||
|
|
||||||
|
INFO(fmt::format("Window '{}' Destroyed", name));
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// =============================================
|
||||||
|
// Aster: window.h
|
||||||
|
// Copyright (c) 2020-2024 Anish Bhobe
|
||||||
|
// =============================================
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "global.h"
|
||||||
|
|
||||||
|
#include "context.h"
|
||||||
|
|
||||||
|
struct Window final {
|
||||||
|
Window(const std::string_view &_title, Context *_context, vk::Extent2D _extent, b8 _full_screen = false);
|
||||||
|
|
||||||
|
Window(const Window &_other) = delete;
|
||||||
|
Window(Window &&_other) noexcept;
|
||||||
|
Window &operator=(const Window &_other) = delete;
|
||||||
|
Window &operator=(Window &&_other) noexcept;
|
||||||
|
|
||||||
|
~Window();
|
||||||
|
|
||||||
|
[[nodiscard]] bool should_close() const noexcept {
|
||||||
|
return glfwWindowShouldClose(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool poll() const noexcept {
|
||||||
|
glfwPollEvents();
|
||||||
|
return !glfwWindowShouldClose(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_window_size(const vk::Extent2D &_extent) noexcept {
|
||||||
|
extent = _extent;
|
||||||
|
glfwSetWindowSize(window, cast<i32>(extent.width), cast<i32>(extent.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_window_size(const u32 _width, const u32 _height) noexcept {
|
||||||
|
set_window_size({ _width, _height });
|
||||||
|
}
|
||||||
|
|
||||||
|
// fields
|
||||||
|
GLFWwindow *window{ nullptr };
|
||||||
|
GLFWmonitor *monitor{ nullptr };
|
||||||
|
vk::raii::SurfaceKHR surface{ nullptr };
|
||||||
|
vk::Extent2D extent;
|
||||||
|
std::string name;
|
||||||
|
b8 full_screen{ false };
|
||||||
|
};
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# CMakeList.txt ; CMake project for Triangle
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
add_library(util_helper STATIC helpers.h helpers.cpp)
|
|
||||||
|
|
||||||
target_link_libraries(util_helper PRIVATE aster_core)
|
|
||||||
target_include_directories(util_helper PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: helpers.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#include "device.h"
|
|
||||||
#include "physical_device.h"
|
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
|
||||||
|
|
||||||
constexpr QueueSupportFlags REQUIRED_QUEUE_SUPPORT = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics |
|
|
||||||
QueueSupportFlagBits::eCompute | QueueSupportFlagBits::ePresent |
|
|
||||||
QueueSupportFlagBits::eTransfer;
|
|
||||||
|
|
||||||
bool
|
|
||||||
IsSuitableDevice(const PhysicalDevice *physicalDevice)
|
|
||||||
{
|
|
||||||
const bool hasAllRequiredQueues =
|
|
||||||
std::ranges::any_of(physicalDevice->m_QueueFamilies, [](const auto &queueFamilyProp) {
|
|
||||||
return (queueFamilyProp.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT;
|
|
||||||
});
|
|
||||||
|
|
||||||
const bool isNotCpu = physicalDevice->m_DeviceProperties.deviceType != vk::PhysicalDeviceType::eCpu;
|
|
||||||
|
|
||||||
const bool hasPresentMode = !physicalDevice->m_PresentModes.empty();
|
|
||||||
|
|
||||||
const bool hasSurfaceFormat = !physicalDevice->m_SurfaceFormats.empty();
|
|
||||||
|
|
||||||
return hasSurfaceFormat && hasPresentMode && isNotCpu && hasAllRequiredQueues;
|
|
||||||
}
|
|
||||||
|
|
||||||
PhysicalDevice
|
|
||||||
FindSuitableDevice(const PhysicalDevices &physicalDevices)
|
|
||||||
{
|
|
||||||
for (auto &physicalDevice : physicalDevices)
|
|
||||||
{
|
|
||||||
if (IsSuitableDevice(&physicalDevice))
|
|
||||||
{
|
|
||||||
return physicalDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ERROR("No suitable GPU available on the system.")
|
|
||||||
THEN_ABORT(vk::Result::eErrorUnknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
QueueAllocation
|
|
||||||
FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice)
|
|
||||||
{
|
|
||||||
for (auto &queueFamilyInfo : physicalDevice->m_QueueFamilies)
|
|
||||||
{
|
|
||||||
if ((queueFamilyInfo.m_Support & REQUIRED_QUEUE_SUPPORT) == REQUIRED_QUEUE_SUPPORT)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.m_Family = queueFamilyInfo.m_Index,
|
|
||||||
.m_Count = queueFamilyInfo.m_Count,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ERROR("No suitable queue family on the GPU.")
|
|
||||||
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 == 1024);
|
|
||||||
|
|
||||||
return outputVec;
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: helpers.h
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
|
|
||||||
#include "queue_allocation.h"
|
|
||||||
#include <EASTL/vector.h>
|
|
||||||
|
|
||||||
struct PhysicalDevice;
|
|
||||||
class PhysicalDevices;
|
|
||||||
|
|
||||||
PhysicalDevice FindSuitableDevice(const PhysicalDevices &physicalDevices);
|
|
||||||
QueueAllocation FindAppropriateQueueAllocation(const PhysicalDevice *physicalDevice);
|
|
||||||
eastl::vector<u32> ReadFile(cstr fileName);
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
# CMakeList.txt ; CMake project for Triangle
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
add_executable(triangle "triangle.cpp")
|
|
||||||
add_shader(triangle shader/triangle.vert.glsl)
|
|
||||||
add_shader(triangle shader/triangle.frag.glsl)
|
|
||||||
|
|
||||||
target_link_libraries(triangle PRIVATE aster_core)
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
@ -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,540 +0,0 @@
|
||||||
// =============================================
|
|
||||||
// Aster: triangle.cpp
|
|
||||||
// Copyright (c) 2020-2024 Anish Bhobe
|
|
||||||
// =============================================
|
|
||||||
|
|
||||||
#include "buffer.h"
|
|
||||||
#include "constants.h"
|
|
||||||
#include "context.h"
|
|
||||||
#include "device.h"
|
|
||||||
#include "physical_device.h"
|
|
||||||
#include "window.h"
|
|
||||||
|
|
||||||
#include "global.h"
|
|
||||||
#include "pipeline.h"
|
|
||||||
#include "swapchain.h"
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#include <EASTL/array.h>
|
|
||||||
|
|
||||||
constexpr u32 MAX_FRAMES_IN_FLIGHT = 3;
|
|
||||||
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
|
|
||||||
{
|
|
||||||
float m_Position[4];
|
|
||||||
float m_Color[4];
|
|
||||||
|
|
||||||
constexpr static vk::VertexInputBindingDescription
|
|
||||||
GetBinding(const u32 binding)
|
|
||||||
{
|
|
||||||
return {.binding = binding, .stride = sizeof(Vertex), .inputRate = vk::VertexInputRate::eVertex};
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr static eastl::array<vk::VertexInputAttributeDescription, 2>
|
|
||||||
GetAttributes(const u32 binding)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
vk::VertexInputAttributeDescription{
|
|
||||||
.location = 0,
|
|
||||||
.binding = binding,
|
|
||||||
.format = vk::Format::eR32G32B32A32Sfloat,
|
|
||||||
.offset = offsetof(Vertex, m_Position),
|
|
||||||
},
|
|
||||||
vk::VertexInputAttributeDescription{
|
|
||||||
.location = 1,
|
|
||||||
.binding = binding,
|
|
||||||
.format = vk::Format::eR32G32B32A32Sfloat,
|
|
||||||
.offset = offsetof(Vertex, m_Color),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Frame
|
|
||||||
{
|
|
||||||
const Device *m_Device;
|
|
||||||
vk::CommandPool m_Pool;
|
|
||||||
vk::Fence m_FrameAvailableFence;
|
|
||||||
vk::Semaphore m_ImageAcquireSem;
|
|
||||||
vk::Semaphore m_RenderFinishSem;
|
|
||||||
|
|
||||||
Frame(const Device *device, u32 queueFamilyIndex, u32 frameCount);
|
|
||||||
~Frame();
|
|
||||||
|
|
||||||
[[nodiscard]] vk::CommandBuffer AllocateCommandBuffer() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int, char **)
|
|
||||||
{
|
|
||||||
MIN_LOG_LEVEL(Logger::LogType::eInfo);
|
|
||||||
|
|
||||||
Context context = {"Triangle", VERSION};
|
|
||||||
Window window = {"Triangle (Aster)", &context, {640, 480}};
|
|
||||||
|
|
||||||
PhysicalDevices physicalDevices = {&window, &context};
|
|
||||||
PhysicalDevice deviceToUse = FindSuitableDevice(physicalDevices);
|
|
||||||
|
|
||||||
INFO("Using {} as the primary device.", deviceToUse.m_DeviceProperties.deviceName.data());
|
|
||||||
|
|
||||||
Features enabledDeviceFeatures = {.m_Vulkan13Features = {.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 = {&window, &device, "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 vertices = {
|
|
||||||
Vertex{.m_Position = {-0.5f, -0.5f, 0.0f, 1.0f}, .m_Color = {1.0f, 0.0f, 0.0f, 1.0f}},
|
|
||||||
Vertex{.m_Position = {0.5f, -0.5f, 0.0f, 1.0f}, .m_Color = {0.0f, 1.0f, 0.0f, 1.0f}},
|
|
||||||
Vertex{.m_Position = {0.0f, 0.5f, 0.0f, 1.0f}, .m_Color = {0.0f, 0.0f, 1.0f, 1.0f}},
|
|
||||||
};
|
|
||||||
VertexBuffer vbo;
|
|
||||||
vbo.Init(&device, vertices.size() * sizeof vertices[0], "VBO");
|
|
||||||
{
|
|
||||||
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
|
|
||||||
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 = {
|
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
|
||||||
.baseMipLevel = 0,
|
|
||||||
.levelCount = 1,
|
|
||||||
.baseArrayLayer = 0,
|
|
||||||
.layerCount = 1,
|
|
||||||
};
|
|
||||||
vk::ImageMemoryBarrier topOfThePipeBarrier = {
|
|
||||||
.oldLayout = vk::ImageLayout::eUndefined,
|
|
||||||
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
.subresourceRange = subresourceRange,
|
|
||||||
};
|
|
||||||
vk::ImageMemoryBarrier renderToPresentBarrier = {
|
|
||||||
.oldLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
||||||
.newLayout = vk::ImageLayout::ePresentSrcKHR,
|
|
||||||
.srcQueueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
.dstQueueFamilyIndex = queueAllocation.m_Family,
|
|
||||||
.subresourceRange = subresourceRange,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Frames
|
|
||||||
eastl::fixed_vector<Frame, MAX_FRAMES_IN_FLIGHT> frames;
|
|
||||||
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
|
|
||||||
{
|
|
||||||
frames.emplace_back(&device, queueAllocation.m_Family, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
INFO("Starting loop");
|
|
||||||
u32 frameIndex = 0;
|
|
||||||
while (window.Poll())
|
|
||||||
{
|
|
||||||
Frame *currentFrame = &frames[frameIndex];
|
|
||||||
|
|
||||||
auto result = device.m_Device.waitForFences(1, ¤tFrame->m_FrameAvailableFence, true, MaxValue<u64>);
|
|
||||||
ERROR_IF(Failed(result), "Waiting for fence {} failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
u32 imageIndex;
|
|
||||||
result = device.m_Device.acquireNextImageKHR(swapchain.m_Swapchain, MaxValue<u64>,
|
|
||||||
currentFrame->m_ImageAcquireSem, nullptr, &imageIndex);
|
|
||||||
if (Failed(result))
|
|
||||||
{
|
|
||||||
switch (result)
|
|
||||||
{
|
|
||||||
case vk::Result::eErrorOutOfDateKHR:
|
|
||||||
case vk::Result::eSuboptimalKHR:
|
|
||||||
INFO("Recreating Swapchain. Cause: {}", result);
|
|
||||||
swapchain.Create(&window);
|
|
||||||
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, {});
|
|
||||||
ERROR_IF(Failed(result), "Command pool {} reset failed. Cause: {}", frameIndex, result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::ImageView currentImageView = swapchain.m_ImageViews[imageIndex];
|
|
||||||
vk::Image currentImage = swapchain.m_Images[imageIndex];
|
|
||||||
vk::CommandBuffer cmd = currentFrame->AllocateCommandBuffer();
|
|
||||||
|
|
||||||
topOfThePipeBarrier.image = currentImage;
|
|
||||||
renderToPresentBarrier.image = currentImage;
|
|
||||||
|
|
||||||
vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
|
||||||
result = cmd.begin(&beginInfo);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer begin failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
|
||||||
{}, 0, nullptr, 0, nullptr, 1, &topOfThePipeBarrier);
|
|
||||||
|
|
||||||
// Render
|
|
||||||
vk::RenderingAttachmentInfo attachmentInfo = {
|
|
||||||
.imageView = currentImageView,
|
|
||||||
.imageLayout = vk::ImageLayout::eColorAttachmentOptimal,
|
|
||||||
.resolveMode = vk::ResolveModeFlagBits::eNone,
|
|
||||||
.loadOp = vk::AttachmentLoadOp::eClear,
|
|
||||||
.storeOp = vk::AttachmentStoreOp::eStore,
|
|
||||||
.clearValue = vk::ClearColorValue{0.0f, 0.0f, 0.0f, 1.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
vk::RenderingInfo renderingInfo = {
|
|
||||||
.renderArea = {.extent = swapchain.m_Extent},
|
|
||||||
.layerCount = 1,
|
|
||||||
.colorAttachmentCount = 1,
|
|
||||||
.pColorAttachments = &attachmentInfo,
|
|
||||||
};
|
|
||||||
|
|
||||||
cmd.beginRendering(&renderingInfo);
|
|
||||||
|
|
||||||
cmd.setViewport(0, 1, &viewport);
|
|
||||||
cmd.setScissor(0, 1, &scissor);
|
|
||||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
|
||||||
usize offsets = 0;
|
|
||||||
cmd.bindVertexBuffers(0, 1, &vbo.m_Buffer, &offsets);
|
|
||||||
cmd.draw(3, 1, 0, 0);
|
|
||||||
|
|
||||||
cmd.endRendering();
|
|
||||||
|
|
||||||
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::PipelineStageFlagBits::eBottomOfPipe,
|
|
||||||
{}, 0, nullptr, 0, nullptr, 1, &renderToPresentBarrier);
|
|
||||||
|
|
||||||
result = cmd.end();
|
|
||||||
ERROR_IF(Failed(result), "Command buffer end failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
vk::PipelineStageFlags waitDstStage = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
||||||
vk::SubmitInfo submitInfo = {
|
|
||||||
.waitSemaphoreCount = 1,
|
|
||||||
.pWaitSemaphores = ¤tFrame->m_ImageAcquireSem,
|
|
||||||
.pWaitDstStageMask = &waitDstStage,
|
|
||||||
.commandBufferCount = 1,
|
|
||||||
.pCommandBuffers = &cmd,
|
|
||||||
.signalSemaphoreCount = 1,
|
|
||||||
.pSignalSemaphores = ¤tFrame->m_RenderFinishSem,
|
|
||||||
};
|
|
||||||
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(&window);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto result = device.m_Device.waitIdle();
|
|
||||||
ERROR_IF(Failed(result), "Wait idle failed. Cause: {}", result);
|
|
||||||
|
|
||||||
device.m_Device.destroy(copyPool, nullptr);
|
|
||||||
vbo.Destroy(&device);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
DEBUG("Frame {} created successfully.", frameCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
vk::CommandBuffer
|
|
||||||
Frame::AllocateCommandBuffer() const
|
|
||||||
{
|
|
||||||
const vk::CommandBufferAllocateInfo allocateInfo = {
|
|
||||||
.commandPool = m_Pool, .level = vk::CommandBufferLevel::ePrimary, .commandBufferCount = 1};
|
|
||||||
|
|
||||||
vk::CommandBuffer commandBuffer;
|
|
||||||
vk::Result result = m_Device->m_Device.allocateCommandBuffers(&allocateInfo, &commandBuffer);
|
|
||||||
ERROR_IF(Failed(result), "Command buffer allocation failed. Cause: {}", result)
|
|
||||||
THEN_ABORT(result);
|
|
||||||
|
|
||||||
return commandBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# CMakeList.txt ; CMake project for Samples
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
add_subdirectory("01_triangle")
|
|
||||||
add_subdirectory("00_util")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
{
|
{
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"fmt",
|
|
||||||
"glfw3",
|
"glfw3",
|
||||||
"glm",
|
"glm",
|
||||||
"scottt-debugbreak",
|
"scottt-debugbreak",
|
||||||
"vulkan-memory-allocator",
|
"vulkan-memory-allocator",
|
||||||
"eastl"
|
"fmt"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue