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
|
||||
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
OnContextRelease();
|
||||
NotifyContextDestruction(this);
|
||||
|
||||
m_loader.wglDeleteContext(m_handle);
|
||||
|
|
|
|||
Loading…
Reference in New Issue