Files
MobileGL/MobileGL/MG_Backend/DirectGLES/Managers.cpp
T

479 lines
23 KiB
C++

#include "Managers.h"
#include "Utils.h"
#include "DirectGLES.h"
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
#include <MG_Util/Converters/MGToGL/DataTypeConverter.h>
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
#include <MG_Util/Converters/MGToGL/FramebufferEnumConverter.h>
#include <MG_State/GLState/FramebufferState/FramebufferObject.h>
namespace MobileGL::MG_Backend::DirectGLES {
namespace BufferImpl {
BackendBufferObject::BackendBufferObject() {
MG_External::GLES::glGenBuffers(1, &m_backendBufferId);
if (m_backendBufferId == 0) {
MGLOG_E("Failed to generate buffer object.");
} else {
MGLOG_D("Generated buffer object with ID: %u.", m_backendBufferId);
}
}
const GLenum TempBufferTarget = GL_ARRAY_BUFFER;
void BackendBufferObject::SyncToBackend(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
if (!stateBufferObject) {
MGLOG_E("State buffer object is null, cannot sync to backend.");
return;
}
SizeT bufferSize = stateBufferObject->GetSize();
if (bufferSize == 0) {
MGLOG_W("Buffer size is zero, skipping sync for object with ID: %u", m_backendBufferId);
return;
}
MGLOG_D("Syncing buffer object with ID: %u to backend for state: %s", m_backendBufferId,
stateBufferObject.get());
Bool needsRegeneration =
!m_isInitialized || bufferSize > m_prevBufferSize || bufferSize > m_prevBufferSize * 2;
if (needsRegeneration) {
MGLOG_D("Buffer size changed significantly or not initialized, regenerating buffer with ID: %u",
m_backendBufferId);
SyncToBackend_glBufferData(stateBufferObject);
m_isInitialized = true;
return;
}
switch (stateBufferObject->GetUsage()) {
case BufferUsage::StaticDraw:
SyncToBackend_glBufferSubData(stateBufferObject);
break;
case BufferUsage::DynamicDraw:
case BufferUsage::StreamDraw:
SyncToBackend_glMapBufferRange(stateBufferObject);
break;
default:
SyncToBackend_glBufferSubData(stateBufferObject);
break;
}
m_prevBufferSize = bufferSize;
}
void BackendBufferObject::SyncToBackend_glBufferData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget);
MGLOG_D("Syncing buffer data (glBufferData) for object with ID : %u", m_backendBufferId);
const void* data = stateBufferObject->GetDataReadOnly().get();
SizeT size = stateBufferObject->GetSize();
GLenum usage = MG_Util::ConvertBufferUsageToGLEnum(stateBufferObject->GetUsage());
MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId);
MG_External::GLES::glBufferData(TempBufferTarget, size, data, usage);
stateBufferObject->ClearDirty();
}
void BackendBufferObject::SyncToBackend_glBufferSubData(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject) {
BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget);
MGLOG_D("Syncing buffer sub-data (glBufferSubData) for object with ID : %u", m_backendBufferId);
const void* data = stateBufferObject->GetDataReadOnly().get();
auto range = stateBufferObject->GetDirtyRange();
// dirty range: [range.start, range.end)
MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId);
MG_External::GLES::glBufferSubData(TempBufferTarget, range.start, range.end - range.start,
reinterpret_cast<const char*>(data) + range.start);
}
void BackendBufferObject::SyncToBackend_glMapBufferRange(
SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject, Bool invalidate) {
BackendBufferBindingProtector backendBufferBindingProtector(TempBufferTarget);
MGLOG_D("Syncing buffer map (glMapBuffer) for object with ID : %u", m_backendBufferId);
MGLOG_D("Mapping buffer with ID: %u", m_backendBufferId);
MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId);
auto range = stateBufferObject->GetDirtyRange();
void* mappedData = MG_External::GLES::glMapBufferRange(
TempBufferTarget, range.start, range.end - range.start,
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : 0) | GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
const void* data = stateBufferObject->GetDataReadOnly().get();
if (mappedData) {
Memcpy(reinterpret_cast<const void*>(reinterpret_cast<const char*>(data) + range.start), mappedData,
range.end - range.start);
MGLOG_D("Mapped buffer data successfully for object with ID: %u", m_backendBufferId);
MG_External::GLES::glUnmapBuffer(TempBufferTarget);
} else {
MGLOG_E("Failed to map buffer with ID: %u", m_backendBufferId);
}
}
void BackendBufferObject::Bind() {
MG_External::GLES::glBindBuffer(TempBufferTarget, m_backendBufferId);
}
void BackendBufferObject::Bind(GLenum target) {
MG_External::GLES::glBindBuffer(target, m_backendBufferId);
}
UnorderedMap<SharedPtr<MG_State::GLState::BufferObject>, SharedPtr<BackendBufferObject>> g_backendBufferObjects;
} // namespace BufferImpl
namespace VertexArrayImpl {
BackendVertexArrayObject::BackendVertexArrayObject() {
MG_External::GLES::glGenVertexArrays(1, &m_backendVAOId);
if (m_backendVAOId == 0) {
MGLOG_E("Failed to generate vertex array object.");
} else {
MGLOG_D("Generated vertex array object with ID: %u.", m_backendVAOId);
}
}
void BackendVertexArrayObject::Bind() {
MG_External::GLES::glBindVertexArray(m_backendVAOId);
}
void BackendVertexArrayObject::SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject) {
if (!stateVAOObject) {
MGLOG_E("State VAO object is null, cannot sync to backend.");
return;
}
MGLOG_D("Syncing VAO object with ID: %u to backend for state: %s", m_backendVAOId, stateVAOObject.get());
BufferImpl::BackendBufferBindingProtector backendBufferBindingProtector(BufferImpl::TempBufferTarget);
BackendVertexArrayBindingProtector backendVAOBindingProtector;
Bind();
for (const auto& attribIndex : stateVAOObject->GetDirtyAttributeIndices()) {
const auto& attrib = stateVAOObject->GetAttribute(attribIndex);
auto bufferObject = attrib.Buffer;
if (!bufferObject) {
MGLOG_W("Attribute has no bound buffer, skipping.");
continue;
}
auto backendBufferIt = BufferImpl::g_backendBufferObjects.find(bufferObject);
if (backendBufferIt == BufferImpl::g_backendBufferObjects.end()) {
MGLOG_E("No backend buffer found for attribute's buffer, cannot bind attribute.");
continue;
}
auto backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ARRAY_BUFFER);
MG_External::GLES::glEnableVertexAttribArray(attribIndex);
MG_External::GLES::glVertexAttribPointer(
attribIndex, attrib.Size, MG_Util::ConvertDataTypeToGLEnum(attrib.Type),
attrib.Normalized ? GL_TRUE : GL_FALSE, attrib.Stride,
reinterpret_cast<const void*>(static_cast<SizeT>(attrib.Offset)));
// TODO: divisor
}
auto indexBufferBinding = stateVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
if (indexBufferBinding) {
auto backendBufferIt = BufferImpl::g_backendBufferObjects.find(indexBufferBinding);
if (backendBufferIt != BufferImpl::g_backendBufferObjects.end()) {
auto backendBufferObject = backendBufferIt->second;
backendBufferObject->Bind(GL_ELEMENT_ARRAY_BUFFER);
} else {
MGLOG_E("No backend buffer found for index buffer binding, cannot bind index buffer.");
}
}
stateVAOObject->ClearDirtyAttributes();
}
UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>>
g_backendVertexArrayObjects;
} // namespace VertexArrayImpl
namespace TextureImpl {
BackendTextureObject::BackendTextureObject() {
MG_External::GLES::glGenTextures(1, &m_backendTextureId);
if (m_backendTextureId == 0) {
MGLOG_E("Failed to generate texture object.");
} else {
MGLOG_D("Generated texture object with ID: %u.", m_backendTextureId);
}
}
void BackendTextureObject::Bind(GLenum target) {
MG_External::GLES::glBindTexture(target, m_backendTextureId);
}
Uint BackendTextureObject::GetBackendTextureId() {
return m_backendTextureId;
}
void BackendTextureObject::SyncToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject) {
if (!stateTextureObject) {
MGLOG_E("State texture object is null, cannot sync to backend.");
return;
}
MGLOG_D("Syncing texture object with ID: %u to backend for state: %s", m_backendTextureId,
stateTextureObject.get());
GLenum target = MG_Util::ConvertTextureTargetToGLEnum(stateTextureObject->GetTarget());
// The texture needs to be regenerated completely with glTexImage* calls if:
// 1. Not initialized
// 2. InternalFormat changed
// 3. Size changed
// 4. Mipmap levels changed
if (!stateTextureObject->IsComplete()) {
MGLOG_E("Texture object with ID: %u is not complete, skipping sync.", m_backendTextureId);
return;
}
BackendTextureBindingProtector backendTextureBindingProtector(target);
Bind(target);
StateTextureBasicInfo currentTextureInfo = {
stateTextureObject->GetFormat(), static_cast<SizeT>(stateTextureObject->GetBaseSize().x()),
static_cast<SizeT>(stateTextureObject->GetBaseSize().y()),
static_cast<SizeT>(stateTextureObject->GetBaseSize().z()), stateTextureObject->GetMipmaps().size()};
Bool needsRegeneration = !m_isInitialized || (currentTextureInfo != m_prevTextureInfo);
if (needsRegeneration) {
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u",
m_backendTextureId);
// Regenerate all mipmap levels
const auto& mipmaps = stateTextureObject->GetMipmaps();
GLenum glInternalFormat, glType, glFormat;
TextureImpl::GenerateTextureFormatInfo(stateTextureObject->GetFormat(), &glInternalFormat, &glType,
&glFormat);
for (SizeT level = 0; level < mipmaps.size(); ++level) {
const auto& mipmap = mipmaps[level];
MG_External::GLES::glTexImage2D(GL_TEXTURE_2D, static_cast<GLint>(level), glInternalFormat,
static_cast<GLsizei>(mipmap.size.x()),
static_cast<GLsizei>(mipmap.size.y()), 0, glFormat, glType,
mipmap.data.data());
MGLOG_D("Regenerated mipmap level %d for texture with ID: %u", level, m_backendTextureId);
stateTextureObject->UnmarkMipmapDirty(level);
}
m_isInitialized = true;
}
{ // Update sampler parameters; TODO: always use sampler objects in backend
auto samplerObject = stateTextureObject->GetSamplerObject();
if (samplerObject) {
MG_External::GLES::glTexParameteri(
target, GL_TEXTURE_MIN_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObject->GetMinFilter()));
MG_External::GLES::glTexParameteri(
target, GL_TEXTURE_MAG_FILTER,
MG_Util::ConvertSamplerFilterModeToGLEnum(samplerObject->GetMagFilter()));
MG_External::GLES::glTexParameteri(
target, GL_TEXTURE_WRAP_S, MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObject->GetWrapS()));
MG_External::GLES::glTexParameteri(
target, GL_TEXTURE_WRAP_T, MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObject->GetWrapT()));
MG_External::GLES::glTexParameteri(
target, GL_TEXTURE_WRAP_R, MG_Util::ConvertSamplerWrapModeToGLEnum(samplerObject->GetWrapR()));
}
}
{ // Update all dirty mipmap levels
const auto& mipmaps = stateTextureObject->GetMipmaps();
GLenum glInternalFormat, glType, glFormat;
TextureImpl::GenerateTextureFormatInfo(stateTextureObject->GetFormat(), &glInternalFormat, &glType,
&glFormat);
for (const auto& mipmap : stateTextureObject->GetMipmaps()) {
if (!mipmap.dirty) {
continue;
}
if (mipmap.data.empty()) {
MGLOG_W("Mipmap level %d has no data, skipping update.", mipmap.level);
continue;
}
MG_External::GLES::glTexSubImage2D(
GL_TEXTURE_2D, static_cast<GLint>(mipmap.level), 0, 0, static_cast<GLsizei>(mipmap.size.x()),
static_cast<GLsizei>(mipmap.size.y()), glFormat, glType, mipmap.data.data());
stateTextureObject->UnmarkMipmapDirty(mipmap.level);
}
}
m_prevTextureInfo = currentTextureInfo;
}
UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>>
g_backendTextureObjects;
} // namespace TextureImpl
namespace FramebufferImpl {
BackendFramebufferObject::BackendFramebufferObject() {
MG_External::GLES::glGenFramebuffers(1, &m_backendFBOId);
if (m_backendFBOId == 0) {
MGLOG_E("Failed to generate framebuffer object.");
} else {
MGLOG_D("Generated framebuffer object with ID: %u.", m_backendFBOId);
}
}
void BackendFramebufferObject::Bind() {
MG_External::GLES::glBindFramebuffer(GL_FRAMEBUFFER, m_backendFBOId);
}
void BackendFramebufferObject::SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject) {
if (!stateFBOObject) {
MGLOG_E("State FBO object is null, cannot sync to backend.");
return;
}
MGLOG_D("Syncing FBO object with ID: %u to backend for state: %s", m_backendFBOId, stateFBOObject.get());
BackendFramebufferBindingProtector backendFBOBindingProtector(GL_FRAMEBUFFER);
Bind();
// TODO: add dirty check
// Sync all attachments
const auto& attachments = stateFBOObject->GetAllAttachments();
for (SizeT index = 0; index < attachments.size(); ++index) {
FramebufferAttachmentType attachmentType = static_cast<FramebufferAttachmentType>(index);
const auto& attachment = attachments[index];
if (!attachment.IsComplete()) {
MG_External::GLES::glFramebufferRenderbuffer(
GL_FRAMEBUFFER, MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(attachmentType),
GL_RENDERBUFFER, 0);
continue;
}
if (attachment.IsTexture()) {
auto textureObject = attachment.GetTexture();
auto backendTextureIt = TextureImpl::g_backendTextureObjects.find(textureObject);
if (backendTextureIt == TextureImpl::g_backendTextureObjects.end()) {
MGLOG_E("No backend texture found for FBO attachment, cannot bind texture.");
continue;
}
auto backendTextureObject = backendTextureIt->second;
backendTextureObject->Bind(MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()));
MG_External::GLES::glFramebufferTexture2D(
GL_FRAMEBUFFER, MG_Util::ConvertFramebufferAttachmentTypeToGLEnum(attachmentType),
MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()),
backendTextureObject->GetBackendTextureId(), static_cast<GLint>(attachment.GetTextureLevel()));
} else if (attachment.IsRenderbuffer()) {
// TODO
}
}
}
UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>>
g_backendFramebufferObjects;
} // namespace FramebufferImpl
namespace PrgramImpl {
UnorderedMap<SharedPtr<MG_State::GLState::ShaderObject>, SharedPtr<BackendShaderObjectImpl>>
g_backendShaderObjects;
UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>>
g_backendProgramObjects;
BackendShaderObjectImpl::BackendShaderObjectImpl() {
m_backendShaderId = MG_External::GLES::glCreateShader(GL_VERTEX_SHADER);
if (m_backendShaderId == 0) {
MGLOG_E("Failed to create shader object in backend.");
}
}
BackendShaderObjectImpl::~BackendShaderObjectImpl() {
if (m_backendShaderId != 0) {
MG_External::GLES::glDeleteShader(m_backendShaderId);
}
}
void BackendShaderObjectImpl::SyncToBackend(SharedPtr<MG_State::GLState::ShaderObject>& stateShaderObject) {
if (!stateShaderObject || !stateShaderObject->GetCompileStatus()) {
MGLOG_E("Shader object is null or not compiled, skipping backend sync.");
return;
}
String source = stateShaderObject->GetShaderSource();
source = removeLayoutBinding(source);
source = ProcessOutColorLocations(source);
source = ForceSupporterOutput(source);
GLenum shaderType = ConvertGLShaderTypeByMGLShaderStage(stateShaderObject->GetShaderStage());
const char* sourceCStr = source.c_str();
MG_External::GLES::glShaderSource(m_backendShaderId, 1, &sourceCStr, nullptr);
MG_External::GLES::glCompileShader(m_backendShaderId);
GLint compileStatus;
MG_External::GLES::glGetShaderiv(m_backendShaderId, GL_COMPILE_STATUS, &compileStatus);
if (compileStatus != GL_TRUE) {
GLint logLength;
MG_External::GLES::glGetShaderiv(m_backendShaderId, GL_INFO_LOG_LENGTH, &logLength);
Vector<GLchar> log(logLength);
MG_External::GLES::glGetShaderInfoLog(m_backendShaderId, logLength, nullptr, log.data());
MGLOG_E("Shader compilation failed: %s", log.data());
}
m_isInitialized = true;
}
BackendProgramObjectImpl::BackendProgramObjectImpl() {
m_backendProgramId = MG_External::GLES::glCreateProgram();
if (m_backendProgramId == 0) {
MGLOG_E("Failed to create program object in backend.");
}
}
BackendProgramObjectImpl::~BackendProgramObjectImpl() {
if (m_backendProgramId != 0) {
MG_External::GLES::glDeleteProgram(m_backendProgramId);
}
}
void BackendProgramObjectImpl::SyncToBackend(SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject) {
if (!stateProgramObject || !stateProgramObject->GetLinkStatus()) {
MGLOG_E("Program object is null or not linked, skipping backend sync.");
return;
}
// Detach all existing shaders
GLint attachedCount = 0;
MG_External::GLES::glGetProgramiv(m_backendProgramId, GL_ATTACHED_SHADERS, &attachedCount);
if (attachedCount > 0) {
Vector<GLuint> attachedShaders(attachedCount);
GLsizei actualCount;
MG_External::GLES::glGetAttachedShaders(m_backendProgramId, attachedCount, &actualCount,
attachedShaders.data());
for (GLsizei i = 0; i < actualCount; ++i) {
MG_External::GLES::glDetachShader(m_backendProgramId, attachedShaders[i]);
}
}
// Attach current shaders
for (auto& shader : stateProgramObject->GetAttachedShaders()) {
auto it = g_backendShaderObjects.find(shader);
if (it != g_backendShaderObjects.end() && it->second) {
MG_External::GLES::glAttachShader(m_backendProgramId, it->second->GetBackendShaderId());
}
}
// Link program
MG_External::GLES::glLinkProgram(m_backendProgramId);
GLint linkStatus;
MG_External::GLES::glGetProgramiv(m_backendProgramId, GL_LINK_STATUS, &linkStatus);
if (linkStatus != GL_TRUE) {
GLint logLength;
MG_External::GLES::glGetProgramiv(m_backendProgramId, GL_INFO_LOG_LENGTH, &logLength);
Vector<GLchar> log(logLength);
MG_External::GLES::glGetProgramInfoLog(m_backendProgramId, logLength, nullptr, log.data());
MGLOG_E("Program linking failed: %s", log.data());
}
m_isInitialized = true;
}
void BackendProgramObjectImpl::Use() {
MG_External::GLES::glUseProgram(m_backendProgramId);
}
} // namespace PrgramImpl
namespace Utils {} // namespace Utils
} // namespace MobileGL::MG_Backend::DirectGLES