diff --git a/include/Nazara/OpenGLRenderer/OpenGLVaoCache.hpp b/include/Nazara/OpenGLRenderer/OpenGLVaoCache.hpp new file mode 100644 index 000000000..0a9bc14d1 --- /dev/null +++ b/include/Nazara/OpenGLRenderer/OpenGLVaoCache.hpp @@ -0,0 +1,73 @@ +// Copyright (C) 2015 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 + +#pragma once + +#ifndef NAZARA_OPENGLRENDERER_OPENGLVAOCACHE_HPP +#define NAZARA_OPENGLRENDERER_OPENGLVAOCACHE_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz::GL +{ + struct OpenGLVaoSetup + { + struct Attribs + { + GLuint vertexBuffer; + GLint size; + GLenum type; + GLboolean normalized; + GLsizei stride; + const void* pointer; + }; + + GLuint indexBuffer = 0; + std::array, 16> vertexAttribs; + + inline friend bool operator==(const OpenGLVaoSetup& lhs, const OpenGLVaoSetup& rhs); + }; + + struct OpenGLVaoSetupHasher + { + inline std::size_t operator()(const OpenGLVaoSetup& setup) const; + }; + + class Context; + class VertexArray; + + class NAZARA_OPENGLRENDERER_API OpenGLVaoCache + { + friend Context; + + public: + OpenGLVaoCache(Context& owner); + OpenGLVaoCache(const OpenGLVaoCache&) = delete; + OpenGLVaoCache(OpenGLVaoCache&&) = delete; + ~OpenGLVaoCache(); + + void Clear(); + + const VertexArray& Get(const OpenGLVaoSetup& setup) const; + + OpenGLVaoCache& operator=(const OpenGLVaoCache&) = delete; + OpenGLVaoCache& operator=(OpenGLVaoCache&&) = delete; + + private: + void NotifyBufferDestruction(GLuint buffer); + + mutable std::unordered_map, OpenGLVaoSetupHasher> m_vertexArrays; + Context& m_context; + }; +} + +#include + +#endif diff --git a/include/Nazara/OpenGLRenderer/OpenGLVaoCache.inl b/include/Nazara/OpenGLRenderer/OpenGLVaoCache.inl new file mode 100644 index 000000000..07a08dc12 --- /dev/null +++ b/include/Nazara/OpenGLRenderer/OpenGLVaoCache.inl @@ -0,0 +1,81 @@ +// 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 +#include +#include + +namespace Nz::GL +{ + inline bool operator==(const OpenGLVaoSetup& lhs, const OpenGLVaoSetup& rhs) + { + if (lhs.indexBuffer != rhs.indexBuffer) + return false; + + const auto& compareAttribs = [](const auto& lAttribOpt, const auto& rAttribOpt) + { + if (lAttribOpt.has_value() != rAttribOpt.has_value()) + return false; + + if (lAttribOpt) + { + const auto& lAttrib = *lAttribOpt; + const auto& rAttrib = *rAttribOpt; + + if (lAttrib.vertexBuffer != rAttrib.vertexBuffer) + return false; + + if (lAttrib.size != rAttrib.size) + return false; + + if (lAttrib.type != rAttrib.type) + return false; + + if (lAttrib.normalized != rAttrib.normalized) + return false; + + if (lAttrib.stride != rAttrib.stride) + return false; + + if (lAttrib.pointer != rAttrib.pointer) + return false; + } + + + return true; + }; + + return std::equal(lhs.vertexAttribs.begin(), lhs.vertexAttribs.end(), rhs.vertexAttribs.begin(), compareAttribs); + } + + inline std::size_t OpenGLVaoSetupHasher::operator()(const OpenGLVaoSetup& setup) const + { + std::size_t seed = 0; + + HashCombine(seed, setup.indexBuffer); + + std::size_t bindingIndex = 0; + for (const auto& attribOpt : setup.vertexAttribs) + { + if (attribOpt) + { + const auto& attrib = attribOpt.value(); + + HashCombine(seed, bindingIndex); + HashCombine(seed, attrib.vertexBuffer); + HashCombine(seed, attrib.size); + HashCombine(seed, attrib.type); + HashCombine(seed, attrib.normalized); + HashCombine(seed, attrib.stride); + HashCombine(seed, attrib.pointer); + } + + bindingIndex++; + } + + return seed; + } +} + +#include diff --git a/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.hpp b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.hpp new file mode 100644 index 000000000..cc5d0d4af --- /dev/null +++ b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.hpp @@ -0,0 +1,51 @@ +// 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 + +#pragma once + +#ifndef NAZARA_OPENGLRENDERER_GLCONTEXTOBJECT_HPP +#define NAZARA_OPENGLRENDERER_GLCONTEXTOBJECT_HPP + +#include +#include +#include +#include + +namespace Nz::GL +{ + template + class ContextObject + { + public: + ContextObject() = default; + ContextObject(const ContextObject&) = delete; + ContextObject(ContextObject&& object) noexcept = default; + ~ContextObject(); + + bool Create(const Context& context, CreateArgs... args); + void Destroy(); + + bool IsValid() const; + + const Context* GetContext() const; + GLuint GetObjectId() const; + + void SetDebugName(const std::string_view& name); + + ContextObject& operator=(const ContextObject&) = delete; + ContextObject& operator=(ContextObject&& object) noexcept = default; + + static constexpr GLuint InvalidObject = 0; + + protected: + void EnsureContext(); + + MovablePtr m_context; + MovableValue m_objectId; + }; +} + +#include + +#endif diff --git a/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl new file mode 100644 index 000000000..a48a71a94 --- /dev/null +++ b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl @@ -0,0 +1,88 @@ +// 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 +#include +#include +#include + +namespace Nz::GL +{ + template + ContextObject::~ContextObject() + { + Destroy(); + } + + template + bool ContextObject::Create(const Context& context, CreateArgs... args) + { + Destroy(); + + m_context = &context; + + EnsureContext(); + + m_objectId = C::CreateHelper(*m_context, args...); + if (m_objectId == InvalidObject) + { + NazaraError("Failed to create OpenGL object"); //< TODO: Handle error message + return false; + } + + return true; + } + + template + void ContextObject::Destroy() + { + if (IsValid()) + { + EnsureContext(); + + C::DestroyHelper(*m_context, m_objectId); + m_objectId = InvalidObject; + } + } + + template + bool ContextObject::IsValid() const + { + return m_objectId != InvalidObject; + } + + template + const Context* ContextObject::GetContext() const + { + return m_context.Get(); + } + + template + GLuint ContextObject::GetObjectId() const + { + return m_objectId; + } + + template + void ContextObject::SetDebugName(const std::string_view& name) + { + EnsureContext(); + + if (m_context->glObjectLabel) + m_context->glObjectLabel(ObjectType, m_objectId, name.size(), name.data()); + } + + template + void ContextObject::EnsureContext() + { + const Context* activeContext = Context::GetCurrentContext(); + if (activeContext != m_context.Get()) + { + if (!Context::SetCurrentContext(m_context.Get())) + throw std::runtime_error("failed to activate context"); + } + } +} + +#include diff --git a/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp b/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp index df762b109..6adcee7e1 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp @@ -4,36 +4,36 @@ #pragma once -#ifndef NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP -#define NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP +#ifndef NAZARA_OPENGLRENDERER_GLVERTEXARRAY_HPP +#define NAZARA_OPENGLRENDERER_GLVERTEXARRAY_HPP #include -#include +#include -namespace Nz +namespace Nz::GL { - namespace Vk + class VertexArray : public ContextObject { - class ImageView : public DeviceObject - { - friend DeviceObject; + friend ContextObject; - public: - ImageView() = default; - ImageView(const ImageView&) = delete; - ImageView(ImageView&&) = default; - ~ImageView() = default; + public: + VertexArray(const VertexArray&) = delete; + VertexArray(VertexArray&&) = default; + ~VertexArray() = default; - ImageView& operator=(const ImageView&) = delete; - ImageView& operator=(ImageView&&) = delete; + VertexArray& operator=(const VertexArray&) = delete; + VertexArray& operator=(VertexArray&&) = delete; - private: - static inline VkResult CreateHelper(Device& device, const VkImageViewCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkImageView* handle); - static inline void DestroyHelper(Device& device, VkImageView handle, const VkAllocationCallbacks* allocator); - }; - } + template static VertexArray Build(const Context& context, F&& callback); + + private: + VertexArray() = default; + + static inline GLuint CreateHelper(const Context& context); + static inline void DestroyHelper(const Context& context, GLuint objectId); + }; } -#include +#include -#endif // NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP +#endif diff --git a/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.inl b/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.inl index 29ae8e968..91bbe698b 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/VertexArray.inl @@ -2,22 +2,39 @@ // This file is part of the "Nazara Engine - OpenGL Renderer" // For conditions of distribution and use, see copyright notice in Config.hpp -#include +#include +#include #include -namespace Nz +namespace Nz::GL { - namespace Vk + template + static VertexArray VertexArray::Build(const Context& context, F&& callback) { - inline VkResult ImageView::CreateHelper(Device& device, const VkImageViewCreateInfo* createInfo, const VkAllocationCallbacks* allocator, VkImageView* handle) - { - return device.vkCreateImageView(device, createInfo, allocator, handle); - } + VertexArray vao; + if (!vao.Create(context)) + throw std::runtime_error("failed to create vao"); - inline void ImageView::DestroyHelper(Device& device, VkImageView handle, const VkAllocationCallbacks* allocator) - { - return device.vkDestroyImageView(device, handle, allocator); - } + context.BindVertexArray(vao.GetObjectId(), true); + callback(); + context.BindVertexArray(0, true); + + return vao; + } + + inline GLuint VertexArray::CreateHelper(const Context& context) + { + GLuint vao = 0; + context.glGenVertexArrays(1U, &vao); + + return vao; + } + + inline void VertexArray::DestroyHelper(const Context& context, GLuint objectId) + { + context.glDeleteVertexArrays(1U, &objectId); + + context.NotifyVertexArrayDestruction(objectId); } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLVaoCache.cpp b/src/Nazara/OpenGLRenderer/OpenGLVaoCache.cpp new file mode 100644 index 000000000..8602d70c2 --- /dev/null +++ b/src/Nazara/OpenGLRenderer/OpenGLVaoCache.cpp @@ -0,0 +1,78 @@ +// 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 +#include +#include + +namespace Nz::GL +{ + OpenGLVaoCache::OpenGLVaoCache(Context& context) : + m_context(context) + { + } + + OpenGLVaoCache::~OpenGLVaoCache() = default; + + void OpenGLVaoCache::Clear() + { + m_vertexArrays.clear(); + } + + const VertexArray& OpenGLVaoCache::Get(const OpenGLVaoSetup& setup) const + { + auto it = m_vertexArrays.find(setup); + if (it == m_vertexArrays.end()) + { + VertexArray vao = VertexArray::Build(m_context, [&] + { + if (setup.indexBuffer != 0) + m_context.BindBuffer(BufferTarget::ElementArray, setup.indexBuffer, true); + + std::size_t bindingIndex = 0; + for (const auto& attribOpt : setup.vertexAttribs) + { + if (attribOpt) + { + const auto& attrib = attribOpt.value(); + + assert(attrib.vertexBuffer != 0); + m_context.BindBuffer(BufferTarget::Array, attrib.vertexBuffer, true); + + m_context.glEnableVertexAttribArray(bindingIndex); + m_context.glVertexAttribPointer(bindingIndex, attrib.size, attrib.type, attrib.normalized, attrib.stride, attrib.pointer); + } + + bindingIndex++; + } + }); + + it = m_vertexArrays.emplace(setup, std::make_unique(std::move(vao))).first; + } + + return *it->second; + } + + void OpenGLVaoCache::NotifyBufferDestruction(GLuint buffer) + { + for (auto it = m_vertexArrays.begin(); it != m_vertexArrays.end();) + { + const OpenGLVaoSetup& setup = it->first; + + // If a VAO is based on at least one of these buffers, delete it + auto FindVertexBuffer = [&](const auto& attribOpt) + { + if (!attribOpt) + return false; + + return attribOpt->vertexBuffer == buffer; + }; + + if (setup.indexBuffer == buffer || std::any_of(setup.vertexAttribs.begin(), setup.vertexAttribs.end(), FindVertexBuffer)) + it = m_vertexArrays.erase(it); + else + ++it; + } + } +} diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.cpp index 8f056bc62..1593cef1c 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Win32/WGLContext.cpp @@ -48,6 +48,7 @@ namespace Nz::GL { if (m_handle) { + OnContextRelease(); NotifyContextDestruction(this); m_loader.wglDeleteContext(m_handle);