OpenGL: Implement VAOs

This commit is contained in:
Lynix 2020-05-11 14:10:36 +02:00
parent 332278dded
commit fe5b70ae1c
8 changed files with 422 additions and 33 deletions

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/OpenGLRenderer/Config.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp>
#include <array>
#include <memory>
#include <optional>
#include <unordered_map>
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<std::optional<Attribs>, 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<OpenGLVaoSetup, std::unique_ptr<VertexArray>, OpenGLVaoSetupHasher> m_vertexArrays;
Context& m_context;
};
}
#include <Nazara/OpenGLRenderer/OpenGLVaoCache.inl>
#endif

View File

@ -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 <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
#include <Nazara/Core/Algorithm.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
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 <Nazara/OpenGLRenderer/DebugOff.hpp>

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/Core/MovableValue.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
#include <string>
namespace Nz::GL
{
template<typename C, GLenum ObjectType, typename... CreateArgs>
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<const Context> m_context;
MovableValue<GLuint> m_objectId;
};
}
#include <Nazara/OpenGLRenderer/Wrapper/ContextObject.inl>
#endif

View File

@ -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 <Nazara/OpenGLRenderer/Wrapper/ContextObject.hpp>
#include <Nazara/Core/Error.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz::GL
{
template<typename C, GLenum ObjectType, typename... CreateArgs>
ContextObject<C, ObjectType, CreateArgs...>::~ContextObject()
{
Destroy();
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
bool ContextObject<C, ObjectType, CreateArgs...>::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<typename C, GLenum ObjectType, typename... CreateArgs>
void ContextObject<C, ObjectType, CreateArgs...>::Destroy()
{
if (IsValid())
{
EnsureContext();
C::DestroyHelper(*m_context, m_objectId);
m_objectId = InvalidObject;
}
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
bool ContextObject<C, ObjectType, CreateArgs...>::IsValid() const
{
return m_objectId != InvalidObject;
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
const Context* ContextObject<C, ObjectType, CreateArgs...>::GetContext() const
{
return m_context.Get();
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
GLuint ContextObject<C, ObjectType, CreateArgs...>::GetObjectId() const
{
return m_objectId;
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
void ContextObject<C, ObjectType, CreateArgs...>::SetDebugName(const std::string_view& name)
{
EnsureContext();
if (m_context->glObjectLabel)
m_context->glObjectLabel(ObjectType, m_objectId, name.size(), name.data());
}
template<typename C, GLenum ObjectType, typename... CreateArgs>
void ContextObject<C, ObjectType, CreateArgs...>::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 <Nazara/OpenGLRenderer/DebugOff.hpp>

View File

@ -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 <Nazara/Prerequisites.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/DeviceObject.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/ContextObject.hpp>
namespace Nz
namespace Nz::GL
{
namespace Vk
class VertexArray : public ContextObject<VertexArray, GL_VERTEX_ARRAY>
{
class ImageView : public DeviceObject<ImageView, VkImageView, VkImageViewCreateInfo, VK_OBJECT_TYPE_IMAGE_VIEW>
{
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<typename F> 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 <Nazara/OpenGLRenderer/Wrapper/ImageView.inl>
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.inl>
#endif // NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP
#endif

View File

@ -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 <Nazara/OpenGLRenderer/Wrapper/ImageView.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
namespace Nz::GL
{
namespace Vk
template<typename F>
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);
}
}

View File

@ -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 <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp>
#include <Nazara/OpenGLRenderer/Debug.hpp>
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<VertexArray>(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;
}
}
}

View File

@ -48,6 +48,7 @@ namespace Nz::GL
{
if (m_handle)
{
OnContextRelease();
NotifyContextDestruction(this);
m_loader.wglDeleteContext(m_handle);