Add OpenGLRenderer (WIP)

This commit is contained in:
Lynix
2020-04-15 19:38:11 +02:00
parent ebb271a089
commit 68760209c1
118 changed files with 19236 additions and 414 deletions

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2014 AUTHORS
// This file is part of the "Nazara Engine - Module name"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Config.hpp>
#if NAZARA_OPENGLRENDERER_MANAGE_MEMORY
#include <Nazara/Core/MemoryManager.hpp>
#include <new> // Nécessaire ?
void* operator new(std::size_t size)
{
return Nz::MemoryManager::Allocate(size, false);
}
void* operator new[](std::size_t size)
{
return Nz::MemoryManager::Allocate(size, true);
}
void operator delete(void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, false);
}
void operator delete[](void* pointer) noexcept
{
Nz::MemoryManager::Free(pointer, true);
}
#endif // NAZARA_OPENGLRENDERER_MANAGE_MEMORY

View File

@@ -0,0 +1,15 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Prerequisites.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderer.hpp>
extern "C"
{
NAZARA_EXPORT Nz::RendererImpl* NazaraRenderer_Instantiate()
{
std::unique_ptr<Nz::OpenGLRenderer> renderer = std::make_unique<Nz::OpenGLRenderer>();
return renderer.release();
}
}

View File

@@ -0,0 +1,72 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/OpenGL.hpp>
#include <Nazara/Core/DynLib.hpp>
#include <Nazara/Core/Error.hpp>
#ifdef NAZARA_PLATFORM_WINDOWS
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLLoader.hpp>
#endif
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
struct OpenGLImpl
{
DynLib opengl32Lib;
};
static std::unique_ptr<OpenGLImpl> s_impl;
bool OpenGL::Initialize()
{
if (s_impl)
return true;
auto impl = std::make_unique<OpenGLImpl>();
if (!impl->opengl32Lib.Load("opengl32" NAZARA_DYNLIB_EXTENSION))
{
NazaraError("Failed to load opengl32 library, is OpenGL installed on your system?");
return false;
}
std::unique_ptr<GL::Loader> loader;
#ifdef NAZARA_PLATFORM_WINDOWS
try
{
loader = std::make_unique<GL::WGLLoader>(impl->opengl32Lib);
}
catch (const std::exception& e)
{
NazaraWarning(std::string("Failed to load WGL: ") + e.what());
}
#endif
if (!loader)
{
NazaraError("Failed to initialize OpenGL loader");
return false;
}
s_impl = std::move(impl);
return true;
}
bool OpenGL::IsInitialized()
{
return s_impl != nullptr;
}
void OpenGL::Uninitialize()
{
if (!s_impl)
return;
s_impl.reset();
}
}

View File

@@ -0,0 +1,151 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLBuffer.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/CommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/QueueHandle.hpp>
#include <vma/vk_mem_alloc.h>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLBuffer::~OpenGLBuffer()
{
vmaDestroyBuffer(m_device.GetMemoryAllocator(), m_buffer, m_allocation);
}
bool OpenGLBuffer::Fill(const void* data, UInt64 offset, UInt64 size)
{
void* ptr = Map(BufferAccess_WriteOnly, offset, size);
if (!ptr)
return false;
Nz::CallOnExit unmapOnExit([this]() { Unmap(); });
std::memcpy(ptr, data, size);
return true;
}
bool OpenGLBuffer::Initialize(UInt64 size, BufferUsageFlags usage)
{
m_size = size;
m_usage = usage;
VkBufferUsageFlags bufferUsage = ToOpenGL(m_type);
if ((usage & BufferUsage_DirectMapping) == 0)
bufferUsage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = size;
createInfo.usage = bufferUsage;
VmaAllocationCreateInfo allocInfo = {};
if (usage & BufferUsage_DeviceLocal)
{
if (usage & BufferUsage_DirectMapping)
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
else
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
}
else
allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
if (usage & BufferUsage_PersistentMapping)
allocInfo.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;
VkResult result = vmaCreateBuffer(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &m_buffer, &m_allocation, nullptr);
if (result != VK_SUCCESS)
{
NazaraError("Failed to allocate buffer: " + TranslateOpenGLError(result));
return false;
}
return true;
}
UInt64 OpenGLBuffer::GetSize() const
{
return m_size;
}
DataStorage OpenGLBuffer::GetStorage() const
{
return DataStorage_Hardware;
}
void* OpenGLBuffer::Map(BufferAccess /*access*/, UInt64 offset, UInt64 size)
{
if (m_usage & BufferUsage_DirectMapping)
{
void* mappedPtr;
VkResult result = vmaMapMemory(m_device.GetMemoryAllocator(), m_allocation, &mappedPtr);
if (result != VK_SUCCESS)
{
NazaraError("Failed to map buffer: " + TranslateOpenGLError(result));
return nullptr;
}
return static_cast<UInt8*>(mappedPtr) + offset;
}
else
{
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = size;
createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
VmaAllocationInfo allocationInfo;
VkResult result = vmaCreateBuffer(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &m_stagingBuffer, &m_stagingAllocation, &allocationInfo);
if (result != VK_SUCCESS)
{
NazaraError("Failed to allocate staging buffer: " + TranslateOpenGLError(result));
return nullptr;
}
return allocationInfo.pMappedData;
}
}
bool OpenGLBuffer::Unmap()
{
if (m_usage & BufferUsage_DirectMapping)
{
vmaUnmapMemory(m_device.GetMemoryAllocator(), m_allocation);
return true;
}
else
{
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Transfer);
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
copyCommandBuffer->CopyBuffer(m_stagingBuffer, m_buffer, m_size);
if (!copyCommandBuffer->End())
return false;
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Transfer), 0);
if (!transferQueue.Submit(copyCommandBuffer))
return false;
transferQueue.WaitIdle();
vmaDestroyBuffer(m_device.GetMemoryAllocator(), m_stagingBuffer, m_stagingAllocation);
return true;
}
}
}
#endif

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
}
#endif

View File

@@ -0,0 +1,189 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp>
#include <Nazara/Core/StackArray.hpp>
#include <Nazara/OpenGLRenderer/OpenGLBuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLMultipleFramebuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLSingleFramebuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp>
#include <Nazara/OpenGLRenderer/OpenGLUploadPool.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
void OpenGLCommandBufferBuilder::BeginDebugRegion(const std::string_view& regionName, const Nz::Color& color)
{
// Ensure \0 at the end of string
StackArray<char> regionNameEOS = NazaraStackArrayNoInit(char, regionName.size() + 1);
std::memcpy(regionNameEOS.data(), regionName.data(), regionName.size());
regionNameEOS[regionName.size()] = '\0';
m_commandBuffer.BeginDebugRegion(regionNameEOS.data(), color);
}
void OpenGLCommandBufferBuilder::BeginRenderPass(const Framebuffer& framebuffer, const RenderPass& renderPass, Nz::Recti renderRect, std::initializer_list<ClearValues> clearValues)
{
const OpenGLRenderPass& vkRenderPass = static_cast<const OpenGLRenderPass&>(renderPass);
const Vk::Framebuffer& vkFramebuffer = [&] () -> const Vk::Framebuffer&
{
const OpenGLFramebuffer& vkFramebuffer = static_cast<const OpenGLFramebuffer&>(framebuffer);
switch (vkFramebuffer.GetType())
{
case OpenGLFramebuffer::Type::Multiple:
{
const OpenGLMultipleFramebuffer& vkMultipleFramebuffer = static_cast<const OpenGLMultipleFramebuffer&>(vkFramebuffer);
m_framebufferCount = std::max(m_framebufferCount, vkMultipleFramebuffer.GetFramebufferCount());
return vkMultipleFramebuffer.GetFramebuffer(m_imageIndex);
}
case OpenGLFramebuffer::Type::Single:
return static_cast<const OpenGLSingleFramebuffer&>(vkFramebuffer).GetFramebuffer();
}
throw std::runtime_error("Unhandled framebuffer type " + std::to_string(UnderlyingCast(vkFramebuffer.GetType())));
}();
VkRect2D renderArea;
renderArea.offset.x = renderRect.x;
renderArea.offset.y = renderRect.y;
renderArea.extent.width = renderRect.width;
renderArea.extent.height = renderRect.height;
StackArray<VkClearValue> vkClearValues = NazaraStackArray(VkClearValue, clearValues.size());
std::size_t index = 0;
for (const ClearValues& values : clearValues)
{
auto& vkValues = vkClearValues[index];
if (PixelFormatInfo::GetContent(vkRenderPass.GetAttachmentFormat(index)) == PixelFormatContent_ColorRGBA)
{
vkValues.color.float32[0] = values.color.r / 255.f;
vkValues.color.float32[1] = values.color.g / 255.f;
vkValues.color.float32[2] = values.color.b / 255.f;
vkValues.color.float32[3] = values.color.a / 255.f;
}
else
{
vkValues.depthStencil.depth = values.depth;
vkValues.depthStencil.stencil = values.stencil;
}
index++;
}
VkRenderPassBeginInfo beginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
beginInfo.renderPass = vkRenderPass.GetRenderPass();
beginInfo.framebuffer = vkFramebuffer;
beginInfo.renderArea.offset.x = renderRect.x;
beginInfo.renderArea.offset.y = renderRect.y;
beginInfo.renderArea.extent.width = renderRect.width;
beginInfo.renderArea.extent.height = renderRect.height;
beginInfo.clearValueCount = vkClearValues.size();
beginInfo.pClearValues = vkClearValues.data();
m_commandBuffer.BeginRenderPass(beginInfo);
m_currentRenderPass = &vkRenderPass;
}
void OpenGLCommandBufferBuilder::BindIndexBuffer(Nz::AbstractBuffer* indexBuffer, UInt64 offset)
{
OpenGLBuffer& vkBuffer = *static_cast<OpenGLBuffer*>(indexBuffer);
m_commandBuffer.BindIndexBuffer(vkBuffer.GetBuffer(), offset, VK_INDEX_TYPE_UINT16); //< Fuck me right?
}
void OpenGLCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline)
{
if (!m_currentRenderPass)
throw std::runtime_error("BindPipeline must be called in a RenderPass");
const OpenGLRenderPipeline& vkBinding = static_cast<const OpenGLRenderPipeline&>(pipeline);
m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkBinding.Get(m_currentRenderPass->GetRenderPass()));
}
void OpenGLCommandBufferBuilder::BindShaderBinding(const ShaderBinding& binding)
{
const OpenGLShaderBinding& vkBinding = static_cast<const OpenGLShaderBinding&>(binding);
const OpenGLRenderPipelineLayout& pipelineLayout = vkBinding.GetOwner();
m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.GetPipelineLayout(), 0U, vkBinding.GetDescriptorSet());
}
void OpenGLCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset)
{
OpenGLBuffer& vkBuffer = *static_cast<OpenGLBuffer*>(vertexBuffer);
m_commandBuffer.BindVertexBuffer(binding, vkBuffer.GetBuffer(), offset);
}
void OpenGLCommandBufferBuilder::CopyBuffer(const RenderBufferView& source, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{
OpenGLBuffer& sourceBuffer = *static_cast<OpenGLBuffer*>(source.GetBuffer());
OpenGLBuffer& targetBuffer = *static_cast<OpenGLBuffer*>(target.GetBuffer());
m_commandBuffer.CopyBuffer(sourceBuffer.GetBuffer(), targetBuffer.GetBuffer(), size, sourceOffset + source.GetOffset(), targetOffset + target.GetOffset());
}
void OpenGLCommandBufferBuilder::CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{
const auto& vkAllocation = static_cast<const OpenGLUploadPool::OpenGLAllocation&>(allocation);
OpenGLBuffer& targetBuffer = *static_cast<OpenGLBuffer*>(target.GetBuffer());
m_commandBuffer.CopyBuffer(vkAllocation.buffer, targetBuffer.GetBuffer(), size, vkAllocation.offset + sourceOffset, target.GetOffset() + targetOffset);
}
void OpenGLCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)
{
m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance);
}
void OpenGLCommandBufferBuilder::DrawIndexed(UInt32 indexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance)
{
m_commandBuffer.DrawIndexed(indexCount, instanceCount, firstVertex, 0, firstInstance);
}
void OpenGLCommandBufferBuilder::EndDebugRegion()
{
m_commandBuffer.EndDebugRegion();
}
void OpenGLCommandBufferBuilder::EndRenderPass()
{
m_commandBuffer.EndRenderPass();
m_currentRenderPass = nullptr;
}
void OpenGLCommandBufferBuilder::PreTransferBarrier()
{
m_commandBuffer.MemoryBarrier(VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0U, VK_ACCESS_TRANSFER_READ_BIT);
}
void OpenGLCommandBufferBuilder::PostTransferBarrier()
{
m_commandBuffer.MemoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT);
}
void OpenGLCommandBufferBuilder::SetScissor(Nz::Recti scissorRegion)
{
m_commandBuffer.SetScissor(scissorRegion);
}
void OpenGLCommandBufferBuilder::SetViewport(Nz::Recti viewportRegion)
{
m_commandBuffer.SetViewport(Nz::Rectf(viewportRegion), 0.f, 1.f);
}
}
#endif

View File

@@ -0,0 +1,42 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/CommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
std::unique_ptr<CommandBuffer> OpenGLCommandPool::BuildCommandBuffer(const std::function<void(CommandBufferBuilder& builder)>& callback)
{
std::vector<Vk::AutoCommandBuffer> commandBuffers;
auto BuildCommandBuffer = [&](std::size_t imageIndex)
{
Vk::AutoCommandBuffer& commandBuffer = commandBuffers.emplace_back(m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY));
if (!commandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT))
throw std::runtime_error("failed to begin command buffer: " + TranslateOpenGLError(commandBuffer->GetLastErrorCode()));
OpenGLCommandBufferBuilder builder(commandBuffer.Get(), imageIndex);
callback(builder);
if (!commandBuffer->End())
throw std::runtime_error("failed to build command buffer: " + TranslateOpenGLError(commandBuffer->GetLastErrorCode()));
return builder.GetMaxFramebufferCount();
};
std::size_t maxFramebufferCount = BuildCommandBuffer(0);
for (std::size_t i = 1; i < maxFramebufferCount; ++i)
BuildCommandBuffer(i);
return std::make_unique<OpenGLCommandBuffer>(std::move(commandBuffers));
}
}
#endif

View File

@@ -0,0 +1,64 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLDevice::~OpenGLDevice() = default;
std::unique_ptr<AbstractBuffer> OpenGLDevice::InstantiateBuffer(BufferType type)
{
return std::make_unique<OpenGLBuffer>(*this, type);
}
std::unique_ptr<CommandPool> OpenGLDevice::InstantiateCommandPool(QueueType queueType)
{
return std::make_unique<OpenGLCommandPool>(*this, queueType);
}
std::unique_ptr<RenderPipeline> OpenGLDevice::InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo)
{
return std::make_unique<OpenGLRenderPipeline>(*this, std::move(pipelineInfo));
}
std::shared_ptr<RenderPipelineLayout> OpenGLDevice::InstantiateRenderPipelineLayout(RenderPipelineLayoutInfo pipelineLayoutInfo)
{
auto pipelineLayout = std::make_shared<OpenGLRenderPipelineLayout>();
if (!pipelineLayout->Create(*this, std::move(pipelineLayoutInfo)))
return {};
return pipelineLayout;
}
std::shared_ptr<ShaderStageImpl> OpenGLDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
auto stage = std::make_shared<OpenGLShaderStage>();
if (!stage->Create(*this, type, lang, source, sourceSize))
return {};
return stage;
}
std::unique_ptr<Texture> OpenGLDevice::InstantiateTexture(const TextureInfo& params)
{
return std::make_unique<OpenGLTexture>(*this, params);
}
std::unique_ptr<TextureSampler> OpenGLDevice::InstantiateTextureSampler(const TextureSamplerInfo& params)
{
return std::make_unique<OpenGLTextureSampler>(*this, params);
}
}
#endif

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLFramebuffer.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
}
#endif

View File

@@ -0,0 +1,97 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLRenderImage.hpp>
#include <Nazara/OpenGLRenderer/VkRenderWindow.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLRenderImage::OpenGLRenderImage(VkRenderWindow& owner) :
m_owner(owner),
m_uploadPool(m_owner.GetDevice(), 2 * 1024 * 1024)
{
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();
if (!m_commandPool.Create(m_owner.GetDevice(), graphicsQueue.GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT))
throw std::runtime_error("failed to create command pool: " + TranslateOpenGLError(m_commandPool.GetLastErrorCode()));
if (!m_imageAvailableSemaphore.Create(m_owner.GetDevice()))
throw std::runtime_error("failed to create image available semaphore: " + TranslateOpenGLError(m_imageAvailableSemaphore.GetLastErrorCode()));
if (!m_renderFinishedSemaphore.Create(m_owner.GetDevice()))
throw std::runtime_error("failed to create image finished semaphore: " + TranslateOpenGLError(m_imageAvailableSemaphore.GetLastErrorCode()));
if (!m_inFlightFence.Create(m_owner.GetDevice(), VK_FENCE_CREATE_SIGNALED_BIT))
throw std::runtime_error("failed to create in-flight fence: " + TranslateOpenGLError(m_inFlightFence.GetLastErrorCode()));
}
OpenGLRenderImage::~OpenGLRenderImage()
{
m_inFlightCommandBuffers.clear();
}
void OpenGLRenderImage::Execute(const std::function<void(CommandBufferBuilder& builder)>& callback, QueueTypeFlags queueTypeFlags)
{
Vk::CommandBuffer* commandBuffer;
if (m_currentCommandBuffer >= m_inFlightCommandBuffers.size())
{
Vk::AutoCommandBuffer& newlyAllocatedBuffer = m_inFlightCommandBuffers.emplace_back(m_commandPool.AllocateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY));
commandBuffer = &newlyAllocatedBuffer.Get();
m_currentCommandBuffer++;
}
else
commandBuffer = &m_inFlightCommandBuffers[m_currentCommandBuffer++].Get();
if (!commandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
throw std::runtime_error("failed to begin command buffer: " + TranslateOpenGLError(commandBuffer->GetLastErrorCode()));
OpenGLCommandBufferBuilder builder(*commandBuffer, m_imageIndex);
callback(builder);
if (!commandBuffer->End())
throw std::runtime_error("failed to build command buffer: " + TranslateOpenGLError(commandBuffer->GetLastErrorCode()));
SubmitCommandBuffer(*commandBuffer, queueTypeFlags);
}
OpenGLUploadPool& OpenGLRenderImage::GetUploadPool()
{
return m_uploadPool;
}
void OpenGLRenderImage::SubmitCommandBuffer(CommandBuffer* commandBuffer, QueueTypeFlags queueTypeFlags)
{
OpenGLCommandBuffer& vkCommandBuffer = *static_cast<OpenGLCommandBuffer*>(commandBuffer);
return SubmitCommandBuffer(vkCommandBuffer.GetCommandBuffer(m_imageIndex), queueTypeFlags);
}
void OpenGLRenderImage::SubmitCommandBuffer(VkCommandBuffer commandBuffer, QueueTypeFlags queueTypeFlags)
{
if (queueTypeFlags & QueueType::Graphics)
m_graphicalCommandsBuffers.push_back(commandBuffer);
else
{
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();
if (!graphicsQueue.Submit(commandBuffer))
throw std::runtime_error("Failed to submit command buffer: " + TranslateOpenGLError(graphicsQueue.GetLastErrorCode()));
}
}
void OpenGLRenderImage::Present()
{
Vk::QueueHandle& graphicsQueue = m_owner.GetGraphicsQueue();
if (!graphicsQueue.Submit(UInt32(m_graphicalCommandsBuffers.size()), m_graphicalCommandsBuffers.data(), m_imageAvailableSemaphore, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, m_renderFinishedSemaphore, m_inFlightFence))
throw std::runtime_error("Failed to submit command buffers: " + TranslateOpenGLError(graphicsQueue.GetLastErrorCode()));
m_owner.Present(m_imageIndex, m_renderFinishedSemaphore);
}
}
#endif

View File

@@ -0,0 +1,14 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
}
#endif

View File

@@ -0,0 +1,274 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <cassert>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLRenderPipeline::OpenGLRenderPipeline(Vk::Device& device, RenderPipelineInfo pipelineInfo) :
m_device(&device),
m_pipelineInfo(std::move(pipelineInfo))
{
m_pipelineCreateInfo = BuildCreateInfo(m_pipelineInfo);
}
VkPipeline OpenGLRenderPipeline::Get(const Vk::RenderPass& renderPass) const
{
if (auto it = m_pipelines.find(renderPass); it != m_pipelines.end())
return it->second;
// Copy create info to make Get re-entrant
VkGraphicsPipelineCreateInfo pipelineCreateInfo = m_pipelineCreateInfo.pipelineInfo;
pipelineCreateInfo.renderPass = renderPass;
Vk::Pipeline newPipeline;
if (!newPipeline.CreateGraphics(*m_device, pipelineCreateInfo))
return VK_NULL_HANDLE;
auto it = m_pipelines.emplace(renderPass, std::move(newPipeline)).first;
return it->second;
}
std::vector<VkPipelineColorBlendAttachmentState> OpenGLRenderPipeline::BuildColorBlendAttachmentStateList(const RenderPipelineInfo& pipelineInfo)
{
std::vector<VkPipelineColorBlendAttachmentState> colorBlendStates;
VkPipelineColorBlendAttachmentState& colorBlendState = colorBlendStates.emplace_back();
colorBlendState.blendEnable = pipelineInfo.blending;
colorBlendState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; //< TODO
if (pipelineInfo.blending)
{
//TODO
/*switch (pipelineInfo.dstBlend)
{
blendState.dstAlphaBlendFactor
}*/
}
else
{
colorBlendState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendState.colorBlendOp = VK_BLEND_OP_ADD;
colorBlendState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorBlendState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorBlendState.alphaBlendOp = VK_BLEND_OP_ADD;
}
return colorBlendStates;
}
VkPipelineColorBlendStateCreateInfo OpenGLRenderPipeline::BuildColorBlendInfo(const RenderPipelineInfo& pipelineInfo, const std::vector<VkPipelineColorBlendAttachmentState>& attachmentState)
{
VkPipelineColorBlendStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
createInfo.attachmentCount = std::uint32_t(attachmentState.size());
createInfo.pAttachments = attachmentState.data();
return createInfo;
}
VkPipelineDepthStencilStateCreateInfo OpenGLRenderPipeline::BuildDepthStencilInfo(const RenderPipelineInfo& pipelineInfo)
{
VkPipelineDepthStencilStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
createInfo.depthTestEnable = pipelineInfo.depthBuffer;
createInfo.depthWriteEnable = pipelineInfo.depthWrite;
createInfo.depthCompareOp = ToOpenGL(pipelineInfo.depthCompare);
createInfo.stencilTestEnable = pipelineInfo.stencilTest;
createInfo.front = BuildStencilOp(pipelineInfo, true);
createInfo.back = BuildStencilOp(pipelineInfo, false);
return createInfo;
}
VkPipelineDynamicStateCreateInfo OpenGLRenderPipeline::BuildDynamicStateInfo(const RenderPipelineInfo& pipelineInfo, const std::vector<VkDynamicState>& dynamicStates)
{
VkPipelineDynamicStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
createInfo.dynamicStateCount = std::uint32_t(dynamicStates.size());
createInfo.pDynamicStates = dynamicStates.data();
return createInfo;
}
std::vector<VkDynamicState> OpenGLRenderPipeline::BuildDynamicStateList(const RenderPipelineInfo& pipelineInfo)
{
return { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
}
VkPipelineInputAssemblyStateCreateInfo OpenGLRenderPipeline::BuildInputAssemblyInfo(const RenderPipelineInfo& pipelineInfo)
{
VkPipelineInputAssemblyStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
createInfo.topology = ToOpenGL(pipelineInfo.primitiveMode);
return createInfo;
}
VkPipelineMultisampleStateCreateInfo OpenGLRenderPipeline::BuildMultisampleInfo(const RenderPipelineInfo& pipelineInfo)
{
VkPipelineMultisampleStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
createInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
createInfo.minSampleShading = 1.0f; //< TODO: Remove
return createInfo;
}
VkPipelineRasterizationStateCreateInfo OpenGLRenderPipeline::BuildRasterizationInfo(const RenderPipelineInfo& pipelineInfo)
{
VkPipelineRasterizationStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
createInfo.polygonMode = ToOpenGL(pipelineInfo.faceFilling);
createInfo.cullMode = ToOpenGL(pipelineInfo.cullingSide);
createInfo.frontFace = VK_FRONT_FACE_CLOCKWISE; //< TODO
createInfo.lineWidth = pipelineInfo.lineWidth;
return createInfo;
}
VkPipelineViewportStateCreateInfo OpenGLRenderPipeline::BuildViewportInfo(const RenderPipelineInfo& pipelineInfo)
{
VkPipelineViewportStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
createInfo.scissorCount = createInfo.viewportCount = 1; //< TODO
return createInfo;
}
VkStencilOpState OpenGLRenderPipeline::BuildStencilOp(const RenderPipelineInfo& pipelineInfo, bool front)
{
const auto& pipelineStencil = (front) ? pipelineInfo.stencilFront : pipelineInfo.stencilBack;
VkStencilOpState stencilStates;
stencilStates.compareMask = pipelineStencil.compareMask;
stencilStates.compareOp = ToOpenGL(pipelineStencil.compare);
stencilStates.depthFailOp = ToOpenGL(pipelineStencil.depthFail);
stencilStates.failOp = ToOpenGL(pipelineStencil.fail);
stencilStates.passOp = ToOpenGL(pipelineStencil.pass);
stencilStates.reference = pipelineStencil.reference;
stencilStates.writeMask = pipelineStencil.writeMask;
return stencilStates;
}
std::vector<VkPipelineShaderStageCreateInfo> OpenGLRenderPipeline::BuildShaderStageInfo(const RenderPipelineInfo& pipelineInfo)
{
std::vector<VkPipelineShaderStageCreateInfo> shaderStageCreateInfos;
for (auto&& stagePtr : pipelineInfo.shaderStages)
{
Nz::OpenGLShaderStage& vulkanStage = *static_cast<Nz::OpenGLShaderStage*>(stagePtr.get());
VkPipelineShaderStageCreateInfo& createInfo = shaderStageCreateInfos.emplace_back();
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
createInfo.module = vulkanStage.GetHandle();
createInfo.pName = "main";
createInfo.stage = ToOpenGL(vulkanStage.GetStageType());
}
return shaderStageCreateInfos;
}
std::vector<VkVertexInputAttributeDescription> OpenGLRenderPipeline::BuildVertexAttributeDescription(const RenderPipelineInfo& pipelineInfo)
{
std::vector<VkVertexInputAttributeDescription> vertexAttributes;
std::uint32_t locationIndex = 0;
for (const auto& bufferData : pipelineInfo.vertexBuffers)
{
std::uint32_t binding = std::uint32_t(bufferData.binding);
for (const auto& componentInfo : *bufferData.declaration)
{
auto& bufferAttribute = vertexAttributes.emplace_back();
bufferAttribute.binding = binding;
bufferAttribute.location = locationIndex++;
bufferAttribute.offset = std::uint32_t(componentInfo.offset);
bufferAttribute.format = ToOpenGL(componentInfo.type);
}
}
return vertexAttributes;
}
std::vector<VkVertexInputBindingDescription> OpenGLRenderPipeline::BuildVertexBindingDescription(const RenderPipelineInfo& pipelineInfo)
{
std::vector<VkVertexInputBindingDescription> vertexBindings;
for (const auto& bufferData : pipelineInfo.vertexBuffers)
{
auto& bufferBinding = vertexBindings.emplace_back();
bufferBinding.binding = std::uint32_t(bufferData.binding);
bufferBinding.stride = std::uint32_t(bufferData.declaration->GetStride());
bufferBinding.inputRate = ToOpenGL(bufferData.declaration->GetInputRate());
}
return vertexBindings;
}
VkPipelineVertexInputStateCreateInfo OpenGLRenderPipeline::BuildVertexInputInfo(const RenderPipelineInfo& pipelineInfo, const std::vector<VkVertexInputAttributeDescription>& vertexAttributes, const std::vector<VkVertexInputBindingDescription>& bindingDescriptions)
{
VkPipelineVertexInputStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
createInfo.vertexAttributeDescriptionCount = std::uint32_t(vertexAttributes.size());
createInfo.pVertexAttributeDescriptions = vertexAttributes.data();
createInfo.vertexBindingDescriptionCount = std::uint32_t(bindingDescriptions.size());
createInfo.pVertexBindingDescriptions = bindingDescriptions.data();
return createInfo;
}
auto OpenGLRenderPipeline::BuildCreateInfo(const RenderPipelineInfo& pipelineInfo) -> CreateInfo
{
CreateInfo createInfo = {};
createInfo.stateData = std::make_unique<CreateInfo::StateData>();
createInfo.colorBlendAttachmentState = BuildColorBlendAttachmentStateList(pipelineInfo);
createInfo.dynamicStates = BuildDynamicStateList(pipelineInfo);
createInfo.shaderStages = BuildShaderStageInfo(pipelineInfo);
createInfo.vertexAttributesDescription = BuildVertexAttributeDescription(pipelineInfo);
createInfo.vertexBindingDescription = BuildVertexBindingDescription(pipelineInfo);
createInfo.stateData->colorBlendState = BuildColorBlendInfo(pipelineInfo, createInfo.colorBlendAttachmentState);
createInfo.stateData->depthStencilState = BuildDepthStencilInfo(pipelineInfo);
createInfo.stateData->dynamicState = BuildDynamicStateInfo(pipelineInfo, createInfo.dynamicStates);
createInfo.stateData->inputAssemblyState = BuildInputAssemblyInfo(pipelineInfo);
createInfo.stateData->multiSampleState = BuildMultisampleInfo(pipelineInfo);
createInfo.stateData->rasterizationState = BuildRasterizationInfo(pipelineInfo);
createInfo.stateData->viewportState = BuildViewportInfo(pipelineInfo);
createInfo.stateData->vertexInputState = BuildVertexInputInfo(pipelineInfo, createInfo.vertexAttributesDescription, createInfo.vertexBindingDescription);
createInfo.pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.pipelineInfo.stageCount = std::uint32_t(createInfo.shaderStages.size());
createInfo.pipelineInfo.pStages = createInfo.shaderStages.data();
createInfo.pipelineInfo.pColorBlendState = &createInfo.stateData->colorBlendState;
createInfo.pipelineInfo.pDepthStencilState = &createInfo.stateData->depthStencilState;
createInfo.pipelineInfo.pDynamicState = &createInfo.stateData->dynamicState;
createInfo.pipelineInfo.pInputAssemblyState = &createInfo.stateData->inputAssemblyState;
createInfo.pipelineInfo.pMultisampleState = &createInfo.stateData->multiSampleState;
createInfo.pipelineInfo.pRasterizationState = &createInfo.stateData->rasterizationState;
createInfo.pipelineInfo.pVertexInputState = &createInfo.stateData->vertexInputState;
createInfo.pipelineInfo.pViewportState = &createInfo.stateData->viewportState;
OpenGLRenderPipelineLayout& pipelineLayout = *static_cast<OpenGLRenderPipelineLayout*>(pipelineInfo.pipelineLayout.get());
createInfo.pipelineInfo.layout = pipelineLayout.GetPipelineLayout();
return createInfo;
}
}
#endif

View File

@@ -0,0 +1,140 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/MemoryHelper.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <cassert>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLRenderPipelineLayout::~OpenGLRenderPipelineLayout()
{
for (auto& pool : m_descriptorPools)
{
if (!pool.freeBindings.TestAll())
NazaraWarning("Not all ShaderBinding have been released!");
}
}
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateShaderBinding()
{
for (std::size_t i = 0; i < m_descriptorPools.size(); ++i)
{
ShaderBindingPtr bindingPtr = AllocateFromPool(i);
if (!bindingPtr)
continue;
return bindingPtr;
}
// No allocation could be made, time to allocate a new pool
std::size_t newPoolIndex = m_descriptorPools.size();
AllocatePool();
ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex);
if (!bindingPtr)
throw std::runtime_error("Failed to allocate shader binding");
return bindingPtr;
}
bool OpenGLRenderPipelineLayout::Create(Vk::Device& device, RenderPipelineLayoutInfo layoutInfo)
{
m_device = &device;
m_layoutInfo = std::move(layoutInfo);
StackVector<VkDescriptorSetLayoutBinding> layoutBindings = NazaraStackVector(VkDescriptorSetLayoutBinding, m_layoutInfo.bindings.size());
for (const auto& bindingInfo : m_layoutInfo.bindings)
{
VkDescriptorSetLayoutBinding& layoutBinding = layoutBindings.emplace_back();
layoutBinding.binding = bindingInfo.index;
layoutBinding.descriptorCount = 1U;
layoutBinding.descriptorType = ToOpenGL(bindingInfo.type);
layoutBinding.stageFlags = ToOpenGL(bindingInfo.shaderStageFlags);
}
if (!m_descriptorSetLayout.Create(*m_device, UInt32(layoutBindings.size()), layoutBindings.data()))
return false;
if (!m_pipelineLayout.Create(*m_device, m_descriptorSetLayout))
return false;
return true;
}
auto OpenGLRenderPipelineLayout::AllocatePool() -> DescriptorPool&
{
StackVector<VkDescriptorPoolSize> poolSizes = NazaraStackVector(VkDescriptorPoolSize, m_layoutInfo.bindings.size());
constexpr UInt32 MaxSet = 128;
for (const auto& bindingInfo : m_layoutInfo.bindings)
{
VkDescriptorPoolSize& poolSize = poolSizes.emplace_back();
poolSize.descriptorCount = MaxSet;
poolSize.type = ToOpenGL(bindingInfo.type);
}
DescriptorPool pool;
if (!pool.descriptorPool.Create(*m_device, MaxSet, UInt32(poolSizes.size()), poolSizes.data(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT))
throw std::runtime_error("Failed to allocate new descriptor pool: " + TranslateOpenGLError(pool.descriptorPool.GetLastErrorCode()));
pool.freeBindings.Resize(MaxSet, true);
pool.storage = std::make_unique<DescriptorPool::BindingStorage[]>(MaxSet);
return m_descriptorPools.emplace_back(std::move(pool));
}
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex)
{
auto& pool = m_descriptorPools[poolIndex];
std::size_t freeBindingId = pool.freeBindings.FindFirst();
if (freeBindingId == pool.freeBindings.npos)
return {}; //< No free binding in this pool
Vk::DescriptorSet descriptorSet = pool.descriptorPool.AllocateDescriptorSet(m_descriptorSetLayout);
if (!descriptorSet)
{
NazaraWarning("Failed to allocate descriptor set: " + TranslateOpenGLError(pool.descriptorPool.GetLastErrorCode()));
return {};
}
pool.freeBindings.Reset(freeBindingId);
OpenGLShaderBinding* freeBindingMemory = reinterpret_cast<OpenGLShaderBinding*>(&pool.storage[freeBindingId]);
return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId, std::move(descriptorSet)));
}
void OpenGLRenderPipelineLayout::Release(ShaderBinding& binding)
{
OpenGLShaderBinding& vulkanBinding = static_cast<OpenGLShaderBinding&>(binding);
std::size_t poolIndex = vulkanBinding.GetPoolIndex();
std::size_t bindingIndex = vulkanBinding.GetBindingIndex();
assert(poolIndex < m_descriptorPools.size());
auto& pool = m_descriptorPools[poolIndex];
assert(!pool.freeBindings.Test(bindingIndex));
OpenGLShaderBinding* bindingMemory = reinterpret_cast<OpenGLShaderBinding*>(&pool.storage[bindingIndex]);
PlacementDestroy(bindingMemory);
pool.freeBindings.Set(bindingIndex);
// Try to free pool if it's one of the last one
if (poolIndex >= m_descriptorPools.size() - 1 && poolIndex <= m_descriptorPools.size())
TryToShrink();
}
}
#endif

View File

@@ -0,0 +1,486 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Engine - Renderer module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/VkRenderWindow.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Core/StackArray.hpp>
#include <Nazara/Math/Vector2.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/OpenGLRenderer/OpenGL.hpp>
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
#include <Nazara/OpenGLRenderer/OpenGLDevice.hpp>
#include <Nazara/OpenGLRenderer/OpenGLSurface.hpp>
#include <array>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
VkRenderWindow::VkRenderWindow() :
m_currentFrame(0),
m_depthStencilFormat(VK_FORMAT_MAX_ENUM)
{
}
VkRenderWindow::~VkRenderWindow()
{
if (m_device)
m_device->WaitForIdle();
m_concurrentImageData.clear();
m_renderPass.reset();
m_framebuffer.reset();
m_swapchain.Destroy();
}
OpenGLRenderImage& VkRenderWindow::Acquire()
{
OpenGLRenderImage& currentFrame = m_concurrentImageData[m_currentFrame];
Vk::Fence& inFlightFence = currentFrame.GetInFlightFence();
// Wait until previous rendering to this image has been done
inFlightFence.Wait();
UInt32 imageIndex;
if (!m_swapchain.AcquireNextImage(std::numeric_limits<UInt64>::max(), currentFrame.GetImageAvailableSemaphore(), VK_NULL_HANDLE, &imageIndex))
throw std::runtime_error("Failed to acquire next image: " + TranslateOpenGLError(m_swapchain.GetLastErrorCode()));
if (m_inflightFences[imageIndex])
m_inflightFences[imageIndex]->Wait();
m_inflightFences[imageIndex] = &inFlightFence;
m_inflightFences[imageIndex]->Reset();
currentFrame.Reset(imageIndex);
return currentFrame;
}
bool VkRenderWindow::Create(RendererImpl* /*renderer*/, RenderSurface* surface, const Vector2ui& size, const RenderWindowParameters& parameters)
{
const auto& deviceInfo = OpenGL::GetPhysicalDevices()[0];
Vk::Surface& vulkanSurface = static_cast<OpenGLSurface*>(surface)->GetSurface();
UInt32 graphicsFamilyQueueIndex;
UInt32 presentableFamilyQueueIndex;
UInt32 transferFamilyQueueIndex;
m_device = OpenGL::SelectDevice(deviceInfo, vulkanSurface, &graphicsFamilyQueueIndex, &presentableFamilyQueueIndex, &transferFamilyQueueIndex);
if (!m_device)
{
NazaraError("Failed to get compatible OpenGL device");
return false;
}
m_graphicsQueue = m_device->GetQueue(graphicsFamilyQueueIndex, 0);
m_presentQueue = m_device->GetQueue(presentableFamilyQueueIndex, 0);
m_transferQueue = m_device->GetQueue(transferFamilyQueueIndex, 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats;
if (!vulkanSurface.GetFormats(deviceInfo.physDevice, &surfaceFormats))
{
NazaraError("Failed to query supported surface formats");
return false;
}
m_surfaceFormat = [&] () -> VkSurfaceFormatKHR
{
if (surfaceFormats.size() == 1 && surfaceFormats.front().format == VK_FORMAT_UNDEFINED)
{
// If the list contains one undefined format, it means any format can be used
return { VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
}
else
{
// Search for RGBA8 and default to first format
for (const VkSurfaceFormatKHR& surfaceFormat : surfaceFormats)
{
if (surfaceFormat.format == VK_FORMAT_R8G8B8A8_UNORM)
return surfaceFormat;
}
return surfaceFormats.front();
}
}();
if (!parameters.depthFormats.empty())
{
for (PixelFormat format : parameters.depthFormats)
{
switch (format)
{
case PixelFormat_Depth16:
m_depthStencilFormat = VK_FORMAT_D16_UNORM;
break;
case PixelFormat_Depth24:
case PixelFormat_Depth24Stencil8:
m_depthStencilFormat = VK_FORMAT_D24_UNORM_S8_UINT;
break;
case PixelFormat_Depth32:
m_depthStencilFormat = VK_FORMAT_D32_SFLOAT;
break;
case PixelFormat_Stencil1:
case PixelFormat_Stencil4:
case PixelFormat_Stencil8:
m_depthStencilFormat = VK_FORMAT_S8_UINT;
break;
case PixelFormat_Stencil16:
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
break;
default:
{
PixelFormatContent formatContent = PixelFormatInfo::GetContent(format);
if (formatContent != PixelFormatContent_DepthStencil && formatContent != PixelFormatContent_Stencil)
NazaraWarning("Invalid format " + PixelFormatInfo::GetName(format) + " for depth-stencil attachment");
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
break;
}
}
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM)
{
VkFormatProperties formatProperties = m_device->GetInstance().GetPhysicalDeviceFormatProperties(deviceInfo.physDevice, m_depthStencilFormat);
if (formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
break; //< Found it
m_depthStencilFormat = VK_FORMAT_MAX_ENUM;
}
}
}
if (!SetupSwapchain(deviceInfo, vulkanSurface, size))
{
NazaraError("Failed to create swapchain");
return false;
}
if (m_depthStencilFormat != VK_FORMAT_MAX_ENUM && !SetupDepthBuffer(size))
{
NazaraError("Failed to create depth buffer");
return false;
}
if (!SetupRenderPass())
{
NazaraError("Failed to create render pass");
return false;
}
UInt32 imageCount = m_swapchain.GetBufferCount();
// Framebuffers
m_inflightFences.resize(imageCount);
Nz::StackArray<Vk::Framebuffer> framebuffers = NazaraStackArray(Vk::Framebuffer, imageCount);
for (UInt32 i = 0; i < imageCount; ++i)
{
std::array<VkImageView, 2> attachments = { m_swapchain.GetBuffer(i).view, m_depthBufferView };
VkFramebufferCreateInfo frameBufferCreate = {
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
nullptr,
0,
m_renderPass->GetRenderPass(),
(attachments[1] != VK_NULL_HANDLE) ? 2U : 1U,
attachments.data(),
size.x,
size.y,
1U
};
if (!framebuffers[i].Create(*m_device, frameBufferCreate))
{
NazaraError("Failed to create framebuffer for image #" + String::Number(i) + ": " + TranslateOpenGLError(framebuffers[i].GetLastErrorCode()));
return false;
}
}
m_framebuffer.emplace(framebuffers.data(), framebuffers.size());
const std::size_t MaxConcurrentImage = imageCount;
m_concurrentImageData.reserve(MaxConcurrentImage);
for (std::size_t i = 0; i < MaxConcurrentImage; ++i)
m_concurrentImageData.emplace_back(*this);
m_clock.Restart();
return true;
}
std::unique_ptr<CommandPool> VkRenderWindow::CreateCommandPool(QueueType queueType)
{
UInt32 queueFamilyIndex;
switch (queueType)
{
case QueueType::Compute:
queueFamilyIndex = m_device->GetDefaultFamilyIndex(QueueType::Compute);
break;
case QueueType::Graphics:
queueFamilyIndex = m_graphicsQueue.GetQueueFamilyIndex();
break;
case QueueType::Transfer:
queueFamilyIndex = m_transferQueue.GetQueueFamilyIndex();
break;
}
return std::make_unique<OpenGLCommandPool>(*m_device, queueFamilyIndex);
}
const OpenGLRenderPass& VkRenderWindow::GetRenderPass() const
{
return *m_renderPass;
}
bool VkRenderWindow::SetupDepthBuffer(const Vector2ui& size)
{
VkImageCreateInfo imageCreateInfo = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0U, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
m_depthStencilFormat, // VkFormat format;
{size.x, size.y, 1U}, // VkExtent3D extent;
1U, // uint32_t mipLevels;
1U, // uint32_t arrayLayers;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
0U, // uint32_t queueFamilyIndexCount;
nullptr, // const uint32_t* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
if (!m_depthBuffer.Create(*m_device, imageCreateInfo))
{
NazaraError("Failed to create depth buffer");
return false;
}
VkMemoryRequirements memoryReq = m_depthBuffer.GetMemoryRequirements();
if (!m_depthBufferMemory.Create(*m_device, memoryReq.size, memoryReq.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT))
{
NazaraError("Failed to allocate depth buffer memory");
return false;
}
if (!m_depthBuffer.BindImageMemory(m_depthBufferMemory))
{
NazaraError("Failed to bind depth buffer to buffer");
return false;
}
VkImageViewCreateInfo imageViewCreateInfo = {
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0, // VkImageViewCreateFlags flags;
m_depthBuffer, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_depthStencilFormat, // VkFormat format;
{ // VkComponentMapping components;
VK_COMPONENT_SWIZZLE_R, // VkComponentSwizzle .r;
VK_COMPONENT_SWIZZLE_G, // VkComponentSwizzle .g;
VK_COMPONENT_SWIZZLE_B, // VkComponentSwizzle .b;
VK_COMPONENT_SWIZZLE_A // VkComponentSwizzle .a;
},
{ // VkImageSubresourceRange subresourceRange;
VK_IMAGE_ASPECT_DEPTH_BIT, // VkImageAspectFlags .aspectMask;
0, // uint32_t .baseMipLevel;
1, // uint32_t .levelCount;
0, // uint32_t .baseArrayLayer;
1 // uint32_t .layerCount;
}
};
if (!m_depthBufferView.Create(*m_device, imageViewCreateInfo))
{
NazaraError("Failed to create depth buffer view");
return false;
}
return true;
}
bool VkRenderWindow::SetupRenderPass()
{
std::array<VkAttachmentDescription, 2> attachments = {
{
{
0, // VkAttachmentDescriptionFlags flags;
m_surfaceFormat.format, // VkFormat format;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR // VkImageLayout finalLayout;
},
{
0, // VkAttachmentDescriptionFlags flags;
m_depthStencilFormat, // VkFormat format;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp storeOp;
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout;
},
}
};
VkAttachmentReference colorReference = {
0, // uint32_t attachment;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
};
VkAttachmentReference depthReference = {
1, // uint32_t attachment;
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL // VkImageLayout layout;
};
VkSubpassDescription subpass = {
0, // VkSubpassDescriptionFlags flags;
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
0U, // uint32_t inputAttachmentCount;
nullptr, // const VkAttachmentReference* pInputAttachments;
1U, // uint32_t colorAttachmentCount;
&colorReference, // const VkAttachmentReference* pColorAttachments;
nullptr, // const VkAttachmentReference* pResolveAttachments;
(m_depthStencilFormat != VK_FORMAT_MAX_ENUM) ? &depthReference : nullptr, // const VkAttachmentReference* pDepthStencilAttachment;
0U, // uint32_t preserveAttachmentCount;
nullptr // const uint32_t* pPreserveAttachments;
};
std::array<VkSubpassDependency, 2> dependencies;
// First dependency at the start of the render pass
// Does the transition from final to initial layout
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; // Producer of the dependency
dependencies[0].dstSubpass = 0; // Consumer is our single subpass that will wait for the execution dependency
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = 0;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
// Second dependency at the end the render pass
// Does the transition from the initial to the final layout
dependencies[1].srcSubpass = 0; // Producer of the dependency is our single subpass
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; // Consumer are all commands outside of the render pass
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
VkRenderPassCreateInfo createInfo = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0, // VkRenderPassCreateFlags flags;
(m_depthStencilFormat != VK_FORMAT_MAX_ENUM) ? 2U : 1U, // uint32_t attachmentCount;
attachments.data(), // const VkAttachmentDescription* pAttachments;
1U, // uint32_t subpassCount;
&subpass, // const VkSubpassDescription* pSubpasses;
UInt32(dependencies.size()), // uint32_t dependencyCount;
dependencies.data() // const VkSubpassDependency* pDependencies;
};
Vk::RenderPass renderPass;
if (!renderPass.Create(*m_device, createInfo))
{
NazaraError("Failed to create render pass: " + TranslateOpenGLError(renderPass.GetLastErrorCode()));
return false;
}
std::initializer_list<PixelFormat> fixmeplease = { PixelFormat::PixelFormat_RGB8, PixelFormat::PixelFormat_Depth24Stencil8 };
m_renderPass.emplace(std::move(renderPass), fixmeplease);
return true;
}
bool VkRenderWindow::SetupSwapchain(const Vk::PhysicalDevice& deviceInfo, Vk::Surface& surface, const Vector2ui& size)
{
VkSurfaceCapabilitiesKHR surfaceCapabilities;
if (!surface.GetCapabilities(deviceInfo.physDevice, &surfaceCapabilities))
{
NazaraError("Failed to query surface capabilities");
return false;
}
Nz::UInt32 imageCount = surfaceCapabilities.minImageCount + 1;
if (surfaceCapabilities.maxImageCount > 0 && imageCount > surfaceCapabilities.maxImageCount)
imageCount = surfaceCapabilities.maxImageCount;
VkExtent2D extent;
if (surfaceCapabilities.currentExtent.width == -1)
{
extent.width = Nz::Clamp<Nz::UInt32>(size.x, surfaceCapabilities.minImageExtent.width, surfaceCapabilities.maxImageExtent.width);
extent.height = Nz::Clamp<Nz::UInt32>(size.y, surfaceCapabilities.minImageExtent.height, surfaceCapabilities.maxImageExtent.height);
}
else
extent = surfaceCapabilities.currentExtent;
std::vector<VkPresentModeKHR> presentModes;
if (!surface.GetPresentModes(deviceInfo.physDevice, &presentModes))
{
NazaraError("Failed to query supported present modes");
return false;
}
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
for (VkPresentModeKHR presentMode : presentModes)
{
if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
{
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR;
break;
}
if (presentMode == VK_PRESENT_MODE_IMMEDIATE_KHR)
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
}
VkSwapchainCreateInfoKHR swapchainInfo = {
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
nullptr,
0,
surface,
imageCount,
m_surfaceFormat.format,
m_surfaceFormat.colorSpace,
extent,
1,
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
VK_SHARING_MODE_EXCLUSIVE,
0, nullptr,
surfaceCapabilities.currentTransform,
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
swapchainPresentMode,
VK_TRUE,
VK_NULL_HANDLE
};
if (!m_swapchain.Create(*m_device, swapchainInfo))
{
NazaraError("Failed to create swapchain");
return false;
}
return true;
}
}
#endif

View File

@@ -0,0 +1,79 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/OpenGLRenderer.hpp>
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/Renderer/RenderDevice.hpp>
#include <Nazara/Renderer/RenderSurface.hpp>
#include <Nazara/Renderer/RenderWindowImpl.hpp>
#include <Nazara/OpenGLRenderer/OpenGL.hpp>
#include <cassert>
#include <sstream>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLRenderer::~OpenGLRenderer()
{
OpenGL::Uninitialize();
}
std::unique_ptr<RenderSurface> OpenGLRenderer::CreateRenderSurfaceImpl()
{
return {};
}
std::unique_ptr<RenderWindowImpl> OpenGLRenderer::CreateRenderWindowImpl()
{
return {};
}
std::shared_ptr<RenderDevice> OpenGLRenderer::InstanciateRenderDevice(std::size_t deviceIndex)
{
//assert(deviceIndex < m_physDevices.size());
//return OpenGL::SelectDevice(m_physDevices[deviceIndex]);
return {};
}
bool OpenGLRenderer::IsBetterThan(const RendererImpl* other) const
{
if (other->QueryAPI() == RenderAPI::OpenGL && QueryAPIVersion() > other->QueryAPIVersion())
return true;
return false; //< OpenGL is mostly a fallback to other renderers
}
bool OpenGLRenderer::Prepare(const ParameterList& parameters)
{
return OpenGL::Initialize();
}
RenderAPI OpenGLRenderer::QueryAPI() const
{
return RenderAPI::OpenGL;
}
std::string OpenGLRenderer::QueryAPIString() const
{
std::ostringstream ss;
ss << "OpenGL ES renderer 3.0";
return ss.str();
}
UInt32 OpenGLRenderer::QueryAPIVersion() const
{
return 300;
}
std::vector<RenderDeviceInfo> OpenGLRenderer::QueryRenderDevices() const
{
std::vector<RenderDeviceInfo> devices;
auto& dummyDevice = devices.emplace_back();
dummyDevice.name = "OpenGL Default Device";
dummyDevice.type = RenderDeviceType::Unknown;
return devices;
}
}

View File

@@ -0,0 +1,79 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/Core/StackVector.hpp>
#include <Nazara/OpenGLRenderer/OpenGLBuffer.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
void OpenGLShaderBinding::Update(std::initializer_list<Binding> bindings)
{
StackVector<VkDescriptorBufferInfo> bufferBinding = NazaraStackVector(VkDescriptorBufferInfo, bindings.size());
StackVector<VkDescriptorImageInfo> imageBinding = NazaraStackVector(VkDescriptorImageInfo, bindings.size());
StackVector<VkWriteDescriptorSet> writeOps = NazaraStackVector(VkWriteDescriptorSet, bindings.size());
for (const Binding& binding : bindings)
{
VkWriteDescriptorSet& writeOp = writeOps.emplace_back();
writeOp.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeOp.dstSet = m_descriptorSet;
writeOp.dstBinding = UInt32(binding.bindingIndex);
std::visit([&](auto&& arg)
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, TextureBinding>)
{
OpenGLTexture& vkTexture = *static_cast<OpenGLTexture*>(arg.texture);
OpenGLTextureSampler& vkSampler = *static_cast<OpenGLTextureSampler*>(arg.sampler);
VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back();
imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
imageInfo.imageView = vkTexture.GetImageView();
imageInfo.sampler = vkSampler.GetSampler();
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeOp.pImageInfo = &imageInfo;
}
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
{
OpenGLBuffer& vkBuffer = *static_cast<OpenGLBuffer*>(arg.buffer);
VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back();
bufferInfo.buffer = vkBuffer.GetBuffer();
bufferInfo.offset = arg.offset;
bufferInfo.range = arg.range;
writeOp.descriptorCount = 1;
writeOp.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
writeOp.pBufferInfo = &bufferInfo;
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, binding.content);
}
m_owner.GetDevice()->vkUpdateDescriptorSets(*m_owner.GetDevice(), UInt32(writeOps.size()), writeOps.data(), 0U, nullptr);
}
void OpenGLShaderBinding::Release()
{
m_owner.Release(*this);
}
}
#endif

View File

@@ -0,0 +1,31 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
bool OpenGLShaderStage::Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
if (lang != ShaderLanguage::SpirV)
{
NazaraError("Only Spir-V is supported for now");
return false;
}
if (!m_shaderModule.Create(device, reinterpret_cast<const Nz::UInt32*>(source), sourceSize))
{
NazaraError("Failed to create shader module");
return false;
}
m_stage = type;
return true;
}
}
#endif

View File

@@ -0,0 +1,49 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLSurface.hpp>
#include <Nazara/OpenGLRenderer/OpenGL.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLSurface::OpenGLSurface() :
m_surface(OpenGL::GetInstance())
{
}
OpenGLSurface::~OpenGLSurface() = default;
bool OpenGLSurface::Create(WindowHandle handle)
{
bool success = false;
#if defined(NAZARA_PLATFORM_WINDOWS)
{
HWND winHandle = reinterpret_cast<HWND>(handle);
HINSTANCE instance = reinterpret_cast<HINSTANCE>(GetWindowLongPtrW(winHandle, GWLP_HINSTANCE));
success = m_surface.Create(instance, winHandle);
}
#else
#error This OS is not supported by OpenGL
#endif
if (!success)
{
NazaraError("Failed to create OpenGL surface: " + TranslateOpenGLError(m_surface.GetLastErrorCode()));
return false;
}
return true;
}
void OpenGLSurface::Destroy()
{
m_surface.Destroy();
}
}
#endif

View File

@@ -0,0 +1,291 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Utility/PixelFormat.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/CommandBuffer.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/QueueHandle.hpp>
#include <stdexcept>
#include <vma/vk_mem_alloc.h>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
namespace
{
inline unsigned int GetLevelSize(unsigned int size, UInt8 level)
{
if (size == 0) // Possible dans le cas d'une image invalide
return 0;
return std::max(size >> level, 1U);
}
}
OpenGLTexture::OpenGLTexture(Vk::Device& device, const TextureInfo& params) :
m_image(VK_NULL_HANDLE),
m_allocation(nullptr),
m_device(device),
m_params(params)
{
VkImageCreateInfo createInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
createInfo.mipLevels = params.mipmapLevel;
createInfo.samples = VK_SAMPLE_COUNT_1_BIT;
createInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
createInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
VkImageViewCreateInfo createInfoView = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
createInfoView.subresourceRange = {
VK_IMAGE_ASPECT_COLOR_BIT,
0,
1,
0,
1
};
InitForFormat(params.pixelFormat, createInfo, createInfoView);
switch (params.type)
{
case ImageType_1D:
NazaraAssert(params.width > 0, "Width must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D;
createInfo.imageType = VK_IMAGE_TYPE_1D;
createInfo.extent.width = params.width;
createInfo.extent.height = 1;
createInfo.extent.depth = 1;
createInfo.arrayLayers = 1;
break;
case ImageType_1D_Array:
NazaraAssert(params.width > 0, "Width must be over zero");
NazaraAssert(params.height > 0, "Height must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_1D_ARRAY;
createInfo.imageType = VK_IMAGE_TYPE_1D;
createInfo.extent.width = params.width;
createInfo.extent.height = 1;
createInfo.extent.depth = 1;
createInfo.arrayLayers = params.height;
break;
case ImageType_2D:
NazaraAssert(params.width > 0, "Width must be over zero");
NazaraAssert(params.height > 0, "Height must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.imageType = VK_IMAGE_TYPE_2D;
createInfo.extent.width = params.width;
createInfo.extent.height = params.height;
createInfo.extent.depth = 1;
createInfo.arrayLayers = 1;
break;
case ImageType_2D_Array:
NazaraAssert(params.width > 0, "Width must be over zero");
NazaraAssert(params.height > 0, "Height must be over zero");
NazaraAssert(params.depth > 0, "Depth must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
createInfo.imageType = VK_IMAGE_TYPE_2D;
createInfo.extent.width = params.width;
createInfo.extent.height = params.height;
createInfo.extent.depth = 1;
createInfo.arrayLayers = params.height;
break;
case ImageType_3D:
NazaraAssert(params.width > 0, "Width must be over zero");
NazaraAssert(params.height > 0, "Height must be over zero");
NazaraAssert(params.depth > 0, "Depth must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_3D;
createInfo.imageType = VK_IMAGE_TYPE_3D;
createInfo.extent.width = params.width;
createInfo.extent.height = params.height;
createInfo.extent.depth = params.depth;
createInfo.arrayLayers = 1;
break;
case ImageType_Cubemap:
NazaraAssert(params.width > 0, "Width must be over zero");
NazaraAssert(params.height > 0, "Height must be over zero");
createInfoView.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
createInfo.imageType = VK_IMAGE_TYPE_2D;
createInfo.extent.width = params.width;
createInfo.extent.height = params.height;
createInfo.extent.depth = 1;
createInfo.arrayLayers = 6;
createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
break;
default:
break;
}
VmaAllocationCreateInfo allocInfo = {};
allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
VkResult result = vmaCreateImage(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &m_image, &m_allocation, nullptr);
if (result != VK_SUCCESS)
throw std::runtime_error("Failed to allocate image: " + TranslateOpenGLError(result));
createInfoView.image = m_image;
if (!m_imageView.Create(device, createInfoView))
{
// FIXME
vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation);
throw std::runtime_error("Failed to create image view: " + TranslateOpenGLError(m_imageView.GetLastErrorCode()));
}
}
OpenGLTexture::~OpenGLTexture()
{
vmaDestroyImage(m_device.GetMemoryAllocator(), m_image, m_allocation);
}
PixelFormat OpenGLTexture::GetFormat() const
{
return m_params.pixelFormat;
}
UInt8 OpenGLTexture::GetLevelCount() const
{
return m_params.mipmapLevel;
}
Vector3ui OpenGLTexture::GetSize(UInt8 level) const
{
return Vector3ui(GetLevelSize(m_params.width, level), GetLevelSize(m_params.height, level), GetLevelSize(m_params.depth, level));
}
ImageType OpenGLTexture::GetType() const
{
return m_params.type;
}
bool OpenGLTexture::Update(const void* ptr)
{
std::size_t textureSize = m_params.width * m_params.height * m_params.depth * PixelFormatInfo::GetBytesPerPixel(m_params.pixelFormat);
VkBufferCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
createInfo.size = textureSize;
createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocInfo = {};
allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
VmaAllocationInfo allocationInfo;
VkBuffer stagingBuffer;
VmaAllocation stagingAllocation;
VkResult result = vmaCreateBuffer(m_device.GetMemoryAllocator(), &createInfo, &allocInfo, &stagingBuffer, &stagingAllocation, &allocationInfo);
if (result != VK_SUCCESS)
{
NazaraError("Failed to allocate staging buffer: " + TranslateOpenGLError(result));
return false;
}
CallOnExit freeStaging([&] {
vmaDestroyBuffer(m_device.GetMemoryAllocator(), stagingBuffer, stagingAllocation);
});
std::memcpy(allocationInfo.pMappedData, ptr, textureSize);
Vk::AutoCommandBuffer copyCommandBuffer = m_device.AllocateCommandBuffer(QueueType::Graphics);
if (!copyCommandBuffer->Begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
return false;
copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
copyCommandBuffer->CopyBufferToImage(stagingBuffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, m_params.width, m_params.height, m_params.depth);
copyCommandBuffer->SetImageLayout(m_image, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
if (!copyCommandBuffer->End())
return false;
Vk::QueueHandle transferQueue = m_device.GetQueue(m_device.GetDefaultFamilyIndex(QueueType::Graphics), 0);
if (!transferQueue.Submit(copyCommandBuffer))
return false;
transferQueue.WaitIdle();
return true;
}
void OpenGLTexture::InitForFormat(PixelFormat pixelFormat, VkImageCreateInfo& createImage, VkImageViewCreateInfo& createImageView)
{
createImageView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A
};
switch (pixelFormat)
{
case PixelFormat_L8:
{
createImage.format = VK_FORMAT_R8_SRGB;
createImageView.format = createImage.format;
createImageView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_A
};
break;
}
case PixelFormat_LA8:
{
createImage.format = VK_FORMAT_R8G8_SRGB;
createImageView.format = createImage.format;
createImageView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G
};
break;
}
case PixelFormat_RGB8:
{
createImage.format = VK_FORMAT_R8G8B8_SRGB;
createImageView.format = createImage.format;
break;
}
case PixelFormat_RGBA8:
{
createImage.format = VK_FORMAT_R8G8B8A8_SRGB;
createImageView.format = createImage.format;
break;
}
default:
throw std::runtime_error(("Unsupported pixel format " + PixelFormatInfo::GetName(pixelFormat)).ToStdString());
}
}
}
#endif

View File

@@ -0,0 +1,35 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLTextureSampler::OpenGLTextureSampler(Vk::Device& device, TextureSamplerInfo samplerInfo)
{
VkSamplerCreateInfo createInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO };
createInfo.magFilter = ToOpenGL(samplerInfo.magFilter);
createInfo.minFilter = ToOpenGL(samplerInfo.minFilter);
createInfo.addressModeU = ToOpenGL(samplerInfo.wrapModeU);
createInfo.addressModeV = ToOpenGL(samplerInfo.wrapModeV);
createInfo.addressModeW = ToOpenGL(samplerInfo.wrapModeW);
createInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
createInfo.mipmapMode = ToOpenGL(samplerInfo.mipmapMode);
if (samplerInfo.anisotropyLevel > 0.f)
{
createInfo.anisotropyEnable = VK_TRUE;
createInfo.maxAnisotropy = samplerInfo.anisotropyLevel;
}
if (!m_sampler.Create(device, createInfo))
throw std::runtime_error("Failed to create sampler: " + TranslateOpenGLError(m_sampler.GetLastErrorCode()));
}
}
#endif

View File

@@ -0,0 +1,91 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/OpenGLUploadPool.hpp>
#include <cassert>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
auto OpenGLUploadPool::Allocate(UInt64 size) -> OpenGLAllocation&
{
const auto& deviceProperties = m_device.GetPhysicalDeviceInfo().properties;
UInt64 preferredAlignement = deviceProperties.limits.optimalBufferCopyOffsetAlignment;
return Allocate(size, preferredAlignement);
}
auto OpenGLUploadPool::Allocate(UInt64 size, UInt64 alignment) -> OpenGLAllocation&
{
assert(size <= m_blockSize);
// Try to minimize lost space
struct
{
Block* block = nullptr;
UInt64 alignedOffset = 0;
UInt64 lostSpace = 0;
} bestBlock;
for (Block& block : m_blocks)
{
UInt64 alignedOffset = AlignPow2(block.freeOffset, alignment);
if (alignedOffset + size > m_blockSize)
continue; //< Not enough space
UInt64 lostSpace = alignedOffset - block.freeOffset;
if (!bestBlock.block || lostSpace < bestBlock.lostSpace)
{
bestBlock.block = &block;
bestBlock.alignedOffset = alignedOffset;
bestBlock.lostSpace = lostSpace;
}
}
// No block found, allocate a new one
if (!bestBlock.block)
{
Block newBlock;
if (!newBlock.buffer.Create(m_device, 0U, m_blockSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
throw std::runtime_error("Failed to create block buffer: " + TranslateOpenGLError(newBlock.buffer.GetLastErrorCode()));
VkMemoryRequirements requirement = newBlock.buffer.GetMemoryRequirements();
if (!newBlock.blockMemory.Create(m_device, requirement.size, requirement.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
throw std::runtime_error("Failed to allocate block memory: " + TranslateOpenGLError(newBlock.blockMemory.GetLastErrorCode()));
if (!newBlock.buffer.BindBufferMemory(newBlock.blockMemory))
throw std::runtime_error("Failed to bind buffer memory: " + TranslateOpenGLError(newBlock.buffer.GetLastErrorCode()));
if (!newBlock.blockMemory.Map())
throw std::runtime_error("Failed to map buffer memory: " + TranslateOpenGLError(newBlock.buffer.GetLastErrorCode()));
bestBlock.block = &m_blocks.emplace_back(std::move(newBlock));
bestBlock.alignedOffset = 0;
bestBlock.lostSpace = 0;
}
OpenGLAllocation& allocationData = m_allocations.emplace_back();
allocationData.buffer = bestBlock.block->buffer;
allocationData.mappedPtr = static_cast<UInt8*>(bestBlock.block->blockMemory.GetMappedPointer()) + bestBlock.alignedOffset;
allocationData.offset = bestBlock.alignedOffset;
allocationData.size = size;
return allocationData;
}
void OpenGLUploadPool::Reset()
{
for (Block& block : m_blocks)
block.freeOffset = 0;
m_allocations.clear();
}
}
#endif

View File

@@ -0,0 +1,106 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#if 0
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
std::string TranslateOpenGLError(VkResult code)
{
// From https://www.khronos.org/registry/vulkan/specs/1.0-wsi_extensions/xhtml/vkspec.html#VkResult
switch (code)
{
case VK_SUCCESS:
return "No error";
case VK_NOT_READY:
return "A fence or query has not yet completed";
case VK_TIMEOUT:
return "A wait operation has not completed in the specified time";
case VK_EVENT_SET:
return "An event is signaled";
case VK_EVENT_RESET:
return "An event is unsignaled";
case VK_INCOMPLETE:
return "A return array was too small for the result";
case VK_ERROR_OUT_OF_HOST_MEMORY:
return "A host memory allocation has failed";
case VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "A device memory allocation has failed";
case VK_ERROR_INITIALIZATION_FAILED:
return "Initialization of an object could not be completed for implementation-specific reasons";
case VK_ERROR_DEVICE_LOST:
return "The logical or physical device has been lost";
case VK_ERROR_MEMORY_MAP_FAILED:
return "Mapping of the memory object has failed";
case VK_ERROR_LAYER_NOT_PRESENT:
return "A requested layer is not present or could not be loaded";
case VK_ERROR_EXTENSION_NOT_PRESENT:
return "A requested extension is not supported";
case VK_ERROR_FEATURE_NOT_PRESENT:
return "A requested feature is not supported";
case VK_ERROR_INCOMPATIBLE_DRIVER:
return "The requested version of OpenGL is not supported by the driver or is otherwise incompatible for implementation-specific reasons";
case VK_ERROR_TOO_MANY_OBJECTS:
return "Too many objects of the type have already been created";
case VK_ERROR_FORMAT_NOT_SUPPORTED:
return "A requested format is not supported on this device";
case VK_ERROR_FRAGMENTED_POOL:
return "A requested pool allocation has failed due to fragmentation of the pools memory";
case VK_ERROR_SURFACE_LOST_KHR:
return "A surface is no longer available";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "The requested window is already connected to a VkSurfaceKHR, or to some other non-OpenGL API";
case VK_SUBOPTIMAL_KHR:
return "A swapchain no longer matches the surface properties exactly, but can still be used to present to the surface successfully";
case VK_ERROR_OUT_OF_DATE_KHR:
return "A surface has changed in such a way that it is no longer compatible with the swapchain, and further presentation requests using the swapchain will fail. Applications must query the new surface properties and recreate their swapchain if they wish to continue presenting to the surface";
case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return "The display used by a swapchain does not use the same presentable image layout, or is incompatible in a way that prevents sharing an image";
case VK_ERROR_VALIDATION_FAILED_EXT:
return "A validation layer found an error";
case VK_ERROR_INVALID_SHADER_NV:
return "One or more shaders failed to compile or link";
case VK_ERROR_OUT_OF_POOL_MEMORY_KHR:
return "A requested pool allocation has failed";
case VK_ERROR_INVALID_EXTERNAL_HANDLE:
return "An external handle is not a valid handle of the specified type";
default:
break;
}
return "Unknown OpenGL error (0x" + String::Number(code, 16).ToStdString() + ')';
}
}
#endif

View File

@@ -0,0 +1,10 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
}

View File

@@ -0,0 +1,44 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/GLContext.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
GLContext::~GLContext() = default;
bool GLContext::LoadCoreFunctions(Loader& loader)
{
if (!Activate())
{
NazaraError("failed to activate context");
return false;
}
auto LoadSymbol = [&](auto& func, const char* funcName)
{
func = reinterpret_cast<std::decay_t<decltype(func)>>(loader.LoadFunction(funcName));
if (!func)
throw std::runtime_error("failed to load core function " + std::string(funcName));
};
try
{
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(name, #name);
NAZARA_OPENGLRENDERER_FOREACH_GLES_FUNC(NAZARA_OPENGLRENDERER_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
}
catch (const std::exception& e)
{
NazaraError(e.what());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,11 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
Loader::~Loader() = default;
}

View File

@@ -0,0 +1,278 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.hpp>
#include <Nazara/Core/CallOnExit.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLLoader.hpp>
#include <array>
#include <cassert>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
thread_local WGLContext* s_currentContext = nullptr;
GL::WGLContext::WGLContext(WGLLoader& loader) :
m_loader(loader)
{
}
WGLContext::~WGLContext()
{
Destroy();
}
bool WGLContext::Activate()
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext)
{
if (currentContext == this)
return true;
// Only one context can be activated per thread
currentContext->Desactivate();
}
bool succeeded = m_loader.wglMakeCurrent(m_deviceContext, m_handle);
if (!succeeded)
{
NazaraError("failed to activate context: " + Error::GetLastSystemError());
return false;
}
currentContext = this;
return true;
}
bool WGLContext::Create(const ContextParams& params)
{
Destroy();
// Creating a context requires a Window
m_window.reset(::CreateWindowA("STATIC", nullptr, WS_DISABLED | WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, GetModuleHandle(nullptr), nullptr));
if (!m_window)
{
NazaraError("failed to create dummy window: " + Error::GetLastSystemError());
return false;
}
::ShowWindow(m_window.get(), FALSE);
m_deviceContext = ::GetDC(m_window.get());
if (!m_deviceContext)
{
NazaraError("failed to retrieve dummy window device context: " + Error::GetLastSystemError());
return false;
}
if (!SetPixelFormat(params))
return false;
WGLContext* currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext && currentContext->wglCreateContextAttribsARB)
{
struct OpenGLVersion
{
int major;
int minor;
};
std::array<OpenGLVersion, 8> supportedVersions = {
{
{ 4, 6 },
{ 4, 5 },
{ 4, 4 },
{ 4, 3 },
{ 4, 2 },
{ 4, 1 },
{ 4, 0 },
{ 3, 3 }
}
};
for (const OpenGLVersion& version : supportedVersions)
{
std::array<int, 3 * 2 + 1> attributes = {
WGL_CONTEXT_MAJOR_VERSION_ARB, version.major,
WGL_CONTEXT_MINOR_VERSION_ARB, version.minor,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB
};
m_handle = currentContext->wglCreateContextAttribsARB(m_deviceContext, nullptr, attributes.data());
if (m_handle)
break;
}
if (!m_handle)
{
NazaraError("failed to create WGL context: " + Error::GetLastSystemError());
return false;
}
}
else
{
m_handle = m_loader.wglCreateContext(m_deviceContext);
if (!m_handle)
{
NazaraError("failed to create WGL context: " + Error::GetLastSystemError());
return false;
}
}
LoadWGLExt();
return true;
}
void WGLContext::Destroy()
{
if (m_handle)
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext == this)
currentContext = nullptr;
m_loader.wglDeleteContext(m_handle);
m_handle = nullptr;
}
}
void WGLContext::EnableVerticalSync(bool enabled)
{
}
void WGLContext::SwapBuffers()
{
m_loader.SwapBuffers(m_deviceContext);
}
void WGLContext::Desactivate()
{
WGLContext*& currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext == this)
{
m_loader.wglMakeCurrent(nullptr, nullptr);
currentContext = nullptr;
}
}
bool WGLContext::LoadWGLExt()
{
if (!Activate())
return false;
#define NAZARA_OPENGLRENDERER_FUNC(name, sig)
#define NAZARA_OPENGLRENDERER_EXT_BEGIN(ext)
#define NAZARA_OPENGLRENDERER_EXT_END()
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) name = reinterpret_cast<sig>(m_loader.wglGetProcAddress(#name));
NAZARA_OPENGLRENDERER_FOREACH_WGL_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_BEGIN, NAZARA_OPENGLRENDERER_EXT_END, NAZARA_OPENGLRENDERER_EXT_FUNC)
#undef NAZARA_OPENGLRENDERER_EXT_BEGIN
#undef NAZARA_OPENGLRENDERER_EXT_END
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
#undef NAZARA_OPENGLRENDERER_FUNC
const char* extensionString = nullptr;
if (wglGetExtensionsStringARB)
extensionString = wglGetExtensionsStringARB(m_deviceContext);
else if (wglGetExtensionsStringEXT)
extensionString = wglGetExtensionsStringEXT();
if (extensionString)
{
SplitString(extensionString, " ", [&](std::string_view extension)
{
m_supportedExtensions.emplace(extension);
return true;
});
}
return true;
}
bool WGLContext::SetPixelFormat(const ContextParams& params)
{
PIXELFORMATDESCRIPTOR descriptor = {};
descriptor.nSize = sizeof(PIXELFORMATDESCRIPTOR);
descriptor.nVersion = 1;
int pixelFormat = 0;
if (params.sampleCount > 1)
{
WGLContext* currentContext = s_currentContext; //< Pay TLS cost only once
if (currentContext)
{
// WGL_ARB_pixel_format and WGL_EXT_pixel_format are the same, except for the symbol
auto wglChoosePixelFormat = (currentContext->wglChoosePixelFormatARB) ? currentContext->wglChoosePixelFormatARB : currentContext->wglChoosePixelFormatEXT;
if (wglChoosePixelFormat)
{
std::array<int, 10 * 2 + 1> attributes = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB, (params.bitsPerPixel == 32) ? 24 : params.bitsPerPixel,
WGL_ALPHA_BITS_ARB, (params.bitsPerPixel == 32) ? 8 : 0,
WGL_DEPTH_BITS_ARB, params.depthBits,
WGL_STENCIL_BITS_ARB, params.stencilBits,
WGL_DOUBLE_BUFFER_ARB, (params.doubleBuffering) ? GL_TRUE : GL_FALSE,
WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
WGL_SAMPLES_ARB, params.sampleCount
};
int& sampleCount = attributes[attributes.size() - 3];
do
{
UINT formatCount;
if (currentContext->wglChoosePixelFormatARB(m_deviceContext, attributes.data(), nullptr, 1, &pixelFormat, &formatCount))
{
if (formatCount > 0)
break;
}
sampleCount--;
}
while (sampleCount > 1);
if (params.sampleCount != sampleCount)
NazaraWarning("couldn't find a pixel format matching " + std::to_string(params.sampleCount) + " sample count, using " + std::to_string(sampleCount) + " sample(s) instead");
}
}
}
if (pixelFormat == 0)
{
descriptor.cColorBits = BYTE((params.bitsPerPixel == 32) ? 24 : params.bitsPerPixel);
descriptor.cDepthBits = BYTE(params.depthBits);
descriptor.cStencilBits = BYTE(params.stencilBits);
descriptor.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
descriptor.iPixelType = PFD_TYPE_RGBA;
if (params.bitsPerPixel == 32)
descriptor.cAlphaBits = 8;
if (params.doubleBuffering)
descriptor.dwFlags |= PFD_DOUBLEBUFFER;
pixelFormat = m_loader.ChoosePixelFormat(m_deviceContext, &descriptor);
if (pixelFormat == 0)
{
NazaraError("Failed to choose pixel format: " + Error::GetLastSystemError());
return false;
}
}
if (!m_loader.SetPixelFormat(m_deviceContext, pixelFormat, &descriptor))
{
NazaraError("Failed to choose pixel format: " + Error::GetLastSystemError());
return false;
}
return true;
}
}

View File

@@ -0,0 +1,70 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLLoader.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
WGLLoader::WGLLoader(DynLib& openglLib) :
m_opengl32Lib(openglLib)
{
if (!m_gdi32Lib.Load("gdi32.dll"))
throw std::runtime_error("failed to load gdi32.dll: " + m_gdi32Lib.GetLastError());
auto LoadSymbol = [](DynLib& lib, auto& func, const char* funcName)
{
func = reinterpret_cast<std::decay_t<decltype(func)>>(lib.GetSymbol(funcName));
if (!func)
throw std::runtime_error("failed to load core function " + std::string(funcName));
};
// Load gdi32 functions
#define NAZARA_OPENGLRENDERER_EXT_BEGIN(ext)
#define NAZARA_OPENGLRENDERER_EXT_END()
#define NAZARA_OPENGLRENDERER_EXT_FUNC(name, sig) //< Ignore extensions
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(m_gdi32Lib, name, #name);
NAZARA_OPENGLRENDERER_FOREACH_GDI32_FUNC(NAZARA_OPENGLRENDERER_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
// Load base WGL functions
#define NAZARA_OPENGLRENDERER_FUNC(name, sig) LoadSymbol(m_opengl32Lib, name, #name);
NAZARA_OPENGLRENDERER_FOREACH_WGL_FUNC(NAZARA_OPENGLRENDERER_FUNC, NAZARA_OPENGLRENDERER_EXT_BEGIN, NAZARA_OPENGLRENDERER_EXT_END, NAZARA_OPENGLRENDERER_EXT_FUNC)
#undef NAZARA_OPENGLRENDERER_FUNC
// In order to load OpenGL functions, we have to create a context first
WGLContext dummyContext(*this);
if (!dummyContext.Create({}))
throw std::runtime_error("failed to create load context");
WGLContext loadContext(*this);
if (!loadContext.Create({}))
throw std::runtime_error("failed to create load context");
if (!loadContext.LoadCoreFunctions(*this))
throw std::runtime_error("failed to load OpenGL functions");
#undef NAZARA_OPENGLRENDERER_EXT_BEGIN
#undef NAZARA_OPENGLRENDERER_EXT_END
#undef NAZARA_OPENGLRENDERER_EXT_FUNC
}
std::unique_ptr<GLContext> WGLLoader::CreateContext()
{
return {}; //< TODO
}
GLFunction WGLLoader::LoadFunction(const char* name)
{
GLFunction func = reinterpret_cast<GLFunction>(wglGetProcAddress(name));
if (!func) //< wglGetProcAddress doesn't work for OpenGL 1.1 functions
func = reinterpret_cast<GLFunction>(m_opengl32Lib.GetSymbol(name));
return func;
}
}

View File

@@ -1,41 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine".
// For conditions of distribution and use, see copyright notice in Config.hpp
#pragma once
#ifndef NAZARA_CONTEXTIMPL_HPP
#define NAZARA_CONTEXTIMPL_HPP
#include <Nazara/Prerequisites.hpp>
#include <Nazara/Renderer/ContextParameters.hpp>
#include <windows.h>
namespace Nz
{
class ContextImpl
{
public:
ContextImpl();
bool Activate() const;
bool Create(ContextParameters& parameters);
void Destroy();
void EnableVerticalSync(bool enabled);
void SwapBuffers();
static bool Desactivate();
private:
HDC m_deviceContext;
HGLRC m_context;
HWND m_window;
bool m_ownsWindow;
};
}
#endif // NAZARA_CONTEXTIMPL_HPP