[Vulkan] Float controls properties
This commit is contained in:
parent
8febf02a39
commit
715d614f5e
|
@ -28,7 +28,8 @@ SpirvShaderTranslator::Features::Features(bool all)
|
||||||
max_storage_buffer_range(all ? UINT32_MAX : (128 * 1024 * 1024)),
|
max_storage_buffer_range(all ? UINT32_MAX : (128 * 1024 * 1024)),
|
||||||
clip_distance(all),
|
clip_distance(all),
|
||||||
cull_distance(all),
|
cull_distance(all),
|
||||||
float_controls(all) {}
|
signed_zero_inf_nan_preserve_float32(all),
|
||||||
|
denorm_flush_to_zero_float32(all) {}
|
||||||
|
|
||||||
SpirvShaderTranslator::Features::Features(
|
SpirvShaderTranslator::Features::Features(
|
||||||
const ui::vulkan::VulkanProvider& provider)
|
const ui::vulkan::VulkanProvider& provider)
|
||||||
|
@ -48,8 +49,18 @@ SpirvShaderTranslator::Features::Features(
|
||||||
} else {
|
} else {
|
||||||
spirv_version = spv::Spv_1_0;
|
spirv_version = spv::Spv_1_0;
|
||||||
}
|
}
|
||||||
float_controls = spirv_version >= spv::Spv_1_4 ||
|
if (spirv_version >= spv::Spv_1_4 ||
|
||||||
device_extensions.khr_shader_float_controls;
|
device_extensions.khr_shader_float_controls) {
|
||||||
|
const VkPhysicalDeviceFloatControlsPropertiesKHR&
|
||||||
|
float_controls_properties = provider.device_float_controls_properties();
|
||||||
|
signed_zero_inf_nan_preserve_float32 =
|
||||||
|
bool(float_controls_properties.shaderSignedZeroInfNanPreserveFloat32);
|
||||||
|
denorm_flush_to_zero_float32 =
|
||||||
|
bool(float_controls_properties.shaderDenormFlushToZeroFloat32);
|
||||||
|
} else {
|
||||||
|
signed_zero_inf_nan_preserve_float32 = false;
|
||||||
|
denorm_flush_to_zero_float32 = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpirvShaderTranslator::SpirvShaderTranslator(const Features& features)
|
SpirvShaderTranslator::SpirvShaderTranslator(const Features& features)
|
||||||
|
@ -82,7 +93,8 @@ void SpirvShaderTranslator::StartTranslation() {
|
||||||
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
|
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
|
||||||
: spv::CapabilityShader);
|
: spv::CapabilityShader);
|
||||||
if (features_.spirv_version < spv::Spv_1_4) {
|
if (features_.spirv_version < spv::Spv_1_4) {
|
||||||
if (features_.float_controls) {
|
if (features_.signed_zero_inf_nan_preserve_float32 ||
|
||||||
|
features_.denorm_flush_to_zero_float32) {
|
||||||
builder_->addExtension("SPV_KHR_float_controls");
|
builder_->addExtension("SPV_KHR_float_controls");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -511,21 +523,21 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||||
? spv::ExecutionModelTessellationEvaluation
|
? spv::ExecutionModelTessellationEvaluation
|
||||||
: spv::ExecutionModelVertex;
|
: spv::ExecutionModelVertex;
|
||||||
}
|
}
|
||||||
// TODO(Triang3l): Re-enable float controls when
|
if (features_.denorm_flush_to_zero_float32) {
|
||||||
// VkPhysicalDeviceFloatControlsPropertiesKHR are handled.
|
|
||||||
/* if (features_.float_controls) {
|
|
||||||
// Flush to zero, similar to the real hardware, also for things like Shader
|
// Flush to zero, similar to the real hardware, also for things like Shader
|
||||||
// Model 3 multiplication emulation.
|
// Model 3 multiplication emulation.
|
||||||
builder_->addCapability(spv::CapabilityDenormFlushToZero);
|
builder_->addCapability(spv::CapabilityDenormFlushToZero);
|
||||||
builder_->addExecutionMode(function_main_,
|
builder_->addExecutionMode(function_main_,
|
||||||
spv::ExecutionModeDenormFlushToZero, 32);
|
spv::ExecutionModeDenormFlushToZero, 32);
|
||||||
|
}
|
||||||
|
if (features_.signed_zero_inf_nan_preserve_float32) {
|
||||||
// Signed zero used to get VFACE from ps_param_gen, also special behavior
|
// Signed zero used to get VFACE from ps_param_gen, also special behavior
|
||||||
// for infinity in certain instructions (such as logarithm, reciprocal,
|
// for infinity in certain instructions (such as logarithm, reciprocal,
|
||||||
// muls_prev2).
|
// muls_prev2).
|
||||||
builder_->addCapability(spv::CapabilitySignedZeroInfNanPreserve);
|
builder_->addCapability(spv::CapabilitySignedZeroInfNanPreserve);
|
||||||
builder_->addExecutionMode(function_main_,
|
builder_->addExecutionMode(function_main_,
|
||||||
spv::ExecutionModeSignedZeroInfNanPreserve, 32);
|
spv::ExecutionModeSignedZeroInfNanPreserve, 32);
|
||||||
} */
|
}
|
||||||
spv::Instruction* entry_point =
|
spv::Instruction* entry_point =
|
||||||
builder_->addEntryPoint(execution_model, function_main_, "main");
|
builder_->addEntryPoint(execution_model, function_main_, "main");
|
||||||
for (spv::Id interface_id : main_interface_) {
|
for (spv::Id interface_id : main_interface_) {
|
||||||
|
|
|
@ -92,7 +92,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
uint32_t max_storage_buffer_range;
|
uint32_t max_storage_buffer_range;
|
||||||
bool clip_distance;
|
bool clip_distance;
|
||||||
bool cull_distance;
|
bool cull_distance;
|
||||||
bool float_controls;
|
bool signed_zero_inf_nan_preserve_float32;
|
||||||
|
bool denorm_flush_to_zero_float32;
|
||||||
};
|
};
|
||||||
SpirvShaderTranslator(const Features& features);
|
SpirvShaderTranslator(const Features& features);
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,12 @@ bool VulkanProvider::Initialize() {
|
||||||
library_functions_loaded &=
|
library_functions_loaded &=
|
||||||
(lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr(
|
(lfn_.vkCreateInstance = PFN_vkCreateInstance(lfn_.vkGetInstanceProcAddr(
|
||||||
VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr;
|
VK_NULL_HANDLE, "vkCreateInstance"))) != nullptr;
|
||||||
|
library_functions_loaded &=
|
||||||
|
(lfn_.vkEnumerateInstanceExtensionProperties =
|
||||||
|
PFN_vkEnumerateInstanceExtensionProperties(
|
||||||
|
lfn_.vkGetInstanceProcAddr(
|
||||||
|
VK_NULL_HANDLE,
|
||||||
|
"vkEnumerateInstanceExtensionProperties"))) != nullptr;
|
||||||
if (!library_functions_loaded) {
|
if (!library_functions_loaded) {
|
||||||
XELOGE(
|
XELOGE(
|
||||||
"Failed to get Vulkan library function pointers via "
|
"Failed to get Vulkan library function pointers via "
|
||||||
|
@ -144,11 +150,58 @@ bool VulkanProvider::Initialize() {
|
||||||
VK_SUCCESS) {
|
VK_SUCCESS) {
|
||||||
instance_api_version = VK_API_VERSION_1_0;
|
instance_api_version = VK_API_VERSION_1_0;
|
||||||
}
|
}
|
||||||
XELOGVK("Vulkan instance version {}.{}.{}",
|
XELOGVK("Vulkan instance version: {}.{}.{}",
|
||||||
VK_VERSION_MAJOR(instance_api_version),
|
VK_VERSION_MAJOR(instance_api_version),
|
||||||
VK_VERSION_MINOR(instance_api_version),
|
VK_VERSION_MINOR(instance_api_version),
|
||||||
VK_VERSION_PATCH(instance_api_version));
|
VK_VERSION_PATCH(instance_api_version));
|
||||||
|
|
||||||
|
// Get the instance extensions.
|
||||||
|
std::vector<VkExtensionProperties> instance_extension_properties;
|
||||||
|
VkResult instance_extensions_enumerate_result;
|
||||||
|
for (;;) {
|
||||||
|
uint32_t instance_extension_count =
|
||||||
|
uint32_t(instance_extension_properties.size());
|
||||||
|
bool instance_extensions_was_empty = !instance_extension_count;
|
||||||
|
instance_extensions_enumerate_result =
|
||||||
|
lfn_.vkEnumerateInstanceExtensionProperties(
|
||||||
|
nullptr, &instance_extension_count,
|
||||||
|
instance_extensions_was_empty
|
||||||
|
? nullptr
|
||||||
|
: instance_extension_properties.data());
|
||||||
|
// If the original extension count was 0 (first call), SUCCESS is
|
||||||
|
// returned, not INCOMPLETE.
|
||||||
|
if (instance_extensions_enumerate_result == VK_SUCCESS ||
|
||||||
|
instance_extensions_enumerate_result == VK_INCOMPLETE) {
|
||||||
|
instance_extension_properties.resize(instance_extension_count);
|
||||||
|
if (instance_extensions_enumerate_result == VK_SUCCESS &&
|
||||||
|
(!instance_extensions_was_empty || !instance_extension_count)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (instance_extensions_enumerate_result != VK_SUCCESS) {
|
||||||
|
instance_extension_properties.clear();
|
||||||
|
}
|
||||||
|
std::memset(&instance_extensions_, 0, sizeof(instance_extensions_));
|
||||||
|
if (instance_api_version >= VK_MAKE_VERSION(1, 1, 0)) {
|
||||||
|
instance_extensions_.khr_get_physical_device_properties2 = true;
|
||||||
|
}
|
||||||
|
for (const VkExtensionProperties& instance_extension :
|
||||||
|
instance_extension_properties) {
|
||||||
|
const char* instance_extension_name = instance_extension.extensionName;
|
||||||
|
if (!instance_extensions_.khr_get_physical_device_properties2 &&
|
||||||
|
!std::strcmp(instance_extension_name,
|
||||||
|
"VK_KHR_get_physical_device_properties2")) {
|
||||||
|
instance_extensions_.khr_get_physical_device_properties2 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XELOGVK("Vulkan instance extensions:");
|
||||||
|
XELOGVK(
|
||||||
|
"* VK_KHR_get_physical_device_properties2: {}",
|
||||||
|
instance_extensions_.khr_get_physical_device_properties2 ? "yes" : "no");
|
||||||
|
|
||||||
// Create the instance.
|
// Create the instance.
|
||||||
std::vector<const char*> instance_extensions_enabled;
|
std::vector<const char*> instance_extensions_enabled;
|
||||||
instance_extensions_enabled.push_back("VK_KHR_surface");
|
instance_extensions_enabled.push_back("VK_KHR_surface");
|
||||||
|
@ -157,6 +210,12 @@ bool VulkanProvider::Initialize() {
|
||||||
#elif XE_PLATFORM_WIN32
|
#elif XE_PLATFORM_WIN32
|
||||||
instance_extensions_enabled.push_back("VK_KHR_win32_surface");
|
instance_extensions_enabled.push_back("VK_KHR_win32_surface");
|
||||||
#endif
|
#endif
|
||||||
|
if (instance_api_version < VK_MAKE_VERSION(1, 1, 0)) {
|
||||||
|
if (instance_extensions_.khr_get_physical_device_properties2) {
|
||||||
|
instance_extensions_enabled.push_back(
|
||||||
|
"VK_KHR_get_physical_device_properties2");
|
||||||
|
}
|
||||||
|
}
|
||||||
VkApplicationInfo application_info;
|
VkApplicationInfo application_info;
|
||||||
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
application_info.pNext = nullptr;
|
application_info.pNext = nullptr;
|
||||||
|
@ -205,11 +264,16 @@ bool VulkanProvider::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get instance functions.
|
// Get instance functions.
|
||||||
|
std::memset(&ifn_, 0, sizeof(ifn_));
|
||||||
bool instance_functions_loaded = true;
|
bool instance_functions_loaded = true;
|
||||||
#define XE_VULKAN_LOAD_IFN(name) \
|
#define XE_VULKAN_LOAD_IFN(name) \
|
||||||
instance_functions_loaded &= \
|
instance_functions_loaded &= \
|
||||||
(ifn_.name = PFN_##name( \
|
(ifn_.name = PFN_##name( \
|
||||||
lfn_.vkGetInstanceProcAddr(instance_, #name))) != nullptr;
|
lfn_.vkGetInstanceProcAddr(instance_, #name))) != nullptr;
|
||||||
|
#define XE_VULKAN_LOAD_IFN_SYMBOL(name, symbol) \
|
||||||
|
instance_functions_loaded &= \
|
||||||
|
(ifn_.name = PFN_##name( \
|
||||||
|
lfn_.vkGetInstanceProcAddr(instance_, symbol))) != nullptr;
|
||||||
XE_VULKAN_LOAD_IFN(vkCreateDevice);
|
XE_VULKAN_LOAD_IFN(vkCreateDevice);
|
||||||
XE_VULKAN_LOAD_IFN(vkDestroyDevice);
|
XE_VULKAN_LOAD_IFN(vkDestroyDevice);
|
||||||
XE_VULKAN_LOAD_IFN(vkDestroySurfaceKHR);
|
XE_VULKAN_LOAD_IFN(vkDestroySurfaceKHR);
|
||||||
|
@ -229,6 +293,13 @@ bool VulkanProvider::Initialize() {
|
||||||
#elif XE_PLATFORM_WIN32
|
#elif XE_PLATFORM_WIN32
|
||||||
XE_VULKAN_LOAD_IFN(vkCreateWin32SurfaceKHR);
|
XE_VULKAN_LOAD_IFN(vkCreateWin32SurfaceKHR);
|
||||||
#endif
|
#endif
|
||||||
|
if (instance_extensions_.khr_get_physical_device_properties2) {
|
||||||
|
XE_VULKAN_LOAD_IFN_SYMBOL(vkGetPhysicalDeviceProperties2KHR,
|
||||||
|
(instance_api_version >= VK_MAKE_VERSION(1, 1, 0))
|
||||||
|
? "vkGetPhysicalDeviceProperties2"
|
||||||
|
: "vkGetPhysicalDeviceProperties2KHR");
|
||||||
|
}
|
||||||
|
#undef XE_VULKAN_LOAD_IFN_SYMBOL
|
||||||
#undef XE_VULKAN_LOAD_IFN
|
#undef XE_VULKAN_LOAD_IFN
|
||||||
if (!instance_functions_loaded) {
|
if (!instance_functions_loaded) {
|
||||||
XELOGE("Failed to get Vulkan instance function pointers");
|
XELOGE("Failed to get Vulkan instance function pointers");
|
||||||
|
@ -470,6 +541,32 @@ bool VulkanProvider::Initialize() {
|
||||||
"support");
|
"support");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get additional device properties.
|
||||||
|
std::memset(&device_float_controls_properties_, 0,
|
||||||
|
sizeof(device_float_controls_properties_));
|
||||||
|
if (instance_extensions_.khr_get_physical_device_properties2) {
|
||||||
|
VkPhysicalDeviceProperties2KHR device_properties_2;
|
||||||
|
device_properties_2.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR;
|
||||||
|
device_properties_2.pNext = nullptr;
|
||||||
|
VkPhysicalDeviceProperties2KHR* device_properties_2_last =
|
||||||
|
&device_properties_2;
|
||||||
|
if (device_extensions_.khr_shader_float_controls) {
|
||||||
|
device_float_controls_properties_.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
|
||||||
|
device_float_controls_properties_.pNext = nullptr;
|
||||||
|
device_properties_2_last->pNext = &device_float_controls_properties_;
|
||||||
|
device_properties_2_last =
|
||||||
|
reinterpret_cast<VkPhysicalDeviceProperties2KHR*>(
|
||||||
|
&device_float_controls_properties_);
|
||||||
|
}
|
||||||
|
if (device_properties_2_last != &device_properties_2) {
|
||||||
|
ifn_.vkGetPhysicalDeviceProperties2KHR(physical_device_,
|
||||||
|
&device_properties_2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
XELOGVK(
|
XELOGVK(
|
||||||
"Vulkan device: {} (vendor {:04X}, device {:04X}, driver {:08X}, API "
|
"Vulkan device: {} (vendor {:04X}, device {:04X}, driver {:08X}, API "
|
||||||
"{}.{}.{})",
|
"{}.{}.{})",
|
||||||
|
@ -487,6 +584,17 @@ bool VulkanProvider::Initialize() {
|
||||||
device_extensions_.khr_shader_float_controls ? "yes" : "no");
|
device_extensions_.khr_shader_float_controls ? "yes" : "no");
|
||||||
XELOGVK("* VK_KHR_spirv_1_4: {}",
|
XELOGVK("* VK_KHR_spirv_1_4: {}",
|
||||||
device_extensions_.khr_spirv_1_4 ? "yes" : "no");
|
device_extensions_.khr_spirv_1_4 ? "yes" : "no");
|
||||||
|
if (device_extensions_.khr_shader_float_controls) {
|
||||||
|
XELOGVK(
|
||||||
|
"* Signed zero, inf, nan preserve for float32: {}",
|
||||||
|
device_float_controls_properties_.shaderSignedZeroInfNanPreserveFloat32
|
||||||
|
? "yes"
|
||||||
|
: "no");
|
||||||
|
XELOGVK("* Denorm flush to zero for float32: {}",
|
||||||
|
device_float_controls_properties_.shaderDenormFlushToZeroFloat32
|
||||||
|
? "yes"
|
||||||
|
: "no");
|
||||||
|
}
|
||||||
// TODO(Triang3l): Report properties, features.
|
// TODO(Triang3l): Report properties, features.
|
||||||
|
|
||||||
// Create the device.
|
// Create the device.
|
||||||
|
|
|
@ -57,12 +57,21 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
PFN_vkDestroyInstance vkDestroyInstance;
|
PFN_vkDestroyInstance vkDestroyInstance;
|
||||||
// From vkGetInstanceProcAddr.
|
// From vkGetInstanceProcAddr.
|
||||||
PFN_vkCreateInstance vkCreateInstance;
|
PFN_vkCreateInstance vkCreateInstance;
|
||||||
|
PFN_vkEnumerateInstanceExtensionProperties
|
||||||
|
vkEnumerateInstanceExtensionProperties;
|
||||||
struct {
|
struct {
|
||||||
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
|
PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
|
||||||
} v_1_1;
|
} v_1_1;
|
||||||
};
|
};
|
||||||
const LibraryFunctions& lfn() const { return lfn_; }
|
const LibraryFunctions& lfn() const { return lfn_; }
|
||||||
|
|
||||||
|
struct InstanceExtensions {
|
||||||
|
// Core since 1.1.0.
|
||||||
|
bool khr_get_physical_device_properties2;
|
||||||
|
};
|
||||||
|
const InstanceExtensions& instance_extensions() const {
|
||||||
|
return instance_extensions_;
|
||||||
|
}
|
||||||
VkInstance instance() const { return instance_; }
|
VkInstance instance() const { return instance_; }
|
||||||
struct InstanceFunctions {
|
struct InstanceFunctions {
|
||||||
PFN_vkCreateDevice vkCreateDevice;
|
PFN_vkCreateDevice vkCreateDevice;
|
||||||
|
@ -75,6 +84,8 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
|
PFN_vkGetPhysicalDeviceFeatures vkGetPhysicalDeviceFeatures;
|
||||||
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
|
||||||
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
|
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
|
||||||
|
// VK_KHR_get_physical_device_properties2 or 1.1.0.
|
||||||
|
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
|
||||||
PFN_vkGetPhysicalDeviceQueueFamilyProperties
|
PFN_vkGetPhysicalDeviceQueueFamilyProperties
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties;
|
vkGetPhysicalDeviceQueueFamilyProperties;
|
||||||
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
|
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
|
||||||
|
@ -129,6 +140,10 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
uint32_t queue_family_graphics_compute() const {
|
uint32_t queue_family_graphics_compute() const {
|
||||||
return queue_family_graphics_compute_;
|
return queue_family_graphics_compute_;
|
||||||
}
|
}
|
||||||
|
const VkPhysicalDeviceFloatControlsPropertiesKHR&
|
||||||
|
device_float_controls_properties() const {
|
||||||
|
return device_float_controls_properties_;
|
||||||
|
}
|
||||||
|
|
||||||
VkDevice device() const { return device_; }
|
VkDevice device() const { return device_; }
|
||||||
struct DeviceFunctions {
|
struct DeviceFunctions {
|
||||||
|
@ -262,8 +277,9 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
|
|
||||||
LibraryFunctions lfn_ = {};
|
LibraryFunctions lfn_ = {};
|
||||||
|
|
||||||
|
InstanceExtensions instance_extensions_;
|
||||||
VkInstance instance_ = VK_NULL_HANDLE;
|
VkInstance instance_ = VK_NULL_HANDLE;
|
||||||
InstanceFunctions ifn_ = {};
|
InstanceFunctions ifn_;
|
||||||
|
|
||||||
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
|
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
|
||||||
VkPhysicalDeviceProperties device_properties_;
|
VkPhysicalDeviceProperties device_properties_;
|
||||||
|
@ -274,6 +290,7 @@ class VulkanProvider : public GraphicsProvider {
|
||||||
uint32_t memory_types_host_coherent_;
|
uint32_t memory_types_host_coherent_;
|
||||||
uint32_t memory_types_host_cached_;
|
uint32_t memory_types_host_cached_;
|
||||||
uint32_t queue_family_graphics_compute_;
|
uint32_t queue_family_graphics_compute_;
|
||||||
|
VkPhysicalDeviceFloatControlsPropertiesKHR device_float_controls_properties_;
|
||||||
|
|
||||||
VkDevice device_ = VK_NULL_HANDLE;
|
VkDevice device_ = VK_NULL_HANDLE;
|
||||||
DeviceFunctions dfn_ = {};
|
DeviceFunctions dfn_ = {};
|
||||||
|
|
Loading…
Reference in New Issue