Renderer: Allow to enable or disable API validation layers using config

This commit is contained in:
SirLynix 2022-08-10 00:04:46 +02:00
parent 38e32025e9
commit 117f7c2a4b
19 changed files with 101 additions and 37 deletions

View File

@ -13,6 +13,7 @@
#include <Nazara/Platform/WindowHandle.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/Renderer/RenderDeviceInfo.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <unordered_set>
#include <vector>
@ -23,7 +24,7 @@ namespace Nz
friend GL::Context;
public:
OpenGLDevice(GL::Loader& loader);
OpenGLDevice(GL::Loader& loader, const Renderer::Config& config);
OpenGLDevice(const OpenGLDevice&) = delete;
OpenGLDevice(OpenGLDevice&&) = delete; ///TODO?
~OpenGLDevice();

View File

@ -32,7 +32,7 @@ namespace Nz
UInt32 QueryAPIVersion() const override;
const std::vector<RenderDeviceInfo>& QueryRenderDevices() const override;
bool Prepare(const ParameterList& parameters) override;
bool Prepare(const Renderer::Config& config) override;
private:
std::unique_ptr<GL::Loader> SelectLoader();

View File

@ -14,6 +14,7 @@
#include <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Renderer/RenderStates.hpp>
#include <array>
#include <string>
@ -96,6 +97,7 @@ namespace Nz::GL
struct ContextParams
{
ContextType type = ContextType::OpenGL_ES;
RenderAPIValidationLevel validationLevel = RenderAPIValidationLevel::Warnings;
bool doubleBuffering = true;
bool wrapErrorHandling = false;
unsigned int bitsPerPixel = 32;

View File

@ -122,6 +122,16 @@ namespace Nz
constexpr std::size_t RenderAPICount = static_cast<std::size_t>(RenderAPI::Max) + 1;
enum class RenderAPIValidationLevel
{
None = 0,
Errors = 1,
Warnings = 2,
Verbose = 3,
Debug = 4
};
enum class RenderDeviceType
{
Integrated, ///< Hardware-accelerated chipset integrated to a CPU (ex: Intel Graphics HD 4000)

View File

@ -9,14 +9,16 @@
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Core/DynLib.hpp>
#include <Nazara/Core/ParameterList.hpp>
#include <Nazara/Platform/Platform.hpp>
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Renderer/RendererImpl.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
namespace Nz
{
class Buffer;
class RendererImpl;
class NAZARA_RENDERER_API Renderer : public ModuleBase<Renderer>
{
@ -42,13 +44,20 @@ namespace Nz
struct Config
{
ParameterList customParameters;
RenderAPI preferredAPI = RenderAPI::Unknown;
#ifdef NAZARA_DEBUG
RenderAPIValidationLevel validationLevel = RenderAPIValidationLevel::Verbose;
#else
RenderAPIValidationLevel validationLevel = RenderAPIValidationLevel::Errors;
#endif
};
private:
void LoadBackend(const Config& config);
std::unique_ptr<RendererImpl> m_rendererImpl;
Config m_config;
DynLib m_rendererLib;
static Renderer* s_instance;

View File

@ -12,6 +12,7 @@
#include <Nazara/Renderer/Config.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/Renderer/RenderDeviceInfo.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Utility/Enums.hpp>
#include <string>
#include <vector>
@ -44,7 +45,7 @@ namespace Nz
virtual const std::vector<RenderDeviceInfo>& QueryRenderDevices() const = 0;
virtual bool Prepare(const ParameterList& parameters) = 0;
virtual bool Prepare(const Renderer::Config& config) = 0;
};
}

View File

@ -47,7 +47,7 @@ namespace Nz
static const std::vector<Vk::PhysicalDevice>& GetPhysicalDevices();
static const Vk::PhysicalDevice& GetPhysicalDeviceInfo(VkPhysicalDevice physDevice);
static bool Initialize(UInt32 targetApiVersion, const ParameterList& parameters);
static bool Initialize(UInt32 targetApiVersion, RenderAPIValidationLevel validationLevel, const ParameterList& parameters);
static bool IsInitialized();

View File

@ -35,7 +35,7 @@ namespace Nz
UInt32 QueryAPIVersion() const override;
const std::vector<RenderDeviceInfo>& QueryRenderDevices() const override;
bool Prepare(const ParameterList& parameters) override;
bool Prepare(const Renderer::Config& parameters) override;
static constexpr UInt32 APIVersion = VK_API_VERSION_1_2;

View File

@ -8,6 +8,7 @@
#define NAZARA_VULKANRENDERER_WRAPPER_INSTANCE_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/Enums.hpp>
#include <Nazara/VulkanRenderer/Config.hpp>
#include <Nazara/VulkanRenderer/Wrapper/Loader.hpp>
#include <vulkan/vulkan_core.h>
@ -59,8 +60,8 @@ namespace Nz
Instance(Instance&&) = delete;
~Instance();
bool Create(const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr);
inline bool Create(const std::string& appName, UInt32 appVersion, const std::string& engineName, UInt32 engineVersion, UInt32 apiVersion, const std::vector<const char*>& layers, const std::vector<const char*>& extensions, const VkAllocationCallbacks* allocator = nullptr);
bool Create(RenderAPIValidationLevel validationLevel, const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator = nullptr);
inline bool Create(RenderAPIValidationLevel validationLevel, const std::string& appName, UInt32 appVersion, const std::string& engineName, UInt32 engineVersion, UInt32 apiVersion, const std::vector<const char*>& layers, const std::vector<const char*>& extensions, const VkAllocationCallbacks* allocator = nullptr);
inline void Destroy();
bool EnumeratePhysicalDevices(std::vector<VkPhysicalDevice>* physicalDevices) const;
@ -78,6 +79,7 @@ namespace Nz
inline VkPhysicalDeviceProperties GetPhysicalDeviceProperties(VkPhysicalDevice device) const;
bool GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device, std::vector<VkQueueFamilyProperties>* queueFamilyProperties) const;
inline PFN_vkVoidFunction GetProcAddr(const char* name) const;
inline RenderAPIValidationLevel GetValidationLevel() const;
void InstallDebugMessageCallback();
@ -108,6 +110,7 @@ namespace Nz
VkAllocationCallbacks m_allocator;
VkInstance m_instance;
mutable VkResult m_lastErrorCode;
RenderAPIValidationLevel m_validationLevel;
UInt32 m_apiVersion;
};
}

View File

@ -12,7 +12,7 @@ namespace Nz
{
namespace Vk
{
inline bool Instance::Create(const std::string& appName, UInt32 appVersion, const std::string& engineName, UInt32 engineVersion, UInt32 apiVersion, const std::vector<const char*>& layers, const std::vector<const char*>& extensions, const VkAllocationCallbacks* allocator)
inline bool Instance::Create(RenderAPIValidationLevel validationLevel, const std::string& appName, UInt32 appVersion, const std::string& engineName, UInt32 engineVersion, UInt32 apiVersion, const std::vector<const char*>& layers, const std::vector<const char*>& extensions, const VkAllocationCallbacks* allocator)
{
VkApplicationInfo appInfo =
{
@ -37,7 +37,7 @@ namespace Nz
(!extensions.empty()) ? extensions.data() : nullptr
};
return Create(instanceInfo, allocator);
return Create(validationLevel, instanceInfo, allocator);
}
inline void Instance::Destroy()
@ -77,6 +77,11 @@ namespace Nz
return func;
}
inline RenderAPIValidationLevel Instance::GetValidationLevel() const
{
return m_validationLevel;
}
inline bool Instance::IsExtensionLoaded(const std::string& extensionName) const
{
return m_loadedExtensions.count(extensionName) > 0;

View File

@ -20,11 +20,12 @@
namespace Nz
{
OpenGLDevice::OpenGLDevice(GL::Loader& loader) :
OpenGLDevice::OpenGLDevice(GL::Loader& loader, const Renderer::Config& config) :
m_loader(loader)
{
GL::ContextParams params;
params.type = loader.GetPreferredContextType();
params.validationLevel = config.validationLevel;
#ifdef NAZARA_OPENGLRENDERER_DEBUG
params.wrapErrorHandling = true;

View File

@ -53,7 +53,7 @@ namespace Nz
return m_device;
}
bool OpenGLRenderer::Prepare(const ParameterList& /*parameters*/)
bool OpenGLRenderer::Prepare(const Renderer::Config& config)
{
std::unique_ptr<GL::Loader> loader = SelectLoader();
if (!loader)
@ -64,7 +64,7 @@ namespace Nz
m_loader = std::move(loader);
m_device = std::make_shared<OpenGLDevice>(*m_loader);
m_device = std::make_shared<OpenGLDevice>(*m_loader, config);
m_deviceInfos.emplace_back(m_device->GetDeviceInfo());
return true;

View File

@ -450,13 +450,18 @@ namespace Nz::GL
NazaraWarning("desktop support for OpenGL ES is missing, falling back to OpenGL...");
}
// Set debug callback (if supported)
if (glDebugMessageCallback)
// Set debug callback (if supported and enabled)
if (glDebugMessageCallback && params.validationLevel != RenderAPIValidationLevel::None)
{
m_params.validationLevel = params.validationLevel;
glEnable(GL_DEBUG_OUTPUT);
#ifdef NAZARA_DEBUG
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
// Always enable synchronous debug output for debug libraries
#ifndef NAZARA_DEBUG
if (m_params.validationLevel == RenderAPIValidationLevel::Debug)
#endif
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam)
{
@ -473,10 +478,18 @@ namespace Nz::GL
if (glPopDebugGroup)
glDebugMessageControl(GL_DONT_CARE, GL_DEBUG_TYPE_POP_GROUP, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);
// Disable driver notifications (NVidia driver is very verbose)
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);
// Handle verbosity level
if (m_params.validationLevel < RenderAPIValidationLevel::Debug)
// Disable driver notifications except in debug (NVidia driver is very verbose)
glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);
else if (m_params.validationLevel < RenderAPIValidationLevel::Verbose)
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, GL_FALSE);
else if (m_params.validationLevel < RenderAPIValidationLevel::Warnings)
glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr, GL_FALSE);
}
}
else
m_params.validationLevel = m_params.validationLevel;
GLint maxTextureUnits = -1;
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits);

View File

@ -5,6 +5,7 @@
#include <Nazara/Renderer/RenderWindow.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Renderer/Renderer.hpp>
#include <Nazara/Renderer/RendererImpl.hpp>
#include <chrono>
#include <thread>
#include <Nazara/Renderer/Debug.hpp>

View File

@ -8,6 +8,7 @@
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/Platform/Platform.hpp>
#include <Nazara/Renderer/RenderBuffer.hpp>
#include <Nazara/Renderer/RendererImpl.hpp>
#include <Nazara/Utility/Buffer.hpp>
#include <Nazara/Utility/Image.hpp>
#include <Nazara/Utility/Utility.hpp>
@ -37,9 +38,10 @@
namespace Nz
{
Renderer::Renderer(Config config) :
ModuleBase("Renderer", this)
ModuleBase("Renderer", this),
m_config(std::move(config))
{
LoadBackend(config);
LoadBackend(m_config);
}
Renderer::~Renderer()
@ -170,7 +172,7 @@ namespace Nz
#else
std::unique_ptr<RendererImpl> impl = rendererImpl.factory();
#endif
if (!impl || !impl->Prepare({}))
if (!impl || !impl->Prepare(config))
{
NazaraError("Failed to create renderer implementation");
continue;

View File

@ -116,7 +116,7 @@ namespace Nz
return dummy;
}
bool Vulkan::Initialize(UInt32 targetApiVersion, const ParameterList& parameters)
bool Vulkan::Initialize(UInt32 targetApiVersion, RenderAPIValidationLevel validationLevel, const ParameterList& parameters)
{
NazaraAssert(!IsInitialized(), "Vulkan is already initialized");
@ -186,13 +186,14 @@ namespace Nz
{
//< Nazara default layers goes here
#ifdef NAZARA_DEBUG
// Enable Vulkan validation if available in debug mode
if (availableLayerByName.count("VK_LAYER_KHRONOS_validation"))
enabledLayers.push_back("VK_LAYER_KHRONOS_validation");
else if (availableLayerByName.count("VK_LAYER_LUNARG_standard_validation"))
enabledLayers.push_back("VK_LAYER_LUNARG_standard_validation");
#endif
if (validationLevel != RenderAPIValidationLevel::None)
{
// Enable Vulkan validation if available in debug mode
if (availableLayerByName.count("VK_LAYER_KHRONOS_validation"))
enabledLayers.push_back("VK_LAYER_KHRONOS_validation");
else if (availableLayerByName.count("VK_LAYER_LUNARG_standard_validation"))
enabledLayers.push_back("VK_LAYER_LUNARG_standard_validation");
}
}
std::vector<const char*> enabledExtensions;
@ -324,7 +325,7 @@ namespace Nz
instanceInfo.enabledLayerCount = UInt32(enabledLayers.size());
instanceInfo.ppEnabledLayerNames = enabledLayers.data();
if (!s_instance.Create(instanceInfo))
if (!s_instance.Create(validationLevel, instanceInfo))
{
NazaraError("Failed to create instance: " + TranslateVulkanError(s_instance.GetLastErrorCode()));
return false;

View File

@ -38,9 +38,9 @@ namespace Nz
return Vulkan::CreateDevice(physDevices[deviceIndex], enabledFeatures);
}
bool VulkanRenderer::Prepare(const ParameterList& parameters)
bool VulkanRenderer::Prepare(const Renderer::Config& config)
{
if (!Vulkan::Initialize(APIVersion, parameters))
if (!Vulkan::Initialize(APIVersion, config.validationLevel, config.customParameters))
return false;
const auto& physDevices = Vulkan::GetPhysicalDevices();

View File

@ -148,7 +148,7 @@ namespace Nz
{
switch (type)
{
case QueueType::Compute: return VK_QUEUE_COMPUTE_BIT;
case QueueType::Compute: return VK_QUEUE_COMPUTE_BIT;
case QueueType::Graphics: return VK_QUEUE_GRAPHICS_BIT;
case QueueType::Transfer: return VK_QUEUE_COMPUTE_BIT | VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT; //< Compute/graphics imply transfer
}

View File

@ -21,8 +21,22 @@ namespace Nz
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* /*pUserData*/)
void* pUserData)
{
Instance& instance = *static_cast<Instance*>(pUserData);
if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
{
RenderAPIValidationLevel validationLevel = instance.GetValidationLevel();
if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) && validationLevel < RenderAPIValidationLevel::Debug)
return VK_FALSE;
if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) && validationLevel < RenderAPIValidationLevel::Verbose)
return VK_FALSE;
if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) && validationLevel < RenderAPIValidationLevel::Warnings)
return VK_FALSE;
}
std::stringstream ss;
ss << "Vulkan log: ";
@ -48,7 +62,6 @@ namespace Nz
if (messageTypes & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT)
ss << "[Validation]";
ss << "[" << pCallbackData->messageIdNumber;
if (pCallbackData->pMessageIdName)
ss << ":" << pCallbackData->pMessageIdName;
@ -82,7 +95,7 @@ namespace Nz
DestroyInstance();
}
bool Instance::Create(const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator)
bool Instance::Create(RenderAPIValidationLevel validationLevel, const VkInstanceCreateInfo& createInfo, const VkAllocationCallbacks* allocator)
{
m_lastErrorCode = Loader::vkCreateInstance(&createInfo, allocator, &m_instance);
if (m_lastErrorCode != VkResult::VK_SUCCESS)
@ -92,6 +105,7 @@ namespace Nz
}
m_apiVersion = createInfo.pApplicationInfo->apiVersion;
m_validationLevel = validationLevel;
// Store the allocator to access them when needed
if (allocator)
@ -223,6 +237,7 @@ namespace Nz
callbackCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
callbackCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
callbackCreateInfo.pfnUserCallback = &DebugCallback;
callbackCreateInfo.pUserData = this;
if (!m_internalData->debugMessenger.Create(*this, callbackCreateInfo))
{