first release
This commit is contained in:
334
.gitignore
vendored
Normal file
334
.gitignore
vendored
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
/cmake-build-debug/
|
||||||
|
/bin/
|
||||||
|
|
||||||
|
perf.csv
|
||||||
15
.gitmodules
vendored
Normal file
15
.gitmodules
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[submodule "external/spdlog"]
|
||||||
|
path = external/spdlog
|
||||||
|
url = https://github.com/gabime/spdlog
|
||||||
|
[submodule "external/glm"]
|
||||||
|
path = external/glm
|
||||||
|
url = https://github.com/g-truc/glm
|
||||||
|
[submodule "external/glfw"]
|
||||||
|
path = external/glfw
|
||||||
|
url = https://github.com/glfw/glfw
|
||||||
|
[submodule "external/fmt"]
|
||||||
|
path = external/fmt
|
||||||
|
url = https://github.com/fmtlib/fmt
|
||||||
|
[submodule "external/assimp"]
|
||||||
|
path = external/assimp
|
||||||
|
url = https://github.com/assimp/assimp
|
||||||
56
CMakeLists.txt
Normal file
56
CMakeLists.txt
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.7 FATAL_ERROR)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_SOURCE_DIR}/bin/release")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_SOURCE_DIR}/bin/debug")
|
||||||
|
set(CMAKE_DEBUG_POSTFIX "d")
|
||||||
|
|
||||||
|
project (openVulkanoCpp)
|
||||||
|
add_executable(openVulkanoCpp openVulkanoCpp/main.cpp)
|
||||||
|
|
||||||
|
#Setup Vulkan
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
set(Vulkan_LIBRARIES Vulkan::Vulkan)
|
||||||
|
target_link_libraries(openVulkanoCpp PRIVATE ${Vulkan_LIBRARIES})
|
||||||
|
target_include_directories(openVulkanoCpp PUBLIC ${Vulkan_INCLUDE_DIR})
|
||||||
|
|
||||||
|
if (ANDROID)
|
||||||
|
add_definitions(-DVK_USE_PLATFORM_ANDROID_KHR)
|
||||||
|
elseif (WIN32)
|
||||||
|
add_definitions(-DVK_USE_PLATFORM_WIN32_KHR)
|
||||||
|
else()
|
||||||
|
add_definitions(-DVK_USE_PLATFORM_XCB_KHR)
|
||||||
|
find_package(XCB REQUIRED)
|
||||||
|
link_libraries(${XCB_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_property(TARGET openVulkanoCpp PROPERTY CXX_STANDARD 14)
|
||||||
|
target_compile_options(openVulkanoCpp PRIVATE -Wall)
|
||||||
|
|
||||||
|
# glfw
|
||||||
|
if (NOT ANDROID)
|
||||||
|
add_subdirectory(external/glfw EXCLUDE_FROM_ALL)
|
||||||
|
add_dependencies(openVulkanoCpp glfw)
|
||||||
|
target_include_directories(openVulkanoCpp PUBLIC external/glfw/include/GLFW)
|
||||||
|
target_link_libraries(openVulkanoCpp PRIVATE glfw)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# glm
|
||||||
|
add_subdirectory(external/glm EXCLUDE_FROM_ALL)
|
||||||
|
target_link_libraries(openVulkanoCpp PRIVATE glm)
|
||||||
|
|
||||||
|
# fmt
|
||||||
|
#add_subdirectory(external/fmt EXCLUDE_FROM_ALL)
|
||||||
|
#target_link_libraries(openVulkanoCpp PRIVATE fmt)
|
||||||
|
|
||||||
|
# spdlog
|
||||||
|
add_subdirectory(external/spdlog EXCLUDE_FROM_ALL)
|
||||||
|
target_link_libraries(openVulkanoCpp PRIVATE spdlog)
|
||||||
|
#add_definitions(-SPDLOG_FMT_EXTERNAL)
|
||||||
|
|
||||||
|
# assimp
|
||||||
|
add_subdirectory(external/assimp EXCLUDE_FROM_ALL)
|
||||||
|
target_link_libraries(openVulkanoCpp PRIVATE assimp)
|
||||||
|
|
||||||
|
target_sources(openVulkanoCpp PRIVATE openVulkanoCpp/Vulkan/FrameBuffer.cpp)
|
||||||
373
LICENSE
Normal file
373
LICENSE
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
Mozilla Public License Version 2.0
|
||||||
|
==================================
|
||||||
|
|
||||||
|
1. Definitions
|
||||||
|
--------------
|
||||||
|
|
||||||
|
1.1. "Contributor"
|
||||||
|
means each individual or legal entity that creates, contributes to
|
||||||
|
the creation of, or owns Covered Software.
|
||||||
|
|
||||||
|
1.2. "Contributor Version"
|
||||||
|
means the combination of the Contributions of others (if any) used
|
||||||
|
by a Contributor and that particular Contributor's Contribution.
|
||||||
|
|
||||||
|
1.3. "Contribution"
|
||||||
|
means Covered Software of a particular Contributor.
|
||||||
|
|
||||||
|
1.4. "Covered Software"
|
||||||
|
means Source Code Form to which the initial Contributor has attached
|
||||||
|
the notice in Exhibit A, the Executable Form of such Source Code
|
||||||
|
Form, and Modifications of such Source Code Form, in each case
|
||||||
|
including portions thereof.
|
||||||
|
|
||||||
|
1.5. "Incompatible With Secondary Licenses"
|
||||||
|
means
|
||||||
|
|
||||||
|
(a) that the initial Contributor has attached the notice described
|
||||||
|
in Exhibit B to the Covered Software; or
|
||||||
|
|
||||||
|
(b) that the Covered Software was made available under the terms of
|
||||||
|
version 1.1 or earlier of the License, but not also under the
|
||||||
|
terms of a Secondary License.
|
||||||
|
|
||||||
|
1.6. "Executable Form"
|
||||||
|
means any form of the work other than Source Code Form.
|
||||||
|
|
||||||
|
1.7. "Larger Work"
|
||||||
|
means a work that combines Covered Software with other material, in
|
||||||
|
a separate file or files, that is not Covered Software.
|
||||||
|
|
||||||
|
1.8. "License"
|
||||||
|
means this document.
|
||||||
|
|
||||||
|
1.9. "Licensable"
|
||||||
|
means having the right to grant, to the maximum extent possible,
|
||||||
|
whether at the time of the initial grant or subsequently, any and
|
||||||
|
all of the rights conveyed by this License.
|
||||||
|
|
||||||
|
1.10. "Modifications"
|
||||||
|
means any of the following:
|
||||||
|
|
||||||
|
(a) any file in Source Code Form that results from an addition to,
|
||||||
|
deletion from, or modification of the contents of Covered
|
||||||
|
Software; or
|
||||||
|
|
||||||
|
(b) any new file in Source Code Form that contains any Covered
|
||||||
|
Software.
|
||||||
|
|
||||||
|
1.11. "Patent Claims" of a Contributor
|
||||||
|
means any patent claim(s), including without limitation, method,
|
||||||
|
process, and apparatus claims, in any patent Licensable by such
|
||||||
|
Contributor that would be infringed, but for the grant of the
|
||||||
|
License, by the making, using, selling, offering for sale, having
|
||||||
|
made, import, or transfer of either its Contributions or its
|
||||||
|
Contributor Version.
|
||||||
|
|
||||||
|
1.12. "Secondary License"
|
||||||
|
means either the GNU General Public License, Version 2.0, the GNU
|
||||||
|
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||||
|
Public License, Version 3.0, or any later versions of those
|
||||||
|
licenses.
|
||||||
|
|
||||||
|
1.13. "Source Code Form"
|
||||||
|
means the form of the work preferred for making modifications.
|
||||||
|
|
||||||
|
1.14. "You" (or "Your")
|
||||||
|
means an individual or a legal entity exercising rights under this
|
||||||
|
License. For legal entities, "You" includes any entity that
|
||||||
|
controls, is controlled by, or is under common control with You. For
|
||||||
|
purposes of this definition, "control" means (a) the power, direct
|
||||||
|
or indirect, to cause the direction or management of such entity,
|
||||||
|
whether by contract or otherwise, or (b) ownership of more than
|
||||||
|
fifty percent (50%) of the outstanding shares or beneficial
|
||||||
|
ownership of such entity.
|
||||||
|
|
||||||
|
2. License Grants and Conditions
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
2.1. Grants
|
||||||
|
|
||||||
|
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||||
|
non-exclusive license:
|
||||||
|
|
||||||
|
(a) under intellectual property rights (other than patent or trademark)
|
||||||
|
Licensable by such Contributor to use, reproduce, make available,
|
||||||
|
modify, display, perform, distribute, and otherwise exploit its
|
||||||
|
Contributions, either on an unmodified basis, with Modifications, or
|
||||||
|
as part of a Larger Work; and
|
||||||
|
|
||||||
|
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||||
|
for sale, have made, import, and otherwise transfer either its
|
||||||
|
Contributions or its Contributor Version.
|
||||||
|
|
||||||
|
2.2. Effective Date
|
||||||
|
|
||||||
|
The licenses granted in Section 2.1 with respect to any Contribution
|
||||||
|
become effective for each Contribution on the date the Contributor first
|
||||||
|
distributes such Contribution.
|
||||||
|
|
||||||
|
2.3. Limitations on Grant Scope
|
||||||
|
|
||||||
|
The licenses granted in this Section 2 are the only rights granted under
|
||||||
|
this License. No additional rights or licenses will be implied from the
|
||||||
|
distribution or licensing of Covered Software under this License.
|
||||||
|
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||||
|
Contributor:
|
||||||
|
|
||||||
|
(a) for any code that a Contributor has removed from Covered Software;
|
||||||
|
or
|
||||||
|
|
||||||
|
(b) for infringements caused by: (i) Your and any other third party's
|
||||||
|
modifications of Covered Software, or (ii) the combination of its
|
||||||
|
Contributions with other software (except as part of its Contributor
|
||||||
|
Version); or
|
||||||
|
|
||||||
|
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||||
|
its Contributions.
|
||||||
|
|
||||||
|
This License does not grant any rights in the trademarks, service marks,
|
||||||
|
or logos of any Contributor (except as may be necessary to comply with
|
||||||
|
the notice requirements in Section 3.4).
|
||||||
|
|
||||||
|
2.4. Subsequent Licenses
|
||||||
|
|
||||||
|
No Contributor makes additional grants as a result of Your choice to
|
||||||
|
distribute the Covered Software under a subsequent version of this
|
||||||
|
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||||
|
permitted under the terms of Section 3.3).
|
||||||
|
|
||||||
|
2.5. Representation
|
||||||
|
|
||||||
|
Each Contributor represents that the Contributor believes its
|
||||||
|
Contributions are its original creation(s) or it has sufficient rights
|
||||||
|
to grant the rights to its Contributions conveyed by this License.
|
||||||
|
|
||||||
|
2.6. Fair Use
|
||||||
|
|
||||||
|
This License is not intended to limit any rights You have under
|
||||||
|
applicable copyright doctrines of fair use, fair dealing, or other
|
||||||
|
equivalents.
|
||||||
|
|
||||||
|
2.7. Conditions
|
||||||
|
|
||||||
|
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||||
|
in Section 2.1.
|
||||||
|
|
||||||
|
3. Responsibilities
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
3.1. Distribution of Source Form
|
||||||
|
|
||||||
|
All distribution of Covered Software in Source Code Form, including any
|
||||||
|
Modifications that You create or to which You contribute, must be under
|
||||||
|
the terms of this License. You must inform recipients that the Source
|
||||||
|
Code Form of the Covered Software is governed by the terms of this
|
||||||
|
License, and how they can obtain a copy of this License. You may not
|
||||||
|
attempt to alter or restrict the recipients' rights in the Source Code
|
||||||
|
Form.
|
||||||
|
|
||||||
|
3.2. Distribution of Executable Form
|
||||||
|
|
||||||
|
If You distribute Covered Software in Executable Form then:
|
||||||
|
|
||||||
|
(a) such Covered Software must also be made available in Source Code
|
||||||
|
Form, as described in Section 3.1, and You must inform recipients of
|
||||||
|
the Executable Form how they can obtain a copy of such Source Code
|
||||||
|
Form by reasonable means in a timely manner, at a charge no more
|
||||||
|
than the cost of distribution to the recipient; and
|
||||||
|
|
||||||
|
(b) You may distribute such Executable Form under the terms of this
|
||||||
|
License, or sublicense it under different terms, provided that the
|
||||||
|
license for the Executable Form does not attempt to limit or alter
|
||||||
|
the recipients' rights in the Source Code Form under this License.
|
||||||
|
|
||||||
|
3.3. Distribution of a Larger Work
|
||||||
|
|
||||||
|
You may create and distribute a Larger Work under terms of Your choice,
|
||||||
|
provided that You also comply with the requirements of this License for
|
||||||
|
the Covered Software. If the Larger Work is a combination of Covered
|
||||||
|
Software with a work governed by one or more Secondary Licenses, and the
|
||||||
|
Covered Software is not Incompatible With Secondary Licenses, this
|
||||||
|
License permits You to additionally distribute such Covered Software
|
||||||
|
under the terms of such Secondary License(s), so that the recipient of
|
||||||
|
the Larger Work may, at their option, further distribute the Covered
|
||||||
|
Software under the terms of either this License or such Secondary
|
||||||
|
License(s).
|
||||||
|
|
||||||
|
3.4. Notices
|
||||||
|
|
||||||
|
You may not remove or alter the substance of any license notices
|
||||||
|
(including copyright notices, patent notices, disclaimers of warranty,
|
||||||
|
or limitations of liability) contained within the Source Code Form of
|
||||||
|
the Covered Software, except that You may alter any license notices to
|
||||||
|
the extent required to remedy known factual inaccuracies.
|
||||||
|
|
||||||
|
3.5. Application of Additional Terms
|
||||||
|
|
||||||
|
You may choose to offer, and to charge a fee for, warranty, support,
|
||||||
|
indemnity or liability obligations to one or more recipients of Covered
|
||||||
|
Software. However, You may do so only on Your own behalf, and not on
|
||||||
|
behalf of any Contributor. You must make it absolutely clear that any
|
||||||
|
such warranty, support, indemnity, or liability obligation is offered by
|
||||||
|
You alone, and You hereby agree to indemnify every Contributor for any
|
||||||
|
liability incurred by such Contributor as a result of warranty, support,
|
||||||
|
indemnity or liability terms You offer. You may include additional
|
||||||
|
disclaimers of warranty and limitations of liability specific to any
|
||||||
|
jurisdiction.
|
||||||
|
|
||||||
|
4. Inability to Comply Due to Statute or Regulation
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
If it is impossible for You to comply with any of the terms of this
|
||||||
|
License with respect to some or all of the Covered Software due to
|
||||||
|
statute, judicial order, or regulation then You must: (a) comply with
|
||||||
|
the terms of this License to the maximum extent possible; and (b)
|
||||||
|
describe the limitations and the code they affect. Such description must
|
||||||
|
be placed in a text file included with all distributions of the Covered
|
||||||
|
Software under this License. Except to the extent prohibited by statute
|
||||||
|
or regulation, such description must be sufficiently detailed for a
|
||||||
|
recipient of ordinary skill to be able to understand it.
|
||||||
|
|
||||||
|
5. Termination
|
||||||
|
--------------
|
||||||
|
|
||||||
|
5.1. The rights granted under this License will terminate automatically
|
||||||
|
if You fail to comply with any of its terms. However, if You become
|
||||||
|
compliant, then the rights granted under this License from a particular
|
||||||
|
Contributor are reinstated (a) provisionally, unless and until such
|
||||||
|
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||||
|
ongoing basis, if such Contributor fails to notify You of the
|
||||||
|
non-compliance by some reasonable means prior to 60 days after You have
|
||||||
|
come back into compliance. Moreover, Your grants from a particular
|
||||||
|
Contributor are reinstated on an ongoing basis if such Contributor
|
||||||
|
notifies You of the non-compliance by some reasonable means, this is the
|
||||||
|
first time You have received notice of non-compliance with this License
|
||||||
|
from such Contributor, and You become compliant prior to 30 days after
|
||||||
|
Your receipt of the notice.
|
||||||
|
|
||||||
|
5.2. If You initiate litigation against any entity by asserting a patent
|
||||||
|
infringement claim (excluding declaratory judgment actions,
|
||||||
|
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||||
|
directly or indirectly infringes any patent, then the rights granted to
|
||||||
|
You by any and all Contributors for the Covered Software under Section
|
||||||
|
2.1 of this License shall terminate.
|
||||||
|
|
||||||
|
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||||
|
end user license agreements (excluding distributors and resellers) which
|
||||||
|
have been validly granted by You or Your distributors under this License
|
||||||
|
prior to termination shall survive termination.
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 6. Disclaimer of Warranty *
|
||||||
|
* ------------------------- *
|
||||||
|
* *
|
||||||
|
* Covered Software is provided under this License on an "as is" *
|
||||||
|
* basis, without warranty of any kind, either expressed, implied, or *
|
||||||
|
* statutory, including, without limitation, warranties that the *
|
||||||
|
* Covered Software is free of defects, merchantable, fit for a *
|
||||||
|
* particular purpose or non-infringing. The entire risk as to the *
|
||||||
|
* quality and performance of the Covered Software is with You. *
|
||||||
|
* Should any Covered Software prove defective in any respect, You *
|
||||||
|
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||||
|
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||||
|
* essential part of this License. No use of any Covered Software is *
|
||||||
|
* authorized under this License except under this disclaimer. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
************************************************************************
|
||||||
|
* *
|
||||||
|
* 7. Limitation of Liability *
|
||||||
|
* -------------------------- *
|
||||||
|
* *
|
||||||
|
* Under no circumstances and under no legal theory, whether tort *
|
||||||
|
* (including negligence), contract, or otherwise, shall any *
|
||||||
|
* Contributor, or anyone who distributes Covered Software as *
|
||||||
|
* permitted above, be liable to You for any direct, indirect, *
|
||||||
|
* special, incidental, or consequential damages of any character *
|
||||||
|
* including, without limitation, damages for lost profits, loss of *
|
||||||
|
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||||
|
* and all other commercial damages or losses, even if such party *
|
||||||
|
* shall have been informed of the possibility of such damages. This *
|
||||||
|
* limitation of liability shall not apply to liability for death or *
|
||||||
|
* personal injury resulting from such party's negligence to the *
|
||||||
|
* extent applicable law prohibits such limitation. Some *
|
||||||
|
* jurisdictions do not allow the exclusion or limitation of *
|
||||||
|
* incidental or consequential damages, so this exclusion and *
|
||||||
|
* limitation may not apply to You. *
|
||||||
|
* *
|
||||||
|
************************************************************************
|
||||||
|
|
||||||
|
8. Litigation
|
||||||
|
-------------
|
||||||
|
|
||||||
|
Any litigation relating to this License may be brought only in the
|
||||||
|
courts of a jurisdiction where the defendant maintains its principal
|
||||||
|
place of business and such litigation shall be governed by laws of that
|
||||||
|
jurisdiction, without reference to its conflict-of-law provisions.
|
||||||
|
Nothing in this Section shall prevent a party's ability to bring
|
||||||
|
cross-claims or counter-claims.
|
||||||
|
|
||||||
|
9. Miscellaneous
|
||||||
|
----------------
|
||||||
|
|
||||||
|
This License represents the complete agreement concerning the subject
|
||||||
|
matter hereof. If any provision of this License is held to be
|
||||||
|
unenforceable, such provision shall be reformed only to the extent
|
||||||
|
necessary to make it enforceable. Any law or regulation which provides
|
||||||
|
that the language of a contract shall be construed against the drafter
|
||||||
|
shall not be used to construe this License against a Contributor.
|
||||||
|
|
||||||
|
10. Versions of the License
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
10.1. New Versions
|
||||||
|
|
||||||
|
Mozilla Foundation is the license steward. Except as provided in Section
|
||||||
|
10.3, no one other than the license steward has the right to modify or
|
||||||
|
publish new versions of this License. Each version will be given a
|
||||||
|
distinguishing version number.
|
||||||
|
|
||||||
|
10.2. Effect of New Versions
|
||||||
|
|
||||||
|
You may distribute the Covered Software under the terms of the version
|
||||||
|
of the License under which You originally received the Covered Software,
|
||||||
|
or under the terms of any subsequent version published by the license
|
||||||
|
steward.
|
||||||
|
|
||||||
|
10.3. Modified Versions
|
||||||
|
|
||||||
|
If you create software not governed by this License, and you want to
|
||||||
|
create a new license for such software, you may create and use a
|
||||||
|
modified version of this License if you rename the license and remove
|
||||||
|
any references to the name of the license steward (except to note that
|
||||||
|
such modified license differs from this License).
|
||||||
|
|
||||||
|
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||||
|
Licenses
|
||||||
|
|
||||||
|
If You choose to distribute Source Code Form that is Incompatible With
|
||||||
|
Secondary Licenses under the terms of this version of the License, the
|
||||||
|
notice described in Exhibit B of this License must be attached.
|
||||||
|
|
||||||
|
Exhibit A - Source Code Form License Notice
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
If it is not possible or desirable to put the notice in a particular
|
||||||
|
file, then You may include the notice in a location (such as a LICENSE
|
||||||
|
file in a relevant directory) where a recipient would be likely to look
|
||||||
|
for such a notice.
|
||||||
|
|
||||||
|
You may add additional accurate notices of copyright ownership.
|
||||||
|
|
||||||
|
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||||
|
---------------------------------------------------------
|
||||||
|
|
||||||
|
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
defined by the Mozilla Public License, v. 2.0.
|
||||||
17
README.md
Normal file
17
README.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# openVulkano
|
||||||
|
openVulkano is a simple Vulkan rendering engine which is capable of recording command buffers from multiple threads.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
### Visual Studio
|
||||||
|
#### Requierements
|
||||||
|
* Visual Studio 2019, 2017 might work but not tested
|
||||||
|
* [Vulkan SDK v1.1.x](https://vulkan.lunarg.com/sdk/home#windows)
|
||||||
|
* [Assimp SDK v4.1](https://github.com/assimp/assimp/releases/download/v4.1.0/assimp-sdk-4.1.0-setup.exe)
|
||||||
|
* git
|
||||||
|
|
||||||
|
#### Build it
|
||||||
|
1. Clone it `git clone --recursive https://github.com/GeorgH93/openVulkano`
|
||||||
|
2. Open the `openVulkanoCpp.sln` with Visual Studio
|
||||||
|
3. If you are not using the default path of Assimp change it in the project properties
|
||||||
|
4. Use Visual Studio to build or run it
|
||||||
|
|
||||||
1
external/assimp
vendored
Submodule
1
external/assimp
vendored
Submodule
Submodule external/assimp added at 799fd74714
1
external/fmt
vendored
Submodule
1
external/fmt
vendored
Submodule
Submodule external/fmt added at 91f7619cc9
1
external/glfw
vendored
Submodule
1
external/glfw
vendored
Submodule
Submodule external/glfw added at d25248343e
1
external/glm
vendored
Submodule
1
external/glm
vendored
Submodule
Submodule external/glm added at ea678faff9
1
external/spdlog
vendored
Submodule
1
external/spdlog
vendored
Submodule
Submodule external/spdlog added at aa65dd8905
25
openVulkanoCpp.sln
Normal file
25
openVulkanoCpp.sln
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.28803.352
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openVulkanoCpp", "openVulkanoCpp\openVulkanoCpp.vcxproj", "{D546A70B-536A-487A-91E1-1CD4563A0104}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{D546A70B-536A-487A-91E1-1CD4563A0104}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{D546A70B-536A-487A-91E1-1CD4563A0104}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{D546A70B-536A-487A-91E1-1CD4563A0104}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{D546A70B-536A-487A-91E1-1CD4563A0104}.Release|x64.Build.0 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {F4F4FB70-77B5-4739-A960-EE5528AB1A4A}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
14
openVulkanoCpp.sln.DotSettings
Normal file
14
openVulkanoCpp.sln.DotSettings
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Classes_0020and_0020structs/@EntryIndexedValue"><NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020fields/@EntryIndexedValue"><NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue"><NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020public_0020fields/@EntryIndexedValue"><NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Enum_0020members/@EntryIndexedValue"><NamingElement Priority="13"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="scoped enumerator" /><type Name="unscoped enumerator" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Namespaces/@EntryIndexedValue"><NamingElement Priority="16"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="namespace" /><type Name="namespace alias" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /></NamingElement></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue"><NamingElement Priority="5"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></NamingElement></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=AABB/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=BORDERLESS/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=GLFW/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=SPDLOG/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vulkan/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Vulkano/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
32
openVulkanoCpp/Base/EngineConfiguration.hpp
Normal file
32
openVulkanoCpp/Base/EngineConfiguration.hpp
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class EngineConfiguration
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
EngineConfiguration() = default;
|
||||||
|
~EngineConfiguration() = default;
|
||||||
|
|
||||||
|
uint32_t numThreads = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static EngineConfiguration* GetEngineConfiguration()
|
||||||
|
{
|
||||||
|
static EngineConfiguration* config = new EngineConfiguration();
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNumThreads(uint32_t numThreads)
|
||||||
|
{
|
||||||
|
this->numThreads = numThreads;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetNumThreads() const
|
||||||
|
{
|
||||||
|
return std::max(static_cast<uint32_t>(1), numThreads);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
31
openVulkanoCpp/Base/EngineConstants.hpp
Normal file
31
openVulkanoCpp/Base/EngineConstants.hpp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <spdlog/fmt/fmt.h> //TODO replace with external fmt
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
#define MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch))
|
||||||
|
|
||||||
|
const char* ENGINE_NAME = "openVulkanoCpp";
|
||||||
|
|
||||||
|
struct EngineVersion
|
||||||
|
{
|
||||||
|
int major, minor, patch;
|
||||||
|
int intVersion;
|
||||||
|
std::string stringVersion;
|
||||||
|
|
||||||
|
EngineVersion(int major, int minor, int patch, int build = 0) : major(major), minor(minor), patch(patch)
|
||||||
|
{
|
||||||
|
intVersion = ((major) << 24) | ((minor) << 16) | (patch);
|
||||||
|
std::string buildConfig = "";
|
||||||
|
#ifdef _DEBUG
|
||||||
|
buildConfig += "-MSVC_DEBUG";
|
||||||
|
#elif DEBUG
|
||||||
|
buildConfig += "-DEBUG";
|
||||||
|
#endif
|
||||||
|
stringVersion = fmt::format("v{0}.{1}.{2}.{3}{4}", major, minor, patch, build, buildConfig);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const EngineVersion ENGINE_VERSION(0, 0, 1);
|
||||||
|
}
|
||||||
12
openVulkanoCpp/Base/ICloseable.hpp
Normal file
12
openVulkanoCpp/Base/ICloseable.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class ICloseable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ICloseable() = default;
|
||||||
|
|
||||||
|
virtual void Close() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
25
openVulkanoCpp/Base/IGraphicsApp.hpp
Normal file
25
openVulkanoCpp/Base/IGraphicsApp.hpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include "IInitable.hpp"
|
||||||
|
#include "ITickable.hpp"
|
||||||
|
#include "ICloseable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class IGraphicsAppManager;
|
||||||
|
|
||||||
|
class IGraphicsApp : public IInitable, public ITickable, public ICloseable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IGraphicsAppManager* manager = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~IGraphicsApp() = default;
|
||||||
|
|
||||||
|
IGraphicsAppManager* GetGraphicsAppManager() const { return manager; }
|
||||||
|
void SetGraphicsAppManager(IGraphicsAppManager* manager) { this->manager = manager; }
|
||||||
|
virtual std::string GetAppName() = 0;
|
||||||
|
virtual std::string GetAppVersion() = 0;
|
||||||
|
virtual int GetAppVersionAsInt() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
30
openVulkanoCpp/Base/IGraphicsAppManager.hpp
Normal file
30
openVulkanoCpp/Base/IGraphicsAppManager.hpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include "PlatformEnums.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class IWindow;
|
||||||
|
class IGraphicsApp;
|
||||||
|
class IRenderer;
|
||||||
|
|
||||||
|
class IGraphicsAppManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IGraphicsAppManager() = default;
|
||||||
|
|
||||||
|
virtual RenderAPI::RenderApi GetRenderApi() const = 0;
|
||||||
|
virtual IGraphicsApp* GetGraphicsApp() const = 0;
|
||||||
|
virtual IRenderer* GetRenderer() const = 0;
|
||||||
|
virtual bool IsRunning() const = 0;
|
||||||
|
virtual bool IsPaused() const = 0;
|
||||||
|
virtual void Stop() = 0;
|
||||||
|
virtual void Run() = 0;
|
||||||
|
virtual void Pause() = 0;
|
||||||
|
virtual void Resume() = 0;
|
||||||
|
|
||||||
|
virtual float GetAvgFrameTime() const = 0;
|
||||||
|
virtual float GetAvgFps() const = 0;
|
||||||
|
virtual uint64_t GetFrameCount() const = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
openVulkanoCpp/Base/IInitable.hpp
Normal file
12
openVulkanoCpp/Base/IInitable.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class IInitable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IInitable() = default;
|
||||||
|
|
||||||
|
virtual void Init() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
12
openVulkanoCpp/Base/ITickable.hpp
Normal file
12
openVulkanoCpp/Base/ITickable.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class ITickable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ITickable() = default;
|
||||||
|
|
||||||
|
virtual void Tick() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
13
openVulkanoCpp/Base/Logger.cpp
Normal file
13
openVulkanoCpp/Base/Logger.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "Logger.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
std::vector<spdlog::sink_ptr> Logger::sinks;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::WINDOW = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::MANAGER = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::RENDER = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::PHYSIC = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::AUDIO = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::DATA = nullptr;
|
||||||
|
std::shared_ptr<spdlog::logger> Logger::SCENE = nullptr;
|
||||||
|
}
|
||||||
95
openVulkanoCpp/Base/Logger.hpp
Normal file
95
openVulkanoCpp/Base/Logger.hpp
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define SPDLOG_DEBUG_ON
|
||||||
|
#define SPDLOG_TRACE_ON
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
#include "spdlog/sinks/null_sink.h"
|
||||||
|
#ifndef NO_CONSOLE_LOG
|
||||||
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
|
#endif
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include "spdlog/sinks/msvc_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class Logger
|
||||||
|
{ //TODO add custom sink for in game/engine console
|
||||||
|
static std::vector<spdlog::sink_ptr> sinks;
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<spdlog::logger> WINDOW;
|
||||||
|
static std::shared_ptr<spdlog::logger> MANAGER;
|
||||||
|
static std::shared_ptr<spdlog::logger> RENDER;
|
||||||
|
static std::shared_ptr<spdlog::logger> PHYSIC;
|
||||||
|
static std::shared_ptr<spdlog::logger> AUDIO;
|
||||||
|
static std::shared_ptr<spdlog::logger> DATA;
|
||||||
|
static std::shared_ptr<spdlog::logger> SCENE;
|
||||||
|
|
||||||
|
static void SetupLogger(std::string logFolder = "logs", std::string logFile = "openVulkano.log")
|
||||||
|
{
|
||||||
|
static bool initialized = false;
|
||||||
|
if (initialized) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{ //TODO allow log files in folders
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logFile, 1024 * 1024 * 512, 3, true));
|
||||||
|
}
|
||||||
|
catch (const spdlog::spdlog_ex& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Log create file log sink: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
#ifndef NO_CONSOLE_LOG
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||||
|
#endif
|
||||||
|
#ifdef _MSC_VER // If it was build with msvc in debug we can use the msvc sink
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
|
||||||
|
#endif
|
||||||
|
// Make sure that there is always a sink for the loggers
|
||||||
|
if (sinks.empty()) sinks.push_back(std::make_shared<spdlog::sinks::null_sink_mt>());
|
||||||
|
|
||||||
|
MANAGER = CreateLogger("manager");
|
||||||
|
WINDOW = CreateLogger("window");
|
||||||
|
RENDER = CreateLogger("render");
|
||||||
|
PHYSIC = CreateLogger("physic");
|
||||||
|
AUDIO = CreateLogger("audio");
|
||||||
|
DATA = CreateLogger("data");
|
||||||
|
SCENE = CreateLogger("scene");
|
||||||
|
|
||||||
|
spdlog::flush_every(std::chrono::seconds(5));
|
||||||
|
|
||||||
|
MANAGER->info("Logger initialized");
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
catch (const spdlog::spdlog_ex& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Log initialization failed: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates a new custom logger that writes to the main log file.
|
||||||
|
* \param name The name of the logger
|
||||||
|
* \param reg If set to true the logger can be accessed again with spdlog::get(name)
|
||||||
|
* \return The created logger
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<spdlog::logger> CreateLogger(const std::string& name, const bool reg = true)
|
||||||
|
{
|
||||||
|
auto logger = std::make_shared<spdlog::logger>(name, sinks.begin(), sinks.end());
|
||||||
|
if (reg) spdlog::register_logger(logger);
|
||||||
|
#ifdef LOG_DATE
|
||||||
|
logger->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [T%t] [%^%l%$] [%n]: %v");
|
||||||
|
#else
|
||||||
|
logger->set_pattern("[%H:%M:%S.%e] [T%t] [%^%l%$] [%n]: %v");
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG
|
||||||
|
logger->set_level(spdlog::level::debug);
|
||||||
|
#endif
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
45
openVulkanoCpp/Base/PlatformEnums.hpp
Normal file
45
openVulkanoCpp/Base/PlatformEnums.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace RenderAPI
|
||||||
|
{
|
||||||
|
enum RenderApi
|
||||||
|
{
|
||||||
|
VULKAN = 0,
|
||||||
|
//OpenGL,
|
||||||
|
//DirectX11,
|
||||||
|
//DirectX12,
|
||||||
|
MAX_VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string ToString(RenderApi api)
|
||||||
|
{
|
||||||
|
switch (api)
|
||||||
|
{
|
||||||
|
case VULKAN: return "Vulkan";
|
||||||
|
}
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Platform
|
||||||
|
{
|
||||||
|
enum Platform
|
||||||
|
{
|
||||||
|
Windows = 0, MacOS, Linux, Android, MAX_VALUE
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string ToString(Platform os)
|
||||||
|
{
|
||||||
|
switch (os)
|
||||||
|
{
|
||||||
|
case Windows: return "Windows";
|
||||||
|
case MacOS: return "Windows";
|
||||||
|
case Linux: return "Windows";
|
||||||
|
case Android: return "Windows";
|
||||||
|
}
|
||||||
|
return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
openVulkanoCpp/Base/Render/IRenderer.hpp
Normal file
25
openVulkanoCpp/Base/Render/IRenderer.hpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include "../ITickable.hpp"
|
||||||
|
#include "../ICloseable.hpp"
|
||||||
|
#include "../../Scene/Scene.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class IWindow;
|
||||||
|
class IGraphicsAppManager;
|
||||||
|
|
||||||
|
class IRenderer : virtual public ITickable, virtual public ICloseable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IRenderer() = default;
|
||||||
|
|
||||||
|
virtual void Init(IGraphicsAppManager* graphicsAppManager, IWindow* window) = 0;
|
||||||
|
|
||||||
|
virtual std::string GetMainRenderDeviceName() = 0;
|
||||||
|
virtual void Resize(uint32_t newWidth, uint32_t newHeight) = 0;
|
||||||
|
|
||||||
|
virtual void SetScene(Scene::Scene* scene) = 0;
|
||||||
|
virtual Scene::Scene* GetScene() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
98
openVulkanoCpp/Base/Timer.hpp
Normal file
98
openVulkanoCpp/Base/Timer.hpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief High-res timer
|
||||||
|
*/
|
||||||
|
class Timer
|
||||||
|
{ //TODO maybe add a Windows option that uses QPC https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
|
||||||
|
std::chrono::high_resolution_clock::time_point tPrev, tStop;
|
||||||
|
int64_t tickNanoseconds, tickMilliseconds;
|
||||||
|
uint64_t totalNanoseconds;
|
||||||
|
double tickSeconds, totalSeconds;
|
||||||
|
bool stopped;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Timer()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
~Timer() = default;
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
tickNanoseconds = 0;
|
||||||
|
tickMilliseconds = 0;
|
||||||
|
tickSeconds = 0;
|
||||||
|
totalNanoseconds = 0;
|
||||||
|
totalSeconds = 0;
|
||||||
|
tPrev = std::chrono::high_resolution_clock::now();
|
||||||
|
stopped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
if (stopped)
|
||||||
|
{
|
||||||
|
tPrev += std::chrono::high_resolution_clock::now() - tStop;
|
||||||
|
stopped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stop()
|
||||||
|
{
|
||||||
|
tStop = std::chrono::high_resolution_clock::now();
|
||||||
|
stopped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Will update the timer
|
||||||
|
*/
|
||||||
|
void Tick()
|
||||||
|
{
|
||||||
|
if (stopped)
|
||||||
|
{
|
||||||
|
tickNanoseconds = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
tickNanoseconds = std::chrono::duration<int64_t, std::nano>(now - tPrev).count();
|
||||||
|
tPrev = now;
|
||||||
|
if (tickNanoseconds < 0) tickNanoseconds = 0;
|
||||||
|
}
|
||||||
|
totalNanoseconds += tickNanoseconds;
|
||||||
|
tickMilliseconds = tickNanoseconds / 1000000;
|
||||||
|
tickSeconds = tickNanoseconds / 1000000000.0;
|
||||||
|
totalSeconds += tickSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t GetTickNanoseconds() const { return tickNanoseconds; }
|
||||||
|
|
||||||
|
int64_t GetTickMilliseconds() const { return tickMilliseconds; }
|
||||||
|
|
||||||
|
double GetTickSeconds() const { return tickSeconds; }
|
||||||
|
|
||||||
|
uint64_t GetTotalNanoseconds() const { return totalNanoseconds; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets the total amount of seconds past since the timer has been started. This will drift over time!
|
||||||
|
* \return The summed total runtime of the timer.
|
||||||
|
*/
|
||||||
|
double GetTotalSeconds() const { return totalSeconds; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Will recalculate the past time from the total nanoseconds and return it. This is more precise but also slower.
|
||||||
|
* \return The calculated total runtime of the timer.
|
||||||
|
*/
|
||||||
|
double GetTotalSecondsPrecise()
|
||||||
|
{
|
||||||
|
totalSeconds = totalNanoseconds / 1000000000.0;
|
||||||
|
return totalSeconds;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
99
openVulkanoCpp/Base/UI/BaseWindow.hpp
Normal file
99
openVulkanoCpp/Base/UI/BaseWindow.hpp
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "IWindow.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class BaseWindow : virtual public IWindow
|
||||||
|
{
|
||||||
|
const int windowId;
|
||||||
|
public:
|
||||||
|
BaseWindow() : windowId(CreateWindowId()) {}
|
||||||
|
virtual ~BaseWindow() = default;
|
||||||
|
|
||||||
|
void GetSize(int* width, int* height) override = 0;
|
||||||
|
|
||||||
|
void GetSize(uint32_t* width, uint32_t* height) override
|
||||||
|
{
|
||||||
|
int w, h;
|
||||||
|
GetSize(&w, &h);
|
||||||
|
*width = w;
|
||||||
|
*height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetWidth() override
|
||||||
|
{
|
||||||
|
uint32_t width, height;
|
||||||
|
GetSize(&width, &height);
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetHeight() override
|
||||||
|
{
|
||||||
|
uint32_t width, height;
|
||||||
|
GetSize(&width, &height);
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::ivec2 GetSize() override
|
||||||
|
{
|
||||||
|
glm::ivec2 size;
|
||||||
|
this->GetSize(&size.x, &size.y);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSize(uint32_t width, uint32_t height) override = 0;
|
||||||
|
|
||||||
|
void SetSize(glm::ivec2 size) override
|
||||||
|
{
|
||||||
|
SetSize(size.x, size.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetPosition(int* x, int* y) override = 0;
|
||||||
|
|
||||||
|
int GetPositionX() override
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
GetPosition(&x, &y);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetPositionY() override
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
GetPosition(&x, &y);
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
glm::ivec2 GetPosition() override
|
||||||
|
{
|
||||||
|
glm::ivec2 position;
|
||||||
|
GetPosition(&position.x, &position.y);
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPosition(int posX, int posY) override = 0;
|
||||||
|
|
||||||
|
void SetPosition(glm::ivec2 pos) override { SetPosition(pos.x, pos.y); }
|
||||||
|
|
||||||
|
void Show() override = 0;
|
||||||
|
void Hide() override = 0;
|
||||||
|
|
||||||
|
void Show(const bool show) override { if (show) Show(); else Hide(); }
|
||||||
|
|
||||||
|
IVulkanWindow* GetVulkanWindow() override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOpenGlWindow* GetOpenGlWindow() override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetWindowId() const override
|
||||||
|
{
|
||||||
|
return windowId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
113
openVulkanoCpp/Base/UI/IWindow.hpp
Normal file
113
openVulkanoCpp/Base/UI/IWindow.hpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../PlatformEnums.hpp"
|
||||||
|
#include "../ITickable.hpp"
|
||||||
|
#include "../ICloseable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
enum WindowMode
|
||||||
|
{
|
||||||
|
WINDOWED, BORDERLESS, FULLSCREEN, BORDERLESS_FULLSCREEN
|
||||||
|
};
|
||||||
|
|
||||||
|
class IWindowHandler;
|
||||||
|
class IVulkanWindow;
|
||||||
|
class IOpenGlWindow;
|
||||||
|
|
||||||
|
class IWindow : public ITickable, public ICloseable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IWindow() = default;
|
||||||
|
|
||||||
|
virtual void Init(RenderAPI::RenderApi renderApi) = 0;
|
||||||
|
|
||||||
|
virtual const std::string& GetTitle() = 0;
|
||||||
|
virtual void SetTitle(const std::string& title) = 0;
|
||||||
|
|
||||||
|
virtual WindowMode GetWindowMode() = 0;
|
||||||
|
virtual void SetWindowMode(WindowMode) = 0;
|
||||||
|
virtual void SetFullscreen() { SetWindowMode(FULLSCREEN); }
|
||||||
|
virtual void SetWindowed() { SetWindowMode(WINDOWED); }
|
||||||
|
|
||||||
|
virtual uint32_t GetWidth() = 0;
|
||||||
|
virtual uint32_t GetHeight() = 0;
|
||||||
|
virtual void GetSize(int* width, int* height) = 0;
|
||||||
|
virtual void GetSize(uint32_t* width, uint32_t* height) = 0;
|
||||||
|
virtual glm::ivec2 GetSize() = 0;
|
||||||
|
virtual void SetSize(uint32_t width, uint32_t height) = 0;
|
||||||
|
virtual void SetSize(glm::ivec2 size) { SetSize(size.x, size.y); }
|
||||||
|
virtual void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) = 0;
|
||||||
|
|
||||||
|
virtual int GetPositionX() = 0;
|
||||||
|
virtual int GetPositionY() = 0;
|
||||||
|
virtual void GetPosition(int* x, int* y) = 0;
|
||||||
|
virtual glm::ivec2 GetPosition() = 0;
|
||||||
|
virtual void SetPosition(int posX, int posY) = 0;
|
||||||
|
virtual void SetPosition(glm::ivec2 pos) = 0;
|
||||||
|
|
||||||
|
virtual void Show() = 0;
|
||||||
|
virtual void Hide() = 0;
|
||||||
|
virtual void Show(bool show) = 0;
|
||||||
|
|
||||||
|
virtual IWindowHandler* GetWindowHandler() = 0;
|
||||||
|
virtual void SetWindowHandler(IWindowHandler* handler) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets the vulkan window implementation of the window.
|
||||||
|
* \return The IVulkanWindow reference of the window. nullptr if the current Window dose not implement IVulkanWindow
|
||||||
|
*/
|
||||||
|
virtual IVulkanWindow* GetVulkanWindow() = 0;
|
||||||
|
virtual IOpenGlWindow* GetOpenGlWindow() = 0;
|
||||||
|
|
||||||
|
virtual int GetWindowId() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static int CreateWindowId()
|
||||||
|
{
|
||||||
|
static int id = 0;
|
||||||
|
return id++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class IVulkanWindow : virtual public IWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IVulkanWindow() = default;
|
||||||
|
|
||||||
|
virtual vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator = nullptr) = 0;
|
||||||
|
virtual std::vector<std::string> GetRequiredInstanceExtensions() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IOpenGlWindow : virtual public IWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IOpenGlWindow() = default;
|
||||||
|
|
||||||
|
virtual void MakeCurrentThread() = 0;
|
||||||
|
virtual void Present() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IWindowHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IWindowHandler() = default;
|
||||||
|
|
||||||
|
virtual void OnWindowMinimize(IWindow* window) = 0;
|
||||||
|
virtual void OnWindowRestore(IWindow* window) = 0;
|
||||||
|
virtual void OnWindowFocusLost(IWindow* window) = 0;
|
||||||
|
virtual void OnWindowFocusGained(IWindow* window) = 0;
|
||||||
|
virtual void OnWindowMove(IWindow* window, int posX, int posY) = 0;
|
||||||
|
virtual void OnWindowResize(IWindow* window, uint32_t newWidth, uint32_t newHeight) = 0;
|
||||||
|
virtual void OnWindowClose(IWindow* window) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WindowInitFailedException : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WindowInitFailedException(char const* const message) : runtime_error(message) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
56
openVulkanoCpp/Base/Utils.hpp
Normal file
56
openVulkanoCpp/Base/Utils.hpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class Utils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::vector<const char*> toCString(const std::vector<std::string>& values)
|
||||||
|
{
|
||||||
|
std::vector<const char*> result;
|
||||||
|
result.reserve(values.size());
|
||||||
|
for (const auto& string : values) {
|
||||||
|
result.push_back(string.c_str());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<const char*> toCString(const std::set<std::string>& values)
|
||||||
|
{
|
||||||
|
std::vector<const char*> result;
|
||||||
|
result.reserve(values.size());
|
||||||
|
for (const auto& string : values) {
|
||||||
|
result.push_back(string.c_str());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static bool Contains(std::vector<T>& vec, const T& element)
|
||||||
|
{
|
||||||
|
return (std::find(vec.begin(), vec.end(), element) != vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void Remove(std::vector<T>& vec, const T& element)
|
||||||
|
{
|
||||||
|
vec.erase(std::remove(vec.begin(), vec.end(), element), vec.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Enumeration>
|
||||||
|
static auto EnumAsInt(Enumeration const value)
|
||||||
|
-> typename std::underlying_type<Enumeration>::type
|
||||||
|
{
|
||||||
|
return static_cast<typename std::underlying_type<Enumeration>::type>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool MatchesAnyElementWise(const glm::vec3& a, const glm::vec3& b)
|
||||||
|
{
|
||||||
|
return a.x == b.x || a.y == b.y || a.z == b.z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
44
openVulkanoCpp/Data/ReadOnlyAtomicArrayQueue.hpp
Normal file
44
openVulkanoCpp/Data/ReadOnlyAtomicArrayQueue.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <atomic>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Data
|
||||||
|
{
|
||||||
|
template <class T>
|
||||||
|
class ReadOnlyAtomicArrayQueue final
|
||||||
|
{
|
||||||
|
T* data;
|
||||||
|
std::atomic<size_t> size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ReadOnlyAtomicArrayQueue(std::vector<T>& data)
|
||||||
|
{
|
||||||
|
this->data = data.data();
|
||||||
|
size.store(data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyAtomicArrayQueue(T* data, size_t size)
|
||||||
|
{
|
||||||
|
this->data = data;
|
||||||
|
this->size.store(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
~ReadOnlyAtomicArrayQueue() = default;
|
||||||
|
|
||||||
|
size_t GetSize() const
|
||||||
|
{
|
||||||
|
return size.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
T* Pop()
|
||||||
|
{
|
||||||
|
size_t s = size.load(std::memory_order_relaxed);
|
||||||
|
while (size > 0 && !size.compare_exchange_weak(s, s - 1));
|
||||||
|
if (s > 0) return &data[s - 1];
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
227
openVulkanoCpp/Host/GraphicsAppManager.hpp
Normal file
227
openVulkanoCpp/Host/GraphicsAppManager.hpp
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "../Base/IGraphicsAppManager.hpp"
|
||||||
|
#include "../Base/UI/IWindow.hpp"
|
||||||
|
#include "../Base/IGraphicsApp.hpp"
|
||||||
|
#include "../Base/PlatformEnums.hpp"
|
||||||
|
#include "../Base/Logger.hpp"
|
||||||
|
#include "../Base/Timer.hpp"
|
||||||
|
#include "../Base/Render/IRenderer.hpp"
|
||||||
|
#include "PlatformProducer.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A simple GraphicsAppManager. It can only handle on window.
|
||||||
|
*/
|
||||||
|
class GraphicsAppManager : virtual public IGraphicsAppManager, virtual public IWindowHandler
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
IWindow* window;
|
||||||
|
IGraphicsApp* app;
|
||||||
|
IRenderer* renderer;
|
||||||
|
RenderAPI::RenderApi renderApi;
|
||||||
|
bool paused = false, running = false;
|
||||||
|
float fpsTimer = 0, avgFps = 0, avgFrameTime = 0;
|
||||||
|
uint64_t frameCount = 0, lastFrameCount = 0;
|
||||||
|
Timer* frameTimer;
|
||||||
|
std::string windowTitleFormat;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GraphicsAppManager(IGraphicsApp* app, RenderAPI::RenderApi renderApi = RenderAPI::VULKAN) : app(app), renderApi(renderApi)
|
||||||
|
{
|
||||||
|
if (renderApi >= RenderAPI::MAX_VALUE) throw std::runtime_error("Invalid RenderAPI");
|
||||||
|
Logger::SetupLogger();
|
||||||
|
if (!app)
|
||||||
|
{
|
||||||
|
const auto msg = "The app must not be null!";
|
||||||
|
Logger::MANAGER->error(msg);
|
||||||
|
throw std::runtime_error(msg);
|
||||||
|
}
|
||||||
|
window = PlatformProducer::CreateBestWindow(renderApi);
|
||||||
|
renderer = PlatformProducer::CreateRenderManager(renderApi);
|
||||||
|
app->SetGraphicsAppManager(this);
|
||||||
|
window->SetWindowHandler(this);
|
||||||
|
frameTimer = new Timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
~GraphicsAppManager() override
|
||||||
|
{
|
||||||
|
delete renderer;
|
||||||
|
delete window;
|
||||||
|
delete frameTimer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // Getter
|
||||||
|
RenderAPI::RenderApi GetRenderApi() const override
|
||||||
|
{
|
||||||
|
return renderApi;
|
||||||
|
}
|
||||||
|
|
||||||
|
IGraphicsApp* GetGraphicsApp() const override
|
||||||
|
{
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRenderer* GetRenderer() const override
|
||||||
|
{
|
||||||
|
return renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning() const override
|
||||||
|
{
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPaused() const override
|
||||||
|
{
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // Setter
|
||||||
|
void Stop() override
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
Logger::MANAGER->info("Graphics application stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pause() override
|
||||||
|
{
|
||||||
|
paused = true;
|
||||||
|
frameTimer->Stop();
|
||||||
|
Logger::MANAGER->info("Graphics application paused");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resume() override
|
||||||
|
{
|
||||||
|
paused = false;
|
||||||
|
frameTimer->Start();
|
||||||
|
Logger::MANAGER->info("Graphics application resumed");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Run() override
|
||||||
|
{
|
||||||
|
running = true;
|
||||||
|
StartUp();
|
||||||
|
frameTimer->Reset();
|
||||||
|
Loop(); // Runs the rendering loop
|
||||||
|
ShutDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void StartUp()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger::MANAGER->info("Initializing ...");
|
||||||
|
app->Init();
|
||||||
|
window->Init(renderApi);
|
||||||
|
//TODO restore window settings if there are any set
|
||||||
|
renderer->Init((IGraphicsAppManager*)this, window);
|
||||||
|
windowTitleFormat = app->GetAppName() + " " + app->GetAppVersion() + " - " + renderer->GetMainRenderDeviceName() + " - {:.1f} fps ({:.1f} ms)";
|
||||||
|
Logger::MANAGER->info("Initialized");
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
Logger::MANAGER->error("Failed to initiate: {0}", e.what());
|
||||||
|
running = false;
|
||||||
|
#ifdef DEBUG
|
||||||
|
throw e;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Loop()
|
||||||
|
{
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
window->Tick();
|
||||||
|
if (paused)
|
||||||
|
{ // The rendering is paused
|
||||||
|
// No need to burn cpu time if the app is paused
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app->Tick();
|
||||||
|
renderer->Tick();
|
||||||
|
frameTimer->Tick();
|
||||||
|
UpdateFps();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutDown() const
|
||||||
|
{
|
||||||
|
Logger::MANAGER->info("Shutting down ...");
|
||||||
|
renderer->Close();
|
||||||
|
window->Close();
|
||||||
|
app->Close();
|
||||||
|
Logger::MANAGER->info("Shutdown complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateFps()
|
||||||
|
{
|
||||||
|
frameCount++;
|
||||||
|
fpsTimer += frameTimer->GetTickSeconds();
|
||||||
|
|
||||||
|
if(fpsTimer > 1.0f)
|
||||||
|
{
|
||||||
|
avgFps = static_cast<float>(frameCount - lastFrameCount) / fpsTimer;
|
||||||
|
avgFrameTime = (1 / avgFps) * 1000.0f;
|
||||||
|
lastFrameCount = frameCount;
|
||||||
|
fpsTimer = 0;
|
||||||
|
window->SetTitle(fmt::format(windowTitleFormat, avgFps, avgFrameTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public: //FPS stuff
|
||||||
|
uint64_t GetFrameCount() const override
|
||||||
|
{
|
||||||
|
return frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetAvgFrameTime() const override
|
||||||
|
{
|
||||||
|
return avgFrameTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetAvgFps() const override
|
||||||
|
{
|
||||||
|
return avgFps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // Window Manager
|
||||||
|
void OnWindowMinimize(IWindow* window) override
|
||||||
|
{
|
||||||
|
if (window != this->window) return;
|
||||||
|
Pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnWindowRestore(IWindow* window) override
|
||||||
|
{
|
||||||
|
if (window != this->window) return;
|
||||||
|
Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnWindowFocusLost(IWindow* window) override {}
|
||||||
|
void OnWindowFocusGained(IWindow* window) override {}
|
||||||
|
void OnWindowMove(IWindow* window, int posX, int posY) override {} //TODO save window pos
|
||||||
|
|
||||||
|
void OnWindowResize(IWindow* window, const uint32_t newWidth, const uint32_t newHeight) override
|
||||||
|
{
|
||||||
|
if(window != this->window) return;
|
||||||
|
renderer->Resize(newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnWindowClose(IWindow* window) override
|
||||||
|
{
|
||||||
|
if (window != this->window) return;
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
51
openVulkanoCpp/Host/PlatformProducer.hpp
Normal file
51
openVulkanoCpp/Host/PlatformProducer.hpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "../Base/Logger.hpp"
|
||||||
|
#include "../Vulkan/Renderer.hpp"
|
||||||
|
#include "../Base/PlatformEnums.hpp"
|
||||||
|
#include "WindowGLFW.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief Helper class the produces all the platform depending classes
|
||||||
|
*/
|
||||||
|
class PlatformProducer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates the renderer for the given render api
|
||||||
|
* \param renderApi The render api that should be used
|
||||||
|
* \return The created Renderer.
|
||||||
|
* \throws std::runtime_error if the render api is not supported
|
||||||
|
*/
|
||||||
|
static IRenderer* CreateRenderManager(RenderAPI::RenderApi renderApi)
|
||||||
|
{
|
||||||
|
switch (renderApi)
|
||||||
|
{
|
||||||
|
case RenderAPI::VULKAN: return new Vulkan::Renderer();
|
||||||
|
default:
|
||||||
|
Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast<int>(renderApi));
|
||||||
|
throw std::runtime_error("Unsupported render api requested!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates a window that fits best for the current environment
|
||||||
|
* \param renderApi The render api that should be used when searching for the best suited window
|
||||||
|
* \return The created window. nullptr if no window is supported on the current platform
|
||||||
|
* \throws std::runtime_error if the render api is not supported
|
||||||
|
*/
|
||||||
|
static IWindow* CreateBestWindow(RenderAPI::RenderApi renderApi)
|
||||||
|
{ //TODO add more windows to chose from
|
||||||
|
switch(renderApi)
|
||||||
|
{
|
||||||
|
case RenderAPI::VULKAN: return new WindowGLFW();
|
||||||
|
default:
|
||||||
|
Logger::RENDER->error("Unsupported render api requested! Requested %d", static_cast<int>(renderApi));
|
||||||
|
throw std::runtime_error("Unsupported render api requested!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
374
openVulkanoCpp/Host/WindowGLFW.hpp
Normal file
374
openVulkanoCpp/Host/WindowGLFW.hpp
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
#include "../Base/UI/BaseWindow.hpp"
|
||||||
|
#include "../Base/Logger.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
class WindowGLFW : public BaseWindow, virtual public IVulkanWindow, virtual public IOpenGlWindow
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
GLFWwindow* window = nullptr;
|
||||||
|
uint32_t width = 1280, height = 720;
|
||||||
|
std::string title = "Window Title";
|
||||||
|
WindowMode windowMode = WINDOWED;
|
||||||
|
IWindowHandler* handler = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WindowGLFW() = default;
|
||||||
|
virtual ~WindowGLFW() { if (window != nullptr) Close(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Create()
|
||||||
|
{
|
||||||
|
window = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
|
||||||
|
if(!window) return;
|
||||||
|
glfwSetWindowUserPointer(window, this);
|
||||||
|
RegisterCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterCallbacks() const
|
||||||
|
{
|
||||||
|
glfwSetErrorCallback(ErrorCallback);
|
||||||
|
|
||||||
|
glfwSetDropCallback(window, DropCallback);
|
||||||
|
glfwSetFramebufferSizeCallback(window, ResizeCallback);
|
||||||
|
glfwSetWindowFocusCallback(window, FocusCallback);
|
||||||
|
glfwSetWindowRefreshCallback(window, RefreshCallback);
|
||||||
|
glfwSetWindowIconifyCallback(window, MinimizeCallback);
|
||||||
|
glfwSetWindowPosCallback(window, WindowMoveCallback);
|
||||||
|
glfwSetWindowCloseCallback(window, CloseCallback);
|
||||||
|
|
||||||
|
// Input Callbacks
|
||||||
|
glfwSetKeyCallback(window, KeyboardCallback);
|
||||||
|
glfwSetMouseButtonCallback(window, MouseButtonCallback);
|
||||||
|
glfwSetCursorPosCallback(window, MouseMoveCallback);
|
||||||
|
glfwSetScrollCallback(window, MouseScrollCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLFWmonitor* GetPrimaryMonitor()
|
||||||
|
{
|
||||||
|
return glfwGetPrimaryMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<GLFWmonitor*> GetMonitors()
|
||||||
|
{
|
||||||
|
int count;
|
||||||
|
GLFWmonitor** monitorsArray = glfwGetMonitors(&count);
|
||||||
|
std::vector<GLFWmonitor*> monitors;
|
||||||
|
monitors.reserve(count);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
monitors[i] = monitorsArray[i];
|
||||||
|
}
|
||||||
|
return monitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // IWindow implementation
|
||||||
|
void Init(RenderAPI::RenderApi renderApi) override
|
||||||
|
{
|
||||||
|
if (!glfwInit()) throw WindowInitFailedException("Failed to initialize glfw");
|
||||||
|
if(renderApi == RenderAPI::VULKAN) glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
|
Create();
|
||||||
|
if(!window)
|
||||||
|
{
|
||||||
|
glfwTerminate();
|
||||||
|
throw WindowInitFailedException("Failed to initialize window");
|
||||||
|
}
|
||||||
|
if (renderApi != RenderAPI::VULKAN) MakeCurrentThread();
|
||||||
|
Logger::WINDOW->info("GLFW Window created (id: {0})", GetWindowId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
glfwDestroyWindow(window);
|
||||||
|
window = nullptr;
|
||||||
|
glfwTerminate();
|
||||||
|
Logger::WINDOW->info("GLFW Window destroyed (id: {0})", GetWindowId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Present() const override
|
||||||
|
{
|
||||||
|
glfwSwapBuffers(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Show() override
|
||||||
|
{
|
||||||
|
glfwShowWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hide() override
|
||||||
|
{
|
||||||
|
glfwHideWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tick() override
|
||||||
|
{
|
||||||
|
glfwPollEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTitle(const std::string& title) override
|
||||||
|
{
|
||||||
|
this->title = title;
|
||||||
|
glfwSetWindowTitle(window, title.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& GetTitle() override
|
||||||
|
{
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSize(uint32_t width, uint32_t height) override
|
||||||
|
{
|
||||||
|
if (!window)
|
||||||
|
{
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
glfwSetWindowSize(window, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPosition(int posX, int posY) override
|
||||||
|
{
|
||||||
|
glfwSetWindowPos(window, posX, posY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSizeLimits(int minWidth, int minHeight, int maxWidth, int maxHeight) override
|
||||||
|
{
|
||||||
|
minWidth = (minWidth < 0) ? GLFW_DONT_CARE : minWidth;
|
||||||
|
minHeight = (minHeight < 0) ? GLFW_DONT_CARE : minHeight;
|
||||||
|
maxWidth = (maxWidth < 0) ? GLFW_DONT_CARE : maxWidth;
|
||||||
|
maxHeight = (maxHeight < 0) ? GLFW_DONT_CARE : maxHeight;
|
||||||
|
glfwSetWindowSizeLimits(window, minWidth, minHeight, maxWidth, maxHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MakeCurrentThread() override
|
||||||
|
{
|
||||||
|
glfwMakeContextCurrent(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWindowMode(WindowMode windowMode) override
|
||||||
|
{
|
||||||
|
if(windowMode == this->windowMode) return; //Nothing change here
|
||||||
|
//TODO
|
||||||
|
this->windowMode = windowMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetWindowHandler(IWindowHandler* handler) override
|
||||||
|
{
|
||||||
|
this->handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
IVulkanWindow* GetVulkanWindow() override
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOpenGlWindow* GetOpenGlWindow() override
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status getter
|
||||||
|
WindowMode GetWindowMode() override
|
||||||
|
{
|
||||||
|
return windowMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetSize(int* width, int* height) override
|
||||||
|
{
|
||||||
|
glfwGetWindowSize(window, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetPosition(int* x, int* y) override
|
||||||
|
{
|
||||||
|
glfwGetWindowPos(window, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
IWindowHandler* GetWindowHandler() override
|
||||||
|
{
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
//IVulkanWindow stuff
|
||||||
|
vk::SurfaceKHR CreateSurface(const vk::Instance& instance, const vk::AllocationCallbacks* pAllocator) override
|
||||||
|
{
|
||||||
|
VkSurfaceKHR rawSurface;
|
||||||
|
const auto result = static_cast<vk::Result>(glfwCreateWindowSurface(static_cast<VkInstance>(instance), window, reinterpret_cast<const VkAllocationCallbacks*>(pAllocator), &rawSurface));
|
||||||
|
return createResultValue(result, rawSurface, "vk::CommandBuffer::begin");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> GetRequiredInstanceExtensions() override
|
||||||
|
{
|
||||||
|
return GetVulkanRequiredInstanceExtensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // Window events
|
||||||
|
void OnResize(const uint32_t newWidth, const uint32_t newHeight)
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) resized (width: {1}, height: {2})", GetWindowId(), newWidth, newHeight);
|
||||||
|
handler->OnWindowResize(this, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMinimize()
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) minimized", GetWindowId());
|
||||||
|
handler->OnWindowMinimize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnRestore()
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) restored", GetWindowId());
|
||||||
|
handler->OnWindowRestore(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFocusLost()
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) focus lost", GetWindowId());
|
||||||
|
handler->OnWindowFocusLost(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnFocusGained()
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) focus gained", GetWindowId());
|
||||||
|
handler->OnWindowFocusGained(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnMove(const int posX, const int posY)
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) moved (x: {1}, y: {2})", GetWindowId(), posX, posY);
|
||||||
|
handler->OnWindowMove(this, posX, posY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClose()
|
||||||
|
{
|
||||||
|
Logger::WINDOW->debug("Window (id: {0}) closed", GetWindowId());
|
||||||
|
handler->OnWindowClose(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public: // Input events TODO
|
||||||
|
virtual void OnKeyPressed(int key, int mods) {}
|
||||||
|
virtual void OnKeyReleased(int key, int mods) {}
|
||||||
|
virtual void OnMousePressed(int button, int mods) {}
|
||||||
|
virtual void OnMouseReleased(int button, int mods) {}
|
||||||
|
virtual void OnMouseMoved(double posX, double posY) {}
|
||||||
|
virtual void OnMouseScrolled(double delta) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void OnKeyEvent(int key, int scanCode, int action, int mods)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GLFW_PRESS: OnKeyPressed(key, mods); break;
|
||||||
|
case GLFW_RELEASE: OnKeyReleased(key, mods); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnMouseButtonEvent(int button, int action, int mods)
|
||||||
|
{
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case GLFW_PRESS: OnMousePressed(button, mods); break;
|
||||||
|
case GLFW_RELEASE: OnMouseReleased(button, mods); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private: // Callbacks
|
||||||
|
static WindowGLFW* GetWindow(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
return static_cast<WindowGLFW*>(glfwGetWindowUserPointer(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void KeyboardCallback(GLFWwindow* window, int key, int scanCode, int action, int mods)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnKeyEvent(key, scanCode, action, mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MouseButtonCallback(GLFWwindow* window, int button, int action, int mods)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnMouseButtonEvent(button, action, mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MouseMoveCallback(GLFWwindow* window, double posX, double posY)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnMouseMoved(posX, posY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MouseScrollCallback(GLFWwindow* window, double xOffset, double yOffset)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnMouseScrolled(yOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ResizeCallback(GLFWwindow* window, int width, int height)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnResize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FocusCallback(GLFWwindow* window, const int focused)
|
||||||
|
{
|
||||||
|
if (focused == GLFW_TRUE)
|
||||||
|
GetWindow(window)->OnFocusGained();
|
||||||
|
else
|
||||||
|
GetWindow(window)->OnFocusLost();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void MinimizeCallback(GLFWwindow* window, const int minimized)
|
||||||
|
{
|
||||||
|
if(minimized == GLFW_TRUE)
|
||||||
|
GetWindow(window)->OnMinimize();
|
||||||
|
else
|
||||||
|
GetWindow(window)->OnRestore();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RefreshCallback(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
//TODO is there really anything to do? or is it ok if the window is only redrawn on the next frame?
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WindowMoveCallback(GLFWwindow* window, const int posX, const int posY)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnMove(posX, posY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseCallback(GLFWwindow* window)
|
||||||
|
{
|
||||||
|
GetWindow(window)->OnClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DropCallback(GLFWwindow* window, const int count, const char** paths)
|
||||||
|
{
|
||||||
|
//TODO something useful
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ErrorCallback(const int error, const char* description)
|
||||||
|
{
|
||||||
|
Logger::WINDOW->error("GLFW error (e{0}): {1}", error, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::vector<std::string> GetVulkanRequiredInstanceExtensions()
|
||||||
|
{
|
||||||
|
std::vector<std::string> result;
|
||||||
|
uint32_t count = 0;
|
||||||
|
const char** names = glfwGetRequiredInstanceExtensions(&count);
|
||||||
|
if (names && count)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
result.emplace_back(names[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
98
openVulkanoCpp/Scene/AABB.hpp
Normal file
98
openVulkanoCpp/Scene/AABB.hpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include "../Base/IInitable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A class that represents an axis aligned bounding box
|
||||||
|
*/
|
||||||
|
class AABB final : public virtual IInitable
|
||||||
|
{
|
||||||
|
glm::vec3 min, max;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AABB() : min(INFINITY), max(-INFINITY) {}
|
||||||
|
~AABB() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initiates the AABB to min=Inf, max=-Inf
|
||||||
|
*/
|
||||||
|
void Init() override
|
||||||
|
{
|
||||||
|
min = glm::vec3(INFINITY);
|
||||||
|
max = glm::vec3(-INFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initiates the AABB to a single point (min=max=point)
|
||||||
|
* \param point The point that should be used as min and max of the AABB
|
||||||
|
*/
|
||||||
|
void Init(const glm::vec3& point)
|
||||||
|
{
|
||||||
|
min = max = point;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Initiates the AABB from some other AABB
|
||||||
|
* \param other The other AABB that should be copied
|
||||||
|
*/
|
||||||
|
void Init(const AABB& other)
|
||||||
|
{
|
||||||
|
min = other.GetMin();
|
||||||
|
max = other.GetMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::vec3& GetMin() const { return min; }
|
||||||
|
|
||||||
|
const glm::vec3& GetMax() const { return max; }
|
||||||
|
|
||||||
|
void Grow(const glm::vec3& point)
|
||||||
|
{
|
||||||
|
min = glm::min(min, point);
|
||||||
|
max = glm::max(max, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Grow(const AABB& otherAABB)
|
||||||
|
{
|
||||||
|
min = glm::min(min, otherAABB.GetMin());
|
||||||
|
max = glm::max(max, otherAABB.GetMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Grow(const AABB& otherAABB, glm::mat4x4 transformation)
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 GetDiagonal() const
|
||||||
|
{
|
||||||
|
return max - min;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 GetCenter() const
|
||||||
|
{
|
||||||
|
return min + (GetDiagonal() * 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks if the AABB overlaps with an other AABB
|
||||||
|
* \param other The other AABB that should be checked
|
||||||
|
* \return true if the AABB overlaps with the other, false if not
|
||||||
|
*/
|
||||||
|
bool IsOverlapping(const AABB& other) const
|
||||||
|
{
|
||||||
|
return !(other.min.x > max.x || other.max.x < min.x || other.min.y > max.y || other.max.y < min.y || other.min.z > max.z || other.max.z < min.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Resets the AABB to min=Inf, max=-Inf, same as Init()
|
||||||
|
*/
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
169
openVulkanoCpp/Scene/Camera.hpp
Normal file
169
openVulkanoCpp/Scene/Camera.hpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
#pragma once
|
||||||
|
#define _USE_MATH_DEFINES
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
class Camera : public Node
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
float nearPlane, farPlane;
|
||||||
|
float width, height;
|
||||||
|
public:
|
||||||
|
glm::mat4x4 projection, view, viewProjection;
|
||||||
|
|
||||||
|
Camera() = default;
|
||||||
|
virtual ~Camera() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Init(float width, float height, float nearPlane, float farPlane)
|
||||||
|
{
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
this->nearPlane = nearPlane;
|
||||||
|
this->farPlane = farPlane;
|
||||||
|
Node::Init();
|
||||||
|
UpdateProjectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetSize(const float& width, const float& height)
|
||||||
|
{
|
||||||
|
this->width = width;
|
||||||
|
this->height = height;
|
||||||
|
UpdateProjectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNearPlane(float nearPlane)
|
||||||
|
{
|
||||||
|
this->nearPlane = nearPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFarPlane(float farPlane)
|
||||||
|
{
|
||||||
|
this->farPlane = farPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float NearPlane() const
|
||||||
|
{
|
||||||
|
return nearPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
float FarPlane() const
|
||||||
|
{
|
||||||
|
return farPlane;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UpdateProjectionMatrix() = 0;
|
||||||
|
|
||||||
|
void UpdateViewProjectionMatrix()
|
||||||
|
{ // In vulkan the screen space is defined as y=0=top and y=1=bottom and thus the coordinate have to be flipped
|
||||||
|
viewProjection = projection * glm::mat4x4(1,0,0,0,0,-1,0,0,0,0,1,0,0,0,0,1) * view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateWorldMatrix(const glm::mat4x4& parentWorldMat) override
|
||||||
|
{
|
||||||
|
Node::UpdateWorldMatrix(parentWorldMat);
|
||||||
|
view = glm::inverse(GetWorldMatrix());
|
||||||
|
UpdateViewProjectionMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::mat4x4& GetViewProjectionMatrix() const
|
||||||
|
{
|
||||||
|
return viewProjection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::mat4x4* GetViewProjectionMatrixPointer() const
|
||||||
|
{
|
||||||
|
return &viewProjection;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PerspectiveCamera : public Camera
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
float fov, aspect;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Init(float fovDegrees, float width, float height, float nearPlane, float farPlane)
|
||||||
|
{
|
||||||
|
this->fov = glm::radians(fovDegrees);
|
||||||
|
aspect = width / height;
|
||||||
|
Camera::Init(width, height, nearPlane, farPlane);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetSize(const float& width, const float& height) override
|
||||||
|
{
|
||||||
|
aspect = width / height;
|
||||||
|
Camera::SetSize(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAspect(const float& aspect)
|
||||||
|
{
|
||||||
|
this->aspect = aspect;
|
||||||
|
Camera::SetSize(aspect, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFovX(const float& fov)
|
||||||
|
{
|
||||||
|
SetFov(2 * atan(tan(fov * 0.5f) * aspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFovXRad(const float& fov)
|
||||||
|
{
|
||||||
|
SetFovRad(2 * atan(tan(fov * 0.5f) * aspect));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFov(const float& fov)
|
||||||
|
{
|
||||||
|
SetFovRad(glm::radians(fov));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetFovRad(const float& fov)
|
||||||
|
{
|
||||||
|
this->fov = fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetFov() const
|
||||||
|
{
|
||||||
|
return glm::degrees(fov);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetFovX() const
|
||||||
|
{
|
||||||
|
return 2 * atan(tan(GetFov() * 0.5f) * aspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetFovRad() const
|
||||||
|
{
|
||||||
|
return fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GetFovXRad() const
|
||||||
|
{
|
||||||
|
return 2 * atan(tan(fov * 0.5f) * aspect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateProjectionMatrix() override
|
||||||
|
{
|
||||||
|
projection = glm::perspectiveLH_ZO(fov, aspect, nearPlane, farPlane);
|
||||||
|
UpdateViewProjectionMatrix();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class OrthographicCamera : public Camera
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void UpdateProjectionMatrix() override
|
||||||
|
{
|
||||||
|
const float widthHalf = width * 0.5f, heightHalf = height * 0.5f;
|
||||||
|
projection = glm::orthoLH_ZO(-widthHalf, widthHalf, -heightHalf, heightHalf, nearPlane, farPlane);
|
||||||
|
UpdateViewProjectionMatrix();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
26
openVulkanoCpp/Scene/Drawable.cpp
Normal file
26
openVulkanoCpp/Scene/Drawable.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "Drawable.hpp"
|
||||||
|
#include "Scene.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
void Drawable::SetScene(Scene* scene)
|
||||||
|
{
|
||||||
|
if (this->scene == scene) return;
|
||||||
|
if (scene && this->scene) throw std::runtime_error("Drawable has been associated with a scene already!");
|
||||||
|
this->scene = scene;
|
||||||
|
if(scene) scene->RegisterDrawable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Drawable::RemoveNode(Node* node)
|
||||||
|
{
|
||||||
|
Utils::Remove(nodes, node);
|
||||||
|
if (nodes.empty())
|
||||||
|
{
|
||||||
|
scene = nullptr;
|
||||||
|
scene->RemoveDrawable(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
openVulkanoCpp/Scene/Drawable.hpp
Normal file
82
openVulkanoCpp/Scene/Drawable.hpp
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "Geometry.hpp"
|
||||||
|
#include "Material.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
class Node;
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
struct Drawable : virtual public ICloseable
|
||||||
|
{
|
||||||
|
std::vector<Node*> nodes;
|
||||||
|
Scene* scene = nullptr;
|
||||||
|
Geometry* mesh = nullptr;
|
||||||
|
Material* material = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Drawable() = default;
|
||||||
|
|
||||||
|
explicit Drawable(const Drawable* toCopy)
|
||||||
|
{
|
||||||
|
mesh = toCopy->mesh;
|
||||||
|
material = toCopy->material;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Drawable()
|
||||||
|
{
|
||||||
|
if(mesh) Drawable::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Drawable* Copy() const
|
||||||
|
{
|
||||||
|
return new Drawable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(Geometry* mesh, Material* material)
|
||||||
|
{
|
||||||
|
if (this->mesh || this->material) throw std::runtime_error("Drawable is already initialized.");
|
||||||
|
this->mesh = mesh;
|
||||||
|
this->material = material;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(Drawable* drawable)
|
||||||
|
{
|
||||||
|
if (mesh || material) throw std::runtime_error("Drawable is already initialized.");
|
||||||
|
this->mesh = drawable->mesh;
|
||||||
|
this->material = drawable->material;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
if (!nodes.empty()) throw std::runtime_error("Drawable is still being used!!!");
|
||||||
|
mesh = nullptr;
|
||||||
|
material = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* GetScene() const
|
||||||
|
{
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Node;
|
||||||
|
friend class Scene;
|
||||||
|
|
||||||
|
void AddNode(Node* node)
|
||||||
|
{
|
||||||
|
if (!mesh) throw std::runtime_error("Drawable is not initialized.");
|
||||||
|
if (Utils::Contains(nodes, node)) throw std::runtime_error("A drawable must not use the same node more than once.");
|
||||||
|
nodes.push_back(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetScene(Scene* scene);
|
||||||
|
|
||||||
|
void RemoveNode(Node* node);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
206
openVulkanoCpp/Scene/Geometry.hpp
Normal file
206
openVulkanoCpp/Scene/Geometry.hpp
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <assimp/Importer.hpp>
|
||||||
|
#include <assimp/scene.h>
|
||||||
|
#include <assimp/mesh.h>
|
||||||
|
#include <assimp/postprocess.h>
|
||||||
|
#include "Vertex.hpp"
|
||||||
|
#include "../Base/Logger.hpp"
|
||||||
|
#include "../Base/Utils.hpp"
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "AABB.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
enum class VertexIndexType
|
||||||
|
{
|
||||||
|
UINT16 = sizeof(uint16_t), UINT32 = sizeof(uint32_t)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Geometry : public virtual ICloseable
|
||||||
|
{
|
||||||
|
uint32_t vertexCount = 0, indexCount = 0;
|
||||||
|
Vertex* vertices;
|
||||||
|
void* indices;
|
||||||
|
VertexIndexType indexType;
|
||||||
|
AABB aabb;
|
||||||
|
ICloseable* renderGeo = nullptr;
|
||||||
|
|
||||||
|
Vertex* GetVertices() const { return vertices; }
|
||||||
|
void* GetIndices() const { return indices; }
|
||||||
|
uint16_t* GetIndices16() const { return static_cast<uint16_t*>(indices); }
|
||||||
|
uint32_t* GetIndices32() const { return static_cast<uint32_t*>(indices); }
|
||||||
|
uint32_t GetIndexCount() const { return indexCount; }
|
||||||
|
uint32_t GetVertexCount() const { return vertexCount; }
|
||||||
|
|
||||||
|
static Geometry* LoadFromFile(const std::string file)
|
||||||
|
{
|
||||||
|
Geometry* mesh = new Geometry();
|
||||||
|
mesh->InitFromFile(file);
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
Geometry() : vertexCount(0), indexCount(0), vertices(nullptr), indices(nullptr), indexType(VertexIndexType::UINT16) {}
|
||||||
|
|
||||||
|
~Geometry()
|
||||||
|
{
|
||||||
|
if (vertices) Geometry::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitFromFile(const std::string file)
|
||||||
|
{
|
||||||
|
Assimp::Importer importer;
|
||||||
|
|
||||||
|
const uint32_t flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals |
|
||||||
|
aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials | aiProcess_GenUVCoords | aiProcess_TransformUVCoords |
|
||||||
|
aiProcess_ConvertToLeftHanded | aiProcess_PreTransformVertices | aiProcess_OptimizeGraph;
|
||||||
|
|
||||||
|
const aiScene* scene = importer.ReadFile(file, flags);
|
||||||
|
if (!scene) throw std::runtime_error("Failed to load file \"" + file + "\" Error: " + importer.GetErrorString());
|
||||||
|
if (!scene->HasMeshes()) throw std::runtime_error("File \"" + file + "\" does not have any meshes");
|
||||||
|
if (scene->mNumMeshes > 1) Logger::DATA->warn("File {0} contains more than one mesh. Only first one will be loaded", file);
|
||||||
|
Init(scene->mMeshes[0]);
|
||||||
|
importer.FreeScene();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Creates the arrays for the vertices and indices. They will not be filled!
|
||||||
|
* \param vertexCount The amount of vertices that will be used
|
||||||
|
* \param indexCount The amount of indices that will be used
|
||||||
|
*/
|
||||||
|
void Init(uint32_t vertexCount, uint32_t indexCount)
|
||||||
|
{
|
||||||
|
if (this->vertexCount || this->indexCount) throw std::runtime_error("Geometry is already initialized.");
|
||||||
|
this->vertexCount = vertexCount;
|
||||||
|
this->indexCount = indexCount;
|
||||||
|
indexType = (vertexCount > UINT16_MAX) ? VertexIndexType::UINT32 : VertexIndexType::UINT16;
|
||||||
|
vertices = new Vertex[vertexCount];
|
||||||
|
indices = malloc(static_cast<size_t>(Utils::EnumAsInt(indexType)) * indexCount);
|
||||||
|
renderGeo = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(aiMesh* mesh)
|
||||||
|
{
|
||||||
|
aabb.Init();
|
||||||
|
Init(mesh->mNumVertices, mesh->mNumFaces * 3); // Reserve the space for the data
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
|
||||||
|
{
|
||||||
|
vertices[i].Set(mesh->mVertices[i]);
|
||||||
|
if (mesh->HasNormals()) vertices[i].SetNormal(mesh->mNormals[i]);
|
||||||
|
if (mesh->HasTangentsAndBitangents())
|
||||||
|
{
|
||||||
|
vertices[i].SetTangentAndBiTangent(mesh->mTangents[i], mesh->mBitangents[i]);
|
||||||
|
}
|
||||||
|
if (mesh->HasTextureCoords(0)) vertices[i].SetTextureCoordinates(mesh->mTextureCoords[0][i]);
|
||||||
|
if (mesh->HasVertexColors(0)) vertices[i].SetColor(mesh->mColors[0][i]);
|
||||||
|
aabb.Grow(vertices[i].position);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
|
||||||
|
{
|
||||||
|
const aiFace face = mesh->mFaces[i];
|
||||||
|
if (face.mNumIndices != 3) throw std::runtime_error("Mesh is not a triangle mesh!");
|
||||||
|
for (unsigned int j = 0; j < face.mNumIndices; j++)
|
||||||
|
{
|
||||||
|
if (indexType == VertexIndexType::UINT16)
|
||||||
|
{
|
||||||
|
static_cast<uint16_t*>(indices)[i * face.mNumIndices + j] = static_cast<uint16_t>(face.mIndices[j]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_cast<uint32_t*>(indices)[i * face.mNumIndices + j] = face.mIndices[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO load bones
|
||||||
|
//TODO load materials
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitCube(float x = 1, float y = 1, float z = 1, glm::vec4 color = glm::vec4(1))
|
||||||
|
{
|
||||||
|
Init(24, 36);
|
||||||
|
SetIndices(new uint32_t[indexCount]{
|
||||||
|
0, 1, 2, 0, 2, 3, // front face index data
|
||||||
|
4, 5, 6, 4, 6, 7, // back face index data
|
||||||
|
8, 9, 10, 8, 10, 11, // top face index data
|
||||||
|
12, 13, 14, 12, 14, 15, // bottom face index data
|
||||||
|
16, 17, 18, 16, 18, 19, // left face index data
|
||||||
|
20, 21, 22, 20, 22, 23 // right face index data
|
||||||
|
}, indexCount);
|
||||||
|
x *= 0.5f; y *= 0.5f; z *= 0.5f;
|
||||||
|
int i = 0;
|
||||||
|
// front face vertex data
|
||||||
|
vertices[i++].Set(-x, +y, -z, +0, +0, -1, +0, +0);
|
||||||
|
vertices[i++].Set(-x, -y, -z, +0, +0, -1, +0, +1);
|
||||||
|
vertices[i++].Set(+x, -y, -z, +0, +0, -1, +1, +1);
|
||||||
|
vertices[i++].Set(+x, +y, -z, +0, +0, -1, +1, +0);
|
||||||
|
// back face vertex data
|
||||||
|
vertices[i++].Set(-x, +y, +z, +0, +0, +1, +1, +0);
|
||||||
|
vertices[i++].Set(+x, +y, +z, +0, +0, +1, +0, +0);
|
||||||
|
vertices[i++].Set(+x, -y, +z, +0, +0, +1, +0, +1);
|
||||||
|
vertices[i++].Set(-x, -y, +z, +0, +0, +1, +1, +1);
|
||||||
|
// top face vertex data
|
||||||
|
vertices[i++].Set(-x, -y, -z, +0, +1, +0, +0, +0);
|
||||||
|
vertices[i++].Set(-x, -y, +z, +0, +1, +0, +0, +1);
|
||||||
|
vertices[i++].Set(+x, -y, +z, +0, +1, +0, +1, +1);
|
||||||
|
vertices[i++].Set(+x, -y, -z, +0, +1, +0, +1, +0);
|
||||||
|
// bottom face vertex data
|
||||||
|
vertices[i++].Set(-x, +y, -z, +0, -1, +0, +1, +0);
|
||||||
|
vertices[i++].Set(+x, +y, -z, +0, -1, +0, +0, +0);
|
||||||
|
vertices[i++].Set(+x, +y, +z, +0, -1, +0, +0, +1);
|
||||||
|
vertices[i++].Set(-x, +y, +z, +0, -1, +0, +1, +1);
|
||||||
|
// Fill in the left face vertex data
|
||||||
|
vertices[i++].Set(-x, +y, +z, -1, +0, +0, +0, +0);
|
||||||
|
vertices[i++].Set(-x, -y, +z, -1, +0, +0, +0, +1);
|
||||||
|
vertices[i++].Set(-x, -y, -z, -1, +0, +0, +1, +1);
|
||||||
|
vertices[i++].Set(-x, +y, -z, -1, +0, +0, +1, +0);
|
||||||
|
// Fill in the right face vertex data
|
||||||
|
vertices[i++].Set(+x, +y, -z, +1, +0, +0, +0, +0);
|
||||||
|
vertices[i++].Set(+x, -y, -z, +1, +0, +0, +0, +1);
|
||||||
|
vertices[i++].Set(+x, -y, +z, +1, +0, +0, +1, +1);
|
||||||
|
vertices[i].Set(+x, +y, +z, +1, +0, +0, +1, +0);
|
||||||
|
|
||||||
|
for(i = 0; i < vertexCount; i++)
|
||||||
|
{
|
||||||
|
vertices[i].color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIndices(const uint32_t* data, uint32_t size, uint32_t offset = 0) const
|
||||||
|
{
|
||||||
|
size += offset;
|
||||||
|
for(; offset < size; offset++)
|
||||||
|
{
|
||||||
|
if (indexType == VertexIndexType::UINT16)
|
||||||
|
{
|
||||||
|
static_cast<uint16_t*>(indices)[offset] = static_cast<uint16_t>(data[offset]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_cast<uint32_t*>(indices)[offset] = data[offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
vertexCount = 0;
|
||||||
|
indexCount = 0;
|
||||||
|
Free();
|
||||||
|
renderGeo->Close();
|
||||||
|
renderGeo = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Free()
|
||||||
|
{
|
||||||
|
if(vertices) delete[] vertices;
|
||||||
|
free(indices);
|
||||||
|
vertices = nullptr;
|
||||||
|
indices = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
13
openVulkanoCpp/Scene/Material.hpp
Normal file
13
openVulkanoCpp/Scene/Material.hpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Shader.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
struct Material
|
||||||
|
{
|
||||||
|
Shader* shader;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
9
openVulkanoCpp/Scene/Node.cpp
Normal file
9
openVulkanoCpp/Scene/Node.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include "Node.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
const glm::mat4x4 Node::IDENTITY = glm::mat4(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
212
openVulkanoCpp/Scene/Node.hpp
Normal file
212
openVulkanoCpp/Scene/Node.hpp
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include "../Base/Utils.hpp"
|
||||||
|
#include "../Base/IInitable.hpp"
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "Drawable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
enum class UpdateFrequency
|
||||||
|
{
|
||||||
|
Always, Sometimes, Never
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Node : virtual IInitable, virtual ICloseable
|
||||||
|
{
|
||||||
|
friend Scene;
|
||||||
|
protected:
|
||||||
|
static const glm::mat4x4 IDENTITY;
|
||||||
|
public:
|
||||||
|
glm::mat4x4 localMat, worldMat;
|
||||||
|
bool enabled = true;
|
||||||
|
Node* parent = nullptr;
|
||||||
|
Scene* scene = nullptr;
|
||||||
|
std::vector<Node*> children;
|
||||||
|
std::vector<Drawable*> drawables;
|
||||||
|
UpdateFrequency matrixUpdateFrequency = UpdateFrequency::Never;
|
||||||
|
ICloseable* renderNode = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Node() = default;
|
||||||
|
virtual ~Node() = default;
|
||||||
|
|
||||||
|
void Init() override
|
||||||
|
{
|
||||||
|
if (parent || scene || !children.empty() || !drawables.empty()) throw std::runtime_error("Node already initialized");
|
||||||
|
localMat = worldMat = IDENTITY;
|
||||||
|
enabled = true;
|
||||||
|
parent = nullptr;
|
||||||
|
children = std::vector<Node*>();
|
||||||
|
drawables = std::vector<Drawable*>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
children.clear();
|
||||||
|
if (renderNode) renderNode->Close();
|
||||||
|
parent = nullptr;
|
||||||
|
scene = nullptr;
|
||||||
|
enabled = false;
|
||||||
|
if (!children.empty()) Logger::SCENE->warn("Closing Node that has children!");
|
||||||
|
for (Node* child : children)
|
||||||
|
{
|
||||||
|
child->SetParent(nullptr);
|
||||||
|
}
|
||||||
|
children.clear();
|
||||||
|
for(size_t i = drawables.size(); i > 0; i--)
|
||||||
|
{
|
||||||
|
RemoveDrawable(drawables[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddChild(Node* node)
|
||||||
|
{
|
||||||
|
node->SetParent(this);
|
||||||
|
children.push_back(node);
|
||||||
|
node->UpdateWorldMatrix(worldMat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddChild(Drawable* drawable)
|
||||||
|
{
|
||||||
|
AddDrawable(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveChild(Node* node)
|
||||||
|
{
|
||||||
|
if (node->parent == this)
|
||||||
|
{
|
||||||
|
Utils::Remove(children, node);
|
||||||
|
node->SetParent(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveChild(Drawable* drawable)
|
||||||
|
{
|
||||||
|
RemoveDrawable(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDrawable(Drawable* drawable)
|
||||||
|
{
|
||||||
|
if (scene) drawable->SetScene(scene);
|
||||||
|
else if (drawable->GetScene()) Logger::SCENE->warn("Drawable is already associated with a scene, but the node it was added to is not!");
|
||||||
|
drawable->AddNode(this);
|
||||||
|
drawables.push_back(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveDrawable(Drawable* drawable)
|
||||||
|
{
|
||||||
|
drawable->RemoveNode(this);
|
||||||
|
Utils::Remove(drawables, drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetMatrix(glm::mat4x4 mat)
|
||||||
|
{
|
||||||
|
localMat = mat;
|
||||||
|
UpdateWorldMatrix(parent ? parent->GetWorldMatrix() : IDENTITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::mat4x4& GetMatrix() const
|
||||||
|
{
|
||||||
|
return localMat;
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::mat4x4& GetWorldMatrix() const
|
||||||
|
{
|
||||||
|
return worldMat;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsEnabled() const
|
||||||
|
{
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Enable()
|
||||||
|
{
|
||||||
|
enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disable()
|
||||||
|
{
|
||||||
|
enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* GetParent() const
|
||||||
|
{
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* GetScene() const
|
||||||
|
{
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRoot() const
|
||||||
|
{
|
||||||
|
return scene && parent == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateFrequency GetUpdateFrequency()
|
||||||
|
{
|
||||||
|
return matrixUpdateFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUpdateFrequency(UpdateFrequency frequency)
|
||||||
|
{
|
||||||
|
if (!children.empty()) throw std::runtime_error("The update must not be changed for nodes with children.");
|
||||||
|
this->matrixUpdateFrequency = frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void UpdateWorldMatrix(const glm::mat4x4& parentWorldMat)
|
||||||
|
{
|
||||||
|
worldMat = parentWorldMat * localMat;
|
||||||
|
for (const auto& node : children)
|
||||||
|
{
|
||||||
|
node->UpdateWorldMatrix(worldMat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetParent(Node* parent)
|
||||||
|
{
|
||||||
|
if (this->parent && parent) throw std::runtime_error("Node already has a parent! Nodes must not be used multiple times!");
|
||||||
|
this->parent = parent;
|
||||||
|
if(parent && parent != this) this->scene = parent->scene;
|
||||||
|
if (!parent) SetScene(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetScene(Scene* scene)
|
||||||
|
{
|
||||||
|
if (this->scene && scene) throw std::runtime_error("Node already has a scene!");
|
||||||
|
this->scene = scene;
|
||||||
|
for (const auto& node : children)
|
||||||
|
{
|
||||||
|
node->SetScene(scene);
|
||||||
|
}
|
||||||
|
if (scene)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < drawables.size(); i++)
|
||||||
|
{
|
||||||
|
Scene* drawableScene = drawables[i]->GetScene();
|
||||||
|
if(drawableScene)
|
||||||
|
{
|
||||||
|
if(drawableScene != scene)
|
||||||
|
{
|
||||||
|
Logger::SCENE->warn("Drawable is already associated with a scene! Creating copy.");
|
||||||
|
drawables[i] = drawables[i]->Copy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawables[i]->SetScene(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
89
openVulkanoCpp/Scene/Scene.hpp
Normal file
89
openVulkanoCpp/Scene/Scene.hpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Node.hpp"
|
||||||
|
#include "Camera.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
struct Scene : virtual public IInitable, virtual public ICloseable
|
||||||
|
{
|
||||||
|
Node* root;
|
||||||
|
std::vector<Drawable*> shapeList;
|
||||||
|
Shader* shader;
|
||||||
|
Camera* camera;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Scene() : root(nullptr) {}
|
||||||
|
|
||||||
|
virtual ~Scene()
|
||||||
|
{
|
||||||
|
if (root) Scene::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init() override
|
||||||
|
{
|
||||||
|
Node* newRoot = new Node();
|
||||||
|
newRoot->Init();
|
||||||
|
Init(newRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(Node* root)
|
||||||
|
{
|
||||||
|
if (root->GetParent()) throw std::runtime_error("Node has a parent! Only nodes without a parent may be a root node!");
|
||||||
|
root->SetScene(this);
|
||||||
|
root->SetParent(root);
|
||||||
|
this->root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* GetRoot() const
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterDrawable(Drawable* drawable)
|
||||||
|
{
|
||||||
|
if (drawable->GetScene() != this) drawable->SetScene(this);
|
||||||
|
if (Utils::Contains(shapeList, drawable)) return; // Prevent duplicate entries
|
||||||
|
shapeList.push_back(drawable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveDrawable(Drawable* drawable)
|
||||||
|
{
|
||||||
|
Utils::Remove(shapeList, drawable);
|
||||||
|
drawable->SetScene(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCamera(Camera* camera)
|
||||||
|
{
|
||||||
|
this->camera = camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera* GetCamera() const
|
||||||
|
{
|
||||||
|
return camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Checks if the scene is valid and attempts to fix problems.
|
||||||
|
*/
|
||||||
|
void Validate()
|
||||||
|
{
|
||||||
|
for (Drawable* drawable : shapeList)
|
||||||
|
{
|
||||||
|
if(drawable->GetScene() != this)
|
||||||
|
{
|
||||||
|
if (!drawable->GetScene()) drawable->SetScene(this);
|
||||||
|
else Logger::SCENE->error("Scene is linked with drawable from different scene!!!"); //TODO handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO check node tree
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
38
openVulkanoCpp/Scene/Shader.hpp
Normal file
38
openVulkanoCpp/Scene/Shader.hpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Scene
|
||||||
|
{
|
||||||
|
enum class Topology
|
||||||
|
{
|
||||||
|
PointList, LineList, LineStripe, TriangleList, TriangleStripe
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Shader : public virtual ICloseable
|
||||||
|
{
|
||||||
|
std::string vertexShaderName, fragmentShaderName;
|
||||||
|
Topology topology = Topology::TriangleList;
|
||||||
|
ICloseable* renderShader = nullptr;
|
||||||
|
|
||||||
|
Shader() = default;
|
||||||
|
~Shader() { if (renderShader) Shader::Close(); }
|
||||||
|
|
||||||
|
void Init(const std::string& vertexShaderName, const std::string& fragmentShaderName)
|
||||||
|
{
|
||||||
|
if (renderShader) throw std::runtime_error("Shader already initialized!");
|
||||||
|
this->vertexShaderName = vertexShaderName;
|
||||||
|
this->fragmentShaderName = fragmentShaderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
renderShader->Close();
|
||||||
|
renderShader = nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
178
openVulkanoCpp/Scene/Vertex.hpp
Normal file
178
openVulkanoCpp/Scene/Vertex.hpp
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <assimp/vector2.h>
|
||||||
|
#include <assimp/vector3.h>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
struct Vertex
|
||||||
|
{
|
||||||
|
glm::vec3 position, normal, tangent, biTangent, textureCoordinates;
|
||||||
|
glm::vec4 color;
|
||||||
|
|
||||||
|
Vertex() = default;
|
||||||
|
|
||||||
|
Vertex(const aiVector3D& pos) : position(pos.x, pos.y, pos.z), normal(), tangent(), biTangent(), textureCoordinates(), color()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Vertex(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
|
||||||
|
: position({ x, y, z }), normal({ nx, ny, nz }), tangent(), biTangent(), textureCoordinates({ u, v, 0 }), color()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Vertex(const glm::vec3& position, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& biTangent, const glm::vec2& textureCoordinates)
|
||||||
|
: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates, 0), color()
|
||||||
|
{}
|
||||||
|
|
||||||
|
Vertex(const glm::vec3 & position, const glm::vec3 & normal, const glm::vec3 & tangent, const glm::vec3 & biTangent, const glm::vec3 & textureCoordinates)
|
||||||
|
: position(position), normal(normal), tangent(tangent), biTangent(biTangent), textureCoordinates(textureCoordinates), color()
|
||||||
|
{}
|
||||||
|
|
||||||
|
~Vertex() = default;
|
||||||
|
|
||||||
|
void Set(const float& x, const float& y, const float& z)
|
||||||
|
{
|
||||||
|
position = { x, y, z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const glm::vec3& position)
|
||||||
|
{
|
||||||
|
this->position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const aiVector3D& position)
|
||||||
|
{
|
||||||
|
this->position = { position.x, position.y, position.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const float& x, const float& y, const float& z, const float& nx, const float& ny, const float& nz, const float& u, const float& v)
|
||||||
|
{
|
||||||
|
this->position = { x, y, z };
|
||||||
|
this->normal = { nx, ny, nz };
|
||||||
|
this->textureCoordinates = { u, v, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const glm::vec3& position, const glm::vec3& normal, const glm::vec2& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->position = position;
|
||||||
|
this->normal = normal;
|
||||||
|
this->textureCoordinates = { textureCoordinates, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const glm::vec3& position, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& biTangent, const glm::vec2& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->position = position;
|
||||||
|
this->normal = normal;
|
||||||
|
this->tangent = tangent;
|
||||||
|
this->biTangent = biTangent;
|
||||||
|
this->textureCoordinates = { textureCoordinates,0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNormal(const float& nx, const float& ny, const float& nz)
|
||||||
|
{
|
||||||
|
this->normal = { nx, ny, nz };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNormal(const glm::vec3& normal)
|
||||||
|
{
|
||||||
|
this->normal = normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetNormal(const aiVector3D& normal)
|
||||||
|
{
|
||||||
|
this->normal = { normal.x, normal.y, normal.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangent(const float& tx, const float& ty, const float& tz)
|
||||||
|
{
|
||||||
|
this->tangent = { tx, ty, tz };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangent(const glm::vec3& tangent)
|
||||||
|
{
|
||||||
|
this->tangent = tangent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangent(const aiVector3D& tangent)
|
||||||
|
{
|
||||||
|
this->tangent = { tangent.x, tangent.y, tangent.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangentAndBiTangent(const float& tx, const float& ty, const float& tz, const float& bx, const float& by, const float& bz)
|
||||||
|
{
|
||||||
|
this->biTangent = { bx, by, bz };
|
||||||
|
this->tangent = { tx, ty, tz };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangentAndBiTangent(const glm::vec3& tangent, const glm::vec3& biTangent)
|
||||||
|
{
|
||||||
|
this->biTangent = biTangent;
|
||||||
|
this->tangent = tangent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTangentAndBiTangent(const aiVector3D& tangent, const aiVector3D& biTangent)
|
||||||
|
{
|
||||||
|
this->tangent = { tangent.x, tangent.y, tangent.z };
|
||||||
|
this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetBiTangent(const float& bx, const float& by, const float& bz)
|
||||||
|
{
|
||||||
|
this->biTangent = { bx, by, bz };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetBiTangent(const glm::vec3& biTangent)
|
||||||
|
{
|
||||||
|
this->biTangent = biTangent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetBiTangent(const aiVector3D& biTangent)
|
||||||
|
{
|
||||||
|
this->biTangent = { biTangent.x, biTangent.y, biTangent.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const float& u, const float& v)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = { u, v, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const float& u, const float& v, const float& w)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = { u, v, w };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const glm::vec2& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = { textureCoordinates, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const glm::vec3& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = textureCoordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const aiVector2D& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetTextureCoordinates(const aiVector3D& textureCoordinates)
|
||||||
|
{
|
||||||
|
this->textureCoordinates = { textureCoordinates.x, textureCoordinates.y, textureCoordinates.z };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetColor(const float& r, const float& g, const float& b, const float& a = 1)
|
||||||
|
{
|
||||||
|
color = { r,g,b,a };
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetColor(const glm::vec4& color)
|
||||||
|
{
|
||||||
|
this->color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetColor(const aiColor4D& color)
|
||||||
|
{
|
||||||
|
this->color = { color.r, color.g, color.b, color.a };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
7
openVulkanoCpp/Shader/CompileShaders.bat
Normal file
7
openVulkanoCpp/Shader/CompileShaders.bat
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
REM Make the directory batch file resides in as the working directory.
|
||||||
|
pushd %~dp0
|
||||||
|
|
||||||
|
glslangvalidator -V basic.vert -o basic.vert.spv
|
||||||
|
glslangvalidator -V basic.frag -o basic.frag.spv
|
||||||
|
|
||||||
|
popd
|
||||||
9
openVulkanoCpp/Shader/basic.frag
Normal file
9
openVulkanoCpp/Shader/basic.frag
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#version 450
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 color;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
outColor = color;
|
||||||
|
}
|
||||||
BIN
openVulkanoCpp/Shader/basic.frag.spv
Normal file
BIN
openVulkanoCpp/Shader/basic.frag.spv
Normal file
Binary file not shown.
29
openVulkanoCpp/Shader/basic.vert
Normal file
29
openVulkanoCpp/Shader/basic.vert
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#version 450
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location = 0) in vec3 position;
|
||||||
|
layout(location = 1) in vec3 normal;
|
||||||
|
layout(location = 2) in vec3 tangent;
|
||||||
|
layout(location = 3) in vec3 biTangent;
|
||||||
|
layout(location = 4) in vec3 textureCoordinates;
|
||||||
|
layout(location = 5) in vec4 color;
|
||||||
|
layout(location = 0) out vec4 outColor;
|
||||||
|
|
||||||
|
layout(binding = 0) uniform NodeData
|
||||||
|
{
|
||||||
|
mat4 world;
|
||||||
|
} node;
|
||||||
|
|
||||||
|
layout(std140, push_constant) uniform CameraData {
|
||||||
|
mat4 viewProjection;
|
||||||
|
} cam;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 light = normalize(vec3(1));
|
||||||
|
vec4 worldPos = node.world * vec4(position, 1.0);
|
||||||
|
vec3 worldNormal = normalize(transpose(inverse(mat3(node.world))) * normal);
|
||||||
|
float brightness = max(0.0, dot(worldNormal, light));
|
||||||
|
outColor = vec4(clamp(color.rgb * (0.5 + brightness / 2), 0, 1), 1);
|
||||||
|
gl_Position = normalize(cam.viewProjection * worldPos);
|
||||||
|
}
|
||||||
BIN
openVulkanoCpp/Shader/basic.vert.spv
Normal file
BIN
openVulkanoCpp/Shader/basic.vert.spv
Normal file
Binary file not shown.
109
openVulkanoCpp/Vulkan/Buffer.hpp
Normal file
109
openVulkanoCpp/Vulkan/Buffer.hpp
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "Device.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* \brief A not managed buffer. This should be used rarely.
|
||||||
|
*/
|
||||||
|
struct Buffer : virtual public ICloseable
|
||||||
|
{
|
||||||
|
vk::Device device;
|
||||||
|
vk::DeviceMemory memory;
|
||||||
|
vk::DeviceSize size = 0, alignment = 0, allocSize = 0;
|
||||||
|
vk::MemoryPropertyFlags memoryPropertyFlags;
|
||||||
|
void* mapped = nullptr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Maps the buffer into the memory of the host.
|
||||||
|
* \tparam T The type of the buffers data.
|
||||||
|
* \param offset The offset from where to map the buffer.
|
||||||
|
* \param size The size to be mapped. VK_WHOLE_SIZE to map the whole buffer.
|
||||||
|
* \return The pointer to the mapped buffer.
|
||||||
|
*/
|
||||||
|
template <typename T = void>
|
||||||
|
T * Map(size_t offset = 0, VkDeviceSize size = VK_WHOLE_SIZE)
|
||||||
|
{
|
||||||
|
mapped = device.mapMemory(memory, offset, size, vk::MemoryMapFlags());
|
||||||
|
return static_cast<T*>(mapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Un-maps the buffer from the host.
|
||||||
|
*/
|
||||||
|
void UnMap()
|
||||||
|
{
|
||||||
|
device.unmapMemory(memory);
|
||||||
|
mapped = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
|
||||||
|
* \param size The size of the data to copy.
|
||||||
|
* \param data The data to copy
|
||||||
|
* \param offset The offset for where to copy the data to in the buffer.
|
||||||
|
*/
|
||||||
|
void Copy(size_t size, const void* data, VkDeviceSize offset = 0) const
|
||||||
|
{
|
||||||
|
if (!mapped) return;
|
||||||
|
memcpy(static_cast<uint8_t*>(mapped) + offset, data, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
|
||||||
|
* \param data The data to copy.
|
||||||
|
* \param offset The offset for where to copy the data to in the buffer.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Copy(const T& data, VkDeviceSize offset = 0) const
|
||||||
|
{
|
||||||
|
Copy(sizeof(T), &data, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Copies data into the mapped buffer. Will not do anything if the buffer is not mapped!
|
||||||
|
* \param data The data to copy.
|
||||||
|
* \param offset The offset for where to copy the data to in the buffer.
|
||||||
|
*/
|
||||||
|
template <typename T>
|
||||||
|
void Copy(const std::vector<T>& data, VkDeviceSize offset = 0) const
|
||||||
|
{
|
||||||
|
copy(sizeof(T) * data.size(), data.data(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Flushes the memory region of the buffer to the device. This should be only necessary for non coherent memory.
|
||||||
|
* \param size The amount to flush. VK_WHOLE_SIZE flushes the entire buffer.
|
||||||
|
* \param offset The offset from where to start the flush.
|
||||||
|
*/
|
||||||
|
void Flush(vk::DeviceSize size = VK_WHOLE_SIZE, vk::DeviceSize offset = 0) const
|
||||||
|
{
|
||||||
|
device.flushMappedMemoryRanges(vk::MappedMemoryRange(memory, offset, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Invalidates the memory region of the buffer to allow access from the host. This should be only necessary for non coherent memory.
|
||||||
|
* \param size The amount to make available. VK_WHOLE_SIZE invalidate the entire buffer.
|
||||||
|
* \param offset The offset from where to make the memory available.
|
||||||
|
*/
|
||||||
|
void Invalidate(vk::DeviceSize size = VK_WHOLE_SIZE, vk::DeviceSize offset = 0) const
|
||||||
|
{
|
||||||
|
device.invalidateMappedMemoryRanges(vk::MappedMemoryRange(memory, offset, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
if (mapped) UnMap();
|
||||||
|
if(memory)
|
||||||
|
{
|
||||||
|
device.free(memory);
|
||||||
|
memory = vk::DeviceMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
45
openVulkanoCpp/Vulkan/CommandHelper.hpp
Normal file
45
openVulkanoCpp/Vulkan/CommandHelper.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct CommandHelper : virtual ICloseable
|
||||||
|
{
|
||||||
|
vk::Device device;
|
||||||
|
vk::CommandPool cmdPool;
|
||||||
|
vk::CommandBuffer cmdBuffer;
|
||||||
|
vk::CommandBufferLevel level;
|
||||||
|
|
||||||
|
CommandHelper() = default;
|
||||||
|
~CommandHelper() { if (cmdPool) CommandHelper::Close(); }
|
||||||
|
|
||||||
|
void Init(vk::Device device, uint32_t queueIndex, vk::CommandBufferLevel level = vk::CommandBufferLevel::eSecondary)
|
||||||
|
{
|
||||||
|
this->level = level;
|
||||||
|
this->device = device;
|
||||||
|
cmdPool = device.createCommandPool(vk::CommandPoolCreateInfo({}, queueIndex));
|
||||||
|
vk::CommandBufferAllocateInfo bufferAllocInfo = { cmdPool, level, 1 };
|
||||||
|
cmdBuffer = device.allocateCommandBuffers(bufferAllocInfo)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset() const
|
||||||
|
{
|
||||||
|
device.resetCommandPool(cmdPool, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::CommandBufferLevel GetLevel() const
|
||||||
|
{
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
device.freeCommandBuffers(cmdPool, 1, &cmdBuffer);
|
||||||
|
device.destroyCommandPool(cmdPool);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
119
openVulkanoCpp/Vulkan/Context.hpp
Normal file
119
openVulkanoCpp/Vulkan/Context.hpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../Base/IGraphicsApp.hpp"
|
||||||
|
#include "../Base/IGraphicsAppManager.hpp"
|
||||||
|
#include "../Base/EngineConstants.hpp"
|
||||||
|
#include "../Base/Utils.hpp"
|
||||||
|
#include "Debuging/ValidationLayer.hpp"
|
||||||
|
#include "DeviceManager.hpp"
|
||||||
|
#include "SwapChain.hpp"
|
||||||
|
#include "RenderPass.hpp"
|
||||||
|
#include "Pipeline.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class Context : virtual public ICloseable
|
||||||
|
{
|
||||||
|
bool enableValidationLayer, initialized;
|
||||||
|
std::set<std::string> requiredExtensions;
|
||||||
|
public:
|
||||||
|
DeviceManager deviceManager;
|
||||||
|
vk::Instance instance; // Vulkan instance
|
||||||
|
vk::DispatchLoaderDynamic dynamicDispatch; // for access to features not available in statically linked Vulkan lib
|
||||||
|
vk::SurfaceKHR surface; // Vulkan surface to display framebuffer on
|
||||||
|
Device* device = nullptr;
|
||||||
|
SwapChain swapChain;
|
||||||
|
RenderPass swapChainRenderPass;
|
||||||
|
IVulkanWindow* window = nullptr;
|
||||||
|
IGraphicsAppManager* graphicsAppManager = nullptr;
|
||||||
|
Pipeline pipeline;
|
||||||
|
|
||||||
|
Context() : initialized(false)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
enableValidationLayer = true;
|
||||||
|
#else
|
||||||
|
enableValidationLayer = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
virtual ~Context()
|
||||||
|
{
|
||||||
|
if (initialized) Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(IGraphicsAppManager* graphicsAppManager, IVulkanWindow* window)
|
||||||
|
{
|
||||||
|
if (initialized) throw std::runtime_error("The context is already initialized");
|
||||||
|
this->graphicsAppManager = graphicsAppManager;
|
||||||
|
this->window = window;
|
||||||
|
|
||||||
|
// Get the extensions required to display on the window
|
||||||
|
for (const auto& requiredExtension : window->GetRequiredInstanceExtensions()) { RequireExtension(requiredExtension.c_str()); }
|
||||||
|
|
||||||
|
CreateInstance(); // Create the vulkan instance
|
||||||
|
surface = window->CreateSurface(instance); // Create the surface from the window
|
||||||
|
CreateDevice();
|
||||||
|
|
||||||
|
|
||||||
|
swapChain.Init(device, surface, window);
|
||||||
|
swapChainRenderPass.Init(device, &swapChain);
|
||||||
|
|
||||||
|
pipeline.Init(device->device);
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
if (!initialized) return;
|
||||||
|
device->WaitIdle();
|
||||||
|
|
||||||
|
pipeline.Close();
|
||||||
|
swapChainRenderPass.Close();
|
||||||
|
swapChain.Close();
|
||||||
|
deviceManager.Close();
|
||||||
|
//TODO
|
||||||
|
|
||||||
|
if (enableValidationLayer) Debug::CloseValidationLayers(instance);
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resize(const uint32_t newWidth, const uint32_t newHeight)
|
||||||
|
{
|
||||||
|
device->WaitIdle();
|
||||||
|
swapChain.Resize(newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequireExtension(const char* extension)
|
||||||
|
{
|
||||||
|
requiredExtensions.emplace(extension);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void CreateInstance()
|
||||||
|
{
|
||||||
|
if (enableValidationLayer) RequireExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||||
|
vk::ApplicationInfo appInfo(graphicsAppManager->GetGraphicsApp()->GetAppName().c_str(), graphicsAppManager->GetGraphicsApp()->GetAppVersionAsInt(), ENGINE_NAME, ENGINE_VERSION.intVersion, VK_MAKE_VERSION(1, 1, 0));
|
||||||
|
std::vector<const char*> extensions = Utils::toCString(requiredExtensions), layers = Debug::GetValidationLayers();
|
||||||
|
|
||||||
|
const vk::InstanceCreateInfo createInfo(vk::InstanceCreateFlags(), &appInfo, enableValidationLayer ? layers.size() : 0,
|
||||||
|
layers.data(), extensions.size(), extensions.data());
|
||||||
|
|
||||||
|
instance = vk::createInstance(createInfo);
|
||||||
|
|
||||||
|
if (enableValidationLayer) Debug::SetupValidationLayers(instance, vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning /*| vk::DebugReportFlagBitsEXT::eInformation | vk::DebugReportFlagBitsEXT::eDebug*/);
|
||||||
|
dynamicDispatch.init(instance, &vkGetInstanceProcAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateDevice()
|
||||||
|
{
|
||||||
|
deviceManager.Init(instance);
|
||||||
|
device = deviceManager.GetCompatibleDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });
|
||||||
|
device->PrepareDevice({ VK_KHR_SWAPCHAIN_EXTENSION_NAME }, surface);
|
||||||
|
dynamicDispatch.init(instance, &vkGetInstanceProcAddr, device->device, &vkGetDeviceProcAddr);
|
||||||
|
Logger::RENDER->info("Found device: {0}", device->GetDeviceName());;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
97
openVulkanoCpp/Vulkan/Debuging/ValidationLayer.hpp
Normal file
97
openVulkanoCpp/Vulkan/Debuging/ValidationLayer.hpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <set>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
#define RENDER_DOC
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
namespace Debug
|
||||||
|
{
|
||||||
|
std::list<std::string> activeValidationLayerNames = {
|
||||||
|
"VK_LAYER_LUNARG_assistant_layer",
|
||||||
|
"VK_LAYER_LUNARG_standard_validation",
|
||||||
|
//"VK_EXT_debug_marker",
|
||||||
|
#ifdef RENDER_DOC
|
||||||
|
"VK_LAYER_RENDERDOC_Capture", // RenderDoc must be open for this layer to work!
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::set<std::string> GetAvailableValidationLayers()
|
||||||
|
{
|
||||||
|
auto layers = vk::enumerateInstanceLayerProperties();
|
||||||
|
std::set<std::string> layersVector;
|
||||||
|
std::string layerList = "";
|
||||||
|
for(const auto& layer : layers)
|
||||||
|
{
|
||||||
|
std::string name = layer.layerName;
|
||||||
|
layersVector.insert(name);
|
||||||
|
if (layerList.length() > 0) layerList += ", ";
|
||||||
|
layerList += name;
|
||||||
|
}
|
||||||
|
Logger::RENDER->debug("Available Vulkan Validation Layers: {0}", layerList);
|
||||||
|
return layersVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<const char*> GetValidationLayers()
|
||||||
|
{
|
||||||
|
std::set<std::string> availableLayers = GetAvailableValidationLayers();
|
||||||
|
std::vector<const char*> layers;
|
||||||
|
std::string layerList = "";
|
||||||
|
for (const auto& name : activeValidationLayerNames)
|
||||||
|
{
|
||||||
|
if (availableLayers.count(name) != 0)
|
||||||
|
{
|
||||||
|
layers.push_back(name.c_str());
|
||||||
|
if (layerList.length() > 0) layerList += ", ";
|
||||||
|
layerList += name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger::RENDER->debug("Active Vulkan Validation Layers: {0}", layerList);
|
||||||
|
return layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::once_flag dispatcherInitFlag;
|
||||||
|
vk::DispatchLoaderDynamic dispatcher;
|
||||||
|
vk::DebugReportCallbackEXT msgCallback;
|
||||||
|
|
||||||
|
inline VkBool32 ValidationLayerCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType,
|
||||||
|
uint64_t srcObject, size_t location, int32_t msgCode, const char* layerPrefix,
|
||||||
|
const char* msg, void* pUserData)
|
||||||
|
{
|
||||||
|
std::string prefix = "VK_DEBUG:";
|
||||||
|
spdlog::level::level_enum level = spdlog::level::info;
|
||||||
|
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) level = spdlog::level::err;
|
||||||
|
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) level = spdlog::level::warn;
|
||||||
|
else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)
|
||||||
|
{
|
||||||
|
level = spdlog::level::warn;
|
||||||
|
prefix = "[PERF] " + prefix;
|
||||||
|
}
|
||||||
|
else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) level = spdlog::level::debug;
|
||||||
|
|
||||||
|
Logger::RENDER->log(level, "{0} [{1}] Code {2}: {3}", prefix, layerPrefix, msgCode, msg);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetupValidationLayers(const vk::Instance& instance, const vk::DebugReportFlagsEXT& flags)
|
||||||
|
{
|
||||||
|
Logger::RENDER->info("Setting up Vulkan Validation Layer");
|
||||||
|
std::call_once(dispatcherInitFlag, [&] { dispatcher.init(instance, &vkGetInstanceProcAddr); });
|
||||||
|
vk::DebugReportCallbackCreateInfoEXT dbgCreateInfo = {};
|
||||||
|
dbgCreateInfo.pfnCallback = (PFN_vkDebugReportCallbackEXT)ValidationLayerCallback;
|
||||||
|
dbgCreateInfo.flags = flags;
|
||||||
|
msgCallback = instance.createDebugReportCallbackEXT(dbgCreateInfo, nullptr, dispatcher);
|
||||||
|
Logger::RENDER->info("Vulkan Validation Layer setup");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CloseValidationLayers(const vk::Instance& instance) {
|
||||||
|
std::call_once(dispatcherInitFlag, [&] { dispatcher.init(instance, &vkGetInstanceProcAddr); });
|
||||||
|
instance.destroyDebugReportCallbackEXT(msgCallback, nullptr, dispatcher);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
279
openVulkanoCpp/Vulkan/Device.hpp
Normal file
279
openVulkanoCpp/Vulkan/Device.hpp
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include <set>
|
||||||
|
#include <functional>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class DeviceQueueCreateInfoBuilder
|
||||||
|
{
|
||||||
|
std::vector<vk::DeviceQueueCreateInfo> createInfos;
|
||||||
|
std::vector<std::vector<float>> prioritiesVector;
|
||||||
|
public:
|
||||||
|
DeviceQueueCreateInfoBuilder() = default;
|
||||||
|
~DeviceQueueCreateInfoBuilder() = default;
|
||||||
|
|
||||||
|
void AddQueueFamily(const uint32_t queueFamilyIndex, const std::vector<float>& priorities)
|
||||||
|
{
|
||||||
|
prioritiesVector.push_back(priorities);
|
||||||
|
createInfos.emplace_back(vk::DeviceQueueCreateFlags(), queueFamilyIndex, priorities.size(), prioritiesVector[prioritiesVector.size()-1].data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddQueueFamily(uint32_t queueFamilyIndex, uint32_t count = 1)
|
||||||
|
{
|
||||||
|
std::vector<float> priorities;
|
||||||
|
priorities.resize(count);
|
||||||
|
std::fill(priorities.begin(), priorities.end(), 0.0f);
|
||||||
|
AddQueueFamily(queueFamilyIndex, priorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<vk::DeviceQueueCreateInfo>& GetDeviceQueueCreateInfos()
|
||||||
|
{
|
||||||
|
return createInfos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Device : virtual public ICloseable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vk::PhysicalDevice physicalDevice;
|
||||||
|
std::vector<vk::QueueFamilyProperties> queueFamilyProperties; // Queue family properties
|
||||||
|
vk::PhysicalDeviceProperties properties; // Physical device properties (for e.g. checking device limits)
|
||||||
|
vk::PhysicalDeviceFeatures features; // Physical device features (for e.g. checking if a feature is available)
|
||||||
|
vk::PhysicalDeviceMemoryProperties memoryProperties; // available memory properties
|
||||||
|
vk::Device device; // Logical device, application's view of the physical device (GPU)
|
||||||
|
vk::PipelineCache pipelineCache;
|
||||||
|
vk::CommandPool graphicsCommandPool;
|
||||||
|
std::set<std::string> supportedExtensions;
|
||||||
|
vk::Queue graphicsQueue;
|
||||||
|
|
||||||
|
struct QueueIndices {
|
||||||
|
uint32_t graphics = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
uint32_t compute = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
uint32_t transfer = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
|
||||||
|
uint32_t GetGraphics() const { return graphics; }
|
||||||
|
uint32_t GetCompute() const { return compute != graphics ? compute : VK_QUEUE_FAMILY_IGNORED; }
|
||||||
|
uint32_t GetTransfer() const { return (transfer != graphics && transfer != compute) ? transfer : VK_QUEUE_FAMILY_IGNORED; }
|
||||||
|
} queueIndices;
|
||||||
|
|
||||||
|
bool useDebugMarkers;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Device(vk::PhysicalDevice& physicalDevice)
|
||||||
|
{
|
||||||
|
this->physicalDevice = physicalDevice;
|
||||||
|
useDebugMarkers = false;
|
||||||
|
QueryDevice();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDeviceName() const
|
||||||
|
{
|
||||||
|
return properties.deviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareDevice(const vk::ArrayProxy<const std::string>& requestedExtensions, const vk::SurfaceKHR& surface)
|
||||||
|
{
|
||||||
|
queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics, surface); // Make sure that the graphics queue supports the surface
|
||||||
|
BuildDevice(requestedExtensions);
|
||||||
|
//TODO setup debug marker
|
||||||
|
pipelineCache = device.createPipelineCache(vk::PipelineCacheCreateInfo());
|
||||||
|
|
||||||
|
graphicsQueue = device.getQueue(queueIndices.graphics, 0);
|
||||||
|
graphicsCommandPool = device.createCommandPool({ vk::CommandPoolCreateFlagBits::eResetCommandBuffer, queueIndices.graphics, });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> GetExtensions() const
|
||||||
|
{
|
||||||
|
return supportedExtensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsExtensionAvailable(const vk::ArrayProxy<const std::string>& extensions) const
|
||||||
|
{
|
||||||
|
for(const auto& extension : extensions)
|
||||||
|
{
|
||||||
|
if (supportedExtensions.count(extension) == 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitIdle() const
|
||||||
|
{ //TODO wait all queues idle
|
||||||
|
graphicsQueue.waitIdle();
|
||||||
|
device.waitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vk::CommandBuffer CreateCommandBuffer(vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary) const
|
||||||
|
{
|
||||||
|
const vk::CommandBufferAllocateInfo cmdBufferAllocInfo(graphicsCommandPool, level, 1);
|
||||||
|
return device.allocateCommandBuffers(cmdBufferAllocInfo)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlushCommandBuffer(vk::CommandBuffer& cmdBuffer) const
|
||||||
|
{
|
||||||
|
graphicsQueue.submit(vk::SubmitInfo{ 0, nullptr, nullptr, 1, &cmdBuffer }, vk::Fence());
|
||||||
|
WaitIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExecuteNow(const std::function<void(const vk::CommandBuffer& commandBuffer)>& function) const
|
||||||
|
{
|
||||||
|
vk::CommandBuffer commandBuffer = CreateCommandBuffer();
|
||||||
|
commandBuffer.begin(vk::CommandBufferBeginInfo{ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
|
||||||
|
function(commandBuffer);
|
||||||
|
commandBuffer.end();
|
||||||
|
FlushCommandBuffer(commandBuffer);
|
||||||
|
device.freeCommandBuffers(graphicsCommandPool, commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ShaderModule CreateShaderModule(const std::string filename)
|
||||||
|
{
|
||||||
|
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||||
|
if (!file.is_open()) throw std::runtime_error("Failed to open shader file!");
|
||||||
|
const size_t fileSize = static_cast<size_t>(file.tellg());
|
||||||
|
std::vector<char> buffer(fileSize);
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(buffer.data(), fileSize);
|
||||||
|
file.close();
|
||||||
|
vk::ShaderModuleCreateInfo smci = { {}, buffer.size(), reinterpret_cast<const uint32_t*>(buffer.data()) };
|
||||||
|
return CreateShaderModule(smci);
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ShaderModule CreateShaderModule(vk::ShaderModuleCreateInfo& createInfo) const
|
||||||
|
{
|
||||||
|
return device.createShaderModule(createInfo);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void QueryDevice()
|
||||||
|
{
|
||||||
|
// Query device features
|
||||||
|
queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
|
||||||
|
properties = physicalDevice.getProperties();
|
||||||
|
features = physicalDevice.getFeatures();
|
||||||
|
for (auto& ext : physicalDevice.enumerateDeviceExtensionProperties()) { supportedExtensions.insert(ext.extensionName); }
|
||||||
|
|
||||||
|
// Query device memory properties
|
||||||
|
memoryProperties = physicalDevice.getMemoryProperties();
|
||||||
|
queueIndices.graphics = FindBestQueue(vk::QueueFlagBits::eGraphics);
|
||||||
|
queueIndices.compute = FindBestQueue(vk::QueueFlagBits::eCompute);
|
||||||
|
queueIndices.transfer = FindBestQueue(vk::QueueFlagBits::eTransfer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildDevice(const vk::ArrayProxy<const std::string>& requestedExtensions)
|
||||||
|
{
|
||||||
|
vk::DeviceCreateInfo deviceCreateInfo;
|
||||||
|
deviceCreateInfo.pEnabledFeatures = &features; //TODO add option to disable not needed features
|
||||||
|
|
||||||
|
|
||||||
|
DeviceQueueCreateInfoBuilder deviceQueueCreateInfoBuilder;
|
||||||
|
deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetGraphics(), queueFamilyProperties[queueIndices.GetGraphics()].queueCount);
|
||||||
|
if (queueIndices.GetCompute() != VK_QUEUE_FAMILY_IGNORED)
|
||||||
|
deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetCompute(), queueFamilyProperties[queueIndices.GetCompute()].queueCount);
|
||||||
|
if (queueIndices.GetTransfer() != VK_QUEUE_FAMILY_IGNORED)
|
||||||
|
deviceQueueCreateInfoBuilder.AddQueueFamily(queueIndices.GetTransfer(), queueFamilyProperties[queueIndices.GetTransfer()].queueCount);
|
||||||
|
const std::vector<vk::DeviceQueueCreateInfo> deviceQueueCreateInfos = deviceQueueCreateInfoBuilder.GetDeviceQueueCreateInfos();
|
||||||
|
|
||||||
|
deviceCreateInfo.queueCreateInfoCount = static_cast<uint32_t>(deviceQueueCreateInfos.size());
|
||||||
|
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos.data();
|
||||||
|
|
||||||
|
std::vector<const char*> enabledExtensions;
|
||||||
|
for (const auto& extension : requestedExtensions)
|
||||||
|
{
|
||||||
|
enabledExtensions.push_back(extension.c_str());
|
||||||
|
}
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (IsExtensionAvailable({ VK_EXT_DEBUG_MARKER_EXTENSION_NAME }))
|
||||||
|
{ // Enable debug marker extension if available
|
||||||
|
enabledExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||||
|
useDebugMarkers = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
deviceCreateInfo.enabledExtensionCount = static_cast<uint32_t>(enabledExtensions.size());
|
||||||
|
deviceCreateInfo.ppEnabledExtensionNames = enabledExtensions.data();
|
||||||
|
|
||||||
|
device = physicalDevice.createDevice(deviceCreateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FindBestQueue(const vk::QueueFlags& desiredFlags, const vk::SurfaceKHR& surface = nullptr) const
|
||||||
|
{
|
||||||
|
uint32_t best = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
VkQueueFlags bestExtraFlagsCount = VK_QUEUE_FLAG_BITS_MAX_ENUM;
|
||||||
|
for (size_t i = 0; i < queueFamilyProperties.size(); i++)
|
||||||
|
{
|
||||||
|
vk::QueueFlags flags = queueFamilyProperties[i].queueFlags;
|
||||||
|
if (!(flags & desiredFlags)) continue; // Skip queue without desired flags
|
||||||
|
if (surface && VK_FALSE == physicalDevice.getSurfaceSupportKHR(i, surface)) continue;
|
||||||
|
const VkQueueFlags currentExtraFlags = (flags & ~desiredFlags).operator VkQueueFlags();
|
||||||
|
|
||||||
|
if (0 == currentExtraFlags) return i; // return exact match
|
||||||
|
|
||||||
|
if (best == VK_QUEUE_FAMILY_IGNORED || currentExtraFlags < bestExtraFlagsCount)
|
||||||
|
{
|
||||||
|
best = i;
|
||||||
|
bestExtraFlagsCount = currentExtraFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* \brief Vulkan does not require does not define a depth buffer format that must be supported. This method checks for the first supported format from an given array of formats.
|
||||||
|
* \param depthFormats Array of depth formats
|
||||||
|
* \return The first format supported as a depth buffer
|
||||||
|
* \throws If no depth buffer format is supported
|
||||||
|
*/
|
||||||
|
vk::Format GetSupportedDepthFormat(const std::vector<vk::Format>& depthFormats = { vk::Format::eD32SfloatS8Uint, vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint, vk::Format::eD16UnormS8Uint, vk::Format::eD16Unorm }) const
|
||||||
|
{
|
||||||
|
for (auto& format : depthFormats)
|
||||||
|
{
|
||||||
|
vk::FormatProperties formatProps;
|
||||||
|
formatProps = physicalDevice.getFormatProperties(format);
|
||||||
|
if (formatProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment)
|
||||||
|
{
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("No supported depth format");
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Bool32 GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties, uint32_t* typeIndex) const
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < 32; i++)
|
||||||
|
{
|
||||||
|
if ((typeBits & 1) == 1)
|
||||||
|
{
|
||||||
|
if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||||
|
{
|
||||||
|
*typeIndex = i;
|
||||||
|
return VK_TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
typeBits >>= 1;
|
||||||
|
}
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetMemoryType(uint32_t typeBits, const vk::MemoryPropertyFlags& properties) const
|
||||||
|
{
|
||||||
|
uint32_t result = 0;
|
||||||
|
if (VK_FALSE == GetMemoryType(typeBits, properties, &result))
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Unable to find memory type " + to_string(properties));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
device.destroyCommandPool(graphicsCommandPool);
|
||||||
|
//TODO fill
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
44
openVulkanoCpp/Vulkan/DeviceManager.hpp
Normal file
44
openVulkanoCpp/Vulkan/DeviceManager.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <vector>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "Device.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
|
||||||
|
class DeviceManager : virtual public ICloseable
|
||||||
|
{
|
||||||
|
std::vector<Device> devices;
|
||||||
|
public:
|
||||||
|
void Init(const vk::Instance& instance)
|
||||||
|
{
|
||||||
|
devices = std::vector<Device>();
|
||||||
|
for (auto& physicalDevice : instance.enumeratePhysicalDevices())
|
||||||
|
{
|
||||||
|
devices.emplace_back(physicalDevice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Device* GetCompatibleDevice(const std::vector<std::string>& deviceExtensions)
|
||||||
|
{
|
||||||
|
for (auto& device : devices)
|
||||||
|
{
|
||||||
|
if (device.IsExtensionAvailable(deviceExtensions)) return &device;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("No device with required extensions found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
for (auto& device : devices)
|
||||||
|
{
|
||||||
|
device.Close();
|
||||||
|
}
|
||||||
|
devices.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
76
openVulkanoCpp/Vulkan/FrameBuffer.cpp
Normal file
76
openVulkanoCpp/Vulkan/FrameBuffer.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#include "FrameBuffer.hpp"
|
||||||
|
#include "RenderPass.hpp"
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::Init(Device* device, vk::Extent3D size, bool useDepthBuffer)
|
||||||
|
{
|
||||||
|
this->size = size;
|
||||||
|
this->device = device;
|
||||||
|
this->useDepthBuffer = useDepthBuffer;
|
||||||
|
colorFormat = FindColorFormat();
|
||||||
|
if (useDepthBuffer)
|
||||||
|
{
|
||||||
|
depthBufferFormat = FindDepthFormat();
|
||||||
|
CreateDepthStencil();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::InitRenderPass(RenderPass* renderPass)
|
||||||
|
{
|
||||||
|
if (!device) throw std::
|
||||||
|
runtime_error("The frame buffer needs to be initialized before binding it to a render pass");
|
||||||
|
this->renderPass = renderPass;
|
||||||
|
CreateFrameBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::Resize(vk::Extent3D size)
|
||||||
|
{
|
||||||
|
this->size = size;
|
||||||
|
DestroyFrameBuffer();
|
||||||
|
if (depthBuffer) depthBuffer.Close();
|
||||||
|
if (useDepthBuffer) CreateDepthStencil();
|
||||||
|
CreateFrameBuffer();
|
||||||
|
renderPass->UpdateBeginInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::CreateDepthStencil()
|
||||||
|
{
|
||||||
|
vk::ImageCreateInfo depthStencilCreateInfo({}, vk::ImageType::e2D, depthBufferFormat,
|
||||||
|
size, 1, 1);
|
||||||
|
depthStencilCreateInfo.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::
|
||||||
|
eTransferSrc;
|
||||||
|
|
||||||
|
const vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
|
||||||
|
const vk::ImageViewCreateInfo depthStencilViewCreateInfo({}, {}, vk::ImageViewType::e2D, depthBufferFormat,
|
||||||
|
{}, vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1));
|
||||||
|
depthBuffer.Init(device, depthStencilCreateInfo, depthStencilViewCreateInfo);
|
||||||
|
|
||||||
|
device->ExecuteNow([&](auto commandBuffer)
|
||||||
|
{
|
||||||
|
depthBuffer.SetLayout(commandBuffer, aspectMask, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::CreateFrameBuffer()
|
||||||
|
{
|
||||||
|
vk::ImageView attachments[2]; // First attachment is the color buffer, second (optional) the depth buffer
|
||||||
|
if (useDepthBuffer) attachments[1] = depthBuffer.view; //Depth buffer is the same for all frame buffers
|
||||||
|
const vk::FramebufferCreateInfo fbCreateInfo({}, renderPass->renderPass, useDepthBuffer ? 2 : 1, attachments, size.width,
|
||||||
|
size.height, 1);
|
||||||
|
|
||||||
|
auto images = GetImages();
|
||||||
|
frameBuffers.resize(images.size());
|
||||||
|
for (uint32_t i = 0; i < frameBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
attachments[0] = images[i]->GetView();
|
||||||
|
frameBuffers[i] = device->device.createFramebuffer(fbCreateInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openVulkanoCpp::Vulkan::FrameBuffer::DestroyFrameBuffer()
|
||||||
|
{
|
||||||
|
for (const auto frameBuffer : frameBuffers)
|
||||||
|
{
|
||||||
|
device->device.destroyFramebuffer(frameBuffer);
|
||||||
|
}
|
||||||
|
frameBuffers.clear();
|
||||||
|
}
|
||||||
107
openVulkanoCpp/Vulkan/FrameBuffer.hpp
Normal file
107
openVulkanoCpp/Vulkan/FrameBuffer.hpp
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
#include "Image.hpp"
|
||||||
|
#include "Device.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class RenderPass;
|
||||||
|
|
||||||
|
class FrameBuffer : ICloseable
|
||||||
|
{
|
||||||
|
Image depthBuffer;
|
||||||
|
std::vector<vk::Framebuffer> frameBuffers;
|
||||||
|
vk::Format depthBufferFormat = vk::Format::eUndefined, colorFormat = vk::Format::eUndefined;
|
||||||
|
vk::Extent3D size;
|
||||||
|
RenderPass* renderPass;
|
||||||
|
bool useDepthBuffer;
|
||||||
|
Device* device = nullptr;
|
||||||
|
protected:
|
||||||
|
uint32_t currentFrameBufferId = 0;
|
||||||
|
|
||||||
|
FrameBuffer() = default;
|
||||||
|
|
||||||
|
virtual ~FrameBuffer()
|
||||||
|
{
|
||||||
|
if (device) FrameBuffer::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(Device* device, vk::Extent3D size, bool useDepthBuffer = true);
|
||||||
|
|
||||||
|
void SetCurrentFrameId(uint32_t id)
|
||||||
|
{
|
||||||
|
currentFrameBufferId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetCurrentFrameId() const
|
||||||
|
{
|
||||||
|
return currentFrameBufferId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void InitRenderPass(RenderPass* renderPass);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Resize(vk::Extent3D size);
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
DestroyFrameBuffer();
|
||||||
|
if(depthBuffer) depthBuffer.Close();
|
||||||
|
device = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void CreateDepthStencil();
|
||||||
|
|
||||||
|
virtual void CreateFrameBuffer();
|
||||||
|
|
||||||
|
void DestroyFrameBuffer();
|
||||||
|
|
||||||
|
virtual vk::Format FindColorFormat() = 0;
|
||||||
|
|
||||||
|
virtual vk::Format FindDepthFormat()
|
||||||
|
{
|
||||||
|
return device->GetSupportedDepthFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual vk::Format GetColorFormat()
|
||||||
|
{
|
||||||
|
return colorFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual vk::Format GetDepthFormat()
|
||||||
|
{
|
||||||
|
return depthBufferFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual std::vector<IImage*> GetImages() = 0;
|
||||||
|
|
||||||
|
bool UseDepthBuffer() const
|
||||||
|
{
|
||||||
|
return useDepthBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Extent3D GetSize3D() const
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Extent2D GetSize2D() const
|
||||||
|
{
|
||||||
|
return { size.width, size.height };
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Framebuffer& GetCurrentFrameBuffer()
|
||||||
|
{
|
||||||
|
return frameBuffers[currentFrameBufferId];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
98
openVulkanoCpp/Vulkan/Image.hpp
Normal file
98
openVulkanoCpp/Vulkan/Image.hpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "Buffer.hpp"
|
||||||
|
#include "VulkanUtils.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class IImage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IImage() = default;
|
||||||
|
|
||||||
|
virtual vk::Image GetImage() = 0;
|
||||||
|
virtual vk::ImageView GetView() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Image : public Buffer, virtual public IImage
|
||||||
|
{
|
||||||
|
vk::Image image;
|
||||||
|
vk::Extent3D extent;
|
||||||
|
vk::ImageView view;
|
||||||
|
vk::Sampler sampler;
|
||||||
|
vk::Format format = vk::Format::eUndefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief
|
||||||
|
* \param device
|
||||||
|
* \param imageCreateInfo
|
||||||
|
* \param imageViewCreateInfo The image will be set automatically after it's creation
|
||||||
|
* \param memoryPropertyFlags
|
||||||
|
*/
|
||||||
|
void Init(const Device* device, const vk::ImageCreateInfo& imageCreateInfo, vk::ImageViewCreateInfo imageViewCreateInfo, const vk::MemoryPropertyFlags& memoryPropertyFlags = vk::MemoryPropertyFlagBits::eDeviceLocal)
|
||||||
|
{
|
||||||
|
this->device = device->device;
|
||||||
|
image = device->device.createImage(imageCreateInfo);
|
||||||
|
format = imageCreateInfo.format;
|
||||||
|
extent = imageCreateInfo.extent;
|
||||||
|
|
||||||
|
const vk::MemoryRequirements memRequirements = device->device.getImageMemoryRequirements(image);
|
||||||
|
size = allocSize = memRequirements.size;
|
||||||
|
const vk::MemoryAllocateInfo memAllocInfo = { allocSize, device->GetMemoryType(memRequirements.memoryTypeBits, memoryPropertyFlags) };
|
||||||
|
memory = device->device.allocateMemory(memAllocInfo);
|
||||||
|
device->device.bindImageMemory(image, memory, 0);
|
||||||
|
|
||||||
|
imageViewCreateInfo.image = image;
|
||||||
|
view = device->device.createImageView(imageViewCreateInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLayout(vk::CommandBuffer& cmdBuffer, vk::ImageSubresourceRange subResourceRange, vk::ImageLayout newLayout, vk::ImageLayout oldLayout = vk::ImageLayout::eUndefined) const
|
||||||
|
{
|
||||||
|
const vk::ImageMemoryBarrier imgMemBarrier(VulkanUtils::GetAccessFlagsForLayout(oldLayout), VulkanUtils::GetAccessFlagsForLayout(newLayout), oldLayout,
|
||||||
|
newLayout, 0, 0, image, subResourceRange);
|
||||||
|
cmdBuffer.pipelineBarrier(VulkanUtils::GetPipelineStageForLayout(oldLayout), VulkanUtils::GetPipelineStageForLayout(newLayout),
|
||||||
|
{}, nullptr, nullptr, imgMemBarrier);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLayout(vk::CommandBuffer& cmdBuffer, vk::ImageAspectFlags aspectMask, vk::ImageLayout newLayout, vk::ImageLayout oldLayout = vk::ImageLayout::eUndefined) const
|
||||||
|
{
|
||||||
|
SetLayout(cmdBuffer, vk::ImageSubresourceRange(aspectMask, 0, 1, 0, 1), newLayout, oldLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
if(sampler)
|
||||||
|
{
|
||||||
|
device.destroySampler(sampler);
|
||||||
|
sampler = vk::Sampler();
|
||||||
|
}
|
||||||
|
if(view)
|
||||||
|
{
|
||||||
|
device.destroyImageView(view);
|
||||||
|
view = vk::ImageView();
|
||||||
|
}
|
||||||
|
if(image)
|
||||||
|
{
|
||||||
|
device.destroyImage(image);
|
||||||
|
image = vk::Image();
|
||||||
|
}
|
||||||
|
Buffer::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const { return image.operator bool(); }
|
||||||
|
|
||||||
|
|
||||||
|
vk::Image GetImage() override
|
||||||
|
{
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageView GetView() override
|
||||||
|
{
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
42
openVulkanoCpp/Vulkan/Pipeline.hpp
Normal file
42
openVulkanoCpp/Vulkan/Pipeline.hpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../Base/ICloseable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct Pipeline : virtual ICloseable
|
||||||
|
{
|
||||||
|
vk::Device device;
|
||||||
|
vk::DescriptorSetLayout descriptorSetLayout;
|
||||||
|
vk::PipelineLayout pipelineLayout;
|
||||||
|
vk::DescriptorPool descriptorPool;
|
||||||
|
|
||||||
|
void Init(vk::Device& device)
|
||||||
|
{
|
||||||
|
this->device = device;
|
||||||
|
|
||||||
|
CreatePipelineLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
device.destroyPipelineLayout(pipelineLayout);
|
||||||
|
device.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreatePipelineLayout()
|
||||||
|
{
|
||||||
|
vk::PushConstantRange camPushConstantDesc = { vk::ShaderStageFlagBits::eVertex, 0, 64 };
|
||||||
|
vk::DescriptorSetLayoutBinding nodeLayoutBinding = { 0, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex };
|
||||||
|
std::array<vk::DescriptorSetLayoutBinding, 1> layoutBindings = { nodeLayoutBinding };
|
||||||
|
vk::DescriptorSetLayoutCreateInfo dslci = { {}, layoutBindings.size(), layoutBindings.data() };
|
||||||
|
descriptorSetLayout = device.createDescriptorSetLayout(dslci);
|
||||||
|
vk::PipelineLayoutCreateInfo plci = { {}, 1, &descriptorSetLayout, 1, &camPushConstantDesc };
|
||||||
|
pipelineLayout = this->device.createPipelineLayout(plci);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
146
openVulkanoCpp/Vulkan/RenderPass.hpp
Normal file
146
openVulkanoCpp/Vulkan/RenderPass.hpp
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "Device.hpp"
|
||||||
|
#include "FrameBuffer.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class RenderPass : virtual public ICloseable
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
vk::Device device;
|
||||||
|
vk::RenderPassBeginInfo beginInfo;
|
||||||
|
vk::ClearColorValue clearColor;
|
||||||
|
vk::ClearDepthStencilValue clearDepth;
|
||||||
|
std::vector<vk::ClearValue> clearValues;
|
||||||
|
FrameBuffer* frameBuffer = nullptr;
|
||||||
|
bool useClearColor = false, useClearDepth = true;
|
||||||
|
|
||||||
|
public:
|
||||||
|
vk::RenderPass renderPass;
|
||||||
|
|
||||||
|
RenderPass() = default;
|
||||||
|
|
||||||
|
~RenderPass()
|
||||||
|
{
|
||||||
|
if (frameBuffer) RenderPass::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Init(Device* device, FrameBuffer* frameBuffer)
|
||||||
|
{
|
||||||
|
this->device = device->device;
|
||||||
|
this->frameBuffer = frameBuffer;
|
||||||
|
CreateRenderPass();
|
||||||
|
frameBuffer->InitRenderPass(this);
|
||||||
|
SetClearColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
device.destroy(renderPass);
|
||||||
|
frameBuffer = nullptr;
|
||||||
|
clearValues.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClearColor(vk::ClearColorValue clearColor = vk::ClearColorValue(std::array<float, 4>{ 0.39f, 0.58f, 0.93f, 1.0f }))
|
||||||
|
{
|
||||||
|
this->clearColor = clearColor;
|
||||||
|
useClearColor = true;
|
||||||
|
UpdateBeginInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableClearColor()
|
||||||
|
{
|
||||||
|
useClearColor = false;
|
||||||
|
UpdateBeginInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetClearDepth(vk::ClearDepthStencilValue clearDepth = vk::ClearDepthStencilValue(1, 0))
|
||||||
|
{
|
||||||
|
this->clearDepth = clearDepth;
|
||||||
|
useClearDepth = true;
|
||||||
|
UpdateBeginInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DisableClearDepth()
|
||||||
|
{
|
||||||
|
useClearDepth = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void UpdateBeginInfo()
|
||||||
|
{ //TODO allow to control the render rect size
|
||||||
|
clearValues.clear();
|
||||||
|
if (useClearColor) clearValues.emplace_back(clearColor);
|
||||||
|
if(frameBuffer->UseDepthBuffer() && useClearDepth) clearValues.emplace_back(clearDepth);
|
||||||
|
|
||||||
|
beginInfo = vk::RenderPassBeginInfo(renderPass, vk::Framebuffer(),
|
||||||
|
vk::Rect2D(vk::Offset2D(), frameBuffer->GetSize2D()),
|
||||||
|
clearValues.size(), clearValues.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Begin(vk::CommandBuffer& commandBuffer)
|
||||||
|
{
|
||||||
|
beginInfo.framebuffer = frameBuffer->GetCurrentFrameBuffer();
|
||||||
|
commandBuffer.beginRenderPass(beginInfo, vk::SubpassContents::eSecondaryCommandBuffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void End(vk::CommandBuffer& commandBuffer)
|
||||||
|
{
|
||||||
|
commandBuffer.endRenderPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameBuffer* GetFrameBuffer() const
|
||||||
|
{
|
||||||
|
return frameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void CreateRenderPass()
|
||||||
|
{
|
||||||
|
std::vector<vk::AttachmentDescription> attachments;
|
||||||
|
attachments.reserve(frameBuffer->UseDepthBuffer() ? 2 : 1);
|
||||||
|
|
||||||
|
// Color attachment
|
||||||
|
attachments.emplace_back(vk::AttachmentDescriptionFlags(), frameBuffer->GetColorFormat(), vk::SampleCountFlagBits::e1,
|
||||||
|
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eStore, vk::AttachmentLoadOp::eLoad,
|
||||||
|
vk::AttachmentStoreOp::eStore, vk::ImageLayout::eUndefined, vk::ImageLayout::ePresentSrcKHR);
|
||||||
|
|
||||||
|
vk::AttachmentReference* depthReference = nullptr;;
|
||||||
|
if (frameBuffer->UseDepthBuffer())
|
||||||
|
{ // Depth attachment
|
||||||
|
attachments.emplace_back(vk::AttachmentDescriptionFlags(), frameBuffer->GetDepthFormat(), vk::SampleCountFlagBits::e1,
|
||||||
|
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare, vk::AttachmentLoadOp::eClear,
|
||||||
|
vk::AttachmentStoreOp::eDontCare, vk::ImageLayout::eUndefined, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||||
|
depthReference = new vk::AttachmentReference(1, vk::ImageLayout::eDepthStencilAttachmentOptimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<vk::AttachmentReference> colorAttachmentReferences;
|
||||||
|
colorAttachmentReferences.emplace_back(0, vk::ImageLayout::eColorAttachmentOptimal);
|
||||||
|
|
||||||
|
std::vector<vk::SubpassDescription> subPasses;
|
||||||
|
subPasses.emplace_back(vk::SubpassDescriptionFlags(), vk::PipelineBindPoint::eGraphics, 0, nullptr,
|
||||||
|
colorAttachmentReferences.size(), colorAttachmentReferences.data(), nullptr, depthReference, 0, nullptr);
|
||||||
|
std::vector<vk::SubpassDependency> subPassDependencies;
|
||||||
|
subPassDependencies.emplace_back(0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eColorAttachmentOutput,
|
||||||
|
vk::PipelineStageFlagBits::eBottomOfPipe, vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
|
||||||
|
vk::AccessFlagBits::eMemoryRead, vk::DependencyFlagBits::eByRegion);
|
||||||
|
subPassDependencies.emplace_back(VK_SUBPASS_EXTERNAL, 0, vk::PipelineStageFlagBits::eBottomOfPipe,
|
||||||
|
vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::AccessFlagBits::eMemoryRead,
|
||||||
|
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
|
||||||
|
vk::DependencyFlagBits::eByRegion);
|
||||||
|
|
||||||
|
const vk::RenderPassCreateInfo createInfo(vk::RenderPassCreateFlags(), attachments.size(), attachments.data(),
|
||||||
|
subPasses.size(), subPasses.data(), subPassDependencies.size(), subPassDependencies.data());
|
||||||
|
CreateRenderPass(createInfo);
|
||||||
|
delete depthReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateRenderPass(const vk::RenderPassCreateInfo& renderPassCreateInfo)
|
||||||
|
{
|
||||||
|
renderPass = device.createRenderPass(renderPassCreateInfo);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
220
openVulkanoCpp/Vulkan/Renderer.hpp
Normal file
220
openVulkanoCpp/Vulkan/Renderer.hpp
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include "../Base/Render/IRenderer.hpp"
|
||||||
|
#include "../Base/UI/IWindow.hpp"
|
||||||
|
#include "../Base/Logger.hpp"
|
||||||
|
#include "Context.hpp"
|
||||||
|
#include "Resources/ResourceManager.hpp"
|
||||||
|
#include "../Data/ReadOnlyAtomicArrayQueue.hpp"
|
||||||
|
#include "CommandHelper.hpp"
|
||||||
|
#include "../Base/EngineConfiguration.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct WaitSemaphores
|
||||||
|
{
|
||||||
|
std::vector<vk::Semaphore> renderReady, renderComplete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Renderer : public IRenderer
|
||||||
|
{
|
||||||
|
Context context;
|
||||||
|
std::shared_ptr<spdlog::logger> logger;
|
||||||
|
std::vector<WaitSemaphores> waitSemaphores;
|
||||||
|
Scene::Scene* scene = nullptr;
|
||||||
|
std::ofstream perfFile;
|
||||||
|
ResourceManager resourceManager;
|
||||||
|
uint32_t currentImageId = -1;
|
||||||
|
std::vector<std::thread> threadPool;
|
||||||
|
std::vector<std::vector<CommandHelper>> commands;
|
||||||
|
std::vector<std::vector<vk::CommandBuffer>> submitBuffers;
|
||||||
|
VulkanShader* shader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer() = default;
|
||||||
|
virtual ~Renderer() = default;
|
||||||
|
|
||||||
|
void Init(IGraphicsAppManager* graphicsAppManager, IWindow* window) override
|
||||||
|
{
|
||||||
|
logger = Logger::RENDER;
|
||||||
|
logger->info("Initializing Vulkan renderer ...");
|
||||||
|
IVulkanWindow* vulkanWindow = window->GetVulkanWindow();
|
||||||
|
if (!vulkanWindow)
|
||||||
|
{
|
||||||
|
logger->error("The provided window is not compatible with Vulkan.");
|
||||||
|
throw std::runtime_error("The provided window is not compatible with Vulkan.");
|
||||||
|
}
|
||||||
|
context.Init(graphicsAppManager, vulkanWindow);
|
||||||
|
for (int i = 0; i < context.swapChain.GetImageCount(); i++)
|
||||||
|
{
|
||||||
|
waitSemaphores.emplace_back();
|
||||||
|
waitSemaphores[i].renderComplete.push_back(context.device->device.createSemaphore({}));
|
||||||
|
waitSemaphores[i].renderReady.resize(2);
|
||||||
|
}
|
||||||
|
resourceManager.Init(&context, context.swapChain.GetImageCount());
|
||||||
|
threadPool.resize(EngineConfiguration::GetEngineConfiguration()->GetNumThreads() - 1);
|
||||||
|
|
||||||
|
//Setup cmd pools and buffers
|
||||||
|
commands.resize(threadPool.size() + 2); // One extra cmd object for the primary buffer and one for the main thread
|
||||||
|
for(uint32_t i = 0; i < commands.size(); i++)
|
||||||
|
{
|
||||||
|
commands[i] = std::vector<CommandHelper>(context.swapChain.GetImageCount());
|
||||||
|
for(size_t j = 0; j < commands[i].size(); j++)
|
||||||
|
{
|
||||||
|
commands[i][j].Init(context.device->device, context.device->queueIndices.GetGraphics(),
|
||||||
|
(i == commands.size() - 1) ? vk::CommandBufferLevel::ePrimary : vk::CommandBufferLevel::eSecondary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
submitBuffers.resize(context.swapChain.GetImageCount());
|
||||||
|
for(uint32_t i = 0; i < submitBuffers.size(); i++)
|
||||||
|
{
|
||||||
|
submitBuffers[i].resize(commands.size() - 1);
|
||||||
|
for (size_t j = 0; j < submitBuffers[i].size(); j++)
|
||||||
|
{
|
||||||
|
submitBuffers[i][j] = commands[j][i].cmdBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shader = resourceManager.CreateShader(scene->shader);
|
||||||
|
|
||||||
|
perfFile.open("perf.csv");
|
||||||
|
perfFile << "sep=,\ntotal,fps\n";
|
||||||
|
|
||||||
|
logger->info("Vulkan renderer initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tick() override
|
||||||
|
{
|
||||||
|
currentImageId = context.swapChain.AcquireNextImage();
|
||||||
|
auto tickStart= std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
Render();
|
||||||
|
|
||||||
|
// Perf logging
|
||||||
|
auto tickDone = std::chrono::high_resolution_clock::now();
|
||||||
|
auto time = std::chrono::duration_cast<std::chrono::microseconds>(tickDone - tickStart).count();
|
||||||
|
perfFile << time << ',' << 1000000000.0 / time << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
perfFile.close();
|
||||||
|
//context.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetMainRenderDeviceName() override
|
||||||
|
{
|
||||||
|
return (context.device) ? context.device->GetDeviceName() : "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resize(const uint32_t newWidth, const uint32_t newHeight) override
|
||||||
|
{
|
||||||
|
context.Resize(newWidth, newHeight);
|
||||||
|
resourceManager.Resize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetScene(Scene::Scene* scene) override
|
||||||
|
{
|
||||||
|
this->scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene::Scene* GetScene() override
|
||||||
|
{
|
||||||
|
return scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandHelper* GetCommandData(uint32_t poolId)
|
||||||
|
{
|
||||||
|
return &commands[poolId][currentImageId];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RunThread(Renderer* renderer, Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, uint32_t id)
|
||||||
|
{
|
||||||
|
renderer->RecordSecondaryBuffer(jobQueue, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartThreads(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue)
|
||||||
|
{
|
||||||
|
for(uint32_t i = 0; i < threadPool.size(); i++)
|
||||||
|
{
|
||||||
|
threadPool[i] = std::thread(RunThread, this, jobQueue, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordPrimaryBuffer()
|
||||||
|
{
|
||||||
|
CommandHelper* cmdHelper = GetCommandData(commands.size() - 1);
|
||||||
|
cmdHelper->Reset();
|
||||||
|
cmdHelper->cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
|
||||||
|
context.swapChainRenderPass.Begin(cmdHelper->cmdBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Submit()
|
||||||
|
{
|
||||||
|
for (auto& thread : threadPool) { thread.join(); } // Wait till everything is recorded
|
||||||
|
CommandHelper* cmdHelper = GetCommandData(commands.size() - 1);
|
||||||
|
cmdHelper->cmdBuffer.executeCommands(submitBuffers[currentImageId].size(), submitBuffers[currentImageId].data());
|
||||||
|
context.swapChainRenderPass.End(cmdHelper->cmdBuffer);
|
||||||
|
cmdHelper->cmdBuffer.end();
|
||||||
|
std::array<vk::PipelineStageFlags, 2> stateFlags = { vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput), vk::PipelineStageFlags(vk::PipelineStageFlagBits::eColorAttachmentOutput) };
|
||||||
|
waitSemaphores[currentImageId].renderReady[0] = resourceManager.EndFrame();
|
||||||
|
waitSemaphores[currentImageId].renderReady[1] = context.swapChain.imageAvailableSemaphore;
|
||||||
|
vk::SubmitInfo si = vk::SubmitInfo(
|
||||||
|
waitSemaphores[currentImageId].renderReady.size(), waitSemaphores[currentImageId].renderReady.data(), stateFlags.data(),
|
||||||
|
1, &cmdHelper->cmdBuffer,
|
||||||
|
waitSemaphores[currentImageId].renderComplete.size(), waitSemaphores[currentImageId].renderComplete.data());
|
||||||
|
context.device->graphicsQueue.submit(1, &si, context.swapChain.GetCurrentSubmitFence());
|
||||||
|
context.swapChain.Present(context.device->graphicsQueue, waitSemaphores[currentImageId].renderComplete);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Render()
|
||||||
|
{
|
||||||
|
resourceManager.StartFrame(currentImageId);
|
||||||
|
Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*> jobQueue(scene->shapeList);
|
||||||
|
StartThreads(&jobQueue);
|
||||||
|
RecordPrimaryBuffer();
|
||||||
|
RecordSecondaryBuffer(&jobQueue, threadPool.size());
|
||||||
|
Submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordSecondaryBuffer(Data::ReadOnlyAtomicArrayQueue<Scene::Drawable*>* jobQueue, uint32_t poolId)
|
||||||
|
{
|
||||||
|
Scene::Geometry* lastGeo = nullptr;
|
||||||
|
Scene::Node* lastNode = nullptr;
|
||||||
|
CommandHelper* cmdHelper = GetCommandData(poolId);
|
||||||
|
cmdHelper->Reset();
|
||||||
|
vk::CommandBufferInheritanceInfo inheritance = { context.swapChainRenderPass.renderPass, 0, context.swapChainRenderPass.GetFrameBuffer()->GetCurrentFrameBuffer() };
|
||||||
|
cmdHelper->cmdBuffer.begin(vk::CommandBufferBeginInfo{ vk::CommandBufferUsageFlagBits::eOneTimeSubmit | vk::CommandBufferUsageFlagBits::eRenderPassContinue, &inheritance });
|
||||||
|
shader->Record(cmdHelper->cmdBuffer, currentImageId);
|
||||||
|
cmdHelper->cmdBuffer.pushConstants(context.pipeline.pipelineLayout, vk::ShaderStageFlagBits::eVertex, 0, 64, scene->GetCamera()->GetViewProjectionMatrixPointer());
|
||||||
|
Scene::Drawable** drawablePointer;
|
||||||
|
while((drawablePointer = jobQueue->Pop()) != nullptr)
|
||||||
|
{
|
||||||
|
Scene::Drawable* drawable = *drawablePointer;
|
||||||
|
Scene::Geometry* mesh = drawable->mesh;
|
||||||
|
if (mesh != lastGeo)
|
||||||
|
{
|
||||||
|
if (!mesh->renderGeo) resourceManager.PrepareGeometry(mesh);
|
||||||
|
dynamic_cast<VulkanGeometry*>(mesh->renderGeo)->Record(cmdHelper->cmdBuffer, currentImageId);
|
||||||
|
lastGeo = mesh;
|
||||||
|
}
|
||||||
|
for(Scene::Node* node : drawable->nodes)
|
||||||
|
{
|
||||||
|
if (node != lastNode)
|
||||||
|
{
|
||||||
|
if (!node->renderNode) resourceManager.PrepareNode(node);
|
||||||
|
dynamic_cast<VulkanNode*>(node->renderNode)->Record(cmdHelper->cmdBuffer, currentImageId);
|
||||||
|
lastNode = node;
|
||||||
|
}
|
||||||
|
cmdHelper->cmdBuffer.drawIndexed(mesh->GetIndexCount(), 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmdHelper->cmdBuffer.end();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
16
openVulkanoCpp/Vulkan/Resources/IShaderOwner.hpp
Normal file
16
openVulkanoCpp/Vulkan/Resources/IShaderOwner.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../Scene/VulkanShader.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct VulkanShader;
|
||||||
|
|
||||||
|
class IShaderOwner
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void RemoveShader(VulkanShader* shader) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
93
openVulkanoCpp/Vulkan/Resources/ManagedResource.hpp
Normal file
93
openVulkanoCpp/Vulkan/Resources/ManagedResource.hpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct MemoryAllocation
|
||||||
|
{
|
||||||
|
vk::DeviceMemory memory;
|
||||||
|
size_t used, size;
|
||||||
|
uint32_t type;
|
||||||
|
|
||||||
|
MemoryAllocation(size_t size, uint32_t type)
|
||||||
|
{
|
||||||
|
memory = nullptr;
|
||||||
|
this->size = size;
|
||||||
|
used = 0;
|
||||||
|
this->type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t FreeSpace() const
|
||||||
|
{
|
||||||
|
return size - used;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ManagedBuffer
|
||||||
|
{
|
||||||
|
MemoryAllocation* allocation;
|
||||||
|
vk::DeviceSize offset, size;
|
||||||
|
vk::Buffer buffer;
|
||||||
|
vk::BufferUsageFlags usage;
|
||||||
|
vk::MemoryPropertyFlags properties;
|
||||||
|
vk::Device device;
|
||||||
|
void* mapped = nullptr;
|
||||||
|
|
||||||
|
bool IsLast()
|
||||||
|
{
|
||||||
|
return (offset + size == allocation->used);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Maps the buffer into the memory of the host.
|
||||||
|
* \tparam T The type of the buffers data.
|
||||||
|
* \param offset The offset from where to map the buffer.
|
||||||
|
* \param size The size to be mapped. VK_WHOLE_SIZE to map the whole buffer.
|
||||||
|
* \return The pointer to the mapped buffer.
|
||||||
|
*/
|
||||||
|
template <typename T = void>
|
||||||
|
T* Map(size_t offset = 0, vk::DeviceSize size = VK_WHOLE_SIZE)
|
||||||
|
{
|
||||||
|
if (size == VK_WHOLE_SIZE) size = this->size;
|
||||||
|
mapped = device.mapMemory(allocation->memory, this->offset + offset, size, vk::MemoryMapFlags());
|
||||||
|
return static_cast<T*>(mapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Un-maps the buffer from the host.
|
||||||
|
*/
|
||||||
|
void UnMap()
|
||||||
|
{
|
||||||
|
device.unmapMemory(allocation->memory);
|
||||||
|
mapped = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Copy(void* data) const
|
||||||
|
{
|
||||||
|
if(mapped)
|
||||||
|
{
|
||||||
|
memcpy(mapped, data, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void* dataMapped = device.mapMemory(allocation->memory, offset, size);
|
||||||
|
memcpy(dataMapped, data, size);
|
||||||
|
device.unmapMemory(allocation->memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Copy(void* data, uint32_t size, uint32_t offset) const
|
||||||
|
{
|
||||||
|
if(mapped) memcpy(static_cast<char*>(mapped) + offset, data, size);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
void* dataMapped = device.mapMemory(allocation->memory, this->offset + offset, size);
|
||||||
|
memcpy(dataMapped, data, size);
|
||||||
|
device.unmapMemory(allocation->memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
258
openVulkanoCpp/Vulkan/Resources/ResourceManager.hpp
Normal file
258
openVulkanoCpp/Vulkan/Resources/ResourceManager.hpp
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vulkan/vulkan.hpp"
|
||||||
|
#include "../Device.hpp"
|
||||||
|
#include "../../Base/ICloseable.hpp"
|
||||||
|
#include "../Scene/VulkanShader.hpp"
|
||||||
|
#include "IShaderOwner.hpp"
|
||||||
|
#include "../Scene/VulkanGeometry.hpp"
|
||||||
|
#include "ManagedResource.hpp"
|
||||||
|
#include "../Scene/VulkanNode.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class ResourceManager : virtual public ICloseable, virtual public IShaderOwner
|
||||||
|
{
|
||||||
|
Context* context;
|
||||||
|
vk::Device device = nullptr;
|
||||||
|
vk::Queue transferQueue = nullptr;
|
||||||
|
vk::CommandPool* cmdPools = nullptr;
|
||||||
|
vk::CommandBuffer* cmdBuffers = nullptr;
|
||||||
|
vk::Semaphore* semaphores = nullptr;
|
||||||
|
std::vector<MemoryAllocation*> allocations;
|
||||||
|
std::vector<VulkanShader*> shaders;
|
||||||
|
MemoryAllocation* lastAllocation = nullptr;
|
||||||
|
std::mutex mutex;
|
||||||
|
vk::DeviceSize uniformBufferAlignment;
|
||||||
|
std::vector<std::vector<ManagedBuffer*>> toFree;
|
||||||
|
std::vector<ManagedBuffer*> recycleBuffers;
|
||||||
|
|
||||||
|
int buffers = -1, currentBuffer = -1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ResourceManager() = default;
|
||||||
|
virtual ~ResourceManager() { if (device) ResourceManager::Close(); }
|
||||||
|
|
||||||
|
void Init(Context* context, int buffers = 2)
|
||||||
|
{
|
||||||
|
this->context = context;
|
||||||
|
this->device = context->device->device;
|
||||||
|
this->buffers = buffers;
|
||||||
|
|
||||||
|
uniformBufferAlignment = context->device->properties.limits.minUniformBufferOffsetAlignment;
|
||||||
|
|
||||||
|
cmdPools = new vk::CommandPool[buffers];
|
||||||
|
cmdBuffers = new vk::CommandBuffer[buffers];
|
||||||
|
semaphores = new vk::Semaphore[buffers];
|
||||||
|
for (int i = 0; i < buffers; i++)
|
||||||
|
{
|
||||||
|
cmdPools[i] = this->device.createCommandPool({ {}, context->device->queueIndices.transfer });
|
||||||
|
cmdBuffers[i] = this->device.allocateCommandBuffers({ cmdPools[i], vk::CommandBufferLevel::ePrimary, 1 })[0];
|
||||||
|
semaphores[i] = this->device.createSemaphore({});
|
||||||
|
}
|
||||||
|
toFree.resize(buffers);
|
||||||
|
|
||||||
|
transferQueue = this->device.getQueue(context->device->queueIndices.transfer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
transferQueue.waitIdle();
|
||||||
|
for (int i = 0; i < buffers; i++)
|
||||||
|
{
|
||||||
|
device.freeCommandBuffers(cmdPools[i], 1, &cmdBuffers[i]);
|
||||||
|
device.destroyCommandPool(cmdPools[0]);
|
||||||
|
}
|
||||||
|
for (auto shader : shaders)
|
||||||
|
{
|
||||||
|
shader->Close();
|
||||||
|
}
|
||||||
|
cmdBuffers = nullptr;
|
||||||
|
cmdPools = nullptr;
|
||||||
|
device = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StartFrame(uint64_t frameId)
|
||||||
|
{
|
||||||
|
currentBuffer = frameId;
|
||||||
|
FreeBuffers();
|
||||||
|
device.resetCommandPool(cmdPools[currentBuffer], {});
|
||||||
|
cmdBuffers[currentBuffer].begin({ vk::CommandBufferUsageFlagBits::eOneTimeSubmit });
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Semaphore EndFrame()
|
||||||
|
{
|
||||||
|
cmdBuffers[currentBuffer].end();
|
||||||
|
vk::SubmitInfo si = { 0, nullptr, nullptr, 1, &cmdBuffers[currentBuffer], 1, &semaphores[currentBuffer] };
|
||||||
|
transferQueue.submit(1, &si, vk::Fence());
|
||||||
|
return semaphores[currentBuffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resize()
|
||||||
|
{
|
||||||
|
for (auto shader : shaders)
|
||||||
|
{
|
||||||
|
Scene::Shader* s = shader->shader;
|
||||||
|
shader->Close();
|
||||||
|
shader->Init(context, s, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareGeometry(Scene::Geometry* geometry)
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
if(!geometry->renderGeo)
|
||||||
|
{
|
||||||
|
VulkanGeometry* vkGeometry = new VulkanGeometry();
|
||||||
|
ManagedBuffer* vertexBuffer = CreateDeviceOnlyBufferWithData(sizeof(Vertex) * geometry->GetVertexCount(), vk::BufferUsageFlagBits::eVertexBuffer, geometry->GetVertices());
|
||||||
|
ManagedBuffer* indexBuffer = CreateDeviceOnlyBufferWithData(Utils::EnumAsInt(geometry->indexType) * geometry->GetIndexCount(), vk::BufferUsageFlagBits::eIndexBuffer, geometry->GetIndices());
|
||||||
|
vkGeometry->Init(geometry, vertexBuffer->buffer, indexBuffer->buffer);
|
||||||
|
geometry->renderGeo = vkGeometry;
|
||||||
|
}
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareMaterial(Scene::Material* material)
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
if(!material->shader->renderShader)
|
||||||
|
{
|
||||||
|
material->shader->renderShader = CreateShader(material->shader);
|
||||||
|
}
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareNode(Scene::Node* node)
|
||||||
|
{
|
||||||
|
mutex.lock();
|
||||||
|
if (!node->renderNode)
|
||||||
|
{
|
||||||
|
UniformBuffer* uBuffer = new UniformBuffer();
|
||||||
|
ManagedBuffer* buffer;
|
||||||
|
VulkanNode* vkNode;
|
||||||
|
const vk::DeviceSize allocSize = aligned(sizeof(glm::mat4x4), uniformBufferAlignment);
|
||||||
|
if (node->GetUpdateFrequency() != Scene::UpdateFrequency::Never)
|
||||||
|
{
|
||||||
|
vkNode = new VulkanNodeDynamic();
|
||||||
|
uint32_t imgs = context->swapChain.GetImageCount();
|
||||||
|
buffer = CreateBuffer(imgs * allocSize, vk::BufferUsageFlagBits::eUniformBuffer, vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible);
|
||||||
|
buffer->Map();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vkNode = new VulkanNode();
|
||||||
|
buffer = CreateDeviceOnlyBufferWithData(sizeof(glm::mat4), vk::BufferUsageFlagBits::eUniformBuffer, &node->worldMat);
|
||||||
|
}
|
||||||
|
uBuffer->Init(buffer, allocSize, &context->pipeline.descriptorSetLayout, context->pipeline.pipelineLayout);
|
||||||
|
vkNode->Init(node, uBuffer);
|
||||||
|
node->renderNode = vkNode;
|
||||||
|
}
|
||||||
|
mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveShader(VulkanShader* shader) override
|
||||||
|
{
|
||||||
|
Utils::Remove(shaders, shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected: // Allocation management
|
||||||
|
static vk::DeviceSize aligned(vk::DeviceSize size, vk::DeviceSize byteAlignment)
|
||||||
|
{
|
||||||
|
return (size + byteAlignment - 1) & ~(byteAlignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeBuffer(ManagedBuffer* buffer)
|
||||||
|
{
|
||||||
|
toFree[currentBuffer].push_back(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DoFreeBuffer(ManagedBuffer* buffer)
|
||||||
|
{
|
||||||
|
if (buffer->IsLast())
|
||||||
|
{
|
||||||
|
device.destroyBuffer(buffer->buffer);
|
||||||
|
buffer->allocation->used -= buffer->size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
recycleBuffers.push_back(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeBuffers()
|
||||||
|
{
|
||||||
|
for (auto& i : toFree[currentBuffer])
|
||||||
|
{
|
||||||
|
DoFreeBuffer(i);
|
||||||
|
}
|
||||||
|
toFree[currentBuffer].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedBuffer* CreateDeviceOnlyBufferWithData(vk::DeviceSize size, vk::BufferUsageFlagBits usage, void* data)
|
||||||
|
{
|
||||||
|
ManagedBuffer* target = CreateBuffer(size, usage | vk::BufferUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal);
|
||||||
|
ManagedBuffer* uploadBuffer = CreateBuffer(size, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eHostVisible);
|
||||||
|
uploadBuffer->Copy(data, size, 0);
|
||||||
|
RecordCopy(uploadBuffer->buffer, target->buffer, size);
|
||||||
|
FreeBuffer(uploadBuffer);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordCopy(vk::Buffer src, vk::Buffer dest, vk::DeviceSize size) const
|
||||||
|
{
|
||||||
|
vk::BufferCopy copyRegion = { 0, 0, size };
|
||||||
|
cmdBuffers[currentBuffer].copyBuffer(src, dest, 1, ©Region);
|
||||||
|
}
|
||||||
|
|
||||||
|
ManagedBuffer* CreateBuffer(vk::DeviceSize size, const vk::BufferUsageFlags& usage, const vk::MemoryPropertyFlags& properties)
|
||||||
|
{
|
||||||
|
size = aligned(size, uniformBufferAlignment);
|
||||||
|
const vk::BufferCreateInfo bufferCreateInfo = { {}, size, usage, vk::SharingMode::eExclusive };
|
||||||
|
vk::Buffer buffer = device.createBuffer(bufferCreateInfo);
|
||||||
|
const vk::MemoryRequirements memoryRequirements = device.getBufferMemoryRequirements(buffer);
|
||||||
|
uint32_t memtype = context->device->GetMemoryType(memoryRequirements.memoryTypeBits, properties);
|
||||||
|
if (memoryRequirements.size != size) Logger::DATA->warn("Memory Requirement Size ({0}) != Size ({1})", memoryRequirements.size, size);
|
||||||
|
MemoryAllocation* allocation = GetFreeMemoryAllocation(memoryRequirements.size, memtype);
|
||||||
|
uint32_t offset = allocation->used;
|
||||||
|
device.bindBufferMemory(buffer, allocation->memory, offset);
|
||||||
|
allocation->used += memoryRequirements.size;
|
||||||
|
return new ManagedBuffer{ allocation, offset, size, buffer, usage, properties, device, nullptr };
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAllocation* CreateMemoryAllocation(size_t size, uint32_t type, bool addToCache = true)
|
||||||
|
{
|
||||||
|
MemoryAllocation* alloc = new MemoryAllocation(size, type);
|
||||||
|
const vk::MemoryAllocateInfo allocInfo = { size, type };
|
||||||
|
alloc->memory = device.allocateMemory(allocInfo);
|
||||||
|
if (addToCache) allocations.push_back(alloc);
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryAllocation* GetFreeMemoryAllocation(size_t size, uint32_t type, bool createIfAllFull = true)
|
||||||
|
{
|
||||||
|
MemoryAllocation* alloc = nullptr;
|
||||||
|
for (MemoryAllocation* allocation : allocations)
|
||||||
|
{
|
||||||
|
if (allocation->type == type && allocation->FreeSpace() >= size)
|
||||||
|
{
|
||||||
|
alloc = allocation;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!alloc && createIfAllFull) alloc = CreateMemoryAllocation(256 * 1024 * 1024, type, true);
|
||||||
|
if(alloc) lastAllocation = alloc;
|
||||||
|
return alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
VulkanShader* CreateShader(Scene::Shader* shader)
|
||||||
|
{
|
||||||
|
VulkanShader* vkShader = new VulkanShader();
|
||||||
|
vkShader->Init(context, shader, this);
|
||||||
|
shaders.push_back(vkShader);
|
||||||
|
return vkShader;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
54
openVulkanoCpp/Vulkan/Resources/UniformBuffer.hpp
Normal file
54
openVulkanoCpp/Vulkan/Resources/UniformBuffer.hpp
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../Base/ICloseable.hpp"
|
||||||
|
#include "../Scene/IRecordable.hpp"
|
||||||
|
#include "ManagedResource.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct UniformBuffer : virtual ICloseable, virtual IRecordable
|
||||||
|
{
|
||||||
|
ManagedBuffer* buffer;
|
||||||
|
vk::DescriptorPool descPool;
|
||||||
|
vk::DescriptorSet descSet;
|
||||||
|
vk::PipelineLayout layout;
|
||||||
|
uint32_t allocSizeFrame;
|
||||||
|
|
||||||
|
void Init(ManagedBuffer* buffer, uint32_t allocSizeFrame, vk::DescriptorSetLayout* descriptorSetLayout, vk::PipelineLayout layout)
|
||||||
|
{
|
||||||
|
this->buffer = buffer;
|
||||||
|
this->layout = layout;
|
||||||
|
this->allocSizeFrame = allocSizeFrame;
|
||||||
|
vk::DescriptorPoolSize poolSize = { vk::DescriptorType::eUniformBufferDynamic, 1 };
|
||||||
|
const vk::DescriptorPoolCreateInfo poolCreateInfo = { {}, 1, 1, &poolSize };
|
||||||
|
descPool = buffer->device.createDescriptorPool(poolCreateInfo);
|
||||||
|
const vk::DescriptorSetAllocateInfo descSetAllocInfo = { descPool, 1, descriptorSetLayout };
|
||||||
|
descSet = buffer->device.allocateDescriptorSets(descSetAllocInfo)[0];
|
||||||
|
vk::DescriptorBufferInfo bufferInfo = { buffer->buffer, 0, allocSizeFrame };
|
||||||
|
vk::WriteDescriptorSet writeDescriptorSet = { descSet };
|
||||||
|
writeDescriptorSet.descriptorCount = 1;
|
||||||
|
writeDescriptorSet.descriptorType = vk::DescriptorType::eUniformBufferDynamic;
|
||||||
|
writeDescriptorSet.pBufferInfo = &bufferInfo;
|
||||||
|
buffer->device.updateDescriptorSets(1, &writeDescriptorSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
|
||||||
|
{
|
||||||
|
uint32_t frameOffset = allocSizeFrame * bufferId;
|
||||||
|
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, layout, 0, 1,
|
||||||
|
&descSet, 1, &frameOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(void* data, uint32_t size, uint32_t bufferId) const
|
||||||
|
{
|
||||||
|
buffer->Copy(data, size, allocSizeFrame * bufferId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
buffer->device.destroyDescriptorPool(descPool);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
15
openVulkanoCpp/Vulkan/Scene/IRecordable.hpp
Normal file
15
openVulkanoCpp/Vulkan/Scene/IRecordable.hpp
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "vulkan/vulkan.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class IRecordable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IRecordable() = default;
|
||||||
|
virtual void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
41
openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp
Normal file
41
openVulkanoCpp/Vulkan/Scene/VulkanGeometry.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "IRecordable.hpp"
|
||||||
|
#include "../../Scene/Scene.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class VulkanGeometry : virtual public IRecordable, virtual public ICloseable
|
||||||
|
{
|
||||||
|
Scene::Geometry* geometry = nullptr;
|
||||||
|
vk::Buffer vertexBuffer, indexBuffer;
|
||||||
|
vk::IndexType indexType;
|
||||||
|
vk::DeviceSize* offsets = new vk::DeviceSize();
|
||||||
|
|
||||||
|
public:
|
||||||
|
VulkanGeometry() = default;
|
||||||
|
virtual ~VulkanGeometry() { if (vertexBuffer) VulkanGeometry::Close(); };
|
||||||
|
|
||||||
|
void Init(Scene::Geometry* geo, vk::Buffer vertexBuffer, vk::Buffer indexBuffer)
|
||||||
|
{
|
||||||
|
this->geometry = geo;
|
||||||
|
offsets[0] = 0;
|
||||||
|
indexType = (geo->indexType == Scene::VertexIndexType::UINT16) ? vk::IndexType::eUint16 : vk::IndexType::eUint32;
|
||||||
|
this->vertexBuffer = vertexBuffer;
|
||||||
|
this->indexBuffer = indexBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
|
||||||
|
{
|
||||||
|
cmdBuffer.bindVertexBuffers(0, 1, &vertexBuffer, offsets);
|
||||||
|
cmdBuffer.bindIndexBuffer(indexBuffer, 0, indexType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
53
openVulkanoCpp/Vulkan/Scene/VulkanNode.hpp
Normal file
53
openVulkanoCpp/Vulkan/Scene/VulkanNode.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "../../Base/ICloseable.hpp"
|
||||||
|
#include "IRecordable.hpp"
|
||||||
|
#include "../../Scene/Camera.hpp"
|
||||||
|
#include "../Resources/UniformBuffer.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct VulkanNode : virtual IRecordable, virtual ICloseable
|
||||||
|
{
|
||||||
|
Scene::Node* node = nullptr;
|
||||||
|
UniformBuffer* buffer = nullptr;
|
||||||
|
|
||||||
|
virtual void Init(Scene::Node* node, UniformBuffer* uniformBuffer)
|
||||||
|
{
|
||||||
|
this->node = node;
|
||||||
|
this->buffer = uniformBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
|
||||||
|
{
|
||||||
|
buffer->Record(cmdBuffer, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VulkanNodeDynamic : VulkanNode
|
||||||
|
{
|
||||||
|
uint32_t lastUpdate = -1;
|
||||||
|
|
||||||
|
void Init(Scene::Node* node, UniformBuffer* uniformBuffer) override
|
||||||
|
{
|
||||||
|
VulkanNode::Init(node, uniformBuffer);
|
||||||
|
lastUpdate = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
|
||||||
|
{
|
||||||
|
if(bufferId != lastUpdate)
|
||||||
|
{
|
||||||
|
lastUpdate = bufferId;
|
||||||
|
buffer->Update(&node->worldMat, sizeof(glm::mat4x4), bufferId);
|
||||||
|
}
|
||||||
|
buffer->Record(cmdBuffer, bufferId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override{}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
97
openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp
Normal file
97
openVulkanoCpp/Vulkan/Scene/VulkanShader.hpp
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "../Device.hpp"
|
||||||
|
#include "../../Scene/Shader.hpp"
|
||||||
|
#include "../../Scene/Vertex.hpp"
|
||||||
|
#include "../../Base/ICloseable.hpp"
|
||||||
|
#include "../Resources/IShaderOwner.hpp"
|
||||||
|
#include "IRecordable.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
vk::PrimitiveTopology ToVkTopology(Scene::Topology topology)
|
||||||
|
{
|
||||||
|
switch (topology) {
|
||||||
|
case Scene::Topology::PointList: return vk::PrimitiveTopology::ePointList;
|
||||||
|
case Scene::Topology::LineList: return vk::PrimitiveTopology::eLineList;
|
||||||
|
case Scene::Topology::LineStripe: return vk::PrimitiveTopology::eLineStrip;
|
||||||
|
case Scene::Topology::TriangleList: return vk::PrimitiveTopology::eTriangleList;
|
||||||
|
case Scene::Topology::TriangleStripe: return vk::PrimitiveTopology::eTriangleStrip;
|
||||||
|
default: throw std::runtime_error("Unknown topology!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VulkanShader : virtual public ICloseable, virtual public IRecordable
|
||||||
|
{
|
||||||
|
Scene::Shader* shader = nullptr;
|
||||||
|
vk::Device device;
|
||||||
|
vk::ShaderModule shaderModuleVertex, shaderModuleFragment;
|
||||||
|
vk::Pipeline pipeline;
|
||||||
|
IShaderOwner* owner;
|
||||||
|
|
||||||
|
VulkanShader() = default;
|
||||||
|
virtual ~VulkanShader() { if (shader) VulkanShader::Close(); }
|
||||||
|
|
||||||
|
void Init(Context* context, Scene::Shader* shader, IShaderOwner* owner)
|
||||||
|
{
|
||||||
|
this->device = context->device->device;
|
||||||
|
this->shader = shader;
|
||||||
|
this->owner = owner;
|
||||||
|
shaderModuleVertex = context->device->CreateShaderModule(shader->vertexShaderName + ".vert.spv");
|
||||||
|
shaderModuleFragment = context->device->CreateShaderModule(shader->fragmentShaderName + ".frag.spv");
|
||||||
|
std::vector<vk::PipelineShaderStageCreateInfo> shaderStageCreateInfos(2);
|
||||||
|
shaderStageCreateInfos[0] = { {}, vk::ShaderStageFlagBits::eVertex, shaderModuleVertex, "main" };
|
||||||
|
shaderStageCreateInfos[1] = { {}, vk::ShaderStageFlagBits::eFragment, shaderModuleFragment, "main" };
|
||||||
|
|
||||||
|
auto vertexBindDesc = vk::VertexInputBindingDescription(0, sizeof(Vertex), vk::VertexInputRate::eVertex);
|
||||||
|
std::vector<vk::VertexInputAttributeDescription> attributeDescriptions;
|
||||||
|
attributeDescriptions.emplace_back(0, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, position));
|
||||||
|
attributeDescriptions.emplace_back(1, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, normal));
|
||||||
|
attributeDescriptions.emplace_back(2, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, tangent));
|
||||||
|
attributeDescriptions.emplace_back(3, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, biTangent));
|
||||||
|
attributeDescriptions.emplace_back(4, 0, vk::Format::eR32G32B32Sfloat, offsetof(Vertex, textureCoordinates));
|
||||||
|
attributeDescriptions.emplace_back(5, 0, vk::Format::eR32G32B32A32Sfloat, offsetof(Vertex, color));
|
||||||
|
|
||||||
|
auto viewport = context->swapChain.GetFullscreenViewport();
|
||||||
|
auto scissor = context->swapChain.GetFullscreenScissor();
|
||||||
|
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = { {}, 1, &viewport, 1, &scissor };
|
||||||
|
vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = { {}, 1, &vertexBindDesc,
|
||||||
|
static_cast<uint32_t>(attributeDescriptions.size()), attributeDescriptions.data() };
|
||||||
|
vk::PipelineInputAssemblyStateCreateInfo inputAssembly = { {}, ToVkTopology(shader->topology), 0 };
|
||||||
|
vk::PipelineRasterizationStateCreateInfo rasterizer = {};
|
||||||
|
rasterizer.cullMode = vk::CullModeFlagBits::eBack;
|
||||||
|
vk::PipelineMultisampleStateCreateInfo msaa = {};
|
||||||
|
vk::PipelineDepthStencilStateCreateInfo depth = { {}, 1, 1, vk::CompareOp::eGreater };
|
||||||
|
vk::PipelineColorBlendAttachmentState colorBlendAttachment = {};
|
||||||
|
colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eA | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eR;
|
||||||
|
vk::PipelineColorBlendStateCreateInfo colorInfo = {};
|
||||||
|
colorInfo.attachmentCount = 1;
|
||||||
|
colorInfo.pAttachments = &colorBlendAttachment;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = { {}, static_cast<uint32_t>(shaderStageCreateInfos.size()), shaderStageCreateInfos.data(), &pipelineVertexInputStateCreateInfo, &inputAssembly,
|
||||||
|
nullptr, &viewportStateCreateInfo, &rasterizer, &msaa, &depth, &colorInfo, nullptr, context->pipeline.pipelineLayout, context->swapChainRenderPass.renderPass };
|
||||||
|
pipeline = this->device.createGraphicsPipeline({}, pipelineCreateInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Record(vk::CommandBuffer& cmdBuffer, uint32_t bufferId) override
|
||||||
|
{
|
||||||
|
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
owner->RemoveShader(this);
|
||||||
|
shader = nullptr;
|
||||||
|
device.destroyPipeline(pipeline);
|
||||||
|
device.destroyShaderModule(shaderModuleVertex);
|
||||||
|
device.destroyShaderModule(shaderModuleFragment);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
261
openVulkanoCpp/Vulkan/SwapChain.hpp
Normal file
261
openVulkanoCpp/Vulkan/SwapChain.hpp
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include "Device.hpp"
|
||||||
|
#include "Image.hpp"
|
||||||
|
#include "FrameBuffer.hpp"
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
struct SwapChainImage : virtual public IImage
|
||||||
|
{
|
||||||
|
vk::Image image;
|
||||||
|
vk::ImageView view;
|
||||||
|
vk::Fence fence;
|
||||||
|
|
||||||
|
|
||||||
|
vk::Image GetImage() override
|
||||||
|
{
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::ImageView GetView() override
|
||||||
|
{
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SwapChain : public FrameBuffer
|
||||||
|
{
|
||||||
|
vk::SurfaceKHR surface;
|
||||||
|
std::vector<SwapChainImage> images;
|
||||||
|
Device* device = nullptr;
|
||||||
|
IVulkanWindow* window = nullptr;
|
||||||
|
vk::SurfaceFormatKHR surfaceFormat;
|
||||||
|
vk::PresentModeKHR presentMode;
|
||||||
|
vk::Viewport fullscreenViewport;
|
||||||
|
bool useVsync = false;
|
||||||
|
|
||||||
|
uint32_t preferredImageCount = 2; //TODO add option
|
||||||
|
vk::Extent2D size{0,0};
|
||||||
|
|
||||||
|
public:
|
||||||
|
vk::SwapchainKHR swapChain;
|
||||||
|
vk::Semaphore imageAvailableSemaphore;
|
||||||
|
|
||||||
|
SwapChain() = default;
|
||||||
|
~SwapChain() { if (device) SwapChain::Close(); }
|
||||||
|
|
||||||
|
void Init(Device* device, vk::SurfaceKHR surface, IVulkanWindow* window)
|
||||||
|
{
|
||||||
|
if (!device) throw std::runtime_error("The device must not be null");
|
||||||
|
if (!window) throw std::runtime_error("The window must not be null");
|
||||||
|
this->device = device;
|
||||||
|
this->surface = surface;
|
||||||
|
this->window = window;
|
||||||
|
|
||||||
|
imageAvailableSemaphore = device->device.createSemaphore({});
|
||||||
|
CreateSwapChain({window->GetWidth(), window->GetHeight() });
|
||||||
|
|
||||||
|
FrameBuffer::Init(device, vk::Extent3D(size, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override
|
||||||
|
{
|
||||||
|
DestroySwapChain();
|
||||||
|
device->device.destroySemaphore(imageAvailableSemaphore);
|
||||||
|
device = nullptr;
|
||||||
|
FrameBuffer::Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resize(const uint32_t newWidth, const uint32_t newHeight)
|
||||||
|
{
|
||||||
|
if(newWidth == 0 || newHeight == 0) return; // Swap chain size of 0 pixel is not allowed
|
||||||
|
|
||||||
|
CreateSwapChain({ newWidth, newHeight });
|
||||||
|
FrameBuffer::Resize(vk::Extent3D(size, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Extent2D GetSize() const
|
||||||
|
{
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Viewport GetFullscreenViewport() const
|
||||||
|
{
|
||||||
|
return fullscreenViewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Rect2D GetFullscreenScissor() const
|
||||||
|
{
|
||||||
|
return { {0,0}, GetSize() };
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t AcquireNextImage(const vk::Fence fence = vk::Fence())
|
||||||
|
{
|
||||||
|
const auto resultValue = device->device.acquireNextImageKHR(swapChain, UINT64_MAX, imageAvailableSemaphore, fence);
|
||||||
|
const vk::Result result = resultValue.result;
|
||||||
|
if (result != vk::Result::eSuccess && result != vk::Result::eSuboptimalKHR) throw std::error_code(result);
|
||||||
|
SetCurrentFrameId(resultValue.value);
|
||||||
|
|
||||||
|
vk::Fence submitFence = GetCurrentSubmitFence();
|
||||||
|
device->device.waitForFences(1, &submitFence, true, -1);
|
||||||
|
device->device.resetFences(1, &submitFence);
|
||||||
|
|
||||||
|
return currentFrameBufferId;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::Fence& GetCurrentSubmitFence()
|
||||||
|
{
|
||||||
|
return images[currentFrameBufferId].fence;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Present(vk::Queue& queue ,std::vector<vk::Semaphore>& semaphores) const
|
||||||
|
{
|
||||||
|
queue.presentKHR(vk::PresentInfoKHR(semaphores.size(), semaphores.data(),
|
||||||
|
1, &swapChain, ¤tFrameBufferId));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UseVsync() { return useVsync; }
|
||||||
|
|
||||||
|
void SetVsync(bool useVsync)
|
||||||
|
{ //TODO change swap chain
|
||||||
|
this->useVsync = useVsync;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetImageCount() const
|
||||||
|
{
|
||||||
|
return images.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CreateSwapChain(vk::Extent2D size)
|
||||||
|
{
|
||||||
|
Logger::RENDER->debug("Creating swap chain for window {0} ...", window->GetWindowId());
|
||||||
|
surfaceFormat = ChoseSurfaceFormat();
|
||||||
|
presentMode = ChosePresentMode();
|
||||||
|
const vk::SurfaceCapabilitiesKHR surfaceCapabilities = device->physicalDevice.getSurfaceCapabilitiesKHR(surface);
|
||||||
|
if(surfaceCapabilities.currentExtent.width != ~static_cast<uint32_t>(0))
|
||||||
|
{ // The surface does provide it's size to the vulkan driver
|
||||||
|
size = surfaceCapabilities.currentExtent;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::SurfaceTransformFlagBitsKHR preTransform; //TODO add option to allow rotation and other modifications
|
||||||
|
if (surfaceCapabilities.supportedTransforms & vk::SurfaceTransformFlagBitsKHR::eIdentity)
|
||||||
|
{ preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity; }
|
||||||
|
else { preTransform = surfaceCapabilities.currentTransform; }
|
||||||
|
|
||||||
|
uint32_t usingImages = std::max(preferredImageCount, surfaceCapabilities.minImageCount);
|
||||||
|
if (surfaceCapabilities.maxImageCount > 0) //GPU has limit of swap chain images
|
||||||
|
usingImages = std::min(usingImages, surfaceCapabilities.maxImageCount);
|
||||||
|
Logger::RENDER->debug("GPU supports {0} to {1} swap chain images. Preferred: {2}, Using: {3}", surfaceCapabilities.minImageCount, surfaceCapabilities.maxImageCount, preferredImageCount, usingImages);
|
||||||
|
|
||||||
|
const vk::SwapchainCreateInfoKHR createInfo({}, surface, usingImages, surfaceFormat.format,
|
||||||
|
surfaceFormat.colorSpace, size, 1,
|
||||||
|
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst,
|
||||||
|
vk::SharingMode::eExclusive, 0, nullptr, preTransform,
|
||||||
|
vk::CompositeAlphaFlagBitsKHR::eOpaque, presentMode, VK_TRUE, swapChain);
|
||||||
|
const vk::SwapchainKHR newSwapChain = device->device.createSwapchainKHR(createInfo);
|
||||||
|
|
||||||
|
DestroySwapChain();
|
||||||
|
swapChain = newSwapChain;
|
||||||
|
this->size = size;
|
||||||
|
|
||||||
|
CreateSwapChainImages();
|
||||||
|
|
||||||
|
fullscreenViewport = vk::Viewport{ 0, 0, (float)size.width, (float)size.height, 0, 1 };
|
||||||
|
Logger::RENDER->debug("Swap chain for window {0} created", window->GetWindowId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateSwapChainImages()
|
||||||
|
{
|
||||||
|
vk::ImageViewCreateInfo imgViewCreateInfo;
|
||||||
|
imgViewCreateInfo.format = surfaceFormat.format;
|
||||||
|
imgViewCreateInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||||
|
imgViewCreateInfo.subresourceRange.levelCount = 1;
|
||||||
|
imgViewCreateInfo.subresourceRange.layerCount = 1;
|
||||||
|
imgViewCreateInfo.viewType = vk::ImageViewType::e2D;
|
||||||
|
|
||||||
|
auto swapChainImages = device->device.getSwapchainImagesKHR(swapChain);
|
||||||
|
|
||||||
|
images.resize(swapChainImages.size());
|
||||||
|
for (uint32_t i = 0; i < swapChainImages.size(); i++)
|
||||||
|
{
|
||||||
|
images[i].image = swapChainImages[i];
|
||||||
|
imgViewCreateInfo.image = swapChainImages[i];
|
||||||
|
images[i].view = device->device.createImageView(imgViewCreateInfo);
|
||||||
|
images[i].fence = device->device.createFence({ vk::FenceCreateFlags(vk::FenceCreateFlagBits::eSignaled)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroySwapChain() const
|
||||||
|
{
|
||||||
|
for(auto& image : images)
|
||||||
|
{
|
||||||
|
device->device.destroyImageView(image.view);
|
||||||
|
device->device.destroyFence(image.fence);
|
||||||
|
}
|
||||||
|
device->device.destroySwapchainKHR(swapChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
vk::Format FindColorFormat() override
|
||||||
|
{
|
||||||
|
return surfaceFormat.format;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual vk::PresentModeKHR ChosePresentMode()
|
||||||
|
{
|
||||||
|
std::vector<vk::PresentModeKHR> presentModes = device->physicalDevice.getSurfacePresentModesKHR(surface);
|
||||||
|
#ifdef DEBUG
|
||||||
|
std::string availableModes = "";
|
||||||
|
for (const auto& presentMode : presentModes)
|
||||||
|
{
|
||||||
|
if (availableModes.length() > 0) availableModes += ", ";
|
||||||
|
availableModes += vk::to_string(presentMode);
|
||||||
|
}
|
||||||
|
Logger::RENDER->debug("Available swap chain present modes {0}. Searching best mode for: vsync={1}", availableModes, useVsync);
|
||||||
|
#endif
|
||||||
|
vk::PresentModeKHR mode = vk::PresentModeKHR::eFifo;
|
||||||
|
if (useVsync)
|
||||||
|
{
|
||||||
|
if (Utils::Contains(presentModes, vk::PresentModeKHR::eMailbox)) mode = vk::PresentModeKHR::eMailbox;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Utils::Contains(presentModes, vk::PresentModeKHR::eImmediate)) mode = vk::PresentModeKHR::eImmediate;
|
||||||
|
else if (Utils::Contains(presentModes, vk::PresentModeKHR::eFifoRelaxed)) mode = vk::PresentModeKHR::eFifoRelaxed;
|
||||||
|
}
|
||||||
|
Logger::RENDER->debug("Using swap chain present mode {0}", vk::to_string(mode));
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual vk::SurfaceFormatKHR ChoseSurfaceFormat()
|
||||||
|
{ //TODO allow to chose output format
|
||||||
|
std::vector<vk::SurfaceFormatKHR> surfaceFormats = device->physicalDevice.getSurfaceFormatsKHR(surface);
|
||||||
|
if(surfaceFormats.size() == 1 && surfaceFormats[0].format == vk::Format::eUndefined)
|
||||||
|
{ // GPU does not have a preferred surface format
|
||||||
|
return vk::SurfaceFormatKHR{ vk::Format::eB8G8R8A8Unorm, surfaceFormats[0].colorSpace };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //TODO chose best fitting
|
||||||
|
return surfaceFormats[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<IImage*> GetImages() override
|
||||||
|
{
|
||||||
|
std::vector<IImage*> imgs;
|
||||||
|
for (auto& image : images)
|
||||||
|
{
|
||||||
|
imgs.push_back(&image);
|
||||||
|
}
|
||||||
|
return imgs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
41
openVulkanoCpp/Vulkan/VulkanUtils.hpp
Normal file
41
openVulkanoCpp/Vulkan/VulkanUtils.hpp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
|
||||||
|
namespace openVulkanoCpp
|
||||||
|
{
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
class VulkanUtils
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static vk::AccessFlags GetAccessFlagsForLayout(vk::ImageLayout layout)
|
||||||
|
{
|
||||||
|
switch (layout)
|
||||||
|
{
|
||||||
|
case vk::ImageLayout::ePreinitialized: return vk::AccessFlagBits::eHostWrite;
|
||||||
|
case vk::ImageLayout::eTransferSrcOptimal: return vk::AccessFlagBits::eTransferRead;
|
||||||
|
case vk::ImageLayout::eTransferDstOptimal: return vk::AccessFlagBits::eTransferWrite;
|
||||||
|
case vk::ImageLayout::eShaderReadOnlyOptimal: return vk::AccessFlagBits::eShaderRead;
|
||||||
|
case vk::ImageLayout::eColorAttachmentOptimal: return vk::AccessFlagBits::eColorAttachmentWrite;
|
||||||
|
case vk::ImageLayout::eDepthStencilAttachmentOptimal: return vk::AccessFlagBits::eDepthStencilAttachmentWrite;
|
||||||
|
default: return vk::AccessFlags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static vk::PipelineStageFlags GetPipelineStageForLayout(vk::ImageLayout layout)
|
||||||
|
{
|
||||||
|
switch (layout)
|
||||||
|
{
|
||||||
|
case vk::ImageLayout::ePreinitialized: return vk::PipelineStageFlagBits::eHost;
|
||||||
|
case vk::ImageLayout::eTransferDstOptimal:
|
||||||
|
case vk::ImageLayout::eTransferSrcOptimal: return vk::PipelineStageFlagBits::eTransfer;
|
||||||
|
case vk::ImageLayout::eShaderReadOnlyOptimal: return vk::PipelineStageFlagBits::eFragmentShader;
|
||||||
|
case vk::ImageLayout::eColorAttachmentOptimal: return vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||||
|
case vk::ImageLayout::eDepthStencilAttachmentOptimal: return vk::PipelineStageFlagBits::eEarlyFragmentTests;
|
||||||
|
case vk::ImageLayout::eUndefined: return vk::PipelineStageFlagBits::eTopOfPipe;
|
||||||
|
default: return vk::PipelineStageFlagBits::eBottomOfPipe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
108
openVulkanoCpp/main.cpp
Normal file
108
openVulkanoCpp/main.cpp
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
#include "Host/GraphicsAppManager.hpp"
|
||||||
|
#include "Scene/Scene.hpp"
|
||||||
|
#include "Scene/Shader.hpp"
|
||||||
|
#include "Base/EngineConfiguration.hpp"
|
||||||
|
|
||||||
|
using namespace openVulkanoCpp::Scene;
|
||||||
|
|
||||||
|
uint32_t GEOS = 3000, OBJECTS = 10000, DYNAMIC = 1000;
|
||||||
|
|
||||||
|
class ExampleApp : public openVulkanoCpp::IGraphicsApp
|
||||||
|
{
|
||||||
|
Scene scene;
|
||||||
|
PerspectiveCamera cam;
|
||||||
|
Material mat;
|
||||||
|
Shader shader;
|
||||||
|
std::vector<Drawable*> drawablesPool;
|
||||||
|
std::vector<Node*> nodesPool;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string GetAppName() override { return "ExampleApp"; }
|
||||||
|
std::string GetAppVersion() override { return "v1.0"; }
|
||||||
|
int GetAppVersionAsInt() override { return 1; }
|
||||||
|
|
||||||
|
void Init() override
|
||||||
|
{
|
||||||
|
std::srand(1);
|
||||||
|
scene.Init();
|
||||||
|
cam.Init(70, 16, 9, 0.1f, 100);
|
||||||
|
scene.SetCamera(&cam);
|
||||||
|
cam.SetMatrix(glm::translate(glm::mat4(1), glm::vec3(0,0,-10)));
|
||||||
|
shader.Init("Shader\\basic", "Shader\\basic");
|
||||||
|
drawablesPool.resize(GEOS);
|
||||||
|
for(int i = 0; i < GEOS; i++)
|
||||||
|
{
|
||||||
|
Geometry* geo = new Geometry();
|
||||||
|
geo->InitCube(std::rand() % 1000 / 1000.0f + 0.01f, std::rand() % 1000 / 1000.0f + 0.01f, std::rand() % 1000 / 1000.0f + 0.01f, glm::vec4((std::rand() % 255) / 255.0f, (std::rand() % 255) / 255.0f, (std::rand() % 255) / 255.0f, 1));
|
||||||
|
drawablesPool[i] = new Drawable();
|
||||||
|
drawablesPool[i]->Init(geo, &mat);
|
||||||
|
}
|
||||||
|
nodesPool.resize(OBJECTS);
|
||||||
|
for(int i = 0; i < OBJECTS; i++)
|
||||||
|
{
|
||||||
|
nodesPool[i] = new Node();
|
||||||
|
nodesPool[i]->Init();
|
||||||
|
scene.GetRoot()->AddChild(nodesPool[i]);
|
||||||
|
if (i < DYNAMIC) nodesPool[i]->SetUpdateFrequency(UpdateFrequency::Always);
|
||||||
|
nodesPool[i]->AddDrawable(drawablesPool[std::rand() % GEOS]);
|
||||||
|
nodesPool[i]->SetMatrix(glm::translate(glm::mat4x4(1), glm::vec3((std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.shader = &shader;
|
||||||
|
|
||||||
|
GetGraphicsAppManager()->GetRenderer()->SetScene(&scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tick() override
|
||||||
|
{
|
||||||
|
for(int i = 0; i < DYNAMIC; i++)
|
||||||
|
{
|
||||||
|
nodesPool[i]->SetMatrix(glm::translate(glm::mat4x4(1), glm::vec3((std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5, (std::rand() % 10000) / 1000.0f - 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Close() override{}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
std::cout << "Amount of Threads to use [2]: ";
|
||||||
|
int threads = 2;
|
||||||
|
std::string input;
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
if (!input.empty())
|
||||||
|
{
|
||||||
|
std::istringstream stream(input);
|
||||||
|
stream >> threads;
|
||||||
|
}
|
||||||
|
std::cout << "Amount of geometries to produce [" << GEOS << "]: ";
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
if (!input.empty())
|
||||||
|
{
|
||||||
|
std::istringstream stream(input);
|
||||||
|
stream >> GEOS;
|
||||||
|
}
|
||||||
|
std::cout << "Amount of objects to render [" << OBJECTS << "]: ";
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
if (!input.empty())
|
||||||
|
{
|
||||||
|
std::istringstream stream(input);
|
||||||
|
stream >> OBJECTS;
|
||||||
|
}
|
||||||
|
std::cout << "Amount of moving objects [" << DYNAMIC << "]: ";
|
||||||
|
std::getline(std::cin, input);
|
||||||
|
if (!input.empty())
|
||||||
|
{
|
||||||
|
std::istringstream stream(input);
|
||||||
|
stream >> DYNAMIC;
|
||||||
|
}
|
||||||
|
DYNAMIC = std::min(DYNAMIC, OBJECTS);
|
||||||
|
openVulkanoCpp::EngineConfiguration::GetEngineConfiguration()->SetNumThreads(threads);
|
||||||
|
openVulkanoCpp::IGraphicsAppManager* manager = new openVulkanoCpp::GraphicsAppManager(new ExampleApp());
|
||||||
|
manager->Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
168
openVulkanoCpp/openVulkanoCpp.vcxproj
Normal file
168
openVulkanoCpp/openVulkanoCpp.vcxproj
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
<ProjectGuid>{D546A70B-536A-487A-91E1-1CD4563A0104}</ProjectGuid>
|
||||||
|
<RootNamespace>openVulkanoCpp</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v142</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)\external\spdlog\include;C:\Program Files\Assimp\include</AdditionalIncludeDirectories>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<PreprocessorDefinitions>_MBCS;%(PreprocessorDefinitions);DEBUG</PreprocessorDefinitions>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>vulkan-1.lib;assimp-vc140-mt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<AdditionalLibraryDirectories>$(VULKAN_SDK)\Lib;C:\Program Files\Assimp\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>call "$(ProjectDir)Shader\CompileShaders.bat"
|
||||||
|
xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<AdditionalIncludeDirectories>$(VULKAN_SDK)\Include;$(SolutionDir)\external\spdlog\include;C:\Program Files\Assimp\include</AdditionalIncludeDirectories>
|
||||||
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalDependencies>vulkan-1.lib;assimp-vc140-mt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
<AdditionalLibraryDirectories>$(VULKAN_SDK)\Lib;C:\Program Files\Assimp\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<EntryPointSymbol>mainCRTStartup</EntryPointSymbol>
|
||||||
|
</Link>
|
||||||
|
<PostBuildEvent>
|
||||||
|
<Command>call "$(ProjectDir)Shader\CompileShaders.bat"
|
||||||
|
xcopy /y "$(ProjectDir)Shader\*.spv" "$(OutDir)\Shader\"</Command>
|
||||||
|
</PostBuildEvent>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="packages.config" />
|
||||||
|
<None Include="Shader\basic.frag" />
|
||||||
|
<None Include="Shader\basic.frag.spv" />
|
||||||
|
<None Include="Shader\basic.vert" />
|
||||||
|
<None Include="Shader\basic.vert.spv" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="Base\Logger.cpp" />
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
<ClCompile Include="Scene\Drawable.cpp" />
|
||||||
|
<ClCompile Include="Scene\Node.cpp" />
|
||||||
|
<ClCompile Include="Vulkan\FrameBuffer.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="Base\EngineConstants.hpp" />
|
||||||
|
<ClInclude Include="Base\ICloseable.hpp" />
|
||||||
|
<ClInclude Include="Base\IGraphicsApp.hpp" />
|
||||||
|
<ClInclude Include="Base\IGraphicsAppManager.hpp" />
|
||||||
|
<ClInclude Include="Base\IInitable.hpp" />
|
||||||
|
<ClInclude Include="Base\Logger.hpp" />
|
||||||
|
<ClInclude Include="Base\PlatformEnums.hpp" />
|
||||||
|
<ClInclude Include="Base\ITickable.hpp" />
|
||||||
|
<ClInclude Include="Base\Timer.hpp" />
|
||||||
|
<ClInclude Include="Base\UI\BaseWindow.hpp" />
|
||||||
|
<ClInclude Include="Base\UI\IWindow.hpp" />
|
||||||
|
<ClInclude Include="Base\Render\IRenderer.hpp" />
|
||||||
|
<ClInclude Include="Base\Utils.hpp" />
|
||||||
|
<ClInclude Include="Data\ReadOnlyAtomicArrayQueue.hpp" />
|
||||||
|
<ClInclude Include="Base\EngineConfiguration.hpp" />
|
||||||
|
<ClInclude Include="Scene\AABB.hpp" />
|
||||||
|
<ClInclude Include="Scene\Drawable.hpp" />
|
||||||
|
<ClInclude Include="Scene\Material.hpp" />
|
||||||
|
<ClInclude Include="Scene\Geometry.hpp" />
|
||||||
|
<ClInclude Include="Scene\Scene.hpp" />
|
||||||
|
<ClInclude Include="Scene\Shader.hpp" />
|
||||||
|
<ClInclude Include="Scene\Vertex.hpp" />
|
||||||
|
<ClInclude Include="Host\GraphicsAppManager.hpp" />
|
||||||
|
<ClInclude Include="Host\PlatformProducer.hpp" />
|
||||||
|
<ClInclude Include="Host\WindowGLFW.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Buffer.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\CommandHelper.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Context.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Debuging\ValidationLayer.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Device.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\DeviceManager.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\FrameBuffer.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Image.hpp" />
|
||||||
|
<ClInclude Include="Scene\Camera.hpp" />
|
||||||
|
<ClInclude Include="Scene\Node.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Pipeline.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Renderer.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\RenderPass.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Resources\ManagedResource.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Resources\ResourceManager.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Resources\IShaderOwner.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Resources\UniformBuffer.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Scene\IRecordable.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Scene\VulkanGeometry.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Scene\VulkanNode.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\Scene\VulkanShader.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\SwapChain.hpp" />
|
||||||
|
<ClInclude Include="Vulkan\VulkanUtils.hpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
<Import Project="..\packages\glm.0.9.9.500\build\native\glm.targets" Condition="Exists('..\packages\glm.0.9.9.500\build\native\glm.targets')" />
|
||||||
|
<Import Project="..\packages\glfw.3.3.0.1\build\native\glfw.targets" Condition="Exists('..\packages\glfw.3.3.0.1\build\native\glfw.targets')" />
|
||||||
|
</ImportGroup>
|
||||||
|
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Error Condition="!Exists('..\packages\glm.0.9.9.500\build\native\glm.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\glm.0.9.9.500\build\native\glm.targets'))" />
|
||||||
|
<Error Condition="!Exists('..\packages\glfw.3.3.0.1\build\native\glfw.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\glfw.3.3.0.1\build\native\glfw.targets'))" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
5
openVulkanoCpp/packages.config
Normal file
5
openVulkanoCpp/packages.config
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<packages>
|
||||||
|
<package id="glfw" version="3.3.0.1" targetFramework="native" />
|
||||||
|
<package id="glm" version="0.9.9.500" targetFramework="native" />
|
||||||
|
</packages>
|
||||||
Reference in New Issue
Block a user