Render+Scene: Implement uniformbuffer objects
This commit is contained in:
+10
-12
@@ -23,6 +23,7 @@
|
||||
#include "inferno/render/buffer.h"
|
||||
#include "inferno/render/context.h"
|
||||
#include "inferno/render/framebuffer.h"
|
||||
#include "inferno/render/uniformbuffer.h"
|
||||
// #include "inferno/render/gltf.h"
|
||||
#include "inferno/asset/shader.h"
|
||||
#include "inferno/asset/texture.h"
|
||||
@@ -67,6 +68,13 @@ Application::Application()
|
||||
.clearBit = GL_COLOR_BUFFER_BIT,
|
||||
});
|
||||
|
||||
Uniformbuffer::the().setLayout(
|
||||
"Camera", 0,
|
||||
{
|
||||
{ BufferElementType::Mat4, "u_projectionView" },
|
||||
});
|
||||
Uniformbuffer::the().create("Camera");
|
||||
|
||||
m_scene = std::make_shared<Scene>();
|
||||
m_scene->initialize();
|
||||
|
||||
@@ -90,6 +98,7 @@ Application::Application()
|
||||
Application::~Application()
|
||||
{
|
||||
m_scene->destroy();
|
||||
Uniformbuffer::destroy();
|
||||
|
||||
RendererFont::destroy();
|
||||
Renderer2D::destroy();
|
||||
@@ -189,20 +198,9 @@ int Application::run()
|
||||
|
||||
render();
|
||||
|
||||
std::pair<glm::mat4, glm::mat4> projectionView = m_scene->cameraProjectionView();
|
||||
RendererCubemap::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
||||
Renderer3D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
||||
Renderer2D::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
||||
RendererFont::the().beginScene(projectionView.first, projectionView.second); // camera, lights, environment
|
||||
|
||||
m_scene->render();
|
||||
// RendererCharacter::the().drawCharacter(character, f->texture());
|
||||
|
||||
RendererCubemap::the().endScene();
|
||||
Renderer3D::the().endScene();
|
||||
Renderer2D::the().endScene();
|
||||
RendererFont::the().endScene();
|
||||
|
||||
m_framebuffer->unbind();
|
||||
|
||||
// ---------------------------------
|
||||
@@ -214,7 +212,7 @@ int Application::run()
|
||||
RenderCommand::clearBit(m_screenFramebuffer->clearBit());
|
||||
|
||||
Renderer2D::the().setEnableDepthBuffer(false);
|
||||
Renderer2D::the().beginScene(matIdentity, matIdentity);
|
||||
Uniformbuffer::the().setFloat("Camera", "u_projectionView", matIdentity);
|
||||
Renderer2D::the().drawQuad(transformIdentity, vectorOne, m_framebuffer->texture(0));
|
||||
Renderer2D::the().endScene();
|
||||
Renderer2D::the().setEnableDepthBuffer(true);
|
||||
|
||||
@@ -50,16 +50,18 @@ Shader::~Shader()
|
||||
}
|
||||
}
|
||||
|
||||
int32_t Shader::findUniformLocation(std::string_view name)
|
||||
// -----------------------------------------
|
||||
|
||||
uint32_t Shader::findUniformLocation(std::string_view name)
|
||||
{
|
||||
// Cache uniform locations, prevent going to the GPU every call
|
||||
if (m_uniformLocation.find(name) != m_uniformLocation.end()) {
|
||||
return m_uniformLocation[name];
|
||||
if (m_uniformLocations.find(name) != m_uniformLocations.end()) {
|
||||
return m_uniformLocations[name];
|
||||
}
|
||||
|
||||
int32_t location = glGetUniformLocation(m_id, name.data());
|
||||
VERIFY(location != -1, "Shader could not find uniform '{}'", name);
|
||||
m_uniformLocation[name] = location;
|
||||
m_uniformLocations[name] = static_cast<uint32_t>(location);
|
||||
|
||||
return location;
|
||||
}
|
||||
@@ -128,6 +130,8 @@ void Shader::unbind() const
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
uint32_t Shader::compileShader(int32_t type, const char* source) const
|
||||
{
|
||||
// Create new shader
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
// Factory function
|
||||
static std::shared_ptr<Shader> create(std::string_view path);
|
||||
|
||||
int32_t findUniformLocation(std::string_view name);
|
||||
uint32_t findUniformLocation(std::string_view name);
|
||||
|
||||
void setInt(std::string_view name, int value);
|
||||
void setInt(std::string_view name, int* values, uint32_t count);
|
||||
@@ -53,7 +53,7 @@ private:
|
||||
|
||||
private:
|
||||
uint32_t m_id { 0 };
|
||||
std::unordered_map<std::string_view, int32_t> m_uniformLocation;
|
||||
std::unordered_map<std::string_view, uint32_t> m_uniformLocations;
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint8_t, uint32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string>
|
||||
#include <utility> // std::pair
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include "ruc/meta/assert.h"
|
||||
|
||||
@@ -61,10 +67,10 @@ uint32_t BufferElement::getTypeSize(const BufferElementType type)
|
||||
case BufferElementType::Vec3:
|
||||
case BufferElementType::Vec4:
|
||||
return sizeof(float) * getTypeCount(type);
|
||||
case BufferElementType::VecDouble:
|
||||
case BufferElementType::VecDouble2:
|
||||
case BufferElementType::VecDouble3:
|
||||
case BufferElementType::VecDouble4:
|
||||
case BufferElementType::Double:
|
||||
case BufferElementType::Vec2Double:
|
||||
case BufferElementType::Vec3Double:
|
||||
case BufferElementType::Vec4Double:
|
||||
return sizeof(double) * getTypeCount(type);
|
||||
case BufferElementType::Mat2:
|
||||
case BufferElementType::Mat3:
|
||||
@@ -89,25 +95,25 @@ uint32_t BufferElement::getTypeCount(const BufferElementType type)
|
||||
case BufferElementType::Int:
|
||||
case BufferElementType::Uint:
|
||||
case BufferElementType::Float:
|
||||
case BufferElementType::VecDouble:
|
||||
case BufferElementType::Double:
|
||||
return 1;
|
||||
case BufferElementType::Bool2:
|
||||
case BufferElementType::Int2:
|
||||
case BufferElementType::Uint2:
|
||||
case BufferElementType::Vec2:
|
||||
case BufferElementType::VecDouble2:
|
||||
case BufferElementType::Vec2Double:
|
||||
return 2;
|
||||
case BufferElementType::Bool3:
|
||||
case BufferElementType::Int3:
|
||||
case BufferElementType::Uint3:
|
||||
case BufferElementType::Vec3:
|
||||
case BufferElementType::VecDouble3:
|
||||
case BufferElementType::Vec3Double:
|
||||
return 3;
|
||||
case BufferElementType::Bool4:
|
||||
case BufferElementType::Int4:
|
||||
case BufferElementType::Uint4:
|
||||
case BufferElementType::Vec4:
|
||||
case BufferElementType::VecDouble4:
|
||||
case BufferElementType::Vec4Double:
|
||||
return 4;
|
||||
case BufferElementType::Mat2:
|
||||
return 2 * 2;
|
||||
@@ -152,10 +158,10 @@ uint32_t BufferElement::getTypeGL(const BufferElementType type)
|
||||
case BufferElementType::Vec3:
|
||||
case BufferElementType::Vec4:
|
||||
return GL_FLOAT;
|
||||
case BufferElementType::VecDouble:
|
||||
case BufferElementType::VecDouble2:
|
||||
case BufferElementType::VecDouble3:
|
||||
case BufferElementType::VecDouble4:
|
||||
case BufferElementType::Double:
|
||||
case BufferElementType::Vec2Double:
|
||||
case BufferElementType::Vec3Double:
|
||||
case BufferElementType::Vec4Double:
|
||||
return GL_DOUBLE;
|
||||
case BufferElementType::Mat2:
|
||||
case BufferElementType::Mat3:
|
||||
@@ -184,7 +190,7 @@ void BufferLayout::calculateOffsetsAndStride()
|
||||
m_stride = 0;
|
||||
for (auto& element : m_elements) {
|
||||
element.setOffset(m_stride);
|
||||
m_stride += element.getSize();
|
||||
m_stride += element.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +208,7 @@ VertexBuffer::VertexBuffer(size_t size, float* vertices)
|
||||
bind();
|
||||
|
||||
// Upload data to the GPU
|
||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_DYNAMIC_DRAW);
|
||||
|
||||
unbind();
|
||||
}
|
||||
@@ -242,7 +248,7 @@ IndexBuffer::IndexBuffer(uint32_t* indices, size_t size)
|
||||
bind();
|
||||
|
||||
// Upload data to the GPU
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, indices, GL_DYNAMIC_DRAW);
|
||||
|
||||
unbind();
|
||||
}
|
||||
@@ -298,7 +304,7 @@ void VertexArray::unbind() const
|
||||
void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||
{
|
||||
const auto& layout = vertexBuffer->getLayout();
|
||||
VERIFY(layout.getElements().size(), "VertexBuffer has no layout");
|
||||
VERIFY(layout.elements().size(), "VertexBuffer has no layout");
|
||||
|
||||
bind();
|
||||
vertexBuffer->bind();
|
||||
@@ -306,7 +312,7 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||
uint32_t index = 0;
|
||||
for (const auto& element : layout) {
|
||||
glEnableVertexAttribArray(index);
|
||||
switch (element.getType()) {
|
||||
switch (element.type()) {
|
||||
case BufferElementType::None:
|
||||
break;
|
||||
case BufferElementType::Int:
|
||||
@@ -321,8 +327,8 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||
index,
|
||||
element.getTypeCount(),
|
||||
element.getTypeGL(),
|
||||
layout.getStride(),
|
||||
reinterpret_cast<const void*>(element.getOffset()));
|
||||
layout.stride(),
|
||||
reinterpret_cast<const void*>(element.offset()));
|
||||
break;
|
||||
}
|
||||
case BufferElementType::Bool:
|
||||
@@ -340,15 +346,15 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||
index,
|
||||
element.getTypeCount(),
|
||||
element.getTypeGL(),
|
||||
element.getNormalized() ? GL_TRUE : GL_FALSE,
|
||||
layout.getStride(),
|
||||
reinterpret_cast<const void*>(element.getOffset()));
|
||||
element.normalized() ? GL_TRUE : GL_FALSE,
|
||||
layout.stride(),
|
||||
reinterpret_cast<const void*>(element.offset()));
|
||||
break;
|
||||
}
|
||||
case BufferElementType::VecDouble:
|
||||
case BufferElementType::VecDouble2:
|
||||
case BufferElementType::VecDouble3:
|
||||
case BufferElementType::VecDouble4:
|
||||
case BufferElementType::Double:
|
||||
case BufferElementType::Vec2Double:
|
||||
case BufferElementType::Vec3Double:
|
||||
case BufferElementType::Vec4Double:
|
||||
case BufferElementType::MatDouble2:
|
||||
case BufferElementType::MatDouble3:
|
||||
case BufferElementType::MatDouble4: {
|
||||
@@ -356,8 +362,8 @@ void VertexArray::addVertexBuffer(std::shared_ptr<VertexBuffer> vertexBuffer)
|
||||
index,
|
||||
element.getTypeCount(),
|
||||
element.getTypeGL(),
|
||||
layout.getStride(),
|
||||
reinterpret_cast<const void*>(element.getOffset()));
|
||||
layout.stride(),
|
||||
reinterpret_cast<const void*>(element.offset()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
+13
-12
@@ -7,10 +7,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint32_t
|
||||
#include <cstdint> // int32_t, uint8_t, uint32_t
|
||||
#include <memory> // std::shared_ptr
|
||||
#include <string> // std::string
|
||||
#include <vector> // std::vector
|
||||
#include <string>
|
||||
#include <utility> // std::pair
|
||||
#include <vector>
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
@@ -22,7 +23,7 @@ enum class BufferElementType {
|
||||
Int, Int2, Int3, Int4, // ivec
|
||||
Uint, Uint2, Uint3, Uint4, // uvec
|
||||
Float, Vec2, Vec3, Vec4, // vec
|
||||
VecDouble, VecDouble2, VecDouble3, VecDouble4, // dvec
|
||||
Double, Vec2Double, Vec3Double, Vec4Double, // dvec
|
||||
Mat2, Mat3, Mat4, // mat
|
||||
MatDouble2, MatDouble3, MatDouble4, // dmat
|
||||
};
|
||||
@@ -43,11 +44,11 @@ public:
|
||||
static uint32_t getTypeCount(const BufferElementType type);
|
||||
static uint32_t getTypeGL(const BufferElementType type);
|
||||
|
||||
BufferElementType getType() const { return m_type; }
|
||||
std::string getName() const { return m_name; }
|
||||
uint32_t getSize() const { return m_size; }
|
||||
uint32_t getOffset() const { return m_offset; }
|
||||
bool getNormalized() const { return m_normalized; }
|
||||
BufferElementType type() const { return m_type; }
|
||||
std::string name() const { return m_name; }
|
||||
uint32_t size() const { return m_size; }
|
||||
uint32_t offset() const { return m_offset; }
|
||||
bool normalized() const { return m_normalized; }
|
||||
|
||||
void setType(const BufferElementType& type) { m_type = type; }
|
||||
void setName(const std::string& name) { m_name = name; }
|
||||
@@ -72,8 +73,8 @@ public:
|
||||
BufferLayout(const std::initializer_list<BufferElement>& elements);
|
||||
~BufferLayout() = default;
|
||||
|
||||
const std::vector<BufferElement>& getElements() const { return m_elements; }
|
||||
uint32_t getStride() const { return m_stride; }
|
||||
const std::vector<BufferElement>& elements() const { return m_elements; }
|
||||
uint32_t stride() const { return m_stride; }
|
||||
|
||||
// Iterators
|
||||
std::vector<BufferElement>::iterator begin() { return m_elements.begin(); }
|
||||
@@ -105,7 +106,7 @@ public:
|
||||
|
||||
const BufferLayout& getLayout() const { return m_layout; }
|
||||
|
||||
inline void setLayout(const BufferLayout& layout) { m_layout = layout; }
|
||||
void setLayout(const BufferLayout& layout) { m_layout = layout; }
|
||||
|
||||
private:
|
||||
uint32_t m_id { 0 };
|
||||
|
||||
@@ -228,13 +228,6 @@ Renderer2D::Renderer2D(s)
|
||||
ruc::info("Renderer2D initialized");
|
||||
}
|
||||
|
||||
void Renderer2D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
|
||||
{
|
||||
m_shader->bind();
|
||||
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
|
||||
m_shader->unbind();
|
||||
}
|
||||
|
||||
void Renderer2D::drawQuad(const TransformComponent& transform, glm::vec4 color)
|
||||
{
|
||||
drawQuad(transform, color, nullptr);
|
||||
@@ -536,13 +529,6 @@ void Renderer3D::drawModel(std::span<const Vertex> vertices, std::span<const uin
|
||||
m_elementIndex += elements.size();
|
||||
}
|
||||
|
||||
void Renderer3D::beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView)
|
||||
{
|
||||
m_shader->bind();
|
||||
m_shader->setFloat("u_projectionView", cameraProjection * cameraView);
|
||||
m_shader->unbind();
|
||||
}
|
||||
|
||||
void Renderer3D::createElementBuffer()
|
||||
{
|
||||
// ---------------------------------
|
||||
|
||||
@@ -132,8 +132,6 @@ public:
|
||||
|
||||
using Singleton<Renderer2D>::destroy;
|
||||
|
||||
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
|
||||
|
||||
void drawQuad(const TransformComponent& transform, glm::vec4 color);
|
||||
void drawQuad(const TransformComponent& transform, glm::mat4 color);
|
||||
void drawQuad(const TransformComponent& transform, glm::vec4 color, std::shared_ptr<Texture> texture);
|
||||
@@ -199,8 +197,6 @@ public:
|
||||
|
||||
using Singleton<Renderer3D>::destroy;
|
||||
|
||||
virtual void beginScene(glm::mat4 cameraProjection, glm::mat4 cameraView) override;
|
||||
|
||||
void drawModel(std::span<const Vertex> vertices, std::span<const uint32_t> indices, const TransformComponent& transform, std::shared_ptr<Texture> texture);
|
||||
|
||||
private:
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // int32_t, uint8_t
|
||||
#include <string_view>
|
||||
|
||||
#include "glad/glad.h"
|
||||
#include "glm/gtc/type_ptr.hpp" // glm::value_ptr
|
||||
#include "ruc/meta/assert.h"
|
||||
|
||||
#include "inferno/render/buffer.h"
|
||||
#include "inferno/render/uniformbuffer.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
Uniformbuffer::Uniformbuffer(s)
|
||||
{
|
||||
// Get maximum uniformbuffer bindings the GPU supports
|
||||
int32_t maxBindingPoints = 0;
|
||||
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxBindingPoints);
|
||||
m_maxBindingPoints = static_cast<uint8_t>(maxBindingPoints);
|
||||
}
|
||||
|
||||
Uniformbuffer::~Uniformbuffer()
|
||||
{
|
||||
for (const auto& [_, block] : m_blocks) {
|
||||
glDeleteBuffers(1, &block.id);
|
||||
}
|
||||
m_blocks.clear();
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
void Uniformbuffer::setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout)
|
||||
{
|
||||
VERIFY(bindingPoint < m_maxBindingPoints, "{} < {}", bindingPoint, m_maxBindingPoints);
|
||||
|
||||
if (!exists(blockName)) {
|
||||
m_blocks[blockName] = {};
|
||||
}
|
||||
|
||||
UniformbufferBlock& block = m_blocks[blockName];
|
||||
|
||||
// Example block layout:
|
||||
// - mat3
|
||||
// - float
|
||||
// - vec2
|
||||
// - vec2
|
||||
// - float
|
||||
|
||||
// Chunks, 4 slots, 4 bytes per slot
|
||||
// [x][x][x][ ] #1
|
||||
// [x][x][x][ ] #2
|
||||
// [x][x][x][ ] #3
|
||||
// [x][ ][x][x] #4
|
||||
// [x][x][x][ ] #5
|
||||
|
||||
size_t chunk = 0;
|
||||
uint8_t offset = 0;
|
||||
|
||||
for (auto it = layout.begin(); it != layout.end(); ++it) {
|
||||
BufferElementType type = it->type();
|
||||
const std::string& name = it->name();
|
||||
|
||||
// Calculate offset
|
||||
switch (type) {
|
||||
// Scalar 1
|
||||
case BufferElementType::Bool:
|
||||
case BufferElementType::Int:
|
||||
case BufferElementType::Uint:
|
||||
case BufferElementType::Float: {
|
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
|
||||
// Jump
|
||||
offset += 1;
|
||||
break;
|
||||
}
|
||||
// Scalar 2
|
||||
case BufferElementType::Bool2:
|
||||
case BufferElementType::Int2:
|
||||
case BufferElementType::Uint2:
|
||||
case BufferElementType::Vec2: {
|
||||
// Add padding
|
||||
if (offset == 1) {
|
||||
offset++;
|
||||
}
|
||||
if (offset == 3) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
|
||||
// Jump
|
||||
offset += 2;
|
||||
break;
|
||||
}
|
||||
// Scalar 3
|
||||
case BufferElementType::Bool3:
|
||||
case BufferElementType::Int3:
|
||||
case BufferElementType::Uint3:
|
||||
case BufferElementType::Vec3: {
|
||||
// Add padding
|
||||
if (offset != 0) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
|
||||
// Jump
|
||||
offset += 3;
|
||||
break;
|
||||
}
|
||||
// Scalar 4
|
||||
case BufferElementType::Bool4:
|
||||
case BufferElementType::Int4:
|
||||
case BufferElementType::Uint4:
|
||||
case BufferElementType::Vec4: {
|
||||
// Add padding
|
||||
if (offset != 0) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
|
||||
// Jump
|
||||
offset += 4;
|
||||
break;
|
||||
}
|
||||
// Array types
|
||||
case BufferElementType::Mat2:
|
||||
case BufferElementType::Mat3:
|
||||
case BufferElementType::Mat4: {
|
||||
// Add padding
|
||||
if (offset != 0) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
// Offset
|
||||
block.uniformLocations[name] = (chunk * 16) + (offset * 4);
|
||||
|
||||
// Additional rows
|
||||
if (type == BufferElementType::Mat2) {
|
||||
chunk += 1;
|
||||
}
|
||||
else if (type == BufferElementType::Mat3) {
|
||||
chunk += 2;
|
||||
}
|
||||
else {
|
||||
chunk += 3;
|
||||
}
|
||||
|
||||
// Jump
|
||||
offset += 4;
|
||||
break;
|
||||
}
|
||||
// TODO: Implement these types
|
||||
case BufferElementType::Double:
|
||||
case BufferElementType::Vec2Double:
|
||||
case BufferElementType::Vec3Double:
|
||||
case BufferElementType::Vec4Double:
|
||||
case BufferElementType::MatDouble2:
|
||||
case BufferElementType::MatDouble3:
|
||||
case BufferElementType::MatDouble4:
|
||||
VERIFY_NOT_REACHED();
|
||||
case BufferElementType::None:
|
||||
VERIFY_NOT_REACHED();
|
||||
};
|
||||
|
||||
// Overflow slots to next chunk
|
||||
if (offset > 3) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad the end of the buffer
|
||||
if (offset != 0) {
|
||||
offset = 0;
|
||||
chunk++;
|
||||
}
|
||||
|
||||
block.size = chunk * 16;
|
||||
}
|
||||
|
||||
void Uniformbuffer::create(std::string_view blockName)
|
||||
{
|
||||
VERIFY(exists(blockName), "uniformbuffer block doesnt exist");
|
||||
|
||||
UniformbufferBlock& block = m_blocks[blockName];
|
||||
|
||||
if (block.id != 0) {
|
||||
glDeleteBuffers(1, &block.id);
|
||||
}
|
||||
|
||||
// Allocate buffer
|
||||
block.id = UINT_MAX;
|
||||
glGenBuffers(1, &block.id);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, block.id);
|
||||
glBufferData(GL_UNIFORM_BUFFER, block.size, NULL, GL_DYNAMIC_DRAW);
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
|
||||
// Bind buffer to binding point
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, block.bindingPoint, block.id);
|
||||
}
|
||||
|
||||
void Uniformbuffer::setFloat(std::string_view blockName, std::string_view member, glm::mat4 matrix)
|
||||
{
|
||||
VERIFY(exists(blockName), "uniformbuffer block doesnt exist");
|
||||
|
||||
const UniformbufferBlock& block = m_blocks[blockName];
|
||||
|
||||
VERIFY(block.uniformLocations.find(member.data()) != block.uniformLocations.end(),
|
||||
"uniformbuffer block member doesnt exist");
|
||||
|
||||
// Note: Uniformbuffers are bound to a binding point for the entire pipeline,
|
||||
// it remains accessible without needing to rebind it every time
|
||||
glBufferSubData(GL_UNIFORM_BUFFER, block.uniformLocations.at(member.data()), sizeof(glm::mat4), glm::value_ptr(matrix));
|
||||
}
|
||||
|
||||
} // namespace Inferno
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Riyyi
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstdint> // uint8_t, uint32_t
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "glm/ext/matrix_float4x4.hpp" // glm::mat4
|
||||
#include "ruc/singleton.h"
|
||||
|
||||
#include "inferno/render/buffer.h"
|
||||
|
||||
namespace Inferno {
|
||||
|
||||
struct UniformbufferBlock {
|
||||
uint32_t id { 0 };
|
||||
uint32_t size { 0 };
|
||||
uint8_t bindingPoint { 0 };
|
||||
std::unordered_map<std::string, uint32_t> uniformLocations {};
|
||||
};
|
||||
|
||||
class Uniformbuffer final : public ruc::Singleton<Uniformbuffer> { // Uniform Buffer Object, UBO
|
||||
public:
|
||||
Uniformbuffer(s);
|
||||
~Uniformbuffer();
|
||||
|
||||
void setLayout(std::string_view blockName, uint8_t bindingPoint, const BufferLayout& layout);
|
||||
void create(std::string_view blockName);
|
||||
|
||||
void setFloat(std::string_view blockName, std::string_view member, glm::mat4 matrix);
|
||||
|
||||
bool exists(std::string_view blockName) const { return m_blocks.find(blockName) != m_blocks.end(); }
|
||||
|
||||
private:
|
||||
uint8_t m_maxBindingPoints { 0 };
|
||||
std::unordered_map<std::string_view, UniformbufferBlock> m_blocks;
|
||||
};
|
||||
|
||||
} // namespace Inferno
|
||||
|
||||
#if 0
|
||||
|
||||
// -----------------------------------------
|
||||
// Example usage:
|
||||
|
||||
Uniformbuffer::the().setLayout(
|
||||
"ExampleBlock", 0,
|
||||
{
|
||||
{ BufferElementType::Mat3, "a" },
|
||||
{ BufferElementType::Float, "b" },
|
||||
{ BufferElementType::Vec2, "c" },
|
||||
{ BufferElementType::Vec2, "d" },
|
||||
{ BufferElementType::Float, "e" },
|
||||
});
|
||||
Uniformbuffer::the().create("ExampleBlock");
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------
|
||||
// Memory alignment of uniform blocks using std140
|
||||
//
|
||||
// Main points:
|
||||
// - Memory is organized into chunks.
|
||||
// - A block is at least the size of 1 chunk.
|
||||
// - One chunk has 4 slots, 4 bytes per slot.
|
||||
// - Can't fit? Move to next chunk.
|
||||
//
|
||||
// The rules:
|
||||
// 1. Scalar (bool, int, uint, float) takes up 1 slot, can appear after anything
|
||||
// 2. Vec2 takes up 2 slots, in first or last half of a chunk
|
||||
// 3. Vec3 takes up 3 slots, only at the start of a chunk
|
||||
// 4. Everything else:
|
||||
// - Take up maxumum room
|
||||
// - As often as needed
|
||||
// - Add padding as needed
|
||||
// 5. Mat3 (or any matrix) are treated like arrays
|
||||
// 6. Each member of *any* array gets its own chunk
|
||||
|
||||
// TODO: How do double types work?
|
||||
|
||||
// -----------------------------------------
|
||||
// References:
|
||||
// - https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL
|
||||
// - https://www.khronos.org/opengl/wiki/Interface_Block_(GLSL)#Memory_layout
|
||||
// - https://www.oreilly.com/library/view/opengl-programming-guide/9780132748445/app09lev1sec2.html (The std140 Layout Rules)
|
||||
// - https://www.youtube.com/watch?v=JPvbRko9lBg (WebGL 2: Uniform Buffer Objects)
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "inferno/component/tagcomponent.h"
|
||||
#include "inferno/component/textareacomponent.h"
|
||||
#include "inferno/component/transformcomponent.h"
|
||||
#include "inferno/render/uniformbuffer.h"
|
||||
#include "inferno/scene/scene.h"
|
||||
#include "inferno/script/nativescript.h"
|
||||
#include "inferno/system/camerasystem.h"
|
||||
@@ -83,8 +84,17 @@ void Scene::update(float deltaTime)
|
||||
|
||||
void Scene::render()
|
||||
{
|
||||
auto [projection, view] = CameraSystem::the().projectionView();
|
||||
Uniformbuffer::the().setFloat("Camera", "u_projectionView", projection * view);
|
||||
RendererCubemap::the().beginScene(projection, view); // camera, lights, environment
|
||||
|
||||
RenderSystem::the().render();
|
||||
TextAreaSystem::the().render();
|
||||
|
||||
RendererCubemap::the().endScene();
|
||||
Renderer3D::the().endScene();
|
||||
Renderer2D::the().endScene();
|
||||
RendererFont::the().endScene();
|
||||
}
|
||||
|
||||
void Scene::destroy()
|
||||
|
||||
Reference in New Issue
Block a user