Compare commits
25 Commits
31efa504a3
...
1a8f113323
| Author | SHA1 | Date |
|---|---|---|
|
|
1a8f113323 | |
|
|
7c17d09822 | |
|
|
363259a52e | |
|
|
c338c34337 | |
|
|
0ca2779014 | |
|
|
d9b0e82be7 | |
|
|
ccb0aa5fbe | |
|
|
5744e7a13c | |
|
|
eaf4556bad | |
|
|
c6ff2f4f76 | |
|
|
76a7927643 | |
|
|
8769215437 | |
|
|
e120b38066 | |
|
|
e55f30e7e7 | |
|
|
c16456c610 | |
|
|
f603bd5752 | |
|
|
7bf87f5127 | |
|
|
ce4dd9b096 | |
|
|
44173ffdbc | |
|
|
2f4db7ffaf | |
|
|
20cf0876eb | |
|
|
7f66176895 | |
|
|
39732c1a27 | |
|
|
8b0a7f2622 | |
|
|
a0a84f30f8 |
214
.clang-format
214
.clang-format
|
|
@ -3,197 +3,25 @@
|
|||
# chosen value in case the base style changes (last sync: Clang 14.0).
|
||||
---
|
||||
### General config, applies to all languages ###
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
# AlignArrayOfStructures: None
|
||||
# AlignConsecutiveMacros: None
|
||||
# AlignConsecutiveAssignments: None
|
||||
# AlignConsecutiveBitFields: None
|
||||
# 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
|
||||
BasedOnStyle: Microsoft
|
||||
#AccessModifierOffset: -2
|
||||
AlwaysBreakAfterReturnType: AllDefinitions
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
#ColumnLimit: 0
|
||||
#ConstructorInitializerIndentWidth: 4
|
||||
#ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
IncludeCategories:
|
||||
- Regex: '".*"'
|
||||
Priority: 1
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*'
|
||||
Priority: 3
|
||||
# IncludeIsMainRegex: '(Test)?$'
|
||||
# IncludeIsMainSourceRegex: ''
|
||||
# IndentAccessModifiers: false
|
||||
IndentCaseLabels: true
|
||||
# IndentCaseBlocks: false
|
||||
# IndentGotoLabels: true
|
||||
# IndentPPDirectives: None
|
||||
# IndentExternBlock: AfterExternBlock
|
||||
# IndentRequires: false
|
||||
IndentWidth: 4
|
||||
# IndentWrappedFunctionNames: false
|
||||
# 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
|
||||
# UseCRLF: false
|
||||
UseTab: Always
|
||||
# WhitespaceSensitiveMacros:
|
||||
# - STRINGIZE
|
||||
# - PP_STRINGIZE
|
||||
# - BOOST_PP_STRINGIZE
|
||||
# - NS_SWIFT_NAME
|
||||
# - CF_SWIFT_NAME
|
||||
---
|
||||
### C++ specific config ###
|
||||
Language: Cpp
|
||||
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']
|
||||
...
|
||||
- Regex: '".*"'
|
||||
Priority: 1
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*'
|
||||
Priority: 3
|
||||
#IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
#KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
PenaltyReturnTypeOnItsOwnLine: 0
|
||||
TabWidth: 4
|
||||
Language: Cpp
|
||||
Standard: c++20
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
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,3 +1,4 @@
|
|||
.idea/
|
||||
.cache/
|
||||
build/
|
||||
.vs/
|
||||
|
|
@ -1,13 +1,22 @@
|
|||
# CMakeLists.txt ; Top-level CMake project file.
|
||||
|
||||
cmake_minimum_required( VERSION 3.13 )
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
project( Aster VERSION 0.1.0 )
|
||||
project(Aster VERSION 0.1.0)
|
||||
|
||||
set( CMAKE_CXX_STANDARD 23 )
|
||||
set( CMAKE_CXX_STANDARD_REQUIRED ON )
|
||||
set( CMAKE_CXX_EXTENSIONS OFF )
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
set( CMAKE_CXX_FLAGS -Wall )
|
||||
if (MSVC)
|
||||
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 ()
|
||||
|
||||
add_subdirectory( "aster_core" )
|
||||
include(add_shader.cmake)
|
||||
|
||||
add_subdirectory("aster")
|
||||
add_subdirectory("samples")
|
||||
|
|
|
|||
|
|
@ -12,6 +12,16 @@
|
|||
"CMAKE_CXX_COMPILER": "/usr/bin/clang++",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
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)
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# CMakeList.txt ; CMake project for Aster Core
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
find_package(glfw3 CONFIG REQUIRED)
|
||||
find_path(SCOTTT_DEBUGBREAK_INCLUDE_DIRS "debugbreak.h")
|
||||
find_package(Vulkan REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
find_package(VulkanMemoryAllocator CONFIG REQUIRED)
|
||||
find_package(EASTL CONFIG REQUIRED)
|
||||
|
||||
set(HEADER_FILES
|
||||
constants.h
|
||||
config.h
|
||||
logger.h
|
||||
global.h
|
||||
context.h
|
||||
window.h
|
||||
physical_device.h
|
||||
device.h
|
||||
swapchain.h
|
||||
"pipeline.h"
|
||||
queue_allocation.h
|
||||
buffer.h)
|
||||
|
||||
set(SOURCE_FILES
|
||||
logger.cpp
|
||||
global.cpp
|
||||
context.cpp
|
||||
window.cpp
|
||||
physical_device.cpp
|
||||
device.cpp
|
||||
swapchain.cpp
|
||||
pipeline.cpp
|
||||
buffer.cpp)
|
||||
|
||||
add_library(aster_core STATIC ${SOURCE_FILES} ${HEADER_FILES})
|
||||
set_property(TARGET aster_core PROPERTY CXX_STANDARD 20)
|
||||
|
||||
target_precompile_headers(aster_core PUBLIC global.h)
|
||||
|
||||
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_include_directories(aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS})
|
||||
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)
|
||||
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// =============================================
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -11,7 +11,12 @@
|
|||
#define GLFW_INCLUDE_VULKAN
|
||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
|
||||
#define VULKAN_HPP_NO_EXCEPTIONS
|
||||
#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)
|
||||
#define USE_OPTICK (0)
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
};
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
// =============================================
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// =============================================
|
||||
// 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];
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
// =============================================
|
||||
// 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())); });
|
||||
}
|
||||
};
|
||||
|
|
@ -5,13 +5,16 @@
|
|||
|
||||
#include "logger.h"
|
||||
|
||||
Logger g_logger = Logger();
|
||||
Logger g_Logger = Logger();
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
|
||||
/* Credits to Const-me */
|
||||
//namespace eastl {
|
||||
// void __cdecl AssertionFailure(const char* af)
|
||||
// {
|
||||
// ERROR(af);
|
||||
// __debugbreak();
|
||||
// }
|
||||
//}
|
||||
namespace eastl
|
||||
{
|
||||
void
|
||||
AssertionFailure(const char *af)
|
||||
{
|
||||
ERROR("{}", af);
|
||||
debug_break();
|
||||
}
|
||||
} // namespace eastl
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
// =============================================
|
||||
// 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)
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
};
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
};
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// =============================================
|
||||
// Aster: queue_allocation.h
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "global.h"
|
||||
|
||||
struct QueueAllocation
|
||||
{
|
||||
u32 m_Family;
|
||||
u32 m_Count;
|
||||
};
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/// =============================================
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/// =============================================
|
||||
// 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();
|
||||
};
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
};
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# CMakeList.txt ; CMake project for Aster Core
|
||||
|
||||
cmake_minimum_required( VERSION 3.13 )
|
||||
|
||||
find_package( glm CONFIG REQUIRED )
|
||||
find_package( glfw3 CONFIG REQUIRED )
|
||||
find_path( SCOTTT_DEBUGBREAK_INCLUDE_DIRS "debugbreak.h" )
|
||||
find_package( Vulkan REQUIRED )
|
||||
# find_package( VulkanHeaders CONFIG REQUIRED )
|
||||
find_package( VulkanMemoryAllocator CONFIG REQUIRED )
|
||||
|
||||
set( HEADER_FILES "constants.h" "config.h" "logger.h" "global.h" "context.h" "window.h" )
|
||||
set( SOURCE_FILES "logger.cpp" "global.cpp" "context.cpp" "window.cpp" )
|
||||
|
||||
add_library( aster_core ${SOURCE_FILES} ${HEADER_FILES} )
|
||||
set_property( TARGET aster_core PROPERTY CXX_STANDARD 23 )
|
||||
|
||||
target_link_libraries( aster_core PRIVATE glm::glm-header-only )
|
||||
target_link_libraries( aster_core PRIVATE glfw )
|
||||
target_include_directories( aster_core PRIVATE ${SCOTTT_DEBUGBREAK_INCLUDE_DIRS})
|
||||
target_link_libraries( aster_core PRIVATE Vulkan::Vulkan 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 )
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#include <iostream>
|
||||
#include "constants.h"
|
||||
#include "glfw_context.h"
|
||||
#include "window.h"
|
||||
|
||||
int main(int, char**) {
|
||||
|
||||
GlfwContext* glfw = new GlfwContext();
|
||||
Context* context = new Context("Aster", VERSION);
|
||||
Window* window = new Window("Aster1", context, { 640, 480 });
|
||||
|
||||
delete window;
|
||||
delete context;
|
||||
delete glfw;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
// =============================================
|
||||
// Aster: context.cpp
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#include "context.h"
|
||||
|
||||
VKAPI_ATTR b32 VKAPI_CALL Context::debug_callback(VkDebugUtilsMessageSeverityFlagBitsEXT _message_severity, 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) {
|
||||
vk::Result result;
|
||||
|
||||
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 = nullptr;
|
||||
|
||||
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);
|
||||
|
||||
tie(result, instance) = vk::createInstance({
|
||||
.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(),
|
||||
});
|
||||
ERROR_IF(failed(result), "Failed to create Vulkan instance with "s + to_string(result))
|
||||
THEN_CRASH(result)
|
||||
ELSE_INFO("Instance Created.");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||
|
||||
// Debug Messenger
|
||||
if (enable_validation_layers) {
|
||||
tie(result, debug_messenger) = instance.createDebugUtilsMessengerEXT(debug_messenger_create_info);
|
||||
ERROR_IF(failed(result), "Debug Messenger creation failed with "s + to_string(result))
|
||||
ELSE_INFO("Debug Messenger Created.");
|
||||
}
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
if (instance) {
|
||||
if (enable_validation_layers && debug_messenger) {
|
||||
instance.destroyDebugUtilsMessengerEXT(debug_messenger);
|
||||
}
|
||||
instance.destroy();
|
||||
INFO("Context destroyed");
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
// =============================================
|
||||
// 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::Instance instance;
|
||||
vk::DebugUtilsMessengerEXT debug_messenger;
|
||||
|
||||
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);
|
||||
};
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
// =============================================
|
||||
// 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;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// =============================================
|
||||
// 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
|
||||
|
||||
//int Vsnprintf8(char* pDestination, size_t n, const char* pFormat, va_list arguments) {
|
||||
// //ERROR_IF(pDestination == nullptr, "Null buffer") THEN_CRASH(1) ELSE_IF_ERROR(n == 0, "Empty buffer") THEN_CRASH(1);
|
||||
//#ifdef _MSC_VER
|
||||
// auto v = vsnprintf(pDestination, n, pFormat, arguments);
|
||||
// ERROR_IF(v == 0, "Final requirement cannot be 0") THEN_CRASH(1);
|
||||
// return v;
|
||||
//#else
|
||||
// return vsnprintf(pDestination, n, pFormat, arguments);
|
||||
//#endif
|
||||
//}
|
||||
//
|
||||
//int VsnprintfW(wchar_t* pDestination, size_t n, const wchar_t* pFormat, va_list arguments) {
|
||||
// //ERROR_IF(pDestination == nullptr, "Null buffer") THEN_CRASH(1) ELSE_IF_ERROR(n == 0, "Empty buffer") THEN_CRASH(1);
|
||||
//#ifdef _MSC_VER
|
||||
// if (pDestination == nullptr && n == 0) {
|
||||
// return _vscwprintf(pFormat, arguments);
|
||||
// } else {
|
||||
// return _vsnwprintf_s(pDestination, n, _TRUNCATE, pFormat, arguments);
|
||||
// }
|
||||
//#else
|
||||
// char* d = new char[n + 1];
|
||||
// int r = vsnprintf(d, n, convertstring<char16_t, char>(pFormat).c_str(), arguments);
|
||||
// memcpy(pDestination, convertstring<char, char16_t>(d).c_str(), (n + 1) * sizeof(char16_t));
|
||||
// delete[] d;
|
||||
// return r;
|
||||
//#endif
|
||||
//}
|
||||
//
|
||||
//int Vsnprintf16(char16_t* pDestination, size_t n, const char16_t* pFormat, va_list arguments) {
|
||||
// //ERROR_IF(pDestination == nullptr, "Null buffer") THEN_CRASH(1) ELSE_IF_ERROR(n == 0, "Empty buffer") THEN_CRASH(1);
|
||||
//#ifdef _MSC_VER
|
||||
// if (pDestination == nullptr && n == 0) {
|
||||
// return _vscwprintf((wchar_t*)pFormat, arguments);
|
||||
// } else {
|
||||
// return _vsnwprintf_s((wchar_t*)pDestination, n, _TRUNCATE, (wchar_t*)pFormat, arguments);
|
||||
// }
|
||||
//#else
|
||||
// char* d = new char[n + 1];
|
||||
// int r = vsnprintf(d, n, convertstring<char16_t, char>(pFormat).c_str(), arguments);
|
||||
// memcpy(pDestination, convertstring<char, char16_t>(d).c_str(), (n + 1) * sizeof(char16_t));
|
||||
// delete[] d;
|
||||
// return r;
|
||||
//#endif
|
||||
//}
|
||||
|
||||
std::string std::impl::format(const char *_fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, _fmt);
|
||||
|
||||
const auto req = vsnprintf(nullptr, 0, _fmt, args) + 1;
|
||||
string buf(req, '\0');
|
||||
vsnprintf(buf.data(), buf.size(), _fmt, args);
|
||||
|
||||
va_end(args);
|
||||
return buf;
|
||||
}
|
||||
|
|
@ -1,100 +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 <glm/gtc/matrix_transform.hpp>
|
||||
#include <string>
|
||||
|
||||
#define VULKAN_HPP_ASSERT(expr) DEBUG_IF(!(expr), "Vulkan assert failed")
|
||||
#include <vulkan/vulkan.hpp>
|
||||
#include <vk_mem_alloc.h>
|
||||
|
||||
#define CODE_LOC " @ " __FILE__ ":" VULKAN_HPP_STRINGIFY(__LINE__)
|
||||
[[nodiscard]] inline bool failed(const vk::Result _result) {
|
||||
return _result != vk::Result::eSuccess;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace impl {
|
||||
string format(const char *_fmt, ...);
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
[[nodiscard]] string fmt(const char *_fmt, Ts &&..._args) {
|
||||
return impl::format(_fmt, forward<Ts>(_args)...);
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
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<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,178 +0,0 @@
|
|||
// =============================================
|
||||
// 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]: ";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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, 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)
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
// =============================================
|
||||
// Aster: window.cpp
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#include "window.h"
|
||||
#include "logger.h"
|
||||
#include "context.h"
|
||||
#include "glfw_context.h"
|
||||
|
||||
Window::Window(const std::string_view& _title, Context* _context, vk::Extent2D _extent, b8 _full_screen)
|
||||
: parent_context{ std::move(_context) }
|
||||
, 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(extent.width, extent.height, name.data(), full_screen ? monitor : nullptr, nullptr);
|
||||
ERROR_IF(window == nullptr, "Window creation failed") ELSE_INFO(std::fmt("Window '%s' created with resolution '%dx%d'", name.data(), extent.width, extent.height));
|
||||
if (window == nullptr) {
|
||||
auto code = GlfwContext::post_error();
|
||||
glfwTerminate();
|
||||
CRASH(code);
|
||||
}
|
||||
|
||||
if (full_screen == false) {
|
||||
glfwSetWindowPos(window, (w_ - extent.width) / 2, (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_));
|
||||
ERROR_IF(failed(result), "Failed to create Surface with "s + to_string(result)) THEN_CRASH(result) ELSE_INFO("Surface Created");
|
||||
surface = vk::SurfaceKHR(surface_);
|
||||
}
|
||||
|
||||
Window::Window(Window&& _other) noexcept: parent_context{ std::move(_other.parent_context) }
|
||||
, 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;
|
||||
parent_context = std::move(_other.parent_context);
|
||||
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 (parent_context && surface) {
|
||||
parent_context->instance.destroy(surface);
|
||||
INFO("Surface Destroyed");
|
||||
}
|
||||
|
||||
if (window != nullptr) {
|
||||
glfwDestroyWindow(window);
|
||||
window = nullptr;
|
||||
}
|
||||
monitor = nullptr;
|
||||
|
||||
INFO("Window '" + name + "' Destroyed");
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// =============================================
|
||||
// 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();
|
||||
|
||||
bool should_close() const noexcept {
|
||||
return glfwWindowShouldClose(window);
|
||||
}
|
||||
|
||||
bool poll() const noexcept {
|
||||
glfwPollEvents();
|
||||
return !glfwWindowShouldClose(window);
|
||||
}
|
||||
|
||||
void set_window_size(const vk::Extent2D& _extent) noexcept {
|
||||
extent = _extent;
|
||||
glfwSetWindowSize(window, extent.width, extent.height);
|
||||
}
|
||||
|
||||
void set_window_size(const u32 _width, const u32 _height) noexcept {
|
||||
set_window_size({ _width, _height });
|
||||
}
|
||||
|
||||
// fields
|
||||
Context* parent_context{};
|
||||
|
||||
GLFWwindow* window{ nullptr };
|
||||
GLFWmonitor* monitor{ nullptr };
|
||||
vk::SurfaceKHR surface;
|
||||
vk::Extent2D extent;
|
||||
std::string name;
|
||||
b8 full_screen{ false };
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# 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})
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
// =============================================
|
||||
// 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// =============================================
|
||||
// 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);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# 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)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
layout (location = 0) in vec3 inColor;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(inColor, 1.0);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#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); //*/
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -0,0 +1,540 @@
|
|||
// =============================================
|
||||
// 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");
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
# CMakeList.txt ; CMake project for Samples
|
||||
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
add_subdirectory("01_triangle")
|
||||
add_subdirectory("00_util")
|
||||
|
||||
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
{
|
||||
"dependencies": [
|
||||
"fmt",
|
||||
"glfw3",
|
||||
"glm",
|
||||
"scottt-debugbreak",
|
||||
"vulkan-memory-allocator"
|
||||
"vulkan-memory-allocator",
|
||||
"eastl"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue