aurora-rendering-engine/src/resource/resource_manager.cpp

668 lines
17 KiB
C++

#include "resource/resource_manager.h"
#include "utils/logger.h"
#include <glad/glad.h>
namespace are {
ResourceManager::ResourceManager()
: initialized_(false) {
}
ResourceManager::~ResourceManager() {
release();
}
ResourceManager &ResourceManager::instance() {
static ResourceManager manager;
return manager;
}
bool ResourceManager::initialize() {
if (initialized_) {
ARE_LOG_WARN("ResourceManager already initialized");
return true;
}
initialized_ = true;
ARE_LOG_INFO("ResourceManager initialized successfully");
return true;
}
void ResourceManager::release() {
if (!initialized_)
return;
ARE_LOG_INFO("Releasing ResourceManager resources...");
// Release all textures
for (auto &[handle, resource] : textures_) {
glDeleteTextures(1, &resource.gl_handle);
}
textures_.clear();
// Release all buffers
for (auto &[handle, resource] : buffers_) {
glDeleteBuffers(1, &resource.gl_handle);
}
buffers_.clear();
// Release all framebuffers and their attachments
for (auto &[handle, resource] : framebuffers_) {
// Attachments are owned by textures_ map, already deleted
glDeleteFramebuffers(1, &resource.gl_handle);
}
framebuffers_.clear();
initialized_ = false;
ARE_LOG_INFO("ResourceManager released");
}
// === Texture Management ===
TextureHandle ResourceManager::create_texture(const TextureDescription &desc) {
TextureHandle handle;
glGenTextures(1, &handle);
glBindTexture(GL_TEXTURE_2D, handle);
// Convert format to GL types
GLenum gl_internal_format = GL_RGBA8;
GLenum gl_format = GL_RGBA;
GLenum gl_type = GL_UNSIGNED_BYTE;
switch (desc.format) {
case TextureFormat::R8:
gl_internal_format = GL_R8;
gl_format = GL_RED;
gl_type = GL_UNSIGNED_BYTE;
break;
case TextureFormat::RG8:
gl_internal_format = GL_RG8;
gl_format = GL_RG;
gl_type = GL_UNSIGNED_BYTE;
break;
case TextureFormat::RGB8:
gl_internal_format = GL_RGB8;
gl_format = GL_RGB;
gl_type = GL_UNSIGNED_BYTE;
break;
case TextureFormat::RGBA8:
gl_internal_format = GL_RGBA8;
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
break;
case TextureFormat::RGBA32F:
gl_internal_format = GL_RGBA32F;
gl_format = GL_RGBA;
gl_type = GL_FLOAT;
break;
case TextureFormat::R32F:
gl_internal_format = GL_R32F;
gl_format = GL_RED;
gl_type = GL_FLOAT;
break;
case TextureFormat::RG32F:
gl_internal_format = GL_RG32F;
gl_format = GL_RG;
gl_type = GL_FLOAT;
break;
case TextureFormat::DEPTH24_STENCIL8:
gl_internal_format = GL_DEPTH24_STENCIL8;
gl_format = GL_DEPTH_STENCIL;
gl_type = GL_UNSIGNED_INT_24_8;
break;
default:
gl_internal_format = GL_RGBA8;
gl_format = GL_RGBA;
gl_type = GL_UNSIGNED_BYTE;
break;
}
glTexImage2D(GL_TEXTURE_2D, 0, gl_internal_format, desc.width, desc.height,
0, gl_format, gl_type, nullptr);
// Set filter
GLenum gl_min_filter = GL_LINEAR;
GLenum gl_mag_filter = GL_LINEAR;
switch (desc.filter) {
case TextureFilter::NEAREST:
gl_min_filter = GL_NEAREST;
gl_mag_filter = GL_NEAREST;
break;
case TextureFilter::LINEAR:
gl_min_filter = GL_LINEAR;
gl_mag_filter = GL_LINEAR;
break;
case TextureFilter::NEAREST_MIPMAP_NEAREST:
gl_min_filter = GL_NEAREST_MIPMAP_NEAREST;
gl_mag_filter = GL_NEAREST;
break;
case TextureFilter::LINEAR_MIPMAP_NEAREST:
gl_min_filter = GL_LINEAR_MIPMAP_NEAREST;
gl_mag_filter = GL_LINEAR;
break;
case TextureFilter::NEAREST_MIPMAP_LINEAR:
gl_min_filter = GL_NEAREST_MIPMAP_LINEAR;
gl_mag_filter = GL_NEAREST;
break;
case TextureFilter::LINEAR_MIPMAP_LINEAR:
gl_min_filter = GL_LINEAR_MIPMAP_LINEAR;
gl_mag_filter = GL_LINEAR;
break;
default:
gl_min_filter = GL_LINEAR;
gl_mag_filter = GL_LINEAR;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_min_filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_mag_filter);
// Set wrap
GLenum gl_wrap = GL_REPEAT;
switch (desc.wrap) {
case TextureWrap::REPEAT:
gl_wrap = GL_REPEAT;
break;
case TextureWrap::MIRRORED_REPEAT:
gl_wrap = GL_MIRRORED_REPEAT;
break;
case TextureWrap::CLAMP_TO_EDGE:
gl_wrap = GL_CLAMP_TO_EDGE;
break;
case TextureWrap::CLAMP_TO_BORDER:
gl_wrap = GL_CLAMP_TO_BORDER;
break;
default:
gl_wrap = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gl_wrap);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gl_wrap);
// Generate mipmaps if requested
if (desc.generate_mipmaps) {
glGenerateMipmap(GL_TEXTURE_2D);
}
glBindTexture(GL_TEXTURE_2D, 0);
TextureResource resource;
resource.gl_handle = handle;
resource.width = desc.width;
resource.height = desc.height;
resource.format = desc.format;
textures_[handle] = resource;
ARE_LOG_DEBUG("Texture created: " + std::to_string(handle));
return handle;
}
TextureHandle ResourceManager::create_texture(uint width, uint height, TextureFormat format) {
TextureDescription desc;
desc.width = width;
desc.height = height;
desc.format = format;
return create_texture(desc);
}
TextureHandle ResourceManager::create_texture(const std::string &path) {
// Use Texture class for file loading (stb_image)
auto tex = std::make_shared<Texture>();
if (!tex->load_from_file(path, true)) {
ARE_LOG_ERROR("Failed to load texture: " + path);
return INVALID_HANDLE;
}
TextureHandle handle = tex->get_handle();
// Register in manager (but don't own - Texture class manages lifetime)
TextureResource resource;
resource.gl_handle = handle;
resource.width = tex->get_width();
resource.height = tex->get_height();
resource.format = tex->get_format();
textures_[handle] = resource;
return handle;
}
void ResourceManager::destroy_texture(TextureHandle handle) {
auto it = textures_.find(handle);
if (it == textures_.end()) {
ARE_LOG_WARN("Attempting to destroy unknown texture: " + std::to_string(handle));
return;
}
glDeleteTextures(1, &it->second.gl_handle);
textures_.erase(it);
ARE_LOG_DEBUG("Texture destroyed: " + std::to_string(handle));
}
// === Buffer Management ===
BufferHandle ResourceManager::create_buffer(const BufferDescription &desc) {
BufferHandle handle;
glGenBuffers(1, &handle);
GLenum gl_type = GL_ARRAY_BUFFER;
switch (desc.type) {
case BufferType::VERTEX_BUFFER:
gl_type = GL_ARRAY_BUFFER;
break;
case BufferType::INDEX_BUFFER:
gl_type = GL_ELEMENT_ARRAY_BUFFER;
break;
case BufferType::UNIFORM_BUFFER:
gl_type = GL_UNIFORM_BUFFER;
break;
case BufferType::SHADER_STORAGE_BUFFER:
gl_type = GL_SHADER_STORAGE_BUFFER;
break;
default:
gl_type = GL_ARRAY_BUFFER;
break;
}
GLenum gl_usage = GL_STATIC_DRAW;
switch (desc.usage) {
case BufferUsage::STATIC_DRAW:
gl_usage = GL_STATIC_DRAW;
break;
case BufferUsage::DYNAMIC_DRAW:
gl_usage = GL_DYNAMIC_DRAW;
break;
case BufferUsage::STREAM_DRAW:
gl_usage = GL_STREAM_DRAW;
break;
default:
gl_usage = GL_STATIC_DRAW;
break;
}
glBindBuffer(gl_type, handle);
glBufferData(gl_type, desc.size, desc.data, gl_usage);
glBindBuffer(gl_type, 0);
BufferResource resource;
resource.gl_handle = handle;
resource.type = desc.type;
resource.usage = desc.usage;
resource.size = desc.size;
buffers_[handle] = resource;
ARE_LOG_DEBUG("Buffer created: " + std::to_string(handle) + " size: " + std::to_string(desc.size));
return handle;
}
void ResourceManager::update_buffer(BufferHandle handle, size_t offset, size_t size, const void *data) {
auto it = buffers_.find(handle);
if (it == buffers_.end()) {
ARE_LOG_ERROR("Attempting to update unknown buffer: " + std::to_string(handle));
return;
}
GLenum gl_type = GL_ARRAY_BUFFER;
switch (it->second.type) {
case BufferType::VERTEX_BUFFER:
gl_type = GL_ARRAY_BUFFER;
break;
case BufferType::INDEX_BUFFER:
gl_type = GL_ELEMENT_ARRAY_BUFFER;
break;
case BufferType::UNIFORM_BUFFER:
gl_type = GL_UNIFORM_BUFFER;
break;
case BufferType::SHADER_STORAGE_BUFFER:
gl_type = GL_SHADER_STORAGE_BUFFER;
break;
default:
gl_type = GL_ARRAY_BUFFER;
break;
}
glBindBuffer(gl_type, handle);
glBufferSubData(gl_type, offset, size, data);
glBindBuffer(gl_type, 0);
}
void ResourceManager::destroy_buffer(BufferHandle handle) {
auto it = buffers_.find(handle);
if (it == buffers_.end()) {
ARE_LOG_WARN("Attempting to destroy unknown buffer: " + std::to_string(handle));
return;
}
glDeleteBuffers(1, &it->second.gl_handle);
buffers_.erase(it);
ARE_LOG_DEBUG("Buffer destroyed: " + std::to_string(handle));
}
// === Framebuffer Management ===
FramebufferHandle ResourceManager::create_framebuffer(const FramebufferDescription &desc) {
FramebufferHandle fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
FramebufferResource resource;
resource.gl_handle = fbo;
resource.width = desc.width;
resource.height = desc.height;
// Create color attachments
for (uint i = 0; i < desc.color_attachment_count; ++i) {
TextureDescription tex_desc;
tex_desc.width = desc.width;
tex_desc.height = desc.height;
tex_desc.format = TextureFormat::RGBA32F;
tex_desc.filter = TextureFilter::NEAREST;
tex_desc.wrap = TextureWrap::CLAMP_TO_EDGE;
TextureHandle color_tex = create_texture(tex_desc);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
GL_TEXTURE_2D, color_tex, 0);
resource.color_attachments.push_back(color_tex);
}
// Create depth attachment if requested
if (desc.create_depth) {
TextureDescription depth_desc;
depth_desc.width = desc.width;
depth_desc.height = desc.height;
depth_desc.format = TextureFormat::DEPTH24_STENCIL8;
depth_desc.filter = TextureFilter::NEAREST;
depth_desc.wrap = TextureWrap::CLAMP_TO_EDGE;
TextureHandle depth_tex = create_texture(depth_desc);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, depth_tex, 0);
resource.depth_attachment = depth_tex;
}
// Set draw buffers if color attachments exist
if (desc.color_attachment_count > 0) {
std::vector<GLenum> draw_buffers(desc.color_attachment_count);
for (uint i = 0; i < desc.color_attachment_count; ++i) {
draw_buffers[i] = GL_COLOR_ATTACHMENT0 + i;
}
glDrawBuffers(desc.color_attachment_count, draw_buffers.data());
}
// Check completeness
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ARE_LOG_ERROR("Framebuffer is not complete!");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
destroy_framebuffer(fbo);
return INVALID_HANDLE;
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
framebuffers_[fbo] = resource;
ARE_LOG_DEBUG("Framebuffer created: " + std::to_string(fbo));
return fbo;
}
TextureHandle ResourceManager::get_framebuffer_color_attachment(FramebufferHandle fbo, uint index) {
auto it = framebuffers_.find(fbo);
if (it == framebuffers_.end()) {
ARE_LOG_ERROR("Invalid framebuffer handle: " + std::to_string(fbo));
return INVALID_HANDLE;
}
if (index >= it->second.color_attachments.size()) {
ARE_LOG_ERROR("Color attachment index out of range: " + std::to_string(index));
return INVALID_HANDLE;
}
return it->second.color_attachments[index];
}
TextureHandle ResourceManager::get_framebuffer_depth_attachment(FramebufferHandle fbo) {
auto it = framebuffers_.find(fbo);
if (it == framebuffers_.end()) {
ARE_LOG_ERROR("Invalid framebuffer handle: " + std::to_string(fbo));
return INVALID_HANDLE;
}
return it->second.depth_attachment;
}
void ResourceManager::destroy_framebuffer(FramebufferHandle fbo) {
auto it = framebuffers_.find(fbo);
if (it == framebuffers_.end()) {
ARE_LOG_WARN("Attempting to destroy unknown framebuffer: " + std::to_string(fbo));
return;
}
// Destroy color attachments
for (auto &color_tex : it->second.color_attachments) {
destroy_texture(color_tex);
}
// Destroy depth attachment
if (it->second.depth_attachment != INVALID_HANDLE) {
destroy_texture(it->second.depth_attachment);
}
glDeleteFramebuffers(1, &it->second.gl_handle);
framebuffers_.erase(it);
ARE_LOG_DEBUG("Framebuffer destroyed: " + std::to_string(fbo));
}
// === Texture Array Management ===
TextureHandle ResourceManager::create_texture_array(const TextureArrayDescription &desc) {
if (desc.textures.empty()) {
ARE_LOG_WARN("Creating empty texture array");
return INVALID_HANDLE;
}
// Get dimensions from first texture
int tex_width = desc.textures[0]->get_width();
int tex_height = desc.textures[0]->get_height();
// Create texture array
GLuint tex_array;
glGenTextures(1, &tex_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex_array);
// Allocate storage
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, tex_width, tex_height,
static_cast<int>(desc.textures.size()), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
// Copy each texture to array layer
for (size_t i = 0; i < desc.textures.size(); ++i) {
auto &tex = desc.textures[i];
GLuint tex_handle = tex->get_handle();
if (tex_handle == 0)
continue;
TextureFormat orig_format = tex->get_format();
int orig_width = tex->get_width();
int orig_height = tex->get_height();
// Get format for reading
GLenum orig_gl_format = GL_RGBA;
int channels = 4;
switch (orig_format) {
case TextureFormat::R8:
orig_gl_format = GL_RED;
channels = 1;
break;
case TextureFormat::RG8:
orig_gl_format = GL_RG;
channels = 2;
break;
case TextureFormat::RGB8:
orig_gl_format = GL_RGB;
channels = 3;
break;
case TextureFormat::RGBA8:
orig_gl_format = GL_RGBA;
channels = 4;
break;
default:
orig_gl_format = GL_RGBA;
channels = 4;
break;
}
// Read texture data
std::vector<uint8_t> orig_pixels(orig_width * orig_height * channels);
glBindTexture(GL_TEXTURE_2D, tex_handle);
glGetTexImage(GL_TEXTURE_2D, 0, orig_gl_format, GL_UNSIGNED_BYTE, orig_pixels.data());
glBindTexture(GL_TEXTURE_2D, 0);
// Convert to RGBA
std::vector<uint8_t> pixels(orig_width * orig_height * 4, 255);
for (int y = 0; y < orig_height; ++y) {
for (int x = 0; x < orig_width; ++x) {
int src_idx = (y * orig_width + x) * channels;
int dst_idx = (y * orig_width + x) * 4;
pixels[dst_idx + 0] = orig_pixels[src_idx + 0];
pixels[dst_idx + 1] = (channels >= 2) ? orig_pixels[src_idx + 1] : orig_pixels[src_idx + 0];
pixels[dst_idx + 2] = (channels >= 3) ? orig_pixels[src_idx + 2] : orig_pixels[src_idx + 0];
pixels[dst_idx + 3] = (channels >= 4) ? orig_pixels[src_idx + 3] : 255;
}
}
// Upload to array layer
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, static_cast<int>(i),
tex_width, tex_height, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
}
// Set filter
GLenum gl_min_filter = GL_LINEAR;
GLenum gl_mag_filter = GL_LINEAR;
switch (desc.filter) {
case TextureFilter::NEAREST:
gl_min_filter = GL_NEAREST;
gl_mag_filter = GL_NEAREST;
break;
case TextureFilter::LINEAR:
gl_min_filter = GL_LINEAR;
gl_mag_filter = GL_LINEAR;
break;
default:
gl_min_filter = GL_LINEAR;
gl_mag_filter = GL_LINEAR;
break;
}
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, gl_min_filter);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, gl_mag_filter);
// Set wrap
GLenum gl_wrap = GL_REPEAT;
switch (desc.wrap) {
case TextureWrap::REPEAT:
gl_wrap = GL_REPEAT;
break;
case TextureWrap::CLAMP_TO_EDGE:
gl_wrap = GL_CLAMP_TO_EDGE;
break;
default:
gl_wrap = GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, gl_wrap);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, gl_wrap);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
TextureResource resource;
resource.gl_handle = tex_array;
resource.width = tex_width;
resource.height = tex_height;
resource.format = TextureFormat::RGBA8;
textures_[tex_array] = resource;
ARE_LOG_DEBUG("Texture array created: " + std::to_string(tex_array) + " layers: " + std::to_string(desc.textures.size()));
return tex_array;
}
void ResourceManager::destroy_texture_array(TextureHandle handle) {
destroy_texture(handle);
}
// === VAO Management ===
VertexArrayHandle ResourceManager::create_vertex_array() {
VertexArrayHandle vao;
glGenVertexArrays(1, &vao);
ARE_LOG_DEBUG("Vertex array created: " + std::to_string(vao));
return vao;
}
void ResourceManager::destroy_vertex_array(VertexArrayHandle vao) {
glDeleteVertexArrays(1, &vao);
ARE_LOG_DEBUG("Vertex array destroyed: " + std::to_string(vao));
}
// === Binding Management ===
void ResourceManager::bind_buffer(BufferHandle buffer, uint binding_point) {
auto it = buffers_.find(buffer);
if (it == buffers_.end()) {
ARE_LOG_ERROR("Attempting to bind unknown buffer: " + std::to_string(buffer));
return;
}
GLenum gl_type = GL_ARRAY_BUFFER;
switch (it->second.type) {
case BufferType::UNIFORM_BUFFER:
gl_type = GL_UNIFORM_BUFFER;
break;
case BufferType::SHADER_STORAGE_BUFFER:
gl_type = GL_SHADER_STORAGE_BUFFER;
break;
default:
ARE_LOG_ERROR("Buffer type not supported for bind_buffer");
return;
}
glBindBufferBase(gl_type, binding_point, buffer);
}
void ResourceManager::bind_image_texture(TextureHandle texture, uint binding, bool read, bool write) {
GLenum access = GL_READ_ONLY;
if (read && write) {
access = GL_READ_WRITE;
} else if (write) {
access = GL_WRITE_ONLY;
}
glBindImageTexture(binding, texture, 0, GL_FALSE, 0, access, GL_RGBA32F);
}
void ResourceManager::bind_texture_to_unit(TextureHandle texture, uint unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D, texture);
}
// === Query ===
bool ResourceManager::is_texture_valid(TextureHandle handle) const {
return textures_.find(handle) != textures_.end();
}
bool ResourceManager::is_buffer_valid(BufferHandle handle) const {
return buffers_.find(handle) != buffers_.end();
}
bool ResourceManager::is_framebuffer_valid(FramebufferHandle handle) const {
return framebuffers_.find(handle) != framebuffers_.end();
}
} // namespace are