From fb3c56f945c289729f163a980f69b3c5fff2112c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 18:02:38 +0100 Subject: [PATCH 01/85] doc: we have a helper for flextGL updates now. --- doc/developers.dox | 68 ++++++++++++++++++---------------------------- 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/doc/developers.dox b/doc/developers.dox index c261d1f3dd..8d92b66926 100644 --- a/doc/developers.dox +++ b/doc/developers.dox @@ -563,54 +563,38 @@ in inverse --- but usually @ref developers-deprecation "deprecate first". @section developers-gl-extensions Checklist for adding / removing GL versions and extensions 1. Install [flextGL](https://github.com/mosra/flextgl) -2. Go to `src/MagnumExternal/OpenGL/GL/`: - - Update `extensions.txt` (bump a version or add/remove extensions) - - Run @cb{.sh} .../flextGLgen.py -D . -t . extensions.txt @ce to generate - `flextGL.h`, `flextGL.cpp` and `flextGLPlatform.cpp` files -3. Go to `src/MagnumExternal/OpenGL/GLES2/`: - - Update `extensions.txt` and `Emscripten/extensions.txt` (add/remove +2. Go to `src/MagnumExternal/OpenGL/`: + - Update `GL/extensions.txt`, `GLES2/extensions.txt`, + `GLES2/Emscripten/extensions.txt`, `GLES3/extensions.txt`, + `GLES3/Emscripten/extensions.txt` (bump versions or add/remove extensions) - - Run @cb{.sh} .../flextGLgen.py -D . -t . extensions.txt @ce to generate - `flextGL.h`, `flextGL.cpp`, `flextGLPlatform.cpp`, - `flextGLWindowsDesktop.h`, `flextGLWindowsDesktop.cpp`, - `flextGLPlatformWindowsDesktop.cpp` and `flextGLPlatformIOS.cpp` files. - Desktop GLES on Windows still links to the ancient `opengl32.dll` - which exports only OpenGL 1.1 symbols, so we have a special set of - headers that queries pointers for everything above OpenGL 1.1 (instead - of everything above OpenGL ES 2.0). iOS, on the other hand, doesn't - have any extension loader mechanism and all supported entrypoints are - exported from the library, so we set the function pointers to those - exported symbols in case the system GL header defines them. - - Run @cb{.sh} .../flextGLgen.py -D . -t Emscripten/ Emscripten/extensions.txt @ce - to generate a stripped-down `flextGLEmscripten.h` file. Emscripten - doesn't have the ability to manually load extension pointers, thus it - has only header files. -4. Go to `src/MagnumExternal/OpenGL/GLES3/`: - - Update `extensions.txt` and `Emscripten/extensions.txt` (bump a version - or add/remove extensions) - - Run @cb{.sh} .../flextGLgen.py -D . -t . extensions.txt @ce to generate - `flextGL.h`, `flextGL.cpp`, `flextGLPlatform.cpp`, - `flextGLWindowsDesktop.h`, `flextGLWindowsDesktop.cpp`, - `flextGLPlatformWindowsDesktop.cpp` and `flextGLPlatformIOS.cpp` files. - See above why there are so many. - - Run @cb{.sh} .../flextGLgen.py -D . -t Emscripten/ Emscripten/extensions.txt @ce - to generate a stripped-down `flextGLEmscripten.h` file. See above for - details. -5. Check @cb{.sh} git diff @ce for suspicious changes and whitespace-at-EOL -6. For every new added function, add an entry to `doc/opengl-mapping.dox` -7. For every new added limit query (various `GL_MIN_*` and `GL_MAX_*` macros + - Run `./update-flextgl.sh` to update everything. Reason there is so many + variants of the files are the following: + - Desktop GLES on Windows still links to the ancient `opengl32.dll` + which exports only OpenGL 1.1 symbols, so we have a special set of + headers that queries pointers for everything above OpenGL 1.1 + (instead of everything above OpenGL ES 2.0). + - iOS, on the other hand, doesn't have any extension loader mechanism + and all supported entrypoints are exported from the library, so we + set the function pointers to those exported symbols in case the + system GL header defines them. + - Emscripten doesn't have the ability to manually load extension + pointers, thus it has only header files. +3. Check @cb{.sh} git diff @ce for suspicious changes and whitespace-at-EOL +4. For every new added function, add an entry to `doc/opengl-mapping.dox` +5. For every new added limit query (various `GL_MIN_*` and `GL_MAX_*` macros etc.), add an entry to the bottom of `doc/opengl-mapping.dox` -8. Add a table listing the new version and all new extensions in it to +6. Add a table listing the new version and all new extensions in it to `doc/opengl-support.dox` (take a list of them from the changelog in the official spec PDF). Some extensions might be already present in the general extension list, move them out of there. -9. Add a new `requires-glXY`, `requires-glesXY` or `requires-webglXY` page +7. Add a new `requires-glXY`, `requires-glesXY` or `requires-webglXY` page with @c \@m_footernavigation to `doc/opengl-support.dox`, mention it as a @c \@subpage at a correct position in the list -10. Add a new `requires-glXY`, `requires-glesXY` or `requires-webglXY` alias +8. Add a new `requires-glXY`, `requires-glesXY` or `requires-webglXY` alias to `Doxyfile`, `Doxyfile-mcss` and `Doxyfile-public`, copypaste it from existing and change the numbers -11. Add new version enum value: +9. Add new version enum value: - to `src/Magnum/GL/Version.h` - to debug output in `src/Magnum/GL/Version.cpp` - to @ref GL::Extension::extensions() in `src/Magnum/GL/Context.cpp` @@ -618,7 +602,7 @@ in inverse --- but usually @ref developers-deprecation "deprecate first". - to specify GLSL version in `src/Magnum/GL/Shader.cpp` - to the list in `src/Magnum/Platform/gl-info.cpp` - to the test in `src/Magnum/GL/Test/ContextTest.cpp` -12. Add new extensions to `src/Magnum/GL/Extensions.h` +10. Add new extensions to `src/Magnum/GL/Extensions.h` - order them by extension ID that is mentioned in every extension spec file - update the numbering to stay monotonic and unique, round up start index @@ -626,9 +610,9 @@ in inverse --- but usually @ref developers-deprecation "deprecate first". - in case there's a lot of new extensions, @cpp Implementation::ExtensionCount @ce might needed to be increased - run `ContextTest` to verify everything is still okay -13. Update existing extensions with version in which they become core (last +11. Update existing extensions with version in which they become core (last parameter of the `_extension()` macro) -14. Update extension list in `src/magnum/GL/Context.cpp` according to changes +12. Update extension list in `src/magnum/GL/Context.cpp` according to changes in `src/Magnum/GL/Extensions.h` In order to remove GL functionality, be sure to touch all places mentioned From f4f8612f7e60ffa610268a1e80cf303feecefa26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 12 May 2020 18:11:02 +0200 Subject: [PATCH 02/85] Vk: wrap VkResult, add an assert macro for it. --- src/Magnum/Vk/CMakeLists.txt | 37 ++++--- src/Magnum/Vk/Result.cpp | 65 ++++++++++++ src/Magnum/Vk/Result.h | 162 ++++++++++++++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/ResultTest.cpp | 87 ++++++++++++++++ src/Magnum/Vk/Vk.h | 6 +- 6 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 src/Magnum/Vk/Result.cpp create mode 100644 src/Magnum/Vk/Result.h create mode 100644 src/Magnum/Vk/Test/ResultTest.cpp diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index c5f9c4afa8..83a99e5110 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -26,39 +26,44 @@ find_package(Vulkan REQUIRED) -set(MagnumVk_SRCS ) +set(MagnumVk_SRCS + Result.cpp) + set(MagnumVk_GracefulAssert_SRCS Enums.cpp) + set(MagnumVk_HEADERS Enums.h Integration.h + Result.h Vk.h Vulkan.h visibility.h) + set(MagnumVk_PRIVATE_HEADERS Implementation/compressedPixelFormatMapping.hpp Implementation/pixelFormatMapping.hpp Implementation/vertexFormatMapping.hpp) # Objects shared between main and test library -# add_library(MagnumVkObjects OBJECT -# ${MagnumVk_SRCS} -# ${MagnumVk_HEADERS} -# ${MagnumVk_PRIVATE_HEADERS}) -# target_include_directories(MagnumVkObjects PUBLIC -# $) -# if(NOT BUILD_STATIC) -# target_compile_definitions(MagnumVkObjects PRIVATE "MagnumVkObjects_EXPORTS" "FlextVk_EXPORTS") -# endif() -# if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) -# set_target_properties(MagnumVkObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) -# endif() -# set_target_properties(MagnumVkObjects PROPERTIES FOLDER "Magnum/Vk") +add_library(MagnumVkObjects OBJECT + ${MagnumVk_SRCS} + ${MagnumVk_HEADERS} + ${MagnumVk_PRIVATE_HEADERS}) +target_include_directories(MagnumVkObjects PUBLIC + $) +if(NOT BUILD_STATIC) + target_compile_definitions(MagnumVkObjects PRIVATE "MagnumVkObjects_EXPORTS" "FlextVk_EXPORTS") +endif() +if(NOT BUILD_STATIC OR BUILD_STATIC_PIC) + set_target_properties(MagnumVkObjects PROPERTIES POSITION_INDEPENDENT_CODE ON) +endif() +set_target_properties(MagnumVkObjects PROPERTIES FOLDER "Magnum/Vk") # Vk library add_library(MagnumVk ${SHARED_OR_STATIC} -# $ + $ $ ${MagnumVk_GracefulAssert_SRCS}) set_target_properties(MagnumVk PROPERTIES @@ -86,7 +91,7 @@ install(FILES ${MagnumVk_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Vk) if(BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumVkTestLib ${SHARED_OR_STATIC} -# $ + $ $ ${MagnumVk_GracefulAssert_SRCS}) set_target_properties(MagnumVkTestLib PROPERTIES diff --git a/src/Magnum/Vk/Result.cpp b/src/Magnum/Vk/Result.cpp new file mode 100644 index 0000000000..9400696890 --- /dev/null +++ b/src/Magnum/Vk/Result.cpp @@ -0,0 +1,65 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Result.h" + +#include + +namespace Magnum { namespace Vk { + +Debug& operator<<(Debug& debug, const Result value) { + debug << "Vk::Result" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::Result::value: return debug << "::" << Debug::nospace << #value; + _c(Success) + _c(NotReady) + _c(Timeout) + _c(EventSet) + _c(EventReset) + _c(Incomplete) + _c(ErrorOutOfHostMemory) + _c(ErrorOutOfDeviceMemory) + _c(ErrorInitializationFailed) + _c(ErrorDeviceLost) + _c(ErrorMemoryMapFailed) + _c(ErrorLayerNotPresent) + _c(ErrorExtensionNotPresent) + _c(ErrorFeatureNotPresent) + _c(ErrorIncompatibleDriver) + _c(ErrorTooManyObjects) + _c(ErrorFormatNotSupported) + _c(ErrorFragmentedPool) + _c(ErrorUnknown) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Vulkan docs have the values in decimal, so not converting to hex */ + return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; +} + +}} diff --git a/src/Magnum/Vk/Result.h b/src/Magnum/Vk/Result.h new file mode 100644 index 0000000000..9e1fc11492 --- /dev/null +++ b/src/Magnum/Vk/Result.h @@ -0,0 +1,162 @@ +#ifndef Magnum_Vk_Result_h +#define Magnum_Vk_Result_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Enum @ref Magnum::Vk::Result, macro @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() + * @m_since_latest + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/visibility.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { + +/** +@brief Result +@m_since_latest + +Wraps a @type_vk_keyword{Result}. +@m_enum_values_as_keywords +@see @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() +*/ +enum class Result: Int { + /** Command successfully completed */ + Success = VK_SUCCESS, + + /** A fence or query has not yet completed */ + NotReady = VK_NOT_READY, + + /** A wait operation has not completed in the specified time */ + Timeout = VK_TIMEOUT, + + /** An event is signaled */ + EventSet = VK_EVENT_SET, + + /** An event is unsignaled */ + EventReset = VK_EVENT_RESET, + + /** A return array was too small for the result */ + Incomplete = VK_INCOMPLETE, + + /** A host memory allocation has failed */ + ErrorOutOfHostMemory = VK_ERROR_OUT_OF_HOST_MEMORY, + + /** A device memory allocation has failed */ + ErrorOutOfDeviceMemory = VK_ERROR_OUT_OF_DEVICE_MEMORY, + + /** + * Initialization of an object could not be completed for + * implementation-specific reasons + */ + ErrorInitializationFailed = VK_ERROR_INITIALIZATION_FAILED, + + /** The logical or physical device has been lost */ + ErrorDeviceLost = VK_ERROR_DEVICE_LOST, + + /** Mapping of a memory object has failed */ + ErrorMemoryMapFailed = VK_ERROR_MEMORY_MAP_FAILED, + + /** A requested layer is not present or could not be loaded */ + ErrorLayerNotPresent = VK_ERROR_LAYER_NOT_PRESENT, + + /** A requested extension is not supported */ + ErrorExtensionNotPresent = VK_ERROR_EXTENSION_NOT_PRESENT, + + /** A requested feature is not supported */ + ErrorFeatureNotPresent = VK_ERROR_FEATURE_NOT_PRESENT, + + /** + * The requested version of Vulkan is not supported by the driver or is + * otherwise incompatible for implementation-specific reasons + */ + ErrorIncompatibleDriver = VK_ERROR_INCOMPATIBLE_DRIVER, + + /** Too many objects of the type have already been created */ + ErrorTooManyObjects = VK_ERROR_TOO_MANY_OBJECTS, + + /** A requested format is not supported on this device */ + ErrorFormatNotSupported = VK_ERROR_FORMAT_NOT_SUPPORTED, + + /** + * A pool allocation has failed due to fragmentation of the pool's memory + */ + ErrorFragmentedPool = VK_ERROR_FRAGMENTED_POOL, + + /** + * An unknown error has occurred; either the application has provided + * invalid input, or an implementation failure has occurred + */ + ErrorUnknown = VK_ERROR_UNKNOWN +}; + +/** +@debugoperatorenum{Result} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, Result value); + +/** +@brief Assert that a Vulkan function call doesn't fail +@m_since_latest + +Compared to using @ref CORRADE_INTERNAL_ASSERT_OUTPUT() to verify that +@cpp call == VK_SUCCESS @ce, this macro also prints the result value. Otherwise +the behavior is the same, including interactions with +@ref CORRADE_STANDARD_ASSERT and @ref CORRADE_NO_ASSERT. Works with both plain +Vulkan functions returning @type_vk{Result} and APIs returning +@ref Magnum::Vk::Result "Vk::Result". + +You can override this implementation by placing your own +@cpp #define MAGNUM_VK_INTERNAL_ASSERT_RESULT @ce before including the +@ref Magnum/Vk/Result.h header. +*/ +#ifndef MAGNUM_VK_INTERNAL_ASSERT_RESULT +#if defined(CORRADE_NO_ASSERT) || (defined(CORRADE_STANDARD_ASSERT) && defined(NDEBUG)) +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + static_cast(call) +#elif defined(CORRADE_STANDARD_ASSERT) +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + assert(Magnum::Vk::Result(call) == Magnum::Vk::Result::Success) +#else +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + do { \ + const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ + if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success) { \ + Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << "Call " #call " failed with" << _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) << "at " __FILE__ ":" CORRADE_LINE_STRING; \ + std::abort(); \ + } \ + } while(false) +#endif +#endif + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index ea38061a0d..9948569dfe 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -26,3 +26,4 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/ResultTest.cpp b/src/Magnum/Vk/Test/ResultTest.cpp new file mode 100644 index 0000000000..2e200ca6c0 --- /dev/null +++ b/src/Magnum/Vk/Test/ResultTest.cpp @@ -0,0 +1,87 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ResultTest: TestSuite::Tester { + explicit ResultTest(); + + void assertResult(); + void assertVkResult(); + + void debug(); + + bool _failAssertResult, _failAssertVkResult; +}; + +ResultTest::ResultTest(): TestSuite::Tester{TesterConfiguration{}.setSkippedArgumentPrefixes({"fail-on"})} { + addTests({&ResultTest::assertResult, + &ResultTest::assertVkResult, + + &ResultTest::debug}); + + Utility::Arguments args{"fail-on"}; + args.addOption("assert-result", "false").setHelp("assert-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with Vk::Result", "BOOL") + .addOption("assert-vk-result", "false").setHelp("assert-vk-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with VkResult", "BOOL") + .parse(arguments().first, arguments().second); + + _failAssertResult = args.value("assert-result"); + _failAssertVkResult = args.value("assert-vk-result"); +} + +void ResultTest::assertResult() { + Result a = Result::ErrorUnknown; + + Result r = _failAssertResult ? Result::ErrorFragmentedPool : Result::Success; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + + CORRADE_COMPARE(a, Result::Success); +} + +void ResultTest::assertVkResult() { + VkResult a = VK_ERROR_UNKNOWN; + + VkResult r = _failAssertVkResult ? VK_ERROR_FRAGMENTED_POOL : VK_SUCCESS; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + + CORRADE_COMPARE(Result(a), Result::Success); +} + +void ResultTest::debug() { + std::ostringstream out; + Debug{&out} << Result::ErrorExtensionNotPresent << Result(-10007655); + CORRADE_COMPARE(out.str(), "Vk::Result::ErrorExtensionNotPresent Vk::Result(-10007655)\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ResultTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index bc7a50b34d..cae3d1d962 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -29,9 +29,13 @@ * @brief Forward declarations for the @ref Magnum::Vk namespace */ -namespace Magnum { namespace Vk { +#include "Magnum/Magnum.h" +namespace Magnum { namespace Vk { +#ifndef DOXYGEN_GENERATING_OUTPUT +enum class Result: Int; +#endif }} From 65552a6c26cf5a50b915088579551275baae91e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 12 May 2020 23:32:11 +0200 Subject: [PATCH 03/85] Vk: add Version enum, utities for version comparison and extraction. Same as the VK_VERSION_*() macros but without C-style casts that produce warnings. --- src/Magnum/Vk/CMakeLists.txt | 4 +- src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/VersionTest.cpp | 121 +++++++++++++++++++++++++ src/Magnum/Vk/Version.cpp | 41 +++++++++ src/Magnum/Vk/Version.h | 136 +++++++++++++++++++++++++++++ src/Magnum/Vk/Vk.h | 1 + 6 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/Test/VersionTest.cpp create mode 100644 src/Magnum/Vk/Version.cpp create mode 100644 src/Magnum/Vk/Version.h diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 83a99e5110..0a89627229 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,7 +27,8 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS - Result.cpp) + Result.cpp + Version.cpp) set(MagnumVk_GracefulAssert_SRCS Enums.cpp) @@ -36,6 +37,7 @@ set(MagnumVk_HEADERS Enums.h Integration.h Result.h + Version.h Vk.h Vulkan.h diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 9948569dfe..42ef5c9232 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -27,3 +27,4 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/VersionTest.cpp b/src/Magnum/Vk/Test/VersionTest.cpp new file mode 100644 index 0000000000..91af9026f7 --- /dev/null +++ b/src/Magnum/Vk/Test/VersionTest.cpp @@ -0,0 +1,121 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/Version.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct VersionTest: TestSuite::Tester { + explicit VersionTest(); + + void packing(); + void comparison(); + + void debug(); +}; + +VersionTest::VersionTest() { + addTests({&VersionTest::packing, + &VersionTest::comparison, + + &VersionTest::debug}); +} + +void VersionTest::packing() { + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + constexpr Version packed = version(1, 5, 789); + constexpr UnsignedInt major = versionMajor(packed); + constexpr UnsignedInt minor = versionMinor(packed); + constexpr UnsignedInt patch = versionPatch(packed); + CORRADE_COMPARE(packed, Version(VK_MAKE_VERSION(1, 5, 789))); + CORRADE_COMPARE(major, VK_VERSION_MAJOR(packed)); + CORRADE_COMPARE(minor, VK_VERSION_MINOR(packed)); + CORRADE_COMPARE(patch, VK_VERSION_PATCH(packed)); + CORRADE_COMPARE(major, 1); + CORRADE_COMPARE(minor, 5); + CORRADE_COMPARE(patch, 789); + #ifdef CORRADE_TARGET_GCC + #pragma GCC diagnostic pop + #endif +} + +void VersionTest::comparison() { + CORRADE_VERIFY(!(version(1, 5, 3) < version(1, 5, 3))); + CORRADE_VERIFY(!(version(1, 5, 3) > version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) < version(1, 5, 4)); + CORRADE_VERIFY(version(1, 5, 4) > version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) > version(1, 5, 4))); + CORRADE_VERIFY(!(version(1, 5, 4) < version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) < version(1, 6, 3)); + CORRADE_VERIFY(version(1, 6, 3) > version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) > version(1, 6, 3))); + CORRADE_VERIFY(!(version(1, 6, 3) < version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) < version(2, 5, 3)); + CORRADE_VERIFY(version(2, 5, 3) > version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) > version(2, 5, 3))); + CORRADE_VERIFY(!(version(2, 5, 3) < version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) <= version(1, 5, 3)); + CORRADE_VERIFY(version(1, 5, 3) >= version(1, 5, 3)); + + CORRADE_VERIFY(version(1, 5, 3) <= version(1, 5, 4)); + CORRADE_VERIFY(version(1, 5, 4) >= version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) >= version(1, 5, 4))); + CORRADE_VERIFY(!(version(1, 5, 4) <= version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) <= version(1, 6, 3)); + CORRADE_VERIFY(version(1, 6, 3) >= version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) >= version(1, 6, 3))); + CORRADE_VERIFY(!(version(1, 6, 3) <= version(1, 5, 3))); + + CORRADE_VERIFY(version(1, 5, 3) <= version(2, 5, 3)); + CORRADE_VERIFY(version(2, 5, 3) >= version(1, 5, 3)); + CORRADE_VERIFY(!(version(1, 5, 3) >= version(2, 5, 3))); + CORRADE_VERIFY(!(version(2, 5, 3) <= version(1, 5, 3))); +} + +void VersionTest::debug() { + std::ostringstream out; + Debug{&out} << Version::Vk12 << version(1, 5, 789) << Version::None + /* packed should omit "Vulkan" */ + << Debug::packed << version(20, 6); + CORRADE_COMPARE(out.str(), "Vulkan 1.2 Vulkan 1.5.789 Vulkan 1023.1023.4095 20.6\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::VersionTest) diff --git a/src/Magnum/Vk/Version.cpp b/src/Magnum/Vk/Version.cpp new file mode 100644 index 0000000000..d4f8b7cb57 --- /dev/null +++ b/src/Magnum/Vk/Version.cpp @@ -0,0 +1,41 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Version.h" + +#include + +namespace Magnum { namespace Vk { + +Debug& operator<<(Debug& debug, const Version value) { + if(!(debug.immediateFlags() & Debug::Flag::Packed)) + debug << "Vulkan"; + debug << versionMajor(value) << Debug::nospace << "." << Debug::nospace << versionMinor(value); + if(const UnsignedInt patch = versionPatch(value)) + debug << Debug::nospace << "." << Debug::nospace << patch; + return debug; +} + +}} diff --git a/src/Magnum/Vk/Version.h b/src/Magnum/Vk/Version.h new file mode 100644 index 0000000000..5e27bef361 --- /dev/null +++ b/src/Magnum/Vk/Version.h @@ -0,0 +1,136 @@ +#ifndef Magnum_Vk_Version_h +#define Magnum_Vk_Version_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Enum @ref Magnum::Vk::Version, function @ref Magnum::Vk::version(), @ref Magnum::Vk::versionMajor(), @ref Magnum::Vk::versionMinor(), @ref Magnum::Vk::versionPatch() + * @m_since_latest + */ + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Vulkan version +@m_since_latest + +@see @ref version(), @ref versionMajor(), @ref versionMinor(), @ref versionPatch() +*/ +enum class Version: UnsignedInt { + None = 0xffffffffu, /**< Unspecified */ + + /* Not using VK_MAKE_VERSION() because it would warn about C-style casts */ + Vk10 = (1 << 22) | (0 << 12), /**< Vulkan 1.0 */ + Vk11 = (1 << 22) | (1 << 12), /**< Vulkan 1.1 */ + Vk12 = (1 << 22) | (2 << 12) /**< Vulkan 1.2 */ +}; + +/** +@brief Create a version from components +@m_since_latest + +Equivalent to @def_vk_keyword{MAKE_VERSION}, but without C-style casts. +*/ +constexpr Version version(UnsignedInt major, UnsignedInt minor, UnsignedInt patch = 0) { + return Version((major << 22) | (minor << 12) | patch); +} + +/** +@brief Extract major version number from a packed representation +@m_since_latest + +Equivalent to @def_vk_keyword{VERSION_MAJOR}, but without C-style casts. +*/ +constexpr UnsignedInt versionMajor(Version version) { + return UnsignedInt(version) >> 22; +} + +/** +@brief Extract minor version number from a packed representation +@m_since_latest + +Equivalent to @def_vk_keyword{VERSION_MINOR}, but without C-style casts. +*/ +constexpr UnsignedInt versionMinor(Version version) { + return (UnsignedInt(version) >> 12) & 0x3ff; +} + +/** +@brief Extract minor version number from a packed representation +@m_since_latest + +Equivalent to @def_vk_keyword{VERSION_PATCH}, but without C-style casts. +*/ +constexpr UnsignedInt versionPatch(Version version) { + return UnsignedInt(version) & 0xfff; +} + +/** +@brief Whether a version is smaller than the other +@m_since_latest +*/ +constexpr bool operator<(Version a, Version b) { + return UnsignedInt(a) < UnsignedInt(b); +} + +/** +@brief Whether a version is smaller than or equal to the other +@m_since_latest +*/ +constexpr bool operator<=(Version a, Version b) { + return UnsignedInt(a) <= UnsignedInt(b); +} + +/** +@brief Whether a version is greater than or equal to the other +@m_since_latest +*/ +constexpr bool operator>=(Version a, Version b) { + return UnsignedInt(a) >= UnsignedInt(b); +} + +/** +@brief Whether a version is greater than the other +@m_since_latest +*/ +constexpr bool operator>(Version a, Version b) { + return UnsignedInt(a) > UnsignedInt(b); +} + +/** +@debugoperatorenum{Version} +@m_since_latest + +Prints the version as @cb{.shell-session} .. @ce, or just +as @cb{.shell-session} . @ce if patch is zero. +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, Version value); + +}} + +#endif diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index cae3d1d962..b9725d3eac 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -35,6 +35,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT enum class Result: Int; +enum class Version: UnsignedInt; #endif }} From 21a91e8b0ae2e8d85e3cb997326c1b7ce0798baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 19 Jun 2020 23:34:37 +0200 Subject: [PATCH 04/85] external: fetch vkEnumerateInstanceVersion() in a static constructor. Version query. Such a simple and obvious thing and because they forgot to add such function to Vulkan 1.0, the only three possible ways to retrieve a version retrieval function are: 1. with a static constructor that could make a library crash even before main() is reached if the driver is *extra* shitty 2. with a init() function that would cause race conditions if ever called from multiple threads (which of course can happen because you need to know instance version in order to use the correct function pointers and route pNext fields) 3. with a init() function that internally uses std::mutexes and std::call_once and and atomics and whatnot and thus takes longer to compile than the rest of the engine Approach 2 chosen originally but the race condition countermeasures turned out to be extra annoying to use, so switched to approach 1 now. --- src/MagnumExternal/Vulkan/flextVk.cpp | 6 +----- src/MagnumExternal/Vulkan/flextVk.cpp.template | 16 ++-------------- src/MagnumExternal/Vulkan/flextVk.h | 7 +++---- src/MagnumExternal/Vulkan/flextVk.h.template | 7 +++---- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/MagnumExternal/Vulkan/flextVk.cpp b/src/MagnumExternal/Vulkan/flextVk.cpp index 9162a36813..e8ddf1ae5d 100644 --- a/src/MagnumExternal/Vulkan/flextVk.cpp +++ b/src/MagnumExternal/Vulkan/flextVk.cpp @@ -26,16 +26,12 @@ #include "flextVk.h" #include "flextVkGlobal.h" -VkResult(VKAPI_PTR *flextvkEnumerateInstanceVersion)(uint32_t*) = nullptr; +VkResult(VKAPI_PTR *flextvkEnumerateInstanceVersion)(uint32_t*) = reinterpret_cast(vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); FlextVkInstance flextVkInstance{}; FlextVkDevice flextVkDevice{}; -void flextVkInit() { - flextvkEnumerateInstanceVersion = reinterpret_cast(vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); -} - void flextVkInitInstance(VkInstance instance, FlextVkInstance* data) { data->EnumeratePhysicalDeviceGroupsKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHR")); data->GetPhysicalDeviceExternalFencePropertiesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalFencePropertiesKHR")); diff --git a/src/MagnumExternal/Vulkan/flextVk.cpp.template b/src/MagnumExternal/Vulkan/flextVk.cpp.template index c78e3ae87b..6862616434 100644 --- a/src/MagnumExternal/Vulkan/flextVk.cpp.template +++ b/src/MagnumExternal/Vulkan/flextVk.cpp.template @@ -32,7 +32,8 @@ @for f in funcs: @if f.name in ['EnumerateInstanceVersion']: @f.returntype\ -(VKAPI_PTR *flextvk@f.name)(@f.param_type_list_string()) = nullptr; +(VKAPI_PTR *flextvk@f.name)(@f.param_type_list_string()) = reinterpret_cast<@f.returntype\ +(VKAPI_PTR*)(@f.param_type_list_string())>(vkGetInstanceProcAddr(nullptr, "vk@f.name")); @end @end @end @@ -42,19 +43,6 @@ FlextVkInstance flextVkInstance{}; FlextVkDevice flextVkDevice{}; -void flextVkInit() { - @for category,funcs in functions: - @if funcs: - @for f in funcs: - @if f.name in ['EnumerateInstanceVersion']: - flextvk@f.name = reinterpret_cast<@f.returntype\ -(VKAPI_PTR*)(@f.param_type_list_string())>(vkGetInstanceProcAddr(nullptr, "vk@f.name")); - @end - @end - @end - @end -} - void flextVkInitInstance(VkInstance instance, FlextVkInstance* data) { @for category,funcs in functions: @if funcs: diff --git a/src/MagnumExternal/Vulkan/flextVk.h b/src/MagnumExternal/Vulkan/flextVk.h index 1b1b44ea62..5d848b8534 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h +++ b/src/MagnumExternal/Vulkan/flextVk.h @@ -3994,13 +3994,12 @@ VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties(uint32_t*, VkL VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance, const char*); /* Global function pointers. These are not present in all Vulkan versions, so - they need to be loaded at runtime. */ + they need to be loaded at runtime. To avoid race conditions when calling + an init function manually, the function pointer is fetched in a static + constructor. */ extern FLEXTVK_EXPORT VkResult(VKAPI_PTR *flextvkEnumerateInstanceVersion)(uint32_t*); #define vkEnumerateInstanceVersion flextvkEnumerateInstanceVersion -/* Global function pointer initialization */ -void FLEXTVK_EXPORT flextVkInit(); - /* Per-instance function pointers */ struct FlextVkInstance { diff --git a/src/MagnumExternal/Vulkan/flextVk.h.template b/src/MagnumExternal/Vulkan/flextVk.h.template index 3bd801dfdb..b475864045 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h.template +++ b/src/MagnumExternal/Vulkan/flextVk.h.template @@ -107,7 +107,9 @@ VKAPI_ATTR @f.returntype VKAPI_CALL vk@f.name\ @end /* Global function pointers. These are not present in all Vulkan versions, so - they need to be loaded at runtime. */ + they need to be loaded at runtime. To avoid race conditions when calling + an init function manually, the function pointer is fetched in a static + constructor. */ @for cat,funcs in functions: @if funcs: @for f in funcs: @@ -120,9 +122,6 @@ extern FLEXTVK_EXPORT @f.returntype\ @end @end -/* Global function pointer initialization */ -void FLEXTVK_EXPORT flextVkInit(); - /* Per-instance function pointers */ struct FlextVkInstance { @for cat,funcs in functions: From 7524fd28e9c695c1bfbcba9572382776a9f7320c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 19 Jun 2020 23:40:18 +0200 Subject: [PATCH 05/85] CMake: add BUILD_VK_TESTS option. --- CMakeLists.txt | 1 + doc/building.dox | 3 +++ package/archlinux/PKGBUILD | 1 + package/archlinux/PKGBUILD-android-arm64 | 3 ++- package/archlinux/PKGBUILD-clang | 3 ++- package/archlinux/PKGBUILD-clang-addressanitizer | 3 ++- package/archlinux/PKGBUILD-clang-analyzer | 1 + package/archlinux/PKGBUILD-clang-libc++ | 3 ++- package/archlinux/PKGBUILD-coverage | 1 + package/archlinux/PKGBUILD-gcc48 | 3 ++- package/archlinux/PKGBUILD-mingw-w64 | 2 ++ package/archlinux/PKGBUILD-release | 2 ++ package/ci/appveyor-desktop-vulkan.bat | 3 ++- package/ci/unix-desktop-vulkan.sh | 3 ++- 14 files changed, 25 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 260b362dd7..d948833ee2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -198,6 +198,7 @@ option(BUILD_PLUGINS_STATIC "Build static plugins (default are dynamic)" OFF) option(BUILD_TESTS "Build unit tests" OFF) cmake_dependent_option(BUILD_GL_TESTS "Build unit tests for OpenGL code" OFF "BUILD_TESTS;TARGET_GL" OFF) cmake_dependent_option(BUILD_AL_TESTS "Build unit tests for OpenAL code" ON "BUILD_TESTS;WITH_AUDIO" OFF) +cmake_dependent_option(BUILD_VK_TESTS "Build unit tests for Vulkan code" OFF "BUILD_TESTS;TARGET_VK" OFF) if(BUILD_TESTS) find_package(Corrade REQUIRED TestSuite) if(CORRADE_TARGET_IOS) diff --git a/doc/building.dox b/doc/building.dox index 5524bf76ce..af0679bd69 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -760,6 +760,9 @@ running CTest, e.g.: ctest -R GLTest # run only tests requiring OpenGL context @endcode +Tests requiring Vulkan to work are also disabled by default, enable them with +`BUILD_VK_TESTS`. + @section building-doc Building documentation The documentation is generated using [Doxygen](http://doxygen.org) with the diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index b0559874a8..9f4158cb50 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -56,6 +56,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -G Ninja ninja } diff --git a/package/archlinux/PKGBUILD-android-arm64 b/package/archlinux/PKGBUILD-android-arm64 index 6c478ac59a..ea91314917 100644 --- a/package/archlinux/PKGBUILD-android-arm64 +++ b/package/archlinux/PKGBUILD-android-arm64 @@ -49,7 +49,8 @@ build() { -DWITH_GL_INFO=ON \ -DTARGET_GLES2=OFF \ -DBUILD_TESTS=ON \ - -DBUILD_GL_TESTS=ON + -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON ninja } diff --git a/package/archlinux/PKGBUILD-clang b/package/archlinux/PKGBUILD-clang index adaa31ff66..da225875b0 100644 --- a/package/archlinux/PKGBUILD-clang +++ b/package/archlinux/PKGBUILD-clang @@ -58,7 +58,8 @@ build() { -DWITH_GL_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ - -DBUILD_GL_TESTS=ON + -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON ninja } diff --git a/package/archlinux/PKGBUILD-clang-addressanitizer b/package/archlinux/PKGBUILD-clang-addressanitizer index 150b72d5e9..7894ed7350 100644 --- a/package/archlinux/PKGBUILD-clang-addressanitizer +++ b/package/archlinux/PKGBUILD-clang-addressanitizer @@ -59,7 +59,8 @@ build() { -DWITH_GL_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ - -DBUILD_GL_TESTS=ON + -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON ninja } diff --git a/package/archlinux/PKGBUILD-clang-analyzer b/package/archlinux/PKGBUILD-clang-analyzer index c71ae35c04..70f00400cf 100644 --- a/package/archlinux/PKGBUILD-clang-analyzer +++ b/package/archlinux/PKGBUILD-clang-analyzer @@ -51,6 +51,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -G Ninja scan-build --use-c++=$(which clang++) --view ninja } diff --git a/package/archlinux/PKGBUILD-clang-libc++ b/package/archlinux/PKGBUILD-clang-libc++ index 66543f52d3..132f27ba45 100644 --- a/package/archlinux/PKGBUILD-clang-libc++ +++ b/package/archlinux/PKGBUILD-clang-libc++ @@ -61,7 +61,8 @@ build() { -DWITH_GL_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ - -DBUILD_GL_TESTS=ON + -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON ninja } diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 3b2518090e..b7166d540e 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -57,6 +57,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -G Ninja ninja } diff --git a/package/archlinux/PKGBUILD-gcc48 b/package/archlinux/PKGBUILD-gcc48 index 8af8244857..341d38d870 100644 --- a/package/archlinux/PKGBUILD-gcc48 +++ b/package/archlinux/PKGBUILD-gcc48 @@ -61,7 +61,8 @@ build() { -DWITH_GL_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ - -DBUILD_GL_TESTS=ON + -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON ninja } diff --git a/package/archlinux/PKGBUILD-mingw-w64 b/package/archlinux/PKGBUILD-mingw-w64 index 2078372a65..bd22f9503b 100644 --- a/package/archlinux/PKGBUILD-mingw-w64 +++ b/package/archlinux/PKGBUILD-mingw-w64 @@ -46,6 +46,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=OFF \ -G Ninja ninja @@ -82,6 +83,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=OFF \ -G Ninja ninja } diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index f320c01613..7d793b45aa 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -51,6 +51,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -G Ninja ninja @@ -91,6 +92,7 @@ build() { -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -G Ninja ninja } diff --git a/package/ci/appveyor-desktop-vulkan.bat b/package/ci/appveyor-desktop-vulkan.bat index ae383ebed0..d65cbff054 100644 --- a/package/ci/appveyor-desktop-vulkan.bat +++ b/package/ci/appveyor-desktop-vulkan.bat @@ -61,12 +61,13 @@ cmake .. ^ -DWITH_GLFWAPPLICATION=ON ^ -DBUILD_TESTS=ON ^ -DBUILD_GL_TESTS=OFF ^ + -DBUILD_VK_TESTS=ON ^ -G Ninja || exit /b cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E GLTest || exit /b +ctest -V -E "(GL|Vk)Test" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index 19dd8839a7..cbd52c98c7 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -65,10 +65,11 @@ cmake .. \ -DWITH_GLFWAPPLICATION=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=OFF \ + -DBUILD_VK_TESTS=ON \ -DBUILD_DEPRECATED=$BUILD_DEPRECATED \ -G Ninja ninja -ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$TRAVIS_BUILD_DIR/package/ci/leaksanitizer.conf" CORRADE_TEST_COLOR=ON ctest -V -E GLTest +ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$TRAVIS_BUILD_DIR/package/ci/leaksanitizer.conf" CORRADE_TEST_COLOR=ON ctest -V -E "(GL|Vk)Test" # Test install, after running the tests as for them it shouldn't be needed ninja install From 6c9c9d9194474e8cbb330348f9d6e363e0488695 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 20 Jun 2020 00:17:38 +0200 Subject: [PATCH 06/85] Vk: expose all extensions for version 1.2. Also provide compile-time info with them, a runtime list and everything else similar to what's done for GL. --- doc/Doxyfile | 2 + doc/Doxyfile-mcss | 2 + doc/Doxyfile-public | 2 + doc/vulkan-mapping.dox | 299 +++++++++++ doc/vulkan-support.dox | 128 +++++ doc/vulkan.dox | 43 +- src/Magnum/Vk/CMakeLists.txt | 5 + src/Magnum/Vk/Device.h | 43 ++ src/Magnum/Vk/Extensions.cpp | 135 +++++ src/Magnum/Vk/Extensions.h | 248 +++++++++ src/Magnum/Vk/Instance.h | 43 ++ src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/ExtensionsTest.cpp | 229 ++++++++ src/Magnum/Vk/TypeTraits.h | 47 ++ src/Magnum/Vk/Vk.h | 3 + src/MagnumExternal/Vulkan/extensions.txt | 92 +++- src/MagnumExternal/Vulkan/flextVk.cpp | 33 +- src/MagnumExternal/Vulkan/flextVk.h | 608 +++++++++++++++++----- src/MagnumExternal/Vulkan/flextVkGlobal.h | 81 ++- 19 files changed, 1887 insertions(+), 157 deletions(-) create mode 100644 doc/vulkan-mapping.dox create mode 100644 doc/vulkan-support.dox create mode 100644 src/Magnum/Vk/Device.h create mode 100644 src/Magnum/Vk/Extensions.cpp create mode 100644 src/Magnum/Vk/Extensions.h create mode 100644 src/Magnum/Vk/Instance.h create mode 100644 src/Magnum/Vk/Test/ExtensionsTest.cpp create mode 100644 src/Magnum/Vk/TypeTraits.h diff --git a/doc/Doxyfile b/doc/Doxyfile index cbdf75f6c6..fd6a8a2c0a 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -303,6 +303,8 @@ ALIASES = \ "type_vk{1}=Vk\1" \ "val_vk{2}=VK_\1" \ "def_vk{1}=VK_\1" \ + "requires_vk11=@xrefitem requires-vk11 \"Requires Vulkan 1.1\" \"Functionality requiring Vulkan 1.1\"" \ + "requires_vk12=@xrefitem requires-vk12 \"Requires Vulkan 1.2\" \"Functionality requiring Vulkan 1.2\"" \ "requires_vk_extension=@xrefitem requires-vk-extension \"Requires Vulkan extension\" \"Functionality requiring specific Vulkan extension\"" \ "vk_extension{2}= \1_\2" \ "fn_al{1}=`al\1()`" \ diff --git a/doc/Doxyfile-mcss b/doc/Doxyfile-mcss index 1aa2182c77..fdfda237a4 100644 --- a/doc/Doxyfile-mcss +++ b/doc/Doxyfile-mcss @@ -60,6 +60,8 @@ ALIASES = \ "type_vk{1}=@m_class{m-doc-external} Vk\1" \ "val_vk{2}=@m_class{m-doc-external} VK_\1" \ "def_vk{1}=@m_class{m-doc-external} VK_\1" \ + "requires_vk11=@xrefitem requires-vk11 \"Requires Vulkan 1.1\" \"Functionality requiring Vulkan 1.1\"" \ + "requires_vk12=@xrefitem requires-vk12 \"Requires Vulkan 1.2\" \"Functionality requiring Vulkan 1.2\"" \ "requires_vk_extension=@xrefitem requires-vk-extension \"Requires Vulkan extension\" \"Functionality requiring specific Vulkan extension\"" \ "vk_extension{2}=@m_class{m-doc-external} \1_\2" \ "fn_al{1}=@m_class{m-doc-external} al\1()" \ diff --git a/doc/Doxyfile-public b/doc/Doxyfile-public index 29d4e19712..4460f654f0 100644 --- a/doc/Doxyfile-public +++ b/doc/Doxyfile-public @@ -56,6 +56,8 @@ ALIASES = \ "type_vk{1}=@m_class{m-doc-external} Vk\1" \ "val_vk{2}=@m_class{m-doc-external} VK_\1" \ "def_vk{1}=@m_class{m-doc-external} VK_\1" \ + "requires_vk11=@xrefitem requires-vk11 \"Requires Vulkan 1.1\" \"Functionality requiring Vulkan 1.1\"" \ + "requires_vk12=@xrefitem requires-vk12 \"Requires Vulkan 1.2\" \"Functionality requiring Vulkan 1.2\"" \ "requires_vk_extension=@xrefitem requires-vk-extension \"Requires Vulkan extension\" \"Functionality requiring specific Vulkan extension\"" \ "vk_extension{2}=@m_class{m-doc-external} \1_\2" \ "fn_al{1}=@m_class{m-doc-external} al\1()" \ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox new file mode 100644 index 0000000000..c53492551d --- /dev/null +++ b/doc/vulkan-mapping.dox @@ -0,0 +1,299 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +namespace Magnum { namespace Vk { + +/** @page vulkan-mapping Command mapping +@brief List of Vulkan commands corresponding to particular Magnum API. + +@tableofcontents +@m_footernavigation + +Legend: + +- @m_class{m-label m-flat m-success} **X.Y** --- given feature is new in + Vulkan version X.Y +- @m_class{m-label m-flat m-success} **ABC, X.Y** --- given feature is + exposed though an ABC extension and then promoted to Vulkan version X.Y; + both the extension and the core entrypoints are available +- @m_class{m-label m-flat m-warning} **ABC** --- given feature is + exposed though an ABC extension +- @m_class{m-label m-danger} **deprecated** --- given feature is deprecated + in newer versions of Vulkan and replaced by different functionality + +@section vulkan-mapping-functions Functions + +@subsection vulkan-mapping-functions-a A + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | | +@fn_vk{AllocateDescriptorSets}, \n @fn_vk{FreeDescriptorSets} | | +@fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | | + +@subsection vulkan-mapping-functions-b B + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{BeginCommandBuffer}, \n \fn_vk{EndCommandBuffer} | | +@fn_vk{BindBufferMemory}, \n @fn_vk{BindBufferMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{BindImageMemory}, \n @fn_vk{BindImageMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | + +@subsection vulkan-mapping-functions-c C + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{CmdBeginQuery}, \n @fn_vk{CmdEndQuery} | | +@fn_vk{CmdBeginDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{CmdEndebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdBeginRenderPass}, \n @fn_vk{CmdBeginRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{CmdEndRenderpass}, \n @fn_vk{CmdEndRenderpass2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{CmdBindDescriptorSets} | | +@fn_vk{CmdBindIndexBuffer} | | +@fn_vk{CmdBindPipeline} | | +@fn_vk{CmdBindVertexBuffers} | | +@fn_vk{CmdBlitImage} | | +@fn_vk{CmdClearAttachments} | | +@fn_vk{CmdClearColorImage} | | +@fn_vk{CmdClearDepthStencilImage} | | +@fn_vk{CmdCopyBuffer} | | +@fn_vk{CmdCopyBufferToImage} | | +@fn_vk{CmdCopyImage} | | +@fn_vk{CmdCopyImageToBuffer} | | +@fn_vk{CmdCopyQueryPoolResults} | | +@fn_vk{CmdDebugMarkerBeginEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{CmdDebugMarkerEndEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdDebugMarkerInsertEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdDispatch} | | +@fn_vk{CmdDispatchBase} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{CmdDispatchIndirect} | | +@fn_vk{CmdDraw} | | +@fn_vk{CmdDrawIndexed} | | +@fn_vk{CmdDrawIndexedIndirect} | | +@fn_vk{CmdDrawIndexedIndirectCount} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{CmdDrawIndirect} | | +@fn_vk{CmdDrawIndirectCount} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{CmdExecuteCommands} | | +@fn_vk{CmdFillBuffer} | | +@fn_vk{CmdInsertDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CmdNextSubpass}, \n @fn_vk{CmdNextSubpass2} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{CmdPipelineBarrier} | | +@fn_vk{CmdPushConstants} | | +@fn_vk{CmdResetEvent} | | +@fn_vk{CmdResetQueryPool} | | +@fn_vk{CmdResolveImage} | | +@fn_vk{CmdSetBlendConstants} | | +@fn_vk{CmdSetDepthBias} | | +@fn_vk{CmdSetDeviceMask} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{CmdSetDepthBounds} | | +@fn_vk{CmdSetEvent} | | +@fn_vk{CmdSetLineWidth} | | +@fn_vk{CmdSetScissor} | | +@fn_vk{CmdSetStencilCompareMask} | | +@fn_vk{CmdSetStencilReference} | | +@fn_vk{CmdSetStencilWriteMask} | | +@fn_vk{CmdSetViewport} | | +@fn_vk{CmdUpdateBuffer} | | +@fn_vk{CmdWaitEvents} | | +@fn_vk{CmdWriteTimestamp} | | +@fn_vk{CreateBuffer}, \n @fn_vk{DestroyBuffer} | | +@fn_vk{CreateBufferView}, \n @fn_vk{DestroyBufferView} | | +@fn_vk{CreateCommandPool}, \n @fn_vk{DestroyCommandPool} | | +@fn_vk{CreateComputePipelines}, \n @fn_vk{DestroyComputePipelines} | | +@fn_vk{CreateDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CreateDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{CreateDescriptorPool}, \n @fn_vk{DestroyDescriptorPool} | | +@fn_vk{CreateDescriptorSetLayout}, \n @fn_vk{DestroyDescriptorSetLayout} | | +@fn_vk{CreateDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1**, \n @fn_vk{DestroyDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | | +@fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | +@fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | | +@fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | +@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | | +@fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | | +@fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | | +@fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | +@fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | | +@fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | | +@fn_vk{CreateQueryPool}, \n @fn_vk{DestroyQueryPool} | | +@fn_vk{CreateRenderPass}, \n @fn_vk{CreateRenderPass2} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{DestroyRenderPass} | | +@fn_vk{CreateSampler}, \n @fn_vk{DestroySampler} | | +@fn_vk{CreateSamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** , \n @fn_vk{DestroySamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{CreateSemaphore}, \n @fn_vk{DestroySemaphore} | | +@fn_vk{CreateShaderModule}, \n @fn_vk{DestroyShaderModule} | | + +@subsection vulkan-mapping-functions-d D + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{DeviceWaitIdle} | | +@fn_vk{DebugMarkerSetObjectNameEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{DebugMarkerSetObjectTagEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{DebugReportMessageEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | + +@subsection vulkan-mapping-functions-e E + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{EnumerateDeviceLayerProperties} @m_class{m-label m-danger} **deprecated in 1.0.13** | not exposed, [spec commit](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) +@fn_vk{EnumerateDeviceExtensionProperties} | | +@fn_vk{EnumerateInstanceExtensionProperties} | | +@fn_vk{EnumerateInstanceLayerProperties} | | +@fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | | +@fn_vk{EnumeratePhysicalDevices} | | +@fn_vk{EnumeratePhysicalDeviceGroups} @m_class{m-label m-flat m-success} **KHR, 1.1** | | + +@subsection vulkan-mapping-functions-f F + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{FlushMappedMemoryRanges} | | + +@subsection vulkan-mapping-functions-g G + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{GetBufferDeviceAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{GetBufferOpaqueCaptureAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{GetDeviceMemoryOpaqueCaptureAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{GetBufferMemoryRequirements}, \n @fn_vk{GetBufferMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetDescriptorSetLayoutSupport} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetDeviceMemoryCommitment} | | +@fn_vk{GetDeviceProcAddr} | | +@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | | +@fn_vk{GetEventStatus} | | +@fn_vk{GetFenceStatus} | | +@fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetImageSubresourceLayout} | | +@fn_vk{GetInstanceProcAddr} | | +@fn_vk{GetPhysicalDeviceExternalBufferProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceExternalFenceProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceExternalSemaphoreProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceFeatures}, \n @fn_vk{GetPhysicalDeviceFeatures2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceFormatProperties}, \n @fn_vk{GetPhysicalDeviceFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceMemoryProperties}, \n @fn_vk{GetPhysicalDeviceMemoryProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceProperties}, \n @fn_vk{GetPhysicalDeviceProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceQueueFamilyProperties}, \n @fn_vk{GetPhysicalDeviceQueueFamilyProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceSparseImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceSparseImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPipelineCacheData} | | +@fn_vk{GetQueryPoolResults} | | +@fn_vk{GetRenderAreaGranularity} | | +@fn_vk{GetSemaphoreCounterValue} @m_class{m-label m-flat m-success} **KHR, 1.2** | | + +@subsection vulkan-mapping-functions-i I + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{InvalidateMappedMemoryRanges} | | + +@subsection vulkan-mapping-functions-m M + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{MapMemory}, \n @fn_vk{UnmapMemory} | | +@fn_vk{MergePipelineCaches} | | + +@subsection vulkan-mapping-functions-q Q + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{QueueBeginDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{QueueEndDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{QueueBindSparse} | | +@fn_vk{QueueInsertDebugUtilsLabelEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{QueueSubmit} | | +@fn_vk{QueueWaitIdle} | | + +@subsection vulkan-mapping-functions-r R + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{ResetCommandBuffer} | | +@fn_vk{ResetCommandPool} | | +@fn_vk{ResetDescriptorPool} | | +@fn_vk{ResetFences} | | +@fn_vk{ResetQueryPool} @m_class{m-label m-flat m-success} **EXT, 1.2** | | + +@subsection vulkan-mapping-functions-s S + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{SetDebugUtilsObjectNameEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{SetDebugUtilsObjectTagEXT} @m_class{m-label m-flat m-warning} **EXT** | | +@fn_vk{SetEvent}, \n @fn_vk{ResetEvent} | | +@fn_vk{SignalSemaphore} @m_class{m-label m-flat m-success} **KHR, 1.2**, \n @fn_vk{WaitSemaphores} @m_class{m-label m-flat m-success} **KHR, 1.2** | | +@fn_vk{SubmitDebugUtilsMessageEXT} @m_class{m-label m-flat m-warning} **EXT** | | + +@subsection vulkan-mapping-functions-t T + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{TrimCommandPool} @m_class{m-label m-flat m-success} **KHR, 1.1** | | + +@subsection vulkan-mapping-functions-u U + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{UpdateDescriptorSets} | | +@fn_vk{UpdateDescriptorSetWithTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | | + +@subsection vulkan-mapping-functions-w W + +@m_class{m-fullwidth} + +Vulkan function | Matching API +--------------------------------------- | ------------ +@fn_vk{WaitForFences} | | + +*/ + +}} diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox new file mode 100644 index 0000000000..95309a5dbf --- /dev/null +++ b/doc/vulkan-support.dox @@ -0,0 +1,128 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +namespace Magnum { + +/** @page vulkan-support Support state +@brief List of (un)supported Vulkan features and extensions. +@m_since_latest + +@tableofcontents +@m_footernavigation + +@section vulkan-support-state Vulkan implementation state + +The extension implementation is considered complete if all its defined types, +functions and enum values are exposed through the API. + +All extensions from the below lists are available in the @ref Vk::Extensions +namespace. Extensions marked with @m_class{m-label m-info} **instance** are +instance extensions, unmarked ones are device extensions. Extensions marked +with @m_class{m-label m-danger} **deprecated** are superseded by other +extensions, but still included for compatibility with drivers that don't +provide support for the replacement extensions. + +@subsection vulkan-support-10 Vulkan 1.0 + +TBD + +@subsection vulkan-support-11 Vulkan 1.1 + +@m_class{m-fullwidth} + +Extension | Status +--------------------------------------------------- | ------ +@vk_extension{KHR,multiview} | | +@vk_extension{KHR,get_physical_device_properties2} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,device_group} | | +@vk_extension{KHR,shader_draw_parameters} | | +@vk_extension{KHR,maintenance1} | | +@vk_extension{KHR,device_group_creation} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,external_memory_capabilities} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,external_memory} | | +@vk_extension{KHR,external_semaphore_capabilities} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,external_semaphore} | | +@vk_extension{KHR,16bit_storage} | | +@vk_extension{KHR,descriptor_update_template} | | +@vk_extension{KHR,external_fence_capabilities} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,external_fence} | | +@vk_extension{KHR,maintenance2} | | +@vk_extension{KHR,variable_pointers} | | +@vk_extension{KHR,dedicated_allocation} | | +@vk_extension{KHR,storage_buffer_storage_class} | | +@vk_extension{KHR,relaxed_block_layout} | | +@vk_extension{KHR,get_memory_requirements2} | | +@vk_extension{KHR,sampler_ycbcr_conversion} | | +@vk_extension{KHR,bind_memory2} | | +@vk_extension{KHR,maintenance3} | | + +@subsection vulkan-support-12 Vulkan 1.2 + +@m_class{m-fullwidth} + +Extension | Status +--------------------------------------------------- | ------ +@vk_extension{KHR,sampler_mirror_clamp_to_edge} | | +@vk_extension{KHR,shader_float16_int8} | | +@vk_extension{KHR,imageless_framebuffer} | | +@vk_extension{KHR,create_renderpass2} | | +@vk_extension{EXT,sampler_filter_minmax} | | +@vk_extension{KHR,image_format_list} | | +@vk_extension{EXT,descriptor_indexing} | | +@vk_extension{EXT,shader_viewport_index_layer} | | +@vk_extension{KHR,draw_indirect_count} | | +@vk_extension{KHR,shader_subgroup_extended_types} | | +@vk_extension{KHR,8bit_storage} | | +@vk_extension{KHR,shader_atomic_int64} | | +@vk_extension{KHR,driver_properties} | | +@vk_extension{KHR,shader_float_controls} | | +@vk_extension{KHR,depth_stencil_resolve} | | +@vk_extension{KHR,timeline_semaphore} | | +@vk_extension{KHR,vulkan_memory_model} | | +@vk_extension{KHR,spirv_1_4} | | +@vk_extension{EXT,scalar_block_layout} | | +@vk_extension{KHR,separate_depth_stencil_layouts} | | +@vk_extension{EXT,separate_stencil_usage} | | +@vk_extension{KHR,uniform_buffer_standard_layout} | | +@vk_extension{KHR,buffer_device_address} | | +@vk_extension{EXT,host_query_reset} | | + +@section vulkan-support-extensions-vendor Vendor Vulkan extensions + +@m_class{m-fullwidth} + +Extension | Status +--------------------------------------------------- | ------ +@vk_extension{EXT,debug_report} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-info} **instance** | | +@vk_extension{EXT,debug_marker} @m_class{m-label m-danger} **deprecated** | | +@vk_extension{EXT,texture_compression_astc_hdr} | | +@vk_extension{EXT,debug_utils} @m_class{m-label m-info} **instance** | | +@vk_extension{EXT,validation_features} @m_class{m-label m-info} **instance** | | +@vk_extension{EXT,index_type_uint8} | | +@vk_extension{IMG,format_pvrtc} | | + +*/ + +} diff --git a/doc/vulkan.dox b/doc/vulkan.dox index 8a851ef011..71035fe463 100644 --- a/doc/vulkan.dox +++ b/doc/vulkan.dox @@ -28,7 +28,48 @@ namespace Magnum { /** @page vulkan Vuulkan @brief State of Vulkan support, version and extension requirements. -Nothing to see here yet, sorry. +The following table maps Vulkan function names to Magnum API, useful for +developers with existing Vulkan background. Note that, as reverse mapping, each +function documentation also contains list of Vulkan functions used. You can +also use the search functionality to find out which Vulkan API corresponds to +which Magnum function, class or enum value. Just enter name of an Vulkan +function, define or GLSL name into the search box. + +- @subpage vulkan-mapping + +State of implementation of particular Vulkan versions and extensions is in the +following table. + +- @subpage vulkan-support + +@section vulkan-required-extensions Version and extension requirements + +The engine can work on unextended Vulkan 1.0, but some specific functionality +has greater requirements. Following are lists of features requiring specific +Vulkan version. In most cases it is also specified which extension is required, +so if a particular driver supports the required extension, it doesn't need to +have required Vulkan version too (e.g. @vk_extension{KHR,sampler_mirror_clamp_to_edge} +is usually supported everywhere, even if the drivers don't have Vulkan 1.2). + +- @subpage requires-vk11 +- @subpage requires-vk12 +- @subpage requires-vk-extension + */ +/** @page requires-vk11 Functionality requiring Vulkan 1.1 + * @m_footernavigation + * @m_since_latest + */ + +/** @page requires-vk12 Functionality requiring Vulkan 1.2 + * @m_footernavigation + * @m_since_latest + */ + +/** @page requires-vk-extension Functionality requiring specific Vulkan extension + * @m_footernavigation + * @m_since_latest + */ + } diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 0a89627229..117450208b 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + Extensions.cpp Result.cpp Version.cpp) @@ -34,9 +35,13 @@ set(MagnumVk_GracefulAssert_SRCS Enums.cpp) set(MagnumVk_HEADERS + Device.h Enums.h + Extensions.h + Instance.h Integration.h Result.h + TypeTraits.h Version.h Vk.h Vulkan.h diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h new file mode 100644 index 0000000000..c707338d16 --- /dev/null +++ b/src/Magnum/Vk/Device.h @@ -0,0 +1,43 @@ +#ifndef Magnum_Vk_Device_h +#define Magnum_Vk_Device_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Nothing, haha + * @m_since_latest + */ + +#include + +namespace Magnum { namespace Vk { + +namespace Implementation { + enum: std::size_t { ExtensionCount = 72 }; +} + +}} + +#endif diff --git a/src/Magnum/Vk/Extensions.cpp b/src/Magnum/Vk/Extensions.cpp new file mode 100644 index 0000000000..b34631784d --- /dev/null +++ b/src/Magnum/Vk/Extensions.cpp @@ -0,0 +1,135 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Extensions.h" + +#include + +namespace Magnum { namespace Vk { + +namespace { + +/* When adding a new list, InstanceExtension::extensions() needs to be adapted. + Binary search is performed on the extensions, thus they have to be sorted + alphabetically. */ +constexpr InstanceExtension InstanceExtensions[] { + Extensions::EXT::debug_report{}, + Extensions::EXT::debug_utils{}, + Extensions::EXT::validation_features{}, +}; +constexpr InstanceExtension InstanceExtensions11[] { + Extensions::KHR::device_group_creation{}, + Extensions::KHR::external_fence_capabilities{}, + Extensions::KHR::external_memory_capabilities{}, + Extensions::KHR::external_semaphore_capabilities{}, + Extensions::KHR::get_physical_device_properties2{}, +}; +/* No Vulkan 1.2 instance extensions */ + +} + +Containers::ArrayView InstanceExtension::extensions(const Version version) { + switch(version) { + case Version::None: return Containers::arrayView(InstanceExtensions); + case Version::Vk10: return nullptr; + case Version::Vk11: return Containers::arrayView(InstanceExtensions11); + case Version::Vk12: return nullptr; + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +namespace { + +/* When adding a new list, Extension::extensions() needs to be adapted. Binary + search is performed on the extensions, thus they have to be sorted + alphabetically. */ +constexpr Extension DeviceExtensions[] { + Extensions::EXT::debug_marker{}, + Extensions::EXT::index_type_uint8{}, + Extensions::EXT::texture_compression_astc_hdr{}, + Extensions::IMG::format_pvrtc{}, +}; +constexpr Extension DeviceExtensions11[] { + //Extensions::KHR::16bit_storage{}, + Extensions::KHR::bind_memory2{}, + Extensions::KHR::dedicated_allocation{}, + Extensions::KHR::descriptor_update_template{}, + Extensions::KHR::device_group{}, + Extensions::KHR::external_fence{}, + Extensions::KHR::external_memory{}, + Extensions::KHR::external_semaphore{}, + Extensions::KHR::get_memory_requirements2{}, + Extensions::KHR::maintenance1{}, + Extensions::KHR::maintenance2{}, + Extensions::KHR::maintenance3{}, + Extensions::KHR::multiview{}, + Extensions::KHR::relaxed_block_layout{}, + Extensions::KHR::sampler_ycbcr_conversion{}, + Extensions::KHR::shader_draw_parameters{}, + Extensions::KHR::storage_buffer_storage_class{}, + Extensions::KHR::variable_pointers{}, +}; +constexpr Extension DeviceExtensions12[] { + Extensions::EXT::descriptor_indexing{}, + Extensions::EXT::host_query_reset{}, + Extensions::EXT::sampler_filter_minmax{}, + Extensions::EXT::scalar_block_layout{}, + Extensions::EXT::separate_stencil_usage{}, + Extensions::EXT::shader_viewport_index_layer{}, + //Extensions::KHR::8bit_storage{}, + Extensions::KHR::buffer_device_address{}, + Extensions::KHR::create_renderpass2{}, + Extensions::KHR::depth_stencil_resolve{}, + Extensions::KHR::draw_indirect_count{}, + Extensions::KHR::driver_properties{}, + Extensions::KHR::image_format_list{}, + Extensions::KHR::imageless_framebuffer{}, + Extensions::KHR::sampler_mirror_clamp_to_edge{}, + Extensions::KHR::separate_depth_stencil_layouts{}, + Extensions::KHR::shader_atomic_int64{}, + Extensions::KHR::shader_float16_int8{}, + Extensions::KHR::shader_float_controls{}, + Extensions::KHR::shader_subgroup_extended_types{}, + Extensions::KHR::spirv_1_4{}, + Extensions::KHR::timeline_semaphore{}, + Extensions::KHR::uniform_buffer_standard_layout{}, + Extensions::KHR::vulkan_memory_model{}, +}; + +} + +Containers::ArrayView Extension::extensions(const Version version) { + switch(version) { + case Version::None: return Containers::arrayView(DeviceExtensions); + case Version::Vk10: return nullptr; + case Version::Vk11: return Containers::arrayView(DeviceExtensions11); + case Version::Vk12: return Containers::arrayView(DeviceExtensions12); + } + + CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */ +} + +}} diff --git a/src/Magnum/Vk/Extensions.h b/src/Magnum/Vk/Extensions.h new file mode 100644 index 0000000000..f35d26d0d6 --- /dev/null +++ b/src/Magnum/Vk/Extensions.h @@ -0,0 +1,248 @@ +#ifndef Magnum_Vk_Extensions_h +#define Magnum_Vk_Extensions_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Namespace @ref Magnum::Vk::Extensions, class @ref Magnum::Vk::InstanceExtension, @ref Magnum::Vk::Extension + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/TypeTraits.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { + +/** +@brief Compile-time information about Vulkan instance and device extensions +@m_since_latest + +Each extension is a @cpp struct @ce named hierarchically by prefix, vendor and +extension name taken from list at @ref vulkan-support, for example +@cpp Vk::Extensions::EXT::debug_report @ce. + +Each struct has the same public methods as the @ref InstanceExtension / +@ref Extension class (@ref Extension::requiredVersion() "requiredVersion()", +@ref Extension::coreVersion() "coreVersion()" and @ref Extension::string() "string()"), +but these structs are better suited for compile-time decisions rather than +@ref Extension instances. + +This library is built if `WITH_VK` is enabled when building Magnum. To use this +library with CMake, you need to request the `Vk` component of the `Magnum` +package and link to the `Magnum::Vk` target: + +@code{.cmake} +find_package(Magnum REQUIRED Vk) + +# ... +target_link_libraries(your-app Magnum::Vk) +@endcode + +See @ref building, @ref cmake and @ref vulkan for more information. +*/ +namespace Extensions { + +#ifndef DOXYGEN_GENERATING_OUTPUT + +#define _extension(index, vendor, extension, _requiredVersion, _coreVersion) \ + struct extension { \ + enum: std::size_t { InstanceIndex = index }; \ + constexpr static Version requiredVersion() { return Version::_requiredVersion; } \ + constexpr static Version coreVersion() { return Version::_coreVersion; } \ + constexpr static Containers::StringView string() { \ + using namespace Containers::Literals; \ + return "VK_" #vendor "_" #extension ""_s; \ + } \ + }; +namespace EXT { + /** @todo remove EXT_debug_report when all platforms have EXT_debug_utils + (my Huawei P10 doesn't have it) */ + _extension(0, EXT,debug_report, Vk10, None) // #12 + _extension(1, EXT,debug_utils, Vk10, None) // #129 + _extension(2, EXT,validation_features, Vk10, None) // #248 +} namespace KHR { + _extension(10, KHR,get_physical_device_properties2, Vk10, Vk11) // #60 + _extension(11, KHR,device_group_creation, Vk10, Vk11) // #71 + _extension(12, KHR,external_memory_capabilities, Vk10, Vk11) // #72 + _extension(13, KHR,external_semaphore_capabilities, Vk10, Vk11) // #77 + _extension(14, KHR,external_fence_capabilities, Vk10, Vk11) // #113 +} +#undef _extension + +#define _extension(index, vendor, extension, _requiredVersion, _coreVersion) \ + struct extension { \ + enum: std::size_t { Index = index }; \ + constexpr static Version requiredVersion() { return Version::_requiredVersion; } \ + constexpr static Version coreVersion() { return Version::_coreVersion; } \ + constexpr static Containers::StringView string() { \ + using namespace Containers::Literals; \ + return "VK_" #vendor "_" #extension ""_s; \ + } \ + }; +namespace EXT { + /** @todo remove EXT_debug_marker when all platforms have EXT_debug_utils + (my Huawei P10 doesn't have it) */ + _extension(0, EXT,debug_marker, Vk10, None) // #23 + _extension(1, EXT,texture_compression_astc_hdr, Vk10, None) // #67 + _extension(2, EXT,sampler_filter_minmax, Vk10, Vk12) // #131 + _extension(3, EXT,descriptor_indexing, Vk10, Vk12) // #162 + _extension(4, EXT,shader_viewport_index_layer, Vk10, Vk12) // #163 + _extension(5, EXT,scalar_block_layout, Vk10, Vk12) // #222 + _extension(6, EXT,separate_stencil_usage, Vk10, Vk12) // #247 + _extension(7, EXT,host_query_reset, Vk10, Vk12) // #262 + _extension(8, EXT,index_type_uint8, Vk10, None) // #266 +} namespace IMG { + _extension(20, IMG,format_pvrtc, Vk10, None) // #55 +} namespace KHR { + _extension(30, KHR,sampler_mirror_clamp_to_edge, Vk10, Vk12) // #15 + _extension(31, KHR,multiview, Vk10, Vk11) // #54 + _extension(32, KHR,device_group, Vk10, Vk11) // #61 + _extension(33, KHR,shader_draw_parameters, Vk10, Vk11) // #64 + _extension(34, KHR,maintenance1, Vk10, Vk11) // #70 + _extension(35, KHR,external_semaphore, Vk10, Vk11) // #78 + _extension(36, KHR,shader_float16_int8, Vk10, Vk12) // #83 + //_extension(37, KHR,16bit_storage, VK10, Vk11) // #84 + _extension(38, KHR,descriptor_update_template, Vk10, Vk11) // #86 + _extension(39, KHR,external_memory, Vk10, Vk11) // #73 + _extension(40, KHR,imageless_framebuffer, Vk10, Vk12) // #109 + _extension(41, KHR,create_renderpass2, Vk10, Vk12) // #110 + _extension(42, KHR,external_fence, Vk10, Vk11) // #114 + _extension(43, KHR,maintenance2, Vk10, Vk11) // #118 + _extension(44, KHR,variable_pointers, Vk10, Vk11) // #121 + _extension(45, KHR,dedicated_allocation, Vk10, Vk11) // #128 + _extension(46, KHR,storage_buffer_storage_class, Vk10, Vk11) // #142 + _extension(47, KHR,relaxed_block_layout, Vk10, Vk11) // #145 + _extension(48, KHR,get_memory_requirements2, Vk10, Vk11) // #147 + _extension(49, KHR,image_format_list, Vk10, Vk12) // #148 + _extension(50, KHR,sampler_ycbcr_conversion, Vk10, Vk11) // #157 + _extension(51, KHR,bind_memory2, Vk10, Vk11) // #158 + _extension(52, KHR,maintenance3, Vk10, Vk11) // #169 + _extension(53, KHR,draw_indirect_count, Vk10, Vk12) // #170 + _extension(54, KHR,shader_subgroup_extended_types, Vk11, Vk12) // #176 + //_extension(55, KHR,8bit_storage, Vk10, Vk12) // #178 + _extension(56, KHR,shader_atomic_int64, Vk10, Vk12) // #181 + _extension(57, KHR,driver_properties, Vk10, Vk12) // #197 + _extension(58, KHR,shader_float_controls, Vk10, Vk12) // #198 + _extension(59, KHR,depth_stencil_resolve, Vk10, Vk12) // #200 + _extension(60, KHR,timeline_semaphore, Vk10, Vk12) // #208 + _extension(61, KHR,vulkan_memory_model, Vk10, Vk12) // #212 + _extension(62, KHR,spirv_1_4, Vk11, Vk12) // #237 + _extension(63, KHR,separate_depth_stencil_layouts, Vk10, Vk12) // #242 + _extension(64, KHR,uniform_buffer_standard_layout, Vk10, Vk12) // #254 + _extension(65, KHR,buffer_device_address, Vk10, Vk12) // #258 +} +#undef _extension + +#endif + +} + +/** +@brief Run-time information about a Vulkan instance extension +@m_since_latest + +Encapsulates runtime information about a Vulkan extension, such as name string, +minimal required Vulkan version and version in which the extension was adopted +to core. + +See also the @ref Extensions namespace, which contain compile-time information +about Vulkan extensions. +*/ +class MAGNUM_VK_EXPORT InstanceExtension { + public: + /** @brief All known instance extensions for given Vulkan version */ + static Containers::ArrayView extensions(Version version); + + /** @brief Internal unique extension index */ + constexpr std::size_t index() const { return _index; } + + /** @brief Minimal version required by this extension */ + constexpr Version requiredVersion() const { return _requiredVersion; } + + /** @brief Version in which this extension was adopted to core */ + constexpr Version coreVersion() const { return _coreVersion; } + + /** + * @brief Extension string + * + * The returned view is a global memory. + */ + constexpr Containers::StringView string() const { return _string; } + + /** @brief Construct from a compile-time instance extension */ + template::value>::type> constexpr /*implicit*/ InstanceExtension(const E&): _index{E::InstanceIndex}, _requiredVersion{E::requiredVersion()}, _coreVersion{E::coreVersion()}, _string{E::string()} {} + + private: + std::size_t _index; + Version _requiredVersion; + Version _coreVersion; + Containers::StringView _string; +}; + +/** +@brief Run-time information about a Vulkan device extension +@m_since_latest + +Encapsulates runtime information about a Vulkan extension, such as name string, +minimal required Vulkan version and version in which the extension was adopted +to core. + +See also the @ref Extensions namespace, which contain compile-time information +about Vulkan extensions. +*/ +class MAGNUM_VK_EXPORT Extension { + public: + /** @brief All known device extensions for given Vulkan version */ + static Containers::ArrayView extensions(Version version); + + /** @brief Internal unique extension index */ + constexpr std::size_t index() const { return _index; } + + /** @brief Minimal version required by this extension */ + constexpr Version requiredVersion() const { return _requiredVersion; } + + /** @brief Version in which this extension was adopted to core */ + constexpr Version coreVersion() const { return _coreVersion; } + + /** @brief Extension string */ + constexpr Containers::StringView string() const { return _string; } + + /** @brief Construct from a compile-time device extension */ + /** @todo prohibit conversion from GL/AL extensions also? */ + template::value>::type> constexpr /*implicit*/ Extension(const E&): _index{E::Index}, _requiredVersion{E::requiredVersion()}, _coreVersion{E::coreVersion()}, _string{E::string()} {} + + private: + std::size_t _index; + Version _requiredVersion; + Version _coreVersion; + Containers::StringView _string; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h new file mode 100644 index 0000000000..77024c35ab --- /dev/null +++ b/src/Magnum/Vk/Instance.h @@ -0,0 +1,43 @@ +#ifndef Magnum_Vk_Instance_h +#define Magnum_Vk_Instance_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Nothing, haha + * @m_since_latest + */ + +#include + +namespace Magnum { namespace Vk { + +namespace Implementation { + enum: std::size_t { InstanceExtensionCount = 16 }; +} + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 42ef5c9232..c0cffea2be 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -25,6 +25,7 @@ # corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) +corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/ExtensionsTest.cpp b/src/Magnum/Vk/Test/ExtensionsTest.cpp new file mode 100644 index 0000000000..40b0e41278 --- /dev/null +++ b/src/Magnum/Vk/Test/ExtensionsTest.cpp @@ -0,0 +1,229 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Instance.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ExtensionsTest: TestSuite::Tester { + explicit ExtensionsTest(); + + void isInstanceExtension(); + void isExtension(); + + void constructInstanceExtensionFromCompileTimeExtension(); + void constructExtensionFromCompileTimeExtension(); + + void instanceExtensions(); + void extensions(); +}; + +ExtensionsTest::ExtensionsTest() { + addTests({&ExtensionsTest::isInstanceExtension, + &ExtensionsTest::isExtension, + + &ExtensionsTest::constructInstanceExtensionFromCompileTimeExtension, + &ExtensionsTest::constructExtensionFromCompileTimeExtension, + + &ExtensionsTest::instanceExtensions, + &ExtensionsTest::extensions}); +} + +void ExtensionsTest::isInstanceExtension() { + CORRADE_VERIFY(Implementation::IsInstanceExtension::value); + CORRADE_VERIFY(!Implementation::IsInstanceExtension::value); + CORRADE_VERIFY(!Implementation::IsInstanceExtension::value); +} + +void ExtensionsTest::isExtension() { + CORRADE_VERIFY(Implementation::IsExtension::value); + CORRADE_VERIFY(!Implementation::IsExtension::value); + CORRADE_VERIFY(!Implementation::IsExtension::value); + + { + /* Not really a problem right now, but once people hit this we might + want to guard against this (especially because the Index might be + out of bounds) */ + struct GLExtension { + enum: std::size_t { Index }; + }; + CORRADE_EXPECT_FAIL("GL/AL extensions are not rejected right now."); + CORRADE_VERIFY(!Implementation::IsExtension::value); + } +} + +void ExtensionsTest::constructInstanceExtensionFromCompileTimeExtension() { + InstanceExtension a{Extensions::KHR::get_physical_device_properties2{}}; + CORRADE_COMPARE(a.index(), Extensions::KHR::get_physical_device_properties2::InstanceIndex); + CORRADE_COMPARE(a.requiredVersion(), Extensions::KHR::get_physical_device_properties2::requiredVersion()); + CORRADE_COMPARE(a.coreVersion(), Extensions::KHR::get_physical_device_properties2::coreVersion()); + CORRADE_COMPARE(a.string(), Extensions::KHR::get_physical_device_properties2::string()); + + /* Should be convertible from device extensions, but not instance exts */ + CORRADE_VERIFY((std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + + /* Shouldn't be convertible from strings to avoid ambiguity in APIs that + have string/extension overloads */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ExtensionsTest::constructExtensionFromCompileTimeExtension() { + Extension a{Extensions::KHR::external_memory{}}; + CORRADE_COMPARE(a.index(), Extensions::KHR::external_memory::Index); + CORRADE_COMPARE(a.requiredVersion(), Extensions::KHR::external_memory::requiredVersion()); + CORRADE_COMPARE(a.coreVersion(), Extensions::KHR::external_memory::coreVersion()); + CORRADE_COMPARE(a.string(), Extensions::KHR::external_memory::string()); + + /* Should be convertible from device extensions, but not instance exts */ + CORRADE_VERIFY((std::is_convertible::value)); + CORRADE_VERIFY(!(std::is_convertible::value)); + + /* Shouldn't be convertible from strings to avoid ambiguity in APIs that + have string/extension overloads */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ExtensionsTest::instanceExtensions() { + Containers::StringView used[Implementation::InstanceExtensionCount]{}; + + std::set unique; + + /* Check that all extension indices are unique, are in correct lists, are + listed just once etc. */ + for(Version version: { + Version::Vk10, + Version::Vk11, + Version::Vk12, + Version::None}) + { + Containers::StringView previous; + for(const InstanceExtension& e: InstanceExtension::extensions(version)) { + CORRADE_ITERATION(e.string()); + + /** @todo convert to CORRADE_ERROR() when that's done */ + + /* Binary search is performed on each list to find known + extensions, so the exts have to be sorted */ + if(!previous.isEmpty() && previous >= e.string()) { + Error{} << "Extension not sorted after" << previous; + CORRADE_VERIFY(false); + } + + if(e.index() >= Implementation::InstanceExtensionCount) { + Error{} << "Index" << e.index() << "larger than" << Implementation::InstanceExtensionCount; + CORRADE_VERIFY(false); + } + + if(used[e.index()] != nullptr) { + Error{} << "Index" << e.index() << "already used by" << used[e.index()]; + CORRADE_VERIFY(false); + } + + used[e.index()] = e.string(); + if(!unique.insert(e.string()).second) { + Error{} << "Extension listed more than once"; + CORRADE_VERIFY(false); + } + + CORRADE_COMPARE_AS(e.coreVersion(), e.requiredVersion(), TestSuite::Compare::GreaterOrEqual); + if(e.coreVersion() != version) { + Error{} << "Extension should have core version" << version << "but has" << e.coreVersion(); + CORRADE_VERIFY(false); + } + + previous = e.string(); + } + } + + CORRADE_VERIFY(true); +} + +void ExtensionsTest::extensions() { + Containers::StringView used[Implementation::ExtensionCount]{}; + + std::set unique; + + /* Check that all extension indices are unique, are in correct lists, are + not compiled on versions that shouldn't have them, are listed just once + etc. */ + for(Version version: { + Version::Vk10, + Version::Vk11, + Version::Vk12, + Version::None}) + { + Containers::StringView previous; + for(const Extension& e: Extension::extensions(version)) { + CORRADE_ITERATION(e.string()); + + /** @todo convert to CORRADE_ERROR() when that's done */ + + /* Binary search is performed on each list to find known + extensions, so the exts have to be sorted */ + if(!previous.isEmpty() && previous >= e.string()) { + Error{} << "Extension not sorted after" << previous; + CORRADE_VERIFY(false); + } + + if(e.index() >= Implementation::ExtensionCount) { + Error{} << "Index" << e.index() << "larger than" << Implementation::ExtensionCount; + CORRADE_VERIFY(false); + } + + if(used[e.index()] != nullptr) { + Error{} << "Index" << e.index() << "already used by" << used[e.index()]; + CORRADE_VERIFY(false); + } + + used[e.index()] = e.string(); + if(!unique.insert(e.string()).second) { + Error{} << "Extension listed more than once"; + CORRADE_VERIFY(false); + } + + CORRADE_COMPARE_AS(e.coreVersion(), e.requiredVersion(), TestSuite::Compare::GreaterOrEqual); + if(e.coreVersion() != version) { + Error{} << "Extensio should have core version" << version << "but has" << e.coreVersion(); + CORRADE_VERIFY(false); + } + + previous = e.string(); + } + } + + CORRADE_VERIFY(true); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ExtensionsTest) diff --git a/src/Magnum/Vk/TypeTraits.h b/src/Magnum/Vk/TypeTraits.h new file mode 100644 index 0000000000..406c6fbad2 --- /dev/null +++ b/src/Magnum/Vk/TypeTraits.h @@ -0,0 +1,47 @@ +#ifndef Magnum_Vk_TypeTraits_h +#define Magnum_Vk_TypeTraits_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Vulkan type traits + * @m_since_latest + */ + +#include + +namespace Magnum { namespace Vk { + +namespace Implementation { + /* Put into a separate header so APIs that need it don't need to pull in + the whole Extensions.h. Tested in ExtensionsTest also. */ + /** @todo filter out GL/AL extensions also */ + CORRADE_HAS_TYPE(IsInstanceExtension, decltype(T::InstanceIndex)); + CORRADE_HAS_TYPE(IsExtension, decltype(T::Index)); +} + +}} + +#endif diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index b9725d3eac..4b7697e945 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -34,6 +34,9 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class Extension; +class InstanceExtension; + enum class Result: Int; enum class Version: UnsignedInt; #endif diff --git a/src/MagnumExternal/Vulkan/extensions.txt b/src/MagnumExternal/Vulkan/extensions.txt index a63b7f01c7..a00871bc4f 100644 --- a/src/MagnumExternal/Vulkan/extensions.txt +++ b/src/MagnumExternal/Vulkan/extensions.txt @@ -1,32 +1,72 @@ version 1.2 vulkan -extension KHR_16bit_storage optional -extension KHR_bind_memory2 optional -extension KHR_dedicated_allocation optional -extension KHR_descriptor_update_template optional -extension KHR_device_group optional -extension KHR_device_group_creation optional -extension KHR_external_memory optional -extension KHR_external_memory_capabilities optional -extension KHR_external_semaphore optional -extension KHR_external_semaphore_capabilities optional -extension KHR_external_fence optional -extension KHR_external_fence_capabilities optional -extension KHR_get_memory_requirements2 optional -extension KHR_get_physical_device_properties2 optional -extension KHR_maintenance1 optional -extension KHR_maintenance2 optional -extension KHR_maintenance3 optional -extension KHR_multiview optional -extension KHR_relaxed_block_layout optional -extension KHR_sampler_ycbcr_conversion optional -extension KHR_shader_draw_parameters optional -extension KHR_storage_buffer_storage_class optional -extension KHR_variable_pointers optional +# For core extensions, only ones that introduce suffixed function pointers are +# enabled, as structures, types and enums are already present. +# TODO: extend flextGL to include only function pointers from these -extension EXT_texture_compression_astc_hdr optional -extension EXT_index_type_uint8 optional +# Instance extensions +extension KHR_get_physical_device_properties2 optional +extension KHR_device_group_creation optional +extension KHR_external_memory_capabilities optional +extension KHR_external_semaphore_capabilities optional +extension KHR_external_fence_capabilities optional -extension IMG_format_pvrtc optional +# Device extensions +#extension EXT_sampler_filter_minmax optional +#extension EXT_descriptor_indexing optional +#extension EXT_shader_viewport_index_layer optional +#extension EXT_scalar_block_layout optional +#extension EXT_separate_stencil_usage optional +extension EXT_host_query_reset optional +#extension KHR_sampler_mirror_clamp_to_edge optional +#extension KHR_multiview optional +extension KHR_device_group optional +#extension KHR_shader_draw_parameters optional +extension KHR_maintenance1 optional +#extension KHR_external_semaphore optional +#extension KHR_shader_float16_int8 optional +#extension KHR_16bit_storage optional +extension KHR_descriptor_update_template optional +#extension KHR_external_memory optional +#extension KHR_imageless_framebuffer optional +extension KHR_create_renderpass2 optional +#extension KHR_external_fence optional +#extension KHR_maintenance2 optional +#extension KHR_variable_pointers optional +#extension KHR_dedicated_allocation optional +#extension KHR_storage_buffer_storage_class optional +#extension KHR_relaxed_block_layout optional +extension KHR_get_memory_requirements2 optional +#extension KHR_image_format_list optional +extension KHR_sampler_ycbcr_conversion optional +extension KHR_bind_memory2 optional +extension KHR_maintenance3 optional +extension KHR_draw_indirect_count optional +#extension KHR_shader_subgroup_extended_types optional +#extension KHR_8bit_storage optional +#extension KHR_shader_atomic_int64 optional +#extension KHR_driver_properties optional +#extension KHR_shader_float_controls optional +#extension KHR_depth_stencil_resolve optional +extension KHR_timeline_semaphore optional +#extension KHR_vulkan_memory_model optional +#extension KHR_spirv_1_4 optional +#extension KHR_separate_depth_stencil_layouts optional +#extension KHR_uniform_buffer_standard_layout optional +extension KHR_buffer_device_address optional + +# Non-core / vendor extensions +extension EXT_debug_report optional +extension EXT_debug_marker optional +extension EXT_debug_utils optional +extension EXT_validation_features optional +extension EXT_texture_compression_astc_hdr optional +extension EXT_index_type_uint8 optional +extension IMG_format_pvrtc optional + +begin functions blacklist + # Deprecated since 1.0.13, not used + EnumerateDeviceLayerProperties +end functions blacklist # kate: hl python diff --git a/src/MagnumExternal/Vulkan/flextVk.cpp b/src/MagnumExternal/Vulkan/flextVk.cpp index e8ddf1ae5d..2f332a15dc 100644 --- a/src/MagnumExternal/Vulkan/flextVk.cpp +++ b/src/MagnumExternal/Vulkan/flextVk.cpp @@ -33,6 +33,12 @@ FlextVkInstance flextVkInstance{}; FlextVkDevice flextVkDevice{}; void flextVkInitInstance(VkInstance instance, FlextVkInstance* data) { + data->CreateDebugReportCallbackEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugReportCallbackEXT")); + data->DebugReportMessageEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDebugReportMessageEXT")); + data->DestroyDebugReportCallbackEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugReportCallbackEXT")); + data->CreateDebugUtilsMessengerEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT")); + data->DestroyDebugUtilsMessengerEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT")); + data->SubmitDebugUtilsMessageEXT = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkSubmitDebugUtilsMessageEXT")); data->EnumeratePhysicalDeviceGroupsKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDeviceGroupsKHR")); data->GetPhysicalDeviceExternalFencePropertiesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalFencePropertiesKHR")); data->GetPhysicalDeviceExternalBufferPropertiesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalBufferPropertiesKHR")); @@ -47,7 +53,6 @@ void flextVkInitInstance(VkInstance instance, FlextVkInstance* data) { data->CreateDevice = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateDevice")); data->DestroyInstance = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkDestroyInstance")); data->EnumerateDeviceExtensionProperties = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkEnumerateDeviceExtensionProperties")); - data->EnumerateDeviceLayerProperties = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkEnumerateDeviceLayerProperties")); data->EnumeratePhysicalDevices = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkEnumeratePhysicalDevices")); data->GetDeviceProcAddr = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetDeviceProcAddr")); data->GetPhysicalDeviceFeatures = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceFeatures")); @@ -71,14 +76,37 @@ void flextVkInitInstance(VkInstance instance, FlextVkInstance* data) { } void flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction(VKAPI_PTR *getDeviceProcAddr)(VkDevice, const char*)) { + data->CmdDebugMarkerBeginEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDebugMarkerBeginEXT")); + data->CmdDebugMarkerEndEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDebugMarkerEndEXT")); + data->CmdDebugMarkerInsertEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDebugMarkerInsertEXT")); + data->DebugMarkerSetObjectNameEXT = reinterpret_cast(getDeviceProcAddr(device, "vkDebugMarkerSetObjectNameEXT")); + data->DebugMarkerSetObjectTagEXT = reinterpret_cast(getDeviceProcAddr(device, "vkDebugMarkerSetObjectTagEXT")); + data->CmdBeginDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdBeginDebugUtilsLabelEXT")); + data->CmdEndDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdEndDebugUtilsLabelEXT")); + data->CmdInsertDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkCmdInsertDebugUtilsLabelEXT")); + data->QueueBeginDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkQueueBeginDebugUtilsLabelEXT")); + data->QueueEndDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkQueueEndDebugUtilsLabelEXT")); + data->QueueInsertDebugUtilsLabelEXT = reinterpret_cast(getDeviceProcAddr(device, "vkQueueInsertDebugUtilsLabelEXT")); + data->SetDebugUtilsObjectNameEXT = reinterpret_cast(getDeviceProcAddr(device, "vkSetDebugUtilsObjectNameEXT")); + data->SetDebugUtilsObjectTagEXT = reinterpret_cast(getDeviceProcAddr(device, "vkSetDebugUtilsObjectTagEXT")); + data->ResetQueryPoolEXT = reinterpret_cast(getDeviceProcAddr(device, "vkResetQueryPoolEXT")); data->BindBufferMemory2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkBindBufferMemory2KHR")); data->BindImageMemory2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkBindImageMemory2KHR")); + data->GetBufferDeviceAddressKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetBufferDeviceAddressKHR")); + data->GetBufferOpaqueCaptureAddressKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetBufferOpaqueCaptureAddressKHR")); + data->GetDeviceMemoryOpaqueCaptureAddressKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetDeviceMemoryOpaqueCaptureAddressKHR")); + data->CmdBeginRenderPass2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdBeginRenderPass2KHR")); + data->CmdEndRenderPass2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdEndRenderPass2KHR")); + data->CmdNextSubpass2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdNextSubpass2KHR")); + data->CreateRenderPass2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkCreateRenderPass2KHR")); data->CreateDescriptorUpdateTemplateKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCreateDescriptorUpdateTemplateKHR")); data->DestroyDescriptorUpdateTemplateKHR = reinterpret_cast(getDeviceProcAddr(device, "vkDestroyDescriptorUpdateTemplateKHR")); data->UpdateDescriptorSetWithTemplateKHR = reinterpret_cast(getDeviceProcAddr(device, "vkUpdateDescriptorSetWithTemplateKHR")); data->CmdDispatchBaseKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDispatchBaseKHR")); data->CmdSetDeviceMaskKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdSetDeviceMaskKHR")); data->GetDeviceGroupPeerMemoryFeaturesKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetDeviceGroupPeerMemoryFeaturesKHR")); + data->CmdDrawIndexedIndirectCountKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDrawIndexedIndirectCountKHR")); + data->CmdDrawIndirectCountKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCmdDrawIndirectCountKHR")); data->GetBufferMemoryRequirements2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetBufferMemoryRequirements2KHR")); data->GetImageMemoryRequirements2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetImageMemoryRequirements2KHR")); data->GetImageSparseMemoryRequirements2KHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetImageSparseMemoryRequirements2KHR")); @@ -86,6 +114,9 @@ void flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction( data->GetDescriptorSetLayoutSupportKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetDescriptorSetLayoutSupportKHR")); data->CreateSamplerYcbcrConversionKHR = reinterpret_cast(getDeviceProcAddr(device, "vkCreateSamplerYcbcrConversionKHR")); data->DestroySamplerYcbcrConversionKHR = reinterpret_cast(getDeviceProcAddr(device, "vkDestroySamplerYcbcrConversionKHR")); + data->GetSemaphoreCounterValueKHR = reinterpret_cast(getDeviceProcAddr(device, "vkGetSemaphoreCounterValueKHR")); + data->SignalSemaphoreKHR = reinterpret_cast(getDeviceProcAddr(device, "vkSignalSemaphoreKHR")); + data->WaitSemaphoresKHR = reinterpret_cast(getDeviceProcAddr(device, "vkWaitSemaphoresKHR")); data->AllocateCommandBuffers = reinterpret_cast(getDeviceProcAddr(device, "vkAllocateCommandBuffers")); data->AllocateDescriptorSets = reinterpret_cast(getDeviceProcAddr(device, "vkAllocateDescriptorSets")); data->AllocateMemory = reinterpret_cast(getDeviceProcAddr(device, "vkAllocateMemory")); diff --git a/src/MagnumExternal/Vulkan/flextVk.h b/src/MagnumExternal/Vulkan/flextVk.h index 5d848b8534..ffffe47de0 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h +++ b/src/MagnumExternal/Vulkan/flextVk.h @@ -93,22 +93,21 @@ extern "C" { #define VK_MAX_DESCRIPTION_SIZE 256 #define VK_MAX_MEMORY_TYPES 32 #define VK_MAX_MEMORY_HEAPS 16 -#define VK_QUEUE_FAMILY_EXTERNAL (~0U-1) #define VK_MAX_DEVICE_GROUP_SIZE 32 #define VK_MAX_DRIVER_NAME_SIZE 256 #define VK_MAX_DRIVER_INFO_SIZE 256 /* VK_VERSION_1_0 */ -#define VK_LOD_CLAMP_NONE 1000.0f -#define VK_REMAINING_MIP_LEVELS (~0U) -#define VK_REMAINING_ARRAY_LAYERS (~0U) -#define VK_WHOLE_SIZE (~0ULL) #define VK_ATTACHMENT_UNUSED (~0U) -#define VK_TRUE 1 #define VK_FALSE 0 +#define VK_LOD_CLAMP_NONE 1000.0f #define VK_QUEUE_FAMILY_IGNORED (~0U) +#define VK_REMAINING_ARRAY_LAYERS (~0U) +#define VK_REMAINING_MIP_LEVELS (~0U) #define VK_SUBPASS_EXTERNAL (~0U) +#define VK_TRUE 1 +#define VK_WHOLE_SIZE (~0ULL) /* VK_VERSION_1_1 */ @@ -126,120 +125,119 @@ extern "C" { #define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 2 #define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" -/* VK_KHR_storage_buffer_storage_class */ - -#define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_SPEC_VERSION 1 -#define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class" - -/* VK_KHR_16bit_storage */ - -#define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1 -#define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage" - -/* VK_KHR_bind_memory2 */ - -#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1 -#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2" - -/* VK_KHR_get_memory_requirements2 */ - -#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1 -#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" - -/* VK_KHR_dedicated_allocation */ - -#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 -#define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" - -/* VK_KHR_descriptor_update_template */ - -#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 -#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" - /* VK_KHR_device_group_creation */ #define VK_KHR_DEVICE_GROUP_CREATION_SPEC_VERSION 1 #define VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHR_device_group_creation" #define VK_MAX_DEVICE_GROUP_SIZE_KHR VK_MAX_DEVICE_GROUP_SIZE -/* VK_KHR_device_group */ - -#define VK_KHR_DEVICE_GROUP_SPEC_VERSION 4 -#define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" - /* VK_KHR_external_memory_capabilities */ #define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_memory_capabilities" #define VK_LUID_SIZE_KHR VK_LUID_SIZE -/* VK_KHR_external_memory */ - -#define VK_KHR_EXTERNAL_MEMORY_SPEC_VERSION 1 -#define VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHR_external_memory" -#define VK_QUEUE_FAMILY_EXTERNAL_KHR VK_QUEUE_FAMILY_EXTERNAL - /* VK_KHR_external_semaphore_capabilities */ #define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_semaphore_capabilities" #define VK_LUID_SIZE_KHR VK_LUID_SIZE -/* VK_KHR_external_semaphore */ - -#define VK_KHR_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 -#define VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHR_external_semaphore" - /* VK_KHR_external_fence_capabilities */ #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1 #define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities" #define VK_LUID_SIZE_KHR VK_LUID_SIZE -/* VK_KHR_external_fence */ +/* VK_EXT_host_query_reset */ + +#define VK_EXT_HOST_QUERY_RESET_SPEC_VERSION 1 +#define VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME "VK_EXT_host_query_reset" -#define VK_KHR_EXTERNAL_FENCE_SPEC_VERSION 1 -#define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence" +/* VK_KHR_bind_memory2 */ + +#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1 +#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2" + +/* VK_KHR_device_group */ + +#define VK_KHR_DEVICE_GROUP_SPEC_VERSION 4 +#define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" /* VK_KHR_maintenance1 */ #define VK_KHR_MAINTENANCE1_SPEC_VERSION 2 #define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" -/* VK_KHR_maintenance2 */ +/* VK_EXT_debug_report */ -#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 9 +#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" -/* VK_KHR_maintenance3 */ +/* VK_KHR_descriptor_update_template */ -#define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 -#define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" +#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 +#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" /* VK_KHR_multiview */ #define VK_KHR_MULTIVIEW_SPEC_VERSION 1 #define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview" -/* VK_KHR_relaxed_block_layout */ +/* VK_KHR_maintenance2 */ + +#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" + +/* VK_KHR_create_renderpass2 */ + +#define VK_KHR_CREATE_RENDERPASS_2_SPEC_VERSION 1 +#define VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME "VK_KHR_create_renderpass2" -#define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1 -#define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout" +/* VK_KHR_get_memory_requirements2 */ + +#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1 +#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" /* VK_KHR_sampler_ycbcr_conversion */ #define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 14 #define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" -/* VK_KHR_shader_draw_parameters */ +/* VK_KHR_maintenance3 */ + +#define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" + +/* VK_KHR_draw_indirect_count */ + +#define VK_KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 +#define VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_KHR_draw_indirect_count" + +/* VK_KHR_timeline_semaphore */ + +#define VK_KHR_TIMELINE_SEMAPHORE_SPEC_VERSION 2 +#define VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME "VK_KHR_timeline_semaphore" + +/* VK_KHR_buffer_device_address */ -#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 -#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" +#define VK_KHR_BUFFER_DEVICE_ADDRESS_SPEC_VERSION 1 +#define VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME "VK_KHR_buffer_device_address" -/* VK_KHR_variable_pointers */ +/* VK_EXT_debug_marker */ -#define VK_KHR_VARIABLE_POINTERS_SPEC_VERSION 1 -#define VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME "VK_KHR_variable_pointers" +#define VK_EXT_DEBUG_MARKER_SPEC_VERSION 4 +#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" + +/* VK_EXT_debug_utils */ + +#define VK_EXT_DEBUG_UTILS_SPEC_VERSION 2 +#define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils" + +/* VK_EXT_validation_features */ + +#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 3 +#define VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME "VK_EXT_validation_features" /* VK_EXT_texture_compression_astc_hdr */ @@ -259,7 +257,7 @@ extern "C" { /* Data types */ #define VK_MAKE_VERSION(major, minor, patch) \ - (((major) << 22) | ((minor) << 12) | (patch)) + ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch))) #define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) #define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) #define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) @@ -270,7 +268,9 @@ extern "C" { // Vulkan 1.2 version number #define VK_API_VERSION_1_2 VK_MAKE_VERSION(1, 2, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 133 +#define VK_HEADER_VERSION 146 +// Complete version of this file +#define VK_HEADER_VERSION_COMPLETE VK_MAKE_VERSION(1, 2, VK_HEADER_VERSION) #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; #if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) @@ -350,6 +350,7 @@ typedef VkFlags VkPeerMemoryFeatureFlags; typedef VkPeerMemoryFeatureFlags VkPeerMemoryFeatureFlagsKHR; typedef VkFlags VkMemoryAllocateFlags; +typedef VkFlags VkDebugReportFlagsEXT; typedef VkFlags VkCommandPoolTrimFlags; typedef VkCommandPoolTrimFlags VkCommandPoolTrimFlagsKHR; @@ -359,6 +360,10 @@ typedef VkFlags VkExternalSemaphoreHandleTypeFlags; typedef VkFlags VkExternalSemaphoreFeatureFlags; typedef VkFlags VkExternalFenceHandleTypeFlags; typedef VkFlags VkExternalFenceFeatureFlags; +typedef VkFlags VkDebugUtilsMessageSeverityFlagsEXT; +typedef VkFlags VkDebugUtilsMessageTypeFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCreateFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT; typedef VkFlags VkDescriptorBindingFlags; typedef VkFlags VkResolveModeFlags; VK_DEFINE_HANDLE(VkInstance) @@ -392,6 +397,8 @@ typedef VkDescriptorUpdateTemplate VkDescriptorUpdateTemplateKHR; VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversion) typedef VkSamplerYcbcrConversion VkSamplerYcbcrConversionKHR; +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) typedef enum { VK_ATTACHMENT_LOAD_OP_LOAD = 0, @@ -443,7 +450,9 @@ typedef enum { VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5 } VkBorderColor; -typedef int VkFramebufferCreateFlagBits; +typedef enum { + VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT = 1 << 0 +} VkFramebufferCreateFlagBits; typedef int VkRenderPassCreateFlagBits; @@ -453,18 +462,25 @@ typedef enum { VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1 } VkPipelineCacheHeaderVersion; +typedef int VkPipelineCacheCreateFlagBits; + typedef int VkPipelineShaderStageCreateFlagBits; -typedef int VkDescriptorSetLayoutCreateFlagBits; +typedef enum { + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT = 1 << 1 +} VkDescriptorSetLayoutCreateFlagBits; -typedef int VkDeviceQueueCreateFlagBits; +typedef enum { + VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 1 << 0 +} VkDeviceQueueCreateFlagBits; typedef enum { VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 1 << 0, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 1 << 1, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 1 << 2, VK_BUFFER_CREATE_PROTECTED_BIT = 1 << 3, - VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 1 << 4 + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 1 << 4, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT } VkBufferCreateFlagBits; typedef enum { @@ -477,7 +493,8 @@ typedef enum { VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 1 << 6, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 1 << 7, VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 1 << 8, - VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT = 1 << 17 + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT = 1 << 17, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_KHR = VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT } VkBufferUsageFlagBits; typedef enum { @@ -1152,8 +1169,9 @@ typedef enum { VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, VK_ERROR_FRAGMENTATION = -1000161000, VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000, - VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = VK_ERROR_INVALID_EXTERNAL_HANDLE, - VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY + VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS } VkResult; typedef enum { @@ -1365,17 +1383,6 @@ typedef enum { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, - VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, - VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, - VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, - VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, - VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, - VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES, VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, @@ -1383,32 +1390,63 @@ typedef enum { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, - VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES, - VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, + VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO, VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT, - VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2, + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR = VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2, + VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR = VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO, + VK_STRUCTURE_TYPE_SUBPASS_END_INFO_KHR = VK_STRUCTURE_TYPE_SUBPASS_END_INFO, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO, VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES, + VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, + VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO, + VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_KHR = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_BUFFER_OPAQUE_CAPTURE_ADDRESS_CREATE_INFO, + VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_OPAQUE_CAPTURE_ADDRESS_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_MEMORY_OPAQUE_CAPTURE_ADDRESS_INFO, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, + VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, + VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = 1000066000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000 } VkStructureType; @@ -1501,6 +1539,41 @@ typedef enum { VK_DEPENDENCY_VIEW_LOCAL_BIT_KHR = VK_DEPENDENCY_VIEW_LOCAL_BIT } VkDependencyFlagBits; +typedef enum { + VK_OBJECT_TYPE_UNKNOWN = 0, + VK_OBJECT_TYPE_INSTANCE = 1, + VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, + VK_OBJECT_TYPE_DEVICE = 3, + VK_OBJECT_TYPE_QUEUE = 4, + VK_OBJECT_TYPE_SEMAPHORE = 5, + VK_OBJECT_TYPE_COMMAND_BUFFER = 6, + VK_OBJECT_TYPE_FENCE = 7, + VK_OBJECT_TYPE_DEVICE_MEMORY = 8, + VK_OBJECT_TYPE_BUFFER = 9, + VK_OBJECT_TYPE_IMAGE = 10, + VK_OBJECT_TYPE_EVENT = 11, + VK_OBJECT_TYPE_QUERY_POOL = 12, + VK_OBJECT_TYPE_BUFFER_VIEW = 13, + VK_OBJECT_TYPE_IMAGE_VIEW = 14, + VK_OBJECT_TYPE_SHADER_MODULE = 15, + VK_OBJECT_TYPE_PIPELINE_CACHE = 16, + VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, + VK_OBJECT_TYPE_RENDER_PASS = 18, + VK_OBJECT_TYPE_PIPELINE = 19, + VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, + VK_OBJECT_TYPE_SAMPLER = 21, + VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, + VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, + VK_OBJECT_TYPE_FRAMEBUFFER = 24, + VK_OBJECT_TYPE_COMMAND_POOL = 25, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, + VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000 +} VkObjectType; + typedef enum { VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0 } VkDescriptorUpdateTemplateType; @@ -1531,15 +1604,82 @@ typedef enum { typedef enum { VK_SEMAPHORE_TYPE_BINARY = 0, - VK_SEMAPHORE_TYPE_TIMELINE = 1 + VK_SEMAPHORE_TYPE_TIMELINE = 1, + VK_SEMAPHORE_TYPE_BINARY_KHR = VK_SEMAPHORE_TYPE_BINARY, + VK_SEMAPHORE_TYPE_TIMELINE_KHR = VK_SEMAPHORE_TYPE_TIMELINE } VkSemaphoreType; typedef enum { - VK_SEMAPHORE_WAIT_ANY_BIT = 1 << 0 + VK_SEMAPHORE_WAIT_ANY_BIT = 1 << 0, + VK_SEMAPHORE_WAIT_ANY_BIT_KHR = VK_SEMAPHORE_WAIT_ANY_BIT } VkSemaphoreWaitFlagBits; typedef int VkShaderModuleCreateFlagBits; +typedef enum { + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 1 << 0, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 1 << 1, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 1 << 2, + VK_DEBUG_REPORT_ERROR_BIT_EXT = 1 << 3, + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 1 << 4 +} VkDebugReportFlagBitsEXT; + +typedef enum { + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, + VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, + VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, + VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, + VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, + VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, + VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, + VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, + VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, + VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, + VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, + VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, + VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000 +} VkDebugReportObjectTypeEXT; + +typedef enum { + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT = 0, + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT = 1, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT = 2, + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT = 3 +} VkValidationFeatureEnableEXT; + +typedef enum { + VK_VALIDATION_FEATURE_DISABLE_ALL_EXT = 0, + VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT = 1, + VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT = 2, + VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT = 3, + VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT = 4, + VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT = 5, + VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT = 6 +} VkValidationFeatureDisableEXT; + typedef enum { VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT = 1 << 0, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT = 1 << 1, @@ -1619,7 +1759,9 @@ typedef enum { VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 1 << 0, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT = 1 << 1, VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT = 1 << 2, - VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT + VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT, + VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT } VkMemoryAllocateFlagBits; typedef enum { @@ -1673,6 +1815,19 @@ typedef enum { VK_SAMPLER_REDUCTION_MODE_MAX = 2 } VkSamplerReductionMode; +typedef enum { + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 1 << 0, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 1 << 4, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 1 << 8, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 1 << 12 +} VkDebugUtilsMessageSeverityFlagBitsEXT; + +typedef enum { + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 1 << 0, + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 1 << 1, + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 1 << 2 +} VkDebugUtilsMessageTypeFlagBitsEXT; + typedef enum { VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY = 0, VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL = 1, @@ -1683,7 +1838,8 @@ typedef enum { VK_VENDOR_ID_VIV = 0x10001, VK_VENDOR_ID_VSI = 0x10002, VK_VENDOR_ID_KAZAN = 0x10003, - VK_VENDOR_ID_CODEPLAY = 0x10004 + VK_VENDOR_ID_CODEPLAY = 0x10004, + VK_VENDOR_ID_MESA = 0x10005 } VkVendorId; typedef enum { @@ -1698,7 +1854,8 @@ typedef enum { VK_DRIVER_ID_ARM_PROPRIETARY = 9, VK_DRIVER_ID_GOOGLE_SWIFTSHADER = 10, VK_DRIVER_ID_GGP_PROPRIETARY = 11, - VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12 + VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12, + VK_DRIVER_ID_MESA_LLVMPIPE = 13 } VkDriverId; typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData, @@ -1725,6 +1882,50 @@ typedef void (VKAPI_PTR *PFN_vkFreeFunction)( void* pUserData, void* pMemory); typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData); + +typedef struct VkDebugUtilsLabelEXT { + VkStructureType sType; + const void* pNext; + const char* pLabelName; + float color[4]; +} VkDebugUtilsLabelEXT; + +typedef struct VkDebugUtilsObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkObjectType objectType; + uint64_t objectHandle; + const char* pObjectName; +} VkDebugUtilsObjectNameInfoEXT; + +typedef struct VkDebugUtilsMessengerCallbackDataEXT { + VkStructureType sType; + const void* pNext; + VkDebugUtilsMessengerCallbackDataFlagsEXT flags; + const char* pMessageIdName; + int32_t messageIdNumber; + const char* pMessage; + uint32_t queueLabelCount; + const VkDebugUtilsLabelEXT* pQueueLabels; + uint32_t cmdBufLabelCount; + const VkDebugUtilsLabelEXT* pCmdBufLabels; + uint32_t objectCount; + const VkDebugUtilsObjectNameInfoEXT* pObjects; +} VkDebugUtilsMessengerCallbackDataEXT; +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugUtilsMessengerCallbackEXT)( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); typedef struct VkBaseOutStructure { VkStructureType sType; @@ -2811,6 +3012,48 @@ typedef struct VkSubmitInfo { const VkSemaphore* pSignalSemaphores; } VkSubmitInfo; +typedef struct VkDebugReportCallbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void* pUserData; +} VkDebugReportCallbackCreateInfoEXT; + +typedef struct VkValidationFeaturesEXT { + VkStructureType sType; + const void* pNext; + uint32_t enabledValidationFeatureCount; + const VkValidationFeatureEnableEXT* pEnabledValidationFeatures; + uint32_t disabledValidationFeatureCount; + const VkValidationFeatureDisableEXT* pDisabledValidationFeatures; +} VkValidationFeaturesEXT; + +typedef struct VkDebugMarkerObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + const char* pObjectName; +} VkDebugMarkerObjectNameInfoEXT; + +typedef struct VkDebugMarkerObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugMarkerObjectTagInfoEXT; + +typedef struct VkDebugMarkerMarkerInfoEXT { + VkStructureType sType; + const void* pNext; + const char* pMarkerName; + float color[4]; +} VkDebugMarkerMarkerInfoEXT; + typedef struct VkPhysicalDeviceFeatures2 { VkStructureType sType; void* pNext; @@ -2914,10 +3157,6 @@ typedef struct VkPhysicalDeviceVariablePointersFeatures { VkBool32 variablePointers; } VkPhysicalDeviceVariablePointersFeatures; -typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointersFeaturesKHR; - -typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeaturesKHR; - typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeatures; typedef struct VkExternalMemoryProperties { @@ -2978,24 +3217,18 @@ typedef struct VkExternalMemoryImageCreateInfo { VkExternalMemoryHandleTypeFlags handleTypes; } VkExternalMemoryImageCreateInfo; -typedef VkExternalMemoryImageCreateInfo VkExternalMemoryImageCreateInfoKHR; - typedef struct VkExternalMemoryBufferCreateInfo { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlags handleTypes; } VkExternalMemoryBufferCreateInfo; -typedef VkExternalMemoryBufferCreateInfo VkExternalMemoryBufferCreateInfoKHR; - typedef struct VkExportMemoryAllocateInfo { VkStructureType sType; const void* pNext; VkExternalMemoryHandleTypeFlags handleTypes; } VkExportMemoryAllocateInfo; -typedef VkExportMemoryAllocateInfo VkExportMemoryAllocateInfoKHR; - typedef struct VkPhysicalDeviceExternalSemaphoreInfo { VkStructureType sType; const void* pNext; @@ -3020,8 +3253,6 @@ typedef struct VkExportSemaphoreCreateInfo { VkExternalSemaphoreHandleTypeFlags handleTypes; } VkExportSemaphoreCreateInfo; -typedef VkExportSemaphoreCreateInfo VkExportSemaphoreCreateInfoKHR; - typedef struct VkPhysicalDeviceExternalFenceInfo { VkStructureType sType; const void* pNext; @@ -3046,8 +3277,6 @@ typedef struct VkExportFenceCreateInfo { VkExternalFenceHandleTypeFlags handleTypes; } VkExportFenceCreateInfo; -typedef VkExportFenceCreateInfo VkExportFenceCreateInfoKHR; - typedef struct VkPhysicalDeviceMultiviewFeatures { VkStructureType sType; void* pNext; @@ -3232,8 +3461,6 @@ typedef struct VkPhysicalDevice16BitStorageFeatures { VkBool32 storageInputOutput16; } VkPhysicalDevice16BitStorageFeatures; -typedef VkPhysicalDevice16BitStorageFeatures VkPhysicalDevice16BitStorageFeaturesKHR; - typedef struct VkPhysicalDeviceSubgroupProperties { VkStructureType sType; void* pNext; @@ -3304,8 +3531,6 @@ typedef struct VkMemoryDedicatedRequirements { VkBool32 requiresDedicatedAllocation; } VkMemoryDedicatedRequirements; -typedef VkMemoryDedicatedRequirements VkMemoryDedicatedRequirementsKHR; - typedef struct VkMemoryDedicatedAllocateInfo { VkStructureType sType; const void* pNext; @@ -3313,8 +3538,6 @@ typedef struct VkMemoryDedicatedAllocateInfo { VkBuffer buffer; } VkMemoryDedicatedAllocateInfo; -typedef VkMemoryDedicatedAllocateInfo VkMemoryDedicatedAllocateInfoKHR; - typedef struct VkImageViewUsageCreateInfo { VkStructureType sType; const void* pNext; @@ -3492,6 +3715,28 @@ typedef struct VkPhysicalDeviceHostQueryResetFeatures { VkBool32 hostQueryReset; } VkPhysicalDeviceHostQueryResetFeatures; +typedef VkPhysicalDeviceHostQueryResetFeatures VkPhysicalDeviceHostQueryResetFeaturesEXT; + +typedef struct VkDebugUtilsObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkObjectType objectType; + uint64_t objectHandle; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugUtilsObjectTagInfoEXT; + +typedef struct VkDebugUtilsMessengerCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugUtilsMessengerCreateFlagsEXT flags; + VkDebugUtilsMessageSeverityFlagsEXT messageSeverity; + VkDebugUtilsMessageTypeFlagsEXT messageType; + PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback; + void* pUserData; +} VkDebugUtilsMessengerCreateInfoEXT; + typedef struct VkPhysicalDeviceDescriptorIndexingFeatures { VkStructureType sType; void* pNext; @@ -3630,29 +3875,39 @@ typedef struct VkRenderPassCreateInfo2 { const uint32_t* pCorrelatedViewMasks; } VkRenderPassCreateInfo2; +typedef VkRenderPassCreateInfo2 VkRenderPassCreateInfo2KHR; + typedef struct VkSubpassBeginInfo { VkStructureType sType; const void* pNext; VkSubpassContents contents; } VkSubpassBeginInfo; +typedef VkSubpassBeginInfo VkSubpassBeginInfoKHR; + typedef struct VkSubpassEndInfo { VkStructureType sType; const void* pNext; } VkSubpassEndInfo; +typedef VkSubpassEndInfo VkSubpassEndInfoKHR; + typedef struct VkPhysicalDeviceTimelineSemaphoreFeatures { VkStructureType sType; void* pNext; VkBool32 timelineSemaphore; } VkPhysicalDeviceTimelineSemaphoreFeatures; +typedef VkPhysicalDeviceTimelineSemaphoreFeatures VkPhysicalDeviceTimelineSemaphoreFeaturesKHR; + typedef struct VkPhysicalDeviceTimelineSemaphoreProperties { VkStructureType sType; void* pNext; uint64_t maxTimelineSemaphoreValueDifference; } VkPhysicalDeviceTimelineSemaphoreProperties; +typedef VkPhysicalDeviceTimelineSemaphoreProperties VkPhysicalDeviceTimelineSemaphorePropertiesKHR; + typedef struct VkSemaphoreTypeCreateInfo { VkStructureType sType; const void* pNext; @@ -3660,6 +3915,8 @@ typedef struct VkSemaphoreTypeCreateInfo { uint64_t initialValue; } VkSemaphoreTypeCreateInfo; +typedef VkSemaphoreTypeCreateInfo VkSemaphoreTypeCreateInfoKHR; + typedef struct VkTimelineSemaphoreSubmitInfo { VkStructureType sType; const void* pNext; @@ -3669,6 +3926,8 @@ typedef struct VkTimelineSemaphoreSubmitInfo { const uint64_t* pSignalSemaphoreValues; } VkTimelineSemaphoreSubmitInfo; +typedef VkTimelineSemaphoreSubmitInfo VkTimelineSemaphoreSubmitInfoKHR; + typedef struct VkSemaphoreWaitInfo { VkStructureType sType; const void* pNext; @@ -3678,6 +3937,8 @@ typedef struct VkSemaphoreWaitInfo { const uint64_t* pValues; } VkSemaphoreWaitInfo; +typedef VkSemaphoreWaitInfo VkSemaphoreWaitInfoKHR; + typedef struct VkSemaphoreSignalInfo { VkStructureType sType; const void* pNext; @@ -3685,6 +3946,8 @@ typedef struct VkSemaphoreSignalInfo { uint64_t value; } VkSemaphoreSignalInfo; +typedef VkSemaphoreSignalInfo VkSemaphoreSignalInfoKHR; + typedef struct VkPhysicalDevice8BitStorageFeatures { VkStructureType sType; void* pNext; @@ -3751,18 +4014,24 @@ typedef struct VkPhysicalDeviceBufferDeviceAddressFeatures { VkBool32 bufferDeviceAddressMultiDevice; } VkPhysicalDeviceBufferDeviceAddressFeatures; +typedef VkPhysicalDeviceBufferDeviceAddressFeatures VkPhysicalDeviceBufferDeviceAddressFeaturesKHR; + typedef struct VkBufferDeviceAddressInfo { VkStructureType sType; const void* pNext; VkBuffer buffer; } VkBufferDeviceAddressInfo; +typedef VkBufferDeviceAddressInfo VkBufferDeviceAddressInfoKHR; + typedef struct VkBufferOpaqueCaptureAddressCreateInfo { VkStructureType sType; const void* pNext; uint64_t opaqueCaptureAddress; } VkBufferOpaqueCaptureAddressCreateInfo; +typedef VkBufferOpaqueCaptureAddressCreateInfo VkBufferOpaqueCaptureAddressCreateInfoKHR; + typedef struct VkPhysicalDeviceImagelessFramebufferFeatures { VkStructureType sType; void* pNext; @@ -3832,12 +4101,16 @@ typedef struct VkMemoryOpaqueCaptureAddressAllocateInfo { uint64_t opaqueCaptureAddress; } VkMemoryOpaqueCaptureAddressAllocateInfo; +typedef VkMemoryOpaqueCaptureAddressAllocateInfo VkMemoryOpaqueCaptureAddressAllocateInfoKHR; + typedef struct VkDeviceMemoryOpaqueCaptureAddressInfo { VkStructureType sType; const void* pNext; VkDeviceMemory memory; } VkDeviceMemoryOpaqueCaptureAddressInfo; +typedef VkDeviceMemoryOpaqueCaptureAddressInfo VkDeviceMemoryOpaqueCaptureAddressInfoKHR; + typedef struct VkPhysicalDeviceVulkan11Features { VkStructureType sType; void* pNext; @@ -4003,9 +4276,33 @@ extern FLEXTVK_EXPORT VkResult(VKAPI_PTR *flextvkEnumerateInstanceVersion)(uint3 /* Per-instance function pointers */ struct FlextVkInstance { + /* VK_EXT_debug_marker */ + + + /* VK_EXT_debug_report */ + + VkResult (VKAPI_PTR *CreateDebugReportCallbackEXT)(VkInstance, const VkDebugReportCallbackCreateInfoEXT*, const VkAllocationCallbacks*, VkDebugReportCallbackEXT*); + void (VKAPI_PTR *DebugReportMessageEXT)(VkInstance, VkDebugReportFlagsEXT, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char*); + void (VKAPI_PTR *DestroyDebugReportCallbackEXT)(VkInstance, VkDebugReportCallbackEXT, const VkAllocationCallbacks*); + + /* VK_EXT_debug_utils */ + + VkResult (VKAPI_PTR *CreateDebugUtilsMessengerEXT)(VkInstance, const VkDebugUtilsMessengerCreateInfoEXT*, const VkAllocationCallbacks*, VkDebugUtilsMessengerEXT*); + void (VKAPI_PTR *DestroyDebugUtilsMessengerEXT)(VkInstance, VkDebugUtilsMessengerEXT, const VkAllocationCallbacks*); + void (VKAPI_PTR *SubmitDebugUtilsMessageEXT)(VkInstance, VkDebugUtilsMessageSeverityFlagBitsEXT, VkDebugUtilsMessageTypeFlagsEXT, const VkDebugUtilsMessengerCallbackDataEXT*); + + /* VK_EXT_host_query_reset */ + + /* VK_KHR_bind_memory2 */ + /* VK_KHR_buffer_device_address */ + + + /* VK_KHR_create_renderpass2 */ + + /* VK_KHR_descriptor_update_template */ @@ -4016,6 +4313,9 @@ struct FlextVkInstance { VkResult (VKAPI_PTR *EnumeratePhysicalDeviceGroupsKHR)(VkInstance, uint32_t*, VkPhysicalDeviceGroupProperties*); + /* VK_KHR_draw_indirect_count */ + + /* VK_KHR_external_fence_capabilities */ void (VKAPI_PTR *GetPhysicalDeviceExternalFencePropertiesKHR)(VkPhysicalDevice, const VkPhysicalDeviceExternalFenceInfo*, VkExternalFenceProperties*); @@ -4050,12 +4350,14 @@ struct FlextVkInstance { /* VK_KHR_sampler_ycbcr_conversion */ + /* VK_KHR_timeline_semaphore */ + + /* VK_VERSION_1_0 */ VkResult (VKAPI_PTR *CreateDevice)(VkPhysicalDevice, const VkDeviceCreateInfo*, const VkAllocationCallbacks*, VkDevice*); void (VKAPI_PTR *DestroyInstance)(VkInstance, const VkAllocationCallbacks*); VkResult (VKAPI_PTR *EnumerateDeviceExtensionProperties)(VkPhysicalDevice, const char*, uint32_t*, VkExtensionProperties*); - VkResult (VKAPI_PTR *EnumerateDeviceLayerProperties)(VkPhysicalDevice, uint32_t*, VkLayerProperties*); VkResult (VKAPI_PTR *EnumeratePhysicalDevices)(VkInstance, uint32_t*, VkPhysicalDevice*); PFN_vkVoidFunction (VKAPI_PTR *GetDeviceProcAddr)(VkDevice, const char*); void (VKAPI_PTR *GetPhysicalDeviceFeatures)(VkPhysicalDevice, VkPhysicalDeviceFeatures*); @@ -4090,11 +4392,50 @@ void FLEXTVK_EXPORT flextVkInitInstance(VkInstance instance, FlextVkInstance* da /* Per-device function pointers */ struct FlextVkDevice { + /* VK_EXT_debug_marker */ + + void (VKAPI_PTR *CmdDebugMarkerBeginEXT)(VkCommandBuffer, const VkDebugMarkerMarkerInfoEXT*); + void (VKAPI_PTR *CmdDebugMarkerEndEXT)(VkCommandBuffer); + void (VKAPI_PTR *CmdDebugMarkerInsertEXT)(VkCommandBuffer, const VkDebugMarkerMarkerInfoEXT*); + VkResult (VKAPI_PTR *DebugMarkerSetObjectNameEXT)(VkDevice, const VkDebugMarkerObjectNameInfoEXT*); + VkResult (VKAPI_PTR *DebugMarkerSetObjectTagEXT)(VkDevice, const VkDebugMarkerObjectTagInfoEXT*); + + /* VK_EXT_debug_report */ + + + /* VK_EXT_debug_utils */ + + void (VKAPI_PTR *CmdBeginDebugUtilsLabelEXT)(VkCommandBuffer, const VkDebugUtilsLabelEXT*); + void (VKAPI_PTR *CmdEndDebugUtilsLabelEXT)(VkCommandBuffer); + void (VKAPI_PTR *CmdInsertDebugUtilsLabelEXT)(VkCommandBuffer, const VkDebugUtilsLabelEXT*); + void (VKAPI_PTR *QueueBeginDebugUtilsLabelEXT)(VkQueue, const VkDebugUtilsLabelEXT*); + void (VKAPI_PTR *QueueEndDebugUtilsLabelEXT)(VkQueue); + void (VKAPI_PTR *QueueInsertDebugUtilsLabelEXT)(VkQueue, const VkDebugUtilsLabelEXT*); + VkResult (VKAPI_PTR *SetDebugUtilsObjectNameEXT)(VkDevice, const VkDebugUtilsObjectNameInfoEXT*); + VkResult (VKAPI_PTR *SetDebugUtilsObjectTagEXT)(VkDevice, const VkDebugUtilsObjectTagInfoEXT*); + + /* VK_EXT_host_query_reset */ + + void (VKAPI_PTR *ResetQueryPoolEXT)(VkDevice, VkQueryPool, uint32_t, uint32_t); + /* VK_KHR_bind_memory2 */ VkResult (VKAPI_PTR *BindBufferMemory2KHR)(VkDevice, uint32_t, const VkBindBufferMemoryInfo*); VkResult (VKAPI_PTR *BindImageMemory2KHR)(VkDevice, uint32_t, const VkBindImageMemoryInfo*); + /* VK_KHR_buffer_device_address */ + + VkDeviceAddress (VKAPI_PTR *GetBufferDeviceAddressKHR)(VkDevice, const VkBufferDeviceAddressInfo*); + uint64_t (VKAPI_PTR *GetBufferOpaqueCaptureAddressKHR)(VkDevice, const VkBufferDeviceAddressInfo*); + uint64_t (VKAPI_PTR *GetDeviceMemoryOpaqueCaptureAddressKHR)(VkDevice, const VkDeviceMemoryOpaqueCaptureAddressInfo*); + + /* VK_KHR_create_renderpass2 */ + + void (VKAPI_PTR *CmdBeginRenderPass2KHR)(VkCommandBuffer, const VkRenderPassBeginInfo*, const VkSubpassBeginInfo*); + void (VKAPI_PTR *CmdEndRenderPass2KHR)(VkCommandBuffer, const VkSubpassEndInfo*); + void (VKAPI_PTR *CmdNextSubpass2KHR)(VkCommandBuffer, const VkSubpassBeginInfo*, const VkSubpassEndInfo*); + VkResult (VKAPI_PTR *CreateRenderPass2KHR)(VkDevice, const VkRenderPassCreateInfo2*, const VkAllocationCallbacks*, VkRenderPass*); + /* VK_KHR_descriptor_update_template */ VkResult (VKAPI_PTR *CreateDescriptorUpdateTemplateKHR)(VkDevice, const VkDescriptorUpdateTemplateCreateInfo*, const VkAllocationCallbacks*, VkDescriptorUpdateTemplate*); @@ -4110,6 +4451,11 @@ struct FlextVkDevice { /* VK_KHR_device_group_creation */ + /* VK_KHR_draw_indirect_count */ + + void (VKAPI_PTR *CmdDrawIndexedIndirectCountKHR)(VkCommandBuffer, VkBuffer, VkDeviceSize, VkBuffer, VkDeviceSize, uint32_t, uint32_t); + void (VKAPI_PTR *CmdDrawIndirectCountKHR)(VkCommandBuffer, VkBuffer, VkDeviceSize, VkBuffer, VkDeviceSize, uint32_t, uint32_t); + /* VK_KHR_external_fence_capabilities */ @@ -4141,6 +4487,12 @@ struct FlextVkDevice { VkResult (VKAPI_PTR *CreateSamplerYcbcrConversionKHR)(VkDevice, const VkSamplerYcbcrConversionCreateInfo*, const VkAllocationCallbacks*, VkSamplerYcbcrConversion*); void (VKAPI_PTR *DestroySamplerYcbcrConversionKHR)(VkDevice, VkSamplerYcbcrConversion, const VkAllocationCallbacks*); + /* VK_KHR_timeline_semaphore */ + + VkResult (VKAPI_PTR *GetSemaphoreCounterValueKHR)(VkDevice, VkSemaphore, uint64_t*); + VkResult (VKAPI_PTR *SignalSemaphoreKHR)(VkDevice, const VkSemaphoreSignalInfo*); + VkResult (VKAPI_PTR *WaitSemaphoresKHR)(VkDevice, const VkSemaphoreWaitInfo*, uint64_t); + /* VK_VERSION_1_0 */ VkResult (VKAPI_PTR *AllocateCommandBuffers)(VkDevice, const VkCommandBufferAllocateInfo*, VkCommandBuffer*); diff --git a/src/MagnumExternal/Vulkan/flextVkGlobal.h b/src/MagnumExternal/Vulkan/flextVkGlobal.h index 14eb61603c..11aed5a6aa 100644 --- a/src/MagnumExternal/Vulkan/flextVkGlobal.h +++ b/src/MagnumExternal/Vulkan/flextVkGlobal.h @@ -36,9 +36,33 @@ extern "C" { /* Per-instance function pointers */ extern FLEXTVK_EXPORT FlextVkInstance flextVkInstance; +/* VK_EXT_debug_marker */ + + +/* VK_EXT_debug_report */ + +#define vkCreateDebugReportCallbackEXT flextVkInstance.CreateDebugReportCallbackEXT +#define vkDebugReportMessageEXT flextVkInstance.DebugReportMessageEXT +#define vkDestroyDebugReportCallbackEXT flextVkInstance.DestroyDebugReportCallbackEXT + +/* VK_EXT_debug_utils */ + +#define vkCreateDebugUtilsMessengerEXT flextVkInstance.CreateDebugUtilsMessengerEXT +#define vkDestroyDebugUtilsMessengerEXT flextVkInstance.DestroyDebugUtilsMessengerEXT +#define vkSubmitDebugUtilsMessageEXT flextVkInstance.SubmitDebugUtilsMessageEXT + +/* VK_EXT_host_query_reset */ + + /* VK_KHR_bind_memory2 */ +/* VK_KHR_buffer_device_address */ + + +/* VK_KHR_create_renderpass2 */ + + /* VK_KHR_descriptor_update_template */ @@ -49,6 +73,9 @@ extern FLEXTVK_EXPORT FlextVkInstance flextVkInstance; #define vkEnumeratePhysicalDeviceGroupsKHR flextVkInstance.EnumeratePhysicalDeviceGroupsKHR +/* VK_KHR_draw_indirect_count */ + + /* VK_KHR_external_fence_capabilities */ #define vkGetPhysicalDeviceExternalFencePropertiesKHR flextVkInstance.GetPhysicalDeviceExternalFencePropertiesKHR @@ -83,12 +110,14 @@ extern FLEXTVK_EXPORT FlextVkInstance flextVkInstance; /* VK_KHR_sampler_ycbcr_conversion */ +/* VK_KHR_timeline_semaphore */ + + /* VK_VERSION_1_0 */ #define vkCreateDevice flextVkInstance.CreateDevice #define vkDestroyInstance flextVkInstance.DestroyInstance #define vkEnumerateDeviceExtensionProperties flextVkInstance.EnumerateDeviceExtensionProperties -#define vkEnumerateDeviceLayerProperties flextVkInstance.EnumerateDeviceLayerProperties #define vkEnumeratePhysicalDevices flextVkInstance.EnumeratePhysicalDevices #define vkGetDeviceProcAddr flextVkInstance.GetDeviceProcAddr #define vkGetPhysicalDeviceFeatures flextVkInstance.GetPhysicalDeviceFeatures @@ -119,11 +148,50 @@ extern FLEXTVK_EXPORT FlextVkInstance flextVkInstance; /* Per-device function pointers */ extern FLEXTVK_EXPORT FlextVkDevice flextVkDevice; +/* VK_EXT_debug_marker */ + +#define vkCmdDebugMarkerBeginEXT flextVkDevice.CmdDebugMarkerBeginEXT +#define vkCmdDebugMarkerEndEXT flextVkDevice.CmdDebugMarkerEndEXT +#define vkCmdDebugMarkerInsertEXT flextVkDevice.CmdDebugMarkerInsertEXT +#define vkDebugMarkerSetObjectNameEXT flextVkDevice.DebugMarkerSetObjectNameEXT +#define vkDebugMarkerSetObjectTagEXT flextVkDevice.DebugMarkerSetObjectTagEXT + +/* VK_EXT_debug_report */ + + +/* VK_EXT_debug_utils */ + +#define vkCmdBeginDebugUtilsLabelEXT flextVkDevice.CmdBeginDebugUtilsLabelEXT +#define vkCmdEndDebugUtilsLabelEXT flextVkDevice.CmdEndDebugUtilsLabelEXT +#define vkCmdInsertDebugUtilsLabelEXT flextVkDevice.CmdInsertDebugUtilsLabelEXT +#define vkQueueBeginDebugUtilsLabelEXT flextVkDevice.QueueBeginDebugUtilsLabelEXT +#define vkQueueEndDebugUtilsLabelEXT flextVkDevice.QueueEndDebugUtilsLabelEXT +#define vkQueueInsertDebugUtilsLabelEXT flextVkDevice.QueueInsertDebugUtilsLabelEXT +#define vkSetDebugUtilsObjectNameEXT flextVkDevice.SetDebugUtilsObjectNameEXT +#define vkSetDebugUtilsObjectTagEXT flextVkDevice.SetDebugUtilsObjectTagEXT + +/* VK_EXT_host_query_reset */ + +#define vkResetQueryPoolEXT flextVkDevice.ResetQueryPoolEXT + /* VK_KHR_bind_memory2 */ #define vkBindBufferMemory2KHR flextVkDevice.BindBufferMemory2KHR #define vkBindImageMemory2KHR flextVkDevice.BindImageMemory2KHR +/* VK_KHR_buffer_device_address */ + +#define vkGetBufferDeviceAddressKHR flextVkDevice.GetBufferDeviceAddressKHR +#define vkGetBufferOpaqueCaptureAddressKHR flextVkDevice.GetBufferOpaqueCaptureAddressKHR +#define vkGetDeviceMemoryOpaqueCaptureAddressKHR flextVkDevice.GetDeviceMemoryOpaqueCaptureAddressKHR + +/* VK_KHR_create_renderpass2 */ + +#define vkCmdBeginRenderPass2KHR flextVkDevice.CmdBeginRenderPass2KHR +#define vkCmdEndRenderPass2KHR flextVkDevice.CmdEndRenderPass2KHR +#define vkCmdNextSubpass2KHR flextVkDevice.CmdNextSubpass2KHR +#define vkCreateRenderPass2KHR flextVkDevice.CreateRenderPass2KHR + /* VK_KHR_descriptor_update_template */ #define vkCreateDescriptorUpdateTemplateKHR flextVkDevice.CreateDescriptorUpdateTemplateKHR @@ -139,6 +207,11 @@ extern FLEXTVK_EXPORT FlextVkDevice flextVkDevice; /* VK_KHR_device_group_creation */ +/* VK_KHR_draw_indirect_count */ + +#define vkCmdDrawIndexedIndirectCountKHR flextVkDevice.CmdDrawIndexedIndirectCountKHR +#define vkCmdDrawIndirectCountKHR flextVkDevice.CmdDrawIndirectCountKHR + /* VK_KHR_external_fence_capabilities */ @@ -170,6 +243,12 @@ extern FLEXTVK_EXPORT FlextVkDevice flextVkDevice; #define vkCreateSamplerYcbcrConversionKHR flextVkDevice.CreateSamplerYcbcrConversionKHR #define vkDestroySamplerYcbcrConversionKHR flextVkDevice.DestroySamplerYcbcrConversionKHR +/* VK_KHR_timeline_semaphore */ + +#define vkGetSemaphoreCounterValueKHR flextVkDevice.GetSemaphoreCounterValueKHR +#define vkSignalSemaphoreKHR flextVkDevice.SignalSemaphoreKHR +#define vkWaitSemaphoresKHR flextVkDevice.WaitSemaphoresKHR + /* VK_VERSION_1_0 */ #define vkAllocateCommandBuffers flextVkDevice.AllocateCommandBuffers From ffbceef22cd589a9bc789ba54480ac157bdb62f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 19 Jun 2020 23:52:27 +0200 Subject: [PATCH 07/85] Vk: retrieving instance version. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/Test/CMakeLists.txt | 4 +++ src/Magnum/Vk/Test/VersionVkTest.cpp | 51 ++++++++++++++++++++++++++++ src/Magnum/Vk/Version.cpp | 10 ++++++ src/Magnum/Vk/Version.h | 14 ++++++++ 5 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/Test/VersionVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index c53492551d..4cd61af6af 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -168,7 +168,7 @@ Vulkan function | Matching API @fn_vk{EnumerateDeviceExtensionProperties} | | @fn_vk{EnumerateInstanceExtensionProperties} | | @fn_vk{EnumerateInstanceLayerProperties} | | -@fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | | +@fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() @fn_vk{EnumeratePhysicalDevices} | | @fn_vk{EnumeratePhysicalDeviceGroups} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index c0cffea2be..f622bf76fc 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -29,3 +29,7 @@ corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) + +if(BUILD_VK_TESTS) + corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) +endif() diff --git a/src/Magnum/Vk/Test/VersionVkTest.cpp b/src/Magnum/Vk/Test/VersionVkTest.cpp new file mode 100644 index 0000000000..36dee616ee --- /dev/null +++ b/src/Magnum/Vk/Test/VersionVkTest.cpp @@ -0,0 +1,51 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct VersionVkTest: TestSuite::Tester { + explicit VersionVkTest(); + + void enumerate(); +}; + +VersionVkTest::VersionVkTest() { + addTests({&VersionVkTest::enumerate}); +} + +void VersionVkTest::enumerate() { + Version version = enumerateInstanceVersion(); + Debug{} << "Available version:" << version; + CORRADE_COMPARE_AS(version, Version::Vk10, TestSuite::Compare::GreaterOrEqual); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::VersionVkTest) diff --git a/src/Magnum/Vk/Version.cpp b/src/Magnum/Vk/Version.cpp index d4f8b7cb57..1ae1dae7aa 100644 --- a/src/Magnum/Vk/Version.cpp +++ b/src/Magnum/Vk/Version.cpp @@ -27,6 +27,8 @@ #include +#include "Magnum/Vk/Result.h" + namespace Magnum { namespace Vk { Debug& operator<<(Debug& debug, const Version value) { @@ -38,4 +40,12 @@ Debug& operator<<(Debug& debug, const Version value) { return debug; } +Version enumerateInstanceVersion() { + if(!vkEnumerateInstanceVersion) return Version::Vk10; + + UnsignedInt version; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceVersion(&version)); + return Version(version); +} + }} diff --git a/src/Magnum/Vk/Version.h b/src/Magnum/Vk/Version.h index 5e27bef361..79fa57a373 100644 --- a/src/Magnum/Vk/Version.h +++ b/src/Magnum/Vk/Version.h @@ -131,6 +131,20 @@ as @cb{.shell-session} . @ce if patch is zero. */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, Version value); +/** +@brief Enumerate instance version +@m_since_latest + +Note that the @fn_vk{EnumerateInstanceVersion} function isn't available in +Vulkan 1.0 and thus is fetched at runtime via @fn_vk{GetInstanceProcAddr} +called in a static constructor. On Vulkan 1.0 always returns +@ref Version::Vk10, in newer versions the returned version number contains also +patch information. +@see @ref versionMajor(), @ref versionMinor(), @ref versionPatch(), + @fn_vk_keyword{EnumerateInstanceVersion} +*/ +MAGNUM_VK_EXPORT Version enumerateInstanceVersion(); + }} #endif From 18e223e4d9ea69d8fc0ae2d100f5f6f3e1056307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 20 Jun 2020 00:06:55 +0200 Subject: [PATCH 08/85] Vk: enumerating layer properties. Instance layers, with the same interface designed to be reused for device-level layers. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 4 +- src/Magnum/Vk/LayerProperties.cpp | 118 ++++++++++++++ src/Magnum/Vk/LayerProperties.h | 157 +++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/LayerPropertiesTest.cpp | 61 +++++++ src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp | 142 +++++++++++++++++ src/Magnum/Vk/Vk.h | 1 + 8 files changed, 485 insertions(+), 2 deletions(-) create mode 100644 src/Magnum/Vk/LayerProperties.cpp create mode 100644 src/Magnum/Vk/LayerProperties.h create mode 100644 src/Magnum/Vk/Test/LayerPropertiesTest.cpp create mode 100644 src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 4cd61af6af..8586ce65a6 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -167,7 +167,7 @@ Vulkan function | Matching API @fn_vk{EnumerateDeviceLayerProperties} @m_class{m-label m-danger} **deprecated in 1.0.13** | not exposed, [spec commit](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) @fn_vk{EnumerateDeviceExtensionProperties} | | @fn_vk{EnumerateInstanceExtensionProperties} | | -@fn_vk{EnumerateInstanceLayerProperties} | | +@fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() @fn_vk{EnumeratePhysicalDevices} | | @fn_vk{EnumeratePhysicalDeviceGroups} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 117450208b..1debdbfe15 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -32,7 +32,8 @@ set(MagnumVk_SRCS Version.cpp) set(MagnumVk_GracefulAssert_SRCS - Enums.cpp) + Enums.cpp + LayerProperties.cpp) set(MagnumVk_HEADERS Device.h @@ -40,6 +41,7 @@ set(MagnumVk_HEADERS Extensions.h Instance.h Integration.h + LayerProperties.h Result.h TypeTraits.h Version.h diff --git a/src/Magnum/Vk/LayerProperties.cpp b/src/Magnum/Vk/LayerProperties.cpp new file mode 100644 index 0000000000..717d16dd48 --- /dev/null +++ b/src/Magnum/Vk/LayerProperties.cpp @@ -0,0 +1,118 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "LayerProperties.h" + +#include +#include + +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +LayerProperties::LayerProperties(NoCreateT) {} + +LayerProperties::LayerProperties() = default; + +LayerProperties::LayerProperties(LayerProperties&&) noexcept = default; + +LayerProperties::~LayerProperties() = default; + +LayerProperties& LayerProperties::operator=(LayerProperties&&) noexcept = default; + +Containers::ArrayView LayerProperties::names() { + return {reinterpret_cast(_layers.end()), _layers.size()}; +} + +bool LayerProperties::isSupported(const Containers::StringView layer) { + return std::binary_search( + reinterpret_cast(_layers.end()), + reinterpret_cast(_layers.end()) + _layers.size(), + layer); +} + +UnsignedInt LayerProperties::count() { + return UnsignedInt(_layers.size()); +} + +Containers::StringView LayerProperties::name(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::name(): index" << id << "out of range for" << _layers.size() << "entries", {}); + /* Not returning the string views at the end because those are in a + different order */ + return _layers[id].layerName; +} + +UnsignedInt LayerProperties::revision(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::revision(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return _layers[id].implementationVersion; +} + +Version LayerProperties::version(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::version(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return Version(_layers[id].specVersion); +} + +Containers::StringView LayerProperties::description(const UnsignedInt id) { + CORRADE_ASSERT(id < _layers.size(), + "Vk::LayerProperties::description(): index" << id << "out of range for" << _layers.size() << "entries", {}); + return _layers[id].description; +} + +LayerProperties enumerateLayerProperties() { + LayerProperties out; + + /* Retrieve layer count */ + UnsignedInt count; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, nullptr)); + + /* No layers, nothing to do */ + if(!count) return out; + + /* Allocate extra for a list of string views that we'll use to sort & + search the values; query the layers */ + out._layers = Containers::Array{ + reinterpret_cast(new char[count*(sizeof(VkLayerProperties) + sizeof(Containers::StringView))]), + count, + [](VkLayerProperties* data, std::size_t) { + delete[] reinterpret_cast(data); + }}; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, out._layers.data())); + + /* Expect the layer count didn't change between calls */ + CORRADE_INTERNAL_ASSERT(count == out._layers.size()); + + /* Populate the views and sort them so we can search in O(log n) later */ + Containers::ArrayView layerNames{reinterpret_cast(out._layers.end()), count}; + for(std::size_t i = 0; i != layerNames.size(); ++i) + layerNames[i] = out._layers[i].layerName; + std::sort(layerNames.begin(), layerNames.end()); + + return out; +} + +}} diff --git a/src/Magnum/Vk/LayerProperties.h b/src/Magnum/Vk/LayerProperties.h new file mode 100644 index 0000000000..98a6cc0e27 --- /dev/null +++ b/src/Magnum/Vk/LayerProperties.h @@ -0,0 +1,157 @@ +#ifndef Magnum_Vk_LayerProperties_h +#define Magnum_Vk_LayerProperties_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::LayerProperties, function @ref Magnum::Vk::enumerateLayerProperties() + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Vulkan layer properties +@m_since_latest + +Provides a searchable container of Vulkan layers enumerated with +@ref enumerateLayerProperties(). Only instance layers are enumerated, as device +layers are [deprecated since Vulkan 1.0.13](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) +and the assumption is that no drivers currently use rely on these anymore. See +[§ 37.3.1](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap38.html#extendingvulkan-layers-devicelayerdeprecation) +for more information. +@see @ref ExtensionProperties, @ref enumerateInstanceExtensionProperties(), + @type_vk_keyword{LayerProperties} +*/ +class MAGNUM_VK_EXPORT LayerProperties { + public: + /** + * @brief Construct without populating the contents + * + * Equivalent to a moved-from state. Move over the result of + * @ref enumerateLayerProperties() to make it usable. + */ + explicit LayerProperties(NoCreateT); + + ~LayerProperties(); + + /** @brief Copying is not allowed */ + LayerProperties(const LayerProperties&) = delete; + + /** @brief Move constructor */ + LayerProperties(LayerProperties&&) noexcept; + + /** @brief Copying is not allowed */ + LayerProperties& operator=(const LayerProperties&) = delete; + + /** @brief Move assignment */ + LayerProperties& operator=(LayerProperties&&) noexcept; + + /** + * @brief Layer names + * + * A list of all layers reported by the driver. Use @ref isSupported() + * to query support of a particular layer name. Note that the list is + * sorted and thus may be different than the order in which the + * @ref name(), @ref revision(), @ref version() and @ref description() + * accessors return values. + * + * The returned views are owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::ArrayView names(); + + /** + * @brief Whether given layer is supported + * + * Search complexity is @f$ \mathcal{O}(\log n) @f$ in the total layer + * count. + */ + bool isSupported(Containers::StringView layer); + + /** @brief Count of layers reported by the driver */ + UnsignedInt count(); + + /** + * @brief Layer name + * @param id Layer index, expected to be smaller than @ref count() + * + * The returned view is owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::StringView name(UnsignedInt id); + + /** + * @brief Layer revision + * @param id Layer index, expected to be smaller than @ref count() + */ + UnsignedInt revision(UnsignedInt id); + + /** + * @brief Vulkan version the layer is implemented against + * @param id Layer index, expected to be smaller than @ref count() + */ + Version version(UnsignedInt id); + + /** + * @brief Layer description + * @param id Layer index, expected to be smaller than @ref count() + * + * The returned view is owned by the @ref LayerProperties instance + * (i.e., *not* a global memory). + */ + Containers::StringView description(UnsignedInt id); + + private: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* The DAMN THING lists this among friends, which is AN IMPLEMENTATION + DETAIL */ + friend MAGNUM_VK_EXPORT LayerProperties enumerateLayerProperties(); + #endif + + explicit LayerProperties(); + + Containers::Array _layers; +}; + +/** +@brief Enumerate instance layers +@m_since_latest + +@see @fn_vk_keyword{EnumerateInstanceLayerProperties} +*/ +MAGNUM_VK_EXPORT LayerProperties enumerateLayerProperties(); + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index f622bf76fc..22b99e6069 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -27,9 +27,11 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/LayerPropertiesTest.cpp b/src/Magnum/Vk/Test/LayerPropertiesTest.cpp new file mode 100644 index 0000000000..852d2b0bb0 --- /dev/null +++ b/src/Magnum/Vk/Test/LayerPropertiesTest.cpp @@ -0,0 +1,61 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include + +#include "Magnum/Vk/LayerProperties.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct LayerPropertiesTest: TestSuite::Tester { + explicit LayerPropertiesTest(); + + void constructNoCreate(); + void constructCopy(); +}; + +LayerPropertiesTest::LayerPropertiesTest() { + addTests({&LayerPropertiesTest::constructNoCreate, + &LayerPropertiesTest::constructCopy}); +} + +void LayerPropertiesTest::constructNoCreate() { + { + LayerProperties properties{NoCreate}; + CORRADE_COMPARE(properties.count(), 0); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void LayerPropertiesTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::LayerPropertiesTest) diff --git a/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp b/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp new file mode 100644 index 0000000000..8c74f3bf9a --- /dev/null +++ b/src/Magnum/Vk/Test/LayerPropertiesVkTest.cpp @@ -0,0 +1,142 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct LayerPropertiesVkTest: TestSuite::Tester { + explicit LayerPropertiesVkTest(); + + void constructMove(); + + void enumerate(); + void outOfRange(); + void isSupported(); +}; + +LayerPropertiesVkTest::LayerPropertiesVkTest() { + addTests({&LayerPropertiesVkTest::constructMove, + + &LayerPropertiesVkTest::enumerate, + &LayerPropertiesVkTest::outOfRange, + &LayerPropertiesVkTest::isSupported}); +} + +using namespace Containers::Literals; + +void LayerPropertiesVkTest::constructMove() { + LayerProperties a = enumerateLayerProperties(); + const UnsignedInt count = a.count(); + if(!count) CORRADE_SKIP("No extensions reported, can't test"); + + LayerProperties b = std::move(a); + CORRADE_COMPARE(b.count(), count); + + LayerProperties c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.count(), count); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void LayerPropertiesVkTest::enumerate() { + LayerProperties properties = enumerateLayerProperties(); + + if(!properties.count()) + CORRADE_SKIP("The driver reported no instance layers, can't test."); + + CORRADE_COMPARE(properties.count(), properties.names().size()); + Debug{} << "Available instance layers:" << properties.names(); + + Containers::ArrayView names = properties.names(); + CORRADE_COMPARE_AS(names.size(), 0, TestSuite::Compare::Greater); + /* The list should be sorted */ + for(std::size_t i = 1; i != names.size(); ++i) { + CORRADE_COMPARE_AS(names[i - 1], names[i], + TestSuite::Compare::Less); + } + + CORRADE_COMPARE_AS(properties.name(0).size(), "VK_LAYER_"_s.size(), + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.revision(0), 0, + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.version(0), Version::Vk10, + TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(properties.description(0).size(), 10, + TestSuite::Compare::Greater); +} + +void LayerPropertiesVkTest::outOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + LayerProperties properties = enumerateLayerProperties(); + const UnsignedInt count = properties.count(); + + std::ostringstream out; + Error redirectError{&out}; + properties.name(count); + properties.revision(count); + properties.version(count); + properties.description(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::LayerProperties::name(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::revision(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::version(): index {0} out of range for {0} entries\n" + "Vk::LayerProperties::description(): index {0} out of range for {0} entries\n", count)); +} + +void LayerPropertiesVkTest::isSupported() { + LayerProperties properties = enumerateLayerProperties(); + + CORRADE_VERIFY(!properties.isSupported("this layer doesn't exist")); + + if(!properties.count()) + CORRADE_SKIP("The driver reported no instance layers, can't fully test."); + + for(UnsignedInt i = 0; i != properties.count(); ++i) { + CORRADE_ITERATION(properties.name(i)); + CORRADE_VERIFY(properties.isSupported(properties.name(i))); + } + + /* Verify that we're not just comparing a prefix */ + const std::string layer = std::string(properties.name(0)) + "_hello"; + CORRADE_VERIFY(!properties.isSupported(layer)); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::LayerPropertiesVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 4b7697e945..2531764898 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT class Extension; class InstanceExtension; +class LayerProperties; enum class Result: Int; enum class Version: UnsignedInt; From 64ba1e673236985239ccaaec642207f25a8f1431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 20 Jun 2020 21:18:30 +0200 Subject: [PATCH 09/85] Vk: enumerating instance extension properties. Device extension properties will reuse a large part of this API. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/ExtensionProperties.cpp | 182 ++++++++++++ src/Magnum/Vk/ExtensionProperties.h | 263 ++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + .../Vk/Test/ExtensionPropertiesTest.cpp | 82 ++++++ .../Vk/Test/ExtensionPropertiesVkTest.cpp | 221 +++++++++++++++ src/Magnum/Vk/Vk.h | 2 + 8 files changed, 755 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/ExtensionProperties.cpp create mode 100644 src/Magnum/Vk/ExtensionProperties.h create mode 100644 src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp create mode 100644 src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 8586ce65a6..02ce1316f9 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -166,7 +166,7 @@ Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{EnumerateDeviceLayerProperties} @m_class{m-label m-danger} **deprecated in 1.0.13** | not exposed, [spec commit](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) @fn_vk{EnumerateDeviceExtensionProperties} | | -@fn_vk{EnumerateInstanceExtensionProperties} | | +@fn_vk{EnumerateInstanceExtensionProperties} | @ref enumerateInstanceExtensionProperties() @fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() @fn_vk{EnumeratePhysicalDevices} | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 1debdbfe15..1ab5af6337 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -33,12 +33,14 @@ set(MagnumVk_SRCS set(MagnumVk_GracefulAssert_SRCS Enums.cpp + ExtensionProperties.cpp LayerProperties.cpp) set(MagnumVk_HEADERS Device.h Enums.h Extensions.h + ExtensionProperties.h Instance.h Integration.h LayerProperties.h diff --git a/src/Magnum/Vk/ExtensionProperties.cpp b/src/Magnum/Vk/ExtensionProperties.cpp new file mode 100644 index 0000000000..7cd760c125 --- /dev/null +++ b/src/Magnum/Vk/ExtensionProperties.cpp @@ -0,0 +1,182 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "ExtensionProperties.h" + +#include +#include +#include +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { + +ExtensionProperties::ExtensionProperties(NoCreateT) {} + +ExtensionProperties::ExtensionProperties(ExtensionProperties&&) noexcept = default; + +ExtensionProperties::~ExtensionProperties() = default; + +ExtensionProperties& ExtensionProperties::operator=(ExtensionProperties&&) noexcept = default; + +ExtensionProperties::ExtensionProperties(const Containers::ArrayView layers, VkResult(*const enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* const state) { + /* Retrieve total extension count for all layers + the global extensions */ + std::size_t totalCount = 0; + for(std::size_t i = 0; i <= layers.size(); ++i) { + UnsignedInt count; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(enumerator(state, + i == 0 ? nullptr : + Containers::String::nullTerminatedView(layers[i - 1]).data(), + &count, nullptr)); + + totalCount += count; + } + + /* Allocate extra for a list of string views that we'll use to sort & + search the values and a layer index so we can map the extensions back + to which layer they come from */ + _extensions = Containers::Array{ + reinterpret_cast(new char[totalCount*(sizeof(VkExtensionProperties) + sizeof(Containers::StringView) + sizeof(UnsignedInt))]), + totalCount, + [](VkExtensionProperties* data, std::size_t) { + delete[] reinterpret_cast(data); + }}; + Containers::ArrayView extensionNames{reinterpret_cast(_extensions.end()), totalCount}; + Containers::ArrayView extensionLayers{reinterpret_cast(extensionNames.end()), totalCount}; + + /* Query the extensions, save layer ID for each */ + std::size_t offset = 0; + for(std::size_t i = 0; i <= layers.size(); ++i) { + UnsignedInt count = totalCount - offset; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(enumerator(state, + i == 0 ? nullptr : + Containers::String::nullTerminatedView(layers[i - 1]).data(), + &count, reinterpret_cast(_extensions.data()) + offset)); + for(std::size_t j = 0; j != count; ++j) extensionLayers[offset + j] = i; + offset += count; + } + + /* Expect the total extension count didn't change between calls */ + CORRADE_INTERNAL_ASSERT(offset == totalCount); + + /* Populate the views, sort them and remove duplicates so we can search in + O(log n) later */ + for(std::size_t i = 0; i != extensionNames.size(); ++i) + extensionNames[i] = _extensions[i].extensionName; + std::sort(extensionNames.begin(), extensionNames.end()); + _uniqueExtensionCount = std::unique(extensionNames.begin(), extensionNames.end()) - extensionNames.begin(); +} + +Containers::ArrayView ExtensionProperties::names() const { + return {reinterpret_cast(_extensions.end()), _uniqueExtensionCount}; +} + +bool ExtensionProperties::isSupported(const Containers::StringView extension) const { + return std::binary_search( + reinterpret_cast(_extensions.end()), + reinterpret_cast(_extensions.end()) + _uniqueExtensionCount, + extension); +} + +bool ExtensionProperties::isSupported(const Extension& extension) const { + return isSupported(extension.string()); +} + +Containers::StringView ExtensionProperties::name(const UnsignedInt id) const { + CORRADE_ASSERT(id < _extensions.size(), + "Vk::ExtensionProperties::name(): index" << id << "out of range for" << _extensions.size() << "entries", {}); + /* Not returning the string views at the end because those are in a + different order */ + return _extensions[id].extensionName; +} + +UnsignedInt ExtensionProperties::revision(const Extension& extension) const { + return revision(extension.string()); +} + +UnsignedInt ExtensionProperties::revision(const UnsignedInt id) const { + CORRADE_ASSERT(id < _extensions.size(), + "Vk::ExtensionProperties::revision(): index" << id << "out of range for" << _extensions.size() << "entries", {}); + /* WTF, why VkLayerProperties::specVersion is an actual Vulkan version and + here it is a revision number?! Consistency my ass. */ + return _extensions[id].specVersion; +} + +UnsignedInt ExtensionProperties::revision(const Containers::StringView extension) const { + /* Thanks, C++, for forcing me to do one more comparison than strictly + necessary */ + auto found = std::lower_bound( + reinterpret_cast(_extensions.end()), + reinterpret_cast(_extensions.end()) + _uniqueExtensionCount, + extension); + if(*found != extension) return 0; + + /* The view target is contents of the VkExtensionProperties structure, + the revision is stored nearby */ + return reinterpret_cast(found->data() - offsetof(VkExtensionProperties, extensionName))->specVersion; +} + +UnsignedInt ExtensionProperties::layer(const UnsignedInt id) const { + CORRADE_ASSERT(id < _extensions.size(), + "Vk::xtensionProperties::layer(): index" << id << "out of range for" << _extensions.size() << "entries", {}); + return reinterpret_cast(reinterpret_cast(_extensions.end()) + _extensions.size())[id]; +} + +InstanceExtensionProperties::InstanceExtensionProperties(InstanceExtensionProperties&&) noexcept = default; + +InstanceExtensionProperties::~InstanceExtensionProperties() = default; + +InstanceExtensionProperties& InstanceExtensionProperties::operator=(InstanceExtensionProperties&&) noexcept = default; + +bool InstanceExtensionProperties::isSupported(Containers::StringView extension) const { + return ExtensionProperties::isSupported(extension); +} + +bool InstanceExtensionProperties::isSupported(const InstanceExtension& extension) const { + return isSupported(extension.string()); +} + +UnsignedInt InstanceExtensionProperties::revision(Containers::StringView extension) const { + return ExtensionProperties::revision(extension); +} + +UnsignedInt InstanceExtensionProperties::revision(const InstanceExtension& extension) const { + return revision(extension.string()); +} + +InstanceExtensionProperties enumerateInstanceExtensionProperties(const Containers::ArrayView layers) { + return InstanceExtensionProperties{layers, [](void*, const char* const layer, UnsignedInt* count, VkExtensionProperties* properties) { + return vkEnumerateInstanceExtensionProperties(layer, count, properties); + }, nullptr}; +} + +InstanceExtensionProperties enumerateInstanceExtensionProperties(const std::initializer_list layers) { + return enumerateInstanceExtensionProperties(Containers::arrayView(layers)); +} + +}} diff --git a/src/Magnum/Vk/ExtensionProperties.h b/src/Magnum/Vk/ExtensionProperties.h new file mode 100644 index 0000000000..4c3bfe9cd2 --- /dev/null +++ b/src/Magnum/Vk/ExtensionProperties.h @@ -0,0 +1,263 @@ +#ifndef Magnum_Vk_ExtensionProperties_h +#define Magnum_Vk_ExtensionProperties_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::ExtensionProperties, @ref Magnum::Vk::InstanceExtensionProperties, function @ref enumerateInstanceExtensionProperties() + * @m_since_latest + */ + +#include + +#include "Magnum/Tags.h" +#include "Magnum/Magnum.h" +#include "Magnum/Vk/TypeTraits.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Extension properties +@m_since_latest + +@see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties} +*/ +class MAGNUM_VK_EXPORT ExtensionProperties { + public: + /** + * @brief Construct without populating the contents + * + * Equivalent to a moved-from state. + */ + explicit ExtensionProperties(NoCreateT); + + /** @brief Copying is not allowed */ + ExtensionProperties(const ExtensionProperties&) = delete; + + /** @brief Move constructor */ + ExtensionProperties(ExtensionProperties&&) noexcept; + + ~ExtensionProperties(); + + /** @brief Copying is not allowed */ + ExtensionProperties& operator=(const ExtensionProperties&) = delete; + + /** @brief Move assignment */ + ExtensionProperties& operator=(ExtensionProperties&&) noexcept; + + /** + * @brief Instance extensions + * + * A list of all extension strings reported by the driver for all + * layers passed to the constructor, with duplicates removed. Use + * @ref isSupported() to query support of a particular + * extension. Note that the list is sorted and thus may be different + * than the order in which the @ref name() and @ref revision() + * accessors return values. + * + * The returned views are owned by the + * @ref ExtensionProperties instance (i.e., *not* a global + * memory). + */ + Containers::ArrayView names() const; + + /** + * @brief Whether given extension is supported + * + * Accepts extensions from the @ref Extension namespace as a template + * parameter. Use the other overloads to query support of a runtime + * extension or a plain extension string. + * + * Search complexity is @f$ \mathcal{O}(\log n) @f$ in the total + * extension count; in contrast extension queries on a created instance + * are @f$ \mathcal{O}(1) @f$. + * @see @ref revision() + */ + bool isSupported(Containers::StringView extension) const; + + /** @overload */ + bool isSupported(const Extension& extension) const; + + /** @overload */ + template bool isSupported() const { + static_assert(Implementation::IsExtension::value, "expected a Vulkan deviceension"); + return isSupported(E::string()); + } + + /** + * @brief Count of extensions reported by the driver for all layers + * + * The count includes potential duplicates when an extension is both + * available globally and through a particular layer. + */ + UnsignedInt count() const { return _extensions.size(); } + + /** + * @brief Extension name + * @param id Extension index, expected to be smaller than @ref count() + * + * The returned view is owned by the + * @ref ExtensionProperties instance (i.e., *not* a global + * memory). + */ + Containers::StringView name(UnsignedInt id) const; + + /** + * @brief Extension revision + * @param id Extension index, expected to be smaller than @ref count() + */ + UnsignedInt revision(UnsignedInt id) const; + + /** + * @brief Revision of a particular extension name + * + * If the extension is not supported, returns @cpp 0 @ce, supported + * extensions always have a non-zero revision. If the extension is + * implemented by more than one layer, returns revision of the first + * layer implementing it --- use @ref revision(UnsignedInt) const + * to get revision of a concrete extension in a concrete layer. + * @see @ref isSupported() + */ + UnsignedInt revision(Containers::StringView extension) const; + + /** @overload */ + UnsignedInt revision(const Extension& extension) const; + + /** @overload */ + template UnsignedInt revision() const { + static_assert(Implementation::IsExtension::value, "expected a Vulkan device extension"); + return revision(E::string()); + } + + /** + * @brief Extension layer index + * @param id Extension index, expected to be smaller than @ref count() + * + * Returns ID of the layer the extension comes from. @cpp 0 @ce is + * global extensions, @cpp 1 @ce is the first layer passed to + * @ref enumerateInstanceExtensionProperties() and so on. + */ + UnsignedInt layer(UnsignedInt id) const; + + private: + friend class InstanceExtensionProperties; + + explicit ExtensionProperties(const Containers::ArrayView layers, VkResult(*enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* state); + + Containers::Array _extensions; + std::size_t _uniqueExtensionCount; +}; + +/** +@brief Instance extension properties +@m_since_latest + +Provides a searchable container of Vulkan device extensions enumerated with +@ref enumerateInstanceExtensionProperties(). +@see @ref ExtensionProperties, @ref LayerProperties +*/ +class MAGNUM_VK_EXPORT InstanceExtensionProperties: public ExtensionProperties { + public: + /** + * @brief Construct without populating the contents + * + * Equivalent to a moved-from state. Move over the result of + * @ref enumerateInstanceExtensionProperties() to make it usable. + */ + explicit InstanceExtensionProperties(NoCreateT): ExtensionProperties{NoCreate} {} + + /** @brief Copying is not allowed */ + InstanceExtensionProperties(const InstanceExtensionProperties&) = delete; + + /** @brief Move constructor */ + InstanceExtensionProperties(InstanceExtensionProperties&&) noexcept; + + ~InstanceExtensionProperties(); + + /** @brief Copying is not allowed */ + InstanceExtensionProperties& operator=(const InstanceExtensionProperties&) = delete; + + /** @brief Move assignment */ + InstanceExtensionProperties& operator=(InstanceExtensionProperties&&) noexcept; + + /** @copydoc ExtensionProperties::isSupported(Containers::StringView) const */ + bool isSupported(Containers::StringView extension) const; + + /** @overload */ + bool isSupported(const InstanceExtension& extension) const; + + /** @overload */ + template bool isSupported() const { + static_assert(Implementation::IsInstanceExtension::value, "expected a Vulkan instance extension"); + return isSupported(E::string()); + } + + /** @copydoc ExtensionProperties::revision(UnsignedInt) const */ + UnsignedInt revision(UnsignedInt id) const { + return ExtensionProperties::revision(id); + } + + /** @copydoc ExtensionProperties::revision(Containers::StringView) const */ + UnsignedInt revision(Containers::StringView extension) const; + + /** @overload */ + UnsignedInt revision(const InstanceExtension& extension) const; + + /** @overload */ + template UnsignedInt revision() const { + static_assert(Implementation::IsInstanceExtension::value, "expected a Vulkan instance extension"); + return revision(E::string()); + } + + private: + #ifndef DOXYGEN_GENERATING_OUTPUT + /* The DAMN THING forgets parameter name if this is present, FFS. It + also lists this among friends, which is AN IMPLEMENTATION DETAIL */ + friend MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView); + #endif + + explicit InstanceExtensionProperties(const Containers::ArrayView layers, VkResult(*enumerator)(void*, const char*, UnsignedInt*, VkExtensionProperties*), void* state): ExtensionProperties{layers, enumerator, state} {} +}; + +/** +@brief Enumerate instance extension properties +@param layers Additional layers to list extensions from +@m_since_latest + +Expects that all listed layers are supported. +@see @ref LayerProperties::isSupported(), + @fn_vk_keyword{EnumerateExtensionProperties} +*/ +MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView layers = {}); + +/** @overload */ +MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(std::initializer_list layers); + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 22b99e6069..bc38912814 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -26,12 +26,14 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp b/src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp new file mode 100644 index 0000000000..b997d8f30a --- /dev/null +++ b/src/Magnum/Vk/Test/ExtensionPropertiesTest.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include + +#include "Magnum/Vk/ExtensionProperties.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ExtensionPropertiesTest: TestSuite::Tester { + explicit ExtensionPropertiesTest(); + + void constructNoCreate(); + void constructInstanceNoCreate(); + + void constructCopy(); + void constructInstanceCopy(); +}; + +ExtensionPropertiesTest::ExtensionPropertiesTest() { + addTests({&ExtensionPropertiesTest::constructNoCreate, + &ExtensionPropertiesTest::constructInstanceNoCreate, + + &ExtensionPropertiesTest::constructCopy, + &ExtensionPropertiesTest::constructInstanceCopy}); +} + +void ExtensionPropertiesTest::constructNoCreate() { + { + ExtensionProperties properties{NoCreate}; + CORRADE_COMPARE(properties.count(), 0); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ExtensionPropertiesTest::constructInstanceNoCreate() { + { + InstanceExtensionProperties properties{NoCreate}; + CORRADE_COMPARE(properties.count(), 0); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ExtensionPropertiesTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void ExtensionPropertiesTest::constructInstanceCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ExtensionPropertiesTest) diff --git a/src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp b/src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp new file mode 100644 index 0000000000..513191e978 --- /dev/null +++ b/src/Magnum/Vk/Test/ExtensionPropertiesVkTest.cpp @@ -0,0 +1,221 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ExtensionPropertiesVkTest: TestSuite::Tester { + explicit ExtensionPropertiesVkTest(); + + void constructMove(); + /* Device extension move tested in DevicePropertiesVkTest */ + + void enumerateInstance(); + void enumerateInstanceWithKhronosValidationLayer(); + void enumerateInstanceNonexistentLayer(); + void instanceExtensionIsSupported(); + + /* Device extensions tested in DevicePropertiesVkTest */ + void outOfRange(); + void namedRevision(); +}; + +ExtensionPropertiesVkTest::ExtensionPropertiesVkTest() { + addTests({&ExtensionPropertiesVkTest::constructMove, + + &ExtensionPropertiesVkTest::enumerateInstance, + &ExtensionPropertiesVkTest::enumerateInstanceWithKhronosValidationLayer, + &ExtensionPropertiesVkTest::enumerateInstanceNonexistentLayer, + &ExtensionPropertiesVkTest::instanceExtensionIsSupported, + + &ExtensionPropertiesVkTest::outOfRange, + &ExtensionPropertiesVkTest::namedRevision}); +} + +using namespace Containers::Literals; + +void ExtensionPropertiesVkTest::constructMove() { + InstanceExtensionProperties a = enumerateInstanceExtensionProperties(); + const UnsignedInt count = a.count(); + if(!count) CORRADE_SKIP("No extensions reported, can't test"); + + InstanceExtensionProperties b = std::move(a); + CORRADE_COMPARE(b.count(), count); + + InstanceExtensionProperties c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.count(), count); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void ExtensionPropertiesVkTest::enumerateInstance() { + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + Debug{} << "Available instance extension count:" << properties.names().size(); + + CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater); + for(std::size_t i = 0; i != properties.count(); ++i) { + using namespace Containers::Literals; + CORRADE_ITERATION(properties.name(i)); + CORRADE_COMPARE_AS(properties.name(i).size(), "VK_"_s.size(), + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.revision(i), 0, + TestSuite::Compare::Greater); + /* All extensions are from the global layer */ + CORRADE_COMPARE(properties.layer(i), 0); + } + + /* The extension list should be sorted and unique (so Less, not + LessOrEqual) */ + Containers::ArrayView extensions = properties.names(); + for(std::size_t i = 1; i != extensions.size(); ++i) { + CORRADE_COMPARE_AS(extensions[i - 1], extensions[i], + TestSuite::Compare::Less); + } +} + +void ExtensionPropertiesVkTest::enumerateInstanceWithKhronosValidationLayer() { + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + /* There should be more extensions with this layer enabled */ + InstanceExtensionProperties global = enumerateInstanceExtensionProperties(); + InstanceExtensionProperties withKhronosValidation = enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + CORRADE_COMPARE_AS(global.count(), + withKhronosValidation.count(), + TestSuite::Compare::Less); + + /* The extension list should be sorted even including the extra layers, and + unique (so Less, not LessOrEqual) */ + Containers::ArrayView extensions = withKhronosValidation.names(); + for(std::size_t i = 1; i != extensions.size(); ++i) { + CORRADE_COMPARE_AS(extensions[i - 1], extensions[i], + TestSuite::Compare::Less); + } + + /* The VK_LAYER_KHRONOS_validation adds extensions that are supported + globally, which means extensionCount() should be larger than + extensions.size() as it has some duplicates */ + CORRADE_COMPARE_AS(withKhronosValidation.count(), extensions.size(), + TestSuite::Compare::Greater); + + /* The last extension should be from the validation layer */ + CORRADE_COMPARE(withKhronosValidation.layer(0), 0); + CORRADE_COMPARE(withKhronosValidation.layer(withKhronosValidation.count() - 1), 1); + + /* VK_EXT_validation_features is only in the layer */ + CORRADE_VERIFY(!global.isSupported("VK_EXT_validation_features")); + CORRADE_VERIFY(withKhronosValidation.isSupported("VK_EXT_validation_features")); +} + +void ExtensionPropertiesVkTest::enumerateInstanceNonexistentLayer() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + enumerateInstanceExtensionProperties({"VK_LAYER_this_doesnt_exist"}); + CORRADE_COMPARE(out.str(), "TODO"); +} + +void ExtensionPropertiesVkTest::instanceExtensionIsSupported() { + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater); + + for(UnsignedInt i = 0; i != properties.count(); ++i) { + CORRADE_ITERATION(properties.name(i)); + CORRADE_VERIFY(properties.isSupported(properties.name(i))); + } + + CORRADE_VERIFY(!properties.isSupported("VK_this_doesnt_exist")); + + /* Verify that we're not just comparing a prefix */ + const std::string extension = std::string(properties.name(0)) + "_hello"; + CORRADE_VERIFY(!properties.isSupported(extension)); + + /* This extension should be available almost always */ + if(!properties.isSupported("VK_KHR_get_physical_device_properties2")) + CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't fully test"); + + /* Verify the overloads that take our extension wrappers work as well */ + CORRADE_VERIFY(properties.isSupported()); + CORRADE_VERIFY(properties.isSupported(Extensions::KHR::get_physical_device_properties2{})); +} + +void ExtensionPropertiesVkTest::outOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + const UnsignedInt count = properties.count(); + + std::ostringstream out; + Error redirectError{&out}; + properties.name(count); + properties.revision(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::ExtensionProperties::name(): index {0} out of range for {0} entries\n" + "Vk::ExtensionProperties::revision(): index {0} out of range for {0} entries\n", count)); +} + +void ExtensionPropertiesVkTest::namedRevision() { + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + /** @todo use Extensions::KHR::surface once the extension is recognized */ + if(!properties.isSupported("VK_KHR_surface")) + CORRADE_SKIP("VK_KHR_surface not supported, can't test"); + if(!properties.isSupported()) + CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test"); + + /* It was at revision 25 in January 2016, which is four months before + Vulkan first came out, so it's safe to assume all drivers have this + revision by now */ + CORRADE_COMPARE_AS(properties.revision("VK_KHR_surface"), 25, + TestSuite::Compare::GreaterOrEqual); + + /* Unknown extensions return 0 */ + CORRADE_COMPARE(properties.revision("VK_this_doesnt_exist"), 0); + + /* Verify the overloads that take our extension wrappers work as well */ + CORRADE_COMPARE_AS(properties.revision(), 0, + TestSuite::Compare::Greater); + CORRADE_COMPARE_AS(properties.revision(Extensions::KHR::get_physical_device_properties2{}), 0, + TestSuite::Compare::Greater); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ExtensionPropertiesVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 2531764898..7ebd6d8381 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -35,7 +35,9 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT class Extension; +class ExtensionProperties; class InstanceExtension; +class InstanceExtensionProperties; class LayerProperties; enum class Result: Int; From 189dad59c6cc0a4eadbc61f2092d63bc2d70536a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 12 Jun 2020 23:00:08 +0200 Subject: [PATCH 10/85] Vk: yeah well of course we can't encode Magnum version into VK_VERSION. Sigh. --- src/Magnum/Vk/Test/VersionTest.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Magnum/Vk/Test/VersionTest.cpp b/src/Magnum/Vk/Test/VersionTest.cpp index 91af9026f7..dd7d8ebefe 100644 --- a/src/Magnum/Vk/Test/VersionTest.cpp +++ b/src/Magnum/Vk/Test/VersionTest.cpp @@ -37,6 +37,7 @@ struct VersionTest: TestSuite::Tester { explicit VersionTest(); void packing(); + void packingMagnumVersion(); void comparison(); void debug(); @@ -44,6 +45,7 @@ struct VersionTest: TestSuite::Tester { VersionTest::VersionTest() { addTests({&VersionTest::packing, + &VersionTest::packingMagnumVersion, &VersionTest::comparison, &VersionTest::debug}); @@ -70,6 +72,16 @@ void VersionTest::packing() { #endif } +void VersionTest::packingMagnumVersion() { + Version a = version(2019, 10, 1506); + { + CORRADE_EXPECT_FAIL("Vulkan version encoding can't fit full years."); + CORRADE_COMPARE(versionMajor(a), 2019); + } + CORRADE_COMPARE(versionMinor(a), 10); + CORRADE_COMPARE(versionPatch(a), 1506); +} + void VersionTest::comparison() { CORRADE_VERIFY(!(version(1, 5, 3) < version(1, 5, 3))); CORRADE_VERIFY(!(version(1, 5, 3) > version(1, 5, 3))); From 132bcacf288754a5696061327edbb9df016bd29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 13 Jun 2020 11:17:25 +0200 Subject: [PATCH 11/85] Vk: add HandleFlags, similar to GL::ObjectFlags. --- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/Handle.cpp | 52 ++++++++++++++++++++++ src/Magnum/Vk/Handle.h | 73 +++++++++++++++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/HandleTest.cpp | 60 +++++++++++++++++++++++++ src/Magnum/Vk/Vk.h | 2 + 6 files changed, 190 insertions(+) create mode 100644 src/Magnum/Vk/Handle.cpp create mode 100644 src/Magnum/Vk/Handle.h create mode 100644 src/Magnum/Vk/Test/HandleTest.cpp diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 1ab5af6337..c61905bf85 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -28,6 +28,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS Extensions.cpp + Handle.cpp Result.cpp Version.cpp) @@ -41,6 +42,7 @@ set(MagnumVk_HEADERS Enums.h Extensions.h ExtensionProperties.h + Handle.h Instance.h Integration.h LayerProperties.h diff --git a/src/Magnum/Vk/Handle.cpp b/src/Magnum/Vk/Handle.cpp new file mode 100644 index 0000000000..e9638482a1 --- /dev/null +++ b/src/Magnum/Vk/Handle.cpp @@ -0,0 +1,52 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Handle.h" + +#include +#include + +namespace Magnum { namespace Vk { + +Debug& operator<<(Debug& debug, const HandleFlag value) { + debug << "Vk::HandleFlag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::HandleFlag::value: return debug << "::" << Debug::nospace << #value; + _c(DestroyOnDestruction) + #undef _c + /* LCOV_EXCL_STOP */ + } + + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const HandleFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Vk::HandleFlags{}", { + HandleFlag::DestroyOnDestruction}); +} + +}} diff --git a/src/Magnum/Vk/Handle.h b/src/Magnum/Vk/Handle.h new file mode 100644 index 0000000000..50225273e6 --- /dev/null +++ b/src/Magnum/Vk/Handle.h @@ -0,0 +1,73 @@ +#ifndef Magnum_Vk_Handle_h +#define Magnum_Vk_Handle_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Enum @ref Magnum::Vk::HandleFlag, enum set @ref Magnum::Vk::HandleFlags + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Handle wrapping flag +@m_since_latest + +@see @ref HandleFlags +*/ +enum class HandleFlag: UnsignedByte { + /** Destroy the handle on destruction. */ + DestroyOnDestruction = 1 << 0 +}; + +/** +@brief Handle wrapping flags +@m_since_latest +*/ +typedef Containers::EnumSet HandleFlags; + +CORRADE_ENUMSET_OPERATORS(HandleFlags) + +/** +@debugoperatorenum{HandleFlag} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, HandleFlag value); + +/** +@debugoperatorenum{HandleFlags} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, HandleFlags value); + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index bc38912814..c41e2585e8 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -27,6 +27,7 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/HandleTest.cpp b/src/Magnum/Vk/Test/HandleTest.cpp new file mode 100644 index 0000000000..0df0980c92 --- /dev/null +++ b/src/Magnum/Vk/Test/HandleTest.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include + +#include "Magnum/Vk/Handle.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct HandleTest: TestSuite::Tester { + explicit HandleTest(); + + void debugHandleFlag(); + void debugHandleFlags(); +}; + +HandleTest::HandleTest() { + addTests({&HandleTest::debugHandleFlag, + &HandleTest::debugHandleFlags}); +} + +void HandleTest::debugHandleFlag() { + std::ostringstream out; + Debug{&out} << HandleFlag::DestroyOnDestruction << HandleFlag(0xf0); + CORRADE_COMPARE(out.str(), "Vk::HandleFlag::DestroyOnDestruction Vk::HandleFlag(0xf0)\n"); +} + +void HandleTest::debugHandleFlags() { + std::ostringstream out; + Debug{&out} << (HandleFlag::DestroyOnDestruction|HandleFlag(0xf0)) << HandleFlags{}; + CORRADE_COMPARE(out.str(), "Vk::HandleFlag::DestroyOnDestruction|Vk::HandleFlag(0xf0) Vk::HandleFlags{}\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::HandleTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 7ebd6d8381..0239685258 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,8 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT class Extension; class ExtensionProperties; +enum class HandleFlag: UnsignedByte; +typedef Containers::EnumSet HandleFlags; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; From bb00bf43bf1b6111d65a70c5c994fb9eb053d55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 15 Jun 2020 19:24:21 +0200 Subject: [PATCH 12/85] Vk: variadic Is[Instance]Extension checks. Because I'd like to static_assert on these. --- src/Magnum/Vk/Test/ExtensionsTest.cpp | 38 +++++++++++++++++++++++++++ src/Magnum/Vk/TypeTraits.h | 16 +++++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Vk/Test/ExtensionsTest.cpp b/src/Magnum/Vk/Test/ExtensionsTest.cpp index 40b0e41278..4e29d07e10 100644 --- a/src/Magnum/Vk/Test/ExtensionsTest.cpp +++ b/src/Magnum/Vk/Test/ExtensionsTest.cpp @@ -62,6 +62,25 @@ void ExtensionsTest::isInstanceExtension() { CORRADE_VERIFY(Implementation::IsInstanceExtension::value); CORRADE_VERIFY(!Implementation::IsInstanceExtension::value); CORRADE_VERIFY(!Implementation::IsInstanceExtension::value); + + /* Variadic check (used in variadic addEnabledExtensions()), check that it + properly fails for each occurence of a device extension */ + CORRADE_VERIFY((Implementation::IsInstanceExtension< + Extensions::KHR::get_physical_device_properties2, + Extensions::KHR::external_memory_capabilities, + Extensions::KHR::external_fence_capabilities>::value)); + CORRADE_VERIFY(!(Implementation::IsInstanceExtension< + Extensions::KHR::draw_indirect_count, /* not */ + Extensions::KHR::external_memory_capabilities, + Extensions::KHR::external_fence_capabilities>::value)); + CORRADE_VERIFY(!(Implementation::IsInstanceExtension< + Extensions::KHR::get_physical_device_properties2, + Extensions::KHR::external_memory, /* not */ + Extensions::KHR::external_fence_capabilities>::value)); + CORRADE_VERIFY(!(Implementation::IsInstanceExtension< + Extensions::KHR::get_physical_device_properties2, + Extensions::KHR::external_memory_capabilities, + Extensions::KHR::external_fence>::value)); /* not */ } void ExtensionsTest::isExtension() { @@ -79,6 +98,25 @@ void ExtensionsTest::isExtension() { CORRADE_EXPECT_FAIL("GL/AL extensions are not rejected right now."); CORRADE_VERIFY(!Implementation::IsExtension::value); } + + /* Variadic check (used in variadic addEnabledExtensions()), check that it + properly fails for each occurence of a device extension */ + CORRADE_VERIFY((Implementation::IsExtension< + Extensions::KHR::external_memory, + Extensions::KHR::depth_stencil_resolve, + Extensions::KHR::external_fence>::value)); + CORRADE_VERIFY(!(Implementation::IsExtension< + Extensions::KHR::external_memory_capabilities, /* not */ + Extensions::KHR::depth_stencil_resolve, + Extensions::KHR::external_fence>::value)); + CORRADE_VERIFY(!(Implementation::IsExtension< + Extensions::KHR::external_memory, + Extensions::EXT::debug_report, /* not */ + Extensions::KHR::external_fence>::value)); + CORRADE_VERIFY(!(Implementation::IsExtension< + Extensions::KHR::external_memory, + Extensions::KHR::depth_stencil_resolve, + Extensions::KHR::external_fence_capabilities>::value)); /* not */ } void ExtensionsTest::constructInstanceExtensionFromCompileTimeExtension() { diff --git a/src/Magnum/Vk/TypeTraits.h b/src/Magnum/Vk/TypeTraits.h index 406c6fbad2..ed14c25cb9 100644 --- a/src/Magnum/Vk/TypeTraits.h +++ b/src/Magnum/Vk/TypeTraits.h @@ -38,8 +38,20 @@ namespace Implementation { /* Put into a separate header so APIs that need it don't need to pull in the whole Extensions.h. Tested in ExtensionsTest also. */ /** @todo filter out GL/AL extensions also */ - CORRADE_HAS_TYPE(IsInstanceExtension, decltype(T::InstanceIndex)); - CORRADE_HAS_TYPE(IsExtension, decltype(T::Index)); + + template class IsInstanceExtension; + CORRADE_HAS_TYPE(IsInstanceExtension, decltype(T::InstanceIndex)); + template class IsInstanceExtension { + /** @todo C++17: use &&... instead of all this */ + public: enum: bool { value = IsInstanceExtension::value && IsInstanceExtension::value }; + }; + + template class IsExtension; + CORRADE_HAS_TYPE(IsExtension, decltype(T::Index)); + template class IsExtension { + /** @todo C++17: use &&... instead of all this */ + public: enum: bool { value = IsExtension::value && IsExtension::value }; + }; } }} From b7b466d49aaee0010e0a096542d0ac7d1dd01e50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Jun 2020 12:21:48 +0200 Subject: [PATCH 13/85] Vk: parsing Version from configuration. Needed so I can implement --magnum-vulkan-version. --- src/Magnum/Vk/Test/VersionTest.cpp | 38 +++++++++++++++++++++++++++++- src/Magnum/Vk/Version.cpp | 25 ++++++++++++++++++++ src/Magnum/Vk/Version.h | 20 ++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Test/VersionTest.cpp b/src/Magnum/Vk/Test/VersionTest.cpp index dd7d8ebefe..87230dc579 100644 --- a/src/Magnum/Vk/Test/VersionTest.cpp +++ b/src/Magnum/Vk/Test/VersionTest.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include "Magnum/Magnum.h" @@ -41,6 +42,8 @@ struct VersionTest: TestSuite::Tester { void comparison(); void debug(); + + void configuration(); }; VersionTest::VersionTest() { @@ -48,7 +51,9 @@ VersionTest::VersionTest() { &VersionTest::packingMagnumVersion, &VersionTest::comparison, - &VersionTest::debug}); + &VersionTest::debug, + + &VersionTest::configuration}); } void VersionTest::packing() { @@ -128,6 +133,37 @@ void VersionTest::debug() { CORRADE_COMPARE(out.str(), "Vulkan 1.2 Vulkan 1.5.789 Vulkan 1023.1023.4095 20.6\n"); } +void VersionTest::configuration() { + Utility::Configuration c; + + /* The ideal thing */ + c.setValue("version", "1.1"); + CORRADE_COMPARE(c.value("version"), Version::Vk11); + + /* Errors */ + c.setValue("version", ""); + CORRADE_COMPARE(c.value("version"), Version::None); + c.setValue("version", "1"); + CORRADE_COMPARE(c.value("version"), Version::None); + c.setValue("version", "1."); + CORRADE_COMPARE(c.value("version"), Version::None); + c.setValue("version", ".1"); + CORRADE_COMPARE(c.value("version"), Version::None); + + /* Leading spaces */ + c.setValue("version", " 12. 5"); + CORRADE_COMPARE(c.value("version"), version(12, 5)); + + /* Trailing spaces */ + { + CORRADE_EXPECT_FAIL("Parsing of trailing spaces not implemented yet."); + c.setValue("version", "12 .5"); + CORRADE_COMPARE(c.value("version"), version(12, 5)); + c.setValue("version", "12.5 "); + CORRADE_COMPARE(c.value("version"), version(12, 5)); + } +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::VersionTest) diff --git a/src/Magnum/Vk/Version.cpp b/src/Magnum/Vk/Version.cpp index 1ae1dae7aa..d189a5320a 100644 --- a/src/Magnum/Vk/Version.cpp +++ b/src/Magnum/Vk/Version.cpp @@ -25,6 +25,8 @@ #include "Version.h" +#include +#include #include #include "Magnum/Vk/Result.h" @@ -49,3 +51,26 @@ Version enumerateInstanceVersion() { } }} + +namespace Corrade { namespace Utility { + +using namespace Magnum; + +Vk::Version ConfigurationValue::fromString(const Containers::StringView& stringValue, ConfigurationValueFlags) { + /** @todo trim the string first, once Utility::String::trim() works for + views */ + CORRADE_INTERNAL_ASSERT(stringValue.flags() & Containers::StringViewFlag::NullTerminated); + + char* end; + const UnsignedInt major = std::strtoull(stringValue.data(), &end, 10); + if(end == stringValue.begin() || end == stringValue.end() || *end != '.' || end + 1 == stringValue.end()) + return Vk::Version::None; + + const UnsignedInt minor = std::strtoull(end + 1, &end, 10); + if(end != stringValue.end()) + return Vk::Version::None; + + return Vk::version(major, minor); +} + +}} diff --git a/src/Magnum/Vk/Version.h b/src/Magnum/Vk/Version.h index 79fa57a373..09e665823e 100644 --- a/src/Magnum/Vk/Version.h +++ b/src/Magnum/Vk/Version.h @@ -147,4 +147,24 @@ MAGNUM_VK_EXPORT Version enumerateInstanceVersion(); }} +namespace Corrade { namespace Utility { + +/** +@configurationvalue{Magnum::Vk::Version} +@m_since_latest + +Parses a value in the form of `.`, returns +@ref Magnum::Vk::Version::None on error. Saving a value to string is not +implemented. +*/ +template<> struct ConfigurationValue { + ConfigurationValue() = delete; + + #ifndef DOXYGEN_GENERATING_OUTPUT + MAGNUM_VK_EXPORT static Magnum::Vk::Version fromString(const Containers::StringView& stringValue, ConfigurationValueFlags); + #endif +}; + +}} + #endif From 0392aeb878cf1ad10c13ae1f569f1dec15e03493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 21 Jun 2020 00:13:03 +0200 Subject: [PATCH 14/85] Vk: instance creation. --- doc/snippets/MagnumVk.cpp | 35 ++ doc/vulkan-mapping.dox | 22 +- src/Magnum/Vk/CMakeLists.txt | 9 +- src/Magnum/Vk/Extensions.cpp | 6 +- src/Magnum/Vk/Extensions.h | 3 +- src/Magnum/Vk/Implementation/Arguments.cpp | 49 ++ src/Magnum/Vk/Implementation/Arguments.h | 40 ++ .../Vk/Implementation/InstanceState.cpp | 32 ++ src/Magnum/Vk/Implementation/InstanceState.h | 42 ++ src/Magnum/Vk/Instance.cpp | 348 ++++++++++++ src/Magnum/Vk/Instance.h | 342 +++++++++++- src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/InstanceTest.cpp | 93 ++++ src/Magnum/Vk/Test/InstanceVkTest.cpp | 508 ++++++++++++++++++ src/Magnum/Vk/Vk.h | 2 + 15 files changed, 1524 insertions(+), 9 deletions(-) create mode 100644 src/Magnum/Vk/Implementation/Arguments.cpp create mode 100644 src/Magnum/Vk/Implementation/Arguments.h create mode 100644 src/Magnum/Vk/Implementation/InstanceState.cpp create mode 100644 src/Magnum/Vk/Implementation/InstanceState.h create mode 100644 src/Magnum/Vk/Instance.cpp create mode 100644 src/Magnum/Vk/Test/InstanceTest.cpp create mode 100644 src/Magnum/Vk/Test/InstanceVkTest.cpp diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index dcf1e6b2de..7f446cb49c 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -23,13 +23,48 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Integration.h" +#include "MagnumExternal/Vulkan/flextVkGlobal.h" using namespace Magnum; int main() { +{ +Vk::Instance instance; +/* [Instance-isExtensionEnabled] */ +if(instance.isExtensionEnabled()) { + // use the fancy debugging APIs +} else if(instance.isExtensionEnabled()) { + // use the non-fancy and deprecated debugging APIs +} else { + // well, tough luck +} +/* [Instance-isExtensionEnabled] */ +} + +{ +/* Header included again inside a function, but it's fine as the guards will + make it empty */ +/* [Instance-global-function-pointers] */ +#include + +// … + +Vk::Instance instance; +instance.populateGlobalFunctionPointers(); + +VkPhysicalDeviceGroupProperties properties[10]; +UnsignedInt count = Containers::arraySize(properties); +vkEnumeratePhysicalDeviceGroupsKHR(instance, &count, properties); +/* [Instance-global-function-pointers] */ +} + { /* [Integration] */ VkOffset2D a{64, 32}; diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 02ce1316f9..0d7724e865 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -136,7 +136,7 @@ Vulkan function | Matching API @fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | @fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | | @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | | -@fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | | +@fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor @fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | @fn_vk{CreatePipelineCache}, \n @fn_vk{DestroyPipelineCache} | | @fn_vk{CreatePipelineLayout}, \n @fn_vk{DestroyPipelineLayout} | | @@ -200,7 +200,7 @@ Vulkan function | Matching API @fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageSubresourceLayout} | | -@fn_vk{GetInstanceProcAddr} | | +@fn_vk{GetInstanceProcAddr} | @ref Instance constructor @fn_vk{GetPhysicalDeviceExternalBufferProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceExternalFenceProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceExternalSemaphoreProperties} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @@ -294,6 +294,24 @@ Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{WaitForFences} | | +@section vulkan-mapping-structures Info structures + +@subsection vulkan-mapping-structures-a A + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{ApplicationInfo} | @ref InstanceCreateInfo + +@subsection vulkan-mapping-structures-i I + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo + */ }} diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index c61905bf85..07246bb36c 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -29,8 +29,12 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS Extensions.cpp Handle.cpp + Instance.cpp Result.cpp - Version.cpp) + Version.cpp + + Implementation/Arguments.cpp + Implementation/InstanceState.cpp) set(MagnumVk_GracefulAssert_SRCS Enums.cpp @@ -55,6 +59,9 @@ set(MagnumVk_HEADERS visibility.h) set(MagnumVk_PRIVATE_HEADERS + Implementation/Arguments.h + Implementation/InstanceState.h + Implementation/compressedPixelFormatMapping.hpp Implementation/pixelFormatMapping.hpp Implementation/vertexFormatMapping.hpp) diff --git a/src/Magnum/Vk/Extensions.cpp b/src/Magnum/Vk/Extensions.cpp index b34631784d..3f610df63f 100644 --- a/src/Magnum/Vk/Extensions.cpp +++ b/src/Magnum/Vk/Extensions.cpp @@ -31,9 +31,9 @@ namespace Magnum { namespace Vk { namespace { -/* When adding a new list, InstanceExtension::extensions() needs to be adapted. - Binary search is performed on the extensions, thus they have to be sorted - alphabetically. */ +/* When adding a new list, InstanceExtension::extensions() and + Instance::initialize() needs to be adapted. Binary search is performed on + the extensions, thus they have to be sorted alphabetically. */ constexpr InstanceExtension InstanceExtensions[] { Extensions::EXT::debug_report{}, Extensions::EXT::debug_utils{}, diff --git a/src/Magnum/Vk/Extensions.h b/src/Magnum/Vk/Extensions.h index f35d26d0d6..557de7aafe 100644 --- a/src/Magnum/Vk/Extensions.h +++ b/src/Magnum/Vk/Extensions.h @@ -50,7 +50,8 @@ Each struct has the same public methods as the @ref InstanceExtension / @ref Extension class (@ref Extension::requiredVersion() "requiredVersion()", @ref Extension::coreVersion() "coreVersion()" and @ref Extension::string() "string()"), but these structs are better suited for compile-time decisions rather than -@ref Extension instances. +@ref Extension instances. See @ref Instance::isExtensionEnabled() for example +usage. This library is built if `WITH_VK` is enabled when building Magnum. To use this library with CMake, you need to request the `Vk` component of the `Magnum` diff --git a/src/Magnum/Vk/Implementation/Arguments.cpp b/src/Magnum/Vk/Implementation/Arguments.cpp new file mode 100644 index 0000000000..1b64c25f03 --- /dev/null +++ b/src/Magnum/Vk/Implementation/Arguments.cpp @@ -0,0 +1,49 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Arguments.h" + +#include + +namespace Magnum { namespace Vk { namespace Implementation { + +Utility::Arguments arguments() { + Utility::Arguments args{"magnum"}; + args.addOption("disable-layers").setHelp("disable-layers", "Vulkan layers to disable", "LIST") + .addOption("disable-extensions").setHelp("disable-extensions", "Vulkan extensions to disable", "LIST") + .addOption("enable-layers").setHelp("enable-layers", "Vulkan layers to enable in addition to the defaults and what the application requests", "LIST") + .addOption("enable-instance-extensions").setHelp("enable-instance-extensions", "Vulkan instance extensions to enable in addition to the defaults and what the application requests", "LIST") + .addOption("vulkan-version").setHelp("vulkan-version", "force Vulkan version", "X.Y") + .addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .setFromEnvironment("disable-layers") + .setFromEnvironment("disable-extensions") + .setFromEnvironment("enable-layers") + .setFromEnvironment("enable-instance-extensions") + .setFromEnvironment("vulkan-version") + .setFromEnvironment("log"); + return args; +} + +}}} diff --git a/src/Magnum/Vk/Implementation/Arguments.h b/src/Magnum/Vk/Implementation/Arguments.h new file mode 100644 index 0000000000..0c2fb36f0f --- /dev/null +++ b/src/Magnum/Vk/Implementation/Arguments.h @@ -0,0 +1,40 @@ +#ifndef Magnum_Vk_Implementation_Arguments_h +#define Magnum_Vk_Implementation_Arguments_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include + +#include "Magnum/Magnum.h" + +namespace Magnum { namespace Vk { namespace Implementation { + +/* Used by both InstanceCreateInfo and DeviceCreateInfo, each takes a subset + of the arguments */ +Utility::Arguments arguments(); + +}}} + +#endif diff --git a/src/Magnum/Vk/Implementation/InstanceState.cpp b/src/Magnum/Vk/Implementation/InstanceState.cpp new file mode 100644 index 0000000000..ffd0c0337d --- /dev/null +++ b/src/Magnum/Vk/Implementation/InstanceState.cpp @@ -0,0 +1,32 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "InstanceState.h" + +namespace Magnum { namespace Vk { namespace Implementation { + +InstanceState::InstanceState(Instance&, Int argc, const char** argv): argc{argc}, argv{argv} {} + +}}} diff --git a/src/Magnum/Vk/Implementation/InstanceState.h b/src/Magnum/Vk/Implementation/InstanceState.h new file mode 100644 index 0000000000..077464da64 --- /dev/null +++ b/src/Magnum/Vk/Implementation/InstanceState.h @@ -0,0 +1,42 @@ +#ifndef Magnum_Vk_Implementation_InstanceState_h +#define Magnum_Vk_Implementation_InstanceState_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { namespace Implementation { + +struct InstanceState { + explicit InstanceState(Instance& instance, Int argc, const char** argv); + + Int argc; + const char** argv; +}; + +}}} + +#endif diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp new file mode 100644 index 0000000000..ed033f3878 --- /dev/null +++ b/src/Magnum/Vk/Instance.cpp @@ -0,0 +1,348 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Instance.h" + +#include +#include +#include +#include +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" +#include "Magnum/Vk/Implementation/Arguments.h" +#include "Magnum/Vk/Implementation/InstanceState.h" +#include "MagnumExternal/Vulkan/flextVkGlobal.h" + +namespace Magnum { namespace Vk { + +struct InstanceCreateInfo::State { + Containers::String applicationName; + Containers::Array ownedStrings; + Containers::Array layers; + Containers::Array extensions; + + Containers::String disabledLayersStorage, disabledExtensionsStorage; + Containers::Array disabledLayers, disabledExtensions; + bool quietLog = false; + Version version = Version::None; + Int argc; + const char** argv; +}; + +InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, const LayerProperties* const layerProperties, const InstanceExtensionProperties* const extensionProperties, const Flags flags): _info{}, _applicationInfo{} { + Utility::Arguments args = Implementation::arguments(); + args.parse(argc, argv); + + if(args.value("log") == "quiet") + _state.emplace().quietLog = true; + if(argc && argv) { + if(!_state) _state.emplace(); + _state->argc = argc; + _state->argv = argv; + } + + _info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + /** @todo filter out magnum-specific flags once there are any */ + _info.flags = VkInstanceCreateFlags(flags); + _info.pApplicationInfo = &_applicationInfo; + _applicationInfo.pEngineName = "Magnum"; + /** @todo magnum version? 2020 can't fit into Vulkan's version + representation, sigh */ + + /* If there's a forced Vulkan version, use that, otherwise use the reported + instance version */ + Containers::StringView version = args.value("vulkan-version"); + if(!version.isEmpty()) { + if(!_state) _state.emplace(); + + if((_state->version = args.value("vulkan-version")) == Version::None) + Warning{} << "Invalid --magnum-vulkan-version" << args.value("vulkan-version") << Debug::nospace << ", ignoring"; + } + if(_state && _state->version == Version::None) + _state->version = enumerateInstanceVersion(); + _applicationInfo.apiVersion = UnsignedInt(_state ? _state->version : enumerateInstanceVersion()); + + /* If there are any disabled layers or extensions, sort them and save for + later -- we'll use them to filter the ones added by the app */ + Containers::String disabledLayers = args.value("disable-layers"); + Containers::String disabledExtensions = args.value("disable-extensions"); + if(!disabledLayers.isEmpty()) { + if(!_state) _state.emplace(); + + _state->disabledLayersStorage = std::move(disabledLayers); + _state->disabledLayers = Containers::StringView{_state->disabledLayersStorage}.splitWithoutEmptyParts(); + std::sort(_state->disabledLayers.begin(), _state->disabledLayers.end()); + } + if(!disabledExtensions.isEmpty()) { + if(!_state) _state.emplace(); + + _state->disabledExtensionsStorage = std::move(disabledExtensions); + _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); + std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); + } + + /* Add all layers and extensions enabled on command-line. The blacklist is + applied on those as well. */ + /** @todo use a generator split() so we can avoid the growing allocation + of the output array */ + /** @todo unfortunately even though the split and value retrieval is mostly + allocation-free, the strings will be turned into owning copies because + none of them is null-terminated or global -- could be a better idea to + just grow one giant string internally (once we have growable strings) */ + addEnabledLayers(args.value("enable-layers").splitWithoutEmptyParts()); + addEnabledExtensions(args.value("enable-instance-extensions").splitWithoutEmptyParts()); + + /** @todo use this (enabling debug layers etc.) */ + static_cast(layerProperties); + static_cast(extensionProperties); +} + +InstanceCreateInfo::InstanceCreateInfo(NoInitT) noexcept {} + +InstanceCreateInfo::InstanceCreateInfo(const VkInstanceCreateInfo& info): _info{info} {} + +InstanceCreateInfo::~InstanceCreateInfo() = default; + +InstanceCreateInfo& InstanceCreateInfo::setApplicationInfo(const Containers::StringView name, const Version version) { + /* Keep an owned copy of the name if it's not global / null-terminated; + Use nullptr if the view is empty */ + if(!name.isEmpty()) { + if(!_state) _state.emplace(); + + _state->applicationName = Containers::String::nullTerminatedGlobalView(name); + _applicationInfo.pApplicationName = _state->applicationName.data(); + } else { + if(_state) _state->applicationName = nullptr; + _applicationInfo.pApplicationName = nullptr; + } + + _applicationInfo.applicationVersion = UnsignedInt(version); + return *this; +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const Containers::ArrayView layers) { + if(layers.empty()) return *this; + if(!_state) _state.emplace(); + + /* Add null-terminated strings to the layer array */ + arrayReserve(_state->layers, _state->layers.size() + layers.size()); + for(const Containers::StringView layer: layers) { + /* If the layer is blacklisted, skip it */ + if(std::binary_search(_state->disabledLayers.begin(), _state->disabledLayers.end(), layer)) continue; + + /* Keep an owned *allocated* copy of the string if it's not global or + null-terminated -- ideally, if people use string view literals, + those will be, so this won't allocate. Allocated so the pointers + don't get invalidated when the array gets reallocated. */ + const char* data; + if(!(layer.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) + data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, + Containers::AllocatedInit, layer).data(); + else data = layer.data(); + + arrayAppend(_state->layers, data); + } + + /* Update the layer count, re-route the pointer to the layers array in case + it got reallocated */ + _info.enabledLayerCount = _state->layers.size(); + _info.ppEnabledLayerNames = _state->layers.data(); + return *this; +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledLayers(const std::initializer_list layers) { + return addEnabledLayers(Containers::arrayView(layers)); +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { + if(extensions.empty()) return *this; + if(!_state) _state.emplace(); + + /* Add null-terminated strings to the extension array */ + arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); + for(const Containers::StringView extension: extensions) { + /* If the extension is blacklisted, skip it */ + if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension)) continue; + + /* Keep an owned *allocated* copy of the string if it's not global or + null-terminated -- ideally, if people use string view literals, + those will be, so this won't allocate. Allocated so the pointers + don't get invalidated when the array gets reallocated. */ + const char* data; + if(!(extension.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) + data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, + Containers::AllocatedInit, extension).data(); + else data = extension.data(); + + arrayAppend(_state->extensions, data); + } + + /* Update the extension count, re-route the pointer to the layers array in + case it got reallocated */ + _info.enabledExtensionCount = _state->extensions.size(); + _info.ppEnabledExtensionNames = _state->extensions.data(); + return *this; +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { + return addEnabledExtensions(Containers::arrayView(extensions)); +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { + if(extensions.empty()) return *this; + if(!_state) _state.emplace(); + + arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); + for(const InstanceExtension& extension: extensions) { + /* If the extension is blacklisted, skip it */ + if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension.string())) continue; + + arrayAppend(_state->extensions, extension.string().data()); + } + + /* Update the extension count, re-route the pointer to the layers array in + case it got reallocated */ + _info.enabledExtensionCount = _state->extensions.size(); + _info.ppEnabledExtensionNames = _state->extensions.data(); + return *this; +} + +InstanceCreateInfo& InstanceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { + return addEnabledExtensions(Containers::arrayView(extensions)); +} + +Instance Instance::wrap(const VkInstance handle, const Version version, const Containers::ArrayView enabledExtensions, const HandleFlags flags) { + /* Compared to the constructor nothing is printed here as it would be just + repeating what was passed to the constructor */ + Instance out{NoCreate}; + out._handle = handle; + out._flags = flags; + out.initializeExtensions(enabledExtensions); + out.initialize(version, 0, nullptr); + return out; +} + +Instance Instance::wrap(const VkInstance handle, const Version version, const std::initializer_list enabledExtensions, const HandleFlags flags) { + return wrap(handle, version, Containers::arrayView(enabledExtensions), flags); +} + +Instance::Instance(const InstanceCreateInfo& info): _flags{HandleFlag::DestroyOnDestruction} { + const Version version = info._state && info._state->version != Version::None ? info._state->version : enumerateInstanceVersion(); + + /* Print all enabled layers and extensions if we're not told to be quiet */ + if(!info._state || !info._state->quietLog) { + Debug{} << "Instance version:" << version; + + if(info->enabledLayerCount) { + Debug{} << "Enabled layers:"; + for(std::size_t i = 0, max = info->enabledLayerCount; i != max; ++i) + Debug{} << " " << info->ppEnabledLayerNames[i]; + } + + if(info->enabledExtensionCount) { + Debug{} << "Enabled instance extensions:"; + for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i) + Debug{} << " " << info->ppEnabledExtensionNames[i]; + } + } + + MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkCreateInstance(info, nullptr, &_handle)); + + initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); + if(info._state) + initialize(version, info._state->argc, info._state->argv); + else + initialize(version, 0, nullptr); +} + +Instance::Instance(NoCreateT): _handle{}, _functionPointers{} {} + +Instance::Instance(Instance&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} { + other._handle = nullptr; + other._functionPointers = {}; +} + +Instance::~Instance() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + _functionPointers.DestroyInstance(_handle, nullptr); +} + +Instance& Instance::operator=(Instance&& other) noexcept { + using std::swap; + swap(other._handle, _handle); + swap(other._flags, _flags); + swap(other._version, _version); + swap(other._extensionStatus, _extensionStatus); + swap(other._state, _state); + swap(other._functionPointers, _functionPointers); + return *this; +} + +template void Instance::initializeExtensions(const Containers::ArrayView enabledExtensions) { + /* Mark all known extensions as enabled */ + for(const T extension: enabledExtensions) { + for(Containers::ArrayView knownExtensions: { + InstanceExtension::extensions(Version::None), + /*InstanceExtension::extensions(Version::Vk10), is empty */ + InstanceExtension::extensions(Version::Vk11), + /*InstanceExtension::extensions(Version::Vk12) is empty */ + }) { + auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const InstanceExtension& a, const T& b) { + return a.string() < static_cast(b); + }); + if(found->string() != extension) continue; + _extensionStatus.set(found->index(), true); + } + } +} + +void Instance::initialize(const Version version, const Int argc, const char** const argv) { + /* Init version, function pointers */ + _version = version; + flextVkInitInstance(_handle, &_functionPointers); + + /* Set up extension-dependent functionality */ + _state.emplace(*this, argc, argv); +} + +VkInstance Instance::release() { + const VkInstance handle = _handle; + _handle = nullptr; + return handle; +} + +bool Instance::isExtensionEnabled(const InstanceExtension& extension) const { + return _extensionStatus[extension.index()]; +} + +void Instance::populateGlobalFunctionPointers() { + flextVkInstance = _functionPointers; +} + +}} diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 77024c35ab..352592d0bf 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -26,18 +26,356 @@ */ /** @file - * @brief Nothing, haha + * @brief Class @ref Magnum::Vk::InstanceCreateInfo, @ref Magnum::Vk::Instance * @m_since_latest */ -#include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Math/BoolVector.h" +#include "Magnum/Vk/TypeTraits.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" namespace Magnum { namespace Vk { namespace Implementation { enum: std::size_t { InstanceExtensionCount = 16 }; + + struct InstanceState; } +/** +@brief Instance creation info +@m_since_latest + +Wraps a @type_vk_keyword{InstanceCreateInfo} and +@type_vk_keyword{ApplicationInfo}. +@see @ref Instance +*/ +class MAGNUM_VK_EXPORT InstanceCreateInfo { + public: + /** + * @brief Instance creation flag + * + * Wraps @type_vk_keyword{InstanceCreateFlagBits}. + * @see @ref Flags, @ref InstanceCreateInfo(Int, const char**, const LayerProperties*, const InstanceExtensionProperties*, Flags) + */ + enum class Flag: UnsignedInt {}; + + /** + * @brief Instance creation flags + * + * Type-safe wrapper for @type_vk_keyword{InstanceCreateFlags}. + * @see @ref InstanceCreateInfo(Int, const char**, const LayerProperties*, const InstanceExtensionProperties*, Flags) + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param argc Command-line argument count. Can be @cpp 0 @ce. + * @param argv Command-line argument values. Can be + * @cpp nullptr @ce. If set, is expected to stay in scope for the + * whole instance lifetime. + * @param layerProperties Existing @ref LayerProperties instance for + * querying available Vulkan layers. If @cpp nullptr @ce, a new + * instance may be created internally if needed. + * @param extensionProperties Existing @ref InstanceExtensionProperties + * instance for querying available Vulkan extensions. If + * @cpp nullptr @ce, a new instance may be created internally if + * needed. + * @param flags Instance creation flags + * + * The following @type_vk{InstanceCreateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `pApplicationInfo` + * - @cpp pApplicationInfo->apiVersion @ce to + * @ref enumerateInstanceVersion() + * - @cpp pApplicationInfo->engineName @ce to @cpp "Magnum" @ce + */ + explicit InstanceCreateInfo(Int argc, const char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* const extensionProperties, Flags flags = {}); + + /** @overload */ + explicit InstanceCreateInfo(Int argc, char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, const_cast(argv), layerProperties, extensionProperties, flags} {} + + /** @overload */ + explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, static_cast(argv), layerProperties, extensionProperties, flags} {} + + /** @overload */ + explicit InstanceCreateInfo(Int argc, const char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + + /** @overload */ + explicit InstanceCreateInfo(Int argc, char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + + /** @overload */ + explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + + /** @overload */ + explicit InstanceCreateInfo(Flags flags = {}): InstanceCreateInfo{0, nullptr, flags} {} + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit InstanceCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit InstanceCreateInfo(const VkInstanceCreateInfo& info); + + ~InstanceCreateInfo(); + + /** + * @brief Set application info + * @return Reference to self (for method chaining) + * + * Use the @ref version() helper to create the @p version value. The + * name is @cpp nullptr @ce by default. + */ + InstanceCreateInfo& setApplicationInfo(Containers::StringView name, Version version); + + /** + * @brief Add enabled layers + * @return Reference to self (for method chaining) + * + * All listed layers are expected be supported, use + * @ref LayerProperties::isSupported() to check for their presence. + * + * The function makes copies of string views that are not owning or + * null-terminated, use the @link Containers::Literals::operator""_s() @endlink + * literal to prevent that where possible. + */ + InstanceCreateInfo& addEnabledLayers(Containers::ArrayView layers); + /** @overload */ + InstanceCreateInfo& addEnabledLayers(std::initializer_list layers); + + /** + * @brief Add enabled instance extensions + * @return Reference to self (for method chaining) + * + * All listed extensions are expected to be supported either globally + * or in at least one of the enabled layers, use + * @ref InstanceExtensionProperties::isSupported() to check for their + * presence. + * + * The function makes copies of string views that are not owning or + * null-terminated, use the @link Containers::Literals::operator""_s() @endlink + * literal to prevent that where possible. + */ + InstanceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + /** @overload */ + InstanceCreateInfo& addEnabledExtensions(std::initializer_list extension); + /** @overload */ + InstanceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + /** @overload */ + InstanceCreateInfo& addEnabledExtensions(std::initializer_list extension); + /** @overload */ + template InstanceCreateInfo& addEnabledExtensions() { + static_assert(Implementation::IsInstanceExtension::value, "expected only Vulkan instance extensions"); + return addEnabledExtensions({E{}...}); + } + + /** @brief Underlying @type_vk{InstanceCreateInfo} structure */ + VkInstanceCreateInfo& operator*() { return _info; } + /** @overload */ + const VkInstanceCreateInfo& operator*() const { return _info; } + /** @overload */ + VkInstanceCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkInstanceCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkInstanceCreateInfo*() const { return &_info; } + + private: + friend Instance; + + VkInstanceCreateInfo _info; + VkApplicationInfo _applicationInfo; + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(InstanceCreateInfo::Flags) + +/** +@brief Instance +@m_since_latest + +Wraps a @type_vk_keyword{Instance} and stores instance-specific Vulkan function +pointers. +@see @ref vulkan-wrapping +*/ +class MAGNUM_VK_EXPORT Instance { + public: + /** + * @brief Wrap existing Vulkan handle + * @param handle The @type_vk{Instance} handle + * @param version Vulkan version that's assumed to be used on the + * instance + * @param enabledExtensions Extensions that are assumed to be enabled + * on the instance + * @param flags Handle flags + * + * The @p handle is expected to be of an existing Vulkan instance. The + * @p version and @p enabledExtensions parameters populate internal + * info about supported version and extensions and will be reflected in + * @ref isVersionSupported() and @ref isExtensionEnabled(), among other + * things. If @p enabledExtensions empty, the instance will behave as + * if no extensions were enabled. + * + * Unlike an instance created using a constructor, the Vulkan instance + * is by default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Instance wrap(VkInstance handle, Version version, Containers::ArrayView enabledExtensions, HandleFlags flags = {}); + + /** @overload */ + static Instance wrap(VkInstance handle, Version version, std::initializer_list enabledExtensions, HandleFlags flags = {}); + + /** + * @brief Constructor + * + * @see @fn_vk_keyword{CreateInstance} + */ + explicit Instance(const InstanceCreateInfo& info = InstanceCreateInfo{}); + + /** + * @brief Construct without creating the instance + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Instance(NoCreateT); + + /** @brief Copying is not allowed */ + Instance(const Instance&) = delete; + + /** @brief Move constructor */ + Instance(Instance&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Instance} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{DestroyInstance}, @ref release() + */ + ~Instance(); + + /** @brief Copying is not allowed */ + Instance& operator=(const Instance&) = delete; + + /** @brief Move assignment */ + Instance& operator=(Instance&& other) noexcept; + + /** @brief Underlying @type_vk{Instance} handle */ + VkInstance handle() { return _handle; } + /** @overload */ + operator VkInstance() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** @brief Instance version */ + Version version() const { return _version; } + + /** @brief Whether given version is supported on the instance */ + bool isVersionSupported(Version version) const { + return _version >= version; + } + + /** + * @brief Whether given extension is enabled + * + * Accepts instance extensions from the @ref Extensions namespace, + * listed also in the @ref vulkan-support "Vulkan support tables". + * Search complexity is @f$ \mathcal{O}(1) @f$. Example usage: + * + * @snippet MagnumVk.cpp Instance-isExtensionEnabled + * + * Note that this returns @cpp true @ce only if given extension is + * supported by the driver *and* it was enabled in + * @ref InstanceCreateInfo when creating the @ref Instance. For + * querying extension support before creating an instance use + * @ref InstanceExtensionProperties::isSupported(). + */ + template bool isExtensionEnabled() const { + static_assert(Implementation::IsInstanceExtension::value, "expected a Vulkan instance extension"); + return _extensionStatus[E::InstanceIndex]; + } + + /** @overload */ + bool isExtensionEnabled(const InstanceExtension& extension) const; + + /** + * @brief Instance-specific Vulkan function pointers + * + * Function pointers are implicitly stored per-instance, use + * @ref populateGlobalFunctionPointers() to populate the global `vk*` + * functions. + */ + const FlextVkInstance& operator*() const { return _functionPointers; } + /** @overload */ + const FlextVkInstance* operator->() const { return &_functionPointers; } + + /** + * @brief Release the underlying Vulkan instance + * + * Releases ownership of the Vulkan instance and returns its handle so + * @fn_vk{DestroyInstance} is not called on destruction. The internal + * state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkInstance release(); + + /** + * @brief Populate global instance-level function pointers to be used with third-party code + * + * Populates instance-level global function pointers so third-party + * code is able to call global instance-level `vk*` functions: + * + * @snippet MagnumVk.cpp Instance-global-function-pointers + * + * @attention This operation is changing global state. You need to + * ensure that this function is not called simultaenously from + * multiple threads and code using those function points is + * calling them with the same instance as the one returned by + * @ref handle(). + */ + void populateGlobalFunctionPointers(); + + #ifdef DOXYGEN_GENERATING_OUTPUT + private: + #endif + Implementation::InstanceState& state() { return *_state; } + + private: + template MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView enabledExtensions); + MAGNUM_VK_LOCAL void initialize(Version version, Int argc, const char** argv); + + VkInstance _handle; + HandleFlags _flags; + Version _version; + Math::BoolVector _extensionStatus; + Containers::Pointer _state; + + /* This member is bigger than you might think */ + FlextVkInstance _functionPointers; +}; + }} #endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index c41e2585e8..d49d3b4748 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -28,6 +28,7 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) @@ -36,5 +37,6 @@ corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/InstanceTest.cpp b/src/Magnum/Vk/Test/InstanceTest.cpp new file mode 100644 index 0000000000..d42be6ffa3 --- /dev/null +++ b/src/Magnum/Vk/Test/InstanceTest.cpp @@ -0,0 +1,93 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Instance.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct InstanceTest: TestSuite::Tester { + explicit InstanceTest(); + + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +InstanceTest::InstanceTest() { + addTests({&InstanceTest::createInfoConstructNoInit, + &InstanceTest::createInfoConstructFromVk, + + &InstanceTest::constructNoCreate, + &InstanceTest::constructCopy}); +} + +void InstanceTest::createInfoConstructNoInit() { + InstanceCreateInfo info; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) InstanceCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void InstanceTest::createInfoConstructFromVk() { + VkInstanceCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + InstanceCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void InstanceTest::constructNoCreate() { + { + Instance instance{NoCreate}; + CORRADE_VERIFY(!instance.handle()); + /* Instance function pointers should be null */ + CORRADE_VERIFY(!instance->CreateDevice); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void InstanceTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::InstanceTest) diff --git a/src/Magnum/Vk/Test/InstanceVkTest.cpp b/src/Magnum/Vk/Test/InstanceVkTest.cpp new file mode 100644 index 0000000000..672b358296 --- /dev/null +++ b/src/Magnum/Vk/Test/InstanceVkTest.cpp @@ -0,0 +1,508 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" + +#include "MagnumExternal/Vulkan/flextVkGlobal.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct InstanceVkTest: TestSuite::Tester { + explicit InstanceVkTest(); + + void createInfoConstructDefault(); + void createInfoApplicationInfo(); + void createInfoLayers(); + void createInfoExtensions(); + void createInfoCopiedStrings(); + + void construct(); + void constructLayerExtension(); + void constructCommandLineDisable(); + void constructCommandLineEnable(); + void constructMove(); + void constructUnknownLayer(); + void constructUnknownExtension(); + void wrap(); + void populateGlobalFunctionPointers(); +}; + +struct { + const char* nameDisable; + const char* nameEnable; + Containers::Array argsDisable, argsEnable; + bool driverVersionSupported, debugReportEnabled, validationFeaturesEnabled; + const char* log; +} ConstructCommandLineData[] { + /* Shouldn't print anything about version, enabled layers/exts if quiet + output is enabled. */ + {"quiet", "quiet, enabled layer + both extensions", + Containers::array({"", "--magnum-log", "quiet"}), + Containers::array({"", "--magnum-log", "quiet", + "--magnum-enable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}), + true, true, true, + ""}, + {"", "enabled layer + both extensions", nullptr, + Containers::array({"", + "--magnum-enable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}), + true, true, true, + "Instance version: Vulkan {}.{}{}\n" + "Enabled layers:\n" + " VK_LAYER_KHRONOS_validation\n" + "Enabled instance extensions:\n" + " VK_EXT_debug_report\n" + " VK_EXT_validation_features\n"}, + {"forced invalid version", "forced invalid version, enabled layer + both extensions", + Containers::array({"", "--magnum-vulkan-version", "eh"}), + Containers::array({"", "--magnum-vulkan-version", "eh", + "--magnum-enable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}), + true, true, true, + "Invalid --magnum-vulkan-version eh, ignoring\n" + "Instance version: Vulkan {}.{}{}\n" + "Enabled layers:\n" + " VK_LAYER_KHRONOS_validation\n" + "Enabled instance extensions:\n" + " VK_EXT_debug_report\n" + " VK_EXT_validation_features\n"}, + {"forced version", "forced version, enabled layer + both extensions", + Containers::array({"", "--magnum-vulkan-version", "1.0"}), + Containers::array({"", "--magnum-vulkan-version", "1.0", + "--magnum-enable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-enable-instance-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}), + false, true, true, + "Instance version: Vulkan 1.0\n" + "Enabled layers:\n" + " VK_LAYER_KHRONOS_validation\n" + "Enabled instance extensions:\n" + " VK_EXT_debug_report\n" + " VK_EXT_validation_features\n"}, + {"disabled layer + layer-only extension", "enabled extension", + Containers::array({"", + "--magnum-disable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-disable-extensions", "VK_EXT_validation_features"}), + Containers::array({"", + "--magnum-enable-instance-extensions", "VK_EXT_debug_report"}), + true, true, false, + "Instance version: Vulkan {}.{}{}\n" + "Enabled instance extensions:\n" + " VK_EXT_debug_report\n"}, + {"disabled extension", "enabled layer + one extension", + Containers::array({"", + "--magnum-disable-extensions", "VK_EXT_debug_report"}), + Containers::array({"", + "--magnum-enable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-enable-instance-extensions", "VK_EXT_validation_features"}), + true, false, true, + "Instance version: Vulkan {}.{}{}\n" + "Enabled layers:\n" + " VK_LAYER_KHRONOS_validation\n" + "Enabled instance extensions:\n" + " VK_EXT_validation_features\n"}, + {"disabled extensions + layer", "", + Containers::array({"", + "--magnum-disable-layers", "VK_LAYER_KHRONOS_validation", + "--magnum-disable-extensions", "VK_EXT_debug_report VK_EXT_validation_features"}), + nullptr, + true, false, false, + "Instance version: Vulkan {}.{}{}\n"}, +}; + +InstanceVkTest::InstanceVkTest() { + addTests({&InstanceVkTest::createInfoConstructDefault, + &InstanceVkTest::createInfoApplicationInfo, + &InstanceVkTest::createInfoLayers, + &InstanceVkTest::createInfoExtensions, + &InstanceVkTest::createInfoCopiedStrings, + + &InstanceVkTest::construct, + &InstanceVkTest::constructLayerExtension}); + + addInstancedTests({&InstanceVkTest::constructCommandLineDisable, + &InstanceVkTest::constructCommandLineEnable}, + Containers::arraySize(ConstructCommandLineData)); + + addTests({&InstanceVkTest::constructMove, + &InstanceVkTest::constructUnknownLayer, + &InstanceVkTest::constructUnknownExtension, + &InstanceVkTest::wrap, + &InstanceVkTest::populateGlobalFunctionPointers}); +} + +using namespace Containers::Literals; + +void InstanceVkTest::createInfoConstructDefault() { + InstanceCreateInfo info; + CORRADE_VERIFY(info->sType); + CORRADE_VERIFY(!info->pNext); + CORRADE_VERIFY(!info->ppEnabledLayerNames); + CORRADE_COMPARE(info->enabledLayerCount, 0); + CORRADE_VERIFY(!info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 0); + + CORRADE_VERIFY(info->pApplicationInfo); + CORRADE_COMPARE(Version(info->pApplicationInfo->apiVersion), enumerateInstanceVersion()); + CORRADE_COMPARE(info->pApplicationInfo->applicationVersion, 0); + CORRADE_COMPARE(info->pApplicationInfo->engineVersion, 0); + CORRADE_COMPARE(info->pApplicationInfo->pEngineName, "Magnum"_s); +} + +void InstanceVkTest::createInfoApplicationInfo() { + Containers::StringView name = "Magnum::Vk::Test::InstanceVkTest"_s; + + InstanceCreateInfo info; + CORRADE_VERIFY(info->pApplicationInfo); + CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName); + CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{}); + + /* Setting an empty name should do nothing */ + info.setApplicationInfo({}, {}); + CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName); + CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{}); + + info.setApplicationInfo(name, version(0, 0, 1)); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->pApplicationInfo->pApplicationName), name.data()); + CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), version(0, 0, 1)); + + /* Setting an empty view should put nullptr back */ + info.setApplicationInfo({}, {}); + CORRADE_VERIFY(!info->pApplicationInfo->pApplicationName); + CORRADE_COMPARE(Version(info->pApplicationInfo->applicationVersion), Version{}); +} + +void InstanceVkTest::createInfoLayers() { + Containers::StringView layer = "VK_LAYER_KHRONOS_validation"_s; + Containers::StringView another = "VK_LAYER_this_doesnt_exist"_s; + + InstanceCreateInfo info; + CORRADE_VERIFY(!info->ppEnabledLayerNames); + CORRADE_COMPARE(info->enabledLayerCount, 0); + + info.addEnabledLayers({layer}); + CORRADE_VERIFY(info->ppEnabledLayerNames); + CORRADE_COMPARE(info->enabledLayerCount, 1); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledLayerNames[0]), layer.data()); + + info.addEnabledLayers({another, layer}); + CORRADE_COMPARE(info->enabledLayerCount, 3); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledLayerNames[0]), layer.data()); + CORRADE_COMPARE(static_cast(info->ppEnabledLayerNames[1]), another.data()); + CORRADE_COMPARE(static_cast(info->ppEnabledLayerNames[2]), layer.data()); +} + +void InstanceVkTest::createInfoExtensions() { + InstanceCreateInfo info; + CORRADE_VERIFY(!info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 0); + + info.addEnabledExtensions(); + CORRADE_VERIFY(info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 1); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[0]), + Extensions::KHR::external_fence_capabilities::string().data()); + + info.addEnabledExtensions( + {Extensions::KHR::external_semaphore_capabilities{}, + Extensions::KHR::get_physical_device_properties2{}}); + CORRADE_COMPARE(info->enabledExtensionCount, 3); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[0]), + Extensions::KHR::external_fence_capabilities::string().data()); + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[1]), + Extensions::KHR::external_semaphore_capabilities::string().data()); + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[2]), + Extensions::KHR::get_physical_device_properties2::string().data()); +} + +void InstanceVkTest::createInfoCopiedStrings() { + Containers::StringView globalButNotNullTerminated = "VK_LAYER_KHRONOS_validation3"_s.except(1); + Containers::String localButNullTerminated = Extensions::KHR::external_memory_capabilities::string(); + + InstanceCreateInfo info; + info.setApplicationInfo(localButNullTerminated, {}) + .addEnabledLayers({globalButNotNullTerminated}) + .addEnabledExtensions({localButNullTerminated}); + CORRADE_COMPARE(info->enabledLayerCount, 1); + CORRADE_COMPARE(info->enabledExtensionCount, 1); + + CORRADE_COMPARE(info->pApplicationInfo->pApplicationName, localButNullTerminated); + CORRADE_VERIFY(info->pApplicationInfo->pApplicationName != localButNullTerminated.data()); + + CORRADE_COMPARE(info->ppEnabledLayerNames[0], globalButNotNullTerminated); + CORRADE_VERIFY(info->ppEnabledLayerNames[0] != globalButNotNullTerminated.data()); + + CORRADE_COMPARE(info->ppEnabledExtensionNames[0], localButNullTerminated); + CORRADE_VERIFY(info->ppEnabledExtensionNames[0] != localButNullTerminated.data()); +} + +void InstanceVkTest::construct() { + { + Instance instance; + CORRADE_VERIFY(instance.handle()); + /* Instance function pointers should be populated */ + CORRADE_VERIFY(instance->CreateDevice); + CORRADE_COMPARE(instance.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(instance.version(), enumerateInstanceVersion()); + /* Instance version is supported */ + CORRADE_VERIFY(instance.isVersionSupported(enumerateInstanceVersion())); + CORRADE_VERIFY(!instance.isVersionSupported(Version::None)); + /* No extensions are enabled by default ... */ + CORRADE_VERIFY(!instance.isExtensionEnabled()); + /* ... and thus also no function pointers loaded */ + CORRADE_VERIFY(!instance->CreateDebugReportCallbackEXT); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void InstanceVkTest::constructLayerExtension() { + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) + CORRADE_SKIP("VK_EXT_debug_report not supported, can't test"); + + Instance instance{InstanceCreateInfo{} + .setApplicationInfo("InstanceVkTest", version(0, 0, 1)) + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}) + .addEnabledExtensions({ + Extensions::EXT::debug_report::string(), + "VK_EXT_validation_features"_s + })}; + CORRADE_VERIFY(instance.handle()); + + /* Extensions should be reported as enabled ... */ + CORRADE_VERIFY(instance.isExtensionEnabled()); + CORRADE_VERIFY(instance.isExtensionEnabled(Extensions::EXT::validation_features{})); + /* ... and function pointers loaded */ + CORRADE_VERIFY(instance->CreateDebugReportCallbackEXT); + /* no entrypoints to test for EXT_validation_features */ +} + +void InstanceVkTest::constructCommandLineDisable() { + auto&& data = ConstructCommandLineData[testCaseInstanceId()]; + setTestCaseDescription(data.nameDisable); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) + CORRADE_SKIP("VK_EXT_validation_features not supported, can't test"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Debug redirectOutput{&out}; + Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} + .setApplicationInfo("InstanceVkTest", version(0, 0, 1)) + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}) + .addEnabledExtensions()}; + CORRADE_VERIFY(instance.handle()); + CORRADE_COMPARE(instance.isVersionSupported(enumerateInstanceVersion()), data.driverVersionSupported); + CORRADE_COMPARE(instance.isExtensionEnabled(), data.debugReportEnabled); + CORRADE_COMPARE(instance.isExtensionEnabled(), data.validationFeaturesEnabled); + + /** @todo cleanup when Debug::toString() or some similar utility exists */ + UnsignedInt major = versionMajor(enumerateInstanceVersion()); + UnsignedInt minor = versionMinor(enumerateInstanceVersion()); + UnsignedInt patch = versionPatch(enumerateInstanceVersion()); + /* Vulkan 1.0 instances report no patch version, special-case that */ + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + + /* Verify that the entrypoint is actually (not) loaded as expected, to + avoid all the above reporting being just smoke & mirrors */ + CORRADE_COMPARE(!!instance->CreateDebugReportCallbackEXT, data.debugReportEnabled); +} + +void InstanceVkTest::constructCommandLineEnable() { + auto&& data = ConstructCommandLineData[testCaseInstanceId()]; + setTestCaseDescription(data.nameEnable); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) + CORRADE_SKIP("VK_EXT_validation_features not supported, can't test"); + + std::ostringstream out; + Warning redirectWarning{&out}; + Debug redirectOutput{&out}; + Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} + /* Nothing enabled by the application */ + }; + CORRADE_VERIFY(instance.handle()); + CORRADE_COMPARE(instance.isVersionSupported(enumerateInstanceVersion()), data.driverVersionSupported); + CORRADE_COMPARE(instance.isExtensionEnabled(), data.debugReportEnabled); + CORRADE_COMPARE(instance.isExtensionEnabled(), data.validationFeaturesEnabled); + + /** @todo cleanup when Debug::toString() or some similar utility exists */ + UnsignedInt major = versionMajor(enumerateInstanceVersion()); + UnsignedInt minor = versionMinor(enumerateInstanceVersion()); + UnsignedInt patch = versionPatch(enumerateInstanceVersion()); + /* Vulkan 1.0 instances report no patch version, special-case that */ + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + + /* Verify that the entrypoint is actually (not) loaded as expected, to + avoid all the above reporting being just smoke & mirrors */ + CORRADE_COMPARE(!!instance->CreateDebugReportCallbackEXT, data.debugReportEnabled); +} + +void InstanceVkTest::constructMove() { + InstanceExtensionProperties extensions = enumerateInstanceExtensionProperties(); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test"); + + Instance a{InstanceCreateInfo{} + .setApplicationInfo("InstanceVkTest", version(0, 0, 1)) + .addEnabledExtensions()}; + VkInstance handle = a.handle(); + Version version = a.version(); + CORRADE_VERIFY(handle); + CORRADE_VERIFY(version != Version{}); + + Instance b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.version(), version); + CORRADE_VERIFY(b.isExtensionEnabled()); + /* Function pointers in a are left in whatever state they were before, as + that doesn't matter */ + CORRADE_VERIFY(b->CreateDevice); + + Instance c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.version(), version); + CORRADE_VERIFY(c.isExtensionEnabled()); + /* Everything is swapped, including function pointers */ + CORRADE_VERIFY(!b->CreateDevice); + CORRADE_VERIFY(c->CreateDevice); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void InstanceVkTest::constructUnknownLayer() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + Instance instance{InstanceCreateInfo{} + .addEnabledLayers({"VK_LAYER_this_doesnt_exist"_s})}; + CORRADE_COMPARE(out.str(), "TODO"); +} + +void InstanceVkTest::constructUnknownExtension() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + Instance instance{InstanceCreateInfo{} + .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; + CORRADE_COMPARE(out.str(), "TODO"); +} + +void InstanceVkTest::wrap() { + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); + if(!properties.isSupported()) + CORRADE_SKIP("VK_EXT_debug_report not supported, can't test"); + if(!properties.isSupported()) + CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test"); + + InstanceCreateInfo info; + info.addEnabledExtensions< + Extensions::EXT::debug_report, + Extensions::KHR::get_physical_device_properties2>(); + + VkInstance instance; + CORRADE_COMPARE(Result(vkCreateInstance(info, nullptr, &instance)), Result::Success); + CORRADE_VERIFY(instance); + + { + /* Wrapping should load the basic function pointers */ + auto wrapped = Instance::wrap(instance, Version::Vk11, { + Extensions::EXT::debug_report::string() + }, HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(wrapped->DestroyInstance); + + /* Specified version should be reported as supported but higher not + regardless of the actual driver version */ + CORRADE_VERIFY(wrapped.isVersionSupported(Version::Vk11)); + CORRADE_VERIFY(!wrapped.isVersionSupported(Version::Vk12)); + + /* Listed extensions should be reported as enabled and function + pointers loaded as well */ + CORRADE_VERIFY(wrapped.isExtensionEnabled()); + CORRADE_VERIFY(wrapped->CreateDebugReportCallbackEXT); + + /* Unlisted not, but function pointers should still be loaded as the + actual instance does have the extension enabled */ + CORRADE_VERIFY(!wrapped.isExtensionEnabled()); + CORRADE_VERIFY(wrapped->GetPhysicalDeviceProperties2KHR); + + /* Releasing won't destroy anything ... */ + CORRADE_COMPARE(wrapped.release(), instance); + } + + /* ...so we can wrap it again, non-owned, and then destroy it manually */ + auto wrapped = Instance::wrap(instance, Version::Vk10, {}); + CORRADE_VERIFY(wrapped->DestroyInstance); + wrapped->DestroyInstance(instance, nullptr); +} + +void InstanceVkTest::populateGlobalFunctionPointers() { + vkDestroyInstance = nullptr; + + Instance instance; + CORRADE_VERIFY(!vkDestroyInstance); + instance.populateGlobalFunctionPointers(); + CORRADE_VERIFY(vkDestroyInstance); + CORRADE_VERIFY(vkDestroyInstance == instance->DestroyInstance); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::InstanceVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 0239685258..587ccef7af 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -38,6 +38,8 @@ class Extension; class ExtensionProperties; enum class HandleFlag: UnsignedByte; typedef Containers::EnumSet HandleFlags; +class Instance; +class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; From 0aeb1d7c6db05b8d7003afb265484e39d106dfef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 16 Jun 2020 17:29:16 +0200 Subject: [PATCH 15/85] package/archlinux: build Vulkan stuff on Android. --- package/archlinux/PKGBUILD-android-arm64 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/archlinux/PKGBUILD-android-arm64 b/package/archlinux/PKGBUILD-android-arm64 index ea91314917..65fb08c03c 100644 --- a/package/archlinux/PKGBUILD-android-arm64 +++ b/package/archlinux/PKGBUILD-android-arm64 @@ -33,6 +33,7 @@ build() { -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/opt/android-ndk/platforms/android-24/arch-arm64/usr \ -DMAGNUM_INCLUDE_INSTALL_PREFIX=/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr \ + -DWITH_VK=ON \ -DWITH_ANYAUDIOIMPORTER=OFF \ -DWITH_ANYIMAGECONVERTER=ON \ -DWITH_ANYIMAGEIMPORTER=ON \ @@ -47,6 +48,7 @@ build() { -DWITH_EGLCONTEXT=ON \ -DWITH_WINDOWLESSEGLAPPLICATION=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DTARGET_GLES2=OFF \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ From aa384a5d385784723e86b0b837cfd994c0e49157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Jun 2020 17:25:24 +0200 Subject: [PATCH 16/85] Vk: initial device enumeration. --- doc/vulkan-mapping.dox | 4 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/DeviceProperties.cpp | 123 ++++++++++ src/Magnum/Vk/DeviceProperties.h | 218 ++++++++++++++++++ .../Vk/Implementation/InstanceState.cpp | 14 +- src/Magnum/Vk/Implementation/InstanceState.h | 2 + src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/DevicePropertiesTest.cpp | 73 ++++++ src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 110 +++++++++ src/Magnum/Vk/Vk.h | 2 + 10 files changed, 547 insertions(+), 3 deletions(-) create mode 100644 src/Magnum/Vk/DeviceProperties.cpp create mode 100644 src/Magnum/Vk/DeviceProperties.h create mode 100644 src/Magnum/Vk/Test/DevicePropertiesTest.cpp create mode 100644 src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 0d7724e865..e9b986a10a 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -169,7 +169,7 @@ Vulkan function | Matching API @fn_vk{EnumerateInstanceExtensionProperties} | @ref enumerateInstanceExtensionProperties() @fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() -@fn_vk{EnumeratePhysicalDevices} | | +@fn_vk{EnumeratePhysicalDevices} | @ref enumerateDevices() @fn_vk{EnumeratePhysicalDeviceGroups} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @subsection vulkan-mapping-functions-f F @@ -208,7 +208,7 @@ Vulkan function | Matching API @fn_vk{GetPhysicalDeviceFormatProperties}, \n @fn_vk{GetPhysicalDeviceFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceMemoryProperties}, \n @fn_vk{GetPhysicalDeviceMemoryProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@fn_vk{GetPhysicalDeviceProperties}, \n @fn_vk{GetPhysicalDeviceProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceProperties}, \n @fn_vk{GetPhysicalDeviceProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties @fn_vk{GetPhysicalDeviceQueueFamilyProperties}, \n @fn_vk{GetPhysicalDeviceQueueFamilyProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceSparseImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceSparseImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPipelineCacheData} | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 07246bb36c..bde2ccd7f8 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + DeviceProperties.cpp Extensions.cpp Handle.cpp Instance.cpp @@ -43,6 +44,7 @@ set(MagnumVk_GracefulAssert_SRCS set(MagnumVk_HEADERS Device.h + DeviceProperties.h Enums.h Extensions.h ExtensionProperties.h diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp new file mode 100644 index 0000000000..bed65c26ec --- /dev/null +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -0,0 +1,123 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "DeviceProperties.h" + +#include +#include +#include + +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Implementation/InstanceState.h" + +namespace Magnum { namespace Vk { + +struct DeviceProperties::State { + VkPhysicalDeviceProperties2 properties{}; +}; + +DeviceProperties::DeviceProperties(NoCreateT) noexcept: _instance{}, _handle{} {} + +DeviceProperties::DeviceProperties(Instance& instance, VkPhysicalDevice handle): _instance{&instance}, _handle{handle} {} + +/* The VkDeviceProperties handle doesn't need to be destroyed so it's enough to + just rely on the implicit behavior */ +DeviceProperties::DeviceProperties(DeviceProperties&&) noexcept = default; + +DeviceProperties::~DeviceProperties() = default; + +DeviceProperties& DeviceProperties::operator=(DeviceProperties&&) noexcept = default; + +Containers::StringView DeviceProperties::name() { + return properties().properties.deviceName; +} + +const VkPhysicalDeviceProperties2& DeviceProperties::properties() { + if(!_state) _state.emplace(); + + /* Properties not fetched yet, do that now */ + if(!_state->properties.sType) { + _state->properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + + _instance->state().getPhysicalDevicePropertiesImplementation(*this, _state->properties); + } + + return _state->properties; +} + +void DeviceProperties::getPropertiesImplementationDefault(DeviceProperties& self, VkPhysicalDeviceProperties2& properties) { + return (**self._instance).GetPhysicalDeviceProperties(self._handle, &properties.properties); +} + +void DeviceProperties::getPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceProperties2& properties) { + return (**self._instance).GetPhysicalDeviceProperties2KHR(self._handle, &properties); +} + +void DeviceProperties::getPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceProperties2& properties) { + return (**self._instance).GetPhysicalDeviceProperties2(self._handle, &properties); +} + +Containers::Array enumerateDevices(Instance& instance) { + /* Retrieve total device count */ + UnsignedInt count; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->EnumeratePhysicalDevices(instance, &count, nullptr)); + + /* Allocate memory for the output, fetch the handles into it */ + Containers::Array out{Containers::NoInit, count}; + Containers::ArrayView handles{reinterpret_cast(out.data()), count}; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->EnumeratePhysicalDevices(instance, &count, handles.data())); + + /* Expect the device count didn't change between calls */ + CORRADE_INTERNAL_ASSERT(count == out.size()); + + /* Construct actual DeviceProperties instances from these, go backwards so + we don't overwrite the not-yet-processed handles */ + for(std::size_t i = count; i != 0; --i) + new(out.data() + i - 1) DeviceProperties{instance, handles[i - 1]}; + + return out; +} + +Debug& operator<<(Debug& debug, const DeviceType value) { + debug << "Vk::DeviceType" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::DeviceType::value: return debug << "::" << Debug::nospace << #value; + _c(Other) + _c(IntegratedGpu) + _c(DiscreteGpu) + _c(VirtualGpu) + _c(Cpu) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Vulkan docs have the values in decimal, so not converting to hex */ + return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; +} + +}} diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h new file mode 100644 index 0000000000..5357004073 --- /dev/null +++ b/src/Magnum/Vk/DeviceProperties.h @@ -0,0 +1,218 @@ +#ifndef Magnum_Vk_DeviceProperties_h +#define Magnum_Vk_DeviceProperties_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices() + * @m_since_latest + */ + +#include +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +namespace Implementation { struct InstanceState; } + +/** +@brief Physical device type +@m_since_latest + +Wraps a @type_vk_keyword{PhysicalDeviceType}. +@see @ref DeviceProperties::type() +@m_enum_values_as_keywords +*/ +enum class DeviceType: Int { + /** Anything that does not match any other available types */ + Other = VK_PHYSICAL_DEVICE_TYPE_OTHER, + + /** + * Typically a device embedded in or tightly coupled with the host + */ + IntegratedGpu = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, + + /** + * Typically a separate processor connected to the host via an + * interlink + */ + DiscreteGpu = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, + + /** Typically a virtual node in a virtualization environment */ + VirtualGpu = VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU, + + /** Typically running on the same processors as the host */ + Cpu = VK_PHYSICAL_DEVICE_TYPE_CPU +}; + +/** +@debugoperatorclassenum{DeviceProperties,DeviceType} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceType value); + +/** +@brief Physical device properties +@m_since_latest + +Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) +properties. +@see @ref enumeratePhysicalDevices() +*/ +class MAGNUM_VK_EXPORT DeviceProperties { + public: + /** + * @brief Wrap existing Vulkan physical device + * @param instance Vulkan instance + * @param handle The @type_vk{PhysicalDevice} handle + * + * The @p handle is expected to be originating from @p instance. Unlike + * with other handle types, the @type_vk{PhysicalDevice} handles don't + * have to be destroyed at the end. so there's no equivalent of e.g. + * @ref Instance::release() or @ref Instance::handleFlags(). + */ + static DeviceProperties wrap(Instance& instance, VkPhysicalDevice handle) { + return DeviceProperties{instance, handle}; + } + + /** + * @brief Construct without populating the contents + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit DeviceProperties(NoCreateT) noexcept; + + /** @brief Copying is not allowed */ + DeviceProperties(const DeviceProperties&) = delete; + + /** @brief Move constructor */ + DeviceProperties(DeviceProperties&&) noexcept; + + ~DeviceProperties(); + + /** @brief Copying is not allowed */ + DeviceProperties& operator=(const DeviceProperties&) = delete; + + /** @brief Move assignment */ + DeviceProperties& operator=(DeviceProperties&&) noexcept; + + /** @brief Underlying @type_vk{PhysicalDevice} handle */ + VkPhysicalDevice handle() { return _handle; } + /** @overload */ + operator VkPhysicalDevice() { return _handle; } + + /** + * @brief Raw device properties + * + * Populated lazily on first request. If Vulkan 1.1 or the + * @vk_extension{KHR,get_physical_device_properties2} extension is not + * enabled on the originating instance, only the Vulkan 1.0 subset of + * device properties is queried, with the `pNext` member being + * @cpp nullptr @ce. + * @see @fn_vk_keyword{GetPhysicalDeviceProperties2}, + * @fn_vk_keyword{GetPhysicalDeviceProperties} + */ + const VkPhysicalDeviceProperties2& properties(); + + /** + * @brief API version + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + Version apiVersion() { + return Version(properties().properties.apiVersion); + } + + /** + * @brief Driver version + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + Version driverVersion() { + return Version(properties().properties.driverVersion); + } + + /** + * @brief Device type + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + DeviceType type() { + return DeviceType(properties().properties.deviceType); + } + + /** + * @brief Device name + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + Containers::StringView name(); + + private: + friend Implementation::InstanceState; + + #ifndef DOXYGEN_GENERATING_OUTPUT + /* The DAMN THING lists this among friends, which is AN IMPLEMENTATION + DETAIL */ + friend MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance&); + #endif + + explicit DeviceProperties(Instance& instance, VkPhysicalDevice handle); + + MAGNUM_VK_LOCAL static void getPropertiesImplementationDefault(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); + MAGNUM_VK_LOCAL static void getPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); + MAGNUM_VK_LOCAL static void getPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); + + /* Can't be a reference because of the NoCreate constructor */ + Instance* _instance; + + VkPhysicalDevice _handle; + struct State; + Containers::Pointer _state; +}; + +/** +@brief Enumerate physical devices +@m_since_latest + +@see @fn_vk_keyword{EnumeratePhysicalDevices} +*/ +MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& instance); + +}} + +#endif diff --git a/src/Magnum/Vk/Implementation/InstanceState.cpp b/src/Magnum/Vk/Implementation/InstanceState.cpp index ffd0c0337d..5c151b5f48 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.cpp +++ b/src/Magnum/Vk/Implementation/InstanceState.cpp @@ -25,8 +25,20 @@ #include "InstanceState.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/DeviceProperties.h" + namespace Magnum { namespace Vk { namespace Implementation { -InstanceState::InstanceState(Instance&, Int argc, const char** argv): argc{argc}, argv{argv} {} +InstanceState::InstanceState(Instance& instance, Int argc, const char** argv): argc{argc}, argv{argv} { + if(instance.isVersionSupported(Version::Vk11)) { + getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementation11; + } else if(instance.isExtensionEnabled()) { + getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementationKHR; + } else { + getPhysicalDevicePropertiesImplementation = DeviceProperties::getPropertiesImplementationDefault; + } +} }}} diff --git a/src/Magnum/Vk/Implementation/InstanceState.h b/src/Magnum/Vk/Implementation/InstanceState.h index 077464da64..f746de43a0 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.h +++ b/src/Magnum/Vk/Implementation/InstanceState.h @@ -35,6 +35,8 @@ struct InstanceState { Int argc; const char** argv; + + void(*getPhysicalDevicePropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceProperties2&); }; }}} diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index d49d3b4748..9ec60218bb 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) @@ -35,6 +36,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp new file mode 100644 index 0000000000..3aab323d60 --- /dev/null +++ b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp @@ -0,0 +1,73 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include + +#include "Magnum/Vk/DeviceProperties.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DevicePropertiesTest: TestSuite::Tester { + explicit DevicePropertiesTest(); + + void constructNoCreate(); + void constructCopy(); + + void debugDeviceType(); +}; + +DevicePropertiesTest::DevicePropertiesTest() { + addTests({&DevicePropertiesTest::constructNoCreate, + &DevicePropertiesTest::constructCopy, + + &DevicePropertiesTest::debugDeviceType}); +} + +void DevicePropertiesTest::constructNoCreate() { + { + DeviceProperties properties{NoCreate}; + CORRADE_VERIFY(!properties.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void DevicePropertiesTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void DevicePropertiesTest::debugDeviceType() { + std::ostringstream out; + Debug{&out} << DeviceType::DiscreteGpu << DeviceType(-10007655); + CORRADE_COMPARE(out.str(), "Vk::DeviceType::DiscreteGpu Vk::DeviceType(-10007655)\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesTest) diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp new file mode 100644 index 0000000000..5e0b98ce95 --- /dev/null +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DevicePropertiesVkTest: TestSuite::Tester { + explicit DevicePropertiesVkTest(); + + void enumerate(); + void constructMove(); + void wrap(); + + Instance _instance; +}; + +DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { + addTests({&DevicePropertiesVkTest::enumerate, + &DevicePropertiesVkTest::constructMove, + &DevicePropertiesVkTest::wrap}); +} + +void DevicePropertiesVkTest::enumerate() { + Containers::Array devices = enumerateDevices(_instance); + Debug{} << "Found" << devices.size() << "devices"; + CORRADE_VERIFY(!devices.empty()); + + for(DeviceProperties& device: devices) { + CORRADE_ITERATION(device.name()); + + CORRADE_VERIFY(device.handle()); + CORRADE_COMPARE_AS(device.apiVersion(), Version::Vk10, + TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(device.driverVersion(), Version::Vk10, + TestSuite::Compare::GreaterOrEqual); + CORRADE_VERIFY(device.type() != DeviceType::Other); + CORRADE_VERIFY(!device.name().isEmpty()); + } +} + +void DevicePropertiesVkTest::constructMove() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + VkPhysicalDevice handle = devices[0].handle(); + Containers::StringView name = devices[0].name(); + + DeviceProperties a = std::move(devices[0]); + CORRADE_COMPARE(a.handle(), handle); + CORRADE_COMPARE(a.name(), name); + + DeviceProperties b = DeviceProperties::wrap(_instance, nullptr); + b = std::move(a); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.name(), name); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void DevicePropertiesVkTest::wrap() { + VkPhysicalDevice handle; + UnsignedInt count = 1; + auto result = Result(_instance->EnumeratePhysicalDevices(_instance, &count, &handle)); + { + /** @todo clean up once Compare::AnyOf exists */ + CORRADE_ITERATION(result); + CORRADE_VERIFY(result == Result::Success || result == Result::Incomplete); + } + + DeviceProperties wrapped = DeviceProperties::wrap(_instance, handle); + CORRADE_VERIFY(wrapped.handle()); + + Containers::Array devices = enumerateDevices(_instance); + CORRADE_COMPARE(wrapped.name(), devices[0].name()); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 587ccef7af..5fe7b1ca44 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -34,6 +34,8 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class DeviceProperties; +enum class DeviceType: Int; class Extension; class ExtensionProperties; enum class HandleFlag: UnsignedByte; From 6515c04be1bcfde9f13f89c8af0b65a583ed9e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Jun 2020 17:48:23 +0200 Subject: [PATCH 17/85] Vk: enable KHR_get_physical_device_properties by default on Vulkan 1.0. --- src/Magnum/Vk/Instance.cpp | 25 +++++++++++++++++++++---- src/Magnum/Vk/Instance.h | 17 ++++++++++++++++- src/Magnum/Vk/Test/InstanceTest.cpp | 3 ++- src/Magnum/Vk/Test/InstanceVkTest.cpp | 27 +++++++++++++++++++++++---- 4 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index ed033f3878..eaece5549e 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -26,12 +26,14 @@ #include "Instance.h" #include +#include #include #include #include #include #include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" @@ -55,7 +57,7 @@ struct InstanceCreateInfo::State { const char** argv; }; -InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, const LayerProperties* const layerProperties, const InstanceExtensionProperties* const extensionProperties, const Flags flags): _info{}, _applicationInfo{} { +InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, const LayerProperties* const layerProperties, const InstanceExtensionProperties* extensionProperties, const Flags flags): _info{}, _applicationInfo{} { Utility::Arguments args = Implementation::arguments(); args.parse(argc, argv); @@ -68,8 +70,7 @@ InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, } _info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - /** @todo filter out magnum-specific flags once there are any */ - _info.flags = VkInstanceCreateFlags(flags); + _info.flags = VkInstanceCreateFlags(flags & ~Flag::NoImplicitExtensions); _info.pApplicationInfo = &_applicationInfo; _applicationInfo.pEngineName = "Magnum"; /** @todo magnum version? 2020 can't fit into Vulkan's version @@ -120,7 +121,23 @@ InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, /** @todo use this (enabling debug layers etc.) */ static_cast(layerProperties); - static_cast(extensionProperties); + + /* Enable implicit extensions unless that's forbidden */ + /** @todo move this somewhere else as this will grow significantly? */ + if(!(flags & Flag::NoImplicitExtensions)) { + if(!_state) _state.emplace(); + + /* Fetch searchable extension properties if not already */ + Containers::Optional extensionPropertiesStorage; + if(!extensionProperties) { + extensionPropertiesStorage = enumerateInstanceExtensionProperties(); + extensionProperties = &*extensionPropertiesStorage; + } + + /* Only if we don't have Vulkan 1.1, on which this is core */ + if(_state->version < Version::Vk11 && extensionProperties->isSupported()) + addEnabledExtensions(); + } } InstanceCreateInfo::InstanceCreateInfo(NoInitT) noexcept {} diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 352592d0bf..f04356a9c0 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -63,7 +63,22 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * Wraps @type_vk_keyword{InstanceCreateFlagBits}. * @see @ref Flags, @ref InstanceCreateInfo(Int, const char**, const LayerProperties*, const InstanceExtensionProperties*, Flags) */ - enum class Flag: UnsignedInt {}; + enum class Flag: UnsignedInt { + /* Any magnum-specific flags added here have to be filtered out + when passing them to _info.flags in the constructor. Using the + highest bits in a hope to prevent conflicts with Vulkan instance + flags added in the future. */ + + /** + * Don't implicitly enable any extensions. + * + * By default, the engine enables various extensions such as + * @vk_extension{KHR,get_physical_device_properties2} to provide a + * broader functionality. If you want to have a complete control + * over what gets enabled, set this flag. + */ + NoImplicitExtensions = 1u << 31 + }; /** * @brief Instance creation flags diff --git a/src/Magnum/Vk/Test/InstanceTest.cpp b/src/Magnum/Vk/Test/InstanceTest.cpp index d42be6ffa3..8b5c8016da 100644 --- a/src/Magnum/Vk/Test/InstanceTest.cpp +++ b/src/Magnum/Vk/Test/InstanceTest.cpp @@ -49,7 +49,8 @@ InstanceTest::InstanceTest() { } void InstanceTest::createInfoConstructNoInit() { - InstanceCreateInfo info; + InstanceCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; new(&info) InstanceCreateInfo{NoInit}; CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); diff --git a/src/Magnum/Vk/Test/InstanceVkTest.cpp b/src/Magnum/Vk/Test/InstanceVkTest.cpp index 672b358296..999fc16c17 100644 --- a/src/Magnum/Vk/Test/InstanceVkTest.cpp +++ b/src/Magnum/Vk/Test/InstanceVkTest.cpp @@ -46,6 +46,7 @@ struct InstanceVkTest: TestSuite::Tester { explicit InstanceVkTest(); void createInfoConstructDefault(); + void createInfoConstructNoImplicitExtensions(); void createInfoApplicationInfo(); void createInfoLayers(); void createInfoExtensions(); @@ -147,6 +148,7 @@ struct { InstanceVkTest::InstanceVkTest() { addTests({&InstanceVkTest::createInfoConstructDefault, + &InstanceVkTest::createInfoConstructNoImplicitExtensions, &InstanceVkTest::createInfoApplicationInfo, &InstanceVkTest::createInfoLayers, &InstanceVkTest::createInfoExtensions, @@ -174,6 +176,21 @@ void InstanceVkTest::createInfoConstructDefault() { CORRADE_VERIFY(!info->pNext); CORRADE_VERIFY(!info->ppEnabledLayerNames); CORRADE_COMPARE(info->enabledLayerCount, 0); + /* Extensions might or might not be enabled */ + + CORRADE_VERIFY(info->pApplicationInfo); + CORRADE_COMPARE(Version(info->pApplicationInfo->apiVersion), enumerateInstanceVersion()); + CORRADE_COMPARE(info->pApplicationInfo->applicationVersion, 0); + CORRADE_COMPARE(info->pApplicationInfo->engineVersion, 0); + CORRADE_COMPARE(info->pApplicationInfo->pEngineName, "Magnum"_s); +} + +void InstanceVkTest::createInfoConstructNoImplicitExtensions() { + InstanceCreateInfo info{InstanceCreateInfo::Flag::NoImplicitExtensions}; + CORRADE_VERIFY(info->sType); + CORRADE_VERIFY(!info->pNext); + CORRADE_VERIFY(!info->ppEnabledLayerNames); + CORRADE_COMPARE(info->enabledLayerCount, 0); CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); @@ -231,7 +248,7 @@ void InstanceVkTest::createInfoLayers() { } void InstanceVkTest::createInfoExtensions() { - InstanceCreateInfo info; + InstanceCreateInfo info{InstanceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); @@ -259,7 +276,7 @@ void InstanceVkTest::createInfoCopiedStrings() { Containers::StringView globalButNotNullTerminated = "VK_LAYER_KHRONOS_validation3"_s.except(1); Containers::String localButNullTerminated = Extensions::KHR::external_memory_capabilities::string(); - InstanceCreateInfo info; + InstanceCreateInfo info{InstanceCreateInfo::Flag::NoImplicitExtensions}; info.setApplicationInfo(localButNullTerminated, {}) .addEnabledLayers({globalButNotNullTerminated}) .addEnabledExtensions({localButNullTerminated}); @@ -332,7 +349,8 @@ void InstanceVkTest::constructCommandLineDisable() { std::ostringstream out; Warning redirectWarning{&out}; Debug redirectOutput{&out}; - Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} + Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable, + InstanceCreateInfo::Flag::NoImplicitExtensions} .setApplicationInfo("InstanceVkTest", version(0, 0, 1)) .addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}) .addEnabledExtensions Date: Mon, 22 Jun 2020 17:49:07 +0200 Subject: [PATCH 18/85] Vk: test coverage with various extensions and versions. --- package/archlinux/PKGBUILD | 3 +++ package/archlinux/PKGBUILD-coverage | 5 ++++ package/archlinux/PKGBUILD-release | 3 +++ src/Magnum/Vk/Test/InstanceVkTest.cpp | 33 +++++++++++++++++++++++++++ 4 files changed, 44 insertions(+) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 9f4158cb50..b4fed704a7 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -69,6 +69,9 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + + MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest } package() { diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index b7166d540e..38b55ccef2 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -65,11 +65,16 @@ build() { check() { cd "$_rootdir/build-coverage" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 || true + MAGNUM_DISABLE_EXTENSIONS="GL_ARB_invalidate_subdata GL_ARB_multi_bind GL_ARB_separate_shader_objects GL_ARB_texture_storage GL_ARB_texture_storage_multisample GL_ARB_texture_multisample GL_ARB_shading_language_420pack GL_ARB_explicit_uniform_location GL_ARB_explicit_attrib_location GL_ARB_texture_filter_anisotropic" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_direct_state_access GL_ARB_robustness GL_ARB_multi_bind" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true + + MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + ./Debug/bin/magnum-al-info > /dev/null ./Debug/bin/magnum-gl-info --limits > /dev/null diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 7d793b45aa..9d88f0980e 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -106,6 +106,9 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_get_texture_sub_image" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest + + MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest done } diff --git a/src/Magnum/Vk/Test/InstanceVkTest.cpp b/src/Magnum/Vk/Test/InstanceVkTest.cpp index 999fc16c17..4ce261e365 100644 --- a/src/Magnum/Vk/Test/InstanceVkTest.cpp +++ b/src/Magnum/Vk/Test/InstanceVkTest.cpp @@ -171,6 +171,9 @@ InstanceVkTest::InstanceVkTest() { using namespace Containers::Literals; void InstanceVkTest::createInfoConstructDefault() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + InstanceCreateInfo info; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); @@ -186,6 +189,9 @@ void InstanceVkTest::createInfoConstructDefault() { } void InstanceVkTest::createInfoConstructNoImplicitExtensions() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + InstanceCreateInfo info{InstanceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); @@ -248,6 +254,9 @@ void InstanceVkTest::createInfoLayers() { } void InstanceVkTest::createInfoExtensions() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + InstanceCreateInfo info{InstanceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); @@ -294,6 +303,9 @@ void InstanceVkTest::createInfoCopiedStrings() { } void InstanceVkTest::construct() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + { Instance instance; CORRADE_VERIFY(instance.handle()); @@ -315,6 +327,9 @@ void InstanceVkTest::construct() { } void InstanceVkTest::constructLayerExtension() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) @@ -341,6 +356,11 @@ void InstanceVkTest::constructCommandLineDisable() { auto&& data = ConstructCommandLineData[testCaseInstanceId()]; setTestCaseDescription(data.nameDisable); + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) @@ -376,6 +396,11 @@ void InstanceVkTest::constructCommandLineEnable() { auto&& data = ConstructCommandLineData[testCaseInstanceId()]; setTestCaseDescription(data.nameEnable); + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); if(!enumerateInstanceExtensionProperties({"VK_LAYER_KHRONOS_validation"}).isSupported()) @@ -406,6 +431,9 @@ void InstanceVkTest::constructCommandLineEnable() { } void InstanceVkTest::constructMove() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + InstanceExtensionProperties extensions = enumerateInstanceExtensionProperties(); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_get_physical_device_properties2 not supported, can't test"); @@ -465,6 +493,11 @@ void InstanceVkTest::constructUnknownExtension() { } void InstanceVkTest::wrap() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + InstanceExtensionProperties properties = enumerateInstanceExtensionProperties(); if(!properties.isSupported()) CORRADE_SKIP("VK_EXT_debug_report not supported, can't test"); From d28442e9b1f3e78e2fd255da90e8b0af8e4e7497 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Jul 2020 15:17:46 +0200 Subject: [PATCH 19/85] Vk: device extension enumeration. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 13 ++ src/Magnum/Vk/DeviceProperties.h | 15 +++ src/Magnum/Vk/ExtensionProperties.h | 7 +- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 124 +++++++++++++++++- 5 files changed, 158 insertions(+), 3 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index e9b986a10a..0475c0f968 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -165,7 +165,7 @@ Vulkan function | Matching API Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{EnumerateDeviceLayerProperties} @m_class{m-label m-danger} **deprecated in 1.0.13** | not exposed, [spec commit](https://github.com/KhronosGroup/Vulkan-Docs/commit/2656f459333b3a1dc63619a9ebd83490eea22e93) -@fn_vk{EnumerateDeviceExtensionProperties} | | +@fn_vk{EnumerateDeviceExtensionProperties} | @ref DeviceProperties::enumerateExtensionProperties() @fn_vk{EnumerateInstanceExtensionProperties} | @ref enumerateInstanceExtensionProperties() @fn_vk{EnumerateInstanceLayerProperties} | @ref enumerateLayerProperties() @fn_vk{EnumerateInstanceVersion} @m_class{m-label m-flat m-success} **1.1** | @ref enumerateInstanceVersion() diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index bed65c26ec..7bbef6ea0d 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -30,6 +30,8 @@ #include #include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Implementation/InstanceState.h" @@ -80,6 +82,17 @@ void DeviceProperties::getPropertiesImplementation11(DeviceProperties& self, VkP return (**self._instance).GetPhysicalDeviceProperties2(self._handle, &properties); } +ExtensionProperties DeviceProperties::enumerateExtensionProperties(Containers::ArrayView layers) { + return InstanceExtensionProperties{layers, [](void* state, const char* const layer, UnsignedInt* count, VkExtensionProperties* properties) { + auto& deviceProperties = *static_cast(state); + return (**deviceProperties._instance).EnumerateDeviceExtensionProperties(deviceProperties._handle, layer, count, properties); + }, this}; +} + +ExtensionProperties DeviceProperties::enumerateExtensionProperties(std::initializer_list layers) { + return enumerateExtensionProperties(Containers::arrayView(layers)); +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 5357004073..27523e1282 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -30,6 +30,7 @@ * @m_since_latest */ +#include #include #include @@ -182,6 +183,20 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ Containers::StringView name(); + /** + * @brief Enumerate device extensions + * @param layers Additional layers to list extensions from + * + * Expects that all listed layers are supported --- however they don't + * need to be enabled on the instance. + * @see @ref LayerProperties::isSupported(), + * @fn_vk_keyword{EnumerateDeviceExtensionProperties} + */ + ExtensionProperties enumerateExtensionProperties(Containers::ArrayView layers = {}); + + /** @overload */ + ExtensionProperties enumerateExtensionProperties(std::initializer_list layers); + private: friend Implementation::InstanceState; diff --git a/src/Magnum/Vk/ExtensionProperties.h b/src/Magnum/Vk/ExtensionProperties.h index 4c3bfe9cd2..b2589ef9a3 100644 --- a/src/Magnum/Vk/ExtensionProperties.h +++ b/src/Magnum/Vk/ExtensionProperties.h @@ -45,6 +45,8 @@ namespace Magnum { namespace Vk { @brief Extension properties @m_since_latest +Provides a searchable container of Vulkan device extensions enumerated with +@ref DeviceProperties::enumerateExtensionProperties(). @see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties} */ class MAGNUM_VK_EXPORT ExtensionProperties { @@ -52,7 +54,9 @@ class MAGNUM_VK_EXPORT ExtensionProperties { /** * @brief Construct without populating the contents * - * Equivalent to a moved-from state. + * Equivalent to a moved-from state. Move over the result of + * @ref DeviceProperties::enumerateExtensionProperties() to make it + * usable. */ explicit ExtensionProperties(NoCreateT); @@ -238,6 +242,7 @@ class MAGNUM_VK_EXPORT InstanceExtensionProperties: public ExtensionProperties { #ifndef DOXYGEN_GENERATING_OUTPUT /* The DAMN THING forgets parameter name if this is present, FFS. It also lists this among friends, which is AN IMPLEMENTATION DETAIL */ + friend DeviceProperties; friend MAGNUM_VK_EXPORT InstanceExtensionProperties enumerateInstanceExtensionProperties(Containers::ArrayView); #endif diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 5e0b98ce95..f11848af84 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -23,13 +23,19 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include #include +#include #include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" @@ -42,13 +48,31 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void constructMove(); void wrap(); + /* Most of this tested already in ExtensionPropertiesVkTest, this only + covers what isn't there already */ + void enumerateExtensions(); + void enumerateExtensionsWithKhronosValidationLayer(); + void enumerateExtensionsNonexistentLayer(); + + void extensionConstructMove(); + void extensionIsSupported(); + void extensionNamedRevision(); + Instance _instance; }; DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { addTests({&DevicePropertiesVkTest::enumerate, &DevicePropertiesVkTest::constructMove, - &DevicePropertiesVkTest::wrap}); + &DevicePropertiesVkTest::wrap, + + &DevicePropertiesVkTest::enumerateExtensions, + &DevicePropertiesVkTest::enumerateExtensionsWithKhronosValidationLayer, + &DevicePropertiesVkTest::enumerateExtensionsNonexistentLayer, + + &DevicePropertiesVkTest::extensionConstructMove, + &DevicePropertiesVkTest::extensionIsSupported, + &DevicePropertiesVkTest::extensionNamedRevision}); } void DevicePropertiesVkTest::enumerate() { @@ -105,6 +129,104 @@ void DevicePropertiesVkTest::wrap() { CORRADE_COMPARE(wrapped.name(), devices[0].name()); } +void DevicePropertiesVkTest::enumerateExtensions() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + Debug{} << "Available device extension count:" << properties.names().size(); + + CORRADE_COMPARE_AS(properties.count(), 0, TestSuite::Compare::Greater); + + /* The extension list should be sorted and unique (so Less, not + LessOrEqual) */ + Containers::ArrayView extensions = properties.names(); + for(std::size_t i = 1; i != extensions.size(); ++i) { + CORRADE_COMPARE_AS(extensions[i - 1], extensions[i], + TestSuite::Compare::Less); + } +} + +void DevicePropertiesVkTest::enumerateExtensionsWithKhronosValidationLayer() { + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + /* There should be more extensions with this layer enabled */ + ExtensionProperties global = devices[0].enumerateExtensionProperties(); + ExtensionProperties withKhronosValidation = devices[0].enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + CORRADE_COMPARE_AS(global.count(), + withKhronosValidation.count(), + TestSuite::Compare::Less); + + /* VK_EXT_tooling_info is only in the layer */ + CORRADE_VERIFY(!global.isSupported("VK_EXT_tooling_info")); + CORRADE_VERIFY(withKhronosValidation.isSupported("VK_EXT_tooling_info")); +} + +void DevicePropertiesVkTest::enumerateExtensionsNonexistentLayer() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + enumerateInstanceExtensionProperties({"VK_LAYER_this_doesnt_exist"}); + CORRADE_COMPARE(out.str(), "TODO"); +} + +void DevicePropertiesVkTest::extensionConstructMove() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties a = devices[0].enumerateExtensionProperties(); + const UnsignedInt count = a.count(); + if(!count) CORRADE_SKIP("No extensions reported, can't test"); + + ExtensionProperties b = std::move(a); + CORRADE_COMPARE(b.count(), count); + + ExtensionProperties c{NoCreate}; + c = std::move(b); + CORRADE_COMPARE(c.count(), count); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void DevicePropertiesVkTest::extensionIsSupported() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + + /* This extension should be available almost always */ + if(!properties.isSupported("VK_KHR_maintenance1")) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't fully test"); + + /* Verify the overloads that take our extension wrappers work as well */ + CORRADE_VERIFY(properties.isSupported()); + CORRADE_VERIFY(properties.isSupported(Extensions::KHR::maintenance1{})); +} + +void DevicePropertiesVkTest::extensionNamedRevision() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + ExtensionProperties properties = devices[0].enumerateExtensionProperties(); + + /* This extension should be available almost always */ + if(!properties.isSupported("VK_KHR_maintenance1")) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't fully test"); + + /* This isn't tested in ExtensionPropertiesVkTest because there's an + overload which takes only InstanceExtensions */ + CORRADE_COMPARE_AS(properties.revision(), 0, + TestSuite::Compare::GreaterOrEqual); + CORRADE_COMPARE_AS(properties.revision(Extensions::KHR::maintenance1{}), 0, + TestSuite::Compare::GreaterOrEqual); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesVkTest) From 722da61e65289c97056e7369cd38a0cd68eb2db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Jul 2020 15:40:18 +0200 Subject: [PATCH 20/85] Vk: externally tuneable device selection. --- src/Magnum/Vk/DeviceProperties.cpp | 56 ++++++++++++++ src/Magnum/Vk/DeviceProperties.h | 24 +++++- src/Magnum/Vk/Implementation/Arguments.cpp | 4 +- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 76 ++++++++++++++++++- 4 files changed, 156 insertions(+), 4 deletions(-) diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 7bbef6ea0d..54ebb33fd1 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -26,13 +26,16 @@ #include "DeviceProperties.h" #include +#include #include +#include #include #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" namespace Magnum { namespace Vk { @@ -114,6 +117,59 @@ Containers::Array enumerateDevices(Instance& instance) { return out; } +Containers::Optional tryPickDevice(Instance& instance) { + Utility::Arguments args = Implementation::arguments(); + args.parse(instance.state().argc, instance.state().argv); + + Containers::Array devices = enumerateDevices(instance); + + /* Pick the first by default */ + if(args.value("device").empty()) { + if(devices.empty()) { + Error{} << "Vk::tryPickDevice(): no Vulkan devices found"; + return {}; + } + return std::move(devices.front()); + } + + /* Pick by ID */ + if(args.value("device")[0] >= '0' && args.value("device")[0] <= '9') { + UnsignedInt id = args.value("device"); + if(id >= devices.size()) { + Error{} << "Vk::tryPickDevice(): index" << id << "out of bounds for" << devices.size() << "Vulkan devices"; + return {}; + } + return std::move(devices[id]); + } + + /* Pick by type */ + DeviceType type; + if(args.value("device") == "integrated") + type = DeviceType::IntegratedGpu; + else if(args.value("device") == "discrete") + type = DeviceType::DiscreteGpu; + else if(args.value("device") == "virtual") + type = DeviceType::VirtualGpu; + else if(args.value("device") == "cpu") + type = DeviceType::Cpu; + else { + Error{} << "Vk::tryPickDevice(): unknown Vulkan device type" << args.value("device"); + return {}; + } + + for(DeviceProperties& device: devices) + if(device.type() == type) return std::move(device); + + Error{} << "Vk::tryPickDevice(): no" << type << "found among" << devices.size() << "Vulkan devices"; + return {}; +} + +DeviceProperties pickDevice(Instance& instance) { + Containers::Optional device = tryPickDevice(instance); + if(device) return *std::move(device); + std::exit(1); /* LCOV_EXCL_LINE */ +} + Debug& operator<<(Debug& debug, const DeviceType value) { debug << "Vk::DeviceType" << Debug::nospace; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 27523e1282..9ab5caf6da 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices() + * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() * @m_since_latest */ @@ -86,7 +86,7 @@ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceType value); Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) properties. -@see @ref enumeratePhysicalDevices() +@see @ref enumerateDevices() */ class MAGNUM_VK_EXPORT DeviceProperties { public: @@ -228,6 +228,26 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& instance); +/** +@brief Pick a physical device +@m_since_latest + +Calls @ref enumerateDevices() and selects a device based on preferences +specified through command-line parameters or the environment. If a device is +not found, exits. See @ref tryPickDevice() for an alternative that doesn't exit +on failure. +*/ +MAGNUM_VK_EXPORT DeviceProperties pickDevice(Instance& instance); + +/** +@brief Try to pick a physical device +@m_since_latest + +Compared to @ref pickDevice() the function returns @ref Containers::NullOpt if +a device isn't found instead of exiting. +*/ +MAGNUM_VK_EXPORT Containers::Optional tryPickDevice(Instance& instance); + }} #endif diff --git a/src/Magnum/Vk/Implementation/Arguments.cpp b/src/Magnum/Vk/Implementation/Arguments.cpp index 1b64c25f03..a9fae5c736 100644 --- a/src/Magnum/Vk/Implementation/Arguments.cpp +++ b/src/Magnum/Vk/Implementation/Arguments.cpp @@ -37,12 +37,14 @@ Utility::Arguments arguments() { .addOption("enable-instance-extensions").setHelp("enable-instance-extensions", "Vulkan instance extensions to enable in addition to the defaults and what the application requests", "LIST") .addOption("vulkan-version").setHelp("vulkan-version", "force Vulkan version", "X.Y") .addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") + .addOption("device").setHelp("device", "device to use", "ID|integrated|discrete|virtual|cpu") .setFromEnvironment("disable-layers") .setFromEnvironment("disable-extensions") .setFromEnvironment("enable-layers") .setFromEnvironment("enable-instance-extensions") .setFromEnvironment("vulkan-version") - .setFromEnvironment("log"); + .setFromEnvironment("log") + .setFromEnvironment("device"); return args; } diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index f11848af84..2a6d6f765d 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -25,11 +25,13 @@ #include #include +#include #include #include #include #include #include +#include #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" @@ -58,9 +60,27 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void extensionIsSupported(); void extensionNamedRevision(); + void pickDevice(); + void pickDeviceIndex(); + void pickDeviceType(); + void pickDeviceError(); + Instance _instance; }; +const struct { + const char* name; + Containers::Array args; + const char* message; +} PickDeviceErrorData[] { + {"nothing for type found", Containers::array({"", "--magnum-device", "virtual"}), + "Vk::tryPickDevice(): no Vk::DeviceType::VirtualGpu found among {} Vulkan devices\n"}, + {"index out of bounds", Containers::array({"", "--magnum-device", "666"}), + "Vk::tryPickDevice(): index 666 out of bounds for {} Vulkan devices\n"}, + {"unknown type", Containers::array({"", "--magnum-device", "FAST"}), + "Vk::tryPickDevice(): unknown Vulkan device type FAST\n"} +}; + DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { addTests({&DevicePropertiesVkTest::enumerate, &DevicePropertiesVkTest::constructMove, @@ -72,7 +92,14 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{a &DevicePropertiesVkTest::extensionConstructMove, &DevicePropertiesVkTest::extensionIsSupported, - &DevicePropertiesVkTest::extensionNamedRevision}); + &DevicePropertiesVkTest::extensionNamedRevision, + + &DevicePropertiesVkTest::pickDevice, + &DevicePropertiesVkTest::pickDeviceIndex, + &DevicePropertiesVkTest::pickDeviceType}); + + addInstancedTests({&DevicePropertiesVkTest::pickDeviceError}, + Containers::arraySize(PickDeviceErrorData)); } void DevicePropertiesVkTest::enumerate() { @@ -227,6 +254,53 @@ void DevicePropertiesVkTest::extensionNamedRevision() { TestSuite::Compare::GreaterOrEqual); } +void DevicePropertiesVkTest::pickDevice() { + /* Default behavior */ + Containers::Optional device = tryPickDevice(_instance); + CORRADE_VERIFY(device); +} + +void DevicePropertiesVkTest::pickDeviceIndex() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + /* Pick the last one */ + CORRADE_COMPARE_AS(devices.size(), 10, TestSuite::Compare::Less); + const char id[] {char('0' + devices.size() - 1), '\0'}; + const char* argv[] {"", "--magnum-device", id}; + + /* Creating another dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; + + Containers::Optional device = tryPickDevice(instance); + CORRADE_VERIFY(device); +} + +void DevicePropertiesVkTest::pickDeviceType() { + const char* argv[] {"", "--magnum-device", "cpu"}; + + /* Creating a dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; + + Containers::Optional device = tryPickDevice(instance); + if(!device) CORRADE_SKIP("No CPU device found."); + + CORRADE_VERIFY(device->type() == DeviceType::Cpu); +} + +void DevicePropertiesVkTest::pickDeviceError() { + auto&& data = PickDeviceErrorData[testCaseInstanceId()]; + setTestCaseDescription(data.name); + + /* Creating a dedicated instance so we can pass custom args */ + Instance instance{InstanceCreateInfo{Int(data.args.size()), const_cast(data.args.data())}}; + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!tryPickDevice(instance)); + CORRADE_COMPARE(out.str(), Utility::formatString(data.message, enumerateDevices(_instance).size())); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesVkTest) From 81cafc9ddf3f2e9d012cc8b4cc3e861474bf5d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Jul 2020 15:35:37 +0200 Subject: [PATCH 21/85] Vk: querying device queue family properties. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 108 ++++++++++++++++ src/Magnum/Vk/DeviceProperties.h | 118 +++++++++++++++++- .../Vk/Implementation/InstanceState.cpp | 3 + src/Magnum/Vk/Implementation/InstanceState.h | 1 + src/Magnum/Vk/Test/DevicePropertiesTest.cpp | 18 ++- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 73 +++++++++++ src/Magnum/Vk/Vk.h | 5 +- 9 files changed, 324 insertions(+), 6 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 0475c0f968..7013bc5758 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -209,7 +209,7 @@ Vulkan function | Matching API @fn_vk{GetPhysicalDeviceImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceMemoryProperties}, \n @fn_vk{GetPhysicalDeviceMemoryProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceProperties}, \n @fn_vk{GetPhysicalDeviceProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties -@fn_vk{GetPhysicalDeviceQueueFamilyProperties}, \n @fn_vk{GetPhysicalDeviceQueueFamilyProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceQueueFamilyProperties}, \n @fn_vk{GetPhysicalDeviceQueueFamilyProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties::queueFamilyProperties() @fn_vk{GetPhysicalDeviceSparseImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceSparseImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPipelineCacheData} | | @fn_vk{GetQueryPoolResults} | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index bde2ccd7f8..19a56b0b89 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,7 +27,6 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS - DeviceProperties.cpp Extensions.cpp Handle.cpp Instance.cpp @@ -38,6 +37,7 @@ set(MagnumVk_SRCS Implementation/InstanceState.cpp) set(MagnumVk_GracefulAssert_SRCS + DeviceProperties.cpp Enums.cpp ExtensionProperties.cpp LayerProperties.cpp) diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 54ebb33fd1..5aabb18e15 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -26,6 +26,7 @@ #include "DeviceProperties.h" #include +#include #include #include #include @@ -42,6 +43,7 @@ namespace Magnum { namespace Vk { struct DeviceProperties::State { VkPhysicalDeviceProperties2 properties{}; + Containers::Array queueFamilyProperties; }; DeviceProperties::DeviceProperties(NoCreateT) noexcept: _instance{}, _handle{} {} @@ -96,6 +98,84 @@ ExtensionProperties DeviceProperties::enumerateExtensionProperties(std::initiali return enumerateExtensionProperties(Containers::arrayView(layers)); } +Containers::ArrayView DeviceProperties::queueFamilyProperties() { + if(!_state) _state.emplace(); + + /* Fetch if not already */ + if(_state->queueFamilyProperties.empty()) { + UnsignedInt count; + _instance->state().getPhysicalDeviceQueueFamilyPropertiesImplementation(*this, count, nullptr); + + _state->queueFamilyProperties = Containers::Array{Containers::ValueInit, count}; + for(VkQueueFamilyProperties2& i: _state->queueFamilyProperties) + i.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; + _instance->state().getPhysicalDeviceQueueFamilyPropertiesImplementation(*this, count, _state->queueFamilyProperties); + CORRADE_INTERNAL_ASSERT(count == _state->queueFamilyProperties.size()); + } + + return _state->queueFamilyProperties; +} + +void DeviceProperties::getQueueFamilyPropertiesImplementationDefault(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties) { + (**self._instance).GetPhysicalDeviceQueueFamilyProperties(self._handle, &count, reinterpret_cast(properties)); + + /* "Sparsen" the returned data to the version 2 structure layout. If the + pointer is null we were just querying the count. */ + if(properties) { + Containers::ArrayView src{reinterpret_cast(properties), count}; + Containers::ArrayView dst{properties, count}; + /* Go backwards so we don't overwrite the yet-to-be-processed data, + additionally copy the VkQueueFamilyProperties first so we don't + overwrite them by setting sType and pNext. */ + for(std::size_t i = count; i != 0; --i) { + dst[i - 1].queueFamilyProperties = src[i - 1]; + dst[i - 1].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; + dst[i - 1].pNext = nullptr; + } + } +} + +void DeviceProperties::getQueueFamilyPropertiesImplementationKHR(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties) { + return (**self._instance).GetPhysicalDeviceQueueFamilyProperties2KHR(self._handle, &count, properties); +} + +void DeviceProperties::getQueueFamilyPropertiesImplementation11(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties) { + return (**self._instance).GetPhysicalDeviceQueueFamilyProperties2(self._handle, &count, properties); +} + +UnsignedInt DeviceProperties::queueFamilyCount() { + return queueFamilyProperties().size(); +} + +UnsignedInt DeviceProperties::queueFamilySize(const UnsignedInt id) { + const Containers::ArrayView properties = queueFamilyProperties(); + CORRADE_ASSERT(id < properties.size(), + "Vk::DeviceProperties::queueFamilySize(): index" << id << "out of range for" << properties.size() << "entries", {}); + return properties[id].queueFamilyProperties.queueCount; +} + +QueueFlags DeviceProperties::queueFamilyFlags(const UnsignedInt id) { + const Containers::ArrayView properties = queueFamilyProperties(); + CORRADE_ASSERT(id < properties.size(), + "Vk::DeviceProperties::queueFamilyFlags(): index" << id << "out of range for" << properties.size() << "entries", {}); + return QueueFlag(properties[id].queueFamilyProperties.queueFlags); +} + +UnsignedInt DeviceProperties::pickQueueFamily(const QueueFlags flags) { + Containers::Optional id = tryPickQueueFamily(flags); + if(id) return *id; + std::exit(1); /* LCOV_EXCL_LINE */ +} + +Containers::Optional DeviceProperties::tryPickQueueFamily(const QueueFlags flags) { + const Containers::ArrayView properties = queueFamilyProperties(); + for(UnsignedInt i = 0; i != properties.size(); ++i) + if(QueueFlag(properties[i].queueFamilyProperties.queueFlags) >= flags) return i; + + Error{} << "Vk::DeviceProperties::tryPickQueueFamily(): no" << flags << "found among" << properties.size() << "queue families"; + return {}; +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; @@ -189,4 +269,32 @@ Debug& operator<<(Debug& debug, const DeviceType value) { return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; } +Debug& operator<<(Debug& debug, const QueueFlag value) { + debug << "Vk::QueueFlag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::QueueFlag::value: return debug << "::" << Debug::nospace << #value; + _c(Graphics) + _c(Compute) + _c(Transfer) + _c(SparseBinding) + _c(Protected) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Flag bits should be in hex, unlike plain values */ + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const QueueFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Vk::QueueFlags{}", { + Vk::QueueFlag::Graphics, + Vk::QueueFlag::Compute, + Vk::QueueFlag::Transfer, + Vk::QueueFlag::SparseBinding, + Vk::QueueFlag::Protected}); +} + }} diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 9ab5caf6da..4e94bbb345 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() + * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, @ref Magnum::Vk::QueueFlag, enum set @ref Magnum::Vk::QueueFlags, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() * @m_since_latest */ @@ -80,12 +80,61 @@ enum class DeviceType: Int { */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceType value); +/** +@brief Queue flag +@m_since_latest + +Wraps a @type_vk_keyword{QueueFlagBits}. +@see @ref QueueFlags, @ref DeviceProperties::queueFamilyFlags() +@m_enum_values_as_keywords +*/ +enum class QueueFlag: UnsignedInt { + /** Supports graphics operations */ + Graphics = VK_QUEUE_GRAPHICS_BIT, + + /** Supports compute operations */ + Compute = VK_QUEUE_COMPUTE_BIT, + + /** Supports transfer operations */ + Transfer = VK_QUEUE_TRANSFER_BIT, + + /** Supports sparse memory management operations */ + SparseBinding = VK_QUEUE_SPARSE_BINDING_BIT, + + /** Supports protected memory operations */ + Protected = VK_QUEUE_PROTECTED_BIT +}; + +/** +@debugoperatorclassenum{DeviceProperties,QueueFlag} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, QueueFlag value); + +/** +@brief Queue flags +@m_since_latest + +Type-safe wrapper for @type_vk_keyword{QueueFlags}. +@see @ref DeviceProperties::queueFamilyFlags() +*/ +typedef Containers::EnumSet QueueFlags; + +CORRADE_ENUMSET_OPERATORS(QueueFlags) + +/** +@debugoperatorclassenum{DeviceProperties,QueueFlags} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, QueueFlags value); + /** @brief Physical device properties @m_since_latest Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) -properties. +properties such as @type_vk_keyword{PhysicalDeviceProperties2} and +@type_vk_keyword{GetPhysicalDeviceQueueFamilyProperties2}. @see @ref enumerateDevices() */ class MAGNUM_VK_EXPORT DeviceProperties { @@ -197,6 +246,67 @@ class MAGNUM_VK_EXPORT DeviceProperties { /** @overload */ ExtensionProperties enumerateExtensionProperties(std::initializer_list layers); + /** + * @brief Queue family properties + * + * Populated lazily on first request. If Vulkan 1.1 or the + * @vk_extension{KHR,get_physical_device_properties2} extension is not + * enabled on the originating instance, only the Vulkan 1.0 subset of + * device properties is queried. + * @see @fn_vk_keyword{GetPhysicalDeviceQueueFamilyProperties2}, + * @fn_vk_keyword{GetPhysicalDeviceQueueFamilyProperties} + */ + Containers::ArrayView queueFamilyProperties(); + + /** + * @brief Queue family count + * + * Convenience access to @ref queueFamilyProperties() internals, + * populated lazily on first request. + */ + UnsignedInt queueFamilyCount(); + + /** + * @brief Queue count in given family + * @param queueFamily Queue family index, expected to be smaller than + * @ref queueFamilyCount() + * + * Convenience access to @ref queueFamilyProperties() internals, + * populated lazily on first request. + */ + UnsignedInt queueFamilySize(UnsignedInt queueFamily); + + /** + * @brief Queue family flags + * @param queueFamily Queue family index, expected to be smaller than + * @ref queueFamilyCount() + * + * Convenience access to @ref queueFamilyProperties() internals, + * populated lazily on first request. + */ + QueueFlags queueFamilyFlags(UnsignedInt queueFamily); + + /** + * @brief Pick a queue family satisfying given flags + * @return Queue family index for use in @ref queueFamilySize() and + * @ref queueFamilyFlags() + * + * Queries queue family properties using @ref queueFamilyProperties() + * and tries to find the first that contains all @p flags. If it is not + * found, exits. See @ref tryPickQueueFamily() for an alternative that + * doesn't exit on failure. + */ + UnsignedInt pickQueueFamily(QueueFlags flags); + + /** + * @brief Try to pick a queue family satisfying given flags + * + * Compared to @ref pickQueueFamily() the function returns + * @ref Containers::NullOpt if a desired family isn't found instead of + * exiting. + */ + Containers::Optional tryPickQueueFamily(QueueFlags flags); + private: friend Implementation::InstanceState; @@ -212,6 +322,10 @@ class MAGNUM_VK_EXPORT DeviceProperties { MAGNUM_VK_LOCAL static void getPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); MAGNUM_VK_LOCAL static void getPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); + MAGNUM_VK_LOCAL static void getQueueFamilyPropertiesImplementationDefault(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties); + MAGNUM_VK_LOCAL static void getQueueFamilyPropertiesImplementationKHR(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties); + MAGNUM_VK_LOCAL static void getQueueFamilyPropertiesImplementation11(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties); + /* Can't be a reference because of the NoCreate constructor */ Instance* _instance; diff --git a/src/Magnum/Vk/Implementation/InstanceState.cpp b/src/Magnum/Vk/Implementation/InstanceState.cpp index 5c151b5f48..1b780ae73d 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.cpp +++ b/src/Magnum/Vk/Implementation/InstanceState.cpp @@ -34,10 +34,13 @@ namespace Magnum { namespace Vk { namespace Implementation { InstanceState::InstanceState(Instance& instance, Int argc, const char** argv): argc{argc}, argv{argv} { if(instance.isVersionSupported(Version::Vk11)) { getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementation11; + getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementation11; } else if(instance.isExtensionEnabled()) { getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementationKHR; + getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationKHR; } else { getPhysicalDevicePropertiesImplementation = DeviceProperties::getPropertiesImplementationDefault; + getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationDefault; } } diff --git a/src/Magnum/Vk/Implementation/InstanceState.h b/src/Magnum/Vk/Implementation/InstanceState.h index f746de43a0..04ff8b61d0 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.h +++ b/src/Magnum/Vk/Implementation/InstanceState.h @@ -37,6 +37,7 @@ struct InstanceState { const char** argv; void(*getPhysicalDevicePropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceProperties2&); + void(*getPhysicalDeviceQueueFamilyPropertiesImplementation)(DeviceProperties&, UnsignedInt&, VkQueueFamilyProperties2*); }; }}} diff --git a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp index 3aab323d60..4cce9babaa 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp @@ -38,13 +38,17 @@ struct DevicePropertiesTest: TestSuite::Tester { void constructCopy(); void debugDeviceType(); + void debugQueueFamilyPropertiesFlag(); + void debugQueueFamilyPropertiesFlags(); }; DevicePropertiesTest::DevicePropertiesTest() { addTests({&DevicePropertiesTest::constructNoCreate, &DevicePropertiesTest::constructCopy, - &DevicePropertiesTest::debugDeviceType}); + &DevicePropertiesTest::debugDeviceType, + &DevicePropertiesTest::debugQueueFamilyPropertiesFlag, + &DevicePropertiesTest::debugQueueFamilyPropertiesFlags}); } void DevicePropertiesTest::constructNoCreate() { @@ -68,6 +72,18 @@ void DevicePropertiesTest::debugDeviceType() { CORRADE_COMPARE(out.str(), "Vk::DeviceType::DiscreteGpu Vk::DeviceType(-10007655)\n"); } +void DevicePropertiesTest::debugQueueFamilyPropertiesFlag() { + std::ostringstream out; + Debug{&out} << QueueFlag::SparseBinding << QueueFlag(0xdeadcafe); + CORRADE_COMPARE(out.str(), "Vk::QueueFlag::SparseBinding Vk::QueueFlag(0xdeadcafe)\n"); +} + +void DevicePropertiesTest::debugQueueFamilyPropertiesFlags() { + std::ostringstream out; + Debug{&out} << (QueueFlag::Compute|QueueFlag::Graphics) << QueueFlags{}; + CORRADE_COMPARE(out.str(), "Vk::QueueFlag::Graphics|Vk::QueueFlag::Compute Vk::QueueFlags{}\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesTest) diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 2a6d6f765d..339622c7b9 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -60,6 +60,11 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void extensionIsSupported(); void extensionNamedRevision(); + void queueFamilies(); + void queueFamiliesOutOfRange(); + void queueFamiliesPick(); + void queueFamiliesPickFailed(); + void pickDevice(); void pickDeviceIndex(); void pickDeviceType(); @@ -94,6 +99,11 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{a &DevicePropertiesVkTest::extensionIsSupported, &DevicePropertiesVkTest::extensionNamedRevision, + &DevicePropertiesVkTest::queueFamilies, + &DevicePropertiesVkTest::queueFamiliesOutOfRange, + &DevicePropertiesVkTest::queueFamiliesPick, + &DevicePropertiesVkTest::queueFamiliesPickFailed, + &DevicePropertiesVkTest::pickDevice, &DevicePropertiesVkTest::pickDeviceIndex, &DevicePropertiesVkTest::pickDeviceType}); @@ -254,6 +264,69 @@ void DevicePropertiesVkTest::extensionNamedRevision() { TestSuite::Compare::GreaterOrEqual); } +void DevicePropertiesVkTest::queueFamilies() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + Debug{} << "Available queue family count:" << devices[0].queueFamilyCount(); + + CORRADE_COMPARE_AS(devices[0].queueFamilyCount(), 0, + TestSuite::Compare::Greater); + + for(std::size_t i = 0; i != devices[0].queueFamilyCount(); ++i) { + CORRADE_ITERATION(i); + CORRADE_ITERATION(devices[0].queueFamilyFlags(i)); + + CORRADE_VERIFY(devices[0].queueFamilyFlags(i) != QueueFlags{}); + CORRADE_COMPARE(devices[0].queueFamilyFlags(i), QueueFlag(devices[0].queueFamilyProperties()[i].queueFamilyProperties.queueFlags)); + + CORRADE_COMPARE_AS(devices[0].queueFamilySize(i), 0, + TestSuite::Compare::Greater); + CORRADE_COMPARE(devices[0].queueFamilySize(i), devices[0].queueFamilyProperties()[i].queueFamilyProperties.queueCount); + } +} + +void DevicePropertiesVkTest::queueFamiliesOutOfRange() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + const UnsignedInt count = devices[0].queueFamilyCount(); + + std::ostringstream out; + Error redirectError{&out}; + devices[0].queueFamilySize(count); + devices[0].queueFamilyFlags(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::DeviceProperties::queueFamilySize(): index {0} out of range for {0} entries\n" + "Vk::DeviceProperties::queueFamilyFlags(): index {0} out of range for {0} entries\n", count)); +} + +void DevicePropertiesVkTest::queueFamiliesPick() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + Containers::Optional id = devices[0].tryPickQueueFamily(QueueFlag::Compute|QueueFlag::Graphics); + CORRADE_VERIFY(id); + CORRADE_COMPARE_AS(*id, devices[0].queueFamilyCount(), TestSuite::Compare::Less); + CORRADE_COMPARE_AS(devices[0].queueFamilyFlags(*id), + QueueFlag::Compute|QueueFlag::Graphics, + TestSuite::Compare::GreaterOrEqual); + + /* Pick should return the same ID, and shouldn't exit */ + CORRADE_COMPARE(devices[0].pickQueueFamily(QueueFlag::Compute|QueueFlag::Graphics), id); +} + +void DevicePropertiesVkTest::queueFamiliesPickFailed() { + Containers::Array devices = enumerateDevices(_instance); + CORRADE_VERIFY(!devices.empty()); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!devices[0].tryPickQueueFamily(QueueFlag(0xc0ffeee0))); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::DeviceProperties::tryPickQueueFamily(): no Vk::QueueFlag(0xc0ffeee0) found among {} queue families\n", devices[0].queueFamilyCount())); +} + void DevicePropertiesVkTest::pickDevice() { /* Default behavior */ Containers::Optional device = tryPickDevice(_instance); diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 5fe7b1ca44..1b618f40e9 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -29,6 +29,8 @@ * @brief Forward declarations for the @ref Magnum::Vk namespace */ +#include + #include "Magnum/Magnum.h" namespace Magnum { namespace Vk { @@ -45,7 +47,8 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; - +enum class QueueFlag: UnsignedInt; +typedef Containers::EnumSet QueueFlags; enum class Result: Int; enum class Version: UnsignedInt; #endif From b91e137058f437f176377b9de5a3006b34a9ff87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 13 May 2020 00:29:18 +0200 Subject: [PATCH 22/85] Vk: initial seed for the magnum-vk-info utility. --- CMakeLists.txt | 5 +- doc/Doxyfile | 1 + doc/building.dox | 4 + doc/cmake.dox | 1 + doc/utilities.dox | 1 + modules/FindMagnum.cmake | 2 + package/archlinux/PKGBUILD | 1 + package/archlinux/PKGBUILD-clang | 1 + .../archlinux/PKGBUILD-clang-addressanitizer | 1 + package/archlinux/PKGBUILD-clang-analyzer | 1 + package/archlinux/PKGBUILD-clang-libc++ | 1 + package/archlinux/PKGBUILD-coverage | 3 + package/archlinux/PKGBUILD-gcc48 | 2 + package/archlinux/PKGBUILD-mingw-w64 | 4 + package/archlinux/PKGBUILD-release | 2 + package/archlinux/magnum-git/PKGBUILD | 1 + package/archlinux/magnum/PKGBUILD | 1 + package/ci/appveyor-desktop-vulkan.bat | 1 + package/ci/unix-desktop-vulkan.sh | 1 + package/msys/PKGBUILD | 1 + package/msys/magnum/PKGBUILD | 1 + src/Magnum/Vk/CMakeLists.txt | 11 + src/Magnum/Vk/vk-info.cpp | 292 ++++++++++++++++++ 23 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/vk-info.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d948833ee2..c02afcc0b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,9 @@ option(WITH_SHADERCONVERTER "Build magnum-shaderconverter utility" OFF) # Magnum AL Info option(WITH_AL_INFO "Build magnum-al-info utility" OFF) +# Magnum Vk Info +option(WITH_VK_INFO "Build magnum-vk-info utility" OFF) + # Plugins option(WITH_ANYIMAGEIMPORTER "Build AnyImageImporter plugin" OFF) option(WITH_ANYAUDIOIMPORTER "Build AnyAudioImporter plugin" OFF) @@ -113,7 +116,7 @@ option(WITH_EGLCONTEXT "Build EglContext library" OFF) # Vulkan, everywhere except Emscripten if(NOT CORRADE_TARGET_EMSCRIPTEN) - option(WITH_VK "Build Vk library" OFF) + cmake_dependent_option(WITH_VK "Build Vk library" OFF "NOT WITH_VK_INFO" ON) cmake_dependent_option(TARGET_VK "Build libraries with Vulkan interoperability" ON "WITH_VK" OFF) endif() diff --git a/doc/Doxyfile b/doc/Doxyfile index fd6a8a2c0a..11ad3f9ff3 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -927,6 +927,7 @@ INPUT = ../src/Magnum \ ../src/Magnum/Text/fontconverter.cpp \ ../src/Magnum/TextureTools/distancefieldconverter.cpp \ ../src/Magnum/Trade/imageconverter.cpp \ + ../src/Magnum/Vk/vk-info.cpp \ . \ ../../magnum-plugins/src/Magnum \ ../../magnum-plugins/src/MagnumPlugins \ diff --git a/doc/building.dox b/doc/building.dox index af0679bd69..088c52954c 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -608,6 +608,10 @@ There are also a few command-line utilities, also all disabled by default: executable, which provides information about the engine OpenGL capabilities. Requires `TARGET_GL` to be enabled; enables building of one of the windowless application libraries based on the target platform. +- `WITH_VK_INFO` --- Build the @ref magnum-vk-info "magnum-vk-info" + executable, which provides information about the engine Vulkan + capabilities. Requires `TARGET_VK` to be enabled; enables building of the + @ref Vk library. - `WITH_AL_INFO` --- Build the @ref magnum-al-info "magnum-al-info" executable, which provides information about the engine OpenAL capabilities. diff --git a/doc/cmake.dox b/doc/cmake.dox index 464d54ac96..e28d6802d7 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -275,6 +275,7 @@ Lastly, a few utility executables are available: - `shaderconverter` --- @ref magnum-shaderconverter "magnum-shaderconverter" executable - `gl-info` --- @ref magnum-gl-info "magnum-gl-info" executable +- `vk-info` --- @ref magnum-vk-info "magnum-vk-info" executable - `al-info` --- @ref magnum-al-info "magnum-al-info" executable Note that [each namespace](namespaces.html), all @ref Platform libraries and diff --git a/doc/utilities.dox b/doc/utilities.dox index a58df6fa13..847489b5e9 100644 --- a/doc/utilities.dox +++ b/doc/utilities.dox @@ -50,6 +50,7 @@ namespace Magnum { @section utilities-cli Command-line utilities - @subpage magnum-gl-info @m_class{m-label m-default m-flat} **live web demo** --- @copybrief magnum-gl-info +- @subpage magnum-vk-info --- @copybrief magnum-vk-info - @subpage magnum-al-info @m_class{m-label m-default m-flat} **live web demo** --- @copybrief magnum-al-info - @subpage magnum-distancefieldconverter --- @copybrief magnum-distancefieldconverter - @subpage magnum-fontconverter --- @copybrief magnum-fontconverter diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 09be796753..d69a5ed836 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -98,6 +98,7 @@ # sceneconverterter - magnum-sceneconverter executable # shaderconverterter - magnum-shaderconverter executable # gl-info - magnum-gl-info executable +# vk-info - magnum-vk-info executable # al-info - magnum-al-info executable # # Example usage with specifying additional components is:: @@ -372,6 +373,7 @@ set(_MAGNUM_IMPLICITLY_ENABLED_COMPONENTS GL Primitives) if(NOT CORRADE_TARGET_EMSCRIPTEN) list(APPEND _MAGNUM_LIBRARY_COMPONENTS Vk) + list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS vk-info) endif() if(NOT CORRADE_TARGET_ANDROID) list(APPEND _MAGNUM_LIBRARY_COMPONENTS Sdl2Application) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index b4fed704a7..37556ce90c 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -53,6 +53,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-clang b/package/archlinux/PKGBUILD-clang index da225875b0..f7b2055e6d 100644 --- a/package/archlinux/PKGBUILD-clang +++ b/package/archlinux/PKGBUILD-clang @@ -56,6 +56,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-clang-addressanitizer b/package/archlinux/PKGBUILD-clang-addressanitizer index 7894ed7350..8e9b9b7420 100644 --- a/package/archlinux/PKGBUILD-clang-addressanitizer +++ b/package/archlinux/PKGBUILD-clang-addressanitizer @@ -57,6 +57,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-clang-analyzer b/package/archlinux/PKGBUILD-clang-analyzer index 70f00400cf..a6b6a8a7d0 100644 --- a/package/archlinux/PKGBUILD-clang-analyzer +++ b/package/archlinux/PKGBUILD-clang-analyzer @@ -48,6 +48,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-clang-libc++ b/package/archlinux/PKGBUILD-clang-libc++ index 132f27ba45..57926c8a89 100644 --- a/package/archlinux/PKGBUILD-clang-libc++ +++ b/package/archlinux/PKGBUILD-clang-libc++ @@ -59,6 +59,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 38b55ccef2..ab80798f5b 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -54,6 +54,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ @@ -77,6 +78,8 @@ check() { ./Debug/bin/magnum-al-info > /dev/null ./Debug/bin/magnum-gl-info --limits > /dev/null + # Not calling vk-info because currently it doesn't do anything that + # wouldn't be covered elsewhere (but it causes false positives in coverage) rm -rf coverage mkdir coverage diff --git a/package/archlinux/PKGBUILD-gcc48 b/package/archlinux/PKGBUILD-gcc48 index 341d38d870..cce7ea060d 100644 --- a/package/archlinux/PKGBUILD-gcc48 +++ b/package/archlinux/PKGBUILD-gcc48 @@ -33,6 +33,7 @@ build() { -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_INSTALL_PREFIX=/usr \ -DWITH_AUDIO=ON \ + -DWITH_VK=ON \ -DWITH_GLFWAPPLICATION=ON \ -DWITH_GLXAPPLICATION=ON \ -DWITH_SDL2APPLICATION=ON \ @@ -60,6 +61,7 @@ build() { -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ -DWITH_AL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ -DBUILD_VK_TESTS=ON diff --git a/package/archlinux/PKGBUILD-mingw-w64 b/package/archlinux/PKGBUILD-mingw-w64 index bd22f9503b..b6ade8bf83 100644 --- a/package/archlinux/PKGBUILD-mingw-w64 +++ b/package/archlinux/PKGBUILD-mingw-w64 @@ -21,6 +21,7 @@ build() { -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/i686-w64-mingw32 \ -DWITH_AUDIO=ON \ + -DWITH_VK=OFF \ -DWITH_SDL2APPLICATION=ON \ -DWITH_WINDOWLESSWGLAPPLICATION=ON \ -DWITH_WGLCONTEXT=ON \ @@ -43,6 +44,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=OFF \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ @@ -58,6 +60,7 @@ build() { -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/x86_64-w64-mingw32 \ -DWITH_AUDIO=ON \ + -DWITH_VK=OFF \ -DWITH_SDL2APPLICATION=ON \ -DWITH_WINDOWLESSWGLAPPLICATION=ON \ -DWITH_WGLCONTEXT=ON \ @@ -80,6 +83,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=OFF \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 9d88f0980e..ebed4bc85a 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -48,6 +48,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ @@ -89,6 +90,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ diff --git a/package/archlinux/magnum-git/PKGBUILD b/package/archlinux/magnum-git/PKGBUILD index 72e6b8b36d..79ffb3257b 100644 --- a/package/archlinux/magnum-git/PKGBUILD +++ b/package/archlinux/magnum-git/PKGBUILD @@ -50,6 +50,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -G Ninja ninja diff --git a/package/archlinux/magnum/PKGBUILD b/package/archlinux/magnum/PKGBUILD index f99222415e..f47a1f9149 100644 --- a/package/archlinux/magnum/PKGBUILD +++ b/package/archlinux/magnum/PKGBUILD @@ -47,6 +47,7 @@ build() { -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_VK=ON \ + -DWITH_VK_INFO=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ -DWITH_WINDOWLESSGLXAPPLICATION=ON \ -GNinja diff --git a/package/ci/appveyor-desktop-vulkan.bat b/package/ci/appveyor-desktop-vulkan.bat index d65cbff054..251c98f610 100644 --- a/package/ci/appveyor-desktop-vulkan.bat +++ b/package/ci/appveyor-desktop-vulkan.bat @@ -56,6 +56,7 @@ cmake .. ^ -DWITH_IMAGECONVERTER=OFF ^ -DWITH_SCENECONVERTER=OFF ^ -DWITH_GL_INFO=OFF ^ + -DWITH_VK_INFO=ON ^ -DWITH_AL_INFO=OFF ^ -DWITH_SDL2APPLICATION=ON ^ -DWITH_GLFWAPPLICATION=ON ^ diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index cbd52c98c7..4a43f456d9 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -43,6 +43,7 @@ cmake .. \ -DWITH_TRADE=ON \ -DWITH_VK=ON \ -DWITH_AL_INFO=OFF \ + -DWITH_VK_INFO=ON \ -DWITH_GL_INFO=OFF \ -DWITH_ANYAUDIOIMPORTER=OFF \ -DWITH_ANYIMAGECONVERTER=OFF \ diff --git a/package/msys/PKGBUILD b/package/msys/PKGBUILD index 3a87e5053b..8b81f7a920 100644 --- a/package/msys/PKGBUILD +++ b/package/msys/PKGBUILD @@ -55,6 +55,7 @@ build() { -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON ninja } diff --git a/package/msys/magnum/PKGBUILD b/package/msys/magnum/PKGBUILD index 00baa928ce..1c659195e0 100644 --- a/package/msys/magnum/PKGBUILD +++ b/package/msys/magnum/PKGBUILD @@ -63,6 +63,7 @@ build() { -DWITH_TGAIMAGECONVERTER=ON \ -DWITH_TGAIMPORTER=ON \ -DWITH_VK=ON \ + -DWITH_VK_INFO=ON \ -DWITH_WAVAUDIOIMPORTER=ON \ -DWITH_WINDOWLESSWGLAPPLICATION=ON \ "${extra_config[@]}" \ diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 19a56b0b89..de57eae0c5 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -110,6 +110,17 @@ install(TARGETS MagnumVk ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) install(FILES ${MagnumVk_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Vk) +if(WITH_VK_INFO) + add_executable(magnum-vk-info vk-info.cpp) + target_link_libraries(magnum-vk-info PRIVATE MagnumVk) + set_target_properties(magnum-vk-info PROPERTIES FOLDER "Magnum/Vk") + + install(TARGETS magnum-vk-info DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}) + + # Magnum Vk Info target alias for superprojects + add_executable(Magnum::vk-info ALIAS magnum-vk-info) +endif() + if(BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumVkTestLib ${SHARED_OR_STATIC} diff --git a/src/Magnum/Vk/vk-info.cpp b/src/Magnum/Vk/vk-info.cpp new file mode 100644 index 0000000000..420885cf08 --- /dev/null +++ b/src/Magnum/Vk/vk-info.cpp @@ -0,0 +1,292 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include + +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Version.h" + +namespace Magnum { + +/** @page magnum-vk-info Magnum Vulkan Info +@brief Displays information about Magnum engine Vulkan capabilities + +@m_footernavigation +@m_keywords{magnum-vk-info vk-info} + +This utility is built if both `WITH_VK` and `WITH_VK_INFO` is enabled when +building Magnum. To use this utility with CMake, you need to request the +`vk-info` component of the `Magnum` package and use the `Magnum::vk-info` +target for example in a custom command: + +@code{.cmake} +find_package(Magnum REQUIRED vk-info) + +add_custom_command(OUTPUT ... COMMAND Magnum::vk-info ...) +@endcode + +See @ref building, @ref cmake and the @ref Vk namespace for more information. +*/ + +} + +using namespace Magnum; + +int main(int argc, char** argv) { + Utility::Arguments args; + args.addBooleanOption("extension-strings").setHelp("extension-strings", "list all extension strings provided by the driver") + .addBooleanOption("all-extensions").setHelp("all-extensions", "display extensions also for fully supported versions") + .addSkippedPrefix("magnum", "engine-specific options") + .setGlobalHelp("Displays information about Magnum engine and Vulkan capabilities.") + .parse(argc, argv); + + /* Setup InstanceCreateInfo before printing anything so --magnum-help has + uncluttered output */ + /** @todo add a NoCreate InstanceInfo that just populates internal state + without querying vulkan for anything? or that's stupid? */ + Vk::LayerProperties layerProperties = Vk::enumerateLayerProperties(); + Vk::InstanceExtensionProperties instanceExtensionProperties = Vk::enumerateInstanceExtensionProperties({layerProperties.names()}); + + Vk::InstanceCreateInfo instanceCreateInfo{argc, argv, &layerProperties, &instanceExtensionProperties}; + + Debug{} << ""; + Debug{} << " +---------------------------------------------------------+"; + Debug{} << " | Information about Magnum engine Vulkan capabilities |"; + Debug{} << " +---------------------------------------------------------+"; + Debug{} << ""; + + Debug{} << "Compilation flags:"; + #ifdef CORRADE_BUILD_DEPRECATED + Debug{} << " CORRADE_BUILD_DEPRECATED"; + #endif + #ifdef CORRADE_BUILD_STATIC + Debug{} << " CORRADE_BUILD_STATIC"; + #endif + #ifdef CORRADE_BUILD_MULTITHREADED + Debug{} << " CORRADE_BUILD_MULTITHREADED"; + #endif + #ifdef CORRADE_TARGET_UNIX + Debug{} << " CORRADE_TARGET_UNIX"; + #endif + #ifdef CORRADE_TARGET_APPLE + Debug{} << " CORRADE_TARGET_APPLE"; + #endif + #ifdef CORRADE_TARGET_IOS + Debug{} << " CORRADE_TARGET_IOS"; + #endif + #ifdef CORRADE_TARGET_WINDOWS + Debug{} << " CORRADE_TARGET_WINDOWS"; + #endif + #ifdef CORRADE_TARGET_WINDOWS_RT + Debug{} << " CORRADE_TARGET_WINDOWS_RT"; + #endif + #ifdef CORRADE_TARGET_ANDROID + Debug{} << " CORRADE_TARGET_ANDROID"; + #endif + #ifdef CORRADE_TARGET_X86 + Debug{} << " CORRADE_TARGET_X86"; + #endif + #ifdef CORRADE_TARGET_ARM + Debug{} << " CORRADE_TARGET_ARM"; + #endif + #ifdef CORRADE_TARGET_POWERPC + Debug{} << " CORRADE_TARGET_POWERPC"; + #endif + #ifdef CORRADE_TARGET_BIG_ENDIAN + Debug{} << " CORRADE_TARGET_BIG_ENDIAN"; + #endif + #ifdef CORRADE_TARGET_GCC + Debug{} << " CORRADE_TARGET_GCC"; + #endif + #ifdef CORRADE_TARGET_CLANG + Debug{} << " CORRADE_TARGET_CLANG"; + #endif + #ifdef CORRADE_TARGET_APPLE_CLANG + Debug{} << " CORRADE_TARGET_APPLE_CLANG"; + #endif + #ifdef CORRADE_TARGET_CLANG_CL + Debug{} << " CORRADE_TARGET_CLANG_CL"; + #endif + #ifdef CORRADE_TARGET_MSVC + Debug{} << " CORRADE_TARGET_MSVC"; + #endif + #ifdef CORRADE_TARGET_MINGW + Debug{} << " CORRADE_TARGET_MINGW"; + #endif + #ifdef CORRADE_TARGET_LIBCXX + Debug{} << " CORRADE_TARGET_LIBCXX"; + #endif + #ifdef CORRADE_TARGET_LIBSTDCXX + Debug{} << " CORRADE_TARGET_LIBSTDCXX"; + #endif + #ifdef CORRADE_TARGET_DINKUMWARE + Debug{} << " CORRADE_TARGET_DINKUMWARE"; + #endif + #ifdef CORRADE_TARGET_SSE2 + Debug{} << " CORRADE_TARGET_SSE2"; + #endif + #ifdef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT + Debug{} << " CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT"; + #endif + #ifdef CORRADE_TESTSUITE_TARGET_XCTEST + Debug{} << " CORRADE_TESTSUITE_TARGET_XCTEST"; + #endif + #ifdef CORRADE_UTILITY_USE_ANSI_COLORS + Debug{} << " CORRADE_UTILITY_USE_ANSI_COLORS"; + #endif + #ifdef MAGNUM_BUILD_DEPRECATED + Debug{} << " MAGNUM_BUILD_DEPRECATED"; + #endif + #ifdef MAGNUM_BUILD_STATIC + Debug{} << " MAGNUM_BUILD_STATIC"; + #endif + Debug{} << ""; + + const Vk::Version version = Vk::enumerateInstanceVersion(); + Debug{} << "Reported instance version:" << version; + Debug{} << "Reported instance layers:"; + for(UnsignedInt i = 0, max = layerProperties.count(); i != max; ++i) { + Debug{} << " " << layerProperties.name(i) << "(r" << Debug::nospace << layerProperties.revision(i) << Debug::nospace << ", written against" << layerProperties.version(i) << Debug::nospace << ")"; + Debug{} << " " << layerProperties.description(i); + } + + constexpr Vk::Version versions[]{ + Vk::Version::Vk11, + Vk::Version::Vk12, + Vk::Version::None + }; + std::size_t future = 0; + + if(!args.isSet("all-extensions")) + while(versions[future] != Vk::Version::None && version >= versions[future]) + ++future; + + /** @todo do better once implemented in format() */ + using namespace Containers::Literals; + constexpr Containers::StringView sixtyfourSpaces = " "_s; + + if(args.isSet("extension-strings")) { + Debug{} << "Reported instance extension strings:"; + for(std::size_t i = 0, max = instanceExtensionProperties.count(); i != max; ++i) { + Debug d; + d << " " << instanceExtensionProperties.name(i) << "(r" << Debug::nospace << instanceExtensionProperties.revision(i) << Debug::nospace; + const UnsignedInt layer = instanceExtensionProperties.layer(i); + if(layer != 0) + d << ", from" << layerProperties.names()[layer - 1] << Debug::nospace; + d << ")"; + } + + } else for(std::size_t i = future; i != Containers::arraySize(versions); ++i) { + Containers::ArrayView extensions = Vk::InstanceExtension::extensions(versions[i]); + if(extensions.empty()) continue; + + if(versions[i] != Vk::Version::None) + Debug{} << versions[i] << "instance extension support:"; + else Debug{} << "Vendor instance extension support:"; + + for(const Vk::InstanceExtension& extension: extensions) { + Debug d; + d << " " << extension.string() << sixtyfourSpaces.prefix(64 - extension.string().size()); + + if(instanceExtensionProperties.isSupported(extension)) + d << "REV." << Debug::nospace << instanceExtensionProperties.revision(extension); + else if(version >= extension.requiredVersion()) + d << " -"; + else + d << " n/a"; + } + } + + Debug{} << ""; + + Vk::Instance instance{instanceCreateInfo}; + + { + Containers::Array devices = Vk::enumerateDevices(instance); + Debug{} << "Found" << devices.size() << "devices:"; + for(Vk::DeviceProperties& device: devices) { + Debug{} << " " << device.name() << Debug::nospace << "," + << device.apiVersion() << Debug::newline + << " " << device.type() << Debug::nospace << ", driver" + << Debug::packed << device.driverVersion(); + } + + if(devices.empty()) return 0; + } + + Debug{} << ""; + + Vk::DeviceProperties device = Vk::pickDevice(instance); + + Debug{} << "Picked device" << device.name() << Debug::newline; + Debug{} << "Reported version:" << device.apiVersion(); + + Vk::ExtensionProperties extensionProperties = device.enumerateExtensionProperties(layerProperties.names()); + + if(args.isSet("extension-strings")) { + Debug{} << "Reported extension strings:"; + for(std::size_t i = 0, max = extensionProperties.count(); i != max; ++i) { + Debug d; + d << " " << extensionProperties.name(i) << "(r" << Debug::nospace << extensionProperties.revision(i) << Debug::nospace; + const UnsignedInt layer = extensionProperties.layer(i); + if(layer != 0) + d << ", from" << layerProperties.names()[layer - 1] << Debug::nospace; + d << ")"; + } + + } else for(std::size_t i = future; i != Containers::arraySize(versions); ++i) { + Containers::ArrayView extensions = Vk::Extension::extensions(versions[i]); + if(extensions.empty()) continue; + + if(versions[i] != Vk::Version::None) + Debug{} << versions[i] << "extension support:"; + else Debug{} << "Vendor extension support:"; + + for(const Vk::Extension& extension: extensions) { + Debug d; + d << " " << extension.string() << sixtyfourSpaces.prefix(64 - extension.string().size()); + + if(extensionProperties.isSupported(extension)) + d << "REV." << Debug::nospace << extensionProperties.revision(extension); + else if(version >= extension.requiredVersion()) + d << " -"; + else + d << " n/a"; + } + } + + /* If we wanted only extension strings, exit now */ + if(args.isSet("extension-strings")) return 0; + + Debug{} << "Queue families:"; + for(UnsignedInt i = 0; i != device.queueFamilyCount(); ++i) { + Debug{} << " " << i << Debug::nospace << ":" << device.queueFamilyFlags(i); + Debug{} << " " << device.queueFamilySize(i) << "queues"; + } +} From 374f8167629eaa64933df0e5b1f857db4291d1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 11 Jul 2020 00:37:05 +0200 Subject: [PATCH 23/85] Vk: device creation. --- doc/snippets/MagnumVk.cpp | 33 ++ doc/vulkan-mapping.dox | 13 +- src/Magnum/Vk/CMakeLists.txt | 3 + src/Magnum/Vk/Device.cpp | 335 ++++++++++++ src/Magnum/Vk/Device.h | 351 +++++++++++- src/Magnum/Vk/DeviceProperties.h | 5 +- src/Magnum/Vk/Implementation/Arguments.cpp | 4 +- src/Magnum/Vk/Implementation/DeviceState.cpp | 32 ++ src/Magnum/Vk/Implementation/DeviceState.h | 40 ++ src/Magnum/Vk/Instance.h | 2 + src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/DeviceTest.cpp | 90 +++ src/Magnum/Vk/Test/DeviceVkTest.cpp | 546 +++++++++++++++++++ src/Magnum/Vk/Vk.h | 2 + 14 files changed, 1452 insertions(+), 6 deletions(-) create mode 100644 src/Magnum/Vk/Device.cpp create mode 100644 src/Magnum/Vk/Implementation/DeviceState.cpp create mode 100644 src/Magnum/Vk/Implementation/DeviceState.h create mode 100644 src/Magnum/Vk/Test/DeviceTest.cpp create mode 100644 src/Magnum/Vk/Test/DeviceVkTest.cpp diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 7f446cb49c..af0efd3293 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -27,6 +27,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" +#include "Magnum/Vk/Device.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Integration.h" @@ -34,7 +35,39 @@ using namespace Magnum; +#define DOXYGEN_IGNORE(...) __VA_ARGS__ + int main() { +{ +Vk::Device device{NoCreate}; +/* [Device-isExtensionEnabled] */ +if(device.isExtensionEnabled()) { + // keep mesh indices 8bit +} else { + // convert them to 16bit +} +/* [Device-isExtensionEnabled] */ +} + +{ +Vk::Instance instance; +/* Header included again inside a function, but it's fine as the guards will + make it empty */ +/* [Device-global-function-pointers] */ +#include + +// … + +Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{instance})}; +device.populateGlobalFunctionPointers(); + +VkCommandPool commandPool; +VkCommandPoolCreateInfo info{}; +// … +vkCreateCommandPool(device, &info, nullptr, &commandPool); +/* [Device-global-function-pointers] */ +} + { Vk::Instance instance; /* [Instance-isExtensionEnabled] */ diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 7013bc5758..eaea4a38db 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -130,7 +130,7 @@ Vulkan function | Matching API @fn_vk{CreateDescriptorPool}, \n @fn_vk{DestroyDescriptorPool} | | @fn_vk{CreateDescriptorSetLayout}, \n @fn_vk{DestroyDescriptorSetLayout} | | @fn_vk{CreateDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1**, \n @fn_vk{DestroyDescriptorUpdateTemplate} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | | +@fn_vk{CreateDevice}, \n @fn_vk{DestroyDevice} | @ref Device constructor and destructor @fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | @fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | | @fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | @@ -193,7 +193,7 @@ Vulkan function | Matching API @fn_vk{GetDescriptorSetLayoutSupport} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceMemoryCommitment} | | -@fn_vk{GetDeviceProcAddr} | | +@fn_vk{GetDeviceProcAddr} | @ref Device constructor @fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | | @fn_vk{GetEventStatus} | | @fn_vk{GetFenceStatus} | | @@ -304,6 +304,15 @@ Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{ApplicationInfo} | @ref InstanceCreateInfo +@subsection vulkan-mapping-structures-d D + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{DeviceCreateInfo} | @ref DeviceCreateInfo +@type_vk{DeviceQueueCreateInfo} | not exposed, but you can pass a custom instance to @ref DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo&) + @subsection vulkan-mapping-structures-i I @m_class{m-fullwidth} diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index de57eae0c5..2ea9e44e42 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -34,9 +34,11 @@ set(MagnumVk_SRCS Version.cpp Implementation/Arguments.cpp + Implementation/DeviceState.cpp Implementation/InstanceState.cpp) set(MagnumVk_GracefulAssert_SRCS + Device.cpp DeviceProperties.cpp Enums.cpp ExtensionProperties.cpp @@ -62,6 +64,7 @@ set(MagnumVk_HEADERS set(MagnumVk_PRIVATE_HEADERS Implementation/Arguments.h + Implementation/DeviceState.h Implementation/InstanceState.h Implementation/compressedPixelFormatMapping.hpp diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp new file mode 100644 index 0000000000..a98411139f --- /dev/null +++ b/src/Magnum/Vk/Device.cpp @@ -0,0 +1,335 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Device.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Magnum/Math/Functions.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" +#include "Magnum/Vk/Implementation/Arguments.h" +#include "Magnum/Vk/Implementation/InstanceState.h" +#include "Magnum/Vk/Implementation/DeviceState.h" +#include "MagnumExternal/Vulkan/flextVkGlobal.h" + +namespace Magnum { namespace Vk { + +struct DeviceCreateInfo::State { + Containers::Array ownedStrings; + Containers::Array extensions; + + Containers::String disabledExtensionsStorage; + Containers::Array disabledExtensions; + Containers::Array queues; + Containers::StaticArray<32, Float> queuePriorities; + + std::size_t nextQueuePriority = 0; + bool quietLog = false; + Version version = Version::None; +}; + +DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): _physicalDevice{deviceProperties}, _info{}, _state{Containers::InPlaceInit} { + Utility::Arguments args = Implementation::arguments(); + args.parse(deviceProperties._instance->state().argc, deviceProperties._instance->state().argv); + + if(args.value("log") == "quiet") + _state->quietLog = true; + + _info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + _info.flags = VkDeviceCreateFlags(flags & ~Flag::NoImplicitExtensions); + + /* Take the minimum of instance and device version. Instance version being + smaller than a device version happens mainly if there's a forced Vulkan + version via --magnum-vulkan version, which will be later used to cap available features. */ + _state->version = Version(Math::min(UnsignedInt(deviceProperties._instance->version()), UnsignedInt(deviceProperties.apiVersion()))); + + /* If there are any disabled extensions, sort them and save for later -- + we'll use them to filter the ones added by the app */ + Containers::String disabledExtensions = args.value("disable-extensions"); + if(!disabledExtensions.isEmpty()) { + _state->disabledExtensionsStorage = std::move(disabledExtensions); + _state->disabledExtensions = Containers::StringView{_state->disabledExtensionsStorage}.splitWithoutEmptyParts(); + std::sort(_state->disabledExtensions.begin(), _state->disabledExtensions.end()); + } + + /* Add all extensions enabled on command-line. The blacklist is applied on + those as well. */ + /** @todo use a generator split() so we can avoid the growing allocation + of the output array */ + /** @todo unfortunately even though the split and value retrieval is mostly + allocation-free, the strings will be turned into owning copies because + none of them is null-terminated or global -- could be a better idea to + just grow one giant string internally (once we have growable strings) */ + addEnabledExtensions(args.value("enable-extensions").splitWithoutEmptyParts()); + + /* Enable implicit extensions unless that's forbidden */ + /** @todo move this somewhere else as this will grow significantly? */ + if(!(flags & Flag::NoImplicitExtensions)) { + /* Fetch searchable extension properties if not already */ + Containers::Optional extensionPropertiesStorage; + if(!extensionProperties) { + /** @todo i'd like to know which layers are enabled so i can list + the exts from those .. but how? */ + extensionPropertiesStorage = deviceProperties.enumerateExtensionProperties(); + extensionProperties = &*extensionPropertiesStorage; + } + + /* No extensions at the moment */ + } +} + +DeviceCreateInfo::DeviceCreateInfo(Instance& instance, const Flags flags): DeviceCreateInfo{pickDevice(instance), flags} {} + +DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {} + +DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, _info{info} {} + +DeviceCreateInfo::~DeviceCreateInfo() = default; + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { + if(extensions.empty()) return *this; + /* This can happen in case we used the NoInit or VkDeviceCreateInfo + constructor */ + if(!_state) _state.emplace(); + + /* Add null-terminated strings to the extension array */ + arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); + for(const Containers::StringView extension: extensions) { + /* If the extension is blacklisted, skip it */ + if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension)) continue; + + /* Keep an owned *allocated* copy of the string if it's not global or + null-terminated -- ideally, if people use string view literals, + those will be, so this won't allocate. Allocated so the pointers + don't get invalidated when the array gets reallocated. */ + const char* data; + if(!(extension.flags() >= (Containers::StringViewFlag::NullTerminated|Containers::StringViewFlag::Global))) + data = arrayAppend(_state->ownedStrings, Containers::InPlaceInit, + Containers::AllocatedInit, extension).data(); + else data = extension.data(); + + arrayAppend(_state->extensions, data); + } + + /* Update the extension count, re-route the pointer to the layers array in + case it got reallocated */ + _info.enabledExtensionCount = _state->extensions.size(); + _info.ppEnabledExtensionNames = _state->extensions.data(); + return *this; +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { + return addEnabledExtensions(Containers::arrayView(extensions)); +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { + if(extensions.empty()) return *this; + /* This can happen in case we used the NoInit or VkDeviceCreateInfo + constructor */ + if(!_state) _state.emplace(); + + arrayReserve(_state->extensions, _state->extensions.size() + extensions.size()); + for(const Extension& extension: extensions) { + /* If the extension is blacklisted, skip it */ + if(std::binary_search(_state->disabledExtensions.begin(), _state->disabledExtensions.end(), extension.string())) continue; + + arrayAppend(_state->extensions, extension.string().data()); + } + + /* Update the extension count, re-route the pointer to the layers array in + case it got reallocated */ + _info.enabledExtensionCount = _state->extensions.size(); + _info.ppEnabledExtensionNames = _state->extensions.data(); + return *this; +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { + return addEnabledExtensions(Containers::arrayView(extensions)); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::ArrayView priorities) { + CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this); + + /* This can happen in case we used the NoInit or VkDeviceCreateInfo + constructor */ + if(!_state) _state.emplace(); + + VkDeviceQueueCreateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + info.queueFamilyIndex = family; + info.queueCount = priorities.size(); + info.pQueuePriorities = _state->queuePriorities + _state->nextQueuePriority; + + /* Copy the passed queue priorities to an internal storage that never + reallocates. If this blows up, see the definition of queuePriorities for + details. We can't easily reallocate if this grows too big as all + pointers would need to be patched, so there's a static limit. */ + CORRADE_INTERNAL_ASSERT(_state->nextQueuePriority + priorities.size() <= _state->queuePriorities.size()); + Utility::copy(priorities, _state->queuePriorities.suffix(_state->nextQueuePriority).prefix(priorities.size())); + _state->nextQueuePriority += priorities.size(); + + return addQueues(info); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, std::initializer_list priorities) { + return addQueues(family, Containers::arrayView(priorities)); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) { + /* This can happen in case we used the NoInit or VkDeviceCreateInfo + constructor */ + if(!_state) _state.emplace(); + + /* Copy the info to an internal storage and re-route the pointer to it. + This handles a potential reallocation and also the case of replacing the + default queue on the first call to addQueues(). */ + arrayAppend(_state->queues, info); + _info.pQueueCreateInfos = _state->queues; + _info.queueCreateInfoCount = _state->queues.size(); + + return *this; +} + +Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const Containers::ArrayView enabledExtensions, const HandleFlags flags) { + /* Compared to the constructor nothing is printed here as it would be just + repeating what was passed to the constructor */ + Device out{NoCreate}; + out._handle = handle; + out._flags = flags; + out.initializeExtensions(enabledExtensions); + out.initialize(instance, version); + return out; +} + +Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const std::initializer_list enabledExtensions, const HandleFlags flags) { + return wrap(instance, handle, version, Containers::arrayView(enabledExtensions), flags); +} + +Device::Device(Instance& instance, const DeviceCreateInfo& info): + #ifdef CORRADE_GRACEFUL_ASSERT + _handle{}, /* Otherwise the destructor dies if we hit the queue assert */ + #endif + _flags{HandleFlag::DestroyOnDestruction} +{ + CORRADE_ASSERT(info._info.queueCreateInfoCount, + "Vk::Device: needs to be created with at least one queue", ); + + const Version version = info._state->version != Version::None ? info._state->version : DeviceProperties::wrap(instance, info._physicalDevice).apiVersion(); + + /* Print all enabled extensions if we're not told to be quiet */ + if(!info._state || !info._state->quietLog) { + Debug{} << "Device version:" << version; + + if(info->enabledExtensionCount) { + Debug{} << "Enabled device extensions:"; + for(std::size_t i = 0, max = info->enabledExtensionCount; i != max; ++i) + Debug{} << " " << info->ppEnabledExtensionNames[i]; + } + } + + MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->CreateDevice(info._physicalDevice, info, nullptr, &_handle)); + + initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); + initialize(instance, version); +} + +Device::Device(NoCreateT): _handle{}, _functionPointers{} {} + +Device::Device(Device&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} { + other._handle = nullptr; + other._functionPointers = {}; +} + +Device::~Device() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + _functionPointers.DestroyDevice(_handle, nullptr); +} + +Device& Device::operator=(Device&& other) noexcept { + using std::swap; + swap(other._handle, _handle); + swap(other._flags, _flags); + swap(other._version, _version); + swap(other._extensionStatus, _extensionStatus); + swap(other._state, _state); + swap(other._functionPointers, _functionPointers); + return *this; +} + +template void Device::initializeExtensions(const Containers::ArrayView enabledExtensions) { + /* Mark all known extensions as enabled */ + for(const T extension: enabledExtensions) { + for(Containers::ArrayView knownExtensions: { + Extension::extensions(Version::None), + /*Extension::extensions(Version::Vk10), is empty */ + Extension::extensions(Version::Vk11), + Extension::extensions(Version::Vk12) + }) { + auto found = std::lower_bound(knownExtensions.begin(), knownExtensions.end(), extension, [](const Extension& a, const T& b) { + return a.string() < static_cast(b); + }); + if(found->string() != extension) continue; + _extensionStatus.set(found->index(), true); + } + } +} + +void Device::initialize(Instance& instance, const Version version) { + /* Init version, function pointers */ + _version = version; + flextVkInitDevice(_handle, &_functionPointers, instance->GetDeviceProcAddr); + + /* Set up extension-dependent functionality */ + _state.emplace(*this); +} + +bool Device::isExtensionEnabled(const Extension& extension) const { + return _extensionStatus[extension.index()]; +} + +VkDevice Device::release() { + const VkDevice handle = _handle; + _handle = nullptr; + return handle; +} + +void Device::populateGlobalFunctionPointers() { + flextVkDevice = _functionPointers; +} + +}} diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index c707338d16..69a14742dc 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -26,18 +26,367 @@ */ /** @file - * @brief Nothing, haha + * @brief Class @ref Magnum::Vk::DeviceCreateInfo, @ref Magnum::Vk::Device * @m_since_latest */ #include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Math/BoolVector.h" +#include "Magnum/Vk/TypeTraits.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" namespace Magnum { namespace Vk { namespace Implementation { enum: std::size_t { ExtensionCount = 72 }; + + struct DeviceState; } +/** +@brief Device creation info +@m_since_latest + +Wraps a @type_vk_keyword{DeviceCreateInfo}. +@see @ref Device +*/ +class MAGNUM_VK_EXPORT DeviceCreateInfo { + public: + /** + * @brief Device creation flag + * + * Wraps @type_vk_keyword{DeviceCreateFlagBits}. + * @see @ref Flags, @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags) + */ + enum class Flag: UnsignedInt { + /* Any magnum-specific flags added here have to be filtered out + when passing them to _info.flags in the constructor. Using the + highest bits in a hope to prevent conflicts with Vulkan instance + flags added in the future. */ + + /** + * Don't implicitly enable any extensions. + * + * By default, the engine enables various extensions such as + * @vk_extension{KHR,get_memory_requirements2} to provide a broader + * functionality. If you want to have a complete control over what + * gets enabled, set this flag. + */ + NoImplicitExtensions = 1u << 31 + }; + + /** + * @brief Device creation flags + * + * Type-safe wrapper for @type_vk_keyword{DeviceCreateFlags}. + * @see @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags) + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param deviceProperties A device to use + * @param extensionProperties Existing @ref ExtensionProperties + * instance for querying available Vulkan extensions. If + * @cpp nullptr @ce, a new instance may be created internally if + * needed. + * @param flags Device creation flags + * + * The following @type_vk{DeviceCreateInfo} fields are pre-filled in + * addition to `sType`: + * + * - `flags` + * + * You need to call at least @ref addQueues() for a valid setup. + */ + explicit DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}); + + /** @overload */ + explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, extensionProperties, flags} {} + + /** @overload */ + explicit DeviceCreateInfo(DeviceProperties& deviceProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, nullptr, flags} {} + + /** @overload */ + explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, Flags flags = {}): DeviceCreateInfo{std::move(deviceProperties), nullptr, flags} {} + + /** + * @brief Construct for an implicitly picked device + * + * Calls @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags) + * with a device picked from @p instance using @ref pickDevice(). + */ + explicit DeviceCreateInfo(Instance& instance, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit DeviceCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info); + + ~DeviceCreateInfo(); + + /** + * @brief Add enabled device extensions + * @return Reference to self (for method chaining) + * + * All listed extensions are expected to be supported either globally + * or in at least one of the enabled layers, use + * @ref ExtensionProperties::isSupported() to check for their presence. + * + * The function makes copies of string views that are not owning or + * null-terminated, use the @link Containers::Literals::operator""_s() @endlink + * literal to prevent that where possible. + */ + DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + /** @overload */ + DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension); + /** @overload */ + DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + /** @overload */ + DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension); + /** @overload */ + template DeviceCreateInfo& addEnabledExtensions() { + static_assert(Implementation::IsExtension::value, "expected only Vulkan device extensions"); + return addEnabledExtensions({E{}...}); + } + + /** + * @brief Add queues + * @param family Family index, smaller than + * @ref DeviceProperties::queueFamilyCount() + * @param priorities Queue priorities. Size of the array implies how + * many queues to add and has to be at least one. + * @return Reference to self (for method chaining) + * + * At least one queue has to be added. + * @see @ref DeviceProperties::pickQueueFamily() + */ + DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities); + /** @overload */ + DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities); + + /** + * @brief Add queues using raw info + * @return Reference to self (for method chaining) + * + * Compared to @ref addQueues() this allows you to specify additional + * queue properties using the `pNext` chain. The info is uses as-is, + * with all pointers expected to stay in scope until device creation. + */ + DeviceCreateInfo& addQueues(const VkDeviceQueueCreateInfo& info); + + /** @brief Underlying @type_vk{DeviceCreateInfo} structure */ + VkDeviceCreateInfo& operator*() { return _info; } + /** @overload */ + const VkDeviceCreateInfo& operator*() const { return _info; } + /** @overload */ + VkDeviceCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkDeviceCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkDeviceCreateInfo*() const { return &_info; } + + private: + friend Device; + + VkPhysicalDevice _physicalDevice; + VkDeviceCreateInfo _info; + struct State; + Containers::Pointer _state; +}; + +CORRADE_ENUMSET_OPERATORS(DeviceCreateInfo::Flags) + +/** +@brief Device +@m_since_latest + +Wraps a @type_vk_keyword{Device} and stores all device-specific function +pointers. +*/ +class MAGNUM_VK_EXPORT Device { + public: + /** + * @brief Wrap existing Vulkan handle + * @param instance Vulkan instance the device is created on + * @param handle The @type_vk{Device} handle + * @param version Vulkan version that's assumed to be used on the + * instance + * @param enabledExtensions Extensions that are assumed to be enabled + * on the instance + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p instance. The + * @p version and @p enabledExtensions parameters populate internal + * info about supported version and extensions and will be reflected in + * @ref isVersionSupported() and @ref isExtensionEnabled(), among other + * things. If @p enabledExtensions is empty, the device will behave as + * if no extensions were enabled. + * + * Unlike a device created using a constructor, the Vulkan device is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Device wrap(Instance& instance, VkDevice handle, Version version, Containers::ArrayView enabledExtensions, HandleFlags flags = {}); + + /** @overload */ + static Device wrap(Instance& instance, VkDevice handle, Version version, std::initializer_list enabledExtensions, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param instance Vulkan instance to create the device on + * @param info Device creation info + * + * @see @fn_vk_keyword{CreateDevice} + */ + explicit Device(Instance& instance, const DeviceCreateInfo& info); + + /** + * @brief Construct without creating the device + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Device(NoCreateT); + + /** @brief Copying is not allowed */ + Device(const Device&) = delete; + + /** @brief Move constructor */ + Device(Device&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Device} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{DestroyDevice}, @ref release() + */ + ~Device(); + + /** @brief Copying is not allowed */ + Device& operator=(const Device&) = delete; + + /** @brief Move assignment */ + Device& operator=(Device&& other) noexcept; + + /** @brief Underlying @type_vk{Device} handle */ + VkDevice handle() { return _handle; } + /** @overload */ + operator VkDevice() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** @brief Device version */ + Version version() const { return _version; } + + /** @brief Whether given version is supported on the device */ + bool isVersionSupported(Version version) const { + return _version >= version; + } + + /** + * @brief Whether given extension is enabled + * + * Accepts device extensions from the @ref Extensions namespace, listed + * also in the @ref vulkan-support "Vulkan support tables". Search + * complexity is @f$ \mathcal{O}(1) @f$. Example usage: + * + * @snippet MagnumVk.cpp Device-isExtensionEnabled + * + * Note that this returns @cpp true @ce only if given extension is + * supported by the driver *and* it was enabled in + * @ref DeviceCreateInfo when creating the @ref Device. For querying + * extension support before creating a device use + * @ref ExtensionProperties::isSupported(). + */ + template bool isExtensionEnabled() const { + static_assert(Implementation::IsExtension::value, "expected a Vulkan device extension"); + return _extensionStatus[E::Index]; + } + + /** @overload */ + bool isExtensionEnabled(const Extension& extension) const; + + /** + * @brief Device-specific Vulkan function pointers + * + * Function pointers are implicitly stored per-device, use + * @ref populateGlobalFunctionPointers() to populate the global `vk*` + * functions. + */ + const FlextVkDevice& operator*() const { return _functionPointers; } + /** @overload */ + const FlextVkDevice* operator->() const { return &_functionPointers; } + + /** + * @brief Release the underlying Vulkan device + * + * Releases ownership of the Vulkan device and returns its handle so + * @fn_vk{DestroyDevice} is not called on destruction. The internal + * state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkDevice release(); + + /** + * @brief Populate global device-level function pointers to be used with third-party code + * + * Populates device-level global function pointers so third-party + * code is able to call global device-level `vk*` functions: + * + * @snippet MagnumVk.cpp Device-global-function-pointers + * + * Use @ref Instance::populateGlobalFunctionPointers() to populate + * instance-level global function pointers. + * @attention This operation is changing global state. You need to + * ensure that this function is not called simultaenously from + * multiple threads and code using those function points is + * calling them with the same device as the one returned by + * @ref handle(). + */ + void populateGlobalFunctionPointers(); + + #ifdef DOXYGEN_GENERATING_OUTPUT + private: + #endif + Implementation::DeviceState& state() { return *_state; } + + private: + template MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView enabledExtensions); + MAGNUM_VK_LOCAL void initialize(Instance& instance, Version version); + + VkDevice _handle; + HandleFlags _flags; + Version _version; + Math::BoolVector _extensionStatus; + Containers::Pointer _state; + + /* This member is bigger than you might think */ + FlextVkDevice _functionPointers; +}; + }} #endif diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 4e94bbb345..9a2efd0b22 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -288,8 +288,8 @@ class MAGNUM_VK_EXPORT DeviceProperties { /** * @brief Pick a queue family satisfying given flags - * @return Queue family index for use in @ref queueFamilySize() and - * @ref queueFamilyFlags() + * @return Queue family index for use in @ref queueFamilySize(), + * @ref queueFamilyFlags() and @ref DeviceCreateInfo::addQueues() * * Queries queue family properties using @ref queueFamilyProperties() * and tries to find the first that contains all @p flags. If it is not @@ -313,6 +313,7 @@ class MAGNUM_VK_EXPORT DeviceProperties { #ifndef DOXYGEN_GENERATING_OUTPUT /* The DAMN THING lists this among friends, which is AN IMPLEMENTATION DETAIL */ + friend DeviceCreateInfo; friend MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance&); #endif diff --git a/src/Magnum/Vk/Implementation/Arguments.cpp b/src/Magnum/Vk/Implementation/Arguments.cpp index a9fae5c736..9a62f80dc7 100644 --- a/src/Magnum/Vk/Implementation/Arguments.cpp +++ b/src/Magnum/Vk/Implementation/Arguments.cpp @@ -32,9 +32,10 @@ namespace Magnum { namespace Vk { namespace Implementation { Utility::Arguments arguments() { Utility::Arguments args{"magnum"}; args.addOption("disable-layers").setHelp("disable-layers", "Vulkan layers to disable", "LIST") - .addOption("disable-extensions").setHelp("disable-extensions", "Vulkan extensions to disable", "LIST") + .addOption("disable-extensions").setHelp("disable-extensions", "Vulkan instance or device extensions to disable", "LIST") .addOption("enable-layers").setHelp("enable-layers", "Vulkan layers to enable in addition to the defaults and what the application requests", "LIST") .addOption("enable-instance-extensions").setHelp("enable-instance-extensions", "Vulkan instance extensions to enable in addition to the defaults and what the application requests", "LIST") + .addOption("enable-extensions").setHelp("enable-extensions", "Vulkan device extensions to enable in addition to the defaults and what the application requests", "LIST") .addOption("vulkan-version").setHelp("vulkan-version", "force Vulkan version", "X.Y") .addOption("log", "default").setHelp("log", "console logging", "default|quiet|verbose") .addOption("device").setHelp("device", "device to use", "ID|integrated|discrete|virtual|cpu") @@ -42,6 +43,7 @@ Utility::Arguments arguments() { .setFromEnvironment("disable-extensions") .setFromEnvironment("enable-layers") .setFromEnvironment("enable-instance-extensions") + .setFromEnvironment("enable-extensions") .setFromEnvironment("vulkan-version") .setFromEnvironment("log") .setFromEnvironment("device"); diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp new file mode 100644 index 0000000000..7475ec1bd6 --- /dev/null +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -0,0 +1,32 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "DeviceState.h" + +namespace Magnum { namespace Vk { namespace Implementation { + +DeviceState::DeviceState(Device&) {} + +}}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h new file mode 100644 index 0000000000..1d09020b65 --- /dev/null +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -0,0 +1,40 @@ +#ifndef Magnum_Vk_Implementation_DeviceState_h +#define Magnum_Vk_Implementation_DeviceState_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" + +namespace Magnum { namespace Vk { namespace Implementation { + +struct DeviceState { + explicit DeviceState(Device& instance); +}; + +}}} + +#endif + diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index f04356a9c0..5c4e520d8a 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -364,6 +364,8 @@ class MAGNUM_VK_EXPORT Instance { * * @snippet MagnumVk.cpp Instance-global-function-pointers * + * Use @ref Device::populateGlobalFunctionPointers() to populate + * device-level global function pointers. * @attention This operation is changing global state. You need to * ensure that this function is not called simultaenously from * multiple threads and code using those function points is diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 9ec60218bb..15190d00f8 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) @@ -36,6 +37,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) diff --git a/src/Magnum/Vk/Test/DeviceTest.cpp b/src/Magnum/Vk/Test/DeviceTest.cpp new file mode 100644 index 0000000000..af372fa1bd --- /dev/null +++ b/src/Magnum/Vk/Test/DeviceTest.cpp @@ -0,0 +1,90 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Device.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DeviceTest: TestSuite::Tester { + explicit DeviceTest(); + + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +DeviceTest::DeviceTest() { + addTests({&DeviceTest::createInfoConstructNoInit, + &DeviceTest::createInfoConstructFromVk, + + &DeviceTest::constructNoCreate, + &DeviceTest::constructCopy}); +} + +void DeviceTest::createInfoConstructNoInit() { + DeviceCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) DeviceCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void DeviceTest::createInfoConstructFromVk() { + VkDeviceCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + DeviceCreateInfo info{VkPhysicalDevice{}, vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void DeviceTest::constructNoCreate() { + { + Device device{NoCreate}; + CORRADE_VERIFY(!device.handle()); + /* Device function pointers should be null */ + CORRADE_VERIFY(!device->CreateBuffer); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void DeviceTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceTest) diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp new file mode 100644 index 0000000000..f1c95f0527 --- /dev/null +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -0,0 +1,546 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include +#include +#include + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" + +#include "MagnumExternal/Vulkan/flextVkGlobal.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct DeviceVkTest: TestSuite::Tester { + explicit DeviceVkTest(); + + void createInfoConstruct(); + void createInfoConstructImplicitDevice(); + void createInfoConstructNoImplicitExtensions(); + void createInfoExtensions(); + void createInfoCopiedStrings(); + void createInfoNoQueuePriorities(); + + void construct(); + void constructExtensions(); + void constructExtensionsCommandLineDisable(); + void constructExtensionsCommandLineEnable(); + void constructMove(); + void constructUnknownExtension(); + void constructNoQueue(); + void wrap(); + void populateGlobalFunctionPointers(); + + Instance _instance; +}; + +struct { + const char* nameDisable; + const char* nameEnable; + Containers::Array argsDisable, argsEnable; + bool driverVersionSupported, debugMarkerEnabled, maintenance1Enabled; + const char* log; +} ConstructCommandLineData[] { + /* Shouldn't print anything about version, enabled layers/exts if quier + output is enabled. */ + {"quiet", "quiet, enabled extensions", + Containers::array({"", "--magnum-log", "quiet"}), + Containers::array({"", "--magnum-log", "quiet", + "--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), + true, true, true, + ""}, + {"", "enabled extensions", nullptr, + Containers::array({"", + "--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), + true, true, true, + "Device version: Vulkan {}.{}{}\n" + "Enabled device extensions:\n" + " VK_EXT_debug_marker\n" + " VK_KHR_maintenance1\n"}, + {"forced version", "forced version, enabled extensions", + Containers::array({"", + "--magnum-vulkan-version", "1.0"}), + Containers::array({"", + "--magnum-vulkan-version", "1.0", + "--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), + false, true, true, + "Device version: Vulkan 1.0\n" + "Enabled device extensions:\n" + " VK_EXT_debug_marker\n" + " VK_KHR_maintenance1\n"}, + {"disabled one extension", "enabled one extension", + Containers::array({"", + "--magnum-disable-extensions", "VK_EXT_debug_marker"}), + Containers::array({"", + "--magnum-enable-extensions", "VK_KHR_maintenance1"}), + true, false, true, + "Device version: Vulkan {}.{}{}\n" + "Enabled device extensions:\n" + " VK_KHR_maintenance1\n"}, + {"disabled extensions", "", + Containers::array({"", + "--magnum-disable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), + nullptr, + true, false, false, + "Device version: Vulkan {}.{}{}\n"}, +}; + +DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { + addTests({&DeviceVkTest::createInfoConstruct, + &DeviceVkTest::createInfoConstructImplicitDevice, + &DeviceVkTest::createInfoConstructNoImplicitExtensions, + &DeviceVkTest::createInfoExtensions, + &DeviceVkTest::createInfoCopiedStrings, + &DeviceVkTest::createInfoNoQueuePriorities, + + &DeviceVkTest::construct, + &DeviceVkTest::constructExtensions}); + + addInstancedTests({&DeviceVkTest::constructExtensionsCommandLineDisable, + &DeviceVkTest::constructExtensionsCommandLineEnable}, + Containers::arraySize(ConstructCommandLineData)); + + addTests({&DeviceVkTest::constructMove, + &DeviceVkTest::constructUnknownExtension, + &DeviceVkTest::constructNoQueue, + + &DeviceVkTest::wrap, + &DeviceVkTest::populateGlobalFunctionPointers}); +} + +using namespace Containers::Literals; + +void DeviceVkTest::createInfoConstruct() { + DeviceCreateInfo info{pickDevice(_instance)}; + CORRADE_VERIFY(info->sType); + CORRADE_VERIFY(!info->pNext); + /* Extensions might or might not be enabled */ +} + +void DeviceVkTest::createInfoConstructImplicitDevice() { + DeviceCreateInfo info{_instance}; + CORRADE_VERIFY(info->sType); + CORRADE_VERIFY(!info->pNext); + /* Extensions might or might not be enabled */ +} + +void DeviceVkTest::createInfoConstructNoImplicitExtensions() { + DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + CORRADE_VERIFY(info->sType); + CORRADE_VERIFY(!info->pNext); + /* No extensions enabled as we explicitly disabled that */ + CORRADE_VERIFY(!info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 0); +} + +void DeviceVkTest::createInfoExtensions() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + CORRADE_VERIFY(!info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 0); + + info.addEnabledExtensions(); + CORRADE_VERIFY(info->ppEnabledExtensionNames); + CORRADE_COMPARE(info->enabledExtensionCount, 1); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[0]), + Extensions::KHR::maintenance1::string().data()); + + info.addEnabledExtensions({ + Extensions::KHR::draw_indirect_count{}, + Extensions::KHR::get_memory_requirements2{} + }); + CORRADE_COMPARE(info->enabledExtensionCount, 3); + /* The pointer should be to the global data */ + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[0]), + Extensions::KHR::maintenance1::string().data()); + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[1]), + Extensions::KHR::draw_indirect_count::string().data()); + CORRADE_COMPARE(static_cast(info->ppEnabledExtensionNames[2]), + Extensions::KHR::get_memory_requirements2::string().data()); +} + +void DeviceVkTest::createInfoCopiedStrings() { + Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1); + Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string(); + + DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + info.addEnabledExtensions({ + globalButNotNullTerminated, + localButNullTerminated + }); + CORRADE_COMPARE(info->enabledExtensionCount, 2); + + CORRADE_COMPARE(info->ppEnabledExtensionNames[0], globalButNotNullTerminated); + CORRADE_VERIFY(info->ppEnabledExtensionNames[0] != globalButNotNullTerminated.data()); + + CORRADE_COMPARE(info->ppEnabledExtensionNames[1], localButNullTerminated); + CORRADE_VERIFY(info->ppEnabledExtensionNames[1] != localButNullTerminated.data()); +} + +void DeviceVkTest::createInfoNoQueuePriorities() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + DeviceCreateInfo{_instance}.addQueues(0, {}); + CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); +} + +void DeviceVkTest::construct() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + + { + DeviceProperties deviceProperties = pickDevice(_instance); + Device device{_instance, DeviceCreateInfo{deviceProperties} + .addQueues(0, {0.0f}) + }; + CORRADE_VERIFY(device.handle()); + /* Device function pointers should be populated */ + CORRADE_VERIFY(device->CreateBuffer); + CORRADE_COMPARE(device.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(device.version(), deviceProperties.apiVersion()); + /* Device version is supported */ + CORRADE_VERIFY(device.isVersionSupported(deviceProperties.apiVersion())); + CORRADE_VERIFY(!device.isVersionSupported(Version::None)); + /* No extensions are enabled by default ... */ + CORRADE_VERIFY(!device.isExtensionEnabled()); + /* ... and thus also no function pointers loaded */ + CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void DeviceVkTest::constructExtensions() { + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + /* Creating a dedicated instance so we can pass custom args and enable + layers & exts independently */ + Instance instance{InstanceCreateInfo{} + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) + /* Needed by VK_EXT_debug_marker */ + .addEnabledExtensions() + }; + + DeviceProperties deviceProperties = pickDevice(instance); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + + Device device{instance, DeviceCreateInfo{deviceProperties} + .addQueues(0, {0.0f}) + .addEnabledExtensions({ + Extensions::EXT::debug_marker::string(), + "VK_KHR_maintenance1"_s + })}; + CORRADE_VERIFY(device.handle()); + + /* Extensions should be reported as enabled ... */ + CORRADE_VERIFY(device.isExtensionEnabled()); + CORRADE_VERIFY(device.isExtensionEnabled(Extensions::KHR::maintenance1{})); + /* ... and function pointers loaded */ + CORRADE_VERIFY(device->CmdDebugMarkerInsertEXT); + CORRADE_VERIFY(device->TrimCommandPoolKHR); +} + +void DeviceVkTest::constructExtensionsCommandLineDisable() { + auto&& data = ConstructCommandLineData[testCaseInstanceId()]; + setTestCaseDescription(data.nameDisable); + + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_LAYERS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + /* Creating a dedicated instance so we can pass custom args and enable + layers independently */ + Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) + /* Needed by VK_EXT_debug_marker */ + .addEnabledExtensions() + }; + + DeviceProperties deviceProperties = pickDevice(instance); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + + std::ostringstream out; + Debug redirectOutput{&out}; + Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} + .addQueues(0, {0.0f}) + .addEnabledExtensions< + Extensions::EXT::debug_marker, + Extensions::KHR::maintenance1 + >()}; + CORRADE_VERIFY(device.handle()); + CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); + CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); + CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); + + /** @todo cleanup when Debug::toString() or some similar utility exists */ + UnsignedInt major = versionMajor(deviceProperties.apiVersion()); + UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); + UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); + /* SwiftShader reports just 1.1 with no patch version, special-case that */ + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + + /* Verify that the entrypoint is actually (not) loaded as expected, to + avoid all the above reporting being just smoke & mirrors */ + CORRADE_COMPARE(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled); + CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); +} + +void DeviceVkTest::constructExtensionsCommandLineEnable() { + auto&& data = ConstructCommandLineData[testCaseInstanceId()]; + setTestCaseDescription(data.nameEnable); + + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_LAYERS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + /* Creating a dedicated instance so we can pass custom args and enable + layers independently */ + Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) + /* Needed by VK_EXT_debug_marker */ + .addEnabledExtensions() + }; + + DeviceProperties deviceProperties = pickDevice(instance); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + + std::ostringstream out; + Debug redirectOutput{&out}; + Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions} + .addQueues(0, {0.0f}) + /* Nothing enabled by the application */ + }; + CORRADE_VERIFY(device.handle()); + CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); + CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); + CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); + + /** @todo cleanup when Debug::toString() or some similar utility exists */ + UnsignedInt major = versionMajor(deviceProperties.apiVersion()); + UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); + UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); + /* SwiftShader reports just 1.1 with no patch version, special-case that */ + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + + /* Verify that the entrypoint is actually (not) loaded as expected, to + avoid all the above reporting being just smoke & mirrors */ + CORRADE_COMPARE(!!device->CmdDebugMarkerInsertEXT, data.debugMarkerEnabled); + CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); +} + +void DeviceVkTest::constructMove() { + DeviceProperties deviceProperties = pickDevice(_instance); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + + Device a{_instance, DeviceCreateInfo{deviceProperties} + .addQueues(0, {0.0f}) + .addEnabledExtensions() + }; + VkDevice handle = a.handle(); + Version version = a.version(); + CORRADE_VERIFY(handle); + CORRADE_VERIFY(version != Version{}); + CORRADE_VERIFY(version != Version::None); + + Device b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.version(), version); + CORRADE_VERIFY(b.isExtensionEnabled()); + /* Function pointers in a are left in whatever state they were before, as + that doesn't matter */ + CORRADE_VERIFY(b->CreateBuffer); + + Device c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.version(), version); + CORRADE_VERIFY(c.isExtensionEnabled()); + /* Everything is swapped, including function pointers */ + CORRADE_VERIFY(!b->CreateBuffer); + CORRADE_VERIFY(c->CreateBuffer); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void DeviceVkTest::constructUnknownExtension() { + CORRADE_SKIP("Currently this hits an internal assert, which can't be tested."); + + std::ostringstream out; + Error redirectError{&out}; + Device device{_instance, DeviceCreateInfo{_instance} + .addQueues(0, {0.0f}) + .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; + CORRADE_COMPARE(out.str(), "TODO"); +} + +void DeviceVkTest::constructNoQueue() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + Device device{_instance, DeviceCreateInfo{_instance}}; + CORRADE_COMPARE(out.str(), "Vk::Device: needs to be created with at least one queue\n"); +} + +void DeviceVkTest::wrap() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + if(std::getenv("MAGNUM_DISABLE_LAYERS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_LAYERS environment variable set"); + if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) + CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); + + if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) + CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); + + /* Creating a dedicated instance so we can enable layers independently */ + Instance instance{InstanceCreateInfo{} + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) + /* Needed by VK_EXT_debug_marker */ + .addEnabledExtensions() + }; + + DeviceProperties deviceProperties = pickDevice(instance); + ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); + if(!extensions.isSupported()) + CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + + VkDevice device; + CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties, + DeviceCreateInfo{instance} + .addQueues(0, {0.0f}) + .addEnabledExtensions< + Extensions::EXT::debug_marker, + Extensions::KHR::maintenance1 + >(), + nullptr, &device)), Result::Success); + CORRADE_VERIFY(device); + + { + /* Wrapping should load the basic function pointers */ + auto wrapped = Device::wrap(instance, device, Version::Vk11, { + Extensions::EXT::debug_marker::string() + }, HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(wrapped->DestroyDevice); + + /* Specified version should be reported as supported but higher not + regardless of the actual driver version */ + CORRADE_VERIFY(wrapped.isVersionSupported(Version::Vk11)); + CORRADE_VERIFY(!wrapped.isVersionSupported(Version::Vk12)); + + /* Listed extensions should be reported as enabled and function + pointers loaded as well */ + CORRADE_VERIFY(wrapped.isExtensionEnabled()); + CORRADE_VERIFY(wrapped->CmdDebugMarkerInsertEXT); + + /* Unlisted not, but function pointers should still be loaded as the + actual instance does have the extension enabled */ + CORRADE_VERIFY(!wrapped.isExtensionEnabled()); + CORRADE_VERIFY(wrapped->TrimCommandPoolKHR); + + /* Releasing won't destroy anything ... */ + CORRADE_COMPARE(wrapped.release(), device); + } + + /* ...so we can wrap it again, non-owned, and then destroy it manually */ + auto wrapped = Device::wrap(instance, device, Version::Vk10, {}); + CORRADE_VERIFY(wrapped->DestroyDevice); + wrapped->DestroyDevice(device, nullptr); +} + +void DeviceVkTest::populateGlobalFunctionPointers() { + vkDestroyDevice = nullptr; + + Device device{_instance, DeviceCreateInfo{_instance} + .addQueues(0, {0.0f}) + }; + CORRADE_VERIFY(!vkDestroyDevice); + device.populateGlobalFunctionPointers(); + CORRADE_VERIFY(vkDestroyDevice); + CORRADE_VERIFY(vkDestroyDevice == device->DestroyDevice); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::DeviceVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 1b618f40e9..89f2af7a43 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,8 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class Device; +class DeviceCreateInfo; class DeviceProperties; enum class DeviceType: Int; class Extension; From 84e1939ee1631fa02d3791929b62cd10468f1be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 11 Jul 2020 00:56:17 +0200 Subject: [PATCH 24/85] external: no need to export the flextVkInit*() functions anymore. --- src/MagnumExternal/Vulkan/flextVk.h | 4 ++-- src/MagnumExternal/Vulkan/flextVk.h.template | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/MagnumExternal/Vulkan/flextVk.h b/src/MagnumExternal/Vulkan/flextVk.h index ffffe47de0..394c08dd6d 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h +++ b/src/MagnumExternal/Vulkan/flextVk.h @@ -4387,7 +4387,7 @@ struct FlextVkInstance { }; /* Per-instance function pointer initialization */ -void FLEXTVK_EXPORT flextVkInitInstance(VkInstance instance, FlextVkInstance* data); +void flextVkInitInstance(VkInstance instance, FlextVkInstance* data); /* Per-device function pointers */ struct FlextVkDevice { @@ -4653,7 +4653,7 @@ struct FlextVkDevice { }; /* Per-device function pointer initialization */ -void FLEXTVK_EXPORT flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction(VKAPI_PTR *getDeviceProcAddr)(VkDevice, const char*)); +void flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction(VKAPI_PTR *getDeviceProcAddr)(VkDevice, const char*)); #endif diff --git a/src/MagnumExternal/Vulkan/flextVk.h.template b/src/MagnumExternal/Vulkan/flextVk.h.template index b475864045..069dbb3e76 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h.template +++ b/src/MagnumExternal/Vulkan/flextVk.h.template @@ -140,7 +140,7 @@ struct FlextVkInstance { }; /* Per-instance function pointer initialization */ -void FLEXTVK_EXPORT flextVkInitInstance(VkInstance instance, FlextVkInstance* data); +void flextVkInitInstance(VkInstance instance, FlextVkInstance* data); /* Per-device function pointers */ struct FlextVkDevice { @@ -160,7 +160,7 @@ struct FlextVkDevice { }; /* Per-device function pointer initialization */ -void FLEXTVK_EXPORT flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction(VKAPI_PTR *getDeviceProcAddr)(VkDevice, const char*)); +void flextVkInitDevice(VkDevice device, FlextVkDevice* data, PFN_vkVoidFunction(VKAPI_PTR *getDeviceProcAddr)(VkDevice, const char*)); #endif From d23b647b43fbf75158bbccd8fffec0ba573261cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 11 Jul 2020 20:48:47 +0200 Subject: [PATCH 25/85] package/archlinux: run Vulkan tests with SwiftShader as well. --- package/archlinux/PKGBUILD | 9 +++++++-- package/archlinux/PKGBUILD-coverage | 8 ++++++-- package/archlinux/PKGBUILD-release | 9 +++++++-- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 37556ce90c..ae52781b56 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -8,6 +8,7 @@ url="https://magnum.graphics" license=('MIT') depends=('corrade' 'openal' 'sdl2' 'glfw' 'vulkan-icd-loader') makedepends=('cmake' 'ninja') +checkdepends=('swiftshader') options=('!strip') provides=('magnum-git') @@ -71,8 +72,12 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + # Run all Vulkan tests with SwiftShader as well + MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + for device in "" cpu; do + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + done } package() { diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index ab80798f5b..1d110e6845 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -73,8 +73,12 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest || true - MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true - MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + # Run all Vulkan tests with SwiftShader as well + MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + for device in "" cpu; do + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + done ./Debug/bin/magnum-al-info > /dev/null ./Debug/bin/magnum-gl-info --limits > /dev/null diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index ebed4bc85a..a055f111b1 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -8,6 +8,7 @@ url="https://magnum.graphics" license=('MIT') depends=('corrade' 'openal' 'sdl2' 'glfw' 'vulkan-icd-loader') makedepends=('cmake' 'ninja') +checkdepends=('swiftshader') options=('!strip') provides=('magnum-git') @@ -109,8 +110,12 @@ check() { MAGNUM_DISABLE_EXTENSIONS="GL_ARB_vertex_array_object" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest MAGNUM_DISABLE_EXTENSIONS="GL_KHR_debug" CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R GLTest - MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + # Run all Vulkan tests with SwiftShader as well + MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + for device in "" cpu; do + MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + done done } From f75cc9330c363c088526c5530251dbfb216908ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 15 Jul 2020 18:51:49 +0200 Subject: [PATCH 26/85] Vk: a non-error-prone way to retrieve queues after device creation. I wonder why this couldn't be done this way in the Vulkan API directly. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 1 + src/Magnum/Vk/Device.cpp | 60 +++++++- src/Magnum/Vk/Device.h | 23 +++- src/Magnum/Vk/Implementation/DeviceState.cpp | 11 +- src/Magnum/Vk/Implementation/DeviceState.h | 3 +- src/Magnum/Vk/Queue.h | 88 ++++++++++++ src/Magnum/Vk/Test/DeviceVkTest.cpp | 136 +++++++++++++++++-- src/Magnum/Vk/Vk.h | 1 + 9 files changed, 299 insertions(+), 26 deletions(-) create mode 100644 src/Magnum/Vk/Queue.h diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index eaea4a38db..a59e501161 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -194,7 +194,7 @@ Vulkan function | Matching API @fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceMemoryCommitment} | | @fn_vk{GetDeviceProcAddr} | @ref Device constructor -@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | | +@fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | @ref Device constructor @fn_vk{GetEventStatus} | | @fn_vk{GetFenceStatus} | | @fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 2ea9e44e42..1b1d4cc744 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -54,6 +54,7 @@ set(MagnumVk_HEADERS Instance.h Integration.h LayerProperties.h + Queue.h Result.h TypeTraits.h Version.h diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index a98411139f..dcfad66a17 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -40,6 +40,7 @@ #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" @@ -57,6 +58,7 @@ struct DeviceCreateInfo::State { Containers::Array disabledExtensions; Containers::Array queues; Containers::StaticArray<32, Float> queuePriorities; + Containers::StaticArray<32, Queue*> queueOutput; std::size_t nextQueuePriority = 0; bool quietLog = false; @@ -182,8 +184,9 @@ DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_ return addEnabledExtensions(Containers::arrayView(extensions)); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::ArrayView priorities) { +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const Containers::ArrayView priorities, const Containers::ArrayView> output) { CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this); + CORRADE_ASSERT(output.size() == priorities.size(), "Vk::DeviceCreateInfo::addQueues(): expected" << priorities.size() << "outuput queue references but got" << output.size(), *this); /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ @@ -195,19 +198,22 @@ DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, Containers::Ar info.queueCount = priorities.size(); info.pQueuePriorities = _state->queuePriorities + _state->nextQueuePriority; - /* Copy the passed queue priorities to an internal storage that never - reallocates. If this blows up, see the definition of queuePriorities for - details. We can't easily reallocate if this grows too big as all - pointers would need to be patched, so there's a static limit. */ + /* Copy the passed queue priorities and output queue references to an + internal storage that never reallocates. If this blows up, see the + definition of queuePriorities for details. We can't easily reallocate if + this grows too big as all pointers would need to be patched, so there's + a static limit. */ CORRADE_INTERNAL_ASSERT(_state->nextQueuePriority + priorities.size() <= _state->queuePriorities.size()); Utility::copy(priorities, _state->queuePriorities.suffix(_state->nextQueuePriority).prefix(priorities.size())); + for(std::size_t i = 0; i != priorities.size(); ++i) + _state->queueOutput[_state->nextQueuePriority + i] = &*output[i]; _state->nextQueuePriority += priorities.size(); return addQueues(info); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(UnsignedInt family, std::initializer_list priorities) { - return addQueues(family, Containers::arrayView(priorities)); +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const std::initializer_list priorities, const std::initializer_list> output) { + return addQueues(family, Containers::arrayView(priorities), Containers::arrayView(output)); } DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) { @@ -266,6 +272,38 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info): initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); initialize(instance, version); + + /* Extension-dependent state is initialized, now we can retrieve the queues + from the device and save them to the outputs specified in addQueues(). + Each of those calls added one or more entries into _state->queueOutput, + maintain an offset into it. */ + UnsignedInt queueOutputIndex = 0; + for(const VkDeviceQueueCreateInfo& createInfo: info._state->queues) { + /* If the info structure doesn't point into our priority array, it + means it was added with the addQueues(VkDeviceQueueCreateInfo) + overload. For that we didn't remember any output, thus skip it */ + if(createInfo.pQueuePriorities < info._state->queuePriorities.begin() || + createInfo.pQueuePriorities >= info._state->queuePriorities.end()) + continue; + + for(UnsignedInt i = 0; i != createInfo.queueCount; ++i) { + VkDeviceQueueInfo2 requestInfo{}; + requestInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2; + requestInfo.queueFamilyIndex = createInfo.queueFamilyIndex; + /* According to the spec we can request each family only once, + which means here we don't need to remember the per-family index + across multiple VkDeviceQueueCreateInfos, making the + implementation a bit simpler. */ + requestInfo.queueIndex = i; + + /* Retrieve the queue handle, create a new Queue object in desired + output location, and increment the output location for the next + queue */ + VkQueue queue; + _state->getDeviceQueueImplementation(*this, requestInfo, queue); + *info._state->queueOutput[queueOutputIndex++] = Queue::wrap(*this, queue); + } + } } Device::Device(NoCreateT): _handle{}, _functionPointers{} {} @@ -332,4 +370,12 @@ void Device::populateGlobalFunctionPointers() { flextVkDevice = _functionPointers; } +void Device::getQueueImplementation11(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue) { + return self->GetDeviceQueue2(self, &info, &queue); +} + +void Device::getQueueImplementationDefault(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue) { + return self->GetDeviceQueue(self, info.queueFamilyIndex, info.queueIndex, &queue); +} + }} diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 69a14742dc..98ddb292cf 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -32,6 +32,7 @@ #include #include +#include #include "Magnum/Tags.h" #include "Magnum/Math/BoolVector.h" @@ -169,18 +170,20 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { /** * @brief Add queues - * @param family Family index, smaller than + * @param[in] family Family index, smaller than * @ref DeviceProperties::queueFamilyCount() - * @param priorities Queue priorities. Size of the array implies how - * many queues to add and has to be at least one. + * @param[in] priorities Queue priorities. Size of the array implies + * how many queues to add and has to be at least one. + * @param[out] output Where to save resulting queues once the + * device is created. Has to have the same sizes as @p priorities. * @return Reference to self (for method chaining) * * At least one queue has to be added. * @see @ref DeviceProperties::pickQueueFamily() */ - DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities); + DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output); /** @overload */ - DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities); + DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output); /** * @brief Add queues using raw info @@ -255,7 +258,10 @@ class MAGNUM_VK_EXPORT Device { * @param instance Vulkan instance to create the device on * @param info Device creation info * - * @see @fn_vk_keyword{CreateDevice} + * After creating the device requests device queues added via + * @ref DeviceCreateInfo::addQueues(UnsignedInt, Containers::ArrayView, Containers::ArrayView>), populating the @ref Queue references. + * @see @fn_vk_keyword{CreateDevice}, @fn_vk_keyword{GetDeviceQueue2}, + * @fn_vk_keyword{GetDeviceQueue} */ explicit Device(Instance& instance, const DeviceCreateInfo& info); @@ -374,9 +380,14 @@ class MAGNUM_VK_EXPORT Device { Implementation::DeviceState& state() { return *_state; } private: + friend Implementation::DeviceState; + template MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView enabledExtensions); MAGNUM_VK_LOCAL void initialize(Instance& instance, Version version); + MAGNUM_VK_LOCAL static void getQueueImplementationDefault(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue); + MAGNUM_VK_LOCAL static void getQueueImplementation11(Device& self, const VkDeviceQueueInfo2& info, VkQueue& queue); + VkDevice _handle; HandleFlags _flags; Version _version; diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 7475ec1bd6..79d481606b 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -25,8 +25,17 @@ #include "DeviceState.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Version.h" + namespace Magnum { namespace Vk { namespace Implementation { -DeviceState::DeviceState(Device&) {} +DeviceState::DeviceState(Device& device) { + if(device.isVersionSupported(Version::Vk11)) { + getDeviceQueueImplementation = &Device::getQueueImplementation11; + } else { + getDeviceQueueImplementation = &Device::getQueueImplementationDefault; + } +} }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 1d09020b65..e350756261 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -32,9 +32,10 @@ namespace Magnum { namespace Vk { namespace Implementation { struct DeviceState { explicit DeviceState(Device& instance); + + void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); }; }}} #endif - diff --git a/src/Magnum/Vk/Queue.h b/src/Magnum/Vk/Queue.h new file mode 100644 index 0000000000..b53a03add5 --- /dev/null +++ b/src/Magnum/Vk/Queue.h @@ -0,0 +1,88 @@ +#ifndef Magnum_Vk_Queue_h +#define Magnum_Vk_Queue_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::Queue + * @m_since_latest + */ + +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Queue +@m_since_latest + +Wraps a @type_vk_keyword{Queue}. +@see @ref DeviceCreateInfo::addQueues() +*/ +class MAGNUM_VK_EXPORT Queue { + public: + /** + * @brief Wrap existing Vulkan queue + * @param device Vulkan device + * @param handle The @type_vk{Queue} handle + * + * The @p handle is expected to be originating from @p device. Unlike + * with other handle types, the @type_vk{Queue} handles don't have to + * be destroyed at the end, so there's no equivalent of e.g. + * @ref Device::release() or @ref Device::handleFlags(). + */ + static Queue wrap(Device& device, VkQueue handle) { + Queue out{NoCreate}; + out._device = &device; + out._handle = handle; + return out; + } + + /** + * @brief Construct without creating the instance + * + * This is the expected way to create a queue that's later populated + * on @ref Device creation through @ref DeviceCreateInfo::addQueues(). + */ + explicit Queue(NoCreateT): _device{}, _handle{} {} + + /** @brief Underlying @type_vk{Queue} handle */ + VkQueue handle() { return _handle; } + /** @overload */ + operator VkQueue() { return _handle; } + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkQueue _handle; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index f1c95f0527..3656f447ab 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -37,6 +37,7 @@ #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" @@ -53,11 +54,14 @@ struct DeviceVkTest: TestSuite::Tester { void createInfoExtensions(); void createInfoCopiedStrings(); void createInfoNoQueuePriorities(); + void createInfoWrongQueueOutputCount(); void construct(); void constructExtensions(); void constructExtensionsCommandLineDisable(); void constructExtensionsCommandLineEnable(); + void constructMultipleQueues(); + void constructRawQueue(); void constructMove(); void constructUnknownExtension(); void constructNoQueue(); @@ -125,6 +129,7 @@ DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, ar &DeviceVkTest::createInfoExtensions, &DeviceVkTest::createInfoCopiedStrings, &DeviceVkTest::createInfoNoQueuePriorities, + &DeviceVkTest::createInfoWrongQueueOutputCount, &DeviceVkTest::construct, &DeviceVkTest::constructExtensions}); @@ -133,7 +138,10 @@ DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, ar &DeviceVkTest::constructExtensionsCommandLineEnable}, Containers::arraySize(ConstructCommandLineData)); - addTests({&DeviceVkTest::constructMove, + addTests({&DeviceVkTest::constructMultipleQueues, + &DeviceVkTest::constructRawQueue, + + &DeviceVkTest::constructMove, &DeviceVkTest::constructUnknownExtension, &DeviceVkTest::constructNoQueue, @@ -220,18 +228,31 @@ void DeviceVkTest::createInfoNoQueuePriorities() { std::ostringstream out; Error redirectError{&out}; - DeviceCreateInfo{_instance}.addQueues(0, {}); + DeviceCreateInfo{_instance}.addQueues(0, {}, {}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); } +void DeviceVkTest::createInfoWrongQueueOutputCount() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + Queue a{NoCreate}, b{NoCreate}; + DeviceCreateInfo{_instance}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); + CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); +} + void DeviceVkTest::construct() { if(std::getenv("MAGNUM_VULKAN_VERSION")) CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); { DeviceProperties deviceProperties = pickDevice(_instance); + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); /* Device function pointers should be populated */ @@ -245,6 +266,9 @@ void DeviceVkTest::construct() { CORRADE_VERIFY(!device.isExtensionEnabled()); /* ... and thus also no function pointers loaded */ CORRADE_VERIFY(!device->CmdDebugMarkerInsertEXT); + + /* The queue should be also filled in */ + CORRADE_VERIFY(queue.handle()); } /* Shouldn't crash or anything */ @@ -273,8 +297,9 @@ void DeviceVkTest::constructExtensions() { if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({ Extensions::EXT::debug_marker::string(), "VK_KHR_maintenance1"_s @@ -320,8 +345,9 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { std::ostringstream out; Debug redirectOutput{&out}; + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 @@ -375,8 +401,9 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { std::ostringstream out; Debug redirectOutput{&out}; + Queue queue{NoCreate}; Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) /* Nothing enabled by the application */ }; CORRADE_VERIFY(device.handle()); @@ -397,14 +424,97 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { CORRADE_COMPARE(!!device->TrimCommandPoolKHR, data.maintenance1Enabled); } +void DeviceVkTest::constructMultipleQueues() { + /* Find a GPU that has at least two queue families and at least four + queues in one family */ + Containers::Array deviceProperties = enumerateDevices(_instance); + + DeviceProperties* deviceWithMultipleQueues = nullptr; + UnsignedInt largeFamily = ~UnsignedInt{}; + for(DeviceProperties& i: deviceProperties) { + if(i.queueFamilyCount() < 2) continue; + for(UnsignedInt family = 0; family != i.queueFamilyCount(); ++family) { + if(i.queueFamilySize(family) < 4) continue; + largeFamily = family; + break; + } + + deviceWithMultipleQueues = &i; + break; + } + + if(!deviceWithMultipleQueues || largeFamily == ~UnsignedInt{}) + CORRADE_SKIP("No device with at least two queue families and at least four queues in one family found, can't test"); + + const UnsignedInt otherFamily = largeFamily == 0 ? 1 : 0; + + constexpr Float zero = 0.0f; + VkDeviceQueueCreateInfo rawQueueInfo{}; + rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + rawQueueInfo.pQueuePriorities = &zero; + rawQueueInfo.queueFamilyIndex = otherFamily; + rawQueueInfo.queueCount = 1; + + Queue a{NoCreate}, b{NoCreate}, c{NoCreate}; + Device device{_instance, DeviceCreateInfo{*deviceWithMultipleQueues} + /* Request a raw queue in the middle of it all to test we skip it when + populating the outputs, and correctly offset the next IDs. According + to the spec we can request each family only once, which makes the + implementation and testing slightly simpler. */ + .addQueues(rawQueueInfo) + /* Request multiple queues in a single family to test we correctly loop + over these as well */ + .addQueues(largeFamily, {0.5f, 0.75f, 1.0f}, {a, b, c})}; + + /* All queues should be found and different */ + CORRADE_VERIFY(a.handle()); + CORRADE_VERIFY(b.handle()); + CORRADE_VERIFY(c.handle()); + CORRADE_VERIFY(a.handle() != b.handle()); + CORRADE_VERIFY(a.handle() != c.handle()); + CORRADE_VERIFY(b.handle() != c.handle()); + + /* Fetching the same queue again should give the same handle */ + VkQueue aAgain; + device->GetDeviceQueue(device, largeFamily, 0, &aAgain); + CORRADE_COMPARE(aAgain, a.handle()); + + /* Fetch the raw queue, should be different from everything else as well */ + VkQueue rawQueue; + device->GetDeviceQueue(device, otherFamily, 0, &rawQueue); + CORRADE_VERIFY(rawQueue); + CORRADE_VERIFY(rawQueue != a.handle()); + CORRADE_VERIFY(rawQueue != b.handle()); + CORRADE_VERIFY(rawQueue != c.handle()); +} + +void DeviceVkTest::constructRawQueue() { + /* Testing a subset of constructQueues() again because not all drivers + have multiple queues and we want to have the coverage */ + constexpr Float zero = 0.0f; + VkDeviceQueueCreateInfo rawQueueInfo{}; + rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + rawQueueInfo.pQueuePriorities = &zero; + rawQueueInfo.queueFamilyIndex = 0; + rawQueueInfo.queueCount = 1; + Device device{_instance, DeviceCreateInfo{_instance} + .addQueues(rawQueueInfo)}; + + /* Fetch the raw queue */ + VkQueue rawQueue; + device->GetDeviceQueue(device, 0, 0, &rawQueue); + CORRADE_VERIFY(rawQueue); +} + void DeviceVkTest::constructMove() { DeviceProperties deviceProperties = pickDevice(_instance); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); + Queue queue{NoCreate}; Device a{_instance, DeviceCreateInfo{deviceProperties} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions() }; VkDevice handle = a.handle(); @@ -444,8 +554,9 @@ void DeviceVkTest::constructUnknownExtension() { std::ostringstream out; Error redirectError{&out}; + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{_instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; CORRADE_COMPARE(out.str(), "TODO"); } @@ -487,15 +598,19 @@ void DeviceVkTest::wrap() { CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); VkDevice device; + Queue queue{NoCreate}; CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties, DeviceCreateInfo{instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, Extensions::KHR::maintenance1 >(), nullptr, &device)), Result::Success); CORRADE_VERIFY(device); + /* Populating the queue handle is done only from Device itself, so it won't + happen here -- would need to call vkGetDeviceQueue[2] directly */ + CORRADE_VERIFY(!queue.handle()); { /* Wrapping should load the basic function pointers */ @@ -532,8 +647,9 @@ void DeviceVkTest::wrap() { void DeviceVkTest::populateGlobalFunctionPointers() { vkDestroyDevice = nullptr; + Queue queue{NoCreate}; Device device{_instance, DeviceCreateInfo{_instance} - .addQueues(0, {0.0f}) + .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(!vkDestroyDevice); device.populateGlobalFunctionPointers(); diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 89f2af7a43..3cb353a7a0 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -49,6 +49,7 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; enum class Result: Int; From e5927fc403797ca0f5bd35226d2bb365ce0dbf17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Sep 2020 16:44:33 +0200 Subject: [PATCH 27/85] package/ci: split Vulkan compile-test and render-test jobs. On Ubuntu, because SwiftShader Vulkan doesn't compile on GCC 4 (or 5) anymore, we use 4.8 on 16.04 for compile-test against libvulkan-dev and 18.04 for render-test with SwiftShader. On Mac, because we want to work with both MoltenVK and Vulkan Loader, we compile-test against MoltenVk in the generic "all build" and render-test against SwiftShader on a new, Vulkan-specific build. That requires us to supply our own loader as well -- apparently no such thing is on Homebrew, which is kinda silly (doesn't anybody do real Vulkan work on this crazy platform?!). --- package/ci/circleci.yml | 109 ++++++++++++++++++++++++------ package/ci/travis.yml | 61 ++++++++++++----- package/ci/unix-desktop-vulkan.sh | 22 +++--- package/ci/unix-desktop.sh | 17 ++++- 4 files changed, 160 insertions(+), 49 deletions(-) diff --git a/package/ci/circleci.yml b/package/ci/circleci.yml index 172ded6b25..68df40178c 100644 --- a/package/ci/circleci.yml +++ b/package/ci/circleci.yml @@ -13,9 +13,15 @@ executors: ubuntu-16_04: docker: - image: ubuntu:xenial-20201014 - xcode-9_4: + ubuntu-18_04: + docker: + - image: ubuntu:bionic-20200921 + xcode-10_3: + macos: + xcode: 10.3.0 + xcode-11_6: macos: - xcode: 9.4.1 + xcode: 11.6.0 emscripten: docker: # 1.39.0 is the oldest on Docker. Anything before 1.39.2 is useless as emar @@ -119,6 +125,9 @@ commands: source $BASH_ENV && cmake --version | grep 3.4 install-swiftshader-gles: + parameters: + build: + type: string steps: - run: name: Install SwiftShader GLES @@ -126,10 +135,39 @@ commands: # self-hosted because GH Actions would make it too simple for people if # you could just download the artifacts directly, right? RIGHT? command: | - apt install -y wget unzip mkdir -p $HOME/swiftshader && cd $HOME/swiftshader - wget https://ci.magnum.graphics/swiftshader-gles-r5464.a6940c8e6e-ubuntu-16.04.zip - unzip swiftshader-gles-r5464.a6940c8e6e-ubuntu-16.04.zip + wget https://ci.magnum.graphics/swiftshader-gles-r5464.a6940c8e6e-<< parameters.build >>.zip + unzip swiftshader-gles-r5464.a6940c8e6e-<< parameters.build >>.zip + + install-swiftshader-vulkan: + parameters: + build: + type: string + steps: + - run: + name: Install SwiftShader Vulkan + # Zip from https://github.com/mosra/magnum-ci/tree/swiftshader and + # self-hosted because GH Actions would make it too simple for people if + # you could just download the artifacts directly, right? RIGHT? + command: | + mkdir -p $HOME/swiftshader && cd $HOME/swiftshader + wget https://ci.magnum.graphics/swiftshader-vulkan-r5464.a6940c8e6e-<< parameters.build >>.zip + unzip swiftshader-vulkan-r5464.a6940c8e6e-<< parameters.build >>.zip + + install-vulkan-loader: + parameters: + build: + type: string + steps: + - run: + name: Install Vulkan Loader + # Zip from https://github.com/mosra/magnum-ci/tree/vulkan-loader and + # self-hosted because GH Actions would make it too simple for people if + # you could just download the artifacts directly, right? RIGHT? + command: | + mkdir -p $HOME/vulkan-loader && cd $HOME/vulkan-loader + wget https://ci.magnum.graphics/vulkan-loader-1.2.153-<< parameters.build >>.zip + unzip vulkan-loader-1.2.153-<< parameters.build >>.zip setup-null-openal-driver: steps: @@ -176,7 +214,7 @@ jobs: PLATFORM_GL_API: GLX steps: - install-base-linux: - extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev + extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev libvulkan-dev - install-gcc-4_8 - install-cmake-3_4 - setup-null-openal-driver @@ -184,15 +222,19 @@ jobs: script: unix-desktop.sh - lcov + # vulkan-loader is broken on 16.04 and swiftshader vulkan needs GCC 7 to + # compile, so using 18.04 for Vulkan tests instead. Compile tests with GCC + # 4.8 are still done on 16.04 linux-vulkan: - executor: ubuntu-16_04 - # Using default GCC and CMake version here + executor: ubuntu-18_04 environment: CMAKE_CXX_FLAGS: --coverage CONFIGURATION: Debug steps: - install-base-linux: - extra: cmake libsdl2-dev libglfw3-dev + extra: cmake libsdl2-dev libglfw3-dev libvulkan-dev wget unzip + - install-swiftshader-vulkan: + build: ubuntu-18.04 - build: script: unix-desktop-vulkan.sh - lcov @@ -208,10 +250,11 @@ jobs: TARGET_GLES2: "ON" steps: - install-base-linux: - extra: libsdl2-dev libglfw3-dev + extra: libsdl2-dev libglfw3-dev wget unzip - install-gcc-4_8 - install-cmake-3_4 - - install-swiftshader-gles + - install-swiftshader-gles: + build: ubuntu-16.04 - build: script: unix-desktop-gles.sh - lcov @@ -227,10 +270,11 @@ jobs: TARGET_GLES2: "OFF" steps: - install-base-linux: - extra: libsdl2-dev libglfw3-dev + extra: libsdl2-dev libglfw3-dev wget unzip - install-gcc-4_8 - install-cmake-3_4 - - install-swiftshader-gles + - install-swiftshader-gles: + build: ubuntu-16.04 - build: script: unix-desktop-gles.sh - lcov @@ -247,7 +291,7 @@ jobs: PLATFORM_GL_API: GLX steps: - install-base-linux: - extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev + extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev libvulkan-dev - install-gcc-4_8 - install-cmake-3_4 - setup-null-openal-driver @@ -265,7 +309,7 @@ jobs: PLATFORM_GL_API: GLX steps: - install-base-linux: - extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev + extra: libgl1-mesa-dev libsdl2-dev libglfw3-dev libopenal-dev libvulkan-dev - install-gcc-4_8 - install-cmake-3_4 - setup-null-openal-driver @@ -284,7 +328,7 @@ jobs: PLATFORM_GL_API: GLX steps: - install-base-linux: - extra: libgl1-mesa-dev libopenal-dev + extra: libgl1-mesa-dev libopenal-dev libvulkan-dev - install-clang-3_8 - install-cmake-3_4 - setup-null-openal-driver @@ -303,7 +347,7 @@ jobs: PLATFORM_GL_API: GLX steps: - install-base-linux: - extra: libgl1-mesa-dev libopenal-dev + extra: libgl1-mesa-dev libopenal-dev libvulkan-dev - install-clang-3_8 - install-cmake-3_4 - setup-null-openal-driver @@ -311,20 +355,42 @@ jobs: script: unix-desktop.sh macos-gl: - executor: xcode-9_4 + # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, + # have to use 10.3 instead + executor: xcode-10_3 environment: CMAKE_CXX_FLAGS: --coverage CONFIGURATION: Debug PLATFORM_GL_API: CGL steps: - install-base-macos: - extra: sdl2 glfw + extra: sdl2 glfw molten-vk - build: script: unix-desktop.sh - lcov + macos-vulkan: + # SwiftShader is built on 10.15 and can't be used on older versions due to + # dyld: lazy symbol binding failed: Symbol not found: ____chkstk_darwin + executor: xcode-11_6 + environment: + CMAKE_CXX_FLAGS: --coverage + CONFIGURATION: Debug + steps: + - install-base-macos: + extra: sdl2 glfw wget + - install-swiftshader-vulkan: + build: macos-10.15 + - install-vulkan-loader: + build: macos-10.15 + - build: + script: unix-desktop-vulkan.sh + - lcov + macos-static: - executor: xcode-9_4 + # Molten-vk isn't in (non-updated) Homebrew on the 9.4 or 10.0/1/2 image, + # have to use 10.3 instead + executor: xcode-10_3 environment: # STUPID yml interprets unquoted ON as a boolean BUILD_STATIC: "ON" @@ -333,7 +399,7 @@ jobs: PLATFORM_GL_API: CGL steps: - install-base-macos: - extra: sdl2 glfw + extra: sdl2 glfw molten-vk - build: script: unix-desktop.sh - lcov @@ -371,6 +437,7 @@ workflows: - linux-sanitizers - linux-threadsanitizer - macos-gl + - macos-vulkan - macos-static - emscripten-webgl1 - emscripten-webgl2 diff --git a/package/ci/travis.yml b/package/ci/travis.yml index 5a7ed4bf8d..86f877ae3a 100644 --- a/package/ci/travis.yml +++ b/package/ci/travis.yml @@ -8,6 +8,7 @@ addons: - libsdl2-dev - libglfw3-dev - libopenal-dev + - libvulkan-dev matrix: # The commented-out jobs are currently handled on Circle CI @@ -52,6 +53,7 @@ matrix: #- libsdl2-dev #- libglfw3-dev #- libopenal-dev + #- libvulkan-dev #- language: cpp #os: linux #dist: xenial @@ -72,9 +74,10 @@ matrix: #- libsdl2-dev #- libglfw3-dev #- libopenal-dev + #- libvulkan-dev #- language: cpp #os: linux - #dist: xenial + #dist: bionic #compiler: gcc #env: #- JOBID=linux-vulkan @@ -87,6 +90,7 @@ matrix: #- lcov #- libsdl2-dev #- libglfw3-dev + #- libvulkan-dev #- language: cpp #os: linux #dist: xenial @@ -108,6 +112,17 @@ matrix: #- TARGET_GLES2=ON #- CMAKE_CXX_FLAGS="--coverage" #- LCOV_EXTRA_OPTS="--gcov-tool /usr/bin/gcov-4.8" + #addons: + #apt: + #packages: + #- libgl1-mesa-dev + #- g++-4.8 + #- ninja-build + #- lcov + #- libsdl2-dev + #- libglfw3-dev + #- libopenal-dev + ## We don't need Vulkan here #- language: cpp #os: linux #dist: xenial @@ -120,16 +135,9 @@ matrix: #- LCOV_EXTRA_OPTS="--gcov-tool /usr/bin/gcov-4.8" #- language: cpp #os: osx - ## The new plugin testing workflow is dynamically loading and unloading them - ## in the tests. When that's combined with coverage and LLVM < 5.0.1, all - ## such tests crash on exit: https://bugs.llvm.org/show_bug.cgi?id=27224 - ## But only in some cases (for example not at all in tests for Corrade - ## PluginManager itself). The commit fixing it is from Jan 3 2018 and all - ## Xcode versions before Xcode 9.3 beta (released on Jan 24) are affected. - ## https://github.com/llvm-mirror/compiler-rt/commit/860d7953a6f36980f406bfbff76f3c00f15eed57 - ## Xcode 9.3 beta has broken git clone ("unknown protocol version 2") since - ## Feb 16 2019, so using version 9.3 now. - #osx_image: xcode9.3 + ## This is currently (Sep 2020) the Travis default, 9.3 didn't have + ## molten-vk in (non-updated) Homebrew. + #osx_image: xcode9.4 #compiler: clang #env: #- JOBID=macos-gl @@ -139,7 +147,7 @@ matrix: #- language: cpp #os: osx ## See above - #osx_image: xcode9.3 + #osx_image: xcode9.4 #compiler: clang #env: #- JOBID=macos-static @@ -147,6 +155,17 @@ matrix: #- CMAKE_CXX_FLAGS="--coverage" #- BUILD_STATIC=ON #- CONFIGURATION=Debug + #- language: cpp + #os: osx + ## SwiftShader is built on 10.15 and can't be used on older versions due to + ## dyld: lazy symbol binding failed: Symbol not found: ____chkstk_darwin + #osx_image: xcode11.6 + #compiler: clang + #env: + #- JOBID=macos-vulkan + #- TARGET=desktop-vulkan + #- CMAKE_CXX_FLAGS="--coverage" + #- CONFIGURATION=Debug - language: cpp os: osx osx_image: xcode7.3 @@ -260,18 +279,26 @@ install: # GLFW (cached) - if [ "$TRAVIS_OS_NAME" == "osx" ] && ( [ "$TARGET" == "desktop" ] || [ "$TARGET" == "desktop-vulkan" ] ); then HOMEBREW_NO_AUTO_UPDATE=1 brew install glfw; fi -# SwiftShader on Linux GLES (16.04). Zip from +# SwiftShader on Linux GLES (16.04) and Vulkan (18.04) (cached). Zip from # https://github.com/mosra/magnum-ci/tree/swiftshader and self-hosted because # GH Actions would make it too simple for people if you could just download the # artifacts directly, right? RIGHT? - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-gles" ] && [ ! -e "$HOME/swiftshader/lib/libGLESv2.so" ]; then cd $HOME ; wget https://ci.magnum.graphics/swiftshader-gles-r5464.a6940c8e6e-ubuntu-16.04.zip && mkdir -p swiftshader && cd swiftshader && unzip ../swiftshader-gles-r5464.a6940c8e6e-ubuntu-16.04.zip && cd $TRAVIS_BUILD_DIR ; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-vulkan" ] && [ ! -e "$HOME/swiftshader/lib/libvk_swiftshader.so" ]; then cd $HOME ; wget https://ci.magnum.graphics/swiftshader-vulkan-r5464.a6940c8e6e-ubuntu-18.04.zip && mkdir -p swiftshader && cd swiftshader && unzip ../swiftshader-vulkan-r5464.a6940c8e6e-ubuntu-18.04.zip && cd $TRAVIS_BUILD_DIR ; fi + +# MoltenVk on macOS for the general desktop build (we just need to link against +# something as the tests aren't run anyway, desktop-vulkan uses Vulkan-Loader +# so this is an opportunity to test both) +- if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install molten-vk; fi + +# SwiftShader on Mac, also self-hosted from GH Actions. We also need Vulkan +# loader there. +- if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop-vulkan" ] && [ ! -e "$HOME/swiftshader/lib/libvk_swiftshader.dylib" ]; then cd $HOME ; wget https://ci.magnum.graphics/swiftshader-r5464.a6940c8e6e-macos-10.15.zip && mkdir -p swiftshader && cd swiftshader && unzip ../swiftshader-r5464.a6940c8e6e-macos-10.15.zip && cd $TRAVIS_BUILD_DIR ; fi +- if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop-vulkan" ] && [ ! -e "$HOME/vulkan-loader/lib/libvulkan.dylib" ]; then cd $HOME ; wget https://ci.magnum.graphics/vulkan-loader-1.2.153-macos-10.15.zip && mkdir -p vulkan-loader && cd vulkan-loader && unzip ../vulkan-loader-1.2.153-macos-10.15.zip && cd $TRAVIS_BUILD_DIR ; fi script: - if [ "$TRAVIS_OS_NAME" == "linux" ] && ( [ "$TARGET" == "desktop" ] || [ "$TARGET" == "desktop-sanitizers" ] ); then ./package/ci/travis-desktop.sh; fi -# Vulkan needs to be in a separate build, because it needs new CMake (for the -# Find module) and in the future also C++14. Unfortunately that means we can't -# apply the sanitizer or non-deprecated build on it. -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-vulkan" ]; then ./package/ci/travis-desktop-vulkan.sh; fi +- if [ "$TARGET" == "desktop-vulkan" ]; then ./package/ci/travis-desktop-vulkan.sh; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-gles" ]; then ./package/ci/travis-desktop-gles.sh; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android" ]; then ./package/ci/travis-android-arm.sh; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop" ]; then ./package/ci/travis-desktop.sh; fi diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index 4a43f456d9..97578fe434 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -17,20 +17,20 @@ cmake .. \ ninja install cd ../.. -# The fastest Vulkan driver ever. See travis.yml for why we have a separate -# Vulkan build. -g++ package/ci/libvulkan.cpp -std=c++11 -shared -o $HOME/libvulkan.so - # Enabling only stuff that's directly affected by Vulkan (which means also # parts of Platform, which need Trade for icon import in tests), disabling -# everything else. +# everything else. On Linux this uses a newer GCC because SwiftShader doesn't +# compile on 4.8; building of Vulkan Magnum code on GCC 4.8 is tested in the +# combined GL + Vulkan job. +# +# Not using CXXFLAGS in order to avoid affecting dependencies. mkdir build && cd build -# Not using CXXFLAGS in order to avoid affecting dependencies cmake .. \ -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ + `# Vulkan Loader is custom-built on Mac` \ + -DCMAKE_PREFIX_PATH="$HOME/deps;$HOME/vulkan-loader" \ -DCMAKE_INSTALL_PREFIX=$HOME/deps \ -DCMAKE_BUILD_TYPE=Debug \ - -DVulkan_LIBRARY=$HOME/libvulkan.so \ -DWITH_AUDIO=OFF \ -DWITH_DEBUGTOOLS=OFF \ -DWITH_GL=OFF \ @@ -70,7 +70,13 @@ cmake .. \ -DBUILD_DEPRECATED=$BUILD_DEPRECATED \ -G Ninja ninja -ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$TRAVIS_BUILD_DIR/package/ci/leaksanitizer.conf" CORRADE_TEST_COLOR=ON ctest -V -E "(GL|Vk)Test" + +export VK_ICD_FILENAMES=$HOME/swiftshader/share/vulkan/icd.d/vk_swiftshader_icd.json +export CORRADE_TEST_COLOR=ON + +ctest -V +MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest +MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/package/ci/unix-desktop.sh b/package/ci/unix-desktop.sh index c4ffa3e53b..5c8732a3ed 100755 --- a/package/ci/unix-desktop.sh +++ b/package/ci/unix-desktop.sh @@ -17,14 +17,23 @@ cmake .. \ ninja install cd ../.. +# Enabling both GL and Vulkan but not running rendering tests for either -- +# that's done by separate GLES and Vulkan jobs. For GL it's because there's no +# way to test desktop GL on SwiftShader, for Vulkan it's because: +# - SwiftShader doesn't compile on GCC 4.8 and we *need* to test compile on +# that, +# - SwiftShader for macOS is built on 10.15 and we want to test on older +# versions as well +# so we need two jobs to verify both compilation and rendering. +# +# Not using CXXFLAGS in order to avoid affecting dependencies. mkdir build && cd build -# Not using CXXFLAGS in order to avoid affecting dependencies cmake .. \ -DCMAKE_CXX_FLAGS="$CMAKE_CXX_FLAGS" \ -DCMAKE_INSTALL_PREFIX=$HOME/deps \ -DCMAKE_BUILD_TYPE=$CONFIGURATION \ -DWITH_AUDIO=ON \ - -DWITH_VK=OFF \ + -DWITH_VK=ON \ -DWITH_GLFWAPPLICATION=$BUILD_APPLICATIONS \ -DWITH_SDL2APPLICATION=$BUILD_APPLICATIONS \ -DWITH_WINDOWLESS${PLATFORM_GL_API}APPLICATION=ON \ @@ -48,15 +57,17 @@ cmake .. \ -DWITH_SCENECONVERTER=ON \ -DWITH_SHADERCONVERTER=ON \ -DWITH_GL_INFO=ON \ + -DWITH_VK_INFO=ON \ -DWITH_AL_INFO=ON \ -DBUILD_TESTS=ON \ -DBUILD_GL_TESTS=ON \ + -DBUILD_VK_TESTS=ON \ -DBUILD_DEPRECATED=$BUILD_DEPRECATED \ -DBUILD_STATIC=$BUILD_STATIC \ -DBUILD_PLUGINS_STATIC=$BUILD_STATIC \ -G Ninja ninja -ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$(pwd)/../package/ci/leaksanitizer.conf" TSAN_OPTIONS="color=always" CORRADE_TEST_COLOR=ON ctest -V -E GLTest +ASAN_OPTIONS="color=always" LSAN_OPTIONS="color=always suppressions=$(pwd)/../package/ci/leaksanitizer.conf" TSAN_OPTIONS="color=always" CORRADE_TEST_COLOR=ON ctest -V -E "(GL|Vk)Test" # Test install, after running the tests as for them it shouldn't be needed ninja install From 55ec24062de01b5261439eddd2aed9cf5e332726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 9 Sep 2020 17:43:44 +0200 Subject: [PATCH 28/85] Vk: GCC 4.8, stop it, it's not Monday today. --- src/Magnum/Vk/Device.cpp | 13 +++++++++++-- src/Magnum/Vk/Enums.cpp | 9 ++++++--- src/Magnum/Vk/Instance.cpp | 13 +++++++++++-- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index dcfad66a17..4d33936d2c 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -119,7 +119,10 @@ DeviceCreateInfo::DeviceCreateInfo(Instance& instance, const Flags flags): Devic DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {} -DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, _info{info} {} +DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} DeviceCreateInfo::~DeviceCreateInfo() = default; @@ -308,7 +311,13 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info): Device::Device(NoCreateT): _handle{}, _functionPointers{} {} -Device::Device(Device&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} { +Device::Device(Device&& other) noexcept: _handle{other._handle}, + _flags{other._flags}, _version{other._version}, + _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _functionPointers(other._functionPointers) +{ other._handle = nullptr; other._functionPointers = {}; } diff --git a/src/Magnum/Vk/Enums.cpp b/src/Magnum/Vk/Enums.cpp index ad8b6d0633..6a45dfe0eb 100644 --- a/src/Magnum/Vk/Enums.cpp +++ b/src/Magnum/Vk/Enums.cpp @@ -59,24 +59,27 @@ constexpr VkIndexType IndexTypeMapping[]{ static_assert(VK_FORMAT_UNDEFINED == 0, "VK_FORMAT_UNDEFINED is assumed to be 0"); constexpr VkFormat VertexFormatMapping[] { + /* GCC 4.8 doesn't like just a {} for default enum values */ #define _c(input, format) VK_FORMAT_ ## format, - #define _s(input) {}, + #define _s(input) VkFormat{}, #include "Magnum/Vk/Implementation/vertexFormatMapping.hpp" #undef _s #undef _c }; constexpr VkFormat PixelFormatMapping[] { + /* GCC 4.8 doesn't like just a {} for default enum values */ #define _c(input, format) VK_FORMAT_ ## format, - #define _s(input) {}, + #define _s(input) VkFormat{}, #include "Magnum/Vk/Implementation/pixelFormatMapping.hpp" #undef _s #undef _c }; constexpr VkFormat CompressedPixelFormatMapping[] { + /* GCC 4.8 doesn't like just a {} for default enum values */ #define _c(input, format) VK_FORMAT_ ## format, - #define _s(input) {}, + #define _s(input) VkFormat{}, #include "Magnum/Vk/Implementation/compressedPixelFormatMapping.hpp" #undef _s #undef _c diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index eaece5549e..0a105d0ed9 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -142,7 +142,10 @@ InstanceCreateInfo::InstanceCreateInfo(const Int argc, const char** const argv, InstanceCreateInfo::InstanceCreateInfo(NoInitT) noexcept {} -InstanceCreateInfo::InstanceCreateInfo(const VkInstanceCreateInfo& info): _info{info} {} +InstanceCreateInfo::InstanceCreateInfo(const VkInstanceCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} InstanceCreateInfo::~InstanceCreateInfo() = default; @@ -300,7 +303,13 @@ Instance::Instance(const InstanceCreateInfo& info): _flags{HandleFlag::DestroyOn Instance::Instance(NoCreateT): _handle{}, _functionPointers{} {} -Instance::Instance(Instance&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, _functionPointers{other._functionPointers} { +Instance::Instance(Instance&& other) noexcept: _handle{other._handle}, + _flags{other._flags}, _version{other._version}, + _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _functionPointers(other._functionPointers) +{ other._handle = nullptr; other._functionPointers = {}; } From 4fa49dd464ec91188a333d5e184cac05f93dc75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 12 Sep 2020 19:15:34 +0200 Subject: [PATCH 29/85] package/ci: add Android Vulkan build. It's not using Android's native Vulkan library because that's only since Android 24 and emulator for that version doesn't even start. So we use our own minimal driver instead. --- ...-android-arm.sh => travis-android-gles.sh} | 0 package/ci/travis-android-vulkan.sh | 112 ++++++++++++++++++ package/ci/travis.yml | 35 ++++-- 3 files changed, 140 insertions(+), 7 deletions(-) rename package/ci/{travis-android-arm.sh => travis-android-gles.sh} (100%) create mode 100755 package/ci/travis-android-vulkan.sh diff --git a/package/ci/travis-android-arm.sh b/package/ci/travis-android-gles.sh similarity index 100% rename from package/ci/travis-android-arm.sh rename to package/ci/travis-android-gles.sh diff --git a/package/ci/travis-android-vulkan.sh b/package/ci/travis-android-vulkan.sh new file mode 100755 index 0000000000..22aeed55ba --- /dev/null +++ b/package/ci/travis-android-vulkan.sh @@ -0,0 +1,112 @@ +#!/bin/bash +set -ev + +# Corrade +git clone --depth 1 git://github.com/mosra/corrade.git +cd corrade + +# Build native corrade-rc +mkdir build && cd build +cmake .. \ + -DCMAKE_INSTALL_PREFIX=$HOME/deps-native \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_INTERCONNECT=OFF \ + -DWITH_PLUGINMANAGER=OFF \ + -DWITH_TESTSUITE=OFF \ + -DWITH_UTILITY=OFF \ + -G Ninja +ninja install +cd .. + +# Crosscompile Corrade +mkdir build-android-arm && cd build-android-arm +cmake .. \ + -DCMAKE_ANDROID_NDK=$TRAVIS_BUILD_DIR/android-ndk-r16b \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_SYSTEM_VERSION=22 \ + -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a \ + -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \ + -DCMAKE_ANDROID_STL_TYPE=c++_static \ + -DCMAKE_BUILD_TYPE=Release \ + -DCORRADE_RC_EXECUTABLE=$HOME/deps-native/bin/corrade-rc \ + -DCMAKE_INSTALL_PREFIX=$HOME/deps \ + -DWITH_INTERCONNECT=OFF \ + -G Ninja +ninja install +cd .. + +cd .. + +# Generate debug keystore for APK signing +keytool -genkeypair -keystore $HOME/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -validity 10000 -dname CN=,OU=,O=,L=,S=,C= + +# *Native* Vulkan requires Android 24, build-tools-25.0.2, android-24 and +# sys-img-armeabi-v7a-android-24 in android.components in the YML, but the +# fucking emulator never starts, which means we can't test shit: +# https://github.com/googlemaps/android-maps-utils/issues/371 +# So don't even bother there, use some ass-old version that works, and link to +# our own library, which is the fastest Vulkan implementation IN THE WORLD. +$TRAVIS_BUILD_DIR/android-ndk-r16b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang++ --target=armv7-none-linux-androideabi --gcc-toolchain=$TRAVIS_BUILD_DIR/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64 --sysroot=$TRAVIS_BUILD_DIR/android-ndk-r16b/platforms/android-22/arch-arm -march=armv7-a -mthumb -mfpu=vfpv3-d16 -mfloat-abi=softfp -funwind-tables -no-canonical-prefixes -D__ANDROID_API__=22 -fexceptions -frtti -O3 -DNDEBUG -Wl,--fix-cortex-a8 -fPIE -pie -Wl,--gc-sections -Wl,-z,nocopyreloc package/ci/libvulkan.cpp -std=c++11 -c -o vulkan.o +$TRAVIS_BUILD_DIR/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ar rcs $HOME/libvulkan.a vulkan.o +$TRAVIS_BUILD_DIR/android-ndk-r16b/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ranlib $HOME/libvulkan.a + +# Crosscompile +mkdir build-android-arm && cd build-android-arm +cmake .. \ + -DANDROID_SDK=/usr/local/android-sdk \ + -DCMAKE_ANDROID_NDK=$TRAVIS_BUILD_DIR/android-ndk-r16b \ + -DCMAKE_SYSTEM_NAME=Android \ + -DCMAKE_SYSTEM_VERSION=22 \ + -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a \ + -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang \ + -DCMAKE_ANDROID_STL_TYPE=c++_static \ + -DCORRADE_RC_EXECUTABLE=$HOME/deps-native/bin/corrade-rc \ + -DVulkan_LIBRARY=$HOME/libvulkan.a \ + -DCMAKE_INSTALL_PREFIX=$HOME/deps \ + -DCMAKE_PREFIX_PATH=$HOME/deps \ + -DCMAKE_FIND_ROOT_PATH=$HOME/deps \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_AUDIO=OFF \ + -DWITH_DEBUGTOOLS=OFF \ + -DWITH_GL=OFF \ + -DWITH_MESHTOOLS=OFF \ + -DWITH_PRIMITIVES=OFF \ + -DWITH_SCENEGRAPH=OFF \ + -DWITH_SHADERS=OFF \ + -DWITH_TEXT=OFF \ + -DWITH_TEXTURETOOLS=OFF \ + -DWITH_TRADE=ON \ + -DWITH_VK=ON \ + -DWITH_AL_INFO=OFF \ + -DWITH_VK_INFO=ON \ + -DWITH_GL_INFO=OFF \ + -DWITH_ANYAUDIOIMPORTER=OFF \ + -DWITH_ANYIMAGECONVERTER=OFF \ + -DWITH_ANYIMAGEIMPORTER=OFF \ + -DWITH_ANYSCENECONVERTER=OFF \ + -DWITH_ANYSCENEIMPORTER=OFF \ + -DWITH_MAGNUMFONT=OFF \ + -DWITH_MAGNUMFONTCONVERTER=OFF \ + -DWITH_OBJIMPORTER=OFF \ + -DWITH_TGAIMAGECONVERTER=OFF \ + -DWITH_TGAIMPORTER=OFF \ + -DWITH_WAVAUDIOIMPORTER=OFF \ + -DWITH_DISTANCEFIELDCONVERTER=OFF \ + -DWITH_FONTCONVERTER=OFF \ + -DWITH_IMAGECONVERTER=OFF \ + -DWITH_SCENECONVERTER=OFF \ + -DWITH_ANDROIDAPPLICATION=ON \ + -DBUILD_TESTS=ON \ + -DBUILD_GL_TESTS=OFF \ + -DBUILD_VK_TESTS=ON \ + -G Ninja +ninja + +# Start simulator and run tests +echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a +emulator -avd test -no-audio -no-window & +android-wait-for-emulator +CORRADE_TEST_COLOR=ON ctest -V -E VkTest + +# Test install, after running the tests as for them it shouldn't be needed +ninja install diff --git a/package/ci/travis.yml b/package/ci/travis.yml index 86f877ae3a..10a0bf86a1 100644 --- a/package/ci/travis.yml +++ b/package/ci/travis.yml @@ -208,7 +208,7 @@ matrix: dist: trusty env: - JOBID=android-gles2 - - TARGET=android + - TARGET=android-gles - TARGET_GLES2=ON addons: apt: @@ -226,7 +226,7 @@ matrix: dist: trusty env: - JOBID=android-gles3 - - TARGET=android + - TARGET=android-gles - TARGET_GLES2=OFF addons: apt: @@ -237,6 +237,26 @@ matrix: - build-tools-22.0.1 - android-22 - sys-img-armeabi-v7a-android-22 + - language: android + os: linux + # Setting xenial here will cause it to use 12.04, with GCC 4.6 and other + # prehistoric nightmares. So staying with 14.04 for the time being. + dist: trusty + env: + - JOBID=android-vulkan + - TARGET=android-vulkan + addons: + apt: + packages: + - ninja-build + android: + components: + # Android API level 24 is the first with Vulkan support, but the crap + # emulator doesn't even start, so it's useless. See + # travis-android-vulkan.sh for how it's done instead. + - build-tools-22.0.1 + - android-22 + - sys-img-armeabi-v7a-android-22 notifications: webhooks: @@ -251,20 +271,20 @@ cache: - $HOME/sdl2 install: -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-sanitizers" ] && [ ! "$TARGET" == "android" ] && [ ! "$TARGET" == "desktop-vulkan" ]; then export CXX=g++-4.8; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-sanitizers" ] && [ ! "$TARGET" == "android-gles" ] && [ ! "$TARGET" == "android-vulkan" ] && [ ! "$TARGET" == "desktop-vulkan" ]; then export CXX=g++-4.8; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-sanitizers" ]; then export CXX=clang++-3.8; fi - if [ "$BUILD_DEPRECATED" != "OFF" ]; then export BUILD_DEPRECATED=ON; fi - if [ "$BUILD_STATIC" != "ON" ]; then export BUILD_STATIC=OFF; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && ( [ "$TARGET" == "desktop" ] || [ "$TARGET" == "desktop-sanitizers" ] ); then export PLATFORM_GL_API=GLX; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-gles" ]; then export PLATFORM_GL_API=EGL; fi -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android" ]; then wget -nc https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && unzip -q android-*.zip; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && ( [ "$TARGET" == "android-gles" ] || [ "$TARGET" == "android-vulkan" ] ); then wget -nc https://dl.google.com/android/repository/android-ndk-r16b-linux-x86_64.zip && unzip -q android-*.zip; fi # Download CMake 3.4.3 to ensure we're still compatible with it (Travis has # 3.9 since December 2017). Also, the PATH setting can't be cached, so it's # separate (bit me two times already). Android needs CMake 3.7, but # https://gitlab.kitware.com/cmake/cmake/issues/17253 is fixed in 3.9.2, so # grab that. FindVulkan is since 3.7, in that case just use the system package. -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-vulkan" ] && [ ! "$TARGET" == "android" ] && [ ! -e "$HOME/cmake/bin" ]; then cd $HOME ; wget -nc --no-check-certificate https://cmake.org/files/v3.4/cmake-3.4.3-Linux-x86_64.tar.gz && mkdir -p cmake && cd cmake && tar --strip-components=1 -xzf ../cmake-3.4.3-Linux-x86_64.tar.gz && cd $TRAVIS_BUILD_DIR ; fi -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android" ] && [ ! -e "$HOME/cmake/bin" ]; then cd $HOME ; wget -nc --no-check-certificate https://cmake.org/files/v3.9/cmake-3.9.2-Linux-x86_64.tar.gz && mkdir -p cmake && cd cmake && tar --strip-components=1 -xzf ../cmake-3.9.2-Linux-x86_64.tar.gz && cd $TRAVIS_BUILD_DIR ; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-vulkan" ] && [ ! "$TARGET" == "android-gles" ] && [ ! "$TARGET" == "android-vulkan" ] && [ ! -e "$HOME/cmake/bin" ]; then cd $HOME ; wget -nc --no-check-certificate https://cmake.org/files/v3.4/cmake-3.4.3-Linux-x86_64.tar.gz && mkdir -p cmake && cd cmake && tar --strip-components=1 -xzf ../cmake-3.4.3-Linux-x86_64.tar.gz && cd $TRAVIS_BUILD_DIR ; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && ( [ "$TARGET" == "android-gles" ] || [ "$TARGET" == "android-vulkan" ] ) && [ ! -e "$HOME/cmake/bin" ]; then cd $HOME ; wget -nc --no-check-certificate https://cmake.org/files/v3.9/cmake-3.9.2-Linux-x86_64.tar.gz && mkdir -p cmake && cd cmake && tar --strip-components=1 -xzf ../cmake-3.9.2-Linux-x86_64.tar.gz && cd $TRAVIS_BUILD_DIR ; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ ! "$TARGET" == "desktop-vulkan" ]; then export PATH=$HOME/cmake/bin:$PATH && cmake --version; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install ninja; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] && [[ "$CMAKE_CXX_FLAGS" == *"--coverage"* ]]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install lcov; fi @@ -300,7 +320,8 @@ script: - if [ "$TRAVIS_OS_NAME" == "linux" ] && ( [ "$TARGET" == "desktop" ] || [ "$TARGET" == "desktop-sanitizers" ] ); then ./package/ci/travis-desktop.sh; fi - if [ "$TARGET" == "desktop-vulkan" ]; then ./package/ci/travis-desktop-vulkan.sh; fi - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "desktop-gles" ]; then ./package/ci/travis-desktop-gles.sh; fi -- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android" ]; then ./package/ci/travis-android-arm.sh; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android-gles" ]; then ./package/ci/travis-android-gles.sh; fi +- if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$TARGET" == "android-vulkan" ]; then ./package/ci/travis-android-vulkan.sh; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "desktop" ]; then ./package/ci/travis-desktop.sh; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "ios-simulator" ]; then ./package/ci/travis-ios-simulator.sh; fi - if [ "$TRAVIS_OS_NAME" == "osx" ] && [ "$TARGET" == "emscripten" ]; then ./package/ci/travis-emscripten.sh; fi From 2cbd50fb733918db511f48aee5bbe6116b5a47c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 12 Sep 2020 20:45:03 +0200 Subject: [PATCH 30/85] package/ci: merge Vulkan Windows build into the GL ones. Otherwise I'd need to duplicate that for clang-cl, mingw and so on. It's enabled only on MSVC 2019 and newer, because I don'r really care about older versions -- those are mainly for compatibility with existing code, and Vulkan is not existing code yet. --- package/ci/appveyor-desktop-mingw.bat | 5 +- package/ci/appveyor-desktop-vulkan.bat | 74 -------------------------- package/ci/appveyor-desktop.bat | 5 +- package/ci/appveyor.yml | 24 +++++---- 4 files changed, 21 insertions(+), 87 deletions(-) delete mode 100644 package/ci/appveyor-desktop-vulkan.bat diff --git a/package/ci/appveyor-desktop-mingw.bat b/package/ci/appveyor-desktop-mingw.bat index 0823e3464f..59d3a1edd2 100644 --- a/package/ci/appveyor-desktop-mingw.bat +++ b/package/ci/appveyor-desktop-mingw.bat @@ -27,6 +27,7 @@ cmake .. ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DCMAKE_PREFIX_PATH="%APPVEYOR_BUILD_FOLDER%/SDL;%APPVEYOR_BUILD_FOLDER%/openal" ^ -DWITH_AUDIO=ON ^ + -DWITH_VK=ON ^ -DWITH_SDL2APPLICATION=ON ^ -DWITH_WINDOWLESSWGLAPPLICATION=ON ^ -DWITH_WGLCONTEXT=ON ^ @@ -49,15 +50,17 @@ cmake .. ^ -DWITH_SCENECONVERTER=ON ^ -DWITH_SHADERCONVERTER=ON ^ -DWITH_GL_INFO=ON ^ + -DWITH_VK_INFO=ON ^ -DWITH_AL_INFO=ON ^ -DBUILD_TESTS=ON ^ -DBUILD_GL_TESTS=ON ^ + -DBUILD_VK_TESTS=ON ^ -G Ninja || exit /b cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E GLTest || exit /b +ctest -V -E "(GL|Vk)Test" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/appveyor-desktop-vulkan.bat b/package/ci/appveyor-desktop-vulkan.bat deleted file mode 100644 index 251c98f610..0000000000 --- a/package/ci/appveyor-desktop-vulkan.bat +++ /dev/null @@ -1,74 +0,0 @@ -if "%APPVEYOR_BUILD_WORKER_IMAGE%" == "Visual Studio 2019" call "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Auxiliary/Build/vcvarsall.bat" x64 || exit /b -set PATH=%APPVEYOR_BUILD_FOLDER%\deps\bin;%PATH% - -rem Build Corrade -git clone --depth 1 git://github.com/mosra/corrade.git || exit /b -cd corrade || exit /b -mkdir build && cd build || exit /b -cmake .. ^ - -DCMAKE_BUILD_TYPE=Debug ^ - -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ - -DWITH_INTERCONNECT=OFF ^ - -DUTILITY_USE_ANSI_COLORS=ON ^ - -G Ninja || exit /b -cmake --build . || exit /b -cmake --build . --target install || exit /b -cd .. && cd .. - -rem Build the fastest Vulkan driver ever. See appveyor.yml for why Vulkan is -rem a separate build for now. -cl.exe /c package/ci/libvulkan.cpp || exit /b -lib.exe /OUT:%APPVEYOR_BUILD_FOLDER%/deps/lib/libvulkan.lib libvulkan.obj || exit /b - -rem Enabling only stuff that's directly affected by Vulkan (which means also -rem parts of Platform, which need Trade for icon import in tests), disabling -rem everything else. -mkdir build && cd build || exit /b -cmake .. ^ - -DCMAKE_BUILD_TYPE=Debug ^ - -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ - -DCMAKE_PREFIX_PATH=%APPVEYOR_BUILD_FOLDER%/SDL ^ - -DVulkan_LIBRARY=%APPVEYOR_BUILD_FOLDER%/deps/lib/libvulkan.lib ^ - -DWITH_AUDIO=OFF ^ - -DWITH_DEBUGTOOLS=OFF ^ - -DWITH_GL=OFF ^ - -DWITH_MESHTOOLS=OFF ^ - -DWITH_PRIMITIVES=OFF ^ - -DWITH_SCENEGRAPH=OFF ^ - -DWITH_SHADERS=OFF ^ - -DWITH_TEXT=OFF ^ - -DWITH_TEXTURETOOLS=OFF ^ - -DWITH_TRADE=ON ^ - -DWITH_VK=ON ^ - -DWITH_ANYAUDIOIMPORTER=OFF ^ - -DWITH_ANYIMAGECONVERTER=OFF ^ - -DWITH_ANYIMAGEIMPORTER=OFF ^ - -DWITH_ANYSCENECONVERTER=ON ^ - -DWITH_ANYSCENEIMPORTER=OFF ^ - -DWITH_MAGNUMFONT=OFF ^ - -DWITH_MAGNUMFONTCONVERTER=OFF ^ - -DWITH_OBJIMPORTER=OFF ^ - -DWITH_TGAIMAGECONVERTER=OFF ^ - -DWITH_TGAIMPORTER=OFF ^ - -DWITH_WAVAUDIOIMPORTER=OFF ^ - -DWITH_DISTANCEFIELDCONVERTER=OFF ^ - -DWITH_FONTCONVERTER=OFF ^ - -DWITH_IMAGECONVERTER=OFF ^ - -DWITH_SCENECONVERTER=OFF ^ - -DWITH_GL_INFO=OFF ^ - -DWITH_VK_INFO=ON ^ - -DWITH_AL_INFO=OFF ^ - -DWITH_SDL2APPLICATION=ON ^ - -DWITH_GLFWAPPLICATION=ON ^ - -DBUILD_TESTS=ON ^ - -DBUILD_GL_TESTS=OFF ^ - -DBUILD_VK_TESTS=ON ^ - -G Ninja || exit /b -cmake --build . || exit /b - -rem Test -set CORRADE_TEST_COLOR=ON -ctest -V -E "(GL|Vk)Test" || exit /b - -rem Test install, after running the tests as for them it shouldn't be needed -cmake --build . --target install || exit /b diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index d1f59f6437..260f23b135 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -33,6 +33,7 @@ cmake .. ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DCMAKE_PREFIX_PATH="%APPVEYOR_BUILD_FOLDER%/SDL;%APPVEYOR_BUILD_FOLDER%/openal" ^ -DWITH_AUDIO=ON ^ + -DWITH_VK=%ENABLE_VULKAN% ^ -DWITH_SDL2APPLICATION=ON ^ -DWITH_GLFWAPPLICATION=ON ^ -DWITH_WINDOWLESSWGLAPPLICATION=ON ^ @@ -56,9 +57,11 @@ cmake .. ^ -DWITH_SCENECONVERTER=ON ^ -DWITH_SHADERCONVERTER=ON ^ -DWITH_GL_INFO=ON ^ + -DWITH_VK_INFO=%ENABLE_VULKAN% ^ -DWITH_AL_INFO=ON ^ -DBUILD_TESTS=ON ^ -DBUILD_GL_TESTS=ON ^ + -DBUILD_VK_TESTS=%ENABLE_VULKAN% ^ -DBUILD_STATIC=%BUILD_STATIC% ^ -DBUILD_PLUGINS_STATIC=%BUILD_STATIC% ^ %COMPILER_EXTRA% -G Ninja || exit /b @@ -66,7 +69,7 @@ cmake --build . || exit /b rem Test set CORRADE_TEST_COLOR=ON -ctest -V -E GLTest || exit /b +ctest -V -E "(GL|Vk)Test" || exit /b rem Test install, after running the tests as for them it shouldn't be needed cmake --build . --target install || exit /b diff --git a/package/ci/appveyor.yml b/package/ci/appveyor.yml index e7e9fc5504..f5612f166f 100644 --- a/package/ci/appveyor.yml +++ b/package/ci/appveyor.yml @@ -8,24 +8,21 @@ environment: APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 APPVEYOR_JOB_NAME: windows-msvc2015 CONFIGURATION: Debug + ENABLE_VULKAN: OFF - TARGET: desktop COMPILER: msvc PLATFORM: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_JOB_NAME: windows-msvc2017 CONFIGURATION: Debug + ENABLE_VULKAN: OFF - TARGET: desktop COMPILER: msvc PLATFORM: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 APPVEYOR_JOB_NAME: windows-msvc2019 CONFIGURATION: Debug - - TARGET: desktop-vulkan - COMPILER: msvc - PLATFORM: x64 - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 - APPVEYOR_JOB_NAME: windows-vulkan-msvc2019 - CONFIGURATION: Debug + ENABLE_VULKAN: ON - TARGET: desktop COMPILER: msvc PLATFORM: x64 @@ -33,12 +30,14 @@ environment: APPVEYOR_JOB_NAME: windows-static-msvc2019 CONFIGURATION: Debug BUILD_STATIC: ON + ENABLE_VULKAN: ON - TARGET: desktop COMPILER: msvc-clang PLATFORM: x64 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 APPVEYOR_JOB_NAME: windows-msvc2019-clang CONFIGURATION: Debug + ENABLE_VULKAN: ON # Build at least one 32bit Release build, so we catch things that are not a # problem on either 64bit or Debug. - TARGET: desktop @@ -52,6 +51,7 @@ environment: COMPILER: mingw APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 APPVEYOR_JOB_NAME: windows-mingw + ENABLE_VULKAN: ON - TARGET: desktop-gles TARGET_GLES2: ON APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 @@ -105,7 +105,6 @@ install: # Build apps on both GL and Vulkan to verify these can build w/o GL as well. # Not on GLES, as there's not much GLES-dependent. - IF "%TARGET%" == "desktop" set BUILD_APPLICATIONS=YES -- IF "%TARGET%" == "desktop-vulkan" set BUILD_APPLICATIONS=YES - IF "%TARGET%" == "desktop-gles" set BUILD_APPLICATIONS=NO # SDL2 @@ -122,6 +121,13 @@ install: - IF "%BUILD_APPLICATIONS%" == "YES" IF "%COMPILER:~0,4%" == "msvc" copy glfw\lib-vc2015\glfw3.dll deps\bin\ && copy glfw\lib-vc2015\glfw3dll.lib deps\lib\glfw3.lib - IF "%BUILD_APPLICATIONS%" == "YES" IF "%COMPILER%" == "mingw" copy glfw\lib-mingw-w64\glfw3.dll deps\bin\ && copy glfw\lib-mingw-w64\libglfw3dll.a deps\lib\libglfw3.a +# SwiftShader for Vulkan + Vulkan loader +# TODO: re-enable SwiftShader when we figure out why vkCreateInstance fails +#- IF "%ENABLE_VULKAN%" == "ON" IF NOT EXIST %APPVEYOR_BUILD_FOLDER%\swiftshader-vulkan-r5464.a6940c8e6e-windows-2019.zip appveyor DownloadFile https://ci.magnum.graphics/swiftshader-vulkan-r5464.a6940c8e6e-windows-2019.zip +- IF "%ENABLE_VULKAN%" == "ON" IF NOT EXIST %APPVEYOR_BUILD_FOLDER%\vulkan-loader-1.2.153-windows-2019.zip appveyor DownloadFile https://ci.magnum.graphics/vulkan-loader-1.2.153-windows-2019.zip +#- IF "%ENABLE_VULKAN%" == "ON" 7z x swiftshader-vulkan-r5464.a6940c8e6e-windows-2019.zip -o%APPVEYOR_BUILD_FOLDER%\deps +- IF "%ENABLE_VULKAN%" == "ON" 7z x vulkan-loader-1.2.153-windows-2019.zip -o%APPVEYOR_BUILD_FOLDER%\deps + # Angle for WinRT. Zip from https://github.com/mosra/magnum-ci/tree/angle and # self-hosted because GH Actions would make it too simple for people if you # could just download the artifacts directly, right? RIGHT? @@ -132,10 +138,6 @@ build_script: - IF "%TARGET%" == "desktop" IF "%COMPILER:~0,4%" == "msvc" call package\ci\appveyor-desktop.bat - IF "%TARGET%" == "desktop" IF "%COMPILER%" == "mingw" call package\ci\appveyor-desktop-mingw.bat - IF "%TARGET%" == "desktop-gles" call package\ci\appveyor-desktop-gles.bat -# Vulkan needs to be in a separate build, because in the future it'll need -# C++14 and building it together with C++11 code would harm verifiability of -# C++11 compatibility. -- IF "%TARGET%" == "desktop-vulkan" call package\ci\appveyor-desktop-vulkan.bat - IF "%TARGET%" == "rt" call package\ci\appveyor-rt.bat cache: From 4fd5e5135843c5ec89bdc51b8d7fa16d965358d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 09:41:34 +0200 Subject: [PATCH 31/85] Vk: fix linker issues on MinGW. --- src/Magnum/Vk/visibility.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Magnum/Vk/visibility.h b/src/Magnum/Vk/visibility.h index a7f37fea4f..0464a9ab6e 100644 --- a/src/Magnum/Vk/visibility.h +++ b/src/Magnum/Vk/visibility.h @@ -32,7 +32,7 @@ #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_BUILD_STATIC - #ifdef MagnumVk_EXPORTS + #if defined(MagnumVk_EXPORTS) || defined(MagnumVkObjects_EXPORTS) #define MAGNUM_VK_EXPORT CORRADE_VISIBILITY_EXPORT #else #define MAGNUM_VK_EXPORT CORRADE_VISIBILITY_IMPORT @@ -47,4 +47,3 @@ #endif #endif - From 3b9acd27fbb0ef916412f58b9566c7ffbea72c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 19:46:31 +0200 Subject: [PATCH 32/85] Vk: base class for Vulkan tests. Not much there yet, but this alone removes the congitive overhead when testing Vulkan functionality on (some, whatever) device. --- CMakeLists.txt | 4 +- doc/Doxyfile | 2 +- doc/building.dox | 2 + doc/cmake.dox | 1 + modules/FindMagnum.cmake | 8 +- src/Magnum/Vk/CMakeLists.txt | 31 ++++++ src/Magnum/Vk/VulkanTester.cpp | 50 ++++++++++ src/Magnum/Vk/VulkanTester.h | 175 +++++++++++++++++++++++++++++++++ 8 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 src/Magnum/Vk/VulkanTester.cpp create mode 100644 src/Magnum/Vk/VulkanTester.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c02afcc0b2..0913b722f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,8 +217,10 @@ if(BUILD_TESTS) endif() endif() -# OpenGLTester library, built by default only if GL tests are enabled +# OpenGLTester / VulkanTester libraries, built by default only if GL / VK tests +# are enabled cmake_dependent_option(WITH_OPENGLTESTER "Build OpenGLTester library" OFF "NOT BUILD_GL_TESTS" ON) +cmake_dependent_option(WITH_VULKANTESTER "Build VulkanTester library" OFF "NOT BUILD_VK_TESTS" ON) # Dynamic linking is meaningless on Emscripten and too inconvenient on Android if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID) diff --git a/doc/Doxyfile b/doc/Doxyfile index 11ad3f9ff3..bdcab4f62f 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -2256,7 +2256,7 @@ PREDEFINED = DOXYGEN_GENERATING_OUTPUT \ CORRADE_VISIBILITY_LOCAL= CORRADE_VISIBILITY_IMPORT= \ CORRADE_IGNORE_DEPRECATED_PUSH= CORRADE_IGNORE_DEPRECATED_POP= \ CORRADE_ENUMSET_OPERATORS(message)= MAGNUM_BUILD_DEPRECATED \ - MAGNUM_TARGET_GL + MAGNUM_TARGET_GL MAGNUM_TARGET_VK # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/doc/building.dox b/doc/building.dox index 088c52954c..db019acd1b 100644 --- a/doc/building.dox +++ b/doc/building.dox @@ -565,6 +565,8 @@ code: - `WITH_OPENGLTESTER` --- The @ref GL::OpenGLTester class. Requires `TARGET_GL` to be enabled; enables building of one of the windowless application libraries based on the target platform. +- `WITH_VULKANTESTER` --- The @ref Vk::VulkanTester class. Requires + `TARGET_VK` to be enabled. Magnum also contains a set of dependency-less plugins for importing essential file formats. Additional plugins are provided in a separate plugin repository, diff --git a/doc/cmake.dox b/doc/cmake.dox index e28d6802d7..15700cf78f 100644 --- a/doc/cmake.dox +++ b/doc/cmake.dox @@ -231,6 +231,7 @@ There are also extensions to @ref Corrade::TestSuite::Tester for testing GPU code: - `OpenGLTester` --- @ref GL::OpenGLTester class +- `VulkanTester` --- @ref Vk::VulkanTester class The library also contains a set of plugins for importing essential file formats. Additional plugins are provided in separate plugin repository, see diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index d69a5ed836..d9d296f815 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -86,6 +86,7 @@ # GlxContext - GLX context # WglContext - WGL context # OpenGLTester - OpenGLTester class +# VulkanTester - VulkanTester class # MagnumFont - Magnum bitmap font plugin # MagnumFontConverter - Magnum bitmap font converter plugin # ObjImporter - OBJ importer plugin @@ -372,7 +373,7 @@ set(_MAGNUM_IMPLICITLY_ENABLED_COMPONENTS DebugTools MeshTools SceneGraph Shaders ShaderTools Text TextureTools Trade GL Primitives) if(NOT CORRADE_TARGET_EMSCRIPTEN) - list(APPEND _MAGNUM_LIBRARY_COMPONENTS Vk) + list(APPEND _MAGNUM_LIBRARY_COMPONENTS Vk VulkanTester) list(APPEND _MAGNUM_EXECUTABLE_COMPONENTS vk-info) endif() if(NOT CORRADE_TARGET_ANDROID) @@ -466,6 +467,7 @@ if(MAGNUM_TARGET_GL) endif() set(_MAGNUM_Trade_DEPENDENCIES ) +set(_MAGNUM_VulkanTester_DEPENDENCIES Vk) set(_MAGNUM_AndroidApplication_DEPENDENCIES GL) set(_MAGNUM_EmscriptenApplication_DEPENDENCIES) if(MAGNUM_TARGET_GL) @@ -891,6 +893,10 @@ foreach(_component ${Magnum_FIND_COMPONENTS}) elseif(_component STREQUAL OpenGLTester) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/GL) + # VulkanTester library + elseif(_component STREQUAL VulkanTester) + set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_SUFFIX Magnum/Vk) + # Primitives library elseif(_component STREQUAL Primitives) set(_MAGNUM_${_COMPONENT}_INCLUDE_PATH_NAMES Cube.h) diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 1b1d4cc744..5704f8af84 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -125,6 +125,37 @@ if(WITH_VK_INFO) add_executable(Magnum::vk-info ALIAS magnum-vk-info) endif() +if(WITH_VULKANTESTER) + if(NOT TARGET_VK) + message(SEND_ERROR "VulkanTester is available only if TARGET_VK is enabled") + endif() + + find_package(Corrade REQUIRED TestSuite) + + set(MagnumVulkanTester_SRCS VulkanTester.cpp) + set(MagnumVulkanTester_HEADERS VulkanTester.h) + + # Unlike with OpenGLTester, we shouldn't need a separate library that links + # exclusively to MagnumVkTestLib as there is no global state that could + # cause problems on Windows. + add_library(MagnumVulkanTester STATIC + ${MagnumVulkanTester_SRCS} + ${MagnumVulkanTester_HEADERS}) + set_target_properties(MagnumVulkanTester PROPERTIES + DEBUG_POSTFIX "-d" + FOLDER "Magnum/Vk") + target_link_libraries(MagnumVulkanTester PUBLIC MagnumVk Corrade::TestSuite) + + install(FILES ${MagnumVulkanTester_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Vk) + install(TARGETS MagnumVulkanTester + RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR} + LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR} + ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) + + # Magnum VulkanTester target alias for superprojects + add_library(Magnum::VulkanTester ALIAS MagnumVulkanTester) +endif() + if(BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumVkTestLib ${SHARED_OR_STATIC} diff --git a/src/Magnum/Vk/VulkanTester.cpp b/src/Magnum/Vk/VulkanTester.cpp new file mode 100644 index 0000000000..b1e5eb0784 --- /dev/null +++ b/src/Magnum/Vk/VulkanTester.cpp @@ -0,0 +1,50 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "VulkanTester.h" + +#include /* sigh, for setSkippedArgumentPrefixes() */ +#include + +#include "Magnum/Vk/DeviceProperties.h" + +namespace Magnum { namespace Vk { + +VulkanTester::VulkanTester(): VulkanTester{NoCreate} { + *_deviceProperties = pickDevice(_instance); + _device = Vk::Device{_instance, Vk::DeviceCreateInfo{*_deviceProperties} + .addQueues(_deviceProperties->pickQueueFamily(Vk::QueueFlag::Graphics), {0.0f}, {_queue}) + }; +} + +VulkanTester::VulkanTester(NoCreateT): VulkanTester{NoCreate, NoCreate} { + _instance = Vk::Instance{Vk::InstanceCreateInfo{arguments().first, arguments().second} + .setApplicationInfo(testName(), {}) + }; +} + +VulkanTester::VulkanTester(NoCreateT, NoCreateT): TestSuite::Tester{TestSuite::Tester::TesterConfiguration{}.setSkippedArgumentPrefixes({"magnum"})}, _instance{NoCreate}, _device{NoCreate}, _deviceProperties{Containers::InPlaceInit, NoCreate}, _queue{NoCreate} {} + +}} diff --git a/src/Magnum/Vk/VulkanTester.h b/src/Magnum/Vk/VulkanTester.h new file mode 100644 index 0000000000..104ae9864c --- /dev/null +++ b/src/Magnum/Vk/VulkanTester.h @@ -0,0 +1,175 @@ +#ifndef Magnum_Vk_VulkanTester_h +#define Magnum_Vk_VulkanTester_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::VulkanTester + * @m_since_latest + */ + +#include "Magnum/configure.h" + +#ifdef MAGNUM_TARGET_VK +#include + +#include "Magnum/Vk/Instance.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Queue.h" + +namespace Magnum { namespace Vk { + +/** +@brief Base class for Vulkan tests and benchmarks +@m_since_latest + +Extends @ref Corrade::TestSuite::Tester with features for Vulkan testing and +benchmarking. Be sure to read its documentation first to have an overview of +the base features. + +This class is built into a separate static library and only if +`WITH_VULKANTESTER` is enabled when building Magnum. To use it with CMake, +request the `VulkanTester` component of the `Magnum` package. Derive your test +class from this class instead of @ref Corrade::TestSuite::Tester and +either link to `Magnum::VulkanTester` target or add it to the `LIBRARIES` +section of the @ref corrade-cmake-add-test "corrade_add_test()" macro: + +@code{.cmake} +find_package(Magnum REQUIRED VulkanTester) + +# ... +corrade_add_test(YourTest YourTest.cpp LIBRARIES Magnum::VulkanTester) +@endcode + +Additionally, if you're using Magnum as a CMake subproject, ensure it's enabled +as it's not built by default: + +@code{.cmake} +set(WITH_OPENGLTESTER ON CACHE BOOL "" FORCE) +add_subdirectory(magnum EXCLUDE_FROM_ALL) +@endcode + +See @ref building, @ref cmake and @ref testsuite for more information. + +@section Vk-VulkanTester-device Vulkan device and instance creation + +The class implicitly creates a Vulkan @ref Instance and @ref Device with +default layers and extensions and one graphics queue. These are then available +through @ref instance(), @ref device(), @ref deviceProperties() and +@ref queue() getters. + +If you want to create a custom device, use the @ref VulkanTester(NoCreateT) +constructor. You can then move the device and queue instances to the getters to +make them available through common interfaces again. If you want to create a +custom instance as well, use the @ref VulkanTester(NoCreateT, NoCreateT) +variant. +*/ +class VulkanTester: public TestSuite::Tester { + public: + /** + * @brief Default constructor + * + * Creates an instance using implicit settings, picks a default device + * and creates a graphics queue on that device. These are then exposed + * through @ref instance(), @ref device(), @ref deviceProperties() and + * @ref queue() getters. + */ + explicit VulkanTester(); + + /** + * @brief Construct without creating a device + * + * Use the @ref instance() to pick and create a device. You can then + * move it to @ref device(), @ref deviceProperties() and @ref queue() + * to have them accessible through common interfaces again. + */ + explicit VulkanTester(NoCreateT); + + /** + * @brief Construct without creating an instance or device + * + * Leaves the initialization completely on the user. You can move the + * instances to @ref instance(), @ref device(), @ref deviceProperties() + * and @ref queue() to have them accessible through common interfaces. + */ + explicit VulkanTester(NoCreateT, NoCreateT); + + protected: + /** + * @brief Vulkan instance + * + * In case the class was constructed using + * @ref VulkanTester(NoCreateT, NoCreateT), this instance is initially + * not created. Move a created instance onto it to make it useful. + */ + Instance& instance() { return _instance; } + + /** + * @brief Vulkan device + * + * In case the class was constructed using @ref VulkanTester(NoCreateT) + * or @ref VulkanTester(NoCreateT, NoCreateT), this instance is + * initially not created. Move a created instance onto it to make it + * useful. + */ + Device& device() { return _device; } + + /** + * @brief Vulkan device properties + * + * In case the class was constructed using @ref VulkanTester(NoCreateT) + * or @ref VulkanTester(NoCreateT, NoCreateT), this instance is + * initially not created. Move a created instance onto it to make it + * useful. + */ + DeviceProperties& deviceProperties() { return *_deviceProperties; } + + /** + * @brief Vulkan queue + * + * In case the calss was constructed using @ref VulkanTester(), the + * queue corresponds to @ref DeviceProperties::pickQueueFamily() with + * @ref QueueFlag::Graphics called on @ref deviceProperties(). + * + * In case the class was constructed using @ref VulkanTester(NoCreateT) + * or @ref VulkanTester(NoCreateT, NoCreateT), this instance is + * initially not created. Move a created instance onto it to make it + * useful. + */ + Queue& queue() { return _queue; } + + private: + Instance _instance; + Device _device; + Containers::Pointer _deviceProperties; + Queue _queue; +}; + +}} +#else +#error this header is available only in the Vulkan build +#endif + +#endif From 4b499049f09762bd7692842c1ca7631c09cf4fe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 19:54:33 +0200 Subject: [PATCH 33/85] Vk: use the new VulkanTester in Device tests. It's there only to create the instance right now. Not using it to test instance-level functionality right now as we'd be only working around it anyway. --- src/Magnum/Vk/Test/CMakeLists.txt | 4 +- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 52 +++++++-------- src/Magnum/Vk/Test/DeviceVkTest.cpp | 66 +++++++++---------- 3 files changed, 59 insertions(+), 63 deletions(-) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 15190d00f8..d39b1c1243 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -37,8 +37,8 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) - corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib) - corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) + corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 339622c7b9..76f230d282 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -40,10 +39,11 @@ #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" +#include "Magnum/Vk/VulkanTester.h" namespace Magnum { namespace Vk { namespace Test { namespace { -struct DevicePropertiesVkTest: TestSuite::Tester { +struct DevicePropertiesVkTest: VulkanTester { explicit DevicePropertiesVkTest(); void enumerate(); @@ -69,8 +69,6 @@ struct DevicePropertiesVkTest: TestSuite::Tester { void pickDeviceIndex(); void pickDeviceType(); void pickDeviceError(); - - Instance _instance; }; const struct { @@ -86,7 +84,7 @@ const struct { "Vk::tryPickDevice(): unknown Vulkan device type FAST\n"} }; -DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { +DevicePropertiesVkTest::DevicePropertiesVkTest(): VulkanTester{NoCreate} { addTests({&DevicePropertiesVkTest::enumerate, &DevicePropertiesVkTest::constructMove, &DevicePropertiesVkTest::wrap, @@ -113,7 +111,7 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): _instance{InstanceCreateInfo{a } void DevicePropertiesVkTest::enumerate() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); Debug{} << "Found" << devices.size() << "devices"; CORRADE_VERIFY(!devices.empty()); @@ -131,7 +129,7 @@ void DevicePropertiesVkTest::enumerate() { } void DevicePropertiesVkTest::constructMove() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); VkPhysicalDevice handle = devices[0].handle(); Containers::StringView name = devices[0].name(); @@ -140,7 +138,7 @@ void DevicePropertiesVkTest::constructMove() { CORRADE_COMPARE(a.handle(), handle); CORRADE_COMPARE(a.name(), name); - DeviceProperties b = DeviceProperties::wrap(_instance, nullptr); + DeviceProperties b = DeviceProperties::wrap(instance(), nullptr); b = std::move(a); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.name(), name); @@ -152,22 +150,22 @@ void DevicePropertiesVkTest::constructMove() { void DevicePropertiesVkTest::wrap() { VkPhysicalDevice handle; UnsignedInt count = 1; - auto result = Result(_instance->EnumeratePhysicalDevices(_instance, &count, &handle)); + auto result = Result(instance()->EnumeratePhysicalDevices(instance(), &count, &handle)); { /** @todo clean up once Compare::AnyOf exists */ CORRADE_ITERATION(result); CORRADE_VERIFY(result == Result::Success || result == Result::Incomplete); } - DeviceProperties wrapped = DeviceProperties::wrap(_instance, handle); + DeviceProperties wrapped = DeviceProperties::wrap(instance(), handle); CORRADE_VERIFY(wrapped.handle()); - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_COMPARE(wrapped.name(), devices[0].name()); } void DevicePropertiesVkTest::enumerateExtensions() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); ExtensionProperties properties = devices[0].enumerateExtensionProperties(); @@ -188,7 +186,7 @@ void DevicePropertiesVkTest::enumerateExtensionsWithKhronosValidationLayer() { if(!enumerateLayerProperties().isSupported("VK_LAYER_KHRONOS_validation")) CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); /* There should be more extensions with this layer enabled */ @@ -213,7 +211,7 @@ void DevicePropertiesVkTest::enumerateExtensionsNonexistentLayer() { } void DevicePropertiesVkTest::extensionConstructMove() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); ExtensionProperties a = devices[0].enumerateExtensionProperties(); @@ -232,7 +230,7 @@ void DevicePropertiesVkTest::extensionConstructMove() { } void DevicePropertiesVkTest::extensionIsSupported() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); ExtensionProperties properties = devices[0].enumerateExtensionProperties(); @@ -247,7 +245,7 @@ void DevicePropertiesVkTest::extensionIsSupported() { } void DevicePropertiesVkTest::extensionNamedRevision() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); ExtensionProperties properties = devices[0].enumerateExtensionProperties(); @@ -265,7 +263,7 @@ void DevicePropertiesVkTest::extensionNamedRevision() { } void DevicePropertiesVkTest::queueFamilies() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); Debug{} << "Available queue family count:" << devices[0].queueFamilyCount(); @@ -287,7 +285,7 @@ void DevicePropertiesVkTest::queueFamilies() { } void DevicePropertiesVkTest::queueFamiliesOutOfRange() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); const UnsignedInt count = devices[0].queueFamilyCount(); @@ -302,7 +300,7 @@ void DevicePropertiesVkTest::queueFamiliesOutOfRange() { } void DevicePropertiesVkTest::queueFamiliesPick() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); Containers::Optional id = devices[0].tryPickQueueFamily(QueueFlag::Compute|QueueFlag::Graphics); @@ -317,7 +315,7 @@ void DevicePropertiesVkTest::queueFamiliesPick() { } void DevicePropertiesVkTest::queueFamiliesPickFailed() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); std::ostringstream out; @@ -329,12 +327,12 @@ void DevicePropertiesVkTest::queueFamiliesPickFailed() { void DevicePropertiesVkTest::pickDevice() { /* Default behavior */ - Containers::Optional device = tryPickDevice(_instance); + Containers::Optional device = tryPickDevice(instance()); CORRADE_VERIFY(device); } void DevicePropertiesVkTest::pickDeviceIndex() { - Containers::Array devices = enumerateDevices(_instance); + Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); /* Pick the last one */ @@ -353,9 +351,9 @@ void DevicePropertiesVkTest::pickDeviceType() { const char* argv[] {"", "--magnum-device", "cpu"}; /* Creating a dedicated instance so we can pass custom args */ - Instance instance{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; + Instance instance2{InstanceCreateInfo{Int(Containers::arraySize(argv)), argv}}; - Containers::Optional device = tryPickDevice(instance); + Containers::Optional device = tryPickDevice(instance2); if(!device) CORRADE_SKIP("No CPU device found."); CORRADE_VERIFY(device->type() == DeviceType::Cpu); @@ -366,12 +364,12 @@ void DevicePropertiesVkTest::pickDeviceError() { setTestCaseDescription(data.name); /* Creating a dedicated instance so we can pass custom args */ - Instance instance{InstanceCreateInfo{Int(data.args.size()), const_cast(data.args.data())}}; + Instance instance2{InstanceCreateInfo{Int(data.args.size()), const_cast(data.args.data())}}; std::ostringstream out; Error redirectError{&out}; - CORRADE_VERIFY(!tryPickDevice(instance)); - CORRADE_COMPARE(out.str(), Utility::formatString(data.message, enumerateDevices(_instance).size())); + CORRADE_VERIFY(!tryPickDevice(instance2)); + CORRADE_COMPARE(out.str(), Utility::formatString(data.message, enumerateDevices(instance2).size())); } }}}} diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index 3656f447ab..fb3f5846ff 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -40,12 +39,13 @@ #include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" +#include "Magnum/Vk/VulkanTester.h" #include "MagnumExternal/Vulkan/flextVkGlobal.h" namespace Magnum { namespace Vk { namespace Test { namespace { -struct DeviceVkTest: TestSuite::Tester { +struct DeviceVkTest: VulkanTester { explicit DeviceVkTest(); void createInfoConstruct(); @@ -67,8 +67,6 @@ struct DeviceVkTest: TestSuite::Tester { void constructNoQueue(); void wrap(); void populateGlobalFunctionPointers(); - - Instance _instance; }; struct { @@ -122,7 +120,7 @@ struct { "Device version: Vulkan {}.{}{}\n"}, }; -DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, arguments().second}} { +DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { addTests({&DeviceVkTest::createInfoConstruct, &DeviceVkTest::createInfoConstructImplicitDevice, &DeviceVkTest::createInfoConstructNoImplicitExtensions, @@ -152,21 +150,21 @@ DeviceVkTest::DeviceVkTest(): _instance{InstanceCreateInfo{arguments().first, ar using namespace Containers::Literals; void DeviceVkTest::createInfoConstruct() { - DeviceCreateInfo info{pickDevice(_instance)}; + DeviceCreateInfo info{pickDevice(instance())}; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); /* Extensions might or might not be enabled */ } void DeviceVkTest::createInfoConstructImplicitDevice() { - DeviceCreateInfo info{_instance}; + DeviceCreateInfo info{instance()}; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); /* Extensions might or might not be enabled */ } void DeviceVkTest::createInfoConstructNoImplicitExtensions() { - DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); /* No extensions enabled as we explicitly disabled that */ @@ -178,7 +176,7 @@ void DeviceVkTest::createInfoExtensions() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); - DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); @@ -207,7 +205,7 @@ void DeviceVkTest::createInfoCopiedStrings() { Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1); Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string(); - DeviceCreateInfo info{_instance, DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; info.addEnabledExtensions({ globalButNotNullTerminated, localButNullTerminated @@ -228,7 +226,7 @@ void DeviceVkTest::createInfoNoQueuePriorities() { std::ostringstream out; Error redirectError{&out}; - DeviceCreateInfo{_instance}.addQueues(0, {}, {}); + DeviceCreateInfo{instance()}.addQueues(0, {}, {}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); } @@ -240,7 +238,7 @@ void DeviceVkTest::createInfoWrongQueueOutputCount() { std::ostringstream out; Error redirectError{&out}; Queue a{NoCreate}, b{NoCreate}; - DeviceCreateInfo{_instance}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); + DeviceCreateInfo{instance()}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); } @@ -249,9 +247,9 @@ void DeviceVkTest::construct() { CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); { - DeviceProperties deviceProperties = pickDevice(_instance); + DeviceProperties deviceProperties = pickDevice(instance()); Queue queue{NoCreate}; - Device device{_instance, DeviceCreateInfo{deviceProperties} + Device device{instance(), DeviceCreateInfo{deviceProperties} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(device.handle()); @@ -330,13 +328,13 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { /* Creating a dedicated instance so we can pass custom args and enable layers independently */ - Instance instance{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} + Instance instance2{InstanceCreateInfo{Int(data.argsDisable.size()), data.argsDisable} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; - DeviceProperties deviceProperties = pickDevice(instance); + DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); @@ -346,7 +344,7 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; - Device device{instance, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} + Device device{instance2, DeviceCreateInfo{deviceProperties, DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, @@ -386,13 +384,13 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { /* Creating a dedicated instance so we can pass custom args and enable layers independently */ - Instance instance{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} + Instance instance2{InstanceCreateInfo{Int(data.argsEnable.size()), data.argsEnable} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; - DeviceProperties deviceProperties = pickDevice(instance); + DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); @@ -402,7 +400,7 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; - Device device{instance, DeviceCreateInfo{instance, DeviceCreateInfo::Flag::NoImplicitExtensions} + Device device{instance2, DeviceCreateInfo{instance2, DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) /* Nothing enabled by the application */ }; @@ -427,7 +425,7 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { void DeviceVkTest::constructMultipleQueues() { /* Find a GPU that has at least two queue families and at least four queues in one family */ - Containers::Array deviceProperties = enumerateDevices(_instance); + Containers::Array deviceProperties = enumerateDevices(instance()); DeviceProperties* deviceWithMultipleQueues = nullptr; UnsignedInt largeFamily = ~UnsignedInt{}; @@ -456,7 +454,7 @@ void DeviceVkTest::constructMultipleQueues() { rawQueueInfo.queueCount = 1; Queue a{NoCreate}, b{NoCreate}, c{NoCreate}; - Device device{_instance, DeviceCreateInfo{*deviceWithMultipleQueues} + Device device{instance(), DeviceCreateInfo{*deviceWithMultipleQueues} /* Request a raw queue in the middle of it all to test we skip it when populating the outputs, and correctly offset the next IDs. According to the spec we can request each family only once, which makes the @@ -497,7 +495,7 @@ void DeviceVkTest::constructRawQueue() { rawQueueInfo.pQueuePriorities = &zero; rawQueueInfo.queueFamilyIndex = 0; rawQueueInfo.queueCount = 1; - Device device{_instance, DeviceCreateInfo{_instance} + Device device{instance(), DeviceCreateInfo{instance()} .addQueues(rawQueueInfo)}; /* Fetch the raw queue */ @@ -507,13 +505,13 @@ void DeviceVkTest::constructRawQueue() { } void DeviceVkTest::constructMove() { - DeviceProperties deviceProperties = pickDevice(_instance); + DeviceProperties deviceProperties = pickDevice(instance()); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); if(!extensions.isSupported()) CORRADE_SKIP("VK_KHR_maintenance1 not supported, can't test"); Queue queue{NoCreate}; - Device a{_instance, DeviceCreateInfo{deviceProperties} + Device a{instance(), DeviceCreateInfo{deviceProperties} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions() }; @@ -555,7 +553,7 @@ void DeviceVkTest::constructUnknownExtension() { std::ostringstream out; Error redirectError{&out}; Queue queue{NoCreate}; - Device device{_instance, DeviceCreateInfo{_instance} + Device device{instance(), DeviceCreateInfo{instance()} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; CORRADE_COMPARE(out.str(), "TODO"); @@ -568,7 +566,7 @@ void DeviceVkTest::constructNoQueue() { std::ostringstream out; Error redirectError{&out}; - Device device{_instance, DeviceCreateInfo{_instance}}; + Device device{instance(), DeviceCreateInfo{instance()}}; CORRADE_COMPARE(out.str(), "Vk::Device: needs to be created with at least one queue\n"); } @@ -584,13 +582,13 @@ void DeviceVkTest::wrap() { CORRADE_SKIP("VK_LAYER_KHRONOS_validation not supported, can't test"); /* Creating a dedicated instance so we can enable layers independently */ - Instance instance{InstanceCreateInfo{} + Instance instance2{InstanceCreateInfo{} .addEnabledLayers({"VK_LAYER_KHRONOS_validation"}) /* Needed by VK_EXT_debug_marker */ .addEnabledExtensions() }; - DeviceProperties deviceProperties = pickDevice(instance); + DeviceProperties deviceProperties = pickDevice(instance2); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties({"VK_LAYER_KHRONOS_validation"}); if(!extensions.isSupported()) CORRADE_SKIP("VK_EXT_debug_marker not supported, can't test"); @@ -599,8 +597,8 @@ void DeviceVkTest::wrap() { VkDevice device; Queue queue{NoCreate}; - CORRADE_COMPARE(Result(instance->CreateDevice(deviceProperties, - DeviceCreateInfo{instance} + CORRADE_COMPARE(Result(instance2->CreateDevice(deviceProperties, + DeviceCreateInfo{instance2} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, @@ -614,7 +612,7 @@ void DeviceVkTest::wrap() { { /* Wrapping should load the basic function pointers */ - auto wrapped = Device::wrap(instance, device, Version::Vk11, { + auto wrapped = Device::wrap(instance2, device, Version::Vk11, { Extensions::EXT::debug_marker::string() }, HandleFlag::DestroyOnDestruction); CORRADE_VERIFY(wrapped->DestroyDevice); @@ -639,7 +637,7 @@ void DeviceVkTest::wrap() { } /* ...so we can wrap it again, non-owned, and then destroy it manually */ - auto wrapped = Device::wrap(instance, device, Version::Vk10, {}); + auto wrapped = Device::wrap(instance2, device, Version::Vk10, {}); CORRADE_VERIFY(wrapped->DestroyDevice); wrapped->DestroyDevice(device, nullptr); } @@ -648,7 +646,7 @@ void DeviceVkTest::populateGlobalFunctionPointers() { vkDestroyDevice = nullptr; Queue queue{NoCreate}; - Device device{_instance, DeviceCreateInfo{_instance} + Device device{instance(), DeviceCreateInfo{instance()} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(!vkDestroyDevice); From 4f538a07192791060012fad4885b8851d660f517 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 20:34:41 +0200 Subject: [PATCH 34/85] Vk: command pool creation. --- doc/vulkan-mapping.dox | 10 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/CommandPool.cpp | 84 +++++++++ src/Magnum/Vk/CommandPool.h | 222 +++++++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/CommandPoolTest.cpp | 96 ++++++++++ src/Magnum/Vk/Test/CommandPoolVkTest.cpp | 102 +++++++++++ src/Magnum/Vk/Vk.h | 1 + 8 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/CommandPool.cpp create mode 100644 src/Magnum/Vk/CommandPool.h create mode 100644 src/Magnum/Vk/Test/CommandPoolTest.cpp create mode 100644 src/Magnum/Vk/Test/CommandPoolVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index a59e501161..f4bd98f799 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -123,7 +123,7 @@ Vulkan function | Matching API @fn_vk{CmdWriteTimestamp} | | @fn_vk{CreateBuffer}, \n @fn_vk{DestroyBuffer} | | @fn_vk{CreateBufferView}, \n @fn_vk{DestroyBufferView} | | -@fn_vk{CreateCommandPool}, \n @fn_vk{DestroyCommandPool} | | +@fn_vk{CreateCommandPool}, \n @fn_vk{DestroyCommandPool} | @ref CommandPool constructor and destructor @fn_vk{CreateComputePipelines}, \n @fn_vk{DestroyComputePipelines} | | @fn_vk{CreateDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugReportCallbackEXT} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-flat m-warning} **EXT** | | @fn_vk{CreateDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT**, \n @fn_vk{DestroyDebugUtilsMessengerEXT} @m_class{m-label m-flat m-warning} **EXT** | | @@ -304,6 +304,14 @@ Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{ApplicationInfo} | @ref InstanceCreateInfo +@subsection vulkan-mapping-structures-c C + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{CommandPoolCreateInfo} | @ref CommandPoolCreateInfo + @subsection vulkan-mapping-structures-d D @m_class{m-fullwidth} diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 5704f8af84..0504291d07 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + CommandPool.cpp Extensions.cpp Handle.cpp Instance.cpp @@ -45,6 +46,7 @@ set(MagnumVk_GracefulAssert_SRCS LayerProperties.cpp) set(MagnumVk_HEADERS + CommandPool.h Device.h DeviceProperties.h Enums.h diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp new file mode 100644 index 0000000000..35e9064964 --- /dev/null +++ b/src/Magnum/Vk/CommandPool.cpp @@ -0,0 +1,84 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "CommandPool.h" + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +CommandPoolCreateInfo::CommandPoolCreateInfo(const UnsignedInt queueFamilyIndex, const Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + _info.flags = VkCommandPoolCreateFlags(flags); + _info.queueFamilyIndex = queueFamilyIndex; +} + +CommandPoolCreateInfo::CommandPoolCreateInfo(NoInitT) noexcept {} + +CommandPoolCreateInfo::CommandPoolCreateInfo(const VkCommandPoolCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +CommandPool CommandPool::wrap(Device& device, const VkCommandPool handle, const HandleFlags flags) { + CommandPool out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +CommandPool::CommandPool(Device& device, const CommandPoolCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateCommandPool(device, info, nullptr, &_handle)); +} + +CommandPool::CommandPool(NoCreateT) noexcept: _device{}, _handle{} {} + +CommandPool::CommandPool(CommandPool&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +CommandPool::~CommandPool() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyCommandPool(*_device, _handle, nullptr); +} + +CommandPool& CommandPool::operator=(CommandPool&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkCommandPool CommandPool::release() { + const VkCommandPool handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h new file mode 100644 index 0000000000..02d01117f3 --- /dev/null +++ b/src/Magnum/Vk/CommandPool.h @@ -0,0 +1,222 @@ +#ifndef Magnum_Vk_CommandPool_h +#define Magnum_Vk_CommandPool_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool + * @m_since_latest + */ + +#include + +#include "Magnum/Tags.h" +#include "Magnum/Magnum.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Command pool creation info +@m_since_latest + +Wraps a @type_vk_keyword{CommandPoolCreateInfo}. +@see @ref CommandPool +*/ +class MAGNUM_VK_EXPORT CommandPoolCreateInfo { + public: + /** + * @brief Command pool creation flag + * + * Wraps @type_vk_keyword{CommandPoolCreateFlagBits}. + * @see @ref Flags, @ref CommandPoolCreateInfo(UnsignedInt, Flags) + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** Command buffers allocated from this pool will be short-lived */ + Transient = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, + + /** + * Allow individual command buffers to be reset to initial state + * instead of just the whole pool. + * + * @m_class{m-note m-success} + * + * @par + * Not using this flag may help the driver to use simpler + * per-pool allocators instead of per-buffer. + */ + ResetCommandBuffer = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + }; + + /** + * @brief Command pool creation flags + * + * Type-safe wrapper for @type_vk_keyword{CommandPoolCreateFlags}. + * @see @ref CommandPoolCreateInfo(UnsignedInt, Flags) + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param queueFamilyIndex Queue family index + * @param flags Command pool creation flags + * + * The following @type_vk{CommandPoolCreateInfo} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `queueFamilyIndex` + * + * @see @ref DeviceProperties::pickQueueFamily() + */ + explicit CommandPoolCreateInfo(UnsignedInt queueFamilyIndex, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit CommandPoolCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit CommandPoolCreateInfo(const VkCommandPoolCreateInfo& info); + + /** @brief Underlying @type_vk{CommandPoolCreateInfo} structure */ + VkCommandPoolCreateInfo& operator*() { return _info; } + /** @overload */ + const VkCommandPoolCreateInfo& operator*() const { return _info; } + /** @overload */ + VkCommandPoolCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkCommandPoolCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkCommandPoolCreateInfo*() const { return &_info; } + + private: + VkCommandPoolCreateInfo _info; +}; + +CORRADE_ENUMSET_OPERATORS(CommandPoolCreateInfo::Flags) + +/** +@brief Command pool +@m_since_latest + +Wraps a @type_vk_keyword{CommandPool}. +*/ +class MAGNUM_VK_EXPORT CommandPool { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the command pool is created on + * @param handle The @type_vk{CommandPool} handle + * @param flags Handle flags + * + * The @p handle is expected to be of an existing Vulkan command pool. + * Unlike a command pool created using a constructor, the Vulkan + * command pool is by default not deleted on destruction, use @p flags + * for different behavior. + */ + static CommandPool wrap(Device& device, VkCommandPool handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to create the command pool on + * @param info Command pool creation info + * + * @see @fn_vk_keyword{CreateCommandPool} + */ + explicit CommandPool(Device& device, const CommandPoolCreateInfo& info); + + /** + * @brief Construct without creating the instance + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit CommandPool(NoCreateT) noexcept; + + /** @brief Copying is not allowed */ + CommandPool(const CommandPool&) = delete; + + /** @brief Move constructor */ + CommandPool(CommandPool&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{CommandPool} handle, unless the + * instance was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{DestroyCommandPool}, @ref release() + */ + ~CommandPool(); + + /** @brief Copying is not allowed */ + CommandPool& operator=(const CommandPool&) = delete; + + /** @brief Move assignment */ + CommandPool& operator=(CommandPool&& other) noexcept; + + /** @brief Underlying @type_vk{CommandPool} handle */ + VkCommandPool handle() { return _handle; } + /** @overload */ + operator VkCommandPool() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan command pool + * + * Releases ownership of the Vulkan command pool and returns its handle + * so @fn_vk{DestroyCommandPool} is not called on destruction. The + * internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkCommandPool release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkCommandPool _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index d39b1c1243..c7e59593e8 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) @@ -37,6 +38,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) diff --git a/src/Magnum/Vk/Test/CommandPoolTest.cpp b/src/Magnum/Vk/Test/CommandPoolTest.cpp new file mode 100644 index 0000000000..8e26bd4770 --- /dev/null +++ b/src/Magnum/Vk/Test/CommandPoolTest.cpp @@ -0,0 +1,96 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/CommandPool.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandPoolTest: TestSuite::Tester { + explicit CommandPoolTest(); + + void createInfoConstruct(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +CommandPoolTest::CommandPoolTest() { + addTests({&CommandPoolTest::createInfoConstruct, + &CommandPoolTest::createInfoConstructNoInit, + &CommandPoolTest::createInfoConstructFromVk, + + &CommandPoolTest::constructNoCreate, + &CommandPoolTest::constructCopy}); +} + +void CommandPoolTest::createInfoConstruct() { + CommandPoolCreateInfo info{37, CommandPoolCreateInfo::Flag::ResetCommandBuffer|CommandPoolCreateInfo::Flag::Transient}; + CORRADE_COMPARE(info->queueFamilyIndex, 37); + CORRADE_COMPARE(info->flags, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT|VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); +} + +void CommandPoolTest::createInfoConstructNoInit() { + CommandPoolCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) CommandPoolCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void CommandPoolTest::createInfoConstructFromVk() { + VkCommandPoolCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + CommandPoolCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void CommandPoolTest::constructNoCreate() { + { + CommandPool pool{NoCreate}; + CORRADE_VERIFY(!pool.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void CommandPoolTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandPoolTest) diff --git a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp new file mode 100644 index 0000000000..4846bf6f87 --- /dev/null +++ b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp @@ -0,0 +1,102 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/CommandPool.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandPoolVkTest: VulkanTester { + explicit CommandPoolVkTest(); + + void construct(); + void constructMove(); + void wrap(); +}; + +CommandPoolVkTest::CommandPoolVkTest() { + addTests({&CommandPoolVkTest::construct, + &CommandPoolVkTest::constructMove, + &CommandPoolVkTest::wrap}); +} + +void CommandPoolVkTest::construct() { + { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics), + CommandPoolCreateInfo::Flag::ResetCommandBuffer}}; + CORRADE_VERIFY(pool.handle()); + CORRADE_COMPARE(pool.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void CommandPoolVkTest::constructMove() { + CommandPool a{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics), + CommandPoolCreateInfo::Flag::Transient}}; + VkCommandPool handle = a.handle(); + + CommandPool b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + CommandPool c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void CommandPoolVkTest::wrap() { + VkCommandPool pool{}; + CORRADE_COMPARE(Result(device()->CreateCommandPool(device(), + CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}, + nullptr, &pool)), Result::Success); + CORRADE_VERIFY(pool); + + auto wrapped = CommandPool::wrap(device(), pool, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), pool); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), pool); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyCommandPool(device(), pool, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandPoolVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 3cb353a7a0..171a2a170f 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class CommandPool; class Device; class DeviceCreateInfo; class DeviceProperties; From 2353ba8c1a6d29c0ad8e1156cfed0c786c28fea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 22:35:00 +0200 Subject: [PATCH 35/85] Vk: command buffer allocation. --- doc/vulkan-mapping.dox | 3 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/CommandBuffer.cpp | 68 +++++++++++ src/Magnum/Vk/CommandBuffer.h | 129 +++++++++++++++++++++ src/Magnum/Vk/CommandPool.cpp | 17 +++ src/Magnum/Vk/CommandPool.h | 25 +++- src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/CommandBufferTest.cpp | 62 ++++++++++ src/Magnum/Vk/Test/CommandBufferVkTest.cpp | 110 ++++++++++++++++++ src/Magnum/Vk/Test/CommandPoolVkTest.cpp | 15 ++- src/Magnum/Vk/Vk.h | 1 + 11 files changed, 431 insertions(+), 3 deletions(-) create mode 100644 src/Magnum/Vk/CommandBuffer.cpp create mode 100644 src/Magnum/Vk/CommandBuffer.h create mode 100644 src/Magnum/Vk/Test/CommandBufferTest.cpp create mode 100644 src/Magnum/Vk/Test/CommandBufferVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index f4bd98f799..4a5cd8fddf 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -51,7 +51,7 @@ Legend: Vulkan function | Matching API --------------------------------------- | ------------ -@fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | | +@fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | @ref CommandPool::allocate(), @ref CommandBuffer destructor @fn_vk{AllocateDescriptorSets}, \n @fn_vk{FreeDescriptorSets} | | @fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | | @@ -310,6 +310,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ +@type_vk{CommandBufferAllocateInfo} | not exposed, internal to @ref CommandPool::allocate() @type_vk{CommandPoolCreateInfo} | @ref CommandPoolCreateInfo @subsection vulkan-mapping-structures-d D diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 0504291d07..8b5f87d8ee 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + CommandBuffer.cpp CommandPool.cpp Extensions.cpp Handle.cpp @@ -46,6 +47,7 @@ set(MagnumVk_GracefulAssert_SRCS LayerProperties.cpp) set(MagnumVk_HEADERS + CommandBuffer.h CommandPool.h Device.h DeviceProperties.h diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp new file mode 100644 index 0000000000..0e4244c67c --- /dev/null +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -0,0 +1,68 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "CommandBuffer.h" + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" + +namespace Magnum { namespace Vk { + +CommandBuffer CommandBuffer::wrap(Device& device, const VkCommandPool pool, const VkCommandBuffer handle, const HandleFlags flags) { + CommandBuffer out{NoCreate}; + out._device = &device; + out._pool = pool; + out._handle = handle; + out._flags = flags; + return out; +} + +CommandBuffer::CommandBuffer(NoCreateT) noexcept: _device{}, _pool{}, _handle{} {} + +CommandBuffer::CommandBuffer(CommandBuffer&& other) noexcept: _device{other._device}, _pool{other._pool}, _handle{other._handle}, _flags{other._flags} { + other._handle = nullptr; +} + +CommandBuffer::~CommandBuffer() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).FreeCommandBuffers(*_device, _pool, 1, &_handle); +} + +CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._pool, _pool); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkCommandBuffer CommandBuffer::release() { + const VkCommandBuffer handle = _handle; + _handle = nullptr; + return handle; +} + +}} diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h new file mode 100644 index 0000000000..adc7c31533 --- /dev/null +++ b/src/Magnum/Vk/CommandBuffer.h @@ -0,0 +1,129 @@ +#ifndef Magnum_Vk_CommandBuffer_h +#define Magnum_Vk_CommandBuffer_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::CommandBuffer + * @m_since_latest + */ + +#include +#include + +#include "Magnum/Tags.h" +#include "Magnum/Magnum.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Command buffer +@m_since_latest + +Wraps a @type_vk_keyword{CommandBuffer}. +*/ +class MAGNUM_VK_EXPORT CommandBuffer { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the command buffer is created on + * @param pool Command pool the buffer is allocated from + * @param handle The @type_vk{CommandBuffer} handle + * @param flags Handle flags + * + * The @p handle is expected to be of an existing Vulkan command + * buffer. Unlike a command buffer allocated using + * @ref CommandPool::allocate(), the Vulkan command buffer is by + * default not deleted on destruction, use @p flags for different + * behavior. + */ + static CommandBuffer wrap(Device& device, VkCommandPool pool, VkCommandBuffer handle, HandleFlags flags = {}); + + /** + * @brief Construct without creating the instance + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit CommandBuffer(NoCreateT) noexcept; + + /** @brief Copying is not allowed */ + CommandBuffer(const CommandBuffer&) = delete; + + /** @brief Move constructor */ + CommandBuffer(CommandBuffer&& other) noexcept; + + /** + * @brief Destructor + * + * Frees associated @type_vk{CommandBuffer} handle, unless the + * instance was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{FreeCommandBuffers}, @ref release() + */ + ~CommandBuffer(); + + /** @brief Copying is not allowed */ + CommandBuffer& operator=(const CommandBuffer&) = delete; + + /** @brief Move assignment */ + CommandBuffer& operator=(CommandBuffer&& other) noexcept; + + /** @brief Underlying @type_vk{CommandBuffer} handle */ + VkCommandBuffer handle() { return _handle; } + /** @overload */ + operator VkCommandBuffer() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan command buffer + * + * Releases ownership of the Vulkan command buffer and returns its + * handle so @fn_vk{FreeCommandBuffers} is not called on destruction. + * The internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkCommandBuffer release(); + + private: + friend CommandPool; + + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkCommandPool _pool; /* Used only for vkFreeCommandBuffers() */ + VkCommandBuffer _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp index 35e9064964..c921bed95f 100644 --- a/src/Magnum/Vk/CommandPool.cpp +++ b/src/Magnum/Vk/CommandPool.cpp @@ -25,6 +25,7 @@ #include "CommandPool.h" +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Result.h" @@ -75,6 +76,22 @@ CommandPool& CommandPool::operator=(CommandPool&& other) noexcept { return *this; } +CommandBuffer CommandPool::allocate(const CommandBufferLevel level) { + CommandBuffer out{NoCreate}; + out._device = _device; + out._pool = _handle; + out._flags = HandleFlag::DestroyOnDestruction; + + VkCommandBufferAllocateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = _handle; + info.commandBufferCount = 1; + info.level = VkCommandBufferLevel(level); + MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).AllocateCommandBuffers(*_device, &info, &out._handle)); + + return out; +} + VkCommandPool CommandPool::release() { const VkCommandPool handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h index 02d01117f3..7abacbe77a 100644 --- a/src/Magnum/Vk/CommandPool.h +++ b/src/Magnum/Vk/CommandPool.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool + * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool, enum @ref Magnum::Vk::CommandBufferLevel * @m_since_latest */ @@ -130,6 +130,22 @@ class MAGNUM_VK_EXPORT CommandPoolCreateInfo { CORRADE_ENUMSET_OPERATORS(CommandPoolCreateInfo::Flags) +/** +@brief Command buffer level +@m_since_latest + +Wraps a @type_vk_keyword{CommandBufferLevel}. +@m_enum_values_as_keywords +@see @ref CommandPool::allocate() +*/ +enum class CommandBufferLevel: Int { + /** Primary command buffer */ + Primary = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + + /** Secondary command buffer */ + Secondary = VK_COMMAND_BUFFER_LEVEL_SECONDARY +}; + /** @brief Command pool @m_since_latest @@ -199,6 +215,13 @@ class MAGNUM_VK_EXPORT CommandPool { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Allocate a command buffer + * + * @see @fn_vk_keyword{AllocateCommandBuffers} + */ + CommandBuffer allocate(CommandBufferLevel level = CommandBufferLevel::Primary); + /** * @brief Release the underlying Vulkan command pool * diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index c7e59593e8..f65e42b55a 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkCommandBufferTest CommandBufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDevicePropertiesTest DevicePropertiesTest.cpp LIBRARIES MagnumVk) @@ -38,6 +39,7 @@ corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) diff --git a/src/Magnum/Vk/Test/CommandBufferTest.cpp b/src/Magnum/Vk/Test/CommandBufferTest.cpp new file mode 100644 index 0000000000..87fd6f2d69 --- /dev/null +++ b/src/Magnum/Vk/Test/CommandBufferTest.cpp @@ -0,0 +1,62 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/CommandBuffer.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandBufferTest: TestSuite::Tester { + explicit CommandBufferTest(); + + void constructNoCreate(); + void constructCopy(); +}; + +CommandBufferTest::CommandBufferTest() { + addTests({&CommandBufferTest::constructNoCreate, + &CommandBufferTest::constructCopy}); +} + +void CommandBufferTest::constructNoCreate() { + { + CommandBuffer buffer{NoCreate}; + CORRADE_VERIFY(!buffer.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void CommandBufferTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandBufferTest) diff --git a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp new file mode 100644 index 0000000000..fb637ca4a1 --- /dev/null +++ b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp @@ -0,0 +1,110 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPool.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct CommandBufferVkTest: VulkanTester { + explicit CommandBufferVkTest(); + + void construct(); + void constructMove(); + void wrap(); +}; + +CommandBufferVkTest::CommandBufferVkTest() { + addTests({&CommandBufferVkTest::construct, + &CommandBufferVkTest::constructMove, + &CommandBufferVkTest::wrap}); +} + +void CommandBufferVkTest::construct() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + { + CommandBuffer buffer = pool.allocate(); + CORRADE_VERIFY(buffer.handle()); + CORRADE_COMPARE(buffer.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void CommandBufferVkTest::constructMove() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + CommandBuffer a = pool.allocate(); + VkCommandBuffer handle = a.handle(); + + CommandBuffer b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + CommandBuffer c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void CommandBufferVkTest::wrap() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + VkCommandBuffer buffer{}; + VkCommandBufferAllocateInfo info{}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = pool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + CORRADE_COMPARE(Result(device()->AllocateCommandBuffers(device(), &info, &buffer)), Result::Success); + CORRADE_VERIFY(buffer); + + auto wrapped = CommandBuffer::wrap(device(), pool, buffer, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), buffer); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), buffer); + CORRADE_VERIFY(!wrapped.handle()); + device()->FreeCommandBuffers(device(), pool, 1, &buffer); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandBufferVkTest) diff --git a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp index 4846bf6f87..3278e4d071 100644 --- a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp @@ -23,6 +23,7 @@ DEALINGS IN THE SOFTWARE. */ +#include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" @@ -37,12 +38,16 @@ struct CommandPoolVkTest: VulkanTester { void construct(); void constructMove(); void wrap(); + + void allocate(); }; CommandPoolVkTest::CommandPoolVkTest() { addTests({&CommandPoolVkTest::construct, &CommandPoolVkTest::constructMove, - &CommandPoolVkTest::wrap}); + &CommandPoolVkTest::wrap, + + &CommandPoolVkTest::allocate}); } void CommandPoolVkTest::construct() { @@ -97,6 +102,14 @@ void CommandPoolVkTest::wrap() { device()->DestroyCommandPool(device(), pool, nullptr); } +void CommandPoolVkTest::allocate() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + CommandBuffer a = pool.allocate(CommandBufferLevel::Secondary); + CORRADE_VERIFY(a.handle()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandPoolVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 171a2a170f..57138970e1 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class CommandBuffer; class CommandPool; class Device; class DeviceCreateInfo; From b45c9c310f65e2393526683d4b1426efd38e020e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Sep 2020 19:09:40 +0200 Subject: [PATCH 36/85] Vk: command pool and buffer reset. --- doc/vulkan-mapping.dox | 4 +- src/Magnum/Vk/CommandBuffer.cpp | 5 +++ src/Magnum/Vk/CommandBuffer.h | 39 ++++++++++++++++++- src/Magnum/Vk/CommandPool.cpp | 4 ++ src/Magnum/Vk/CommandPool.h | 45 ++++++++++++++++++++-- src/Magnum/Vk/Test/CommandBufferVkTest.cpp | 19 ++++++++- src/Magnum/Vk/Test/CommandPoolVkTest.cpp | 12 ++++++ 7 files changed, 120 insertions(+), 8 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 4a5cd8fddf..67988d1181 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -251,8 +251,8 @@ Vulkan function | Matching API Vulkan function | Matching API --------------------------------------- | ------------ -@fn_vk{ResetCommandBuffer} | | -@fn_vk{ResetCommandPool} | | +@fn_vk{ResetCommandBuffer} | @ref CommandBuffer::reset() +@fn_vk{ResetCommandPool} | @ref CommandPool::reset() @fn_vk{ResetDescriptorPool} | | @fn_vk{ResetFences} | | @fn_vk{ResetQueryPool} @m_class{m-label m-flat m-success} **EXT, 1.2** | | diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp index 0e4244c67c..ba563a636b 100644 --- a/src/Magnum/Vk/CommandBuffer.cpp +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -27,6 +27,7 @@ #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" namespace Magnum { namespace Vk { @@ -59,6 +60,10 @@ CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept { return *this; } +void CommandBuffer::reset(const CommandBufferResetFlags flags) { + MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).ResetCommandBuffer(_handle, VkCommandBufferResetFlags(flags))); +} + VkCommandBuffer CommandBuffer::release() { const VkCommandBuffer handle = _handle; _handle = nullptr; diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index adc7c31533..f673dccb1d 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::CommandBuffer + * @brief Class @ref Magnum::Vk::CommandBuffer, enum @ref Magnum::Vk::CommandPoolResetFlag, enum set @ref Magnum::Vk::CommandPoolResetFlags * @m_since_latest */ @@ -41,6 +41,32 @@ namespace Magnum { namespace Vk { +/** +@brief Command buffer reset flag +@m_since_latest + +Wraps @type_vk_keyword{CommandBufferResetFlagBits}. +@m_enum_values_as_keywords +@see @ref CommandBufferResetFlags, @ref CommandBuffer::reset(), + @ref CommandPoolResetFlag, @ref CommandPool::reset() +*/ +enum class CommandBufferResetFlag: UnsignedInt { + /** Recycle all resources from the command pool back to the system */ + ReleaseResources = VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT +}; + +/** +@brief Command buffer reset flags +@m_since_latest + +Wraps @type_vk_keyword{CommandBufferResetFlags}. +@see @ref CommandBuffer::reset(), @ref CommandPoolResetFlags, + @ref CommandPool::reset() +*/ +typedef Containers::EnumSet CommandBufferResetFlags; + +CORRADE_ENUMSET_OPERATORS(CommandBufferResetFlags) + /** @brief Command buffer @m_since_latest @@ -103,6 +129,17 @@ class MAGNUM_VK_EXPORT CommandBuffer { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Reset the command buffer + * + * This operation is allowed only if the originating @ref CommandPool + * was created with @ref CommandPoolCreateInfo::Flag::ResetCommandBuffer. + * If not, the only way to reset is to reset the whole command pool + * using @ref CommandPool::reset(). + * @see @fn_vk_keyword{ResetCommandBuffer} + */ + void reset(CommandBufferResetFlags flags = {}); + /** * @brief Release the underlying Vulkan command buffer * diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp index c921bed95f..625ebc8413 100644 --- a/src/Magnum/Vk/CommandPool.cpp +++ b/src/Magnum/Vk/CommandPool.cpp @@ -92,6 +92,10 @@ CommandBuffer CommandPool::allocate(const CommandBufferLevel level) { return out; } +void CommandPool::reset(const CommandPoolResetFlags flags) { + MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).ResetCommandPool(*_device, _handle, VkCommandPoolResetFlags(flags))); +} + VkCommandPool CommandPool::release() { const VkCommandPool handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h index 7abacbe77a..329fdbca14 100644 --- a/src/Magnum/Vk/CommandPool.h +++ b/src/Magnum/Vk/CommandPool.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool, enum @ref Magnum::Vk::CommandBufferLevel + * @brief Class @ref Magnum::Vk::CommandPoolCreateInfo, @ref Magnum::Vk::CommandPool, enum @ref Magnum::Vk::CommandBufferLevel, @ref Magnum::Vk::CommandPoolResetFlag, enum set @ref Magnum::Vk::CommandPoolResetFlags * @m_since_latest */ @@ -44,8 +44,8 @@ namespace Magnum { namespace Vk { @brief Command pool creation info @m_since_latest -Wraps a @type_vk_keyword{CommandPoolCreateInfo}. -@see @ref CommandPool +Wraps a @type_vk_keyword{CommandPoolCreateInfo}. See @ref CommandPool for usage +information. */ class MAGNUM_VK_EXPORT CommandPoolCreateInfo { public: @@ -62,7 +62,8 @@ class MAGNUM_VK_EXPORT CommandPoolCreateInfo { /** * Allow individual command buffers to be reset to initial state - * instead of just the whole pool. + * using @ref CommandBuffer::reset() instead of just the whole pool + * using @ref CommandPool::reset(). * * @m_class{m-note m-success} * @@ -146,6 +147,32 @@ enum class CommandBufferLevel: Int { Secondary = VK_COMMAND_BUFFER_LEVEL_SECONDARY }; +/** +@brief Command buffer reset flag +@m_since_latest + +Wraps @type_vk_keyword{CommandPoolResetFlagBits}. +@m_enum_values_as_keywords +@see @ref CommandPoolResetFlags, @ref CommandPool::reset(), + @ref CommandBufferResetFlag, @ref CommandBuffer::reset() +*/ +enum class CommandPoolResetFlag: UnsignedInt { + /** Recycle all resources from the command pool back to the system */ + ReleaseResources = VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT +}; + +/** +@brief Command pool reset flags +@m_since_latest + +Wraps @type_vk_keyword{CommandPoolResetFlags}. +@see @ref CommandPool::reset(), @ref CommandBufferResetFlags, + @ref CommandBuffer::reset() +*/ +typedef Containers::EnumSet CommandPoolResetFlags; + +CORRADE_ENUMSET_OPERATORS(CommandPoolResetFlags) + /** @brief Command pool @m_since_latest @@ -222,6 +249,16 @@ class MAGNUM_VK_EXPORT CommandPool { */ CommandBuffer allocate(CommandBufferLevel level = CommandBufferLevel::Primary); + /** + * @brief Reset the command pool + * + * All command buffers allocated from this command pool are reset as + * well. See @ref CommandBuffer::reset() for a way to reset a single + * command buffer. + * @see @fn_vk_keyword{ResetCommandPool} + */ + void reset(CommandPoolResetFlags flags = {}); + /** * @brief Release the underlying Vulkan command pool * diff --git a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp index fb637ca4a1..623f950a9f 100644 --- a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp @@ -38,12 +38,16 @@ struct CommandBufferVkTest: VulkanTester { void construct(); void constructMove(); void wrap(); + + void reset(); }; CommandBufferVkTest::CommandBufferVkTest() { addTests({&CommandBufferVkTest::construct, &CommandBufferVkTest::constructMove, - &CommandBufferVkTest::wrap}); + &CommandBufferVkTest::wrap, + + &CommandBufferVkTest::reset}); } void CommandBufferVkTest::construct() { @@ -105,6 +109,19 @@ void CommandBufferVkTest::wrap() { device()->FreeCommandBuffers(device(), pool, 1, &buffer); } +void CommandBufferVkTest::reset() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics), + CommandPoolCreateInfo::Flag::ResetCommandBuffer}}; + + CommandBuffer a = pool.allocate(); + + a.reset(CommandBufferResetFlag::ReleaseResources); + + /* Does not do anything visible, so just test that it didn't blow up */ + CORRADE_VERIFY(true); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::CommandBufferVkTest) diff --git a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp index 3278e4d071..be8c36eaf7 100644 --- a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp @@ -39,6 +39,7 @@ struct CommandPoolVkTest: VulkanTester { void constructMove(); void wrap(); + void reset(); void allocate(); }; @@ -47,6 +48,7 @@ CommandPoolVkTest::CommandPoolVkTest() { &CommandPoolVkTest::constructMove, &CommandPoolVkTest::wrap, + &CommandPoolVkTest::reset, &CommandPoolVkTest::allocate}); } @@ -102,6 +104,16 @@ void CommandPoolVkTest::wrap() { device()->DestroyCommandPool(device(), pool, nullptr); } +void CommandPoolVkTest::reset() { + CommandPool pool{device(), CommandPoolCreateInfo{ + deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + + pool.reset(CommandPoolResetFlag::ReleaseResources); + + /* Does not do anything visible, so just test that it didn't blow up */ + CORRADE_VERIFY(true); +} + void CommandPoolVkTest::allocate() { CommandPool pool{device(), CommandPoolCreateInfo{ deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; From c6f377b45f0e47e3eb04cc9a686b016a0a5a89b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 15 Sep 2020 19:07:47 +0200 Subject: [PATCH 37/85] Vk: this DeviceCreateInfo constructor is garbage. It only adds confusion and can be replaced by only a moderately longer but infinitely more descriptive oneliner. Delete. --- doc/snippets/MagnumVk.cpp | 3 +-- src/Magnum/Vk/Device.cpp | 2 -- src/Magnum/Vk/Device.h | 8 -------- src/Magnum/Vk/Test/DeviceVkTest.cpp | 31 ++++++++++------------------- 4 files changed, 12 insertions(+), 32 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index af0efd3293..b209edeaa0 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -50,7 +50,6 @@ if(device.isExtensionEnabled()) { } { -Vk::Instance instance; /* Header included again inside a function, but it's fine as the guards will make it empty */ /* [Device-global-function-pointers] */ @@ -58,7 +57,7 @@ Vk::Instance instance; // … -Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{instance})}; +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; device.populateGlobalFunctionPointers(); VkCommandPool commandPool; diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 4d33936d2c..4bcde0da41 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -115,8 +115,6 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext } } -DeviceCreateInfo::DeviceCreateInfo(Instance& instance, const Flags flags): DeviceCreateInfo{pickDevice(instance), flags} {} - DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {} DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 98ddb292cf..c3d086075b 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -116,14 +116,6 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { /** @overload */ explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, Flags flags = {}): DeviceCreateInfo{std::move(deviceProperties), nullptr, flags} {} - /** - * @brief Construct for an implicitly picked device - * - * Calls @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags) - * with a device picked from @p instance using @ref pickDevice(). - */ - explicit DeviceCreateInfo(Instance& instance, Flags flags = {}); - /** * @brief Construct without initializing the contents * diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index fb3f5846ff..7eaa6804e1 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -49,7 +49,6 @@ struct DeviceVkTest: VulkanTester { explicit DeviceVkTest(); void createInfoConstruct(); - void createInfoConstructImplicitDevice(); void createInfoConstructNoImplicitExtensions(); void createInfoExtensions(); void createInfoCopiedStrings(); @@ -122,7 +121,6 @@ struct { DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { addTests({&DeviceVkTest::createInfoConstruct, - &DeviceVkTest::createInfoConstructImplicitDevice, &DeviceVkTest::createInfoConstructNoImplicitExtensions, &DeviceVkTest::createInfoExtensions, &DeviceVkTest::createInfoCopiedStrings, @@ -156,15 +154,8 @@ void DeviceVkTest::createInfoConstruct() { /* Extensions might or might not be enabled */ } -void DeviceVkTest::createInfoConstructImplicitDevice() { - DeviceCreateInfo info{instance()}; - CORRADE_VERIFY(info->sType); - CORRADE_VERIFY(!info->pNext); - /* Extensions might or might not be enabled */ -} - void DeviceVkTest::createInfoConstructNoImplicitExtensions() { - DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{pickDevice(instance()), DeviceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(info->sType); CORRADE_VERIFY(!info->pNext); /* No extensions enabled as we explicitly disabled that */ @@ -176,7 +167,7 @@ void DeviceVkTest::createInfoExtensions() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); - DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{pickDevice(instance()), DeviceCreateInfo::Flag::NoImplicitExtensions}; CORRADE_VERIFY(!info->ppEnabledExtensionNames); CORRADE_COMPARE(info->enabledExtensionCount, 0); @@ -205,7 +196,7 @@ void DeviceVkTest::createInfoCopiedStrings() { Containers::StringView globalButNotNullTerminated = "VK_KHR_maintenance25"_s.except(1); Containers::String localButNullTerminated = Extensions::KHR::draw_indirect_count::string(); - DeviceCreateInfo info{instance(), DeviceCreateInfo::Flag::NoImplicitExtensions}; + DeviceCreateInfo info{pickDevice(instance()), DeviceCreateInfo::Flag::NoImplicitExtensions}; info.addEnabledExtensions({ globalButNotNullTerminated, localButNullTerminated @@ -226,7 +217,7 @@ void DeviceVkTest::createInfoNoQueuePriorities() { std::ostringstream out; Error redirectError{&out}; - DeviceCreateInfo{instance()}.addQueues(0, {}, {}); + DeviceCreateInfo{pickDevice(instance())}.addQueues(0, {}, {}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified\n"); } @@ -238,7 +229,7 @@ void DeviceVkTest::createInfoWrongQueueOutputCount() { std::ostringstream out; Error redirectError{&out}; Queue a{NoCreate}, b{NoCreate}; - DeviceCreateInfo{instance()}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); + DeviceCreateInfo{pickDevice(instance())}.addQueues(0, {0.0f, 1.0f, 0.3f}, {a, b}); CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); } @@ -400,7 +391,7 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { std::ostringstream out; Debug redirectOutput{&out}; Queue queue{NoCreate}; - Device device{instance2, DeviceCreateInfo{instance2, DeviceCreateInfo::Flag::NoImplicitExtensions} + Device device{instance2, DeviceCreateInfo{pickDevice(instance2), DeviceCreateInfo::Flag::NoImplicitExtensions} .addQueues(0, {0.0f}, {queue}) /* Nothing enabled by the application */ }; @@ -495,7 +486,7 @@ void DeviceVkTest::constructRawQueue() { rawQueueInfo.pQueuePriorities = &zero; rawQueueInfo.queueFamilyIndex = 0; rawQueueInfo.queueCount = 1; - Device device{instance(), DeviceCreateInfo{instance()} + Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(rawQueueInfo)}; /* Fetch the raw queue */ @@ -553,7 +544,7 @@ void DeviceVkTest::constructUnknownExtension() { std::ostringstream out; Error redirectError{&out}; Queue queue{NoCreate}; - Device device{instance(), DeviceCreateInfo{instance()} + Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions({"VK_this_doesnt_exist"_s})}; CORRADE_COMPARE(out.str(), "TODO"); @@ -566,7 +557,7 @@ void DeviceVkTest::constructNoQueue() { std::ostringstream out; Error redirectError{&out}; - Device device{instance(), DeviceCreateInfo{instance()}}; + Device device{instance(), DeviceCreateInfo{pickDevice(instance())}}; CORRADE_COMPARE(out.str(), "Vk::Device: needs to be created with at least one queue\n"); } @@ -598,7 +589,7 @@ void DeviceVkTest::wrap() { VkDevice device; Queue queue{NoCreate}; CORRADE_COMPARE(Result(instance2->CreateDevice(deviceProperties, - DeviceCreateInfo{instance2} + DeviceCreateInfo{pickDevice(instance2)} .addQueues(0, {0.0f}, {queue}) .addEnabledExtensions< Extensions::EXT::debug_marker, @@ -646,7 +637,7 @@ void DeviceVkTest::populateGlobalFunctionPointers() { vkDestroyDevice = nullptr; Queue queue{NoCreate}; - Device device{instance(), DeviceCreateInfo{instance()} + Device device{instance(), DeviceCreateInfo{pickDevice(instance())} .addQueues(0, {0.0f}, {queue}) }; CORRADE_VERIFY(!vkDestroyDevice); From a62e2b8f096b6997b13cd97ab60ed5bcbaf5367b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 15 Sep 2020 19:11:53 +0200 Subject: [PATCH 38/85] Vk: start writing some actual docs. Because I put this on hold for two months and almost forgot what design guidelines I wanted to follow and how the heck is the whole thing used. --- doc/features.dox | 1 + doc/snippets/MagnumVk.cpp | 203 ++++++++++++++++++++++++---- doc/vulkan-wrapping.dox | 102 ++++++++++++++ src/Magnum/Vk/CommandBuffer.h | 3 +- src/Magnum/Vk/CommandPool.h | 18 ++- src/Magnum/Vk/Device.h | 109 +++++++++++++-- src/Magnum/Vk/DeviceProperties.h | 15 +- src/Magnum/Vk/ExtensionProperties.h | 8 +- src/Magnum/Vk/Instance.h | 145 ++++++++++++++++++-- src/Magnum/Vk/LayerProperties.h | 4 + 10 files changed, 560 insertions(+), 48 deletions(-) create mode 100644 doc/vulkan-wrapping.dox diff --git a/doc/features.dox b/doc/features.dox index 44142a4b9f..e4d6047f19 100644 --- a/doc/features.dox +++ b/doc/features.dox @@ -39,6 +39,7 @@ necessary to read through everything, pick only what you need. - @subpage plugins --- @copybrief plugins - @subpage file-formats --- @copybrief file-formats - @subpage opengl-wrapping --- @copybrief opengl-wrapping +- @subpage vulkan-wrapping --- @copybrief vulkan-wrapping - @subpage shaders --- @copybrief shaders - @subpage scenegraph --- @copybrief scenegraph - @subpage debug-tools --- @copybrief debug-tools diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index b209edeaa0..2cce629f7a 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -27,10 +27,16 @@ #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" +#include "Magnum/Vk/CommandBuffer.h" +#include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Queue.h" #include "MagnumExternal/Vulkan/flextVkGlobal.h" using namespace Magnum; @@ -38,65 +44,215 @@ using namespace Magnum; #define DOXYGEN_IGNORE(...) __VA_ARGS__ int main() { + { -Vk::Device device{NoCreate}; -/* [Device-isExtensionEnabled] */ -if(device.isExtensionEnabled()) { - // keep mesh indices 8bit -} else { - // convert them to 16bit +/* [wrapping-extending-create-info] */ +Vk::InstanceCreateInfo info{DOXYGEN_IGNORE()}; + +/* Add a custom validation features setup */ +VkValidationFeaturesEXT validationFeatures{}; +validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT; +validationFeatures.enabledValidationFeatureCount = 1; +constexpr auto bestPractices = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; +validationFeatures.pEnabledValidationFeatures = &bestPractices; +CORRADE_INTERNAL_ASSERT(!info->pNext); // or find the end of the pNext chain +info->pNext = &validationFeatures; +/* [wrapping-extending-create-info] */ } -/* [Device-isExtensionEnabled] */ + +{ +Vk::Instance instance; +/* [CommandPool-usage] */ +Vk::DeviceProperties props = DOXYGEN_IGNORE(pickDevice(instance)); +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; + +Vk::CommandPool graphicsCommandPool{device, Vk::CommandPoolCreateInfo{ + props.pickQueueFamily(Vk::QueueFlag::Graphics) +}}; +/* [CommandPool-usage] */ + +/* [CommandPool-usage-allocate] */ +Vk::CommandBuffer commandBuffer = graphicsCommandPool.allocate(); + +// fill the buffer, submit … +/* [CommandPool-usage-allocate] */ +} + +{ +Vk::Instance instance; +/* [Device-usage-pick] */ +Vk::DeviceProperties props = Vk::pickDevice(instance); +/* [Device-usage-pick] */ + +/* [Device-usage-construct-queue] */ +Vk::Queue queue{NoCreate}; +Vk::Device device{instance, Vk::DeviceCreateInfo{props} + .addQueues(props.pickQueueFamily(Vk::QueueFlag::Graphics), {0.0f}, {queue}) +}; +/* [Device-usage-construct-queue] */ } { +Vk::Instance instance; +Vk::DeviceProperties props{NoCreate}; +using namespace Containers::Literals; +/* [Device-usage-extensions] */ +Vk::Device device{instance, Vk::DeviceCreateInfo{props} + DOXYGEN_IGNORE() + .addEnabledExtensions< // predefined extensions + Vk::Extensions::EXT::index_type_uint8, + Vk::Extensions::KHR::device_group>() + .addEnabledExtensions({"VK_NV_mesh_shader"_s}) // can be plain strings too +}; +/* [Device-usage-extensions] */ +} + +{ +Vk::Instance instance; +Vk::DeviceProperties props{NoCreate}; +using namespace Containers::Literals; +/* [Device-usage-check-supported] */ +Vk::ExtensionProperties extensions = props.enumerateExtensionProperties(); + +Vk::DeviceCreateInfo info{props}; +if(extensions.isSupported()) + info.addEnabledExtensions(); +if(extensions.isSupported("VK_NV_mesh_shader"_s)) + info.addEnabledExtensions({"VK_NV_mesh_shader"_s}); +DOXYGEN_IGNORE() +/* [Device-usage-check-supported] */ +} + +{ +Vk::Instance instance; +VkQueryPool pool{}; +/* [Device-function-pointers] */ +Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)})}; + +// ... +device->ResetQueryPoolEXT(device, DOXYGEN_IGNORE(pool, 0, 0)); +/* [Device-function-pointers] */ +} + +{ +VkQueryPool pool{}; /* Header included again inside a function, but it's fine as the guards will make it empty */ /* [Device-global-function-pointers] */ #include -// … +DOXYGEN_IGNORE() Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; device.populateGlobalFunctionPointers(); -VkCommandPool commandPool; -VkCommandPoolCreateInfo info{}; -// … -vkCreateCommandPool(device, &info, nullptr, &commandPool); +DOXYGEN_IGNORE() +vkResetQueryPoolEXT(device, DOXYGEN_IGNORE(pool, 0, 0)); /* [Device-global-function-pointers] */ } { -Vk::Instance instance; -/* [Instance-isExtensionEnabled] */ -if(instance.isExtensionEnabled()) { - // use the fancy debugging APIs -} else if(instance.isExtensionEnabled()) { - // use the non-fancy and deprecated debugging APIs +Vk::Device device{NoCreate}; +/* [Device-isExtensionEnabled] */ +if(device.isExtensionEnabled()) { + // keep mesh indices 8bit } else { - // well, tough luck + // convert them to 16bit } -/* [Instance-isExtensionEnabled] */ +/* [Device-isExtensionEnabled] */ } { +int argc{}; +const char** argv{}; +/* [Instance-usage] */ +using namespace Containers::Literals; + +Vk::Instance instance{Vk::InstanceCreateInfo{argc, argv} + .setApplicationInfo("My Vulkan Application"_s, Vk::version(1, 2, 3)) +}; +/* [Instance-usage] */ +} + +{ +int argc{}; +const char** argv{}; +using namespace Containers::Literals; +/* [Instance-usage-layers-extensions] */ +Vk::Instance instance{Vk::InstanceCreateInfo{argc, argv} + DOXYGEN_IGNORE() + .addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}) + .addEnabledExtensions< // predefined extensions + Vk::Extensions::EXT::debug_report, + Vk::Extensions::KHR::external_fence_capabilities>() + .addEnabledExtensions({"VK_KHR_xcb_surface"_s}) // can be plain strings too +}; +/* [Instance-usage-layers-extensions] */ +} + +{ +int argc{}; +const char** argv{}; +using namespace Containers::Literals; +/* [Instance-usage-check-supported] */ +/* Query layer and extension support */ +Vk::LayerProperties layers = Vk::enumerateLayerProperties(); +Vk::InstanceExtensionProperties extensions = + /* ... including extensions exposed only by the extra layers */ + Vk::enumerateInstanceExtensionProperties(layers.names()); + +/* Enable only those that are supported */ +Vk::InstanceCreateInfo info{argc, argv, &layers, &extensions}; +if(layers.isSupported("VK_LAYER_KHRONOS_validation"_s)) + info.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}); +if(extensions.isSupported()) + info.addEnabledExtensions(); +DOXYGEN_IGNORE() + +Vk::Instance instance{info}; +/* [Instance-usage-check-supported] */ +} + +{ +/* [Instance-function-pointers] */ +Vk::Instance instance{DOXYGEN_IGNORE()}; + +VkPhysicalDeviceGroupPropertiesKHR properties[10]; +UnsignedInt count = Containers::arraySize(properties); +instance->EnumeratePhysicalDeviceGroupsKHR(instance, &count, properties); +/* [Instance-function-pointers] */ +} + +{ +Vk::Instance instance; /* Header included again inside a function, but it's fine as the guards will make it empty */ /* [Instance-global-function-pointers] */ #include -// … +DOXYGEN_IGNORE() -Vk::Instance instance; instance.populateGlobalFunctionPointers(); -VkPhysicalDeviceGroupProperties properties[10]; +VkPhysicalDeviceGroupPropertiesKHR properties[10]; UnsignedInt count = Containers::arraySize(properties); vkEnumeratePhysicalDeviceGroupsKHR(instance, &count, properties); /* [Instance-global-function-pointers] */ } +{ +Vk::Instance instance; +/* [Instance-isExtensionEnabled] */ +if(instance.isExtensionEnabled()) { + // use the fancy debugging APIs +} else if(instance.isExtensionEnabled()) { + // use the non-fancy and deprecated debugging APIs +} else { + // well, tough luck +} +/* [Instance-isExtensionEnabled] */ +} + { /* [Integration] */ VkOffset2D a{64, 32}; @@ -108,4 +264,5 @@ VkClearColorValue c = VkClearColorValue(0xff9391_srgbf); static_cast(b); static_cast(c); } + } diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox new file mode 100644 index 0000000000..6649bcefa1 --- /dev/null +++ b/doc/vulkan-wrapping.dox @@ -0,0 +1,102 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +namespace Magnum { +/** @page vulkan-wrapping Vulkan wrapping layer +@brief Overview of the base Vulkan wrapper API + +@tableofcontents +@m_footernavigation + +The @ref Magnum::Vk library is a thin but high-level abstraction of the +[Vulkan](https://www.khronos.org/vulkan/) GPU API, providing sane defaults with +ability to opt-in for greater control and performance. + +@section vulkan-wrapping-instance-device Instance and device wrappers + +Compared to OpenGL, which has a concept of "current context", Vulkan doesn't +have any implicit globals. The @ref Vk library follows that, with each object +carrying a reference to a corresponding instance or device along. This was +chosen as a reasonable tradeoff between requiring an explicit instance/device +parameter in each API (which would be too error-prone and annoying to use) and +having an implicit thread-local instance/device (which would repeat the +well-known pain points of OpenGL). + +Vulkan API entrypoints aren't global either because each instance and device +can have a different set of enabled layers and extensions, and thus different +instance- and device-local function pointers. While the Vulkan specification +allows device-level functions to be queried on an instance and thus use the +same function pointers on a variety of devices, such workflow implies +additional dispatch overhead, and thus isn't recommended. Magnum instead +stores instance- and device-level function pointers locally in each +@ref Vk::Instance and @ref Vk::Device to avoid this overhead --- these are then +accessible through @ref Vk::Instance::operator->() "operator->()" on both: + +@snippet MagnumVk.cpp Instance-function-pointers + +For convenience and for easier interaction with 3rd party code, such pointers +can be made global by calling @ref Vk::Instance::populateGlobalFunctionPointers() +and @ref Vk::Device::populateGlobalFunctionPointers(), after which you can use +the `vk*` functions as usual. However, all implications coming from these being +tied to a particular instance/device still apply: + +@snippet MagnumVk.cpp Instance-global-function-pointers + +@section vulkan-wrapping-create-info CreateInfo structure wrappers + +In most cases, a`Vk::*CreateInfo` instance has all required fields set to valid +values upon construction, with everything else optional. One exception to this +rule is for example @ref Vk::DeviceCreateInfo, where the user is expected to +call @ref Vk::DeviceCreateInfo::addQueues() "addQueues()". + +@section vulkan-wrapping-raw Common interfaces for interaction with raw Vulkan code + +Each wrapped Vulkan object has a @ref Vk::Instance::handle() "handle()" getter, +giving back the underlying Vulkan handle such as @type_vk{Instance}. In +addition it's also implicitly convertible to that handle type, which means you +can pass it as-is to raw Vulkan APIs. You can also use +@ref Vk::Instance::release() "release()" to release its ownership and continue +to use it as a regular handle. Conversely, any Vulkan handle can be wrapped +into a first-class Magnum object using a corresponding +@ref Vk::Instance::wrap() "wrap()" function. + +Similarly, all @ref Vk::InstanceCreateInfo "Vk::*CreateInfo" wrapper classes +are convertible to a `Vk*CreateInfo` pointer in order to be easily passable +directly to Vulkan APIs. You can create them from an existing +`Vk*CreateInfo` instances as well, and use +@ref Vk::InstanceCreateInfo::operator->() "operator->()" to access the wrapped +structure to supply additional parameters not exposed by Magnum. However take +care to not clash with values and pointers already set: + +@snippet MagnumVk.cpp wrapping-extending-create-info + +To completely mitigate the overhead from instantiating wrapper `*CreateInfo` +classes, each of them can also be constructed using the @ref NoInit tag, which +will skip all initialization and leave the contents unspecified to be filled +later. Note that at that point you have the full responsibility to correctly +set up all members. + +*/ +} diff --git a/src/Magnum/Vk/CommandBuffer.h b/src/Magnum/Vk/CommandBuffer.h index f673dccb1d..3997307fd9 100644 --- a/src/Magnum/Vk/CommandBuffer.h +++ b/src/Magnum/Vk/CommandBuffer.h @@ -71,7 +71,8 @@ CORRADE_ENUMSET_OPERATORS(CommandBufferResetFlags) @brief Command buffer @m_since_latest -Wraps a @type_vk_keyword{CommandBuffer}. +Wraps a @type_vk_keyword{CommandBuffer}. A command buffer instance is usually +allocated from a @ref CommandPool, see its documentation for usage information. */ class MAGNUM_VK_EXPORT CommandBuffer { public: diff --git a/src/Magnum/Vk/CommandPool.h b/src/Magnum/Vk/CommandPool.h index 329fdbca14..2d2b5c38f1 100644 --- a/src/Magnum/Vk/CommandPool.h +++ b/src/Magnum/Vk/CommandPool.h @@ -177,7 +177,23 @@ CORRADE_ENUMSET_OPERATORS(CommandPoolResetFlags) @brief Command pool @m_since_latest -Wraps a @type_vk_keyword{CommandPool}. +Wraps a @type_vk_keyword{CommandPool} and handles allocation of +@ref CommandBuffer "CommandBuffer"s. + +@section Vk-CommandPool-usage Usage + +A @ref CommandPoolCreateInfo doesn't need many inputs --- the only required is +queue family index coming from @ref DeviceProperties of the device it's created +on: + +@snippet MagnumVk.cpp CommandPool-usage + +After that, you can allocate command buffers as follows. The command buffers +are freed at the end of their instance lifetime, you can also put all allocated +buffers back to initial state by calling @ref reset(), or alternatively reset +each buffer separately using @ref CommandBuffer::reset(). + +@snippet MagnumVk.cpp CommandPool-usage-allocate */ class MAGNUM_VK_EXPORT CommandPool { public: diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index c3d086075b..73fe11b292 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -53,8 +53,8 @@ namespace Implementation { @brief Device creation info @m_since_latest -Wraps a @type_vk_keyword{DeviceCreateInfo}. -@see @ref Device +Wraps a @type_vk_keyword{DeviceCreateInfo}. See @ref Device for usage +information. */ class MAGNUM_VK_EXPORT DeviceCreateInfo { public: @@ -213,8 +213,99 @@ CORRADE_ENUMSET_OPERATORS(DeviceCreateInfo::Flags) @brief Device @m_since_latest -Wraps a @type_vk_keyword{Device} and stores all device-specific function +Wraps a @type_vk_keyword{Device} and stores device-specific Vulkan function pointers. + +@section Vk-Device-usage Usage + +With an @ref Instance ready, a device has to be picked first. Commonly it's +done by calling @ref pickDevice() and letting the library choose. This +selection is affected by the `--magnum-device` +@ref Vk-Device-command-line "command-line option", giving the end users an +ability to pick a particular device, choose a discrete or integrated GPU or +even a software implementation. If the application needs something specific, +you can use @ref enumerateDevices() instead, pick a device from the list +manually, provide the users with a list to choose from etc. + +@snippet MagnumVk.cpp Device-usage-pick + +After a device is picked, a @ref Device can be created using +@ref DeviceCreateInfo. At the very least you'll need to set up queues, as every +Vulkan device needs at least one. That's done by creating an empty @ref Queue +instance and then referencing it from @ref DeviceCreateInfo::addQueues(). After +the device is constructed, the queue gets populated and is ready to be used. + +@snippet MagnumVk.cpp Device-usage-construct-queue + +In the above snippet, we requested a graphics queue --- the +@ref DeviceProperties instance we made earlier acts as knowledge base for a +particular device, providing info about available queues, extensions, features, +memory heaps and implementation limits --- and we used a convenience API to +pick the first available graphics queue. As with device picking, you can +also iterate through all @ref DeviceProperties::queueFamilyCount() and choose +one manually. + +Same as with @ref Instance, the above won't enable any additional extensions +except for what the engine itself needs or what's supplied on the command line. Use @ref DeviceCreateInfo::addEnabledExtensions() to enable them, you can use +both string names as well as predefined *device* extensions from the +@ref Extensions namespace. Later on, presence of predefined extensions can be +checked with @ref isExtensionEnabled(). + +@snippet MagnumVk.cpp Device-usage-extensions + +Usually you'll be first checking for extension availability instead, which is +again accessible through the @ref DeviceProperties instance. Similarly to +@ref Instance, as the extension list is used internally as well, pass it in the +constructor to avoid querying them again internally: + +@snippet MagnumVk.cpp Device-usage-check-supported + +With both @ref Instance and @ref Device created, you can proceed to setting up +a @ref CommandPool. + +@section Vk-Device-command-line Command-line options + +The @ref Device inherits a subset of the +@ref Vk-Instance-command-line "Instance command-line options", in particular +the following. If the @ref Instance didn't get `argc` / `argv` passed, only the +environment variables are used. + +- `--magnum-disable-extensions LIST` --- Vulkan instance or device extensions + to disable, meaning @ref DeviceCreateInfo::addEnabledExtensions() will skip + them (environment: `MAGNUM_DISABLE_EXTENSIONS`) +- `--magnum-enable-extensions LIST` --- Vulkan device extensions to enable in + addition to @ref DeviceCreateInfo defaults and what the application + requests (environment: `MAGNUM_ENABLE_EXTENSIONS`) +- `--magnum-vulkan-version X.Y` --- force @ref Device Vulkan version instead + of using what the device reports as supported, affecting what entrypoints + and extensions get used (environment: `MAGNUM_VULKAN_VERSION`) +- `--magnum-log default|quiet|verbose` --- console logging (environment: + `MAGNUM_LOG`) (default: `default`) +- `--magnum-device ID|integrated|discrete|virtual|cpu` --- device ID or kind + to pick in @ref pickDevice(); if a device is selected through + @ref enumerateDevices() or any other way, this option has no effect + (environment: `MAGNUM_DEVICE`) + +@section Vk-Device-raw Interaction with raw Vulkan code + +In addition to the common properties explained in @ref vulkan-wrapping-raw, +the @ref Device contains device-level Vulkan function pointers, accessible +through @ref operator->(): + +@snippet MagnumVk.cpp Device-function-pointers + +These functions are by default not accessible globally (and neither there is a +global "current instance"), which is done in order to avoid multiple +independent instances affecting each other. Sometimes it is however desirable +to have global function pointers --- for example when a 3rd party code needs to +operate on the same instance, or when writing quick prototype code --- and then +it's possible to populate those using @ref populateGlobalFunctionPointers(). +Compared to the above, the same custom code would then look like this: + +@snippet MagnumVk.cpp Device-global-function-pointers + +Similarly you can use @ref Instance::populateGlobalFunctionPointers() to +populate instance-level global function pointers. */ class MAGNUM_VK_EXPORT Device { public: @@ -235,6 +326,11 @@ class MAGNUM_VK_EXPORT Device { * things. If @p enabledExtensions is empty, the device will behave as * if no extensions were enabled. * + * Note that this function retrieves all device-specific Vulkan + * function pointers, which is a relatively costly operation. It's thus + * not recommended to call this function repeatedly for creating + * short-lived device instances, even though it's technically correct. + * * Unlike a device created using a constructor, the Vulkan device is by * default not deleted on destruction, use @p flags for different * behavior. @@ -352,12 +448,9 @@ class MAGNUM_VK_EXPORT Device { * @brief Populate global device-level function pointers to be used with third-party code * * Populates device-level global function pointers so third-party - * code is able to call global device-level `vk*` functions: - * - * @snippet MagnumVk.cpp Device-global-function-pointers + * code is able to call global device-level `vk*` functions. See + * @ref Vk-Device-raw for more information. * - * Use @ref Instance::populateGlobalFunctionPointers() to populate - * instance-level global function pointers. * @attention This operation is changing global state. You need to * ensure that this function is not called simultaenously from * multiple threads and code using those function points is diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 9a2efd0b22..ff0a7860a4 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -135,7 +135,11 @@ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, QueueFlags value); Wraps a @type_vk_keyword{PhysicalDevice} along with its (lazy-populated) properties such as @type_vk_keyword{PhysicalDeviceProperties2} and @type_vk_keyword{GetPhysicalDeviceQueueFamilyProperties2}. -@see @ref enumerateDevices() + +See the @ref Vk-Device-usage "Device class docs" for an example of using this +class for enumerating available devices and picking one of them. + +@see @ref pickDevice(), @ref enumerateDevices() */ class MAGNUM_VK_EXPORT DeviceProperties { public: @@ -339,6 +343,9 @@ class MAGNUM_VK_EXPORT DeviceProperties { @brief Enumerate physical devices @m_since_latest +Returns a list of all devices present on the system. See @ref pickDevice() for +an alternative that pick just a single appropriate device. See @ref Device for +general usage information. @see @fn_vk_keyword{EnumeratePhysicalDevices} */ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& instance); @@ -348,9 +355,9 @@ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& @m_since_latest Calls @ref enumerateDevices() and selects a device based on preferences -specified through command-line parameters or the environment. If a device is -not found, exits. See @ref tryPickDevice() for an alternative that doesn't exit -on failure. +specified through the `--magnum-device` @ref Vk-Instance-command-line "command-line option". +If a device is not found, exits. See @ref tryPickDevice() for an alternative +that doesn't exit on failure. See @ref Device for general usage information. */ MAGNUM_VK_EXPORT DeviceProperties pickDevice(Instance& instance); diff --git a/src/Magnum/Vk/ExtensionProperties.h b/src/Magnum/Vk/ExtensionProperties.h index b2589ef9a3..4935f6e302 100644 --- a/src/Magnum/Vk/ExtensionProperties.h +++ b/src/Magnum/Vk/ExtensionProperties.h @@ -47,7 +47,13 @@ namespace Magnum { namespace Vk { Provides a searchable container of Vulkan device extensions enumerated with @ref DeviceProperties::enumerateExtensionProperties(). -@see @ref InstanceExtensionProperties, @type_vk_keyword{ExtensionProperties} + +See the @ref Vk-Device-usage "Device class docs" for an example of using this +class for checking available extensions before enabling them on a device. See +@ref Vk-Instance-usage "Instance class docs" for the same but using +@ref InstanceExtensionProperties. + +@see @type_vk_keyword{ExtensionProperties} */ class MAGNUM_VK_EXPORT ExtensionProperties { public: diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 5c4e520d8a..7abf547f1a 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -52,8 +52,7 @@ namespace Implementation { @m_since_latest Wraps a @type_vk_keyword{InstanceCreateInfo} and -@type_vk_keyword{ApplicationInfo}. -@see @ref Instance +@type_vk_keyword{ApplicationInfo}. See @ref Instance for usage information. */ class MAGNUM_VK_EXPORT InstanceCreateInfo { public: @@ -156,6 +155,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * * Use the @ref version() helper to create the @p version value. The * name is @cpp nullptr @ce by default. + * + * The function makes copies of string views that are not owning or + * null-terminated, use the @link Containers::Literals::operator""_s() @endlink + * literal to prevent that where possible. */ InstanceCreateInfo& setApplicationInfo(Containers::StringView name, Version version); @@ -164,7 +167,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * @return Reference to self (for method chaining) * * All listed layers are expected be supported, use - * @ref LayerProperties::isSupported() to check for their presence. + * @ref LayerProperties::isSupported() to check for their presence. If + * a particular layer is listed among `--magnum-disable-layers` in + * @ref Vk-Instance-command-line "command-line options", it's not + * added. * * The function makes copies of string views that are not owning or * null-terminated, use the @link Containers::Literals::operator""_s() @endlink @@ -181,7 +187,10 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * All listed extensions are expected to be supported either globally * or in at least one of the enabled layers, use * @ref InstanceExtensionProperties::isSupported() to check for their - * presence. + * presence. If a particular extension is listed among + * `--magnum-disable-extensions` in + * @ref Vk-Instance-command-line "command-line options", it's not + * added. * * The function makes copies of string views that are not owning or * null-terminated, use the @link Containers::Literals::operator""_s() @endlink @@ -228,7 +237,121 @@ CORRADE_ENUMSET_OPERATORS(InstanceCreateInfo::Flags) Wraps a @type_vk_keyword{Instance} and stores instance-specific Vulkan function pointers. -@see @ref vulkan-wrapping + +@section Vk-Instance-usage Usage + +While an @ref Instance can be default-constructed without much fuss, it's +recommended to pass a @ref InstanceCreateInfo with at least the `argc` / `argv` +pair, which allows you to use various `--magnum-*` +@ref Vk-Instance-command-line "command-line options" listed below. Setting +application info may be beneficial for the driver, but it's not required +either. + +@snippet MagnumVk.cpp Instance-usage + + + +@m_class{m-note m-success} + +@par + The above code uses the @link Containers::Literals::operator""_s() @endlink + literal, which lets the library know that given string is global and + null-terminated. Such strings then don't need to be copied internally to + keep them in scope until they're consumed by Vulkan APIs. The same is + recommended to do for extension and layer names where possible. + +The above won't enable any additional layers or extensions except for what the +engine itself needs or what's supplied on the command line. Use +@ref InstanceCreateInfo::addEnabledLayers() and +@ref InstanceCreateInfo::addEnabledExtensions() to enable them, you can use +both string names as well as predefined *instance* extensions from the +@ref Extensions namespace. Later on, presence of predefined extensions can be +checked with @ref isExtensionEnabled(). + +@snippet MagnumVk.cpp Instance-usage-layers-extensions + +However, with the above approach, if any layer or extension isn't available, +the instance creation will abort. The recommended workflow is thus first +checking layer and extension availability using @ref enumerateLayerProperties() +and @ref enumerateInstanceExtensionProperties(). The @ref InstanceCreateInfo +class uses these properties internally as well, pass them in the constructor +to avoid querying those again internally: + +@snippet MagnumVk.cpp Instance-usage-check-supported + +Next step after creating a Vulkan instance is picking and creating a +@ref Device. + +@section Vk-Instance-command-line Command-line options + +The @ref Instance is configurable through command-line options that are passed +through the @ref InstanceCreateInfo `argc` / `argv` parameters. If those are +not passed, only the environment variables are used. A subset of these options +is reused by a subsequently created @ref Device as well. + +@code{.sh} + [--magnum-help] + [--magnum-disable-layers LIST] + [--magnum-disable-extensions LIST] + [--magnum-enable-layers LIST] + [--magnum-enable-instance-extensions LIST] + [--magnum-enable-extensions LIST] + [--magnum-vulkan-version X.Y] + [--magnum-log default|quiet|verbose] + [--magnum-device ID|integrated|discrete|virtual|cpu] ... +@endcode + +Arguments: + +- `...` --- main application arguments (see `-h` or `--help` for details) +- `--magnum-help` --- display this help message and exit +- `--magnum-disable-layers LIST` --- Vulkan layers to disable, meaning + @ref InstanceCreateInfo::addEnabledLayers() will skip them (environment: + `MAGNUM_DISABLE_LAYERS`) +- `--magnum-disable-extensions LIST` --- Vulkan instance or device extensions + to disable, meaning @ref InstanceCreateInfo::addEnabledExtensions() and + @ref DeviceCreateInfo::addEnabledExtensions() will skip them (environment: + `MAGNUM_DISABLE_EXTENSIONS`) +- `--magnum-enable-layers LIST` --- Vulkan layers to enable in addition to + @ref InstanceCreateInfo defaults and what the application requests + (environment: `MAGNUM_ENABLE_LAYERS`) +- `--magnum-enable-instance-extensions LIST` --- Vulkan instance extensions + to enable in addition to @ref InstanceCreateInfo defaults and what the + application requests (environment: `MAGNUM_ENABLE_INSTANCE_EXTENSIONS`) +- `--magnum-enable-extensions LIST` --- Vulkan device extensions to enable in + addition to @ref DeviceCreateInfo defaults and what the application + requests (environment: `MAGNUM_ENABLE_EXTENSIONS`) +- `--magnum-vulkan-version X.Y` --- force @ref Instance and @ref Device + Vulkan version instead of using what the instance / device reports as + supported, affecting what entrypoints and extensions get used (environment: + `MAGNUM_VULKAN_VERSION`) +- `--magnum-log default|quiet|verbose` --- console logging (environment: + `MAGNUM_LOG`) (default: `default`) +- `--magnum-device ID|integrated|discrete|virtual|cpu` --- device ID or kind + to pick in @ref pickDevice(); if a device is selected through + @ref enumerateDevices() or any other way, this option has no effect + (environment: `MAGNUM_DEVICE`) + +@section Vk-Instance-raw Interaction with raw Vulkan code + +In addition to the common properties explained in @ref vulkan-wrapping-raw, the +@ref Instance contains instance-level Vulkan function pointers, accessible +through @ref operator->(): + +@snippet MagnumVk.cpp Instance-function-pointers + +These functions are by default not accessible globally (and neither there is a +global "current instance"), which is done in order to avoid multiple +independent instances affecting each other. Sometimes it is however desirable +to have global function pointers --- for example when a 3rd party code needs to +operate on the same instance, or when writing quick prototype code --- and then +it's possible to populate those using @ref populateGlobalFunctionPointers(). +Compared to the above, the same custom code would then look like this: + +@snippet MagnumVk.cpp Instance-global-function-pointers + +Similarly you can use @ref Device::populateGlobalFunctionPointers() to populate +device-level global function pointers. */ class MAGNUM_VK_EXPORT Instance { public: @@ -248,6 +371,11 @@ class MAGNUM_VK_EXPORT Instance { * things. If @p enabledExtensions empty, the instance will behave as * if no extensions were enabled. * + * Note that this function retrieves all instance-specific Vulkan + * function pointers, which is a relatively costly operation. It's thus + * not recommended to call this function repeatedly for creating + * short-lived instances, even though it's technically correct. + * * Unlike an instance created using a constructor, the Vulkan instance * is by default not deleted on destruction, use @p flags for different * behavior. @@ -360,12 +488,9 @@ class MAGNUM_VK_EXPORT Instance { * @brief Populate global instance-level function pointers to be used with third-party code * * Populates instance-level global function pointers so third-party - * code is able to call global instance-level `vk*` functions: - * - * @snippet MagnumVk.cpp Instance-global-function-pointers + * code is able to call global instance-level `vk*` functions. See + * @ref Vk-Instance-raw for more information. * - * Use @ref Device::populateGlobalFunctionPointers() to populate - * device-level global function pointers. * @attention This operation is changing global state. You need to * ensure that this function is not called simultaenously from * multiple threads and code using those function points is diff --git a/src/Magnum/Vk/LayerProperties.h b/src/Magnum/Vk/LayerProperties.h index 98a6cc0e27..a3b669f4e4 100644 --- a/src/Magnum/Vk/LayerProperties.h +++ b/src/Magnum/Vk/LayerProperties.h @@ -50,6 +50,10 @@ layers are [deprecated since Vulkan 1.0.13](https://github.com/KhronosGroup/Vulk and the assumption is that no drivers currently use rely on these anymore. See [§ 37.3.1](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap38.html#extendingvulkan-layers-devicelayerdeprecation) for more information. + +See the @ref Vk-Instance-usage "Instance class docs" for an example of using +this class for checking available layers before enabling them on an instance. + @see @ref ExtensionProperties, @ref enumerateInstanceExtensionProperties(), @type_vk_keyword{LayerProperties} */ From a4d906de86aaada27a1bde60755329a4e865c91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 26 Sep 2020 15:53:33 +0200 Subject: [PATCH 39/85] Vk: querying device memory heaps and types. Memory type flags are put into a new, separate Memory.h header as those will be needed more often than the (ever-growing) DeviceProperties -- from Image and Buffer constructors, in particular. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/DeviceProperties.cpp | 80 ++++++++++++ src/Magnum/Vk/DeviceProperties.h | 116 +++++++++++++++++- .../Vk/Implementation/InstanceState.cpp | 3 + src/Magnum/Vk/Implementation/InstanceState.h | 1 + src/Magnum/Vk/Memory.cpp | 61 +++++++++ src/Magnum/Vk/Memory.h | 114 +++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/DevicePropertiesTest.cpp | 18 ++- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 102 +++++++++++++++ src/Magnum/Vk/Test/MemoryTest.cpp | 60 +++++++++ src/Magnum/Vk/Vk.h | 4 + 13 files changed, 561 insertions(+), 3 deletions(-) create mode 100644 src/Magnum/Vk/Memory.cpp create mode 100644 src/Magnum/Vk/Memory.h create mode 100644 src/Magnum/Vk/Test/MemoryTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 67988d1181..12d5a52f45 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -207,7 +207,7 @@ Vulkan function | Matching API @fn_vk{GetPhysicalDeviceFeatures}, \n @fn_vk{GetPhysicalDeviceFeatures2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceFormatProperties}, \n @fn_vk{GetPhysicalDeviceFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetPhysicalDeviceImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@fn_vk{GetPhysicalDeviceMemoryProperties}, \n @fn_vk{GetPhysicalDeviceMemoryProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetPhysicalDeviceMemoryProperties}, \n @fn_vk{GetPhysicalDeviceMemoryProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties::memoryProperties() @fn_vk{GetPhysicalDeviceProperties}, \n @fn_vk{GetPhysicalDeviceProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties @fn_vk{GetPhysicalDeviceQueueFamilyProperties}, \n @fn_vk{GetPhysicalDeviceQueueFamilyProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref DeviceProperties::queueFamilyProperties() @fn_vk{GetPhysicalDeviceSparseImageFormatProperties}, \n @fn_vk{GetPhysicalDeviceSparseImageFormatProperties2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 8b5f87d8ee..3f8906b939 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -32,6 +32,7 @@ set(MagnumVk_SRCS Extensions.cpp Handle.cpp Instance.cpp + Memory.cpp Result.cpp Version.cpp @@ -58,6 +59,7 @@ set(MagnumVk_HEADERS Instance.h Integration.h LayerProperties.h + Memory.h Queue.h Result.h TypeTraits.h diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 5aabb18e15..e6e0d39811 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -43,6 +43,7 @@ namespace Magnum { namespace Vk { struct DeviceProperties::State { VkPhysicalDeviceProperties2 properties{}; + VkPhysicalDeviceMemoryProperties2 memoryProperties{}; Containers::Array queueFamilyProperties; }; @@ -176,6 +177,65 @@ Containers::Optional DeviceProperties::tryPickQueueFamily(const Que return {}; } +const VkPhysicalDeviceMemoryProperties2& DeviceProperties::memoryProperties() { + if(!_state) _state.emplace(); + + if(!_state->memoryProperties.sType) { + _state->memoryProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; + _instance->state().getPhysicalDeviceMemoryPropertiesImplementation(*this, _state->memoryProperties); + } + + return _state->memoryProperties; +} + +void DeviceProperties::getMemoryPropertiesImplementationDefault(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties) { + return (**self._instance).GetPhysicalDeviceMemoryProperties(self._handle, &properties.memoryProperties); +} + +void DeviceProperties::getMemoryPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties) { + return (**self._instance).GetPhysicalDeviceMemoryProperties2KHR(self._handle, &properties); +} + +void DeviceProperties::getMemoryPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties) { + return (**self._instance).GetPhysicalDeviceMemoryProperties2(self._handle, &properties); +} + +UnsignedInt DeviceProperties::memoryHeapCount() { + return memoryProperties().memoryProperties.memoryHeapCount; +} + +UnsignedLong DeviceProperties::memoryHeapSize(const UnsignedInt heap) { + const VkPhysicalDeviceMemoryProperties& properties = memoryProperties().memoryProperties; + CORRADE_ASSERT(heap < properties.memoryHeapCount, + "Vk::DeviceProperties::memoryHeapSize(): index" << heap << "out of range for" << properties.memoryHeapCount << "memory heaps", {}); + return properties.memoryHeaps[heap].size; +} + +MemoryHeapFlags DeviceProperties::memoryHeapFlags(const UnsignedInt heap) { + const VkPhysicalDeviceMemoryProperties& properties = memoryProperties().memoryProperties; + CORRADE_ASSERT(heap < properties.memoryHeapCount, + "Vk::DeviceProperties::memoryHeapFlags(): index" << heap << "out of range for" << properties.memoryHeapCount << "memory heaps", {}); + return MemoryHeapFlag(properties.memoryHeaps[heap].flags); +} + +UnsignedInt DeviceProperties::memoryCount() { + return memoryProperties().memoryProperties.memoryTypeCount; +} + +MemoryFlags DeviceProperties::memoryFlags(const UnsignedInt memory) { + const VkPhysicalDeviceMemoryProperties& properties = memoryProperties().memoryProperties; + CORRADE_ASSERT(memory < properties.memoryTypeCount, + "Vk::DeviceProperties::memoryFlags(): index" << memory << "out of range for" << properties.memoryTypeCount << "memory types", {}); + return MemoryFlag(properties.memoryTypes[memory].propertyFlags); +} + +UnsignedInt DeviceProperties::memoryHeapIndex(const UnsignedInt memory) { + const VkPhysicalDeviceMemoryProperties& properties = memoryProperties().memoryProperties; + CORRADE_ASSERT(memory < properties.memoryTypeCount, + "Vk::DeviceProperties::memoryHeapIndex(): index" << memory << "out of range for" << properties.memoryTypeCount << "memory types", {}); + return properties.memoryTypes[memory].heapIndex; +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; @@ -297,4 +357,24 @@ Debug& operator<<(Debug& debug, const QueueFlags value) { Vk::QueueFlag::Protected}); } +Debug& operator<<(Debug& debug, const MemoryHeapFlag value) { + debug << "Vk::MemoryHeapFlag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::MemoryHeapFlag::value: return debug << "::" << Debug::nospace << #value; + _c(DeviceLocal) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Flag bits should be in hex, unlike plain values */ + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const MemoryHeapFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Vk::MemoryHeapFlags{}", { + Vk::MemoryHeapFlag::DeviceLocal}); +} + }} diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index ff0a7860a4..82e3e00788 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, @ref Magnum::Vk::QueueFlag, enum set @ref Magnum::Vk::QueueFlags, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() + * @brief Class @ref Magnum::Vk::DeviceProperties, enum @ref Magnum::Vk::DeviceType, @ref Magnum::Vk::QueueFlag, @ref Magnum::Vk::MemoryHeapFlag, enum set @ref Magnum::Vk::QueueFlags, @ref Magnum::Vk::MemoryHeapFlags, function @ref Magnum::Vk::enumerateDevices(), @ref Magnum::Vk::pickDevice(), @ref Magnum::Vk::tryPickDevice() * @m_since_latest */ @@ -128,6 +128,47 @@ CORRADE_ENUMSET_OPERATORS(QueueFlags) */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, QueueFlags value); +/** +@brief Memory heap flag +@m_since_latest + +Wraps a @type_vk_keyword{MemoryHeapFlagBits}. +@see @ref QueueFlags, @ref DeviceProperties::queueFamilyFlags() +@m_enum_values_as_keywords +*/ +enum class MemoryHeapFlag: UnsignedInt { + /** Corresponds to device-local memory */ + DeviceLocal = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, + + /** @todo MultiInstance, Vk 1.1 */ +}; + +/** +@debugoperatorclassenum{DeviceProperties,MemoryHeapFlag} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryHeapFlag value); + +/** +@brief Memory heap flags +@m_since_latest + +Type-safe wrapper for @type_vk_keyword{MemoryHeapFlags}. +@see @ref DeviceProperties::memoryHeapFlags(), @ref MemoryFlags +*/ +typedef Containers::EnumSet MemoryHeapFlags; + +CORRADE_ENUMSET_OPERATORS(MemoryHeapFlags) + +/** +@debugoperatorclassenum{DeviceProperties,MemoryHeapFlags} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryHeapFlags value); + +/* MemoryFlag/MemoryFlags are in Memory.h since those are used mainly in + contexts where DeviceProperties isn't present */ + /** @brief Physical device properties @m_since_latest @@ -311,6 +352,75 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ Containers::Optional tryPickQueueFamily(QueueFlags flags); + /** + * @brief Device memory properties + * + * Populated lazily on first request. If Vulkan 1.1 or the + * @vk_extension{KHR,get_physical_device_properties2} extension is not + * enabled on the originating instance, only the Vulkan 1.0 subset of + * device properties is queried. + * @see @fn_vk_keyword{GetPhysicalDeviceMemoryProperties2}, + * @fn_vk_keyword{GetPhysicalDeviceMemoryProperties} + */ + const VkPhysicalDeviceMemoryProperties2& memoryProperties(); + + /** + * @brief Memory heap count + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + UnsignedInt memoryHeapCount(); + + /** + * @brief Memory heap size + * @param heap Memory heap index, expected to be smaller than + * @ref memoryHeapCount() + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + UnsignedLong memoryHeapSize(UnsignedInt heap); + + /** + * @brief Memory heap size + * @param heap Memory heap index, expected to be smaller than + * @ref memoryHeapCount() + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + MemoryHeapFlags memoryHeapFlags(UnsignedInt heap); + + /** + * @brief Memory type count + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + UnsignedInt memoryCount(); + + /** + * @brief Memory type flags + * @param memory Memory type index, expected to be smaller than + * @ref memoryCount() + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + MemoryFlags memoryFlags(UnsignedInt memory); + + /** + * @brief Memory heap index + * @param memory Memory type index, expected to be smaller than + * @ref memoryCount() + * @return Memory heap index, always smaller than @ref memoryHeapCount() + * + * Convenience access to @ref memoryProperties() internals, populated + * lazily on first request. + */ + UnsignedInt memoryHeapIndex(UnsignedInt memory); + private: friend Implementation::InstanceState; @@ -331,6 +441,10 @@ class MAGNUM_VK_EXPORT DeviceProperties { MAGNUM_VK_LOCAL static void getQueueFamilyPropertiesImplementationKHR(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties); MAGNUM_VK_LOCAL static void getQueueFamilyPropertiesImplementation11(DeviceProperties& self, UnsignedInt& count, VkQueueFamilyProperties2* properties); + MAGNUM_VK_LOCAL static void getMemoryPropertiesImplementationDefault(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties); + MAGNUM_VK_LOCAL static void getMemoryPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties); + MAGNUM_VK_LOCAL static void getMemoryPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceMemoryProperties2& properties); + /* Can't be a reference because of the NoCreate constructor */ Instance* _instance; diff --git a/src/Magnum/Vk/Implementation/InstanceState.cpp b/src/Magnum/Vk/Implementation/InstanceState.cpp index 1b780ae73d..75449384c1 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.cpp +++ b/src/Magnum/Vk/Implementation/InstanceState.cpp @@ -35,12 +35,15 @@ InstanceState::InstanceState(Instance& instance, Int argc, const char** argv): a if(instance.isVersionSupported(Version::Vk11)) { getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementation11; getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementation11; + getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementation11; } else if(instance.isExtensionEnabled()) { getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementationKHR; getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationKHR; + getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationKHR; } else { getPhysicalDevicePropertiesImplementation = DeviceProperties::getPropertiesImplementationDefault; getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationDefault; + getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationDefault; } } diff --git a/src/Magnum/Vk/Implementation/InstanceState.h b/src/Magnum/Vk/Implementation/InstanceState.h index 04ff8b61d0..b6821ea7f4 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.h +++ b/src/Magnum/Vk/Implementation/InstanceState.h @@ -38,6 +38,7 @@ struct InstanceState { void(*getPhysicalDevicePropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceProperties2&); void(*getPhysicalDeviceQueueFamilyPropertiesImplementation)(DeviceProperties&, UnsignedInt&, VkQueueFamilyProperties2*); + void(*getPhysicalDeviceMemoryPropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceMemoryProperties2&); }; }}} diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp new file mode 100644 index 0000000000..f53b372adb --- /dev/null +++ b/src/Magnum/Vk/Memory.cpp @@ -0,0 +1,61 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Memory.h" + +#include +#include + +namespace Magnum { namespace Vk { + +Debug& operator<<(Debug& debug, const MemoryFlag value) { + debug << "Vk::MemoryFlag" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::MemoryFlag::value: return debug << "::" << Debug::nospace << #value; + _c(DeviceLocal) + _c(HostVisible) + _c(HostCoherent) + _c(HostCached) + _c(LazilyAllocated) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Flag bits should be in hex, unlike plain values */ + return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; +} + +Debug& operator<<(Debug& debug, const MemoryFlags value) { + return Containers::enumSetDebugOutput(debug, value, "Vk::MemoryFlags{}", { + Vk::MemoryFlag::DeviceLocal, + Vk::MemoryFlag::HostVisible, + Vk::MemoryFlag::HostCoherent, + Vk::MemoryFlag::HostCached, + Vk::MemoryFlag::LazilyAllocated}); +} + +}} diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h new file mode 100644 index 0000000000..751ab2ddf0 --- /dev/null +++ b/src/Magnum/Vk/Memory.h @@ -0,0 +1,114 @@ +#ifndef Magnum_Vk_Memory_h +#define Magnum_Vk_Memory_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Memory type flag +@m_since_latest + +Wraps a @type_vk_keyword{MemoryPropertyFlagBits}. +@see @ref MemoryFlags, @ref DeviceProperties::memoryFlags() +@m_enum_values_as_keywords +*/ +enum class MemoryFlag: UnsignedInt { + /** + * Device local. Always corresponds to a heap with + * @ref MemoryHeapFlag::DeviceLocal. + * + * @m_class{m-note m-success} + * + * @par + * This memory is the most efficient for device access. + */ + DeviceLocal = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + + /** Memory that can be mapped for host access */ + HostVisible = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + + /** Memory with coherent access on the host */ + HostCoherent = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + + /** + * Memory that is cached on the host. Host memory accesses to uncached + * memory are slower than to cached memory, however uncached memory is + * always @ref MemoryFlag::HostCoherent. + */ + HostCached = VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + + /** + * Lazily allocated memory. Allows only device access (i.e., there's no + * memory that has both this and @ref MemoryFlag::HostVisible set). + * + * @m_class{m-note m-success} + * + * @par + * The device is allowed (but not required) to allocate the memory + * as-needed and thus is useful for example for temporary framebuffer + * attachments --- certain tiled architectures might not even need to + * allocate the memory in that case. + */ + LazilyAllocated = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT, + + /** @todo Protected, VK 1.1 */ +}; + +/** +@debugoperatorclassenum{DeviceProperties,MemoryFlag} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryFlag value); + +/** +@brief Memory type flags +@m_since_latest + +@see @ref DeviceProperties::memoryFlags() +*/ +typedef Containers::EnumSet MemoryFlags; + +CORRADE_ENUMSET_OPERATORS(MemoryFlags) + +/** +@debugoperatorclassenum{DeviceProperties,MemoryFlags} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryFlags value); + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index f65e42b55a..06eee16fe3 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -35,6 +35,7 @@ corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp index 4cce9babaa..72f09eefcb 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp @@ -40,6 +40,8 @@ struct DevicePropertiesTest: TestSuite::Tester { void debugDeviceType(); void debugQueueFamilyPropertiesFlag(); void debugQueueFamilyPropertiesFlags(); + void debugMemoryHeapFlag(); + void debugMemoryHeapFlags(); }; DevicePropertiesTest::DevicePropertiesTest() { @@ -48,7 +50,9 @@ DevicePropertiesTest::DevicePropertiesTest() { &DevicePropertiesTest::debugDeviceType, &DevicePropertiesTest::debugQueueFamilyPropertiesFlag, - &DevicePropertiesTest::debugQueueFamilyPropertiesFlags}); + &DevicePropertiesTest::debugQueueFamilyPropertiesFlags, + &DevicePropertiesTest::debugMemoryHeapFlag, + &DevicePropertiesTest::debugMemoryHeapFlags}); } void DevicePropertiesTest::constructNoCreate() { @@ -84,6 +88,18 @@ void DevicePropertiesTest::debugQueueFamilyPropertiesFlags() { CORRADE_COMPARE(out.str(), "Vk::QueueFlag::Graphics|Vk::QueueFlag::Compute Vk::QueueFlags{}\n"); } +void DevicePropertiesTest::debugMemoryHeapFlag() { + std::ostringstream out; + Debug{&out} << MemoryHeapFlag::DeviceLocal << MemoryHeapFlag(0xdeadcafe); + CORRADE_COMPARE(out.str(), "Vk::MemoryHeapFlag::DeviceLocal Vk::MemoryHeapFlag(0xdeadcafe)\n"); +} + +void DevicePropertiesTest::debugMemoryHeapFlags() { + std::ostringstream out; + Debug{&out} << (MemoryHeapFlag::DeviceLocal|MemoryHeapFlag(0xf0)) << MemoryHeapFlags{}; + CORRADE_COMPARE(out.str(), "Vk::MemoryHeapFlag::DeviceLocal|Vk::MemoryHeapFlag(0xf0) Vk::MemoryHeapFlags{}\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::DevicePropertiesTest) diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 76f230d282..16aeba0e6a 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -37,6 +37,7 @@ #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/VulkanTester.h" @@ -65,6 +66,12 @@ struct DevicePropertiesVkTest: VulkanTester { void queueFamiliesPick(); void queueFamiliesPickFailed(); + void memoryHeaps(); + void memoryHeapOutOfRange(); + + void memoryTypes(); + void memoryTypeOutOfRange(); + void pickDevice(); void pickDeviceIndex(); void pickDeviceType(); @@ -102,6 +109,12 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): VulkanTester{NoCreate} { &DevicePropertiesVkTest::queueFamiliesPick, &DevicePropertiesVkTest::queueFamiliesPickFailed, + &DevicePropertiesVkTest::memoryHeaps, + &DevicePropertiesVkTest::memoryHeapOutOfRange, + + &DevicePropertiesVkTest::memoryTypes, + &DevicePropertiesVkTest::memoryTypeOutOfRange, + &DevicePropertiesVkTest::pickDevice, &DevicePropertiesVkTest::pickDeviceIndex, &DevicePropertiesVkTest::pickDeviceType}); @@ -325,6 +338,95 @@ void DevicePropertiesVkTest::queueFamiliesPickFailed() { "Vk::DeviceProperties::tryPickQueueFamily(): no Vk::QueueFlag(0xc0ffeee0) found among {} queue families\n", devices[0].queueFamilyCount())); } +void DevicePropertiesVkTest::memoryHeaps() { + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + Debug{} << "Available memory heap count:" << devices[0].memoryHeapCount(); + + CORRADE_COMPARE_AS(devices[0].memoryHeapCount(), 0, + TestSuite::Compare::Greater); + + bool atLeastOneDeviceLocal = false; + for(std::size_t i = 0; i != devices[0].memoryHeapCount(); ++i) { + CORRADE_ITERATION(i); + CORRADE_ITERATION(devices[0].memoryHeapFlags(i)); + + if(devices[0].memoryHeapFlags(i) & MemoryHeapFlag::DeviceLocal) + atLeastOneDeviceLocal = true; + + /* A heap should have at least 64 MB (more like at least 512 MB + nowadays but let's be conservative) */ + CORRADE_COMPARE_AS(devices[0].memoryHeapSize(i), std::size_t{1024*1024*64}, + TestSuite::Compare::Greater); + } + + CORRADE_VERIFY(atLeastOneDeviceLocal); +} + +void DevicePropertiesVkTest::memoryHeapOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + const UnsignedInt count = devices[0].memoryHeapCount(); + + std::ostringstream out; + Error redirectError{&out}; + devices[0].memoryHeapSize(count); + devices[0].memoryHeapFlags(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::DeviceProperties::memoryHeapSize(): index {0} out of range for {0} memory heaps\n" + "Vk::DeviceProperties::memoryHeapFlags(): index {0} out of range for {0} memory heaps\n", count)); +} + +void DevicePropertiesVkTest::memoryTypes() { + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + Debug{} << "Available memory type count:" << devices[0].memoryCount(); + + CORRADE_COMPARE_AS(devices[0].memoryCount(), 0, + TestSuite::Compare::Greater); + + bool atLeastOneDeviceLocal = false; + for(std::size_t i = 0; i != devices[0].memoryCount(); ++i) { + CORRADE_ITERATION(i); + CORRADE_ITERATION(devices[0].memoryFlags(i)); + + if(devices[0].memoryFlags(i) & MemoryFlag::DeviceLocal) + atLeastOneDeviceLocal = true; + + /* Heap index should be in range */ + CORRADE_COMPARE_AS(devices[0].memoryHeapIndex(i), devices[0].memoryHeapCount(), + TestSuite::Compare::Less); + } + + CORRADE_VERIFY(atLeastOneDeviceLocal); +} + +void DevicePropertiesVkTest::memoryTypeOutOfRange() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + const UnsignedInt count = devices[0].memoryCount(); + + std::ostringstream out; + Error redirectError{&out}; + devices[0].memoryFlags(count); + devices[0].memoryHeapIndex(count); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::DeviceProperties::memoryFlags(): index {0} out of range for {0} memory types\n" + "Vk::DeviceProperties::memoryHeapIndex(): index {0} out of range for {0} memory types\n", count)); +} + void DevicePropertiesVkTest::pickDevice() { /* Default behavior */ Containers::Optional device = tryPickDevice(instance()); diff --git a/src/Magnum/Vk/Test/MemoryTest.cpp b/src/Magnum/Vk/Test/MemoryTest.cpp new file mode 100644 index 0000000000..c7440eec0e --- /dev/null +++ b/src/Magnum/Vk/Test/MemoryTest.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include + +#include "Magnum/Vk/Memory.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct MemoryTest: TestSuite::Tester { + explicit MemoryTest(); + + void debugMemoryFlag(); + void debugMemoryFlags(); +}; + +MemoryTest::MemoryTest() { + addTests({&MemoryTest::debugMemoryFlag, + &MemoryTest::debugMemoryFlags}); +} + +void MemoryTest::debugMemoryFlag() { + std::ostringstream out; + Debug{&out} << MemoryFlag::HostCached << MemoryFlag(0xdeadcafe); + CORRADE_COMPARE(out.str(), "Vk::MemoryFlag::HostCached Vk::MemoryFlag(0xdeadcafe)\n"); +} + +void MemoryTest::debugMemoryFlags() { + std::ostringstream out; + Debug{&out} << (MemoryFlag::HostCached|MemoryFlag::LazilyAllocated) << MemoryFlags{}; + CORRADE_COMPARE(out.str(), "Vk::MemoryFlag::HostCached|Vk::MemoryFlag::LazilyAllocated Vk::MemoryFlags{}\n"); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 57138970e1..51afdd2a23 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -51,6 +51,10 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +enum class MemoryFlag: UnsignedInt; +typedef Containers::EnumSet MemoryFlags; +enum class MemoryHeapFlag: UnsignedInt; +typedef Containers::EnumSet MemoryHeapFlags; class Queue; enum class QueueFlag: UnsignedInt; typedef Containers::EnumSet QueueFlags; From 33c838bcff6ea8366a6b0a59c790895675f66319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 26 Sep 2020 15:54:39 +0200 Subject: [PATCH 40/85] vk-info: show memory heaps and types. --- src/Magnum/Vk/vk-info.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Magnum/Vk/vk-info.cpp b/src/Magnum/Vk/vk-info.cpp index 420885cf08..787db48d43 100644 --- a/src/Magnum/Vk/vk-info.cpp +++ b/src/Magnum/Vk/vk-info.cpp @@ -29,6 +29,7 @@ #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Version.h" @@ -289,4 +290,16 @@ int main(int argc, char** argv) { Debug{} << " " << i << Debug::nospace << ":" << device.queueFamilyFlags(i); Debug{} << " " << device.queueFamilySize(i) << "queues"; } + + Debug{} << "Memory heaps:"; + for(UnsignedInt i = 0; i != device.memoryHeapCount(); ++i) { + Debug{} << " " << i << Debug::nospace << ":" << device.memoryHeapFlags(i); + Debug{} << " size:" << device.memoryHeapSize(i)/1024/1024 << "MB"; + } + + Debug{} << "Memory types:"; + for(UnsignedInt i = 0; i != device.memoryCount(); ++i) { + Debug{} << " " << i << Debug::nospace << ":" << device.memoryFlags(i); + Debug{} << " heap index:" << device.memoryHeapIndex(i); + } } From 67ebe78a6e00eb3e331058bdee5e5437b6659f6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 26 Sep 2020 17:32:08 +0200 Subject: [PATCH 41/85] Vk: ability to pick a memory type. Goes in-line with APIs for device and queue selection. --- src/Magnum/Vk/DeviceProperties.cpp | 41 +++++++++++++ src/Magnum/Vk/DeviceProperties.h | 43 +++++++++++++ src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 60 +++++++++++++++++++ 3 files changed, 144 insertions(+) diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index e6e0d39811..7db752ad39 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -32,9 +32,11 @@ #include #include +#include "Magnum/Math/Functions.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/LayerProperties.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" @@ -236,6 +238,45 @@ UnsignedInt DeviceProperties::memoryHeapIndex(const UnsignedInt memory) { return properties.memoryTypes[memory].heapIndex; } +UnsignedInt DeviceProperties::pickMemory(const MemoryFlags requiredFlags, const MemoryFlags preferredFlags, const UnsignedInt memories) { + Containers::Optional id = tryPickMemory(requiredFlags, preferredFlags, memories); + if(id) return *id; + std::exit(1); /* LCOV_EXCL_LINE */ +} + +Containers::Optional DeviceProperties::tryPickMemory(const MemoryFlags requiredFlags, const MemoryFlags preferredFlags, const UnsignedInt memories) { + const VkPhysicalDeviceMemoryProperties properties = memoryProperties().memoryProperties; + + /* The picking strategy is basically equivalent to vmaFindMemoryTypeIndex() + from AMD's Vulkan Memory Allocator -- choosing the one that has the most + bits set. */ + Int maxPreferredBitCount = -1; + UnsignedInt maxPreferredBitCountMemory = ~UnsignedInt{}; + UnsignedInt bit = 1; + for(UnsignedInt i = 0; i != properties.memoryTypeCount; ++i, bit <<= 1) { + /* Not among considered memory types, skip */ + if(!(memories & bit)) + continue; + + /* Not all required flags present, skip */ + if(!(MemoryFlag(properties.memoryTypes[i].propertyFlags) >= requiredFlags)) + continue; + + /* Check how many of the preferred flags are present and use the one + with highest count */ + const Int preferredBitCount = Math::popcount(properties.memoryTypes[i].propertyFlags & UnsignedInt(preferredFlags)); + if(preferredBitCount > maxPreferredBitCount) { + maxPreferredBitCount = preferredBitCount; + maxPreferredBitCountMemory = i; + } + } + + if(maxPreferredBitCount >= 0) return maxPreferredBitCountMemory; + + Error{} << "Vk::DeviceProperties::tryPickMemory(): no" << requiredFlags << "found among" << Math::popcount(memories & ((1 << properties.memoryTypeCount) - 1)) << "considered memory types"; + return {}; +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 82e3e00788..7e82e348d2 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -421,6 +421,49 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ UnsignedInt memoryHeapIndex(UnsignedInt memory); + /** + * @brief Pick a memory type satisfying given flags + * @param requiredFlags Memory flags that should be present in + * picked memory type. Can be an empty set, but picking such + * memory is probably not very useful. + * @param preferredFlags If there's more than one memory type + * matching @p requiredFlags, prefer one that has most of these + * as well. Defaults to an empty set. + * @param memories Bits indicating which memory types should + * be considered (bit @cpp 0 @ce indicates memory type @cpp 0 @ce + * should be considered etc.). Expected to have at least one bit + * of the first @ref memoryCount() bits set, otherwise the + * function will always fail. Defaults to all bits set, meaning + * all memory types are considered. Corresponds to the + * `memoryTypeBits` field of @type_vk{MemoryRequirements}. + * + * Queries memory properties using @ref memoryProperties() and out of + * memory types set in @p memoryBits tries to find one that contains + * all @p requiredFlags and most of @p optionalFlags. If it is not + * found, exits. See @ref tryPickMemory() for an alternative that + * doesn't exit on failure. + * + * @m_class{m-note m-success} + * + * @par + * The @p preferredFlags can be used for example to ask for a + * @ref MemoryFlag::HostVisible bit on a + * @ref MemoryFlag::DeviceLocal memory --- on discrete GPUs this + * combination is usually not possible so you get just a + * device-only memory, but on integrated GPUs it can be used to + * avoid a need for a copy through a temporary staging buffer. + */ + UnsignedInt pickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{}); + + /** + * @brief Try to pick a memory type satisfying given flags + * + * Compared to @ref pickMemory() the function returns + * @ref Containers::NullOpt if a desired memory type isn't found + * instead of exiting. + */ + Containers::Optional tryPickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{}); + private: friend Implementation::InstanceState; diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 16aeba0e6a..085a09a353 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -71,6 +71,9 @@ struct DevicePropertiesVkTest: VulkanTester { void memoryTypes(); void memoryTypeOutOfRange(); + void memoryTypesPick(); + void memoryTypesPickIgnoreSomePreferred(); + void memoryTypesPickFailed(); void pickDevice(); void pickDeviceIndex(); @@ -114,6 +117,9 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): VulkanTester{NoCreate} { &DevicePropertiesVkTest::memoryTypes, &DevicePropertiesVkTest::memoryTypeOutOfRange, + &DevicePropertiesVkTest::memoryTypesPick, + &DevicePropertiesVkTest::memoryTypesPickIgnoreSomePreferred, + &DevicePropertiesVkTest::memoryTypesPickFailed, &DevicePropertiesVkTest::pickDevice, &DevicePropertiesVkTest::pickDeviceIndex, @@ -427,6 +433,60 @@ void DevicePropertiesVkTest::memoryTypeOutOfRange() { "Vk::DeviceProperties::memoryHeapIndex(): index {0} out of range for {0} memory types\n", count)); } +void DevicePropertiesVkTest::memoryTypesPick() { + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + Containers::Optional id = devices[0].tryPickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent); + CORRADE_VERIFY(id); + CORRADE_COMPARE_AS(*id, devices[0].memoryCount(), TestSuite::Compare::Less); + CORRADE_COMPARE_AS(devices[0].memoryFlags(*id), + MemoryFlag::HostVisible|MemoryFlag::HostCoherent, + TestSuite::Compare::GreaterOrEqual); + + /* Pick should return the same ID, and shouldn't exit */ + CORRADE_COMPARE(devices[0].pickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent), id); + + /* If we put the same into preferred flags and leave the required empty, it + should pick the same (the first one as well) */ + Containers::Optional idPreferred = devices[0].tryPickMemory({}, MemoryFlag::HostVisible|MemoryFlag::HostCoherent); + CORRADE_COMPARE(idPreferred, id); +} + +void DevicePropertiesVkTest::memoryTypesPickIgnoreSomePreferred() { + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + Containers::Optional id = devices[0].tryPickMemory({}, MemoryFlag::HostVisible|MemoryFlag::HostCoherent|MemoryFlag(0xcafe0000u)); + CORRADE_VERIFY(id); + CORRADE_COMPARE_AS(*id, devices[0].memoryCount(), TestSuite::Compare::Less); + /* Should pick all what makes sense and ignore what doesn't */ + CORRADE_COMPARE_AS(devices[0].memoryFlags(*id), + MemoryFlag::HostVisible|MemoryFlag::HostCoherent, + TestSuite::Compare::GreaterOrEqual); + + /* And should be the same as picking the same required or halfway */ + CORRADE_COMPARE(id, devices[0].tryPickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent)); + CORRADE_COMPARE(id, devices[0].tryPickMemory(MemoryFlag::HostVisible, MemoryFlag::HostCoherent)); +} + +void DevicePropertiesVkTest::memoryTypesPickFailed() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Containers::Array devices = enumerateDevices(instance()); + CORRADE_VERIFY(!devices.empty()); + + std::ostringstream out; + Error redirectError{&out}; + CORRADE_VERIFY(!devices[0].tryPickMemory(MemoryFlag(0xc0ffeee0))); + CORRADE_VERIFY(!devices[0].tryPickMemory({}, {}, 0)); + CORRADE_COMPARE(out.str(), Utility::formatString( + "Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlag(0xc0ffeee0) found among {} considered memory types\n" + "Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlags{{}} found among 0 considered memory types\n", devices[0].memoryCount())); +} + void DevicePropertiesVkTest::pickDevice() { /* Default behavior */ Containers::Optional device = tryPickDevice(instance()); From cc9c71f6015d3e394dc9fc3b3537a8549703d610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 9 Nov 2020 22:31:57 +0100 Subject: [PATCH 42/85] Add a NoAllocate tag. Will be used by Vulkan. --- doc/changelog.dox | 2 ++ src/Magnum/Tags.h | 23 +++++++++++++++++++++++ src/Magnum/Test/TagsTest.cpp | 2 ++ 3 files changed, 27 insertions(+) diff --git a/doc/changelog.dox b/doc/changelog.dox index 8545120ea2..a348e3b6c2 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -40,6 +40,8 @@ See also: @subsection changelog-latest-new New features +- New @ref NoAllocate constructor tag, to be used by the @ref Vk library + @subsubsection changelog-latest-new-debugtools DebugTools library - Added @ref DebugTools::ColorMap::coolWarmSmooth() and diff --git a/src/Magnum/Tags.h b/src/Magnum/Tags.h index 22e506ad3b..7138d0c9b1 100644 --- a/src/Magnum/Tags.h +++ b/src/Magnum/Tags.h @@ -53,6 +53,21 @@ Vulkan / ... object. */ typedef Corrade::Containers::NoCreateT NoCreateT; +/** +@brief No allocation tag type +@m_since_latest + +Used to distinguish construction without allocating memory. +@see @ref NoAllocate +*/ +/* Explicit constructor to avoid ambiguous calls when using {} */ +struct NoAllocateT { + #ifndef DOXYGEN_GENERATING_OUTPUT + struct Init{}; + constexpr explicit NoAllocateT(Init) {} + #endif +}; + /** @brief No initialization tag @m_since{2020,06} @@ -80,6 +95,14 @@ constexpr NoCreateT NoCreate{}; using Corrade::Containers::NoCreate; #endif +/** +@brief No allocation tag +@m_since_latest + +Use for construction without allocating memory. +*/ +constexpr NoAllocateT NoAllocate{NoAllocateT::Init{}}; + } #endif diff --git a/src/Magnum/Test/TagsTest.cpp b/src/Magnum/Test/TagsTest.cpp index c742b13ba0..9e8c6dc41d 100644 --- a/src/Magnum/Test/TagsTest.cpp +++ b/src/Magnum/Test/TagsTest.cpp @@ -45,11 +45,13 @@ TagsTest::TagsTest() { void TagsTest::noDefaultConstructor() { CORRADE_VERIFY(!std::is_default_constructible::value); CORRADE_VERIFY(!std::is_default_constructible::value); + CORRADE_VERIFY(!std::is_default_constructible::value); } void TagsTest::inlineDefinition() { CORRADE_VERIFY((std::is_same::value)); CORRADE_VERIFY((std::is_same::value)); + CORRADE_VERIFY((std::is_same::value)); } }}} From fcd0afb306e7fdf21fccb39c48c81a9208937923 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Sep 2020 20:21:04 +0200 Subject: [PATCH 43/85] Vk: add an Image wrapper. Not exactly sure about the usage, so no docs yet. Will come once memory allocation is complete. --- doc/vulkan-mapping.dox | 3 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/Image.cpp | 105 ++++++++ src/Magnum/Vk/Image.h | 418 +++++++++++++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Test/ImageTest.cpp | 204 ++++++++++++++ src/Magnum/Vk/Test/ImageVkTest.cpp | 185 +++++++++++++ src/Magnum/Vk/Vk.h | 1 + 8 files changed, 919 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/Image.cpp create mode 100644 src/Magnum/Vk/Image.h create mode 100644 src/Magnum/Vk/Test/ImageTest.cpp create mode 100644 src/Magnum/Vk/Test/ImageVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 12d5a52f45..5686fd3c42 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -134,7 +134,7 @@ Vulkan function | Matching API @fn_vk{CreateEvent}, \n @fn_vk{DestroyEvent} | | @fn_vk{CreateFence}, \n @fn_vk{DestroyFence} | | @fn_vk{CreateFramebuffer}, \n @fn_vk{DestroyFramebuffer} | | -@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | | +@fn_vk{CreateImage}, \n @fn_vk{DestroyImage} | @ref Image constructor and destructor @fn_vk{CreateImageView}, \n @fn_vk{DestroyImageView} | | @fn_vk{CreateInstance}, \n @fn_vk{DestroyInstance} | @ref Instance constructor and destructor @fn_vk{CreatePipeline}, \n @fn_vk{DestroyPipeline} | | @@ -328,6 +328,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ +@type_vk{ImageCreateInfo} | @ref ImageCreateInfo @type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo */ diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 3f8906b939..afc2038da7 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -31,6 +31,7 @@ set(MagnumVk_SRCS CommandPool.cpp Extensions.cpp Handle.cpp + Image.cpp Instance.cpp Memory.cpp Result.cpp @@ -56,6 +57,7 @@ set(MagnumVk_HEADERS Extensions.h ExtensionProperties.h Handle.h + Image.h Instance.h Integration.h LayerProperties.h diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp new file mode 100644 index 0000000000..cd9a819344 --- /dev/null +++ b/src/Magnum/Vk/Image.cpp @@ -0,0 +1,105 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Image.h" + +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { + +ImageCreateInfo::ImageCreateInfo(const VkImageType type, const ImageUsages usages, const VkFormat format, const Vector3i& size, const Int layers, const Int levels, const Int samples, const Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + _info.flags = VkImageCreateFlags(flags); + _info.imageType = type; + _info.format = format; + _info.extent = VkExtent3D(size); + _info.mipLevels = levels; + _info.arrayLayers = layers; + _info.samples = VkSampleCountFlagBits(samples); + _info.tiling = VK_IMAGE_TILING_OPTIMAL; + _info.usage = VkImageUsageFlags(usages); + /* _info.sharingMode is implicitly VK_SHARING_MODE_EXCLUSIVE; + _info.queueFamilyIndexCount and _info.pQueueFamilyIndices should be + filled only for VK_SHARING_MODE_CONCURRENT */ + /* _info.initialLayout is implicitly VK_IMAGE_LAYOUT_UNDEFINED. The only + other possible value is VK_IMAGE_LAYOUT_PREDEFINED, which however also + needs VK_IMAGE_TILING_LINEAR, one sample and possibly other restrictions + -- https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkImageCreateInfo.html#_description + + Such images need to be allocated from host-visible memory which on + discrete GPUs is not fast for device access and thus is recommended to + go through a staging buffer (not image) instead. This is however still + useful for iGPUs, as the the memory is shared and this avoid an + expensive extra copy. */ +} + +ImageCreateInfo::ImageCreateInfo(NoInitT) noexcept {} + +ImageCreateInfo::ImageCreateInfo(const VkImageCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Image Image::wrap(Device& device, const VkImage handle, const HandleFlags flags) { + Image out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateImage(device, info, nullptr, &_handle)); +} + +Image::Image(NoCreateT): _device{}, _handle{} {} + +Image::Image(Image&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Image::~Image() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyImage(*_device, _handle, nullptr); +} + +Image& Image::operator=(Image&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkImage Image::release() { + const VkImage handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h new file mode 100644 index 0000000000..cbc3148873 --- /dev/null +++ b/src/Magnum/Vk/Image.h @@ -0,0 +1,418 @@ +#ifndef Magnum_Vk_Image_h +#define Magnum_Vk_Image_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::ImageCreateInfo, @ref Magnum::Vk::ImageCreateInfo1D, @ref Magnum::Vk::ImageCreateInfo2D, @ref Magnum::Vk::ImageCreateInfo3D, @ref Magnum::Vk::ImageCreateInfo1DArray, @ref Magnum::Vk::ImageCreateInfo2DArray, @ref Magnum::Vk::ImageCreateInfoCubeMap, @ref Magnum::Vk::ImageCreateInfoCubeMapArray, @ref Magnum::Vk::Image, enum @ref Magnum::Vk::ImageUsage, enum set @ref Magnum::Vk::ImageUsages + * @m_since_latest + */ + +#include + +#include "Magnum/DimensionTraits.h" +#include "Magnum/Magnum.h" +#include "Magnum/Math/Vector3.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Image usage +@m_since_latest + +Wraps a @type_vk_keyword{ImageUsageFlagBits}. +@see @ref ImageUsages, @ref ImageCreateInfo +@m_enum_values_as_keywords +*/ +enum class ImageUsage: UnsignedInt { + /** Source of a transfer command */ + TransferSource = VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + + /** Destination of a transfer command */ + TransferDestination = VK_IMAGE_USAGE_TRANSFER_DST_BIT, + + /** Sampled by a shader */ + Sampled = VK_IMAGE_USAGE_SAMPLED_BIT, + + /** Shader storage */ + Storage = VK_IMAGE_USAGE_STORAGE_BIT, + + /** Color attachment */ + ColorAttachment = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + + /** Depth/stencil attachment */ + DepthStencilAttachment = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + + /** Transient attachment */ + TransientAttachment = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, + + /** Input attachment in a shader or framebuffer */ + InputAttachment = VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT +}; + +/** +@brief Image usages +@m_since_latest + +Type-safe wrapper for @type_vk_keyword{ImageUsageFlags}. +@see @ref ImageCreateInfo +*/ +typedef Containers::EnumSet ImageUsages; + +CORRADE_ENUMSET_OPERATORS(ImageUsages) + +/** +@brief Image creation info +@m_since_latest + +Wraps a @type_vk_keyword{ImageCreateInfo}. See @ref Image for usage +information. +@see @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, @ref ImageCreateInfo3D, + @ref ImageCreateInfo1DArray, @ref ImageCreateInfo2DArray, + @ref ImageCreateInfoCubeMap, @ref ImageCreateInfoCubeMapArray +*/ +class MAGNUM_VK_EXPORT ImageCreateInfo { + public: + /** + * @brief Image creation flag + * + * Wraps @type_vk_keyword{ImageCreateFlagBits}. + * @see @ref Flags, @ref ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** @todo sparse binding/residency/aliased */ + + /** Allow creating a view of different format */ + MutableFormat = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + + /** Allow creating a cube map view */ + CubeCompatible = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT, + + /** @todo alias, 2D array compatible ... (Vulkan 1.1+) */ + }; + + /** + * @brief Image creation flags + * + * Type-safe wrapper for @type_vk_keyword{ImageCreateFlags}. + * @see @ref ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags), + * @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, + * @ref ImageCreateInfo3D, @ref ImageCreateInfo1DArray, + * @ref ImageCreateInfo2DArray, @ref ImageCreateInfoCubeMap, + * @ref ImageCreateInfoCubeMapArray + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param type Image type + * @param usages Desired image usage. At least one flag is required. + * @param format Image format + * @param size Image size + * @param layers Array layer count + * @param levels Mip level count + * @param samples Sample count + * @param flags Image creation flags + * + * The following @type_vk{ImageCreateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `imageType` to @p type + * - `format` + * - `extent` to @p size + * - `mipLevels` to @p levels + * - `arrayLayers` to @p layers + * - `samples` + * - `tiling` to @val_vk{IMAGE_TILING_OPTIMAL,ImageTiling} + * - `usage` to @p usages + * - `sharingMode` to @val_vk{SHARING_MODE_EXCLUSIVE,SharingMode} + * - `initialLayout` to @val_vk{IMAGE_LAYOUT_UNDEFINED,ImageLayout} + * + * There are various restrictions on @p size, @p layers, @p levels for + * a particular @p type --- for common image types you're encouraged to + * make use of @ref ImageCreateInfo1D, @ref ImageCreateInfo2D, + * @ref ImageCreateInfo3D, @ref ImageCreateInfo1DArray, + * @ref ImageCreateInfo2DArray, @ref ImageCreateInfoCubeMap and + * @ref ImageCreateInfoCubeMapArray convenience classes instead of + * this constructor. + */ + explicit ImageCreateInfo(VkImageType type, ImageUsages usages, VkFormat format, const Vector3i& size, Int layers, Int levels, Int samples = 1, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit ImageCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit ImageCreateInfo(const VkImageCreateInfo& info); + + /** @brief Underlying @type_vk{ImageCreateInfo} structure */ + VkImageCreateInfo& operator*() { return _info; } + /** @overload */ + const VkImageCreateInfo& operator*() const { return _info; } + /** @overload */ + VkImageCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkImageCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkImageCreateInfo*() const { return &_info; } + + private: + VkImageCreateInfo _info; +}; + +CORRADE_ENUMSET_OPERATORS(ImageCreateInfo::Flags) + +/** +@brief Convenience constructor for 1D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_1D,ImageType} with the last two `size` components and +`layers` set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo1D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo1D(ImageUsages usages, VkFormat format, Int size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_1D, usages, format, {size, 1, 1}, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 2D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component and `layers` +set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo2D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo2D(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size, 1}, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 3D images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_3D,ImageType} with `layers` set to @cpp 1 @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo3D: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo3D(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_3D, usages, format, size, 1, levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 1D array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_1D,ImageType} with the last two `size` components set to +@cpp 1 @ce and `layers` set to @cpp size.y() @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo1DArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo1DArray(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_1D, usages, format, {size.x(), 1, 1}, size.y(), levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for 2D array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce and `layers` set to @cpp size.z() @ce. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfo2DArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfo2DArray(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size.xy(), 1}, size.z(), levels, samples, flags} {} +}; + +/** +@brief Convenience constructor for cube map images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce, `layers` set to @cpp 6 @ce and `flags` additionally having +@ref Flag::CubeCompatible. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfoCubeMap: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfoCubeMap(ImageUsages usages, VkFormat format, const Vector2i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size, 1}, 6, levels, samples, flags|Flag::CubeCompatible} {} +}; + +/** +@brief Convenience constructor for cube map array images +@m_since_latest + +Compared to the base @ref ImageCreateInfo constructor creates an image of type +@val_vk{IMAGE_TYPE_2D,ImageType} with the last `size` component set to +@cpp 1 @ce, `layers` set to @cpp size.y() @ce and `flags` additionally having +@ref Flag::CubeCompatible. + +Note that same as with the +@ref ImageCreateInfo::ImageCreateInfo(VkImageType, ImageUsages, VkFormat, const Vector3i&, Int, Int, Int, Flags) +constructor, at least one @ref ImageUsage value is required. +*/ +class ImageCreateInfoCubeMapArray: public ImageCreateInfo { + public: + /** @brief Constructor */ + explicit ImageCreateInfoCubeMapArray(ImageUsages usages, VkFormat format, const Vector3i& size, Int levels, Int samples = 1, Flags flags = {}): ImageCreateInfo{VK_IMAGE_TYPE_2D, usages, format, {size.xy(), 1}, size.z(), levels, samples, flags|Flag::CubeCompatible} {} +}; + +/** +@brief Image +@m_since_latest + +Wraps a @type_vk_keyword{Image}. +*/ +class MAGNUM_VK_EXPORT Image { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the image is created on + * @param handle The @type_vk{Image} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * an image created using a constructor, the Vulkan image is by default + * not deleted on destruction, use @p flags for different behavior. + * @see @ref release() + */ + static Image wrap(Device& device, VkImage handle, HandleFlags flags = {}); + + /** + * @brief Construct an image without allocating + * @param device Vulkan device to create the image on + * @param info Image creation info + * + * @see @fn_vk_keyword{CreateImage} + */ + explicit Image(Device& device, const ImageCreateInfo& info, NoAllocateT); + + /** + * @brief Construct without creating the image + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Image(NoCreateT); + + /** @brief Copying is not allowed */ + Image(const Image&) = delete; + + /** @brief Move constructor */ + Image(Image&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Image} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{DestroyImage}, @ref release() + */ + ~Image(); + + /** @brief Copying is not allowed */ + Image& operator=(const Image&) = delete; + + /** @brief Move assignment */ + Image& operator=(Image&& other) noexcept; + + /** @brief Underlying @type_vk{Image} handle */ + VkImage handle() { return _handle; } + /** @overload */ + operator VkImage() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan image + * + * Releases ownership of the Vulkan image and returns its handle so + * @fn_vk{DestroyImage} is not called on destruction. The internal + * state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkImage release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkImage _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 06eee16fe3..40d6f89c2d 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -32,6 +32,7 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) @@ -46,6 +47,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkDevicePropertiesVkTest DevicePropertiesVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) corrade_add_test(VkExtensionPropertiesVkTest ExtensionPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/ImageTest.cpp b/src/Magnum/Vk/Test/ImageTest.cpp new file mode 100644 index 0000000000..ec69b2b86e --- /dev/null +++ b/src/Magnum/Vk/Test/ImageTest.cpp @@ -0,0 +1,204 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Image.h" +#include "Magnum/Vk/Integration.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ImageTest: TestSuite::Tester { + explicit ImageTest(); + + void createInfoConstruct(); + void createInfoConstruct1D(); + void createInfoConstruct2D(); + void createInfoConstruct3D(); + void createInfoConstruct1DArray(); + void createInfoConstruct2DArray(); + void createInfoConstructCubeMap(); + void createInfoConstructCubeMapArray(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +ImageTest::ImageTest() { + addTests({&ImageTest::createInfoConstruct, + &ImageTest::createInfoConstruct1D, + &ImageTest::createInfoConstruct2D, + &ImageTest::createInfoConstruct3D, + &ImageTest::createInfoConstruct1DArray, + &ImageTest::createInfoConstruct2DArray, + &ImageTest::createInfoConstructCubeMap, + &ImageTest::createInfoConstructCubeMapArray, + &ImageTest::createInfoConstructNoInit, + &ImageTest::createInfoConstructFromVk, + + &ImageTest::constructNoCreate, + &ImageTest::constructCopy}); +} + +void ImageTest::createInfoConstruct() { + ImageCreateInfo info{VK_IMAGE_TYPE_2D, ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_UNORM, {256, 128, 1}, 6, 8, 16, ImageCreateInfo::Flag::CubeCompatible}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 128, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 6); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_SAMPLED_BIT); + CORRADE_COMPARE(info->tiling, VK_IMAGE_TILING_OPTIMAL); + CORRADE_COMPARE(info->sharingMode, VK_SHARING_MODE_EXCLUSIVE); + CORRADE_COMPARE(info->initialLayout, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void ImageTest::createInfoConstruct1D() { + ImageCreateInfo1D info{ImageUsage::Storage, VK_FORMAT_R8G8B8A8_UNORM, 256, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_1D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 1, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_STORAGE_BIT); +} + +void ImageTest::createInfoConstruct2D() { + ImageCreateInfo2D info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstruct3D() { + ImageCreateInfo3D info{ImageUsage::InputAttachment, VK_FORMAT_R8G8B8A8_UNORM, {256, 64, 32}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_3D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 32})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 1); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); +} + +void ImageTest::createInfoConstruct1DArray() { + ImageCreateInfo1DArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_1D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 1, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 64); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstruct2DArray() { + ImageCreateInfo2DArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 64, 32}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 64, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 32); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructCubeMap() { + ImageCreateInfoCubeMap info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT|VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 256, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 6); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructCubeMapArray() { + ImageCreateInfoCubeMapArray info{ImageUsage::TransferDestination, VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 36}, 8, 16, ImageCreateInfo::Flag::MutableFormat}; + CORRADE_COMPARE(info->flags, VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT|VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT); + CORRADE_COMPARE(info->imageType, VK_IMAGE_TYPE_2D); + CORRADE_COMPARE(info->format, VK_FORMAT_R8G8B8A8_UNORM); + CORRADE_COMPARE(Vector3i(info->extent), (Vector3i{256, 256, 1})); + CORRADE_COMPARE(info->mipLevels, 8); + CORRADE_COMPARE(info->arrayLayers, 36); + CORRADE_COMPARE(info->samples, VK_SAMPLE_COUNT_16_BIT); + CORRADE_COMPARE(info->usage, VK_IMAGE_USAGE_TRANSFER_DST_BIT); +} + +void ImageTest::createInfoConstructNoInit() { + ImageCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) ImageCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ImageTest::createInfoConstructFromVk() { + VkImageCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + ImageCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ImageTest::constructNoCreate() { + { + Image image{NoCreate}; + CORRADE_VERIFY(!image.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ImageTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageTest) diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp new file mode 100644 index 0000000000..30b2f3e957 --- /dev/null +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -0,0 +1,185 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/Image.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ImageVkTest: VulkanTester { + explicit ImageVkTest(); + + void construct1D(); + void construct2D(); + void construct3D(); + void construct1DArray(); + void construct2DArray(); + void constructCubeMap(); + void constructCubeMapArray(); + void constructMove(); + + void wrap(); +}; + +ImageVkTest::ImageVkTest() { + addTests({&ImageVkTest::construct1D, + &ImageVkTest::construct2D, + &ImageVkTest::construct3D, + &ImageVkTest::construct1DArray, + &ImageVkTest::construct2DArray, + &ImageVkTest::constructCubeMap, + &ImageVkTest::constructCubeMapArray, + &ImageVkTest::constructMove, + + &ImageVkTest::wrap}); +} + +void ImageVkTest::construct1D() { + { + Image image{device(), ImageCreateInfo1D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, 256, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct2D() { + { + Image image{device(), ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct3D() { + { + Image image{device(), ImageCreateInfo3D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct1DArray() { + { + Image image{device(), ImageCreateInfo1DArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::construct2DArray() { + { + Image image{device(), ImageCreateInfo2DArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 64}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructCubeMap() { + { + Image image{device(), ImageCreateInfoCubeMap{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructCubeMapArray() { + { + Image image{device(), ImageCreateInfoCubeMapArray{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256, 36}, 8}, NoAllocate}; + CORRADE_VERIFY(image.handle()); + CORRADE_COMPARE(image.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ImageVkTest::constructMove() { + Image a{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, NoAllocate}; + VkImage handle = a.handle(); + + Image b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Image c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void ImageVkTest::wrap() { + VkImage image{}; + CORRADE_COMPARE(Result(device()->CreateImage(device(), + ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, + nullptr, &image)), Result::Success); + CORRADE_VERIFY(image); + + auto wrapped = Image::wrap(device(), image, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), image); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), image); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyImage(device(), image, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 51afdd2a23..b3bfe970cd 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -46,6 +46,7 @@ class Extension; class ExtensionProperties; enum class HandleFlag: UnsignedByte; typedef Containers::EnumSet HandleFlags; +class Image; class Instance; class InstanceCreateInfo; class InstanceExtension; From 19c9243d3b388838c937e71ed6dcb49a66a54394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Thu, 12 Nov 2020 16:58:45 +0100 Subject: [PATCH 44/85] Vk: querying image memory requirements. --- doc/vulkan-mapping.dox | 3 +- src/Magnum/Vk/DeviceProperties.h | 4 +- src/Magnum/Vk/Image.cpp | 23 +++++++ src/Magnum/Vk/Image.h | 16 +++++ src/Magnum/Vk/Implementation/DeviceState.cpp | 10 +++ src/Magnum/Vk/Implementation/DeviceState.h | 2 + src/Magnum/Vk/Memory.cpp | 11 ++++ src/Magnum/Vk/Memory.h | 67 +++++++++++++++++++- src/Magnum/Vk/Test/ImageVkTest.cpp | 19 +++++- src/Magnum/Vk/Test/MemoryTest.cpp | 28 +++++++- src/Magnum/Vk/Vk.h | 1 + 11 files changed, 177 insertions(+), 7 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 5686fd3c42..aa91d1dde1 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -197,7 +197,7 @@ Vulkan function | Matching API @fn_vk{GetDeviceQueue}, \n @fn_vk{GetDeviceQueue2} @m_class{m-label m-flat m-success} **1.1** | @ref Device constructor @fn_vk{GetEventStatus} | | @fn_vk{GetFenceStatus} | | -@fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetImageMemoryRequirements}, \n @fn_vk{GetImageMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Image::memoryRequirements() @fn_vk{GetImageSparseMemoryRequirements}, \n @fn_vk{GetImageSparseMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetImageSubresourceLayout} | | @fn_vk{GetInstanceProcAddr} | @ref Instance constructor @@ -329,6 +329,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{ImageCreateInfo} | @ref ImageCreateInfo +@type_vk{ImageMemoryRequirementsInfo} | not exposed, internal to @ref Image::memoryRequirements() @type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo */ diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 7e82e348d2..a690d67dda 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -434,8 +434,8 @@ class MAGNUM_VK_EXPORT DeviceProperties { * should be considered etc.). Expected to have at least one bit * of the first @ref memoryCount() bits set, otherwise the * function will always fail. Defaults to all bits set, meaning - * all memory types are considered. Corresponds to the - * `memoryTypeBits` field of @type_vk{MemoryRequirements}. + * all memory types are considered. Corresponds to + * @ref MemoryRequirements::memories(). * * Queries memory properties using @ref memoryProperties() and out of * memory types set in @p memoryBits tries to find one that contains diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index cd9a819344..0ffc09e993 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -28,7 +28,9 @@ #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Implementation/DeviceState.h" namespace Magnum { namespace Vk { @@ -96,10 +98,31 @@ Image& Image::operator=(Image&& other) noexcept { return *this; } +MemoryRequirements Image::memoryRequirements() const { + MemoryRequirements requirements; + VkImageMemoryRequirementsInfo2 info{}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2; + info.image = _handle; + _device->state().getImageMemoryRequirementsImplementation(*_device, info, requirements); + return requirements; +} + VkImage Image::release() { const VkImage handle = _handle; _handle = {}; return handle; } +void Image::getMemoryRequirementsImplementationDefault(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetImageMemoryRequirements(device, info.image, &requirements.memoryRequirements); +} + +void Image::getMemoryRequirementsImplementationKHR(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetImageMemoryRequirements2KHR(device, &info, &requirements); +} + +void Image::getMemoryRequirementsImplementation11(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetImageMemoryRequirements2(device, &info, &requirements); +} + }} diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index cbc3148873..776cc8f00f 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -41,6 +41,8 @@ namespace Magnum { namespace Vk { +namespace Implementation { struct DeviceState; } + /** @brief Image usage @m_since_latest @@ -395,6 +397,14 @@ class MAGNUM_VK_EXPORT Image { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Image memory requirements + * + * @see @fn_vk_keyword{GetImageMemoryRequirements2}, + * @fn_vk_keyword{GetImageMemoryRequirements} + */ + MemoryRequirements memoryRequirements() const; + /** * @brief Release the underlying Vulkan image * @@ -406,6 +416,12 @@ class MAGNUM_VK_EXPORT Image { VkImage release(); private: + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationDefault(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationKHR(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementation11(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 79d481606b..b6fd25e04b 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -26,6 +26,8 @@ #include "DeviceState.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Image.h" #include "Magnum/Vk/Version.h" namespace Magnum { namespace Vk { namespace Implementation { @@ -36,6 +38,14 @@ DeviceState::DeviceState(Device& device) { } else { getDeviceQueueImplementation = &Device::getQueueImplementationDefault; } + + if(device.isVersionSupported(Version::Vk11)) { + getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementation11; + } else if(device.isExtensionEnabled()) { + getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementationKHR; + } else { + getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementationDefault; + } } }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index e350756261..446f1f0ef3 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -34,6 +34,8 @@ struct DeviceState { explicit DeviceState(Device& instance); void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); + /** @todo put this eventually into a dedicated image state struct? */ + void(*getImageMemoryRequirementsImplementation)(Device&, const VkImageMemoryRequirementsInfo2&, VkMemoryRequirements2&); }; }}} diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index f53b372adb..44b1a1be62 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -30,6 +30,17 @@ namespace Magnum { namespace Vk { +MemoryRequirements::MemoryRequirements(): _requirements{} { + _requirements.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2; +} + +MemoryRequirements::MemoryRequirements(NoInitT) noexcept {} + +MemoryRequirements::MemoryRequirements(const VkMemoryRequirements2& requirements): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _requirements(requirements) {} + Debug& operator<<(Debug& debug, const MemoryFlag value) { debug << "Vk::MemoryFlag" << Debug::nospace; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 751ab2ddf0..12f56ef336 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -26,12 +26,14 @@ */ /** @file - * @brief Enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags + * @brief Class @ref Magnum::Vk::MemoryRequirements, enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags */ #include #include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" #include "Magnum/Vk/Vulkan.h" #include "Magnum/Vk/visibility.h" @@ -109,6 +111,69 @@ CORRADE_ENUMSET_OPERATORS(MemoryFlags) */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryFlags value); +/** +@brief Device memory requirements +@m_since_latest + +Wraps a @type_vk_keyword{MemoryRequirements2}. Not constructible directly, +returned from @ref Image::memoryRequirements(). +@see @ref DeviceProperties::pickMemory() +*/ +class MAGNUM_VK_EXPORT MemoryRequirements { + public: + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit MemoryRequirements(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit MemoryRequirements(const VkMemoryRequirements2& requirements); + + /** @brief Underlying @type_vk{MemoryRequirements} structure */ + VkMemoryRequirements2& requirements() { return _requirements; } + /** @overload */ + const VkMemoryRequirements2& requirements() const { return _requirements; } + /** @overload */ + operator VkMemoryRequirements2&() { return _requirements; } + /** @overload */ + operator const VkMemoryRequirements2&() const { return _requirements; } + /** @overload */ + VkMemoryRequirements2* operator->() { return &_requirements; } + /** @overload */ + const VkMemoryRequirements2* operator->() const { return &_requirements; } + + /** @brief Required memory size */ + UnsignedLong size() const { + return _requirements.memoryRequirements.size; + } + + /** @brief Required memory alignment */ + UnsignedLong alignment() const { + return _requirements.memoryRequirements.alignment; + } + + /** @brief Bits indicating which memory */ + UnsignedInt memories() const { + return _requirements.memoryRequirements.memoryTypeBits; + } + + private: + friend Image; + + explicit MemoryRequirements(); + + VkMemoryRequirements2 _requirements; +}; + }} #endif diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index 30b2f3e957..01043d6350 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -23,8 +23,9 @@ DEALINGS IN THE SOFTWARE. */ -#include "Magnum/Vk/Image.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Image.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/VulkanTester.h" @@ -43,6 +44,8 @@ struct ImageVkTest: VulkanTester { void constructMove(); void wrap(); + + void memoryRequirements(); }; ImageVkTest::ImageVkTest() { @@ -55,7 +58,9 @@ ImageVkTest::ImageVkTest() { &ImageVkTest::constructCubeMapArray, &ImageVkTest::constructMove, - &ImageVkTest::wrap}); + &ImageVkTest::wrap, + + &ImageVkTest::memoryRequirements}); } void ImageVkTest::construct1D() { @@ -180,6 +185,16 @@ void ImageVkTest::wrap() { device()->DestroyImage(device(), image, nullptr); } +void ImageVkTest::memoryRequirements() { + /* Use linear tiling for a deterministic memory size */ + ImageCreateInfo2D info{ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_UNORM, {128, 64}, 1}; + info->tiling = VK_IMAGE_TILING_LINEAR; + Image image{device(), info, NoAllocate}; + + MemoryRequirements requirements = image.memoryRequirements(); + CORRADE_COMPARE(requirements.size(), 128*64*4); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) diff --git a/src/Magnum/Vk/Test/MemoryTest.cpp b/src/Magnum/Vk/Test/MemoryTest.cpp index c7440eec0e..d19e1052be 100644 --- a/src/Magnum/Vk/Test/MemoryTest.cpp +++ b/src/Magnum/Vk/Test/MemoryTest.cpp @@ -34,15 +34,41 @@ namespace Magnum { namespace Vk { namespace Test { namespace { struct MemoryTest: TestSuite::Tester { explicit MemoryTest(); + void requirementsConstructNoInit(); + void requirementsConstructFromVk(); + void debugMemoryFlag(); void debugMemoryFlags(); }; MemoryTest::MemoryTest() { - addTests({&MemoryTest::debugMemoryFlag, + addTests({&MemoryTest::requirementsConstructNoInit, + &MemoryTest::requirementsConstructFromVk, + + &MemoryTest::debugMemoryFlag, &MemoryTest::debugMemoryFlags}); } +void MemoryTest::requirementsConstructNoInit() { + MemoryRequirements requirements{NoInit}; + requirements->sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + new(&requirements) MemoryRequirements{NoInit}; + CORRADE_COMPARE(requirements->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void MemoryTest::requirementsConstructFromVk() { + VkMemoryRequirements2 vkRequirements; + vkRequirements.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + + MemoryRequirements requirements{vkRequirements}; + CORRADE_COMPARE(requirements->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); +} + void MemoryTest::debugMemoryFlag() { std::ostringstream out; Debug{&out} << MemoryFlag::HostCached << MemoryFlag(0xdeadcafe); diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index b3bfe970cd..f942fc4530 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -52,6 +52,7 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +class MemoryRequirements; enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags; enum class MemoryHeapFlag: UnsignedInt; From 14dbc43fcee190701402b4caaa8a01b681e024ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 18:20:09 +0100 Subject: [PATCH 45/85] Vk: implicitly enable VK_KHR_get_memory_requirements2 on Vulkan 1.0. --- package/archlinux/PKGBUILD | 2 +- package/archlinux/PKGBUILD-coverage | 2 +- package/archlinux/PKGBUILD-release | 2 +- package/ci/unix-desktop-vulkan.sh | 2 +- src/Magnum/Vk/Device.cpp | 4 +++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index ae52781b56..7fb3e27a45 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -76,7 +76,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest done } diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 1d110e6845..9380673032 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -77,7 +77,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true done ./Debug/bin/magnum-al-info > /dev/null diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index a055f111b1..84f9b6cd35 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -114,7 +114,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest done done } diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index 97578fe434..80b7accdc0 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -76,7 +76,7 @@ export CORRADE_TEST_COLOR=ON ctest -V MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest -MAGNUM_DISABLE_EXTENSIONS=VK_KHR_get_physical_device_properties2 MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest +MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 4bcde0da41..793457b23c 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -111,7 +111,9 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext extensionProperties = &*extensionPropertiesStorage; } - /* No extensions at the moment */ + /* Only if we don't have Vulkan 1.1, on which this is core */ + if(_state->version < Version::Vk11 && extensionProperties->isSupported()) + addEnabledExtensions(); } } From a16c7d6e7dec75101db84a93b3eb7b9ec5302cb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 28 Sep 2020 22:16:20 +0200 Subject: [PATCH 46/85] Vk: initial memory allocation. --- doc/vulkan-mapping.dox | 10 +- src/Magnum/Vk/Memory.cpp | 54 ++++++++++ src/Magnum/Vk/Memory.h | 149 +++++++++++++++++++++++++++- src/Magnum/Vk/Test/CMakeLists.txt | 1 + src/Magnum/Vk/Test/MemoryTest.cpp | 55 ++++++++++ src/Magnum/Vk/Test/MemoryVkTest.cpp | 94 ++++++++++++++++++ src/Magnum/Vk/Vk.h | 1 + 7 files changed, 362 insertions(+), 2 deletions(-) create mode 100644 src/Magnum/Vk/Test/MemoryVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index aa91d1dde1..439cb6ccb9 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -53,7 +53,7 @@ Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{AllocateCommandBuffers}, \n @fn_vk{FreeCommandBuffers} | @ref CommandPool::allocate(), @ref CommandBuffer destructor @fn_vk{AllocateDescriptorSets}, \n @fn_vk{FreeDescriptorSets} | | -@fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | | +@fn_vk{AllocateMemory}, \n @fn_vk{FreeMemory} | @ref Memory constructor and destructor @subsection vulkan-mapping-functions-b B @@ -332,6 +332,14 @@ Vulkan structure | Matching API @type_vk{ImageMemoryRequirementsInfo} | not exposed, internal to @ref Image::memoryRequirements() @type_vk{InstanceCreateInfo} | @ref InstanceCreateInfo +@subsection vulkan-mapping-structures-m M + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{MemoryAllocateInfo} | @ref MemoryAllocateInfo + */ }} diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 44b1a1be62..0eb299b4da 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -28,6 +28,10 @@ #include #include +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" + namespace Magnum { namespace Vk { MemoryRequirements::MemoryRequirements(): _requirements{} { @@ -41,6 +45,56 @@ MemoryRequirements::MemoryRequirements(const VkMemoryRequirements2& requirements member instead of doing a copy */ _requirements(requirements) {} +MemoryAllocateInfo::MemoryAllocateInfo(UnsignedLong size, UnsignedInt memory): _info{} { + _info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + _info.allocationSize = size; + _info.memoryTypeIndex = memory; +} + +MemoryAllocateInfo::MemoryAllocateInfo(NoInitT) noexcept {} + +MemoryAllocateInfo::MemoryAllocateInfo(const VkMemoryAllocateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Memory Memory::wrap(Device& device, const VkDeviceMemory handle, const HandleFlags flags) { + Memory out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Memory::Memory(Device& device, const MemoryAllocateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->AllocateMemory(device, info, nullptr, &_handle)); +} + +Memory::Memory(NoCreateT): _device{}, _handle{} {} + +Memory::Memory(Memory&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Memory::~Memory() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).FreeMemory(*_device, _handle, nullptr); +} + +Memory& Memory::operator=(Memory&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkDeviceMemory Memory::release() { + const VkDeviceMemory handle = _handle; + _handle = {}; + return handle; +} + Debug& operator<<(Debug& debug, const MemoryFlag value) { debug << "Vk::MemoryFlag" << Debug::nospace; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 12f56ef336..6092be9e4d 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Class @ref Magnum::Vk::MemoryRequirements, enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags + * @brief Class @ref Magnum::Vk::MemoryRequirements, @ref Magnum::Vk::MemoryAllocateInfo, @ref Magnum::Vk::Memory, enum @ref Magnum::Vk::MemoryFlag, enum set @ref Magnum::Vk::MemoryFlags */ #include @@ -174,6 +174,153 @@ class MAGNUM_VK_EXPORT MemoryRequirements { VkMemoryRequirements2 _requirements; }; +/** +@brief Memory allocation info +@m_since_latest + +Wraps a @type_vk_keyword{MemoryAllocateInfo}. See @ref Memory for usage +information. +*/ +class MAGNUM_VK_EXPORT MemoryAllocateInfo { + public: + /** @todo Flags, in VkMemoryAllocateFlagsInfo (1.1) */ + + /** + * @brief Constructor + * @param size Allocation size in bytes + * @param memory Memory index, smaller than + * @ref DeviceProperties::memoryCount() + * + * The following @type_vk{MemoryAllocateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `allocationSize` to @p size + * - `memoryTypeIndex` to @p memory + * + * @see @ref DeviceProperties::pickMemory() + */ + explicit MemoryAllocateInfo(UnsignedLong size, UnsignedInt memory); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit MemoryAllocateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit MemoryAllocateInfo(const VkMemoryAllocateInfo& info); + + /** @brief Underlying @type_vk{MemoryAllocateInfo} structure */ + VkMemoryAllocateInfo& operator*() { return _info; } + /** @overload */ + const VkMemoryAllocateInfo& operator*() const { return _info; } + /** @overload */ + VkMemoryAllocateInfo* operator->() { return &_info; } + /** @overload */ + const VkMemoryAllocateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkMemoryAllocateInfo*() const { return &_info; } + + private: + VkMemoryAllocateInfo _info; +}; + +/** +@brief Device memory +@m_since_latest + +Wraps a @type_vk_keyword{DeviceMemory}. +*/ +class MAGNUM_VK_EXPORT Memory { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the memory is allocated on + * @param handle The @type_vk{DeviceMemory} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a memory allocated using a constructor, the Vulkan memory is by + * default not freed on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Memory wrap(Device& device, VkDeviceMemory handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to allocate the memory on + * @param info Memory allocation info + * + * @see @fn_vk_keyword{AllocateMemory} + */ + explicit Memory(Device& device, const MemoryAllocateInfo& info); + + /** + * @brief Construct without allocating the memory + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Memory(NoCreateT); + + /** @brief Copying is not allowed */ + Memory(const Memory&) = delete; + + /** @brief Move constructor */ + Memory(Memory&& other) noexcept; + + /** + * @brief Destructor + * + * Frees associated @type_vk{DeviceMemory} handle, unless the instance + * was created using @ref wrap() without @ref HandleFlag::DestroyOnDestruction + * specified. + * @see @fn_vk_keyword{FreeMemory}, @ref release() + */ + ~Memory(); + + /** @brief Copying is not allowed */ + Memory& operator=(const Memory&) = delete; + + /** @brief Move assignment */ + Memory& operator=(Memory&& other) noexcept; + + /** @brief Underlying @type_vk{DeviceMemory} handle */ + VkDeviceMemory handle() { return _handle; } + /** @overload */ + operator VkDeviceMemory() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan memory + * + * Releases ownership of the Vulkan memory and returns its handle so + * @fn_vk{FreeMemory} is not called on destruction. The internal state + * is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkDeviceMemory release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkDeviceMemory _handle; + HandleFlags _flags; +}; + }} #endif diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 40d6f89c2d..9f52fd331f 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -49,5 +49,6 @@ if(BUILD_VK_TESTS) corrade_add_test(VkLayerPropertiesVkTest LayerPropertiesVkTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) + corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/MemoryTest.cpp b/src/Magnum/Vk/Test/MemoryTest.cpp index d19e1052be..010df0b1e8 100644 --- a/src/Magnum/Vk/Test/MemoryTest.cpp +++ b/src/Magnum/Vk/Test/MemoryTest.cpp @@ -37,6 +37,13 @@ struct MemoryTest: TestSuite::Tester { void requirementsConstructNoInit(); void requirementsConstructFromVk(); + void allocateInfoConstruct(); + void allocateInfoConstructNoInit(); + void allocateInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); + void debugMemoryFlag(); void debugMemoryFlags(); }; @@ -45,6 +52,13 @@ MemoryTest::MemoryTest() { addTests({&MemoryTest::requirementsConstructNoInit, &MemoryTest::requirementsConstructFromVk, + &MemoryTest::allocateInfoConstruct, + &MemoryTest::allocateInfoConstructNoInit, + &MemoryTest::allocateInfoConstructFromVk, + + &MemoryTest::constructNoCreate, + &MemoryTest::constructCopy, + &MemoryTest::debugMemoryFlag, &MemoryTest::debugMemoryFlags}); } @@ -69,6 +83,47 @@ void MemoryTest::requirementsConstructFromVk() { CORRADE_COMPARE(requirements->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); } +void MemoryTest::allocateInfoConstruct() { + MemoryAllocateInfo info{65536, 1}; + CORRADE_COMPARE(info->allocationSize, 65536); + CORRADE_COMPARE(info->memoryTypeIndex, 1); +} + +void MemoryTest::allocateInfoConstructNoInit() { + MemoryAllocateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) MemoryAllocateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void MemoryTest::allocateInfoConstructFromVk() { + VkMemoryAllocateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + MemoryAllocateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void MemoryTest::constructNoCreate() { + { + Memory memory{NoCreate}; + CORRADE_VERIFY(!memory.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void MemoryTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + void MemoryTest::debugMemoryFlag() { std::ostringstream out; Debug{&out} << MemoryFlag::HostCached << MemoryFlag(0xdeadcafe); diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp new file mode 100644 index 0000000000..16ac06f123 --- /dev/null +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -0,0 +1,94 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Memory.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct MemoryVkTest: VulkanTester { + explicit MemoryVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +MemoryVkTest::MemoryVkTest() { + addTests({&MemoryVkTest::construct, + &MemoryVkTest::constructMove, + + &MemoryVkTest::wrap}); +} + +void MemoryVkTest::construct() { + Memory memory{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + CORRADE_VERIFY(memory.handle()); + CORRADE_COMPARE(memory.handleFlags(), HandleFlag::DestroyOnDestruction); +} + +void MemoryVkTest::constructMove() { + Memory a{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + VkDeviceMemory handle = a.handle(); + + Memory b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Memory c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void MemoryVkTest::wrap() { + VkDeviceMemory memory{}; + CORRADE_COMPARE(Result(device()->AllocateMemory(device(), + MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}, + nullptr, &memory)), Result::Success); + CORRADE_VERIFY(memory); + + auto wrapped = Memory::wrap(device(), memory, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), memory); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), memory); + CORRADE_VERIFY(!wrapped.handle()); + device()->FreeMemory(device(), memory, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index f942fc4530..025ac28fc9 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -52,6 +52,7 @@ class InstanceCreateInfo; class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; +class Memory; class MemoryRequirements; enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags; From fc081256611e18a763e71dbc2961fd307f6119c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 10 Nov 2020 15:07:26 +0100 Subject: [PATCH 47/85] Vk: convenience overload for DeviceProperties::pickMemory(). --- src/Magnum/Vk/DeviceProperties.cpp | 8 ++++++++ src/Magnum/Vk/DeviceProperties.h | 16 ++++++++++++++++ src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 6 +++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 7db752ad39..0417e993a3 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -244,6 +244,10 @@ UnsignedInt DeviceProperties::pickMemory(const MemoryFlags requiredFlags, const std::exit(1); /* LCOV_EXCL_LINE */ } +UnsignedInt DeviceProperties::pickMemory(const MemoryFlags requiredFlags, const UnsignedInt memories) { + return pickMemory(requiredFlags, {}, memories); +} + Containers::Optional DeviceProperties::tryPickMemory(const MemoryFlags requiredFlags, const MemoryFlags preferredFlags, const UnsignedInt memories) { const VkPhysicalDeviceMemoryProperties properties = memoryProperties().memoryProperties; @@ -277,6 +281,10 @@ Containers::Optional DeviceProperties::tryPickMemory(const MemoryFl return {}; } +Containers::Optional DeviceProperties::tryPickMemory(const MemoryFlags requiredFlags, const UnsignedInt memories) { + return tryPickMemory(requiredFlags, {}, memories); +} + Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index a690d67dda..29b5ba8691 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -455,6 +455,14 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ UnsignedInt pickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{}); + /** + * @overload + * + * Equivalent to calling @ref pickMemory(MemoryFlags, MemoryFlags, UnsignedInt) + * with empty @p preferredFlags. + */ + UnsignedInt pickMemory(MemoryFlags requiredFlags, UnsignedInt memories); + /** * @brief Try to pick a memory type satisfying given flags * @@ -464,6 +472,14 @@ class MAGNUM_VK_EXPORT DeviceProperties { */ Containers::Optional tryPickMemory(MemoryFlags requiredFlags, MemoryFlags preferredFlags = {}, UnsignedInt memories = ~UnsignedInt{}); + /** + * @overload + * + * Equivalent to calling @ref tryPickMemory(MemoryFlags, MemoryFlags, UnsignedInt) + * with empty @p preferredFlags. + */ + Containers::Optional tryPickMemory(MemoryFlags requiredFlags, UnsignedInt memories); + private: friend Implementation::InstanceState; diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 085a09a353..624f5a4334 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -444,8 +444,10 @@ void DevicePropertiesVkTest::memoryTypesPick() { MemoryFlag::HostVisible|MemoryFlag::HostCoherent, TestSuite::Compare::GreaterOrEqual); - /* Pick should return the same ID, and shouldn't exit */ + /* Pick should return the same ID, and shouldn't exit. Test also the + overload with no preferred flags. */ CORRADE_COMPARE(devices[0].pickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent), id); + CORRADE_COMPARE(devices[0].pickMemory(MemoryFlag::HostVisible|MemoryFlag::HostCoherent, ~UnsignedInt{}), id); /* If we put the same into preferred flags and leave the required empty, it should pick the same (the first one as well) */ @@ -482,8 +484,10 @@ void DevicePropertiesVkTest::memoryTypesPickFailed() { Error redirectError{&out}; CORRADE_VERIFY(!devices[0].tryPickMemory(MemoryFlag(0xc0ffeee0))); CORRADE_VERIFY(!devices[0].tryPickMemory({}, {}, 0)); + CORRADE_VERIFY(!devices[0].tryPickMemory({}, 0)); CORRADE_COMPARE(out.str(), Utility::formatString( "Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlag(0xc0ffeee0) found among {} considered memory types\n" + "Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlags{{}} found among 0 considered memory types\n" "Vk::DeviceProperties::tryPickMemory(): no Vk::MemoryFlags{{}} found among 0 considered memory types\n", devices[0].memoryCount())); } From b6e41ab1a7ba6d26e2626167eceec77e0660658d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 10 Nov 2020 15:10:20 +0100 Subject: [PATCH 48/85] Vk: initial APIs for binding a memory to an image. You won't believe it, but it took me over a month of sitting on the shitter until this design idea materialized out of [..] air. The whole story, in order: - Vulkan doesn't allow one VkDeviceMemory to be mapped more than once. This is rather sad, because since Vulkan best practices suggest to allocate a large block and suballocate from that, the engine needs an extra layer that "emulates" mapping the suballocations for the users but behind the scenes it inevitably has to map the whole VkDeviceMemory anyway and keep it mapped for as long as any of the sub-mappings is active. - Because if it would map just a certain suballocation and then the user would want to map another suballocation, it would have to discard the original mapping and create a new one spanning both suballocations and that has a risk of suddenly being in a different VM block, making all pointers to the previous mapping invalid. - The Vulkan Memory Allocator implements this approach of mapping the whole thing and because of all the bookkeeping it doesn't give a direct access to the underlying VkDeviceMemory, making it rather hard to integrate. Here I realized that: - Most allocations won't need to be mapped ever, so the hiding and obfuscation done by VMA isn't needed for those --- and we want interoperability with 3rd party code, so preventing access to VkDeviceMemory is out of question. - There's KHR_dedicated_allocation, which (probably?) wasn't around when VMA was originally designed. The extension was created because a dedicated allocation actually *does* make sense in certain cases and on certain architectures. Providing a way to make those thus shouldn't be something "temporary, until a real allocator exists" but rather a well-designed API that's there to stay. - Except for iGPUs, the usual way to populate a GPU buffer would be to first copy the data to some host-accessible scratch buffer and then do a GPU-side copy of that buffer to a device-local memory. The scratch buffer is very likely to have a vastly different suballocation scheme than GPU buffers (grow & discard everything once it's all uploaded, for example) so again trying to put the two under the same allocator umbrella doesn't make sense. Thus: - To avoid implementing a full-blown allocator right from the start, we'll first provide convenience APIs only for dedicated allocations -- making it possible to transfer memory ownership to an Image/Buffer so it can be treated the same way as in GL, and later having the Image/Buffer constructor implicitly allocate a dedicated VkDeviceMemory. - This default allocation will be subsequently equipped with KHR_dedicated_allocation bits. - Thanks to the extensible/layered nature of the design, the user is still capable of being completely in control of allocations, managing VkDeviceMemory sub-allocations by hand. Finally, once allocator APIs are figured out, the default Buffer/Image behavior gets switched from a dedicated allocation to using an allocator, and dedicated allocation will be only used if the KHR_dedicated_allocation bit is requested. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/CMakeLists.txt | 2 +- src/Magnum/Vk/Image.cpp | 47 +++++++++++++-- src/Magnum/Vk/Image.h | 45 ++++++++++++++- src/Magnum/Vk/Implementation/DeviceState.cpp | 8 +++ src/Magnum/Vk/Implementation/DeviceState.h | 1 + src/Magnum/Vk/Test/CMakeLists.txt | 2 +- src/Magnum/Vk/Test/ImageTest.cpp | 22 ++++++- src/Magnum/Vk/Test/ImageVkTest.cpp | 60 +++++++++++++++++++- 9 files changed, 179 insertions(+), 10 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 439cb6ccb9..26ab918ad1 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -63,7 +63,7 @@ Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{BeginCommandBuffer}, \n \fn_vk{EndCommandBuffer} | | @fn_vk{BindBufferMemory}, \n @fn_vk{BindBufferMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | -@fn_vk{BindImageMemory}, \n @fn_vk{BindImageMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{BindImageMemory}, \n @fn_vk{BindImageMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Image::bindMemory() @subsection vulkan-mapping-functions-c C diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index afc2038da7..a709e2c582 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -31,7 +31,6 @@ set(MagnumVk_SRCS CommandPool.cpp Extensions.cpp Handle.cpp - Image.cpp Instance.cpp Memory.cpp Result.cpp @@ -46,6 +45,7 @@ set(MagnumVk_GracefulAssert_SRCS DeviceProperties.cpp Enums.cpp ExtensionProperties.cpp + Image.cpp LayerProperties.cpp) set(MagnumVk_HEADERS diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index 0ffc09e993..9757e351a7 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -28,7 +28,6 @@ #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Integration.h" -#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/Implementation/DeviceState.h" @@ -75,13 +74,13 @@ Image Image::wrap(Device& device, const VkImage handle, const HandleFlags flags) return out; } -Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { +Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction}, _dedicatedMemory{NoCreate} { MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateImage(device, info, nullptr, &_handle)); } -Image::Image(NoCreateT): _device{}, _handle{} {} +Image::Image(NoCreateT): _device{}, _handle{}, _dedicatedMemory{NoCreate} {} -Image::Image(Image&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { +Image::Image(Image&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags}, _dedicatedMemory{std::move(other._dedicatedMemory)} { other._handle = {}; } @@ -95,6 +94,7 @@ Image& Image::operator=(Image&& other) noexcept { swap(other._device, _device); swap(other._handle, _handle); swap(other._flags, _flags); + swap(other._dedicatedMemory, _dedicatedMemory); return *this; } @@ -107,6 +107,32 @@ MemoryRequirements Image::memoryRequirements() const { return requirements; } +void Image::bindMemory(Memory& memory, const UnsignedLong offset) { + VkBindImageMemoryInfo info{}; + info.sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO; + info.image = _handle; + info.memory = memory; + info.memoryOffset = offset; + _device->state().bindImageMemoryImplementation(*_device, 1, &info); +} + +void Image::bindDedicatedMemory(Memory&& memory) { + bindMemory(memory, 0); + _dedicatedMemory = std::move(memory); +} + +bool Image::hasDedicatedMemory() const { + /* Sigh. Though better than needing to have `const handle()` overloads + returning `const VkDeviceMemory_T*` */ + return const_cast(*this)._dedicatedMemory.handle(); +} + +Memory& Image::dedicatedMemory() { + CORRADE_ASSERT(_dedicatedMemory.handle(), + "Vk::Image::dedicatedMemory(): image doesn't have a dedicated memory", _dedicatedMemory); + return _dedicatedMemory; +} + VkImage Image::release() { const VkImage handle = _handle; _handle = {}; @@ -125,4 +151,17 @@ void Image::getMemoryRequirementsImplementation11(Device& device, const VkImageM device->GetImageMemoryRequirements2(device, &info, &requirements); } +void Image::bindMemoryImplementationDefault(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* const infos) { + for(std::size_t i = 0; i != count; ++i) + device->BindImageMemory(device, infos[i].image, infos[i].memory, infos[i].memoryOffset); +} + +void Image::bindMemoryImplementationKHR(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* const infos) { + device->BindImageMemory2KHR(device, count, infos); +} + +void Image::bindMemoryImplementation11(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* const infos) { + device->BindImageMemory2(device, count, infos); +} + }} diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 776cc8f00f..6b381f8176 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -35,6 +35,7 @@ #include "Magnum/DimensionTraits.h" #include "Magnum/Magnum.h" #include "Magnum/Math/Vector3.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Vk.h" #include "Magnum/Vk/Vulkan.h" #include "Magnum/Vk/visibility.h" @@ -400,11 +401,48 @@ class MAGNUM_VK_EXPORT Image { /** * @brief Image memory requirements * - * @see @fn_vk_keyword{GetImageMemoryRequirements2}, + * @see @ref bindMemory(), @fn_vk_keyword{GetImageMemoryRequirements2}, * @fn_vk_keyword{GetImageMemoryRequirements} */ MemoryRequirements memoryRequirements() const; + /** + * @brief Bind image memory + * + * Assumes that @p memory type, the amount of @p memory at @p offset + * and @p offset alignment corresponds to image memory requirements. + * @see @ref memoryRequirements(), @ref bindDedicatedMemory(), + * @fn_vk_keyword{BindImageMemory2}, + * @fn_vk_keyword{BindImageMemory} + */ + void bindMemory(Memory& memory, UnsignedLong offset); + + /** + * @brief Bind a dedicated image memory + * + * Equivalent to @ref bindMemory() with @p offset set to @cpp 0 @ce, + * with the additional effect that @p memory ownership transfers to the + * image and is then available through @ref dedicatedMemory(). + */ + void bindDedicatedMemory(Memory&& memory); + + /** + * @brief Whether the image has a dedicated memory + * + * Returns @cpp true @ce if the image memory was bound using + * @ref bindDedicatedMemory(), @cpp false @ce otherwise. + * @see @ref dedicatedMemory() + */ + bool hasDedicatedMemory() const; + + /** + * @brief Dedicated image memory + * + * Expects that the image has a dedicated memory. + * @see @ref hasDedicatedMemory() + */ + Memory& dedicatedMemory(); + /** * @brief Release the underlying Vulkan image * @@ -422,11 +460,16 @@ class MAGNUM_VK_EXPORT Image { MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationKHR(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementation11(Device& device, const VkImageMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void bindMemoryImplementationDefault(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* infos); + MAGNUM_VK_LOCAL static void bindMemoryImplementationKHR(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* infos); + MAGNUM_VK_LOCAL static void bindMemoryImplementation11(Device& device, UnsignedInt count, const VkBindImageMemoryInfo* infos); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; VkImage _handle; HandleFlags _flags; + Memory _dedicatedMemory; }; }} diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index b6fd25e04b..6ebc74cd42 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -46,6 +46,14 @@ DeviceState::DeviceState(Device& device) { } else { getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementationDefault; } + + if(device.isVersionSupported(Version::Vk11)) { + bindImageMemoryImplementation = &Image::bindMemoryImplementation11; + } else if(device.isExtensionEnabled()) { + bindImageMemoryImplementation = &Image::bindMemoryImplementationKHR; + } else { + bindImageMemoryImplementation = &Image::bindMemoryImplementationDefault; + } } }}} diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 446f1f0ef3..dea6b18561 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -36,6 +36,7 @@ struct DeviceState { void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); /** @todo put this eventually into a dedicated image state struct? */ void(*getImageMemoryRequirementsImplementation)(Device&, const VkImageMemoryRequirementsInfo2&, VkMemoryRequirements2&); + void(*bindImageMemoryImplementation)(Device&, UnsignedInt, const VkBindImageMemoryInfo*); }; }}} diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 9f52fd331f..3e8bd42703 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -32,7 +32,7 @@ corrade_add_test(VkEnumsTest EnumsTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkExtensionsTest ExtensionsTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkExtensionPropertiesTest ExtensionPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkHandleTest HandleTest.cpp LIBRARIES MagnumVk) -corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/ImageTest.cpp b/src/Magnum/Vk/Test/ImageTest.cpp index ec69b2b86e..7f523e8925 100644 --- a/src/Magnum/Vk/Test/ImageTest.cpp +++ b/src/Magnum/Vk/Test/ImageTest.cpp @@ -24,7 +24,9 @@ */ #include +#include #include +#include #include "Magnum/Vk/Image.h" #include "Magnum/Vk/Integration.h" @@ -47,6 +49,8 @@ struct ImageTest: TestSuite::Tester { void constructNoCreate(); void constructCopy(); + + void dedicatedMemoryNotDedicated(); }; ImageTest::ImageTest() { @@ -62,7 +66,9 @@ ImageTest::ImageTest() { &ImageTest::createInfoConstructFromVk, &ImageTest::constructNoCreate, - &ImageTest::constructCopy}); + &ImageTest::constructCopy, + + &ImageTest::dedicatedMemoryNotDedicated}); } void ImageTest::createInfoConstruct() { @@ -199,6 +205,20 @@ void ImageTest::constructCopy() { CORRADE_VERIFY(!(std::is_assignable{})); } +void ImageTest::dedicatedMemoryNotDedicated() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Image image{NoCreate}; + CORRADE_VERIFY(!image.hasDedicatedMemory()); + + std::ostringstream out; + Error redirectError{&out}; + image.dedicatedMemory(); + CORRADE_COMPARE(out.str(), "Vk::Image::dedicatedMemory(): image doesn't have a dedicated memory\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageTest) diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index 01043d6350..8bed7d8b04 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -23,6 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include + +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Image.h" #include "Magnum/Vk/Memory.h" @@ -46,6 +49,9 @@ struct ImageVkTest: VulkanTester { void wrap(); void memoryRequirements(); + + void bindMemory(); + void bindDedicatedMemory(); }; ImageVkTest::ImageVkTest() { @@ -60,7 +66,10 @@ ImageVkTest::ImageVkTest() { &ImageVkTest::wrap, - &ImageVkTest::memoryRequirements}); + &ImageVkTest::memoryRequirements, + + &ImageVkTest::bindMemory, + &ImageVkTest::bindDedicatedMemory}); } void ImageVkTest::construct1D() { @@ -152,17 +161,29 @@ void ImageVkTest::constructMove() { VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, NoAllocate}; VkImage handle = a.handle(); + /* Verify that also the dedicated memory gets moved */ + MemoryRequirements requirements = a.memoryRequirements(); + a.bindDedicatedMemory(Vk::Memory{device(), Vk::MemoryAllocateInfo{requirements.size(), + deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); + VkDeviceMemory memoryHandle = a.dedicatedMemory().handle(); + Image b = std::move(a); CORRADE_VERIFY(!a.handle()); + CORRADE_VERIFY(!a.hasDedicatedMemory()); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(b.hasDedicatedMemory()); + CORRADE_COMPARE(b.dedicatedMemory().handle(), memoryHandle); Image c{NoCreate}; c = std::move(b); CORRADE_VERIFY(!b.handle()); + CORRADE_VERIFY(!b.hasDedicatedMemory()); CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(c.hasDedicatedMemory()); + CORRADE_COMPARE(c.dedicatedMemory().handle(), memoryHandle); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); @@ -195,6 +216,43 @@ void ImageVkTest::memoryRequirements() { CORRADE_COMPARE(requirements.size(), 128*64*4); } +void ImageVkTest::bindMemory() { + Image image{device(), ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + MemoryRequirements requirements = image.memoryRequirements(); + + /* We're testing the offset, so ensure what we hardcode is correctly + aligned */ + constexpr UnsignedLong offset = 4096; + CORRADE_COMPARE_AS(offset, requirements.alignment(), + TestSuite::Compare::Divisible); + + Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ + requirements.size() + offset, + deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + + image.bindMemory(memory, offset); + CORRADE_VERIFY(!image.hasDedicatedMemory()); +} + +void ImageVkTest::bindDedicatedMemory() { + Image image{device(), ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, NoAllocate}; + MemoryRequirements requirements = image.memoryRequirements(); + + /** @todo expand once KHR_dedicated_allocation is implemented */ + + Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ + requirements.size(), + deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + VkDeviceMemory handle = memory.handle(); + CORRADE_VERIFY(handle); + + image.bindDedicatedMemory(std::move(memory)); + CORRADE_VERIFY(image.hasDedicatedMemory()); + CORRADE_COMPARE(image.dedicatedMemory().handle(), handle); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) From 791d3d44e95d9df775b27b0afadb9c58124bd12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 10 Nov 2020 18:06:01 +0100 Subject: [PATCH 49/85] Vk: implicitly enable VK_KHR_bind_memory2 on Vulkan 1.0. --- package/archlinux/PKGBUILD | 2 +- package/archlinux/PKGBUILD-coverage | 2 +- package/archlinux/PKGBUILD-release | 2 +- package/ci/unix-desktop-vulkan.sh | 2 +- src/Magnum/Vk/Device.cpp | 2 ++ 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 7fb3e27a45..cbe22e2177 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -76,7 +76,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest done } diff --git a/package/archlinux/PKGBUILD-coverage b/package/archlinux/PKGBUILD-coverage index 9380673032..948585d712 100644 --- a/package/archlinux/PKGBUILD-coverage +++ b/package/archlinux/PKGBUILD-coverage @@ -77,7 +77,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest || true done ./Debug/bin/magnum-al-info > /dev/null diff --git a/package/archlinux/PKGBUILD-release b/package/archlinux/PKGBUILD-release index 84f9b6cd35..3c12d5fd85 100644 --- a/package/archlinux/PKGBUILD-release +++ b/package/archlinux/PKGBUILD-release @@ -114,7 +114,7 @@ check() { MAGNUM_DEVICE=cpu CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest for device in "" cpu; do MAGNUM_DEVICE=$device MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest - MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest + MAGNUM_DEVICE=$device MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON CORRADE_TEST_COLOR=ON ctest --output-on-failure -j5 -R VkTest done done } diff --git a/package/ci/unix-desktop-vulkan.sh b/package/ci/unix-desktop-vulkan.sh index 80b7accdc0..1ee3c3b8ba 100755 --- a/package/ci/unix-desktop-vulkan.sh +++ b/package/ci/unix-desktop-vulkan.sh @@ -76,7 +76,7 @@ export CORRADE_TEST_COLOR=ON ctest -V MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest -MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest +MAGNUM_DISABLE_EXTENSIONS="VK_KHR_get_physical_device_properties2 VK_KHR_get_memory_requirements2 VK_KHR_bind_memory2" MAGNUM_VULKAN_VERSION=1.0 CORRADE_TEST_SKIP_BENCHMARKS=ON ctest -V -R VkTest # Test install, after running the tests as for them it shouldn't be needed ninja install diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 793457b23c..5f747470da 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -114,6 +114,8 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext /* Only if we don't have Vulkan 1.1, on which this is core */ if(_state->version < Version::Vk11 && extensionProperties->isSupported()) addEnabledExtensions(); + if(_state->version < Version::Vk11 && extensionProperties->isSupported()) + addEnabledExtensions(); } } From 3efe98a638139352e849ee1d7c7c30cd9a758824 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 18:33:35 +0100 Subject: [PATCH 50/85] Vk: even this offset was not enough, heh. I wonder what the RTX3k cards would say about this test. --- src/Magnum/Vk/Test/ImageVkTest.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index 8bed7d8b04..c92c8b92f2 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -222,8 +222,9 @@ void ImageVkTest::bindMemory() { MemoryRequirements requirements = image.memoryRequirements(); /* We're testing the offset, so ensure what we hardcode is correctly - aligned */ - constexpr UnsignedLong offset = 4096; + aligned. For Intel 1 kB was enough, SwiftShader wanted 4 kB, AMD wants + 128 kB. */ + constexpr UnsignedLong offset = 128*1024; CORRADE_COMPARE_AS(offset, requirements.alignment(), TestSuite::Compare::Divisible); From 20c06bb0c92c140699e26bb06195e82554e78299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 18:48:03 +0100 Subject: [PATCH 51/85] Vk: document relation among various version queries. --- src/Magnum/Vk/Device.h | 14 ++++++++++++-- src/Magnum/Vk/Instance.h | 14 ++++++++++++-- src/Magnum/Vk/Version.h | 2 +- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 73fe11b292..94ecaa2a25 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -392,10 +392,20 @@ class MAGNUM_VK_EXPORT Device { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } - /** @brief Device version */ + /** + * @brief Version supported by the device + * + * Unless overriden using `--magnum-vulkan-version` on the + * @ref Vk-Device-command-line "command line", corresponds to + * @ref DeviceProperties::apiVersion(). + */ Version version() const { return _version; } - /** @brief Whether given version is supported on the device */ + /** + * @brief Whether given version is supported on the device + * + * Compares @p version against @ref version(). + */ bool isVersionSupported(Version version) const { return _version >= version; } diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 7abf547f1a..9dec228647 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -432,10 +432,20 @@ class MAGNUM_VK_EXPORT Instance { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } - /** @brief Instance version */ + /** + * @brief Version supported by the instance + * + * Unless overriden using `--magnum-vulkan-version` on the + * @ref Vk-Device-command-line "command line", corresponds to + * @ref enumerateInstanceVersion(). + */ Version version() const { return _version; } - /** @brief Whether given version is supported on the instance */ + /** + * @brief Whether given version is supported on the instance + * + * Compares @p version against @ref version(). + */ bool isVersionSupported(Version version) const { return _version >= version; } diff --git a/src/Magnum/Vk/Version.h b/src/Magnum/Vk/Version.h index 09e665823e..a3a85e9151 100644 --- a/src/Magnum/Vk/Version.h +++ b/src/Magnum/Vk/Version.h @@ -132,7 +132,7 @@ as @cb{.shell-session} . @ce if patch is zero. MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, Version value); /** -@brief Enumerate instance version +@brief Enumerate version supported by the instance @m_since_latest Note that the @fn_vk{EnumerateInstanceVersion} function isn't available in From 99f33a2670694a1533c47ed0cf178945f3d3732f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 13:07:12 +0100 Subject: [PATCH 52/85] Vk: make variadic extension type traits work for an empty list as well. --- src/Magnum/Vk/Test/ExtensionsTest.cpp | 6 ++++++ src/Magnum/Vk/TypeTraits.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/Magnum/Vk/Test/ExtensionsTest.cpp b/src/Magnum/Vk/Test/ExtensionsTest.cpp index 4e29d07e10..06967d7c9f 100644 --- a/src/Magnum/Vk/Test/ExtensionsTest.cpp +++ b/src/Magnum/Vk/Test/ExtensionsTest.cpp @@ -81,6 +81,9 @@ void ExtensionsTest::isInstanceExtension() { Extensions::KHR::get_physical_device_properties2, Extensions::KHR::external_memory_capabilities, Extensions::KHR::external_fence>::value)); /* not */ + + /* Empty variadic list should return true */ + CORRADE_VERIFY(Implementation::IsInstanceExtension<>::value); } void ExtensionsTest::isExtension() { @@ -117,6 +120,9 @@ void ExtensionsTest::isExtension() { Extensions::KHR::external_memory, Extensions::KHR::depth_stencil_resolve, Extensions::KHR::external_fence_capabilities>::value)); /* not */ + + /* Empty variadic list should return true */ + CORRADE_VERIFY(Implementation::IsExtension<>::value); } void ExtensionsTest::constructInstanceExtensionFromCompileTimeExtension() { diff --git a/src/Magnum/Vk/TypeTraits.h b/src/Magnum/Vk/TypeTraits.h index ed14c25cb9..304a0e98ff 100644 --- a/src/Magnum/Vk/TypeTraits.h +++ b/src/Magnum/Vk/TypeTraits.h @@ -40,6 +40,7 @@ namespace Implementation { /** @todo filter out GL/AL extensions also */ template class IsInstanceExtension; + template<> class IsInstanceExtension<> { public: enum: bool { value = true }; }; CORRADE_HAS_TYPE(IsInstanceExtension, decltype(T::InstanceIndex)); template class IsInstanceExtension { /** @todo C++17: use &&... instead of all this */ @@ -47,6 +48,7 @@ namespace Implementation { }; template class IsExtension; + template<> class IsExtension<> { public: enum: bool { value = true }; }; CORRADE_HAS_TYPE(IsExtension, decltype(T::Index)); template class IsExtension { /** @todo C++17: use &&... instead of all this */ From 63acbc7a22a6ba657c656e4b3e6897e5eb33fa65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 20:03:05 +0100 Subject: [PATCH 53/85] Vk: it's inevitable, DeviceProperties are going to stay forever. I have more and more cases where I need to query device properties later down the road (memory capabilities, device name, ...) and leaving all this up to the user / making this impossible to do in the library internals is complicating everything too much. Since there's a shitton of device properties with a new bag of props coming with every other new extension, I expect the queries to get quite involved / complicated over time (chaining 100s structs and such), so let's design this upfront in a way that can avoid reqpeatedly querying the same thing just because we needlessly discarded a fully populated instance before. It also means the users don't need to drag their own DeviceProperties instance along anymore and can just let the Device take care of that. Unfortunately the only nice way to make this work with DeviceCreateInfo method chaining is to add & and && overloads for each. But it's quite easy to test that all of them work and properly return a r-value reference so it shouldn't be too much of a maintenance nightmare. --- doc/snippets/MagnumVk.cpp | 3 +- src/Magnum/Vk/Device.cpp | 78 ++++++++++++++++++++--- src/Magnum/Vk/Device.h | 96 +++++++++++++++++++++++++---- src/Magnum/Vk/Test/DeviceVkTest.cpp | 58 ++++++++++++++++- 4 files changed, 209 insertions(+), 26 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 2cce629f7a..b3dd5514f3 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -63,11 +63,10 @@ info->pNext = &validationFeatures; { Vk::Instance instance; /* [CommandPool-usage] */ -Vk::DeviceProperties props = DOXYGEN_IGNORE(pickDevice(instance)); Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; Vk::CommandPool graphicsCommandPool{device, Vk::CommandPoolCreateInfo{ - props.pickQueueFamily(Vk::QueueFlag::Graphics) + device.properties().pickQueueFamily(Vk::QueueFlag::Graphics) }}; /* [CommandPool-usage] */ diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 5f747470da..b809db08e8 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -63,6 +63,11 @@ struct DeviceCreateInfo::State { std::size_t nextQueuePriority = 0; bool quietLog = false; Version version = Version::None; + /* Gets populated in the DeviceCreateInfo constructor that takes a + DeviceProperties&&, in which case it's then moved to the newly created + Device instance. If not populated, the Device instance gets nothing and + it'll gets populated on first access to Device::properties(). */ + DeviceProperties properties{NoCreate}; }; DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): _physicalDevice{deviceProperties}, _info{}, _state{Containers::InPlaceInit} { @@ -119,6 +124,10 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext } } +DeviceCreateInfo::DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): DeviceCreateInfo{deviceProperties, extensionProperties, flags} { + _state->properties = std::move(deviceProperties); +} + DeviceCreateInfo::DeviceCreateInfo(NoInitT) noexcept {} DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo& info): _physicalDevice{physicalDevice}, @@ -128,7 +137,7 @@ DeviceCreateInfo::DeviceCreateInfo(VkPhysicalDevice physicalDevice, const VkDevi DeviceCreateInfo::~DeviceCreateInfo() = default; -DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) & { if(extensions.empty()) return *this; /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ @@ -160,11 +169,21 @@ DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::Array return *this; } -DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { +DeviceCreateInfo&& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) && { + addEnabledExtensions(extensions); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) & { return addEnabledExtensions(Containers::arrayView(extensions)); } -DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) { +DeviceCreateInfo&& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) && { + addEnabledExtensions(extensions); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) & { if(extensions.empty()) return *this; /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ @@ -185,11 +204,21 @@ DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const Containers::Array return *this; } -DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) { +DeviceCreateInfo&& DeviceCreateInfo::addEnabledExtensions(const Containers::ArrayView extensions) && { + addEnabledExtensions(extensions); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) & { return addEnabledExtensions(Containers::arrayView(extensions)); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const Containers::ArrayView priorities, const Containers::ArrayView> output) { +DeviceCreateInfo&& DeviceCreateInfo::addEnabledExtensions(const std::initializer_list extensions) && { + addEnabledExtensions(extensions); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const Containers::ArrayView priorities, const Containers::ArrayView> output) & { CORRADE_ASSERT(!priorities.empty(), "Vk::DeviceCreateInfo::addQueues(): at least one queue priority has to be specified", *this); CORRADE_ASSERT(output.size() == priorities.size(), "Vk::DeviceCreateInfo::addQueues(): expected" << priorities.size() << "outuput queue references but got" << output.size(), *this); @@ -217,11 +246,21 @@ DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const Co return addQueues(info); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const std::initializer_list priorities, const std::initializer_list> output) { +DeviceCreateInfo&& DeviceCreateInfo::addQueues(const UnsignedInt family, const Containers::ArrayView priorities, const Containers::ArrayView> output) && { + addQueues(family, priorities, output); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(const UnsignedInt family, const std::initializer_list priorities, const std::initializer_list> output) & { return addQueues(family, Containers::arrayView(priorities), Containers::arrayView(output)); } -DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) { +DeviceCreateInfo&& DeviceCreateInfo::addQueues(const UnsignedInt family, const std::initializer_list priorities, const std::initializer_list> output) && { + addQueues(family, priorities, output); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) & { /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ if(!_state) _state.emplace(); @@ -236,6 +275,11 @@ DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& inf return *this; } +DeviceCreateInfo&& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) && { + addQueues(info); + return std::move(*this); +} + Device Device::wrap(Instance& instance, const VkDevice handle, const Version version, const Containers::ArrayView enabledExtensions, const HandleFlags flags) { /* Compared to the constructor nothing is printed here as it would be just repeating what was passed to the constructor */ @@ -251,7 +295,11 @@ Device Device::wrap(Instance& instance, const VkDevice handle, const Version ver return wrap(instance, handle, version, Containers::arrayView(enabledExtensions), flags); } -Device::Device(Instance& instance, const DeviceCreateInfo& info): +Device::Device(Instance& instance, const DeviceCreateInfo& info): Device{instance, info, DeviceProperties{NoCreate}} {} + +Device::Device(Instance& instance, DeviceCreateInfo&& info): Device{instance, info, std::move(info._state->properties)} {} + +Device::Device(Instance& instance, const DeviceCreateInfo& info, DeviceProperties&& properties): #ifdef CORRADE_GRACEFUL_ASSERT _handle{}, /* Otherwise the destructor dies if we hit the queue assert */ #endif @@ -260,7 +308,16 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info): CORRADE_ASSERT(info._info.queueCreateInfoCount, "Vk::Device: needs to be created with at least one queue", ); - const Version version = info._state->version != Version::None ? info._state->version : DeviceProperties::wrap(instance, info._physicalDevice).apiVersion(); + /* If the passed properties are populated, use them. Otherwise create a new + instance as we'd have no other way to remember the VkPhysicalDevice + handle otherwise */ + if(properties.handle()) + _properties.emplace(std::move(properties)); + else + _properties.emplace(DeviceProperties::wrap(instance, info._physicalDevice)); + + const Version version = info._state->version != Version::None ? + info._state->version : _properties->apiVersion(); /* Print all enabled extensions if we're not told to be quiet */ if(!info._state || !info._state->quietLog) { @@ -315,7 +372,7 @@ Device::Device(NoCreateT): _handle{}, _functionPointers{} {} Device::Device(Device&& other) noexcept: _handle{other._handle}, _flags{other._flags}, _version{other._version}, - _extensionStatus{other._extensionStatus}, _state{std::move(other._state)}, + _extensionStatus{other._extensionStatus}, _properties{std::move(other._properties)}, _state{std::move(other._state)}, /* Can't use {} with GCC 4.8 here because it tries to initialize the first member instead of doing a copy */ _functionPointers(other._functionPointers) @@ -335,6 +392,7 @@ Device& Device::operator=(Device&& other) noexcept { swap(other._flags, _flags); swap(other._version, _version); swap(other._extensionStatus, _extensionStatus); + swap(other._properties, _properties); swap(other._state, _state); swap(other._functionPointers, _functionPointers); return *this; diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 94ecaa2a25..1613b56abc 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -95,7 +95,8 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * @param extensionProperties Existing @ref ExtensionProperties * instance for querying available Vulkan extensions. If * @cpp nullptr @ce, a new instance may be created internally if - * needed. + * needed. If a r-value is passed, the instance is later available + * through @ref Device::properties(). * @param flags Device creation flags * * The following @type_vk{DeviceCreateInfo} fields are pre-filled in @@ -107,12 +108,22 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { */ explicit DeviceCreateInfo(DeviceProperties& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}); - /** @overload */ - explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, extensionProperties, flags} {} - /** @overload */ explicit DeviceCreateInfo(DeviceProperties& deviceProperties, Flags flags = {}): DeviceCreateInfo{deviceProperties, nullptr, flags} {} + /** + * @brief Construct with allowing to reuse already populated device properties + * + * Compared to @ref DeviceCreateInfo(DeviceProperties&, const ExtensionProperties*, Flags), + * if the @ref Device is subsequently constructed via + * @ref Device::Device(Instance&, DeviceCreateInfo&&), the + * @p deviceProperties instance gets directly transferred to the + * device, meaning @ref Device::properties() and any APIs relying on it + * can reuse what was possibly already queried without having to repeat + * the potentially complex queries second time. + */ + explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, Flags flags = {}); + /** @overload */ explicit DeviceCreateInfo(DeviceProperties&& deviceProperties, Flags flags = {}): DeviceCreateInfo{std::move(deviceProperties), nullptr, flags} {} @@ -135,6 +146,18 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { ~DeviceCreateInfo(); + /* All the && overloads below are there in order to allow code like + + Device device{instance, DeviceCreateInfo{pickDevice(instance)} + .addQueues(...) + .addEnabledExtensions(...) + ... + }; + + to work and correctly move the DeviceProperties to the Device. + When adding new APIs, expand DeviceVkTest::createInfoRvalue() to + verify everything still works. */ + /** * @brief Add enabled device extensions * @return Reference to self (for method chaining) @@ -147,17 +170,30 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * null-terminated, use the @link Containers::Literals::operator""_s() @endlink * literal to prevent that where possible. */ - DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions) &; + /** @overload */ + DeviceCreateInfo&& addEnabledExtensions(Containers::ArrayView extensions) &&; + /** @overload */ + DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension) &; /** @overload */ - DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension); + DeviceCreateInfo&& addEnabledExtensions(std::initializer_list extension) &&; /** @overload */ - DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions); + DeviceCreateInfo& addEnabledExtensions(Containers::ArrayView extensions) &; /** @overload */ - DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension); + DeviceCreateInfo&& addEnabledExtensions(Containers::ArrayView extensions) &&; /** @overload */ - template DeviceCreateInfo& addEnabledExtensions() { + DeviceCreateInfo& addEnabledExtensions(std::initializer_list extension) &; + /** @overload */ + DeviceCreateInfo&& addEnabledExtensions(std::initializer_list extension) &&; + /** @overload */ + template DeviceCreateInfo& addEnabledExtensions() & { static_assert(Implementation::IsExtension::value, "expected only Vulkan device extensions"); - return addEnabledExtensions({E{}...}); + return addEnabledExtensions(std::initializer_list{E{}...}); + } + /** @overload */ + template DeviceCreateInfo&& addEnabledExtensions() && { + addEnabledExtensions(); + return std::move(*this); } /** @@ -173,9 +209,13 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * At least one queue has to be added. * @see @ref DeviceProperties::pickQueueFamily() */ - DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output); + DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output) &; + /** @overload */ + DeviceCreateInfo&& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output) &&; /** @overload */ - DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output); + DeviceCreateInfo& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output) &; + /** @overload */ + DeviceCreateInfo&& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output) &&; /** * @brief Add queues using raw info @@ -185,7 +225,9 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * queue properties using the `pNext` chain. The info is uses as-is, * with all pointers expected to stay in scope until device creation. */ - DeviceCreateInfo& addQueues(const VkDeviceQueueCreateInfo& info); + DeviceCreateInfo& addQueues(const VkDeviceQueueCreateInfo& info) &; + /** @overload */ + DeviceCreateInfo&& addQueues(const VkDeviceQueueCreateInfo& info) &&; /** @brief Underlying @type_vk{DeviceCreateInfo} structure */ VkDeviceCreateInfo& operator*() { return _info; } @@ -353,6 +395,20 @@ class MAGNUM_VK_EXPORT Device { */ explicit Device(Instance& instance, const DeviceCreateInfo& info); + /** + * @brief Construct with reusing already populated device properties + * + * Compared to @ref Device(Instance&, const DeviceCreateInfo&), it can + * take ownership of the @ref DeviceProperties added to @p info earlier + * via @ref DeviceCreateInfo::DeviceCreateInfo(DeviceProperties&&, const ExtensionProperties*, Flags) or any of the other r-value-taking + * constructors. + * + * With that, the @ref properties() getter and any APIs relying on it + * can reuse what was possibly already queried without having to repeat + * the potentially complex queries second time. + */ + explicit Device(Instance& instance, DeviceCreateInfo&& info); + /** * @brief Construct without creating the device * @@ -392,6 +448,15 @@ class MAGNUM_VK_EXPORT Device { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Device properties + * + * If a r-value @ref DeviceProperties instance was propagated to + * @ref DeviceCreateInfo and then to @ref Device, it's reused here. + * Otherwise the contents are populated on first use. + */ + DeviceProperties& properties() { return *_properties; } + /** * @brief Version supported by the device * @@ -477,6 +542,10 @@ class MAGNUM_VK_EXPORT Device { private: friend Implementation::DeviceState; + /* Common guts for Device(Instance&, DeviceCreateInfo&) and + Device(Instance&, DeviceCreateInfo&&) */ + explicit Device(Instance& isntance, const DeviceCreateInfo&, DeviceProperties&&); + template MAGNUM_VK_LOCAL void initializeExtensions(Containers::ArrayView enabledExtensions); MAGNUM_VK_LOCAL void initialize(Instance& instance, Version version); @@ -487,6 +556,7 @@ class MAGNUM_VK_EXPORT Device { HandleFlags _flags; Version _version; Math::BoolVector _extensionStatus; + Containers::Pointer _properties; Containers::Pointer _state; /* This member is bigger than you might think */ diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index 7eaa6804e1..5e8481cf88 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -54,9 +54,11 @@ struct DeviceVkTest: VulkanTester { void createInfoCopiedStrings(); void createInfoNoQueuePriorities(); void createInfoWrongQueueOutputCount(); + void createInfoRvalue(); void construct(); void constructExtensions(); + void constructTransferDeviceProperties(); void constructExtensionsCommandLineDisable(); void constructExtensionsCommandLineEnable(); void constructMultipleQueues(); @@ -64,7 +66,9 @@ struct DeviceVkTest: VulkanTester { void constructMove(); void constructUnknownExtension(); void constructNoQueue(); + void wrap(); + void populateGlobalFunctionPointers(); }; @@ -126,9 +130,11 @@ DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { &DeviceVkTest::createInfoCopiedStrings, &DeviceVkTest::createInfoNoQueuePriorities, &DeviceVkTest::createInfoWrongQueueOutputCount, + &DeviceVkTest::createInfoRvalue, &DeviceVkTest::construct, - &DeviceVkTest::constructExtensions}); + &DeviceVkTest::constructExtensions, + &DeviceVkTest::constructTransferDeviceProperties}); addInstancedTests({&DeviceVkTest::constructExtensionsCommandLineDisable, &DeviceVkTest::constructExtensionsCommandLineEnable}, @@ -233,6 +239,33 @@ void DeviceVkTest::createInfoWrongQueueOutputCount() { CORRADE_COMPARE(out.str(), "Vk::DeviceCreateInfo::addQueues(): expected 3 outuput queue references but got 2\n"); } +void DeviceVkTest::createInfoRvalue() { + Float zero[1]{}; + Queue a{NoCreate}, b{NoCreate}; + Containers::Reference reference[1]{a}; + + VkDeviceQueueCreateInfo rawQueueInfo{}; + rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + rawQueueInfo.pQueuePriorities = zero; + rawQueueInfo.queueFamilyIndex = 0; + rawQueueInfo.queueCount = 1; + + DeviceCreateInfo&& info = DeviceCreateInfo{pickDevice(instance())} + .addEnabledExtensions(Containers::ArrayView{}) + .addEnabledExtensions(std::initializer_list{}) + .addEnabledExtensions(Containers::ArrayView{}) + .addEnabledExtensions(std::initializer_list{}) + .addEnabledExtensions<>() + .addQueues(0, zero, reference) + .addQueues(0, {0.0f}, {b}) + .addQueues(rawQueueInfo); + + /* Just to test something, main point is that the above compiles, links and + returns a &&. Can't test anything related to the contents because the + destructor gets called at the end of the expression. */ + CORRADE_VERIFY(&info); +} + void DeviceVkTest::construct() { if(std::getenv("MAGNUM_VULKAN_VERSION")) CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); @@ -258,6 +291,11 @@ void DeviceVkTest::construct() { /* The queue should be also filled in */ CORRADE_VERIFY(queue.handle()); + + /* Device properties should be lazy-populated and different from the + above instances because we didn't transfer the ownership */ + CORRADE_COMPARE(device.properties().name(), deviceProperties.name()); + CORRADE_VERIFY(&device.properties().properties() != &deviceProperties.properties()); } /* Shouldn't crash or anything */ @@ -303,6 +341,19 @@ void DeviceVkTest::constructExtensions() { CORRADE_VERIFY(device->TrimCommandPoolKHR); } +void DeviceVkTest::constructTransferDeviceProperties() { + DeviceProperties deviceProperties = pickDevice(instance()); + const void* vkProperties = &deviceProperties.properties(); + Queue queue{NoCreate}; + Device device{instance(), DeviceCreateInfo{std::move(deviceProperties)} + .addQueues(0, {0.0f}, {queue}) + }; + + /* Device properties should be the same address as in the original instance + because the ownership got transferred through */ + CORRADE_COMPARE(&device.properties().properties(), vkProperties); +} + void DeviceVkTest::constructExtensionsCommandLineDisable() { auto&& data = ConstructCommandLineData[testCaseInstanceId()]; setTestCaseDescription(data.nameDisable); @@ -496,6 +547,9 @@ void DeviceVkTest::constructRawQueue() { } void DeviceVkTest::constructMove() { + if(std::getenv("MAGNUM_VULKAN_VERSION")) + CORRADE_SKIP("Can't test with the MAGNUM_VULKAN_VERSION environment variable set"); + DeviceProperties deviceProperties = pickDevice(instance()); ExtensionProperties extensions = deviceProperties.enumerateExtensionProperties(); if(!extensions.isSupported()) @@ -517,6 +571,7 @@ void DeviceVkTest::constructMove() { CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.version(), version); + CORRADE_COMPARE(b.properties().apiVersion(), version); CORRADE_VERIFY(b.isExtensionEnabled()); /* Function pointers in a are left in whatever state they were before, as that doesn't matter */ @@ -529,6 +584,7 @@ void DeviceVkTest::constructMove() { CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.version(), version); + CORRADE_COMPARE(c.properties().apiVersion(), version); CORRADE_VERIFY(c.isExtensionEnabled()); /* Everything is swapped, including function pointers */ CORRADE_VERIFY(!b->CreateBuffer); From c402c973078221151751a17de4e9d101087bebfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 20:16:46 +0100 Subject: [PATCH 54/85] Vk: and thus VulkanTester doesn't need a DeviceProperties instance. --- src/Magnum/Vk/Test/CommandBufferVkTest.cpp | 8 +++---- src/Magnum/Vk/Test/CommandPoolVkTest.cpp | 10 ++++---- src/Magnum/Vk/Test/ImageVkTest.cpp | 6 ++--- src/Magnum/Vk/Test/MemoryVkTest.cpp | 6 ++--- src/Magnum/Vk/VulkanTester.cpp | 9 ++++---- src/Magnum/Vk/VulkanTester.h | 27 ++++++---------------- 6 files changed, 27 insertions(+), 39 deletions(-) diff --git a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp index 623f950a9f..024461c26e 100644 --- a/src/Magnum/Vk/Test/CommandBufferVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandBufferVkTest.cpp @@ -52,7 +52,7 @@ CommandBufferVkTest::CommandBufferVkTest() { void CommandBufferVkTest::construct() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; { CommandBuffer buffer = pool.allocate(); @@ -66,7 +66,7 @@ void CommandBufferVkTest::construct() { void CommandBufferVkTest::constructMove() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; CommandBuffer a = pool.allocate(); VkCommandBuffer handle = a.handle(); @@ -89,7 +89,7 @@ void CommandBufferVkTest::constructMove() { void CommandBufferVkTest::wrap() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; VkCommandBuffer buffer{}; VkCommandBufferAllocateInfo info{}; @@ -111,7 +111,7 @@ void CommandBufferVkTest::wrap() { void CommandBufferVkTest::reset() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics), + device().properties().pickQueueFamily(QueueFlag::Graphics), CommandPoolCreateInfo::Flag::ResetCommandBuffer}}; CommandBuffer a = pool.allocate(); diff --git a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp index be8c36eaf7..a4fd4ea99e 100644 --- a/src/Magnum/Vk/Test/CommandPoolVkTest.cpp +++ b/src/Magnum/Vk/Test/CommandPoolVkTest.cpp @@ -55,7 +55,7 @@ CommandPoolVkTest::CommandPoolVkTest() { void CommandPoolVkTest::construct() { { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics), + device().properties().pickQueueFamily(QueueFlag::Graphics), CommandPoolCreateInfo::Flag::ResetCommandBuffer}}; CORRADE_VERIFY(pool.handle()); CORRADE_COMPARE(pool.handleFlags(), HandleFlag::DestroyOnDestruction); @@ -67,7 +67,7 @@ void CommandPoolVkTest::construct() { void CommandPoolVkTest::constructMove() { CommandPool a{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics), + device().properties().pickQueueFamily(QueueFlag::Graphics), CommandPoolCreateInfo::Flag::Transient}}; VkCommandPool handle = a.handle(); @@ -91,7 +91,7 @@ void CommandPoolVkTest::wrap() { VkCommandPool pool{}; CORRADE_COMPARE(Result(device()->CreateCommandPool(device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}, + device().properties().pickQueueFamily(QueueFlag::Graphics)}, nullptr, &pool)), Result::Success); CORRADE_VERIFY(pool); @@ -106,7 +106,7 @@ void CommandPoolVkTest::wrap() { void CommandPoolVkTest::reset() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; pool.reset(CommandPoolResetFlag::ReleaseResources); @@ -116,7 +116,7 @@ void CommandPoolVkTest::reset() { void CommandPoolVkTest::allocate() { CommandPool pool{device(), CommandPoolCreateInfo{ - deviceProperties().pickQueueFamily(QueueFlag::Graphics)}}; + device().properties().pickQueueFamily(QueueFlag::Graphics)}}; CommandBuffer a = pool.allocate(CommandBufferLevel::Secondary); CORRADE_VERIFY(a.handle()); diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index c92c8b92f2..db840a7070 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -164,7 +164,7 @@ void ImageVkTest::constructMove() { /* Verify that also the dedicated memory gets moved */ MemoryRequirements requirements = a.memoryRequirements(); a.bindDedicatedMemory(Vk::Memory{device(), Vk::MemoryAllocateInfo{requirements.size(), - deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); VkDeviceMemory memoryHandle = a.dedicatedMemory().handle(); Image b = std::move(a); @@ -230,7 +230,7 @@ void ImageVkTest::bindMemory() { Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ requirements.size() + offset, - deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; image.bindMemory(memory, offset); CORRADE_VERIFY(!image.hasDedicatedMemory()); @@ -245,7 +245,7 @@ void ImageVkTest::bindDedicatedMemory() { Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ requirements.size(), - deviceProperties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; VkDeviceMemory handle = memory.handle(); CORRADE_VERIFY(handle); diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp index 16ac06f123..12e5548739 100644 --- a/src/Magnum/Vk/Test/MemoryVkTest.cpp +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -48,13 +48,13 @@ MemoryVkTest::MemoryVkTest() { } void MemoryVkTest::construct() { - Memory memory{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + Memory memory{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::DeviceLocal)}}; CORRADE_VERIFY(memory.handle()); CORRADE_COMPARE(memory.handleFlags(), HandleFlag::DestroyOnDestruction); } void MemoryVkTest::constructMove() { - Memory a{device(), MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}}; + Memory a{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::DeviceLocal)}}; VkDeviceMemory handle = a.handle(); Memory b = std::move(a); @@ -76,7 +76,7 @@ void MemoryVkTest::constructMove() { void MemoryVkTest::wrap() { VkDeviceMemory memory{}; CORRADE_COMPARE(Result(device()->AllocateMemory(device(), - MemoryAllocateInfo{1024*1024, deviceProperties().pickMemory(MemoryFlag::DeviceLocal)}, + MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::DeviceLocal)}, nullptr, &memory)), Result::Success); CORRADE_VERIFY(memory); diff --git a/src/Magnum/Vk/VulkanTester.cpp b/src/Magnum/Vk/VulkanTester.cpp index b1e5eb0784..dc28df3833 100644 --- a/src/Magnum/Vk/VulkanTester.cpp +++ b/src/Magnum/Vk/VulkanTester.cpp @@ -33,9 +33,10 @@ namespace Magnum { namespace Vk { VulkanTester::VulkanTester(): VulkanTester{NoCreate} { - *_deviceProperties = pickDevice(_instance); - _device = Vk::Device{_instance, Vk::DeviceCreateInfo{*_deviceProperties} - .addQueues(_deviceProperties->pickQueueFamily(Vk::QueueFlag::Graphics), {0.0f}, {_queue}) + DeviceProperties deviceProperties = pickDevice(_instance); + UnsignedInt graphicsQueue = deviceProperties.pickQueueFamily(Vk::QueueFlag::Graphics); + _device = Vk::Device{_instance, Vk::DeviceCreateInfo{std::move(deviceProperties)} + .addQueues(graphicsQueue, {0.0f}, {_queue}) }; } @@ -45,6 +46,6 @@ VulkanTester::VulkanTester(NoCreateT): VulkanTester{NoCreate, NoCreate} { }; } -VulkanTester::VulkanTester(NoCreateT, NoCreateT): TestSuite::Tester{TestSuite::Tester::TesterConfiguration{}.setSkippedArgumentPrefixes({"magnum"})}, _instance{NoCreate}, _device{NoCreate}, _deviceProperties{Containers::InPlaceInit, NoCreate}, _queue{NoCreate} {} +VulkanTester::VulkanTester(NoCreateT, NoCreateT): TestSuite::Tester{TestSuite::Tester::TesterConfiguration{}.setSkippedArgumentPrefixes({"magnum"})}, _instance{NoCreate}, _device{NoCreate}, _queue{NoCreate} {} }} diff --git a/src/Magnum/Vk/VulkanTester.h b/src/Magnum/Vk/VulkanTester.h index 104ae9864c..7fc37cb233 100644 --- a/src/Magnum/Vk/VulkanTester.h +++ b/src/Magnum/Vk/VulkanTester.h @@ -77,8 +77,7 @@ See @ref building, @ref cmake and @ref testsuite for more information. The class implicitly creates a Vulkan @ref Instance and @ref Device with default layers and extensions and one graphics queue. These are then available -through @ref instance(), @ref device(), @ref deviceProperties() and -@ref queue() getters. +through @ref instance(), @ref device() and @ref queue() getters. If you want to create a custom device, use the @ref VulkanTester(NoCreateT) constructor. You can then move the device and queue instances to the getters to @@ -93,8 +92,7 @@ class VulkanTester: public TestSuite::Tester { * * Creates an instance using implicit settings, picks a default device * and creates a graphics queue on that device. These are then exposed - * through @ref instance(), @ref device(), @ref deviceProperties() and - * @ref queue() getters. + * through @ref instance(), @ref device() and @ref queue() getters. */ explicit VulkanTester(); @@ -102,8 +100,8 @@ class VulkanTester: public TestSuite::Tester { * @brief Construct without creating a device * * Use the @ref instance() to pick and create a device. You can then - * move it to @ref device(), @ref deviceProperties() and @ref queue() - * to have them accessible through common interfaces again. + * move it to @ref device() and @ref queue() to have them accessible + * through common interfaces again. */ explicit VulkanTester(NoCreateT); @@ -111,8 +109,8 @@ class VulkanTester: public TestSuite::Tester { * @brief Construct without creating an instance or device * * Leaves the initialization completely on the user. You can move the - * instances to @ref instance(), @ref device(), @ref deviceProperties() - * and @ref queue() to have them accessible through common interfaces. + * instances to @ref instance(), @ref device() and @ref queue() to have + * them accessible through common interfaces. */ explicit VulkanTester(NoCreateT, NoCreateT); @@ -136,22 +134,12 @@ class VulkanTester: public TestSuite::Tester { */ Device& device() { return _device; } - /** - * @brief Vulkan device properties - * - * In case the class was constructed using @ref VulkanTester(NoCreateT) - * or @ref VulkanTester(NoCreateT, NoCreateT), this instance is - * initially not created. Move a created instance onto it to make it - * useful. - */ - DeviceProperties& deviceProperties() { return *_deviceProperties; } - /** * @brief Vulkan queue * * In case the calss was constructed using @ref VulkanTester(), the * queue corresponds to @ref DeviceProperties::pickQueueFamily() with - * @ref QueueFlag::Graphics called on @ref deviceProperties(). + * @ref QueueFlag::Graphics called on @ref Device::properties(). * * In case the class was constructed using @ref VulkanTester(NoCreateT) * or @ref VulkanTester(NoCreateT, NoCreateT), this instance is @@ -163,7 +151,6 @@ class VulkanTester: public TestSuite::Tester { private: Instance _instance; Device _device; - Containers::Pointer _deviceProperties; Queue _queue; }; From 103f164cb3292d3ffd2607c9cb106793bae1ad16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 20:32:16 +0100 Subject: [PATCH 55/85] Vk: print device name on creation. --- src/Magnum/Vk/Device.cpp | 1 + src/Magnum/Vk/Test/DeviceVkTest.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index b809db08e8..ff5dfbb4ab 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -321,6 +321,7 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info, DevicePropertie /* Print all enabled extensions if we're not told to be quiet */ if(!info._state || !info._state->quietLog) { + Debug{} << "Device:" << _properties->name(); Debug{} << "Device version:" << version; if(info->enabledExtensionCount) { diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index 5e8481cf88..b6c8d43ba3 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -91,6 +91,7 @@ struct { Containers::array({"", "--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), true, true, true, + "Device: {}\n" "Device version: Vulkan {}.{}{}\n" "Enabled device extensions:\n" " VK_EXT_debug_marker\n" @@ -102,6 +103,7 @@ struct { "--magnum-vulkan-version", "1.0", "--magnum-enable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), false, true, true, + "Device: {}\n" "Device version: Vulkan 1.0\n" "Enabled device extensions:\n" " VK_EXT_debug_marker\n" @@ -112,6 +114,7 @@ struct { Containers::array({"", "--magnum-enable-extensions", "VK_KHR_maintenance1"}), true, false, true, + "Device: {}\n" "Device version: Vulkan {}.{}{}\n" "Enabled device extensions:\n" " VK_KHR_maintenance1\n"}, @@ -120,6 +123,7 @@ struct { "--magnum-disable-extensions", "VK_EXT_debug_marker VK_KHR_maintenance1"}), nullptr, true, false, false, + "Device: {}\n" "Device version: Vulkan {}.{}{}\n"}, }; @@ -402,7 +406,7 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ - CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "")); /* Verify that the entrypoint is actually (not) loaded as expected, to avoid all the above reporting being just smoke & mirrors */ @@ -456,7 +460,7 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ - CORRADE_COMPARE(out.str(), Utility::formatString(data.log, major, minor, patch ? Utility::formatString(".{}", patch) : "")); + CORRADE_COMPARE(out.str(), Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "")); /* Verify that the entrypoint is actually (not) loaded as expected, to avoid all the above reporting being just smoke & mirrors */ From b877eb8537c43413d886b42d7669e568ecef93c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 20:44:01 +0100 Subject: [PATCH 56/85] Vk: treat DeviceProperties as a *view* on the VkPhysicalDevice. Originally I needed this change for something, but not anymore. It seems like a good idea nevertheless. --- src/Magnum/Vk/DeviceProperties.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 29b5ba8691..2fcb09f423 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -221,10 +221,17 @@ class MAGNUM_VK_EXPORT DeviceProperties { /** @brief Move assignment */ DeviceProperties& operator=(DeviceProperties&&) noexcept; - /** @brief Underlying @type_vk{PhysicalDevice} handle */ - VkPhysicalDevice handle() { return _handle; } + /** + * @brief Underlying @type_vk{PhysicalDevice} handle + * + * Unlike most handle getters, this one is marked as @cpp const @ce + * because the @ref DeviceProperties class is treated as a *view* on + * the physical device --- it doesn't change the device state in any + * way, only queries its properties. + */ + VkPhysicalDevice handle() const { return _handle; } /** @overload */ - operator VkPhysicalDevice() { return _handle; } + operator VkPhysicalDevice() const { return _handle; } /** * @brief Raw device properties From 4ca0f78a2b7b132ec60ef5461720373f8fd5686b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 21:49:47 +0100 Subject: [PATCH 57/85] external: updated Vulkan headers to latest. Needed the new VkDriverId entry. --- src/MagnumExternal/Vulkan/flextVk.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/MagnumExternal/Vulkan/flextVk.h b/src/MagnumExternal/Vulkan/flextVk.h index 394c08dd6d..7a548100e2 100644 --- a/src/MagnumExternal/Vulkan/flextVk.h +++ b/src/MagnumExternal/Vulkan/flextVk.h @@ -236,7 +236,7 @@ extern "C" { /* VK_EXT_validation_features */ -#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 3 +#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 4 #define VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME "VK_EXT_validation_features" /* VK_EXT_texture_compression_astc_hdr */ @@ -268,7 +268,7 @@ extern "C" { // Vulkan 1.2 version number #define VK_API_VERSION_1_2 VK_MAKE_VERSION(1, 2, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 146 +#define VK_HEADER_VERSION 152 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_VERSION(1, 2, VK_HEADER_VERSION) #define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; @@ -1667,7 +1667,8 @@ typedef enum { VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT = 0, VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT = 1, VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT = 2, - VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT = 3 + VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT = 3, + VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT = 4 } VkValidationFeatureEnableEXT; typedef enum { @@ -1711,6 +1712,7 @@ typedef enum { VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 1 << 1, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 1 << 2, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT = 1 << 3, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D11_FENCE_BIT = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 1 << 4, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT, @@ -1855,7 +1857,8 @@ typedef enum { VK_DRIVER_ID_GOOGLE_SWIFTSHADER = 10, VK_DRIVER_ID_GGP_PROPRIETARY = 11, VK_DRIVER_ID_BROADCOM_PROPRIETARY = 12, - VK_DRIVER_ID_MESA_LLVMPIPE = 13 + VK_DRIVER_ID_MESA_LLVMPIPE = 13, + VK_DRIVER_ID_MOLTENVK = 14 } VkDriverId; typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( void* pUserData, From 65d84e05ee4bd3ead5f87e94126cd3f104da76da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 15 Nov 2020 21:36:07 +0100 Subject: [PATCH 58/85] Vk: treat instance and device info separately in magnum-vk-info. Otherwise, if the instance has a version 1.1 and the device 1.0, it won't print 1.1 device extensions, only 1.2. --- src/Magnum/Vk/vk-info.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Magnum/Vk/vk-info.cpp b/src/Magnum/Vk/vk-info.cpp index 787db48d43..98ae245d5f 100644 --- a/src/Magnum/Vk/vk-info.cpp +++ b/src/Magnum/Vk/vk-info.cpp @@ -169,8 +169,8 @@ int main(int argc, char** argv) { #endif Debug{} << ""; - const Vk::Version version = Vk::enumerateInstanceVersion(); - Debug{} << "Reported instance version:" << version; + const Vk::Version instanceVersion = Vk::enumerateInstanceVersion(); + Debug{} << "Reported instance version:" << instanceVersion; Debug{} << "Reported instance layers:"; for(UnsignedInt i = 0, max = layerProperties.count(); i != max; ++i) { Debug{} << " " << layerProperties.name(i) << "(r" << Debug::nospace << layerProperties.revision(i) << Debug::nospace << ", written against" << layerProperties.version(i) << Debug::nospace << ")"; @@ -182,11 +182,11 @@ int main(int argc, char** argv) { Vk::Version::Vk12, Vk::Version::None }; - std::size_t future = 0; + std::size_t instanceFuture = 0; if(!args.isSet("all-extensions")) - while(versions[future] != Vk::Version::None && version >= versions[future]) - ++future; + while(versions[instanceFuture] != Vk::Version::None && instanceVersion >= versions[instanceFuture]) + ++instanceFuture; /** @todo do better once implemented in format() */ using namespace Containers::Literals; @@ -203,7 +203,7 @@ int main(int argc, char** argv) { d << ")"; } - } else for(std::size_t i = future; i != Containers::arraySize(versions); ++i) { + } else for(std::size_t i = instanceFuture; i != Containers::arraySize(versions); ++i) { Containers::ArrayView extensions = Vk::InstanceExtension::extensions(versions[i]); if(extensions.empty()) continue; @@ -217,7 +217,7 @@ int main(int argc, char** argv) { if(instanceExtensionProperties.isSupported(extension)) d << "REV." << Debug::nospace << instanceExtensionProperties.revision(extension); - else if(version >= extension.requiredVersion()) + else if(instanceVersion >= extension.requiredVersion()) d << " -"; else d << " n/a"; @@ -246,7 +246,15 @@ int main(int argc, char** argv) { Vk::DeviceProperties device = Vk::pickDevice(instance); Debug{} << "Picked device" << device.name() << Debug::newline; - Debug{} << "Reported version:" << device.apiVersion(); + + const Vk::Version deviceVersion = device.apiVersion(); + Debug{} << "Reported version:" << deviceVersion; + + std::size_t deviceFuture = 0; + + if(!args.isSet("all-extensions")) + while(versions[deviceFuture] != Vk::Version::None && deviceVersion >= versions[deviceFuture]) + ++deviceFuture; Vk::ExtensionProperties extensionProperties = device.enumerateExtensionProperties(layerProperties.names()); @@ -261,7 +269,7 @@ int main(int argc, char** argv) { d << ")"; } - } else for(std::size_t i = future; i != Containers::arraySize(versions); ++i) { + } else for(std::size_t i = deviceFuture; i != Containers::arraySize(versions); ++i) { Containers::ArrayView extensions = Vk::Extension::extensions(versions[i]); if(extensions.empty()) continue; @@ -275,7 +283,7 @@ int main(int argc, char** argv) { if(extensionProperties.isSupported(extension)) d << "REV." << Debug::nospace << extensionProperties.revision(extension); - else if(version >= extension.requiredVersion()) + else if(deviceVersion >= extension.requiredVersion()) d << " -"; else d << " n/a"; From 8c1b85f519cae332c47d2edebab467b201bff4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 22:27:29 +0100 Subject: [PATCH 59/85] Vk: implement VK_KHR_driver_properties. I'm a bit unsure why there's a device extension that actually gets used on an instance and doesn't need any enabling (and thus there's currently no way to disable it to test all code paths, hmm). --- doc/vulkan-support.dox | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 61 +++++++- src/Magnum/Vk/DeviceProperties.h | 136 ++++++++++++++++-- src/Magnum/Vk/Test/DevicePropertiesTest.cpp | 8 ++ src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 27 ++++ src/Magnum/Vk/vk-info.cpp | 13 ++ 6 files changed, 232 insertions(+), 15 deletions(-) diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index 95309a5dbf..f7ceb2bfc5 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -96,7 +96,7 @@ Extension | Status @vk_extension{KHR,shader_subgroup_extended_types} | | @vk_extension{KHR,8bit_storage} | | @vk_extension{KHR,shader_atomic_int64} | | -@vk_extension{KHR,driver_properties} | | +@vk_extension{KHR,driver_properties} | mostly @vk_extension{KHR,shader_float_controls} | | @vk_extension{KHR,depth_stencil_resolve} | | @vk_extension{KHR,timeline_semaphore} | | diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 0417e993a3..e6b0d27bc9 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -33,11 +33,13 @@ #include #include "Magnum/Math/Functions.h" -#include "Magnum/Vk/Instance.h" #include "Magnum/Vk/ExtensionProperties.h" +#include "Magnum/Vk/Extensions.h" +#include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" @@ -45,6 +47,7 @@ namespace Magnum { namespace Vk { struct DeviceProperties::State { VkPhysicalDeviceProperties2 properties{}; + VkPhysicalDeviceDriverProperties driverProperties{}; VkPhysicalDeviceMemoryProperties2 memoryProperties{}; Containers::Array queueFamilyProperties; }; @@ -65,6 +68,21 @@ Containers::StringView DeviceProperties::name() { return properties().properties.deviceName; } +DeviceDriver DeviceProperties::driver() { + /* Ensure the values are populated first */ + return properties(), DeviceDriver(_state->driverProperties.driverID); +} + +Containers::StringView DeviceProperties::driverName() { + /* Ensure the values are populated first */ + return properties(), _state->driverProperties.driverName; +} + +Containers::StringView DeviceProperties::driverInfo() { + /* Ensure the values are populated first */ + return properties(), _state->driverProperties.driverInfo; +} + const VkPhysicalDeviceProperties2& DeviceProperties::properties() { if(!_state) _state.emplace(); @@ -72,6 +90,18 @@ const VkPhysicalDeviceProperties2& DeviceProperties::properties() { if(!_state->properties.sType) { _state->properties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + Containers::Reference next = _state->properties.pNext; + + /* Device extension properties. Populated only if needed. */ + ExtensionProperties extensions{NoCreate}; + + /* Fetch driver properties, if supported */ + if(_instance->isVersionSupported(Version::Vk12) || (extensions = enumerateExtensionProperties()).isSupported()) { + *next = &_state->driverProperties; + next = _state->driverProperties.pNext; + _state->driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + } + _instance->state().getPhysicalDevicePropertiesImplementation(*this, _state->properties); } @@ -378,6 +408,35 @@ Debug& operator<<(Debug& debug, const DeviceType value) { return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; } +Debug& operator<<(Debug& debug, const DeviceDriver value) { + debug << "Vk::DeviceDriver" << Debug::nospace; + + switch(value) { + /* LCOV_EXCL_START */ + #define _c(value) case Vk::DeviceDriver::value: return debug << "::" << Debug::nospace << #value; + _c(Unknown) + _c(AmdOpenSource) + _c(AmdProprietary) + _c(ArmProprietary) + _c(BroadcomProprietary) + _c(GgpProprietary) + _c(GoogleSwiftShader) + _c(ImaginationProprietary) + _c(IntelOpenSourceMesa) + _c(IntelProprietaryWindows) + _c(MesaLlvmpipe) + _c(MesaRadv) + _c(MoltenVk) + _c(NVidiaProprietary) + _c(QualcommProprietary) + #undef _c + /* LCOV_EXCL_STOP */ + } + + /* Vulkan docs have the values in decimal, so not converting to hex */ + return debug << "(" << Debug::nospace << Int(value) << Debug::nospace << ")"; +} + Debug& operator<<(Debug& debug, const QueueFlag value) { debug << "Vk::QueueFlag" << Debug::nospace; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index 2fcb09f423..ebd5f41652 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -80,6 +80,75 @@ enum class DeviceType: Int { */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceType value); +/** +@brief Physical device driver ID +@m_since_latest + +Wraps a @type_vk_keyword{DriverId}. +@see @ref DeviceProperties::driver() +@requires_vk12 Extension @vk_extension{KHR,driver_properties} +@m_enum_values_as_keywords +*/ +enum class DeviceDriver: Int { + /** + * Unknown. Returned from @ref DeviceProperties::driver() in case Vulkan + * 1.2 or the @vk_extension{KHR,driver_properties} extension isn't + * supported. + */ + Unknown = 0, + + /* Unlike with VkDriverId, which gets allocated sequentially, the rest of + this list is sorted alphabetically for easier lookup. */ + + /** Open-source AMD */ + AmdOpenSource = VK_DRIVER_ID_AMD_OPEN_SOURCE, + + /** Proprietary AMD */ + AmdProprietary = VK_DRIVER_ID_AMD_PROPRIETARY, + + /** Proprietary ARM */ + ArmProprietary = VK_DRIVER_ID_ARM_PROPRIETARY, + + /** Proprietary Broadcom */ + BroadcomProprietary = VK_DRIVER_ID_BROADCOM_PROPRIETARY, + + /** Proprietary GGP */ + GgpProprietary = VK_DRIVER_ID_GGP_PROPRIETARY, + + /** Google SwiftShader */ + GoogleSwiftShader = VK_DRIVER_ID_GOOGLE_SWIFTSHADER, + + /** Proprietary Imagination */ + ImaginationProprietary = VK_DRIVER_ID_IMAGINATION_PROPRIETARY, + + /** Open-source Intel Mesa drivers */ + IntelOpenSourceMesa = VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, + + /** Proprietary Intel driver on Windows */ + IntelProprietaryWindows = VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, + + /** Mesa LLVMpipe */ + MesaLlvmpipe = VK_DRIVER_ID_MESA_LLVMPIPE, + + /** Mesa RADV */ + MesaRadv = VK_DRIVER_ID_MESA_RADV, + + /** MoltenVK */ + MoltenVk = VK_DRIVER_ID_MOLTENVK, + + /** Proprietary NVidia */ + NVidiaProprietary = VK_DRIVER_ID_NVIDIA_PROPRIETARY, + + /** Proprietary Qualcomm */ + QualcommProprietary = VK_DRIVER_ID_QUALCOMM_PROPRIETARY, +}; + +/** +@debugoperatorclassenum{DeviceProperties,DeviceDriver} +@m_since_latest +*/ +MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, DeviceDriver value); + /** @brief Queue flag @m_since_latest @@ -236,11 +305,19 @@ class MAGNUM_VK_EXPORT DeviceProperties { /** * @brief Raw device properties * - * Populated lazily on first request. If Vulkan 1.1 or the - * @vk_extension{KHR,get_physical_device_properties2} extension is not - * enabled on the originating instance, only the Vulkan 1.0 subset of - * device properties is queried, with the `pNext` member being - * @cpp nullptr @ce. + * Populated lazily on first request. If Vulkan 1.1 is not supported + * and the @vk_extension{KHR,get_physical_device_properties2} extension + * is not enabled on the originating instance, only the Vulkan 1.0 + * subset of device properties is queried, with the `pNext` member + * being @cpp nullptr @ce. Otherwise: + * + * - If Vulkan 1.2 is supported or the + * @vk_extension{KHR,driver_properties} extension is supported by + * the device, the `pNext` chain contains a + * @type_vk{PhysicalDeviceDriverProperties} structure and the + * @ref driver(), @ref driverName() and @ref driverInfo() + * properties are populated. + * * @see @fn_vk_keyword{GetPhysicalDeviceProperties2}, * @fn_vk_keyword{GetPhysicalDeviceProperties} */ @@ -256,6 +333,37 @@ class MAGNUM_VK_EXPORT DeviceProperties { return Version(properties().properties.apiVersion); } + /** + * @brief Device type + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + DeviceType type() { + return DeviceType(properties().properties.deviceType); + } + + /** + * @brief Device name + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. + */ + Containers::StringView name(); + + /** + * @brief Driver ID + * + * Convenience access to @ref properties() internals, populated lazily + * on first request. Only present if Vulkan 1.2 is supported or the + * @vk_extension{KHR,driver_properties} extension is supported by the + * device, otherwise returns @ref DeviceDriver::Unknown. Note that + * there might be driver IDs not yet listed in the @ref DeviceDriver + * enum, moreover drivers are allowed to return unregistered IDs as + * well. + */ + DeviceDriver driver(); + /** * @brief Driver version * @@ -267,22 +375,24 @@ class MAGNUM_VK_EXPORT DeviceProperties { } /** - * @brief Device type + * @brief Driver name * * Convenience access to @ref properties() internals, populated lazily - * on first request. + * on first request. Only present if Vulkan 1.2 is supported or the + * @vk_extension{KHR,driver_properties} extension is supported by the + * device, otherwise returns an empty string. */ - DeviceType type() { - return DeviceType(properties().properties.deviceType); - } + Containers::StringView driverName(); /** - * @brief Device name + * @brief Driver info * * Convenience access to @ref properties() internals, populated lazily - * on first request. + * on first request. Only present if Vulkan 1.2 is supported or the + * @vk_extension{KHR,driver_properties} extension is supported by the + * device, otherwise returns an empty string. */ - Containers::StringView name(); + Containers::StringView driverInfo(); /** * @brief Enumerate device extensions diff --git a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp index 72f09eefcb..cdf66a460f 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesTest.cpp @@ -38,6 +38,7 @@ struct DevicePropertiesTest: TestSuite::Tester { void constructCopy(); void debugDeviceType(); + void debugDeviceDriver(); void debugQueueFamilyPropertiesFlag(); void debugQueueFamilyPropertiesFlags(); void debugMemoryHeapFlag(); @@ -49,6 +50,7 @@ DevicePropertiesTest::DevicePropertiesTest() { &DevicePropertiesTest::constructCopy, &DevicePropertiesTest::debugDeviceType, + &DevicePropertiesTest::debugDeviceDriver, &DevicePropertiesTest::debugQueueFamilyPropertiesFlag, &DevicePropertiesTest::debugQueueFamilyPropertiesFlags, &DevicePropertiesTest::debugMemoryHeapFlag, @@ -76,6 +78,12 @@ void DevicePropertiesTest::debugDeviceType() { CORRADE_COMPARE(out.str(), "Vk::DeviceType::DiscreteGpu Vk::DeviceType(-10007655)\n"); } +void DevicePropertiesTest::debugDeviceDriver() { + std::ostringstream out; + Debug{&out} << DeviceDriver::MesaLlvmpipe << DeviceDriver(-10007655); + CORRADE_COMPARE(out.str(), "Vk::DeviceDriver::MesaLlvmpipe Vk::DeviceDriver(-10007655)\n"); +} + void DevicePropertiesTest::debugQueueFamilyPropertiesFlag() { std::ostringstream out; Debug{&out} << QueueFlag::SparseBinding << QueueFlag(0xdeadcafe); diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 624f5a4334..0cfc40fe4e 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -61,6 +61,8 @@ struct DevicePropertiesVkTest: VulkanTester { void extensionIsSupported(); void extensionNamedRevision(); + void driverProperties(); + void queueFamilies(); void queueFamiliesOutOfRange(); void queueFamiliesPick(); @@ -107,6 +109,8 @@ DevicePropertiesVkTest::DevicePropertiesVkTest(): VulkanTester{NoCreate} { &DevicePropertiesVkTest::extensionIsSupported, &DevicePropertiesVkTest::extensionNamedRevision, + &DevicePropertiesVkTest::driverProperties, + &DevicePropertiesVkTest::queueFamilies, &DevicePropertiesVkTest::queueFamiliesOutOfRange, &DevicePropertiesVkTest::queueFamiliesPick, @@ -183,6 +187,29 @@ void DevicePropertiesVkTest::wrap() { CORRADE_COMPARE(wrapped.name(), devices[0].name()); } +void DevicePropertiesVkTest::driverProperties() { + Containers::Optional device = tryPickDevice(instance()); + CORRADE_VERIFY(device); + + Debug{} << "Driver ID:" << device->driver(); + + ExtensionProperties extensions = device->enumerateExtensionProperties(); + if(!instance().isExtensionEnabled() || !extensions.isSupported()) { + CORRADE_COMPARE(device->driver(), DeviceDriver::Unknown); + CORRADE_COMPARE(device->driverName(), ""); + CORRADE_COMPARE(device->driverInfo(), ""); + CORRADE_SKIP("KHR_driver_properties not supported."); + } + + CORRADE_VERIFY(Int(device->driver())); + CORRADE_VERIFY(!device->driverName().isEmpty()); + { + CORRADE_EXPECT_FAIL_IF(device->driver() == DeviceDriver::GoogleSwiftShader, + "SwiftShader reports empty driver info."); + CORRADE_VERIFY(!device->driverInfo().isEmpty()); + } +} + void DevicePropertiesVkTest::enumerateExtensions() { Containers::Array devices = enumerateDevices(instance()); CORRADE_VERIFY(!devices.empty()); diff --git a/src/Magnum/Vk/vk-info.cpp b/src/Magnum/Vk/vk-info.cpp index 98ae245d5f..77e1b84203 100644 --- a/src/Magnum/Vk/vk-info.cpp +++ b/src/Magnum/Vk/vk-info.cpp @@ -250,6 +250,19 @@ int main(int argc, char** argv) { const Vk::Version deviceVersion = device.apiVersion(); Debug{} << "Reported version:" << deviceVersion; + /* Driver info. Print only if the device actually reports something, + otherwise assume VK_KHR_driver_properties are not supported. Due to the + mess that's VK_KHR_get_physical_device_properties2 and the interactions + between instance and driver version it's not possible to easily check + for extension presence. */ + if(Int(device.driver())) { + Debug{} << "Driver:" << device.driverName() << "(" << Debug::nospace << device.driver() << Debug::nospace << ")"; + Debug{} << "Driver info:" << device.driverInfo() << "(version" << Debug::packed << device.driverVersion() << Debug::nospace << ")"; + + /* Otherwise just print an Unknown placeholder, indicating the extension + is not supported */ + } else Debug{} << "Driver:" << device.driver(); + std::size_t deviceFuture = 0; if(!args.isSet("all-extensions")) From 86267f1a78162dae836f7b8efb4fef5d71c9e43a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 14 Nov 2020 23:37:29 +0100 Subject: [PATCH 60/85] Vk: move MAGNUM_VK_INTERNAL_ASSER_RESULT() to a dedicated header. Similarly to Corrade Assert.h, so it's possible to include this header without having to worry about irrelevant overhead when asserts are disabled. Also test it properly, as it should have been from the start. --- src/Magnum/Vk/Assert.h | 83 ++++++++++++++++++++++ src/Magnum/Vk/CMakeLists.txt | 1 + src/Magnum/Vk/CommandBuffer.cpp | 2 +- src/Magnum/Vk/CommandPool.cpp | 2 +- src/Magnum/Vk/Device.cpp | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 2 +- src/Magnum/Vk/ExtensionProperties.cpp | 2 +- src/Magnum/Vk/Image.cpp | 2 +- src/Magnum/Vk/Instance.cpp | 2 +- src/Magnum/Vk/LayerProperties.cpp | 2 +- src/Magnum/Vk/Memory.cpp | 2 +- src/Magnum/Vk/Result.h | 41 +---------- src/Magnum/Vk/Test/AssertDisabledTest.cpp | 82 ++++++++++++++++++++++ src/Magnum/Vk/Test/AssertTest.cpp | 84 +++++++++++++++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 39 +++++++++++ src/Magnum/Vk/Test/ResultTest.cpp | 40 ++--------- src/Magnum/Vk/Version.cpp | 3 +- 17 files changed, 306 insertions(+), 85 deletions(-) create mode 100644 src/Magnum/Vk/Assert.h create mode 100644 src/Magnum/Vk/Test/AssertDisabledTest.cpp create mode 100644 src/Magnum/Vk/Test/AssertTest.cpp diff --git a/src/Magnum/Vk/Assert.h b/src/Magnum/Vk/Assert.h new file mode 100644 index 0000000000..dee0f9cf3b --- /dev/null +++ b/src/Magnum/Vk/Assert.h @@ -0,0 +1,83 @@ +#ifndef Magnum_Vk_Assert_h +#define Magnum_Vk_Assert_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Macro @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() + * @m_since_latest + */ + +#include +#include + +#include "Magnum/configure.h" + +#if !defined(CORRADE_NO_ASSERT) && !defined(MAGNUM_VK_INTERNAL_ASSERT_RESULT) +#ifndef CORRADE_STANDARD_ASSERT +#include +#include + +#include "Magnum/Vk/Result.h" +#elif !defined(NDEBUG) +#include +#endif +#endif + +/** +@brief Assert that a Vulkan function call succeeds +@m_since_latest + +Compared to using @ref CORRADE_INTERNAL_ASSERT_OUTPUT() to verify that +@cpp call == VK_SUCCESS @ce, this macro also prints the result value. Otherwise +the behavior is the same, including interactions with +@ref CORRADE_STANDARD_ASSERT and @ref CORRADE_NO_ASSERT. Works with both plain +Vulkan functions returning @type_vk{Result} and APIs returning +@ref Magnum::Vk::Result "Vk::Result". + +You can override this implementation by placing your own +@cpp #define MAGNUM_VK_INTERNAL_ASSERT_RESULT @ce before including the +@ref Magnum/Vk/Assert.h header. +*/ +#ifndef MAGNUM_VK_INTERNAL_ASSERT_RESULT +#if defined(CORRADE_NO_ASSERT) || (defined(CORRADE_STANDARD_ASSERT) && defined(NDEBUG)) +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + static_cast(call) +#elif defined(CORRADE_STANDARD_ASSERT) +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + assert(Magnum::Vk::Result(call) == Magnum::Vk::Result::Success) +#else +#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ + do { \ + const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ + if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success) { \ + Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << "Call " #call " failed with" << _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) << "at " __FILE__ ":" CORRADE_LINE_STRING; \ + std::abort(); \ + } \ + } while(false) +#endif +#endif + +#endif diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index a709e2c582..c7a6f0d843 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -49,6 +49,7 @@ set(MagnumVk_GracefulAssert_SRCS LayerProperties.cpp) set(MagnumVk_HEADERS + Assert.h CommandBuffer.h CommandPool.h Device.h diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp index ba563a636b..9f63075958 100644 --- a/src/Magnum/Vk/CommandBuffer.cpp +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -25,9 +25,9 @@ #include "CommandBuffer.h" +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" -#include "Magnum/Vk/Result.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp index 625ebc8413..de72a6fc66 100644 --- a/src/Magnum/Vk/CommandPool.cpp +++ b/src/Magnum/Vk/CommandPool.cpp @@ -25,10 +25,10 @@ #include "CommandPool.h" +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" -#include "Magnum/Vk/Result.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index ff5dfbb4ab..5005cb20a4 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -35,13 +35,13 @@ #include #include "Magnum/Math/Functions.h" +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Queue.h" -#include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index e6b0d27bc9..3b07bfae93 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -33,12 +33,12 @@ #include #include "Magnum/Math/Functions.h" +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Memory.h" -#include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" diff --git a/src/Magnum/Vk/ExtensionProperties.cpp b/src/Magnum/Vk/ExtensionProperties.cpp index 7cd760c125..2846761b12 100644 --- a/src/Magnum/Vk/ExtensionProperties.cpp +++ b/src/Magnum/Vk/ExtensionProperties.cpp @@ -30,8 +30,8 @@ #include #include +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Extensions.h" -#include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index 9757e351a7..224c36a60d 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -25,10 +25,10 @@ #include "Image.h" +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Integration.h" -#include "Magnum/Vk/Result.h" #include "Magnum/Vk/Implementation/DeviceState.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index 0a105d0ed9..3692005c66 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -32,10 +32,10 @@ #include #include +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Handle.h" -#include "Magnum/Vk/Result.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/Arguments.h" #include "Magnum/Vk/Implementation/InstanceState.h" diff --git a/src/Magnum/Vk/LayerProperties.cpp b/src/Magnum/Vk/LayerProperties.cpp index 717d16dd48..2faa3155da 100644 --- a/src/Magnum/Vk/LayerProperties.cpp +++ b/src/Magnum/Vk/LayerProperties.cpp @@ -28,7 +28,7 @@ #include #include -#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Assert.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 0eb299b4da..101ea42a04 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -28,9 +28,9 @@ #include #include +#include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" -#include "Magnum/Vk/Result.h" namespace Magnum { namespace Vk { diff --git a/src/Magnum/Vk/Result.h b/src/Magnum/Vk/Result.h index 9e1fc11492..4a82ca287e 100644 --- a/src/Magnum/Vk/Result.h +++ b/src/Magnum/Vk/Result.h @@ -26,13 +26,10 @@ */ /** @file - * @brief Enum @ref Magnum::Vk::Result, macro @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() + * @brief Enum @ref Magnum::Vk::Result * @m_since_latest */ -#include -#include - #include "Magnum/Magnum.h" #include "Magnum/Vk/visibility.h" #include "Magnum/Vk/Vulkan.h" @@ -45,7 +42,7 @@ namespace Magnum { namespace Vk { Wraps a @type_vk_keyword{Result}. @m_enum_values_as_keywords -@see @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() +@see @ref MAGNUM_VK_INTERNAL_ASSERT_SUCCESS() */ enum class Result: Int { /** Command successfully completed */ @@ -123,40 +120,6 @@ enum class Result: Int { */ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, Result value); -/** -@brief Assert that a Vulkan function call doesn't fail -@m_since_latest - -Compared to using @ref CORRADE_INTERNAL_ASSERT_OUTPUT() to verify that -@cpp call == VK_SUCCESS @ce, this macro also prints the result value. Otherwise -the behavior is the same, including interactions with -@ref CORRADE_STANDARD_ASSERT and @ref CORRADE_NO_ASSERT. Works with both plain -Vulkan functions returning @type_vk{Result} and APIs returning -@ref Magnum::Vk::Result "Vk::Result". - -You can override this implementation by placing your own -@cpp #define MAGNUM_VK_INTERNAL_ASSERT_RESULT @ce before including the -@ref Magnum/Vk/Result.h header. -*/ -#ifndef MAGNUM_VK_INTERNAL_ASSERT_RESULT -#if defined(CORRADE_NO_ASSERT) || (defined(CORRADE_STANDARD_ASSERT) && defined(NDEBUG)) -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ - static_cast(call) -#elif defined(CORRADE_STANDARD_ASSERT) -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ - assert(Magnum::Vk::Result(call) == Magnum::Vk::Result::Success) -#else -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ - do { \ - const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ - if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success) { \ - Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << "Call " #call " failed with" << _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) << "at " __FILE__ ":" CORRADE_LINE_STRING; \ - std::abort(); \ - } \ - } while(false) -#endif -#endif - }} #endif diff --git a/src/Magnum/Vk/Test/AssertDisabledTest.cpp b/src/Magnum/Vk/Test/AssertDisabledTest.cpp new file mode 100644 index 0000000000..3ea196b892 --- /dev/null +++ b/src/Magnum/Vk/Test/AssertDisabledTest.cpp @@ -0,0 +1,82 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include + +#ifndef CORRADE_STANDARD_ASSERT +#define CORRADE_NO_ASSERT +#endif + +#include +#include + +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct AssertDisabledTest: TestSuite::Tester { + explicit AssertDisabledTest(); + + void result(); + void vkResult(); +}; + +AssertDisabledTest::AssertDisabledTest() { + addTests({&AssertDisabledTest::result, + &AssertDisabledTest::vkResult}); + + #ifdef CORRADE_STANDARD_ASSERT + setTestName("Magum::Vk::Test::AssertStandardDisabledTest"); + #endif +} + +void AssertDisabledTest::result() { + std::ostringstream out; + Error redirectError{&out}; + + Result a = Result::ErrorUnknown; + Result r = Result::ErrorFragmentedPool; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + + CORRADE_COMPARE(a, Result::ErrorFragmentedPool); + CORRADE_COMPARE(out.str(), ""); +} + +void AssertDisabledTest::vkResult() { + std::ostringstream out; + Error redirectError{&out}; + + VkResult a = VK_ERROR_UNKNOWN; + VkResult r = VK_ERROR_FRAGMENTED_POOL; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + + CORRADE_COMPARE(Result(a), Result::ErrorFragmentedPool); + CORRADE_COMPARE(out.str(), ""); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::AssertDisabledTest) diff --git a/src/Magnum/Vk/Test/AssertTest.cpp b/src/Magnum/Vk/Test/AssertTest.cpp new file mode 100644 index 0000000000..33c1ab43fb --- /dev/null +++ b/src/Magnum/Vk/Test/AssertTest.cpp @@ -0,0 +1,84 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#ifdef CORRADE_STANDARD_ASSERT +#undef NDEBUG /* So we can test them */ +#endif + +#include +#include + +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Result.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct AssertTest: TestSuite::Tester { + explicit AssertTest(); + + void result(); + void vkResult(); + + bool _failAssertResult, _failAssertVkResult; +}; + +AssertTest::AssertTest(): TestSuite::Tester{TesterConfiguration{}.setSkippedArgumentPrefixes({"fail-on"})} { + addTests({&AssertTest::result, + &AssertTest::vkResult}); + + Utility::Arguments args{"fail-on"}; + args.addOption("assert-result", "false").setHelp("assert-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with Vk::Result", "BOOL") + .addOption("assert-vk-result", "false").setHelp("assert-vk-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with VkResult", "BOOL") + .parse(arguments().first, arguments().second); + + _failAssertResult = args.value("assert-result"); + _failAssertVkResult = args.value("assert-vk-result"); + + #ifdef CORRADE_STANDARD_ASSERT + setTestName("Magum::Vk::Test::AssertStandardTest"); + #endif +} + +void AssertTest::result() { + Result a = Result::ErrorUnknown; + + Result r = _failAssertResult ? Result::ErrorFragmentedPool : Result::Success; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + + CORRADE_COMPARE(a, Result::Success); +} + +void AssertTest::vkResult() { + VkResult a = VK_ERROR_UNKNOWN; + + VkResult s = _failAssertVkResult ? VK_ERROR_FRAGMENTED_POOL : VK_SUCCESS; + MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = s); + + CORRADE_COMPARE(Result(a), Result::Success); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::AssertTest) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 3e8bd42703..fd8563d7e1 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -38,6 +38,45 @@ corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) + +add_library(VkAssertTestObjects OBJECT AssertTest.cpp) +target_include_directories(VkAssertTestObjects PRIVATE $) +corrade_add_test(VkAssertTest + $ + ${PROJECT_SOURCE_DIR}/src/dummy.cpp + LIBRARIES MagnumVk) +# WILL_FAIL doesn't work for abort() on desktop, test this only on embedded +# then. Oh well. Also the tests could be just one executable added multiple +# times with different arguments, but corrade_add_test() doesn't support that, +# so I'm doing it at least via an OBJECT library. +if(CORRADE_TARGET_ANDROID) + corrade_add_test(VkAssertTestFailAssertResult + $ + ${PROJECT_SOURCE_DIR}/src/dummy.cpp + ARGUMENTS --fail-on-assert-result true + LIBRARIES MagnumVk) + set_tests_properties(VkAssertTestFailAssertResult PROPERTIES + PASS_REGULAR_EXPRESSION "Call a = r failed with Vk::Result::ErrorFragmentedPool at ") + corrade_add_test(VkAssertTestFailAssertVkResult + $ + ${PROJECT_SOURCE_DIR}/src/dummy.cpp + ARGUMENTS --fail-on-assert-vk-result true + LIBRARIES MagnumVk) + set_tests_properties(VkAssertTestFailAssertVkResult PROPERTIES + PASS_REGULAR_EXPRESSION "Call a = s failed with Vk::Result::ErrorFragmentedPool at ") +endif() + +# The same as above, but using CORRADE_STANDARD_ASSERT +corrade_add_test(VkAssertStandardTest AssertTest.cpp LIBRARIES MagnumVk) +target_compile_definitions(VkAssertStandardTest PRIVATE + "CORRADE_STANDARD_ASSERT") + +corrade_add_test(VkAssertDisabledTest AssertDisabledTest.cpp LIBRARIES MagnumVk) +# The same as above, but using CORRADE_STANDARD_ASSERT and NDEBUG +corrade_add_test(VkAssertStandardDisabledTest AssertDisabledTest.cpp LIBRARIES MagnumVk) +target_compile_definitions(VkAssertStandardDisabledTest PRIVATE + "CORRADE_STANDARD_ASSERT" "NDEBUG") + corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) diff --git a/src/Magnum/Vk/Test/ResultTest.cpp b/src/Magnum/Vk/Test/ResultTest.cpp index 2e200ca6c0..2e40969eb6 100644 --- a/src/Magnum/Vk/Test/ResultTest.cpp +++ b/src/Magnum/Vk/Test/ResultTest.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include "Magnum/Vk/Result.h" @@ -35,45 +34,14 @@ namespace Magnum { namespace Vk { namespace Test { namespace { struct ResultTest: TestSuite::Tester { explicit ResultTest(); - void assertResult(); - void assertVkResult(); + /* The assert macro is tested separately, as it needs a bunch of extra + handling */ void debug(); - - bool _failAssertResult, _failAssertVkResult; }; -ResultTest::ResultTest(): TestSuite::Tester{TesterConfiguration{}.setSkippedArgumentPrefixes({"fail-on"})} { - addTests({&ResultTest::assertResult, - &ResultTest::assertVkResult, - - &ResultTest::debug}); - - Utility::Arguments args{"fail-on"}; - args.addOption("assert-result", "false").setHelp("assert-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with Vk::Result", "BOOL") - .addOption("assert-vk-result", "false").setHelp("assert-vk-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with VkResult", "BOOL") - .parse(arguments().first, arguments().second); - - _failAssertResult = args.value("assert-result"); - _failAssertVkResult = args.value("assert-vk-result"); -} - -void ResultTest::assertResult() { - Result a = Result::ErrorUnknown; - - Result r = _failAssertResult ? Result::ErrorFragmentedPool : Result::Success; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); - - CORRADE_COMPARE(a, Result::Success); -} - -void ResultTest::assertVkResult() { - VkResult a = VK_ERROR_UNKNOWN; - - VkResult r = _failAssertVkResult ? VK_ERROR_FRAGMENTED_POOL : VK_SUCCESS; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); - - CORRADE_COMPARE(Result(a), Result::Success); +ResultTest::ResultTest() { + addTests({&ResultTest::debug}); } void ResultTest::debug() { diff --git a/src/Magnum/Vk/Version.cpp b/src/Magnum/Vk/Version.cpp index d189a5320a..b12ad699d4 100644 --- a/src/Magnum/Vk/Version.cpp +++ b/src/Magnum/Vk/Version.cpp @@ -29,7 +29,8 @@ #include #include -#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Vulkan.h" namespace Magnum { namespace Vk { From ed67135b56861ff531790de489aee33956b1bfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 15 Nov 2020 14:18:53 +0100 Subject: [PATCH 61/85] Vk: rename the *ASSERT_{RESULT => SUCCESS}(), add an incomplete variant. We'll need that to optimize some commands, such as device enumeration. --- src/Magnum/Vk/Assert.h | 53 ++++++++++++++++---- src/Magnum/Vk/CommandBuffer.cpp | 2 +- src/Magnum/Vk/CommandPool.cpp | 6 +-- src/Magnum/Vk/Device.cpp | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 4 +- src/Magnum/Vk/ExtensionProperties.cpp | 4 +- src/Magnum/Vk/Image.cpp | 2 +- src/Magnum/Vk/Instance.cpp | 2 +- src/Magnum/Vk/LayerProperties.cpp | 4 +- src/Magnum/Vk/Memory.cpp | 2 +- src/Magnum/Vk/Test/AssertDisabledTest.cpp | 48 ++++++++++++++---- src/Magnum/Vk/Test/AssertTest.cpp | 59 +++++++++++++++++------ src/Magnum/Vk/Test/CMakeLists.txt | 26 +++++++--- src/Magnum/Vk/Version.cpp | 2 +- 14 files changed, 161 insertions(+), 55 deletions(-) diff --git a/src/Magnum/Vk/Assert.h b/src/Magnum/Vk/Assert.h index dee0f9cf3b..a3f30856e4 100644 --- a/src/Magnum/Vk/Assert.h +++ b/src/Magnum/Vk/Assert.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Macro @ref MAGNUM_VK_INTERNAL_ASSERT_RESULT() + * @brief Macro @ref MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(), @ref MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE() * @m_since_latest */ @@ -35,7 +35,7 @@ #include "Magnum/configure.h" -#if !defined(CORRADE_NO_ASSERT) && !defined(MAGNUM_VK_INTERNAL_ASSERT_RESULT) +#if !defined(CORRADE_NO_ASSERT) && (!defined(MAGNUM_VK_INTERNAL_ASSERT_SUCCESS) || !defined(MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE)) #ifndef CORRADE_STANDARD_ASSERT #include #include @@ -58,21 +58,56 @@ Vulkan functions returning @type_vk{Result} and APIs returning @ref Magnum::Vk::Result "Vk::Result". You can override this implementation by placing your own -@cpp #define MAGNUM_VK_INTERNAL_ASSERT_RESULT @ce before including the +@cpp #define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS @ce before including the @ref Magnum/Vk/Assert.h header. +@see @ref MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE() */ -#ifndef MAGNUM_VK_INTERNAL_ASSERT_RESULT +#ifndef MAGNUM_VK_INTERNAL_ASSERT_SUCCESS #if defined(CORRADE_NO_ASSERT) || (defined(CORRADE_STANDARD_ASSERT) && defined(NDEBUG)) -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(call) \ static_cast(call) #elif defined(CORRADE_STANDARD_ASSERT) -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(call) \ assert(Magnum::Vk::Result(call) == Magnum::Vk::Result::Success) #else -#define MAGNUM_VK_INTERNAL_ASSERT_RESULT(call) \ +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(call) \ do { \ - const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ - if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success) { \ + const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ + if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success) { \ + Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << "Call " #call " failed with" << _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) << "at " __FILE__ ":" CORRADE_LINE_STRING; \ + std::abort(); \ + } \ + } while(false) +#endif +#endif + +/** +@brief Assert that a Vulkan function call succeeds or returns incomplete data +@m_since_latest + +A variant of @ref MAGNUM_VK_INTERNAL_ASSERT_SUCCESS() that allows the call to +return @ref Magnum::Vk::Result::Incomplete "Vk::Result::Incomplete" in addition +to @ref Magnum::Vk::Result::Success "Vk::Result::Success". + +You can override this implementation by placing your own +@cpp #define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE @ce before +including the @ref Magnum/Vk/Assert.h header. +*/ +#ifndef MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE +#if defined(CORRADE_NO_ASSERT) || (defined(CORRADE_STANDARD_ASSERT) && defined(NDEBUG)) +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(call) \ + static_cast(call) +#elif defined(CORRADE_STANDARD_ASSERT) +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(call) \ + do { \ + const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ + assert(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) == Magnum::Vk::Result::Success || _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) == Magnum::Vk::Result::Incomplete); \ + } while(false) +#else +#define MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(call) \ + do { \ + const Magnum::Vk::Result _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) = Magnum::Vk::Result(call); \ + if(_CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Success && _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) != Magnum::Vk::Result::Incomplete) { \ Corrade::Utility::Error{Corrade::Utility::Error::defaultOutput()} << "Call " #call " failed with" << _CORRADE_HELPER_PASTE(magnumVkResult, __LINE__) << "at " __FILE__ ":" CORRADE_LINE_STRING; \ std::abort(); \ } \ diff --git a/src/Magnum/Vk/CommandBuffer.cpp b/src/Magnum/Vk/CommandBuffer.cpp index 9f63075958..ffd92628ea 100644 --- a/src/Magnum/Vk/CommandBuffer.cpp +++ b/src/Magnum/Vk/CommandBuffer.cpp @@ -61,7 +61,7 @@ CommandBuffer& CommandBuffer::operator=(CommandBuffer&& other) noexcept { } void CommandBuffer::reset(const CommandBufferResetFlags flags) { - MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).ResetCommandBuffer(_handle, VkCommandBufferResetFlags(flags))); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).ResetCommandBuffer(_handle, VkCommandBufferResetFlags(flags))); } VkCommandBuffer CommandBuffer::release() { diff --git a/src/Magnum/Vk/CommandPool.cpp b/src/Magnum/Vk/CommandPool.cpp index de72a6fc66..3008f7bb24 100644 --- a/src/Magnum/Vk/CommandPool.cpp +++ b/src/Magnum/Vk/CommandPool.cpp @@ -54,7 +54,7 @@ CommandPool CommandPool::wrap(Device& device, const VkCommandPool handle, const } CommandPool::CommandPool(Device& device, const CommandPoolCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { - MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateCommandPool(device, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateCommandPool(device, info, nullptr, &_handle)); } CommandPool::CommandPool(NoCreateT) noexcept: _device{}, _handle{} {} @@ -87,13 +87,13 @@ CommandBuffer CommandPool::allocate(const CommandBufferLevel level) { info.commandPool = _handle; info.commandBufferCount = 1; info.level = VkCommandBufferLevel(level); - MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).AllocateCommandBuffers(*_device, &info, &out._handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).AllocateCommandBuffers(*_device, &info, &out._handle)); return out; } void CommandPool::reset(const CommandPoolResetFlags flags) { - MAGNUM_VK_INTERNAL_ASSERT_RESULT((**_device).ResetCommandPool(*_device, _handle, VkCommandPoolResetFlags(flags))); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).ResetCommandPool(*_device, _handle, VkCommandPoolResetFlags(flags))); } VkCommandPool CommandPool::release() { diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 5005cb20a4..f2834c65e1 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -331,7 +331,7 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info, DevicePropertie } } - MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->CreateDevice(info._physicalDevice, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->CreateDevice(info._physicalDevice, info, nullptr, &_handle)); initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); initialize(instance, version); diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 3b07bfae93..46b95f277d 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -318,12 +318,12 @@ Containers::Optional DeviceProperties::tryPickMemory(const MemoryFl Containers::Array enumerateDevices(Instance& instance) { /* Retrieve total device count */ UnsignedInt count; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->EnumeratePhysicalDevices(instance, &count, nullptr)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->EnumeratePhysicalDevices(instance, &count, nullptr)); /* Allocate memory for the output, fetch the handles into it */ Containers::Array out{Containers::NoInit, count}; Containers::ArrayView handles{reinterpret_cast(out.data()), count}; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(instance->EnumeratePhysicalDevices(instance, &count, handles.data())); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->EnumeratePhysicalDevices(instance, &count, handles.data())); /* Expect the device count didn't change between calls */ CORRADE_INTERNAL_ASSERT(count == out.size()); diff --git a/src/Magnum/Vk/ExtensionProperties.cpp b/src/Magnum/Vk/ExtensionProperties.cpp index 2846761b12..1fbc8c46b1 100644 --- a/src/Magnum/Vk/ExtensionProperties.cpp +++ b/src/Magnum/Vk/ExtensionProperties.cpp @@ -49,7 +49,7 @@ ExtensionProperties::ExtensionProperties(const Containers::ArrayView(_extensions.data()) + offset)); diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index 224c36a60d..c7ae90e095 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -75,7 +75,7 @@ Image Image::wrap(Device& device, const VkImage handle, const HandleFlags flags) } Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction}, _dedicatedMemory{NoCreate} { - MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->CreateImage(device, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateImage(device, info, nullptr, &_handle)); } Image::Image(NoCreateT): _device{}, _handle{}, _dedicatedMemory{NoCreate} {} diff --git a/src/Magnum/Vk/Instance.cpp b/src/Magnum/Vk/Instance.cpp index 3692005c66..1d0dd1a639 100644 --- a/src/Magnum/Vk/Instance.cpp +++ b/src/Magnum/Vk/Instance.cpp @@ -292,7 +292,7 @@ Instance::Instance(const InstanceCreateInfo& info): _flags{HandleFlag::DestroyOn } } - MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkCreateInstance(info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(vkCreateInstance(info, nullptr, &_handle)); initializeExtensions({info->ppEnabledExtensionNames, info->enabledExtensionCount}); if(info._state) diff --git a/src/Magnum/Vk/LayerProperties.cpp b/src/Magnum/Vk/LayerProperties.cpp index 2faa3155da..4f8efbaf7a 100644 --- a/src/Magnum/Vk/LayerProperties.cpp +++ b/src/Magnum/Vk/LayerProperties.cpp @@ -88,7 +88,7 @@ LayerProperties enumerateLayerProperties() { /* Retrieve layer count */ UnsignedInt count; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, nullptr)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(vkEnumerateInstanceLayerProperties(&count, nullptr)); /* No layers, nothing to do */ if(!count) return out; @@ -101,7 +101,7 @@ LayerProperties enumerateLayerProperties() { [](VkLayerProperties* data, std::size_t) { delete[] reinterpret_cast(data); }}; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceLayerProperties(&count, out._layers.data())); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(vkEnumerateInstanceLayerProperties(&count, out._layers.data())); /* Expect the layer count didn't change between calls */ CORRADE_INTERNAL_ASSERT(count == out._layers.size()); diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 101ea42a04..314ce807e1 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -67,7 +67,7 @@ Memory Memory::wrap(Device& device, const VkDeviceMemory handle, const HandleFla } Memory::Memory(Device& device, const MemoryAllocateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { - MAGNUM_VK_INTERNAL_ASSERT_RESULT(device->AllocateMemory(device, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->AllocateMemory(device, info, nullptr, &_handle)); } Memory::Memory(NoCreateT): _device{}, _handle{} {} diff --git a/src/Magnum/Vk/Test/AssertDisabledTest.cpp b/src/Magnum/Vk/Test/AssertDisabledTest.cpp index 3ea196b892..06805350a2 100644 --- a/src/Magnum/Vk/Test/AssertDisabledTest.cpp +++ b/src/Magnum/Vk/Test/AssertDisabledTest.cpp @@ -40,40 +40,68 @@ namespace Magnum { namespace Vk { namespace Test { namespace { struct AssertDisabledTest: TestSuite::Tester { explicit AssertDisabledTest(); - void result(); - void vkResult(); + void success(); + void successOrIncomplete(); + void vkSuccess(); + void vkSuccessOrIncomplete(); }; AssertDisabledTest::AssertDisabledTest() { - addTests({&AssertDisabledTest::result, - &AssertDisabledTest::vkResult}); + addTests({&AssertDisabledTest::success, + &AssertDisabledTest::successOrIncomplete, + &AssertDisabledTest::vkSuccess, + &AssertDisabledTest::vkSuccessOrIncomplete}); #ifdef CORRADE_STANDARD_ASSERT setTestName("Magum::Vk::Test::AssertStandardDisabledTest"); #endif } -void AssertDisabledTest::result() { +void AssertDisabledTest::success() { std::ostringstream out; Error redirectError{&out}; Result a = Result::ErrorUnknown; Result r = Result::ErrorFragmentedPool; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); - + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(a = r); CORRADE_COMPARE(a, Result::ErrorFragmentedPool); + + CORRADE_COMPARE(out.str(), ""); +} + +void AssertDisabledTest::successOrIncomplete() { + std::ostringstream out; + Error redirectError{&out}; + + Result a = Result::ErrorUnknown; + Result r = Result::ErrorExtensionNotPresent; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(a = r); + CORRADE_COMPARE(a, Result::ErrorExtensionNotPresent); + CORRADE_COMPARE(out.str(), ""); } -void AssertDisabledTest::vkResult() { +void AssertDisabledTest::vkSuccess() { std::ostringstream out; Error redirectError{&out}; VkResult a = VK_ERROR_UNKNOWN; VkResult r = VK_ERROR_FRAGMENTED_POOL; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); - + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(a = r); CORRADE_COMPARE(Result(a), Result::ErrorFragmentedPool); + + CORRADE_COMPARE(out.str(), ""); +} + +void AssertDisabledTest::vkSuccessOrIncomplete() { + std::ostringstream out; + Error redirectError{&out}; + + VkResult b = VK_ERROR_UNKNOWN; + VkResult s = VK_ERROR_EXTENSION_NOT_PRESENT; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(b = s); + CORRADE_COMPARE(Result(b), Result::ErrorExtensionNotPresent); + CORRADE_COMPARE(out.str(), ""); } diff --git a/src/Magnum/Vk/Test/AssertTest.cpp b/src/Magnum/Vk/Test/AssertTest.cpp index 33c1ab43fb..0add9ed651 100644 --- a/src/Magnum/Vk/Test/AssertTest.cpp +++ b/src/Magnum/Vk/Test/AssertTest.cpp @@ -38,47 +38,76 @@ namespace Magnum { namespace Vk { namespace Test { namespace { struct AssertTest: TestSuite::Tester { explicit AssertTest(); - void result(); - void vkResult(); + void success(); + void successOrIncomplete(); + void vkSuccess(); + void vkSuccessOrIncomplete(); - bool _failAssertResult, _failAssertVkResult; + bool _failAssertSuccess, _failAssertSuccessOrIncomplete, + _failAssertVkSuccess, _failAssertVkSuccessOrIncomplete; }; AssertTest::AssertTest(): TestSuite::Tester{TesterConfiguration{}.setSkippedArgumentPrefixes({"fail-on"})} { - addTests({&AssertTest::result, - &AssertTest::vkResult}); + addTests({&AssertTest::success, + &AssertTest::successOrIncomplete, + &AssertTest::vkSuccess, + &AssertTest::vkSuccessOrIncomplete}); Utility::Arguments args{"fail-on"}; - args.addOption("assert-result", "false").setHelp("assert-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with Vk::Result", "BOOL") - .addOption("assert-vk-result", "false").setHelp("assert-vk-result", "fail on MAGNUM_VK_INTERNAL_ASSERT_RESULT() with VkResult", "BOOL") + args.addOption("assert-success", "false").setHelp("assert-success", "fail on MAGNUM_VK_INTERNAL_ASSERT_SUCCESS() with Vk::Result", "BOOL") + .addOption("assert-success-or-incomplete", "false").setHelp("assert-success", "fail on MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE() with Vk::Result", "BOOL") + .addOption("assert-vk-success", "false").setHelp("assert-vk-success", "fail on MAGNUM_VK_INTERNAL_ASSERT_SUCCESS() with VkResult", "BOOL") + .addOption("assert-vk-success-or-incomplete", "false").setHelp("assert-vk-success-or-incomplete", "fail on MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE() with VkResult", "BOOL") .parse(arguments().first, arguments().second); - _failAssertResult = args.value("assert-result"); - _failAssertVkResult = args.value("assert-vk-result"); + _failAssertSuccess = args.value("assert-success"); + _failAssertSuccessOrIncomplete = args.value("assert-success-or-incomplete"); + _failAssertVkSuccess = args.value("assert-vk-success"); + _failAssertVkSuccessOrIncomplete = args.value("assert-vk-success-or-incomplete"); #ifdef CORRADE_STANDARD_ASSERT setTestName("Magum::Vk::Test::AssertStandardTest"); #endif } -void AssertTest::result() { +void AssertTest::success() { Result a = Result::ErrorUnknown; - Result r = _failAssertResult ? Result::ErrorFragmentedPool : Result::Success; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = r); + Result r = _failAssertSuccess ? Result::ErrorFragmentedPool : Result::Success; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(a = r); CORRADE_COMPARE(a, Result::Success); } -void AssertTest::vkResult() { +void AssertTest::successOrIncomplete() { + Result a = Result::ErrorUnknown; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(a = Result::Success); + + Result r = _failAssertSuccessOrIncomplete ? Result::ErrorExtensionNotPresent : Result::Incomplete; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(a = r); + + CORRADE_COMPARE(a, Result::Incomplete); +} + +void AssertTest::vkSuccess() { VkResult a = VK_ERROR_UNKNOWN; - VkResult s = _failAssertVkResult ? VK_ERROR_FRAGMENTED_POOL : VK_SUCCESS; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(a = s); + VkResult s = _failAssertVkSuccess ? VK_ERROR_FRAGMENTED_POOL : VK_SUCCESS; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(a = s); CORRADE_COMPARE(Result(a), Result::Success); } +void AssertTest::vkSuccessOrIncomplete() { + VkResult a = VK_ERROR_UNKNOWN; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(a = VK_SUCCESS); + + VkResult s = _failAssertVkSuccessOrIncomplete ? VK_ERROR_EXTENSION_NOT_PRESENT : VK_INCOMPLETE; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(a = s); + + CORRADE_COMPARE(Result(a), Result::Incomplete); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::AssertTest) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index fd8563d7e1..af459e9637 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -50,20 +50,34 @@ corrade_add_test(VkAssertTest # times with different arguments, but corrade_add_test() doesn't support that, # so I'm doing it at least via an OBJECT library. if(CORRADE_TARGET_ANDROID) - corrade_add_test(VkAssertTestFailAssertResult + corrade_add_test(VkAssertTestFailAssertSuccess $ ${PROJECT_SOURCE_DIR}/src/dummy.cpp - ARGUMENTS --fail-on-assert-result true + ARGUMENTS --fail-on-assert-success true LIBRARIES MagnumVk) - set_tests_properties(VkAssertTestFailAssertResult PROPERTIES + set_tests_properties(VkAssertTestFailAssertSuccess PROPERTIES PASS_REGULAR_EXPRESSION "Call a = r failed with Vk::Result::ErrorFragmentedPool at ") - corrade_add_test(VkAssertTestFailAssertVkResult + corrade_add_test(VkAssertTestFailAssertSucce___Incomplete $ ${PROJECT_SOURCE_DIR}/src/dummy.cpp - ARGUMENTS --fail-on-assert-vk-result true + ARGUMENTS --fail-on-assert-success-or-incomplete true LIBRARIES MagnumVk) - set_tests_properties(VkAssertTestFailAssertVkResult PROPERTIES + set_tests_properties(VkAssertTestFailAssertSucce___Incomplete PROPERTIES + PASS_REGULAR_EXPRESSION "Call a = r failed with Vk::Result::ErrorExtensionNotPresent at ") + corrade_add_test(VkAssertTestFailAssertVkSuccess + $ + ${PROJECT_SOURCE_DIR}/src/dummy.cpp + ARGUMENTS --fail-on-assert-vk-success true + LIBRARIES MagnumVk) + set_tests_properties(VkAssertTestFailAssertVkSuccess PROPERTIES PASS_REGULAR_EXPRESSION "Call a = s failed with Vk::Result::ErrorFragmentedPool at ") + corrade_add_test(VkAssertTestFailAssertVkSuc___Incomplete + $ + ${PROJECT_SOURCE_DIR}/src/dummy.cpp + ARGUMENTS --fail-on-assert-vk-success-or-incomplete true + LIBRARIES MagnumVk) + set_tests_properties(VkAssertTestFailAssertVkSuc___Incomplete PROPERTIES + PASS_REGULAR_EXPRESSION "Call a = s failed with Vk::Result::ErrorExtensionNotPresent at ") endif() # The same as above, but using CORRADE_STANDARD_ASSERT diff --git a/src/Magnum/Vk/Version.cpp b/src/Magnum/Vk/Version.cpp index b12ad699d4..cc478b06d8 100644 --- a/src/Magnum/Vk/Version.cpp +++ b/src/Magnum/Vk/Version.cpp @@ -47,7 +47,7 @@ Version enumerateInstanceVersion() { if(!vkEnumerateInstanceVersion) return Version::Vk10; UnsignedInt version; - MAGNUM_VK_INTERNAL_ASSERT_RESULT(vkEnumerateInstanceVersion(&version)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(vkEnumerateInstanceVersion(&version)); return Version(version); } From 32cfb94797159d8f69ca3dda7224dfec3bd1de8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 15 Nov 2020 20:03:26 +0100 Subject: [PATCH 62/85] Vk: enumerate only what's strictly necessary in pickDevice(). This avoids allocating a potentially large array in case just the first device is needed. Originally I did this in a hope to avoid a stall + CPU power management issues due to some bad shit in the AMD driver, but it seems that enumerating even just one device still makes it stall. Sigh. --- src/Magnum/Vk/DeviceProperties.cpp | 53 +++++++++++++++++++++--------- src/Magnum/Vk/DeviceProperties.h | 20 +++++++---- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 46b95f277d..40833869c7 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -315,23 +316,40 @@ Containers::Optional DeviceProperties::tryPickMemory(const MemoryFl return tryPickMemory(requiredFlags, {}, memories); } -Containers::Array enumerateDevices(Instance& instance) { - /* Retrieve total device count */ - UnsignedInt count; - MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->EnumeratePhysicalDevices(instance, &count, nullptr)); +/* Can't be inside an anonymous namespace as it's friended to DeviceProperties */ +namespace Implementation { +UnsignedInt enumerateDevicesInto(Instance& instance, Containers::ArrayView out) { /* Allocate memory for the output, fetch the handles into it */ - Containers::Array out{Containers::NoInit, count}; - Containers::ArrayView handles{reinterpret_cast(out.data()), count}; - MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->EnumeratePhysicalDevices(instance, &count, handles.data())); + Containers::ArrayView handles{reinterpret_cast(out.data()), out.size()}; + UnsignedInt count = out.size(); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS_OR_INCOMPLETE(instance->EnumeratePhysicalDevices(instance, &count, handles.data())); - /* Expect the device count didn't change between calls */ - CORRADE_INTERNAL_ASSERT(count == out.size()); + /* Expect the final count isn't larger than the output array */ + CORRADE_INTERNAL_ASSERT(count <= out.size()); /* Construct actual DeviceProperties instances from these, go backwards so we don't overwrite the not-yet-processed handles */ for(std::size_t i = count; i != 0; --i) new(out.data() + i - 1) DeviceProperties{instance, handles[i - 1]}; + /* Construct the remaining entries so the array destructor doesn't crash */ + for(std::size_t i = count; i != out.size(); ++i) + new(out.data() + i) DeviceProperties{NoCreate}; + + return count; +} + +} + +Containers::Array enumerateDevices(Instance& instance) { + /* Retrieve total device count */ + UnsignedInt count; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(instance->EnumeratePhysicalDevices(instance, &count, nullptr)); + + /* Fetch device handles, expect the device count didn't change between + calls */ + Containers::Array out{Containers::NoInit, count}; + CORRADE_INTERNAL_ASSERT_OUTPUT(Implementation::enumerateDevicesInto(instance, out) == out.size()); return out; } @@ -340,27 +358,32 @@ Containers::Optional tryPickDevice(Instance& instance) { Utility::Arguments args = Implementation::arguments(); args.parse(instance.state().argc, instance.state().argv); - Containers::Array devices = enumerateDevices(instance); - /* Pick the first by default */ if(args.value("device").empty()) { - if(devices.empty()) { + Containers::Array1 devices{Containers::NoInit}; + if(!Implementation::enumerateDevicesInto(instance, devices)) { Error{} << "Vk::tryPickDevice(): no Vulkan devices found"; return {}; } + return std::move(devices.front()); } /* Pick by ID */ if(args.value("device")[0] >= '0' && args.value("device")[0] <= '9') { - UnsignedInt id = args.value("device"); - if(id >= devices.size()) { - Error{} << "Vk::tryPickDevice(): index" << id << "out of bounds for" << devices.size() << "Vulkan devices"; + const UnsignedInt id = args.value("device"); + Containers::Array devices{Containers::NoInit, id + 1}; + const UnsignedInt count = Implementation::enumerateDevicesInto(instance, devices); + if(id >= count) { + Error{} << "Vk::tryPickDevice(): index" << id << "out of bounds for" << count << "Vulkan devices"; return {}; } + return std::move(devices[id]); } + Containers::Array devices = enumerateDevices(instance); + /* Pick by type */ DeviceType type; if(args.value("device") == "integrated") diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index ebd5f41652..e934f6befc 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -42,7 +42,10 @@ namespace Magnum { namespace Vk { -namespace Implementation { struct InstanceState; } +namespace Implementation { + struct InstanceState; + UnsignedInt enumerateDevicesInto(Instance& instance, Containers::ArrayView out); +} /** @brief Physical device type @@ -604,7 +607,7 @@ class MAGNUM_VK_EXPORT DeviceProperties { /* The DAMN THING lists this among friends, which is AN IMPLEMENTATION DETAIL */ friend DeviceCreateInfo; - friend MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance&); + friend UnsignedInt Implementation::enumerateDevicesInto(Instance&, Containers::ArrayView); #endif explicit DeviceProperties(Instance& instance, VkPhysicalDevice handle); @@ -644,10 +647,15 @@ MAGNUM_VK_EXPORT Containers::Array enumerateDevices(Instance& @brief Pick a physical device @m_since_latest -Calls @ref enumerateDevices() and selects a device based on preferences -specified through the `--magnum-device` @ref Vk-Instance-command-line "command-line option". -If a device is not found, exits. See @ref tryPickDevice() for an alternative -that doesn't exit on failure. See @ref Device for general usage information. +Selects a device based on preferences specified through the `--magnum-device` +@ref Vk-Instance-command-line "command-line option". If a device is not found, +exits. See @ref tryPickDevice() for an alternative that doesn't exit on +failure. See @ref Device for general usage information. + +If `--magnum-device` is not specified or `--magnum-device` specifies a device +index, this function enumerates just the first N devices to satisfy the +request. Otherwise calls @ref enumerateDevices() and picks the first device +matching the criteria in `--magnum-device`. */ MAGNUM_VK_EXPORT DeviceProperties pickDevice(Instance& instance); From 022ba3c29184fef4d7172a81928227491504ec3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 15 Nov 2020 23:03:07 +0100 Subject: [PATCH 63/85] Vk: rework device property fetching. Today I spent six hours wrongly convincing myself that it's a driver bug when vkGetPhysicalDeviceProperties2() is null on a 1.1 instance for a 1.0 physical device. It's not a bug, it's me not reading specs carefully. This commit thus basically moves all Instance-level extension-dependent state to DeviceProperties, because it's actually device-dependent. Which makes the DeviceProperties class quite heavy and thus it's good it was readied to be transferred all the way to a Device instance a few commits back -- I don't really want to do all the dispatch, string processing, sorting and other mess more times than strictly necessary. In addition, DeviceProperties::apiVersion() got renamed to version() and a new isVersionSupported() API got added, mirroring what's on Device itself; plus thanks to the chicken-and-egg problem of having to call vkGetPhysicalDeviceProperties() twice, the device version and other things can now be retrieved in a slightly more efficient way. --- src/Magnum/Vk/Device.cpp | 4 +- src/Magnum/Vk/Device.h | 2 +- src/Magnum/Vk/DeviceProperties.cpp | 125 ++++++++++++++++-- src/Magnum/Vk/DeviceProperties.h | 37 ++++-- .../Vk/Implementation/InstanceState.cpp | 20 +-- src/Magnum/Vk/Implementation/InstanceState.h | 8 +- src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp | 10 +- src/Magnum/Vk/Test/DeviceVkTest.cpp | 24 ++-- src/Magnum/Vk/vk-info.cpp | 4 +- 9 files changed, 167 insertions(+), 67 deletions(-) diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index f2834c65e1..20b26f70f7 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -83,7 +83,7 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext /* Take the minimum of instance and device version. Instance version being smaller than a device version happens mainly if there's a forced Vulkan version via --magnum-vulkan version, which will be later used to cap available features. */ - _state->version = Version(Math::min(UnsignedInt(deviceProperties._instance->version()), UnsignedInt(deviceProperties.apiVersion()))); + _state->version = Version(Math::min(UnsignedInt(deviceProperties._instance->version()), UnsignedInt(deviceProperties.version()))); /* If there are any disabled extensions, sort them and save for later -- we'll use them to filter the ones added by the app */ @@ -317,7 +317,7 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info, DevicePropertie _properties.emplace(DeviceProperties::wrap(instance, info._physicalDevice)); const Version version = info._state->version != Version::None ? - info._state->version : _properties->apiVersion(); + info._state->version : _properties->version(); /* Print all enabled extensions if we're not told to be quiet */ if(!info._state || !info._state->quietLog) { diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 1613b56abc..ca2104cbd4 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -462,7 +462,7 @@ class MAGNUM_VK_EXPORT Device { * * Unless overriden using `--magnum-vulkan-version` on the * @ref Vk-Device-command-line "command line", corresponds to - * @ref DeviceProperties::apiVersion(). + * @ref DeviceProperties::version(). */ Version version() const { return _version; } diff --git a/src/Magnum/Vk/DeviceProperties.cpp b/src/Magnum/Vk/DeviceProperties.cpp index 40833869c7..9a5e28d861 100644 --- a/src/Magnum/Vk/DeviceProperties.cpp +++ b/src/Magnum/Vk/DeviceProperties.cpp @@ -47,12 +47,88 @@ namespace Magnum { namespace Vk { struct DeviceProperties::State { + explicit State(Instance& instance, VkPhysicalDevice handle); + + /* Cached device extension properties to dispatch on when querying + properties. Should be only used through + DeviceProperties::extensionPropertiesInternal(). */ + Containers::Optional extensions; + + void(*getPropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceProperties2&); + void(*getQueueFamilyPropertiesImplementation)(DeviceProperties&, UnsignedInt&, VkQueueFamilyProperties2*); + void(*getMemoryPropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceMemoryProperties2&); + VkPhysicalDeviceProperties2 properties{}; VkPhysicalDeviceDriverProperties driverProperties{}; VkPhysicalDeviceMemoryProperties2 memoryProperties{}; Containers::Array queueFamilyProperties; }; +DeviceProperties::State::State(Instance& instance, const VkPhysicalDevice handle) { + /* All this extension-dependent dispatch has to be stored per physical + device, not just on instance, because it's actually instance-level + functionality depending on a version of a particular device. According + to https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap3.html#fundamentals-validusage-versions : + + Physical-device-level functionality or behavior added by a new core + version of the API must not be used unless it is supported by the + physical device as determined by VkPhysicalDeviceProperties::apiVersion + and the specified version of VkApplicationInfo::apiVersion. + + and https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap4.html#_extending_physical_device_core_functionality : + + New core physical-device-level functionality can be used when the + physical-device version is greater than or equal to the version of + Vulkan that added the new functionality. The Vulkan version supported + by a physical device can be obtained by calling + vkGetPhysicalDeviceProperties. + + and https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap4.html#initialization-phys-dev-extensions : + + Applications must not use a VkPhysicalDevice in any command added by an + extension or core version that is not supported by that physical + device. + + Which means for example, if Vulkan 1.1 is supported by the instance, it + doesn't actually imply I can use vkGetPhysicalDeviceProperties2() -- I + can only use that in case the device supports 1.1 as well, which means I + have to call vkGetPhysicalDeviceProperties() first in order to be able + to call vkGetPhysicalDeviceProperties2(). + + On the other hand, if the device is 1.0 but the instance + supports VK_KHR_get_physical_device_properties2, I can call + vkGetPhysicalDeviceProperties2KHR() directly -- https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap4.html#initialization-phys-dev-extensions : + + When the VK_KHR_get_physical_device_properties2 extension is enabled, + or when both the instance and the physical-device versions are at least + 1.1, physical-device-level functionality of a device extension can be + used with a physical device if the corresponding extension is + enumerated by vkEnumerateDeviceExtensionProperties for that physical + device, even before a logical device has been created. + + This also explains why e.g. VK_KHR_driver_properties is a device + extension and not instance extension -- I can only add it to the pNext + chain if the device is able to understand it, even though it's shoveled + there by an instance-level API. */ + + instance->GetPhysicalDeviceProperties(handle, &properties.properties); + + /* Have to check both the instance and device version, see above */ + if(instance.isVersionSupported(Version::Vk11) && Version(properties.properties.apiVersion) >= Version::Vk11) { + getPropertiesImplementation = &DeviceProperties::getPropertiesImplementation11; + getQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementation11; + getMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementation11; + } else if(instance.isExtensionEnabled()) { + getPropertiesImplementation = &DeviceProperties::getPropertiesImplementationKHR; + getQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationKHR; + getMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationKHR; + } else { + getPropertiesImplementation = DeviceProperties::getPropertiesImplementationDefault; + getQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationDefault; + getMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationDefault; + } +} + DeviceProperties::DeviceProperties(NoCreateT) noexcept: _instance{}, _handle{} {} DeviceProperties::DeviceProperties(Instance& instance, VkPhysicalDevice handle): _instance{&instance}, _handle{handle} {} @@ -65,8 +141,20 @@ DeviceProperties::~DeviceProperties() = default; DeviceProperties& DeviceProperties::operator=(DeviceProperties&&) noexcept = default; +Version DeviceProperties::version() { + return Version(properties1().apiVersion); +} + +bool DeviceProperties::isVersionSupported(const Version version) { + return Version(properties1().apiVersion) >= version; +} + +DeviceType DeviceProperties::type() { + return DeviceType(properties1().deviceType); +} + Containers::StringView DeviceProperties::name() { - return properties().properties.deviceName; + return properties1().deviceName; } DeviceDriver DeviceProperties::driver() { @@ -74,6 +162,10 @@ DeviceDriver DeviceProperties::driver() { return properties(), DeviceDriver(_state->driverProperties.driverID); } +Version DeviceProperties::driverVersion() { + return Version(properties1().driverVersion); +} + Containers::StringView DeviceProperties::driverName() { /* Ensure the values are populated first */ return properties(), _state->driverProperties.driverName; @@ -84,8 +176,14 @@ Containers::StringView DeviceProperties::driverInfo() { return properties(), _state->driverProperties.driverInfo; } +const VkPhysicalDeviceProperties& DeviceProperties::properties1() { + if(!_state) _state.emplace(*_instance, _handle); + + return _state->properties.properties; +} + const VkPhysicalDeviceProperties2& DeviceProperties::properties() { - if(!_state) _state.emplace(); + if(!_state) _state.emplace(*_instance, _handle); /* Properties not fetched yet, do that now */ if(!_state->properties.sType) { @@ -93,17 +191,14 @@ const VkPhysicalDeviceProperties2& DeviceProperties::properties() { Containers::Reference next = _state->properties.pNext; - /* Device extension properties. Populated only if needed. */ - ExtensionProperties extensions{NoCreate}; - /* Fetch driver properties, if supported */ - if(_instance->isVersionSupported(Version::Vk12) || (extensions = enumerateExtensionProperties()).isSupported()) { + if(isVersionSupported(Version::Vk12) || extensionPropertiesInternal().isSupported()) { *next = &_state->driverProperties; next = _state->driverProperties.pNext; _state->driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; } - _instance->state().getPhysicalDevicePropertiesImplementation(*this, _state->properties); + _state->getPropertiesImplementation(*this, _state->properties); } return _state->properties; @@ -132,18 +227,24 @@ ExtensionProperties DeviceProperties::enumerateExtensionProperties(std::initiali return enumerateExtensionProperties(Containers::arrayView(layers)); } +const ExtensionProperties& DeviceProperties::extensionPropertiesInternal() { + if(!_state) _state.emplace(*_instance, _handle); + if(!_state->extensions) _state->extensions = enumerateExtensionProperties(); + return *_state->extensions; +} + Containers::ArrayView DeviceProperties::queueFamilyProperties() { - if(!_state) _state.emplace(); + if(!_state) _state.emplace(*_instance, _handle); /* Fetch if not already */ if(_state->queueFamilyProperties.empty()) { UnsignedInt count; - _instance->state().getPhysicalDeviceQueueFamilyPropertiesImplementation(*this, count, nullptr); + _state->getQueueFamilyPropertiesImplementation(*this, count, nullptr); _state->queueFamilyProperties = Containers::Array{Containers::ValueInit, count}; for(VkQueueFamilyProperties2& i: _state->queueFamilyProperties) i.sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; - _instance->state().getPhysicalDeviceQueueFamilyPropertiesImplementation(*this, count, _state->queueFamilyProperties); + _state->getQueueFamilyPropertiesImplementation(*this, count, _state->queueFamilyProperties); CORRADE_INTERNAL_ASSERT(count == _state->queueFamilyProperties.size()); } @@ -211,11 +312,11 @@ Containers::Optional DeviceProperties::tryPickQueueFamily(const Que } const VkPhysicalDeviceMemoryProperties2& DeviceProperties::memoryProperties() { - if(!_state) _state.emplace(); + if(!_state) _state.emplace(*_instance, _handle); if(!_state->memoryProperties.sType) { _state->memoryProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2; - _instance->state().getPhysicalDeviceMemoryPropertiesImplementation(*this, _state->memoryProperties); + _state->getMemoryPropertiesImplementation(*this, _state->memoryProperties); } return _state->memoryProperties; diff --git a/src/Magnum/Vk/DeviceProperties.h b/src/Magnum/Vk/DeviceProperties.h index e934f6befc..101fab0b3c 100644 --- a/src/Magnum/Vk/DeviceProperties.h +++ b/src/Magnum/Vk/DeviceProperties.h @@ -327,14 +327,19 @@ class MAGNUM_VK_EXPORT DeviceProperties { const VkPhysicalDeviceProperties2& properties(); /** - * @brief API version + * @brief Device version * * Convenience access to @ref properties() internals, populated lazily * on first request. */ - Version apiVersion() { - return Version(properties().properties.apiVersion); - } + Version version(); + + /** + * @brief Whether given version is supported on the device + * + * Compares @p version against @ref version(). + */ + bool isVersionSupported(Version version); /** * @brief Device type @@ -342,9 +347,7 @@ class MAGNUM_VK_EXPORT DeviceProperties { * Convenience access to @ref properties() internals, populated lazily * on first request. */ - DeviceType type() { - return DeviceType(properties().properties.deviceType); - } + DeviceType type(); /** * @brief Device name @@ -373,9 +376,7 @@ class MAGNUM_VK_EXPORT DeviceProperties { * Convenience access to @ref properties() internals, populated lazily * on first request. */ - Version driverVersion() { - return Version(properties().properties.driverVersion); - } + Version driverVersion(); /** * @brief Driver name @@ -612,6 +613,22 @@ class MAGNUM_VK_EXPORT DeviceProperties { explicit DeviceProperties(Instance& instance, VkPhysicalDevice handle); + /* The vkPhysicalDeviceProperties() function has to get called in any + case to decide if vkPhysicalDeviceProperties2() can get called. That + means we can get the VkPhysicalDeviceProperties subset in a cheaper + way than when going through properties(). This distinction however + would only confuse the users, so it's a private API and while + version(), name(), driverVersion() etc. queries use it they only + mention properties() in the public docs. */ + MAGNUM_VK_LOCAL const VkPhysicalDeviceProperties& properties1(); + + /* Cached version of enumerateExtensionProperties() for internal use by + the property query functions. The enumerateExtensionProperties() API + returns by-value because every time there can be a different set of + layers, so we can't do any caching on that level but need a separate + function. */ + MAGNUM_VK_LOCAL const ExtensionProperties& extensionPropertiesInternal(); + MAGNUM_VK_LOCAL static void getPropertiesImplementationDefault(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); MAGNUM_VK_LOCAL static void getPropertiesImplementationKHR(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); MAGNUM_VK_LOCAL static void getPropertiesImplementation11(DeviceProperties& self, VkPhysicalDeviceProperties2& properties); diff --git a/src/Magnum/Vk/Implementation/InstanceState.cpp b/src/Magnum/Vk/Implementation/InstanceState.cpp index 75449384c1..ffd0c0337d 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.cpp +++ b/src/Magnum/Vk/Implementation/InstanceState.cpp @@ -25,26 +25,8 @@ #include "InstanceState.h" -#include "Magnum/Vk/Extensions.h" -#include "Magnum/Vk/Instance.h" -#include "Magnum/Vk/DeviceProperties.h" - namespace Magnum { namespace Vk { namespace Implementation { -InstanceState::InstanceState(Instance& instance, Int argc, const char** argv): argc{argc}, argv{argv} { - if(instance.isVersionSupported(Version::Vk11)) { - getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementation11; - getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementation11; - getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementation11; - } else if(instance.isExtensionEnabled()) { - getPhysicalDevicePropertiesImplementation = &DeviceProperties::getPropertiesImplementationKHR; - getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationKHR; - getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationKHR; - } else { - getPhysicalDevicePropertiesImplementation = DeviceProperties::getPropertiesImplementationDefault; - getPhysicalDeviceQueueFamilyPropertiesImplementation = &DeviceProperties::getQueueFamilyPropertiesImplementationDefault; - getPhysicalDeviceMemoryPropertiesImplementation = &DeviceProperties::getMemoryPropertiesImplementationDefault; - } -} +InstanceState::InstanceState(Instance&, Int argc, const char** argv): argc{argc}, argv{argv} {} }}} diff --git a/src/Magnum/Vk/Implementation/InstanceState.h b/src/Magnum/Vk/Implementation/InstanceState.h index b6821ea7f4..9ab7c763ab 100644 --- a/src/Magnum/Vk/Implementation/InstanceState.h +++ b/src/Magnum/Vk/Implementation/InstanceState.h @@ -26,7 +26,6 @@ */ #include "Magnum/Vk/Vk.h" -#include "Magnum/Vk/Vulkan.h" namespace Magnum { namespace Vk { namespace Implementation { @@ -36,9 +35,10 @@ struct InstanceState { Int argc; const char** argv; - void(*getPhysicalDevicePropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceProperties2&); - void(*getPhysicalDeviceQueueFamilyPropertiesImplementation)(DeviceProperties&, UnsignedInt&, VkQueueFamilyProperties2*); - void(*getPhysicalDeviceMemoryPropertiesImplementation)(DeviceProperties&, VkPhysicalDeviceMemoryProperties2&); + /* There are currently no instance-level extension-dependent code paths. + Everything is currently in DeviceProperties internals, as + physical-device-level APIs depend on device version, not instance + version, and thus have to be wired up differently for each device. */ }; }}} diff --git a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp index 0cfc40fe4e..5bc3d42e40 100644 --- a/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp +++ b/src/Magnum/Vk/Test/DevicePropertiesVkTest.cpp @@ -142,8 +142,11 @@ void DevicePropertiesVkTest::enumerate() { CORRADE_ITERATION(device.name()); CORRADE_VERIFY(device.handle()); - CORRADE_COMPARE_AS(device.apiVersion(), Version::Vk10, + CORRADE_COMPARE_AS(device.version(), Version::Vk10, TestSuite::Compare::GreaterOrEqual); + /* Device version is supported */ + CORRADE_VERIFY(device.isVersionSupported(device.version())); + CORRADE_VERIFY(!device.isVersionSupported(Version::None)); CORRADE_COMPARE_AS(device.driverVersion(), Version::Vk10, TestSuite::Compare::GreaterOrEqual); CORRADE_VERIFY(device.type() != DeviceType::Other); @@ -193,15 +196,12 @@ void DevicePropertiesVkTest::driverProperties() { Debug{} << "Driver ID:" << device->driver(); - ExtensionProperties extensions = device->enumerateExtensionProperties(); - if(!instance().isExtensionEnabled() || !extensions.isSupported()) { - CORRADE_COMPARE(device->driver(), DeviceDriver::Unknown); + if(device->driver() == DeviceDriver::Unknown) { CORRADE_COMPARE(device->driverName(), ""); CORRADE_COMPARE(device->driverInfo(), ""); CORRADE_SKIP("KHR_driver_properties not supported."); } - CORRADE_VERIFY(Int(device->driver())); CORRADE_VERIFY(!device->driverName().isEmpty()); { CORRADE_EXPECT_FAIL_IF(device->driver() == DeviceDriver::GoogleSwiftShader, diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index b6c8d43ba3..0c42769401 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -284,9 +284,9 @@ void DeviceVkTest::construct() { /* Device function pointers should be populated */ CORRADE_VERIFY(device->CreateBuffer); CORRADE_COMPARE(device.handleFlags(), HandleFlag::DestroyOnDestruction); - CORRADE_COMPARE(device.version(), deviceProperties.apiVersion()); + CORRADE_COMPARE(device.version(), deviceProperties.version()); /* Device version is supported */ - CORRADE_VERIFY(device.isVersionSupported(deviceProperties.apiVersion())); + CORRADE_VERIFY(device.isVersionSupported(deviceProperties.version())); CORRADE_VERIFY(!device.isVersionSupported(Version::None)); /* No extensions are enabled by default ... */ CORRADE_VERIFY(!device.isExtensionEnabled()); @@ -397,14 +397,14 @@ void DeviceVkTest::constructExtensionsCommandLineDisable() { Extensions::KHR::maintenance1 >()}; CORRADE_VERIFY(device.handle()); - CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); + CORRADE_COMPARE(device.isVersionSupported(deviceProperties.version()), data.driverVersionSupported); CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); /** @todo cleanup when Debug::toString() or some similar utility exists */ - UnsignedInt major = versionMajor(deviceProperties.apiVersion()); - UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); - UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); + UnsignedInt major = versionMajor(deviceProperties.version()); + UnsignedInt minor = versionMinor(deviceProperties.version()); + UnsignedInt patch = versionPatch(deviceProperties.version()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ CORRADE_COMPARE(out.str(), Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "")); @@ -451,14 +451,14 @@ void DeviceVkTest::constructExtensionsCommandLineEnable() { /* Nothing enabled by the application */ }; CORRADE_VERIFY(device.handle()); - CORRADE_COMPARE(device.isVersionSupported(deviceProperties.apiVersion()), data.driverVersionSupported); + CORRADE_COMPARE(device.isVersionSupported(deviceProperties.version()), data.driverVersionSupported); CORRADE_COMPARE(device.isExtensionEnabled(), data.debugMarkerEnabled); CORRADE_COMPARE(device.isExtensionEnabled(), data.maintenance1Enabled); /** @todo cleanup when Debug::toString() or some similar utility exists */ - UnsignedInt major = versionMajor(deviceProperties.apiVersion()); - UnsignedInt minor = versionMinor(deviceProperties.apiVersion()); - UnsignedInt patch = versionPatch(deviceProperties.apiVersion()); + UnsignedInt major = versionMajor(deviceProperties.version()); + UnsignedInt minor = versionMinor(deviceProperties.version()); + UnsignedInt patch = versionPatch(deviceProperties.version()); /* SwiftShader reports just 1.1 with no patch version, special-case that */ CORRADE_COMPARE(out.str(), Utility::formatString(data.log, deviceProperties.name(), major, minor, patch ? Utility::formatString(".{}", patch) : "")); @@ -575,7 +575,7 @@ void DeviceVkTest::constructMove() { CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.version(), version); - CORRADE_COMPARE(b.properties().apiVersion(), version); + CORRADE_COMPARE(b.properties().version(), version); CORRADE_VERIFY(b.isExtensionEnabled()); /* Function pointers in a are left in whatever state they were before, as that doesn't matter */ @@ -588,7 +588,7 @@ void DeviceVkTest::constructMove() { CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.version(), version); - CORRADE_COMPARE(c.properties().apiVersion(), version); + CORRADE_COMPARE(c.properties().version(), version); CORRADE_VERIFY(c.isExtensionEnabled()); /* Everything is swapped, including function pointers */ CORRADE_VERIFY(!b->CreateBuffer); diff --git a/src/Magnum/Vk/vk-info.cpp b/src/Magnum/Vk/vk-info.cpp index 77e1b84203..dbad22f641 100644 --- a/src/Magnum/Vk/vk-info.cpp +++ b/src/Magnum/Vk/vk-info.cpp @@ -233,7 +233,7 @@ int main(int argc, char** argv) { Debug{} << "Found" << devices.size() << "devices:"; for(Vk::DeviceProperties& device: devices) { Debug{} << " " << device.name() << Debug::nospace << "," - << device.apiVersion() << Debug::newline + << device.version() << Debug::newline << " " << device.type() << Debug::nospace << ", driver" << Debug::packed << device.driverVersion(); } @@ -247,7 +247,7 @@ int main(int argc, char** argv) { Debug{} << "Picked device" << device.name() << Debug::newline; - const Vk::Version deviceVersion = device.apiVersion(); + const Vk::Version deviceVersion = device.version(); Debug{} << "Reported version:" << deviceVersion; /* Driver info. Print only if the device actually reports something, From c6763ba195f833837f214d91f641630fd8cb26e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 13:40:48 +0100 Subject: [PATCH 64/85] Vk: XFAIL image memory requirements test on a SwiftShader. For some reason it wants me to allocate 16 bytes more. Why can't that be stored somewhere else, I wonder? Hm, and for this I implemented VK_KHR_driver_properties only to discover that the info is not queryable if we run the tests with KHR_get_physical_device_properties2 disabled. Sigh. --- src/Magnum/Vk/Test/ImageVkTest.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index db840a7070..57763a1863 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -23,6 +23,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include "Magnum/Vk/DeviceProperties.h" @@ -213,7 +214,15 @@ void ImageVkTest::memoryRequirements() { Image image{device(), info, NoAllocate}; MemoryRequirements requirements = image.memoryRequirements(); - CORRADE_COMPARE(requirements.size(), 128*64*4); + { + /* Can't use device().properties().driver() == + DeviceDriver::GoogleSwiftShader because that information is not + available when we run with KHR_get_physical_device_properties2 + disabled :/ */ + CORRADE_EXPECT_FAIL_IF(device().properties().name().hasPrefix("SwiftShader") && requirements.size() == 128*64*4 + 16, + "SwiftShader reports 16 bytes (two pointers?) more than expected for a linear tiling."); + CORRADE_COMPARE(requirements.size(), 128*64*4); + } } void ImageVkTest::bindMemory() { From e725528b5a1f7e84c0bc1fc5484628ff44bd28b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 16:12:12 +0100 Subject: [PATCH 65/85] Vk: ability to allocate Image directly during construction. --- src/Magnum/Vk/Image.cpp | 9 +++++++++ src/Magnum/Vk/Image.h | 20 +++++++++++++++++++- src/Magnum/Vk/Test/ImageVkTest.cpp | 24 +++++++++++++++++------- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Vk/Image.cpp b/src/Magnum/Vk/Image.cpp index c7ae90e095..4f2bb94115 100644 --- a/src/Magnum/Vk/Image.cpp +++ b/src/Magnum/Vk/Image.cpp @@ -27,6 +27,7 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Integration.h" #include "Magnum/Vk/Implementation/DeviceState.h" @@ -78,6 +79,14 @@ Image::Image(Device& device, const ImageCreateInfo& info, NoAllocateT): _device{ MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateImage(device, info, nullptr, &_handle)); } +Image::Image(Device& device, const ImageCreateInfo& info, const MemoryFlags memoryFlags): Image{device, info, NoAllocate} { + const MemoryRequirements requirements = memoryRequirements(); + bindDedicatedMemory(Memory{device, MemoryAllocateInfo{ + requirements.size(), + device.properties().pickMemory(memoryFlags, requirements.memories()) + }}); +} + Image::Image(NoCreateT): _device{}, _handle{}, _dedicatedMemory{NoCreate} {} Image::Image(Image&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags}, _dedicatedMemory{std::move(other._dedicatedMemory)} { diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 6b381f8176..804e736f8a 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -355,10 +355,28 @@ class MAGNUM_VK_EXPORT Image { * @param device Vulkan device to create the image on * @param info Image creation info * - * @see @fn_vk_keyword{CreateImage} + * Use @ref memoryRequirements(), @ref Memory and @ref bindMemory() to + * bind a memory (sub)allocation to the image. + * @see @ref Image(Device&, const ImageCreateInfo&, MemoryFlags), + * @fn_vk_keyword{CreateImage} */ explicit Image(Device& device, const ImageCreateInfo& info, NoAllocateT); + /** + * @brief Construct an image + * @param device Vulkan device to create the image on + * @param info Image creation info + * @param memoryFlags Memory allocation flags + * + * Compared to @ref Image(Device&, const ImageCreateInfo&, NoAllocateT) + * allocates a memory satisfying @p memoryFlags as well. + * + * @attention At this point, a dedicated allocation is used, + * subsequently accessible through @ref dedicatedMemory(). This + * behavior may change in the future. + */ + explicit Image(Device& device, const ImageCreateInfo& info, MemoryFlags memoryFlags); + /** * @brief Construct without creating the image * diff --git a/src/Magnum/Vk/Test/ImageVkTest.cpp b/src/Magnum/Vk/Test/ImageVkTest.cpp index 57763a1863..1fde01e8f4 100644 --- a/src/Magnum/Vk/Test/ImageVkTest.cpp +++ b/src/Magnum/Vk/Test/ImageVkTest.cpp @@ -53,6 +53,8 @@ struct ImageVkTest: VulkanTester { void bindMemory(); void bindDedicatedMemory(); + + void directAllocation(); }; ImageVkTest::ImageVkTest() { @@ -70,7 +72,9 @@ ImageVkTest::ImageVkTest() { &ImageVkTest::memoryRequirements, &ImageVkTest::bindMemory, - &ImageVkTest::bindDedicatedMemory}); + &ImageVkTest::bindDedicatedMemory, + + &ImageVkTest::directAllocation}); } void ImageVkTest::construct1D() { @@ -158,14 +162,11 @@ void ImageVkTest::constructCubeMapArray() { } void ImageVkTest::constructMove() { + /* Verify that also the dedicated memory gets moved */ Image a{device(), ImageCreateInfo2D{ImageUsage::ColorAttachment, - VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, NoAllocate}; + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 1}, + Vk::MemoryFlag::DeviceLocal}; VkImage handle = a.handle(); - - /* Verify that also the dedicated memory gets moved */ - MemoryRequirements requirements = a.memoryRequirements(); - a.bindDedicatedMemory(Vk::Memory{device(), Vk::MemoryAllocateInfo{requirements.size(), - device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); VkDeviceMemory memoryHandle = a.dedicatedMemory().handle(); Image b = std::move(a); @@ -263,6 +264,15 @@ void ImageVkTest::bindDedicatedMemory() { CORRADE_COMPARE(image.dedicatedMemory().handle(), handle); } +void ImageVkTest::directAllocation() { + Image image{device(), ImageCreateInfo2D{ImageUsage::Sampled, + VK_FORMAT_R8G8B8A8_UNORM, {256, 256}, 8}, Vk::MemoryFlag::DeviceLocal}; + + /* Not sure what else to test here */ + CORRADE_VERIFY(image.hasDedicatedMemory()); + CORRADE_VERIFY(image.dedicatedMemory().handle()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::ImageVkTest) From 70805d0acce71c6388f585c47cede54a0db140f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 23:29:34 +0100 Subject: [PATCH 66/85] Vk: remember allocated memory size. Will be used for mapping and in the future possibly for memory slices / views. --- src/Magnum/Vk/Memory.cpp | 10 +++++++--- src/Magnum/Vk/Memory.h | 7 ++++++- src/Magnum/Vk/Test/MemoryVkTest.cpp | 7 ++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 314ce807e1..bc4dcae744 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -58,22 +58,24 @@ MemoryAllocateInfo::MemoryAllocateInfo(const VkMemoryAllocateInfo& info): member instead of doing a copy */ _info(info) {} -Memory Memory::wrap(Device& device, const VkDeviceMemory handle, const HandleFlags flags) { +Memory Memory::wrap(Device& device, const VkDeviceMemory handle, const UnsignedLong size, const HandleFlags flags) { Memory out{NoCreate}; out._device = &device; out._handle = handle; out._flags = flags; + out._size = size; return out; } -Memory::Memory(Device& device, const MemoryAllocateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { +Memory::Memory(Device& device, const MemoryAllocateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction}, _size{info->allocationSize} { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->AllocateMemory(device, info, nullptr, &_handle)); } Memory::Memory(NoCreateT): _device{}, _handle{} {} -Memory::Memory(Memory&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { +Memory::Memory(Memory&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags}, _size{other._size} { other._handle = {}; + other._size = {}; } Memory::~Memory() { @@ -86,12 +88,14 @@ Memory& Memory::operator=(Memory&& other) noexcept { swap(other._device, _device); swap(other._handle, _handle); swap(other._flags, _flags); + swap(other._size, _size); return *this; } VkDeviceMemory Memory::release() { const VkDeviceMemory handle = _handle; _handle = {}; + _size = {}; return handle; } diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 6092be9e4d..292754d50f 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -245,6 +245,7 @@ class MAGNUM_VK_EXPORT Memory { * @brief Wrap existing Vulkan handle * @param device Vulkan device the memory is allocated on * @param handle The @type_vk{DeviceMemory} handle + * @param size Memory size * @param flags Handle flags * * The @p handle is expected to be originating from @p device. Unlike @@ -253,7 +254,7 @@ class MAGNUM_VK_EXPORT Memory { * behavior. * @see @ref release() */ - static Memory wrap(Device& device, VkDeviceMemory handle, HandleFlags flags = {}); + static Memory wrap(Device& device, VkDeviceMemory handle, UnsignedLong size, HandleFlags flags = {}); /** * @brief Constructor @@ -303,6 +304,9 @@ class MAGNUM_VK_EXPORT Memory { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** @brief Memory allocation size */ + UnsignedLong size() const { return _size; } + /** * @brief Release the underlying Vulkan memory * @@ -319,6 +323,7 @@ class MAGNUM_VK_EXPORT Memory { VkDeviceMemory _handle; HandleFlags _flags; + UnsignedLong _size; }; }} diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp index 12e5548739..2ed835235c 100644 --- a/src/Magnum/Vk/Test/MemoryVkTest.cpp +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -51,6 +51,7 @@ void MemoryVkTest::construct() { Memory memory{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::DeviceLocal)}}; CORRADE_VERIFY(memory.handle()); CORRADE_COMPARE(memory.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(memory.size(), 1024*1024); } void MemoryVkTest::constructMove() { @@ -61,6 +62,7 @@ void MemoryVkTest::constructMove() { CORRADE_VERIFY(!a.handle()); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(b.size(), 1024*1024); Memory c{NoCreate}; c = std::move(b); @@ -68,6 +70,7 @@ void MemoryVkTest::constructMove() { CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(c.size(), 1024*1024); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); @@ -80,12 +83,14 @@ void MemoryVkTest::wrap() { nullptr, &memory)), Result::Success); CORRADE_VERIFY(memory); - auto wrapped = Memory::wrap(device(), memory, HandleFlag::DestroyOnDestruction); + auto wrapped = Memory::wrap(device(), memory, 1024*1024, HandleFlag::DestroyOnDestruction); CORRADE_COMPARE(wrapped.handle(), memory); + CORRADE_COMPARE(wrapped.size(), 1024*1024); /* Release the handle again, destroy by hand */ CORRADE_COMPARE(wrapped.release(), memory); CORRADE_VERIFY(!wrapped.handle()); + CORRADE_COMPARE(wrapped.size(), 0); device()->FreeMemory(device(), memory, nullptr); } From 7e5d8114502918343d490630b82cdb308f0e6ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 16 Nov 2020 23:46:12 +0100 Subject: [PATCH 67/85] Vk: implement memory mapping. --- src/Magnum/Vk/Memory.cpp | 25 +++++++++++ src/Magnum/Vk/Memory.h | 64 +++++++++++++++++++++++++++++ src/Magnum/Vk/Test/MemoryVkTest.cpp | 45 +++++++++++++++++++- src/Magnum/Vk/Vk.h | 1 + 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index bc4dcae744..2d0f32254c 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -25,6 +25,7 @@ #include "Memory.h" +#include #include #include @@ -92,6 +93,30 @@ Memory& Memory::operator=(Memory&& other) noexcept { return *this; } +Containers::Array Memory::map(const UnsignedLong offset, const UnsignedLong size) { + void* data; + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS((**_device).MapMemory(*_device, _handle, offset, size, {}, &data)); + return Containers::Array{static_cast(data), size, MemoryMapDeleter{(**_device).UnmapMemory, *_device, _handle}}; +} + +Containers::Array Memory::map() { + return map(0, _size); +} + +Containers::Array Memory::mapRead(const UnsignedLong offset, const UnsignedLong size) { + Containers::Array out = map(offset, size); + + /* Simply "cast" to a const array. Extracting the deleter before because + the order of operations is unspecified and the deleter could be queried + after release() got called */ + const MemoryMapDeleter deleter = out.deleter(); + return Containers::Array{out.release(), size, deleter}; +} + +Containers::Array Memory::mapRead() { + return mapRead(0, _size); +} + VkDeviceMemory Memory::release() { const VkDeviceMemory handle = _handle; _handle = {}; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index 292754d50f..f344435d93 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -39,6 +39,15 @@ namespace Magnum { namespace Vk { +/** @relates Memory +@brief Deleter for mapped memory +@m_since_latest + +Deleter for the array returned from @ref Memory::map(). Calls +@fn_vk_keyword{UnmapMemory}. +*/ +class MemoryMapDeleter; + /** @brief Memory type flag @m_since_latest @@ -307,6 +316,45 @@ class MAGNUM_VK_EXPORT Memory { /** @brief Memory allocation size */ UnsignedLong size() const { return _size; } + /** + * @brief Map a memory range + * @param offset Byte offset + * @param size Memory size + * + * The returned array size is @p size and the deleter performs an + * unmap. For this operation to work, the memory has to be allocated + * with @ref MemoryFlag::HostVisible and the @p offset and @p size be + * in bounds for @ref size(). + * @see @fn_vk_keyword{MapMemory}, @fn_vk{UnmapMemory} + */ + Containers::Array map(UnsignedLong offset, UnsignedLong size); + + /** + * @brief Map the whole memory + * + * Equivalent to calling @ref map(UnsignedLong, UnsignedLong) with + * @cpp 0 @ce and @ref size(). + */ + Containers::Array map(); + + /** + * @brief Map a memory range read-only + * + * Like @ref map(UnsignedLong, UnsignedLong) but returning a + * @cpp const @ce array. Currently Vulkan doesn't have any flags to + * control read/write access, so apart from a different return type the + * behavior is equivalent. + */ + Containers::Array mapRead(UnsignedLong offset, UnsignedLong size); + + /** + * @brief Map the whole memory read-only + * + * Equivalent to calling @ref mapRead(UnsignedLong, UnsignedLong) with + * @cpp 0 @ce and @ref size(). + */ + Containers::Array mapRead(); + /** * @brief Release the underlying Vulkan memory * @@ -326,6 +374,22 @@ class MAGNUM_VK_EXPORT Memory { UnsignedLong _size; }; +#ifndef DOXYGEN_GENERATING_OUTPUT +class MAGNUM_VK_EXPORT MemoryMapDeleter { + public: + explicit MemoryMapDeleter(): _unmap{}, _device{}, _memory{} {} + explicit MemoryMapDeleter(void(*unmap)(VkDevice, VkDeviceMemory), VkDevice device, VkDeviceMemory memory): _unmap{unmap}, _device{device}, _memory{memory} {} + void operator()(const char*, std::size_t) { + if(_unmap) _unmap(_device, _memory); + } + + private: + void(*_unmap)(VkDevice, VkDeviceMemory); + VkDevice _device; + VkDeviceMemory _memory; +}; +#endif + }} #endif diff --git a/src/Magnum/Vk/Test/MemoryVkTest.cpp b/src/Magnum/Vk/Test/MemoryVkTest.cpp index 2ed835235c..ff820390b4 100644 --- a/src/Magnum/Vk/Test/MemoryVkTest.cpp +++ b/src/Magnum/Vk/Test/MemoryVkTest.cpp @@ -23,6 +23,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Memory.h" @@ -38,13 +40,19 @@ struct MemoryVkTest: VulkanTester { void constructMove(); void wrap(); + + void map(); + void mapRead(); }; MemoryVkTest::MemoryVkTest() { addTests({&MemoryVkTest::construct, &MemoryVkTest::constructMove, - &MemoryVkTest::wrap}); + &MemoryVkTest::wrap, + + &MemoryVkTest::map, + &MemoryVkTest::mapRead}); } void MemoryVkTest::construct() { @@ -94,6 +102,41 @@ void MemoryVkTest::wrap() { device()->FreeMemory(device(), memory, nullptr); } +void MemoryVkTest::map() { + Memory a{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::HostVisible)}}; + + /* Map and write */ + { + Containers::Array mapped = a.map(); + CORRADE_COMPARE(mapped.size(), 1024*1024); + mapped[1024 + 37] = 'c'; + } + + /* Map a subrange again -- shouldn't fail since we unmapped implicitly + above */ + { + Containers::Array mapped = a.map(1024, 100); + CORRADE_COMPARE(mapped.size(), 100); + CORRADE_COMPARE(mapped[37], 'c'); + } +} + +void MemoryVkTest::mapRead() { + Memory a{device(), MemoryAllocateInfo{1024*1024, device().properties().pickMemory(MemoryFlag::HostVisible)}}; + + /* Map and write, unmap should be implicit */ + { + Containers::Array mapped = a.mapRead(); + CORRADE_COMPARE(mapped.size(), 1024*1024); + } + + /* Map a subrange again */ + { + Containers::Array mapped = a.mapRead(1024, 100); + CORRADE_COMPARE(mapped.size(), 100); + } +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::MemoryVkTest) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index 025ac28fc9..cef370bf58 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -53,6 +53,7 @@ class InstanceExtension; class InstanceExtensionProperties; class LayerProperties; class Memory; +class MemoryMapDeleter; class MemoryRequirements; enum class MemoryFlag: UnsignedInt; typedef Containers::EnumSet MemoryFlags; From cad08497b1b0a3f1bc6dcfb91065ab77cef4f882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 17 Nov 2020 13:57:25 +0100 Subject: [PATCH 68/85] Vk: DeviceCreateInfo::addQueues() overload taking QueueFlags directly. More convenient to use since one doesn't have to explicitly store a DeviceProperties instance to call pickQueueFamily() on it and then move it to DeviceCreateInfo to keep it efficient. This required a slight redesign of how DeviceProperties are stored in DeviceCreateInfo, now we need the instance to be always valid (but it can get never used). The wrap() API isn't doing any extra work so this won't add any inefficiency. --- doc/snippets/MagnumVk.cpp | 18 ++++------ src/Magnum/Vk/Device.cpp | 54 +++++++++++++++++++++-------- src/Magnum/Vk/Device.h | 49 ++++++++++++++++++-------- src/Magnum/Vk/Test/DeviceVkTest.cpp | 31 +++++++++++++++-- 4 files changed, 109 insertions(+), 43 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index b3dd5514f3..d21090a319 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -79,24 +79,20 @@ Vk::CommandBuffer commandBuffer = graphicsCommandPool.allocate(); { Vk::Instance instance; -/* [Device-usage-pick] */ -Vk::DeviceProperties props = Vk::pickDevice(instance); -/* [Device-usage-pick] */ - /* [Device-usage-construct-queue] */ Vk::Queue queue{NoCreate}; -Vk::Device device{instance, Vk::DeviceCreateInfo{props} - .addQueues(props.pickQueueFamily(Vk::QueueFlag::Graphics), {0.0f}, {queue}) +Vk::Device device{instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)} + .addQueues(Vk::QueueFlag::Graphics, {0.0f}, {queue}) }; /* [Device-usage-construct-queue] */ } { Vk::Instance instance; -Vk::DeviceProperties props{NoCreate}; +Vk::DeviceProperties properties{NoCreate}; using namespace Containers::Literals; /* [Device-usage-extensions] */ -Vk::Device device{instance, Vk::DeviceCreateInfo{props} +Vk::Device device{instance, Vk::DeviceCreateInfo{DOXYGEN_IGNORE(properties)} DOXYGEN_IGNORE() .addEnabledExtensions< // predefined extensions Vk::Extensions::EXT::index_type_uint8, @@ -108,12 +104,12 @@ Vk::Device device{instance, Vk::DeviceCreateInfo{props} { Vk::Instance instance; -Vk::DeviceProperties props{NoCreate}; using namespace Containers::Literals; /* [Device-usage-check-supported] */ -Vk::ExtensionProperties extensions = props.enumerateExtensionProperties(); +Vk::DeviceProperties properties = Vk::pickDevice(instance); +Vk::ExtensionProperties extensions = properties.enumerateExtensionProperties(); -Vk::DeviceCreateInfo info{props}; +Vk::DeviceCreateInfo info{properties}; if(extensions.isSupported()) info.addEnabledExtensions(); if(extensions.isSupported("VK_NV_mesh_shader"_s)) diff --git a/src/Magnum/Vk/Device.cpp b/src/Magnum/Vk/Device.cpp index 20b26f70f7..12c7eb3601 100644 --- a/src/Magnum/Vk/Device.cpp +++ b/src/Magnum/Vk/Device.cpp @@ -63,10 +63,9 @@ struct DeviceCreateInfo::State { std::size_t nextQueuePriority = 0; bool quietLog = false; Version version = Version::None; - /* Gets populated in the DeviceCreateInfo constructor that takes a - DeviceProperties&&, in which case it's then moved to the newly created - Device instance. If not populated, the Device instance gets nothing and - it'll gets populated on first access to Device::properties(). */ + /* Gets populated at the very end of DeviceCreateInfo(DeviceProperties&) + and then possibly overwritten in DeviceCreateInfo(DeviceProperties&&). + Either way, it's meant to be valid after the constructor exits. */ DeviceProperties properties{NoCreate}; }; @@ -122,6 +121,16 @@ DeviceCreateInfo::DeviceCreateInfo(DeviceProperties& deviceProperties, const Ext if(_state->version < Version::Vk11 && extensionProperties->isSupported()) addEnabledExtensions(); } + + /* Conservatively populate the device properties. + - In case the DeviceCreateInfo(DeviceProperties&&) constructor is used, + it'll get overwritten straight away with a populated instance. + - In case the addQueues(QueueFlags) API is not used and DeviceCreateInfo + isn't subsequently moved to the Device, it'll never get touched again + and Device will wrap() its own. + - In case addQueues(QueueFlags) is used it'll get populated and then + possibly discarded if it isn't subsequently moved to the Device. */ + _state->properties = DeviceProperties::wrap(*deviceProperties._instance, deviceProperties._handle); } DeviceCreateInfo::DeviceCreateInfo(DeviceProperties&& deviceProperties, const ExtensionProperties* extensionProperties, const Flags flags): DeviceCreateInfo{deviceProperties, extensionProperties, flags} { @@ -260,6 +269,24 @@ DeviceCreateInfo&& DeviceCreateInfo::addQueues(const UnsignedInt family, const s return std::move(*this); } +DeviceCreateInfo& DeviceCreateInfo::addQueues(const QueueFlags flags, const Containers::ArrayView priorities, const Containers::ArrayView> output) & { + return addQueues(_state->properties.pickQueueFamily(flags), priorities, output); +} + +DeviceCreateInfo&& DeviceCreateInfo::addQueues(const QueueFlags flags, const Containers::ArrayView priorities, const Containers::ArrayView> output) && { + addQueues(flags, priorities, output); + return std::move(*this); +} + +DeviceCreateInfo& DeviceCreateInfo::addQueues(const QueueFlags flags, const std::initializer_list priorities, const std::initializer_list> output) & { + return addQueues(flags, Containers::arrayView(priorities), Containers::arrayView(output)); +} + +DeviceCreateInfo&& DeviceCreateInfo::addQueues(const QueueFlags flags, const std::initializer_list priorities, const std::initializer_list> output) && { + addQueues(flags, priorities, output); + return std::move(*this); +} + DeviceCreateInfo& DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo& info) & { /* This can happen in case we used the NoInit or VkDeviceCreateInfo constructor */ @@ -295,7 +322,7 @@ Device Device::wrap(Instance& instance, const VkDevice handle, const Version ver return wrap(instance, handle, version, Containers::arrayView(enabledExtensions), flags); } -Device::Device(Instance& instance, const DeviceCreateInfo& info): Device{instance, info, DeviceProperties{NoCreate}} {} +Device::Device(Instance& instance, const DeviceCreateInfo& info): Device{instance, info, DeviceProperties::wrap(instance, info._physicalDevice)} {} Device::Device(Instance& instance, DeviceCreateInfo&& info): Device{instance, info, std::move(info._state->properties)} {} @@ -303,19 +330,18 @@ Device::Device(Instance& instance, const DeviceCreateInfo& info, DevicePropertie #ifdef CORRADE_GRACEFUL_ASSERT _handle{}, /* Otherwise the destructor dies if we hit the queue assert */ #endif - _flags{HandleFlag::DestroyOnDestruction} + _flags{HandleFlag::DestroyOnDestruction}, + _properties{Containers::InPlaceInit, std::move(properties)} { + /* The properties should always be a valid instance, either moved from + outside or created again from VkPhysicalDevice, in case it couldn't be + moved. If it's not, something in DeviceCreateInfo or here got messed + up. */ + CORRADE_INTERNAL_ASSERT(_properties->handle()); + CORRADE_ASSERT(info._info.queueCreateInfoCount, "Vk::Device: needs to be created with at least one queue", ); - /* If the passed properties are populated, use them. Otherwise create a new - instance as we'd have no other way to remember the VkPhysicalDevice - handle otherwise */ - if(properties.handle()) - _properties.emplace(std::move(properties)); - else - _properties.emplace(DeviceProperties::wrap(instance, info._physicalDevice)); - const Version version = info._state->version != Version::None ? info._state->version : _properties->version(); diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index ca2104cbd4..9e0e72f18c 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -207,7 +207,8 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * @return Reference to self (for method chaining) * * At least one queue has to be added. - * @see @ref DeviceProperties::pickQueueFamily() + * @see @ref DeviceProperties::pickQueueFamily(), + * @ref addQueues(QueueFlags, Containers::ArrayView, Containers::ArrayView>) */ DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output) &; /** @overload */ @@ -217,6 +218,27 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { /** @overload */ DeviceCreateInfo&& addQueues(UnsignedInt family, std::initializer_list priorities, std::initializer_list> output) &&; + /** + * @brief Add queues of family matching given flags + * + * Equivalent to picking a queue family first using + * @ref DeviceProperties::pickQueueFamily() based on @p flags and then + * calling @ref addQueues(UnsignedInt, Containers::ArrayView, Containers::ArrayView>) + * with the family index. + * + * Note that @ref DeviceProperties::pickQueueFamily() exits in case it + * doesn't find any family satisfying given @p flags --- for a + * failproof scenario you may want to go with the above overload and + * @ref DeviceProperties::tryPickQueueFamily() instead. + */ + DeviceCreateInfo& addQueues(QueueFlags flags, Containers::ArrayView priorities, Containers::ArrayView> output) &; + /** @overload */ + DeviceCreateInfo&& addQueues(QueueFlags flags, Containers::ArrayView priorities, Containers::ArrayView> output) &&; + /** @overload */ + DeviceCreateInfo& addQueues(QueueFlags flags, std::initializer_list priorities, std::initializer_list> output) &; + /** @overload */ + DeviceCreateInfo&& addQueues(QueueFlags flags, std::initializer_list priorities, std::initializer_list> output) &&; + /** * @brief Add queues using raw info * @return Reference to self (for method chaining) @@ -269,23 +291,20 @@ even a software implementation. If the application needs something specific, you can use @ref enumerateDevices() instead, pick a device from the list manually, provide the users with a list to choose from etc. -@snippet MagnumVk.cpp Device-usage-pick - -After a device is picked, a @ref Device can be created using -@ref DeviceCreateInfo. At the very least you'll need to set up queues, as every -Vulkan device needs at least one. That's done by creating an empty @ref Queue -instance and then referencing it from @ref DeviceCreateInfo::addQueues(). After -the device is constructed, the queue gets populated and is ready to be used. +The picked device is then passed to @ref DeviceCreateInfo. At the very least +you'll also need to set up queues, as every Vulkan device needs at least one. +That's done by creating an empty @ref Queue instance and then referencing it +from @ref DeviceCreateInfo::addQueues(). After the device is constructed, the +queue gets populated and is ready to be used. @snippet MagnumVk.cpp Device-usage-construct-queue -In the above snippet, we requested a graphics queue --- the -@ref DeviceProperties instance we made earlier acts as knowledge base for a -particular device, providing info about available queues, extensions, features, -memory heaps and implementation limits --- and we used a convenience API to -pick the first available graphics queue. As with device picking, you can -also iterate through all @ref DeviceProperties::queueFamilyCount() and choose -one manually. +In the above snippet, we requested a graphics queue via a convenience API. The +information about available queues and other device properties is stored in a +@ref DeviceProperties that got returned from @ref pickDevice() and +@ref DeviceCreateInfo called @ref DeviceProperties::pickQueueFamily() for us. +As with device picking, you can also iterate through all +@ref DeviceProperties::queueFamilyCount() and choose one manually. Same as with @ref Instance, the above won't enable any additional extensions except for what the engine itself needs or what's supplied on the command line. Use @ref DeviceCreateInfo::addEnabledExtensions() to enable them, you can use diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index 0c42769401..16e6a3bc7f 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -57,6 +58,7 @@ struct DeviceVkTest: VulkanTester { void createInfoRvalue(); void construct(); + void constructQueueFromFlags(); void constructExtensions(); void constructTransferDeviceProperties(); void constructExtensionsCommandLineDisable(); @@ -137,6 +139,7 @@ DeviceVkTest::DeviceVkTest(): VulkanTester{NoCreate} { &DeviceVkTest::createInfoRvalue, &DeviceVkTest::construct, + &DeviceVkTest::constructQueueFromFlags, &DeviceVkTest::constructExtensions, &DeviceVkTest::constructTransferDeviceProperties}); @@ -244,9 +247,14 @@ void DeviceVkTest::createInfoWrongQueueOutputCount() { } void DeviceVkTest::createInfoRvalue() { + /* Verify that there actually are graphics queues so we don't exit inside + addQueues() */ + CORRADE_VERIFY(pickDevice(instance()).tryPickQueueFamily(QueueFlag::Graphics)); + Float zero[1]{}; - Queue a{NoCreate}, b{NoCreate}; - Containers::Reference reference[1]{a}; + Queue a{NoCreate}, b{NoCreate}, c{NoCreate}, d{NoCreate}; + Containers::Reference referenceA[1]{a}; + Containers::Reference referenceC[1]{c}; VkDeviceQueueCreateInfo rawQueueInfo{}; rawQueueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -260,8 +268,10 @@ void DeviceVkTest::createInfoRvalue() { .addEnabledExtensions(Containers::ArrayView{}) .addEnabledExtensions(std::initializer_list{}) .addEnabledExtensions<>() - .addQueues(0, zero, reference) + .addQueues(0, zero, referenceA) .addQueues(0, {0.0f}, {b}) + .addQueues(QueueFlag::Graphics, zero, referenceC) + .addQueues(QueueFlag::Graphics, {0.0f}, {d}) .addQueues(rawQueueInfo); /* Just to test something, main point is that the above compiles, links and @@ -306,6 +316,21 @@ void DeviceVkTest::construct() { CORRADE_VERIFY(true); } +void DeviceVkTest::constructQueueFromFlags() { + DeviceProperties deviceProperties = pickDevice(instance()); + + /* Verify that there actually are graphics queues so we don't exit after */ + CORRADE_VERIFY(deviceProperties.tryPickQueueFamily(QueueFlag::Graphics)); + + Queue queue{NoCreate}; + Device device{instance(), DeviceCreateInfo{deviceProperties} + .addQueues(QueueFlag::Graphics, {0.0f}, {queue})}; + CORRADE_VERIFY(device.handle()); + + /* The queue should be filled in like usual */ + CORRADE_VERIFY(queue.handle()); +} + void DeviceVkTest::constructExtensions() { if(std::getenv("MAGNUM_DISABLE_EXTENSIONS")) CORRADE_SKIP("Can't test with the MAGNUM_DISABLE_EXTENSIONS environment variable set"); From 7799a8d3ff3f59cbd8a1ed28d6e26e9817994574 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Nov 2020 18:56:38 +0100 Subject: [PATCH 69/85] Vk: Doxygen continues to be a massively underwhelming disappointment. Nope, it's 2020 and there's still no way to link to &-qualified functions. Feature that is in C++ since 2011. --- doc/vulkan-mapping.dox | 2 +- src/Magnum/Vk/Device.h | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index 26ab918ad1..f3453b1441 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -320,7 +320,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{DeviceCreateInfo} | @ref DeviceCreateInfo -@type_vk{DeviceQueueCreateInfo} | not exposed, but you can pass a custom instance to @ref DeviceCreateInfo::addQueues(const VkDeviceQueueCreateInfo&) +@type_vk{DeviceQueueCreateInfo} | not exposed, but you can pass a custom instance to @ref DeviceCreateInfo::addQueues() @subsection vulkan-mapping-structures-i I diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 9e0e72f18c..2a8c92ffed 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -207,8 +207,9 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * @return Reference to self (for method chaining) * * At least one queue has to be added. - * @see @ref DeviceProperties::pickQueueFamily(), - * @ref addQueues(QueueFlags, Containers::ArrayView, Containers::ArrayView>) + * @see @ref DeviceProperties::pickQueueFamily() + * @todoc link to addQueues(QueueFlags) once doxygen finally GROWS UP + * and can link to &-qualified functions FFS */ DeviceCreateInfo& addQueues(UnsignedInt family, Containers::ArrayView priorities, Containers::ArrayView> output) &; /** @overload */ @@ -223,13 +224,14 @@ class MAGNUM_VK_EXPORT DeviceCreateInfo { * * Equivalent to picking a queue family first using * @ref DeviceProperties::pickQueueFamily() based on @p flags and then - * calling @ref addQueues(UnsignedInt, Containers::ArrayView, Containers::ArrayView>) - * with the family index. + * calling the above @ref addQueues() variant with the family index. * * Note that @ref DeviceProperties::pickQueueFamily() exits in case it * doesn't find any family satisfying given @p flags --- for a * failproof scenario you may want to go with the above overload and * @ref DeviceProperties::tryPickQueueFamily() instead. + * @todoc link to addQueues(UnsignedInt) above once doxygen finally + * GROWS UP and can link to &-qualified functions FFS */ DeviceCreateInfo& addQueues(QueueFlags flags, Containers::ArrayView priorities, Containers::ArrayView> output) &; /** @overload */ @@ -408,9 +410,12 @@ class MAGNUM_VK_EXPORT Device { * @param info Device creation info * * After creating the device requests device queues added via - * @ref DeviceCreateInfo::addQueues(UnsignedInt, Containers::ArrayView, Containers::ArrayView>), populating the @ref Queue references. + * @ref DeviceCreateInfo::addQueues(), populating the @ref Queue + * references. * @see @fn_vk_keyword{CreateDevice}, @fn_vk_keyword{GetDeviceQueue2}, * @fn_vk_keyword{GetDeviceQueue} + * @todoc link to a concrete addQueues() overload above once doxygen + * finally GROWS UP and can link to &-qualified functions FFS */ explicit Device(Instance& instance, const DeviceCreateInfo& info); From 6b39e7c2b54bfbb7ec568b3bb6ac53086e246329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 17 Nov 2020 14:30:46 +0100 Subject: [PATCH 70/85] Vk: make InstanceCreateInfo implicitly constructible. Makes it possible to write Vk::Instance instance{{argc, argv}} which is a good tradeoff between passing no arguments at all and doing the fully verbose thing. --- doc/snippets/MagnumVk.cpp | 8 ++++++++ src/Magnum/Vk/Instance.h | 29 +++++++++++++++++++---------- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index d21090a319..d243a0cd6b 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -157,6 +157,14 @@ if(device.isExtensionEnabled()) { /* [Device-isExtensionEnabled] */ } +{ +int argc{}; +const char** argv{}; +/* [Instance-usage-minimal] */ +Vk::Instance instance{{argc, argv}}; +/* [Instance-usage-minimal] */ +} + { int argc{}; const char** argv{}; diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 9dec228647..1fb0208a1c 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -110,25 +110,31 @@ class MAGNUM_VK_EXPORT InstanceCreateInfo { * @ref enumerateInstanceVersion() * - @cpp pApplicationInfo->engineName @ce to @cpp "Magnum" @ce */ - explicit InstanceCreateInfo(Int argc, const char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* const extensionProperties, Flags flags = {}); + /* All those are implicit in order to allow writing + Instance instance{{argc, argv}}; + It's a tradeoff between a completely verbose way and + Instance instance{argc, argv}; + in which case all these overloads would need to be duplicated on + Instance as well. */ + /*implicit*/ InstanceCreateInfo(Int argc, const char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* const extensionProperties, Flags flags = {}); /** @overload */ - explicit InstanceCreateInfo(Int argc, char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, const_cast(argv), layerProperties, extensionProperties, flags} {} + /*implicit*/ InstanceCreateInfo(Int argc, char** argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, const_cast(argv), layerProperties, extensionProperties, flags} {} /** @overload */ - explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, static_cast(argv), layerProperties, extensionProperties, flags} {} + /*implicit*/ InstanceCreateInfo(Int argc, std::nullptr_t argv, const LayerProperties* layerProperties, const InstanceExtensionProperties* extensionProperties, Flags flags = {}): InstanceCreateInfo{argc, static_cast(argv), layerProperties, extensionProperties, flags} {} /** @overload */ - explicit InstanceCreateInfo(Int argc, const char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + /*implicit*/ InstanceCreateInfo(Int argc, const char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} /** @overload */ - explicit InstanceCreateInfo(Int argc, char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + /*implicit*/ InstanceCreateInfo(Int argc, char** argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} /** @overload */ - explicit InstanceCreateInfo(Int argc, std::nullptr_t argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} + /*implicit*/ InstanceCreateInfo(Int argc, std::nullptr_t argv, Flags flags = {}): InstanceCreateInfo{argc, argv, nullptr, nullptr, flags} {} /** @overload */ - explicit InstanceCreateInfo(Flags flags = {}): InstanceCreateInfo{0, nullptr, flags} {} + /*implicit*/ InstanceCreateInfo(Flags flags = {}): InstanceCreateInfo{0, nullptr, flags} {} /** * @brief Construct without initializing the contents @@ -243,9 +249,12 @@ pointers. While an @ref Instance can be default-constructed without much fuss, it's recommended to pass a @ref InstanceCreateInfo with at least the `argc` / `argv` pair, which allows you to use various `--magnum-*` -@ref Vk-Instance-command-line "command-line options" listed below. Setting -application info may be beneficial for the driver, but it's not required -either. +@ref Vk-Instance-command-line "command-line options": + +@snippet MagnumVk.cpp Instance-usage-minimal + +In addition to command-line arguments, setting application info isn't strictly +required either, but may be beneficial for the driver: @snippet MagnumVk.cpp Instance-usage From e44d5af48d8f19164d533acfa3f25a77d04c9eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Nov 2020 18:30:10 +0100 Subject: [PATCH 71/85] Vk: initial Buffer wrapper. Basically the same as with Image, but easier. --- doc/vulkan-mapping.dox | 10 +- src/Magnum/Vk/Buffer.cpp | 88 ++++++++++ src/Magnum/Vk/Buffer.h | 245 ++++++++++++++++++++++++++++ src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/Test/BufferTest.cpp | 98 +++++++++++ src/Magnum/Vk/Test/BufferVkTest.cpp | 97 +++++++++++ src/Magnum/Vk/Test/CMakeLists.txt | 2 + src/Magnum/Vk/Vk.h | 1 + 8 files changed, 542 insertions(+), 1 deletion(-) create mode 100644 src/Magnum/Vk/Buffer.cpp create mode 100644 src/Magnum/Vk/Buffer.h create mode 100644 src/Magnum/Vk/Test/BufferTest.cpp create mode 100644 src/Magnum/Vk/Test/BufferVkTest.cpp diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index f3453b1441..afe1d23e8e 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -121,7 +121,7 @@ Vulkan function | Matching API @fn_vk{CmdUpdateBuffer} | | @fn_vk{CmdWaitEvents} | | @fn_vk{CmdWriteTimestamp} | | -@fn_vk{CreateBuffer}, \n @fn_vk{DestroyBuffer} | | +@fn_vk{CreateBuffer}, \n @fn_vk{DestroyBuffer} | @ref Buffer constructor and destructor @fn_vk{CreateBufferView}, \n @fn_vk{DestroyBufferView} | | @fn_vk{CreateCommandPool}, \n @fn_vk{DestroyCommandPool} | @ref CommandPool constructor and destructor @fn_vk{CreateComputePipelines}, \n @fn_vk{DestroyComputePipelines} | | @@ -304,6 +304,14 @@ Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{ApplicationInfo} | @ref InstanceCreateInfo +@subsection vulkan-mapping-structures-b B + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{BufferCreateInfo} | @ref BufferCreateInfo + @subsection vulkan-mapping-structures-c C @m_class{m-fullwidth} diff --git a/src/Magnum/Vk/Buffer.cpp b/src/Magnum/Vk/Buffer.cpp new file mode 100644 index 0000000000..d8ab43676a --- /dev/null +++ b/src/Magnum/Vk/Buffer.cpp @@ -0,0 +1,88 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Buffer.h" + +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" + +namespace Magnum { namespace Vk { + +BufferCreateInfo::BufferCreateInfo(const BufferUsages usages, const UnsignedLong size, const Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + _info.flags = VkBufferCreateFlags(flags); + _info.size = size; + _info.usage = VkBufferUsageFlags(usages); + /* _info.sharingMode is implicitly VK_SHARING_MODE_EXCLUSIVE; + _info.queueFamilyIndexCount and _info.pQueueFamilyIndices should be + filled only for VK_SHARING_MODE_CONCURRENT */ +} + +BufferCreateInfo::BufferCreateInfo(NoInitT) noexcept {} + +BufferCreateInfo::BufferCreateInfo(const VkBufferCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +Buffer Buffer::wrap(Device& device, const VkBuffer handle, const HandleFlags flags) { + Buffer out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Buffer::Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateBuffer(device, info, nullptr, &_handle)); +} + +Buffer::Buffer(NoCreateT): _device{}, _handle{} {} + +Buffer::Buffer(Buffer&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Buffer::~Buffer() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyBuffer(*_device, _handle, nullptr); +} + +Buffer& Buffer::operator=(Buffer&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkBuffer Buffer::release() { + const VkBuffer handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h new file mode 100644 index 0000000000..377698064e --- /dev/null +++ b/src/Magnum/Vk/Buffer.h @@ -0,0 +1,245 @@ +#ifndef Magnum_Vk_Buffer_h +#define Magnum_Vk_Buffer_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::BufferCreateInfo, @ref Magnum::Vk::Buffer, enum @ref Magnum::Vk::BufferUsage, enum set @ref Magnum::Vk::BufferUsages + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Buffer usage +@m_since_latest + +Wraps a @type_vk_keyword{BufferUsageFlagBits}. +@see @ref BufferUsages, @ref BufferCreateInfo +@m_enum_values_as_keywords +*/ +enum class BufferUsage: UnsignedInt { + TransferSource = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + TransferDestination = VK_BUFFER_USAGE_TRANSFER_DST_BIT, + UniformTexelBuffer = VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, + StorageTexelBuffer = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT, + UniformBuffer = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + StorageBuffer = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, + IndexBuffer = VK_BUFFER_USAGE_INDEX_BUFFER_BIT, + VertexBuffer = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + IndirectBuffer = VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + + /** @todo VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, 1.2 */ +}; + +/** +@brief Buffer usages +@m_since_latest + +Type-safe wrapper for @type_vk_keyword{BufferUsageFlags}. +@see @ref BufferCreateInfo +*/ +typedef Containers::EnumSet BufferUsages; + +CORRADE_ENUMSET_OPERATORS(BufferUsages) + +/** +@brief Buffer creation info +@m_since_latest + +Wraps a @type_vk_keyword{BufferCreateInfo}. See @ref Buffer for usage +information. +*/ +class MAGNUM_VK_EXPORT BufferCreateInfo { + public: + /** + * @brief Buffer creation flag + * + * Wraps @type_vk_keyword{BufferCreateFlagBits}. + * @see @ref Flags, @ref BufferCreateInfo() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt { + /** @todo sparse binding, protected ... */ + }; + + /** + * @brief Buffer creation flags + * + * Type-safe wrapper for @type_vk_keyword{BufferCreateFlags}. + * @see @ref BufferCreateInfo() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param usages Desired buffer usage. At least one flag is + * required. + * @param size Buffer size + * @param flags Buffer creation flags + * + * The following @type_vk{BufferCreateInfo} fields are pre-filled in + * addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `size` + * - `usage` to @p usages + * - `sharingMode` to @val_vk{SHARING_MODE_EXCLUSIVE,SharingMode} + */ + explicit BufferCreateInfo(BufferUsages usages, UnsignedLong size, Flags flags = {}); + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit BufferCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit BufferCreateInfo(const VkBufferCreateInfo& info); + + /** @brief Underlying @type_vk{BufferCreateInfo} structure */ + VkBufferCreateInfo& operator*() { return _info; } + /** @overload */ + const VkBufferCreateInfo& operator*() const { return _info; } + /** @overload */ + VkBufferCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkBufferCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkBufferCreateInfo*() const { return &_info; } + + private: + VkBufferCreateInfo _info; +}; + +CORRADE_ENUMSET_OPERATORS(BufferCreateInfo::Flags) + +/** +@brief Buffer +@m_since_latest + +Wraps a @type_vk_keyword{Buffer}. +*/ +class MAGNUM_VK_EXPORT Buffer { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the buffer is created on + * @param handle The @type_vk{Buffer} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a buffer created using a constructor, the Vulkan buffer is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Buffer wrap(Device& device, VkBuffer handle, HandleFlags flags = {}); + + /** + * @brief Construct a buffer without allocating + * @param device Vulkan device to create the buffer on + * @param info Buffer creation info + * + * @see @fn_vk_keyword{CreateBuffer} + */ + explicit Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT); + + /** + * @brief Construct without creating the buffer + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Buffer(NoCreateT); + + /** @brief Copying is not allowed */ + Buffer(const Buffer&) = delete; + + /** @brief Move constructor */ + Buffer(Buffer&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{Buffer} handle, unless the instance was + * created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyBuffer}, @ref release() + */ + ~Buffer(); + + /** @brief Copying is not allowed */ + Buffer& operator=(const Buffer&) = delete; + + /** @brief Move assignment */ + Buffer& operator=(Buffer&& other) noexcept; + + /** @brief Underlying @type_vk{Buffer} handle */ + VkBuffer handle() { return _handle; } + /** @overload */ + operator VkBuffer() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan buffer + * + * Releases ownership of the Vulkan buffer and returns its handle so + * @fn_vk{DestroyBuffer} is not called on destruction. The internal + * state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkBuffer release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkBuffer _handle; + HandleFlags _flags; +}; + +}} + +#endif diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index c7a6f0d843..21150bf0ea 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,6 +27,7 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS + Buffer.cpp CommandBuffer.cpp CommandPool.cpp Extensions.cpp @@ -50,6 +51,7 @@ set(MagnumVk_GracefulAssert_SRCS set(MagnumVk_HEADERS Assert.h + Buffer.h CommandBuffer.h CommandPool.h Device.h diff --git a/src/Magnum/Vk/Test/BufferTest.cpp b/src/Magnum/Vk/Test/BufferTest.cpp new file mode 100644 index 0000000000..5281937dbe --- /dev/null +++ b/src/Magnum/Vk/Test/BufferTest.cpp @@ -0,0 +1,98 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Buffer.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct BufferTest: TestSuite::Tester { + explicit BufferTest(); + + void createInfoConstruct(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + + void constructNoCreate(); + void constructCopy(); +}; + +BufferTest::BufferTest() { + addTests({&BufferTest::createInfoConstruct, + &BufferTest::createInfoConstructNoInit, + &BufferTest::createInfoConstructFromVk, + + &BufferTest::constructNoCreate, + &BufferTest::constructCopy}); +} + +void BufferTest::createInfoConstruct() { + /** @todo use a real flag once at least one is exposed */ + BufferCreateInfo info{BufferUsage::UniformBuffer, 1024, BufferCreateInfo::Flag(VK_BUFFER_CREATE_PROTECTED_BIT)}; + CORRADE_COMPARE(info->flags, VK_BUFFER_CREATE_PROTECTED_BIT); + CORRADE_COMPARE(info->size, 1024); + CORRADE_COMPARE(info->usage, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); +} + +void BufferTest::createInfoConstructNoInit() { + BufferCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) BufferCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void BufferTest::createInfoConstructFromVk() { + VkBufferCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + BufferCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void BufferTest::constructNoCreate() { + { + Buffer buffer{NoCreate}; + CORRADE_VERIFY(!buffer.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void BufferTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferTest) diff --git a/src/Magnum/Vk/Test/BufferVkTest.cpp b/src/Magnum/Vk/Test/BufferVkTest.cpp new file mode 100644 index 0000000000..065b3202c0 --- /dev/null +++ b/src/Magnum/Vk/Test/BufferVkTest.cpp @@ -0,0 +1,97 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Magnum/Vk/Buffer.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct BufferVkTest: VulkanTester { + explicit BufferVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +BufferVkTest::BufferVkTest() { + addTests({&BufferVkTest::construct, + &BufferVkTest::constructMove, + + &BufferVkTest::wrap}); +} + +void BufferVkTest::construct() { + { + Buffer buffer{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 1024}, NoAllocate}; + CORRADE_VERIFY(buffer.handle()); + CORRADE_COMPARE(buffer.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void BufferVkTest::constructMove() { + Buffer a{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 1024}, NoAllocate}; + VkBuffer handle = a.handle(); + + Buffer b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Buffer c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void BufferVkTest::wrap() { + VkBuffer buffer{}; + CORRADE_COMPARE(Result(device()->CreateBuffer(device(), + BufferCreateInfo{BufferUsage::StorageTexelBuffer, 4096}, + nullptr, &buffer)), Result::Success); + + auto wrapped = Buffer::wrap(device(), buffer, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), buffer); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), buffer); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyBuffer(device(), buffer, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferVkTest) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index af459e9637..5ac6b97274 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,6 +24,7 @@ # DEALINGS IN THE SOFTWARE. # +corrade_add_test(VkBufferTest BufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandBufferTest CommandBufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) @@ -94,6 +95,7 @@ target_compile_definitions(VkAssertStandardDisabledTest PRIVATE corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkDeviceVkTest DeviceVkTest.cpp LIBRARIES MagnumVkTestLib MagnumVulkanTester) diff --git a/src/Magnum/Vk/Vk.h b/src/Magnum/Vk/Vk.h index cef370bf58..7878da264e 100644 --- a/src/Magnum/Vk/Vk.h +++ b/src/Magnum/Vk/Vk.h @@ -36,6 +36,7 @@ namespace Magnum { namespace Vk { #ifndef DOXYGEN_GENERATING_OUTPUT +class Buffer; class CommandBuffer; class CommandPool; class Device; From 579ce0e14f69ce164a54b8c5a7dcdd98d0c6afa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Nov 2020 19:24:48 +0100 Subject: [PATCH 72/85] Vk: querying buffer memory requirements. --- doc/vulkan-mapping.dox | 3 ++- src/Magnum/Vk/Buffer.cpp | 23 ++++++++++++++++++++ src/Magnum/Vk/Buffer.h | 16 ++++++++++++++ src/Magnum/Vk/Implementation/DeviceState.cpp | 4 ++++ src/Magnum/Vk/Implementation/DeviceState.h | 3 ++- src/Magnum/Vk/Memory.h | 4 +++- src/Magnum/Vk/Test/BufferVkTest.cpp | 14 +++++++++++- 7 files changed, 63 insertions(+), 4 deletions(-) diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index afe1d23e8e..c9dcebe311 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -189,7 +189,7 @@ Vulkan function | Matching API @fn_vk{GetBufferDeviceAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @fn_vk{GetBufferOpaqueCaptureAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | @fn_vk{GetDeviceMemoryOpaqueCaptureAddress} @m_class{m-label m-flat m-success} **KHR, 1.2** | | -@fn_vk{GetBufferMemoryRequirements}, \n @fn_vk{GetBufferMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{GetBufferMemoryRequirements}, \n @fn_vk{GetBufferMemoryRequirements2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Buffer::memoryRequirements() @fn_vk{GetDescriptorSetLayoutSupport} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceGroupPeerMemoryFeatures} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{GetDeviceMemoryCommitment} | | @@ -311,6 +311,7 @@ Vulkan structure | Matching API Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{BufferCreateInfo} | @ref BufferCreateInfo +@type_vk{BufferMemoryRequirementsInfo} | not exposed, internal to @ref Buffer::memoryRequirements() @subsection vulkan-mapping-structures-c C diff --git a/src/Magnum/Vk/Buffer.cpp b/src/Magnum/Vk/Buffer.cpp index d8ab43676a..ce4a469431 100644 --- a/src/Magnum/Vk/Buffer.cpp +++ b/src/Magnum/Vk/Buffer.cpp @@ -28,6 +28,8 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Memory.h" +#include "Magnum/Vk/Implementation/DeviceState.h" namespace Magnum { namespace Vk { @@ -79,10 +81,31 @@ Buffer& Buffer::operator=(Buffer&& other) noexcept { return *this; } +MemoryRequirements Buffer::memoryRequirements() const { + MemoryRequirements requirements; + VkBufferMemoryRequirementsInfo2 info{}; + info.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2; + info.buffer = _handle; + _device->state().getBufferMemoryRequirementsImplementation(*_device, info, requirements); + return requirements; +} + VkBuffer Buffer::release() { const VkBuffer handle = _handle; _handle = {}; return handle; } +void Buffer::getMemoryRequirementsImplementationDefault(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetBufferMemoryRequirements(device, info.buffer, &requirements.memoryRequirements); +} + +void Buffer::getMemoryRequirementsImplementationKHR(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetBufferMemoryRequirements2KHR(device, &info, &requirements); +} + +void Buffer::getMemoryRequirementsImplementation11(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements) { + device->GetBufferMemoryRequirements2(device, &info, &requirements); +} + }} diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h index 377698064e..b114742381 100644 --- a/src/Magnum/Vk/Buffer.h +++ b/src/Magnum/Vk/Buffer.h @@ -40,6 +40,8 @@ namespace Magnum { namespace Vk { +namespace Implementation { struct DeviceState; } + /** @brief Buffer usage @m_since_latest @@ -222,6 +224,14 @@ class MAGNUM_VK_EXPORT Buffer { /** @brief Handle flags */ HandleFlags handleFlags() const { return _flags; } + /** + * @brief Buffer memory requirements + * + * @see @fn_vk_keyword{GetBufferMemoryRequirements2}, + * @fn_vk_keyword{GetBufferMemoryRequirements} + */ + MemoryRequirements memoryRequirements() const; + /** * @brief Release the underlying Vulkan buffer * @@ -233,6 +243,12 @@ class MAGNUM_VK_EXPORT Buffer { VkBuffer release(); private: + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationDefault(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationKHR(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementation11(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 6ebc74cd42..aa91516921 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -25,6 +25,7 @@ #include "DeviceState.h" +#include "Magnum/Vk/Buffer.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Image.h" @@ -40,10 +41,13 @@ DeviceState::DeviceState(Device& device) { } if(device.isVersionSupported(Version::Vk11)) { + getBufferMemoryRequirementsImplementation = &Buffer::getMemoryRequirementsImplementation11; getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementation11; } else if(device.isExtensionEnabled()) { + getBufferMemoryRequirementsImplementation = &Buffer::getMemoryRequirementsImplementationKHR; getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementationKHR; } else { + getBufferMemoryRequirementsImplementation = &Buffer::getMemoryRequirementsImplementationDefault; getImageMemoryRequirementsImplementation = &Image::getMemoryRequirementsImplementationDefault; } diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index dea6b18561..07a9b0f089 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -34,7 +34,8 @@ struct DeviceState { explicit DeviceState(Device& instance); void(*getDeviceQueueImplementation)(Device&, const VkDeviceQueueInfo2&, VkQueue&); - /** @todo put this eventually into a dedicated image state struct? */ + /** @todo put this eventually into a dedicated buffer / image state struct? */ + void(*getBufferMemoryRequirementsImplementation)(Device&, const VkBufferMemoryRequirementsInfo2&, VkMemoryRequirements2&); void(*getImageMemoryRequirementsImplementation)(Device&, const VkImageMemoryRequirementsInfo2&, VkMemoryRequirements2&); void(*bindImageMemoryImplementation)(Device&, UnsignedInt, const VkBindImageMemoryInfo*); }; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index f344435d93..d52a2230f5 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -125,7 +125,8 @@ MAGNUM_VK_EXPORT Debug& operator<<(Debug& debug, MemoryFlags value); @m_since_latest Wraps a @type_vk_keyword{MemoryRequirements2}. Not constructible directly, -returned from @ref Image::memoryRequirements(). +returned from @ref Image::memoryRequirements() and +@ref Buffer::memoryRequirements(). @see @ref DeviceProperties::pickMemory() */ class MAGNUM_VK_EXPORT MemoryRequirements { @@ -176,6 +177,7 @@ class MAGNUM_VK_EXPORT MemoryRequirements { } private: + friend Buffer; friend Image; explicit MemoryRequirements(); diff --git a/src/Magnum/Vk/Test/BufferVkTest.cpp b/src/Magnum/Vk/Test/BufferVkTest.cpp index 065b3202c0..9714a3cac2 100644 --- a/src/Magnum/Vk/Test/BufferVkTest.cpp +++ b/src/Magnum/Vk/Test/BufferVkTest.cpp @@ -25,6 +25,7 @@ #include "Magnum/Vk/Buffer.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" #include "Magnum/Vk/VulkanTester.h" @@ -37,13 +38,17 @@ struct BufferVkTest: VulkanTester { void constructMove(); void wrap(); + + void memoryRequirements(); }; BufferVkTest::BufferVkTest() { addTests({&BufferVkTest::construct, &BufferVkTest::constructMove, - &BufferVkTest::wrap}); + &BufferVkTest::wrap, + + &BufferVkTest::memoryRequirements}); } void BufferVkTest::construct() { @@ -92,6 +97,13 @@ void BufferVkTest::wrap() { device()->DestroyBuffer(device(), buffer, nullptr); } +void BufferVkTest::memoryRequirements() { + Buffer buffer{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 16384}, NoAllocate}; + + MemoryRequirements requirements = buffer.memoryRequirements(); + CORRADE_COMPARE(requirements.size(), 16384); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferVkTest) From 086f531f1709f6077497e2578b8a02e802c2f801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Nov 2020 20:01:32 +0100 Subject: [PATCH 73/85] Vk: binding Buffer memory. Again basically the same as with Image. --- src/Magnum/Vk/Buffer.cpp | 47 ++++++++++++++-- src/Magnum/Vk/Buffer.h | 45 +++++++++++++++- src/Magnum/Vk/CMakeLists.txt | 2 +- src/Magnum/Vk/Implementation/DeviceState.cpp | 3 ++ src/Magnum/Vk/Implementation/DeviceState.h | 1 + src/Magnum/Vk/Test/BufferTest.cpp | 22 +++++++- src/Magnum/Vk/Test/BufferVkTest.cpp | 57 +++++++++++++++++++- src/Magnum/Vk/Test/CMakeLists.txt | 2 +- 8 files changed, 170 insertions(+), 9 deletions(-) diff --git a/src/Magnum/Vk/Buffer.cpp b/src/Magnum/Vk/Buffer.cpp index ce4a469431..d20a76f234 100644 --- a/src/Magnum/Vk/Buffer.cpp +++ b/src/Magnum/Vk/Buffer.cpp @@ -28,7 +28,6 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" -#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Implementation/DeviceState.h" namespace Magnum { namespace Vk { @@ -58,13 +57,13 @@ Buffer Buffer::wrap(Device& device, const VkBuffer handle, const HandleFlags fla return out; } -Buffer::Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { +Buffer::Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT): _device{&device}, _flags{HandleFlag::DestroyOnDestruction}, _dedicatedMemory{NoCreate} { MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateBuffer(device, info, nullptr, &_handle)); } -Buffer::Buffer(NoCreateT): _device{}, _handle{} {} +Buffer::Buffer(NoCreateT): _device{}, _handle{}, _dedicatedMemory{NoCreate} {} -Buffer::Buffer(Buffer&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { +Buffer::Buffer(Buffer&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags}, _dedicatedMemory{std::move(other._dedicatedMemory)} { other._handle = {}; } @@ -78,6 +77,7 @@ Buffer& Buffer::operator=(Buffer&& other) noexcept { swap(other._device, _device); swap(other._handle, _handle); swap(other._flags, _flags); + swap(other._dedicatedMemory, _dedicatedMemory); return *this; } @@ -90,6 +90,32 @@ MemoryRequirements Buffer::memoryRequirements() const { return requirements; } +void Buffer::bindMemory(Memory& memory, const UnsignedLong offset) { + VkBindBufferMemoryInfo info{}; + info.sType = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO; + info.buffer = _handle; + info.memory = memory; + info.memoryOffset = offset; + _device->state().bindBufferMemoryImplementation(*_device, 1, &info); +} + +void Buffer::bindDedicatedMemory(Memory&& memory) { + bindMemory(memory, 0); + _dedicatedMemory = std::move(memory); +} + +bool Buffer::hasDedicatedMemory() const { + /* Sigh. Though better than needing to have `const handle()` overloads + returning `const VkDeviceMemory_T*` */ + return const_cast(*this)._dedicatedMemory.handle(); +} + +Memory& Buffer::dedicatedMemory() { + CORRADE_ASSERT(_dedicatedMemory.handle(), + "Vk::Buffer::dedicatedMemory(): buffer doesn't have a dedicated memory", _dedicatedMemory); + return _dedicatedMemory; +} + VkBuffer Buffer::release() { const VkBuffer handle = _handle; _handle = {}; @@ -108,4 +134,17 @@ void Buffer::getMemoryRequirementsImplementation11(Device& device, const VkBuffe device->GetBufferMemoryRequirements2(device, &info, &requirements); } +void Buffer::bindMemoryImplementationDefault(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* const infos) { + for(std::size_t i = 0; i != count; ++i) + device->BindBufferMemory(device, infos[i].buffer, infos[i].memory, infos[i].memoryOffset); +} + +void Buffer::bindMemoryImplementationKHR(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* const infos) { + device->BindBufferMemory2KHR(device, count, infos); +} + +void Buffer::bindMemoryImplementation11(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* const infos) { + device->BindBufferMemory2(device, count, infos); +} + }} diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h index b114742381..85eb8c65d6 100644 --- a/src/Magnum/Vk/Buffer.h +++ b/src/Magnum/Vk/Buffer.h @@ -34,6 +34,7 @@ #include "Magnum/Magnum.h" #include "Magnum/Tags.h" +#include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Vk.h" #include "Magnum/Vk/Vulkan.h" #include "Magnum/Vk/visibility.h" @@ -227,11 +228,48 @@ class MAGNUM_VK_EXPORT Buffer { /** * @brief Buffer memory requirements * - * @see @fn_vk_keyword{GetBufferMemoryRequirements2}, + * @see @ref bindMemory(), @fn_vk_keyword{GetBufferMemoryRequirements2}, * @fn_vk_keyword{GetBufferMemoryRequirements} */ MemoryRequirements memoryRequirements() const; + /** + * @brief Bind buffer memory + * + * Assumes that @p memory type, the amount of @p memory at @p offset + * and @p offset alignment corresponds to buffer memory requirements. + * @see @ref memoryRequirements(), @ref bindDedicatedMemory(), + * @fn_vk_keyword{BindBufferMemory2}, + * @fn_vk_keyword{BindBufferMemory} + */ + void bindMemory(Memory& memory, UnsignedLong offset); + + /** + * @brief Bind a dedicated buffer memory + * + * Equivalent to @ref bindMemory() with @p offset set to @cpp 0 @ce, + * with the additional effect that @p memory ownership transfers to the + * buffer and is then available through @ref dedicatedMemory(). + */ + void bindDedicatedMemory(Memory&& memory); + + /** + * @brief Whether the buffer has a dedicated memory + * + * Returns @cpp true @ce if the buffer memory was bound using + * @ref bindDedicatedMemory(), @cpp false @ce otherwise. + * @see @ref dedicatedMemory() + */ + bool hasDedicatedMemory() const; + + /** + * @brief Dedicated buffer memory + * + * Expects that the buffer has a dedicated memory. + * @see @ref hasDedicatedMemory() + */ + Memory& dedicatedMemory(); + /** * @brief Release the underlying Vulkan buffer * @@ -249,11 +287,16 @@ class MAGNUM_VK_EXPORT Buffer { MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementationKHR(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); MAGNUM_VK_LOCAL static void getMemoryRequirementsImplementation11(Device& device, const VkBufferMemoryRequirementsInfo2& info, VkMemoryRequirements2& requirements); + MAGNUM_VK_LOCAL static void bindMemoryImplementationDefault(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* infos); + MAGNUM_VK_LOCAL static void bindMemoryImplementationKHR(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* infos); + MAGNUM_VK_LOCAL static void bindMemoryImplementation11(Device& device, UnsignedInt count, const VkBindBufferMemoryInfo* infos); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; VkBuffer _handle; HandleFlags _flags; + Memory _dedicatedMemory; }; }} diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 21150bf0ea..5c10f74fbe 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -27,7 +27,6 @@ find_package(Vulkan REQUIRED) set(MagnumVk_SRCS - Buffer.cpp CommandBuffer.cpp CommandPool.cpp Extensions.cpp @@ -42,6 +41,7 @@ set(MagnumVk_SRCS Implementation/InstanceState.cpp) set(MagnumVk_GracefulAssert_SRCS + Buffer.cpp Device.cpp DeviceProperties.cpp Enums.cpp diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index aa91516921..1654df8ba6 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -53,10 +53,13 @@ DeviceState::DeviceState(Device& device) { if(device.isVersionSupported(Version::Vk11)) { bindImageMemoryImplementation = &Image::bindMemoryImplementation11; + bindBufferMemoryImplementation = &Buffer::bindMemoryImplementation11; } else if(device.isExtensionEnabled()) { bindImageMemoryImplementation = &Image::bindMemoryImplementationKHR; + bindBufferMemoryImplementation = &Buffer::bindMemoryImplementationKHR; } else { bindImageMemoryImplementation = &Image::bindMemoryImplementationDefault; + bindBufferMemoryImplementation = &Buffer::bindMemoryImplementationDefault; } } diff --git a/src/Magnum/Vk/Implementation/DeviceState.h b/src/Magnum/Vk/Implementation/DeviceState.h index 07a9b0f089..6b8f847116 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.h +++ b/src/Magnum/Vk/Implementation/DeviceState.h @@ -37,6 +37,7 @@ struct DeviceState { /** @todo put this eventually into a dedicated buffer / image state struct? */ void(*getBufferMemoryRequirementsImplementation)(Device&, const VkBufferMemoryRequirementsInfo2&, VkMemoryRequirements2&); void(*getImageMemoryRequirementsImplementation)(Device&, const VkImageMemoryRequirementsInfo2&, VkMemoryRequirements2&); + void(*bindBufferMemoryImplementation)(Device&, UnsignedInt, const VkBindBufferMemoryInfo*); void(*bindImageMemoryImplementation)(Device&, UnsignedInt, const VkBindImageMemoryInfo*); }; diff --git a/src/Magnum/Vk/Test/BufferTest.cpp b/src/Magnum/Vk/Test/BufferTest.cpp index 5281937dbe..28dd412afc 100644 --- a/src/Magnum/Vk/Test/BufferTest.cpp +++ b/src/Magnum/Vk/Test/BufferTest.cpp @@ -24,7 +24,9 @@ */ #include +#include #include +#include #include "Magnum/Vk/Buffer.h" @@ -39,6 +41,8 @@ struct BufferTest: TestSuite::Tester { void constructNoCreate(); void constructCopy(); + + void dedicatedMemoryNotDedicated(); }; BufferTest::BufferTest() { @@ -47,7 +51,9 @@ BufferTest::BufferTest() { &BufferTest::createInfoConstructFromVk, &BufferTest::constructNoCreate, - &BufferTest::constructCopy}); + &BufferTest::constructCopy, + + &BufferTest::dedicatedMemoryNotDedicated}); } void BufferTest::createInfoConstruct() { @@ -93,6 +99,20 @@ void BufferTest::constructCopy() { CORRADE_VERIFY(!(std::is_assignable{})); } +void BufferTest::dedicatedMemoryNotDedicated() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + Buffer buffer{NoCreate}; + CORRADE_VERIFY(!buffer.hasDedicatedMemory()); + + std::ostringstream out; + Error redirectError{&out}; + buffer.dedicatedMemory(); + CORRADE_COMPARE(out.str(), "Vk::Buffer::dedicatedMemory(): buffer doesn't have a dedicated memory\n"); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferTest) diff --git a/src/Magnum/Vk/Test/BufferVkTest.cpp b/src/Magnum/Vk/Test/BufferVkTest.cpp index 9714a3cac2..2430115b96 100644 --- a/src/Magnum/Vk/Test/BufferVkTest.cpp +++ b/src/Magnum/Vk/Test/BufferVkTest.cpp @@ -23,7 +23,10 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include "Magnum/Vk/Buffer.h" +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Memory.h" #include "Magnum/Vk/Result.h" @@ -40,6 +43,9 @@ struct BufferVkTest: VulkanTester { void wrap(); void memoryRequirements(); + + void bindMemory(); + void bindDedicatedMemory(); }; BufferVkTest::BufferVkTest() { @@ -48,7 +54,10 @@ BufferVkTest::BufferVkTest() { &BufferVkTest::wrap, - &BufferVkTest::memoryRequirements}); + &BufferVkTest::memoryRequirements, + + &BufferVkTest::bindMemory, + &BufferVkTest::bindDedicatedMemory}); } void BufferVkTest::construct() { @@ -66,17 +75,29 @@ void BufferVkTest::constructMove() { Buffer a{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 1024}, NoAllocate}; VkBuffer handle = a.handle(); + /* Verify that also the dedicated memory gets moved */ + MemoryRequirements requirements = a.memoryRequirements(); + a.bindDedicatedMemory(Vk::Memory{device(), Vk::MemoryAllocateInfo{requirements.size(), + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); + VkDeviceMemory memoryHandle = a.dedicatedMemory().handle(); + Buffer b = std::move(a); CORRADE_VERIFY(!a.handle()); + CORRADE_VERIFY(!a.hasDedicatedMemory()); CORRADE_COMPARE(b.handle(), handle); CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(b.hasDedicatedMemory()); + CORRADE_COMPARE(b.dedicatedMemory().handle(), memoryHandle); Buffer c{NoCreate}; c = std::move(b); CORRADE_VERIFY(!b.handle()); + CORRADE_VERIFY(!b.hasDedicatedMemory()); CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); CORRADE_COMPARE(c.handle(), handle); CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + CORRADE_VERIFY(c.hasDedicatedMemory()); + CORRADE_COMPARE(c.dedicatedMemory().handle(), memoryHandle); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); @@ -104,6 +125,40 @@ void BufferVkTest::memoryRequirements() { CORRADE_COMPARE(requirements.size(), 16384); } +void BufferVkTest::bindMemory() { + Buffer buffer{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 16384}, NoAllocate}; + MemoryRequirements requirements = buffer.memoryRequirements(); + + /* Similarly to the Image bindMemory() test, use a 128 kB offset */ + constexpr UnsignedLong offset = 128*1024; + CORRADE_COMPARE_AS(offset, requirements.alignment(), + TestSuite::Compare::Divisible); + + Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ + requirements.size() + offset, + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + + buffer.bindMemory(memory, offset); + CORRADE_VERIFY(!buffer.hasDedicatedMemory()); +} + +void BufferVkTest::bindDedicatedMemory() { + Buffer buffer{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 16384}, NoAllocate}; + MemoryRequirements requirements = buffer.memoryRequirements(); + + /** @todo expand once KHR_dedicated_allocation is implemented */ + + Vk::Memory memory{device(), Vk::MemoryAllocateInfo{ + requirements.size(), + device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}; + VkDeviceMemory handle = memory.handle(); + CORRADE_VERIFY(handle); + + buffer.bindDedicatedMemory(std::move(memory)); + CORRADE_VERIFY(buffer.hasDedicatedMemory()); + CORRADE_COMPARE(buffer.dedicatedMemory().handle(), handle); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferVkTest) diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 5ac6b97274..7b92ec028d 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -24,7 +24,7 @@ # DEALINGS IN THE SOFTWARE. # -corrade_add_test(VkBufferTest BufferTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkBufferTest BufferTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkCommandBufferTest CommandBufferTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkCommandPoolTest CommandPoolTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkDeviceTest DeviceTest.cpp LIBRARIES MagnumVk) From d53dc06e9cf72e902ed10d3d8152187b15550199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Nov 2020 20:07:27 +0100 Subject: [PATCH 74/85] Vk: ability to allocate Buffer directly during construction. Again mirrors what's done in Image. --- src/Magnum/Vk/Buffer.cpp | 9 +++++++++ src/Magnum/Vk/Buffer.h | 18 +++++++++++++++++- src/Magnum/Vk/Test/BufferVkTest.cpp | 24 +++++++++++++++++------- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/Magnum/Vk/Buffer.cpp b/src/Magnum/Vk/Buffer.cpp index d20a76f234..2acd5377e1 100644 --- a/src/Magnum/Vk/Buffer.cpp +++ b/src/Magnum/Vk/Buffer.cpp @@ -27,6 +27,7 @@ #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" +#include "Magnum/Vk/DeviceProperties.h" #include "Magnum/Vk/Handle.h" #include "Magnum/Vk/Implementation/DeviceState.h" @@ -61,6 +62,14 @@ Buffer::Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT): _devi MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateBuffer(device, info, nullptr, &_handle)); } +Buffer::Buffer(Device& device, const BufferCreateInfo& info, const MemoryFlags memoryFlags): Buffer{device, info, NoAllocate} { + const MemoryRequirements requirements = memoryRequirements(); + bindDedicatedMemory(Memory{device, MemoryAllocateInfo{ + requirements.size(), + device.properties().pickMemory(memoryFlags, requirements.memories()) + }}); +} + Buffer::Buffer(NoCreateT): _device{}, _handle{}, _dedicatedMemory{NoCreate} {} Buffer::Buffer(Buffer&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags}, _dedicatedMemory{std::move(other._dedicatedMemory)} { diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h index 85eb8c65d6..f13edcb170 100644 --- a/src/Magnum/Vk/Buffer.h +++ b/src/Magnum/Vk/Buffer.h @@ -182,10 +182,26 @@ class MAGNUM_VK_EXPORT Buffer { * @param device Vulkan device to create the buffer on * @param info Buffer creation info * - * @see @fn_vk_keyword{CreateBuffer} + * @see @ref Buffer(Device&, const BufferCreateInfo&, MemoryFlags), + * @fn_vk_keyword{CreateBuffer} */ explicit Buffer(Device& device, const BufferCreateInfo& info, NoAllocateT); + /** + * @brief Construct a buffer + * @param device Vulkan device to create the buffer on + * @param info Buffer creation info + * @param memoryFlags Memory allocation flags + * + * Compared to @ref Buffer(Device&, const BufferCreateInfo&, NoAllocateT) + * allocates a memory satisfying @p memoryFlags as well. + * + * @attention At this point, a dedicated allocation is used, + * subsequently accessible through @ref dedicatedMemory(). This + * behavior may change in the future. + */ + explicit Buffer(Device& device, const BufferCreateInfo& info, MemoryFlags memoryFlags); + /** * @brief Construct without creating the buffer * diff --git a/src/Magnum/Vk/Test/BufferVkTest.cpp b/src/Magnum/Vk/Test/BufferVkTest.cpp index 2430115b96..ab75a50a71 100644 --- a/src/Magnum/Vk/Test/BufferVkTest.cpp +++ b/src/Magnum/Vk/Test/BufferVkTest.cpp @@ -46,6 +46,8 @@ struct BufferVkTest: VulkanTester { void bindMemory(); void bindDedicatedMemory(); + + void directAllocation(); }; BufferVkTest::BufferVkTest() { @@ -57,7 +59,9 @@ BufferVkTest::BufferVkTest() { &BufferVkTest::memoryRequirements, &BufferVkTest::bindMemory, - &BufferVkTest::bindDedicatedMemory}); + &BufferVkTest::bindDedicatedMemory, + + &BufferVkTest::directAllocation}); } void BufferVkTest::construct() { @@ -72,13 +76,9 @@ void BufferVkTest::construct() { } void BufferVkTest::constructMove() { - Buffer a{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 1024}, NoAllocate}; - VkBuffer handle = a.handle(); - /* Verify that also the dedicated memory gets moved */ - MemoryRequirements requirements = a.memoryRequirements(); - a.bindDedicatedMemory(Vk::Memory{device(), Vk::MemoryAllocateInfo{requirements.size(), - device().properties().pickMemory(Vk::MemoryFlag::DeviceLocal, requirements.memories())}}); + Buffer a{device(), BufferCreateInfo{BufferUsage::StorageBuffer, 1024}, Vk::MemoryFlag::DeviceLocal}; + VkBuffer handle = a.handle(); VkDeviceMemory memoryHandle = a.dedicatedMemory().handle(); Buffer b = std::move(a); @@ -159,6 +159,16 @@ void BufferVkTest::bindDedicatedMemory() { CORRADE_COMPARE(buffer.dedicatedMemory().handle(), handle); } +void BufferVkTest::directAllocation() { + Buffer buffer{device(), + BufferCreateInfo{BufferUsage::StorageBuffer, 16384}, + Vk::MemoryFlag::DeviceLocal}; + + /* Not sure what else to test here */ + CORRADE_VERIFY(buffer.hasDedicatedMemory()); + CORRADE_VERIFY(buffer.dedicatedMemory().handle()); +} + }}}} CORRADE_TEST_MAIN(Magnum::Vk::Test::BufferVkTest) From ae3dbe1c36f26985407be9cc2a00a3e994b92782 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 21 Nov 2020 19:18:33 +0100 Subject: [PATCH 75/85] Vk: Shader wrapper. This was easy ... except for the fun with taking ownership of the binary. And it led to me inventing CORRADE_INTERNAL_ASSERT_EXPRESSION(). --- doc/snippets/MagnumVk.cpp | 16 ++ doc/vulkan-mapping.dox | 12 +- src/Magnum/Vk/CMakeLists.txt | 2 + src/Magnum/Vk/Shader.cpp | 117 ++++++++++ src/Magnum/Vk/Shader.h | 286 ++++++++++++++++++++++++ src/Magnum/Vk/Test/.gitattributes | 10 + src/Magnum/Vk/Test/CMakeLists.txt | 12 + src/Magnum/Vk/Test/ShaderTest.cpp | 187 ++++++++++++++++ src/Magnum/Vk/Test/ShaderVkTest.cpp | 115 ++++++++++ src/Magnum/Vk/Test/configure.h.cmake | 26 +++ src/Magnum/Vk/Test/triangle-shaders.spv | Bin 0 -> 536 bytes 11 files changed, 781 insertions(+), 2 deletions(-) create mode 100644 src/Magnum/Vk/Shader.cpp create mode 100644 src/Magnum/Vk/Shader.h create mode 100644 src/Magnum/Vk/Test/.gitattributes create mode 100644 src/Magnum/Vk/Test/ShaderTest.cpp create mode 100644 src/Magnum/Vk/Test/ShaderVkTest.cpp create mode 100644 src/Magnum/Vk/Test/configure.h.cmake create mode 100644 src/Magnum/Vk/Test/triangle-shaders.spv diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index d243a0cd6b..0af2940128 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -23,7 +23,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" @@ -37,6 +39,7 @@ #include "Magnum/Vk/Integration.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Queue.h" +#include "Magnum/Vk/Shader.h" #include "MagnumExternal/Vulkan/flextVkGlobal.h" using namespace Magnum; @@ -256,6 +259,19 @@ if(instance.isExtensionEnabled()) { /* [Instance-isExtensionEnabled] */ } +{ +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; +/* [Shader-usage] */ +Vk::ShaderCreateInfo info{ + CORRADE_INTERNAL_ASSERT_EXPRESSION(Utility::Directory::read("shader.spv")) +}; + +DOXYGEN_IGNORE() + +Vk::Shader shader{device, info}; +/* [Shader-usage] */ +} + { /* [Integration] */ VkOffset2D a{64, 32}; diff --git a/doc/vulkan-mapping.dox b/doc/vulkan-mapping.dox index c9dcebe311..edc2e709dd 100644 --- a/doc/vulkan-mapping.dox +++ b/doc/vulkan-mapping.dox @@ -62,7 +62,7 @@ Vulkan function | Matching API Vulkan function | Matching API --------------------------------------- | ------------ @fn_vk{BeginCommandBuffer}, \n \fn_vk{EndCommandBuffer} | | -@fn_vk{BindBufferMemory}, \n @fn_vk{BindBufferMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | | +@fn_vk{BindBufferMemory}, \n @fn_vk{BindBufferMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Buffer::bindMemory() @fn_vk{BindImageMemory}, \n @fn_vk{BindImageMemory2} @m_class{m-label m-flat m-success} **KHR, 1.1** | @ref Image::bindMemory() @subsection vulkan-mapping-functions-c C @@ -145,7 +145,7 @@ Vulkan function | Matching API @fn_vk{CreateSampler}, \n @fn_vk{DestroySampler} | | @fn_vk{CreateSamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** , \n @fn_vk{DestroySamplerYcbcrConversion} @m_class{m-label m-flat m-success} **KHR, 1.1** | | @fn_vk{CreateSemaphore}, \n @fn_vk{DestroySemaphore} | | -@fn_vk{CreateShaderModule}, \n @fn_vk{DestroyShaderModule} | | +@fn_vk{CreateShaderModule}, \n @fn_vk{DestroyShaderModule} | @ref Shader constructor and destructor @subsection vulkan-mapping-functions-d D @@ -349,6 +349,14 @@ Vulkan structure | Matching API --------------------------------------- | ------------ @type_vk{MemoryAllocateInfo} | @ref MemoryAllocateInfo +@subsection vulkan-mapping-structures-s S + +@m_class{m-fullwidth} + +Vulkan structure | Matching API +--------------------------------------- | ------------ +@type_vk{ShaderCreateInfo} | @ref ShaderCreateInfo + */ }} diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 5c10f74fbe..930cc768ee 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -34,6 +34,7 @@ set(MagnumVk_SRCS Instance.cpp Memory.cpp Result.cpp + Shader.cpp Version.cpp Implementation/Arguments.cpp @@ -67,6 +68,7 @@ set(MagnumVk_HEADERS Memory.h Queue.h Result.h + Shader.h TypeTraits.h Version.h Vk.h diff --git a/src/Magnum/Vk/Shader.cpp b/src/Magnum/Vk/Shader.cpp new file mode 100644 index 0000000000..ad7995c9ab --- /dev/null +++ b/src/Magnum/Vk/Shader.cpp @@ -0,0 +1,117 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include "Shader.h" + +#include + +#include "Magnum/Vk/Assert.h" +#include "Magnum/Vk/Device.h" +#include "Magnum/Vk/Handle.h" + +namespace Magnum { namespace Vk { + +ShaderCreateInfo::ShaderCreateInfo(Containers::ArrayView code, Flags flags): _info{} { + _info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + _info.flags = VkShaderModuleCreateFlags(flags); + _info.pCode = reinterpret_cast(code.data()); + /* Yes, the size is in bytes, while the code pointer is an int. Have fun + explaining this in every damn Vulkan tutorial. */ + _info.codeSize = code.size(); +} + +ShaderCreateInfo::ShaderCreateInfo(NoInitT) noexcept {} + +ShaderCreateInfo::ShaderCreateInfo(const VkShaderModuleCreateInfo& info): + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(info) {} + +ShaderCreateInfo::ShaderCreateInfo(ShaderCreateInfo&& other) noexcept: + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + _info(other._info), + _originalDeleter{other._originalDeleter}, _deleter{other._deleter} +{ + /* If we have a deleter and thus own the array, clear the original data + pointer as well as it inevitably becomes dangling */ + if(_deleter) { + other._info.pCode = {}; + other._info.codeSize = {}; + } + other._originalDeleter = {}; + other._deleter = {}; +} + +ShaderCreateInfo::~ShaderCreateInfo() { + if(_deleter) _deleter(_originalDeleter, _info.pCode, _info.codeSize); +} + +ShaderCreateInfo& ShaderCreateInfo::operator=(ShaderCreateInfo&& other) noexcept { + using std::swap; + swap(other._info, _info); + swap(other._originalDeleter, _originalDeleter); + swap(other._deleter, _deleter); + return *this; +} + +Shader Shader::wrap(Device& device, const VkShaderModule handle, const HandleFlags flags) { + Shader out{NoCreate}; + out._device = &device; + out._handle = handle; + out._flags = flags; + return out; +} + +Shader::Shader(Device& device, const ShaderCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateShaderModule(device, info, nullptr, &_handle)); +} + +Shader::Shader(NoCreateT): _device{}, _handle{} {} + +Shader::Shader(Shader&& other) noexcept: _device{other._device}, _handle{other._handle}, _flags{other._flags} { + other._handle = {}; +} + +Shader::~Shader() { + if(_handle && (_flags & HandleFlag::DestroyOnDestruction)) + (**_device).DestroyShaderModule(*_device, _handle, nullptr); +} + +Shader& Shader::operator=(Shader&& other) noexcept { + using std::swap; + swap(other._device, _device); + swap(other._handle, _handle); + swap(other._flags, _flags); + return *this; +} + +VkShaderModule Shader::release() { + const VkShaderModule handle = _handle; + _handle = {}; + return handle; +} + +}} diff --git a/src/Magnum/Vk/Shader.h b/src/Magnum/Vk/Shader.h new file mode 100644 index 0000000000..8bf9230cfb --- /dev/null +++ b/src/Magnum/Vk/Shader.h @@ -0,0 +1,286 @@ +#ifndef Magnum_Vk_Shader_h +#define Magnum_Vk_Shader_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +/** @file + * @brief Class @ref Magnum::Vk::ShaderCreateInfo, @ref Magnum::Vk::Shader + * @m_since_latest + */ + +#include + +#include "Magnum/Magnum.h" +#include "Magnum/Tags.h" +#include "Magnum/Vk/Vk.h" +#include "Magnum/Vk/Vulkan.h" +#include "Magnum/Vk/visibility.h" + +namespace Magnum { namespace Vk { + +/** +@brief Shader creation info +@m_since_latest + +Wraps a @type_vk_keyword{ShaderModuleCreateInfo}. See @ref Shader for usage +information. +*/ +class MAGNUM_VK_EXPORT ShaderCreateInfo { + public: + /** + * @brief Shader creation flag + * + * Wraps @type_vk_keyword{ShaderModuleCreateFlagBits}. + * @see @ref Flags, @ref ShaderCreateInfo() + * @m_enum_values_as_keywords + */ + enum class Flag: UnsignedInt {}; + + /** + * @brief Shader creation flags + * + * Type-safe wrapper for @type_vk_keyword{ShaderModuleCreateFlags}. + * @see @ref ShaderCreateInfo() + */ + typedef Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param code Shader code + * @param flags Shader creation flags + * + * The following @type_vk{ShaderModuleCreateInfo} fields are pre-filled + * in addition to `sType`, everything else is zero-filled: + * + * - `flags` + * - `pCode` and `codeSize` to @p code + * + * @attention The class doesn't make any copy of @p code, so you either + * have to ensure it stays in scope until @ref Shader is + * constructed, or instantiate a temporary @ref ShaderCreateInfo + * directly in @ref Shader constructor call expression, as shown + * in its usage docs. If you have the data in + * @ref Corrade::Containers::Array, there's also + * @ref ShaderCreateInfo(Containers::Array&&, Flags). + */ + explicit ShaderCreateInfo(Containers::ArrayView code, Flags flags = {}); + + /** + * @brief Construct taking ownership of a r-value array instance + * + * Behaves like @ref ShaderCreateInfo(Containers::ArrayView, Flags) + * but in addition ensures @p code stays in scope until @ref Shader is + * created, deleting it on destruction. The cleanup relies on the + * pointer and size stored in @type_vk{ShaderModuleCreateInfo}, + * changing the `pCode` and `codeSize` members afterwards may result in + * memory corruption. + */ + template explicit ShaderCreateInfo(Containers::Array&& code, Flags flags = {}); + + /** + * @overload + * + * Sorry, custom @ref Corrade::Containers::Array deleter types can't be + * taken over. + */ + template explicit ShaderCreateInfo(Containers::Array&& code, Flags flags = {}) = delete; + + /** + * @brief Construct without initializing the contents + * + * Note that not even the `sType` field is set --- the structure has to + * be fully initialized afterwards in order to be usable. + */ + explicit ShaderCreateInfo(NoInitT) noexcept; + + /** + * @brief Construct from existing data + * + * Copies the existing values verbatim, pointers are kept unchanged + * without taking over the ownership. Modifying the newly created + * instance will not modify the original data nor the pointed-to data. + */ + explicit ShaderCreateInfo(const VkShaderModuleCreateInfo& info); + + /** @brief Copying is not allowed */ + ShaderCreateInfo(const ShaderCreateInfo&) = delete; + + /** @brief Move constructor */ + ShaderCreateInfo(ShaderCreateInfo&& other) noexcept; + + /** + * @brief Destructor + * + * If the @ref ShaderCreateInfo(Containers::Array&&, Flags) + * constructor was used, calls deleter on the stored array. + */ + ~ShaderCreateInfo(); + + /** @brief Copying is not allowed */ + ShaderCreateInfo& operator=(const ShaderCreateInfo&) = delete; + + /** @brief Move assignment */ + ShaderCreateInfo& operator=(ShaderCreateInfo&& other) noexcept; + + /** @brief Underlying @type_vk{ShaderModuleCreateInfo} structure */ + VkShaderModuleCreateInfo& operator*() { return _info; } + /** @overload */ + const VkShaderModuleCreateInfo& operator*() const { return _info; } + /** @overload */ + VkShaderModuleCreateInfo* operator->() { return &_info; } + /** @overload */ + const VkShaderModuleCreateInfo* operator->() const { return &_info; } + /** @overload */ + operator const VkShaderModuleCreateInfo*() const { return &_info; } + + private: + VkShaderModuleCreateInfo _info; + + /* Used by the Array&& constructor. Instead of wrapping an Array of an + arbitrary type we just take its deleter, the pointer + size pair is + stored in _info already. */ + void(*_originalDeleter)(){}; + void(*_deleter)(void(*)(), const void*, std::size_t){}; +}; + +CORRADE_ENUMSET_OPERATORS(ShaderCreateInfo::Flags) + +/** +@brief Shader +@m_since_latest + +Wraps a @type_vk_keyword{ShaderModule}. + +@section Vk-Shader-usage Usage + +The @ref ShaderCreateInfo structure takes a single required parameter, which is +the SPIR-V binary. Besides accepting a @ref Corrade::Containers::ArrayView, +to which any container is convertible, it can also take ownership of a +@ref Corrade::Containers::Array, which means you don't need to worry about +keeping a loaded file in scope until it's consumed by the @ref Shader +constructor: + +@snippet MagnumVk.cpp Shader-usage +*/ +class MAGNUM_VK_EXPORT Shader { + public: + /** + * @brief Wrap existing Vulkan handle + * @param device Vulkan device the shader is created on + * @param handle The @type_vk{ShaderModule} handle + * @param flags Handle flags + * + * The @p handle is expected to be originating from @p device. Unlike + * a shader created using a constructor, the Vulkan shader is by + * default not deleted on destruction, use @p flags for different + * behavior. + * @see @ref release() + */ + static Shader wrap(Device& device, VkShaderModule handle, HandleFlags flags = {}); + + /** + * @brief Constructor + * @param device Vulkan device to create the shader on + * @param info Shader creation info + * + * @see @fn_vk_keyword{CreateShaderModule} + */ + explicit Shader(Device& device, const ShaderCreateInfo& info); + + /** + * @brief Construct without creating the shader + * + * The constructed instance is equivalent to moved-from state. Useful + * in cases where you will overwrite the instance later anyway. Move + * another object over it to make it useful. + */ + explicit Shader(NoCreateT); + + /** @brief Copying is not allowed */ + Shader(const Shader&) = delete; + + /** @brief Move constructor */ + Shader(Shader&& other) noexcept; + + /** + * @brief Destructor + * + * Destroys associated @type_vk{ShaderModule} handle, unless the + * instance was created using @ref wrap() without + * @ref HandleFlag::DestroyOnDestruction specified. + * @see @fn_vk_keyword{DestroyShaderModule}, @ref release() + */ + ~Shader(); + + /** @brief Copying is not allowed */ + Shader& operator=(const Shader&) = delete; + + /** @brief Move assignment */ + Shader& operator=(Shader&& other) noexcept; + + /** @brief Underlying @type_vk{Buffer} handle */ + VkShaderModule handle() { return _handle; } + /** @overload */ + operator VkShaderModule() { return _handle; } + + /** @brief Handle flags */ + HandleFlags handleFlags() const { return _flags; } + + /** + * @brief Release the underlying Vulkan shader + * + * Releases ownership of the Vulkan shader and returns its handle so + * @fn_vk{DestroyShaderModule} is not called on destruction. The + * internal state is then equivalent to moved-from state. + * @see @ref wrap() + */ + VkShaderModule release(); + + private: + /* Can't be a reference because of the NoCreate constructor */ + Device* _device; + + VkShaderModule _handle; + HandleFlags _flags; +}; + +template /*implicit*/ ShaderCreateInfo::ShaderCreateInfo(Containers::Array&& code, Flags flags): ShaderCreateInfo{code, flags} { + /* Remember the deleter. The pointer and size is stored in + VkShaderModuleCreateInfo and we assume it won't get stomped on + afterwards */ + _originalDeleter = reinterpret_cast(code.deleter() ? code.deleter() : static_cast([](T* data, std::size_t) { delete[] data; })); + _deleter = [](void(*originalDeleter)(), const void* data, std::size_t size) { + /* VkShaderModuleCreateInfo stores the size in bytes, convert it back + to the element count for the deleter */ + reinterpret_cast(originalDeleter)(reinterpret_cast(const_cast(data)), size/sizeof(T)); + }; + + /* Release the original array so the deleter isn't called too early */ + code.release(); +} + +}} + +#endif diff --git a/src/Magnum/Vk/Test/.gitattributes b/src/Magnum/Vk/Test/.gitattributes new file mode 100644 index 0000000000..d715cd5b7a --- /dev/null +++ b/src/Magnum/Vk/Test/.gitattributes @@ -0,0 +1,10 @@ +# You have to add the following to your .git/config or global +# ~/.gitconfig to make the binary diffs work (without the comment +# character, of course). Assumes spirv-dis exists: +# +# [diff "spirv"] +# textconv = spirv-dis +# binary = true +# + +*.spv binary diff=spirv diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 7b92ec028d..06a8efb5e1 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -39,6 +39,7 @@ corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkShaderTest ShaderTest.cpp LIBRARIES MagnumVk) add_library(VkAssertTestObjects OBJECT AssertTest.cpp) target_include_directories(VkAssertTestObjects PRIVATE $) @@ -95,6 +96,15 @@ target_compile_definitions(VkAssertStandardDisabledTest PRIVATE corrade_add_test(VkVersionTest VersionTest.cpp LIBRARIES MagnumVk) if(BUILD_VK_TESTS) + if(CORRADE_TARGET_ANDROID) + set(VK_TEST_DIR ".") + else() + set(VK_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/configure.h) + corrade_add_test(VkBufferVkTest BufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandBufferVkTest CommandBufferVkTest.cpp LIBRARIES MagnumVulkanTester) corrade_add_test(VkCommandPoolVkTest CommandPoolVkTest.cpp LIBRARIES MagnumVulkanTester) @@ -105,5 +115,7 @@ if(BUILD_VK_TESTS) corrade_add_test(VkImageVkTest ImageVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) corrade_add_test(VkInstanceVkTest InstanceVkTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkMemoryVkTest MemoryVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + corrade_add_test(VkShaderVkTest ShaderVkTest.cpp LIBRARIES MagnumVk MagnumVulkanTester) + target_include_directories(VkShaderVkTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(VkVersionVkTest VersionVkTest.cpp LIBRARIES MagnumVk) endif() diff --git a/src/Magnum/Vk/Test/ShaderTest.cpp b/src/Magnum/Vk/Test/ShaderTest.cpp new file mode 100644 index 0000000000..a32bae3a97 --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderTest.cpp @@ -0,0 +1,187 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include + +#include "Magnum/Vk/Shader.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ShaderTest: TestSuite::Tester { + explicit ShaderTest(); + + void createInfoConstruct(); + void createInfoConstructTransferOwnership(); + void createInfoConstructNoInit(); + void createInfoConstructFromVk(); + void createInfoConstructCopy(); + void createInfoConstructMove(); + + void constructNoCreate(); + void constructCopy(); +}; + +ShaderTest::ShaderTest() { + addTests({&ShaderTest::createInfoConstruct, + &ShaderTest::createInfoConstructTransferOwnership, + &ShaderTest::createInfoConstructNoInit, + &ShaderTest::createInfoConstructFromVk, + &ShaderTest::createInfoConstructCopy, + &ShaderTest::createInfoConstructMove, + + &ShaderTest::constructNoCreate, + &ShaderTest::constructCopy}); +} + +void ShaderTest::createInfoConstruct() { + const UnsignedInt data[] { 0xdead, 0xbee5, 0xbaba }; + + ShaderCreateInfo info{data}; + CORRADE_COMPARE(info->flags, 0); + CORRADE_COMPARE(info->pCode, data); + CORRADE_COMPARE(info->codeSize, 3*4); +} + +Int destructorCallCount = 0; +std::size_t destructedSize = 0; +void* destructedData = nullptr; + +void ShaderTest::createInfoConstructTransferOwnership() { + destructorCallCount = 0; + destructedData = nullptr; + destructedSize = 0; + UnsignedInt data[] { 0xdead, 0xbee5, 0xbaba }; + + { + ShaderCreateInfo info{Containers::Array{data, 3, [](UnsignedInt* data, std::size_t size) { + /* This is called in a destructor and exceptions aren't allowed in + a destructor, which means we can't use CORRADE_COMPARE() */ + destructedData = data; + destructedSize = size; + ++destructorCallCount; + }}}; + + CORRADE_COMPARE(info->pCode, data); + CORRADE_COMPARE(info->codeSize, 3*4); + CORRADE_COMPARE(destructorCallCount, 0); + } + + CORRADE_COMPARE(destructorCallCount, 1); + CORRADE_COMPARE(destructedData, static_cast(data)); + CORRADE_COMPARE(destructedSize, 3); +} + +void ShaderTest::createInfoConstructNoInit() { + { + ShaderCreateInfo info{NoInit}; + info->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + new(&info) ShaderCreateInfo{NoInit}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); + } + + /* The deleter should be zero-init'd and thus no function called on + destruction */ + + CORRADE_VERIFY((std::is_nothrow_constructible::value)); + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ShaderTest::createInfoConstructFromVk() { + VkShaderModuleCreateInfo vkInfo; + vkInfo.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + + ShaderCreateInfo info{vkInfo}; + CORRADE_COMPARE(info->sType, VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2); +} + +void ShaderTest::createInfoConstructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void ShaderTest::createInfoConstructMove() { + destructorCallCount = 0; + destructedData = nullptr; + destructedSize = 0; + UnsignedInt data[] { 0xdead, 0xbee5, 0xbaba }; + + { + ShaderCreateInfo a{Containers::Array{data, 3, [](UnsignedInt* data, std::size_t size) { + /* This is called in a destructor and exceptions aren't allowed in + a destructor, which means we can't use CORRADE_COMPARE() */ + destructedData = data; + destructedSize = size; + ++destructorCallCount; + }}}; + CORRADE_COMPARE(destructorCallCount, 0); + + /* Besides the deleter, the original code pointer and size should also + be cleared because it will inevitably become dangling */ + ShaderCreateInfo b = std::move(a); + CORRADE_VERIFY(!a->pCode); + CORRADE_COMPARE(a->codeSize, 0); + CORRADE_COMPARE(b->pCode, data); + CORRADE_COMPARE(b->codeSize, 3*4); + CORRADE_COMPARE(destructorCallCount, 0); + + ShaderCreateInfo c{NoInit}; + c->pCode = &data[1]; + c->codeSize = 2; + c = std::move(b); + /* It just swaps, so the moved-from instance doesn't have the code + pointer cleared */ + CORRADE_COMPARE(b->pCode, &data[1]); + CORRADE_COMPARE(b->codeSize, 2); + CORRADE_COMPARE(c->pCode, data); + CORRADE_COMPARE(c->codeSize, 3*4); + CORRADE_COMPARE(destructorCallCount, 0); + } + + CORRADE_COMPARE(destructorCallCount, 1); + CORRADE_COMPARE(destructedData, static_cast(data)); + CORRADE_COMPARE(destructedSize, 3); +} + +void ShaderTest::constructNoCreate() { + { + Shader shader{NoCreate}; + CORRADE_VERIFY(!shader.handle()); + } + + /* Implicit construction is not allowed */ + CORRADE_VERIFY(!(std::is_convertible::value)); +} + +void ShaderTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ShaderTest) diff --git a/src/Magnum/Vk/Test/ShaderVkTest.cpp b/src/Magnum/Vk/Test/ShaderVkTest.cpp new file mode 100644 index 0000000000..a44ca5d6c4 --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderVkTest.cpp @@ -0,0 +1,115 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#include +#include +#include +#include + +#include "Magnum/Vk/Shader.h" +#include "Magnum/Vk/DeviceProperties.h" +#include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Memory.h" +#include "Magnum/Vk/Result.h" +#include "Magnum/Vk/VulkanTester.h" + +#include "configure.h" + +namespace Magnum { namespace Vk { namespace Test { namespace { + +struct ShaderVkTest: VulkanTester { + explicit ShaderVkTest(); + + void construct(); + void constructMove(); + + void wrap(); +}; + +ShaderVkTest::ShaderVkTest() { + addTests({&ShaderVkTest::construct, + &ShaderVkTest::constructMove, + + &ShaderVkTest::wrap}); +} + +void ShaderVkTest::construct() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")); + CORRADE_VERIFY(data); + + { + Shader shader{device(), ShaderCreateInfo{data}}; + CORRADE_VERIFY(shader.handle()); + CORRADE_COMPARE(shader.handleFlags(), HandleFlag::DestroyOnDestruction); + } + + /* Shouldn't crash or anything */ + CORRADE_VERIFY(true); +} + +void ShaderVkTest::constructMove() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")); + CORRADE_VERIFY(data); + + Shader a{device(), ShaderCreateInfo{data}}; + VkShaderModule handle = a.handle(); + + Shader b = std::move(a); + CORRADE_VERIFY(!a.handle()); + CORRADE_COMPARE(b.handle(), handle); + CORRADE_COMPARE(b.handleFlags(), HandleFlag::DestroyOnDestruction); + + Shader c{NoCreate}; + c = std::move(b); + CORRADE_VERIFY(!b.handle()); + CORRADE_COMPARE(b.handleFlags(), HandleFlags{}); + CORRADE_COMPARE(c.handle(), handle); + CORRADE_COMPARE(c.handleFlags(), HandleFlag::DestroyOnDestruction); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void ShaderVkTest::wrap() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "triangle-shaders.spv")); + CORRADE_VERIFY(data); + + VkShaderModule shader{}; + CORRADE_COMPARE(Result(device()->CreateShaderModule(device(), + ShaderCreateInfo{data}, + nullptr, &shader)), Result::Success); + + auto wrapped = Shader::wrap(device(), shader, HandleFlag::DestroyOnDestruction); + CORRADE_COMPARE(wrapped.handle(), shader); + + /* Release the handle again, destroy by hand */ + CORRADE_COMPARE(wrapped.release(), shader); + CORRADE_VERIFY(!wrapped.handle()); + device()->DestroyShaderModule(device(), shader, nullptr); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Vk::Test::ShaderVkTest) diff --git a/src/Magnum/Vk/Test/configure.h.cmake b/src/Magnum/Vk/Test/configure.h.cmake new file mode 100644 index 0000000000..a03b1b5bf5 --- /dev/null +++ b/src/Magnum/Vk/Test/configure.h.cmake @@ -0,0 +1,26 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + 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 OR COPYRIGHT HOLDERS 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. +*/ + +#define VK_TEST_DIR "${VK_TEST_DIR}" diff --git a/src/Magnum/Vk/Test/triangle-shaders.spv b/src/Magnum/Vk/Test/triangle-shaders.spv new file mode 100644 index 0000000000000000000000000000000000000000..a8be29a099ae92116ac6b218f525e8c5d7e4a2a8 GIT binary patch literal 536 zcmYk2NeTi{3`FBR+fL|C6z?E{=+<+%&;z*e>}~|#`xr5hpHzlQbvNp}PDCvtI Date: Sat, 21 Nov 2020 22:24:26 +0100 Subject: [PATCH 76/85] doc: this is hidden anyway, so it can be NoCreate as well. --- doc/snippets/MagnumVk.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 0af2940128..a40746b408 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -64,7 +64,6 @@ info->pNext = &validationFeatures; } { -Vk::Instance instance; /* [CommandPool-usage] */ Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; @@ -125,7 +124,7 @@ DOXYGEN_IGNORE() Vk::Instance instance; VkQueryPool pool{}; /* [Device-function-pointers] */ -Vk::Device device{DOXYGEN_IGNORE(instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)})}; +Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; // ... device->ResetQueryPoolEXT(device, DOXYGEN_IGNORE(pool, 0, 0)); From 7bc4e8373387865b3add27b9b999a46db0b07ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 23 Nov 2020 19:08:42 +0100 Subject: [PATCH 77/85] doc: property retrieval optimization isn't essentially needed. Thus it has no place in the general overview docs of Vk::Instance and Vk::Device. Better to put it into the VUlkan wrapping docs, where it also makes sense to have both the device and instance side together. --- doc/snippets/MagnumVk.cpp | 54 ++++++++++++++++++++++++++++++++++++++- doc/vulkan-wrapping.dox | 34 ++++++++++++++++++++++++ src/Magnum/Vk/Device.h | 6 ++--- src/Magnum/Vk/Instance.h | 6 ++--- 4 files changed, 93 insertions(+), 7 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index a40746b408..5ee24d0593 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -63,6 +63,58 @@ info->pNext = &validationFeatures; /* [wrapping-extending-create-info] */ } +{ +using namespace Containers::Literals; +int argc{}; +char** argv{}; +/* [wrapping-optimizing-properties-instance] */ +Vk::LayerProperties layers = DOXYGEN_IGNORE(Vk::enumerateLayerProperties()); +Vk::InstanceExtensionProperties extensions = DOXYGEN_IGNORE(Vk::enumerateInstanceExtensionProperties(layers.names())); + +/* Pass the layer and extension properties for use by InstanceCreateInfo */ +Vk::InstanceCreateInfo info{argc, argv, &layers, &extensions}; +if(layers.isSupported("VK_LAYER_KHRONOS_validation"_s)) + info.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}); +if(extensions.isSupported()) + info.addEnabledExtensions(); +DOXYGEN_IGNORE() + +Vk::Instance instance{info}; +/* [wrapping-optimizing-properties-instance] */ +} + +{ +Vk::Instance instance{NoCreate}; +Vk::Queue queue{NoCreate}; +/* [wrapping-optimizing-properties-device-single-expression] */ +Vk::Device device{instance, Vk::DeviceCreateInfo{Vk::pickDevice(instance)} + .addQueues(DOXYGEN_IGNORE(Vk::QueueFlag::Graphics, {0.0f}, {queue})) + DOXYGEN_IGNORE() +}; +/* [wrapping-optimizing-properties-device-single-expression] */ +} + +{ +using namespace Containers::Literals; +Vk::Instance instance{NoCreate}; +/* [wrapping-optimizing-properties-device-move] */ +Vk::DeviceProperties properties = Vk::pickDevice(instance); +Vk::ExtensionProperties extensions = properties.enumerateExtensionProperties(); + +/* Move the device properties to the info structure, pass extension properties + to allow reuse as well */ +Vk::DeviceCreateInfo info{std::move(properties), &extensions}; +if(extensions.isSupported()) + info.addEnabledExtensions(); +if(extensions.isSupported("VK_NV_mesh_shader"_s)) + info.addEnabledExtensions({"VK_NV_mesh_shader"_s}); +DOXYGEN_IGNORE() + +/* Finally, be sure to move the info structure to the device as well */ +Vk::Device device{instance, std::move(info)}; +/* [wrapping-optimizing-properties-device-move] */ +} + { /* [CommandPool-usage] */ Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; @@ -207,7 +259,7 @@ Vk::InstanceExtensionProperties extensions = Vk::enumerateInstanceExtensionProperties(layers.names()); /* Enable only those that are supported */ -Vk::InstanceCreateInfo info{argc, argv, &layers, &extensions}; +Vk::InstanceCreateInfo info{argc, argv}; if(layers.isSupported("VK_LAYER_KHRONOS_validation"_s)) info.addEnabledLayers({"VK_LAYER_KHRONOS_validation"_s}); if(extensions.isSupported()) diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox index 6649bcefa1..0e77dc44e9 100644 --- a/doc/vulkan-wrapping.dox +++ b/doc/vulkan-wrapping.dox @@ -98,5 +98,39 @@ will skip all initialization and leave the contents unspecified to be filled later. Note that at that point you have the full responsibility to correctly set up all members. +@section vulkan-wrapping-optimizing-properties Optimizing instance and device property retrieval + +Retrieving layer and extension info as well as device properties involves +allocations, string operations, sorting and other potentially expensive work +both on the application side and in the driver. While the +@ref Vk::LayerProperties, @ref Vk::InstanceExtensionProperties / +@ref Vk::ExtensionProperties and @ref Vk::DeviceProperties APIs are made in a +way to optimize subsequent queries as much as possible, care should be taken to +avoid constructing and initializing them more times than necessary. + +For @ref Vk::Instance creation, the @ref Vk::InstanceCreateInfo constructor is +able to take pointers to existing @ref Vk::LayerProperties and +@ref Vk::InstanceExtensionProperties instances and reuse them to query +availability of implicitly enabled layers and extensions. If they're not +passed, the class may (but also might not) create its own instances internally: + +@snippet MagnumVk.cpp wrapping-optimizing-properties-instance + +For @ref Vk::Device creation, the @ref Vk::DeviceProperties should ideally be +* *moved* all the way to the @ref Vk::Device constructor, at which point it's +made available through @ref Vk::Device::properties() to any code that needs it. +If you have @ref Vk::pickDevice(), @ref Vk::DeviceCreateInfo and +@ref Vk::Device constructor all in a single expression, the optimal operation +is done implicitly: + +@snippet MagnumVk.cpp wrapping-optimizing-properties-device-single-expression + +However, if you instantiate @ref Vk::DeviceProperties and/or +@ref Vk::DeviceCreateInfo separately, you have to @cpp std::move() @ce them to +achieve the desired effect. An existing @ref Vk::ExtensionProperties instance +can be also passed to @ref Vk::DeviceCreateInfo to allow reuse: + +@snippet MagnumVk.cpp wrapping-optimizing-properties-device-move + */ } diff --git a/src/Magnum/Vk/Device.h b/src/Magnum/Vk/Device.h index 2a8c92ffed..3dadf45629 100644 --- a/src/Magnum/Vk/Device.h +++ b/src/Magnum/Vk/Device.h @@ -317,15 +317,15 @@ checked with @ref isExtensionEnabled(). @snippet MagnumVk.cpp Device-usage-extensions Usually you'll be first checking for extension availability instead, which is -again accessible through the @ref DeviceProperties instance. Similarly to -@ref Instance, as the extension list is used internally as well, pass it in the -constructor to avoid querying them again internally: +again accessible through the @ref DeviceProperties instance: @snippet MagnumVk.cpp Device-usage-check-supported With both @ref Instance and @ref Device created, you can proceed to setting up a @ref CommandPool. +@see @ref vulkan-wrapping-optimizing-properties + @section Vk-Device-command-line Command-line options The @ref Device inherits a subset of the diff --git a/src/Magnum/Vk/Instance.h b/src/Magnum/Vk/Instance.h index 1fb0208a1c..89cfc2d7c6 100644 --- a/src/Magnum/Vk/Instance.h +++ b/src/Magnum/Vk/Instance.h @@ -282,15 +282,15 @@ checked with @ref isExtensionEnabled(). However, with the above approach, if any layer or extension isn't available, the instance creation will abort. The recommended workflow is thus first checking layer and extension availability using @ref enumerateLayerProperties() -and @ref enumerateInstanceExtensionProperties(). The @ref InstanceCreateInfo -class uses these properties internally as well, pass them in the constructor -to avoid querying those again internally: +and @ref enumerateInstanceExtensionProperties(): @snippet MagnumVk.cpp Instance-usage-check-supported Next step after creating a Vulkan instance is picking and creating a @ref Device. +@see @ref vulkan-wrapping-optimizing-properties + @section Vk-Instance-command-line Command-line options The @ref Instance is configurable through command-line options that are passed From 98dced8c047ec32cbb624136b8a10eda7480d056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 23 Nov 2020 19:13:52 +0100 Subject: [PATCH 78/85] Vk: add MemoryRequirements::alignedSize(). Realized usefulness of such an utility while writing a documentation snippet. --- src/Magnum/Vk/CMakeLists.txt | 4 ++-- src/Magnum/Vk/Memory.cpp | 5 +++++ src/Magnum/Vk/Memory.h | 23 ++++++++++++++++++-- src/Magnum/Vk/Test/CMakeLists.txt | 2 +- src/Magnum/Vk/Test/MemoryTest.cpp | 36 +++++++++++++++++++++++++++++++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/Magnum/Vk/CMakeLists.txt b/src/Magnum/Vk/CMakeLists.txt index 930cc768ee..0fcdc7016f 100644 --- a/src/Magnum/Vk/CMakeLists.txt +++ b/src/Magnum/Vk/CMakeLists.txt @@ -32,7 +32,6 @@ set(MagnumVk_SRCS Extensions.cpp Handle.cpp Instance.cpp - Memory.cpp Result.cpp Shader.cpp Version.cpp @@ -48,7 +47,8 @@ set(MagnumVk_GracefulAssert_SRCS Enums.cpp ExtensionProperties.cpp Image.cpp - LayerProperties.cpp) + LayerProperties.cpp + Memory.cpp) set(MagnumVk_HEADERS Assert.h diff --git a/src/Magnum/Vk/Memory.cpp b/src/Magnum/Vk/Memory.cpp index 2d0f32254c..4356a283d4 100644 --- a/src/Magnum/Vk/Memory.cpp +++ b/src/Magnum/Vk/Memory.cpp @@ -46,6 +46,11 @@ MemoryRequirements::MemoryRequirements(const VkMemoryRequirements2& requirements member instead of doing a copy */ _requirements(requirements) {} +UnsignedLong MemoryRequirements::alignedSize(const UnsignedLong alignment) const { + CORRADE_ASSERT(alignment, "Vk::MemoryRequirements::alignedSize(): alignment can't be zero", {}); + return ((_requirements.memoryRequirements.size + alignment - 1)/alignment)*alignment; +} + MemoryAllocateInfo::MemoryAllocateInfo(UnsignedLong size, UnsignedInt memory): _info{} { _info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; _info.allocationSize = size; diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index d52a2230f5..ae82909cd9 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -161,16 +161,35 @@ class MAGNUM_VK_EXPORT MemoryRequirements { /** @overload */ const VkMemoryRequirements2* operator->() const { return &_requirements; } - /** @brief Required memory size */ + /** + * @brief Required memory size + * + * @see @ref alignedSize() + */ UnsignedLong size() const { return _requirements.memoryRequirements.size; } - /** @brief Required memory alignment */ + /** + * @brief Required memory alignment + * + * @see @ref alignedSize() + */ UnsignedLong alignment() const { return _requirements.memoryRequirements.alignment; } + /** + * @brief Required memory size rounded up for given alignment + * + * Pads @ref size() with given alignment requirements. For example, a + * 13765-byte buffer aligned to 4 kB would be 16384 bytes. See the + * @ref Memory class for more information and example usage. + * + * The alignment is expected to be non-zero. + */ + UnsignedLong alignedSize(UnsignedLong alignment) const; + /** @brief Bits indicating which memory */ UnsignedInt memories() const { return _requirements.memoryRequirements.memoryTypeBits; diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 06a8efb5e1..dd753028d0 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -37,7 +37,7 @@ corrade_add_test(VkImageTest ImageTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkInstanceTest InstanceTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkIntegrationTest IntegrationTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkLayerPropertiesTest LayerPropertiesTest.cpp LIBRARIES MagnumVk) -corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVk) +corrade_add_test(VkMemoryTest MemoryTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkShaderTest ShaderTest.cpp LIBRARIES MagnumVk) diff --git a/src/Magnum/Vk/Test/MemoryTest.cpp b/src/Magnum/Vk/Test/MemoryTest.cpp index 010df0b1e8..209d104785 100644 --- a/src/Magnum/Vk/Test/MemoryTest.cpp +++ b/src/Magnum/Vk/Test/MemoryTest.cpp @@ -37,6 +37,9 @@ struct MemoryTest: TestSuite::Tester { void requirementsConstructNoInit(); void requirementsConstructFromVk(); + void requirementsAlignedSize(); + void requirementsAlignedSizeZeroAlignement(); + void allocateInfoConstruct(); void allocateInfoConstructNoInit(); void allocateInfoConstructFromVk(); @@ -52,6 +55,9 @@ MemoryTest::MemoryTest() { addTests({&MemoryTest::requirementsConstructNoInit, &MemoryTest::requirementsConstructFromVk, + &MemoryTest::requirementsAlignedSize, + &MemoryTest::requirementsAlignedSizeZeroAlignement, + &MemoryTest::allocateInfoConstruct, &MemoryTest::allocateInfoConstructNoInit, &MemoryTest::allocateInfoConstructFromVk, @@ -83,6 +89,36 @@ void MemoryTest::requirementsConstructFromVk() { CORRADE_COMPARE(requirements->sType, VK_STRUCTURE_TYPE_APPLICATION_INFO); } +void MemoryTest::requirementsAlignedSize() { + /* Creating from a raw Vulkan structure because there's no other way */ + VkMemoryRequirements2 vkRequirements{}; + vkRequirements.memoryRequirements.size = 13765; + CORRADE_COMPARE(MemoryRequirements{vkRequirements}.alignedSize(4096), 16384); + + vkRequirements.memoryRequirements.size = 16383; + CORRADE_COMPARE(MemoryRequirements{vkRequirements}.alignedSize(4096), 16384); + + vkRequirements.memoryRequirements.size = 16384; + CORRADE_COMPARE(MemoryRequirements{vkRequirements}.alignedSize(4096), 16384); + + vkRequirements.memoryRequirements.size = 0; + CORRADE_COMPARE(MemoryRequirements{vkRequirements}.alignedSize(4096), 0); +} + +void MemoryTest::requirementsAlignedSizeZeroAlignement() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + VkMemoryRequirements2 vkRequirements{}; + vkRequirements.memoryRequirements.size = 16384; + + std::ostringstream out; + Error redirectError{&out}; + MemoryRequirements{vkRequirements}.alignedSize(0); + CORRADE_COMPARE(out.str(), "Vk::MemoryRequirements::alignedSize(): alignment can't be zero\n"); +} + void MemoryTest::allocateInfoConstruct() { MemoryAllocateInfo info{65536, 1}; CORRADE_COMPARE(info->allocationSize, 65536); From e81f4d52246399833063d7ca008310dbe9a2a730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 23 Nov 2020 19:17:26 +0100 Subject: [PATCH 79/85] Vk: overview docs for Memory, Buffer and Image. --- doc/snippets/MagnumVk.cpp | 106 ++++++++++++++++++++++++++++++++++++++ src/Magnum/Vk/Buffer.h | 31 ++++++++++- src/Magnum/Vk/Image.h | 32 +++++++++++- src/Magnum/Vk/Memory.h | 60 ++++++++++++++++++++- 4 files changed, 226 insertions(+), 3 deletions(-) diff --git a/doc/snippets/MagnumVk.cpp b/doc/snippets/MagnumVk.cpp index 5ee24d0593..c703c85edf 100644 --- a/doc/snippets/MagnumVk.cpp +++ b/doc/snippets/MagnumVk.cpp @@ -25,10 +25,12 @@ #include #include +#include #include #include "Magnum/Magnum.h" #include "Magnum/Math/Color.h" +#include "Magnum/Vk/Buffer.h" #include "Magnum/Vk/CommandBuffer.h" #include "Magnum/Vk/CommandPool.h" #include "Magnum/Vk/Device.h" @@ -37,6 +39,7 @@ #include "Magnum/Vk/ExtensionProperties.h" #include "Magnum/Vk/Instance.h" #include "Magnum/Vk/Integration.h" +#include "Magnum/Vk/Image.h" #include "Magnum/Vk/LayerProperties.h" #include "Magnum/Vk/Queue.h" #include "Magnum/Vk/Shader.h" @@ -115,6 +118,35 @@ Vk::Device device{instance, std::move(info)}; /* [wrapping-optimizing-properties-device-move] */ } +{ +Vk::Device device{NoCreate}; +/* [Buffer-usage] */ +Vk::Buffer buffer{device, + Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, 1024*1024}, + Vk::MemoryFlag::DeviceLocal +}; +/* [Buffer-usage] */ +} + +{ +Vk::Device device{NoCreate}; +/* [Buffer-usage-custom-allocation] */ +Vk::Buffer buffer{device, + Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, 1024*1024}, + NoAllocate +}; + +Vk::MemoryRequirements requirements = buffer.memoryRequirements(); +Vk::Memory memory{device, Vk::MemoryAllocateInfo{ + requirements.size(), + device.properties().pickMemory(Vk::MemoryFlag::DeviceLocal, + requirements.memories()) +}}; + +buffer.bindMemory(memory, 0); +/* [Buffer-usage-custom-allocation] */ +} + { /* [CommandPool-usage] */ Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; @@ -211,6 +243,35 @@ if(device.isExtensionEnabled()) { /* [Device-isExtensionEnabled] */ } +{ +Vk::Device device{NoCreate}; +/* [Image-usage] */ +Vk::Image image{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_SRGB, {1024, 1024}, 1 + }, Vk::MemoryFlag::DeviceLocal +}; +/* [Image-usage] */ +} + +{ +Vk::Device device{NoCreate}; +/* [Image-usage-custom-allocation] */ +Vk::Image image{device, Vk::ImageCreateInfo2D{ + Vk::ImageUsage::Sampled, VK_FORMAT_R8G8B8A8_SRGB, {1024, 1024}, 1 + }, NoAllocate +}; + +Vk::MemoryRequirements requirements = image.memoryRequirements(); +Vk::Memory memory{device, Vk::MemoryAllocateInfo{ + requirements.size(), + device.properties().pickMemory(Vk::MemoryFlag::DeviceLocal, + requirements.memories()) +}}; + +image.bindMemory(memory, 0); +/* [Image-usage-custom-allocation] */ +} + { int argc{}; const char** argv{}; @@ -310,6 +371,51 @@ if(instance.isExtensionEnabled()) { /* [Instance-isExtensionEnabled] */ } +{ +Vk::Device device{NoCreate}; +Containers::ArrayView vertexData, indexData; +/* [Memory-usage] */ +/* Create buffers without allocating them */ +Vk::Buffer vertices{device, + Vk::BufferCreateInfo{Vk::BufferUsage::VertexBuffer, vertexData.size()}, + NoAllocate}; +Vk::Buffer indices{device, + Vk::BufferCreateInfo{Vk::BufferUsage::IndexBuffer, vertexData.size()}, + NoAllocate}; + +/* Query memory requirements of both buffers, calculate max alignment */ +Vk::MemoryRequirements verticesRequirements = vertices.memoryRequirements(); +Vk::MemoryRequirements indicesRequirements = indices.memoryRequirements(); +const UnsignedLong alignment = Math::max(verticesRequirements.alignment(), + indicesRequirements.alignment()); + +/* Allocate memory that's large enough to contain both buffers including + the strictest alignment, and is of a type satisfying requirements of both */ +Vk::Memory memory{device, Vk::MemoryAllocateInfo{ + verticesRequirements.alignedSize(alignment) + + indicesRequirements.alignedSize(alignment), + device.properties().pickMemory(Vk::MemoryFlag::HostVisible, + verticesRequirements.memories() & indicesRequirements.memories()) +}}; + +const UnsignedLong indicesOffset = verticesRequirements.alignedSize(alignment); + +/* Bind the respective sub-ranges to the buffers */ +vertices.bindMemory(memory, 0); +indices.bindMemory(memory, indicesOffset); +/* [Memory-usage] */ + +/* [Memory-mapping] */ +/* The memory gets unmapped again at the end of scope */ +{ + Containers::Array mapped = memory.map(); + Utility::copy(vertexData, mapped.prefix(vertexData.size())); + Utility::copy(indexData, + mapped.slice(indicesOffset, indicesOffset + indexData.size())); +} +/* [Memory-mapping] */ +} + { Vk::Device device{DOXYGEN_IGNORE(NoCreate)}; /* [Shader-usage] */ diff --git a/src/Magnum/Vk/Buffer.h b/src/Magnum/Vk/Buffer.h index f13edcb170..85db17d20d 100644 --- a/src/Magnum/Vk/Buffer.h +++ b/src/Magnum/Vk/Buffer.h @@ -159,7 +159,36 @@ CORRADE_ENUMSET_OPERATORS(BufferCreateInfo::Flags) @brief Buffer @m_since_latest -Wraps a @type_vk_keyword{Buffer}. +Wraps a @type_vk_keyword{Buffer} and its memory. + +@section Vk-Buffer-usage Basic usage + +Pass a @ref BufferCreateInfo with desired usage and size to the @ref Buffer +constructor together with specifying @ref MemoryFlags for the allocation. + +@snippet MagnumVk.cpp Buffer-usage + +@attention At this point, a dedicated allocation is used, subsequently + accessible through @ref dedicatedMemory(). This behavior may change in the + future. + +@section Vk-Buffer-usage-custom-allocation Custom memory allocation + +Using @ref Buffer(Device&, const BufferCreateInfo&, NoAllocateT), the buffer +will be created without any memory bound. Buffer memory requirements can be +then queried using @ref memoryRequirements() and an allocated memory bound with +@ref bindMemory(). See @ref Memory for further details about memory allocation. + +@snippet MagnumVk.cpp Buffer-usage-custom-allocation + +Using @ref bindDedicatedMemory() instead of @ref bindMemory() will transfer +ownership of the @ref Memory to the buffer instance, making it subsequently +available through @ref dedicatedMemory(). This matches current behavior of the +@ref Buffer(Device&, const BufferCreateInfo&, MemoryFlags) constructor shown +above, except that you have more control over choosing and allocating the +memory. + +@see @ref Image */ class MAGNUM_VK_EXPORT Buffer { public: diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 804e736f8a..9ac256f24e 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -333,7 +333,37 @@ class ImageCreateInfoCubeMapArray: public ImageCreateInfo { @brief Image @m_since_latest -Wraps a @type_vk_keyword{Image}. +Wraps a @type_vk_keyword{Image} and its memory. + +@section Vk-Image-usage Basic usage + +Pass one of the @ref ImageCreateInfo subclasses depending on desired image type +with desired usage, format, size and other propoerties to the @ref Image +constructor together with specifying @ref MemoryFlags for memory allocation. + +@snippet MagnumVk.cpp Image-usage + +@attention At this point, a dedicated allocation is used, subsequently + accessible through @ref dedicatedMemory(). This behavior may change in the + future. + +@section Vk-Image-usage-custom-allocation Custom memory allocation + +Using @ref Image(Device&, const ImageCreateInfo&, NoAllocateT), the image will +be created without any memory attached. Image memory requirements can be +subsequently queried using @ref memoryRequirements() and an allocated memory +bound with @ref bindMemory(). See @ref Memory for further details about memory allocation. + +@snippet MagnumVk.cpp Image-usage-custom-allocation + +Using @ref bindDedicatedMemory() instead of @ref bindMemory() will transfer +ownership of the @ref Memory to the image instance, making it subsequently +available through @ref dedicatedMemory(). This matches current behavior of the +@ref Image(Device&, const ImageCreateInfo&, MemoryFlags) constructor shown +above, except that you have more control over choosing and allocating the +memory. + +@see @ref Buffer */ class MAGNUM_VK_EXPORT Image { public: diff --git a/src/Magnum/Vk/Memory.h b/src/Magnum/Vk/Memory.h index ae82909cd9..e8f2263a25 100644 --- a/src/Magnum/Vk/Memory.h +++ b/src/Magnum/Vk/Memory.h @@ -267,7 +267,65 @@ class MAGNUM_VK_EXPORT MemoryAllocateInfo { @brief Device memory @m_since_latest -Wraps a @type_vk_keyword{DeviceMemory}. +Wraps a @type_vk_keyword{DeviceMemory} and handles its allocation and mapping. + +@section Vk-Memory-usage Usage + +By default, the memory will get allocated for you during the creation of +@ref Buffer, @ref Image and other objects. In case you want to handle the +allocation yourself instead (which you indicate by passing the @ref NoAllocate +tag to constructors of these objects), it consists of these steps: + +1. Querying memory requirements of a particular object, for example using + @ref Buffer::memoryRequirements() or @ref Image::memoryRequirements() +2. Picking a memory type satisfying requirements of the object it's being + allocated for (such as allowed memory types) and user requirements (whether + it should be device-local, host-mappable etc.) using + @ref DeviceProperties::pickMemory() +3. Allocating a new @ref Memory or taking a (correctly aligned) sub-range of + an existing allocation from given memory type +4. Binding the memory (sub-range) to the object, using + @ref Buffer::bindMemory(), @ref Image::bindMemory() and others + +The following example allocates a single block memory for two buffers, one +containing vertex and the other index data: + +@snippet MagnumVk.cpp Memory-usage + +@section Vk-Memory-mapping Memory mapping + +If the memory is created with the @ref MemoryFlag::HostVisible flag, it can be +mapped on the host via @ref map(). The unmapping is then taken care of by a +custom deleter in the returned @ref Corrade::Containers::Array. It's possible +to map either the whole range or a sub-range, however note that one @ref Memory +object can't be mapped twice at the same time --- in the code snippet above, it +means that in order to upload vertex and index data, there are two options: + +- One is to first map the vertex buffer sub-range, upload the data, unmap it, + and then do the same process for the index buffer sub-range. This way is + more encapsulated without having to worry if there's already a mapping and + who owns it, but means more work for the driver. +- Another option is to map the whole memory at once and then upload data of + particular buffers to correct subranges. Here the mapping has to be owned + by some external entity which ensures it's valid for as long as any buffer + wants to map its memory sub-range. + +The following example maps the memory allocated above and copies index and +vertex data to it: + +@snippet MagnumVk.cpp Memory-mapping + + + +@m_class{m-note m-success} + +@par Map temporarily or forever? + Mapping smaller ranges and unmapping again after makes sense on 32-bit + systems where the amount of virtual memory is limited --- otherwise it may + happen that the system won't be able to find a sufficiently large block of + virtual memory, causing the next mapping to fail. On 64-bit systems the + virtual address space is sufficiently large for most use cases and it's + common to just map the whole memory block for its whole lifetime. */ class MAGNUM_VK_EXPORT Memory { public: From b9ec5776bbe864f40ed688d76ca5e715a903f2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 17:33:57 +0100 Subject: [PATCH 80/85] doc: mention ignored overrides for Vulkan host allocations. --- doc/vulkan-wrapping.dox | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/vulkan-wrapping.dox b/doc/vulkan-wrapping.dox index 0e77dc44e9..d743853027 100644 --- a/doc/vulkan-wrapping.dox +++ b/doc/vulkan-wrapping.dox @@ -66,10 +66,20 @@ tied to a particular instance/device still apply: @section vulkan-wrapping-create-info CreateInfo structure wrappers -In most cases, a`Vk::*CreateInfo` instance has all required fields set to valid -values upon construction, with everything else optional. One exception to this -rule is for example @ref Vk::DeviceCreateInfo, where the user is expected to -call @ref Vk::DeviceCreateInfo::addQueues() "addQueues()". +Unless said otherwise in the particular constructor docs, a`Vk::*CreateInfo` +instance has all required fields set to valid values upon construction, with +everything else optional. One exception is for example +@ref Vk::DeviceCreateInfo, where the user is expected to call +@ref Vk::DeviceCreateInfo::addQueues() "addQueues()" as well. + +@section vulkan-wrapping-host-allocation Host memory allocation + +As opposed to device memory allocation, which is exposed through +@ref Vk::Memory and related APIs, overriding host memory allocation via +@type_vk{AllocationCallbacks} is not possible as use cases for overriding host +memory allocators are quite rare. This pointer is thus always set to +@cpp nullptr @ce in any `vkCreate*()` calls. If you want to supply your own +callbacks, @section vulkan-wrapping-raw Common interfaces for interaction with raw Vulkan code From a7ac8ea6fc1e77ea09fb8161ec9285e1a5328e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 18:04:21 +0100 Subject: [PATCH 81/85] doc: add info about adding new Vulkan versions / extensions. --- doc/developers.dox | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/doc/developers.dox b/doc/developers.dox index 8d92b66926..6e85630205 100644 --- a/doc/developers.dox +++ b/doc/developers.dox @@ -729,7 +729,42 @@ unless it doesn't affect public API at all. @section developers-vk-extensions Checklist for adding / removing Vulkan versions and extensions -@todoc adapt from the GL section +1. Install [flextGL](https://github.com/mosra/flextgl) +2. Go to `src/MagnumExternal/Vulkan/`: + - Update `extensions.txt` (bump a version or add/remove extensions) + - Run `./update-flexgl.sh` to update everything +3. Check @cb{.sh} git diff @ce for suspicious changes and whitespace-at-EOL +4. For every new added function and `CreateInfo` structure, add an entry to + `doc/vulkan-mapping.dox` +5. Add a table listing the new version and all new extensions in it to + `doc/vulkan-support.dox` (take a list of them from the changelog in the + official spec PDF). Some extensions might be already present in the general + extension list, move them out of there. +6. Add a new `requires-vkXY` page with @c \@m_footernavigation to + `doc/vulkan-support.dox`, mention it as a @c \@subpage at a correct + position in the list +7. Add a new `requires-vkXY` alias to `Doxyfile`, `Doxyfile-mcss` and + `Doxyfile-public`, copypaste it from existing and change the numbers +8. Add new version enum value: + - to `src/Magnum/Vk/Version.h` + - to the list in `src/Magnum/Vk/vk-info.cpp` + - to @ref Vk::InstanceExtension::extensions() and + @ref Vk::Extension::extensions() in `src/Magnum/Vk/Extensions.cpp` +9. Add new extensions to `src/Magnum/Vk/Extensions.h` + - there's a separate list for instance and device extensions, ensure each + in the right list + - order them by extension ID that is mentioned on every extension spec + page + - update the numbering to stay monotonic and unique, round up start index + of next section to nearest ten to make the updates bearable + - in case there's a lot of new extensions, + @cpp Implementation::InstanceExtensionCount @ce / + @cpp Implementation::DeviceExtensionCount @ce might needed to be + increased +10. Add them alphabetically ordered to the correct list in + `src/Magnum/Vk/Extensions.cpp` +11. Update existing extensions with version in which they become core (last + parameter of the `_extension()` macro) In order to remove Vulkan functionality, be sure to touch all places mentioned above, only in inverse --- but usually @ref developers-deprecation "deprecate first", From 144b6229b29f3b46d208cb05d466f1a26af1d3e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 20:46:01 +0100 Subject: [PATCH 82/85] Vk: recognize SamplerWrapping::MirrorClampToEdge. It's a new extension in Vulkan 1.2. --- doc/changelog.dox | 5 +++++ src/Magnum/Sampler.h | 4 ++-- src/Magnum/Vk/Enums.cpp | 2 +- src/Magnum/Vk/Enums.h | 6 ++---- src/Magnum/Vk/Test/EnumsTest.cpp | 10 ++++++---- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index a348e3b6c2..451c3524fc 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -193,6 +193,11 @@ See also: - @ref magnum-imageconverter "magnum-imageconverter" has a new `--in-place` option for converting images in-place +@subsubsection changelog-latest-changes-vk Vk library + +- @ref Vk::hasVkSamplerAddressMode() and @ref Vk::vkSamplerAddressMode() now + recognizes @ref SamplerWrapping::MirrorClampToEdge + @subsection changelog-latest-buildsystem Build system - Fixed compilation of the @ref GL library on macOS with ANGLE --- new code diff --git a/src/Magnum/Sampler.h b/src/Magnum/Sampler.h index f89ff38c87..7fe849e2d3 100644 --- a/src/Magnum/Sampler.h +++ b/src/Magnum/Sampler.h @@ -169,8 +169,8 @@ enum class SamplerWrapping: UnsignedInt { * Mirror the texture once in negative coordinates and clamp to * edge after that. * - * Corresponds to @ref GL::SamplerWrapping::MirrorClampToEdge. Not - * available on Vulkan. + * Corresponds to @ref GL::SamplerWrapping::MirrorClampToEdge. / + * @val_vk_keyword{SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE,SamplerAddressMode}. */ MirrorClampToEdge }; diff --git a/src/Magnum/Vk/Enums.cpp b/src/Magnum/Vk/Enums.cpp index 6a45dfe0eb..f994558996 100644 --- a/src/Magnum/Vk/Enums.cpp +++ b/src/Magnum/Vk/Enums.cpp @@ -102,7 +102,7 @@ constexpr VkSamplerAddressMode SamplerAddressModeMapping[]{ VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - VkSamplerAddressMode(~UnsignedInt{}), + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, }; } diff --git a/src/Magnum/Vk/Enums.h b/src/Magnum/Vk/Enums.h index 626b03f1ce..cb5d0ade57 100644 --- a/src/Magnum/Vk/Enums.h +++ b/src/Magnum/Vk/Enums.h @@ -209,10 +209,8 @@ MAGNUM_VK_EXPORT VkSamplerMipmapMode vkSamplerMipmapMode(Magnum::SamplerMipmap m /** @brief Check availability of a generic sampler wrapping mode -Some Vulkan targets don't support all generic sampler wrapping modes (for -example the @ref SamplerWrapping::MirrorClampToEdge). Returns @cpp false @ce if -current target can't support such format, @cpp true @ce otherwise. The -@p wrapping value is expected to be valid. +Returns @cpp false @ce if Vulkan doesn't support such wrapping, @cpp true @ce +otherwise. The @p wrapping value is expected to be valid. @note Support of some formats depends on presence of a particular Vulkan extension. Such check is outside of the scope of this function and you are diff --git a/src/Magnum/Vk/Test/EnumsTest.cpp b/src/Magnum/Vk/Test/EnumsTest.cpp index 5a57522c32..b0c95ae87d 100644 --- a/src/Magnum/Vk/Test/EnumsTest.cpp +++ b/src/Magnum/Vk/Test/EnumsTest.cpp @@ -624,14 +624,16 @@ void EnumsTest::mapVkSamplerAddressModeUnsupported() { CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); #endif + #if 1 + CORRADE_SKIP("All sampler address modes are supported."); + #else CORRADE_VERIFY(!hasVkSamplerAddressMode(Magnum::SamplerWrapping::MirrorClampToEdge)); std::ostringstream out; - { - Error redirectError{&out}; - vkSamplerAddressMode(Magnum::SamplerWrapping::MirrorClampToEdge); - } + Error redirectError{&out}; + vkSamplerAddressMode(Magnum::SamplerWrapping::MirrorClampToEdge); CORRADE_COMPARE(out.str(), "Vk::vkSamplerAddressMode(): unsupported wrapping SamplerWrapping::MirrorClampToEdge\n"); + #endif } void EnumsTest::mapVkSamplerAddressModeInvalid() { From 299f2ff0c55b60c9987588eed12d2140d7bdfdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 20:47:22 +0100 Subject: [PATCH 83/85] Vk: what's this blabber about "some Vulkan targets". In Vulkan there's no mess like with GL/GLES/WebGL. Well, yet. --- src/Magnum/Vk/Enums.h | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/Magnum/Vk/Enums.h b/src/Magnum/Vk/Enums.h index cb5d0ade57..b774fdcd34 100644 --- a/src/Magnum/Vk/Enums.h +++ b/src/Magnum/Vk/Enums.h @@ -56,7 +56,7 @@ In case @ref isMeshPrimitiveImplementationSpecific() returns @cpp false @ce for @p primitive stores a Vulkan-specific primitive topology and returns @ref meshPrimitiveUnwrap() cast to @type_vk{PrimitiveTopology}. -Not all generic mesh primitives are available in Vulkan and this function +Not all generic mesh primitives have a Vulkan equivalent and this function expects that given primitive is available. Use @ref hasVkPrimitiveTopology() to query availability of given primitive. @see @ref vkIndexType() @@ -80,7 +80,7 @@ MAGNUM_VK_EXPORT bool hasVkIndexType(Magnum::MeshIndexType type); /** @brief Convert generic mesh index type to Vulkan mesh index type -Not all generic index types are available in Vulkan and this function expects +Not all generic index types have a Vulkan equivalent and this function expects that given type is available. Use @ref hasVkIndexType() to query availability of given index type. @see @ref vkPrimitiveTopology() @@ -91,8 +91,7 @@ MAGNUM_VK_EXPORT VkIndexType vkIndexType(Magnum::MeshIndexType type); @brief Check availability of a generic vertex format @m_since{2020,06} -Some Vulkan targets don't support all generic vertex formats. Returns -@cpp false @ce if current target can't support such format, @cpp true @ce +Returns @cpp false @ce if Vulkan doesn't support such format, @cpp true @ce otherwise. Moreover, returns @cpp true @ce also for all formats that are @ref isVertexFormatImplementationSpecific(). The @p format value is expected to be valid. Note that for matrix formats the function only returns a @@ -111,9 +110,8 @@ MAGNUM_VK_EXPORT bool hasVkFormat(Magnum::VertexFormat format); /** @brief Check availability of a generic pixel format -Some Vulkan targets don't support all generic formats. Returns @cpp false @ce -if current target can't support such format, @cpp true @ce otherwise. Moreover, -returns @cpp true @ce also for all formats that are +Returns @cpp false @ce if Vulkan doesn't support such format, @cpp true @ce +otherwise. Moreover, returns @cpp true @ce also for all formats that are @ref isPixelFormatImplementationSpecific(). The @p format value is expected to be valid. @@ -128,9 +126,8 @@ MAGNUM_VK_EXPORT bool hasVkFormat(Magnum::PixelFormat format); /** @brief Check availability of a generic compressed pixel format -Some Vulkan targets don't support all generic formats. Returns @cpp false @ce -if current target can't support such format, @cpp true @ce otherwise. Moreover, -returns @cpp true @ce also for all formats that are +Returns @cpp false @ce if Vulkan doesn't support such format, @cpp true @ce +otherwise. Moreover, returns @cpp true @ce also for all formats that are @ref isCompressedPixelFormatImplementationSpecific(). The @p format value is expected to be valid. @@ -152,9 +149,9 @@ In case @ref isVertexFormatImplementationSpecific() returns @cpp false @ce for @p format stores a Vulkan-specific format and returns @ref vertexFormatUnwrap() cast to @type_vk{Format}. -Not all generic vertex formats may be available on all targets and this -function expects that given format is available on the target. Use -@ref hasVkFormat(Magnum::VertexFormat) to query availability of given format. +Not all generic vertex formats have a Vulkan equivalent and this function +expects that given format is available. Use @ref hasVkFormat(Magnum::VertexFormat) +to query availability of given format. */ MAGNUM_VK_EXPORT VkFormat vkFormat(Magnum::VertexFormat format); @@ -167,9 +164,9 @@ In case @ref isPixelFormatImplementationSpecific() returns @cpp false @ce for @p format stores a Vulkan-specific format and returns @ref pixelFormatUnwrap() cast to @type_vk{Format}. -Not all generic pixel formats may be available on all targets and this function -expects that given format is available on the target. Use -@ref hasVkFormat(Magnum::PixelFormat) to query availability of given format. +Not all generic pixel formats have a Vulkan equivalent and this function +expects that given format is available. Use @ref hasVkFormat(Magnum::PixelFormat) +to query availability of given format. */ MAGNUM_VK_EXPORT VkFormat vkFormat(Magnum::PixelFormat format); @@ -182,8 +179,8 @@ In case @ref isCompressedPixelFormatImplementationSpecific() returns assumes @p format stores a Vulkan-specific format and returns @ref compressedPixelFormatUnwrap() cast to @type_vk{Format}. -Not all generic pixel formats may be available on all targets and this function -expects that given format is available on the target. Use +Not all generic pixel formats have a Vulkan equivalent and this function +expects that given format is available. Use @ref hasVkFormat(Magnum::CompressedPixelFormat) to query availability of given format. */ @@ -212,9 +209,9 @@ MAGNUM_VK_EXPORT VkSamplerMipmapMode vkSamplerMipmapMode(Magnum::SamplerMipmap m Returns @cpp false @ce if Vulkan doesn't support such wrapping, @cpp true @ce otherwise. The @p wrapping value is expected to be valid. -@note Support of some formats depends on presence of a particular Vulkan +@note Support of some modes depends on presence of a particular Vulkan extension. Such check is outside of the scope of this function and you are - expected to verify extension availability before using such format. + expected to verify extension availability before using such mode. @see @ref vkSamplerAddressMode(), @ref vkFilter(), @ref vkSamplerMipmapMode() */ @@ -223,9 +220,9 @@ MAGNUM_VK_EXPORT bool hasVkSamplerAddressMode(Magnum::SamplerWrapping wrapping); /** @brief Convert generic sampler filter mode to Vulkan sampler address mode -Not all generic sampler wrapping modes may be available on all targets and this -function expects that given format is available on the target. Use -@ref hasVkSamplerAddressMode() to query availability of given mode. +Not all generic sampler wrapping modes have a Vulkan equivalent and this +function expects that given mode is available. Use @ref hasVkSamplerAddressMode() +to query availability of given mode. @see @ref vkFilter(), @ref vkSamplerAddressMode() */ MAGNUM_VK_EXPORT VkSamplerAddressMode vkSamplerAddressMode(Magnum::SamplerWrapping wrapping); From 2121292052a4e1a142a47faad8cecc78c06169f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 20:55:52 +0100 Subject: [PATCH 84/85] Vk: add a TODO. --- src/Magnum/Vk/Image.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Magnum/Vk/Image.h b/src/Magnum/Vk/Image.h index 9ac256f24e..3b07a7c7e8 100644 --- a/src/Magnum/Vk/Image.h +++ b/src/Magnum/Vk/Image.h @@ -111,7 +111,11 @@ class MAGNUM_VK_EXPORT ImageCreateInfo { enum class Flag: UnsignedInt { /** @todo sparse binding/residency/aliased */ - /** Allow creating a view of different format */ + /** + * Allow creating a view of different format + * + * @todo implement @vk_extension{KHR,image_format_list} + */ MutableFormat = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, /** Allow creating a cube map view */ From 518abbbe04ce7e9d15ecca6a01e90fc93875c721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 29 Nov 2020 20:56:11 +0100 Subject: [PATCH 85/85] doc: update changelog, mark implemented Vulkan extensions as such. --- doc/changelog.dox | 20 ++++++++++++++++++++ doc/vulkan-support.dox | 18 +++++++++--------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 451c3524fc..ea548755b6 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -139,6 +139,26 @@ See also: - @ref Trade::LightData got extended to support light attenuation and range parameters as well and spot light inner and outer angle +@subsubsection changelog-latest-new-vk Vk library + +- Boostrapping Vulkan support ([mosra/magnum#234](https://github.com/mosra/magnum/pull/234)), in particular: + - @ref Vk::LayerProperties, @ref Vk::InstanceExtensionProperties and + @ref Vk::enumerateInstanceVersion() APIs for querying instance-level + properties + - @ref Vk::DeviceProperties and @ref Vk::ExtensionProperties for querying + device-level properties + - @ref Vk::Instance and @ref Vk::Device APIs for instance and device + creation and fetching instance/device-specific function pointers, + @ref Vk::Queue setup + - @ref Vk::CommandPool and @ref Vk::CommandBuffer wrappers + - Initial implementations of @ref Vk::Memory, @ref Vk::Buffer and + @ref Vk::Image + - @ref Vk::Shader wrapper + - New @ref magnum-vk-info "magnum-vk-info" utility, a Vulkan-specific + counterpart for @ref magnum-gl-info "magnum-gl-info" + - @ref vulkan "Initial documentation", in particular @ref vulkan-support, + @ref vulkan-wrapping and @ref vulkan-mapping + @subsection changelog-latest-changes Changes and improvements @subsubsection changelog-latest-changes-gl GL library diff --git a/doc/vulkan-support.dox b/doc/vulkan-support.dox index f7ceb2bfc5..57f48b57c3 100644 --- a/doc/vulkan-support.dox +++ b/doc/vulkan-support.dox @@ -46,7 +46,7 @@ provide support for the replacement extensions. @subsection vulkan-support-10 Vulkan 1.0 -TBD +See the @ref vulkan-mapping page for a list of already implemented functions. @subsection vulkan-support-11 Vulkan 1.1 @@ -55,7 +55,7 @@ TBD Extension | Status --------------------------------------------------- | ------ @vk_extension{KHR,multiview} | | -@vk_extension{KHR,get_physical_device_properties2} @m_class{m-label m-info} **instance** | | +@vk_extension{KHR,get_physical_device_properties2} @m_class{m-label m-info} **instance** | device, memory and queue properties only @vk_extension{KHR,device_group} | | @vk_extension{KHR,shader_draw_parameters} | | @vk_extension{KHR,maintenance1} | | @@ -73,9 +73,9 @@ Extension | Status @vk_extension{KHR,dedicated_allocation} | | @vk_extension{KHR,storage_buffer_storage_class} | | @vk_extension{KHR,relaxed_block_layout} | | -@vk_extension{KHR,get_memory_requirements2} | | +@vk_extension{KHR,get_memory_requirements2} | done except sparse memory @vk_extension{KHR,sampler_ycbcr_conversion} | | -@vk_extension{KHR,bind_memory2} | | +@vk_extension{KHR,bind_memory2} | done @vk_extension{KHR,maintenance3} | | @subsection vulkan-support-12 Vulkan 1.2 @@ -84,7 +84,7 @@ Extension | Status Extension | Status --------------------------------------------------- | ------ -@vk_extension{KHR,sampler_mirror_clamp_to_edge} | | +@vk_extension{KHR,sampler_mirror_clamp_to_edge} | @ref Vk::vkSamplerAddressMode() only @vk_extension{KHR,shader_float16_int8} | | @vk_extension{KHR,imageless_framebuffer} | | @vk_extension{KHR,create_renderpass2} | | @@ -101,7 +101,7 @@ Extension | Status @vk_extension{KHR,depth_stencil_resolve} | | @vk_extension{KHR,timeline_semaphore} | | @vk_extension{KHR,vulkan_memory_model} | | -@vk_extension{KHR,spirv_1_4} | | +@vk_extension{KHR,spirv_1_4} | done (shading language only) @vk_extension{EXT,scalar_block_layout} | | @vk_extension{KHR,separate_depth_stencil_layouts} | | @vk_extension{EXT,separate_stencil_usage} | | @@ -117,11 +117,11 @@ Extension | Status --------------------------------------------------- | ------ @vk_extension{EXT,debug_report} @m_class{m-label m-danger} **deprecated** @m_class{m-label m-info} **instance** | | @vk_extension{EXT,debug_marker} @m_class{m-label m-danger} **deprecated** | | -@vk_extension{EXT,texture_compression_astc_hdr} | | +@vk_extension{EXT,texture_compression_astc_hdr} | @ref Vk::vkFormat() only @vk_extension{EXT,debug_utils} @m_class{m-label m-info} **instance** | | @vk_extension{EXT,validation_features} @m_class{m-label m-info} **instance** | | -@vk_extension{EXT,index_type_uint8} | | -@vk_extension{IMG,format_pvrtc} | | +@vk_extension{EXT,index_type_uint8} | @ref Vk::vkIndexType() only +@vk_extension{IMG,format_pvrtc} | @ref Vk::vkFormat() only */