diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index 235ec9f71..e212d753a 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -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(); diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp index 0e86f119a..642e1de40 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderer.hpp @@ -32,7 +32,7 @@ namespace Nz UInt32 QueryAPIVersion() const override; const std::vector& QueryRenderDevices() const override; - bool Prepare(const ParameterList& parameters) override; + bool Prepare(const Renderer::Config& config) override; private: std::unique_ptr SelectLoader(); diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index 26d2a8f68..8e7b251de 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index cd884c3e3..be9f750d3 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -122,6 +122,16 @@ namespace Nz constexpr std::size_t RenderAPICount = static_cast(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) diff --git a/include/Nazara/Renderer/Renderer.hpp b/include/Nazara/Renderer/Renderer.hpp index 14e228118..61b10e6bd 100644 --- a/include/Nazara/Renderer/Renderer.hpp +++ b/include/Nazara/Renderer/Renderer.hpp @@ -9,14 +9,16 @@ #include #include +#include #include #include #include -#include +#include namespace Nz { class Buffer; + class RendererImpl; class NAZARA_RENDERER_API Renderer : public ModuleBase { @@ -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 m_rendererImpl; + Config m_config; DynLib m_rendererLib; static Renderer* s_instance; diff --git a/include/Nazara/Renderer/RendererImpl.hpp b/include/Nazara/Renderer/RendererImpl.hpp index fff740ede..a56066424 100644 --- a/include/Nazara/Renderer/RendererImpl.hpp +++ b/include/Nazara/Renderer/RendererImpl.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,7 @@ namespace Nz virtual const std::vector& QueryRenderDevices() const = 0; - virtual bool Prepare(const ParameterList& parameters) = 0; + virtual bool Prepare(const Renderer::Config& config) = 0; }; } diff --git a/include/Nazara/VulkanRenderer/Vulkan.hpp b/include/Nazara/VulkanRenderer/Vulkan.hpp index 0ce04f1ec..a50520a8c 100644 --- a/include/Nazara/VulkanRenderer/Vulkan.hpp +++ b/include/Nazara/VulkanRenderer/Vulkan.hpp @@ -47,7 +47,7 @@ namespace Nz static const std::vector& 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(); diff --git a/include/Nazara/VulkanRenderer/VulkanRenderer.hpp b/include/Nazara/VulkanRenderer/VulkanRenderer.hpp index 14de7c9df..0ec1c3e67 100644 --- a/include/Nazara/VulkanRenderer/VulkanRenderer.hpp +++ b/include/Nazara/VulkanRenderer/VulkanRenderer.hpp @@ -35,7 +35,7 @@ namespace Nz UInt32 QueryAPIVersion() const override; const std::vector& 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; diff --git a/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp b/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp index 13c31180e..01b9bcfaa 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/Instance.hpp @@ -8,6 +8,7 @@ #define NAZARA_VULKANRENDERER_WRAPPER_INSTANCE_HPP #include +#include #include #include #include @@ -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& layers, const std::vector& 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& layers, const std::vector& extensions, const VkAllocationCallbacks* allocator = nullptr); inline void Destroy(); bool EnumeratePhysicalDevices(std::vector* physicalDevices) const; @@ -78,6 +79,7 @@ namespace Nz inline VkPhysicalDeviceProperties GetPhysicalDeviceProperties(VkPhysicalDevice device) const; bool GetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice device, std::vector* 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; }; } diff --git a/include/Nazara/VulkanRenderer/Wrapper/Instance.inl b/include/Nazara/VulkanRenderer/Wrapper/Instance.inl index 14f37e8fc..db4c8c43f 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/Instance.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/Instance.inl @@ -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& layers, const std::vector& 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& layers, const std::vector& 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; diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index 702bad047..26b6f7369 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -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; diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp index 18a2e3bd2..827a01854 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderer.cpp @@ -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 loader = SelectLoader(); if (!loader) @@ -64,7 +64,7 @@ namespace Nz m_loader = std::move(loader); - m_device = std::make_shared(*m_loader); + m_device = std::make_shared(*m_loader, config); m_deviceInfos.emplace_back(m_device->GetDeviceInfo()); return true; diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 83a863fb9..64161b09a 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -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); diff --git a/src/Nazara/Renderer/RenderWindow.cpp b/src/Nazara/Renderer/RenderWindow.cpp index 2ee57b394..0e44f67d1 100644 --- a/src/Nazara/Renderer/RenderWindow.cpp +++ b/src/Nazara/Renderer/RenderWindow.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Nazara/Renderer/Renderer.cpp b/src/Nazara/Renderer/Renderer.cpp index 63567d77c..fbded67a7 100644 --- a/src/Nazara/Renderer/Renderer.cpp +++ b/src/Nazara/Renderer/Renderer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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 impl = rendererImpl.factory(); #endif - if (!impl || !impl->Prepare({})) + if (!impl || !impl->Prepare(config)) { NazaraError("Failed to create renderer implementation"); continue; diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index e8fbe3c5e..062c71a7a 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -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 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; diff --git a/src/Nazara/VulkanRenderer/VulkanRenderer.cpp b/src/Nazara/VulkanRenderer/VulkanRenderer.cpp index d1ecd035e..8899b9b69 100644 --- a/src/Nazara/VulkanRenderer/VulkanRenderer.cpp +++ b/src/Nazara/VulkanRenderer/VulkanRenderer.cpp @@ -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(); diff --git a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp index 0b89b57c8..5b70393c0 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/Device.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/Device.cpp @@ -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 } diff --git a/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp b/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp index ae231255f..950a5c7f1 100644 --- a/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp +++ b/src/Nazara/VulkanRenderer/Wrapper/Instance.cpp @@ -21,8 +21,22 @@ namespace Nz VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* /*pUserData*/) + void* pUserData) { + Instance& instance = *static_cast(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)) {