258 lines
7.5 KiB
C++
258 lines
7.5 KiB
C++
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
|
// This file is part of the "Nazara Engine - Renderer module"
|
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
|
|
|
#include <Nazara/Renderer/Renderer.hpp>
|
|
#include <Nazara/Core/CommandLineParameters.hpp>
|
|
#include <Nazara/Core/DynLib.hpp>
|
|
#include <Nazara/Core/Log.hpp>
|
|
#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>
|
|
#include <NazaraUtils/CallOnExit.hpp>
|
|
#include <NazaraUtils/EnumArray.hpp>
|
|
#include <frozen/string.h>
|
|
#include <frozen/unordered_map.h>
|
|
#include <filesystem>
|
|
#include <stdexcept>
|
|
|
|
#ifdef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
|
|
#include <Nazara/OpenGLRenderer/OpenGLRenderer.hpp>
|
|
|
|
#ifndef NAZARA_PLATFORM_WEB
|
|
#include <Nazara/VulkanRenderer/VulkanRenderer.hpp>
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#include <Nazara/Renderer/Debug.hpp>
|
|
|
|
#ifdef NAZARA_COMPILER_MSVC
|
|
#define NazaraRendererPrefix ""
|
|
#else
|
|
#define NazaraRendererPrefix "lib"
|
|
#endif
|
|
|
|
#ifdef NAZARA_DEBUG
|
|
#define NazaraRendererDebugSuffix "-d"
|
|
#else
|
|
#define NazaraRendererDebugSuffix ""
|
|
#endif
|
|
|
|
namespace Nz
|
|
{
|
|
Renderer::Renderer(Config config) :
|
|
ModuleBase("Renderer", this),
|
|
m_config(std::move(config))
|
|
{
|
|
LoadBackend(m_config);
|
|
}
|
|
|
|
Renderer::~Renderer()
|
|
{
|
|
// reset Renderer impl before unloading library
|
|
m_rendererImpl.reset();
|
|
}
|
|
|
|
std::shared_ptr<RenderDevice> Renderer::InstanciateRenderDevice(std::size_t deviceIndex, const RenderDeviceFeatures& enabledFeatures)
|
|
{
|
|
return m_rendererImpl->InstanciateRenderDevice(deviceIndex, enabledFeatures);
|
|
}
|
|
|
|
RenderAPI Renderer::QueryAPI() const
|
|
{
|
|
return m_rendererImpl->QueryAPI();
|
|
}
|
|
|
|
std::string Renderer::QueryAPIString() const
|
|
{
|
|
return m_rendererImpl->QueryAPIString();
|
|
}
|
|
|
|
UInt32 Renderer::QueryAPIVersion() const
|
|
{
|
|
return m_rendererImpl->QueryAPIVersion();
|
|
}
|
|
|
|
const std::vector<RenderDeviceInfo>& Renderer::QueryRenderDevices() const
|
|
{
|
|
return m_rendererImpl->QueryRenderDevices();
|
|
}
|
|
|
|
void Renderer::LoadBackend(const Config& config)
|
|
{
|
|
struct RendererImplementations
|
|
{
|
|
#ifdef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
std::function<std::unique_ptr<RendererImpl>()> factory;
|
|
#else
|
|
std::filesystem::path fileName;
|
|
#endif
|
|
int score;
|
|
};
|
|
std::vector<RendererImplementations> implementations;
|
|
|
|
RenderAPI preferredAPI = config.preferredAPI;
|
|
// OpenGL and OpenGL ES are handled by the same implementation (OpenGLES will be handled in OpenGLRenderer code)
|
|
if (preferredAPI == RenderAPI::OpenGL_ES)
|
|
preferredAPI = RenderAPI::OpenGL;
|
|
|
|
#ifdef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
auto RegisterImpl = [&](RenderAPI api, auto ComputeScore, std::function<std::unique_ptr<RendererImpl>()> factory)
|
|
{
|
|
int score = ComputeScore();
|
|
if (score >= 0)
|
|
{
|
|
auto& impl = implementations.emplace_back();
|
|
impl.factory = std::move(factory);
|
|
impl.score = (preferredAPI == api) ? std::numeric_limits<int>::max() : score;
|
|
}
|
|
};
|
|
|
|
RegisterImpl(RenderAPI::OpenGL, [] { return 50; }, [] { return std::make_unique<OpenGLRenderer>(); });
|
|
#ifndef NAZARA_PLATFORM_WEB
|
|
RegisterImpl(RenderAPI::Vulkan, [] { return 100; }, [] { return std::make_unique<VulkanRenderer>(); });
|
|
#endif
|
|
|
|
#else
|
|
constexpr EnumArray<RenderAPI, const char*> rendererPaths = {
|
|
NazaraRendererPrefix "NazaraDirect3DRenderer" NazaraRendererDebugSuffix, // Direct3D
|
|
NazaraRendererPrefix "NazaraMantleRenderer" NazaraRendererDebugSuffix, // Mantle
|
|
NazaraRendererPrefix "NazaraMetalRenderer" NazaraRendererDebugSuffix, // Metal
|
|
NazaraRendererPrefix "NazaraOpenGLRenderer" NazaraRendererDebugSuffix, // OpenGL
|
|
NazaraRendererPrefix "NazaraOpenGLRenderer" NazaraRendererDebugSuffix, // OpenGL_ES
|
|
NazaraRendererPrefix "NazaraVulkanRenderer" NazaraRendererDebugSuffix, // Vulkan
|
|
|
|
nullptr // Unknown
|
|
};
|
|
|
|
auto RegisterImpl = [&](RenderAPI api, auto ComputeScore)
|
|
{
|
|
const char* rendererName = rendererPaths[api];
|
|
assert(rendererName);
|
|
|
|
std::filesystem::path fileName(rendererName);
|
|
fileName.replace_extension(NAZARA_DYNLIB_EXTENSION);
|
|
|
|
int score = ComputeScore();
|
|
if (score >= 0)
|
|
{
|
|
auto& impl = implementations.emplace_back();
|
|
impl.fileName = std::move(fileName);
|
|
impl.score = (preferredAPI == api) ? std::numeric_limits<int>::max() : score;
|
|
}
|
|
};
|
|
|
|
RegisterImpl(RenderAPI::OpenGL, [] { return 50; });
|
|
#ifndef NAZARA_PLATFORM_WEB
|
|
RegisterImpl(RenderAPI::Vulkan, [] { return 100; });
|
|
#endif
|
|
|
|
#endif
|
|
|
|
std::sort(implementations.begin(), implementations.end(), [](const auto& lhs, const auto& rhs) { return lhs.score > rhs.score; });
|
|
|
|
std::unique_ptr<RendererImpl> chosenImpl;
|
|
#ifndef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
NazaraDebug("Searching for renderer implementation");
|
|
|
|
DynLib chosenLib;
|
|
#endif
|
|
|
|
for (auto&& rendererImpl : implementations)
|
|
{
|
|
#ifndef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
std::string fileNameStr = rendererImpl.fileName.generic_u8string();
|
|
|
|
DynLib implLib;
|
|
if (!implLib.Load(rendererImpl.fileName))
|
|
{
|
|
NazaraWarning("Failed to load " + fileNameStr + ": " + implLib.GetLastError());
|
|
continue;
|
|
}
|
|
|
|
CreateRendererImplFunc createRenderer = reinterpret_cast<CreateRendererImplFunc>(implLib.GetSymbol("NazaraRenderer_Instantiate"));
|
|
if (!createRenderer)
|
|
{
|
|
NazaraDebug("Skipped " + fileNameStr + " (symbol not found)");
|
|
continue;
|
|
}
|
|
|
|
std::unique_ptr<RendererImpl> impl(createRenderer());
|
|
#else
|
|
std::unique_ptr<RendererImpl> impl = rendererImpl.factory();
|
|
#endif
|
|
if (!impl || !impl->Prepare(config))
|
|
{
|
|
NazaraError("Failed to create renderer implementation");
|
|
continue;
|
|
}
|
|
|
|
#ifndef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
NazaraDebug("Loaded " + fileNameStr);
|
|
chosenLib = std::move(implLib);
|
|
#endif
|
|
|
|
chosenImpl = std::move(impl); //< Move (and delete previous) implementation before unloading library
|
|
break;
|
|
}
|
|
|
|
if (!chosenImpl)
|
|
throw std::runtime_error("no renderer found");
|
|
|
|
m_rendererImpl = std::move(chosenImpl);
|
|
#ifndef NAZARA_RENDERER_EMBEDDEDBACKENDS
|
|
m_rendererLib = std::move(chosenLib);
|
|
#endif
|
|
|
|
NazaraDebug("Using " + m_rendererImpl->QueryAPIString() + " as renderer");
|
|
}
|
|
|
|
Renderer* Renderer::s_instance = nullptr;
|
|
|
|
|
|
void Renderer::Config::Override(const CommandLineParameters& parameters)
|
|
{
|
|
std::string_view value;
|
|
|
|
if (parameters.GetParameter("render-api", &value))
|
|
{
|
|
constexpr auto renderAPIStr = frozen::make_unordered_map<frozen::string, RenderAPI>({
|
|
{ "auto", RenderAPI::Unknown },
|
|
{ "direct3d", RenderAPI::Direct3D },
|
|
{ "mantle", RenderAPI::Mantle },
|
|
{ "metal", RenderAPI::Metal },
|
|
{ "opengl", RenderAPI::OpenGL },
|
|
{ "opengles", RenderAPI::OpenGL_ES },
|
|
{ "vulkan", RenderAPI::Vulkan }
|
|
});
|
|
|
|
if (auto it = renderAPIStr.find(value); it != renderAPIStr.end())
|
|
preferredAPI = it->second;
|
|
else
|
|
NazaraError("unknown render API \"{0}\"", value);
|
|
}
|
|
|
|
if (parameters.GetParameter("render-api-validation", &value))
|
|
{
|
|
constexpr auto validationStr = frozen::make_unordered_map<frozen::string, RenderAPIValidationLevel>({
|
|
{ "debug", RenderAPIValidationLevel::Debug },
|
|
{ "errors", RenderAPIValidationLevel::Errors },
|
|
{ "none", RenderAPIValidationLevel::None },
|
|
{ "verbose", RenderAPIValidationLevel::Verbose },
|
|
{ "warnings", RenderAPIValidationLevel::Warnings }
|
|
});
|
|
|
|
if (auto it = validationStr.find(value); it != validationStr.end())
|
|
validationLevel = it->second;
|
|
else
|
|
NazaraError("unknown validation level \"{0}\"", value);
|
|
}
|
|
}
|
|
}
|