[Feat|Fix|Improvement] (All): Continue building the basic structure.

Feat: implement MG_Impl/Buffer
Feat: implement MG_State/ErrorState and integrate it into MG_State/GLContext
Feat: add buffer deletion management
Feat: introduce `INSERTION_POINT` (https://github.com/MobileGL-Dev/MobileGLCodeManager)
Feat: add tests about MG_Impl/Buffer and buffer deletion feature
Feat: add several converters
Fix: fix some compiling errors
Fix: fix some incorrect behaviors in MG_State/Buffer
Improvement: remove some current statements at the beginning of the source file, which will be added uniformly soon
Improvement: optimize some naming
This commit is contained in:
BZLZHH
2025-07-23 23:04:36 +08:00
parent 06804d94f8
commit e79a2fa809
37 changed files with 1377 additions and 118 deletions
+2
View File
@@ -9,3 +9,5 @@
CMakeSettings.json
MobileGL/MG_Test/out
MobileGL/MG_Test/.vs
MobileGLCodeManager.*
MobileGLCodeManager
+10 -2
View File
@@ -21,7 +21,11 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp
MobileGL/MG_Util/Converters/MGToStr/GLExtensionConverter.cpp
MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp
MobileGL/MG_Util/Converters/GLToGlslang/GLShaderLangConverter.cpp
MobileGL/MG_Util/Converters/MGToGL/ErrorCodeConverter.cpp
MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp
MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp
MobileGL/MG_Impl/GLXImpl/Exporting/Definitions.cpp
MobileGL/MG_Impl/GLXImpl/LookUp/LookUp.cpp
@@ -29,16 +33,20 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
MobileGL/MG_Impl/EGLImpl/Exporting/Definitions.cpp
MobileGL/MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.cpp
# @INSERTION_POINT:SOURCE_FILE_GLIMPL@ #
MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp
MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp
MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp
MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp
MobileGL/MG_Backend/Init.cpp
MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp
MobileGL/MG_State/GLState/Core.cpp
MobileGL/MG_State/GLState/ErrorState/Error.cpp
MobileGL/MG_State/GLState/BufferState/BufferState.cpp
)
)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/library/arm64-v8a/libglslang.a
@@ -72,4 +80,4 @@ target_link_libraries(MobileGL
# Diligent-Common
# Diligent-GraphicsEngineOpenGL-shared
# Diligent-GraphicsEngineVk-shared
)
)
+23 -17
View File
@@ -1,7 +1,7 @@
#pragma once
#ifdef __ANDROID__
#define __ANDROID_API__ 26 // fix pthread_getname_np not defined
#define __ANDROID_API__ 26 // force Android API level to 26 for compatibility
#endif
#ifdef _WIN32
@@ -41,6 +41,7 @@
#include <format>
#include <memory>
#include <bit>
#include <cstdarg>
#include <functional>
#include <expected>
@@ -55,6 +56,9 @@
#include <glm/glm.hpp>
#include <ankerl/unordered_dense.h>
#include <GLES3/gl32.h>
#include <EGL/egl.h>
#include <GL/gl.h>
#include <GL/glext.h>
#ifdef __ANDROID__
#include <android/log.h>
@@ -85,20 +89,16 @@
typedef int android_LogPriority;
int __android_log_print(int prio, const char *tag, const char *fmt, ...);
inline int __android_log_print(int prio, const char* tag, const char* fmt, ...) { }
#endif
#include <EGL/egl.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include "MG_Util/GLExtensions.h"
#include "MG_Util/Types.h"
#include "MG_Util/Miscellany/IndexGenerator.h"
#include "MG_Util/Debug/Log.h"
#include "MG_Util/Converters/GLToStr/GLEnumConverter.h"
#include "MG_Util/Converters/MGToStr/GLExtensionConverter.h"
#include "MG_Util/Converters/GLToGlslang/GLShaderLangConverter.h"
#include "MG_State/GLState/ErrorState/ErrorCode.h"
#include "MG_State/GLState/ErrorState/ErrorInfo.h"
#include "MG_State/GLState/ErrorState/Error.h"
#include "MG_Backend/Backends.h"
@@ -107,14 +107,20 @@ int __android_log_print(int prio, const char *tag, const char *fmt, ...);
#include "MG_Impl/GLXImpl/LookUp/LookUp.h"
#include "MG_Impl/EGLImpl/Temporary/TemporaryEGLImpl.h"
#include "MG_Impl/GLImpl/Getter/GL_Getter.h"
//#include "MG_Util/Pipelines/PipelineExecutor.hpp"
//#include "MG_Util/Pipelines/ShaderCompilationPipeline.h"
// #include "MG_Util/ShaderTranspiler/glslang/UniformTraverser.h"
#include "MG_State/GLState/BufferState/BufferObject.h"
#include "MG_State/GLState/BufferState/BufferState.h"
/* @INSERTION_POINT:HEADER_FILE_GLIMPL@ */
#include "MG_Impl/GLImpl/Getter/GL_Getter.h"
#include "MG_Impl/GLImpl/Buffer/GL_Buffer.h"
#include "MG_Impl/GLImpl/Buffer/Validators.h"
#include "MG_State/GLState/Core.h"
#include "MG_Util/Converters/GLToMG/BufferEnumConverter.h"
#include "MG_Util/Converters/MGToGL/BufferEnumConverter.h"
#include "MG_Util/Converters/MGToGL/ErrorCodeConverter.h"
#include "MG_Util/Converters/GLToStr/GLEnumConverter.h"
#include "MG_Util/Converters/MGToStr/BufferEnumConverter.h"
#include "MG_Util/Converters/MGToStr/GLExtensionConverter.h"
#include "MG_Util/Converters/GLToGlslang/GLShaderLangConverter.h"
@@ -0,0 +1,440 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Impl::GLImpl {
void DeleteBuffers_State(GLsizei n, const GLuint* buffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteBuffers_State",
"n must be non-negative."));
return;
}
if (!buffers) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteBuffers_State",
"Buffer names array cannot be null."));
return;
}
for (SizeT i = 0; i < static_cast<SizeT>(n); ++i) {
Uint bufferName = buffers[i];
if (bufferName == 0) continue;
if (!BufferImpl::ValidateBufferName(bufferName)) continue;
MG_State::pGLContext->MarkBufferObjectForDeletion(bufferName);
}
}
void FlushMappedBufferRange_State(GLenum target, GLintptr offset, GLsizeiptr length) {
if (length < 0 || offset < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
"Offset and length must be non-negative."));
return;
}
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
"Buffer target is bound to no buffer object."));
return;
}
if (!bufferObject->IsMapped()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
"Cannot flush a buffer object that is not mapped."));
return;
}
if (offset + length > bufferObject->GetSize()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
"Offset and length exceed buffer size."));
return;
}
BufferMappingAccessBit mappingAccess = bufferObject->GetMappingAccess();
if(!Any(mappingAccess & BufferMappingAccessBit::FlushExplicit)) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "FlushMappedBufferRange_State",
"Cannot flush a buffer object that is not mapped with GL_MAP_FLUSH_EXPLICIT_BIT."));
return;
}
bufferObject->FlushMemoryRange(static_cast<SizeT>(offset), static_cast<SizeT>(length));
}
GLboolean UnmapBuffer_State(GLenum target) {
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return GL_FALSE;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "UnmapBuffer_State",
"Buffer target is bound to no buffer object."));
return GL_FALSE;
}
if (!bufferObject->IsMapped()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "UnmapBuffer_State",
"Cannot unmap a buffer object that is not mapped."));
return GL_FALSE;
}
bufferObject->ReleaseMemory();
return GL_TRUE;
}
void* MapBufferRange_State(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
if (length < 0 || offset < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Offset and length must be non-negative."));
return nullptr;
}
if (length == 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Length must be greater than zero."));
return nullptr;
}
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return nullptr;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Buffer target is bound to no buffer object."));
return nullptr;
}
if (offset + length > bufferObject->GetSize()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Offset and length exceed buffer size."));
return nullptr;
}
auto accessBits = MG_Util::ConvertGLEnumToBufferMappingAccess(access);
if (!BufferImpl::ValidateBufferMappingAccess(accessBits)) return nullptr;
if (!Any(accessBits & (BufferMappingAccessBit::Read | BufferMappingAccessBit::Write))) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"At least one of GL_MAP_READ_BIT or GL_MAP_WRITE_BIT must be set."));
return nullptr;
}
if (Any(accessBits & BufferMappingAccessBit::Read)) {
const auto invalidFlags = BufferMappingAccessBit::InvalidateRange |
BufferMappingAccessBit::InvalidateBuffer |
BufferMappingAccessBit::Unsynchronized;
if (Any(accessBits & invalidFlags)) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"GL_MAP_READ_BIT cannot be combined with invalidation or unsynchronized flags."));
return nullptr;
}
}
if (Any(accessBits & BufferMappingAccessBit::FlushExplicit)) {
if (!(Any(accessBits & BufferMappingAccessBit::Write))) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"GL_MAP_FLUSH_EXPLICIT_BIT requires GL_MAP_WRITE_BIT."));
return nullptr;
}
}
const BufferMappingAccessBit storageFlags =
BufferMappingAccessBit::Persistent | BufferMappingAccessBit::Coherent;
BufferMappingAccessBit requiredFlags = accessBits & storageFlags;
if (Any(requiredFlags)) {
// TODO: check if the buffer data is created by BufferStorage and its flags after its implementation
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Access flags require matching storage flags in buffer."));
return nullptr;
}
if (bufferObject->IsMapped()) {
const auto invalidateFlags = BufferMappingAccessBit::InvalidateRange |
BufferMappingAccessBit::InvalidateBuffer;
if (!Any(accessBits & invalidateFlags)) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Cannot map a buffer object that is already mapped."));
return nullptr;
}
}
void* result = bufferObject->AcquireMemoryRange(
{ static_cast<SizeT>(offset), static_cast<SizeT>(offset + length) },
accessBits
);
if (!result) {
MG_State::pGLContext->RecordError(ErrorCode::OutOfMemory,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBufferRange_State",
"Failed to map buffer due to insufficient memory."));
return nullptr;
}
return result;
}
void* MapBuffer_State(GLenum target, GLenum access) {
bool readable = access == GL_READ_ONLY || access == GL_READ_WRITE;
bool writable = access == GL_WRITE_ONLY || access == GL_READ_WRITE;
if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBuffer_State",
"Access must be one of GL_READ_ONLY, GL_WRITE_ONLY, or GL_READ_WRITE."));
return nullptr;
}
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return nullptr;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBuffer_State",
"Buffer target is bound to no buffer object."));
return nullptr;
}
if (bufferObject->IsMapped()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBuffer_State",
"Cannot map a buffer object that is already mapped."));
return nullptr;
}
void* result = bufferObject->AcquireMemory(true, readable, writable);
if (!result) {
MG_State::pGLContext->RecordError(ErrorCode::OutOfMemory,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "MapBuffer_State",
"Failed to map buffer due to insufficient memory."));
return nullptr;
}
return result;
}
void CopyBufferSubData_State(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) {
if (size < 0 || readOffset < 0 || writeOffset < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CopyBufferSubData_State",
"Offset and size must be non-negative."));
return;
}
BufferTarget readBufferTarget = MG_Util::ConvertGLEnumToBufferTarget(readTarget);
BufferTarget writeBufferTarget = MG_Util::ConvertGLEnumToBufferTarget(writeTarget);
if (!BufferImpl::ValidateBufferTarget(readBufferTarget) || !BufferImpl::ValidateBufferTarget(writeBufferTarget)) return;
auto& readBindingSlot = MG_State::pGLContext->GetBufferBindingSlot(readBufferTarget);
auto& writeBindingSlot = MG_State::pGLContext->GetBufferBindingSlot(writeBufferTarget);
auto readBufferObject = readBindingSlot.GetBoundObject();
auto writeBufferObject = writeBindingSlot.GetBoundObject();
if (!readBufferObject || !writeBufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CopyBufferSubData_State",
"One of the buffer targets is bound to no buffer object."));
return;
}
if (readOffset + size > readBufferObject->GetSize() || writeOffset + size > writeBufferObject->GetSize()) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CopyBufferSubData_State",
"Offset and size must be within the bounds of the buffer objects."));
return;
}
if (readBufferObject == writeBufferObject) {
if ((readOffset <= writeOffset && readOffset + size > writeOffset) ||
(writeOffset <= readOffset && writeOffset + size > readOffset))
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CopyBufferSubData_State",
"Source and destination buffers overlap in the specified ranges."));
return;
}
auto isIllegallyMapped = [](const SharedPtr<MG_State::GLState::BufferObject>& buffer) {
return buffer->IsMapped() && !Any(buffer->GetMappingAccess() & BufferMappingAccessBit::Persistent);
};
if (isIllegallyMapped(readBufferObject) || isIllegallyMapped(writeBufferObject)) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "CopyBufferSubData_State",
"Cannot copy data from/to a mapped buffer object unless it was mapped with GL_MAP_PERSISTENT_BIT."));
return;
}
writeBufferObject->CopyDataFrom(readBufferObject, readOffset, writeOffset, size);
}
void BufferSubData_State(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
if (!data) {
MG_State::pGLContext->RecordError(ErrorCode::NoError, // somehow OpenGL does not generate an error for this
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
"Data pointer cannot be null."));
return;
}
if (size < 0 || offset < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
"Offset and size must be non-negative."));
return;
}
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
"Buffer target is bound to no buffer object."));
return;
}
SizeT bufferSize = bufferObject->GetSize();
Range1D mappedRange = bufferObject->GetMappedRange();
if ((offset < mappedRange.end) && (offset + size > mappedRange.start)) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
"Offset and size must not overlap with the mapped range of the buffer object."));
return;
}
BufferMappingAccessBit mappingAccess = bufferObject->GetMappingAccess();
if (bufferObject->IsMapped() && !Any(mappingAccess & BufferMappingAccessBit::Persistent)) {
Range1D mappedRange = bufferObject->GetMappedRange();
if (offset + size >= mappedRange.start) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferSubData_State",
"Cannot modify a mapped buffer object unless it was mapped with GL_MAP_PERSISTENT_BIT."));
return;
}
}
bufferObject->UploadSubData({ (void*)data, (SizeT)size }, offset);
}
void BufferData_State(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
if (size < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferData_State",
"Size must be non-negative."));
return;
}
BufferUsage bufferUsage = MG_Util::ConvertGLEnumToBufferUsage(usage);
if (!BufferImpl::ValidateBufferUsage(bufferUsage)) return;
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
auto bufferObject = bindingSlot.GetBoundObject();
if (!bufferObject) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "BufferData_State",
"Buffer target is bound to no buffer object."));
return;
}
bufferObject->SetUsage(bufferUsage);
bufferObject->Resize(size);
if (data) {
bufferObject->UploadData({ (void*)data, (SizeT)size }, 0);
}
}
void BindBuffer_State(GLenum target, GLuint buffer) {
if (buffer != 0 && !BufferImpl::ValidateBufferName(buffer)) return;
BufferTarget bufferTarget = MG_Util::ConvertGLEnumToBufferTarget(target);
if (!BufferImpl::ValidateBufferTarget(bufferTarget)) return;
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
if (!bufferObject) {
MG_State::pGLContext->CreateBufferObject(buffer);
bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
}
auto& bindingSlot = MG_State::pGLContext->GetBufferBindingSlot(bufferTarget);
bindingSlot.Bind(bufferObject);
}
void GenBuffers_State(GLsizei n, GLuint* buffers) {
if (n < 0) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl", "GenBuffers_State", "n must be non-negative"));
return;
}
auto bufferNames = MG_State::pGLContext->GenBufferNames(n);
Copy(bufferNames.data(), buffers, bufferNames.size());
}
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
void DeleteBuffers(GLsizei n, const GLuint* buffers) {
DeleteBuffers_State(n, buffers);
}
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length) {
FlushMappedBufferRange_State(target, offset, length);
}
GLboolean UnmapBuffer(GLenum target) {
return UnmapBuffer_State(target);
}
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) {
return MapBufferRange_State(target, offset, length, access);
}
void* MapBuffer(GLenum target, GLenum access) {
return MapBuffer_State(target, access);
}
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) {
CopyBufferSubData_State(readTarget, writeTarget, readOffset, writeOffset, size);
}
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) {
BufferSubData_State(target, offset, size, data);
}
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
BufferData_State(target, size, data, usage);
}
void BindBuffer(GLenum target, GLuint buffer) {
BindBuffer_State(target, buffer);
}
void GenBuffers(GLsizei n, GLuint* buffers) {
GenBuffers_State(n, buffers);
}
}
}
@@ -0,0 +1,17 @@
#pragma once
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
void DeleteBuffers(GLsizei n, const GLuint* buffers);
void FlushMappedBufferRange(GLenum target, GLintptr offset, GLsizeiptr length);
GLboolean UnmapBuffer(GLenum target);
void* MapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
void* MapBuffer(GLenum target, GLenum access);
void CopyBufferSubData(GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size);
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
void BindBuffer(GLenum target, GLuint buffer);
void GenBuffers(GLsizei n, GLuint* buffers);
}
}
@@ -0,0 +1,71 @@
#include "../../../Includes.h"
namespace MobileGL::MG_Impl::GLImpl {
namespace BufferImpl {
bool ValidateBufferTarget(BufferTarget target) {
if (target != BufferTarget::Unknown)
return true;
using namespace MG_Util;
String bufferTargetStr = ConvertBufferTargetToString(target);
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
std::format("Target {} ({}) is not valid.",
bufferTargetStr, glTargetStr)));
return false;
}
bool ValidateBufferName(Uint index) {
bool isValid = MG_State::pGLContext->ValidateBufferName(index);
if (isValid)
return true;
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName",
std::format("Buffer name {} is not valid.", index)));
return false;
}
bool ValidateBufferUsage(BufferUsage usage) {
if (usage != BufferUsage::Unknown) {
return true;
}
using namespace MG_Util;
String bufferUsageStr = ConvertBufferUsageToString(usage);
String glUsageStr = ConvertGLEnumToString(ConvertBufferUsageToGLEnum(usage));
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferUsage",
std::format("Usage {} ({}) is not one of the allowable values.",
bufferUsageStr, glUsageStr)));
return false;
}
bool ValidateBufferMappingAccess(BufferMappingAccessBit accessBits) {
if (accessBits == BufferMappingAccessBit::Null) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
"Access bits cannot be null."));
return false;
}
const BufferMappingAccessBit validBits =
BufferMappingAccessBit::Read |
BufferMappingAccessBit::Write |
BufferMappingAccessBit::InvalidateRange |
BufferMappingAccessBit::InvalidateBuffer |
BufferMappingAccessBit::FlushExplicit |
BufferMappingAccessBit::Unsynchronized |
BufferMappingAccessBit::Persistent |
BufferMappingAccessBit::Coherent;
if ((accessBits & validBits) != accessBits) {
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
MakeShared<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
"Access bits cannot contain invalid flags."));
return false;
}
return true;
}
}
}
@@ -0,0 +1,10 @@
#pragma once
namespace MobileGL::MG_Impl::GLImpl {
namespace BufferImpl {
bool ValidateBufferTarget(BufferTarget target);
bool ValidateBufferName(Uint index);
bool ValidateBufferUsage(BufferUsage usage);
bool ValidateBufferMappingAccess(BufferMappingAccessBit accessBits);
}
}
@@ -27,12 +27,12 @@ MOBILEGL_GL_API type gl##name(__VA_ARGS__) {
DECLARE_GL_FUNCTION_HEAD(const GLubyte*, GetStringi, GLenum name, GLuint index) DECLARE_GL_FUNCTION_END(const GLubyte*, GetStringi, name, index)
DECLARE_GL_FUNCTION_HEAD(const GLubyte*, GetString, GLenum name) DECLARE_GL_FUNCTION_END(const GLubyte*, GetString, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void*, MapBuffer, GLenum target, GLenum access) DECLARE_GL_FUNCTION_STUB_END(void*, MapBuffer, target, access)
DECLARE_GL_FUNCTION_HEAD(void*, MapBuffer, GLenum target, GLenum access) DECLARE_GL_FUNCTION_END(void*, MapBuffer, target, access)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearDepth, GLclampd depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearDepth, depth)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ActiveTexture, GLenum texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ActiveTexture, texture)
DECLARE_GL_FUNCTION_STUB_HEAD(void, AttachShader, GLuint program, GLuint shader) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, AttachShader, program, shader)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindAttribLocation, GLuint program, GLuint index, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindAttribLocation, program, index, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindBuffer, GLenum target, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindBuffer, target, buffer)
DECLARE_GL_FUNCTION_HEAD(void, BindBuffer, GLenum target, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindBuffer, target, buffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFramebuffer, GLenum target, GLuint framebuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFramebuffer, target, framebuffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindRenderbuffer, target, renderbuffer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindTexture, GLenum target, GLuint texture) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindTexture, target, texture)
@@ -41,8 +41,8 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquation, GLenum mode) DECLARE_GL_FUNCT
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendEquationSeparate, GLenum modeRGB, GLenum modeAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendEquationSeparate, modeRGB, modeAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFunc, GLenum sfactor, GLenum dfactor) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFunc, sfactor, dfactor)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BlendFuncSeparate, GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlendFuncSeparate, sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BufferData, GLenum target, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BufferData, target, size, data, usage)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BufferSubData, target, offset, size, data)
DECLARE_GL_FUNCTION_HEAD(void, BufferData, GLenum target, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferData, target, size, data, usage)
DECLARE_GL_FUNCTION_HEAD(void, BufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferSubData, target, offset, size, data)
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, CheckFramebufferStatus, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLenum, CheckFramebufferStatus, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, Clear, GLbitfield mask) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, Clear, mask)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearColor, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearColor, red, green, blue, alpha)
@@ -57,7 +57,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTexSubImage2D, GLenum target, GLint leve
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateProgram) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateProgram)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateShader, GLenum type) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateShader, type)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CullFace, GLenum mode) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CullFace, mode)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteBuffers, GLsizei n, const GLuint* buffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteBuffers, n, buffers)
DECLARE_GL_FUNCTION_HEAD(void, DeleteBuffers, GLsizei n, const GLuint* buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteBuffers, n, buffers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteFramebuffers, GLsizei n, const GLuint* framebuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteFramebuffers, n, framebuffers)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteProgram, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteProgram, program)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteRenderbuffers, GLsizei n, const GLuint* renderbuffers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteRenderbuffers, n, renderbuffers)
@@ -89,7 +89,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, GetAttachedShaders, GLuint program, GLsizei
DECLARE_GL_FUNCTION_STUB_HEAD(GLint, GetAttribLocation, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END(GLint, GetAttribLocation, program, name)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBooleanv, GLenum pname, GLboolean* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBooleanv, pname, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferParameteriv, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(GLenum, GetError) DECLARE_GL_FUNCTION_STUB_END(GLenum, GetError)
DECLARE_GL_FUNCTION_HEAD(GLenum, GetError) DECLARE_GL_FUNCTION_END(GLenum, GetError)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetFloatv, GLenum pname, GLfloat* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetFloatv, pname, data)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetFramebufferAttachmentParameteriv, GLenum target, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetFramebufferAttachmentParameteriv, target, attachment, pname, params)
DECLARE_GL_FUNCTION_HEAD(void, GetIntegerv, GLenum pname, GLint* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetIntegerv, pname, data)
@@ -184,7 +184,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, BeginQuery, GLenum target, GLuint id) DECLAR
DECLARE_GL_FUNCTION_STUB_HEAD(void, EndQuery, GLenum target) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, EndQuery, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryiv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryiv, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryObjectuiv, GLuint id, GLenum pname, GLuint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryObjectuiv, id, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, UnmapBuffer, GLenum target) DECLARE_GL_FUNCTION_STUB_END(GLboolean, UnmapBuffer, target)
DECLARE_GL_FUNCTION_HEAD(GLboolean, UnmapBuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLboolean, UnmapBuffer, target)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetBufferPointerv, GLenum target, GLenum pname, void** params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetBufferPointerv, target, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DrawBuffers, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DrawBuffers, n, bufs)
DECLARE_GL_FUNCTION_STUB_HEAD(void, UniformMatrix2x3fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UniformMatrix2x3fv, location, count, transpose, value)
@@ -197,7 +197,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitFramebuffer, GLint srcX0, GLint srcY0, G
//DECLARE_GL_FUNCTION_STUB_HEAD(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BlitFramebuffer, srcX0,srcY0,srcX1,srcY1,dstX0,dstY0,dstX1,dstY1,mask,filter)
DECLARE_GL_FUNCTION_STUB_HEAD(void, RenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, RenderbufferStorageMultisample, target, samples, internalformat, width, height)
DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferTextureLayer, GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferTextureLayer, target, attachment, texture, level, layer)
DECLARE_GL_FUNCTION_STUB_HEAD(void, FlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FlushMappedBufferRange, target, offset, length)
DECLARE_GL_FUNCTION_HEAD(void, FlushMappedBufferRange, GLenum target, GLintptr offset, GLsizeiptr length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FlushMappedBufferRange, target, offset, length)
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindVertexArray, GLuint array) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindVertexArray, array)
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteVertexArrays, GLsizei n, const GLuint* arrays) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteVertexArrays, n, arrays)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenVertexArrays, GLsizei n, GLuint* arrays) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenVertexArrays, n, arrays)
@@ -230,7 +230,7 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferiv, GLenum buffer, GLint drawbuff
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferuiv, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearBufferuiv, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferfv, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearBufferfv, buffer, drawbuffer, value)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearBufferfi, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearBufferfi, buffer, drawbuffer, depth, stencil)
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size)
DECLARE_GL_FUNCTION_HEAD(void, CopyBufferSubData, GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyBufferSubData, readTarget, writeTarget, readOffset, writeOffset, size)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetUniformIndices, GLuint program, GLsizei uniformCount, const GLchar* const* uniformNames, GLuint* uniformIndices) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetUniformIndices, program, uniformCount, uniformNames, uniformIndices)
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetActiveUniformsiv, GLuint program, GLsizei uniformCount, const GLuint* uniformIndices, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetActiveUniformsiv, program, uniformCount, uniformIndices, pname, params)
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, GetUniformBlockIndex, GLuint program, const GLchar* uniformBlockName) DECLARE_GL_FUNCTION_STUB_END(GLuint, GetUniformBlockIndex, program, uniformBlockName)
@@ -2792,4 +2792,4 @@ DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fVertex3fvSUN, con
DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fNormal3fVertex3fSUN, GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReplacementCodeuiTexCoord2fNormal3fVertex3fSUN, rc, s, t, nx, ny, nz, x, y, z)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN, const GLuint* rc, const GLfloat* tc, const GLfloat* n, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN, rc, tc, n, v)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN, GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN, rc, s, t, r, g, b, a, nx, ny, nz, x, y, z)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN, const GLuint* rc, const GLfloat* tc, const GLfloat* c, const GLfloat* n, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN, rc, tc, c, n, v)
DECLARE_GL_FUNCTION_STUB_HEAD(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN, const GLuint* rc, const GLfloat* tc, const GLfloat* c, const GLfloat* n, const GLfloat* v) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN, rc, tc, c, n, v)
+11 -5
View File
@@ -2,11 +2,16 @@
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
const GLubyte* GetString(GLenum name) {
static String vendorString;
static String versionStr;
static String rendererString;
static String shadingLanguageVersion;
static String extensionsString;
MGLOG_D("glGetString, name: %s", MG_Util::ConvertGLEnumToString(name).c_str());
switch (name) {
case GL_VENDOR:
static String vendorString;
if (vendorString.empty()) {
if (MG_Config::RendererInfoPtr->ExtraVendor.has_value()) {
vendorString = std::format("{}{}",
@@ -17,7 +22,6 @@ namespace MobileGL {
}
return (const GLubyte*)vendorString.c_str();
case GL_VERSION: {
static String versionStr;
if (versionStr.empty()) {
versionStr = std::format("{} {}, {} Backend, Version {}",
MG_Config::RendererInfoPtr->RendererGLInfo.TargetGLVersion.toString().c_str(),
@@ -30,7 +34,6 @@ namespace MobileGL {
case GL_RENDERER:
{
static String rendererString;
if (rendererString.empty()) {
String deviceInfo;
rendererString = std::format("{} ({}) ({})",
@@ -41,7 +44,6 @@ namespace MobileGL {
return (const GLubyte*)rendererString.c_str();
}
case GL_SHADING_LANGUAGE_VERSION:
static String shadingLanguageVersion;
if (shadingLanguageVersion.empty()) {
shadingLanguageVersion = std::format("{} MobileGL",
MG_Config::RendererInfoPtr->RendererGLInfo.TargetGLSLVersion.toString(
@@ -49,7 +51,6 @@ namespace MobileGL {
}
return (const GLubyte*)shadingLanguageVersion.c_str();
case GL_EXTENSIONS:
static String extensionsString;
if (extensionsString.empty()) {
for (auto& ext : MG_Config::RendererInfoPtr->RendererGLInfo.Extensions) {
extensionsString += MG_Util::ConvertetGLExtToString(ext);
@@ -124,5 +125,10 @@ namespace MobileGL {
break;
}
}
GLenum GetError() {
ErrorCode errorCode = MG_State::pGLContext->PopGLError().value_or(Error{ ErrorCode::NoError, nullptr }).code;
return MG_Util::ConvertErrorCodeToGLEnum(errorCode);
}
}
}
@@ -2,8 +2,10 @@
namespace MobileGL {
namespace MG_Impl::GLImpl {
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
const GLubyte* GetString(GLenum name);
const GLubyte* GetStringi(GLenum name, GLuint index);
void GetIntegerv(GLenum pname, GLint* params);
GLenum GetError();
}
}
@@ -16,7 +16,8 @@ namespace MobileGL {
DispatchIndirect,
DrawIndirect,
ShaderStorage,
BufferTargetCount
BufferTargetCount,
Unknown = -1
};
enum class BufferUsage {
@@ -28,7 +29,8 @@ namespace MobileGL {
StaticCopy,
DynamicDraw,
DynamicRead,
DynamicCopy
DynamicCopy,
Unknown = -1
};
enum class BufferMappingAccessBit : Uint {
@@ -38,7 +40,9 @@ namespace MobileGL {
InvalidateRange = 0x04,
InvalidateBuffer = 0x08,
FlushExplicit = 0x10,
Unsynchronized = 0x20
Unsynchronized = 0x20,
Persistent = 0x40,
Coherent = 0x80
};
inline BufferMappingAccessBit operator|(BufferMappingAccessBit a, BufferMappingAccessBit b) {
@@ -56,7 +60,7 @@ namespace MobileGL {
return static_cast<BufferMappingAccessBit>(static_cast<T>(a) & static_cast<T>(b));
}
inline bool any(BufferMappingAccessBit a) {
inline bool Any(BufferMappingAccessBit a) {
using T = std::underlying_type_t<BufferMappingAccessBit>;
return static_cast<T>(a) != 0;
}
@@ -76,7 +80,7 @@ namespace MobileGL {
void* AcquireMemoryRange(Range1D range, BufferMappingAccessBit access);
void ReleaseMemory();
void FlushMemoryRange(SizeT offset, SizeT length);
void UploadSubData(SizeT offset, SizeT size, const void* data);
void UploadSubData(DataPtr data, SizeT atOffset);
void CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size);
void ClearDirty();
@@ -84,7 +88,8 @@ namespace MobileGL {
SizeT GetSize() const;
BufferUsage GetUsage() const;
Range1D GetDirtyRange() const;
Range1D GetMappedRange() const;
BufferMappingAccessBit GetMappingAccess() const;
private:
Int m_id = 0;
SizeT m_size = 0;
@@ -31,8 +31,8 @@ namespace MobileGL {
void BufferObject::ReleaseMemory() {
if (!m_isMapped) return;
if (any(m_mappingAccess & BufferMappingAccessBit::Write)) { // if we wrote to the buffer
if (!any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
if (Any(m_mappingAccess & BufferMappingAccessBit::Write)) { // if we wrote to the buffer
if (!Any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit)) { // if we didn't flush explicitly
memcpy(m_data.data() + m_mappedRange.start,
m_stagingData.data(),
m_mappedRange.end - m_mappedRange.start);
@@ -50,8 +50,8 @@ namespace MobileGL {
void BufferObject::FlushMemoryRange(SizeT offset, SizeT length) {
assert(m_isMapped);
assert(any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit));
assert(any(m_mappingAccess & BufferMappingAccessBit::Write));
assert(Any(m_mappingAccess & BufferMappingAccessBit::FlushExplicit));
assert(Any(m_mappingAccess & BufferMappingAccessBit::Write));
SizeT start = m_mappedRange.start + offset;
SizeT end = start + length;
@@ -61,12 +61,12 @@ namespace MobileGL {
m_dirtyRange.UnionUpdate(start, end);
}
void BufferObject::UploadSubData(SizeT offset, SizeT size, const void* data) {
void BufferObject::UploadSubData(DataPtr data, SizeT atOffset) {
assert(!m_isMapped);
assert(offset + size <= m_size);
assert(atOffset + data.size <= m_size);
memcpy(m_data.data() + offset, data, size);
m_dirtyRange.UnionUpdate(offset, offset + size);
memcpy(m_data.data() + atOffset, data.data, data.size);
m_dirtyRange.UnionUpdate(atOffset, atOffset + data.size);
}
void BufferObject::CopyDataFrom(const SharedPtr<BufferObject>& src, SizeT srcOffset, SizeT dstOffset, SizeT size) {
@@ -88,11 +88,11 @@ namespace MobileGL {
(write ? BufferMappingAccessBit::Write : BufferMappingAccessBit::Null);
m_mappedRange = { 0, m_size };
if (any(m_mappingAccess & BufferMappingAccessBit::Write)) {
if (Any(m_mappingAccess & BufferMappingAccessBit::Write)) {
m_stagingData.resize(m_size);
m_ownsStagingData = true;
if (!any(m_mappingAccess & (BufferMappingAccessBit::InvalidateRange |
if (!Any(m_mappingAccess & (BufferMappingAccessBit::InvalidateRange |
BufferMappingAccessBit::InvalidateBuffer))) {
memcpy(m_stagingData.data(), m_data.data(), m_size);
}
@@ -110,11 +110,11 @@ namespace MobileGL {
m_mappingAccess = access;
m_mappedRange = range;
if (any(access & BufferMappingAccessBit::Write)) {
if (Any(access & BufferMappingAccessBit::Write)) {
m_stagingData.resize(range.end - range.start);
m_ownsStagingData = true;
if (!any(access & (BufferMappingAccessBit::InvalidateRange |
if (!Any(access & (BufferMappingAccessBit::InvalidateRange |
BufferMappingAccessBit::InvalidateBuffer))) {
memcpy(m_stagingData.data(), m_data.data() + range.start, m_stagingData.size());
}
@@ -147,7 +147,22 @@ namespace MobileGL {
return m_isMapped;
}
Range1D BufferObject::GetMappedRange() const {
return m_isMapped ? m_mappedRange : Range1D{ 0, 0 };
}
BufferMappingAccessBit BufferObject::GetMappingAccess() const {
return m_isMapped ? m_mappingAccess : BufferMappingAccessBit::Null;
}
// BufferState
BufferState::BufferState()
: m_indexGenerator(1024, 1) {
for (SizeT i = 0; i < (SizeT)BufferTarget::BufferTargetCount; ++i) {
m_bindingSlots[i] = BindingSlot<BufferObject>((BufferTarget)i);
}
}
SharedPtr<BufferObject> BufferState::GetBufferObject(Uint index) {
auto it = m_bufferObjects.find(index);
if (it != m_bufferObjects.end()) {
@@ -171,6 +186,29 @@ namespace MobileGL {
BindingSlot<BufferObject>& BufferState::GetBindingSlot(BufferTarget target) {
return m_bindingSlots[(SizeT)target];
}
void BufferState::MarkBufferObjectForDeletion(Uint index) {
if (m_indexGenerator.IsValid(index)) {
auto it = m_bufferObjects.find(index);
if (it != m_bufferObjects.end()) {
for (SizeT i = 0; i < (SizeT)BufferTarget::BufferTargetCount; ++i) {
if (m_bindingSlots[i].GetBoundObject() == it->second) {
m_bindingSlots[i].Bind(nullptr);
}
}
m_bufferObjects.erase(it);
}
m_indexGenerator.Delete(index);
}
}
bool BufferState::ValidateName(Uint index) const {
return m_indexGenerator.IsValid(index);
}
bool BufferState::ValidateBufferObject(Uint index) const {
return m_bufferObjects.find(index) != m_bufferObjects.end();
}
}
}
}
@@ -5,16 +5,15 @@ namespace MobileGL {
namespace GLState {
class BufferState {
public:
BufferState() {
for (SizeT i = 0; i < (SizeT)BufferTarget::BufferTargetCount; ++i) {
m_bindingSlots[i] = BindingSlot<BufferObject>((BufferTarget)i);
}
}
BufferState();
SharedPtr<BufferObject> GetBufferObject(Uint index);
Vector<Uint> GenerateNames(Uint number);
SharedPtr<BufferObject> CreateBufferObject(Uint index);
BindingSlot<BufferObject>& GetBindingSlot(BufferTarget target);
void MarkBufferObjectForDeletion(Uint index);
bool ValidateName(Uint index) const;
bool ValidateBufferObject(Uint index) const;
private:
UnorderedMap<Uint, SharedPtr<BufferObject>> m_bufferObjects;
+50 -4
View File
@@ -3,20 +3,66 @@
namespace MobileGL {
namespace MG_State {
namespace GLState {
// Error
void GLContext::RecordError(ErrorCode code, SharedPtr<ErrorInfo> info) {
m_errorState.RecordError(code, info);
}
bool GLContext::HasGLError() const {
return m_errorState.HasGLError();
}
Optional<const Error> GLContext::PeekGLError() const {
return m_errorState.PeekGLError();
}
Optional<Error> GLContext::PopGLError() {
return m_errorState.PopGLError();
}
bool GLContext::HasNonGLError() const {
return m_errorState.HasNonGLError();
}
Optional<const Error> GLContext::PeekNonGLError() const {
return m_errorState.PeekNonGLError();
}
Optional<Error> GLContext::PopNonGLError() {
return m_errorState.PopNonGLError();
}
void GLContext::ClearErrors() {
m_errorState.Clear();
}
// Buffer
Vector<Uint> GLContext::GenBufferNames(Uint number) {
return bufferState_.GenerateNames(number);
return m_bufferState.GenerateNames(number);
}
SharedPtr<BufferObject> GLContext::GetBufferObject(Uint index) {
return bufferState_.GetBufferObject(index);
return m_bufferState.GetBufferObject(index);
}
BindingSlot<BufferObject>& GLContext::GetBufferBindingSlot(BufferTarget target) {
return bufferState_.GetBindingSlot(target);
return m_bufferState.GetBindingSlot(target);
}
SharedPtr<BufferObject> GLContext::CreateBufferObject(Uint index) {
return bufferState_.CreateBufferObject(index);
return m_bufferState.CreateBufferObject(index);
}
void GLContext::MarkBufferObjectForDeletion(Uint index) {
m_bufferState.MarkBufferObjectForDeletion(index);
}
bool GLContext::ValidateBufferName(Uint index) const {
return m_bufferState.ValidateName(index);
}
bool GLContext::ValidateBufferObject(Uint index) const {
return m_bufferState.ValidateBufferObject(index);
}
}
+18 -2
View File
@@ -7,15 +7,31 @@ namespace MobileGL {
public:
GLContext() = default;
// Error
void RecordError(ErrorCode code, SharedPtr<ErrorInfo> info = nullptr);
bool HasGLError() const;
Optional<const Error> PeekGLError() const;
Optional<Error> PopGLError();
bool HasNonGLError() const;
Optional<const Error> PeekNonGLError() const;
Optional<Error> PopNonGLError();
void ClearErrors();
// Buffer
Vector<Uint> GenBufferNames(Uint number);
SharedPtr<BufferObject> GetBufferObject(Uint index);
BindingSlot<BufferObject>& GetBufferBindingSlot(BufferTarget target);
SharedPtr<BufferObject> CreateBufferObject(Uint index);
SharedPtr<BufferObject> CreateBufferObject(Uint index);
void MarkBufferObjectForDeletion(Uint index);
bool ValidateBufferName(Uint index) const;
bool ValidateBufferObject(Uint index) const;
private:
// Error
ErrorState m_errorState;
// Buffer
BufferState bufferState_;
BufferState m_bufferState;
};
}
@@ -0,0 +1,58 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_State {
namespace GLState {
void ErrorState::RecordError(ErrorCode code, SharedPtr<ErrorInfo> info) {
if (code == ErrorCode::NoError) {
MGLOG_E("Recording Non-OpenGL error:\n%s",
info->ToString().c_str());
m_nonGLErrors.push_back(Error{ code, info });
}
else {
MGLOG_E("Recording OpenGL error (%s):\n%s",
MG_Util::ConvertGLEnumToString(MG_Util::ConvertErrorCodeToGLEnum(code)).c_str(),
info->ToString().c_str());
m_errors.push_back(Error{ code, info });
}
}
bool ErrorState::HasNonGLError() const {
return !m_nonGLErrors.empty();
}
Optional<const Error> ErrorState::PeekNonGLError() const {
if (m_nonGLErrors.empty()) return Optional<const Error>{ };
return Optional<const Error>{m_nonGLErrors.front()};
}
Optional<Error> ErrorState::PopNonGLError() {
if (m_nonGLErrors.empty()) return Optional<const Error>{ };
auto error = Move(m_nonGLErrors.front());
m_nonGLErrors.erase(m_nonGLErrors.begin());
return Optional<const Error>{ error };
}
bool ErrorState::HasGLError() const {
return !m_errors.empty();
}
Optional<const Error> ErrorState::PeekGLError() const {
if (m_errors.empty()) return Optional<const Error>{ };
return Optional<const Error>{m_errors.front()};
}
Optional<Error> ErrorState::PopGLError() {
if (m_errors.empty()) return Optional<const Error>{ };
auto error = Move(m_errors.front());
m_errors.erase(m_errors.begin());
return Optional<const Error>{ error };
}
void ErrorState::Clear() {
m_errors.clear();
m_nonGLErrors.clear();
}
}
}
}
@@ -0,0 +1,28 @@
#pragma once
namespace MobileGL {
struct Error {
ErrorCode code;
SharedPtr<ErrorInfo> info;
};
namespace MG_State {
namespace GLState {
class ErrorState {
public:
void RecordError(ErrorCode code, SharedPtr<ErrorInfo> info = nullptr);
bool HasNonGLError() const;
Optional<const Error> PeekNonGLError() const;
Optional<Error> PopNonGLError();
bool HasGLError() const;
Optional<const Error> PeekGLError() const;
Optional<Error> PopGLError();
void Clear();
private:
Vector<Error> m_errors;
Vector<Error> m_nonGLErrors;
};
}
}
}
@@ -0,0 +1,14 @@
#pragma once
namespace MobileGL {
enum class ErrorCode {
NoError = 0,
InvalidEnum,
InvalidValue,
InvalidOperation,
InvalidFramebufferOperation,
OutOfMemory,
StackUnderflow,
StackOverflow
};
}
@@ -0,0 +1,32 @@
#pragma once
namespace MobileGL {
class ErrorInfo {
public:
virtual ~ErrorInfo() = default;
virtual String ToString() const = 0;
};
class GenericErrorInfo : public ErrorInfo {
public:
explicit GenericErrorInfo(String message) : m_message(Move(message)) {}
explicit GenericErrorInfo(String m_prefix, String message) : m_message(Move(message)), m_prefix(Move(m_prefix)) {}
explicit GenericErrorInfo(String m_prefix, String m_prefix_2, String message) : m_message(Move(message)), m_prefix(Move(m_prefix)), m_prefix_2(Move(m_prefix_2)) {}
String ToString() const override {
StringStream ss;
if (m_prefix.has_value()) {
ss << "[" << m_prefix.value() << "] ";
}
if (m_prefix_2.has_value()) {
ss << "[" << m_prefix_2.value() << "] ";
}
ss << m_message;
return ss.str();
}
private:
String m_message;
Optional<String> m_prefix;
Optional<String> m_prefix_2;
};
}
+249 -3
View File
@@ -291,9 +291,8 @@ TEST_F(BufferTest, PartialUpdate) {
Vector<Int> update{ 999, 888 };
bufObj->UploadSubData(
sizeof(Int),
update.size() * sizeof(Int),
update.data()
{ (void*)(update.data()), (SizeT)(update.size() * sizeof(Int)) },
sizeof(Int)
);
Vector<Int> expected{ 100, 999, 888, 400, 500 };
@@ -307,3 +306,250 @@ TEST_F(BufferTest, PartialUpdate) {
ASSERT_EQ(dirty.start, sizeof(Int));
ASSERT_EQ(dirty.end, 3 * sizeof(Int));
}
TEST_F(BufferTest, DeleteBufferObject) {
auto bufferNames = glContext.GenBufferNames(1);
auto& slot = glContext.GetBufferBindingSlot(BufferTarget::Vertex);
auto bufObj = glContext.CreateBufferObject(bufferNames[0]);
slot.Bind(bufObj);
ASSERT_TRUE(slot.GetBoundObject() == bufObj);
glContext.MarkBufferObjectForDeletion(bufferNames[0]);
ASSERT_TRUE(slot.GetBoundObject() == nullptr);
ASSERT_FALSE(glContext.GetBufferObject(bufferNames[0]));
}
using namespace MobileGL::MG_Impl::GLImpl;
class GeneralBufferTest : public ::testing::Test {
protected:
void SetUp() override {
MG_State::pGLContext = new MG_State::GLState::GLContext();
}
GLuint CreateBoundBuffer(GLenum target, GLsizeiptr size, GLenum usage) {
GLuint buffer;
GenBuffers(1, &buffer);
BindBuffer(target, buffer);
BufferData(target, size, nullptr, usage);
EXPECT_EQ(GetError(), GL_NO_ERROR);
return buffer;
}
};
TEST_F(GeneralBufferTest, General_BufferLifecycle) {
GLuint buffers[3];
GenBuffers(3, buffers);
EXPECT_NE(buffers[0], 0);
EXPECT_NE(buffers[1], 0);
EXPECT_NE(buffers[2], 0);
EXPECT_NE(buffers[0], buffers[1]);
GLuint deleteBuf = buffers[1];
DeleteBuffers(1, &deleteBuf);
BindBuffer(GL_ARRAY_BUFFER, deleteBuf);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_BufferDataOperations) {
GLuint buffer = CreateBoundBuffer(GL_ARRAY_BUFFER, 100, GL_STATIC_DRAW);
const char initData[100] = { 0 };
char readBack[100];
void* mapped = MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
ASSERT_NE(mapped, nullptr);
memcpy(readBack, mapped, 100);
EXPECT_EQ(memcmp(initData, readBack, 100), 0);
UnmapBuffer(GL_ARRAY_BUFFER);
const char subData[] = "TEST";
BufferSubData(GL_ARRAY_BUFFER, 10, sizeof(subData), subData);
mapped = MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
memcpy(readBack, (char*)mapped + 10, sizeof(subData));
EXPECT_STREQ(readBack, "TEST");
UnmapBuffer(GL_ARRAY_BUFFER);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_BufferCopy) {
GLuint src = CreateBoundBuffer(GL_COPY_READ_BUFFER, 50, GL_STATIC_READ);
GLuint dst = CreateBoundBuffer(GL_COPY_WRITE_BUFFER, 50, GL_STATIC_DRAW);
const char srcData[] = "SOURCE_BUFFER_DATA";
BufferSubData(GL_COPY_READ_BUFFER, 0, sizeof(srcData), srcData);
void* srcMappedData = MapBuffer(GL_COPY_READ_BUFFER, GL_READ_ONLY);
EXPECT_STREQ((char*)srcMappedData, "SOURCE_BUFFER_DATA");
UnmapBuffer(GL_COPY_READ_BUFFER);
CopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 10, sizeof(srcData));
char result[50];
void* mapped = MapBuffer(GL_COPY_WRITE_BUFFER, GL_READ_ONLY);
memcpy(result, (char*)mapped + 10, sizeof(srcData));
EXPECT_STREQ(result, "SOURCE_BUFFER_DATA");
UnmapBuffer(GL_COPY_WRITE_BUFFER);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_BufferMapping) {
GLuint buffer = 0;
GenBuffers(1, &buffer);
BindBuffer(GL_ARRAY_BUFFER, buffer);
Vector<char> data;
data.resize(100, '\0');
BufferData(GL_ARRAY_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW);
void* fullMap = MapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
ASSERT_NE(fullMap, nullptr);
strcpy((char*)fullMap, " Full mapping test");
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
void* partialMap = MapBufferRange(GL_ARRAY_BUFFER, 0, 30,
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
ASSERT_NE(partialMap, nullptr);
strcpy((char*)partialMap, "Partial (not valid value)");
FlushMappedBufferRange(GL_ARRAY_BUFFER, 0, 7);
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
partialMap = MapBufferRange(GL_ARRAY_BUFFER, 20, 40,
GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
ASSERT_NE(partialMap, nullptr);
strcpy((char*)partialMap, ": modified data (not valid value)");
FlushMappedBufferRange(GL_ARRAY_BUFFER, 0, 15);
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
char verify[40];
void* verifyMap = MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY);
memcpy(verify, (char*)verifyMap, 40);
EXPECT_STREQ(verify, "Partial mapping test: modified data");
UnmapBuffer(GL_ARRAY_BUFFER);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_InvalidOperations) {
EXPECT_EQ(MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY), nullptr); // GL_INVALID_OPERATION
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
GLuint buffer = CreateBoundBuffer(GL_ARRAY_BUFFER, 50, GL_STREAM_READ);
EXPECT_EQ(MapBuffer(0xFFFFFFFF, GL_READ_ONLY), nullptr); // GL_INVALID_ENUM
void* mapped = MapBuffer(GL_ARRAY_BUFFER, GL_READ_WRITE);
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
EXPECT_FALSE(UnmapBuffer(GL_ARRAY_BUFFER)); // GL_INVALID_OPERATION
EXPECT_EQ(GetError(), GL_INVALID_ENUM);
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_MapFlags) {
GLuint buffer = CreateBoundBuffer(GL_ARRAY_BUFFER, 100, GL_DYNAMIC_COPY);
void* roMap = MapBufferRange(GL_ARRAY_BUFFER, 0, 100, GL_MAP_READ_BIT);
ASSERT_NE(roMap, nullptr);
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
void* nosyncMap = MapBufferRange(GL_ARRAY_BUFFER, 0, 100,
GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
ASSERT_NE(nosyncMap, nullptr);
memset(nosyncMap, 0xAA, 100);
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
TEST_F(GeneralBufferTest, General_GeneralTest_1) {
GLuint buffers[3];
GenBuffers(3, buffers);
const GLuint vbo = buffers[0];
const GLuint ibo = buffers[1];
const GLuint staging = buffers[2];
BindBuffer(GL_ARRAY_BUFFER, vbo);
BufferData(GL_ARRAY_BUFFER, 64, nullptr, GL_STATIC_DRAW);
const float vertexData[] = { 0.1f, 0.2f, 0.3f, 1.0f };
BufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertexData), vertexData);
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
const uint16_t indexData[] = { 0, 1, 2, 3, 0 };
BufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData, GL_STATIC_DRAW);
const uint16_t newIndices[] = { 4, 5 };
BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2 * sizeof(uint16_t),
sizeof(newIndices), newIndices);
BindBuffer(GL_COPY_READ_BUFFER, staging);
const char stagingData[] = "StagingBufferData";
BufferData(GL_COPY_READ_BUFFER, sizeof(stagingData), stagingData, GL_STREAM_COPY);
CopyBufferSubData(GL_COPY_READ_BUFFER, GL_ARRAY_BUFFER,
0, 40, sizeof(stagingData));
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
void* fullMap = MapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_WRITE);
ASSERT_NE(fullMap, nullptr);
uint16_t* indices = static_cast<uint16_t*>(fullMap);
indices[0] = 10;
EXPECT_TRUE(UnmapBuffer(GL_ELEMENT_ARRAY_BUFFER));
BindBuffer(GL_ARRAY_BUFFER, vbo);
void* partialMap = MapBufferRange(GL_ARRAY_BUFFER, 20, 8,
GL_MAP_WRITE_BIT |
GL_MAP_FLUSH_EXPLICIT_BIT);
ASSERT_NE(partialMap, nullptr);
const char partialWriteData[] = "PARTIAL"; // 7 chars + '\0' = 8 bytes
memcpy(partialMap, partialWriteData, sizeof(partialWriteData));
FlushMappedBufferRange(GL_ARRAY_BUFFER, 0, sizeof(partialWriteData));
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
BindBuffer(GL_ARRAY_BUFFER, vbo);
void* verifyMap = MapBufferRange(GL_ARRAY_BUFFER, 0, 64, GL_MAP_READ_BIT);
ASSERT_NE(verifyMap, nullptr);
const float* verts = static_cast<const float*>(verifyMap);
EXPECT_FLOAT_EQ(verts[0], 0.1f);
EXPECT_FLOAT_EQ(verts[1], 0.2f);
const char* partialData = static_cast<const char*>(verifyMap) + 20;
EXPECT_STREQ(partialData, "PARTIAL");
const char* copiedData = static_cast<const char*>(verifyMap) + 40;
EXPECT_STREQ(copiedData, "StagingBufferData");
EXPECT_TRUE(UnmapBuffer(GL_ARRAY_BUFFER));
BindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
void* iboMap = MapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY);
const uint16_t* finalIndices = static_cast<const uint16_t*>(iboMap);
EXPECT_EQ(finalIndices[0], 10);
EXPECT_EQ(finalIndices[2], 4);
EXPECT_TRUE(UnmapBuffer(GL_ELEMENT_ARRAY_BUFFER));
DeleteBuffers(1, &staging);
BindBuffer(GL_COPY_READ_BUFFER, staging);
GLenum err = GetError();
EXPECT_EQ(err, GL_INVALID_OPERATION);
GLuint toDelete[] = { vbo, ibo };
DeleteBuffers(2, toDelete);
EXPECT_EQ(GetError(), GL_NO_ERROR);
}
+13
View File
@@ -6,6 +6,19 @@ add_executable(
${MGL_ROOT}/MobileGL/MG_State/GLState/Core.cpp
${MGL_ROOT}/MobileGL/MG_State/GLState/BufferState/BufferState.cpp
${MGL_ROOT}/MobileGL/MG_State/GLState/ErrorState/Error.cpp
${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Buffer/GL_Buffer.cpp
${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp
${MGL_ROOT}/MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToStr/GLEnumConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToStr/GLExtensionConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToStr/BufferEnumConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToGlslang/GLShaderLangConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/ErrorCodeConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/MGToGL/BufferEnumConverter.cpp
${MGL_ROOT}/MobileGL/MG_Util/Converters/GLToMG/BufferEnumConverter.cpp
${MGL_ROOT}/MobileGL/MG_Backend/Init.cpp
${MGL_ROOT}/MobileGL/MG_Util/Debug/Log.cpp
)
target_include_directories(BufferTest PRIVATE
@@ -1,8 +1,4 @@
//
// Created by Swung 0x48 on 2025/7/20.
//
#include "Includes.h"
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
@@ -1,15 +1,7 @@
//
// Created by Swung 0x48 on 2025/7/20.
//
#ifndef MOBILEGL_GLSHADERLANGCONVERTER_H
#define MOBILEGL_GLSHADERLANGCONVERTER_H
#pragma once
namespace MobileGL {
namespace MG_Util {
EShLanguage ConvertGLEnumToEShLanguage(GLenum shaderType);
}
}
#endif //MOBILEGL_GLSHADERLANGCONVERTER_H
}
@@ -0,0 +1,54 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
BufferTarget ConvertGLEnumToBufferTarget(GLenum bufferTarget) {
switch (bufferTarget) {
case GL_ARRAY_BUFFER: return BufferTarget::Vertex;
case GL_ELEMENT_ARRAY_BUFFER: return BufferTarget::Index;
case GL_UNIFORM_BUFFER: return BufferTarget::Uniform;
case GL_COPY_READ_BUFFER: return BufferTarget::CopyRead;
case GL_COPY_WRITE_BUFFER: return BufferTarget::CopyWrite;
case GL_PIXEL_PACK_BUFFER: return BufferTarget::PixelPack;
case GL_PIXEL_UNPACK_BUFFER: return BufferTarget::PixelUnpack;
case GL_QUERY_BUFFER: return BufferTarget::Query;
case GL_TEXTURE_BUFFER: return BufferTarget::Texture;
case GL_TRANSFORM_FEEDBACK_BUFFER: return BufferTarget::TransformFeedback;
case GL_ATOMIC_COUNTER_BUFFER: return BufferTarget::AtomicCounter;
case GL_DISPATCH_INDIRECT_BUFFER: return BufferTarget::DispatchIndirect;
case GL_DRAW_INDIRECT_BUFFER: return BufferTarget::DrawIndirect;
case GL_SHADER_STORAGE_BUFFER: return BufferTarget::ShaderStorage;
case GL_UNKNOWN_MGL:
default: return BufferTarget::Unknown;
}
}
BufferUsage ConvertGLEnumToBufferUsage(GLenum usage) {
switch (usage) {
case GL_STREAM_DRAW: return BufferUsage::StreamDraw;
case GL_STREAM_READ: return BufferUsage::StreamRead;
case GL_STREAM_COPY: return BufferUsage::StreamCopy;
case GL_STATIC_DRAW: return BufferUsage::StaticDraw;
case GL_STATIC_READ: return BufferUsage::StaticRead;
case GL_STATIC_COPY: return BufferUsage::StaticCopy;
case GL_DYNAMIC_DRAW: return BufferUsage::DynamicDraw;
case GL_DYNAMIC_READ: return BufferUsage::DynamicRead;
case GL_DYNAMIC_COPY: return BufferUsage::DynamicCopy;
default: return BufferUsage::Unknown;
}
}
BufferMappingAccessBit ConvertGLEnumToBufferMappingAccess(GLbitfield access) {
BufferMappingAccessBit result = BufferMappingAccessBit::Null;
if (access & GL_MAP_READ_BIT) result |= BufferMappingAccessBit::Read;
if (access & GL_MAP_WRITE_BIT) result |= BufferMappingAccessBit::Write;
if (access & GL_MAP_INVALIDATE_RANGE_BIT) result |= BufferMappingAccessBit::InvalidateRange;
if (access & GL_MAP_INVALIDATE_BUFFER_BIT) result |= BufferMappingAccessBit::InvalidateBuffer;
if (access & GL_MAP_FLUSH_EXPLICIT_BIT) result |= BufferMappingAccessBit::FlushExplicit;
if (access & GL_MAP_UNSYNCHRONIZED_BIT) result |= BufferMappingAccessBit::Unsynchronized;
if (access & GL_MAP_PERSISTENT_BIT) result |= BufferMappingAccessBit::Persistent;
if (access & GL_MAP_COHERENT_BIT) result |= BufferMappingAccessBit::Coherent;
return result;
}
}
}
@@ -0,0 +1,9 @@
#pragma once
namespace MobileGL {
namespace MG_Util {
BufferTarget ConvertGLEnumToBufferTarget(GLenum bufferTarget);
BufferUsage ConvertGLEnumToBufferUsage(GLenum usage);
BufferMappingAccessBit ConvertGLEnumToBufferMappingAccess(GLbitfield access);
}
}
@@ -0,0 +1,54 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
GLenum ConvertBufferTargetToGLEnum(BufferTarget bufferTarget) {
switch (bufferTarget) {
case BufferTarget::Vertex: return GL_ARRAY_BUFFER;
case BufferTarget::Index: return GL_ELEMENT_ARRAY_BUFFER;
case BufferTarget::Uniform: return GL_UNIFORM_BUFFER;
case BufferTarget::CopyRead: return GL_COPY_READ_BUFFER;
case BufferTarget::CopyWrite: return GL_COPY_WRITE_BUFFER;
case BufferTarget::PixelPack: return GL_PIXEL_PACK_BUFFER;
case BufferTarget::PixelUnpack: return GL_PIXEL_UNPACK_BUFFER;
case BufferTarget::Query: return GL_QUERY_BUFFER;
case BufferTarget::Texture: return GL_TEXTURE_BUFFER;
case BufferTarget::TransformFeedback: return GL_TRANSFORM_FEEDBACK_BUFFER;
case BufferTarget::AtomicCounter: return GL_ATOMIC_COUNTER_BUFFER;
case BufferTarget::DispatchIndirect: return GL_DISPATCH_INDIRECT_BUFFER;
case BufferTarget::DrawIndirect: return GL_DRAW_INDIRECT_BUFFER;
case BufferTarget::ShaderStorage: return GL_SHADER_STORAGE_BUFFER;
case BufferTarget::Unknown:
default: return GL_UNKNOWN_MGL;
}
}
GLenum ConvertBufferUsageToGLEnum(BufferUsage usage) {
switch (usage) {
case BufferUsage::StreamDraw: return GL_STREAM_DRAW;
case BufferUsage::StreamRead: return GL_STREAM_READ;
case BufferUsage::StreamCopy: return GL_STREAM_COPY;
case BufferUsage::StaticDraw: return GL_STATIC_DRAW;
case BufferUsage::StaticRead: return GL_STATIC_READ;
case BufferUsage::StaticCopy: return GL_STATIC_COPY;
case BufferUsage::DynamicDraw: return GL_DYNAMIC_DRAW;
case BufferUsage::DynamicRead: return GL_DYNAMIC_READ;
case BufferUsage::DynamicCopy: return GL_DYNAMIC_COPY;
default: return GL_UNKNOWN_MGL;
}
}
GLbitfield ConvertBufferMappingAccessToGLEnum(BufferMappingAccessBit access) {
GLbitfield result = 0;
if (Any(access & BufferMappingAccessBit::Read)) result |= GL_MAP_READ_BIT;
if (Any(access & BufferMappingAccessBit::Write)) result |= GL_MAP_WRITE_BIT;
if (Any(access & BufferMappingAccessBit::InvalidateRange)) result |= GL_MAP_INVALIDATE_RANGE_BIT;
if (Any(access & BufferMappingAccessBit::InvalidateBuffer)) result |= GL_MAP_INVALIDATE_BUFFER_BIT;
if (Any(access & BufferMappingAccessBit::FlushExplicit)) result |= GL_MAP_FLUSH_EXPLICIT_BIT;
if (Any(access & BufferMappingAccessBit::Unsynchronized)) result |= GL_MAP_UNSYNCHRONIZED_BIT;
if (Any(access & BufferMappingAccessBit::Persistent)) result |= GL_MAP_PERSISTENT_BIT;
if (Any(access & BufferMappingAccessBit::Coherent)) result |= GL_MAP_COHERENT_BIT;
return result;
}
}
}
@@ -0,0 +1,9 @@
#pragma once
namespace MobileGL {
namespace MG_Util {
GLenum ConvertBufferTargetToGLEnum(BufferTarget bufferTarget);
GLenum ConvertBufferUsageToGLEnum(BufferUsage usage);
GLbitfield ConvertBufferMappingAccessToGLEnum(BufferMappingAccessBit access);
}
}
@@ -0,0 +1,28 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
GLenum ConvertErrorCodeToGLEnum(ErrorCode code) {
switch (code) {
case ErrorCode::NoError:
return GL_NO_ERROR;
case ErrorCode::InvalidEnum:
return GL_INVALID_ENUM;
case ErrorCode::InvalidValue:
return GL_INVALID_VALUE;
case ErrorCode::InvalidOperation:
return GL_INVALID_OPERATION;
case ErrorCode::InvalidFramebufferOperation:
return GL_INVALID_FRAMEBUFFER_OPERATION;
case ErrorCode::OutOfMemory:
return GL_OUT_OF_MEMORY;
case ErrorCode::StackUnderflow:
return GL_STACK_UNDERFLOW;
case ErrorCode::StackOverflow:
return GL_STACK_OVERFLOW;
default:
return GL_NO_ERROR;
}
}
}
}
@@ -0,0 +1,7 @@
#pragma once
namespace MobileGL {
namespace MG_Util {
GLenum ConvertErrorCodeToGLEnum(ErrorCode code);
}
}
@@ -0,0 +1,57 @@
#include "../../../Includes.h"
namespace MobileGL {
namespace MG_Util {
String ConvertBufferTargetToString(BufferTarget bufferTarget) {
switch (bufferTarget) {
case BufferTarget::Vertex: return "Vertex";
case BufferTarget::Index: return "Index";
case BufferTarget::Uniform: return "Uniform";
case BufferTarget::CopyRead: return "CopyRead";
case BufferTarget::CopyWrite: return "CopyWrite";
case BufferTarget::PixelPack: return "PixelPack";
case BufferTarget::PixelUnpack: return "PixelUnpack";
case BufferTarget::Query: return "Query";
case BufferTarget::Texture: return "Texture";
case BufferTarget::TransformFeedback: return "TransformFeedback";
case BufferTarget::AtomicCounter: return "AtomicCounter";
case BufferTarget::DispatchIndirect: return "DispatchIndirect";
case BufferTarget::DrawIndirect: return "DrawIndirect";
case BufferTarget::ShaderStorage: return "ShaderStorage";
case BufferTarget::Unknown:
default: return "Unknown";
}
}
String ConvertBufferUsageToString(BufferUsage usage) {
switch (usage) {
case BufferUsage::StreamDraw: return "StreamDraw";
case BufferUsage::StreamRead: return "StreamRead";
case BufferUsage::StreamCopy: return "StreamCopy";
case BufferUsage::StaticDraw: return "StaticDraw";
case BufferUsage::StaticRead: return "StaticRead";
case BufferUsage::StaticCopy: return "StaticCopy";
case BufferUsage::DynamicDraw: return "DynamicDraw";
case BufferUsage::DynamicRead: return "DynamicRead";
case BufferUsage::DynamicCopy: return "DynamicCopy";
default: return "Unknown";
}
}
String ConvertBufferMappingAccessToString(BufferMappingAccessBit access) {
String result = "[";
if (Any(access & BufferMappingAccessBit::Read)) result += "Read, ";
if (Any(access & BufferMappingAccessBit::Write)) result += "Write, ";
if (Any(access & BufferMappingAccessBit::InvalidateRange)) result += "InvalidateRange, ";
if (Any(access & BufferMappingAccessBit::InvalidateBuffer)) result += "InvalidateBuffer, ";
if (Any(access & BufferMappingAccessBit::FlushExplicit)) result += "FlushExplicit, ";
if (Any(access & BufferMappingAccessBit::Unsynchronized)) result += "Unsynchronized, ";
if (Any(access & BufferMappingAccessBit::Persistent)) result += "Persistent, ";
if (Any(access & BufferMappingAccessBit::Coherent)) result += "Coherent, ";
result.pop_back();
result.pop_back();
result += "]";
return result.empty() ? "[]" : result;
}
}
}
@@ -0,0 +1,9 @@
#pragma once
namespace MobileGL {
namespace MG_Util {
String ConvertBufferTargetToString(BufferTarget bufferTarget);
String ConvertBufferUsageToString(BufferUsage usage);
String ConvertBufferMappingAccessToString(BufferMappingAccessBit access);
}
}
+5 -5
View File
@@ -7,15 +7,15 @@ namespace MobileGL {
constexpr char* GetOSName() {
#if defined(_WIN32)
return "Windows";
return (char*)"Windows";
#elif defined(__ANDROID__)
return "Android";
return (char*)"Android";
#elif defined(__APPLE__)
return "macOS";
return (char*)"macOS";
#elif defined(__linux__)
return "Linux";
return (char*)"Linux";
#else
return "UnknownOS";
return (char*)"UnknownOS";
#endif
}
+5 -5
View File
@@ -7,8 +7,8 @@ namespace MobileGL {
template <typename IndexType>
class IndexGenerator {
public:
explicit IndexGenerator(size_t initial_capacity = 1024)
: next_index_(0)
explicit IndexGenerator(size_t initial_capacity = 1024, IndexType first_index = 0)
: next_index_(first_index)
{
is_valid_.reserve(initial_capacity);
}
@@ -50,9 +50,9 @@ namespace MobileGL {
}
private:
IndexType next_index_ = 0;
std::vector<IndexType> freed_indices_;
std::vector<Uint8> is_valid_;
IndexType next_index_ = 0;
std::vector<IndexType> freed_indices_;
std::vector<Uint8> is_valid_;
};
}
@@ -1,8 +1,4 @@
//
// Created by Swung 0x48 on 2025/7/20.
//
#include "Includes.h"
#include "../../Includes.h"
namespace MobileGL {
namespace MG_Util {
@@ -1,9 +1,4 @@
//
// Created by Swung 0x48 on 2025/7/20.
//
#ifndef MOBILEGLTEST_SHADERCOMPILER_H
#define MOBILEGLTEST_SHADERCOMPILER_H
#pragma once
namespace MobileGL {
namespace MG_Util {
@@ -17,6 +12,3 @@ namespace ShaderTranspiler {
}
}
}
#endif //MOBILEGLTEST_SHADERCOMPILER_H
+1 -8
View File
@@ -1,9 +1,4 @@
//
// Created by Swung 0x48 on 2025/7/20.
//
#ifndef MG_UTIL_SHADERTRANSPILER_TYPES_H
#define MG_UTIL_SHADERTRANSPILER_TYPES_H
#pragma once
namespace MobileGL {
namespace MG_Util {
@@ -145,5 +140,3 @@ namespace MobileGL {
}
#include "ShaderCompiler.h"
#endif //MG_UTIL_SHADERTRANSPILER_TYPES_H
+11 -5
View File
@@ -1,5 +1,7 @@
#pragma once
#define GL_UNKNOWN_MGL 0
namespace MobileGL {
using String = std::string;
using StringStream = std::stringstream;
@@ -24,11 +26,11 @@ namespace MobileGL {
using SharedPtr = std::shared_ptr<T>;
template <typename T>
using UniquePtr = std::unique_ptr<T>;
template<typename T, typename... Args>
template <typename T, typename... Args>
inline SharedPtr<T> MakeShared(Args&&... args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
template<typename T, typename... Args>
template <typename T, typename... Args>
inline UniquePtr<T> MakeUnique(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
@@ -37,10 +39,14 @@ namespace MobileGL {
using Array = std::array<T, N>;
template <typename Key, typename Value>
using UnorderedMap = std::unordered_map<Key, Value>;
template<typename T>
template <typename T>
inline constexpr std::remove_reference_t<T>&& Move(T&& t) noexcept {
return static_cast<std::remove_reference_t<T>&&>(t);
}
template <typename T>
inline constexpr void Copy(const T* src, T* dest, SizeT count) {
std::copy(src, src + count, dest);
}
class RuntimeError : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
@@ -100,7 +106,7 @@ namespace MobileGL {
}
};
template<typename ObjectType>
template <typename ObjectType>
class BindingSlot {
public:
using TargetEnum = typename ObjectType::TargetEnum;
@@ -162,4 +168,4 @@ namespace MobileGL {
};
}
#include "MG_Util/ShaderTranspiler/Types.h"
#include "../MG_Util/ShaderTranspiler/Types.h"