Showing preview only (280K chars total). Download the full file or copy to clipboard to get everything.
Repository: krOoze/Hello_Triangle
Branch: master
Commit: ed984cde2cce
Files: 28
Total size: 268.4 KB
Directory structure:
gitextract_0sir708e/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── buildCI.yml
├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc/
│ └── synchronizationTutorial.md
└── src/
├── CompilerMessages.h
├── EnumerateScheme.h
├── ErrorHandling.h
├── ExtensionLoader.h
├── HelloTriangle.cpp
├── LeanWindowsEnvironment.h
├── Vertex.h
├── VulkanEnvironment.h
├── VulkanIntrospection.h
├── WSI/
│ ├── Glfw.h
│ ├── Wayland.h
│ ├── Win32.h
│ ├── Xcb.h
│ ├── Xlib.h
│ └── private/
│ ├── xdg-shell-client-protocol-private.inl
│ └── xdg-shell-client-protocol.h
├── Wsi.h
└── shaders/
├── hello_triangle.frag
└── hello_triangle.vert
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6NRLHTNEP8GH8&item_name=For+Petr+Kraus+%28aka+krOoze%29+if+you+appreciate+any+of+my+work+done+for+free.¤cy_code=USD&source=url', 'https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6NRLHTNEP8GH8&item_name=For+Petr+Kraus+%28aka+krOoze%29+if+you+appreciate+any+of+my+work+done+for+free.¤cy_code=EUR&source=url']
================================================
FILE: .github/workflows/buildCI.yml
================================================
name: build CI
on:
push:
branches: [ '*' ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
Win_jorb:
runs-on: windows-latest
strategy:
matrix:
build_config: ['Debug', 'Release']
arch: ['x64', 'Win32']
defaults:
run:
shell: cmd
env:
VULKAN_SDK: ${{ github.workspace }}\VulkanSDK
steps:
- uses: actions/checkout@v2
with:
repository: 'krOoze/vk_sdk_lite'
path: ${{ env.VULKAN_SDK }}
ref: windows
persist-credentials: false
- uses: actions/checkout@v2
with:
path: 'source'
submodules: 'recursive'
persist-credentials: false
- run: md source\build
- run: cmake -G "Visual Studio 16 2019" -A ${{ matrix.arch }} ..
working-directory: source\build
- run: cmake --build . --config ${{ matrix.build_config }}
working-directory: source\build
- uses: actions/upload-artifact@v1
with:
name: Hello Vulkan Triangle -- Windows ${{ matrix.build_config }}
path: source\build\${{ matrix.build_config }}\HelloTriangle.exe
linux_jorb:
if: endsWith( github.ref, 'dxgi_interop' ) == false
runs-on: ubuntu-latest
strategy:
matrix:
build_config: ['Debug', 'Release']
defaults:
run:
shell: bash
env:
VULKAN_SDK: ${{ github.workspace }}/VulkanSDK/x86_64
steps:
- uses: actions/checkout@v2
with:
repository: 'krOoze/vk_sdk_lite'
path: 'VulkanSDK'
ref: linux
persist-credentials: false
- run: echo "${{ env.VULKAN_SDK }}/bin" >> $GITHUB_PATH
- run: sudo apt update && sudo apt install xorg-dev
- uses: actions/checkout@v2
with:
path: 'source'
submodules: 'recursive'
persist-credentials: false
- run: mkdir -p source/build
- run: cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_config }} -G "Unix Makefiles" ..
working-directory: source/build
- run: cmake --build .
working-directory: source/build
- uses: actions/upload-artifact@v1
with:
name: Hello Vulkan Triangle App -- Linux ${{ matrix.build_config }}
path: source/build/HelloTriangle
mac_jorb:
if: endsWith( github.ref, 'dxgi_interop' ) == false
runs-on: macos-latest
strategy:
matrix:
build_config: ['Debug', 'Release']
defaults:
run:
shell: bash
env:
VULKAN_SDK: ${{ github.workspace }}/VulkanSDK/macOS
steps:
- uses: actions/checkout@v2
with:
repository: 'krOoze/vk_sdk_lite'
path: 'VulkanSDK'
ref: macos
persist-credentials: false
- run: echo "${{ env.VULKAN_SDK }}/bin" >> $GITHUB_PATH
- uses: actions/checkout@v2
with:
path: 'source'
submodules: 'recursive'
persist-credentials: false
- run: mkdir -p source/build
- run: cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_config }} ..
working-directory: source/build
- run: cmake --build .
working-directory: source/build
- uses: actions/upload-artifact@v1
with:
name: Hello Vulkan Triangle App -- macOS ${{ matrix.build_config }}
path: source/build/HelloTriangle
================================================
FILE: .gitignore
================================================
#CMake
/CMakeCache.txt
/CMakeFiles/
/cmake_install.cmake
#Visual Studio
/.vs/
/*.sln
/*.vcxproj
/*.vcxproj.filters
/*.vcxproj.user
/HelloTriangle.dir/
/Debug/
/Release/
/Win32/
/x64/
#linux
/Makefile
#autogenerated shaders
**/*.spv.inl
#output
/*.exe
/HelloTriangle
/build/
/build32/
/build64/
/out/
================================================
FILE: .gitmodules
================================================
[submodule "glfw"]
path = external/glfw
url = https://github.com/glfw/glfw.git
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required( VERSION 3.5.1 )
project( HelloTriangle )
set( TODO ON CACHE BOOL "Enable compiletime TODO messages" )
# Select WSI platform (can use cmake -D)
set( WSI "USE_PLATFORM_GLFW" CACHE STRING "WSI type used by this app" )
message( "WSI: " ${WSI} )
# Find Vulkan SDK
if( NOT DEFINED {VULKAN_SDK} )
if( NOT DEFINED ENV{VULKAN_SDK} )
message( FATAL_ERROR "VULKAN_SDK not found!" )
endif()
if( CYGWIN )
execute_process( COMMAND cygpath "$ENV{VULKAN_SDK}" OUTPUT_VARIABLE VULKAN_SDK )
string( STRIP ${VULKAN_SDK} VULKAN_SDK )
else()
set( VULKAN_SDK "$ENV{VULKAN_SDK}" )
endif()
endif()
message( "Vulkan SDK path: " ${VULKAN_SDK} )
# Compile shaders
set( GLSL_DEBUG_FLAG $<$<CONFIG:Debug>:-g> )
set( GLSL_COMPILER ${VULKAN_SDK}/bin/glslc -mfmt=num ${GLSL_DEBUG_FLAG} )
set(VERT_SHADER "${CMAKE_SOURCE_DIR}/src/shaders/hello_triangle.vert")
set(VERT_SHADER_INCLUDE ${VERT_SHADER}.spv.inl)
add_custom_command(
COMMENT "Compiling vertex shader"
MAIN_DEPENDENCY ${VERT_SHADER}
OUTPUT ${VERT_SHADER_INCLUDE}
COMMAND ${GLSL_COMPILER} -o ${VERT_SHADER_INCLUDE} ${VERT_SHADER}
#VERBATIM -- TODO breaks empty generator-expression
)
set(FRAG_SHADER "${CMAKE_SOURCE_DIR}/src/shaders/hello_triangle.frag")
set(FRAG_SHADER_INCLUDE ${FRAG_SHADER}.spv.inl)
add_custom_command(
COMMENT "Compiling fragment shader"
MAIN_DEPENDENCY ${FRAG_SHADER}
OUTPUT ${FRAG_SHADER_INCLUDE}
COMMAND ${GLSL_COMPILER} -o ${FRAG_SHADER_INCLUDE} ${FRAG_SHADER}
#VERBATIM -- TODO breaks empty generator-expression
)
add_custom_target(
HelloTriangle_shaders
COMMENT "Compiling shaders"
DEPENDS ${VERT_SHADER_INCLUDE} ${FRAG_SHADER_INCLUDE}
)
# Build GLFW
if( ${WSI} STREQUAL "USE_PLATFORM_GLFW" )
set( GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE )
set( GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE )
set( GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE )
if( CYGWIN )
# Hack to make GLFW use Win32 WSI instead of X11
# TODO: Might be cleaner to use cross-compiling
unset( UNIX )
set( WIN32 1 )
endif()
add_subdirectory( external/glfw )
endif()
# HelloTriangle binary
set_property( DIRECTORY PROPERTY VS_STARTUP_PROJECT HelloTriangle )
file(GLOB SOURCE_HEADERS "src/*.h" "src/WSI/*.h")
add_executable( HelloTriangle WIN32 src/HelloTriangle.cpp ${SOURCE_HEADERS} )
add_dependencies( HelloTriangle HelloTriangle_shaders )
if( NOT TODO )
add_definitions( -DNO_TODO )
endif()
if( MSVC )
target_compile_definitions( HelloTriangle PRIVATE $<$<CONFIG:Debug>:_CONSOLE> )
set_target_properties( HelloTriangle PROPERTIES LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE" )
endif()
if("${CMAKE_VERSION}" VERSION_LESS 3.8.2)
set_target_properties(
HelloTriangle
PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
else()
target_compile_features( HelloTriangle PRIVATE cxx_std_14 )
endif()
find_path(
VULKAN_INCLUDE vulkan/vulkan.h
PATHS "${VULKAN_SDK}/Include" "${VULKAN_SDK}/include"
NO_DEFAULT_PATH
)
message( "Vulkan include dir: " ${VULKAN_INCLUDE} )
include_directories( "${VULKAN_INCLUDE}" "src/" "src/WSI/" )
if( CYGWIN )
set( CMAKE_FIND_LIBRARY_PREFIXES "" )
set( CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll" )
endif()
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(VULKAN_LIBRARY_DIRS "${VULKAN_SDK}/lib" "${VULKAN_SDK}/Lib")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(VULKAN_LIBRARY_DIRS "${VULKAN_SDK}/Lib32")
endif()
find_library(
VULKAN_LIBRARY
NAMES vulkan vulkan-1
PATHS ${VULKAN_LIBRARY_DIRS}
NO_DEFAULT_PATH
)
message( "Vulkan libs: " ${VULKAN_LIBRARY} )
if( ${WSI} STREQUAL "USE_PLATFORM_GLFW" )
set( WSI_LIBS glfw )
elseif( ${WSI} STREQUAL "VK_USE_PLATFORM_WIN32_KHR" )
set( WSI_LIBS )
add_definitions( -DUNICODE )
elseif( ${WSI} STREQUAL "VK_USE_PLATFORM_XLIB_KHR" )
set( WSI_LIBS X11 )
elseif( ${WSI} STREQUAL "VK_USE_PLATFORM_XCB_KHR" )
set( WSI_LIBS xcb xcb-keysyms )
elseif( ${WSI} STREQUAL "VK_USE_PLATFORM_WAYLAND_KHR" )
set( WSI_LIBS wayland-client xkbcommon )
endif()
add_definitions( -D${WSI} )
target_link_libraries( HelloTriangle "${VULKAN_LIBRARY}" "${WSI_LIBS}" )
================================================
FILE: CONTRIBUTING.md
================================================
Contributions to this project are welcome.
Copyright
---------
This project is meant as an educational source without any strings attached to the learner (read [LICENSE](LICENSE)). All contributions are made under the same lincense.
Issues
------
Catching bugs is important and so your Issue reports are very welcome.
- Issues about specific bugs in the source code should contain specification rule quote it violates.
- Other kinds of issues should contain info about OS, driver version, used Vulkan SDK and the output from validation layers.
- Relevant questions are also welcome as long as the trafic is low.
- New feature suggestions are pointless, unless you are willing to implement them yourself
Project goals and conventions
------------------------
This project aims to be a starting point in learning Vulkan API. It aims at people that are bad with tutorials. It tries not to insult the readers inteligence; it tries to do things the right/hard way and it tries not to hide things from the reader. Vulkan is a learning staircase; this is above all meant to be the first stair.
- Assume the reader has only vague idea about how Vulkan works. Show the actual commands; do not abstract too much. It should be readable even outsite an IDE.
- There is a flat 2 level design. The top level shows the stream of actions using Vulkan terminology (strongly hinting at what Vulkan commands will be used). The second level contains the verbose Vulkan stuff (setting `CreateInfo`s and such).
- Assume reader has some computer graphics knowledge; show specific Vulkan features, not effects or algorithms.
- The code must be valid and reasonably optimal Vulkan code. This requires carefully reading the [Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.0-extensions/html/vkspec.html) itself.
- Suboptimal constructs (like `vkDeviceWaitIdle`s, or suboptimally set barriers) should be avoided.
- The code must not be platform dependent (unless it shows a specific non-universally supported feature).
- For now examples showing additional Vulkan features are kept as a diff (git branch) to the `master` (which represents the simplest viable Vulkan app). This is to allow easy comparison.
Pull requests
-------------
Pull requests following the conventions above are welcome.
Building should be straightforward and is outlined in [README.md](README.md).
If you need a new Branch just PR against `master`. It should be changeable later when new branch is created.
================================================
FILE: LICENSE
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
================================================
FILE: README.md
================================================
Hello Triangle Vulkan demo
=========================
This is a traditional Hello World style application for the Vulkan API. It
renders a RGB shaded equilateral triangle (well, if the resolution is a square).
The code is relatively flat and basic, so I think it's good enough for learning.
No tutorial or even much comments are provided though (comments do lie anyway
:smirk:). Though some things I found noteworthy are in the `doc/` folder.
My goal here is to be perfectly conformant to the Vulkan specification. Also I
do handle all the Vulkan `VkResult`s (with app termination though). And I try to
do things in the efficient way, rather than simplify. But I am only human.
:innocent:
If you appriciate any of my free work or help (e.g. hosting this), you can send
me some sweet sweet money:
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6NRLHTNEP8GH8&item_name=For+Petr+Kraus+%28aka+krOoze%29+if+you+appreciate+any+of+my+work+done+for+free.¤cy_code=USD&source=url">
<img width="145" height="30" src="http://vulkan.hys.cz/donate.png" alt="Donate $ to krOoze" /></a>
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=6NRLHTNEP8GH8&item_name=For+Petr+Kraus+%28aka+krOoze%29+if+you+appreciate+any+of+my+work+done+for+free.¤cy_code=EUR&source=url">
<img width="145" height="30" src="http://vulkan.hys.cz/donateEur.png" alt="Donate € to krOoze" /></a>
Branches
-----------------
The git branches demonstrate some basic Vulkan features which can be easily
grafted on this basic example (it is nice to see a diff of what exactly needs to
be changed to make it work). Their `README.md` should be edited to reflect the
changes.
| branch | diff link | description |
|---|---|---|
| [master](https://github.com/krOoze/Hello_Triangle/tree/master)| -- | Your regular Hello Triangle app, and base for the others
| [MSAA](https://github.com/krOoze/Hello_Triangle/tree/MSAA) | [diff](https://github.com/krOoze/Hello_Triangle/compare/MSAA) | Antialiasing (i.e. Vulkan's multisample image resolve) |
| [queue_transfer](https://github.com/krOoze/Hello_Triangle/tree/queue_transfer) | [diff](https://github.com/krOoze/Hello_Triangle/compare/queue_transfer) | Transfer of `EXCLUSIVE` image between queue families (separate graphics and compute queue family) |
| [vertex_offset](https://github.com/krOoze/Hello_Triangle/tree/vertex_offset) | [diff](https://github.com/krOoze/Hello_Triangle/compare/vertex_offset) | Demonstrates how offset in `vkCmdBindVertexBuffers()` works |
| [dxgi_interop](https://github.com/krOoze/Hello_Triangle/tree/dxgi_interop) | [diff](https://github.com/krOoze/Hello_Triangle/compare/dxgi_interop) | An experiment that shows how to import swapchain images from DXGI (Direct3D 12 Swapchain) |
Requirements
----------------------------
**OS**: Windows or Linux
**Language**: C++14
**Build environment**: (latest) [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) (requires `VULKAN_SDK` variable being set)
**Build environment[Windows]**: Visual Studio, Cygwin, or MinGW (or IDEs running on top of them)
**Build environment[Linux]**: CMake compatible compiler and build system and `libxcb-dev` and `libxcb-keysyms-dev`
**Build environment[MacOS]**: CMake compatible compiler and build system
**Build Environment[GLFW]**: GLFW 3.2+ (already included as a git submodule), requires `xorg-dev` (or XCB) package on Linux
**Build environment[Xlib]**: Requires `xorg-dev` package
**Build environment[XCB]**: Requires `libxcb1-dev`, `libxcb-util-dev`, `libxcb-keysyms1-dev`, and `x11proto-dev` packages
**Build environment[Wayland]**: Requires `libwayland-dev` and `libxkbcommon-dev` packages
**Target Environment**: installed (latest) Vulkan capable drivers (to see anything)
**Target Environment**: GLFW(recommended), XCB, Xlib, or Wayland based windowing system
On Unix-like environment refer to
[SDK docs](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)
on how to set `VULKAN_SDK` variable.
Adding `VkSurface` support for other platforms should be straightforward using
the provided ones as a template for it.
Files
----------------------------------
| file | description |
|---|---|
| [doc/synchronizationTutorial.md](doc/synchronizationTutorial.md) | Short explanation of Vulkan synchronization in the context of trivial applications |
| [doc/Schema.pdf](doc/Schema.pdf) | Bunch of diagrams explaining the app architecture and synchronization in a graphical form |
| external/glfw/ | GLFW git submodule |
| src/HelloTriangle.cpp | The app souce code, including the `main()` function |
| src/CompilerMessages.h | Allows to make compile-time messages shown in the compiler output |
| src/EnumerateScheme.h | A scheme to unify usage of most Vulkan `vkEnumerate*` and `vkGet*` commands |
| src/ErrorHandling.h | `VkResult` check helpers + `VK_EXT_debug_utils` extension related stuff |
| src/ExtensionLoader.h | Functions handling loading of select Vulkan extension commands |
| src/LeanWindowsEnvironment.h | Included conditionally by `VulkanEnvironment.h` and includes lean `windows.h` header |
| src/Vertex.h | Just simple Vertex definitions |
| src/VulkanEnvironment.h | Contains header configuration, such platform-specific as `VK_USE_PLATFORM_*` |
| src/VulkanIntrospection.h | Introspection of Vulkan entities; e.g. convert Vulkan enumerants to strings |
| src/Wsi.h | Meta-header including one of the platform-specific headers in WSI directory |
| src/WSI/Glfw.h | WSI platform-dependent stuff via GLFW3 library |
| src/WSI/Win32.h | Win32 WSI platform-dependent stuff |
| src/WSI/Xcb.h | XCB WSI platform-dependent stuff |
| src/WSI/Xlib.h | Xlib WSI platform-dependent stuff |
| src/WSI/Wayland.h | Wayland WSI platform-dependent stuff |
| src/WSI/private/ | Stuff the WSI headers need; currently just generated Wayland protocols |
| src/shaders/hello_triangle.vert | The vertex shader program in GLSL |
| src/shaders/hello_triangle.frag | The fragment shader program in GLSL |
| .gitignore | Git filter file ignoring most probable outputs messing up the local repo |
| .gitmodules | Git submodules file describing the dependency on GLFW |
| CMakeLists.txt | CMake makefile |
| CONTRIBUTING.md | Extra info about contributing code, ideas, and bug reports |
| LICENSE | Copyright licence for this project |
| README.md | This file |
Config
---------------------------------------
You can change the application configuration by simply changing following
variables in `HelloTriangle.cpp`.
| config variable | purpose | default |
|---|---|---|
| `appName` | Application name; might show up in title of the window | whatever it is in code |
| `debugSeverity` | Which kinds of debug message severites will be shown | `WARNING` \| `ERROR` |
| `debugType` | Which kinds of debug message types will be shown | all types |
| `useAssistantLayer` | Enable Assistant Layer too when debugging (TODO: does not support new way of enabling) | `false` |
| `fpsCounter` | Enable FPS counter via `VK_LAYER_LUNARG_monitor` layer | `true` |
| `initialWindowWidth` | The initial width of the rendered window | `800` |
| `initialWindowHeight` | The initial height of the rendered window | `800` |
| `presentMode` | The presentation mode of Vulkan used in swapchain | `VK_PRESENT_MODE_FIFO_KHR` <sup>1</sup>|
| `clearColor` | Background color of the rendering | gray (`{0.1f, 0.1f, 0.1f, 1.0f}`) |
| `forceSeparatePresentQueue` | By default the app prioritizes single Graphics and Present queue. This will create separate queues for testing purposes. There are virtually no platforms currently that naturally have separate Present queue family. |
<sup>1</sup> I preferred `VK_PRESENT_MODE_IMMEDIATE_KHR` before but it tends to
make coil whine because of the extreme FPS (which could be unnecessarily
dangerous to the PCB in the long term).
Build
----------------------------------------------
First just get everything:
$ git clone --recurse-submodules https://github.com/krOoze/Hello_Triangle.git
In many platforms CMake style build should work just fine:
$ cmake -G"Your preferred generator"
or even just
$ cmake .
Then use `make`, or the generated Visual Studio `*.sln`, or whatever it created.
There are two cmake options (supplied by `-D`):
- `WSI` -- set this to `USE_PLATFORM_GLFW` or any `VK_USE_PLATFORM_*_KHR` to
select the WSI to be used. Default is GLFW.
- `TODO` -- set this to `OFF` to remove TODO messages during compilation.
You also might want to add `-DCMAKE_BUILD_TYPE=Debug`.
Manual Build
----------------------------------------------
The code base is quite simple and it can be built manually. It only assumes that
the `src` folder is in the include path, that you link the GLFW dependency, and
that you compile the GLSL shader files into SPIR-V.
In Visual Studio you can simply import the code and add the things mentioned
above. Either "console" or "window" subsystem is a valid choice on Windows.
In Linux distro you would do e.g.:
$ g++ --std=c++14 -Wall -m64 -D_DEBUG -DNO_TODO -I$VULKAN_SDK/include -I./src/ -o./HelloTriangle src/HelloTriangle.cpp -ldl -L$VULKAN_SDK/lib -lvulkan -lglfw
GLSL shaders can be compiled using `glslc` from LunarG Vulkan SDK like so:
On Windows-like environment:
%VULKAN_SDK%/Bin/glslc -mfmt=c -o ./src/shaders/hello_triangle.vert.spv.inl ./src/shaders/hello_triangle.vert
%VULKAN_SDK%/Bin/glslc -mfmt=c -o ./src/shaders/hello_triangle.frag.spv.inl ./src/shaders/hello_triangle.frag
Or on Unix-like environment you would use just `$VULKAN_SDK` instead:
$VULKAN_SDK/Bin/glslc -mfmt=c -o ./src/shaders/hello_triangle.vert.spv.inl ./src/shaders/hello_triangle.vert
$VULKAN_SDK/Bin/glslc -mfmt=c -o ./src/shaders/hello_triangle.frag.spv.inl ./src/shaders/hello_triangle.frag
There are annoying (on purpose) TODOs generated on build. They can be disabled
by defining `NO_TODO` preprocessor macro.
Using GLFW is optional. You may choose another windowing platform by modifying
`VulkanEnvironment.h`, or supplying it via preprocessor. By default, all
platforms use GLFW.
Run
------------------------
You just run it as you would anything else.
<kbd>Esc</kbd> does terminate the app.
<kbd>Alt</kbd> + <kbd>Enter</kbd> toggles fullscreen (might not work on some WSI
platforms).
================================================
FILE: doc/synchronizationTutorial.md
================================================
up to [README.md](../README.md)
Quick Tutorial of basic synchronization of typical renderloop
-------------------------------------------------------------------
You will probably have two semaphors: e.g. `imageAcquired` and `renderingDone`.
(You would need more `imageAcquired` semaphores to prevent `vkAcquireNextImage`
to signal the same semaphore twice, but let's ignore this aspect for now.)
Let's cover it in an order in which things should actually get
**executed** (i.e. **not** necesserily in the order we called or recorded the
commands):
1. You provide `imageAcquired` semaphore to the `vkAcquireNextImageKHR` to be
signalled by it.
2. You provide `imageAcquired` semaphore to the command buffer submit to be
waited on. As a `pWaitDstStageMask` you provide the stage where your first write
access to the swapchain `VkImage` happens. Most often it will be
`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` (but could be something different
based on your command buffer contents).
3. In the command buffer you change the layout of the swapchain image
(Subpasses, Pipeline Barriers, or Events can do that) **from** the present one (
use `VK_IMAGE_LAYOUT_UNDEFINED` as an old layout, which means "sacrifice data"
— there's rarely ever need to read it after present ). For source stage
choose the exact same one as in step 2 and source access mask should be `0` (
because memory dependency is already part of the previous semaphore wait). For
efficient loop, destination stage should also be the same as in step 2 and the
new layout and destination access mask should be appropriate for whatever you
plan to do (likely `VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL` and
`VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT`).
In the case this is done with Render Pass (which should be the preferred
method when we need to draw something), the first use of the `VkImage` would be
the automatic layout transition followed by `loadOp` you provided during creation.
For color attachment load operation does occur in the
`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage (so providing that value as
`dstStageMask` makes sure the layout transition does not clash with the load
operation). Access mask depends on the `loadOp` value chosen.
The layout transition in render pass (and in Pipeline Barriers too) happens
between `srcStageMask` and `dstStageMask` stages. So we do not need to worry
about it too much, assuming our barrier\subpass dependency is otherwisely
correct.
4. You would write to your `VkImage`. Let's assume that is
done with a draw command like `vkCmdDraw` (which implies use of a Render Pass).
Now the `loadOp` guarantees that it happens before our first use of an attachment
in a render pass. So, no additional synchronization is needed here.
5. In the command buffer you change the layout of the swapchain image
(again Subpasses, Pipeline Barriers, or Events can do that) **to** the present
one (`VK_IMAGE_LAYOUT_PRESENT_SRC_KHR`). As your source stage, old layout and
source access mask choose whatever your last use of the swapchain image was.
Destination stage should be `VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT` (i.e.
non-blocking, becose that is instead handled by folowing semaphore signal) and
the destination access mask should be `0`.
In the case of this being a render pass, first a `storeOp` happens after the
last use of your image in a render pass. Again that is guaranteed and needs no
additional synchronization to order these two. The Store Operation for color
attachment happens in `VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage and
uses `VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT` access. The Store Operation is
followed by the automatic layout transition to `finalLayout`. So, to prevent
`storeOp` and this transition to clash, `srcStageMask` should be stage the Store
Op happens (`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` as said above).
6. You provide `renderingDone` semaphore to the command buffer submit to be
signalled.
7. You provide `renderingDone` semaphore to the present command to be waited on.
8. The circle is now complete! :mask:
From this it should be evident we are just declaring dependencies between
memory accesses (otherwisely Vulkan is allowed to mercilessly
overlap execution of commands). When there is any kind of memory read or write,
we must make sure any previous write to the same location has finished.
Usually, value used in `dstStageMask` subsequently appears in
following `srcStageMask` — so for a simple application it forms a nice
dependency chain or path or lifetime of image that is not so hard to reason
about:
Image Acquire
=> Semaphore Signal
=> Semaphore Unsignal in `VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT`
=> `srcStage=VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage of Subpass Dependency\Barrier
=> Layout Transition From Present Layout
=> `dstStage=VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage of Subpass Dependency\Barrier
=> Load Op (`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage) => (Implicit Dependency)
=> Draw (`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage again\still)
=> (Implicit Dependency) => Store Op (`VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT` stage again\still)
=> `srcStage` stage of Subpass Dependency\Barrier
=> Layout Transition to Present Layout
=> `dstStage=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT` stage of Subpass Dependency\Barrier
=> Semaphore Signal
=> Semaphore Unsignal
=> Presenting
From this diagram I can be sure the synchronization is correct:
- The pipeline stage of previous and next step matches
- or it is how the given primitive works (e.g. Semaphore wait does cover any
previously submitted semaphore signal)
- or I am given (relatively rare) specification guarantee that things implicitly
happen in specific order (e.g. the Load Op vs first subpass attachment use).
================================================
FILE: src/CompilerMessages.h
================================================
// Allows to show messages during compilation -- somewhat platform dependent
#ifndef COMMON_COMPILER_MESSAGES_H
#define COMMON_COMPILER_MESSAGES_H
// compiler messages -- should work in MSVC and GCC and is ignored by unsupporting compilers
#define STRINGIZE_HELPER(x) #x
#define STRINGIZE(x) STRINGIZE_HELPER(x)
#ifdef NO_TODO
#define TODO(desc)
#else
#if defined(_MSC_VER)
#define TODO(desc) __pragma( message(__FILE__ "(" STRINGIZE(__LINE__) ")" ": warning T: TODO: " desc) )
#elif defined(__GNUG__)
#define DO_PRAGMA(x) _Pragma (#x)
#define TODO(desc) DO_PRAGMA(message "TODO: " desc)
#else
#define TODO(desc)
#endif
#endif // NO_TODO
// usage:
//TODO("Don't forget to do X.")
#endif //COMMON_COMPILER_MESSAGES_H
================================================
FILE: src/EnumerateScheme.h
================================================
// Scheme for the various vkGet* and vkEnumerate* commands
//
// Following the DRY principle, this implements getting a vector of enumerants
// from the similar enumeration commands that return VK_INCOMPELTE.
#ifndef COMMON_ENUMERATE_SCHEME_H
#define COMMON_ENUMERATE_SCHEME_H
#include <functional>
#include <type_traits>
#include <vector>
#include <vulkan/vulkan.h>
#include "CompilerMessages.h"
#include "ErrorHandling.h"
// the enumeration scheme
// takes function VkResult cmd( uint32_t count, Element* pArray ) and Vulkan command name (for debugging purposes)
// returns vector<Element> which contains the enumerants, or throws
template< typename Element, typename Cmd >
std::vector<Element> enumerateScheme( Cmd cmd, const char* cmdName ){
std::vector<Element> enumerants;
VkResult errorCode;
uint32_t enumerantsCount;
// repeat until complete array is returned, or error
do{
errorCode = cmd( &enumerantsCount, nullptr ); RESULT_HANDLER( errorCode, cmdName ); // get current array size
enumerants.resize( enumerantsCount );
errorCode = cmd( &enumerantsCount, enumerants.data() ); // get current array up to enumerantsCount
} while( errorCode == VK_INCOMPLETE );
RESULT_HANDLER( errorCode, cmdName );
enumerants.resize( enumerantsCount ); // shrink in case of enumerantsCount1 > enumerantsCount2
enumerants.shrink_to_fit(); // unlikely the vector will grow from this point on anyway
return enumerants;
}
// Adapters for specific Vulkan commands
///////////////////////////////////////////////
template< typename Element, typename... Ts, typename = std::enable_if_t<!std::is_same<Element, VkInstance>::value> >
std::vector<Element> enumerate( Ts... );
// Tag will be VkInstance if to disambiguate commands that also work on device
template< typename Tag, typename Element, typename... Ts, typename = std::enable_if_t<std::is_same<Tag, VkInstance>::value> >
std::vector<Element> enumerate( Ts... );
// for vkEnumerateInstanceLayerProperties -- auto v = enumerate<VkInstance, VkLayerProperties>();
template<>
std::vector<VkLayerProperties> enumerate<VkInstance, VkLayerProperties>(){
return enumerateScheme<VkLayerProperties>( vkEnumerateInstanceLayerProperties, "vkEnumerateInstanceLayerProperties" );
}
// for vkEnumerateDeviceLayerProperties -- auto v = enumerate<VkLayerProperties>( pd );
template<>
std::vector<VkLayerProperties> enumerate<VkLayerProperties, VkPhysicalDevice>( VkPhysicalDevice physicalDevice ){
using namespace std::placeholders;
const auto cmd = vkEnumerateDeviceLayerProperties;
const auto adapterCmd = std::bind( cmd, physicalDevice, _1, _2 );
return enumerateScheme<VkLayerProperties>( adapterCmd, "vkEnumerateDeviceLayerProperties" );
}
// for vkEnumerateInstanceExtensionProperties -- auto v = enumerate<VkInstance, VkExtensionProperties>( "ln" );
template<>
std::vector<VkExtensionProperties> enumerate<VkInstance, VkExtensionProperties, const char*>( const char* pLayerName ){
using namespace std::placeholders;
const auto cmd = vkEnumerateInstanceExtensionProperties;
const auto adapterCmd = std::bind( cmd, pLayerName, _1, _2 );
return enumerateScheme<VkExtensionProperties>( adapterCmd, "vkEnumerateInstanceExtensionProperties" );
}
// for vkEnumerateInstanceExtensionProperties with nullptr layer -- auto v = enumerate<VkInstance, VkExtensionProperties>();
template<>
std::vector<VkExtensionProperties> enumerate<VkInstance, VkExtensionProperties>(){
using namespace std::placeholders;
const auto cmd = vkEnumerateInstanceExtensionProperties;
const auto adapterCmd = std::bind( cmd, nullptr, _1, _2 );
return enumerateScheme<VkExtensionProperties>( adapterCmd, "vkEnumerateInstanceExtensionProperties" );
}
// for vkEnumerateDeviceExtensionProperties -- auto v = enumerate<VkExtensionProperties>( pd, "ln" );
template<>
std::vector<VkExtensionProperties> enumerate<VkExtensionProperties, VkPhysicalDevice, const char*>( VkPhysicalDevice physicalDevice, const char* pLayerName ){
using namespace std::placeholders;
const auto cmd = vkEnumerateDeviceExtensionProperties;
const auto adapterCmd = std::bind( cmd, physicalDevice, pLayerName, _1, _2 );
return enumerateScheme<VkExtensionProperties>( adapterCmd, "vkEnumerateDeviceExtensionProperties" );
}
// for vkEnumerateInstanceExtensionProperties with nullptr layer -- auto v = enumerate<VkExtensionProperties>( pd );
template<>
std::vector<VkExtensionProperties> enumerate<VkExtensionProperties, VkPhysicalDevice>( VkPhysicalDevice physicalDevice ){
using namespace std::placeholders;
const auto cmd = vkEnumerateDeviceExtensionProperties;
const auto adapterCmd = std::bind( cmd, physicalDevice, nullptr, _1, _2 );
return enumerateScheme<VkExtensionProperties>( adapterCmd, "vkEnumerateDeviceExtensionProperties" );
}
// for vkEnumeratePhysicalDevices -- auto v = enumerate<VkPhysicalDevice>( i );
template<>
std::vector<VkPhysicalDevice> enumerate<VkPhysicalDevice, VkInstance>( VkInstance instance ){
using namespace std::placeholders;
const auto cmd = vkEnumeratePhysicalDevices;
const auto adapterCmd = std::bind( cmd, instance, _1, _2 );
return enumerateScheme<VkPhysicalDevice>( adapterCmd, "vkEnumeratePhysicalDevices" );
}
// for vkGetPhysicalDeviceSurfaceFormatsKHR -- auto v = enumerate<VkSurfaceFormatKHR>( pd, s );
template<>
std::vector<VkSurfaceFormatKHR> enumerate<VkSurfaceFormatKHR, VkPhysicalDevice, VkSurfaceKHR>( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface ){
using namespace std::placeholders;
const auto cmd = vkGetPhysicalDeviceSurfaceFormatsKHR;
const auto adapterCmd = std::bind( cmd, physicalDevice, surface, _1, _2 );
return enumerateScheme<VkSurfaceFormatKHR>( adapterCmd, "vkGetPhysicalDeviceSurfaceFormatsKHR" );
}
// for vkGetPhysicalDeviceSurfacePresentModesKHR -- auto v = enumerate<VkSurfaceFormatKHR>( pd, s );
template<>
std::vector<VkPresentModeKHR> enumerate<VkPresentModeKHR, VkPhysicalDevice, VkSurfaceKHR>( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface ){
using namespace std::placeholders;
const auto cmd = vkGetPhysicalDeviceSurfacePresentModesKHR;
const auto adapterCmd = std::bind( cmd, physicalDevice, surface, _1, _2 );
return enumerateScheme<VkPresentModeKHR>( adapterCmd, "vkGetPhysicalDeviceSurfacePresentModesKHR" );
}
// for vkGetSwapchainImagesKHR -- auto v = enumerate<VkSurfaceFormatKHR>( d, s );
template<>
std::vector<VkImage> enumerate<VkImage, VkDevice, VkSwapchainKHR>( VkDevice device, VkSwapchainKHR swapchain ){
using namespace std::placeholders;
const auto cmd = vkGetSwapchainImagesKHR;
const auto adapterCmd = std::bind( cmd, device, swapchain, _1, _2 );
return enumerateScheme<VkImage>( adapterCmd, "vkGetSwapchainImagesKHR" );
}
// ... others to be added as needed
#endif //COMMON_ENUMERATE_SCHEME_H
================================================
FILE: src/ErrorHandling.h
================================================
// Reusable error handling primitives for Vulkan
#ifndef COMMON_ERROR_HANDLING_H
#define COMMON_ERROR_HANDLING_H
#include <iostream>
#include <string>
#include <sstream>
#include <vulkan/vulkan.h>
#include "VulkanIntrospection.h"
struct VulkanResultException{
const char* file;
unsigned line;
const char* func;
const char* source;
VkResult result;
VulkanResultException( const char* file, unsigned line, const char* func, const char* source, VkResult result )
: file( file ), line( line ), func( func ), source( source ), result( result ){}
};
#define RESULT_HANDLER( errorCode, source ) if( errorCode ) throw VulkanResultException( __FILE__, __LINE__, __func__, source, errorCode )
#define RESULT_HANDLER_EX( cond, errorCode, source ) if( cond ) throw VulkanResultException( __FILE__, __LINE__, __func__, source, errorCode )
#define RUNTIME_ASSERT( cond, source ) if( !(cond) ) throw source " failed";
// just use cout for logging now
std::ostream& logger = std::cout;
enum class Highlight{ off, on };
void genericDebugCallback( std::string flags, Highlight highlight, std::string msgCode, std::string object, const char* message );
VKAPI_ATTR VkBool32 VKAPI_CALL genericDebugReportCallback(
VkDebugReportFlagsEXT msgFlags,
VkDebugReportObjectTypeEXT objType,
uint64_t srcObject,
size_t /*location*/,
int32_t msgCode,
const char* pLayerPrefix,
const char* pMsg,
void* /*pUserData*/
);
VKAPI_ATTR VkBool32 VKAPI_CALL genericDebugUtilsCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* /*pUserData*/
);
enum class DebugObjectType{ debugReport, debugUtils } tag;
struct DebugObjectVariant{
DebugObjectType tag;
union{
VkDebugReportCallbackEXT debugReportCallback;
VkDebugUtilsMessengerEXT debugUtilsMessenger;
};
};
DebugObjectVariant initDebug( const VkInstance instance, const DebugObjectType debugExtension, const VkDebugUtilsMessageSeverityFlagsEXT debugSeverity, const VkDebugUtilsMessageTypeFlagsEXT debugType );
void killDebug( VkInstance instance, DebugObjectVariant debug );
VkDebugReportFlagsEXT translateFlags( const VkDebugUtilsMessageSeverityFlagsEXT debugSeverity, const VkDebugUtilsMessageTypeFlagsEXT debugType );
// Implementation
//////////////////////////////////
void genericDebugCallback( std::string flags, Highlight highlight, std::string msgCode, std::string object, const char* message ){
using std::endl;
using std::string;
const string report = flags + ": " + object + ": " + msgCode + ", \"" + message + '"';
if( highlight != Highlight::off ){
const string border( 80, '!' );
logger << border << endl;
logger << report << endl;
logger << border << endl << endl;
}
else{
logger << report << endl;
}
}
VKAPI_ATTR VkBool32 VKAPI_CALL genericDebugReportCallback(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t /*location*/,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage,
void* /*pUserData*/
){
using std::to_string;
using std::string;
Highlight highlight;
if( (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) || (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) || (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) ){
highlight = Highlight::on;
}
else highlight = Highlight::off;
genericDebugCallback( dbrflags_to_string( flags ), highlight, string(pLayerPrefix) + ", " + to_string( messageCode ), to_string( objectType ) + "(" + to_string_hex( object ) + ")", pMessage );
return VK_FALSE; // no abort on misbehaving command
}
VKAPI_ATTR VkBool32 VKAPI_CALL genericDebugUtilsCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* /*pUserData*/
){
using std::to_string;
using std::string;
Highlight highlight;
if( (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) || (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)){
highlight = Highlight::on;
}
else highlight = Highlight::off;
string objects;
bool first = true;
for( uint32_t i = 0; i < pCallbackData->objectCount; ++i ){
const auto& obj = pCallbackData->pObjects[i];
if( first ) first = false;
else objects += ", ";
objects += to_string( obj.objectType ) + "(" + to_string_hex( obj.objectHandle ) + ")";
}
objects = "[" + objects + "]";
genericDebugCallback( dbutype_to_string( messageTypes ) + "+" + to_string( messageSeverity ), highlight, string(pCallbackData->pMessageIdName) + "(" + to_string( pCallbackData->messageIdNumber ) + ")", objects, pCallbackData->pMessage );
return VK_FALSE; // no abort on misbehaving command
}
VkDebugReportFlagsEXT translateFlags( const VkDebugUtilsMessageSeverityFlagsEXT debugSeverity, const VkDebugUtilsMessageTypeFlagsEXT debugType ){
VkDebugReportFlagsEXT flags = 0;
if( (debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT) || (debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT) ){
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT ) flags |= VK_DEBUG_REPORT_ERROR_BIT_EXT;
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT ) flags |= VK_DEBUG_REPORT_WARNING_BIT_EXT;
if( (debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) && (debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) ) flags |= VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT ) flags |= VK_DEBUG_REPORT_INFORMATION_BIT_EXT;
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT ) flags |= VK_DEBUG_REPORT_DEBUG_BIT_EXT;
}
return flags;
}
DebugObjectVariant initDebug( const VkInstance instance, const DebugObjectType debugExtension, const VkDebugUtilsMessageSeverityFlagsEXT debugSeverity, const VkDebugUtilsMessageTypeFlagsEXT debugType ){
DebugObjectVariant debug;
debug.tag = debugExtension;
if( debugExtension == DebugObjectType::debugUtils ){
const VkDebugUtilsMessengerCreateInfoEXT dmci = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
nullptr, // pNext
0, // flags
debugSeverity,
debugType,
::genericDebugUtilsCallback,
nullptr // pUserData
};
const VkResult errorCode = vkCreateDebugUtilsMessengerEXT( instance, &dmci, nullptr, &debug.debugUtilsMessenger ); RESULT_HANDLER( errorCode, "vkCreateDebugUtilsMessengerEXT" );
}
else if( debugExtension == DebugObjectType::debugReport ){
const VkDebugReportCallbackCreateInfoEXT debugCreateInfo{
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
nullptr, // pNext
translateFlags( debugSeverity, debugType ),
::genericDebugReportCallback,
nullptr // pUserData
};
const VkResult errorCode = vkCreateDebugReportCallbackEXT( instance, &debugCreateInfo, nullptr, &debug.debugReportCallback ); RESULT_HANDLER( errorCode, "vkCreateDebugReportCallbackEXT" );
}
else{
throw "initDebug: unknown debug extension";
}
return debug;
}
void killDebug( const VkInstance instance, const DebugObjectVariant debug ){
if( debug.tag == DebugObjectType::debugUtils ){
vkDestroyDebugUtilsMessengerEXT( instance, debug.debugUtilsMessenger, nullptr );
}
else if( debug.tag == DebugObjectType::debugReport ){
vkDestroyDebugReportCallbackEXT( instance, debug.debugReportCallback, nullptr );
}
else{
throw "initDebug: unknown debug extension";
}
}
#endif //COMMON_ERROR_HANDLING_H
================================================
FILE: src/ExtensionLoader.h
================================================
// Vulkan extensions commands loader
#ifndef EXTENSION_LOADER_H
#define EXTENSION_LOADER_H
#include <vector>
#include <unordered_map>
#include<cstring>
#include <vulkan/vulkan.h>
#include "CompilerMessages.h"
#include "EnumerateScheme.h"
void loadInstanceExtensionsCommands( VkInstance instance, const std::vector<const char*>& instanceExtensions );
void unloadInstanceExtensionsCommands( VkInstance instance );
void loadDeviceExtensionsCommands( VkDevice device, const std::vector<const char*>& instanceExtensions );
void unloadDeviceExtensionsCommands( VkDevice device );
void loadPDProps2Commands( VkInstance instance );
void unloadPDProps2Commands( VkInstance instance );
void loadDebugReportCommands( VkInstance instance );
void unloadDebugReportCommands( VkInstance instance );
void loadDebugUtilsCommands( VkInstance instance );
void unloadDebugUtilsCommands( VkInstance instance );
void loadExternalMemoryCapsCommands( VkInstance instance );
void unloadExternalMemoryCapsCommands( VkInstance instance );
void loadExternalMemoryCommands( VkDevice device );
void unloadExternalMemoryCommands( VkDevice device );
#ifdef VK_USE_PLATFORM_WIN32_KHR
void loadExternalMemoryWin32Commands( VkDevice device );
void unloadExternalMemoryWin32Commands( VkDevice device );
#endif
void loadDedicatedAllocationCommands( VkDevice device );
void unloadDedicatedAllocationCommands( VkDevice device );
////////////////////////////////////////////////////////
std::unordered_map< VkInstance, std::vector<const char*> > instanceExtensionsMap;
std::unordered_map< VkPhysicalDevice, VkInstance > physicalDeviceInstanceMap;
TODO( "Leaks destroyed instances" );
void populatePhysicalDeviceInstaceMap( const VkInstance instance ){
const std::vector<VkPhysicalDevice> physicalDevices = enumerate<VkPhysicalDevice>( instance );
for( const auto pd : physicalDevices ) physicalDeviceInstanceMap[pd] = instance;
}
void loadInstanceExtensionsCommands( const VkInstance instance, const std::vector<const char*>& instanceExtensions ){
using std::strcmp;
instanceExtensionsMap[instance] = instanceExtensions;
for( const auto e : instanceExtensions ){
if( strcmp( e, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) == 0 ) loadPDProps2Commands( instance );
if( strcmp( e, VK_EXT_DEBUG_REPORT_EXTENSION_NAME ) == 0 ) loadDebugReportCommands( instance );
if( strcmp( e, VK_EXT_DEBUG_UTILS_EXTENSION_NAME ) == 0 ) loadDebugUtilsCommands( instance );
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME ) == 0 ) loadExternalMemoryCapsCommands( instance );
// ...
}
}
void unloadInstanceExtensionsCommands( const VkInstance instance ){
using std::strcmp;
for( const auto e : instanceExtensionsMap.at( instance ) ){
if( strcmp( e, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME ) == 0 ) unloadPDProps2Commands( instance );
if( strcmp( e, VK_EXT_DEBUG_REPORT_EXTENSION_NAME ) == 0 ) unloadDebugReportCommands( instance );
if( strcmp( e, VK_EXT_DEBUG_UTILS_EXTENSION_NAME ) == 0 ) unloadDebugUtilsCommands( instance );
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME ) == 0 ) unloadExternalMemoryCapsCommands( instance );
// ...
}
instanceExtensionsMap.erase( instance );
}
std::unordered_map< VkDevice, std::vector<const char*> > deviceExtensionsMap;
void loadDeviceExtensionsCommands( const VkDevice device, const std::vector<const char*>& deviceExtensions ){
using std::strcmp;
deviceExtensionsMap[device] = deviceExtensions;
for( const auto e : deviceExtensions ){
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME ) == 0 ) loadExternalMemoryCommands( device );
#ifdef VK_USE_PLATFORM_WIN32_KHR
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME ) == 0 ) loadExternalMemoryWin32Commands( device );
#endif
if( strcmp( e, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME ) == 0 ) loadDedicatedAllocationCommands( device );
// ...
}
}
void unloadDeviceExtensionsCommands( const VkDevice device ){
using std::strcmp;
for( const auto e : deviceExtensionsMap.at( device ) ){
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME ) == 0 ) unloadExternalMemoryCommands( device );
#ifdef VK_USE_PLATFORM_WIN32_KHR
if( strcmp( e, VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME ) == 0 ) unloadExternalMemoryWin32Commands( device );
#endif
if( strcmp( e, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME ) == 0 ) unloadDedicatedAllocationCommands( device );
// ...
}
deviceExtensionsMap.erase( device );
}
// VK_KHR_get_physical_device_properties2
///////////////////////////////////////////
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceFeatures2KHR > GetPhysicalDeviceFeatures2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceProperties2KHR > GetPhysicalDeviceProperties2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceFormatProperties2KHR > GetPhysicalDeviceFormatProperties2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceImageFormatProperties2KHR > GetPhysicalDeviceImageFormatProperties2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR > GetPhysicalDeviceQueueFamilyProperties2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceMemoryProperties2KHR > GetPhysicalDeviceMemoryProperties2KHRDispatchTable;
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR > GetPhysicalDeviceSparseImageFormatProperties2KHRDispatchTable;
void loadPDProps2Commands( VkInstance instance ){
populatePhysicalDeviceInstaceMap( instance );
PFN_vkVoidFunction temp_fp;
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceFeatures2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceFeatures2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceFeatures2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceFormatProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceFormatProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceFormatProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceImageFormatProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceImageFormatProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceImageFormatProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceQueueFamilyProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceQueueFamilyProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceQueueFamilyProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceMemoryProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceMemoryProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceMemoryProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties2KHR>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceSparseImageFormatProperties2KHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceSparseImageFormatProperties2KHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceSparseImageFormatProperties2KHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR>( temp_fp );
}
void unloadPDProps2Commands( VkInstance instance ){
GetPhysicalDeviceFeatures2KHRDispatchTable.erase( instance );
GetPhysicalDeviceProperties2KHRDispatchTable.erase( instance );
GetPhysicalDeviceFormatProperties2KHRDispatchTable.erase( instance );
GetPhysicalDeviceImageFormatProperties2KHRDispatchTable.erase( instance );
GetPhysicalDeviceQueueFamilyProperties2KHRDispatchTable.erase( instance );
GetPhysicalDeviceMemoryProperties2KHRDispatchTable.erase( instance );
GetPhysicalDeviceSparseImageFormatProperties2KHRDispatchTable.erase( instance );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceFeatures2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pFeatures );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pProperties );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR( VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceFormatProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, format, pFormatProperties );
}
VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceImageFormatProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pImageFormatInfo, pImageFormatProperties );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR( VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceQueueFamilyProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pQueueFamilyPropertyCount, pQueueFamilyProperties );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR( VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceMemoryProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pMemoryProperties );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties ){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceSparseImageFormatProperties2KHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pFormatInfo, pPropertyCount, pProperties );
}
// VK_EXT_debug_report
//////////////////////////////////
std::unordered_map< VkInstance, PFN_vkCreateDebugReportCallbackEXT > CreateDebugReportCallbackEXTDispatchTable;
std::unordered_map< VkInstance, PFN_vkDestroyDebugReportCallbackEXT > DestroyDebugReportCallbackEXTDispatchTable;
std::unordered_map< VkInstance, PFN_vkDebugReportMessageEXT > DebugReportMessageEXTDispatchTable;
void loadDebugReportCommands( VkInstance instance ){
PFN_vkVoidFunction temp_fp;
temp_fp = vkGetInstanceProcAddr( instance, "vkCreateDebugReportCallbackEXT" );
if( !temp_fp ) throw "Failed to load vkCreateDebugReportCallbackEXT"; // check shouldn't be necessary (based on spec)
CreateDebugReportCallbackEXTDispatchTable[instance] = reinterpret_cast<PFN_vkCreateDebugReportCallbackEXT>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkDestroyDebugReportCallbackEXT" );
if( !temp_fp ) throw "Failed to load vkDestroyDebugReportCallbackEXT"; // check shouldn't be necessary (based on spec)
DestroyDebugReportCallbackEXTDispatchTable[instance] = reinterpret_cast<PFN_vkDestroyDebugReportCallbackEXT>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkDebugReportMessageEXT" );
if( !temp_fp ) throw "Failed to load vkDebugReportMessageEXT"; // check shouldn't be necessary (based on spec)
DebugReportMessageEXTDispatchTable[instance] = reinterpret_cast<PFN_vkDebugReportMessageEXT>( temp_fp );
}
void unloadDebugReportCommands( VkInstance instance ){
CreateDebugReportCallbackEXTDispatchTable.erase( instance );
DestroyDebugReportCallbackEXTDispatchTable.erase( instance );
DebugReportMessageEXTDispatchTable.erase( instance );
}
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT(
VkInstance instance,
const VkDebugReportCallbackCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugReportCallbackEXT* pCallback
){
auto dispatched_cmd = CreateDebugReportCallbackEXTDispatchTable.at( instance );
return dispatched_cmd( instance, pCreateInfo, pAllocator, pCallback );
}
VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT(
VkInstance instance,
VkDebugReportCallbackEXT callback,
const VkAllocationCallbacks* pAllocator
){
auto dispatched_cmd = DestroyDebugReportCallbackEXTDispatchTable.at( instance );
return dispatched_cmd( instance, callback, pAllocator );
}
VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT(
VkInstance instance,
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage
){
auto dispatched_cmd = DebugReportMessageEXTDispatchTable.at( instance );
return dispatched_cmd( instance, flags, objectType, object, location, messageCode, pLayerPrefix, pMessage );
}
// VK_EXT_debug_utils
//////////////////////////////////
std::unordered_map< VkInstance, PFN_vkCreateDebugUtilsMessengerEXT > CreateDebugUtilsMessengerEXTDispatchTable;
std::unordered_map< VkInstance, PFN_vkDestroyDebugUtilsMessengerEXT > DestroyDebugUtilsMessengerEXTDispatchTable;
std::unordered_map< VkInstance, PFN_vkSubmitDebugUtilsMessageEXT > SubmitDebugUtilsMessageEXTDispatchTable;
void loadDebugUtilsCommands( VkInstance instance ){
PFN_vkVoidFunction temp_fp;
temp_fp = vkGetInstanceProcAddr( instance, "vkCreateDebugUtilsMessengerEXT" );
if( !temp_fp ) throw "Failed to load vkCreateDebugUtilsMessengerEXT"; // check shouldn't be necessary (based on spec)
CreateDebugUtilsMessengerEXTDispatchTable[instance] = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkDestroyDebugUtilsMessengerEXT" );
if( !temp_fp ) throw "Failed to load vkDestroyDebugUtilsMessengerEXT"; // check shouldn't be necessary (based on spec)
DestroyDebugUtilsMessengerEXTDispatchTable[instance] = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>( temp_fp );
temp_fp = vkGetInstanceProcAddr( instance, "vkSubmitDebugUtilsMessageEXT" );
if( !temp_fp ) throw "Failed to load vkSubmitDebugUtilsMessageEXT"; // check shouldn't be necessary (based on spec)
SubmitDebugUtilsMessageEXTDispatchTable[instance] = reinterpret_cast<PFN_vkSubmitDebugUtilsMessageEXT>( temp_fp );
}
void unloadDebugUtilsCommands( VkInstance instance ){
CreateDebugUtilsMessengerEXTDispatchTable.erase( instance );
DestroyDebugUtilsMessengerEXTDispatchTable.erase( instance );
SubmitDebugUtilsMessageEXTDispatchTable.erase( instance );
}
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pMessenger
){
auto dispatched_cmd = CreateDebugUtilsMessengerEXTDispatchTable.at( instance );
return dispatched_cmd( instance, pCreateInfo, pAllocator, pMessenger );
}
VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(
VkInstance instance,
VkDebugUtilsMessengerEXT messenger,
const VkAllocationCallbacks* pAllocator
){
auto dispatched_cmd = DestroyDebugUtilsMessengerEXTDispatchTable.at( instance );
return dispatched_cmd( instance, messenger, pAllocator );
}
VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT(
VkInstance instance,
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData
){
auto dispatched_cmd = SubmitDebugUtilsMessageEXTDispatchTable.at( instance );
return dispatched_cmd( instance, messageSeverity, messageTypes, pCallbackData );
}
// VK_KHR_external_memory_capabilities
///////////////////////////////////////////
std::unordered_map< VkInstance, PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR > GetPhysicalDeviceExternalBufferPropertiesKHRDispatchTable;
void loadExternalMemoryCapsCommands( VkInstance instance ){
populatePhysicalDeviceInstaceMap( instance );
PFN_vkVoidFunction temp_fp;
temp_fp = vkGetInstanceProcAddr( instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR" );
if( !temp_fp ) throw "Failed to load vkGetPhysicalDeviceExternalBufferPropertiesKHR"; // check shouldn't be necessary (based on spec)
GetPhysicalDeviceExternalBufferPropertiesKHRDispatchTable[instance] = reinterpret_cast<PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR>( temp_fp );
}
void unloadExternalMemoryCapsCommands( VkInstance instance ){
GetPhysicalDeviceExternalBufferPropertiesKHRDispatchTable.erase( instance );
}
VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHR(
VkPhysicalDevice physicalDevice,
const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
VkExternalBufferProperties* pExternalBufferProperties
){
const VkInstance instance = physicalDeviceInstanceMap.at( physicalDevice );
auto dispatched_cmd = GetPhysicalDeviceExternalBufferPropertiesKHRDispatchTable.at( instance );
return dispatched_cmd( physicalDevice, pExternalBufferInfo, pExternalBufferProperties );
}
// VK_KHR_external_memory
///////////////////////////////////////////
void loadExternalMemoryCommands( VkDevice ){
// no commands
}
void unloadExternalMemoryCommands( VkDevice ){
// no commands
}
#ifdef VK_USE_PLATFORM_WIN32_KHR
// VK_KHR_external_memory_win32
///////////////////////////////////////////
std::unordered_map< VkDevice, PFN_vkGetMemoryWin32HandleKHR > GetMemoryWin32HandleKHRDispatchTable;
std::unordered_map< VkDevice, PFN_vkGetMemoryWin32HandlePropertiesKHR > GetMemoryWin32HandlePropertiesKHRDispatchTable;
void loadExternalMemoryWin32Commands( VkDevice device ){
PFN_vkVoidFunction temp_fp;
temp_fp = vkGetDeviceProcAddr( device, "vkGetMemoryWin32HandleKHR" );
if( !temp_fp ) throw "Failed to load vkGetMemoryWin32HandleKHR"; // check shouldn't be necessary (based on spec)
GetMemoryWin32HandleKHRDispatchTable[device] = reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>( temp_fp );
temp_fp = vkGetDeviceProcAddr( device, "vkGetMemoryWin32HandlePropertiesKHR" );
if( !temp_fp ) throw "Failed to load vkGetMemoryWin32HandlePropertiesKHR"; // check shouldn't be necessary (based on spec)
GetMemoryWin32HandlePropertiesKHRDispatchTable[device] = reinterpret_cast<PFN_vkGetMemoryWin32HandlePropertiesKHR>( temp_fp );
}
void unloadExternalMemoryWin32Commands( VkDevice device ){
GetMemoryWin32HandleKHRDispatchTable.erase( device );
GetMemoryWin32HandlePropertiesKHRDispatchTable.erase( device );
}
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHR(
VkDevice device,
const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo,
HANDLE* pHandle
){
auto dispatched_cmd = GetMemoryWin32HandleKHRDispatchTable.at( device );
return dispatched_cmd( device, pGetWin32HandleInfo, pHandle );
}
VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR(
VkDevice device,
VkExternalMemoryHandleTypeFlagBits handleType,
HANDLE handle,
VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties
){
auto dispatched_cmd = GetMemoryWin32HandlePropertiesKHRDispatchTable.at( device );
return dispatched_cmd( device, handleType, handle, pMemoryWin32HandleProperties );
}
#endif
// VK_KHR_dedicated_allocation
///////////////////////////////////////////
void loadDedicatedAllocationCommands( VkDevice ){
// no commands
}
void unloadDedicatedAllocationCommands( VkDevice ){
// no commands
}
#endif //EXTENSION_LOADER_H
================================================
FILE: src/HelloTriangle.cpp
================================================
// Vulkan hello world triangle rendering demo
// Global header settings
//////////////////////////////////////////////////////////////////////////////////
#include "VulkanEnvironment.h" // first include must be before vulkan.h and platform header
// Includes
//////////////////////////////////////////////////////////////////////////////////
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fstream>
#include <functional>
#include <iterator>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <vulkan/vulkan.h> // also assume core+WSI commands are loaded
static_assert( VK_HEADER_VERSION >= REQUIRED_HEADER_VERSION, "Update your SDK! This app is written against Vulkan header version " STRINGIZE(REQUIRED_HEADER_VERSION) "." );
#include "EnumerateScheme.h"
#include "ErrorHandling.h"
#include "ExtensionLoader.h"
#include "Vertex.h"
#include "Wsi.h"
using std::exception;
using std::runtime_error;
using std::string;
using std::to_string;
using std::vector;
// Config
//////////////////////////////////////////////////////////////////////////////////
const char appName[] = u8"Hello Vulkan Triangle";
// layers and debug
#if VULKAN_VALIDATION
constexpr VkDebugUtilsMessageSeverityFlagsEXT debugSeverity =
0
//| VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
//| VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
;
constexpr VkDebugUtilsMessageTypeFlagsEXT debugType =
0
| VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
;
constexpr bool useAssistantLayer = false;
#endif
constexpr bool fpsCounter = true;
// window and swapchain
constexpr uint32_t initialWindowWidth = 800;
constexpr uint32_t initialWindowHeight = 800;
//constexpr VkPresentModeKHR presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; // better not be used often because of coil whine
constexpr VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
//constexpr VkPresentModeKHR presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
// pipeline settings
constexpr VkClearValue clearColor = { { {0.1f, 0.1f, 0.1f, 1.0f} } };
// Makes present queue from different Queue Family than Graphics, for testing purposes
constexpr bool forceSeparatePresentQueue = false;
// needed stuff for main() -- forward declarations
//////////////////////////////////////////////////////////////////////////////////
bool isLayerSupported( const char* layer, const vector<VkLayerProperties>& supportedLayers );
bool isExtensionSupported( const char* extension, const vector<VkExtensionProperties>& supportedExtensions );
// treat layers as optional; app can always run without em -- i.e. return those supported
vector<const char*> checkInstanceLayerSupport( const vector<const char*>& requestedLayers, const vector<VkLayerProperties>& supportedLayers );
vector<VkExtensionProperties> getSupportedInstanceExtensions( const vector<const char*>& providingLayers );
bool checkExtensionSupport( const vector<const char*>& extensions, const vector<VkExtensionProperties>& supportedExtensions );
VkInstance initInstance( const vector<const char*>& layers = {}, const vector<const char*>& extensions = {} );
void killInstance( VkInstance instance );
VkPhysicalDevice getPhysicalDevice( VkInstance instance, VkSurfaceKHR surface = VK_NULL_HANDLE /*seek presentation support if !NULL*/ ); // destroyed with instance
VkPhysicalDeviceProperties getPhysicalDeviceProperties( VkPhysicalDevice physicalDevice );
VkPhysicalDeviceMemoryProperties getPhysicalDeviceMemoryProperties( VkPhysicalDevice physicalDevice );
std::pair<uint32_t, uint32_t> getQueueFamilies( VkPhysicalDevice physDevice, VkSurfaceKHR surface );
vector<VkQueueFamilyProperties> getQueueFamilyProperties( VkPhysicalDevice device );
VkDevice initDevice(
VkPhysicalDevice physDevice,
const VkPhysicalDeviceFeatures& features,
uint32_t graphicsQueueFamily,
uint32_t presentQueueFamily,
const vector<const char*>& layers = {},
const vector<const char*>& extensions = {}
);
void killDevice( VkDevice device );
VkQueue getQueue( VkDevice device, uint32_t queueFamily, uint32_t queueIndex );
enum class ResourceType{ Buffer, Image };
template< ResourceType resourceType, class T >
VkDeviceMemory initMemory(
VkDevice device,
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties,
T resource,
const std::vector<VkMemoryPropertyFlags>& memoryTypePriority
);
void setMemoryData( VkDevice device, VkDeviceMemory memory, void* begin, size_t size );
void killMemory( VkDevice device, VkDeviceMemory memory );
VkBuffer initBuffer( VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage );
void killBuffer( VkDevice device, VkBuffer buffer );
VkImage initImage(
VkDevice device,
VkFormat format,
uint32_t width, uint32_t height,
VkSampleCountFlagBits samples,
VkImageUsageFlags usage
);
void killImage( VkDevice device, VkImage image );
VkImageView initImageView( VkDevice device, VkImage image, VkFormat format );
void killImageView( VkDevice device, VkImageView imageView );
// initSurface() is platform dependent
void killSurface( VkInstance instance, VkSurfaceKHR surface );
VkSurfaceCapabilitiesKHR getSurfaceCapabilities( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface );
VkSurfaceFormatKHR getSurfaceFormat( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface );
VkSwapchainKHR initSwapchain(
VkPhysicalDevice physicalDevice,
VkDevice device,
VkSurfaceKHR surface,
VkSurfaceFormatKHR surfaceFormat,
VkSurfaceCapabilitiesKHR capabilities,
uint32_t graphicsQueueFamily,
uint32_t presentQueueFamily,
VkSwapchainKHR oldSwapchain = VK_NULL_HANDLE
);
void killSwapchain( VkDevice device, VkSwapchainKHR swapchain );
uint32_t getNextImageIndex( VkDevice device, VkSwapchainKHR swapchain, VkSemaphore imageReadyS );
vector<VkImageView> initSwapchainImageViews( VkDevice device, vector<VkImage> images, VkFormat format );
void killSwapchainImageViews( VkDevice device, vector<VkImageView>& imageViews );
VkRenderPass initRenderPass( VkDevice device, VkSurfaceFormatKHR surfaceFormat );
void killRenderPass( VkDevice device, VkRenderPass renderPass );
vector<VkFramebuffer> initFramebuffers(
VkDevice device,
VkRenderPass renderPass,
vector<VkImageView> imageViews,
uint32_t width, uint32_t height
);
void killFramebuffers( VkDevice device, vector<VkFramebuffer>& framebuffers );
VkShaderModule initShaderModule( VkDevice device, const vector<uint32_t>& shaderCode );
VkShaderModule initShaderModule( VkDevice device, string filename );
void killShaderModule( VkDevice device, VkShaderModule shaderModule );
VkPipelineLayout initPipelineLayout( VkDevice device );
void killPipelineLayout( VkDevice device, VkPipelineLayout pipelineLayout );
VkPipeline initPipeline(
VkDevice device,
VkPhysicalDeviceLimits limits,
VkPipelineLayout pipelineLayout,
VkRenderPass renderPass,
VkShaderModule vertexShader,
VkShaderModule fragmentShader,
const uint32_t vertexBufferBinding,
uint32_t width, uint32_t height
);
void killPipeline( VkDevice device, VkPipeline pipeline );
void setVertexData( VkDevice device, VkDeviceMemory memory, vector<Vertex2D_ColorF_pack> vertices );
VkSemaphore initSemaphore( VkDevice device );
vector<VkSemaphore> initSemaphores( VkDevice device, size_t count );
void killSemaphore( VkDevice device, VkSemaphore semaphore );
void killSemaphores( VkDevice device, vector<VkSemaphore>& semaphores );
VkCommandPool initCommandPool( VkDevice device, const uint32_t queueFamily );
void killCommandPool( VkDevice device, VkCommandPool commandPool );
vector<VkFence> initFences( VkDevice device, size_t count, VkFenceCreateFlags flags = 0 );
void killFences( VkDevice device, vector<VkFence>& fences );
void acquireCommandBuffers( VkDevice device, VkCommandPool commandPool, uint32_t count, vector<VkCommandBuffer>& commandBuffers );
void beginCommandBuffer( VkCommandBuffer commandBuffer );
void endCommandBuffer( VkCommandBuffer commandBuffer );
void recordBeginRenderPass(
VkCommandBuffer commandBuffer,
VkRenderPass renderPass,
VkFramebuffer framebuffer,
VkClearValue clearValue,
uint32_t width, uint32_t height
);
void recordEndRenderPass( VkCommandBuffer commandBuffer );
void recordBindPipeline( VkCommandBuffer commandBuffer, VkPipeline pipeline );
void recordBindVertexBuffer( VkCommandBuffer commandBuffer, const uint32_t vertexBufferBinding, VkBuffer vertexBuffer );
void recordDraw( VkCommandBuffer commandBuffer, uint32_t vertexCount );
void submitToQueue( VkQueue queue, VkCommandBuffer commandBuffer, VkSemaphore imageReadyS, VkSemaphore renderDoneS, VkFence fence = VK_NULL_HANDLE );
void present( VkQueue queue, VkSwapchainKHR swapchain, uint32_t swapchainImageIndex, VkSemaphore renderDoneS );
// cleanup dangerous semaphore with signal pending from vkAcquireNextImageKHR
void cleanupUnsafeSemaphore( VkQueue queue, VkSemaphore semaphore );
// main()!
//////////////////////////////////////////////////////////////////////////////////
int helloTriangle() try{
const uint32_t vertexBufferBinding = 0;
const float triangleSize = 1.6f;
const vector<Vertex2D_ColorF_pack> triangle = {
{ /*rb*/ { { 0.5f * triangleSize, sqrtf( 3.0f ) * 0.25f * triangleSize} }, /*R*/{ {1.0f, 0.0f, 0.0f} } },
{ /* t*/ { { 0.0f, -sqrtf( 3.0f ) * 0.25f * triangleSize} }, /*G*/{ {0.0f, 1.0f, 0.0f} } },
{ /*lb*/ { {-0.5f * triangleSize, sqrtf( 3.0f ) * 0.25f * triangleSize} }, /*B*/{ {0.0f, 0.0f, 1.0f} } }
};
const auto supportedLayers = enumerate<VkInstance, VkLayerProperties>();
vector<const char*> requestedLayers;
#if VULKAN_VALIDATION
if( isLayerSupported( "VK_LAYER_KHRONOS_validation", supportedLayers ) ) requestedLayers.push_back( "VK_LAYER_KHRONOS_validation" );
else throw "VULKAN_VALIDATION is enabled but VK_LAYER_KHRONOS_validation layers are not supported!";
if( ::useAssistantLayer ){
if( isLayerSupported( "VK_LAYER_LUNARG_assistant_layer", supportedLayers ) ) requestedLayers.push_back( "VK_LAYER_LUNARG_assistant_layer" );
else throw "VULKAN_VALIDATION is enabled but VK_LAYER_LUNARG_assistant_layer layer is not supported!";
}
#endif
if( ::fpsCounter ) requestedLayers.push_back( "VK_LAYER_LUNARG_monitor" );
requestedLayers = checkInstanceLayerSupport( requestedLayers, supportedLayers );
const auto supportedInstanceExtensions = getSupportedInstanceExtensions( requestedLayers );
const auto platformSurfaceExtension = getPlatformSurfaceExtensionName();
vector<const char*> requestedInstanceExtensions = {
VK_KHR_SURFACE_EXTENSION_NAME,
platformSurfaceExtension.c_str()
};
#if VULKAN_VALIDATION
DebugObjectType debugExtensionTag;
if( isExtensionSupported( VK_EXT_DEBUG_UTILS_EXTENSION_NAME, supportedInstanceExtensions ) ){
debugExtensionTag = DebugObjectType::debugUtils;
requestedInstanceExtensions.push_back( VK_EXT_DEBUG_UTILS_EXTENSION_NAME );
}
else if( isExtensionSupported( VK_EXT_DEBUG_REPORT_EXTENSION_NAME, supportedInstanceExtensions ) ){
debugExtensionTag = DebugObjectType::debugReport;
requestedInstanceExtensions.push_back( VK_EXT_DEBUG_REPORT_EXTENSION_NAME );
}
else throw "VULKAN_VALIDATION is enabled but neither VK_EXT_debug_utils nor VK_EXT_debug_report extension is supported!";
#endif
checkExtensionSupport( requestedInstanceExtensions, supportedInstanceExtensions );
const VkInstance instance = initInstance( requestedLayers, requestedInstanceExtensions );
#if VULKAN_VALIDATION
const auto debugHandle = initDebug( instance, debugExtensionTag, ::debugSeverity, ::debugType );
const int32_t uncoded = 0;
const char* introMsg = "Validation Layers are enabled!";
if( debugExtensionTag == DebugObjectType::debugUtils ){
VkDebugUtilsObjectNameInfoEXT object = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
nullptr, // pNext
VK_OBJECT_TYPE_INSTANCE,
handleToUint64(instance),
"instance"
};
const VkDebugUtilsMessengerCallbackDataEXT dumcd = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT,
nullptr, // pNext
0, // flags
"VULKAN_VALIDATION", // VUID
0, // VUID hash
introMsg,
0, nullptr, 0, nullptr,
1, &object
};
vkSubmitDebugUtilsMessageEXT( instance, VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT, VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, &dumcd );
}
else if( debugExtensionTag == DebugObjectType::debugReport ){
vkDebugReportMessageEXT( instance, VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, (uint64_t)instance, __LINE__, uncoded, "Application", introMsg );
}
#endif
const PlatformWindow window = initWindow( ::appName, ::initialWindowWidth, ::initialWindowHeight );
const VkSurfaceKHR surface = initSurface( instance, window );
const VkPhysicalDevice physicalDevice = getPhysicalDevice( instance, surface );
const VkPhysicalDeviceProperties physicalDeviceProperties = getPhysicalDeviceProperties( physicalDevice );
const VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties = getPhysicalDeviceMemoryProperties( physicalDevice );
uint32_t graphicsQueueFamily, presentQueueFamily;
std::tie( graphicsQueueFamily, presentQueueFamily ) = getQueueFamilies( physicalDevice, surface );
const VkPhysicalDeviceFeatures features = {}; // don't need any special feature for this demo
const vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
const VkDevice device = initDevice( physicalDevice, features, graphicsQueueFamily, presentQueueFamily, requestedLayers, deviceExtensions );
const VkQueue graphicsQueue = getQueue( device, graphicsQueueFamily, 0 );
const VkQueue presentQueue = getQueue( device, presentQueueFamily, 0 );
VkSurfaceFormatKHR surfaceFormat = getSurfaceFormat( physicalDevice, surface );
VkRenderPass renderPass = initRenderPass( device, surfaceFormat );
vector<uint32_t> vertexShaderBinary = {
#include "shaders/hello_triangle.vert.spv.inl"
};
vector<uint32_t> fragmentShaderBinary = {
#include "shaders/hello_triangle.frag.spv.inl"
};
VkShaderModule vertexShader = initShaderModule( device, vertexShaderBinary );
VkShaderModule fragmentShader = initShaderModule( device, fragmentShaderBinary );
VkPipelineLayout pipelineLayout = initPipelineLayout( device );
VkBuffer vertexBuffer = initBuffer( device, sizeof( decltype( triangle )::value_type ) * triangle.size(), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT );
const std::vector<VkMemoryPropertyFlags> memoryTypePriority{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, // preferably wanna device-side memory that can be updated from host without hassle
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT // guaranteed to allways be supported
};
VkDeviceMemory vertexBufferMemory = initMemory<ResourceType::Buffer>(
device,
physicalDeviceMemoryProperties,
vertexBuffer,
memoryTypePriority
);
setVertexData( device, vertexBufferMemory, triangle ); // Writes throug memory map. Synchronization is implicit for any subsequent vkQueueSubmit batches.
VkCommandPool commandPool = initCommandPool( device, graphicsQueueFamily );
// might need synchronization if init is more advanced than this
//VkResult errorCode = vkDeviceWaitIdle( device ); RESULT_HANDLER( errorCode, "vkDeviceWaitIdle" );
// place-holder swapchain dependent objects
VkSwapchainKHR swapchain = VK_NULL_HANDLE; // has to be NULL -- signifies that there's no swapchain
vector<VkImageView> swapchainImageViews;
vector<VkFramebuffer> framebuffers;
VkPipeline pipeline = VK_NULL_HANDLE; // has to be NULL for the case the app ends before even first swapchain
vector<VkCommandBuffer> commandBuffers;
vector<VkSemaphore> imageReadySs;
vector<VkSemaphore> renderDoneSs;
// workaround for validation layer "memory leak" + might also help the driver to cleanup old resources
// this should not be needed for a real-word app, because they are likely to use fences naturaly (e.g. responding to user input )
// read https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/issues/1628
const uint32_t maxInflightSubmissions = 2; // more than 2 probably does not make much sense
uint32_t submissionNr = 0; // index of the current submission modulo maxInflightSubmission
vector<VkFence> submissionFences;
const std::function<bool(void)> recreateSwapchain = [&](){
// swapchain recreation -- will be done before the first frame too;
TODO( "This may be triggered from many sources (e.g. WM_SIZE event, and VK_ERROR_OUT_OF_DATE_KHR too). Should prevent duplicate swapchain recreation." )
const VkSwapchainKHR oldSwapchain = swapchain;
swapchain = VK_NULL_HANDLE;
VkSurfaceCapabilitiesKHR capabilities = getSurfaceCapabilities( physicalDevice, surface );
if( capabilities.currentExtent.width == UINT32_MAX && capabilities.currentExtent.height == UINT32_MAX ){
capabilities.currentExtent.width = getWindowWidth( window );
capabilities.currentExtent.height = getWindowHeight( window );
}
VkExtent2D surfaceSize = { capabilities.currentExtent.width, capabilities.currentExtent.height };
const bool swapchainCreatable = {
surfaceSize.width >= capabilities.minImageExtent.width
&& surfaceSize.width <= capabilities.maxImageExtent.width
&& surfaceSize.width > 0
&& surfaceSize.height >= capabilities.minImageExtent.height
&& surfaceSize.height <= capabilities.maxImageExtent.height
&& surfaceSize.height > 0
};
// cleanup old
vector<VkSemaphore> oldImageReadySs = imageReadySs; imageReadySs.clear();
if( oldSwapchain ){
{VkResult errorCode = vkDeviceWaitIdle( device ); RESULT_HANDLER( errorCode, "vkDeviceWaitIdle" );}
// fences might be in unsignaled state, so kill them too to get fresh signaled
killFences( device, submissionFences );
// semaphores might be in signaled state, so kill them too to get fresh unsignaled
killSemaphores( device, renderDoneSs );
// kill imageReadySs later when oldSwapchain is destroyed
// only reset + later reuse already allocated and create new only if needed
{VkResult errorCode = vkResetCommandPool( device, commandPool, 0 ); RESULT_HANDLER( errorCode, "vkResetCommandPool" );}
killPipeline( device, pipeline );
killFramebuffers( device, framebuffers );
killSwapchainImageViews( device, swapchainImageViews );
// kill oldSwapchain later, after it is potentially used by vkCreateSwapchainKHR
}
// creating new
if( swapchainCreatable ){
// reuses & destroys the oldSwapchain
swapchain = initSwapchain( physicalDevice, device, surface, surfaceFormat, capabilities, graphicsQueueFamily, presentQueueFamily, oldSwapchain );
vector<VkImage> swapchainImages = enumerate<VkImage>( device, swapchain );
swapchainImageViews = initSwapchainImageViews( device, swapchainImages, surfaceFormat.format );
framebuffers = initFramebuffers( device, renderPass, swapchainImageViews, surfaceSize.width, surfaceSize.height );
pipeline = initPipeline(
device,
physicalDeviceProperties.limits,
pipelineLayout,
renderPass,
vertexShader,
fragmentShader,
vertexBufferBinding,
surfaceSize.width, surfaceSize.height
);
acquireCommandBuffers( device, commandPool, static_cast<uint32_t>( swapchainImages.size() ), commandBuffers );
for( size_t i = 0; i < swapchainImages.size(); ++i ){
beginCommandBuffer( commandBuffers[i] );
recordBeginRenderPass( commandBuffers[i], renderPass, framebuffers[i], ::clearColor, surfaceSize.width, surfaceSize.height );
recordBindPipeline( commandBuffers[i], pipeline );
recordBindVertexBuffer( commandBuffers[i], vertexBufferBinding, vertexBuffer );
recordDraw( commandBuffers[i], static_cast<uint32_t>( triangle.size() ) );
recordEndRenderPass( commandBuffers[i] );
endCommandBuffer( commandBuffers[i] );
}
imageReadySs = initSemaphores( device, maxInflightSubmissions );
// per https://github.com/KhronosGroup/Vulkan-Docs/issues/1150 need upto swapchain-image count
renderDoneSs = initSemaphores( device, swapchainImages.size());
submissionFences = initFences( device, maxInflightSubmissions, VK_FENCE_CREATE_SIGNALED_BIT ); // signaled fence means previous execution finished, so we start rendering presignaled
submissionNr = 0;
}
if( oldSwapchain ){
killSwapchain( device, oldSwapchain );
// per current spec, we can't really be sure these are not used :/ at least kill them after the swapchain
// https://github.com/KhronosGroup/Vulkan-Docs/issues/152
killSemaphores( device, oldImageReadySs );
}
return swapchain != VK_NULL_HANDLE;
};
// Finally, rendering! Yay!
const std::function<void(void)> render = [&](){
assert( swapchain ); // should be always true; should have yielded CPU if false
// vkAcquireNextImageKHR produces unsafe semaphore that needs extra cleanup. Track that with this variable.
bool unsafeSemaphore = false;
try{
// remove oldest frame from being in flight before starting new one
// refer to doc/, which talks about the cycle of how the synch primitives are (re)used here
{VkResult errorCode = vkWaitForFences( device, 1, &submissionFences[submissionNr], VK_TRUE, UINT64_MAX ); RESULT_HANDLER( errorCode, "vkWaitForFences" );}
{VkResult errorCode = vkResetFences( device, 1, &submissionFences[submissionNr] ); RESULT_HANDLER( errorCode, "vkResetFences" );}
unsafeSemaphore = true;
uint32_t nextSwapchainImageIndex = getNextImageIndex( device, swapchain, imageReadySs[submissionNr] );
unsafeSemaphore = false;
submitToQueue( graphicsQueue, commandBuffers[nextSwapchainImageIndex], imageReadySs[submissionNr], renderDoneSs[nextSwapchainImageIndex], submissionFences[submissionNr] );
present( presentQueue, swapchain, nextSwapchainImageIndex, renderDoneSs[nextSwapchainImageIndex] );
submissionNr = (submissionNr + 1) % maxInflightSubmissions;
}
catch( VulkanResultException ex ){
if( ex.result == VK_SUBOPTIMAL_KHR || ex.result == VK_ERROR_OUT_OF_DATE_KHR ){
if( unsafeSemaphore && ex.result == VK_SUBOPTIMAL_KHR ){
cleanupUnsafeSemaphore( graphicsQueue, imageReadySs[submissionNr] );
// no way to sanitize vkQueuePresentKHR semaphores, really
}
recreateSwapchain();
// we need to start over...
render();
}
else throw;
}
};
setSizeEventHandler( recreateSwapchain );
setPaintEventHandler( render );
// Finally start the main message loop (and so render too)
showWindow( window );
int exitStatus = messageLoop( window );
// proper Vulkan cleanup
VkResult errorCode = vkDeviceWaitIdle( device ); RESULT_HANDLER( errorCode, "vkDeviceWaitIdle" );
// kill swapchain
killSemaphores( device, renderDoneSs );
// imageReadySs killed after the swapchain
// command buffers killed with pool
killPipeline( device, pipeline );
killFramebuffers( device, framebuffers );
killSwapchainImageViews( device, swapchainImageViews );
killSwapchain( device, swapchain );
// per current spec, we can't really be sure these are not used :/ at least kill them after the swapchain
// https://github.com/KhronosGroup/Vulkan-Docs/issues/152
killSemaphores( device, imageReadySs );
// kill vulkan
killFences( device, submissionFences );
killCommandPool( device, commandPool );
killBuffer( device, vertexBuffer );
killMemory( device, vertexBufferMemory );
killPipelineLayout( device, pipelineLayout );
killShaderModule( device, fragmentShader );
killShaderModule( device, vertexShader );
killRenderPass( device, renderPass );
killDevice( device );
killSurface( instance, surface );
killWindow( window );
#if VULKAN_VALIDATION
killDebug( instance, debugHandle );
#endif
killInstance( instance );
return exitStatus;
}
catch( VulkanResultException vkE ){
logger << "ERROR: Terminated due to an uncaught VkResult exception: "
<< vkE.file << ":" << vkE.line << ":" << vkE.func << "() " << vkE.source << "() returned " << to_string( vkE.result )
<< std::endl;
return EXIT_FAILURE;
}
catch( const char* e ){
logger << "ERROR: Terminated due to an uncaught exception: " << e << std::endl;
return EXIT_FAILURE;
}
catch( string e ){
logger << "ERROR: Terminated due to an uncaught exception: " << e << std::endl;
return EXIT_FAILURE;
}
catch( std::exception e ){
logger << "ERROR: Terminated due to an uncaught exception: " << e.what() << std::endl;
return EXIT_FAILURE;
}
catch( ... ){
logger << "ERROR: Terminated due to an unrecognized uncaught exception." << std::endl;
return EXIT_FAILURE;
}
#if defined(_WIN32) && !defined(_CONSOLE)
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ){
return helloTriangle();
}
#else
int main(){
return helloTriangle();
}
#endif
// Implementation
//////////////////////////////////////////////////////////////////////////////////
bool isLayerSupported( const char* layer, const vector<VkLayerProperties>& supportedLayers ){
const auto isSupportedPred = [layer]( const VkLayerProperties& prop ) -> bool{
return std::strcmp( layer, prop.layerName ) == 0;
};
return std::any_of( supportedLayers.begin(), supportedLayers.end(), isSupportedPred );
}
bool isExtensionSupported( const char* extension, const vector<VkExtensionProperties>& supportedExtensions ){
const auto isSupportedPred = [extension]( const VkExtensionProperties& prop ) -> bool{
return std::strcmp( extension, prop.extensionName ) == 0;
};
return std::any_of( supportedExtensions.begin(), supportedExtensions.end(), isSupportedPred );
}
vector<const char*> checkInstanceLayerSupport( const vector<const char*>& requestedLayers, const vector<VkLayerProperties>& supportedLayers ){
vector<const char*> compiledLayerList;
for( const auto layer : requestedLayers ){
if( isLayerSupported( layer, supportedLayers ) ) compiledLayerList.push_back( layer );
else logger << "WARNING: Requested layer " << layer << " is not supported. It will not be enabled." << std::endl;
}
return compiledLayerList;
}
vector<const char*> checkInstanceLayerSupport( const vector<const char*>& optionalLayers ){
return checkInstanceLayerSupport( optionalLayers, enumerate<VkInstance, VkLayerProperties>() );
}
vector<VkExtensionProperties> getSupportedInstanceExtensions( const vector<const char*>& providingLayers ){
auto supportedExtensions = enumerate<VkInstance, VkExtensionProperties>();
for( const auto pl : providingLayers ){
const auto providedExtensions = enumerate<VkInstance, VkExtensionProperties>( pl );
supportedExtensions.insert( supportedExtensions.end(), providedExtensions.begin(), providedExtensions.end() );
}
return supportedExtensions;
}
vector<VkExtensionProperties> getSupportedDeviceExtensions( const VkPhysicalDevice physDevice, const vector<const char*>& providingLayers ){
auto supportedExtensions = enumerate<VkExtensionProperties>( physDevice );
for( const auto pl : providingLayers ){
const auto providedExtensions = enumerate<VkExtensionProperties>( physDevice, pl );
supportedExtensions.insert( supportedExtensions.end(), providedExtensions.begin(), providedExtensions.end() );
}
return supportedExtensions;
}
bool checkExtensionSupport( const vector<const char*>& extensions, const vector<VkExtensionProperties>& supportedExtensions ){
bool allSupported = true;
for( const auto extension : extensions ){
if( !isExtensionSupported( extension, supportedExtensions ) ){
allSupported = false;
logger << "WARNING: Requested extension " << extension << " is not supported. Trying to enable it will likely fail." << std::endl;
}
}
return allSupported;
}
bool checkDeviceExtensionSupport( const VkPhysicalDevice physDevice, const vector<const char*>& extensions, const vector<const char*>& providingLayers ){
return checkExtensionSupport( extensions, getSupportedDeviceExtensions( physDevice, providingLayers ) );
}
VkInstance initInstance( const vector<const char*>& layers, const vector<const char*>& extensions ){
const VkApplicationInfo appInfo = {
VK_STRUCTURE_TYPE_APPLICATION_INFO,
nullptr, // pNext
::appName, // Nice to meetcha, and what's your name driver?
0, // app version
nullptr, // engine name
0, // engine version
VK_API_VERSION_1_0 // this app is written against the Vulkan 1.0 spec
};
#if VULKAN_VALIDATION
// in effect during vkCreateInstance and vkDestroyInstance duration (because callback object cannot be created without instance)
const VkDebugReportCallbackCreateInfoEXT debugReportCreateInfo{
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
nullptr, // pNext
translateFlags( ::debugSeverity, ::debugType ),
::genericDebugReportCallback,
nullptr // pUserData
};
const VkDebugUtilsMessengerCreateInfoEXT debugUtilsCreateInfo = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
nullptr, // pNext
0, // flags
debugSeverity,
debugType,
::genericDebugUtilsCallback,
nullptr // pUserData
};
bool debugUtils = std::find_if( extensions.begin(), extensions.end(), [](const char* e){ return std::strcmp( e, VK_EXT_DEBUG_UTILS_EXTENSION_NAME ) == 0; } ) != extensions.end();
bool debugReport = std::find_if( extensions.begin(), extensions.end(), [](const char* e){ return std::strcmp( e, VK_EXT_DEBUG_REPORT_EXTENSION_NAME ) == 0; } ) != extensions.end();
if( !debugUtils && !debugReport ) throw "VULKAN_VALIDATION is enabled but neither VK_EXT_debug_utils nor VK_EXT_debug_report extension is being enabled!";
const void* debugpNext = debugUtils ? (void*)&debugUtilsCreateInfo : (void*)&debugReportCreateInfo;
#endif
const VkInstanceCreateInfo instanceInfo{
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
#if VULKAN_VALIDATION
debugpNext,
#else
nullptr, // pNext
#endif
0, // flags - reserved for future use
&appInfo,
static_cast<uint32_t>( layers.size() ),
layers.data(),
static_cast<uint32_t>( extensions.size() ),
extensions.data()
};
VkInstance instance;
const VkResult errorCode = vkCreateInstance( &instanceInfo, nullptr, &instance ); RESULT_HANDLER( errorCode, "vkCreateInstance" );
loadInstanceExtensionsCommands( instance, extensions );
return instance;
}
void killInstance( const VkInstance instance ){
unloadInstanceExtensionsCommands( instance );
vkDestroyInstance( instance, nullptr );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool isPresentationSupported( const VkPhysicalDevice physDevice, const uint32_t queueFamily, const VkSurfaceKHR surface ){
VkBool32 supported;
const VkResult errorCode = vkGetPhysicalDeviceSurfaceSupportKHR( physDevice, queueFamily, surface, &supported ); RESULT_HANDLER( errorCode, "vkGetPhysicalDeviceSurfaceSupportKHR" );
return supported == VK_TRUE;
}
bool isPresentationSupported( const VkPhysicalDevice physDevice, const VkSurfaceKHR surface ){
uint32_t qfCount;
vkGetPhysicalDeviceQueueFamilyProperties( physDevice, &qfCount, nullptr );
for( uint32_t qf = 0; qf < qfCount; ++qf ){
if( isPresentationSupported( physDevice, qf, surface ) ) return true;
}
return false;
}
VkPhysicalDevice getPhysicalDevice( const VkInstance instance, const VkSurfaceKHR surface ){
vector<VkPhysicalDevice> devices = enumerate<VkPhysicalDevice>( instance );
if( surface ){
for( auto it = devices.begin(); it != devices.end(); ){
const auto& pd = *it;
if( !isPresentationSupported( pd, surface ) ) it = devices.erase( it );
else ++it;
}
}
if( devices.empty() ) throw string("ERROR: No Physical Devices (GPUs) ") + (surface ? "with presentation support " : "") + "detected!";
else if( devices.size() == 1 ){
return devices[0];
}
else{
for( const auto pd : devices ){
const VkPhysicalDeviceProperties pdp = getPhysicalDeviceProperties( pd );
if( pdp.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU ){
#if VULKAN_VALIDATION
vkDebugReportMessageEXT(
instance, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, handleToUint64(instance), __LINE__,
1, u8"application", u8"More than one Physical Devices (GPU) found. Choosing the first dedicated one."
);
#endif
return pd;
}
}
#if VULKAN_VALIDATION
vkDebugReportMessageEXT(
instance, VK_DEBUG_REPORT_WARNING_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT, handleToUint64(instance), __LINE__,
1, u8"application", u8"More than one Physical Devices (GPU) found. Just choosing the first one."
);
#endif
return devices[0];
}
}
VkPhysicalDeviceProperties getPhysicalDeviceProperties( VkPhysicalDevice physicalDevice ){
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties( physicalDevice, &properties );
return properties;
}
VkPhysicalDeviceMemoryProperties getPhysicalDeviceMemoryProperties( VkPhysicalDevice physicalDevice ){
VkPhysicalDeviceMemoryProperties memoryInfo;
vkGetPhysicalDeviceMemoryProperties( physicalDevice, &memoryInfo );
return memoryInfo;
}
vector<VkQueueFamilyProperties> getQueueFamilyProperties( VkPhysicalDevice device ){
uint32_t queueFamiliesCount;
vkGetPhysicalDeviceQueueFamilyProperties( device, &queueFamiliesCount, nullptr );
vector<VkQueueFamilyProperties> queueFamilies( queueFamiliesCount );
vkGetPhysicalDeviceQueueFamilyProperties( device, &queueFamiliesCount, queueFamilies.data() );
return queueFamilies;
}
std::pair<uint32_t, uint32_t> getQueueFamilies( const VkPhysicalDevice physDevice, const VkSurfaceKHR surface ){
constexpr uint32_t notFound = VK_QUEUE_FAMILY_IGNORED;
const auto qfps = getQueueFamilyProperties( physDevice );
const auto findQueueFamilyThat = [&qfps, notFound](std::function<bool (const VkQueueFamilyProperties&, const uint32_t)> predicate) -> uint32_t{
for( uint32_t qf = 0; qf < qfps.size(); ++qf ) if( predicate(qfps[qf], qf) ) return qf;
return notFound;
};
const auto isGraphics = [](const VkQueueFamilyProperties& props, const uint32_t = 0){
return props.queueFlags & VK_QUEUE_GRAPHICS_BIT;
};
const auto isPresent = [=](const VkQueueFamilyProperties&, const uint32_t queueFamily){
return isPresentationSupported( physDevice, queueFamily, surface );
};
const auto isFusedGraphicsAndPresent = [=](const VkQueueFamilyProperties& props, const uint32_t queueFamily){
return isGraphics( props ) && isPresent( props, queueFamily );
};
uint32_t graphicsQueueFamily = notFound;
uint32_t presentQueueFamily = notFound;
if( ::forceSeparatePresentQueue ){
graphicsQueueFamily = findQueueFamilyThat( isGraphics );
const auto isSeparatePresent = [graphicsQueueFamily, isPresent](const VkQueueFamilyProperties& props, const uint32_t queueFamily){
return queueFamily != graphicsQueueFamily && isPresent( props, queueFamily );
};
presentQueueFamily = findQueueFamilyThat( isSeparatePresent );
}
else{
graphicsQueueFamily = presentQueueFamily = findQueueFamilyThat( isFusedGraphicsAndPresent );
if( graphicsQueueFamily == notFound || presentQueueFamily == notFound ){
graphicsQueueFamily = findQueueFamilyThat( isGraphics );
presentQueueFamily = findQueueFamilyThat( isPresent );
}
}
if( graphicsQueueFamily == notFound ) throw "Cannot find a graphics queue family!";
if( presentQueueFamily == notFound ) throw "Cannot find a presentation queue family!";
return std::make_pair( graphicsQueueFamily, presentQueueFamily );
}
VkDevice initDevice(
const VkPhysicalDevice physDevice,
const VkPhysicalDeviceFeatures& features,
const uint32_t graphicsQueueFamily,
const uint32_t presentQueueFamily,
const vector<const char*>& layers,
const vector<const char*>& extensions
){
checkDeviceExtensionSupport( physDevice, extensions, layers );
const float priority[] = {1.0f};
vector<VkDeviceQueueCreateInfo> queues = {
{
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
nullptr, // pNext
0, // flags
graphicsQueueFamily,
1, // queue count
priority
}
};
if( presentQueueFamily != graphicsQueueFamily ){
queues.push_back({
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
nullptr, // pNext
0, // flags
presentQueueFamily,
1, // queue count
priority
});
}
const VkDeviceCreateInfo deviceInfo{
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
nullptr, // pNext
0, // flags
static_cast<uint32_t>( queues.size() ),
queues.data(),
static_cast<uint32_t>( layers.size() ),
layers.data(),
static_cast<uint32_t>( extensions.size() ),
extensions.data(),
&features
};
VkDevice device;
const VkResult errorCode = vkCreateDevice( physDevice, &deviceInfo, nullptr, &device ); RESULT_HANDLER( errorCode, "vkCreateDevice" );
loadDeviceExtensionsCommands( device, extensions );
return device;
}
void killDevice( const VkDevice device ){
unloadDeviceExtensionsCommands( device );
vkDestroyDevice( device, nullptr );
}
VkQueue getQueue( const VkDevice device, const uint32_t queueFamily, const uint32_t queueIndex ){
VkQueue queue;
vkGetDeviceQueue( device, queueFamily, queueIndex, &queue );
return queue;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template< ResourceType resourceType, class T >
VkMemoryRequirements getMemoryRequirements( VkDevice device, T resource );
template<>
VkMemoryRequirements getMemoryRequirements< ResourceType::Buffer >( VkDevice device, VkBuffer buffer ){
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements( device, buffer, &memoryRequirements );
return memoryRequirements;
}
template<>
VkMemoryRequirements getMemoryRequirements< ResourceType::Image >( VkDevice device, VkImage image ){
VkMemoryRequirements memoryRequirements;
vkGetImageMemoryRequirements( device, image, &memoryRequirements );
return memoryRequirements;
}
template< ResourceType resourceType, class T >
void bindMemory( VkDevice device, T buffer, VkDeviceMemory memory, VkDeviceSize offset );
template<>
void bindMemory< ResourceType::Buffer >( VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize offset ){
VkResult errorCode = vkBindBufferMemory( device, buffer, memory, offset ); RESULT_HANDLER( errorCode, "vkBindBufferMemory" );
}
template<>
void bindMemory< ResourceType::Image >( VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize offset ){
VkResult errorCode = vkBindImageMemory( device, image, memory, offset ); RESULT_HANDLER( errorCode, "vkBindImageMemory" );
}
template< ResourceType resourceType, class T >
VkDeviceMemory initMemory(
VkDevice device,
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties,
T resource,
const std::vector<VkMemoryPropertyFlags>& memoryTypePriority
){
const VkMemoryRequirements memoryRequirements = getMemoryRequirements<resourceType>( device, resource );
const auto indexToBit = []( const uint32_t index ){ return 0x1 << index; };
const uint32_t memoryTypeNotFound = UINT32_MAX;
uint32_t memoryType = memoryTypeNotFound;
for( const auto desiredMemoryType : memoryTypePriority ){
const uint32_t maxMemoryTypeCount = 32;
for( uint32_t i = 0; memoryType == memoryTypeNotFound && i < maxMemoryTypeCount; ++i ){
if( memoryRequirements.memoryTypeBits & indexToBit(i) ){
if( (physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags & desiredMemoryType) == desiredMemoryType ){
memoryType = i;
}
}
}
}
if( memoryType == memoryTypeNotFound ) throw "Can't find compatible mappable memory for the resource";
VkMemoryAllocateInfo memoryInfo{
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr, // pNext
memoryRequirements.size,
memoryType
};
VkDeviceMemory memory;
VkResult errorCode = vkAllocateMemory( device, &memoryInfo, nullptr, &memory ); RESULT_HANDLER( errorCode, "vkAllocateMemory" );
bindMemory<resourceType>( device, resource, memory, 0 /*offset*/ );
return memory;
}
void setMemoryData( VkDevice device, VkDeviceMemory memory, void* begin, size_t size ){
void* data;
VkResult errorCode = vkMapMemory( device, memory, 0 /*offset*/, VK_WHOLE_SIZE, 0 /*flags - reserved*/, &data ); RESULT_HANDLER( errorCode, "vkMapMemory" );
memcpy( data, begin, size );
vkUnmapMemory( device, memory );
}
void killMemory( VkDevice device, VkDeviceMemory memory ){
vkFreeMemory( device, memory, nullptr );
}
VkBuffer initBuffer( VkDevice device, VkDeviceSize size, VkBufferUsageFlags usage ){
VkBufferCreateInfo bufferInfo{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
nullptr, // pNext
0, // flags
size,
usage,
VK_SHARING_MODE_EXCLUSIVE,
0, // queue family count -- ignored for EXCLUSIVE
nullptr // queue families -- ignored for EXCLUSIVE
};
VkBuffer buffer;
VkResult errorCode = vkCreateBuffer( device, &bufferInfo, nullptr, &buffer ); RESULT_HANDLER( errorCode, "vkCreateBuffer" );
return buffer;
}
void killBuffer( VkDevice device, VkBuffer buffer ){
vkDestroyBuffer( device, buffer, nullptr );
}
VkImage initImage( VkDevice device, VkFormat format, uint32_t width, uint32_t height, VkSampleCountFlagBits samples, VkImageUsageFlags usage ){
VkExtent3D size{
width,
height,
1 // depth
};
VkImageCreateInfo ici{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
nullptr, // pNext
0, // flags
VK_IMAGE_TYPE_2D,
format,
size,
1, // mipLevels
1, // arrayLayers
samples,
VK_IMAGE_TILING_OPTIMAL,
usage,
VK_SHARING_MODE_EXCLUSIVE,
0, // queueFamilyIndexCount -- ignored for EXCLUSIVE
nullptr, // pQueueFamilyIndices -- ignored for EXCLUSIVE
VK_IMAGE_LAYOUT_UNDEFINED
};
VkImage image;
VkResult errorCode = vkCreateImage( device, &ici, nullptr, &image ); RESULT_HANDLER( errorCode, "vkCreateImage" );
return image;
}
void killImage( VkDevice device, VkImage image ){
vkDestroyImage( device, image, nullptr );
}
VkImageView initImageView( VkDevice device, VkImage image, VkFormat format ){
VkImageViewCreateInfo iciv{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
nullptr, // pNext
0, // flags
image,
VK_IMAGE_VIEW_TYPE_2D,
format,
{ VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY },
{
VK_IMAGE_ASPECT_COLOR_BIT,
0, // base mip-level
VK_REMAINING_MIP_LEVELS, // level count
0, // base array layer
VK_REMAINING_ARRAY_LAYERS // array layer count
}
};
VkImageView imageView;
VkResult errorCode = vkCreateImageView( device, &iciv, nullptr, &imageView ); RESULT_HANDLER( errorCode, "vkCreateImageView" );
return imageView;
}
void killImageView( VkDevice device, VkImageView imageView ){
vkDestroyImageView( device, imageView, nullptr );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// initSurface is platform dependent
void killSurface( VkInstance instance, VkSurfaceKHR surface ){
vkDestroySurfaceKHR( instance, surface, nullptr );
}
VkSurfaceFormatKHR getSurfaceFormat( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface ){
const VkFormat preferredFormat1 = VK_FORMAT_B8G8R8A8_UNORM;
const VkFormat preferredFormat2 = VK_FORMAT_B8G8R8A8_SRGB;
vector<VkSurfaceFormatKHR> formats = enumerate<VkSurfaceFormatKHR>( physicalDevice, surface );
if( formats.empty() ) throw "No surface formats offered by Vulkan!";
if( formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED ){
formats[0].format = preferredFormat1;
}
VkSurfaceFormatKHR chosenFormat1 = {VK_FORMAT_UNDEFINED};
VkSurfaceFormatKHR chosenFormat2 = {VK_FORMAT_UNDEFINED};
for( auto f : formats ){
if( f.format == preferredFormat1 ){
chosenFormat1 = f;
break;
}
if( f.format == preferredFormat2 ){
chosenFormat2 = f;
}
}
if( chosenFormat1.format ) return chosenFormat1;
else if( chosenFormat2.format ) return chosenFormat2;
else return formats[0];
}
VkSurfaceCapabilitiesKHR getSurfaceCapabilities( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface ){
VkSurfaceCapabilitiesKHR capabilities;
VkResult errorCode = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( physicalDevice, surface, &capabilities ); RESULT_HANDLER( errorCode, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR" );
return capabilities;
}
int selectedMode = 0;
TODO( "Could use debug_report instead of log" )
VkPresentModeKHR getSurfacePresentMode( VkPhysicalDevice physicalDevice, VkSurfaceKHR surface ){
vector<VkPresentModeKHR> modes = enumerate<VkPresentModeKHR>( physicalDevice, surface );
for( auto m : modes ){
if( m == ::presentMode ){
if( selectedMode != 0 ){
logger << "INFO: Your preferred present mode became supported. Switching to it.\n";
}
selectedMode = 0;
return m;
}
}
for( auto m : modes ){
if( m == VK_PRESENT_MODE_FIFO_KHR ){
if( selectedMode != 1 ){
logger << "WARNING: Your preferred present mode is not supported. Switching to VK_PRESENT_MODE_FIFO_KHR.\n";
}
selectedMode = 1;
return m;
}
}
TODO( "Workaround for bad (Intel Linux Mesa) drivers" )
if( modes.empty() ) throw "Bugged driver reports no supported present modes.";
else{
if( selectedMode != 2 ){
logger << "WARNING: Bugged drivers. VK_PRESENT_MODE_FIFO_KHR not supported. Switching to whatever is.\n";
}
selectedMode = 2;
return modes[0];
}
}
VkSwapchainKHR initSwapchain(
VkPhysicalDevice physicalDevice,
VkDevice device,
VkSurfaceKHR surface,
VkSurfaceFormatKHR surfaceFormat,
VkSurfaceCapabilitiesKHR capabilities,
uint32_t graphicsQueueFamily,
uint32_t presentQueueFamily,
VkSwapchainKHR oldSwapchain
){
// we don't care as we are always setting alpha to 1.0
VkCompositeAlphaFlagBitsKHR compositeAlphaFlag;
if( capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ) compositeAlphaFlag = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
else if( capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR ) compositeAlphaFlag = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
else if( capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR ) compositeAlphaFlag = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
else if( capabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR ) compositeAlphaFlag = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
else throw "Unknown composite alpha reported.";
// minImageCount + 1 seems a sensible default. It means 2 images should always be readily available without blocking. May lead to memory waste though if we care about that.
uint32_t myMinImageCount = capabilities.minImageCount + 1;
if( capabilities.maxImageCount ) myMinImageCount = std::min<uint32_t>( myMinImageCount, capabilities.maxImageCount );
std::vector<uint32_t> queueFamilies = { graphicsQueueFamily };
if(graphicsQueueFamily != presentQueueFamily) queueFamilies.push_back( presentQueueFamily );
VkSwapchainCreateInfoKHR swapchainInfo{
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
nullptr, // pNext for extensions use
0, // flags - reserved for future use
surface,
myMinImageCount, // minImageCount
surfaceFormat.format,
surfaceFormat.colorSpace,
capabilities.currentExtent,
1,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, // VkImage usage flags
// It should be fine to just use CONCURRENT in the off chance we encounter the elusive GPU with separate present queue
queueFamilies.size() > 1 ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE,
static_cast<uint32_t>( queueFamilies.size() ),
queueFamilies.data(),
capabilities.currentTransform,
compositeAlphaFlag,
getSurfacePresentMode( physicalDevice, surface ),
VK_TRUE, // clipped
oldSwapchain
};
VkSwapchainKHR swapchain;
VkResult errorCode = vkCreateSwapchainKHR( device, &swapchainInfo, nullptr, &swapchain ); RESULT_HANDLER( errorCode, "vkCreateSwapchainKHR" );
return swapchain;
}
void killSwapchain( VkDevice device, VkSwapchainKHR swapchain ){
vkDestroySwapchainKHR( device, swapchain, nullptr );
}
uint32_t getNextImageIndex( VkDevice device, VkSwapchainKHR swapchain, VkSemaphore imageReadyS ){
uint32_t nextImageIndex;
VkResult errorCode = vkAcquireNextImageKHR(
device,
swapchain,
UINT64_MAX /* no timeout */,
imageReadyS,
VK_NULL_HANDLE,
&nextImageIndex
); RESULT_HANDLER( errorCode, "vkAcquireNextImageKHR" );
return nextImageIndex;
}
vector<VkImageView> initSwapchainImageViews( VkDevice device, vector<VkImage> images, VkFormat format ){
vector<VkImageView> imageViews;
for( auto image : images ){
VkImageView imageView = initImageView( device, image, format );
imageViews.push_back( imageView );
}
return imageViews;
}
void killSwapchainImageViews( VkDevice device, vector<VkImageView>& imageViews ){
for( auto imageView : imageViews ) vkDestroyImageView( device, imageView, nullptr );
imageViews.clear();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VkRenderPass initRenderPass( VkDevice device, VkSurfaceFormatKHR surfaceFormat ){
VkAttachmentDescription colorAtachment{
0, // flags
surfaceFormat.format,
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR, // color + depth
VK_ATTACHMENT_STORE_OP_STORE, // color + depth
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencil
VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencil
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
};
VkAttachmentReference colorReference{
0, // attachment
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
};
VkSubpassDescription subpass{
0, // flags - reserved for future use
VK_PIPELINE_BIND_POINT_GRAPHICS,
0, // input attachment count
nullptr, // input attachments
1, // color attachment count
&colorReference, // color attachments
nullptr, // resolve attachments
nullptr, // depth stencil attachment
0, // preserve attachment count
nullptr // preserve attachments
};
VkSubpassDependency srcDependency{
VK_SUBPASS_EXTERNAL, // srcSubpass
0, // dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // dstStageMask
0, // srcAccessMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
VK_DEPENDENCY_BY_REGION_BIT, // dependencyFlags
};
// implicitly defined dependency would cover this, but let's replace it with this explicitly defined dependency!
VkSubpassDependency dstDependency{
0, // srcSubpass
VK_SUBPASS_EXTERNAL, // dstSubpass
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, // srcStageMask
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, // dstStageMask
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask
0, // dstAccessMask
VK_DEPENDENCY_BY_REGION_BIT, // dependencyFlags
};
VkSubpassDependency dependencies[] = {srcDependency, dstDependency};
VkRenderPassCreateInfo renderPassInfo{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
1, // attachment count
&colorAtachment, // attachments
1, // subpass count
&subpass, // subpasses
2, // dependency count
dependencies // dependencies
};
VkRenderPass renderPass;
VkResult errorCode = vkCreateRenderPass( device, &renderPassInfo, nullptr, &renderPass ); RESULT_HANDLER( errorCode, "vkCreateRenderPass" );
return renderPass;
}
void killRenderPass( VkDevice device, VkRenderPass renderPass ){
vkDestroyRenderPass( device, renderPass, nullptr );
}
vector<VkFramebuffer> initFramebuffers(
VkDevice device,
VkRenderPass renderPass,
vector<VkImageView> imageViews,
uint32_t width, uint32_t height
){
vector<VkFramebuffer> framebuffers;
for( auto imageView : imageViews ){
VkFramebufferCreateInfo framebufferInfo{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
renderPass,
1, // ImageView count
&imageView,
width, // width
height, // height
1 // layers
};
VkFramebuffer framebuffer;
VkResult errorCode = vkCreateFramebuffer( device, &framebufferInfo, nullptr, &framebuffer ); RESULT_HANDLER( errorCode, "vkCreateFramebuffer" );
framebuffers.push_back( framebuffer );
}
return framebuffers;
}
void killFramebuffers( VkDevice device, vector<VkFramebuffer>& framebuffers ){
for( auto framebuffer : framebuffers ) vkDestroyFramebuffer( device, framebuffer, nullptr );
framebuffers.clear();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename Type = uint8_t>
vector<Type> loadBinaryFile( string filename ){
using std::ifstream;
using std::istreambuf_iterator;
vector<Type> data;
try{
ifstream ifs;
ifs.exceptions( ifs.failbit | ifs.badbit | ifs.eofbit );
ifs.open( filename, ifs.in | ifs.binary | ifs.ate );
const auto fileSize = static_cast<size_t>( ifs.tellg() );
if( fileSize > 0 && (fileSize % sizeof(Type) == 0) ){
ifs.seekg( ifs.beg );
data.resize( fileSize / sizeof(Type) );
ifs.read( reinterpret_cast<char*>(data.data()), fileSize );
}
}
catch( ... ){
data.clear();
}
return data;
}
VkShaderModule initShaderModule( VkDevice device, string filename ){
const auto shaderCode = loadBinaryFile<uint32_t>( filename );
if( shaderCode.empty() ) throw "SPIR-V shader file " + filename + " is invalid or read failed!";
return initShaderModule( device, shaderCode );
}
VkShaderModule initShaderModule( VkDevice device, const vector<uint32_t>& shaderCode ){
VkShaderModuleCreateInfo shaderModuleInfo{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
shaderCode.size() * sizeof(uint32_t),
shaderCode.data()
};
VkShaderModule shaderModule;
VkResult errorCode = vkCreateShaderModule( device, &shaderModuleInfo, nullptr, &shaderModule ); RESULT_HANDLER( errorCode, "vkCreateShaderModule" );
return shaderModule;
}
void killShaderModule( VkDevice device, VkShaderModule shaderModule ){
vkDestroyShaderModule( device, shaderModule, nullptr );
}
VkPipelineLayout initPipelineLayout( VkDevice device ){
VkPipelineLayoutCreateInfo pipelineLayoutInfo{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
0, // descriptorSetLayout count
nullptr,
0, // push constant range count
nullptr // push constant ranges
};
VkPipelineLayout pipelineLayout;
VkResult errorCode = vkCreatePipelineLayout( device, &pipelineLayoutInfo, nullptr, &pipelineLayout ); RESULT_HANDLER( errorCode, "vkCreatePipelineLayout" );
return pipelineLayout;
}
void killPipelineLayout( VkDevice device, VkPipelineLayout pipelineLayout ){
vkDestroyPipelineLayout( device, pipelineLayout, nullptr );
}
VkPipeline initPipeline(
VkDevice device,
VkPhysicalDeviceLimits limits,
VkPipelineLayout pipelineLayout,
VkRenderPass renderPass,
VkShaderModule vertexShader,
VkShaderModule fragmentShader,
const uint32_t vertexBufferBinding,
uint32_t width, uint32_t height
){/*
const VkPipelineShaderStageCreateInfo vertexShaderStage{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_SHADER_STAGE_VERTEX_BIT,
vertexShader,
u8"main",
nullptr // SpecializationInfo - constants pushed to shader on pipeline creation time
};
const VkPipelineShaderStageCreateInfo fragmentShaderStage{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_SHADER_STAGE_FRAGMENT_BIT,
fragmentShader,
u8"main",
nullptr // SpecializationInfo - constants pushed to shader on pipeline creation time
};*/
VkPipelineShaderStageCreateInfo shaderStageStates[] = {
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_SHADER_STAGE_VERTEX_BIT,
vertexShader,
u8"main",
nullptr // SpecializationInfo - constants pushed to shader on pipeline creation time
},
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_SHADER_STAGE_FRAGMENT_BIT,
fragmentShader,
u8"main",
nullptr // SpecializationInfo - constants pushed to shader on pipeline creation time
}
};
const uint32_t vertexBufferStride = sizeof( Vertex2D_ColorF_pack );
if( vertexBufferBinding > limits.maxVertexInputBindings ){
throw string("Implementation does not allow enough input bindings. Needed: ")
+ to_string( vertexBufferBinding ) + string(", max: ")
+ to_string( limits.maxVertexInputBindings );
}
if( vertexBufferStride > limits.maxVertexInputBindingStride ){
throw string("Implementation does not allow big enough vertex buffer stride: ")
+ to_string( vertexBufferStride )
+ string(", max: ")
+ to_string( limits.maxVertexInputBindingStride );
}
VkVertexInputBindingDescription vertexInputBindingDescription{
vertexBufferBinding,
sizeof( Vertex2D_ColorF_pack ), // stride in bytes
VK_VERTEX_INPUT_RATE_VERTEX
};
vector<VkVertexInputBindingDescription> inputBindingDescriptions = { vertexInputBindingDescription };
if( inputBindingDescriptions.size() > limits.maxVertexInputBindings ){
throw "Implementation does not allow enough input bindings.";
}
const uint32_t positionLocation = 0;
const uint32_t colorLocation = 1;
if( colorLocation >= limits.maxVertexInputAttributes ){
throw "Implementation does not allow enough input attributes.";
}
if( offsetof( Vertex2D_ColorF_pack, color ) > limits.maxVertexInputAttributeOffset ){
throw "Implementation does not allow sufficient attribute offset.";
}
VkVertexInputAttributeDescription positionInputAttributeDescription{
positionLocation,
vertexBufferBinding,
VK_FORMAT_R32G32_SFLOAT,
offsetof( Vertex2D_ColorF_pack, position ) // offset in bytes
};
VkVertexInputAttributeDescription colorInputAttributeDescription{
colorLocation,
vertexBufferBinding,
VK_FORMAT_R32G32B32_SFLOAT,
offsetof( Vertex2D_ColorF_pack, color ) // offset in bytes
};
vector<VkVertexInputAttributeDescription> inputAttributeDescriptions = {
positionInputAttributeDescription,
colorInputAttributeDescription
};
VkPipelineVertexInputStateCreateInfo vertexInputState{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
static_cast<uint32_t>( inputBindingDescriptions.size() ),
inputBindingDescriptions.data(),
static_cast<uint32_t>( inputAttributeDescriptions.size() ),
inputAttributeDescriptions.data()
};
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
VK_FALSE // primitive restart
};
VkViewport viewport{
0.0f, // x
0.0f, // y
static_cast<float>( width ? width : 1 ),
static_cast<float>( height ? height : 1 ),
0.0f, // min depth
1.0f // max depth
};
VkRect2D scissor{
{0, 0}, // offset
{width, height}
};
VkPipelineViewportStateCreateInfo viewportState{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
1, // Viewport count
&viewport,
1, // scisor count,
&scissor
};
VkPipelineRasterizationStateCreateInfo rasterizationState{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_FALSE, // depth clamp
VK_FALSE, // rasterizer discard
VK_POLYGON_MODE_FILL,
VK_CULL_MODE_BACK_BIT,
VK_FRONT_FACE_COUNTER_CLOCKWISE,
VK_FALSE, // depth bias
0.0f, // bias constant factor
0.0f, // bias clamp
0.0f, // bias slope factor
1.0f // line width
};
VkPipelineMultisampleStateCreateInfo multisampleState{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_SAMPLE_COUNT_1_BIT,
VK_FALSE, // no sample shading
0.0f, // min sample shading - ignored if disabled
nullptr, // sample mask
VK_FALSE, // alphaToCoverage
VK_FALSE // alphaToOne
};
VkPipelineColorBlendAttachmentState blendAttachmentState{
VK_FALSE, // blending enabled?
VK_BLEND_FACTOR_ZERO, // src blend factor -ignored?
VK_BLEND_FACTOR_ZERO, // dst blend factor
VK_BLEND_OP_ADD, // blend op
VK_BLEND_FACTOR_ZERO, // src alpha blend factor
VK_BLEND_FACTOR_ZERO, // dst alpha blend factor
VK_BLEND_OP_ADD, // alpha blend op
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT // color write mask
};
VkPipelineColorBlendStateCreateInfo colorBlendState{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
nullptr, // pNext
0, // flags - reserved for future use
VK_FALSE, // logic ops
VK_LOGIC_OP_COPY,
1, // attachment count - must be same as color attachment count in renderpass subpass!
&blendAttachmentState,
{0.0f, 0.0f, 0.0f, 0.0f} // blend constants
};
VkGraphicsPipelineCreateInfo pipelineInfo{
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
nullptr, // pNext
0, // flags - e.g. disable optimization
2, // shader stages count - vertex and fragment
shaderStageStates,
&vertexInputState,
&inputAssemblyState,
nullptr, // tesselation
&viewportState,
&rasterizationState,
&multisampleState,
nullptr, // depth stencil
&colorBlendState,
nullptr, // dynamic state
pipelineLayout,
renderPass,
0, // subpass index in renderpass
VK_NULL_HANDLE, // base pipeline
-1 // base pipeline index
};
VkPipeline pipeline;
VkResult errorCode = vkCreateGraphicsPipelines(
device,
VK_NULL_HANDLE /* pipeline cache */,
1 /* info count */,
&pipelineInfo,
nullptr,
&pipeline
); RESULT_HANDLER( errorCode, "vkCreateGraphicsPipelines" );
return pipeline;
}
void killPipeline( VkDevice device, VkPipeline pipeline ){
vkDestroyPipeline( device, pipeline, nullptr );
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void setVertexData( VkDevice device, VkDeviceMemory memory, vector<Vertex2D_ColorF_pack> vertices ){
TODO( "Should be in Device Local memory instead" )
setMemoryData( device, memory, vertices.data(), sizeof( decltype(vertices)::value_type ) * vertices.size() );
}
VkSemaphore initSemaphore( VkDevice device ){
const VkSemaphoreCreateInfo semaphoreInfo{
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
nullptr, // pNext
0 // flags - reserved for future use
};
VkSemaphore semaphore;
VkResult errorCode = vkCreateSemaphore( device, &semaphoreInfo, nullptr, &semaphore ); RESULT_HANDLER( errorCode, "vkCreateSemaphore" );
return semaphore;
}
vector<VkSemaphore> initSemaphores( VkDevice device, size_t count ){
vector<VkSemaphore> semaphores;
std::generate_n( std::back_inserter( semaphores ), count, [device]{ return initSemaphore( device ); } );
return semaphores;
}
void killSemaphore( VkDevice device, VkSemaphore semaphore ){
vkDestroySemaphore( device, semaphore, nullptr );
}
void killSemaphores( VkDevice device, vector<VkSemaphore>& semaphores ){
for( const auto s : semaphores ) killSemaphore( device, s );
semaphores.clear();
}
VkCommandPool initCommandPool( VkDevice device, const uint32_t queueFamily ){
const VkCommandPoolCreateInfo commandPoolInfo{
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
nullptr, // pNext
0, // flags
queueFamily
};
VkCommandPool commandPool;
VkResult errorCode = vkCreateCommandPool( device, &commandPoolInfo, nullptr, &commandPool ); RESULT_HANDLER( errorCode, "vkCreateCommandPool" );
return commandPool;
}
void killCommandPool( VkDevice device, VkCommandPool commandPool ){
vkDestroyCommandPool( device, commandPool, nullptr );
}
VkFence initFence( const VkDevice device, const VkFenceCreateFlags flags = 0 ){
const VkFenceCreateInfo fci{
VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
nullptr, // pNext
flags
};
VkFence fence;
VkResult errorCode = vkCreateFence( device, &fci, nullptr, &fence ); RESULT_HANDLER( errorCode, "vkCreateFence" );
return fence;
}
void killFence( const VkDevice device, const VkFence fence ){
vkDestroyFence( device, fence, nullptr );
}
vector<VkFence> initFences( const VkDevice device, const size_t count, const VkFenceCreateFlags flags ){
vector<VkFence> fences;
std::generate_n( std::back_inserter( fences ), count, [=]{return initFence( device, flags );} );
return fences;
}
void killFences( const VkDevice device, vector<VkFence>& fences ){
for( const auto f : fences ) killFence( device, f );
fences.clear();
}
void acquireCommandBuffers( VkDevice device, VkCommandPool commandPool, uint32_t count, vector<VkCommandBuffer>& commandBuffers ){
const auto oldSize = static_cast<uint32_t>( commandBuffers.size() );
if( count > oldSize ){
VkCommandBufferAllocateInfo commandBufferInfo{
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
nullptr, // pNext
commandPool,
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
count - oldSize // count
};
commandBuffers.resize( count );
VkResult errorCode = vkAllocateCommandBuffers( device, &commandBufferInfo, &commandBuffers[oldSize] ); RESULT_HANDLER( errorCode, "vkAllocateCommandBuffers" );
}
if( count < oldSize ) {
vkFreeCommandBuffers( device, commandPool, oldSize - count, &commandBuffers[count] );
commandBuffers.resize( count );
}
}
void beginCommandBuffer( VkCommandBuffer commandBuffer ){
VkCommandBufferBeginInfo commandBufferInfo{
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
nullptr, // pNext
// same buffer can be re-executed before it finishes from last submit
VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, // flags
nullptr // inheritance
};
VkResult errorCode = vkBeginCommandBuffer( commandBuffer, &commandBufferInfo ); RESULT_HANDLER( errorCode, "vkBeginCommandBuffer" );
}
void endCommandBuffer( VkCommandBuffer commandBuffer ){
VkResult errorCode = vkEndCommandBuffer( commandBuffer ); RESULT_HANDLER( errorCode, "vkEndCommandBuffer" );
}
void recordBeginRenderPass(
VkCommandBuffer commandBuffer,
VkRenderPass renderPass,
VkFramebuffer framebuffer,
VkClearValue clearValue,
uint32_t width, uint32_t height
){
VkRenderPassBeginInfo renderPassInfo{
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
nullptr, // pNext
renderPass,
framebuffer,
{{0,0}, {width,height}}, //render area - offset plus extent
1, // clear value count
&clearValue
};
vkCmdBeginRenderPass( commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE );
}
void recordEndRenderPass( VkCommandBuffer commandBuffer ){
vkCmdEndRenderPass( commandBuffer );
}
void recordBindPipeline( VkCommandBuffer commandBuffer, VkPipeline pipeline ){
vkCmdBindPipeline( commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline );
}
void recordBindVertexBuffer( VkCommandBuffer commandBuffer, const uint32_t vertexBufferBinding, VkBuffer vertexBuffer ){
VkDeviceSize offsets[] = {0};
vkCmdBindVertexBuffers( commandBuffer, vertexBufferBinding, 1 /*binding count*/, &vertexBuffer, offsets );
}
void recordDraw( VkCommandBuffer commandBuffer, const uint32_t vertexCount ){
vkCmdDraw( commandBuffer, vertexCount, 1 /*instance count*/, 0 /*first vertex*/, 0 /*first instance*/ );
}
void submitToQueue( VkQueue queue, VkCommandBuffer commandBuffer, VkSemaphore imageReadyS, VkSemaphore renderDoneS, VkFence fence ){
const VkPipelineStageFlags psw = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
const VkSubmitInfo submit{
VK_STRUCTURE_TYPE_SUBMIT_INFO,
nullptr, // pNext
1, &imageReadyS, // wait semaphores
&psw, // pipeline stages to wait for semaphore
1, &commandBuffer,
1, &renderDoneS // signal semaphores
};
const VkResult errorCode = vkQueueSubmit( queue, 1 /*submit count*/, &submit, fence ); RESULT_HANDLER( errorCode, "vkQueueSubmit" );
}
void present( VkQueue queue, VkSwapchainKHR swapchain, uint32_t swapchainImageIndex, VkSemaphore renderDoneS ){
const VkPresentInfoKHR presentInfo{
VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
nullptr, // pNext
1, &renderDoneS, // wait semaphores
1, &swapchain, &swapchainImageIndex,
nullptr // pResults
};
const VkResult errorCode = vkQueuePresentKHR( queue, &presentInfo ); RESULT_HANDLER( errorCode, "vkQueuePresentKHR" );
}
// cleanup dangerous semaphore with signal pending from vkAcquireNextImageKHR (tie it to a specific queue)
// https://github.com/KhronosGroup/Vulkan-Docs/issues/1059
void cleanupUnsafeSemaphore( VkQueue queue, VkSemaphore semaphore ){
VkPipelineStageFlags psw = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
const VkSubmitInfo submit{
VK_STRUCTURE_TYPE_SUBMIT_INFO,
nullptr, // pNext
1, &semaphore, // wait semaphores
&psw, // pipeline stages to wait for semaphore
0, nullptr, // command buffers
0, nullptr // signal semaphores
};
const VkResult errorCode = vkQueueSubmit( queue, 1 /*submit count*/, &submit, VK_NULL_HANDLE ); RESULT_HANDLER( errorCode, "vkQueueSubmit" );
}
================================================
FILE: src/LeanWindowsEnvironment.h
================================================
// As lean as possible Windows.h header global settings
// Should be included as soon as possible (first line in translation unit preferably)
#ifndef COMMON_LEAN_WINDOWS_ENVIRONMENT_H
#define COMMON_LEAN_WINDOWS_ENVIRONMENT_H
#ifdef _INC_WINDOWS
#error Some Windows.h is already included before this file!
#endif //_INC_WINDOWS
#define WIN32_LEAN_AND_MEAN
#define NOGDICAPMASKS
//#define NOVIRTUALKEYCODES // need virtual keys e.g. VK_ESCAPE
//#define NOWINMESSAGES // need window message definitions
//#define NOWINSTYLES // need window styles to create windows
#define NOSYSMETRICS
#define NOMENUS
//#define NOICONS // need icons to create window
#define NOKEYSTATES
//#define NOSYSCOMMANDS // need to manage LAlt key events
#define NORASTEROPS
//#define NOSHOWWINDOW // need to show windows
#define OEMRESOURCE
#define NOATOM
#define NOCLIPBOARD
//#define NOCOLOR // need default colors
#define NOCTLMGR
#define NODRAWTEXT
#define NOGDI
#define NOKERNEL
//#define NOUSER // need it - contains MSG and lot other things
//#define NONLS // need CP_UTF8
#define NOMB
#define NOMEMMGR
#define NOMETAFILE
#define NOMINMAX
//#define NOMSG // need MSG for event loop
#define NOOPENFILE
#define NOSCROLL
#define NOSERVICE
#define NOSOUND
#define NOTEXTMETRIC
#define NOWH
//#define NOWINOFFSETS // need for GetWindowLongW
#define NOCOMM
#define NOKANJI
#define NOHELP
#define NOPROFILER
#define NODEFERWINDOWPOS
#define NOMCX
// rip ANSI mode
#ifndef UNICODE
#define UNICODE
#endif
#endif //COMMON_LEAN_WINDOWS_ENVIRONMENT_H
================================================
FILE: src/Vertex.h
================================================
// Basic vertex data definitions
#ifndef COMMON_VERTEX_H
#define COMMON_VERTEX_H
struct Vertex2D{
float position[2];
};
struct ColorF{
float color[3];
};
struct Vertex2D_ColorF_pack{
Vertex2D position;
ColorF color;
};
#endif //COMMON_VERTEX_H
================================================
FILE: src/VulkanEnvironment.h
================================================
// vulkan.h header global settings
// Should be included as soon as possible (first line in translation unit preferably)
#ifndef COMMON_VULKAN_ENVIRONMENT_H
#define COMMON_VULKAN_ENVIRONMENT_H
// detect premature vulkan.h
#ifdef VULKAN_H_
#error "vulkan.h is included before VulkanEnvironment.h!"
#endif //VULKAN_H_
#include "CompilerMessages.h"
#define REQUIRED_HEADER_VERSION 176
#ifdef NDEBUG
#ifndef VULKAN_VALIDATION
#define VULKAN_VALIDATION 0
#endif
#else
#ifndef VULKAN_VALIDATION
#define VULKAN_VALIDATION 1
#endif
#endif
#if defined(USE_PLATFORM_NONE) \
|| defined(USE_PLATFORM_GLFW) \
|| defined(VK_USE_PLATFORM_ANDROID_KHR) \
|| defined(VK_USE_PLATFORM_MIR_KHR) \
|| defined(VK_USE_PLATFORM_WAYLAND_KHR) \
|| defined(VK_USE_PLATFORM_WIN32_KHR) \
|| defined(VK_USE_PLATFORM_XCB_KHR) \
|| defined(VK_USE_PLATFORM_XLIB_KHR) \
|| defined(VK_USE_PLATFORM_IOS_MVK) \
|| defined(VK_USE_PLATFORM_MACOS_MVK) \
|| defined(VK_USE_PLATFORM_VI_NN)
#define WSI_CHOSEN_EXTERNALLY
#endif
#if defined(_WIN32) && !defined(_CONSOLE)
#include "LeanWindowsEnvironment.h"
#include <Windows.h>
#endif
// platform specific settings
#if defined(__CYGWIN__)
#ifndef WSI_CHOSEN_EXTERNALLY
#define USE_PLATFORM_GLFW
//#define VK_USE_PLATFORM_WIN32_KHR
#endif
#include "LeanWindowsEnvironment.h" // Windows.h settings must be first -- vulkan.h does include Windows.h
#elif defined(__MINGW32__)
#ifndef WSI_CHOSEN_EXTERNALLY
#define USE_PLATFORM_GLFW
//#define VK_USE_PLATFORM_WIN32_KHR
#endif
#include "LeanWindowsEnvironment.h" // Windows.h settings must be first -- vulkan.h does include Windows.h
#elif defined(_WIN32)
#ifndef WSI_CHOSEN_EXTERNALLY
#define USE_PLATFORM_GLFW
//#define VK_USE_PLATFORM_WIN32_KHR
#endif
#elif defined(__linux__)
#ifndef WSI_CHOSEN_EXTERNALLY
#define USE_PLATFORM_GLFW
//#define VK_USE_PLATFORM_XCB_KHR
//#define VK_USE_PLATFORM_XLIB_KHR
//#define VK_USE_PLATFORM_WAYLAND_KHR
#endif
#else
//#error "Unsupported OS platform." // caught in main.cpp instead
#endif
#endif //COMMON_VULKAN_ENVIRONMENT_H
================================================
FILE: src/VulkanIntrospection.h
================================================
// Introspection for Vulkan enums -- mostly to_string
#ifndef COMMON_VULKAN_INTROSPECTION_H
#define COMMON_VULKAN_INTROSPECTION_H
#include <string>
#include <sstream>
#include <vulkan/vulkan.h>
template <typename PHANDLE_T>
inline uint64_t handleToUint64(const PHANDLE_T *h) { return reinterpret_cast<uint64_t>(h); }
inline uint64_t handleToUint64(const uint64_t h) { return h; }
const char* to_string( const VkResult r ){
switch( r ){
case VK_SUCCESS: return "VK_SUCCESS";
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VK_ERROR_VALIDATION_FAILED_EXT: return "VK_ERROR_VALIDATION_FAILED_EXT";
case VK_ERROR_INVALID_SHADER_NV: return "VK_ERROR_INVALID_SHADER_NV";
case VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VK_ERROR_NOT_PERMITTED_EXT: return "VK_ERROR_NOT_PERMITTED_EXT";
case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
case VK_THREAD_IDLE_KHR: return "VK_THREAD_IDLE_KHR";
case VK_THREAD_DONE_KHR: return "VK_THREAD_DONE_KHR";
case VK_OPERATION_DEFERRED_KHR: return "VK_OPERATION_DEFERRED_KHR";
case VK_OPERATION_NOT_DEFERRED_KHR: return "VK_OPERATION_NOT_DEFERRED_KHR";
case VK_PIPELINE_COMPILE_REQUIRED_EXT: return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
default: return "unrecognized VkResult code";
}
}
std::string to_string( const VkDebugReportObjectTypeEXT o ){
switch( o ){
case VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT: return "unknown";
case VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT: return "Instance";
case VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT: return "PhysicalDevice";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT: return "Device";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT: return "Queue";
case VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT: return "Semaphore";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT: return "CommandBuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT: return "Fence";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT: return "DeviceMemory";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT: return "Buffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT: return "Image";
case VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT: return "Event";
case VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT: return "QueryPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT: return "BufferView";
case VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT: return "ImageView";
case VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT: return "ShaderModule";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT: return "PipelineCache";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT: return "PipelineLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT: return "RenderPass";
case VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT: return "Pipeline";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT: return "DescriptorSetLayout";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT: return "Sampler";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT: return "DescriptorPool";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT: return "DescriptorSet";
case VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT: return "Framebuffer";
case VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT: return "Command pool";
case VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT: return "SurfaceKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT: return "SwapchainKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT: return "DebugReportCallbackEXT";
case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT: return "DisplayKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT: return "DisplayModeKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT: return "ValidationCacheEXT";
case VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT: return "SamplerYcbcrConversion";
case VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT: return "DescriptorUpdateTemplate";
case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT: return "AccelerationStructureKHR";
case VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT: return "AccelerationStructureNV";
default: return "unrecognized type";
}
}
std::string to_string( const VkObjectType o ){
switch( o ){
case VK_OBJECT_TYPE_UNKNOWN: return "unknown";
case VK_OBJECT_TYPE_INSTANCE: return "Instance";
case VK_OBJECT_TYPE_PHYSICAL_DEVICE: return "PhysicalDevice";
case VK_OBJECT_TYPE_DEVICE: return "Device";
case VK_OBJECT_TYPE_QUEUE: return "Queue";
case VK_OBJECT_TYPE_SEMAPHORE: return "Semaphore";
case VK_OBJECT_TYPE_COMMAND_BUFFER: return "CommandBuffer";
case VK_OBJECT_TYPE_FENCE: return "Fence";
case VK_OBJECT_TYPE_DEVICE_MEMORY: return "DeviceMemory";
case VK_OBJECT_TYPE_BUFFER: return "Buffer";
case VK_OBJECT_TYPE_IMAGE: return "Image";
case VK_OBJECT_TYPE_EVENT: return "Event";
case VK_OBJECT_TYPE_QUERY_POOL: return "QueryPool";
case VK_OBJECT_TYPE_BUFFER_VIEW: return "BufferView";
case VK_OBJECT_TYPE_IMAGE_VIEW: return "ImageView";
case VK_OBJECT_TYPE_SHADER_MODULE: return "ShaderModule";
case VK_OBJECT_TYPE_PIPELINE_CACHE: return "PipelineCache";
case VK_OBJECT_TYPE_PIPELINE_LAYOUT: return "PipelineLayout";
case VK_OBJECT_TYPE_RENDER_PASS: return "RenderPass";
case VK_OBJECT_TYPE_PIPELINE: return "Pipeline";
case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT: return "DescriptorSetLayout";
case VK_OBJECT_TYPE_SAMPLER: return "Sampler";
case VK_OBJECT_TYPE_DESCRIPTOR_POOL: return "DescriptorPool";
case VK_OBJECT_TYPE_DESCRIPTOR_SET: return "DescriptorSet";
case VK_OBJECT_TYPE_FRAMEBUFFER: return "Framebuffer";
case VK_OBJECT_TYPE_COMMAND_POOL: return "Command pool";
case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION: return "SamplerYcbcrConversion";
case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE: return "DescriptorUpdateTemplateKHR";
case VK_OBJECT_TYPE_SURFACE_KHR: return "SurfaceKHR";
case VK_OBJECT_TYPE_SWAPCHAIN_KHR: return "SwapchainKHR";
case VK_OBJECT_TYPE_DISPLAY_KHR: return "DisplayKHR";
case VK_OBJECT_TYPE_DISPLAY_MODE_KHR: return "DisplayModeKHR";
case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT: return "DebugReportCallbackEXT";
#ifdef VK_ENABLE_BETA_EXTENSIONS
case VK_OBJECT_TYPE_VIDEO_SESSION_KHR: return "VideoSessionKHR";
case VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR: return "VideoSessionParametersKHR";
#endif
case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT: return "DebugUtilsMessengerEXT";
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR: return "AccelerationStructureKHR";
case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT: return "ValidationCacheEXT";
case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV: return "AccelerationStructureNV";
case VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL: return "PerformanceConfigurationINTEL";
case VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR: return "DeferredOperationKHR";
case VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV: return "IndirectCommandsLayoutNV";
case VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT: return "PrivateDataSlotEXT";
default: return "unrecognized type";
}
}
std::string dbrflags_to_string( VkDebugReportFlagsEXT msgFlags ){
std::string res;
bool first = true;
if( msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT ){
if( !first ) res += " | ";
res += "ERROR";
first = false;
}
if( msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT ){
if( !first ) res += " | ";
res += "WARNING";
first = false;
}
if( msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT ){
if( !first ) res += " | ";
res += "PERFORMANCE";
first = false;
}
if( msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT ){
if( !first ) res += " | ";
res += "Info";
first = false;
}
if( msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT ){
if( !first ) res += " | ";
res += "Debug";
first = false;
}
VkDebugReportFlagsEXT known = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT;
if( msgFlags & ~known ){
if( !first ) res += " | ";
res += "UNRECOGNIZED_FLAG";
first = false;
}
return res;
}
std::string dbuseverity_to_string( const VkDebugUtilsMessageSeverityFlagsEXT debugSeverity ){
std::string res;
bool first = true;
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT ){
if( !first ) res += " | ";
res += "ERROR";
first = false;
}
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT ){
if( !first ) res += " | ";
res += "WARNING";
first = false;
}
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT ){
if( !first ) res += " | ";
res += "Info";
first = false;
}
if( debugSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT ){
if( !first ) res += " | ";
res += "Verbose";
first = false;
}
VkDebugUtilsMessageSeverityFlagsEXT known = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
if( debugSeverity & ~known ){
if( !first ) res += " | ";
res += "UNRECOGNIZED_FLAG";
first = false;
}
return res;
}
std::string to_string( const VkDebugUtilsMessageSeverityFlagBitsEXT debugSeverity ){
return dbuseverity_to_string( debugSeverity );
}
std::string dbutype_to_string( const VkDebugUtilsMessageTypeFlagsEXT debugType ){
std::string res;
bool first = true;
if( debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT ){
if( !first ) res += " | ";
res += "GENERAL";
first = false;
}
if( debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT ){
if( !first ) res += " | ";
res += "VALIDATION";
first = false;
}
if( debugType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT ){
if( !first ) res += " | ";
res += "PERFORMANCE";
first = false;
}
VkDebugUtilsMessageSeverityFlagsEXT known = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
if( debugType & ~known ){
if( !first ) res += " | ";
res += "UNRECOGNIZED_FLAG";
first = false;
}
return res;
}
std::string to_string_hex( const uint64_t n ){
std::stringstream ss;
ss << std::hex << std::noshowbase << std::uppercase << n;
return "0x" + ss.str();
}
#endif //COMMON_VULKAN_INTROSPECTION_H
================================================
FILE: src/WSI/Glfw.h
================================================
// GLFW platform dependent WSI handling and event loop
#ifndef COMMON_GLFW_WSI_H
#define COMMON_GLFW_WSI_H
#include <functional>
#include <string>
#include <queue>
#include <vulkan/vulkan.h>
#define GLFW_INCLUDE_NONE // Actually means include no OpenGL header
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include "CompilerMessages.h"
#include "ErrorHandling.h"
TODO( "Easier to use, but might prevent platform co-existence. Could be namespaced. Make all of this a class?" )
struct PlatformWindow{ GLFWwindow* window; };
std::string getPlatformSurfaceExtensionName();
int messageLoop( PlatformWindow window );
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window );
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight );
void killWindow( PlatformWindow window );
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window );
// killSurface() is not platform dependent
void setSizeEventHandler( std::function<void(void)> newSizeEventHandler );
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler );
void showWindow( PlatformWindow window );
// Implementation
//////////////////////////////////
bool nullHandler(){ return false; }
std::function<bool(void)> sizeEventHandler = nullHandler;
void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
if( !newSizeEventHandler ) sizeEventHandler = nullHandler;
sizeEventHandler = newSizeEventHandler;
}
std::function<void(void)> paintEventHandler = nullHandler;
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler ){
if( !newPaintEventHandler ) paintEventHandler = nullHandler;
paintEventHandler = newPaintEventHandler;
}
struct GlfwError{
int error;
std::string description;
};
std::queue<GlfwError> errors;
void glfwErrorCallback( int error, const char* description ){
errors.push( {error, description} ); // postpone errors because I don't want to throw into C API
}
// just make the global glfw instialization\destruction static
class GlfwSingleton{
static const GlfwSingleton glfwInstance;
void destroyGlfw() noexcept{
glfwTerminate();
glfwSetErrorCallback( nullptr );
}
~GlfwSingleton(){
this->destroyGlfw();
}
GlfwSingleton(){
glfwSetErrorCallback( glfwErrorCallback );
const auto success = glfwInit();
if( !success ){
glfwSetErrorCallback( nullptr );
throw "Trouble initializing GLFW!";
}
if( !glfwVulkanSupported() ){
this->destroyGlfw();
throw "GLFW has trouble acquiring Vulkan support!";
}
}
};
const GlfwSingleton GlfwSingleton::glfwInstance;
bool hasSwapchain = false;
void showWindow( PlatformWindow window ){
glfwShowWindow( window.window );
// on linux there is no automatic initial size event -- need to call explicitly
if( !hasSwapchain ) hasSwapchain = sizeEventHandler();
}
std::string getPlatformSurfaceExtensionName(){
using std::string;
uint32_t count;
const char** extensions = glfwGetRequiredInstanceExtensions( &count ); // GLFW owns **extensions!!!
if( !count || !extensions ) throw "GLFW failed to return required Vulkan extensions!";
if( count != 2 ) throw "GLFW returned unexpected number of required Vulkan extensions!";
if( extensions[0] != string( VK_KHR_SURFACE_EXTENSION_NAME ) ) throw VK_KHR_SURFACE_EXTENSION_NAME " is not a 1st required GLFW extension!";
string surfaceExtension = string( extensions[1] );
return string( extensions[1] );
}
GLFWmonitor* getCurrentMonitor( GLFWwindow* window ){
using std::max;
using std::min;
int bestoverlap = 0;
GLFWmonitor* bestmonitor = NULL;
int wx, wy;
glfwGetWindowPos( window, &wx, &wy );
int ww, wh;
glfwGetWindowSize( window, &ww, &wh );
int nmonitors;
auto monitors = glfwGetMonitors( &nmonitors );
for( int i = 0; i < nmonitors; ++i ){
auto mode = glfwGetVideoMode( monitors[i] );
int mw = mode->width;
int mh = mode->height;
int mx, my;
glfwGetMonitorPos( monitors[i], &mx, &my );
int overlap = max(0, min(wx + ww, mx + mw) - max(wx, mx)) * max(0, min(wy + wh, my + mh) - max(wy, my));
if( bestoverlap < overlap ){
bestoverlap = overlap;
bestmonitor = monitors[i];
}
}
return bestmonitor;
}
void windowSizeCallback( GLFWwindow*, int, int ) noexcept{
hasSwapchain = sizeEventHandler();
}
void windowRefreshCallback( GLFWwindow* ) noexcept{
//logger << "refresh" << std::endl;
if( hasSwapchain ) paintEventHandler();
}
void toggleFullscreen( GLFWwindow* window ){
bool fullscreen = glfwGetWindowMonitor( window ) != NULL;
TODO( "All of this needs to be made a class... death to the static!" )
static int wx = 100;
static int wy = 100;
static int ww = 800;
static int wh = 800;
if( !fullscreen ){
glfwGetWindowPos( window, &wx, &wy );
glfwGetWindowSize( window, &ww, &wh );
GLFWmonitor* monitor = getCurrentMonitor( window );
const GLFWvidmode* vmode = glfwGetVideoMode( monitor );
glfwSetWindowMonitor( window, monitor, 0, 0, vmode->width, vmode->height, vmode->refreshRate );
}
else{
glfwSetWindowMonitor( window, NULL, wx, wy, ww, wh, GLFW_DONT_CARE );
}
}
TODO( "Fullscreen window seem to become unresponsive in Wayland session Ubuntu" )
void keyCallback( GLFWwindow* window, int key, int /*scancode*/, int action, int mods ) noexcept{
if( key == GLFW_KEY_ESCAPE && action == GLFW_PRESS ) glfwSetWindowShouldClose( window, GLFW_TRUE );
if( key == GLFW_KEY_ENTER && action == GLFW_PRESS && mods == GLFW_MOD_ALT ) toggleFullscreen( window );
}
int messageLoop( PlatformWindow window ){
using std::to_string;
while( errors.empty() && !glfwWindowShouldClose( window.window ) ){
if( hasSwapchain ) glfwPollEvents(); // do not block so I can paint
else glfwWaitEvents(); // allows blocking if no events
if( hasSwapchain ) paintEventHandler(); // repaint always even without OS repaint event
}
if( !errors.empty() ) throw to_string( errors.size() ) + " GLFW error(s) on backlog; 1st error: " + to_string( errors.front().error ) + ": " + errors.front().description;
return EXIT_SUCCESS;
}
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow ){
return glfwGetPhysicalDevicePresentationSupport( instance, device, queueFamilyIndex ) == GLFW_TRUE;
}
PlatformWindow initWindow( const std::string& name, const uint32_t canvasWidth, const uint32_t canvasHeight ){
std::string title = name + " -- GLFW";
glfwWindowHint( GLFW_CLIENT_API, GLFW_NO_API );
glfwWindowHint( GLFW_VISIBLE, GLFW_FALSE );
GLFWwindow* window = glfwCreateWindow( static_cast<int>(canvasWidth), static_cast<int>(canvasHeight), title.c_str(), NULL, NULL );
if( !window ) throw "Trouble creating GLFW window!";
glfwSetInputMode( window, GLFW_STICKY_KEYS, GLFW_TRUE );
glfwSetFramebufferSizeCallback( window, windowSizeCallback );
glfwSetWindowRefreshCallback( window, windowRefreshCallback );
glfwSetKeyCallback( window, keyCallback );
return { window };
}
void killWindow( PlatformWindow window ){
glfwDestroyWindow( window.window );
}
VkSurfaceKHR initSurface( const VkInstance instance, const PlatformWindow window ){
VkSurfaceKHR surface;
const VkResult errorCode = glfwCreateWindowSurface( instance, window.window, nullptr, &surface ); RESULT_HANDLER( errorCode, "glfwCreateWindowSurface" );
return surface;
}
#endif //COMMON_GLFW_WSI_H
================================================
FILE: src/WSI/Wayland.h
================================================
// Wayland platform dependent WSI handling and event loop
#ifndef COMMON_WAYLAND_WSI_H
#define COMMON_WAYLAND_WSI_H
#include <functional>
#include <cstring>
#include <string>
#include <vector>
#include <memory>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include <linux/input-event-codes.h>
#include <sys/mman.h>
#include <unistd.h>
#include <vulkan/vulkan.h>
#include "private/xdg-shell-client-protocol.h"
#include "private/xdg-shell-client-protocol-private.inl"
#include "CompilerMessages.h"
#include "ErrorHandling.h"
TODO( "Easier to use, but might prevent platform co-existence. Could be namespaced. Make all of this a class?" )
struct PlatformWindowImpl{
wl_display* display = nullptr;
wl_registry* registry = nullptr;
wl_compositor* compositor = nullptr; uint32_t compositorName;
xdg_wm_base* wmBase = nullptr; uint32_t wmBaseName;
wl_surface* surface = nullptr;
xdg_surface* xdgSurface = nullptr;
xdg_toplevel* toplevel = nullptr;
wl_seat* seat = nullptr; uint32_t seatName;
xkb_context* xkbContext = nullptr;
xkb_keymap* keymap = nullptr;
xkb_state* xkbState = nullptr;
wl_keyboard* keyboard = nullptr;
bool alt = false;
wl_pointer* pointer = nullptr;
bool quit = false;
std::string title;
bool inited = false;
bool hasSwapchain = false;
bool maximized = false, fullscreen = false;
uint32_t width, height;
uint32_t restoredWidth, restoredHeight;
};
struct PlatformWindow{
std::shared_ptr<PlatformWindowImpl> impl;
};
std::string getPlatformSurfaceExtensionName(){ return VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME; };
int messageLoop( PlatformWindow window );
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window );
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight );
void killWindow( PlatformWindow window );
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window );
// killSurface() is not platform dependent
void setSizeEventHandler( std::function<void(void)> newSizeEventHandler );
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler );
void showWindow( PlatformWindow window );
uint32_t getWindowWidth( PlatformWindow window ){ return window.impl->width; }
uint32_t getWindowHeight( PlatformWindow window ){ return window.impl->height; }
// Implementation
//////////////////////////////////
bool nullHandler(){ return false; }
std::function<bool(void)> sizeEventHandler = nullHandler;
void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
if( !newSizeEventHandler ) sizeEventHandler = nullHandler;
sizeEventHandler = newSizeEventHandler;
}
std::function<void(void)> paintEventHandler = nullHandler;
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler ){
if( !newPaintEventHandler ) paintEventHandler = nullHandler;
paintEventHandler = newPaintEventHandler;
}
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window ){
return vkGetPhysicalDeviceWaylandPresentationSupportKHR( device, queueFamilyIndex, window.impl->display ) == VK_TRUE;
}
void registryGlobalHandler( void* data, wl_registry* registry, uint32_t name, const char* interface, uint32_t version ) noexcept{
//logger << "registry: " << interface << std::endl;
PlatformWindowImpl* window = (PlatformWindowImpl*)data;
if( std::strcmp( interface, "wl_compositor" ) == 0 && !window->compositor ){
window->compositor = (wl_compositor*)wl_registry_bind( registry, name, &wl_compositor_interface, 1 );
window->compositorName = name;
}
else if( std::strcmp( interface, "xdg_wm_base" ) == 0 && !window->wmBase ){
window->wmBase = (xdg_wm_base*)wl_registry_bind( registry, name, &xdg_wm_base_interface, 1 );
window->wmBaseName = name;
}
else if( std::strcmp( interface, "wl_seat" ) == 0 && !window->seat ){
window->seat = (wl_seat*)wl_registry_bind( registry, name, &wl_seat_interface, 1 );
window->seatName = name;
}
TODO( "Add Wayland server side decorations when supported." );
}
void registryGlobalRemoveHandler( void* data, wl_registry* registry, uint32_t name ) noexcept{
//logger << "registry remove " << name << std::endl;
PlatformWindowImpl* window = (PlatformWindowImpl*)data;
if( name == window->compositorName ) throw "Needed wl_compositor removed from registry.";
if( name == window->wmBaseName ) throw "Needed xdg_wm_base removed from registry.";
if( name == window->seatName ) throw "Needed wl_seat removed from registry.";
}
void seatCapsHandler( void* data, wl_seat* seat, uint32_t caps ) noexcept{
//logger << "wl_seat caps: " << caps << std::endl;
RUNTIME_ASSERT( caps & WL_SEAT_CAPABILITY_POINTER, "Obtaining pointer wl_seat" );
RUNTIME_ASSERT( caps & WL_SEAT_CAPABILITY_KEYBOARD, "Obtaining keyboard wl_seat" );
}
void keyboardKeymapHandler( void* data, wl_keyboard* keyboard, uint32_t format, int32_t fd, uint32_t size ) noexcept{
//logger << "wl_keyboard format: " << format << ", keymap size: " << size << std::endl;
RUNTIME_ASSERT( format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, "Obtaining XKB V1 keymap" );
PlatformWindowImpl* wnd = (PlatformWindowImpl*)data;
assert( wnd->xkbContext );
xkb_state_unref( wnd->xkbState );
xkb_keymap_unref( wnd->keymap );
const char* keymapBuffer = (const char*)mmap( nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0 ); RUNTIME_ASSERT( keymapBuffer != MAP_FAILED, "mmap of keymap")
wnd->keymap = xkb_keymap_new_from_string( wnd->xkbContext, keymapBuffer, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS ); RUNTIME_ASSERT( wnd->keymap, "xkb_keymap_new_from_string" );
{const auto err = munmap( (void*)keymapBuffer, size ); RUNTIME_ASSERT( !err, "munmap of keymap" );}
{const auto err = close( fd ); RUNTIME_ASSERT( !err, "close of keymap fd" );}
wnd->xkbState = xkb_state_new( wnd->keymap );
}
void keyboardKeyHandler( void* data, wl_keyboard* keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state ) noexcept{
//logger << "wl_keyboard key: key=" << key << ", state=" << state << std::endl;
PlatformWindowImpl* wnd = (PlatformWindowImpl*)data;
const xkb_keycode_t keycode = key + 8; // +8 per Wayland spec
const xkb_keysym_t keysym = xkb_state_key_get_one_sym( wnd->xkbState, keycode );
if( state == WL_KEYBOARD_KEY_STATE_PRESSED ){
switch( keysym ){
case XKB_KEY_Escape:
wnd->quit = true;
break;
case XKB_KEY_Alt_L:
wnd->alt = true;
break;
case XKB_KEY_Return:
if( wnd->alt ){
if( wnd->fullscreen ) xdg_toplevel_unset_fullscreen( wnd->toplevel );
else xdg_toplevel_set_fullscreen( wnd->toplevel, nullptr );
}
break;
}
}
else if( state == WL_KEYBOARD_KEY_STATE_RELEASED ){
switch( keysym ){
case XKB_KEY_Alt_L:
wnd->alt = false;
break;
}
}
}
void pointerButtonHandler( void* data, wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state ){
PlatformWindowImpl* wnd = (PlatformWindowImpl*)data;
if( state == WL_POINTER_BUTTON_STATE_PRESSED ){
switch( button ){
case BTN_LEFT:
xdg_toplevel_move( wnd->toplevel, wnd->seat, serial );
break;
case BTN_RIGHT:
xdg_toplevel_resize( wnd->toplevel, wnd->seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT );
break;
}
}
}
void xdgSurfaceConfigureHandler( void* data, xdg_surface* xdg_surface, uint32_t serial ) noexcept{
//logger << "xdg_surface config" << std::endl;
PlatformWindowImpl* window = (PlatformWindowImpl*)data;
assert( window->inited );
xdg_surface_ack_configure( xdg_surface, serial );
}
const char* state_to_string( uint32_t state ){
switch( state ){
case XDG_TOPLEVEL_STATE_MAXIMIZED: return "maximized";
case XDG_TOPLEVEL_STATE_FULLSCREEN: return "fullscreen";
case XDG_TOPLEVEL_STATE_RESIZING: return "resizing";
case XDG_TOPLEVEL_STATE_ACTIVATED: return "activated";
// v2:
case XDG_TOPLEVEL_STATE_TILED_LEFT: return "tiled_left";
case XDG_TOPLEVEL_STATE_TILED_RIGHT: return "tiled_right";
case XDG_TOPLEVEL_STATE_TILED_TOP: return "tiled_top";
case XDG_TOPLEVEL_STATE_TILED_BOTTOM: return "tiled_bottom";
default: return "unrecognized state";
}
}
bool hasState( const std::vector<uint32_t>& states, const uint32_t state ){
for( const auto s : states ) if( s == state ) return true;
return false;
}
void toplevelConfigureHandler( void* data, xdg_toplevel* toplevel, int32_t width, int32_t height, wl_array* states ) noexcept{
//logger << "toplevel config: w=" << width << ", h=" << height << ", states={";
std::vector<uint32_t> statesv;
for( uint32_t* state = (uint32_t*)states->data; (const uint8_t*) state < ((const uint8_t*) states->data + states->size); ++state ){
statesv.push_back( *state );
}
/*
bool first = true;
for( const auto state : statesv ){
if( !first ) logger << ", ";
else first = false;
logger << state_to_string( state );
}
logger << "}\n";
*/
PlatformWindowImpl* wnd = (PlatformWindowImpl*)data;
assert( wnd->inited );
if( width != wnd->width || height != wnd->height ){
if( !(width == 0 && height == 0) ){
wnd->width = static_cast<uint32_t>( width );
wnd->height = static_cast<uint32_t>( height );
if( !hasState( statesv, XDG_TOPLEVEL_STATE_MAXIMIZED )
&& !hasState( statesv, XDG_TOPLEVEL_STATE_FULLSCREEN ) ){
wnd->restoredWidth = static_cast<uint32_t>( width );
wnd->restoredHeight = static_cast<uint32_t>( height );
}
}
else{
wnd->width = wnd->restoredWidth;
wnd->height = wnd->restoredHeight;
}
wnd->hasSwapchain = sizeEventHandler();
}
if( hasState( statesv, XDG_TOPLEVEL_STATE_FULLSCREEN ) ) wnd->fullscreen = true;
else wnd->fullscreen = false;
if( hasState( statesv, XDG_TOPLEVEL_STATE_MAXIMIZED ) ) wnd->maximized = true;
else wnd->maximized = false;
}
void toplevelCloseHandler( void* data, xdg_toplevel* xdg_toplevel ) noexcept{
//logger << "toplevel close" << std::endl;
PlatformWindowImpl* window = (PlatformWindowImpl*)data;
window->quit = true;
}
void wmBasePingHandler( void* data, xdg_wm_base* xdg_wm_base, uint32_t serial ) noexcept{
//logger << "wm_base ping" << std::endl;
xdg_wm_base_pong( xdg_wm_base, serial );
}
const wl_registry_listener registryListener = { registryGlobalHandler, registryGlobalRemoveHandler };
const wl_seat_listener seatListener = { seatCapsHandler, [](void*, wl_seat*, const char*){} };
TODO( "Should probably implement some of these callbacks.");
const wl_keyboard_listener keyboardListener = { keyboardKeymapHandler, [](void*, wl_keyboard*, uint32_t, wl_surface*, wl_array*){}, [](void*, wl_keyboard*, uint32_t, wl_surface*){}, keyboardKeyHandler, [](void*, wl_keyboard*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t){} };
const wl_pointer_listener pointerListener = { [](void*, wl_pointer*, uint32_t, wl_surface*, wl_fixed_t, wl_fixed_t){}, [](void*, wl_pointer*, uint32_t, wl_surface*){}, [](void*, wl_pointer*, uint32_t, wl_fixed_t, wl_fixed_t){}, pointerButtonHandler, [](void*, wl_pointer*, uint32_t, uint32_t, wl_fixed_t){} };
const xdg_wm_base_listener xdgWmBaseListerner = { wmBasePingHandler };
const xdg_surface_listener xdgSurfaceListener = { xdgSurfaceConfigureHandler };
const xdg_toplevel_listener xdgToplevelListener = { toplevelConfigureHandler, toplevelCloseHandler };
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight ){
auto wnd = std::make_shared<PlatformWindowImpl>();
wnd->width = canvasWidth;
wnd->restoredWidth = canvasWidth;
wnd->height = canvasHeight;
wnd->restoredHeight = canvasHeight;
wnd->display = wl_display_connect( nullptr ); RUNTIME_ASSERT( wnd->display, "wl_display_connect" );
wnd->registry = wl_display_get_registry( wnd->display ); RUNTIME_ASSERT( wnd->registry, "wl_display_get_registry" );
{const auto err = wl_registry_add_listener( wnd->registry, &::registryListener, wnd.get() ); RUNTIME_ASSERT( !err, "wl_registry_add_listener" );}
{const auto dispatched = wl_display_roundtrip( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_roundtrip" );}
// dispatch should be redundant to the roundtrip, but I like it explicit
{const auto dispatched = wl_display_dispatch_pending( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_dispatch_pending" );}
RUNTIME_ASSERT( wnd->compositor, "wl_registry_bind(\"wl_compositor\")" );
RUNTIME_ASSERT( wnd->wmBase, "wl_registry_bind(\"xdg_wm_base\")" );
RUNTIME_ASSERT( wnd->seat, "wl_registry_bind(\"wl_seat\")" );
wnd->xkbContext = xkb_context_new( XKB_CONTEXT_NO_FLAGS ); RUNTIME_ASSERT( wnd->xkbContext, "xkb_context_new" );
{const auto err = wl_seat_add_listener( wnd->seat, &seatListener, wnd.get() ); RUNTIME_ASSERT( !err, "wl_seat_add_listener" );}
{const auto dispatched = wl_display_roundtrip( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_roundtrip" );}
{const auto dispatched = wl_display_dispatch_pending( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_dispatch_pending" );}
wnd->keyboard = wl_seat_get_keyboard( wnd->seat ); RUNTIME_ASSERT( wnd->keyboard, "wl_seat_get_keyboard" );
{const auto err = wl_keyboard_add_listener( wnd->keyboard, &keyboardListener, wnd.get() ); RUNTIME_ASSERT( !err, "wl_keyboard_add_listener" );}
wnd->pointer = wl_seat_get_pointer( wnd->seat ); RUNTIME_ASSERT( wnd->pointer, "wl_seat_get_pointer" );
{const auto err = wl_pointer_add_listener( wnd->pointer, &pointerListener, wnd.get() ); RUNTIME_ASSERT( !err, "wl_pointer_add_listener" );}
wnd->surface = wl_compositor_create_surface( wnd->compositor ); RUNTIME_ASSERT( wnd->surface, "wl_compositor_create_surface" );
{const auto err = xdg_wm_base_add_listener( wnd->wmBase, &xdgWmBaseListerner, wnd.get() ); RUNTIME_ASSERT( !err, "xdg_wm_base_add_listener" );}
wnd->xdgSurface = xdg_wm_base_get_xdg_surface( wnd->wmBase, wnd->surface ); RUNTIME_ASSERT( wnd->xdgSurface, "xdg_wm_base_get_xdg_surface" );
wnd->toplevel = xdg_surface_get_toplevel( wnd->xdgSurface ); RUNTIME_ASSERT( wnd->toplevel, "xdg_surface_get_toplevel" );
{const auto err = xdg_surface_add_listener( wnd->xdgSurface, &xdgSurfaceListener, wnd.get() ); RUNTIME_ASSERT( !err, "xdg_surface_add_listener" );}
{const auto err = xdg_toplevel_add_listener( wnd->toplevel, &xdgToplevelListener, wnd.get() ); RUNTIME_ASSERT( !err, "xdg_toplevel_add_listener" );}
wnd->title = name + " -- Wayland";
xdg_toplevel_set_title( wnd->toplevel, "wnd->title.c_str()" );
TODO( "Shows \"Unknown\" in Ubuntu on taskbar.")
wl_surface_commit( wnd->surface );
{const auto sent = wl_display_flush( wnd->display ); RUNTIME_ASSERT( sent != -1, "wl_display_flush" );}
wnd->inited = true;
return {wnd};
}
void killWindow( PlatformWindow window ){
const auto wnd = window.impl;
wnd->inited = false;
xkb_state_unref( wnd->xkbState );
xkb_keymap_unref( wnd->keymap );
xkb_context_unref( wnd->xkbContext );
TODO( "Explicitly kill all Wayland stuff" );
wl_display_disconnect( wnd->display );
}
void showWindow( PlatformWindow windowh ){
auto wnd = windowh.impl;
// wait for first size event
{const auto dispatched = wl_display_roundtrip( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_roundtrip" );}
// dispatch should be redundant to the roundtrip, but I like it explicit
{const auto dispatched = wl_display_dispatch_pending( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_dispatch_pending" );}
assert( wnd->hasSwapchain );
// In Wayland window is mapped with first present
paintEventHandler();
}
int messageLoop( PlatformWindow windowh ){
auto wnd = windowh.impl;
wnd->quit = false;
while( !wnd->quit ){
{const auto dispatched = wl_display_dispatch_pending( wnd->display ); RUNTIME_ASSERT( dispatched != -1, "wl_display_dispatch_pending" );}
if( wnd->hasSwapchain ){
TODO( "Use frame callback instead?" );
paintEventHandler();
}
}
return 0;
}
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow windowh ){
auto wnd = windowh.impl;
const VkWaylandSurfaceCreateInfoKHR surfaceInfo{
VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
nullptr, // pNext for extensions use
0, // flags - reserved for future use
wnd->display,
wnd->surface
};
VkSurfaceKHR surface;
VkResult errorCode = vkCreateWaylandSurfaceKHR( instance, &surfaceInfo, nullptr, &surface ); RESULT_HANDLER( errorCode, "vkCreateWaylandSurfaceKHR" );
return surface;
}
#endif //COMMON_WAYLAND_WSI_H
================================================
FILE: src/WSI/Win32.h
================================================
// win32 platform dependent WSI handling and event loop
#ifndef COMMON_WIN32_WSI_H
#define COMMON_WIN32_WSI_H
#include <functional>
#include <string>
#include <vector>
#include <cassert>
#include <Windows.h>
#include <vulkan/vulkan.h>
#include "CompilerMessages.h"
#include "ErrorHandling.h"
TODO( "Easier to use, but might prevent platform co-existence. Could be namespaced. Make all of this a class?" )
struct PlatformWindow{ HINSTANCE hInstance; HWND hWnd; };
DWORD windowedStyle = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
DWORD windowedExStyle = WS_EX_OVERLAPPEDWINDOW;
std::string getPlatformSurfaceExtensionName(){ return VK_KHR_WIN32_SURFACE_EXTENSION_NAME; };
int messageLoop( PlatformWindow window );
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window );
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight );
void killWindow( PlatformWindow window );
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window );
// killSurface() is not platform dependent
void setSizeEventHandler( std::function<void(void)> newSizeEventHandler );
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler );
void showWindow( PlatformWindow window );
// Implementation
//////////////////////////////////
bool nullHandler(){ return false; }
std::function<bool(void)> sizeEventHandler = nullHandler;
void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
if( !newSizeEventHandler ) sizeEventHandler = nullHandler;
sizeEventHandler = newSizeEventHandler;
}
std::function<void(void)> paintEventHandler = nullHandler;
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler ){
if( !newPaintEventHandler ) paintEventHandler = nullHandler;
paintEventHandler = newPaintEventHandler;
}
void showWindow( PlatformWindow window ){
ShowWindow( window.hWnd, SW_SHOW );
SetForegroundWindow( window.hWnd );
}
bool hasSwapchain = false;
int messageLoop( PlatformWindow window ){
UNREFERENCED_PARAMETER( window );
MSG msg;
BOOL ret = GetMessageW( &msg, NULL, 0, 0 );
for( ; ret; ret = GetMessageW( &msg, NULL, 0, 0 ) ){
TranslateMessage( &msg );
DispatchMessageW( &msg ); //dispatch to wndProc; ignore return from wndProc
}
if( ret == -1 ) throw std::string( "Trouble getting message: " ) + std::to_string( GetLastError() );
return static_cast<int>( msg.wParam );
}
void toggleFullscreen( HWND hWnd ){
TODO( "All of this needs to be made a class... death to the static!" )
static bool isFullscreen = false;
static int wx = 100;
static int wy = 100;
static int ww = 800;
static int wh = 800;
DWORD style, exStyle;
int x, y, width, height;
if( !isFullscreen ){
windowedStyle = GetWindowLongW( hWnd, GWL_STYLE );
if( !windowedStyle && GetLastError() != ERROR_SUCCESS ) throw std::string( "Trouble setting window style: " ) + std::to_string( GetLastError() );
windowedExStyle = GetWindowLongW( hWnd, GWL_EXSTYLE );
if( !windowedExStyle && GetLastError() != ERROR_SUCCESS ) throw std::string( "Trouble setting window ex style: " ) + std::to_string( GetLastError() );
const DWORD fullscreenStyle = ::windowedStyle & ~(WS_CAPTION | WS_THICKFRAME);
const DWORD fullscreenExStyle = ::windowedExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
style = fullscreenStyle;
exStyle = fullscreenExStyle;
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof( MONITORINFO );
{
const auto succ = GetMonitorInfo( MonitorFromWindow( hWnd, MONITOR_DEFAULTTOPRIMARY ), &monitorInfo );
if( !succ ) throw std::string( "Trouble getting window monitor info: " ) + std::to_string( GetLastError() );
}
x = monitorInfo.rcMonitor.left;
y = monitorInfo.rcMonitor.top;
width = monitorInfo.rcMonitor.right - x; assert( width >= 0 );
height = monitorInfo.rcMonitor.bottom - y; assert( height >= 0 );
RECT wndRect;
{
const auto succ = GetWindowRect( hWnd, &wndRect );
if( !succ ) throw std::string( "Trouble getting window dimensions: " ) + std::to_string( GetLastError() );
}
wx = wndRect.left;
wy = wndRect.top;
ww = wndRect.right - wx;
wh = wndRect.bottom - wy;
}
else {
style = ::windowedStyle;
exStyle = ::windowedExStyle;
x = wx;
y = wy;
width = ww;
height = wh;
}
isFullscreen = !isFullscreen;
assert( GetLastError() == ERROR_SUCCESS );
{
const auto succ = SetWindowLongW( hWnd, GWL_STYLE, style );
if( !succ && GetLastError() != ERROR_SUCCESS ) throw std::string( "Trouble setting window style: " ) + std::to_string( GetLastError() );
}
{
const auto succ = SetWindowLongW( hWnd, GWL_EXSTYLE, exStyle );
if( !succ && GetLastError() != ERROR_SUCCESS ) throw std::string( "Trouble setting window ex style: " ) + std::to_string( GetLastError() );
}
{
const auto succ = SetWindowPos( hWnd, HWND_TOP, x, y, width, height, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOZORDER );
if( !succ ) throw std::string( "Trouble resizing window to fullscreen: " ) + std::to_string( GetLastError() );
}
}
LRESULT CALLBACK wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ){
switch( uMsg ){
case WM_CLOSE:
PostQuitMessage( 0 );
return 0;
case WM_ERASEBKGND:
//logger << "erase\n";
//return DefWindowProc( hWnd, uMsg, wParam, lParam );
return 0; // background will be cleared by Vulkan in WM_PAINT instead
//case WM_NCCALCSIZE:
// //logger << "nccalcsize " << wParam << " " << lParam << endl;
// return DefWindowProc( hWnd, uMsg, wParam, lParam );
case WM_SIZE:
//logger << "size " << wParam << " " << LOWORD( lParam ) << "x" << HIWORD( lParam ) << std::endl;
hasSwapchain = sizeEventHandler();
if( !hasSwapchain ) ValidateRect( hWnd, NULL ); // prevent WM_PAINT on minimized window
return 0;
case WM_PAINT: // sent after WM_SIZE -- react to this immediately to resize seamlessly
//logger << "paint\n";
paintEventHandler();
//ValidateRect( hWnd, NULL ); // never validate so window always gets redrawn
return 0;
case WM_KEYDOWN:
switch( wParam ){
case VK_ESCAPE:
PostQuitMessage( 0 );
return 0;
default:
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
case WM_SYSCOMMAND:
switch( wParam ){
case SC_KEYMENU:
if( lParam == VK_RETURN ){ // Alt-Enter without "no sysmenu hotkey exists" beep
toggleFullscreen( hWnd );
return 0;
}
else return DefWindowProc( hWnd, uMsg, wParam, lParam );
default:
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
default:
//logger << "unknown " << std::hex << std::showbase << uMsg << std::endl;
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
}
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow ){
UNREFERENCED_PARAMETER( instance );
return vkGetPhysicalDeviceWin32PresentationSupportKHR( device, queueFamilyIndex ) == VK_TRUE;
}
ATOM classAtom = 0;
uint64_t classAtomRefCount = 0;
ATOM initWindowClass(){
HINSTANCE hInstance = GetModuleHandleW( NULL );
if( classAtom == 0 ){
WNDCLASSEXW windowClass = {
sizeof( WNDCLASSEXW ), // cbSize
CS_OWNDC /*| CS_HREDRAW | CS_VREDRAW*/, // style -- some window behavior
wndProc, // lpfnWndProc -- set event handler
0, // cbClsExtra -- set 0 extra bytes after class
0, // cbWndExtra -- set 0 extra bytes after class instance
hInstance, // hInstance
LoadIconW( NULL, IDI_APPLICATION ), // hIcon -- application icon
LoadCursorW( NULL, IDC_ARROW ), // hCursor -- cursor inside
NULL, //(HBRUSH)( COLOR_WINDOW + 1 ), // hbrBackground
NULL, // lpszMenuName -- menu class name
TEXT("vkwc"), // lpszClassName -- window class name/identificator
LoadIconW( NULL, IDI_APPLICATION ) // hIconSm
};
// register window class
classAtom = RegisterClassExW( &windowClass );
if( !classAtom ){
throw std::string( "Trouble registering window class: " ) + std::to_string( GetLastError() );
}
}
++classAtomRefCount;
return classAtom;
}
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight ){
using std::string;
using std::to_string;
const string title = name + " -- Win32";
const auto titleU16Size = MultiByteToWideChar( CP_UTF8, 0, title.c_str(), -1, nullptr, 0 );
std::vector<wchar_t> titleU16( titleU16Size );
const auto success = MultiByteToWideChar( CP_UTF8, 0, title.c_str(), -1, titleU16.data(), titleU16Size );
HINSTANCE hInstance = GetModuleHandleW( NULL );
ATOM wndClassAtom = initWindowClass();
// adjust size of window to contain given size canvas
RECT windowRect = { 0, 0, static_cast<LONG>(canvasWidth), static_cast<LONG>(canvasHeight) };
if( !AdjustWindowRectEx( &windowRect, ::windowedStyle, FALSE, ::windowedExStyle ) ){
throw string( "Trouble adjusting window size: " ) + to_string( GetLastError() );
}
// create window instance
HWND hWnd = CreateWindowExW(
::windowedExStyle,
MAKEINTATOM(wndClassAtom),
titleU16.data(),
::windowedStyle,
CW_USEDEFAULT, CW_USEDEFAULT,
windowRect.right - windowRect.left, windowRect.bottom - windowRect.top,
NULL,
NULL,
hInstance,
NULL
);
if( !hWnd ){
throw string( "Trouble creating window instance: " ) + to_string( GetLastError() );
}
return { hInstance, hWnd };
}
void killWindow( PlatformWindow window ){
using std::string;
using std::to_string;
if( !DestroyWindow( window.hWnd ) ) throw string( "Trouble destroying window instance: " ) + to_string( GetLastError() );
--classAtomRefCount;
if( classAtomRefCount == 0 ){
if( !UnregisterClassW( MAKEINTATOM(classAtom), window.hInstance ) ){
throw string( "Trouble unregistering window class: " ) + to_string( GetLastError() );
}
}
}
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window ){
VkWin32SurfaceCreateInfoKHR surfaceInfo{
VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
nullptr, // pNext for extensions use
0, // flags - reserved for future use
window.hInstance,
window.hWnd
};
VkSurfaceKHR surface;
VkResult errorCode = vkCreateWin32SurfaceKHR( instance, &surfaceInfo, nullptr, &surface ); RESULT_HANDLER( errorCode, "vkCreateWin32SurfaceKHR" );
return surface;
}
#endif //COMMON_WIN32_WSI_H
================================================
FILE: src/WSI/Xcb.h
================================================
// XCB linux platform dependent WSI handling and event loop
#ifndef COMMON_XCB_WSI_H
#define COMMON_XCB_WSI_H
#include <functional>
#include <string>
#include <cstring>
#include <xcb/xcb.h>
#include <xcb/xcb_util.h>
#include <xcb/xcb_keysyms.h>
#include <X11/keysym.h>
#include <vulkan/vulkan.h>
#include "CompilerMessages.h"
#include "ErrorHandling.h"
TODO( "Easier to use, but might prevent platform co-existence. Could be namespaced. Make all of this a class?" )
struct PlatformWindow{ xcb_connection_t* connection; xcb_window_t window; xcb_visualid_t visual_id; };
std::string getPlatformSurfaceExtensionName(){ return VK_KHR_XCB_SURFACE_EXTENSION_NAME; };
int messageLoop( PlatformWindow window );
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window );
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight );
void killWindow( PlatformWindow window );
VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window );
// killSurface() is not platform dependent
void setSizeEventHandler( std::function<void(void)> newSizeEventHandler );
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler );
void showWindow( PlatformWindow window );
// Implementation
//////////////////////////////////
bool nullHandler(){ return false; }
std::function<bool(void)> sizeEventHandler = nullHandler;
void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
if( !newSizeEventHandler ) sizeEventHandler = nullHandler;
sizeEventHandler = newSizeEventHandler;
}
std::function<void(void)> paintEventHandler = nullHandler;
void setPaintEventHandler( std::function<void(void)> newPaintEventHandler ){
if( !newPaintEventHandler ) paintEventHandler = nullHandler;
paintEventHandler = newPaintEventHandler;
}
void showWindow( PlatformWindow window ){
xcb_map_window( window.connection, window.window );
xcb_flush( window.connection );
}
int messageLoop( PlatformWindow window ){
int width = -1;
int height = -1;
bool hasSwapchain = false;
bool quit = false;
while( !quit ){
xcb_generic_event_t* e = hasSwapchain ? xcb_poll_for_event( window.connection ) : xcb_wait_for_event( window.connection );
if( e ){
switch( e->response_type & ~0x80 ){
case XCB_EXPOSE:
paintEventHandler();
break;
case XCB_CONFIGURE_NOTIFY:{
xcb_configure_notify_event_t* ce = (xcb_configure_notify_event_t*)e;
if( ce->width != width || ce->height != height ){
width = ce->width;
height = ce->height;
hasSwapchain = sizeEventHandler();
}
break;
}
case XCB_KEY_PRESS:{
xcb_key_press_event_t* kpe = (xcb_key_release_event_t*)e;
xcb_key_symbols_t* keysyms = xcb_key_symbols_alloc( window.connection );
switch( xcb_key_press_lookup_keysym( keysyms, kpe, 0 ) ){
case XK_Escape:
quit = true;
}
xcb_key_symbols_free( keysyms );
/*
switch( kpe->detail ){
case 9: // ESC
quit = true;
}
*/
break;
}
case XCB_CLIENT_MESSAGE:{
xcb_client_message_event_t* cme = (xcb_client_message_event_t*)e;
xcb_intern_atom_cookie_t wmdelCookie = xcb_intern_atom( window.connection, 1, 16, "WM_DELETE_WINDOW" );
xcb_intern_atom_reply_t* wmdelReply = xcb_intern_atom_reply( window.connection, wmdelCookie, 0 );
xcb_atom_t ATOM_WM_DELETE_WINDOW = wmdelReply->atom;
free( wmdelReply );
if( cme->data.data32[0] == ATOM_WM_DELETE_WINDOW ){
quit = true;
}
break;
}
//default:
// throw "Unrecognized event type!";
}
free( e );
}
else if( hasSwapchain ){ // no events pending
paintEventHandler();
}
}
return 0;
}
bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice device, uint32_t queueFamilyIndex, PlatformWindow window ){
return vkGetPhysicalDeviceXcbPresentationSupportKHR( device, queueFamilyIndex, window.connection, window.visual_id ) == VK_TRUE;
}
std::string to_string_xcb_conn( int error ){
switch( error ){
case 0: return "XCB_CONN_SUCCESS";
case XCB_CONN_ERROR: return "XCB_CONN_ERROR";
case XCB_CONN_CLOSED_EXT_NOTSUPPORTED: return "XCB_CONN_CLOSED_EXT_NOTSUPPORTED";
case XCB_CONN_CLOSED_MEM_INSUFFICIENT: return "XCB_CONN_CLOSED_MEM_INSUFFICIENT";
case XCB_CONN_CLOSED_REQ_LEN_EXCEED: return "XCB_CONN_CLOSED_REQ_LEN_EXCEED";
case XCB_CONN_CLOSED_PARSE_ERR: return "XCB_CONN_CLOSED_PARSE_ERR";
case XCB_CONN_CLOSED_INVALID_SCREEN: return "XCB_CONN_CLOSED_INVALID_SCREEN";
default: return "unrecognized XCB connection error";
}
}
xcb_connection_t* initXcbConnection(){
xcb_connection_t* connection = xcb_connect( nullptr, nullptr );
int err = xcb_connection_has_error( connection );
if( err ){
xcb_disconnect( connection );
throw std::string( "Failed to create XCB connection: " ) + to_string_xcb_conn( err );
}
return connection;
}
void killXcbConnection( xcb_connection_t* connection ){
xcb_disconnect( connection );
}
PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth, uint32_t canvasHeight ){
xcb_connection_t* connection = initXcbConnection();
xcb_screen_t* screen = xcb_setup_roots_iterator( xcb_get_setup( connection ) ).data;
uint32_t masks =
/* XCB_CW_BACK_PIXMAP
| XCB_CW_BACK_PIXEL
| XCB_CW_BORDER_PIXMAP
| XCB_CW_BORDER_PIXEL
| XCB_CW_BIT_GRAVITY
| XCB_CW_WIN_GRAVITY
| XCB_CW_BACKING_STORE
| XCB_CW_BACKING_PLANES
| XCB_CW_BACKING_PIXEL
| XCB_CW_OVERRIDE_REDIRECT
| XCB_CW_SAVE_UNDER
|*/ XCB_CW_EVENT_MASK
/*| XCB_CW_DONT_PROPAGATE
| XCB_CW_COLORMAP
| XCB_CW_CURSOR*/
;
uint32_t values[] = {
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
xcb_window_t window = xcb_generate_id( connection );
xcb_create_window(
connection,
XCB_COPY_FROM_PARENT,
window,
screen->root,
0, 0, // x, y
static_cast<uint16_t>( canvasWidth ), static_cast<uint16_t>( canvasHeight ),
1, //border_width
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
masks,
values
);
std::string title = name + " -- XCB";
xcb_change_property( connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, title.size(), title.c_str() );
xcb_change_property( connection, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_ICON_NAME, XCB_ATOM_STRING, 8, title.size(), title.c_str() );
xcb_intern_atom_cookie_t wmprotCookie = xcb_intern_atom( connection, 1, 12, "WM_PROTOCOLS" );
xcb_intern_atom_reply_t* wmprotReply = xcb_intern_atom_reply( connection, wmprotCookie, 0 );
xcb_atom_t ATOM_WM_PROTOCOLS = wmprotReply->atom;
free( wmprotReply );
xcb_intern_atom_cookie_t wmdelCookie = xcb_intern_atom( connection, 0, 16, "WM_DELETE_WINDOW" );
xcb_intern_atom_reply_t* wmdelReply = xcb_intern_atom_reply( connection, wmdelCookie, 0 );
xcb
gitextract_0sir708e/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── buildCI.yml
├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── doc/
│ └── synchronizationTutorial.md
└── src/
├── CompilerMessages.h
├── EnumerateScheme.h
├── ErrorHandling.h
├── ExtensionLoader.h
├── HelloTriangle.cpp
├── LeanWindowsEnvironment.h
├── Vertex.h
├── VulkanEnvironment.h
├── VulkanIntrospection.h
├── WSI/
│ ├── Glfw.h
│ ├── Wayland.h
│ ├── Win32.h
│ ├── Xcb.h
│ ├── Xlib.h
│ └── private/
│ ├── xdg-shell-client-protocol-private.inl
│ └── xdg-shell-client-protocol.h
├── Wsi.h
└── shaders/
├── hello_triangle.frag
└── hello_triangle.vert
SYMBOL INDEX (298 symbols across 12 files)
FILE: src/ErrorHandling.h
type VulkanResultException (line 14) | struct VulkanResultException{
type DebugObjectVariant (line 55) | struct DebugObjectVariant{
function genericDebugCallback (line 71) | void genericDebugCallback( std::string flags, Highlight highlight, std::...
function VKAPI_CALL (line 89) | VKAPI_CALL genericDebugReportCallback(
function VKAPI_CALL (line 114) | VKAPI_CALL genericDebugUtilsCallback(
function VkDebugReportFlagsEXT (line 145) | VkDebugReportFlagsEXT translateFlags( const VkDebugUtilsMessageSeverityF...
function DebugObjectVariant (line 158) | DebugObjectVariant initDebug( const VkInstance instance, const DebugObje...
function killDebug (line 193) | void killDebug( const VkInstance instance, const DebugObjectVariant debu...
FILE: src/ExtensionLoader.h
function populatePhysicalDeviceInstaceMap (line 53) | void populatePhysicalDeviceInstaceMap( const VkInstance instance ){
function unloadInstanceExtensionsCommands (line 72) | void unloadInstanceExtensionsCommands( const VkInstance instance ){
function unloadPDProps2Commands (line 164) | void unloadPDProps2Commands( VkInstance instance ){
function VKAPI_CALL (line 174) | VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR( VkPhysicalDevice physicalDev...
function VKAPI_CALL (line 180) | VKAPI_CALL vkGetPhysicalDeviceProperties2KHR( VkPhysicalDevice physicalD...
function VKAPI_CALL (line 186) | VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR( VkPhysicalDevice phy...
function VKAPI_CALL (line 192) | VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR( VkPhysicalDevic...
function VKAPI_CALL (line 198) | VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR( VkPhysicalDevic...
function VKAPI_CALL (line 204) | VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR( VkPhysicalDevice phy...
function VKAPI_CALL (line 210) | VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( VkPhysica...
function loadDebugReportCommands (line 223) | void loadDebugReportCommands( VkInstance instance ){
function unloadDebugReportCommands (line 239) | void unloadDebugReportCommands( VkInstance instance ){
function VKAPI_CALL (line 245) | VKAPI_CALL vkCreateDebugReportCallbackEXT(
function VKAPI_CALL (line 255) | VKAPI_CALL vkDestroyDebugReportCallbackEXT(
function VKAPI_CALL (line 264) | VKAPI_CALL vkDebugReportMessageEXT(
function loadDebugUtilsCommands (line 285) | void loadDebugUtilsCommands( VkInstance instance ){
function unloadDebugUtilsCommands (line 301) | void unloadDebugUtilsCommands( VkInstance instance ){
function VKAPI_CALL (line 307) | VKAPI_CALL vkCreateDebugUtilsMessengerEXT(
function VKAPI_CALL (line 317) | VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(
function VKAPI_CALL (line 326) | VKAPI_CALL vkSubmitDebugUtilsMessageEXT(
function loadExternalMemoryCapsCommands (line 341) | void loadExternalMemoryCapsCommands( VkInstance instance ){
function unloadExternalMemoryCapsCommands (line 351) | void unloadExternalMemoryCapsCommands( VkInstance instance ){
function VKAPI_CALL (line 355) | VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHR(
function loadExternalMemoryCommands (line 369) | void loadExternalMemoryCommands( VkDevice ){
function unloadExternalMemoryCommands (line 373) | void unloadExternalMemoryCommands( VkDevice ){
function loadExternalMemoryWin32Commands (line 385) | void loadExternalMemoryWin32Commands( VkDevice device ){
function unloadExternalMemoryWin32Commands (line 397) | void unloadExternalMemoryWin32Commands( VkDevice device ){
function VKAPI_CALL (line 402) | VKAPI_CALL vkGetMemoryWin32HandleKHR(
function VKAPI_CALL (line 411) | VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR(
function loadDedicatedAllocationCommands (line 425) | void loadDedicatedAllocationCommands( VkDevice ){
function unloadDedicatedAllocationCommands (line 429) | void unloadDedicatedAllocationCommands( VkDevice ){
FILE: src/HelloTriangle.cpp
type ResourceType (line 118) | enum class ResourceType{ Buffer, Image }
function helloTriangle (line 242) | int helloTriangle() try{
function WinMain (line 615) | int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ){
function main (line 619) | int main(){
function isLayerSupported (line 627) | bool isLayerSupported( const char* layer, const vector<VkLayerProperties...
function isExtensionSupported (line 635) | bool isExtensionSupported( const char* extension, const vector<VkExtensi...
function checkInstanceLayerSupport (line 643) | vector<const char*> checkInstanceLayerSupport( const vector<const char*>...
function checkInstanceLayerSupport (line 654) | vector<const char*> checkInstanceLayerSupport( const vector<const char*>...
function getSupportedInstanceExtensions (line 658) | vector<VkExtensionProperties> getSupportedInstanceExtensions( const vect...
function getSupportedDeviceExtensions (line 669) | vector<VkExtensionProperties> getSupportedDeviceExtensions( const VkPhys...
function checkExtensionSupport (line 680) | bool checkExtensionSupport( const vector<const char*>& extensions, const...
function checkDeviceExtensionSupport (line 693) | bool checkDeviceExtensionSupport( const VkPhysicalDevice physDevice, con...
function killInstance (line 757) | void killInstance( const VkInstance instance ){
function isPresentationSupported (line 765) | bool isPresentationSupported( const VkPhysicalDevice physDevice, const u...
function isPresentationSupported (line 772) | bool isPresentationSupported( const VkPhysicalDevice physDevice, const V...
function VkPhysicalDevice (line 783) | VkPhysicalDevice getPhysicalDevice( const VkInstance instance, const VkS...
function VkPhysicalDeviceProperties (line 826) | VkPhysicalDeviceProperties getPhysicalDeviceProperties( VkPhysicalDevice...
function VkPhysicalDeviceMemoryProperties (line 832) | VkPhysicalDeviceMemoryProperties getPhysicalDeviceMemoryProperties( VkPh...
function getQueueFamilyProperties (line 838) | vector<VkQueueFamilyProperties> getQueueFamilyProperties( VkPhysicalDevi...
function getQueueFamilies (line 848) | std::pair<uint32_t, uint32_t> getQueueFamilies( const VkPhysicalDevice p...
function VkDevice (line 890) | VkDevice initDevice(
function killDevice (line 946) | void killDevice( const VkDevice device ){
function VkQueue (line 952) | VkQueue getQueue( const VkDevice device, const uint32_t queueFamily, con...
function VkMemoryRequirements (line 964) | VkMemoryRequirements getMemoryRequirements< ResourceType::Buffer >( VkDe...
function VkMemoryRequirements (line 972) | VkMemoryRequirements getMemoryRequirements< ResourceType::Image >( VkDev...
function VkDeviceMemory (line 993) | VkDeviceMemory initMemory(
function setMemoryData (line 1033) | void setMemoryData( VkDevice device, VkDeviceMemory memory, void* begin,...
function killMemory (line 1040) | void killMemory( VkDevice device, VkDeviceMemory memory ){
function VkBuffer (line 1045) | VkBuffer initBuffer( VkDevice device, VkDeviceSize size, VkBufferUsageFl...
function killBuffer (line 1062) | void killBuffer( VkDevice device, VkBuffer buffer ){
function VkImage (line 1066) | VkImage initImage( VkDevice device, VkFormat format, uint32_t width, uin...
function killImage (line 1097) | void killImage( VkDevice device, VkImage image ){
function VkImageView (line 1101) | VkImageView initImageView( VkDevice device, VkImage image, VkFormat form...
function killImageView (line 1125) | void killImageView( VkDevice device, VkImageView imageView ){
function killSurface (line 1133) | void killSurface( VkInstance instance, VkSurfaceKHR surface ){
function VkSurfaceFormatKHR (line 1137) | VkSurfaceFormatKHR getSurfaceFormat( VkPhysicalDevice physicalDevice, Vk...
function VkSurfaceCapabilitiesKHR (line 1168) | VkSurfaceCapabilitiesKHR getSurfaceCapabilities( VkPhysicalDevice physic...
function VkPresentModeKHR (line 1178) | VkPresentModeKHR getSurfacePresentMode( VkPhysicalDevice physicalDevice,...
function VkSwapchainKHR (line 1215) | VkSwapchainKHR initSwapchain(
function killSwapchain (line 1268) | void killSwapchain( VkDevice device, VkSwapchainKHR swapchain ){
function getNextImageIndex (line 1272) | uint32_t getNextImageIndex( VkDevice device, VkSwapchainKHR swapchain, V...
function initSwapchainImageViews (line 1286) | vector<VkImageView> initSwapchainImageViews( VkDevice device, vector<VkI...
function killSwapchainImageViews (line 1298) | void killSwapchainImageViews( VkDevice device, vector<VkImageView>& imag...
function VkRenderPass (line 1305) | VkRenderPass initRenderPass( VkDevice device, VkSurfaceFormatKHR surface...
function killRenderPass (line 1377) | void killRenderPass( VkDevice device, VkRenderPass renderPass ){
function initFramebuffers (line 1381) | vector<VkFramebuffer> initFramebuffers(
function killFramebuffers (line 1410) | void killFramebuffers( VkDevice device, vector<VkFramebuffer>& framebuff...
function loadBinaryFile (line 1418) | vector<Type> loadBinaryFile( string filename ){
function VkShaderModule (line 1444) | VkShaderModule initShaderModule( VkDevice device, string filename ){
function VkShaderModule (line 1450) | VkShaderModule initShaderModule( VkDevice device, const vector<uint32_t>...
function killShaderModule (line 1465) | void killShaderModule( VkDevice device, VkShaderModule shaderModule ){
function VkPipelineLayout (line 1469) | VkPipelineLayout initPipelineLayout( VkDevice device ){
function killPipelineLayout (line 1486) | void killPipelineLayout( VkDevice device, VkPipelineLayout pipelineLayou...
function VkPipeline (line 1490) | VkPipeline initPipeline(
function killPipeline (line 1720) | void killPipeline( VkDevice device, VkPipeline pipeline ){
function setVertexData (line 1726) | void setVertexData( VkDevice device, VkDeviceMemory memory, vector<Verte...
function VkSemaphore (line 1731) | VkSemaphore initSemaphore( VkDevice device ){
function initSemaphores (line 1743) | vector<VkSemaphore> initSemaphores( VkDevice device, size_t count ){
function killSemaphore (line 1749) | void killSemaphore( VkDevice device, VkSemaphore semaphore ){
function killSemaphores (line 1753) | void killSemaphores( VkDevice device, vector<VkSemaphore>& semaphores ){
function VkCommandPool (line 1758) | VkCommandPool initCommandPool( VkDevice device, const uint32_t queueFami...
function killCommandPool (line 1771) | void killCommandPool( VkDevice device, VkCommandPool commandPool ){
function VkFence (line 1775) | VkFence initFence( const VkDevice device, const VkFenceCreateFlags flags...
function killFence (line 1787) | void killFence( const VkDevice device, const VkFence fence ){
function initFences (line 1791) | vector<VkFence> initFences( const VkDevice device, const size_t count, c...
function killFences (line 1797) | void killFences( const VkDevice device, vector<VkFence>& fences ){
function acquireCommandBuffers (line 1802) | void acquireCommandBuffers( VkDevice device, VkCommandPool commandPool, ...
function beginCommandBuffer (line 1824) | void beginCommandBuffer( VkCommandBuffer commandBuffer ){
function endCommandBuffer (line 1836) | void endCommandBuffer( VkCommandBuffer commandBuffer ){
function recordBeginRenderPass (line 1841) | void recordBeginRenderPass(
function recordEndRenderPass (line 1861) | void recordEndRenderPass( VkCommandBuffer commandBuffer ){
function recordBindPipeline (line 1865) | void recordBindPipeline( VkCommandBuffer commandBuffer, VkPipeline pipel...
function recordBindVertexBuffer (line 1869) | void recordBindVertexBuffer( VkCommandBuffer commandBuffer, const uint32...
function recordDraw (line 1874) | void recordDraw( VkCommandBuffer commandBuffer, const uint32_t vertexCou...
function submitToQueue (line 1878) | void submitToQueue( VkQueue queue, VkCommandBuffer commandBuffer, VkSema...
function present (line 1893) | void present( VkQueue queue, VkSwapchainKHR swapchain, uint32_t swapchai...
function cleanupUnsafeSemaphore (line 1907) | void cleanupUnsafeSemaphore( VkQueue queue, VkSemaphore semaphore ){
FILE: src/Vertex.h
type Vertex2D (line 6) | struct Vertex2D{
type ColorF (line 10) | struct ColorF{
type Vertex2D_ColorF_pack (line 14) | struct Vertex2D_ColorF_pack{
FILE: src/VulkanIntrospection.h
function handleToUint64 (line 13) | uint64_t handleToUint64(const PHANDLE_T *h) { return reinterpret_cast<ui...
function handleToUint64 (line 14) | inline uint64_t handleToUint64(const uint64_t h) { return h; }
FILE: src/WSI/Glfw.h
type PlatformWindow (line 21) | struct PlatformWindow{ GLFWwindow* window; }
function nullHandler (line 44) | bool nullHandler(){ return false; }
function setSizeEventHandler (line 48) | void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
function setPaintEventHandler (line 55) | void setPaintEventHandler( std::function<void(void)> newPaintEventHandle...
type GlfwError (line 60) | struct GlfwError{
function glfwErrorCallback (line 67) | void glfwErrorCallback( int error, const char* description ){
function class (line 73) | class GlfwSingleton{
function showWindow (line 104) | void showWindow( PlatformWindow window ){
function GLFWmonitor (line 128) | GLFWmonitor* getCurrentMonitor( GLFWwindow* window ){
function windowSizeCallback (line 163) | void windowSizeCallback( GLFWwindow*, int, int ) noexcept{
function windowRefreshCallback (line 167) | void windowRefreshCallback( GLFWwindow* ) noexcept{
function toggleFullscreen (line 172) | void toggleFullscreen( GLFWwindow* window ){
function keyCallback (line 195) | void keyCallback( GLFWwindow* window, int key, int /*scancode*/, int act...
function messageLoop (line 201) | int messageLoop( PlatformWindow window ){
function platformPresentationSupport (line 217) | bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice ...
function PlatformWindow (line 221) | PlatformWindow initWindow( const std::string& name, const uint32_t canva...
function killWindow (line 239) | void killWindow( PlatformWindow window ){
function VkSurfaceKHR (line 243) | VkSurfaceKHR initSurface( const VkInstance instance, const PlatformWindo...
FILE: src/WSI/Wayland.h
type PlatformWindowImpl (line 26) | struct PlatformWindowImpl{
type PlatformWindow (line 53) | struct PlatformWindow{
function getWindowWidth (line 74) | uint32_t getWindowWidth( PlatformWindow window ){ return window.impl->wi...
function getWindowHeight (line 75) | uint32_t getWindowHeight( PlatformWindow window ){ return window.impl->h...
function nullHandler (line 80) | bool nullHandler(){ return false; }
function setSizeEventHandler (line 84) | void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
function setPaintEventHandler (line 91) | void setPaintEventHandler( std::function<void(void)> newPaintEventHandle...
function platformPresentationSupport (line 96) | bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice ...
function registryGlobalHandler (line 100) | void registryGlobalHandler( void* data, wl_registry* registry, uint32_t ...
function registryGlobalRemoveHandler (line 120) | void registryGlobalRemoveHandler( void* data, wl_registry* registry, uin...
function seatCapsHandler (line 129) | void seatCapsHandler( void* data, wl_seat* seat, uint32_t caps ) noexcept{
function keyboardKeymapHandler (line 136) | void keyboardKeymapHandler( void* data, wl_keyboard* keyboard, uint32_t ...
function keyboardKeyHandler (line 154) | void keyboardKeyHandler( void* data, wl_keyboard* keyboard, uint32_t ser...
function pointerButtonHandler (line 187) | void pointerButtonHandler( void* data, wl_pointer* wl_pointer, uint32_t ...
function xdgSurfaceConfigureHandler (line 202) | void xdgSurfaceConfigureHandler( void* data, xdg_surface* xdg_surface, u...
function hasState (line 225) | bool hasState( const std::vector<uint32_t>& states, const uint32_t state ){
function toplevelCloseHandler (line 277) | void toplevelCloseHandler( void* data, xdg_toplevel* xdg_toplevel ) noex...
function wmBasePingHandler (line 284) | void wmBasePingHandler( void* data, xdg_wm_base* xdg_wm_base, uint32_t s...
function PlatformWindow (line 298) | PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth...
function killWindow (line 347) | void killWindow( PlatformWindow window ){
function showWindow (line 359) | void showWindow( PlatformWindow windowh ){
function messageLoop (line 373) | int messageLoop( PlatformWindow windowh ){
function VkSurfaceKHR (line 389) | VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow windowh ){
FILE: src/WSI/Win32.h
type PlatformWindow (line 19) | struct PlatformWindow{ HINSTANCE hInstance; HWND hWnd; }
function nullHandler (line 44) | bool nullHandler(){ return false; }
function setSizeEventHandler (line 48) | void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
function setPaintEventHandler (line 55) | void setPaintEventHandler( std::function<void(void)> newPaintEventHandle...
function showWindow (line 60) | void showWindow( PlatformWindow window ){
function messageLoop (line 67) | int messageLoop( PlatformWindow window ){
function toggleFullscreen (line 83) | void toggleFullscreen( HWND hWnd ){
function LRESULT (line 155) | LRESULT CALLBACK wndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP...
function platformPresentationSupport (line 210) | bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice ...
function ATOM (line 219) | ATOM initWindowClass(){
function PlatformWindow (line 249) | PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth...
function killWindow (line 288) | void killWindow( PlatformWindow window ){
function VkSurfaceKHR (line 302) | VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window ){
FILE: src/WSI/Xcb.h
type PlatformWindow (line 21) | struct PlatformWindow{ xcb_connection_t* connection; xcb_window_t window...
function nullHandler (line 43) | bool nullHandler(){ return false; }
function setSizeEventHandler (line 47) | void setSizeEventHandler( std::function<bool(void)> newSizeEventHandler ){
function setPaintEventHandler (line 54) | void setPaintEventHandler( std::function<void(void)> newPaintEventHandle...
function showWindow (line 59) | void showWindow( PlatformWindow window ){
function messageLoop (line 64) | int messageLoop( PlatformWindow window ){
function platformPresentationSupport (line 142) | bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice ...
function xcb_connection_t (line 159) | xcb_connection_t* initXcbConnection(){
function killXcbConnection (line 171) | void killXcbConnection( xcb_connection_t* connection ){
function PlatformWindow (line 175) | PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth...
function killWindow (line 241) | void killWindow( PlatformWindow window ){
function VkSurfaceKHR (line 248) | VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window ){
FILE: src/WSI/Xlib.h
type PlatformWindow (line 18) | struct PlatformWindow{ Display* display; Window window; VisualID visual_...
function class (line 40) | class XlibSingleton{
function Display (line 52) | Display* initXlibDisplay(){
function killXlibDisplay (line 60) | void killXlibDisplay( Display* display ){
function PlatformWindow (line 64) | PlatformWindow initWindow( const std::string& name, uint32_t canvasWidth...
function killWindow (line 126) | void killWindow( PlatformWindow window ){
function platformPresentationSupport (line 135) | bool platformPresentationSupport( VkInstance instance, VkPhysicalDevice ...
function VkSurfaceKHR (line 139) | VkSurfaceKHR initSurface( VkInstance instance, PlatformWindow window ){
FILE: src/WSI/private/xdg-shell-client-protocol.h
type wl_output (line 52) | struct wl_output
type wl_seat (line 53) | struct wl_seat
type wl_surface (line 54) | struct wl_surface
type xdg_popup (line 55) | struct xdg_popup
type xdg_positioner (line 56) | struct xdg_positioner
type xdg_surface (line 57) | struct xdg_surface
type xdg_toplevel (line 58) | struct xdg_toplevel
type xdg_wm_base (line 59) | struct xdg_wm_base
type wl_interface (line 82) | struct wl_interface
type wl_interface (line 132) | struct wl_interface
type wl_interface (line 222) | struct wl_interface
type wl_interface (line 260) | struct wl_interface
type wl_interface (line 332) | struct wl_interface
type xdg_wm_base_error (line 336) | enum xdg_wm_base_error {
type xdg_wm_base_listener (line 368) | struct xdg_wm_base_listener {
function xdg_wm_base_add_listener (line 394) | static inline int
function xdg_wm_base_set_user_data (line 430) | static inline void
type xdg_wm_base (line 438) | struct xdg_wm_base
type wl_proxy (line 440) | struct wl_proxy
function xdg_wm_base_get_version (line 443) | static inline uint32_t
function xdg_wm_base_destroy (line 458) | static inline void
type xdg_positioner (line 474) | struct xdg_positioner
type xdg_wm_base (line 475) | struct xdg_wm_base
type wl_proxy (line 477) | struct wl_proxy
type wl_proxy (line 479) | struct wl_proxy
type xdg_positioner (line 482) | struct xdg_positioner
type xdg_surface (line 500) | struct xdg_surface
type xdg_wm_base (line 501) | struct xdg_wm_base
type wl_surface (line 501) | struct wl_surface
type wl_proxy (line 503) | struct wl_proxy
type wl_proxy (line 505) | struct wl_proxy
type xdg_surface (line 508) | struct xdg_surface
function xdg_wm_base_pong (line 517) | static inline void
type xdg_positioner_error (line 526) | enum xdg_positioner_error {
type xdg_positioner_anchor (line 536) | enum xdg_positioner_anchor {
type xdg_positioner_gravity (line 551) | enum xdg_positioner_gravity {
type xdg_positioner_constraint_adjustment (line 572) | enum xdg_positioner_constraint_adjustment {
function xdg_positioner_set_user_data (line 622) | static inline void
type xdg_positioner (line 630) | struct xdg_positioner
type wl_proxy (line 632) | struct wl_proxy
function xdg_positioner_get_version (line 635) | static inline uint32_t
function xdg_positioner_destroy (line 646) | static inline void
function xdg_positioner_set_size (line 664) | static inline void
function xdg_positioner_set_anchor_rect (line 685) | static inline void
function xdg_positioner_set_anchor (line 702) | static inline void
function xdg_positioner_set_gravity (line 719) | static inline void
function xdg_positioner_set_constraint_adjustment (line 743) | static inline void
function xdg_positioner_set_offset (line 765) | static inline void
type xdg_surface_error (line 774) | enum xdg_surface_error {
type xdg_surface_listener (line 785) | struct xdg_surface_listener {
function xdg_surface_add_listener (line 818) | static inline int
function xdg_surface_set_user_data (line 859) | static inline void
type xdg_surface (line 867) | struct xdg_surface
type wl_proxy (line 869) | struct wl_proxy
function xdg_surface_get_version (line 872) | static inline uint32_t
function xdg_surface_destroy (line 884) | static inline void
type xdg_toplevel (line 902) | struct xdg_toplevel
type xdg_surface (line 903) | struct xdg_surface
type wl_proxy (line 905) | struct wl_proxy
type wl_proxy (line 907) | struct wl_proxy
type xdg_toplevel (line 910) | struct xdg_toplevel
type xdg_popup (line 925) | struct xdg_popup
type xdg_surface (line 926) | struct xdg_surface
type xdg_surface (line 926) | struct xdg_surface
type xdg_positioner (line 926) | struct xdg_positioner
type wl_proxy (line 928) | struct wl_proxy
type wl_proxy (line 930) | struct wl_proxy
type xdg_popup (line 933) | struct xdg_popup
function xdg_surface_set_window_geometry (line 969) | static inline void
function xdg_surface_ack_configure (line 999) | static inline void
type xdg_toplevel_resize_edge (line 1015) | enum xdg_toplevel_resize_edge {
type xdg_toplevel_state (line 1037) | enum xdg_toplevel_state {
type xdg_toplevel_listener (line 1093) | struct xdg_toplevel_listener {
function xdg_toplevel_add_listener (line 1143) | static inline int
function xdg_toplevel_set_user_data (line 1233) | static inline void
type xdg_toplevel (line 1241) | struct xdg_toplevel
type wl_proxy (line 1243) | struct wl_proxy
function xdg_toplevel_get_version (line 1246) | static inline uint32_t
function xdg_toplevel_destroy (line 1258) | static inline void
function xdg_toplevel_set_parent (line 1287) | static inline void
function xdg_toplevel_set_title (line 1305) | static inline void
function xdg_toplevel_set_app_id (line 1336) | static inline void
function xdg_toplevel_show_window_menu (line 1358) | static inline void
function xdg_toplevel_move (line 1385) | static inline void
function xdg_toplevel_resize (line 1426) | static inline void
function xdg_toplevel_set_max_size (line 1471) | static inline void
function xdg_toplevel_set_min_size (line 1516) | static inline void
function xdg_toplevel_set_maximized (line 1546) | static inline void
function xdg_toplevel_unset_maximized (line 1578) | static inline void
function xdg_toplevel_set_fullscreen (line 1612) | static inline void
function xdg_toplevel_unset_fullscreen (line 1640) | static inline void
function xdg_toplevel_set_minimized (line 1659) | static inline void
type xdg_popup_error (line 1668) | enum xdg_popup_error {
type xdg_popup_listener (line 1680) | struct xdg_popup_listener {
function xdg_popup_add_listener (line 1716) | static inline int
function xdg_popup_set_user_data (line 1746) | static inline void
type xdg_popup (line 1754) | struct xdg_popup
type wl_proxy (line 1756) | struct wl_proxy
function xdg_popup_get_version (line 1759) | static inline uint32_t
function xdg_popup_destroy (line 1774) | static inline void
function xdg_popup_grab (line 1828) | static inline void
FILE: src/Wsi.h
function getWindowWidth (line 32) | uint32_t getWindowWidth( PlatformWindow ){ return 0; }
function getWindowHeight (line 33) | uint32_t getWindowHeight( PlatformWindow ){ return 0; }
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (295K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 1036,
"preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
},
{
"path": ".github/workflows/buildCI.yml",
"chars": 3239,
"preview": "name: build CI\n\non:\n push:\n branches: [ '*' ]\n pull_request:\n branches: [ master ]\n workflow_dispatch:\n\njobs:\n "
},
{
"path": ".gitignore",
"chars": 334,
"preview": "#CMake\r\n/CMakeCache.txt\r\n/CMakeFiles/\r\n/cmake_install.cmake\r\n\r\n#Visual Studio\r\n/.vs/\r\n/*.sln\r\n/*.vcxproj\r\n/*.vcxproj.fil"
},
{
"path": ".gitmodules",
"chars": 81,
"preview": "[submodule \"glfw\"]\n\tpath = external/glfw\n\turl = https://github.com/glfw/glfw.git\n"
},
{
"path": "CMakeLists.txt",
"chars": 4171,
"preview": "cmake_minimum_required( VERSION 3.5.1 )\r\nproject( HelloTriangle )\r\n\r\nset( TODO ON CACHE BOOL \"Enable compiletime TODO me"
},
{
"path": "CONTRIBUTING.md",
"chars": 2489,
"preview": "Contributions to this project are welcome.\n\nCopyright\n---------\nThis project is meant as an educational source without a"
},
{
"path": "LICENSE",
"chars": 1211,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "README.md",
"chars": 10478,
"preview": "Hello Triangle Vulkan demo\r\n=========================\r\n\r\nThis is a traditional Hello World style application for the Vul"
},
{
"path": "doc/synchronizationTutorial.md",
"chars": 5993,
"preview": "up to [README.md](../README.md)\r\n\r\nQuick Tutorial of basic synchronization of typical renderloop\r\n----------------------"
},
{
"path": "src/CompilerMessages.h",
"chars": 758,
"preview": "// Allows to show messages during compilation -- somewhat platform dependent\r\n\r\n#ifndef COMMON_COMPILER_MESSAGES_H\r\n#def"
},
{
"path": "src/EnumerateScheme.h",
"chars": 6914,
"preview": "// Scheme for the various vkGet* and vkEnumerate* commands\r\n//\r\n// Following the DRY principle, this implements getting "
},
{
"path": "src/ErrorHandling.h",
"chars": 7718,
"preview": "// Reusable error handling primitives for Vulkan\r\n\r\n#ifndef COMMON_ERROR_HANDLING_H\r\n#define COMMON_ERROR_HANDLING_H\r\n\r\n"
},
{
"path": "src/ExtensionLoader.h",
"chars": 21394,
"preview": "// Vulkan extensions commands loader\r\n\r\n#ifndef EXTENSION_LOADER_H\r\n#define EXTENSION_LOADER_H\r\n\r\n#include <vector>\r\n\r\n#"
},
{
"path": "src/HelloTriangle.cpp",
"chars": 70893,
"preview": "// Vulkan hello world triangle rendering demo\r\n\r\n\r\n// Global header settings\r\n//////////////////////////////////////////"
},
{
"path": "src/LeanWindowsEnvironment.h",
"chars": 1587,
"preview": "// As lean as possible Windows.h header global settings\r\n\r\n// Should be included as soon as possible (first line in tran"
},
{
"path": "src/Vertex.h",
"chars": 272,
"preview": "// Basic vertex data definitions\r\n\r\n#ifndef COMMON_VERTEX_H\r\n#define COMMON_VERTEX_H\r\n\r\nstruct Vertex2D{\r\n\tfloat positio"
},
{
"path": "src/VulkanEnvironment.h",
"chars": 2164,
"preview": "// vulkan.h header global settings\r\n\r\n// Should be included as soon as possible (first line in translation unit preferab"
},
{
"path": "src/VulkanIntrospection.h",
"chars": 14595,
"preview": "// Introspection for Vulkan enums -- mostly to_string\n\n#ifndef COMMON_VULKAN_INTROSPECTION_H\n#define COMMON_VULKAN_INTRO"
},
{
"path": "src/WSI/Glfw.h",
"chars": 7668,
"preview": "// GLFW platform dependent WSI handling and event loop\r\n\r\n#ifndef COMMON_GLFW_WSI_H\r\n#define COMMON_GLFW_WSI_H\r\n\r\n#inclu"
},
{
"path": "src/WSI/Wayland.h",
"chars": 16462,
"preview": "// Wayland platform dependent WSI handling and event loop\n\n#ifndef COMMON_WAYLAND_WSI_H\n#define COMMON_WAYLAND_WSI_H\n\n#i"
},
{
"path": "src/WSI/Win32.h",
"chars": 10665,
"preview": "// win32 platform dependent WSI handling and event loop\r\n\r\n#ifndef COMMON_WIN32_WSI_H\r\n#define COMMON_WIN32_WSI_H\r\n\r\n#in"
},
{
"path": "src/WSI/Xcb.h",
"chars": 8199,
"preview": "// XCB linux platform dependent WSI handling and event loop\r\n\r\n#ifndef COMMON_XCB_WSI_H\r\n#define COMMON_XCB_WSI_H\r\n\r\n#in"
},
{
"path": "src/WSI/Xlib.h",
"chars": 6748,
"preview": "// Xlib linux platform dependent WSI handling and event loop\r\n\r\n#ifndef COMMON_XLIB_WSI_H\r\n#define COMMON_XLIB_WSI_H\r\n\r\n"
},
{
"path": "src/WSI/private/xdg-shell-client-protocol-private.inl",
"chars": 5190,
"preview": "/* Generated by wayland-scanner 1.16.0 */\n\n/*\n * Copyright © 2008-2013 Kristian Høgsberg\n * Copyright © 2013 Rafael"
},
{
"path": "src/WSI/private/xdg-shell-client-protocol.h",
"chars": 62988,
"preview": "/* Generated by wayland-scanner 1.16.0 */\n\n#ifndef XDG_SHELL_CLIENT_PROTOCOL_H\n#define XDG_SHELL_CLIENT_PROTOCOL_H\n\n#inc"
},
{
"path": "src/Wsi.h",
"chars": 1192,
"preview": "#ifndef HELLO_TRIANGLE_WSI_PLATFORM_H\r\n#define HELLO_TRIANGLE_WSI_PLATFORM_H\r\n\r\n#if !defined(USE_PLATFORM_GLFW) \\\r\n &&"
},
{
"path": "src/shaders/hello_triangle.frag",
"chars": 167,
"preview": "#version 450\r\n\r\nlayout (location = 0) smooth in vec3 inColor;\r\n\r\nlayout (location = 0) out vec4 outFragColor;\r\n\r\nvoid ma"
},
{
"path": "src/shaders/hello_triangle.vert",
"chars": 230,
"preview": "#version 450\r\n\r\nlayout (location = 0) in vec2 inPos;\r\nlayout (location = 1) in vec3 inColor;\r\n\r\nlayout (location = 0) sm"
}
]
About this extraction
This page contains the full source code of the krOoze/Hello_Triangle GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (268.4 KB), approximately 67.6k tokens, and a symbol index with 298 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.