mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
171 lines
8.6 KiB
C++
171 lines
8.6 KiB
C++
// MobileGL - MobileGL/MG_Impl/GLImpl/Buffer/Validators.cpp
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#include "Validators.h"
|
|
#include <MG_Backend/BackendObjects.h>
|
|
#include <MG_State/GLState/Core.h>
|
|
#include <MG_State/GLState/ErrorState/Error.h>
|
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
|
#include <MG_Util/Converters/MGToGL/BufferEnumConverter.h>
|
|
#include <MG_Util/Converters/MGToStr/BufferEnumConverter.h>
|
|
#include <MG_Util/ShaderTranspiler/Types.h>
|
|
|
|
namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
|
|
Bool ValidateBufferTarget(BufferTarget target) {
|
|
if (target == BufferTarget::Unknown) {
|
|
using namespace MG_Util;
|
|
String bufferTargetStr = ConvertBufferTargetToString(target);
|
|
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
|
|
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
|
|
return false;
|
|
}
|
|
|
|
if (target == BufferTarget::Index && MG_State::pGLContext->GetBoundVertexArray() == nullptr) {
|
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
|
|
"ValidateBufferTarget",
|
|
"No vertex array object is bound."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
Bool ValidateBufferBindingPointTarget(BufferTarget target) {
|
|
if (target != BufferTarget::Uniform && target != BufferTarget::AtomicCounter &&
|
|
target != BufferTarget::TransformFeedback && target != BufferTarget::ShaderStorage) {
|
|
using namespace MG_Util;
|
|
String bufferTargetStr = ConvertBufferTargetToString(target);
|
|
String glTargetStr = ConvertGLEnumToString(ConvertBufferTargetToGLEnum(target));
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferTarget",
|
|
std::format("Target {} ({}) is not valid.", bufferTargetStr, glTargetStr)));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
namespace {
|
|
// The GL-visible number of indexed binding points for `target`.
|
|
SizeT GetBufferBindingPointLimit(BufferTarget target) {
|
|
SizeT pointCount = MG_State::pGLContext->GetBufferBindingPointCount(target);
|
|
if (target == BufferTarget::ShaderStorage && MG_Backend::pActiveBackendObject) {
|
|
const Int backendCount =
|
|
MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings;
|
|
pointCount = std::min(pointCount, static_cast<SizeT>(std::max(backendCount, 0)));
|
|
}
|
|
if (target == BufferTarget::TransformFeedback) {
|
|
// GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS bounds the indexed capture
|
|
// binding points in GL 3.3 (no ARB_transform_feedback3).
|
|
pointCount = std::min<SizeT>(pointCount, 4);
|
|
}
|
|
if (target == BufferTarget::AtomicCounter) {
|
|
// GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, which is NOT the state layer's array
|
|
// size: a counter buffer reaches a shader only as a lowered storage block, so the
|
|
// reserved range is the ceiling, and glGetIntegerv advertises the same number.
|
|
pointCount = std::min<SizeT>(
|
|
pointCount, static_cast<SizeT>(MG_Util::ShaderTranspiler::MAX_ATOMIC_COUNTER_BUFFER_BINDINGS));
|
|
}
|
|
return pointCount;
|
|
}
|
|
} // namespace
|
|
|
|
Bool ValidateBufferBindingPointRange(BufferTarget target, Uint first, GLsizei count, const char* funcName) {
|
|
if (count < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", funcName,
|
|
"count must be non-negative."));
|
|
return false;
|
|
}
|
|
const SizeT pointCount = GetBufferBindingPointLimit(target);
|
|
if (static_cast<Uint64>(first) + static_cast<Uint64>(count) > static_cast<Uint64>(pointCount)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl/BufferImpl", funcName,
|
|
std::format("first + count ({} + {}) exceeds the {} indexed binding points of target {}.", first,
|
|
count, pointCount, MG_Util::ConvertBufferTargetToString(target))));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Bool ValidateBufferBindingPointIndex(BufferTarget target, Uint index) {
|
|
const SizeT pointCount = GetBufferBindingPointLimit(target);
|
|
|
|
if (index < pointCount) {
|
|
return true;
|
|
}
|
|
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferBindingPointIndex",
|
|
std::format("Binding point index {} is out of range for target {}.", index,
|
|
MG_Util::ConvertBufferTargetToString(target))));
|
|
return false;
|
|
}
|
|
|
|
Bool ValidateBufferName(Uint index, Bool allowZero) {
|
|
if (index == 0) {
|
|
if (allowZero) return true;
|
|
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferName",
|
|
"Buffer name 0 is not valid."));
|
|
return false;
|
|
}
|
|
Bool isValid = MG_State::pGLContext->ValidateBufferName(index);
|
|
if (isValid) return true;
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<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,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl/BufferImpl", "ValidateBufferUsage",
|
|
std::format("Usage {} ({}) is not one of the allowable values.", bufferUsageStr, glUsageStr)));
|
|
return false;
|
|
}
|
|
|
|
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) {
|
|
// An empty mask is a legal value for a bitfield - it just fails the rule that a mapping
|
|
// must ask for read or write access, which is INVALID_OPERATION and belongs to the callers
|
|
// (both of them check it immediately after this). Rejecting it here as INVALID_ENUM
|
|
// reported the wrong error and hid theirs.
|
|
const auto 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,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl", "ValidateBufferMappingAccess",
|
|
"Access bits cannot contain invalid flags."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // namespace MobileGL::MG_Impl::GLImpl::BufferImpl
|