feat: create vulkan device, surface etc.
- Next up: swapchain - Use volk
This commit is contained in:
parent
b1ff895718
commit
904a26374b
1819
contrib/glad/glad.c
1819
contrib/glad/glad.c
File diff suppressed because it is too large
Load Diff
3656
contrib/glad/glad.h
3656
contrib/glad/glad.h
File diff suppressed because it is too large
Load Diff
@ -1,311 +0,0 @@
|
|||||||
#ifndef __khrplatform_h_
|
|
||||||
#define __khrplatform_h_
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Copyright (c) 2008-2018 The Khronos Group Inc.
|
|
||||||
**
|
|
||||||
** Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
** copy of this software and/or associated documentation files (the
|
|
||||||
** "Materials"), to deal in the Materials without restriction, including
|
|
||||||
** without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
** distribute, sublicense, and/or sell copies of the Materials, and to
|
|
||||||
** permit persons to whom the Materials are 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 Materials.
|
|
||||||
**
|
|
||||||
** THE MATERIALS ARE 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
|
|
||||||
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Khronos platform-specific types and definitions.
|
|
||||||
*
|
|
||||||
* The master copy of khrplatform.h is maintained in the Khronos EGL
|
|
||||||
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
|
|
||||||
* The last semantic modification to khrplatform.h was at commit ID:
|
|
||||||
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
|
|
||||||
*
|
|
||||||
* Adopters may modify this file to suit their platform. Adopters are
|
|
||||||
* encouraged to submit platform specific modifications to the Khronos
|
|
||||||
* group so that they can be included in future versions of this file.
|
|
||||||
* Please submit changes by filing pull requests or issues on
|
|
||||||
* the EGL Registry repository linked above.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* See the Implementer's Guidelines for information about where this file
|
|
||||||
* should be located on your system and for more details of its use:
|
|
||||||
* http://www.khronos.org/registry/implementers_guide.pdf
|
|
||||||
*
|
|
||||||
* This file should be included as
|
|
||||||
* #include <KHR/khrplatform.h>
|
|
||||||
* by Khronos client API header files that use its types and defines.
|
|
||||||
*
|
|
||||||
* The types in khrplatform.h should only be used to define API-specific types.
|
|
||||||
*
|
|
||||||
* Types defined in khrplatform.h:
|
|
||||||
* khronos_int8_t signed 8 bit
|
|
||||||
* khronos_uint8_t unsigned 8 bit
|
|
||||||
* khronos_int16_t signed 16 bit
|
|
||||||
* khronos_uint16_t unsigned 16 bit
|
|
||||||
* khronos_int32_t signed 32 bit
|
|
||||||
* khronos_uint32_t unsigned 32 bit
|
|
||||||
* khronos_int64_t signed 64 bit
|
|
||||||
* khronos_uint64_t unsigned 64 bit
|
|
||||||
* khronos_intptr_t signed same number of bits as a pointer
|
|
||||||
* khronos_uintptr_t unsigned same number of bits as a pointer
|
|
||||||
* khronos_ssize_t signed size
|
|
||||||
* khronos_usize_t unsigned size
|
|
||||||
* khronos_float_t signed 32 bit floating point
|
|
||||||
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
|
|
||||||
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
|
|
||||||
* nanoseconds
|
|
||||||
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
|
|
||||||
* khronos_boolean_enum_t enumerated boolean type. This should
|
|
||||||
* only be used as a base type when a client API's boolean type is
|
|
||||||
* an enum. Client APIs which use an integer or other type for
|
|
||||||
* booleans cannot use this as the base type for their boolean.
|
|
||||||
*
|
|
||||||
* Tokens defined in khrplatform.h:
|
|
||||||
*
|
|
||||||
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
|
|
||||||
*
|
|
||||||
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
|
|
||||||
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
|
|
||||||
*
|
|
||||||
* Calling convention macros defined in this file:
|
|
||||||
* KHRONOS_APICALL
|
|
||||||
* KHRONOS_APIENTRY
|
|
||||||
* KHRONOS_APIATTRIBUTES
|
|
||||||
*
|
|
||||||
* These may be used in function prototypes as:
|
|
||||||
*
|
|
||||||
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
|
|
||||||
* int arg1,
|
|
||||||
* int arg2) KHRONOS_APIATTRIBUTES;
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
|
|
||||||
# define KHRONOS_STATIC 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
* Definition of KHRONOS_APICALL
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
* This precedes the return type of the function in the function prototype.
|
|
||||||
*/
|
|
||||||
#if defined(KHRONOS_STATIC)
|
|
||||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
|
||||||
* header compatible with static linking. */
|
|
||||||
# define KHRONOS_APICALL
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
# define KHRONOS_APICALL __declspec(dllimport)
|
|
||||||
#elif defined (__SYMBIAN32__)
|
|
||||||
# define KHRONOS_APICALL IMPORT_C
|
|
||||||
#elif defined(__ANDROID__)
|
|
||||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
|
||||||
#else
|
|
||||||
# define KHRONOS_APICALL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
* Definition of KHRONOS_APIENTRY
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
* This follows the return type of the function and precedes the function
|
|
||||||
* name in the function prototype.
|
|
||||||
*/
|
|
||||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
|
||||||
/* Win32 but not WinCE */
|
|
||||||
# define KHRONOS_APIENTRY __stdcall
|
|
||||||
#else
|
|
||||||
# define KHRONOS_APIENTRY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
* Definition of KHRONOS_APIATTRIBUTES
|
|
||||||
*-------------------------------------------------------------------------
|
|
||||||
* This follows the closing parenthesis of the function prototype arguments.
|
|
||||||
*/
|
|
||||||
#if defined (__ARMCC_2__)
|
|
||||||
#define KHRONOS_APIATTRIBUTES __softfp
|
|
||||||
#else
|
|
||||||
#define KHRONOS_APIATTRIBUTES
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------
|
|
||||||
* basic type definitions
|
|
||||||
*-----------------------------------------------------------------------*/
|
|
||||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Using <stdint.h>
|
|
||||||
*/
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef int32_t khronos_int32_t;
|
|
||||||
typedef uint32_t khronos_uint32_t;
|
|
||||||
typedef int64_t khronos_int64_t;
|
|
||||||
typedef uint64_t khronos_uint64_t;
|
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
|
||||||
/*
|
|
||||||
* To support platform where unsigned long cannot be used interchangeably with
|
|
||||||
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
|
|
||||||
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
|
|
||||||
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
|
|
||||||
* unsigned long long or similar (this results in different C++ name mangling).
|
|
||||||
* To avoid changes for existing platforms, we restrict usage of intptr_t to
|
|
||||||
* platforms where the size of a pointer is larger than the size of long.
|
|
||||||
*/
|
|
||||||
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
|
|
||||||
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
|
|
||||||
#define KHRONOS_USE_INTPTR_T
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#elif defined(__VMS ) || defined(__sgi)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Using <inttypes.h>
|
|
||||||
*/
|
|
||||||
#include <inttypes.h>
|
|
||||||
typedef int32_t khronos_int32_t;
|
|
||||||
typedef uint32_t khronos_uint32_t;
|
|
||||||
typedef int64_t khronos_int64_t;
|
|
||||||
typedef uint64_t khronos_uint64_t;
|
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
|
||||||
|
|
||||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Win32
|
|
||||||
*/
|
|
||||||
typedef __int32 khronos_int32_t;
|
|
||||||
typedef unsigned __int32 khronos_uint32_t;
|
|
||||||
typedef __int64 khronos_int64_t;
|
|
||||||
typedef unsigned __int64 khronos_uint64_t;
|
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
|
||||||
|
|
||||||
#elif defined(__sun__) || defined(__digital__)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sun or Digital
|
|
||||||
*/
|
|
||||||
typedef int khronos_int32_t;
|
|
||||||
typedef unsigned int khronos_uint32_t;
|
|
||||||
#if defined(__arch64__) || defined(_LP64)
|
|
||||||
typedef long int khronos_int64_t;
|
|
||||||
typedef unsigned long int khronos_uint64_t;
|
|
||||||
#else
|
|
||||||
typedef long long int khronos_int64_t;
|
|
||||||
typedef unsigned long long int khronos_uint64_t;
|
|
||||||
#endif /* __arch64__ */
|
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
|
||||||
|
|
||||||
#elif 0
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hypothetical platform with no float or int64 support
|
|
||||||
*/
|
|
||||||
typedef int khronos_int32_t;
|
|
||||||
typedef unsigned int khronos_uint32_t;
|
|
||||||
#define KHRONOS_SUPPORT_INT64 0
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 0
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generic fallback
|
|
||||||
*/
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef int32_t khronos_int32_t;
|
|
||||||
typedef uint32_t khronos_uint32_t;
|
|
||||||
typedef int64_t khronos_int64_t;
|
|
||||||
typedef uint64_t khronos_uint64_t;
|
|
||||||
#define KHRONOS_SUPPORT_INT64 1
|
|
||||||
#define KHRONOS_SUPPORT_FLOAT 1
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Types that are (so far) the same on all platforms
|
|
||||||
*/
|
|
||||||
typedef signed char khronos_int8_t;
|
|
||||||
typedef unsigned char khronos_uint8_t;
|
|
||||||
typedef signed short int khronos_int16_t;
|
|
||||||
typedef unsigned short int khronos_uint16_t;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
|
||||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
|
||||||
* to be the only LLP64 architecture in current use.
|
|
||||||
*/
|
|
||||||
#ifdef KHRONOS_USE_INTPTR_T
|
|
||||||
typedef intptr_t khronos_intptr_t;
|
|
||||||
typedef uintptr_t khronos_uintptr_t;
|
|
||||||
#elif defined(_WIN64)
|
|
||||||
typedef signed long long int khronos_intptr_t;
|
|
||||||
typedef unsigned long long int khronos_uintptr_t;
|
|
||||||
#else
|
|
||||||
typedef signed long int khronos_intptr_t;
|
|
||||||
typedef unsigned long int khronos_uintptr_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_WIN64)
|
|
||||||
typedef signed long long int khronos_ssize_t;
|
|
||||||
typedef unsigned long long int khronos_usize_t;
|
|
||||||
#else
|
|
||||||
typedef signed long int khronos_ssize_t;
|
|
||||||
typedef unsigned long int khronos_usize_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if KHRONOS_SUPPORT_FLOAT
|
|
||||||
/*
|
|
||||||
* Float type
|
|
||||||
*/
|
|
||||||
typedef float khronos_float_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if KHRONOS_SUPPORT_INT64
|
|
||||||
/* Time types
|
|
||||||
*
|
|
||||||
* These types can be used to represent a time interval in nanoseconds or
|
|
||||||
* an absolute Unadjusted System Time. Unadjusted System Time is the number
|
|
||||||
* of nanoseconds since some arbitrary system event (e.g. since the last
|
|
||||||
* time the system booted). The Unadjusted System Time is an unsigned
|
|
||||||
* 64 bit value that wraps back to 0 every 584 years. Time intervals
|
|
||||||
* may be either signed or unsigned.
|
|
||||||
*/
|
|
||||||
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
|
|
||||||
typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dummy value used to pad enum types to 32 bits.
|
|
||||||
*/
|
|
||||||
#ifndef KHRONOS_MAX_ENUM
|
|
||||||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Enumerated boolean type
|
|
||||||
*
|
|
||||||
* Values other than zero should be considered to be true. Therefore
|
|
||||||
* comparisons should not be made against KHRONOS_TRUE.
|
|
||||||
*/
|
|
||||||
typedef enum {
|
|
||||||
KHRONOS_FALSE = 0,
|
|
||||||
KHRONOS_TRUE = 1,
|
|
||||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
|
||||||
} khronos_boolean_enum_t;
|
|
||||||
|
|
||||||
#endif /* __khrplatform_h_ */
|
|
19
contrib/volk/LICENSE.md
Normal file
19
contrib/volk/LICENSE.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (c) 2018-2023 Arseny Kapoulkine
|
||||||
|
|
||||||
|
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.
|
99
contrib/volk/README.md
Normal file
99
contrib/volk/README.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# 🐺 volk [](https://github.com/zeux/volk/actions)
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
volk is a meta-loader for Vulkan. It allows you to dynamically load entrypoints required to use Vulkan
|
||||||
|
without linking to vulkan-1.dll or statically linking Vulkan loader. Additionally, volk simplifies the use of Vulkan extensions by automatically loading all associated entrypoints. Finally, volk enables loading
|
||||||
|
Vulkan entrypoints directly from the driver which can increase performance by skipping loader dispatch overhead.
|
||||||
|
|
||||||
|
volk is written in C89 and supports Windows, Linux, Android and macOS (via MoltenVK).
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
There are multiple ways to use volk in your project:
|
||||||
|
|
||||||
|
1. You can just add `volk.c` to your build system. Note that the usual preprocessor defines that enable Vulkan's platform-specific functions (VK_USE_PLATFORM_WIN32_KHR, VK_USE_PLATFORM_XLIB_KHR, VK_USE_PLATFORM_MACOS_MVK, etc) must be passed as desired to the compiler when building `volk.c`.
|
||||||
|
2. You can use volk in header-only fashion. Include `volk.h` wherever you want to use Vulkan functions. In exactly one source file, define `VOLK_IMPLEMENTATION` before including `volk.h`. Do not build `volk.c` at all in this case. This method of integrating volk makes it possible to set the platform defines mentioned above with arbitrary (preprocessor) logic in your code.
|
||||||
|
3. You can use provided CMake files, with the usage detailed below.
|
||||||
|
|
||||||
|
## Basic usage
|
||||||
|
|
||||||
|
To use volk, you have to include `volk.h` instead of `vulkan/vulkan.h`; this is necessary to use function definitions from volk.
|
||||||
|
|
||||||
|
If some files in your application include `vulkan/vulkan.h` and don't include `volk.h`, this can result in symbol conflicts; consider defining `VK_NO_PROTOTYPES` when compiling code that uses Vulkan to make sure this doesn't happen. It's also important to make sure that `vulkan-1` is not linked into the application, as this results in symbol name conflicts as well.
|
||||||
|
|
||||||
|
To initialize volk, call this function first:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
VkResult volkInitialize();
|
||||||
|
```
|
||||||
|
|
||||||
|
This will attempt to load Vulkan loader from the system; if this function returns `VK_SUCCESS` you can proceed to create Vulkan instance.
|
||||||
|
If this function fails, this means Vulkan loader isn't installed on your system.
|
||||||
|
|
||||||
|
After creating the Vulkan instance using Vulkan API, call this function:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void volkLoadInstance(VkInstance instance);
|
||||||
|
```
|
||||||
|
|
||||||
|
This function will load all required Vulkan entrypoints, including all extensions; you can use Vulkan from here on as usual.
|
||||||
|
|
||||||
|
## Optimizing device calls
|
||||||
|
|
||||||
|
If you use volk as described in the previous section, all device-related function calls, such as `vkCmdDraw`, will go through Vulkan loader dispatch code.
|
||||||
|
This allows you to transparently support multiple VkDevice objects in the same application, but comes at a price of dispatch overhead which can be as high as 7% depending on the driver and application.
|
||||||
|
|
||||||
|
To avoid this, you have two options:
|
||||||
|
|
||||||
|
1. For applications that use just one VkDevice object, load device-related Vulkan entrypoints directly from the driver with this function:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void volkLoadDevice(VkDevice device);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. For applications that use multiple VkDevice objects, load device-related Vulkan entrypoints into a table:
|
||||||
|
|
||||||
|
```c++
|
||||||
|
void volkLoadDeviceTable(struct VolkDeviceTable* table, VkDevice device);
|
||||||
|
```
|
||||||
|
|
||||||
|
The second option requires you to change the application code to store one `VolkDeviceTable` per `VkDevice` and call functions from this table instead.
|
||||||
|
|
||||||
|
Device entrypoints are loaded using `vkGetDeviceProcAddr`; when no layers are present, this commonly results in most function pointers pointing directly at the driver functions, minimizing the call overhead. When layers are loaded, the entrypoints will point at the implementations in the first applicable layer, so this is compatible with any layers including validation layers.
|
||||||
|
|
||||||
|
Since `volkLoadDevice` overwrites some function pointers with device-specific versions, you can choose to use `volkLoadInstanceOnly` instead of `volkLoadInstance`; when using table-based interface this can also help enforce the usage of the function tables as `volkLoadInstanceOnly` will leave device-specific functions as `NULL`.
|
||||||
|
|
||||||
|
## CMake support
|
||||||
|
|
||||||
|
If your project uses CMake, volk provides you with targets corresponding to the different use cases:
|
||||||
|
|
||||||
|
1. Target `volk` is a static library. Any platform defines can be passed to the compiler by setting `VOLK_STATIC_DEFINES`. Example:
|
||||||
|
```cmake
|
||||||
|
if (WIN32)
|
||||||
|
set(VOLK_STATIC_DEFINES VK_USE_PLATFORM_WIN32_KHR)
|
||||||
|
elseif()
|
||||||
|
...
|
||||||
|
endif()
|
||||||
|
add_subdirectory(volk)
|
||||||
|
target_link_library(my_application PRIVATE volk)
|
||||||
|
```
|
||||||
|
2. Target `volk_headers` is an interface target for the header-only style. Example:
|
||||||
|
```cmake
|
||||||
|
add_subdirectory(volk)
|
||||||
|
target_link_library(my_application PRIVATE volk_headers)
|
||||||
|
```
|
||||||
|
and in the code:
|
||||||
|
```c
|
||||||
|
/* ...any logic setting VK_USE_PLATFORM_WIN32_KHR and friends... */
|
||||||
|
#define VOLK_IMPLEMENTATION
|
||||||
|
#include "volk.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
The above example use `add_subdirectory` to include volk into CMake's build tree. This is a good choice if you copy the volk files into your project tree or as a git submodule.
|
||||||
|
|
||||||
|
Volk also supports installation and config-file packages. Installation is disabled by default (so as to not pollute user projects with install rules), and can be enabled by passing `-DVOLK_INSTALL=ON` to CMake. Once installed, do something like `find_package(volk CONFIG REQUIRED)` in your project's CMakeLists.txt. The imported volk targets are called `volk::volk` and `volk::volk_headers`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This library is available to anybody free of charge, under the terms of MIT License (see LICENSE.md).
|
2811
contrib/volk/volk.c
Normal file
2811
contrib/volk/volk.c
Normal file
File diff suppressed because it is too large
Load Diff
1853
contrib/volk/volk.h
Normal file
1853
contrib/volk/volk.h
Normal file
File diff suppressed because it is too large
Load Diff
20
meson.build
20
meson.build
@ -37,9 +37,6 @@ if get_option('use_xlib')
|
|||||||
add_project_arguments(['-DVY_USE_XLIB'], language : 'c')
|
add_project_arguments(['-DVY_USE_XLIB'], language : 'c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# glfw_proj = subproject('glfw', default_options : 'default_library=static')
|
|
||||||
# glfw_dep = glfw_proj.get_variable('glfw_dep')
|
|
||||||
|
|
||||||
incdir = include_directories(['contrib', 'src'])
|
incdir = include_directories(['contrib', 'src'])
|
||||||
|
|
||||||
runtime_lib = library('vyrt',
|
runtime_lib = library('vyrt',
|
||||||
@ -70,16 +67,29 @@ runtime_lib = library('vyrt',
|
|||||||
c_pch : 'pch/rt_pch.h')
|
c_pch : 'pch/rt_pch.h')
|
||||||
|
|
||||||
if vk_dep.found()
|
if vk_dep.found()
|
||||||
|
platform_defs = []
|
||||||
|
if get_option('use_xlib')
|
||||||
|
platform_defs = ['-DVK_USE_PLATFORM_XLIB_KHR']
|
||||||
|
elif host_machine.system() == 'windows'
|
||||||
|
platform_defs = ['-DVK_USE_PLATFORM_WIN32_KHR']
|
||||||
|
endif
|
||||||
|
|
||||||
|
vk_inc_dep = vk_dep.partial_dependency(compile_args : true, includes : true)
|
||||||
vk_renderer_lib = library('vyvk',
|
vk_renderer_lib = library('vyvk',
|
||||||
# Project Sources
|
# Project Sources
|
||||||
'src/renderer/vk/gpu.h',
|
'src/renderer/vk/gpu.h',
|
||||||
|
|
||||||
'src/renderer/vk/init.c',
|
'src/renderer/vk/init.c',
|
||||||
'src/renderer/vk/gfx_pipelines.c',
|
'src/renderer/vk/gfx_pipelines.c',
|
||||||
dependencies : [m_dep, vk_dep],
|
|
||||||
|
# Contrib Sources
|
||||||
|
'contrib/volk/volk.h',
|
||||||
|
'contrib/volk/volk.c',
|
||||||
|
dependencies : [m_dep, vk_inc_dep],
|
||||||
include_directories : incdir,
|
include_directories : incdir,
|
||||||
link_with : [runtime_lib],
|
link_with : [runtime_lib],
|
||||||
c_pch : 'pch/vk_pch.h')
|
c_pch : 'pch/vk_pch.h',
|
||||||
|
c_args : platform_defs)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
executable('voyage',
|
executable('voyage',
|
||||||
|
@ -1 +0,0 @@
|
|||||||
#include <vulkan/vulkan.h>
|
|
@ -8,8 +8,7 @@
|
|||||||
int WINAPI wWinMain(HINSTANCE hInstance,
|
int WINAPI wWinMain(HINSTANCE hInstance,
|
||||||
HINSTANCE hPrevInstance,
|
HINSTANCE hPrevInstance,
|
||||||
PWSTR pCmdLine,
|
PWSTR pCmdLine,
|
||||||
int nCmdShow) {
|
int nCmdShow) { return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow);
|
||||||
return vyWin32Entry(hInstance, hPrevInstance, pCmdLine, nCmdShow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
#ifndef VY_VK_GPU_H
|
#ifndef VY_VK_GPU_H
|
||||||
#define VY_VK_GPU_H
|
#define VY_VK_GPU_H
|
||||||
|
|
||||||
#include <vulkan/vulkan.h>
|
#include <volk/volk.h>
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
VkInstance instance;
|
VkInstance instance;
|
||||||
|
VkDebugUtilsMessengerEXT messenger;
|
||||||
VkAllocationCallbacks *alloc_cb;
|
VkAllocationCallbacks *alloc_cb;
|
||||||
|
VkPhysicalDevice phys_device;
|
||||||
|
VkDevice device;
|
||||||
|
VkSurfaceKHR surface;
|
||||||
|
VkQueue graphics_queue;
|
||||||
|
VkQueue compute_queue;
|
||||||
|
VkQueue present_queue;
|
||||||
|
|
||||||
|
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props;
|
||||||
|
VkPhysicalDeviceProperties phys_device_props;
|
||||||
} vy_vk_gpu;
|
} vy_vk_gpu;
|
||||||
|
|
||||||
#ifndef VY_VK_DONT_DEFINE_GPU_GLOBAL
|
#ifndef VY_VK_DONT_DEFINE_GPU_GLOBAL
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#define VY_VK_DONT_DEFINE_GPU_GLOBAL
|
#define VY_VK_DONT_DEFINE_GPU_GLOBAL
|
||||||
#include "gpu.h"
|
#include "gpu.h"
|
||||||
@ -9,21 +10,17 @@
|
|||||||
#include "runtime/runtime.h"
|
#include "runtime/runtime.h"
|
||||||
|
|
||||||
VY_CVAR_I(
|
VY_CVAR_I(
|
||||||
r_EnableAPIAllocTracking,
|
r_VkEnableAPIAllocTracking,
|
||||||
"Enable tracking of allocations done by the vulkan api. [0/1] Default: 0",
|
"Enable tracking of allocations done by the vulkan api. [0/1] Default: 0",
|
||||||
0);
|
0);
|
||||||
|
|
||||||
typedef struct {
|
VY_CVAR_S(r_VkPhysDeviceName,
|
||||||
size_t allocated;
|
"Name of the selected physical device. Default: \"\"",
|
||||||
size_t reallocated;
|
"");
|
||||||
size_t freed;
|
|
||||||
size_t max_alignment;
|
|
||||||
} vy_vk_alloc_stats;
|
|
||||||
|
|
||||||
vy_vk_gpu g_gpu;
|
vy_vk_gpu g_gpu;
|
||||||
|
|
||||||
static VkAllocationCallbacks _tracking_alloc_cbs;
|
static VkAllocationCallbacks _tracking_alloc_cbs;
|
||||||
static vy_vk_alloc_stats _alloc_stats;
|
|
||||||
|
|
||||||
static const char *AllocationScopeToString(VkSystemAllocationScope scope) {
|
static const char *AllocationScopeToString(VkSystemAllocationScope scope) {
|
||||||
switch (scope) {
|
switch (scope) {
|
||||||
@ -42,29 +39,15 @@ static const char *AllocationScopeToString(VkSystemAllocationScope scope) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogAllocationStats(const vy_vk_alloc_stats *stats) {
|
|
||||||
vyLog("vk",
|
|
||||||
"Allocated: %zu bytes. Reallocated: %zu bytes. Max. requested "
|
|
||||||
"alignment: %zu bytes",
|
|
||||||
stats->allocated,
|
|
||||||
stats->reallocated,
|
|
||||||
stats->max_alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *TrackAllocation(void *userData,
|
static void *TrackAllocation(void *userData,
|
||||||
size_t size,
|
size_t size,
|
||||||
size_t alignment,
|
size_t alignment,
|
||||||
VkSystemAllocationScope scope) {
|
VkSystemAllocationScope scope) {
|
||||||
vy_vk_alloc_stats *stats = userData;
|
|
||||||
if (alignment > stats->max_alignment)
|
|
||||||
stats->max_alignment = alignment;
|
|
||||||
stats->allocated += size;
|
|
||||||
vyLog("vk",
|
vyLog("vk",
|
||||||
"Allocation. Size: %zu, Alignment: %zu, Scope: %s",
|
"Allocation. Size: %zu, Alignment: %zu, Scope: %s",
|
||||||
size,
|
size,
|
||||||
alignment,
|
alignment,
|
||||||
AllocationScopeToString(scope));
|
AllocationScopeToString(scope));
|
||||||
LogAllocationStats(stats);
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return _aligned_malloc(size, alignment);
|
return _aligned_malloc(size, alignment);
|
||||||
#else
|
#else
|
||||||
@ -77,61 +60,430 @@ static void *TrackReallocation(void *userData,
|
|||||||
size_t size,
|
size_t size,
|
||||||
size_t alignment,
|
size_t alignment,
|
||||||
VkSystemAllocationScope scope) {
|
VkSystemAllocationScope scope) {
|
||||||
vy_vk_alloc_stats *stats = userData;
|
|
||||||
if (alignment > stats->max_alignment)
|
|
||||||
stats->max_alignment = alignment;
|
|
||||||
stats->reallocated += size;
|
|
||||||
vyLog("vk",
|
vyLog("vk",
|
||||||
"Reallocation. Size: %zu, Alignment: %zu, Scope: %s",
|
"Reallocation. Size: %zu, Alignment: %zu, Scope: %s",
|
||||||
size,
|
size,
|
||||||
alignment,
|
alignment,
|
||||||
AllocationScopeToString(scope));
|
AllocationScopeToString(scope));
|
||||||
LogAllocationStats(stats);
|
|
||||||
return realloc(original, size);
|
return realloc(original, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void TrackFree(void *userData, void *memory) {
|
static void TrackFree(void *userData, void *memory) {
|
||||||
vy_vk_alloc_stats *stats = userData;
|
|
||||||
vyLog("vk", "Free.");
|
|
||||||
LogAllocationStats(stats);
|
|
||||||
free(memory);
|
free(memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) {
|
static VkBool32 VKAPI_PTR
|
||||||
vyLog("vk", "Init");
|
DebugUtilsMessengerCb(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT types,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT *callbackData,
|
||||||
|
void *userData) {
|
||||||
|
|
||||||
vyRegisterCVAR(&r_EnableAPIAllocTracking);
|
return VK_FALSE;
|
||||||
|
|
||||||
_tracking_alloc_cbs.pUserData = &_alloc_stats;
|
|
||||||
_tracking_alloc_cbs.pfnAllocation = TrackAllocation;
|
|
||||||
_tracking_alloc_cbs.pfnReallocation = TrackReallocation;
|
|
||||||
_tracking_alloc_cbs.pfnFree = TrackFree;
|
|
||||||
|
|
||||||
if (r_EnableAPIAllocTracking.i) {
|
|
||||||
g_gpu.alloc_cb = &_tracking_alloc_cbs;
|
|
||||||
} else {
|
|
||||||
g_gpu.alloc_cb = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VkResult result;
|
VY_DLLEXPORT void vyRegisterCVars(void) {
|
||||||
|
vyRegisterCVAR(&r_VkEnableAPIAllocTracking);
|
||||||
|
vyRegisterCVAR(&r_VkPhysDeviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CreateInstance(void) {
|
||||||
|
VkResult result = volkInitialize();
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
vyReportError("vk", "Initialization failed: volkInitialize()");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
VkApplicationInfo app_info = {
|
VkApplicationInfo app_info = {
|
||||||
.apiVersion = VK_API_VERSION_1_0,
|
.apiVersion = VK_API_VERSION_1_2,
|
||||||
.applicationVersion = 0x00001000,
|
.applicationVersion = 0x00001000,
|
||||||
.engineVersion = 0x00001000,
|
.engineVersion = 0x00001000,
|
||||||
.pEngineName = "voyageEngine",
|
.pEngineName = "voyageEngine",
|
||||||
.pApplicationName = "Voyage",
|
.pApplicationName = "Voyage",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char *extensions[] = {
|
||||||
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||||
|
#ifdef _WIN32
|
||||||
|
"VK_KHR_win32_surface",
|
||||||
|
#elif defined(VY_USE_XLIB)
|
||||||
|
"VK_KHR_xlib_surface",
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef VY_DEBUG
|
||||||
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *layers[1];
|
||||||
|
unsigned int layer_count = 0;
|
||||||
|
#ifdef VY_DEBUG
|
||||||
|
/* Search for layers we want to enable */
|
||||||
|
uint32_t available_layer_count = 0;
|
||||||
|
result = vkEnumerateInstanceLayerProperties(&available_layer_count, NULL);
|
||||||
|
if (result == VK_SUCCESS) {
|
||||||
|
VkLayerProperties *props =
|
||||||
|
calloc(available_layer_count, sizeof(VkLayerProperties));
|
||||||
|
if (props) {
|
||||||
|
vkEnumerateInstanceLayerProperties(&available_layer_count, props);
|
||||||
|
for (uint32_t i = 0; i < available_layer_count; ++i) {
|
||||||
|
if (strcmp(props[i].layerName, "VK_LAYER_KHRONOS_validation") ==
|
||||||
|
0) {
|
||||||
|
layers[0] = "VK_LAYER_KHRONOS_validation";
|
||||||
|
layer_count = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(props);
|
||||||
|
} else {
|
||||||
|
vyLog("vk",
|
||||||
|
"Failed to allocate storage for instance layer properties.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vyLog("vk", "vkEnumerateInstanceLayerProperties failed.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
VkInstanceCreateInfo instance_info = {
|
VkInstanceCreateInfo instance_info = {
|
||||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||||
.pApplicationInfo = &app_info,
|
.pApplicationInfo = &app_info,
|
||||||
|
.ppEnabledExtensionNames = extensions,
|
||||||
|
.enabledExtensionCount = VY_ARRAY_COUNT(extensions),
|
||||||
|
.ppEnabledLayerNames = layers,
|
||||||
|
.enabledLayerCount = layer_count,
|
||||||
};
|
};
|
||||||
result = vkCreateInstance(&instance_info, g_gpu.alloc_cb, &g_gpu.instance);
|
result = vkCreateInstance(&instance_info, g_gpu.alloc_cb, &g_gpu.instance);
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
vyReportError("vk", "Failed to create the vulkan instance!");
|
vyReportError("vk", "Failed to create the vulkan instance.");
|
||||||
return 1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
volkLoadInstance(g_gpu.instance);
|
||||||
|
|
||||||
|
#ifdef VY_DEBUG
|
||||||
|
/* Create the debug utils messenger */
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT messenger_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
||||||
|
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
|
||||||
|
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
|
||||||
|
.pfnUserCallback = DebugUtilsMessengerCb,
|
||||||
|
};
|
||||||
|
vkCreateDebugUtilsMessengerEXT(g_gpu.instance,
|
||||||
|
&messenger_info,
|
||||||
|
g_gpu.alloc_cb,
|
||||||
|
&g_gpu.messenger);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CreateSurface(const vy_renderer_init_info *info) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
VkWin32SurfaceCreateInfoKHR surface_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
|
||||||
|
.hinstance = info->hInstance,
|
||||||
|
.hwnd = info->hWnd,
|
||||||
|
};
|
||||||
|
if (vkCreateWin32SurfaceKHR(g_gpu.instance,
|
||||||
|
&surface_info,
|
||||||
|
g_gpu.alloc_cb,
|
||||||
|
&g_gpu.surface) == VK_SUCCESS)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -100;
|
||||||
|
#elif defined(VY_USE_XLIB_KHR)
|
||||||
|
VkXlibSurfaceCreateInfoKHR surface_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||||
|
.dpy = info->display,
|
||||||
|
.window = info->window,
|
||||||
|
};
|
||||||
|
if (vkCreateXlibSurfaceKHR(g_gpu.instance,
|
||||||
|
&surface_info,
|
||||||
|
&g_gpu.alloc_cb,
|
||||||
|
&g_gpu.surface) == VK_SUCCESS)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -100;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t graphics;
|
||||||
|
uint32_t compute;
|
||||||
|
uint32_t present;
|
||||||
|
} vy_queue_indices;
|
||||||
|
|
||||||
|
static vy_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfaceKHR surface) {
|
||||||
|
vy_queue_indices indices = {.graphics = UINT32_MAX,
|
||||||
|
.compute = UINT32_MAX,
|
||||||
|
.present = UINT32_MAX};
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, NULL);
|
||||||
|
VkQueueFamilyProperties *props =
|
||||||
|
calloc(count, sizeof(VkQueueFamilyProperties));
|
||||||
|
if (!props) {
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(phys_dev, &count, props);
|
||||||
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
|
if (props[i].queueCount == 0)
|
||||||
|
continue;
|
||||||
|
if ((props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0)
|
||||||
|
indices.graphics = i;
|
||||||
|
if ((props[i].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0)
|
||||||
|
indices.compute = i;
|
||||||
|
|
||||||
|
VkBool32 present_supported = VK_FALSE;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(phys_dev,
|
||||||
|
i,
|
||||||
|
surface,
|
||||||
|
&present_supported);
|
||||||
|
if (present_supported)
|
||||||
|
indices.present = i;
|
||||||
|
}
|
||||||
|
free(props);
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckDeviceExtensionSupported(VkPhysicalDevice phys_dev) {
|
||||||
|
const char *required_extensions[] = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t extension_count;
|
||||||
|
vkEnumerateDeviceExtensionProperties(phys_dev,
|
||||||
|
NULL,
|
||||||
|
&extension_count,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
VkExtensionProperties *supported_extensions =
|
||||||
|
calloc(extension_count, sizeof(VkExtensionProperties));
|
||||||
|
if (!supported_extensions)
|
||||||
|
return false;
|
||||||
|
vkEnumerateDeviceExtensionProperties(phys_dev,
|
||||||
|
NULL,
|
||||||
|
&extension_count,
|
||||||
|
supported_extensions);
|
||||||
|
|
||||||
|
bool supported = true;
|
||||||
|
for (uint32_t i = 0; i < VY_ARRAY_COUNT(required_extensions); ++i) {
|
||||||
|
bool found = false;
|
||||||
|
for (uint32_t j = 0; j < extension_count; ++j) {
|
||||||
|
if (strncmp(supported_extensions[j].extensionName,
|
||||||
|
required_extensions[i],
|
||||||
|
VK_MAX_EXTENSION_NAME_SIZE) == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
supported = false;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(supported_extensions);
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ChoosePhysicalDevice(void) {
|
||||||
|
|
||||||
|
|
||||||
|
g_gpu.phys_device = VK_NULL_HANDLE;
|
||||||
|
uint32_t phys_device_count = 0;
|
||||||
|
VkResult result =
|
||||||
|
vkEnumeratePhysicalDevices(g_gpu.instance, &phys_device_count, NULL);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
vyReportError("vk", "Failed to enumerate the physical devices.");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
VkPhysicalDevice *phys_devices =
|
||||||
|
calloc(phys_device_count, sizeof(VkPhysicalDevice));
|
||||||
|
if (!phys_devices) {
|
||||||
|
vyReportError(
|
||||||
|
"vk",
|
||||||
|
"Failed to enumerate the physical devices: Out of memory.");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
vkEnumeratePhysicalDevices(g_gpu.instance,
|
||||||
|
&phys_device_count,
|
||||||
|
phys_devices);
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t highscore = 0;
|
||||||
|
uint32_t best_index = phys_device_count;
|
||||||
|
for (uint32_t i = 0; i < phys_device_count; ++i) {
|
||||||
|
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
||||||
|
.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
||||||
|
.pNext = NULL,
|
||||||
|
};
|
||||||
|
VkPhysicalDeviceProperties2 props = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||||
|
.pNext = &descriptor_indexing_props,
|
||||||
|
};
|
||||||
|
vkGetPhysicalDeviceProperties2(phys_devices[i], &props);
|
||||||
|
|
||||||
|
if (!CheckDeviceExtensionSupported(phys_devices[i]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vy_queue_indices indices =
|
||||||
|
RetrieveQueueIndices(phys_devices[i], g_gpu.surface);
|
||||||
|
if (indices.compute == UINT32_MAX || indices.present == UINT32_MAX ||
|
||||||
|
indices.graphics == UINT32_MAX)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint32_t score = 0;
|
||||||
|
|
||||||
|
if (props.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
||||||
|
score += 100;
|
||||||
|
|
||||||
|
score += (props.properties.limits.maxFramebufferWidth / 100) *
|
||||||
|
(props.properties.limits.maxFramebufferHeight / 100);
|
||||||
|
|
||||||
|
score += (descriptor_indexing_props
|
||||||
|
.shaderStorageBufferArrayNonUniformIndexingNative)
|
||||||
|
? 100
|
||||||
|
: 0;
|
||||||
|
score += (descriptor_indexing_props
|
||||||
|
.shaderSampledImageArrayNonUniformIndexingNative)
|
||||||
|
? 100
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (score > highscore) {
|
||||||
|
highscore = score;
|
||||||
|
best_index = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(props.properties.deviceName,
|
||||||
|
r_VkPhysDeviceName.s,
|
||||||
|
VK_MAX_PHYSICAL_DEVICE_NAME_SIZE) == 0) {
|
||||||
|
best_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (best_index < phys_device_count) {
|
||||||
|
g_gpu.phys_device = phys_devices[0];
|
||||||
|
VkPhysicalDeviceDescriptorIndexingProperties descriptor_indexing_props = {
|
||||||
|
.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES,
|
||||||
|
.pNext = NULL,
|
||||||
|
};
|
||||||
|
VkPhysicalDeviceProperties2 props = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||||
|
.pNext = &descriptor_indexing_props,
|
||||||
|
};
|
||||||
|
vkGetPhysicalDeviceProperties2(phys_devices[0], &props);
|
||||||
|
g_gpu.phys_device_props = props.properties;
|
||||||
|
g_gpu.descriptor_indexing_props = descriptor_indexing_props;
|
||||||
|
}
|
||||||
|
free(phys_devices);
|
||||||
|
|
||||||
|
if (g_gpu.phys_device == VK_NULL_HANDLE) {
|
||||||
|
vyReportError("vk", "Failed to find a suitable physical device.");
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CreateDevice(void) {
|
||||||
|
const char *extensions[] = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||||
|
};
|
||||||
|
|
||||||
|
vy_queue_indices queue_indices =
|
||||||
|
RetrieveQueueIndices(g_gpu.phys_device, g_gpu.surface);
|
||||||
|
|
||||||
|
float priority = 1.f;
|
||||||
|
|
||||||
|
uint32_t distinct_queue_count = 1;
|
||||||
|
VkDeviceQueueCreateInfo queue_info[3];
|
||||||
|
queue_info[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queue_info[0].pNext = NULL;
|
||||||
|
queue_info[0].flags = 0;
|
||||||
|
queue_info[0].queueCount = 1;
|
||||||
|
queue_info[0].queueFamilyIndex = queue_indices.graphics;
|
||||||
|
queue_info[0].pQueuePriorities = &priority;
|
||||||
|
if (queue_indices.compute != queue_indices.graphics) {
|
||||||
|
queue_info[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queue_info[1].pNext = NULL;
|
||||||
|
queue_info[1].flags = 0;
|
||||||
|
queue_info[1].queueCount = 1;
|
||||||
|
queue_info[1].queueFamilyIndex = queue_indices.compute;
|
||||||
|
queue_info[1].pQueuePriorities = &priority;
|
||||||
|
++distinct_queue_count;
|
||||||
|
}
|
||||||
|
if (queue_indices.present != queue_indices.graphics &&
|
||||||
|
queue_indices.present != queue_indices.compute) {
|
||||||
|
queue_info[distinct_queue_count].sType =
|
||||||
|
VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queue_info[distinct_queue_count].pNext = NULL;
|
||||||
|
queue_info[distinct_queue_count].flags = 0;
|
||||||
|
queue_info[distinct_queue_count].queueCount = 1;
|
||||||
|
queue_info[distinct_queue_count].queueFamilyIndex =
|
||||||
|
queue_indices.present;
|
||||||
|
queue_info[distinct_queue_count].pQueuePriorities = &priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VkDeviceCreateInfo device_info = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||||
|
.enabledExtensionCount = VY_ARRAY_COUNT(extensions),
|
||||||
|
.ppEnabledExtensionNames = extensions,
|
||||||
|
.pQueueCreateInfos = queue_info,
|
||||||
|
.queueCreateInfoCount = distinct_queue_count,
|
||||||
|
};
|
||||||
|
if (vkCreateDevice(g_gpu.phys_device,
|
||||||
|
&device_info,
|
||||||
|
g_gpu.alloc_cb,
|
||||||
|
&g_gpu.device) != VK_SUCCESS) {
|
||||||
|
vyReportError("vk", "Device creation failed.");
|
||||||
|
return -10;
|
||||||
|
}
|
||||||
|
|
||||||
|
vkGetDeviceQueue(g_gpu.device,
|
||||||
|
queue_indices.graphics,
|
||||||
|
0,
|
||||||
|
&g_gpu.graphics_queue);
|
||||||
|
vkGetDeviceQueue(g_gpu.device,
|
||||||
|
queue_indices.compute,
|
||||||
|
0,
|
||||||
|
&g_gpu.compute_queue);
|
||||||
|
vkGetDeviceQueue(g_gpu.device,
|
||||||
|
queue_indices.present,
|
||||||
|
0,
|
||||||
|
&g_gpu.present_queue);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) {
|
||||||
|
vyLog("vk", "Init");
|
||||||
|
|
||||||
|
_tracking_alloc_cbs.pUserData = NULL;
|
||||||
|
_tracking_alloc_cbs.pfnAllocation = TrackAllocation;
|
||||||
|
_tracking_alloc_cbs.pfnReallocation = TrackReallocation;
|
||||||
|
_tracking_alloc_cbs.pfnFree = TrackFree;
|
||||||
|
|
||||||
|
if (r_VkEnableAPIAllocTracking.i) {
|
||||||
|
g_gpu.alloc_cb = &_tracking_alloc_cbs;
|
||||||
|
} else {
|
||||||
|
g_gpu.alloc_cb = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int res = CreateInstance();
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
res = CreateSurface(info);
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
res = ChoosePhysicalDevice();
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
res = CreateDevice();
|
||||||
|
if (res != 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -139,5 +491,7 @@ VY_DLLEXPORT int vyInit(const vy_renderer_init_info *info) {
|
|||||||
VY_DLLEXPORT void vyShutdown(void) {
|
VY_DLLEXPORT void vyShutdown(void) {
|
||||||
vyLog("vk", "Shutdown");
|
vyLog("vk", "Shutdown");
|
||||||
|
|
||||||
|
vkDestroyDevice(g_gpu.device, g_gpu.alloc_cb);
|
||||||
|
vkDestroySurfaceKHR(g_gpu.instance, g_gpu.surface, g_gpu.alloc_cb);
|
||||||
vkDestroyInstance(g_gpu.instance, g_gpu.alloc_cb);
|
vkDestroyInstance(g_gpu.instance, g_gpu.alloc_cb);
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,9 @@ VY_DLLEXPORT int vyWin32Entry(HINSTANCE hInstance,
|
|||||||
int nCmdShow) {
|
int nCmdShow) {
|
||||||
|
|
||||||
__RegisterRuntimeCVars();
|
__RegisterRuntimeCVars();
|
||||||
|
vyRegisterRendererCVars();
|
||||||
|
|
||||||
|
/* TODO: Parse the cvar config file */
|
||||||
|
|
||||||
vy_fio_config fio_config = {0};
|
vy_fio_config fio_config = {0};
|
||||||
if (!vyInitFIO(&fio_config)) {
|
if (!vyInitFIO(&fio_config)) {
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
/* In renderer_api.h -> Not necessary for almost all gfx usage */
|
/* In renderer_api.h -> Not necessary for almost all gfx usage */
|
||||||
typedef struct vy_renderer_init_info_s vy_renderer_init_info;
|
typedef struct vy_renderer_init_info_s vy_renderer_init_info;
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyRegisterRendererCVars(void);
|
||||||
|
|
||||||
VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info);
|
VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info);
|
||||||
|
|
||||||
VY_DLLEXPORT void vyShutdownGFX(void);
|
VY_DLLEXPORT void vyShutdownGFX(void);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
vy_renderer_api g_renderer;
|
vy_renderer_api g_renderer;
|
||||||
static vy_dynlib _renderer_lib;
|
static vy_dynlib _renderer_lib;
|
||||||
|
static bool _renderer_loaded = false;
|
||||||
|
|
||||||
VY_CVAR_S(rt_Renderer,
|
VY_CVAR_S(rt_Renderer,
|
||||||
"Select the render backend. Available options: [vk], Default: vk",
|
"Select the render backend. Available options: [vk], Default: vk",
|
||||||
@ -41,6 +42,7 @@ static bool LoadRenderer(void)
|
|||||||
VY_DLLNAME("vyvk"));
|
VY_DLLNAME("vyvk"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
RETRIEVE_SYMBOL(RegisterCVars, vy_register_renderer_cvars_fn);
|
||||||
RETRIEVE_SYMBOL(Init, vy_init_renderer_fn);
|
RETRIEVE_SYMBOL(Init, vy_init_renderer_fn);
|
||||||
RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn);
|
RETRIEVE_SYMBOL(Shutdown, vy_shutdown_renderer_fn);
|
||||||
RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn);
|
RETRIEVE_SYMBOL(CompileComputePipeline, vy_compile_compute_pipeline_fn);
|
||||||
@ -54,10 +56,22 @@ static bool LoadRenderer(void)
|
|||||||
#undef RETRIEVE_SYMBOL
|
#undef RETRIEVE_SYMBOL
|
||||||
}
|
}
|
||||||
|
|
||||||
VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info) {
|
VY_DLLEXPORT void vyRegisterRendererCVars(void)
|
||||||
|
{
|
||||||
|
if (!_renderer_loaded) {
|
||||||
|
if (!LoadRenderer())
|
||||||
|
return;
|
||||||
|
_renderer_loaded = true;
|
||||||
|
}
|
||||||
|
g_renderer.RegisterCVars();
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT bool vyInitGFX(vy_renderer_init_info *renderer_info) {
|
||||||
|
if (!_renderer_loaded) {
|
||||||
if (!LoadRenderer())
|
if (!LoadRenderer())
|
||||||
return false;
|
return false;
|
||||||
|
g_renderer.RegisterCVars();
|
||||||
|
}
|
||||||
|
|
||||||
if (g_renderer.Init(renderer_info) != 0)
|
if (g_renderer.Init(renderer_info) != 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -37,6 +37,7 @@ typedef struct {
|
|||||||
size_t fragment_source_length;
|
size_t fragment_source_length;
|
||||||
} vy_graphics_pipeline_info;
|
} vy_graphics_pipeline_info;
|
||||||
|
|
||||||
|
typedef void vy_register_renderer_cvars_fn(void);
|
||||||
typedef int vy_init_renderer_fn(const vy_renderer_init_info *info);
|
typedef int vy_init_renderer_fn(const vy_renderer_init_info *info);
|
||||||
typedef void vy_shutdown_renderer_fn(void);
|
typedef void vy_shutdown_renderer_fn(void);
|
||||||
typedef vy_gfx_pipeline_id
|
typedef vy_gfx_pipeline_id
|
||||||
@ -45,6 +46,7 @@ typedef vy_gfx_pipeline_id
|
|||||||
vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info);
|
vy_compile_graphics_pipeline_fn(const vy_graphics_pipeline_info *info);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
vy_register_renderer_cvars_fn *RegisterCVars;
|
||||||
vy_init_renderer_fn *Init;
|
vy_init_renderer_fn *Init;
|
||||||
vy_shutdown_renderer_fn *Shutdown;
|
vy_shutdown_renderer_fn *Shutdown;
|
||||||
vy_compile_compute_pipeline_fn *CompileComputePipeline;
|
vy_compile_compute_pipeline_fn *CompileComputePipeline;
|
||||||
|
Loading…
Reference in New Issue
Block a user