mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
[Feat] (MG_Backend/DirectGLES): Implement basic stuff for DirectGLES backend.
This commit is contained in:
@@ -84,6 +84,10 @@ set(SOURCE_FILES
|
||||
|
||||
MobileGL/MG_Backend/Init.cpp
|
||||
|
||||
MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp
|
||||
MobileGL/MG_Backend/DirectGLES/Utils.cpp
|
||||
MobileGL/MG_Backend/DirectGLES/Managers.cpp
|
||||
|
||||
MobileGL/MG_State/GLState/Core.cpp
|
||||
MobileGL/MG_State/GLState/ErrorState/Error.cpp
|
||||
MobileGL/MG_State/GLState/BufferState/BufferState.cpp
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
#include "DirectGLES.h"
|
||||
#include "Utils.h"
|
||||
#include "Managers.h"
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/RenderStateEnumConverter.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
// TODO: deletion of deleted objects
|
||||
|
||||
namespace BufferImpl {
|
||||
void SyncNeccessaryBuffers() {
|
||||
// All buffers we need are:
|
||||
// 1.VBOs 2.IBO 3.UBOs (TODO) 4.SSBOs (TODO)
|
||||
Vector<SharedPtr<MG_State::GLState::BufferObject>> buffersToSync;
|
||||
const auto& currentVAOObject = MG_State::pGLContext->GetBoundVertexArray();
|
||||
if (!currentVAOObject) {
|
||||
MGLOG_E("No VAO is currently bound, cannot sync necessary buffers.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& attrib : currentVAOObject->GetAllAttributes()) {
|
||||
const auto& bufferObject = attrib.Buffer;
|
||||
if (bufferObject) {
|
||||
buffersToSync.push_back(bufferObject);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& possibleIBO = currentVAOObject->GetIndexBufferBindingSlot().GetBoundObject();
|
||||
if (possibleIBO) {
|
||||
buffersToSync.push_back(possibleIBO);
|
||||
}
|
||||
|
||||
// Do real sync
|
||||
for (auto& bufferObject : buffersToSync) {
|
||||
const auto& backendBufferIt = g_backendBufferObjects.find(bufferObject);
|
||||
SharedPtr<BackendBufferObject> backendBufferObject;
|
||||
if (backendBufferIt != g_backendBufferObjects.end()) {
|
||||
backendBufferObject = MakeShared<BackendBufferObject>();
|
||||
g_backendBufferObjects[bufferObject] = backendBufferObject;
|
||||
} else {
|
||||
backendBufferObject = backendBufferIt->second;
|
||||
}
|
||||
backendBufferObject->SyncToBackend(bufferObject);
|
||||
}
|
||||
}
|
||||
} // namespace BufferImpl
|
||||
|
||||
namespace VertexArrayImpl {
|
||||
void SyncCurrentVAO() {
|
||||
auto currentVAOObject = MG_State::pGLContext->GetBoundVertexArray();
|
||||
if (!currentVAOObject) {
|
||||
MGLOG_E("No VAO is currently bound, cannot sync current VAO.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& backendVAOIt = g_backendVertexArrayObjects.find(currentVAOObject);
|
||||
SharedPtr<VertexArrayImpl::BackendVertexArrayObject> backendVAOObject;
|
||||
if (backendVAOIt == g_backendVertexArrayObjects.end()) {
|
||||
backendVAOObject = MakeShared<VertexArrayImpl::BackendVertexArrayObject>();
|
||||
g_backendVertexArrayObjects[currentVAOObject] = backendVAOObject;
|
||||
} else {
|
||||
backendVAOObject = backendVAOIt->second;
|
||||
}
|
||||
backendVAOObject->SyncToBackend(currentVAOObject);
|
||||
}
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
void SyncNeccessaryTextures() {
|
||||
// All textures we need are:
|
||||
// 1. textures bound to current texture units (TODO: only sync ones that are used in current program)
|
||||
// 2. textures used in current FBO
|
||||
// 3. textures bound to image units (TODO)
|
||||
|
||||
Vector<SharedPtr<MG_State::GLState::ITextureObject>> texturesToSync;
|
||||
|
||||
auto& currentUnit =
|
||||
MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||
for (auto& bindingSlot : currentUnit.GetAllBindingSlots()) {
|
||||
const auto& textureObject = bindingSlot.GetBoundObject();
|
||||
if (textureObject) {
|
||||
texturesToSync.push_back(textureObject);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& currentFBO =
|
||||
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
if (currentFBO) {
|
||||
for (auto& attachment : currentFBO->GetAllAttachments()) {
|
||||
if (!attachment.IsTexture()) continue;
|
||||
const auto& textureObject = attachment.GetTexture();
|
||||
if (textureObject) {
|
||||
texturesToSync.push_back(textureObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do real sync
|
||||
for (auto& textureObject : texturesToSync) {
|
||||
const auto& backendTextureIt = g_backendTextureObjects.find(textureObject);
|
||||
SharedPtr<BackendTextureObject> backendTextureObject;
|
||||
if (backendTextureIt == g_backendTextureObjects.end()) {
|
||||
backendTextureObject = MakeShared<BackendTextureObject>();
|
||||
g_backendTextureObjects[textureObject] = backendTextureObject;
|
||||
} else {
|
||||
backendTextureObject = backendTextureIt->second;
|
||||
}
|
||||
backendTextureObject->SyncToBackend(textureObject);
|
||||
}
|
||||
}
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
void SyncCurrentFBO() {
|
||||
auto currentFBO = MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Draw).GetBoundObject();
|
||||
if (!currentFBO) {
|
||||
MGLOG_E("No FBO is currently bound, cannot sync current FBO.");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& backendFBOIt = g_backendFramebufferObjects.find(currentFBO);
|
||||
SharedPtr<BackendFramebufferObject> backendFBOObject;
|
||||
if (backendFBOIt == g_backendFramebufferObjects.end()) {
|
||||
backendFBOObject = MakeShared<BackendFramebufferObject>();
|
||||
g_backendFramebufferObjects[currentFBO] = backendFBOObject;
|
||||
} else {
|
||||
backendFBOObject = backendFBOIt->second;
|
||||
}
|
||||
backendFBOObject->SyncToBackend(currentFBO);
|
||||
}
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
namespace RenderStateImpl {
|
||||
void SyncRenderState() {
|
||||
/*
|
||||
void SetViewport(IntVec4 viewport); // x, y, width, height
|
||||
const IntVec4& GetViewport() const; // x, y, width, height
|
||||
void SetCapability(CapabilityInput cap, Bool enabled);
|
||||
Bool IsCapabilityEnabled(CapabilityInput cap) const;
|
||||
void SetBlendFunc(BlendFactor srcRGB, BlendFactor dstRGB, BlendFactor srcAlpha, BlendFactor dstAlpha);
|
||||
void GetBlendFunc(BlendFactor& srcRGB, BlendFactor& dstRGB, BlendFactor& srcAlpha,
|
||||
BlendFactor& dstAlpha) const;
|
||||
void SetDepthFunc(DepthTestFunc func);
|
||||
DepthTestFunc GetDepthFunc() const;
|
||||
void SetDepthMask(Bool flag);
|
||||
Bool GetDepthMask() const;
|
||||
void SetColorMask(BoolVec4 mask);
|
||||
const BoolVec4 GetColorMask() const;
|
||||
void SetClearColor(FloatVec4 color);
|
||||
const FloatVec4& GetClearColor() const;
|
||||
void SetClearDepth(Float depth);
|
||||
Float GetClearDepth() const;
|
||||
void SetPixelStoreParam(PixelStoreParam param, Int value);
|
||||
Int GetPixelStoreParam(PixelStoreParam param) const;
|
||||
void SetCullFaceMode(CullFaceMode mode);
|
||||
CullFaceMode GetCullFaceMode() const;*/
|
||||
|
||||
MG_External::GLES::glViewport(
|
||||
MG_State::pGLContext->GetViewport().x(), MG_State::pGLContext->GetViewport().y(),
|
||||
MG_State::pGLContext->GetViewport().z(), MG_State::pGLContext->GetViewport().w());
|
||||
if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::DepthTest)) {
|
||||
MG_External::GLES::glEnable(GL_DEPTH_TEST);
|
||||
} else {
|
||||
MG_External::GLES::glDisable(GL_DEPTH_TEST);
|
||||
}
|
||||
if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::Blend)) {
|
||||
MG_External::GLES::glEnable(GL_BLEND);
|
||||
} else {
|
||||
MG_External::GLES::glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
auto ToGLBoolean = [](Bool b) -> GLboolean { return b ? GL_TRUE : GL_FALSE; };
|
||||
|
||||
{
|
||||
BlendFactor srcRGB, dstRGB, srcAlpha, dstAlpha;
|
||||
MG_State::pGLContext->GetBlendFunc(srcRGB, dstRGB, srcAlpha, dstAlpha);
|
||||
|
||||
MG_External::GLES::glBlendFuncSeparate(
|
||||
MG_Util::ConvertBlendFactorToGLEnum(srcRGB), MG_Util::ConvertBlendFactorToGLEnum(dstRGB),
|
||||
MG_Util::ConvertBlendFactorToGLEnum(srcAlpha), MG_Util::ConvertBlendFactorToGLEnum(dstAlpha));
|
||||
}
|
||||
|
||||
{
|
||||
DepthTestFunc df = MG_State::pGLContext->GetDepthFunc();
|
||||
MG_External::GLES::glDepthFunc(MG_Util::ConvertDepthTestFuncToGLEnum(df));
|
||||
|
||||
MG_External::GLES::glDepthMask(MG_State::pGLContext->GetDepthMask() ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
{
|
||||
BoolVec4 colorMask = MG_State::pGLContext->GetColorMask();
|
||||
MG_External::GLES::glColorMask(ToGLBoolean(colorMask.x()), ToGLBoolean(colorMask.y()),
|
||||
ToGLBoolean(colorMask.z()), ToGLBoolean(colorMask.w()));
|
||||
}
|
||||
|
||||
{
|
||||
const FloatVec4& clearCol = MG_State::pGLContext->GetClearColor();
|
||||
MG_External::GLES::glClearColor(clearCol.x(), clearCol.y(), clearCol.z(), clearCol.w());
|
||||
MG_External::GLES::glClearDepthf(MG_State::pGLContext->GetClearDepth());
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
PixelStoreParam p = PixelStoreParam::UnpackAlignment;
|
||||
GLint v = MG_State::pGLContext->GetPixelStoreParam(p);
|
||||
MG_External::GLES::glPixelStorei(MG_Util::ConvertPixelStoreParamToGLEnum(p), v);
|
||||
}
|
||||
{
|
||||
PixelStoreParam p = PixelStoreParam::PackAlignment;
|
||||
GLint v = MG_State::pGLContext->GetPixelStoreParam(p);
|
||||
MG_External::GLES::glPixelStorei(MG_Util::ConvertPixelStoreParamToGLEnum(p), v);
|
||||
}
|
||||
}
|
||||
|
||||
if (MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::CullFace)) {
|
||||
MG_External::GLES::glEnable(GL_CULL_FACE);
|
||||
} else {
|
||||
MG_External::GLES::glDisable(GL_CULL_FACE);
|
||||
}
|
||||
|
||||
{
|
||||
CullFaceMode cfm = MG_State::pGLContext->GetCullFaceMode();
|
||||
MG_External::GLES::glCullFace(MG_Util::ConvertCullFaceModeToGLEnum(cfm));
|
||||
}
|
||||
}
|
||||
} // namespace RenderStateImpl
|
||||
|
||||
namespace PrgramImpl {
|
||||
void SyncCurrentProgram() {
|
||||
auto currentProgram = MG_State::pGLContext->GetCurrentProgram();
|
||||
if (!currentProgram) {
|
||||
MG_External::GLES::glUseProgram(0);
|
||||
return;
|
||||
}
|
||||
auto backendProgramIt = g_backendProgramObjects.find(currentProgram);
|
||||
SharedPtr<BackendProgramObjectImpl> backendProgram;
|
||||
if (backendProgramIt == g_backendProgramObjects.end()) {
|
||||
backendProgram = MakeShared<BackendProgramObjectImpl>();
|
||||
g_backendProgramObjects[currentProgram] = backendProgram;
|
||||
backendProgram->SyncToBackend(currentProgram);
|
||||
} else {
|
||||
backendProgram = backendProgramIt->second;
|
||||
if (!backendProgram->GetBackendProgramId()) {
|
||||
backendProgram->SyncToBackend(currentProgram);
|
||||
}
|
||||
}
|
||||
backendProgram->Use();
|
||||
}
|
||||
} // namespace PrgramImpl
|
||||
|
||||
void PrepareForDraw() {
|
||||
BufferImpl::SyncNeccessaryBuffers();
|
||||
VertexArrayImpl::SyncCurrentVAO();
|
||||
TextureImpl::SyncNeccessaryTextures();
|
||||
FramebufferImpl::SyncCurrentFBO();
|
||||
PrgramImpl::SyncCurrentProgram();
|
||||
RenderStateImpl::SyncRenderState();
|
||||
}
|
||||
|
||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
||||
PrepareForDraw();
|
||||
MG_External::GLES::glDrawElements(mode, count, type, indices);
|
||||
}
|
||||
|
||||
void Clear(GLbitfield mask) {
|
||||
FramebufferImpl::SyncCurrentFBO();
|
||||
RenderStateImpl::SyncRenderState();
|
||||
MG_External::GLES::glClear(mask);
|
||||
}
|
||||
|
||||
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLint baseVertex) {
|
||||
PrepareForDraw();
|
||||
MG_External::GLES::glDrawElementsBaseVertex(mode, count, type, indices, baseVertex);
|
||||
}
|
||||
|
||||
void MultiDrawElements(GLenum mode, const GLsizei* counts, GLenum type, const void* const* indices,
|
||||
GLsizei drawcount) {
|
||||
PrepareForDraw();
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
MG_External::GLES::glDrawElements(mode, counts[i], type, indices[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* counts, GLenum type, const void* const* indices,
|
||||
GLsizei drawcount, const GLint* baseVertices) {
|
||||
PrepareForDraw();
|
||||
for (GLsizei i = 0; i < drawcount; ++i) {
|
||||
MG_External::GLES::glDrawElementsBaseVertex(mode, counts[i], type, indices[i], baseVertices[i]);
|
||||
}
|
||||
}
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
|
||||
#define CallAndCheck(operation) \
|
||||
MGLOG_D("Call GLES func: %s", #operation); \
|
||||
operation Utils::CheckGLESError();
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
void Clear(GLbitfield mask);
|
||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||
void DrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
void DrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices, GLint basevertex);
|
||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||
GLsizei drawcount);
|
||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const GLvoid* const* indices,
|
||||
GLsizei drawcount, const GLint* basevertex);
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
@@ -0,0 +1,479 @@
|
||||
#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
|
||||
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include "DirectGLES.h"
|
||||
#include <MG_State/GLState/TextureState/TextureObject.h>
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
namespace BufferImpl {
|
||||
class BackendBufferObject {
|
||||
public:
|
||||
BackendBufferObject();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
|
||||
void Bind();
|
||||
void Bind(GLenum target);
|
||||
|
||||
private:
|
||||
void SyncToBackend_glBufferData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
|
||||
void SyncToBackend_glBufferSubData(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject);
|
||||
void SyncToBackend_glMapBufferRange(SharedPtr<MG_State::GLState::BufferObject>& stateBufferObject,
|
||||
Bool invalidate = false);
|
||||
|
||||
Uint m_backendBufferId = 0;
|
||||
SizeT m_prevBufferSize = 0;
|
||||
Bool m_isInitialized = false;
|
||||
};
|
||||
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::BufferObject>, SharedPtr<BackendBufferObject>>
|
||||
g_backendBufferObjects;
|
||||
} // namespace BufferImpl
|
||||
|
||||
namespace VertexArrayImpl {
|
||||
class BackendVertexArrayObject {
|
||||
public:
|
||||
BackendVertexArrayObject();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::VertexArrayObject>& stateVAOObject);
|
||||
void Bind();
|
||||
|
||||
private:
|
||||
Uint m_backendVAOId = 0;
|
||||
Bool m_isInitialized = false;
|
||||
};
|
||||
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::VertexArrayObject>, SharedPtr<BackendVertexArrayObject>>
|
||||
g_backendVertexArrayObjects;
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
struct StateTextureBasicInfo { // Used for tracking texture state changes
|
||||
TextureInternalFormat internalFormat = TextureInternalFormat::Unknown;
|
||||
SizeT width = 0;
|
||||
SizeT height = 0;
|
||||
SizeT depth = 0;
|
||||
SizeT mipmapLevels = 0;
|
||||
|
||||
bool operator==(const StateTextureBasicInfo& other) const {
|
||||
return internalFormat == other.internalFormat && width == other.width && height == other.height &&
|
||||
depth == other.depth && mipmapLevels == other.mipmapLevels;
|
||||
}
|
||||
|
||||
bool operator!=(const StateTextureBasicInfo& other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
class BackendTextureObject {
|
||||
public:
|
||||
BackendTextureObject();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::ITextureObject>& stateTextureObject);
|
||||
void Bind(GLenum target);
|
||||
Uint GetBackendTextureId();
|
||||
|
||||
private:
|
||||
Uint m_backendTextureId = 0;
|
||||
Bool m_isInitialized = false;
|
||||
StateTextureBasicInfo m_prevTextureInfo;
|
||||
};
|
||||
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::ITextureObject>, SharedPtr<BackendTextureObject>>
|
||||
g_backendTextureObjects;
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
class BackendFramebufferObject {
|
||||
public:
|
||||
BackendFramebufferObject();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::FramebufferObject>& stateFBOObject);
|
||||
void Bind();
|
||||
|
||||
private:
|
||||
Uint m_backendFBOId = 0;
|
||||
};
|
||||
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::FramebufferObject>, SharedPtr<BackendFramebufferObject>>
|
||||
g_backendFramebufferObjects;
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
namespace PrgramImpl {
|
||||
class BackendShaderObjectImpl {
|
||||
public:
|
||||
BackendShaderObjectImpl();
|
||||
~BackendShaderObjectImpl();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::ShaderObject>& stateShaderObject);
|
||||
Uint GetBackendShaderId() const { return m_backendShaderId; }
|
||||
|
||||
private:
|
||||
Uint m_backendShaderId = 0;
|
||||
Bool m_isInitialized = false;
|
||||
};
|
||||
class BackendProgramObjectImpl {
|
||||
public:
|
||||
BackendProgramObjectImpl();
|
||||
~BackendProgramObjectImpl();
|
||||
void SyncToBackend(SharedPtr<MG_State::GLState::ProgramObject>& stateProgramObject);
|
||||
void Use();
|
||||
Uint GetBackendProgramId() const { return m_backendProgramId; }
|
||||
|
||||
private:
|
||||
Uint m_backendProgramId = 0;
|
||||
Bool m_isInitialized = false;
|
||||
};
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::ShaderObject>, SharedPtr<BackendShaderObjectImpl>>
|
||||
g_backendShaderObjects;
|
||||
extern UnorderedMap<SharedPtr<MG_State::GLState::ProgramObject>, SharedPtr<BackendProgramObjectImpl>>
|
||||
g_backendProgramObjects;
|
||||
} // namespace PrgramImpl
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
@@ -0,0 +1,472 @@
|
||||
#include "DirectGLES.h"
|
||||
#include "Utils.h"
|
||||
#include "Managers.h"
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
namespace BufferImpl {
|
||||
BackendBufferBindingProtector::BackendBufferBindingProtector(GLenum target) {
|
||||
m_target = target;
|
||||
MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), &m_previousBinding);
|
||||
}
|
||||
|
||||
BackendBufferBindingProtector::~BackendBufferBindingProtector() {
|
||||
MG_External::GLES::glBindBuffer(m_target, m_previousBinding);
|
||||
}
|
||||
} // namespace BufferImpl
|
||||
|
||||
namespace VertexArrayImpl {
|
||||
BackendVertexArrayBindingProtector::BackendVertexArrayBindingProtector() {
|
||||
MG_External::GLES::glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &m_previousBinding);
|
||||
}
|
||||
|
||||
BackendVertexArrayBindingProtector::~BackendVertexArrayBindingProtector() {
|
||||
MG_External::GLES::glBindVertexArray(m_previousBinding);
|
||||
}
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
BackendTextureBindingProtector::BackendTextureBindingProtector(GLenum target) {
|
||||
m_target = target;
|
||||
MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, true), &m_previousBinding);
|
||||
}
|
||||
|
||||
BackendTextureBindingProtector::~BackendTextureBindingProtector() {
|
||||
MG_External::GLES::glBindTexture(m_target, m_previousBinding);
|
||||
}
|
||||
|
||||
void NormalizePixelFormat(GLenum internalFormat, GLenum* outInternalFormat, GLenum* outType,
|
||||
GLenum* outFormat) {
|
||||
switch (internalFormat) {
|
||||
case GL_DEPTH_COMPONENT16:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_SHORT;
|
||||
if (outFormat) *outFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
|
||||
case GL_DEPTH_COMPONENT24:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT;
|
||||
if (outFormat) *outFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
|
||||
case GL_DEPTH_COMPONENT32:
|
||||
if (outInternalFormat) *outInternalFormat = GL_DEPTH_COMPONENT32F;
|
||||
if (outType) *outType = GL_FLOAT;
|
||||
if (outFormat) *outFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
|
||||
case GL_DEPTH_COMPONENT32F:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_FLOAT;
|
||||
if (outFormat) *outFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
|
||||
case GL_DEPTH_COMPONENT:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT;
|
||||
if (outFormat) *outFormat = GL_DEPTH_COMPONENT;
|
||||
break;
|
||||
|
||||
case GL_DEPTH_STENCIL:
|
||||
if (outInternalFormat) *outInternalFormat = GL_DEPTH32F_STENCIL8;
|
||||
if (outType) *outType = GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
|
||||
if (outFormat) *outFormat = GL_DEPTH_STENCIL;
|
||||
break;
|
||||
|
||||
case GL_RGB10_A2:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT_2_10_10_10_REV;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
|
||||
case GL_RGB5_A1:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
|
||||
case GL_COMPRESSED_RED_RGTC1:
|
||||
case GL_COMPRESSED_RG_RGTC2:
|
||||
break;
|
||||
|
||||
case GL_SRGB8:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_RGBA32F:
|
||||
case GL_RGB32F:
|
||||
case GL_RG32F:
|
||||
case GL_R32F:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_FLOAT;
|
||||
if (outFormat) switch (internalFormat) {
|
||||
case GL_RGBA32F:
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
case GL_RGB32F:
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
case GL_RG32F:
|
||||
if (outFormat) *outFormat = GL_RG;
|
||||
break;
|
||||
case GL_R32F:
|
||||
if (outFormat) *outFormat = GL_RED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_RGB9_E5:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT_5_9_9_9_REV;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_R11F_G11F_B10F:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT_10F_11F_11F_REV;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_RGBA32UI:
|
||||
case GL_RGB32UI:
|
||||
case GL_RG32UI:
|
||||
case GL_R32UI:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT;
|
||||
if (outFormat) switch (internalFormat) {
|
||||
case GL_RGBA32UI:
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
case GL_RGB32UI:
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
case GL_RG32UI:
|
||||
if (outFormat) *outFormat = GL_RG;
|
||||
break;
|
||||
case GL_R32UI:
|
||||
if (outFormat) *outFormat = GL_RED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case GL_RGBA32I:
|
||||
case GL_RGB32I:
|
||||
case GL_RG32I:
|
||||
case GL_R32I:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_INT;
|
||||
if (outFormat) switch (internalFormat) {
|
||||
case GL_RGBA32I:
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
case GL_RGB32I:
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
case GL_RG32I:
|
||||
if (outFormat) *outFormat = GL_RG;
|
||||
break;
|
||||
case GL_R32I:
|
||||
if (outFormat) *outFormat = GL_RED;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_RGBA16: {
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_FLOAT;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
case GL_RGBA8:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
|
||||
case GL_RGBA:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
|
||||
case GL_RGBA16F:
|
||||
case GL_R16F:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_HALF_FLOAT;
|
||||
if (outFormat) {
|
||||
if (internalFormat == GL_RGBA16F) {
|
||||
*outFormat = GL_RGBA;
|
||||
} else {
|
||||
*outFormat = GL_RED;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GL_R16:
|
||||
if (outInternalFormat) *outInternalFormat = GL_R16F;
|
||||
if (outType) *outType = GL_FLOAT;
|
||||
if (outFormat) *outFormat = GL_RED;
|
||||
break;
|
||||
|
||||
case GL_RGB16:
|
||||
if (outInternalFormat) *outInternalFormat = GL_RGB16F;
|
||||
if (outType) *outType = GL_HALF_FLOAT;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_RGB16F:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_HALF_FLOAT;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
|
||||
case GL_RG16:
|
||||
case GL_RG16F:
|
||||
if (outInternalFormat) *outInternalFormat = GL_RG16F;
|
||||
if (outType) *outType = GL_HALF_FLOAT;
|
||||
if (outFormat) *outFormat = GL_RG;
|
||||
break;
|
||||
|
||||
case GL_R8:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RED;
|
||||
break;
|
||||
case GL_R8UI:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RED_INTEGER;
|
||||
break;
|
||||
|
||||
case GL_RGB8_SNORM:
|
||||
case GL_RGBA8_SNORM:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_BYTE;
|
||||
if (outFormat) {
|
||||
if (internalFormat == GL_RGB8_SNORM) {
|
||||
*outFormat = GL_RGB;
|
||||
} else {
|
||||
*outFormat = GL_RGBA;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GL_RGB8:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_BYTE;
|
||||
if (outFormat) *outFormat = GL_RGB;
|
||||
break;
|
||||
case GL_RGBA16_SNORM:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_SHORT;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
default:
|
||||
if (outInternalFormat) *outInternalFormat = internalFormat;
|
||||
if (outType) *outType = GL_UNSIGNED_INT;
|
||||
if (outFormat) *outFormat = GL_RGBA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat, GLenum* outType,
|
||||
GLenum* outFormat) {
|
||||
NormalizePixelFormat(MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat), outInternalFormat,
|
||||
outType, outFormat);
|
||||
}
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
BackendFramebufferBindingProtector::BackendFramebufferBindingProtector(GLenum target) {
|
||||
m_target = target;
|
||||
MG_External::GLES::glGetIntegerv(Utils::GetBindingQuery(target, false), &m_previousBinding);
|
||||
}
|
||||
|
||||
BackendFramebufferBindingProtector::~BackendFramebufferBindingProtector() {
|
||||
MG_External::GLES::glBindFramebuffer(m_target, m_previousBinding);
|
||||
}
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
namespace PrgramImpl {
|
||||
String ProcessOutColorLocations(const String& glslCode) {
|
||||
const static std::regex pattern(R"(\n(out highp vec4 outColor)(\d+);)");
|
||||
const String replacement = "\nlayout(location=$2) $1$2;";
|
||||
return std::regex_replace(glslCode, pattern, replacement);
|
||||
}
|
||||
|
||||
String ForceSupporterOutput(const String& glslCode) {
|
||||
Bool hasPrecisionFloat =
|
||||
glslCode.find("precision ") != String::npos && glslCode.find("float;") != String::npos;
|
||||
Bool hasPrecisionInt = glslCode.find("precision ") != String::npos && glslCode.find("int;") != String::npos;
|
||||
|
||||
String result = glslCode;
|
||||
String precisionFloat;
|
||||
String precisionInt;
|
||||
|
||||
if (hasPrecisionFloat && hasPrecisionInt) {
|
||||
std::istringstream iss(result);
|
||||
std::vector<String> lines;
|
||||
String line;
|
||||
while (std::getline(iss, line)) {
|
||||
Bool isPrecisionLine = (line.find("precision ") != String::npos) &&
|
||||
(line.find("float;") != String::npos || line.find("int;") != String::npos);
|
||||
if (!isPrecisionLine) {
|
||||
lines.push_back(line);
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
for (SizeT i = 0; i < lines.size(); ++i) {
|
||||
if (i != 0) result += '\n';
|
||||
result += lines[i];
|
||||
}
|
||||
precisionFloat = "precision highp float;\n";
|
||||
precisionInt = "precision highp int;\n";
|
||||
} else {
|
||||
precisionFloat = hasPrecisionFloat ? "" : "precision highp float;\n";
|
||||
precisionInt = hasPrecisionInt ? "" : "precision highp int;\n";
|
||||
}
|
||||
|
||||
SizeT lastExtensionPos = result.rfind("#extension");
|
||||
SizeT insertionPos = 0;
|
||||
|
||||
if (lastExtensionPos != String::npos) {
|
||||
SizeT nextNewline = result.find('\n', lastExtensionPos);
|
||||
if (nextNewline != String::npos) {
|
||||
insertionPos = nextNewline + 1;
|
||||
} else {
|
||||
insertionPos = result.length();
|
||||
}
|
||||
} else {
|
||||
SizeT firstNewline = result.find('\n');
|
||||
if (firstNewline != String::npos) {
|
||||
insertionPos = firstNewline + 1;
|
||||
} else {
|
||||
result = precisionFloat + precisionInt + result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
result.insert(insertionPos, precisionFloat + precisionInt);
|
||||
return result;
|
||||
}
|
||||
|
||||
String removeLayoutBinding(const String& glslCode) {
|
||||
static std::regex bindingRegex(R"(layout\s*\(\s*binding\s*=\s*\d+\s*\)\s*)");
|
||||
String result = std::regex_replace(glslCode, bindingRegex, "");
|
||||
static std::regex bindingRegex2(R"(layout\s*\(\s*binding\s*=\s*\d+\s*,)");
|
||||
result = std::regex_replace(result, bindingRegex2, "layout(");
|
||||
return result;
|
||||
}
|
||||
} // namespace PrgramImpl
|
||||
|
||||
namespace Utils {
|
||||
void CheckGLESError() {
|
||||
while (GLenum err = MG_External::GLES::glGetError() != GL_NO_ERROR) {
|
||||
MGLOG_E("-> GLES Error: %s", MG_Util::ConvertGLEnumToString(err).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
GLenum GetBindingQuery(GLenum target, bool isTexture) {
|
||||
switch (target) {
|
||||
case GL_TEXTURE_BUFFER:
|
||||
return isTexture ? GL_TEXTURE_BINDING_BUFFER : GL_TEXTURE_BUFFER_BINDING;
|
||||
|
||||
case GL_ARRAY_BUFFER:
|
||||
return GL_ARRAY_BUFFER_BINDING;
|
||||
case GL_ATOMIC_COUNTER_BUFFER:
|
||||
return GL_ATOMIC_COUNTER_BUFFER_BINDING;
|
||||
case GL_COPY_READ_BUFFER:
|
||||
return GL_COPY_READ_BUFFER_BINDING;
|
||||
case GL_COPY_WRITE_BUFFER:
|
||||
return GL_COPY_WRITE_BUFFER_BINDING;
|
||||
case GL_DISPATCH_INDIRECT_BUFFER:
|
||||
return GL_DISPATCH_INDIRECT_BUFFER_BINDING;
|
||||
case GL_DRAW_INDIRECT_BUFFER:
|
||||
return GL_DRAW_INDIRECT_BUFFER_BINDING;
|
||||
case GL_ELEMENT_ARRAY_BUFFER:
|
||||
return GL_ELEMENT_ARRAY_BUFFER_BINDING;
|
||||
case GL_PIXEL_PACK_BUFFER:
|
||||
return GL_PIXEL_PACK_BUFFER_BINDING;
|
||||
case GL_PIXEL_UNPACK_BUFFER:
|
||||
return GL_PIXEL_UNPACK_BUFFER_BINDING;
|
||||
case GL_QUERY_BUFFER:
|
||||
return GL_QUERY_BUFFER_BINDING;
|
||||
case GL_SHADER_STORAGE_BUFFER:
|
||||
return GL_SHADER_STORAGE_BUFFER_BINDING;
|
||||
case GL_TRANSFORM_FEEDBACK_BUFFER:
|
||||
return GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
|
||||
case GL_UNIFORM_BUFFER:
|
||||
return GL_UNIFORM_BUFFER_BINDING;
|
||||
|
||||
case GL_FRAMEBUFFER:
|
||||
return GL_FRAMEBUFFER_BINDING;
|
||||
case GL_DRAW_FRAMEBUFFER:
|
||||
return GL_DRAW_FRAMEBUFFER_BINDING;
|
||||
case GL_READ_FRAMEBUFFER:
|
||||
return GL_READ_FRAMEBUFFER_BINDING;
|
||||
|
||||
case GL_RENDERBUFFER:
|
||||
return GL_RENDERBUFFER_BINDING;
|
||||
|
||||
case GL_VERTEX_ARRAY:
|
||||
return GL_VERTEX_ARRAY_BINDING;
|
||||
case GL_VERTEX_ARRAY_BINDING:
|
||||
return GL_VERTEX_ARRAY_BINDING;
|
||||
|
||||
case GL_PROGRAM_PIPELINE:
|
||||
return GL_PROGRAM_PIPELINE_BINDING;
|
||||
|
||||
case GL_PROGRAM:
|
||||
return GL_CURRENT_PROGRAM;
|
||||
|
||||
case GL_SAMPLER:
|
||||
return GL_SAMPLER_BINDING;
|
||||
|
||||
case GL_TEXTURE:
|
||||
return GL_TEXTURE_BINDING_2D;
|
||||
case GL_TEXTURE_1D:
|
||||
return GL_TEXTURE_BINDING_1D;
|
||||
case GL_TEXTURE_1D_ARRAY:
|
||||
return GL_TEXTURE_BINDING_1D_ARRAY;
|
||||
case GL_TEXTURE_2D:
|
||||
return GL_TEXTURE_BINDING_2D;
|
||||
case GL_TEXTURE_2D_ARRAY:
|
||||
return GL_TEXTURE_BINDING_2D_ARRAY;
|
||||
case GL_TEXTURE_2D_MULTISAMPLE:
|
||||
return GL_TEXTURE_BINDING_2D_MULTISAMPLE;
|
||||
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
|
||||
return GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
|
||||
case GL_TEXTURE_3D:
|
||||
return GL_TEXTURE_BINDING_3D;
|
||||
case GL_TEXTURE_CUBE_MAP:
|
||||
return GL_TEXTURE_BINDING_CUBE_MAP;
|
||||
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
||||
return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
|
||||
case GL_TEXTURE_RECTANGLE:
|
||||
return GL_TEXTURE_BINDING_RECTANGLE;
|
||||
|
||||
case GL_TRANSFORM_FEEDBACK:
|
||||
return GL_TRANSFORM_FEEDBACK_BINDING;
|
||||
|
||||
case GL_SAMPLES_PASSED:
|
||||
return GL_SAMPLES_PASSED;
|
||||
case GL_PRIMITIVES_GENERATED:
|
||||
return GL_PRIMITIVES_GENERATED;
|
||||
|
||||
case GL_DEBUG_OUTPUT:
|
||||
return GL_DEBUG_OUTPUT;
|
||||
case GL_DEBUG_OUTPUT_SYNCHRONOUS:
|
||||
return GL_DEBUG_OUTPUT_SYNCHRONOUS;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} // namespace Utils
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include <MG_State/GLState/Core.h>
|
||||
|
||||
namespace MobileGL::MG_Backend::DirectGLES {
|
||||
namespace BufferImpl {
|
||||
class BackendBufferBindingProtector {
|
||||
public:
|
||||
BackendBufferBindingProtector(GLenum target);
|
||||
|
||||
~BackendBufferBindingProtector();
|
||||
|
||||
private:
|
||||
GLenum m_target;
|
||||
GLint m_previousBinding = 0;
|
||||
};
|
||||
} // namespace BufferImpl
|
||||
|
||||
namespace VertexArrayImpl {
|
||||
GLenum GetBindingQuery(GLenum target, bool isTexture);
|
||||
|
||||
class BackendVertexArrayBindingProtector {
|
||||
public:
|
||||
BackendVertexArrayBindingProtector();
|
||||
|
||||
~BackendVertexArrayBindingProtector();
|
||||
|
||||
private:
|
||||
GLint m_previousBinding = 0;
|
||||
};
|
||||
} // namespace VertexArrayImpl
|
||||
|
||||
namespace TextureImpl {
|
||||
class BackendTextureBindingProtector {
|
||||
public:
|
||||
BackendTextureBindingProtector(GLenum target);
|
||||
|
||||
~BackendTextureBindingProtector();
|
||||
|
||||
private:
|
||||
GLenum m_target;
|
||||
GLint m_previousBinding = 0;
|
||||
};
|
||||
|
||||
void NormalizePixelFormat(GLenum internalFormat, GLenum* outInternalFormat, GLenum* outType, GLenum* outFormat);
|
||||
void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat, GLenum* outType,
|
||||
GLenum* outFormat);
|
||||
} // namespace TextureImpl
|
||||
|
||||
namespace FramebufferImpl {
|
||||
class BackendFramebufferBindingProtector {
|
||||
public:
|
||||
BackendFramebufferBindingProtector(GLenum target);
|
||||
|
||||
~BackendFramebufferBindingProtector();
|
||||
|
||||
private:
|
||||
GLenum m_target;
|
||||
GLint m_previousBinding = 0;
|
||||
};
|
||||
} // namespace FramebufferImpl
|
||||
|
||||
namespace PrgramImpl {
|
||||
String ProcessOutColorLocations(const String& glslCode);
|
||||
String ForceSupporterOutput(const String& glslCode);
|
||||
String removeLayoutBinding(const String& glslCode);
|
||||
} // namespace PrgramImpl
|
||||
|
||||
namespace Utils {
|
||||
void CheckGLESError();
|
||||
GLenum GetBindingQuery(GLenum target, bool isTexture);
|
||||
} // namespace Utils
|
||||
} // namespace MobileGL::MG_Backend::DirectGLES
|
||||
@@ -2,25 +2,48 @@
|
||||
#include <Config.h>
|
||||
#include <MG_State/GLState/Core.h>
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
#include <MG_Backend/DirectGLES/DirectGLES.h>
|
||||
|
||||
namespace MobileGL {
|
||||
namespace MG_Impl::GLImpl {
|
||||
void Clear_Backend(GLbitfield mask) {
|
||||
|
||||
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
|
||||
auto clearColor = MG_State::pGLContext->GetClearColor();
|
||||
MG_External::GLES::glClearColor(clearColor.r(), clearColor.g(), clearColor.b(), clearColor.a());
|
||||
auto clearDepth = MG_State::pGLContext->GetClearDepth();
|
||||
MG_External::GLES::glClearDepthf(clearDepth);
|
||||
MG_External::GLES::glClear(mask);
|
||||
MG_Backend::DirectGLES::Clear(mask);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DrawElements_Backend(GLenum mode, GLsizei count, GLenum type, const void* indices) {
|
||||
// TODO
|
||||
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
|
||||
MG_Backend::DirectGLES::DrawElements(mode, count, type, indices);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiDrawElements_Backend(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||
GLsizei drawcount) {
|
||||
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
|
||||
MG_Backend::DirectGLES::MultiDrawElements(mode, count, type, indices, drawcount);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MultiDrawElementsBaseVertex_Backend(GLenum mode, const GLsizei* count, GLenum type,
|
||||
const void* const* indices, GLsizei drawcount,
|
||||
const GLint* basevertex) {
|
||||
#if MOBILEGL_BACKEND == MOBILEGL_BACKEND_TYPE_DIRECT_GLES
|
||||
MG_Backend::DirectGLES::MultiDrawElementsBaseVertex(mode, count, type, indices, drawcount, basevertex);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
|
||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||
GLsizei drawcount) {
|
||||
MultiDrawElements_Backend(mode, count, type, indices, drawcount);
|
||||
}
|
||||
|
||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices,
|
||||
GLsizei drawcount, const GLint* basevertex) {
|
||||
MultiDrawElementsBaseVertex_Backend(mode, count, type, indices, drawcount, basevertex);
|
||||
}
|
||||
|
||||
void Clear(GLbitfield mask) {
|
||||
Clear_Backend(mask);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
namespace MobileGL {
|
||||
namespace MG_Impl::GLImpl {
|
||||
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
|
||||
void MultiDrawElements(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount);
|
||||
void MultiDrawElementsBaseVertex(GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex);
|
||||
void Clear(GLbitfield mask);
|
||||
void DrawElements(GLenum mode, GLsizei count, GLenum type, const void* indices);
|
||||
} // namespace MG_Impl::GLImpl
|
||||
|
||||
@@ -743,7 +743,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, LoadTransposeMatrixd, const GLdouble* m) DEC
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixf, const GLfloat* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixf, m)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultTransposeMatrixd, const GLdouble* m) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultTransposeMatrixd, m)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawArrays, GLenum mode, const GLint* first, const GLsizei* count, GLsizei drawcount) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawArrays, mode, first, count, drawcount)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElements, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElements, mode, count, type, indices, drawcount)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElements, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElements, mode, count, type, indices, drawcount)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, PointParameterf, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameterf, pname, param)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, PointParameterfv, GLenum pname, const GLfloat* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PointParameterfv, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, PointParameteri, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PointParameteri, pname, param)
|
||||
@@ -819,7 +819,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4uiv, GLuint index, const GLuint
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttrib4usv, GLuint index, const GLushort* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttrib4usv, index, v)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, PrimitiveRestartIndex, GLuint index) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, PrimitiveRestartIndex, index)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformName, GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei* length, GLchar* uniformName) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformName, program, uniformIndex, bufSize, length, uniformName)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, MultiDrawElementsBaseVertex, mode, count, type, indices, drawcount, basevertex)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const GLsizei* count, GLenum type, const void* const* indices, GLsizei drawcount, const GLint* basevertex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MultiDrawElementsBaseVertex, mode, count, type, indices, drawcount, basevertex)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProvokingVertex, mode)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, TexImage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, TexImage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations)
|
||||
|
||||
@@ -9,15 +9,15 @@ namespace MobileGL {
|
||||
|
||||
void BufferObject::Resize(SizeT size) {
|
||||
m_size = size;
|
||||
m_data.reserve(std::bit_ceil(size)); // power-of-2 reserve
|
||||
m_data.resize(size);
|
||||
m_dataPtr->reserve(std::bit_ceil(size)); // power-of-2 reserve
|
||||
m_dataPtr->resize(size);
|
||||
m_dirtyRange = {0, 0};
|
||||
}
|
||||
|
||||
void BufferObject::UploadData(DataPtr data, SizeT atOffset) {
|
||||
assert(atOffset + data.size <= m_size);
|
||||
assert(!m_isMapped);
|
||||
memcpy(m_data.data() + atOffset, data.data, data.size);
|
||||
memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
|
||||
m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace MobileGL {
|
||||
|
||||
if (m_mappingAccess & BufferMappingAccessBit::Write) { // if we wrote to the buffer
|
||||
if (!(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
|
||||
memcpy(m_data.data() + m_mappedRange.start, m_stagingData.data(),
|
||||
memcpy(m_dataPtr->data() + m_mappedRange.start, m_stagingData.data(),
|
||||
m_mappedRange.end - m_mappedRange.start);
|
||||
m_dirtyRange.UnionUpdate(m_mappedRange.start, m_mappedRange.end);
|
||||
}
|
||||
@@ -53,7 +53,7 @@ namespace MobileGL {
|
||||
SizeT end = start + length;
|
||||
assert(end <= m_mappedRange.end);
|
||||
|
||||
memcpy(m_data.data() + start, m_stagingData.data() + offset, length);
|
||||
memcpy(m_dataPtr->data() + start, m_stagingData.data() + offset, length);
|
||||
m_dirtyRange.UnionUpdate(start, end);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace MobileGL {
|
||||
assert(!m_isMapped);
|
||||
assert(atOffset + data.size <= m_size);
|
||||
|
||||
memcpy(m_data.data() + atOffset, data.data, data.size);
|
||||
memcpy(m_dataPtr->data() + atOffset, data.data, data.size);
|
||||
m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size);
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ namespace MobileGL {
|
||||
assert(srcOffset + size <= src->GetSize());
|
||||
assert(dstOffset + size <= m_size);
|
||||
|
||||
const Uint8* srcData = src->m_data.data() + srcOffset;
|
||||
memcpy(m_data.data() + dstOffset, srcData, size);
|
||||
const Uint8* srcData = src->m_dataPtr->data() + srcOffset;
|
||||
memcpy(m_dataPtr->data() + dstOffset, srcData, size);
|
||||
m_dirtyRange.UnionUpdate(dstOffset, dstOffset + size);
|
||||
}
|
||||
|
||||
@@ -91,14 +91,14 @@ namespace MobileGL {
|
||||
|
||||
if (!(m_mappingAccess &
|
||||
(BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
|
||||
memcpy(m_stagingData.data(), m_data.data(), m_size);
|
||||
memcpy(m_stagingData.data(), m_dataPtr->data(), m_size);
|
||||
}
|
||||
|
||||
return m_stagingData.data();
|
||||
}
|
||||
}
|
||||
|
||||
return m_data.data();
|
||||
return m_dataPtr->data();
|
||||
}
|
||||
|
||||
void* BufferObject::AcquireMemoryRange(Range1D range, Flags<BufferMappingAccessBit> access) {
|
||||
@@ -113,16 +113,20 @@ namespace MobileGL {
|
||||
|
||||
if (!(access &
|
||||
(BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer))) {
|
||||
memcpy(m_stagingData.data(), m_data.data() + range.start, m_stagingData.size());
|
||||
memcpy(m_stagingData.data(), m_dataPtr->data() + range.start, m_stagingData.size());
|
||||
}
|
||||
|
||||
return m_stagingData.data();
|
||||
} else {
|
||||
m_ownsStagingData = false;
|
||||
return m_data.data() + range.start;
|
||||
return m_dataPtr->data() + range.start;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Data> BufferObject::GetDataReadOnly() const {
|
||||
return m_dataPtr;
|
||||
}
|
||||
|
||||
void BufferObject::ClearDirty() {
|
||||
m_dirtyRange = {0, 0};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "MG_Util/Types.h"
|
||||
#include <Includes.h>
|
||||
|
||||
namespace MobileGL {
|
||||
@@ -70,18 +71,19 @@ namespace MobileGL {
|
||||
BufferUsage GetUsage() const;
|
||||
Range1D GetDirtyRange() const;
|
||||
Range1D GetMappedRange() const;
|
||||
SharedPtr<Data> GetDataReadOnly() const;
|
||||
Flags<BufferMappingAccessBit> GetMappingAccess() const;
|
||||
|
||||
private:
|
||||
Int m_id = 0;
|
||||
SizeT m_size = 0;
|
||||
BufferUsage m_usage = BufferUsage::StaticDraw;
|
||||
Data m_data;
|
||||
SharedPtr<Data> m_dataPtr;
|
||||
Bool m_isMapped;
|
||||
Flags<BufferMappingAccessBit> m_mappingAccess;
|
||||
Range1D m_dirtyRange;
|
||||
Range1D m_mappedRange;
|
||||
std::vector<Uint8> m_stagingData;
|
||||
Vector<Uint8> m_stagingData;
|
||||
Bool m_ownsStagingData;
|
||||
};
|
||||
} // namespace GLState
|
||||
|
||||
@@ -88,6 +88,12 @@ namespace MobileGL {
|
||||
return m_attachments[static_cast<SizeT>(type)];
|
||||
}
|
||||
|
||||
const Array<FramebufferAttachment,
|
||||
static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>&
|
||||
FramebufferObject::GetAllAttachments() const {
|
||||
return m_attachments;
|
||||
}
|
||||
|
||||
Bool FramebufferObject::CheckCompleteness() const {
|
||||
if (m_attachments.empty()) {
|
||||
return false;
|
||||
|
||||
@@ -89,6 +89,9 @@ namespace MobileGL {
|
||||
std::shared_ptr<RenderbufferObjectStub> renderbuffer);
|
||||
void Detach(FramebufferAttachmentType type);
|
||||
const FramebufferAttachment& GetAttachment(FramebufferAttachmentType type) const;
|
||||
const Array<FramebufferAttachment,
|
||||
static_cast<SizeT>(FramebufferAttachmentType::FramebufferAttachmentTypeCount)>&
|
||||
GetAllAttachments() const;
|
||||
Bool CheckCompleteness() const;
|
||||
void SetDrawBuffers(const std::vector<FramebufferAttachmentType>& buffers);
|
||||
const Vector<FramebufferAttachmentType>& GetDrawBuffers() const;
|
||||
|
||||
@@ -68,6 +68,12 @@ namespace MobileGL {
|
||||
m_internalFormat = format;
|
||||
}
|
||||
|
||||
void TextureObjectBase::UnmarkMipmapDirty(Int index) {
|
||||
if (index >= 0 && index < static_cast<Int>(m_mipmaps.size())) {
|
||||
m_mipmaps[index].dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// TextureObject1D
|
||||
TextureObject1D::TextureObject1D() : TextureObjectBase(TextureTarget::Texture1D) {}
|
||||
|
||||
|
||||
@@ -211,6 +211,7 @@ namespace MobileGL {
|
||||
virtual MipmapLevelInternal& GetMipmap(Int index) = 0;
|
||||
virtual void SetInternalFormat(TextureInternalFormat format) = 0;
|
||||
virtual Bool IsComplete() const = 0;
|
||||
virtual void UnmarkMipmapDirty(Int index) = 0;
|
||||
};
|
||||
|
||||
class TextureObjectBase : public ITextureObject {
|
||||
@@ -227,6 +228,7 @@ namespace MobileGL {
|
||||
MipmapLevelInternal& GetMipmap(Int index) override;
|
||||
void SetInternalFormat(TextureInternalFormat format) override;
|
||||
Bool IsComplete() const override;
|
||||
void UnmarkMipmapDirty(Int index) override;
|
||||
|
||||
protected:
|
||||
virtual void SetMipmapImpl(const MipmapLevelInput& level) = 0;
|
||||
|
||||
@@ -4,7 +4,8 @@ namespace MobileGL {
|
||||
namespace MG_State {
|
||||
namespace GLState {
|
||||
VertexArrayObject::VertexArrayObject() {
|
||||
for (auto& attr : m_attributes) {
|
||||
for (int index = 0; index < MAX_VERTEX_ATTRIBS; ++index) {
|
||||
auto& attr = m_attributes[index];
|
||||
attr.Enabled = false;
|
||||
attr.Size = 4;
|
||||
attr.Type = DataType::Float32;
|
||||
@@ -12,17 +13,21 @@ namespace MobileGL {
|
||||
attr.Stride = 0;
|
||||
attr.Offset = 0;
|
||||
attr.Buffer = nullptr;
|
||||
|
||||
MarkAttributeDirty(index);
|
||||
}
|
||||
}
|
||||
|
||||
void VertexArrayObject::EnableAttribute(Uint index) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
m_attributes[index].Enabled = true;
|
||||
MarkAttributeDirty(index);
|
||||
}
|
||||
|
||||
void VertexArrayObject::DisableAttribute(Uint index) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
m_attributes[index].Enabled = false;
|
||||
MarkAttributeDirty(index);
|
||||
}
|
||||
|
||||
Bool VertexArrayObject::IsAttributeEnabled(Uint index) const {
|
||||
@@ -45,11 +50,14 @@ namespace MobileGL {
|
||||
attr.Stride = stride;
|
||||
attr.Offset = offset;
|
||||
attr.IsInteger = isInteger;
|
||||
|
||||
MarkAttributeDirty(index);
|
||||
}
|
||||
|
||||
void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
m_attributes[index].Buffer = buffer;
|
||||
MarkAttributeDirty(index);
|
||||
}
|
||||
|
||||
BindingSlot<BufferObject>& VertexArrayObject::GetIndexBufferBindingSlot() {
|
||||
@@ -61,6 +69,27 @@ namespace MobileGL {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return emptyAttr;
|
||||
return m_attributes[index];
|
||||
}
|
||||
|
||||
const Array<VertexAttribute, VertexArrayObject::MAX_VERTEX_ATTRIBS>& VertexArrayObject::GetAllAttributes()
|
||||
const {
|
||||
return m_attributes;
|
||||
}
|
||||
|
||||
void VertexArrayObject::MarkAttributeDirty(Uint index) {
|
||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||
if (std::find(m_dirtyAttributes.begin(), m_dirtyAttributes.end(), index) != m_dirtyAttributes.end()) {
|
||||
return;
|
||||
}
|
||||
m_dirtyAttributes.push_back(index);
|
||||
}
|
||||
|
||||
const Vector<Uint>& VertexArrayObject::GetDirtyAttributeIndices() const {
|
||||
return m_dirtyAttributes;
|
||||
}
|
||||
|
||||
void VertexArrayObject::ClearDirtyAttributes() {
|
||||
m_dirtyAttributes.clear();
|
||||
}
|
||||
} // namespace GLState
|
||||
} // namespace MG_State
|
||||
} // namespace MobileGL
|
||||
|
||||
@@ -34,9 +34,16 @@ namespace MobileGL {
|
||||
BindingSlot<BufferObject>& GetIndexBufferBindingSlot();
|
||||
|
||||
const VertexAttribute& GetAttribute(Uint index) const;
|
||||
const Array<VertexAttribute, MAX_VERTEX_ATTRIBS>& GetAllAttributes() const;
|
||||
|
||||
const Vector<Uint>& GetDirtyAttributeIndices() const;
|
||||
void ClearDirtyAttributes();
|
||||
|
||||
private:
|
||||
void MarkAttributeDirty(Uint index);
|
||||
|
||||
Array<VertexAttribute, MAX_VERTEX_ATTRIBS> m_attributes;
|
||||
Vector<Uint> m_dirtyAttributes;
|
||||
BindingSlot<BufferObject> m_indexBufferBindingSlot;
|
||||
};
|
||||
} // namespace GLState
|
||||
|
||||
@@ -56,6 +56,9 @@ namespace MobileGL {
|
||||
inline constexpr void Copy(const T* src, T* dest, SizeT count) {
|
||||
std::copy(src, src + count, dest);
|
||||
}
|
||||
inline constexpr void Memcpy(const void* src, void* dest, SizeT size) {
|
||||
std::memcpy(dest, src, size);
|
||||
}
|
||||
template <typename... Ts>
|
||||
constexpr auto ToArray(Ts&&... elems) {
|
||||
using E = std::common_type_t<Ts...>;
|
||||
|
||||
Reference in New Issue
Block a user