// vk-device.cpp
#include "vk-device.h"
#include "vk-buffer.h"
#include "vk-command-queue.h"
#include "vk-fence.h"
#include "vk-query.h"
#include "vk-render-pass.h"
#include "vk-resource-views.h"
#include "vk-sampler.h"
#include "vk-shader-object.h"
#include "vk-shader-object-layout.h"
#include "vk-shader-program.h"
#include "vk-shader-table.h"
#include "vk-swap-chain.h"
#include "vk-transient-heap.h"
#include "vk-vertex-layout.h"
#include "vk-helper-functions.h"
#ifdef GFX_NV_AFTERMATH
# include "GFSDK_Aftermath.h"
# include "GFSDK_Aftermath_Defines.h"
# include "GFSDK_Aftermath_GpuCrashDump.h"
#endif
namespace gfx
{
using namespace Slang;
namespace vk
{
DeviceImpl::~DeviceImpl()
{
// Check the device queue is valid else, we can't wait on it..
if (m_deviceQueue.isValid())
{
waitForGpu();
}
m_shaderObjectLayoutCache = decltype(m_shaderObjectLayoutCache)();
shaderCache.free();
m_deviceObjectsWithPotentialBackReferences.clearAndDeallocate();
if (m_api.vkDestroySampler)
{
m_api.vkDestroySampler(m_device, m_defaultSampler, nullptr);
}
m_deviceQueue.destroy();
descriptorSetAllocator.close();
m_emptyFramebuffer = nullptr;
if (m_device != VK_NULL_HANDLE)
{
if (m_desc.existingDeviceHandles.handles[2].handleValue == 0)
m_api.vkDestroyDevice(m_device, nullptr);
m_device = VK_NULL_HANDLE;
if (m_debugReportCallback != VK_NULL_HANDLE)
m_api.vkDestroyDebugReportCallbackEXT(m_api.m_instance, m_debugReportCallback, nullptr);
if (m_api.m_instance != VK_NULL_HANDLE &&
m_desc.existingDeviceHandles.handles[0].handleValue == 0)
m_api.vkDestroyInstance(m_api.m_instance, nullptr);
}
}
// TODO: Is "location" still needed for this function?
VkBool32 DeviceImpl::handleDebugMessage(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objType,
uint64_t srcObject,
Size location,
int32_t msgCode,
const char* pLayerPrefix,
const char* pMsg)
{
DebugMessageType msgType = DebugMessageType::Info;
char const* severity = "message";
if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT)
{
severity = "warning";
msgType = DebugMessageType::Warning;
}
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT)
{
severity = "error";
msgType = DebugMessageType::Error;
}
// pMsg can be really big (it can be assembler dump for example)
// Use a dynamic buffer to store
Size bufferSize = strlen(pMsg) + 1 + 1024;
List<char> bufferArray;
bufferArray.setCount(bufferSize);
char* buffer = bufferArray.getBuffer();
sprintf_s(buffer, bufferSize, "%s: %s %d: %s\n", pLayerPrefix, severity, msgCode, pMsg);
getDebugCallback()->handleMessage(msgType, DebugMessageSource::Driver, buffer);
return VK_FALSE;
}
VKAPI_ATTR VkBool32 VKAPI_CALL DeviceImpl::debugMessageCallback(
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objType,
uint64_t srcObject,
Size location,
int32_t msgCode,
const char* pLayerPrefix,
const char* pMsg,
void* pUserData)
{
return ((DeviceImpl*)pUserData)
->handleDebugMessage(flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg);
}
Result DeviceImpl::getNativeDeviceHandles(InteropHandles* outHandles)
{
outHandles->handles[0].handleValue = (uint64_t)m_api.m_instance;
outHandles->handles[0].api = InteropHandleAPI::Vulkan;
outHandles->handles[1].handleValue = (uint64_t)m_api.m_physicalDevice;
outHandles->handles[1].api = InteropHandleAPI::Vulkan;
outHandles->handles[2].handleValue = (uint64_t)m_api.m_device;
outHandles->handles[2].api = InteropHandleAPI::Vulkan;
return SLANG_OK;
}
template<typename T>
static bool _hasAnySetBits(const T& val, size_t offset)
{
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(&val);
for (size_t i = offset; i < sizeof(val); i++)
if (ptr[i]) return true;
return false;
}
Result DeviceImpl::initVulkanInstanceAndDevice(
const InteropHandle* handles, bool useValidationLayer)
{
m_features.clear();
m_queueAllocCount = 0;
VkInstance instance = VK_NULL_HANDLE;
if (handles[0].handleValue == 0)
{
VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
applicationInfo.pApplicationName = "slang-gfx";
applicationInfo.pEngineName = "slang-gfx";
applicationInfo.apiVersion = VK_API_VERSION_1_1;
applicationInfo.engineVersion = 1;
applicationInfo.applicationVersion = 1;
Array<const char*, 6> instanceExtensions;
instanceExtensions.add(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
instanceExtensions.add(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
// Software (swiftshader) implementation currently does not support surface extension,
// so only use it with a hardware implementation.
if (!m_api.m_module->isSoftware())
{
instanceExtensions.add(VK_KHR_SURFACE_EXTENSION_NAME);
// Note: this extension is not yet supported by nvidia drivers, disable for now.
// instanceExtensions.add("VK_GOOGLE_surfaceless_query");
#if SLANG_WINDOWS_FAMILY
instanceExtensions.add(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
#elif defined(SLANG_ENABLE_XLIB)
instanceExtensions.add(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
#endif
if (ENABLE_VALIDATION_LAYER || isGfxDebugLayerEnabled())
instanceExtensions.add(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
VkInstanceCreateInfo instanceCreateInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
instanceCreateInfo.pApplicationInfo = &applicationInfo;
instanceCreateInfo.enabledExtensionCount = (uint32_t)instanceExtensions.getCount();
instanceCreateInfo.ppEnabledExtensionNames = &instanceExtensions[0];
const char* layerNames[] = { nullptr };
if (useValidationLayer)
{
// Depending on driver version, validation layer may or may not exist.
// Newer drivers comes with "VK_LAYER_KHRONOS_validation", while older
// drivers provide only the deprecated
// "VK_LAYER_LUNARG_standard_validation" layer.
// We will check what layers are available, and use the newer
// "VK_LAYER_KHRONOS_validation" layer when possible.
uint32_t layerCount;
m_api.vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
List<VkLayerProperties> availableLayers;
availableLayers.setCount(layerCount);
m_api.vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.getBuffer());
for (auto& layer : availableLayers)
{
if (strncmp(
layer.layerName,
"VK_LAYER_KHRONOS_validation",
sizeof("VK_LAYER_KHRONOS_validation")) == 0)
{
layerNames[0] = "VK_LAYER_KHRONOS_validation";
break;
}
}
// On older drivers, only "VK_LAYER_LUNARG_standard_validation" exists,
// so we try to use it if we can't find "VK_LAYER_KHRONOS_validation".
if (!layerNames[0])
{
for (auto& layer : availableLayers)
{
if (strncmp(
layer.layerName,
"VK_LAYER_LUNARG_standard_validation",
sizeof("VK_LAYER_LUNARG_standard_validation")) == 0)
{
layerNames[0] = "VK_LAYER_LUNARG_standard_validation";
break;
}
}
}
if (layerNames[0])
{
instanceCreateInfo.enabledLayerCount = SLANG_COUNT_OF(layerNames);
instanceCreateInfo.ppEnabledLayerNames = layerNames;
}
}
uint32_t apiVersionsToTry[] = { VK_API_VERSION_1_2, VK_API_VERSION_1_1, VK_API_VERSION_1_0 };
for (auto apiVersion : apiVersionsToTry)
{
applicationInfo.apiVersion = apiVersion;
// If r is VK_ERROR_LAYER_NOT_PRESENT, it's almost certainly
// because the layer shared library failed to load (we check that
// the layer is known earlier). It might, for example, be absent
// from the system library search path, and not referenced with an
// absolute path in VkLayer_khronos_validation.json.
const auto r = m_api.vkCreateInstance(&instanceCreateInfo, nullptr, &instance) ;
if (r == VK_SUCCESS)
{
break;
}
}
}
else
{
instance = (VkInstance)handles[0].handleValue;
}
if (!instance)
return SLANG_FAIL;
SLANG_RETURN_ON_FAIL(m_api.initInstanceProcs(instance));
if (useValidationLayer && m_api.vkCreateDebugReportCallbackEXT)
{
VkDebugReportFlagsEXT debugFlags =
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT;
VkDebugReportCallbackCreateInfoEXT debugCreateInfo = {
VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT };
debugCreateInfo.pfnCallback = &debugMessageCallback;
debugCreateInfo.pUserData = this;
debugCreateInfo.flags = debugFlags;
SLANG_VK_RETURN_ON_FAIL(m_api.vkCreateDebugReportCallbackEXT(
instance, &debugCreateInfo, nullptr, &m_debugReportCallback));
}
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
if (handles[1].handleValue == 0)
{
uint32_t numPhysicalDevices = 0;
SLANG_VK_RETURN_ON_FAIL(
m_api.vkEnumeratePhysicalDevices(instance, &numPhysicalDevices, nullptr));
List<VkPhysicalDevice> physicalDevices;
physicalDevices.setCount(numPhysicalDevices);
SLANG_VK_RETURN_ON_FAIL(m_api.vkEnumeratePhysicalDevices(
instance, &numPhysicalDevices, physicalDevices.getBuffer()));
// Use first physical device by default.
Index selectedDeviceIndex = 0;
// Search for requested adapter.
if (m_desc.adapterLUID)
{
selectedDeviceIndex = -1;
for (Index i = 0; i < physicalDevices.getCount(); ++i)
{
if (vk::getAdapterLUID(m_api, physicalDevices[i]) == *m_desc.adapterLUID)
{
selectedDeviceIndex = i;
break;
}
}
if (selectedDeviceIndex < 0)
return SLANG_E_NOT_FOUND;
}
if (selectedDeviceIndex >= physicalDevices.getCount())
return SLANG_FAIL;
physicalDevice = physicalDevices[selectedDeviceIndex];
}
else
{
physicalDevice = (VkPhysicalDevice)handles[1].handleValue;
}
SLANG_RETURN_ON_FAIL(m_api.initPhysicalDevice(physicalDevice));
// Obtain the name of the selected adapter.
{
VkPhysicalDeviceProperties basicProps = {};
m_api.vkGetPhysicalDeviceProperties(physicalDevice, &basicProps);
m_adapterName = basicProps.deviceName;
m_info.adapterName = m_adapterName.begin();
}
List<const char*> deviceExtensions;
deviceExtensions.add(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pEnabledFeatures = &m_api.m_deviceFeatures;
// Get the device features (doesn't use, but useful when debugging)
if (m_api.vkGetPhysicalDeviceFeatures2)
{
VkPhysicalDeviceFeatures2 deviceFeatures2 = {};
deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
m_api.vkGetPhysicalDeviceFeatures2(m_api.m_physicalDevice, &deviceFeatures2);
}
VkPhysicalDeviceProperties basicProps = {};
m_api.vkGetPhysicalDeviceProperties(m_api.m_physicalDevice, &basicProps);
// Compute timestamp frequency.
m_info.timestampFrequency = uint64_t(1e9 / basicProps.limits.timestampPeriod);
// Get device limits.
{
DeviceLimits limits = {};
limits.maxTextureDimension1D = basicProps.limits.maxImageDimension1D;
limits.maxTextureDimension2D = basicProps.limits.maxImageDimension2D;
limits.maxTextureDimension3D = basicProps.limits.maxImageDimension3D;
limits.maxTextureDimensionCube = basicProps.limits.maxImageDimensionCube;
limits.maxTextureArrayLayers = basicProps.limits.maxImageArrayLayers;
limits.maxVertexInputElements = basicProps.limits.maxVertexInputAttributes;
limits.maxVertexInputElementOffset = basicProps.limits.maxVertexInputAttributeOffset;
limits.maxVertexStreams = basicProps.limits.maxVertexInputBindings;
|