first release

This commit is contained in:
2019-10-14 23:02:51 +02:00
commit 542ef348ee
72 changed files with 5990 additions and 0 deletions

334
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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

Submodule external/assimp added at 799fd74714

1
external/fmt vendored Submodule

Submodule external/fmt added at 91f7619cc9

1
external/glfw vendored Submodule

Submodule external/glfw added at d25248343e

1
external/glm vendored Submodule

Submodule external/glm added at ea678faff9

1
external/spdlog vendored Submodule

Submodule external/spdlog added at aa65dd8905

25
openVulkanoCpp.sln Normal file
View 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

View 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">&lt;NamingElement Priority="1"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="__interface" /&gt;&lt;type Name="class" /&gt;&lt;type Name="struct" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020fields/@EntryIndexedValue">&lt;NamingElement Priority="10"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="class field" /&gt;&lt;type Name="struct field" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020methods/@EntryIndexedValue">&lt;NamingElement Priority="9"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="member function" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Class_0020and_0020struct_0020public_0020fields/@EntryIndexedValue">&lt;NamingElement Priority="11"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"&gt;&lt;type Name="class field" /&gt;&lt;type Name="struct field" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Enum_0020members/@EntryIndexedValue">&lt;NamingElement Priority="13"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="scoped enumerator" /&gt;&lt;type Name="unscoped enumerator" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Namespaces/@EntryIndexedValue">&lt;NamingElement Priority="16"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="namespace" /&gt;&lt;type Name="namespace alias" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/NamingElement&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CppNaming/Rules/=Parameters/@EntryIndexedValue">&lt;NamingElement Priority="5"&gt;&lt;Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"&gt;&lt;type Name="function parameter" /&gt;&lt;type Name="lambda parameter" /&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;&lt;/NamingElement&gt;</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>

View 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);
}
};
}

View 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);
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class ICloseable
{
public:
virtual ~ICloseable() = default;
virtual void Close() = 0;
};
}

View 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;
};
}

View 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;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class IInitable
{
public:
virtual ~IInitable() = default;
virtual void Init() = 0;
};
}

View File

@@ -0,0 +1,12 @@
#pragma once
namespace openVulkanoCpp
{
class ITickable
{
public:
virtual ~ITickable() = default;
virtual void Tick() = 0;
};
}

View 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;
}

View 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;
}
};
}

View 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";
}
}
}

View 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;
};
}

View 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;
}
};
}

View 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;
}
};
}

View 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) {}
};
}

View 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;
}
};
}

View 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;
}
};
}
}

View 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();
}
};
}

View 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!");
}
}
};
}

View 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;
}
};
}

View 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();
}
};
}
}

View 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();
}
};
}
}

View 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);
}
}
}
}

View 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);
};
}
}

View 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;
}
};
}
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include "Shader.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
struct Material
{
Shader* shader;
};
}
}

View File

@@ -0,0 +1,9 @@
#include "Node.hpp"
namespace openVulkanoCpp
{
namespace Scene
{
const glm::mat4x4 Node::IDENTITY = glm::mat4(1);
}
}

View 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);
}
}
}
};
}
}

View 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
}
};
}
}

View 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;
}
};
}
}

View 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 };
}
};
}

View 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

View File

@@ -0,0 +1,9 @@
#version 450
layout(location = 0) in vec4 color;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = color;
}

Binary file not shown.

View 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);
}

Binary file not shown.

View 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();
}
}
};
}
}

View 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);
}
};
}
}

View 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());;
}
};
}
}

View 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);
}
};
}
}

View 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
}
};
}
}

View 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();
}
};
}
}

View 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();
}

View 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];
}
};
}
}

View 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;
}
};
}
}

View 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);
}
};
}
}

View 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);
}
};
}
}

View 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();
}
};
}
}

View 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;
};
}
}

View 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);
}
}
};
}
}

View 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, &copyRegion);
}
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;
}
};
}
}

View 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);
}
};
}
}

View 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;
};
}
}

View 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
{
}
};
}
}

View 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{}
};
}
}

View 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);
}
};
}
}

View 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, &currentFrameBufferId));
}
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;
}
};
}
}

View 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
View 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;
}

View 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>

View 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>