mirror of
https://github.com/w23/xash3d-fwgs
synced 2024-12-16 22:20:01 +01:00
2c4d0846d4
- make staging/prep phase use separate command buffer - flush this command buffer early if needed - move command pool to its own module - move shader loading stuff to pipeline module - cleanup lots of command buffer passing for model loading, it can use staging explicitly now
830 lines
28 KiB
C
830 lines
28 KiB
C
#include "vk_core.h"
|
|
|
|
#include "vk_common.h"
|
|
#include "vk_textures.h"
|
|
#include "vk_overlay.h"
|
|
#include "vk_renderstate.h"
|
|
#include "vk_staging.h"
|
|
#include "vk_framectl.h"
|
|
#include "vk_brush.h"
|
|
#include "vk_scene.h"
|
|
#include "vk_cvar.h"
|
|
#include "vk_pipeline.h"
|
|
#include "vk_render.h"
|
|
#include "vk_geometry.h"
|
|
#include "vk_studio.h"
|
|
#include "vk_rtx.h"
|
|
#include "vk_descriptor.h"
|
|
#include "vk_nv_aftermath.h"
|
|
#include "vk_devmem.h"
|
|
#include "vk_commandpool.h"
|
|
|
|
// FIXME move this rt-specific stuff out
|
|
#include "vk_light.h"
|
|
|
|
#include "xash3d_types.h"
|
|
#include "cvardef.h"
|
|
#include "const.h" // required for ref_api.h
|
|
#include "ref_api.h"
|
|
#include "crtlib.h"
|
|
#include "com_strings.h"
|
|
#include "eiface.h"
|
|
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define XVK_PARSE_VERSION(v) \
|
|
VK_VERSION_MAJOR(v), \
|
|
VK_VERSION_MINOR(v), \
|
|
VK_VERSION_PATCH(v)
|
|
|
|
#define NULLINST_FUNCS(X) \
|
|
X(vkEnumerateInstanceVersion) \
|
|
X(vkCreateInstance) \
|
|
|
|
static PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
|
|
|
|
#define X(f) PFN_##f f = NULL;
|
|
NULLINST_FUNCS(X)
|
|
INSTANCE_FUNCS(X)
|
|
INSTANCE_DEBUG_FUNCS(X)
|
|
DEVICE_FUNCS(X)
|
|
DEVICE_FUNCS_RTX(X)
|
|
#undef X
|
|
|
|
static dllfunc_t nullinst_funcs[] = {
|
|
#define X(f) {#f, (void**)&f},
|
|
NULLINST_FUNCS(X)
|
|
#undef X
|
|
};
|
|
|
|
static dllfunc_t instance_funcs[] = {
|
|
#define X(f) {#f, (void**)&f},
|
|
INSTANCE_FUNCS(X)
|
|
#undef X
|
|
};
|
|
|
|
static dllfunc_t instance_debug_funcs[] = {
|
|
#define X(f) {#f, (void**)&f},
|
|
INSTANCE_DEBUG_FUNCS(X)
|
|
#undef X
|
|
};
|
|
|
|
static dllfunc_t device_funcs[] = {
|
|
#define X(f) {#f, (void**)&f},
|
|
DEVICE_FUNCS(X)
|
|
#undef X
|
|
};
|
|
|
|
static dllfunc_t device_funcs_rtx[] = {
|
|
#define X(f) {#f, (void**)&f},
|
|
DEVICE_FUNCS_RTX(X)
|
|
#undef X
|
|
};
|
|
|
|
static const char *validation_layers[] = {
|
|
"VK_LAYER_KHRONOS_validation",
|
|
};
|
|
|
|
static const char* device_extensions[] = {
|
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
|
|
|
// Optional: RTX
|
|
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
|
|
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
|
|
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
|
|
|
|
// FIXME make this not depend on RTX
|
|
#ifdef USE_AFTERMATH
|
|
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME,
|
|
VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
|
|
#endif
|
|
};
|
|
|
|
VkBool32 VKAPI_PTR debugCallback(
|
|
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
void *pUserData) {
|
|
(void)(pUserData);
|
|
(void)(messageTypes);
|
|
(void)(messageSeverity);
|
|
|
|
if (Q_strcmp(pCallbackData->pMessageIdName, "VUID-vkMapMemory-memory-00683") == 0)
|
|
return VK_FALSE;
|
|
|
|
/* if (messageSeverity != VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { */
|
|
/* gEngine.Con_Printf(S_WARN "Validation: %s\n", pCallbackData->pMessage); */
|
|
/* } */
|
|
|
|
// TODO better messages, not only errors, what are other arguments for, ...
|
|
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
|
|
gEngine.Con_Printf(S_ERROR "Validation: %s\n", pCallbackData->pMessage);
|
|
#ifdef _MSC_VER
|
|
__debugbreak();
|
|
#else
|
|
__builtin_trap();
|
|
#endif
|
|
}
|
|
return VK_FALSE;
|
|
}
|
|
|
|
vulkan_core_t vk_core = {0};
|
|
|
|
static void loadInstanceFunctions(dllfunc_t *funcs, int count)
|
|
{
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
*funcs[i].func = vkGetInstanceProcAddr(vk_core.instance, funcs[i].name);
|
|
if (!*funcs[i].func)
|
|
{
|
|
gEngine.Con_Printf( S_WARN "Function %s was not loaded\n", funcs[i].name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void loadDeviceFunctions(dllfunc_t *funcs, int count)
|
|
{
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
*funcs[i].func = vkGetDeviceProcAddr(vk_core.device, funcs[i].name);
|
|
if (!*funcs[i].func)
|
|
{
|
|
gEngine.Con_Printf( S_WARN "Function %s was not loaded\n", funcs[i].name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static qboolean createInstance( void )
|
|
{
|
|
const char ** instance_extensions = NULL;
|
|
unsigned int num_instance_extensions = vk_core.debug ? 1 : 0;
|
|
const VkApplicationInfo app_info = {
|
|
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
// TODO support versions 1.0 and 1.1 for simple traditional rendering
|
|
// This would require using older physical device features and props query structures
|
|
// .apiVersion = vk_core.rtx ? VK_API_VERSION_1_2 : VK_API_VERSION_1_1,
|
|
.apiVersion = VK_API_VERSION_1_2,
|
|
.applicationVersion = VK_MAKE_VERSION(0, 0, 0), // TODO
|
|
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
|
|
.pApplicationName = "",
|
|
.pEngineName = "xash3d-fwgs",
|
|
};
|
|
const VkValidationFeatureEnableEXT validation_features[] = {
|
|
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
|
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
|
|
};
|
|
const VkValidationFeaturesEXT validation_ext = {
|
|
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
|
|
.pEnabledValidationFeatures = validation_features,
|
|
.enabledValidationFeatureCount = COUNTOF(validation_features),
|
|
};
|
|
VkInstanceCreateInfo create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
|
.pApplicationInfo = &app_info,
|
|
.pNext = vk_core.validate ? &validation_ext : NULL,
|
|
};
|
|
|
|
int vid_extensions = gEngine.XVK_GetInstanceExtensions(0, NULL);
|
|
if (vid_extensions < 0)
|
|
{
|
|
gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" );
|
|
return false;
|
|
}
|
|
|
|
num_instance_extensions += vid_extensions;
|
|
|
|
instance_extensions = Mem_Malloc(vk_core.pool, sizeof(const char*) * num_instance_extensions);
|
|
vid_extensions = gEngine.XVK_GetInstanceExtensions(vid_extensions, instance_extensions);
|
|
if (vid_extensions < 0)
|
|
{
|
|
gEngine.Con_Printf( S_ERROR "Cannot get Vulkan instance extensions\n" );
|
|
Mem_Free((void*)instance_extensions);
|
|
return false;
|
|
}
|
|
|
|
if (vk_core.debug)
|
|
{
|
|
instance_extensions[vid_extensions] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
|
|
}
|
|
|
|
gEngine.Con_Reportf("Requesting instance extensions: %d\n", num_instance_extensions);
|
|
for (int i = 0; i < num_instance_extensions; ++i)
|
|
{
|
|
gEngine.Con_Reportf("\t%d: %s\n", i, instance_extensions[i]);
|
|
}
|
|
|
|
create_info.enabledExtensionCount = num_instance_extensions;
|
|
create_info.ppEnabledExtensionNames = instance_extensions;
|
|
|
|
if (vk_core.validate)
|
|
{
|
|
create_info.enabledLayerCount = ARRAYSIZE(validation_layers);
|
|
create_info.ppEnabledLayerNames = validation_layers;
|
|
|
|
gEngine.Con_Printf(S_WARN "Using Vulkan validation layers, expect severely degraded performance\n");
|
|
}
|
|
|
|
// TODO handle errors gracefully -- let it try next renderer
|
|
XVK_CHECK(vkCreateInstance(&create_info, NULL, &vk_core.instance));
|
|
|
|
loadInstanceFunctions(instance_funcs, ARRAYSIZE(instance_funcs));
|
|
|
|
if (vk_core.debug || vk_core.validate)
|
|
{
|
|
loadInstanceFunctions(instance_debug_funcs, ARRAYSIZE(instance_debug_funcs));
|
|
|
|
if (vk_core.validate) {
|
|
if (vkCreateDebugUtilsMessengerEXT) {
|
|
VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
|
|
.messageSeverity = 0x1111, //:vovka: VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
|
|
.messageType = 0x07,
|
|
.pfnUserCallback = debugCallback,
|
|
};
|
|
XVK_CHECK(vkCreateDebugUtilsMessengerEXT(vk_core.instance, &debug_create_info, NULL, &vk_core.debug_messenger));
|
|
} else {
|
|
gEngine.Con_Printf(S_WARN "Vulkan debug utils messenger is not available\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
Mem_Free((void*)instance_extensions);
|
|
return true;
|
|
}
|
|
|
|
static const VkExtensionProperties *findExtension( const VkExtensionProperties *exts, uint32_t num_exts, const char *extension )
|
|
{
|
|
for (uint32_t i = 0; i < num_exts; ++i) {
|
|
if (strncmp(exts[i].extensionName, extension, sizeof(exts[i].extensionName)) == 0)
|
|
return exts + i;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static qboolean deviceSupportsRtx( const VkExtensionProperties *exts, uint32_t num_exts )
|
|
{
|
|
qboolean result = true;
|
|
|
|
for (int i = 1 /* skip swapchain ext */; i < ARRAYSIZE(device_extensions); ++i) {
|
|
if (!findExtension(exts, num_exts, device_extensions[i])) {
|
|
gEngine.Con_Reportf(S_ERROR "Extension %s is not supported\n", device_extensions[i]);
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// FIXME this is almost exactly the physical_device_t, reuse
|
|
typedef struct {
|
|
VkPhysicalDevice device;
|
|
VkPhysicalDeviceFeatures2 features;
|
|
VkPhysicalDeviceProperties props;
|
|
uint32_t queue_index;
|
|
qboolean anisotropy;
|
|
qboolean ray_tracing;
|
|
} vk_available_device_t;
|
|
|
|
static int enumerateDevices( vk_available_device_t **available_devices ) {
|
|
VkPhysicalDevice *physical_devices = NULL;
|
|
uint32_t num_physical_devices = 0;
|
|
vk_available_device_t *this_device = NULL;
|
|
|
|
XVK_CHECK(vkEnumeratePhysicalDevices(vk_core.instance, &num_physical_devices, physical_devices));
|
|
physical_devices = Mem_Malloc(vk_core.pool, sizeof(VkPhysicalDevice) * num_physical_devices);
|
|
XVK_CHECK(vkEnumeratePhysicalDevices(vk_core.instance, &num_physical_devices, physical_devices));
|
|
gEngine.Con_Reportf("Have %u devices:\n", num_physical_devices);
|
|
|
|
vk_core.num_devices = num_physical_devices;
|
|
vk_core.devices = Mem_Calloc( vk_core.pool, num_physical_devices * sizeof( *vk_core.devices ));
|
|
|
|
*available_devices = Mem_Malloc(vk_core.pool, num_physical_devices * sizeof(vk_available_device_t));
|
|
this_device = *available_devices;
|
|
for (uint32_t i = 0; i < num_physical_devices; ++i) {
|
|
uint32_t queue_index = VK_QUEUE_FAMILY_IGNORED;
|
|
VkPhysicalDeviceProperties props;
|
|
VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,};
|
|
|
|
// FIXME also pay attention to various device limits. We depend on them implicitly now.
|
|
|
|
vkGetPhysicalDeviceProperties(physical_devices[i], &props);
|
|
|
|
// Store devices list in vk_core.devices for pfnGetRenderDevices
|
|
vk_core.devices[i].vendorID = props.vendorID;
|
|
vk_core.devices[i].deviceID = props.deviceID;
|
|
switch( props.deviceType )
|
|
{
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
|
|
vk_core.devices[i].deviceType = REF_DEVICE_TYPE_INTERGRATED_GPU;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
|
|
vk_core.devices[i].deviceType = REF_DEVICE_TYPE_DISCRETE_GPU;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
|
|
vk_core.devices[i].deviceType = REF_DEVICE_TYPE_VIRTUAL_GPU;
|
|
break;
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU:
|
|
vk_core.devices[i].deviceType = REF_DEVICE_TYPE_CPU;
|
|
break;
|
|
default:
|
|
vk_core.devices[i].deviceType = REF_DEVICE_TYPE_OTHER;
|
|
break;
|
|
}
|
|
Q_strncpy( vk_core.devices[i].deviceName, props.deviceName, sizeof( vk_core.devices[i].deviceName ));
|
|
|
|
gEngine.Con_Printf("\t%u: %04x:%04x %d %s %u.%u.%u %u.%u.%u\n",
|
|
i, props.vendorID, props.deviceID, props.deviceType, props.deviceName,
|
|
XVK_PARSE_VERSION(props.driverVersion), XVK_PARSE_VERSION(props.apiVersion));
|
|
|
|
{
|
|
uint32_t num_queue_family_properties = 0;
|
|
VkQueueFamilyProperties *queue_family_props = NULL;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &num_queue_family_properties, queue_family_props);
|
|
queue_family_props = Mem_Malloc(vk_core.pool, sizeof(VkQueueFamilyProperties) * num_queue_family_properties);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &num_queue_family_properties, queue_family_props);
|
|
|
|
// Find queue family that supports needed properties
|
|
for (uint32_t j = 0; j < num_queue_family_properties; ++j) {
|
|
VkBool32 supports_present = 0;
|
|
const qboolean supports_graphics = !!(queue_family_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT);
|
|
const qboolean supports_compute = !!(queue_family_props[j].queueFlags & VK_QUEUE_COMPUTE_BIT);
|
|
vkGetPhysicalDeviceSurfaceSupportKHR(physical_devices[i], j, vk_core.surface.surface, &supports_present);
|
|
|
|
gEngine.Con_Reportf("\t\tQueue %d/%d present: %d graphics: %d compute: %d\n", j, num_queue_family_properties, supports_present, supports_graphics, supports_compute);
|
|
|
|
if (!supports_present)
|
|
continue;
|
|
|
|
// ray tracing needs compute
|
|
// also, by vk spec graphics queue must support compute
|
|
if (!supports_graphics || !supports_compute)
|
|
continue;
|
|
|
|
queue_index = j;
|
|
break;
|
|
}
|
|
|
|
Mem_Free(queue_family_props);
|
|
}
|
|
|
|
if (queue_index == VK_QUEUE_FAMILY_IGNORED) {
|
|
gEngine.Con_Printf( S_WARN "\t\tSkipping this device as compatible queue (which has both compute and graphics and also can present) not found\n" );
|
|
continue;
|
|
}
|
|
|
|
{
|
|
uint32_t num_device_extensions = 0;
|
|
VkExtensionProperties *extensions;
|
|
|
|
XVK_CHECK(vkEnumerateDeviceExtensionProperties(physical_devices[i], NULL, &num_device_extensions, NULL));
|
|
extensions = Mem_Malloc(vk_core.pool, sizeof(VkExtensionProperties) * num_device_extensions);
|
|
XVK_CHECK(vkEnumerateDeviceExtensionProperties(physical_devices[i], NULL, &num_device_extensions, extensions));
|
|
|
|
gEngine.Con_Reportf( "\t\tSupported device extensions: %u\n", num_device_extensions);
|
|
for (int j = 0; j < ARRAYSIZE(device_extensions); ++j) {
|
|
const VkExtensionProperties *ext_prop = findExtension(extensions, num_device_extensions, device_extensions[j]);
|
|
if (!ext_prop) {
|
|
gEngine.Con_Printf( "\t\t\t%s: N/A\n", device_extensions[j]);
|
|
} else {
|
|
gEngine.Con_Printf( "\t\t\t%s: %u.%u.%u\n", ext_prop->extensionName, XVK_PARSE_VERSION(ext_prop->specVersion));
|
|
}
|
|
}
|
|
|
|
vkGetPhysicalDeviceFeatures2(physical_devices[i], &features);
|
|
this_device->anisotropy = features.features.samplerAnisotropy;
|
|
gEngine.Con_Printf("\t\tAnistoropy supported: %d\n", this_device->anisotropy);
|
|
|
|
this_device->ray_tracing = deviceSupportsRtx(extensions, num_device_extensions);
|
|
gEngine.Con_Printf("\t\tRay tracing supported: %d\n", this_device->ray_tracing);
|
|
|
|
Mem_Free(extensions);
|
|
}
|
|
|
|
this_device->device = physical_devices[i];
|
|
this_device->queue_index = queue_index;
|
|
this_device->features = features;
|
|
this_device->props = props;
|
|
++this_device;
|
|
}
|
|
|
|
Mem_Free(physical_devices);
|
|
|
|
return this_device - *available_devices;
|
|
}
|
|
|
|
static void devicePrintMemoryInfo(const VkPhysicalDeviceMemoryProperties *props, const VkPhysicalDeviceMemoryBudgetPropertiesEXT *budget) {
|
|
gEngine.Con_Printf("Memory heaps: %d\n", props->memoryHeapCount);
|
|
for (int i = 0; i < (int)props->memoryHeapCount; ++i) {
|
|
const VkMemoryHeap* const heap = props->memoryHeaps + i;
|
|
gEngine.Con_Printf(" %d: size=%dMb used=%dMb avail=%dMb device_local=%d\n", i,
|
|
(int)(heap->size / (1024 * 1024)),
|
|
(int)(budget->heapUsage[i] / (1024 * 1024)),
|
|
(int)(budget->heapBudget[i] / (1024 * 1024)),
|
|
!!(heap->flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT));
|
|
}
|
|
|
|
gEngine.Con_Printf("Memory types: %d\n", props->memoryTypeCount);
|
|
for (int i = 0; i < (int)props->memoryTypeCount; ++i) {
|
|
const VkMemoryType* const type = props->memoryTypes + i;
|
|
gEngine.Con_Printf(" %d: bit=0x%x heap=%d flags=%c%c%c%c%c\n", i,
|
|
(1 << i),
|
|
type->heapIndex,
|
|
type->propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ? 'D' : '.',
|
|
type->propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT ? 'V' : '.',
|
|
type->propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ? 'C' : '.',
|
|
type->propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT ? '$' : '.',
|
|
type->propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT ? 'L' : '.'
|
|
);
|
|
}
|
|
}
|
|
|
|
static qboolean createDevice( void ) {
|
|
void *head = NULL;
|
|
vk_available_device_t *available_devices;
|
|
const int num_available_devices = enumerateDevices( &available_devices );
|
|
char unique_deviceID[16];
|
|
const qboolean is_target_device = vk_device_target_id && Q_stricmp(vk_device_target_id->string, "") && num_available_devices > 0;
|
|
qboolean is_target_device_found = false;
|
|
|
|
for (int i = 0; i < num_available_devices; ++i) {
|
|
const vk_available_device_t *candidate_device = available_devices + i;
|
|
// Skip non-target device
|
|
Q_snprintf( unique_deviceID, sizeof( unique_deviceID ), "%04x:%04x", candidate_device->props.vendorID, candidate_device->props.deviceID );
|
|
if (is_target_device && !is_target_device_found && Q_stricmp(vk_device_target_id->string, unique_deviceID)) {
|
|
if (i == num_available_devices-1) {
|
|
gEngine.Con_Printf("Not found device %s, start on %s. Please set a valid device.\n", vk_device_target_id->string, unique_deviceID);
|
|
} else {
|
|
gEngine.Con_Printf("Skip device %s, because selected %s\n", unique_deviceID, vk_device_target_id->string);
|
|
continue;
|
|
}
|
|
} else {
|
|
is_target_device_found = true;
|
|
}
|
|
|
|
if (candidate_device->ray_tracing && !CVAR_TO_BOOL(vk_only)) {
|
|
vk_core.rtx = true;
|
|
}
|
|
|
|
VkPhysicalDeviceAccelerationStructureFeaturesKHR accel_feature = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR,
|
|
.pNext = head,
|
|
.accelerationStructure = VK_TRUE,
|
|
};
|
|
head = &accel_feature;
|
|
VkPhysicalDevice16BitStorageFeatures sixteen_bit_feature = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
|
|
.pNext = head,
|
|
.storageBuffer16BitAccess = VK_TRUE,
|
|
};
|
|
head = &sixteen_bit_feature;
|
|
VkPhysicalDeviceVulkan12Features vk12_features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
|
.pNext = head,
|
|
.shaderSampledImageArrayNonUniformIndexing = VK_TRUE, // Needed for texture sampling in closest hit shader
|
|
.storageBuffer8BitAccess = VK_TRUE,
|
|
.uniformAndStorageBuffer8BitAccess = VK_TRUE,
|
|
.bufferDeviceAddress = VK_TRUE,
|
|
};
|
|
head = &vk12_features;
|
|
VkPhysicalDeviceRayTracingPipelineFeaturesKHR ray_tracing_pipeline_feature = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR,
|
|
.pNext = head,
|
|
.rayTracingPipeline = VK_TRUE,
|
|
// TODO .rayTraversalPrimitiveCulling = VK_TRUE,
|
|
};
|
|
|
|
if (vk_core.rtx) {
|
|
head = &ray_tracing_pipeline_feature;
|
|
} else {
|
|
head = NULL;
|
|
}
|
|
|
|
VkPhysicalDeviceFeatures2 features = {
|
|
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
|
.pNext = head,
|
|
.features.samplerAnisotropy = candidate_device->features.features.samplerAnisotropy,
|
|
};
|
|
head = &features;
|
|
|
|
#ifdef USE_AFTERMATH
|
|
VkDeviceDiagnosticsConfigCreateInfoNV diag_config_nv = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV,
|
|
.pNext = head,
|
|
.flags = VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV | VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV
|
|
};
|
|
head = &diag_config_nv;
|
|
#endif
|
|
|
|
const float queue_priorities[1] = {1.f};
|
|
VkDeviceQueueCreateInfo queue_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
|
.flags = 0,
|
|
.queueFamilyIndex = candidate_device->queue_index,
|
|
.queueCount = ARRAYSIZE(queue_priorities),
|
|
.pQueuePriorities = queue_priorities,
|
|
};
|
|
|
|
VkDeviceCreateInfo create_info = {
|
|
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
|
.pNext = head,
|
|
.flags = 0,
|
|
.queueCreateInfoCount = 1,
|
|
.pQueueCreateInfos = &queue_info,
|
|
|
|
// TODO for now device_extensions array contains one required extension (swapchain)
|
|
// and a bunch of RTX-optional extensions. Use only one if RTX was not requested,
|
|
// and only use all of them if it was.
|
|
.enabledExtensionCount = vk_core.rtx ? ARRAYSIZE(device_extensions) : 1,
|
|
.ppEnabledExtensionNames = device_extensions,
|
|
};
|
|
|
|
{
|
|
vk_core.physical_device.memory_properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
|
|
vk_core.physical_device.memory_properties2.pNext = &vk_core.physical_device.memory_budget;
|
|
vk_core.physical_device.memory_budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
|
|
vk_core.physical_device.memory_budget.pNext = NULL;
|
|
vkGetPhysicalDeviceMemoryProperties2(candidate_device->device, &vk_core.physical_device.memory_properties2);
|
|
}
|
|
|
|
gEngine.Con_Printf("Trying device #%d: %04x:%04x %d %s %u.%u.%u %u.%u.%u\n",
|
|
i, candidate_device->props.vendorID, candidate_device->props.deviceID, candidate_device->props.deviceType, candidate_device->props.deviceName,
|
|
XVK_PARSE_VERSION(candidate_device->props.driverVersion), XVK_PARSE_VERSION(candidate_device->props.apiVersion));
|
|
|
|
devicePrintMemoryInfo(&vk_core.physical_device.memory_properties2.memoryProperties, &vk_core.physical_device.memory_budget);
|
|
|
|
{
|
|
const VkResult result = vkCreateDevice(candidate_device->device, &create_info, NULL, &vk_core.device);
|
|
if (result != VK_SUCCESS) {
|
|
gEngine.Con_Printf( S_ERROR "%s:%d vkCreateDevice failed (%d): %s\n",
|
|
__FILE__, __LINE__, result, R_VkResultName(result));
|
|
continue;
|
|
}
|
|
}
|
|
|
|
vk_core.physical_device.device = candidate_device->device;
|
|
vk_core.physical_device.anisotropy_enabled = features.features.samplerAnisotropy;
|
|
vk_core.physical_device.properties = candidate_device->props;
|
|
|
|
loadDeviceFunctions(device_funcs, ARRAYSIZE(device_funcs));
|
|
|
|
if (vk_core.rtx)
|
|
{
|
|
loadDeviceFunctions(device_funcs_rtx, ARRAYSIZE(device_funcs_rtx));
|
|
vk_core.physical_device.properties2.pNext = &vk_core.physical_device.properties_accel;
|
|
vk_core.physical_device.properties_accel.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_PROPERTIES_KHR;
|
|
vk_core.physical_device.properties_accel.pNext = &vk_core.physical_device.properties_ray_tracing_pipeline;
|
|
vk_core.physical_device.properties_ray_tracing_pipeline.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR;
|
|
vk_core.physical_device.properties_ray_tracing_pipeline.pNext = NULL;
|
|
}
|
|
|
|
// TODO should we check Vk version first?
|
|
vk_core.physical_device.properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
vkGetPhysicalDeviceProperties2(vk_core.physical_device.device, &vk_core.physical_device.properties2);
|
|
|
|
if (vk_core.rtx) {
|
|
//g_rtx.sbt_record_size = ALIGN_UP(vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupHandleSize, vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupHandleAlignment);
|
|
vk_core.physical_device.sbt_record_size = ALIGN_UP(vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupHandleSize, vk_core.physical_device.properties_ray_tracing_pipeline.shaderGroupBaseAlignment);
|
|
}
|
|
|
|
vkGetDeviceQueue(vk_core.device, 0, 0, &vk_core.queue);
|
|
return true;
|
|
}
|
|
|
|
gEngine.Con_Printf( S_ERROR "No compatibe Vulkan devices found. Vulkan render will not be available\n" );
|
|
return false;
|
|
}
|
|
static qboolean initSurface( void )
|
|
{
|
|
XVK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_present_modes, vk_core.surface.present_modes));
|
|
vk_core.surface.present_modes = Mem_Malloc(vk_core.pool, sizeof(*vk_core.surface.present_modes) * vk_core.surface.num_present_modes);
|
|
XVK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_present_modes, vk_core.surface.present_modes));
|
|
|
|
gEngine.Con_Printf("Supported surface present modes: %u\n", vk_core.surface.num_present_modes);
|
|
for (uint32_t i = 0; i < vk_core.surface.num_present_modes; ++i)
|
|
{
|
|
gEngine.Con_Reportf("\t%u: %s (%u)\n", i, R_VkPresentModeName(vk_core.surface.present_modes[i]), vk_core.surface.present_modes[i]);
|
|
}
|
|
|
|
XVK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_surface_formats, vk_core.surface.surface_formats));
|
|
vk_core.surface.surface_formats = Mem_Malloc(vk_core.pool, sizeof(*vk_core.surface.surface_formats) * vk_core.surface.num_surface_formats);
|
|
XVK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_core.physical_device.device, vk_core.surface.surface, &vk_core.surface.num_surface_formats, vk_core.surface.surface_formats));
|
|
|
|
gEngine.Con_Reportf("Supported surface formats: %u\n", vk_core.surface.num_surface_formats);
|
|
for (uint32_t i = 0; i < vk_core.surface.num_surface_formats; ++i)
|
|
{
|
|
// TODO symbolicate
|
|
gEngine.Con_Reportf("\t%u: %s(%u) %s(%u)\n", i,
|
|
R_VkFormatName(vk_core.surface.surface_formats[i].format), vk_core.surface.surface_formats[i].format,
|
|
R_VkColorSpaceName(vk_core.surface.surface_formats[i].colorSpace), vk_core.surface.surface_formats[i].colorSpace);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
qboolean R_VkInit( void )
|
|
{
|
|
// FIXME !!!! handle initialization errors properly: destroy what has already been created
|
|
|
|
vk_core.validate = !!gEngine.Sys_CheckParm("-vkvalidate");
|
|
vk_core.debug = vk_core.validate || !!(gEngine.Sys_CheckParm("-vkdebug") || gEngine.Sys_CheckParm("-gldebug"));
|
|
vk_core.rtx = false;
|
|
VK_LoadCvars();
|
|
|
|
if( !gEngine.R_Init_Video( REF_VULKAN )) // request Vulkan surface
|
|
{
|
|
gEngine.Con_Printf( S_ERROR "Cannot initialize Vulkan video\n" );
|
|
return false;
|
|
}
|
|
|
|
vkGetInstanceProcAddr = gEngine.XVK_GetVkGetInstanceProcAddr();
|
|
if (!vkGetInstanceProcAddr)
|
|
{
|
|
gEngine.Con_Printf( S_ERROR "Cannot get vkGetInstanceProcAddr address\n" );
|
|
return false;
|
|
}
|
|
|
|
vk_core.pool = Mem_AllocPool("Vulkan pool");
|
|
|
|
loadInstanceFunctions(nullinst_funcs, ARRAYSIZE(nullinst_funcs));
|
|
|
|
if (vkEnumerateInstanceVersion)
|
|
{
|
|
vkEnumerateInstanceVersion(&vk_core.vulkan_version);
|
|
}
|
|
else
|
|
{
|
|
vk_core.vulkan_version = VK_MAKE_VERSION(1, 0, 0);
|
|
}
|
|
|
|
gEngine.Con_Printf( "Vulkan version %u.%u.%u\n", XVK_PARSE_VERSION(vk_core.vulkan_version));
|
|
|
|
if (!createInstance())
|
|
return false;
|
|
|
|
vk_core.surface.surface = gEngine.XVK_CreateSurface(vk_core.instance);
|
|
if (!vk_core.surface.surface)
|
|
{
|
|
gEngine.Con_Printf( S_ERROR "Cannot create Vulkan surface\n" );
|
|
return false;
|
|
}
|
|
|
|
#if USE_AFTERMATH
|
|
if (!VK_AftermathInit()) {
|
|
gEngine.Con_Printf( S_ERROR "Cannot initialize Nvidia Nsight Aftermath SDK\n" );
|
|
}
|
|
#endif
|
|
|
|
if (!createDevice())
|
|
return false;
|
|
|
|
VK_LoadCvarsAfterInit();
|
|
|
|
if (!initSurface())
|
|
return false;
|
|
|
|
if (!VK_DevMemInit())
|
|
return false;
|
|
|
|
if (!R_VkStagingInit())
|
|
return false;
|
|
|
|
// TODO move this to vk_texture module
|
|
{
|
|
VkSamplerCreateInfo sci = {
|
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
|
.magFilter = VK_FILTER_LINEAR,
|
|
.minFilter = VK_FILTER_LINEAR,
|
|
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,// TODO CLAMP_TO_EDGE, for menus
|
|
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,//CLAMP_TO_EDGE,
|
|
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
|
|
.anisotropyEnable = vk_core.physical_device.anisotropy_enabled,
|
|
.maxAnisotropy = vk_core.physical_device.properties.limits.maxSamplerAnisotropy,
|
|
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
|
.unnormalizedCoordinates = VK_FALSE,
|
|
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR,
|
|
.minLod = 0.f,
|
|
.maxLod = 16.,
|
|
};
|
|
XVK_CHECK(vkCreateSampler(vk_core.device, &sci, NULL, &vk_core.default_sampler));
|
|
}
|
|
|
|
if (!VK_PipelineInit())
|
|
return false;
|
|
|
|
// TODO ...
|
|
if (!VK_DescriptorInit())
|
|
return false;
|
|
|
|
if (!VK_FrameCtlInit())
|
|
return false;
|
|
|
|
if (!R_GeometryBuffer_Init())
|
|
return false;
|
|
|
|
if (!VK_RenderInit())
|
|
return false;
|
|
|
|
VK_StudioInit();
|
|
|
|
VK_SceneInit();
|
|
|
|
initTextures();
|
|
|
|
// All below need render_pass
|
|
|
|
if (!R_VkOverlay_Init())
|
|
return false;
|
|
|
|
if (!VK_BrushInit())
|
|
return false;
|
|
|
|
if (vk_core.rtx)
|
|
{
|
|
if (!VK_RayInit())
|
|
return false;
|
|
|
|
// FIXME move all this to rt-specific modules
|
|
VK_LightsInit();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void R_VkShutdown( void ) {
|
|
XVK_CHECK(vkDeviceWaitIdle(vk_core.device));
|
|
|
|
if (vk_core.rtx)
|
|
{
|
|
VK_LightsShutdown();
|
|
VK_RayShutdown();
|
|
}
|
|
|
|
VK_BrushShutdown();
|
|
VK_StudioShutdown();
|
|
R_VkOverlay_Shutdown();
|
|
|
|
VK_RenderShutdown();
|
|
R_GeometryBuffer_Shutdown();
|
|
|
|
VK_FrameCtlShutdown();
|
|
|
|
destroyTextures();
|
|
|
|
VK_PipelineShutdown();
|
|
|
|
VK_DescriptorShutdown();
|
|
|
|
vkDestroySampler(vk_core.device, vk_core.default_sampler, NULL);
|
|
R_VkStagingShutdown();
|
|
|
|
VK_DevMemDestroy();
|
|
|
|
vkDestroyDevice(vk_core.device, NULL);
|
|
|
|
#if USE_AFTERMATH
|
|
VK_AftermathShutdown();
|
|
#endif
|
|
|
|
if (vk_core.debug_messenger)
|
|
{
|
|
vkDestroyDebugUtilsMessengerEXT(vk_core.instance, vk_core.debug_messenger, NULL);
|
|
}
|
|
|
|
Mem_Free(vk_core.surface.present_modes);
|
|
Mem_Free(vk_core.surface.surface_formats);
|
|
vkDestroySurfaceKHR(vk_core.instance, vk_core.surface.surface, NULL);
|
|
vkDestroyInstance(vk_core.instance, NULL);
|
|
Mem_FreePool(&vk_core.pool);
|
|
|
|
gEngine.R_Free_Video();
|
|
}
|
|
|
|
VkSemaphore R_VkSemaphoreCreate( void ) {
|
|
VkSemaphore sema;
|
|
VkSemaphoreCreateInfo sci = {
|
|
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
|
.flags = 0,
|
|
};
|
|
XVK_CHECK(vkCreateSemaphore(vk_core.device, &sci, NULL, &sema));
|
|
return sema;
|
|
}
|
|
|
|
void R_VkSemaphoreDestroy(VkSemaphore sema) {
|
|
vkDestroySemaphore(vk_core.device, sema, NULL);
|
|
}
|
|
|
|
VkFence R_VkFenceCreate( qboolean signaled ) {
|
|
VkFence fence;
|
|
const VkFenceCreateInfo fci = {
|
|
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
.flags = signaled ? VK_FENCE_CREATE_SIGNALED_BIT : 0,
|
|
};
|
|
XVK_CHECK(vkCreateFence(vk_core.device, &fci, NULL, &fence));
|
|
return fence;
|
|
}
|
|
|
|
void R_VkFenceDestroy(VkFence fence) {
|
|
vkDestroyFence(vk_core.device, fence, NULL);
|
|
}
|