mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
1088 lines
38 KiB
C++
1088 lines
38 KiB
C++
#include "nvrenderer_vulkan.h"
|
|
|
|
#include <GLFW/glfw3.h>
|
|
#include <fmt/format.h>
|
|
|
|
#include "utilities/Globals.h"
|
|
#include "utilities/Logs.h"
|
|
#include "config.h"
|
|
#include "nvrenderer/nvrenderer.h"
|
|
|
|
#if LIBMANUL_WITH_VULKAN
|
|
|
|
#include <nvrhi/validation.h>
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
|
|
|
#include "nvrendererbackend.h"
|
|
|
|
#define MA_ERROR_LOG(fmt_str, ...) ErrorLog(fmt::format(fmt_str, __VA_ARGS__))
|
|
#define MA_INFO_LOG(fmt_str, ...) WriteLog(fmt::format(fmt_str, __VA_ARGS__))
|
|
#define CHECK(...) \
|
|
if (!(__VA_ARGS__)) { \
|
|
MA_ERROR_LOG("Initialization failure at {:s}", #__VA_ARGS__); \
|
|
return false; \
|
|
}
|
|
|
|
namespace {
|
|
std::vector<const char*> stringSetToVector(
|
|
const std::unordered_set<std::string>& set) {
|
|
std::vector<const char*> ret;
|
|
for (const auto& s : set) {
|
|
ret.push_back(s.c_str());
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
template <typename T>
|
|
std::vector<T> setToVector(const std::unordered_set<T>& set) {
|
|
std::vector<T> ret;
|
|
for (const auto& s : set) {
|
|
ret.push_back(s);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
|
|
VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objType,
|
|
uint64_t obj, size_t location, int32_t code, const char* layerPrefix,
|
|
// ReSharper disable once CppParameterMayBeConstPtrOrRef
|
|
const char* msg, void* userData) {
|
|
// auto manager = static_cast<const NvRendererBackend_Vulkan*>(userData);
|
|
|
|
// if (manager) {
|
|
// const auto& ignored =
|
|
// manager->m_DeviceParams.ignoredVulkanValidationMessageLocations;
|
|
// const auto found = std::find(ignored.begin(), ignored.end(), location);
|
|
// if (found != ignored.end()) return VK_FALSE;
|
|
// }
|
|
if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
|
MA_ERROR_LOG("[Vulkan: location={:#x} code={:d}, layerPrefix='{:s}'] {:s}",
|
|
location, code, layerPrefix, msg);
|
|
// throw std::runtime_error("");
|
|
} else {
|
|
MA_INFO_LOG("[Vulkan: location={:#x} code={:d}, layerPrefix='{:s}'] {:s}",
|
|
location, code, layerPrefix, msg);
|
|
}
|
|
|
|
return VK_FALSE;
|
|
}
|
|
} // namespace
|
|
|
|
bool NvRenderer::InitForVulkan(GLFWwindow* Window) {
|
|
// Create d3d device
|
|
const auto Backend = std::make_shared<NvRendererBackend_Vulkan>();
|
|
m_backend = Backend;
|
|
|
|
Backend->m_Window = Window;
|
|
Backend->m_renderer = this;
|
|
|
|
Backend->m_BackBufferWidth = Global.window_size.x;
|
|
Backend->m_BackBufferHeight = Global.window_size.y;
|
|
//glfwGetWindowSize(Window, &Backend->m_BackBufferWidth,
|
|
// &Backend->m_BackBufferHeight);
|
|
Backend->m_EnableComputeQueue = true;
|
|
Backend->m_EnableCopyQueue = true;
|
|
Backend->m_EnableNvrhiValidationLayer = false;
|
|
Backend->m_VsyncEnabled = Global.VSync;
|
|
Backend->m_SwapChainBufferCount = 3;
|
|
Backend->m_EnableDebugRuntime = Global.gfx_gldebug;
|
|
|
|
if (!Backend->CreateInstance()) return false;
|
|
if (!Backend->CreateDevice()) return false;
|
|
if (!Backend->CreateSwapChain()) return false;
|
|
|
|
Backend->m_BackBufferWidth = 0;
|
|
Backend->m_BackBufferHeight = 0;
|
|
|
|
Backend->UpdateWindowSize();
|
|
// if (!Backend->CreateDevice()) {
|
|
// return false;
|
|
// }
|
|
//
|
|
// if (!Backend->CreateSwapChain(Window)) {
|
|
// return false;
|
|
//}
|
|
//
|
|
// if (!Backend->CreateRenderTarget()) {
|
|
// return false;
|
|
//}
|
|
|
|
return true;
|
|
}
|
|
|
|
nvrhi::IDevice* NvRendererBackend_Vulkan::GetDevice() {
|
|
return m_EnableNvrhiValidationLayer ? m_ValidationLayer : m_NvrhiDevice;
|
|
}
|
|
|
|
std::string NvRendererBackend_Vulkan::LoadShaderBlob(
|
|
const std::string& filename, const std::string& entrypoint) {
|
|
return {};
|
|
}
|
|
|
|
int NvRendererBackend_Vulkan::GetCurrentBackBufferIndex() {
|
|
return m_SwapChainIndex;
|
|
}
|
|
|
|
int NvRendererBackend_Vulkan::GetBackBufferCount() {
|
|
return static_cast<uint32_t>(m_SwapChainImages.size());
|
|
}
|
|
|
|
nvrhi::TextureHandle NvRendererBackend_Vulkan::GetBackBuffer(int index) {
|
|
return m_SwapChainImages[index].rhiHandle;
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::BeginFrame() {
|
|
const auto& semaphore = m_PresentSemaphores[m_PresentSemaphoreIndex];
|
|
|
|
vk::Result res = vk::Result::eErrorUnknown;
|
|
|
|
constexpr int maxAttempts = 3;
|
|
for (int attempt = 0; attempt < maxAttempts; ++attempt) {
|
|
res = m_VulkanDevice.acquireNextImageKHR(
|
|
m_SwapChain,
|
|
std::numeric_limits<uint64_t>::max(), // timeout
|
|
semaphore, vk::Fence(), &m_SwapChainIndex);
|
|
|
|
if (res == vk::Result::eErrorOutOfDateKHR) {
|
|
// BackBufferResizing();
|
|
auto surfaceCaps =
|
|
m_VulkanPhysicalDevice.getSurfaceCapabilitiesKHR(m_WindowSurface);
|
|
|
|
m_BackBufferWidth = static_cast<int>(surfaceCaps.currentExtent.width);
|
|
m_BackBufferHeight = static_cast<int>(surfaceCaps.currentExtent.height);
|
|
|
|
ResizeSwapChain();
|
|
// BackBufferResized();
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (res == vk::Result::eSuccess) {
|
|
m_NvrhiDevice->queueWaitForSemaphore(nvrhi::CommandQueue::Graphics,
|
|
semaphore, 0);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
void NvRendererBackend_Vulkan::Present() {
|
|
const auto& semaphore = m_PresentSemaphores[m_PresentSemaphoreIndex];
|
|
|
|
m_NvrhiDevice->queueSignalSemaphore(nvrhi::CommandQueue::Graphics, semaphore,
|
|
0);
|
|
|
|
m_BarrierCommandList->open(); // umm...
|
|
m_BarrierCommandList->close();
|
|
m_NvrhiDevice->executeCommandList(m_BarrierCommandList);
|
|
|
|
const vk::PresentInfoKHR info = vk::PresentInfoKHR()
|
|
.setWaitSemaphoreCount(1)
|
|
.setPWaitSemaphores(&semaphore)
|
|
.setSwapchainCount(1)
|
|
.setPSwapchains(&m_SwapChain)
|
|
.setPImageIndices(&m_SwapChainIndex);
|
|
|
|
const vk::Result res = m_PresentQueue.presentKHR(&info);
|
|
assert(res == vk::Result::eSuccess || res == vk::Result::eErrorOutOfDateKHR);
|
|
|
|
m_PresentSemaphoreIndex =
|
|
(m_PresentSemaphoreIndex + 1) % m_PresentSemaphores.size();
|
|
|
|
#ifndef _WIN32
|
|
if (m_VsyncEnabled) {
|
|
m_PresentQueue.waitIdle();
|
|
}
|
|
#endif
|
|
|
|
while (m_FramesInFlight.size() >= m_MaxFramesInFlight) {
|
|
auto query = m_FramesInFlight.front();
|
|
m_FramesInFlight.pop();
|
|
|
|
m_NvrhiDevice->waitEventQuery(query);
|
|
|
|
m_QueryPool.push_back(query);
|
|
}
|
|
|
|
nvrhi::EventQueryHandle query;
|
|
if (!m_QueryPool.empty()) {
|
|
query = m_QueryPool.back();
|
|
m_QueryPool.pop_back();
|
|
} else {
|
|
query = m_NvrhiDevice->createEventQuery();
|
|
}
|
|
|
|
m_NvrhiDevice->resetEventQuery(query);
|
|
m_NvrhiDevice->setEventQuery(query, nvrhi::CommandQueue::Graphics);
|
|
m_FramesInFlight.push(query);
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::IsHDRDisplay() const { return false; }
|
|
|
|
bool NvRendererBackend_Vulkan::CreateInstance() {
|
|
if (m_EnableDebugRuntime) {
|
|
enabledExtensions.instance.insert("VK_EXT_debug_report");
|
|
enabledExtensions.layers.insert("VK_LAYER_KHRONOS_validation");
|
|
}
|
|
|
|
const auto vkGetInstanceProcAddr =
|
|
m_dynamicLoader.getProcAddress<PFN_vkGetInstanceProcAddr>(
|
|
"vkGetInstanceProcAddr");
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
|
return createInstance();
|
|
}
|
|
|
|
PFN_vkGetDeviceProcAddr NvRendererBackend_Vulkan::GetDeviceProcAddr() {
|
|
return m_dynamicLoader.getProcAddress<PFN_vkGetDeviceProcAddr>(
|
|
"vkGetDeviceProcAddr");
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::CreateDevice() {
|
|
if (m_EnableDebugRuntime) {
|
|
installDebugCallback();
|
|
}
|
|
|
|
std::vector<std::string> requiredVulkanDeviceExtensions{
|
|
VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME};
|
|
|
|
// add device extensions requested by the user
|
|
for (const std::string& name : requiredVulkanDeviceExtensions) {
|
|
// enabledExtensions.device.insert(name);
|
|
}
|
|
// for (const std::string& name :
|
|
// m_DeviceParams.optionalVulkanDeviceExtensions) {
|
|
// optionalExtensions.device.insert(name);
|
|
//}
|
|
|
|
// if (!m_DeviceParams.headlessDevice) {
|
|
// Need to adjust the swap chain format before creating the device because
|
|
// it affects physical device selection
|
|
// if (m_DeviceParams.swapChainFormat == nvrhi::Format::SRGBA8_UNORM)
|
|
// m_DeviceParams.swapChainFormat = nvrhi::Format::SBGRA8_UNORM;
|
|
// else if (m_DeviceParams.swapChainFormat == nvrhi::Format::RGBA8_UNORM)
|
|
// m_DeviceParams.swapChainFormat = nvrhi::Format::BGRA8_UNORM;
|
|
|
|
CHECK(createWindowSurface())
|
|
//}
|
|
CHECK(pickPhysicalDevice())
|
|
CHECK(findQueueFamilies(m_VulkanPhysicalDevice))
|
|
CHECK(createDevice())
|
|
|
|
auto vecInstanceExt = stringSetToVector(enabledExtensions.instance);
|
|
auto vecLayers = stringSetToVector(enabledExtensions.layers);
|
|
auto vecDeviceExt = stringSetToVector(enabledExtensions.device);
|
|
|
|
nvrhi::vulkan::DeviceDesc deviceDesc;
|
|
deviceDesc.errorCB = GetMessageCallback();
|
|
deviceDesc.instance = m_VulkanInstance;
|
|
deviceDesc.physicalDevice = m_VulkanPhysicalDevice;
|
|
deviceDesc.device = m_VulkanDevice;
|
|
deviceDesc.graphicsQueue = m_GraphicsQueue;
|
|
deviceDesc.graphicsQueueIndex = m_GraphicsQueueFamily;
|
|
if (m_EnableComputeQueue) {
|
|
deviceDesc.computeQueue = m_ComputeQueue;
|
|
deviceDesc.computeQueueIndex = m_ComputeQueueFamily;
|
|
}
|
|
if (m_EnableCopyQueue) {
|
|
deviceDesc.transferQueue = m_TransferQueue;
|
|
deviceDesc.transferQueueIndex = m_TransferQueueFamily;
|
|
}
|
|
deviceDesc.instanceExtensions = vecInstanceExt.data();
|
|
deviceDesc.numInstanceExtensions = vecInstanceExt.size();
|
|
deviceDesc.deviceExtensions = vecDeviceExt.data();
|
|
deviceDesc.numDeviceExtensions = vecDeviceExt.size();
|
|
deviceDesc.bufferDeviceAddressSupported = m_BufferDeviceAddressSupported;
|
|
|
|
m_NvrhiDevice = nvrhi::vulkan::createDevice(deviceDesc);
|
|
|
|
if (m_EnableNvrhiValidationLayer) {
|
|
m_ValidationLayer = nvrhi::validation::createValidationLayer(m_NvrhiDevice);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::CreateSwapChain() {
|
|
CHECK(createSwapChain())
|
|
|
|
m_BarrierCommandList = m_NvrhiDevice->createCommandList();
|
|
|
|
m_PresentSemaphores.reserve(m_MaxFramesInFlight + 1);
|
|
for (uint32_t i = 0; i < m_MaxFramesInFlight + 1; ++i) {
|
|
m_PresentSemaphores.push_back(
|
|
m_VulkanDevice.createSemaphore(vk::SemaphoreCreateInfo()));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
void NvRendererBackend_Vulkan::ResizeSwapChain() {
|
|
if (m_VulkanDevice) {
|
|
destroySwapChain();
|
|
createSwapChain();
|
|
}
|
|
}
|
|
|
|
void NvRendererBackend_Vulkan::UpdateWindowSize() {
|
|
int width;
|
|
int height;
|
|
glfwGetWindowSize(m_Window, &width, &height);
|
|
|
|
// if (width == 0 || height == 0) {
|
|
// // window is minimized
|
|
// m_windowVisible = false;
|
|
// return;
|
|
// }
|
|
//
|
|
// m_windowVisible = true;
|
|
|
|
if (m_BackBufferWidth != width || m_BackBufferHeight != height) {
|
|
// window is not minimized, and the size has changed
|
|
|
|
// BackBufferResizing();
|
|
|
|
m_BackBufferWidth = width;
|
|
m_BackBufferHeight = height;
|
|
// m_DeviceParams.vsyncEnabled = m_RequestedVSync;
|
|
|
|
ResizeSwapChain();
|
|
|
|
// BackBufferResized();
|
|
}
|
|
|
|
// m_DeviceParams.vsyncEnabled = m_RequestedVSync;
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::createInstance() {
|
|
if (!glfwVulkanSupported()) {
|
|
MA_ERROR_LOG(
|
|
"{:s}",
|
|
"GLFW reports that Vulkan is not supported. Perhaps missing a call to "
|
|
"glfwInit()?");
|
|
return false;
|
|
}
|
|
|
|
// add any extensions required by GLFW
|
|
uint32_t glfwExtCount;
|
|
const char** glfwExt = glfwGetRequiredInstanceExtensions(&glfwExtCount);
|
|
assert(glfwExt);
|
|
|
|
for (uint32_t i = 0; i < glfwExtCount; i++) {
|
|
enabledExtensions.instance.insert(std::string(glfwExt[i]));
|
|
}
|
|
|
|
// add instance extensions requested by the user
|
|
// for (const std::string& name :
|
|
// m_DeviceParams.requiredVulkanInstanceExtensions) {
|
|
// enabledExtensions.instance.insert(name);
|
|
//}
|
|
// for (const std::string& name :
|
|
// m_DeviceParams.optionalVulkanInstanceExtensions) {
|
|
// optionalExtensions.instance.insert(name);
|
|
//}
|
|
|
|
// add layers requested by the user
|
|
// for (const std::string& name : m_DeviceParams.requiredVulkanLayers) {
|
|
// enabledExtensions.layers.insert(name);
|
|
//}
|
|
// for (const std::string& name : m_DeviceParams.optionalVulkanLayers) {
|
|
// optionalExtensions.layers.insert(name);
|
|
//}
|
|
|
|
std::unordered_set<std::string> requiredExtensions =
|
|
enabledExtensions.instance;
|
|
|
|
// figure out which optional extensions are supported
|
|
for (const auto& instanceExt : vk::enumerateInstanceExtensionProperties()) {
|
|
const std::string name = instanceExt.extensionName;
|
|
if (optionalExtensions.instance.find(name) !=
|
|
optionalExtensions.instance.end()) {
|
|
enabledExtensions.instance.insert(name);
|
|
}
|
|
|
|
requiredExtensions.erase(name);
|
|
}
|
|
|
|
if (!requiredExtensions.empty()) {
|
|
std::stringstream ss;
|
|
ss << "Cannot create a Vulkan instance because the following required "
|
|
"extension(s) are not supported:";
|
|
for (const auto& ext : requiredExtensions) ss << std::endl << " - " << ext;
|
|
|
|
MA_ERROR_LOG("{:s}", ss.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
MA_INFO_LOG("{:s}", "Enabled Vulkan instance extensions:");
|
|
for (const auto& ext : enabledExtensions.instance) {
|
|
MA_INFO_LOG(" {:s}", ext.c_str());
|
|
}
|
|
|
|
std::unordered_set<std::string> requiredLayers = enabledExtensions.layers;
|
|
|
|
for (const auto& layer : vk::enumerateInstanceLayerProperties()) {
|
|
const std::string name = layer.layerName;
|
|
if (optionalExtensions.layers.find(name) !=
|
|
optionalExtensions.layers.end()) {
|
|
enabledExtensions.layers.insert(name);
|
|
}
|
|
|
|
requiredLayers.erase(name);
|
|
}
|
|
|
|
if (!requiredLayers.empty()) {
|
|
std::stringstream ss;
|
|
ss << "Cannot create a Vulkan instance because the following required "
|
|
"layer(s) are not supported:";
|
|
for (const auto& ext : requiredLayers) ss << std::endl << " - " << ext;
|
|
|
|
MA_ERROR_LOG("{:s}", ss.str().c_str());
|
|
return false;
|
|
}
|
|
|
|
MA_INFO_LOG("{:s}", "Enabled Vulkan layers:");
|
|
for (const auto& layer : enabledExtensions.layers) {
|
|
MA_INFO_LOG(" {:s}", layer.c_str());
|
|
}
|
|
|
|
auto instanceExtVec = stringSetToVector(enabledExtensions.instance);
|
|
auto layerVec = stringSetToVector(enabledExtensions.layers);
|
|
|
|
auto applicationInfo = vk::ApplicationInfo();
|
|
|
|
// Query the Vulkan API version supported on the system to make sure we use at
|
|
// least 1.3 when that's present.
|
|
vk::Result res = vk::enumerateInstanceVersion(&applicationInfo.apiVersion);
|
|
|
|
if (res != vk::Result::eSuccess) {
|
|
MA_ERROR_LOG("Call to vkEnumerateInstanceVersion failed, error code = {:s}",
|
|
nvrhi::vulkan::resultToString(static_cast<VkResult>(res)));
|
|
return false;
|
|
}
|
|
|
|
constexpr uint32_t minimumVulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0);
|
|
|
|
// Check if the Vulkan API version is sufficient.
|
|
if (applicationInfo.apiVersion < minimumVulkanVersion) {
|
|
MA_ERROR_LOG(
|
|
"The Vulkan API version supported on the system ({:d}.{:d}.{:d}) is "
|
|
"too low, "
|
|
"at least {:d}.{:d}.{:d} is required.",
|
|
VK_API_VERSION_MAJOR(applicationInfo.apiVersion),
|
|
VK_API_VERSION_MINOR(applicationInfo.apiVersion),
|
|
VK_API_VERSION_PATCH(applicationInfo.apiVersion),
|
|
VK_API_VERSION_MAJOR(minimumVulkanVersion),
|
|
VK_API_VERSION_MINOR(minimumVulkanVersion),
|
|
VK_API_VERSION_PATCH(minimumVulkanVersion));
|
|
return false;
|
|
}
|
|
|
|
// Spec says: A non-zero variant indicates the API is a variant of the Vulkan
|
|
// API and applications will typically need to be modified to run against it.
|
|
if (VK_API_VERSION_VARIANT(applicationInfo.apiVersion) != 0) {
|
|
MA_ERROR_LOG(
|
|
"The Vulkan API supported on the system uses an unexpected variant: "
|
|
"{:d}.",
|
|
VK_API_VERSION_VARIANT(applicationInfo.apiVersion));
|
|
return false;
|
|
}
|
|
|
|
// Create the vulkan instance
|
|
vk::InstanceCreateInfo info =
|
|
vk::InstanceCreateInfo()
|
|
.setEnabledLayerCount(static_cast<uint32_t>(layerVec.size()))
|
|
.setPpEnabledLayerNames(layerVec.data())
|
|
.setEnabledExtensionCount(
|
|
static_cast<uint32_t>(instanceExtVec.size()))
|
|
.setPpEnabledExtensionNames(instanceExtVec.data())
|
|
.setPApplicationInfo(&applicationInfo);
|
|
|
|
res = vk::createInstance(&info, nullptr, &m_VulkanInstance);
|
|
if (res != vk::Result::eSuccess) {
|
|
MA_ERROR_LOG("Failed to create a Vulkan instance, error code = {:s}",
|
|
nvrhi::vulkan::resultToString(static_cast<VkResult>(res)));
|
|
return false;
|
|
}
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_VulkanInstance);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::createWindowSurface() {
|
|
if (const VkResult res = glfwCreateWindowSurface(
|
|
m_VulkanInstance, m_Window, nullptr,
|
|
reinterpret_cast<VkSurfaceKHR*>(&m_WindowSurface));
|
|
res != VK_SUCCESS) {
|
|
MA_ERROR_LOG("Failed to create a GLFW window surface, error code = {:s}",
|
|
nvrhi::vulkan::resultToString(res));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void NvRendererBackend_Vulkan::installDebugCallback() {
|
|
auto info = vk::DebugReportCallbackCreateInfoEXT()
|
|
.setFlags(vk::DebugReportFlagBitsEXT::eError |
|
|
vk::DebugReportFlagBitsEXT::eWarning |
|
|
vk::DebugReportFlagBitsEXT::eInformation |
|
|
vk::DebugReportFlagBitsEXT::ePerformanceWarning)
|
|
.setPfnCallback(&vulkanDebugCallback)
|
|
.setPUserData(this);
|
|
|
|
const vk::Result res = m_VulkanInstance.createDebugReportCallbackEXT(
|
|
&info, nullptr, &m_DebugReportCallback);
|
|
assert(res == vk::Result::eSuccess);
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::pickPhysicalDevice() {
|
|
VkFormat requestedFormat =
|
|
nvrhi::vulkan::convertFormat(nvrhi::Format::BGRA8_UNORM);
|
|
vk::Extent2D requestedExtent(m_BackBufferWidth, m_BackBufferHeight);
|
|
|
|
auto devices = m_VulkanInstance.enumeratePhysicalDevices();
|
|
|
|
int firstDevice = 0;
|
|
int lastDevice = static_cast<int>(devices.size()) - 1;
|
|
// if (m_DeviceParams.adapterIndex >= 0) {
|
|
// if (m_DeviceParams.adapterIndex > lastDevice) {
|
|
// log::error("The specified Vulkan physical device %d does not exist.",
|
|
// m_DeviceParams.adapterIndex);
|
|
// return false;
|
|
// }
|
|
// firstDevice = m_DeviceParams.adapterIndex;
|
|
// lastDevice = m_DeviceParams.adapterIndex;
|
|
// }
|
|
|
|
// Start building an error message in case we cannot find a device.
|
|
std::stringstream errorStream;
|
|
errorStream << "Cannot find a Vulkan device that supports all the required "
|
|
"extensions and properties.";
|
|
|
|
// build a list of GPUs
|
|
std::vector<vk::PhysicalDevice> discreteGPUs;
|
|
std::vector<vk::PhysicalDevice> otherGPUs;
|
|
for (int deviceIndex = firstDevice; deviceIndex <= lastDevice;
|
|
++deviceIndex) {
|
|
vk::PhysicalDevice const& dev = devices[deviceIndex];
|
|
vk::PhysicalDeviceProperties prop = dev.getProperties();
|
|
|
|
errorStream << std::endl << prop.deviceName.data() << ":";
|
|
|
|
// check that all required device extensions are present
|
|
std::unordered_set<std::string> requiredExtensions =
|
|
enabledExtensions.device;
|
|
auto deviceExtensions = dev.enumerateDeviceExtensionProperties();
|
|
for (const auto& ext : deviceExtensions) {
|
|
requiredExtensions.erase(std::string(ext.extensionName.data()));
|
|
}
|
|
|
|
bool deviceIsGood = true;
|
|
|
|
if (!requiredExtensions.empty()) {
|
|
// device is missing one or more required extensions
|
|
for (const auto& ext : requiredExtensions) {
|
|
errorStream << std::endl << " - missing " << ext;
|
|
}
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
auto deviceFeatures = dev.getFeatures();
|
|
if (!deviceFeatures.samplerAnisotropy) {
|
|
// device is a toaster oven
|
|
errorStream << std::endl << " - does not support samplerAnisotropy";
|
|
deviceIsGood = false;
|
|
}
|
|
if (!deviceFeatures.textureCompressionBC) {
|
|
errorStream << std::endl << " - does not support textureCompressionBC";
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
if (!findQueueFamilies(dev)) {
|
|
// device doesn't have all the queue families we need
|
|
errorStream << std::endl
|
|
<< " - does not support the necessary queue types";
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
if (deviceIsGood && m_WindowSurface) {
|
|
if (bool surfaceSupported =
|
|
dev.getSurfaceSupportKHR(m_PresentQueueFamily, m_WindowSurface);
|
|
!surfaceSupported) {
|
|
errorStream << std::endl << " - does not support the window surface";
|
|
deviceIsGood = false;
|
|
} else {
|
|
// check that this device supports our intended swap chain creation
|
|
// parameters
|
|
auto surfaceCaps = dev.getSurfaceCapabilitiesKHR(m_WindowSurface);
|
|
auto surfaceFmts = dev.getSurfaceFormatsKHR(m_WindowSurface);
|
|
|
|
if (surfaceCaps.minImageCount > m_SwapChainBufferCount ||
|
|
(surfaceCaps.maxImageCount < m_SwapChainBufferCount &&
|
|
surfaceCaps.maxImageCount > 0)) {
|
|
errorStream
|
|
<< std::endl
|
|
<< " - cannot support the requested swap chain image count:";
|
|
errorStream << " requested " << m_SwapChainBufferCount
|
|
<< ", available " << surfaceCaps.minImageCount << " - "
|
|
<< surfaceCaps.maxImageCount;
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
if (surfaceCaps.minImageExtent.width > requestedExtent.width ||
|
|
surfaceCaps.minImageExtent.height > requestedExtent.height ||
|
|
surfaceCaps.maxImageExtent.width < requestedExtent.width ||
|
|
surfaceCaps.maxImageExtent.height < requestedExtent.height) {
|
|
errorStream << std::endl
|
|
<< " - cannot support the requested swap chain size:";
|
|
errorStream << " requested " << requestedExtent.width << "x"
|
|
<< requestedExtent.height << ", ";
|
|
errorStream << " available " << surfaceCaps.minImageExtent.width
|
|
<< "x" << surfaceCaps.minImageExtent.height;
|
|
errorStream << " - " << surfaceCaps.maxImageExtent.width << "x"
|
|
<< surfaceCaps.maxImageExtent.height;
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
bool surfaceFormatPresent = false;
|
|
for (const vk::SurfaceFormatKHR& surfaceFmt : surfaceFmts) {
|
|
if (surfaceFmt.format == static_cast<vk::Format>(requestedFormat)) {
|
|
surfaceFormatPresent = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!surfaceFormatPresent) {
|
|
// can't create a swap chain using the format requested
|
|
errorStream << std::endl
|
|
<< " - does not support the requested swap chain format";
|
|
deviceIsGood = false;
|
|
}
|
|
|
|
// check that we can present from the graphics queue
|
|
if (uint32_t canPresent = dev.getSurfaceSupportKHR(
|
|
m_GraphicsQueueFamily, m_WindowSurface);
|
|
!canPresent) {
|
|
errorStream << std::endl << " - cannot present";
|
|
deviceIsGood = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!deviceIsGood) continue;
|
|
|
|
if (prop.deviceType == vk::PhysicalDeviceType::eDiscreteGpu) {
|
|
discreteGPUs.push_back(dev);
|
|
} else {
|
|
otherGPUs.push_back(dev);
|
|
}
|
|
}
|
|
|
|
// pick the first discrete GPU if it exists, otherwise the first integrated
|
|
// GPU
|
|
if (!discreteGPUs.empty()) {
|
|
m_VulkanPhysicalDevice = discreteGPUs[0];
|
|
return true;
|
|
}
|
|
|
|
if (!otherGPUs.empty()) {
|
|
m_VulkanPhysicalDevice = otherGPUs[0];
|
|
return true;
|
|
}
|
|
|
|
MA_ERROR_LOG("{:s}", errorStream.str().c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
bool NvRendererBackend_Vulkan::findQueueFamilies(
|
|
vk::PhysicalDevice physicalDevice) {
|
|
auto props = physicalDevice.getQueueFamilyProperties();
|
|
|
|
for (int i = 0; i < static_cast<int>(props.size()); i++) {
|
|
const auto& queueFamily = props[i];
|
|
|
|
if (m_GraphicsQueueFamily == -1) {
|
|
if (queueFamily.queueCount > 0 &&
|
|
(queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)) {
|
|
m_GraphicsQueueFamily = i;
|
|
}
|
|
}
|
|
|
|
if (m_ComputeQueueFamily == -1) {
|
|
if (queueFamily.queueCount > 0 &&
|
|
(queueFamily.queueFlags & vk::QueueFlagBits::eCompute) &&
|
|
!(queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)) {
|
|
m_ComputeQueueFamily = i;
|
|
}
|
|
}
|
|
|
|
if (m_TransferQueueFamily == -1) {
|
|
if (queueFamily.queueCount > 0 &&
|
|
(queueFamily.queueFlags & vk::QueueFlagBits::eTransfer) &&
|
|
!(queueFamily.queueFlags & vk::QueueFlagBits::eCompute) &&
|
|
!(queueFamily.queueFlags & vk::QueueFlagBits::eGraphics)) {
|
|
m_TransferQueueFamily = i;
|
|
}
|
|
}
|
|
|
|
if (m_PresentQueueFamily == -1) {
|
|
if (queueFamily.queueCount > 0 &&
|
|
glfwGetPhysicalDevicePresentationSupport(m_VulkanInstance,
|
|
physicalDevice, i)) {
|
|
m_PresentQueueFamily = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_GraphicsQueueFamily == -1 || m_PresentQueueFamily == -1 ||
|
|
(m_ComputeQueueFamily == -1 && m_EnableComputeQueue) ||
|
|
(m_TransferQueueFamily == -1 && m_EnableCopyQueue)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
bool NvRendererBackend_Vulkan::createDevice() {
|
|
// figure out which optional extensions are supported
|
|
auto deviceExtensions =
|
|
m_VulkanPhysicalDevice.enumerateDeviceExtensionProperties();
|
|
for (const auto& ext : deviceExtensions) {
|
|
const std::string name = ext.extensionName;
|
|
if (optionalExtensions.device.find(name) !=
|
|
optionalExtensions.device.end()) {
|
|
enabledExtensions.device.insert(name);
|
|
}
|
|
|
|
// if (m_DeviceParams.enableRayTracingExtensions &&
|
|
// m_RayTracingExtensions.find(name) != m_RayTracingExtensions.end()) {
|
|
// enabledExtensions.device.insert(name);
|
|
// }
|
|
}
|
|
|
|
// if (!m_DeviceParams.headlessDevice) {
|
|
enabledExtensions.device.insert(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
|
//}
|
|
|
|
const vk::PhysicalDeviceProperties physicalDeviceProperties =
|
|
m_VulkanPhysicalDevice.getProperties();
|
|
m_RendererString = std::string(physicalDeviceProperties.deviceName.data());
|
|
|
|
bool accelStructSupported = false;
|
|
bool rayPipelineSupported = false;
|
|
bool rayQuerySupported = false;
|
|
bool meshletsSupported = false;
|
|
bool vrsSupported = false;
|
|
bool synchronization2Supported = false;
|
|
bool maintenance4Supported = false;
|
|
|
|
MA_INFO_LOG("{:s}", "Enabled Vulkan device extensions:");
|
|
for (const auto& ext : enabledExtensions.device) {
|
|
MA_INFO_LOG(" {:s}", ext.c_str());
|
|
|
|
if (ext == VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME)
|
|
accelStructSupported = true;
|
|
else if (ext == VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME)
|
|
rayPipelineSupported = true;
|
|
else if (ext == VK_KHR_RAY_QUERY_EXTENSION_NAME)
|
|
rayQuerySupported = true;
|
|
else if (ext == VK_NV_MESH_SHADER_EXTENSION_NAME)
|
|
meshletsSupported = true;
|
|
else if (ext == VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)
|
|
vrsSupported = true;
|
|
else if (ext == VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME)
|
|
synchronization2Supported = true;
|
|
else if (ext == VK_KHR_MAINTENANCE_4_EXTENSION_NAME)
|
|
maintenance4Supported = true;
|
|
else if (ext == VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME)
|
|
m_SwapChainMutableFormatSupported = true;
|
|
}
|
|
|
|
#define APPEND_EXTENSION(condition, desc) \
|
|
if (condition) { \
|
|
(desc).pNext = pNext; \
|
|
pNext = &(desc); \
|
|
} // NOLINT(cppcoreguidelines-macro-usage)
|
|
void* pNext = nullptr;
|
|
|
|
vk::PhysicalDeviceFeatures2 physicalDeviceFeatures2;
|
|
// Determine support for Buffer Device Address, the Vulkan 1.2 way
|
|
auto bufferDeviceAddressFeatures =
|
|
vk::PhysicalDeviceBufferDeviceAddressFeatures();
|
|
// Determine support for maintenance4
|
|
auto maintenance4Features = vk::PhysicalDeviceMaintenance4Features();
|
|
|
|
// Put the user-provided extension structure at the end of the chain
|
|
// pNext = m_DeviceParams.physicalDeviceFeatures2Extensions;
|
|
APPEND_EXTENSION(true, bufferDeviceAddressFeatures);
|
|
APPEND_EXTENSION(maintenance4Supported, maintenance4Features);
|
|
|
|
physicalDeviceFeatures2.pNext = pNext;
|
|
m_VulkanPhysicalDevice.getFeatures2(&physicalDeviceFeatures2);
|
|
|
|
std::unordered_set<int> uniqueQueueFamilies = {m_GraphicsQueueFamily};
|
|
|
|
// if (!m_DeviceParams.headlessDevice)
|
|
uniqueQueueFamilies.insert(m_PresentQueueFamily);
|
|
|
|
if (m_EnableComputeQueue) uniqueQueueFamilies.insert(m_ComputeQueueFamily);
|
|
|
|
if (m_EnableCopyQueue) uniqueQueueFamilies.insert(m_TransferQueueFamily);
|
|
|
|
float priority = 1.f;
|
|
std::vector<vk::DeviceQueueCreateInfo> queueDesc;
|
|
queueDesc.reserve(uniqueQueueFamilies.size());
|
|
for (int queueFamily : uniqueQueueFamilies) {
|
|
queueDesc.push_back(vk::DeviceQueueCreateInfo()
|
|
.setQueueFamilyIndex(queueFamily)
|
|
.setQueueCount(1)
|
|
.setPQueuePriorities(&priority));
|
|
}
|
|
|
|
auto accelStructFeatures =
|
|
vk::PhysicalDeviceAccelerationStructureFeaturesKHR()
|
|
.setAccelerationStructure(true);
|
|
auto rayPipelineFeatures = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR()
|
|
.setRayTracingPipeline(true)
|
|
.setRayTraversalPrimitiveCulling(true);
|
|
auto rayQueryFeatures =
|
|
vk::PhysicalDeviceRayQueryFeaturesKHR().setRayQuery(true);
|
|
auto meshletFeatures = vk::PhysicalDeviceMeshShaderFeaturesNV()
|
|
.setTaskShader(true)
|
|
.setMeshShader(true);
|
|
auto vrsFeatures = vk::PhysicalDeviceFragmentShadingRateFeaturesKHR()
|
|
.setPipelineFragmentShadingRate(true)
|
|
.setPrimitiveFragmentShadingRate(true)
|
|
.setAttachmentFragmentShadingRate(true);
|
|
auto vulkan13features =
|
|
vk::PhysicalDeviceVulkan13Features()
|
|
.setSynchronization2(synchronization2Supported)
|
|
.setMaintenance4(maintenance4Features.maintenance4);
|
|
|
|
pNext = nullptr;
|
|
APPEND_EXTENSION(accelStructSupported, accelStructFeatures)
|
|
APPEND_EXTENSION(rayPipelineSupported, rayPipelineFeatures)
|
|
APPEND_EXTENSION(rayQuerySupported, rayQueryFeatures)
|
|
APPEND_EXTENSION(meshletsSupported, meshletFeatures)
|
|
APPEND_EXTENSION(vrsSupported, vrsFeatures)
|
|
APPEND_EXTENSION(physicalDeviceProperties.apiVersion >= VK_API_VERSION_1_3,
|
|
vulkan13features)
|
|
APPEND_EXTENSION(physicalDeviceProperties.apiVersion < VK_API_VERSION_1_3 &&
|
|
maintenance4Supported,
|
|
maintenance4Features);
|
|
#undef APPEND_EXTENSION
|
|
|
|
auto deviceFeatures = vk::PhysicalDeviceFeatures()
|
|
.setShaderImageGatherExtended(true)
|
|
.setSamplerAnisotropy(true)
|
|
.setTessellationShader(true)
|
|
.setTextureCompressionBC(true)
|
|
.setGeometryShader(true)
|
|
.setImageCubeArray(true)
|
|
.setShaderFloat64(true)
|
|
.setShaderInt16(true)
|
|
.setDualSrcBlend(true)
|
|
.setMultiDrawIndirect(true);
|
|
|
|
auto vulkan12features =
|
|
vk::PhysicalDeviceVulkan12Features()
|
|
.setDescriptorIndexing(true)
|
|
.setRuntimeDescriptorArray(true)
|
|
.setDescriptorBindingPartiallyBound(true)
|
|
.setDescriptorBindingVariableDescriptorCount(true)
|
|
.setTimelineSemaphore(true)
|
|
.setShaderSampledImageArrayNonUniformIndexing(true)
|
|
.setSamplerFilterMinmax(true)
|
|
.setShaderFloat16(true)
|
|
.setBufferDeviceAddress(
|
|
bufferDeviceAddressFeatures.bufferDeviceAddress)
|
|
.setPNext(pNext);
|
|
|
|
auto layerVec = stringSetToVector(enabledExtensions.layers);
|
|
auto extVec = stringSetToVector(enabledExtensions.device);
|
|
|
|
auto deviceDesc =
|
|
vk::DeviceCreateInfo()
|
|
.setPQueueCreateInfos(queueDesc.data())
|
|
.setQueueCreateInfoCount(static_cast<uint32_t>(queueDesc.size()))
|
|
.setPEnabledFeatures(&deviceFeatures)
|
|
.setEnabledExtensionCount(static_cast<uint32_t>(extVec.size()))
|
|
.setPpEnabledExtensionNames(extVec.data())
|
|
.setEnabledLayerCount(static_cast<uint32_t>(layerVec.size()))
|
|
.setPpEnabledLayerNames(layerVec.data())
|
|
.setPNext(&vulkan12features);
|
|
|
|
// if (m_DeviceParams.deviceCreateInfoCallback)
|
|
// m_DeviceParams.deviceCreateInfoCallback(deviceDesc);
|
|
|
|
if (const vk::Result res = m_VulkanPhysicalDevice.createDevice(
|
|
&deviceDesc, nullptr, &m_VulkanDevice);
|
|
res != vk::Result::eSuccess) {
|
|
MA_ERROR_LOG("Failed to create a Vulkan physical device, error code = {:s}",
|
|
nvrhi::vulkan::resultToString(static_cast<VkResult>(res)));
|
|
return false;
|
|
}
|
|
|
|
m_VulkanDevice.getQueue(m_GraphicsQueueFamily, 0, &m_GraphicsQueue);
|
|
if (m_EnableComputeQueue)
|
|
m_VulkanDevice.getQueue(m_ComputeQueueFamily, 0, &m_ComputeQueue);
|
|
if (m_EnableCopyQueue)
|
|
m_VulkanDevice.getQueue(m_TransferQueueFamily, 0, &m_TransferQueue);
|
|
// if (!m_DeviceParams.headlessDevice)
|
|
m_VulkanDevice.getQueue(m_PresentQueueFamily, 0, &m_PresentQueue);
|
|
|
|
VULKAN_HPP_DEFAULT_DISPATCHER.init(m_VulkanDevice);
|
|
|
|
// remember the bufferDeviceAddress feature enablement
|
|
m_BufferDeviceAddressSupported = vulkan12features.bufferDeviceAddress;
|
|
|
|
MA_INFO_LOG("Created Vulkan device: {:s}", m_RendererString.c_str());
|
|
|
|
return true;
|
|
}
|
|
bool NvRendererBackend_Vulkan::createSwapChain() {
|
|
destroySwapChain();
|
|
|
|
m_SwapChainFormat = {static_cast<vk::Format>(nvrhi::vulkan::convertFormat(
|
|
nvrhi::Format::BGRA8_UNORM)),
|
|
vk::ColorSpaceKHR::eSrgbNonlinear};
|
|
|
|
vk::Extent2D extent = vk::Extent2D(m_BackBufferWidth, m_BackBufferHeight);
|
|
|
|
std::unordered_set<uint32_t> uniqueQueues = {
|
|
static_cast<uint32_t>(m_GraphicsQueueFamily),
|
|
static_cast<uint32_t>(m_PresentQueueFamily)};
|
|
|
|
std::vector<uint32_t> queues = setToVector(uniqueQueues);
|
|
|
|
const bool enableSwapChainSharing = queues.size() > 1;
|
|
|
|
auto desc =
|
|
vk::SwapchainCreateInfoKHR()
|
|
.setSurface(m_WindowSurface)
|
|
.setMinImageCount(m_SwapChainBufferCount)
|
|
.setImageFormat(m_SwapChainFormat.format)
|
|
.setImageColorSpace(m_SwapChainFormat.colorSpace)
|
|
.setImageExtent(extent)
|
|
.setImageArrayLayers(1)
|
|
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment |
|
|
vk::ImageUsageFlagBits::eTransferDst |
|
|
vk::ImageUsageFlagBits::eSampled)
|
|
.setImageSharingMode(enableSwapChainSharing
|
|
? vk::SharingMode::eConcurrent
|
|
: vk::SharingMode::eExclusive)
|
|
.setFlags(m_SwapChainMutableFormatSupported
|
|
? vk::SwapchainCreateFlagBitsKHR::eMutableFormat
|
|
: static_cast<vk::SwapchainCreateFlagBitsKHR>(0))
|
|
.setQueueFamilyIndexCount(
|
|
enableSwapChainSharing ? static_cast<uint32_t>(queues.size()) : 0)
|
|
.setPQueueFamilyIndices(enableSwapChainSharing ? queues.data()
|
|
: nullptr)
|
|
.setPreTransform(vk::SurfaceTransformFlagBitsKHR::eIdentity)
|
|
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
|
|
.setPresentMode(m_VsyncEnabled ? vk::PresentModeKHR::eFifo
|
|
: vk::PresentModeKHR::eImmediate)
|
|
.setClipped(true)
|
|
.setOldSwapchain(nullptr);
|
|
|
|
std::vector<vk::Format> imageFormats = {m_SwapChainFormat.format};
|
|
switch (m_SwapChainFormat.format) {
|
|
case vk::Format::eR8G8B8A8Unorm:
|
|
imageFormats.push_back(vk::Format::eR8G8B8A8Srgb);
|
|
break;
|
|
case vk::Format::eR8G8B8A8Srgb:
|
|
imageFormats.push_back(vk::Format::eR8G8B8A8Unorm);
|
|
break;
|
|
case vk::Format::eB8G8R8A8Unorm:
|
|
imageFormats.push_back(vk::Format::eB8G8R8A8Srgb);
|
|
break;
|
|
case vk::Format::eB8G8R8A8Srgb:
|
|
imageFormats.push_back(vk::Format::eB8G8R8A8Unorm);
|
|
break;
|
|
default:;
|
|
}
|
|
|
|
auto imageFormatListCreateInfo =
|
|
vk::ImageFormatListCreateInfo().setViewFormats(imageFormats);
|
|
|
|
if (m_SwapChainMutableFormatSupported)
|
|
desc.pNext = &imageFormatListCreateInfo;
|
|
|
|
if (const vk::Result res =
|
|
m_VulkanDevice.createSwapchainKHR(&desc, nullptr, &m_SwapChain);
|
|
res != vk::Result::eSuccess) {
|
|
MA_ERROR_LOG("Failed to create a Vulkan swap chain, error code = {:s}",
|
|
nvrhi::vulkan::resultToString(static_cast<VkResult>(res)));
|
|
return false;
|
|
}
|
|
|
|
struct DisplayChromaticities {
|
|
float RedX;
|
|
float RedY;
|
|
float GreenX;
|
|
float GreenY;
|
|
float BlueX;
|
|
float BlueY;
|
|
float WhiteX;
|
|
float WhiteY;
|
|
} Chromacities{0.70800f, 0.29200f, 0.17000f, 0.79700f,
|
|
0.13100f, 0.04600f, 0.31270f, 0.32900f};
|
|
|
|
//vk::HdrMetadataEXT hdr_metadata{{Chromacities.RedX, Chromacities.RedY},
|
|
// {Chromacities.GreenX, Chromacities.GreenY},
|
|
// {Chromacities.BlueX, Chromacities.BlueY},
|
|
// {Chromacities.WhiteX, Chromacities.WhiteY},
|
|
// 2500.f,
|
|
// .001f,
|
|
// 2000.f,
|
|
// 500.f,
|
|
// nullptr};
|
|
// m_VulkanDevice.setHdrMetadataEXT(1, &m_SwapChain, &hdr_metadata);
|
|
|
|
// retrieve swap chain images
|
|
auto images = m_VulkanDevice.getSwapchainImagesKHR(m_SwapChain);
|
|
for (auto image : images) {
|
|
SwapChainImage sci;
|
|
sci.image = image;
|
|
|
|
nvrhi::TextureDesc textureDesc;
|
|
textureDesc.width = m_BackBufferWidth;
|
|
textureDesc.height = m_BackBufferHeight;
|
|
textureDesc.format = nvrhi::Format::BGRA8_UNORM;
|
|
textureDesc.debugName = "Swap chain image";
|
|
textureDesc.initialState = nvrhi::ResourceStates::Present;
|
|
textureDesc.keepInitialState = true;
|
|
textureDesc.isRenderTarget = true;
|
|
|
|
sci.rhiHandle = m_NvrhiDevice->createHandleForNativeTexture(
|
|
nvrhi::ObjectTypes::VK_Image, nvrhi::Object(sci.image), textureDesc);
|
|
m_SwapChainImages.push_back(sci);
|
|
}
|
|
m_framebuffers.clear();
|
|
for (int i = 0; i < GetBackBufferCount(); ++i) {
|
|
m_framebuffers.emplace_back(GetDevice()->createFramebuffer(
|
|
nvrhi::FramebufferDesc().addColorAttachment(
|
|
nvrhi::FramebufferAttachment().setTexture(GetBackBuffer(i)))));
|
|
}
|
|
|
|
m_SwapChainIndex = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void NvRendererBackend_Vulkan::destroySwapChain() {
|
|
if (m_VulkanDevice) {
|
|
m_VulkanDevice.waitIdle();
|
|
}
|
|
|
|
if (m_SwapChain) {
|
|
m_VulkanDevice.destroySwapchainKHR(m_SwapChain);
|
|
m_SwapChain = nullptr;
|
|
}
|
|
|
|
m_SwapChainImages.clear();
|
|
}
|
|
|
|
#endif |