OpenGL: Implement VAOs
This commit is contained in:
parent
332278dded
commit
fe5b70ae1c
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -4,36 +4,36 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP
|
#ifndef NAZARA_OPENGLRENDERER_GLVERTEXARRAY_HPP
|
||||||
#define NAZARA_OPENGLRENDERER_VKIMAGEVIEW_HPP
|
#define NAZARA_OPENGLRENDERER_GLVERTEXARRAY_HPP
|
||||||
|
|
||||||
#include <Nazara/Prerequisites.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 ContextObject;
|
||||||
{
|
|
||||||
friend DeviceObject;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImageView() = default;
|
VertexArray(const VertexArray&) = delete;
|
||||||
ImageView(const ImageView&) = delete;
|
VertexArray(VertexArray&&) = default;
|
||||||
ImageView(ImageView&&) = default;
|
~VertexArray() = default;
|
||||||
~ImageView() = default;
|
|
||||||
|
|
||||||
ImageView& operator=(const ImageView&) = delete;
|
VertexArray& operator=(const VertexArray&) = delete;
|
||||||
ImageView& operator=(ImageView&&) = delete;
|
VertexArray& operator=(VertexArray&&) = delete;
|
||||||
|
|
||||||
private:
|
template<typename F> static VertexArray Build(const Context& context, F&& callback);
|
||||||
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);
|
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
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,39 @@
|
||||||
// This file is part of the "Nazara Engine - OpenGL Renderer"
|
// This file is part of the "Nazara Engine - OpenGL Renderer"
|
||||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
// 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>
|
#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)
|
VertexArray vao;
|
||||||
{
|
if (!vao.Create(context))
|
||||||
return device.vkCreateImageView(device, createInfo, allocator, handle);
|
throw std::runtime_error("failed to create vao");
|
||||||
}
|
|
||||||
|
|
||||||
inline void ImageView::DestroyHelper(Device& device, VkImageView handle, const VkAllocationCallbacks* allocator)
|
context.BindVertexArray(vao.GetObjectId(), true);
|
||||||
{
|
callback();
|
||||||
return device.vkDestroyImageView(device, handle, allocator);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,6 +48,7 @@ namespace Nz::GL
|
||||||
{
|
{
|
||||||
if (m_handle)
|
if (m_handle)
|
||||||
{
|
{
|
||||||
|
OnContextRelease();
|
||||||
NotifyContextDestruction(this);
|
NotifyContextDestruction(this);
|
||||||
|
|
||||||
m_loader.wglDeleteContext(m_handle);
|
m_loader.wglDeleteContext(m_handle);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue