mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
Create 6 / Flywheel 1.0.6 now renders correctly with both flywheel:instancing and flywheel:indirect on DirectGLES and DirectVulkan (verified in-game on Adreno 830: waterwheels and cogwheels solid, animated, correct pairing, no crashes across all four combinations). - MG_State/MG_Impl: sync explicitly-ranged SSBO bindings of FLUSH_EXPLICIT persistent maps to the backend before compute dispatches. Flywheel writes its scatter-copy descriptors into the staging ring's persistent map and never flushes that span (UB per spec, works on drivers whose maps alias GPU-visible memory); our maps alias the CPU shadow, so the descriptors never reached the GPU: the scatter compute copied nothing (GLES: empty draw commands) or stale garbage (Vulkan: wild indirect commands ending in VK_ERROR_DEVICE_LOST). - MG_Impl/MG_Backend: real glFenceSync objects backed by backend fences (GLES: native ES syncs guarded by context generation and owner thread; Vulkan: buffer-manager frame serials), replacing always-signaled stubs that let Flywheel reclaim staging memory the GPU still reads. - MG_Backend/DirectGLES: compute dispatches now run the same per-program resource sync as draws (uniform-block bindings and sampler units must be re-established through the API because layout(binding) is stripped from transpiled ESSL) and rebind texture units afterwards; the cull shader used to read a stale _FlwFrameUniforms binding and the depth-pyramid downsample sampled a stale unit-0 texture, zeroing the Hi-Z pyramid and occlusion-culling all Flywheel geometry. Image uniforms are excluded from glUniform1i (ES bakes their unit via layout(binding)); image-unit sync is clamped to the device limit; eliminated/SSBO-classified uniform blocks are skipped. - MG_Backend/DirectGLES: gl_BaseInstance in native indirect draws reads the GPU-written command buffer through an injected mg_IndirectParams SSBO view addressed per draw instead of the zero CPU shadow; layout(binding) is preserved for SSBO/image declarations (ES has no API rebinding for them); the ES context ownership claim moved to a global atomic owner thread with an EGL ground-truth check, and deferred buffer op state is mutex-guarded, so ops cannot silently no-op after context migration. - MG_Backend/DirectVulkan: new RebaseInstanceIndexPass rewrites vertex InstanceIndex loads to (InstanceIndex - BaseInstance). glslang's relaxed Vulkan mode aliases gl_InstanceID to InstanceIndex, which includes firstInstance, but GL's gl_InstanceID is zero-based - draws with nonzero baseInstance paired meshes with wrong instance data (cogwheel drawn as a waterwheel, another wheel collapsed invisible). Gated on the shaderDrawParameters device feature. Sampled-read barriers additionally cover the compute stage (the Hi-Z downsample samples the depth attachment from compute), and short uniform-buffer ranges keep the existing zero-padding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
452 lines
20 KiB
C++
452 lines
20 KiB
C++
// MobileGL - MobileGL/MG_Backend/DirectGLES/Utils.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 "DirectGLES.h"
|
|
#include "Utils.h"
|
|
#include "Managers.h"
|
|
#include "MG_Backend/BackendObjects.h"
|
|
#include "MG_Util/Converters/GLToMG/FramebufferEnumConverter.h"
|
|
#include "MG_Util/Texture/TextureFormatProcessor.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/FramebufferEnumConverter.h>
|
|
|
|
namespace MobileGL::MG_Backend::DirectGLES {
|
|
namespace {
|
|
Flags<PixelFormatNormalizeOptionBit> GetForcedPixelFormatNormalizeOptions() {
|
|
Flags<PixelFormatNormalizeOptionBit> options;
|
|
if (g_GLESCapabilities.GLESRendererString.find("ANGLE") != String::npos) {
|
|
options |= PixelFormatNormalizeOptionBit::NoRgb16;
|
|
options |= PixelFormatNormalizeOptionBit::NoSnorm16;
|
|
options |= PixelFormatNormalizeOptionBit::NoSnorm8;
|
|
}
|
|
return options;
|
|
}
|
|
|
|
Flags<PixelFormatNormalizeOptionBit> GetDriverPixelFormatNormalizeOptions() {
|
|
Flags<PixelFormatNormalizeOptionBit> options = PixelFormatNormalizeOptionBit::NoDepthComponent32;
|
|
options |= PixelFormatNormalizeOptionBit::NoRGBA8Snorm;
|
|
options |= PixelFormatNormalizeOptionBit::NoRGB16Snorm;
|
|
if (!g_GLESCapabilities.SupportsNorm16Texture) {
|
|
options |= PixelFormatNormalizeOptionBit::NoNorm16;
|
|
}
|
|
return options;
|
|
}
|
|
|
|
Flags<PixelFormatNormalizeOptionBit> GetRuntimeFallbackNormalizeOptions(GLenum requestedInternalFormat) {
|
|
using namespace MG_Util::TextureFormatProcessor;
|
|
const Flags<PixelFormatNormalizeOptionBit> forcedOptions =
|
|
GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat, GetForcedPixelFormatNormalizeOptions());
|
|
if (forcedOptions) {
|
|
return forcedOptions;
|
|
}
|
|
return GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat,
|
|
GetDriverPixelFormatNormalizeOptions());
|
|
}
|
|
|
|
Bool HasCachedFormatCapability(TextureInternalFormat internalFormat,
|
|
SizeT targetIndex,
|
|
Bool caveat,
|
|
FormatCapability capability) {
|
|
if (!pActiveBackendObject || targetIndex >= kFormatCapabilityTargetCount) {
|
|
return false;
|
|
}
|
|
const SizeT formatIndex = static_cast<SizeT>(internalFormat);
|
|
if (formatIndex >= kFormatCapabilityFormatCount) {
|
|
return false;
|
|
}
|
|
|
|
const FormatCapabilityCache& cache = pActiveBackendObject->GetFormatCapabilities();
|
|
const FormatCapabilityFlags caps =
|
|
caveat ? cache.CaveatCaps[targetIndex][formatIndex] : cache.FullCaps[targetIndex][formatIndex];
|
|
return HasFormatCapability(caps, capability);
|
|
}
|
|
|
|
Bool HasAnyCachedFormatCapability(TextureInternalFormat internalFormat,
|
|
Bool caveat,
|
|
FormatCapability capability) {
|
|
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTargetCount; ++targetIndex) {
|
|
if (HasCachedFormatCapability(internalFormat, targetIndex, caveat, capability)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Bool ShouldUseCaveatFormat(TextureInternalFormat internalFormat, SizeT targetIndex) {
|
|
if (targetIndex < kFormatCapabilityTargetCount) {
|
|
const Bool fullCreatable =
|
|
HasCachedFormatCapability(internalFormat, targetIndex, false, FormatCapability::Creatable);
|
|
const Bool caveatCreatable =
|
|
HasCachedFormatCapability(internalFormat, targetIndex, true, FormatCapability::Creatable);
|
|
const Bool fullRenderable =
|
|
HasCachedFormatCapability(internalFormat, targetIndex, false, FormatCapability::FramebufferRenderable);
|
|
const Bool caveatRenderable =
|
|
HasCachedFormatCapability(internalFormat, targetIndex, true, FormatCapability::FramebufferRenderable);
|
|
return (!fullCreatable && caveatCreatable) || (!fullRenderable && caveatRenderable);
|
|
}
|
|
|
|
if (HasAnyCachedFormatCapability(internalFormat, false, FormatCapability::Creatable)) {
|
|
return false;
|
|
}
|
|
return HasAnyCachedFormatCapability(internalFormat, true, FormatCapability::Creatable);
|
|
}
|
|
|
|
void GenerateFormatInfo(TextureInternalFormat internalFormat,
|
|
SizeT targetIndex,
|
|
GLenum* outInternalFormat,
|
|
GLenum* outFormat,
|
|
GLenum* outType) {
|
|
using namespace MobileGL::MG_Util::TextureFormatProcessor;
|
|
const GLenum requestedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(internalFormat);
|
|
Flags<PixelFormatNormalizeOptionBit> options;
|
|
if (!pActiveBackendObject || ShouldUseCaveatFormat(internalFormat, targetIndex)) {
|
|
options = GetRuntimeFallbackNormalizeOptions(requestedInternalFormat);
|
|
}
|
|
NormalizePixelFormat(requestedInternalFormat, options, outInternalFormat, outFormat, outType);
|
|
}
|
|
} // namespace
|
|
|
|
namespace TextureImpl {
|
|
void GenerateTextureFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat,
|
|
GLenum* outFormat, GLenum* outType, TextureTarget target) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
const SizeT targetIndex =
|
|
target == TextureTarget::Unknown ? kFormatCapabilityTargetCount : GetFormatCapabilityTargetIndex(target);
|
|
GenerateFormatInfo(internalFormat, targetIndex, outInternalFormat, outFormat, outType);
|
|
}
|
|
|
|
void GenerateRenderbufferFormatInfo(TextureInternalFormat internalFormat, GLenum* outInternalFormat,
|
|
GLenum* outFormat, GLenum* outType) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
GenerateFormatInfo(internalFormat, GetRenderbufferFormatCapabilityTargetIndex(), outInternalFormat,
|
|
outFormat, outType);
|
|
}
|
|
|
|
Bool ShouldUseCaveatTextureFormat(TextureInternalFormat internalFormat, TextureTarget target) {
|
|
const SizeT targetIndex =
|
|
target == TextureTarget::Unknown ? kFormatCapabilityTargetCount : GetFormatCapabilityTargetIndex(target);
|
|
return ShouldUseCaveatFormat(internalFormat, targetIndex);
|
|
}
|
|
|
|
Bool ShouldUseCaveatRenderbufferFormat(TextureInternalFormat internalFormat) {
|
|
return ShouldUseCaveatFormat(internalFormat, GetRenderbufferFormatCapabilityTargetIndex());
|
|
}
|
|
} // namespace TextureImpl
|
|
namespace PrgramImpl {
|
|
String ProcessOutColorLocations(const String& glslCode) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
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) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
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 ClampNormFallbackOutputs(String glslCode, GLenum shaderType, Uint32 snormOutputMask,
|
|
Uint32 unormOutputMask) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
const Uint32 outputMask = snormOutputMask | unormOutputMask;
|
|
if (shaderType != GL_FRAGMENT_SHADER || outputMask == 0) {
|
|
return glslCode;
|
|
}
|
|
|
|
const std::regex outputPattern(
|
|
R"(layout\s*\(\s*location\s*=\s*([0-9]+)\s*\)\s*out\s+(?:(?:lowp|mediump|highp)\s+)?vec4\s+([A-Za-z_][A-Za-z0-9_]*)\s*;)");
|
|
std::sregex_iterator outputIt(glslCode.begin(), glslCode.end(), outputPattern);
|
|
std::sregex_iterator outputEnd;
|
|
struct OutputClamp {
|
|
String Name;
|
|
Bool Signed;
|
|
};
|
|
Vector<OutputClamp> outputClamps;
|
|
for (; outputIt != outputEnd; ++outputIt) {
|
|
const Uint location = static_cast<Uint>(std::stoul((*outputIt)[1].str()));
|
|
if (location < 32 && (outputMask & (1u << location))) {
|
|
outputClamps.push_back({(*outputIt)[2].str(), static_cast<Bool>(snormOutputMask & (1u << location))});
|
|
}
|
|
}
|
|
if (outputClamps.empty()) {
|
|
return glslCode;
|
|
}
|
|
|
|
const std::regex mainPattern(R"(void\s+main\s*\([^)]*\)\s*\{)");
|
|
std::smatch mainMatch;
|
|
if (!std::regex_search(glslCode, mainMatch, mainPattern)) {
|
|
return glslCode;
|
|
}
|
|
|
|
SizeT bracePos = static_cast<SizeT>(mainMatch.position(0) + mainMatch.length(0) - 1);
|
|
Int depth = 0;
|
|
for (SizeT pos = bracePos; pos < glslCode.size(); ++pos) {
|
|
if (glslCode[pos] == '{') {
|
|
++depth;
|
|
} else if (glslCode[pos] == '}') {
|
|
--depth;
|
|
if (depth == 0) {
|
|
String clampLine;
|
|
for (const OutputClamp& outputClamp : outputClamps) {
|
|
const String minValue = outputClamp.Signed ? "-1.0" : "0.0";
|
|
clampLine += "\n " + outputClamp.Name + " = clamp(" + outputClamp.Name +
|
|
", vec4(" + minValue + "), vec4(1.0));";
|
|
}
|
|
clampLine += "\n";
|
|
glslCode.insert(pos, clampLine);
|
|
return glslCode;
|
|
}
|
|
}
|
|
}
|
|
return glslCode;
|
|
}
|
|
|
|
String ForceFlatIntegerVaryings(const String& glslCode, GLenum shaderType) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
String result = glslCode;
|
|
const String integerType = R"((?:(?:lowp|mediump|highp)\s+)?(?:u?int|[iu]vec[234])\b)";
|
|
|
|
auto addFlatQualifier = [&result, &integerType](const String& qualifier) {
|
|
const std::regex pattern("(layout\\s*\\([^)]*\\)\\s*)(?!(?:flat|smooth|noperspective)\\s)(" +
|
|
qualifier + "\\s+" + integerType + ")");
|
|
result = std::regex_replace(result, pattern, "$1flat $2");
|
|
};
|
|
|
|
switch (shaderType) {
|
|
case GL_VERTEX_SHADER:
|
|
addFlatQualifier("out");
|
|
break;
|
|
case GL_GEOMETRY_SHADER:
|
|
addFlatQualifier("in");
|
|
addFlatQualifier("out");
|
|
break;
|
|
case GL_FRAGMENT_SHADER:
|
|
addFlatQualifier("in");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
String RemoveLayoutBinding(const String& glslCode) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
// Sampler and uniform-block bindings are re-established at draw time through the
|
|
// API, so their layout qualifiers are stripped (they may exceed ES limits). SSBO
|
|
// blocks and image uniforms are different: ES has no glShaderStorageBlockBinding,
|
|
// and image units cannot be set with glUniform1i, so for those declarations the
|
|
// binding qualifier is the only binding mechanism and must be preserved.
|
|
static std::regex bindingRegex(R"(layout\s*\(\s*binding\s*=\s*\d+\s*\)\s*)");
|
|
static std::regex bindingRegex2(R"(layout\s*\(\s*binding\s*=\s*\d+\s*,)");
|
|
static std::regex keepBindingRegex(R"(\b(buffer|[iu]?image[A-Za-z0-9]*)\b)");
|
|
|
|
String result;
|
|
result.reserve(glslCode.size());
|
|
SizeT lineStart = 0;
|
|
while (lineStart <= glslCode.size()) {
|
|
SizeT lineEnd = glslCode.find('\n', lineStart);
|
|
const Bool lastLine = lineEnd == String::npos;
|
|
String line = glslCode.substr(lineStart, lastLine ? String::npos : lineEnd - lineStart);
|
|
|
|
if (!std::regex_search(line, keepBindingRegex)) {
|
|
line = std::regex_replace(line, bindingRegex, "");
|
|
line = std::regex_replace(line, bindingRegex2, "layout(");
|
|
}
|
|
|
|
result += line;
|
|
if (lastLine) {
|
|
break;
|
|
}
|
|
result += '\n';
|
|
lineStart = lineEnd + 1;
|
|
}
|
|
return result;
|
|
}
|
|
} // namespace PrgramImpl
|
|
|
|
namespace Utils {
|
|
void CheckGLESError() {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
for (GLenum err = g_GLESFuncs.glGetError(); err != GL_NO_ERROR; err = g_GLESFuncs.glGetError()) {
|
|
MGLOG_E("-> GLES Error: %s", MG_Util::ConvertGLEnumToString(err).c_str());
|
|
}
|
|
}
|
|
|
|
GLenum GetBindingQuery(GLenum target, bool isTexture) {
|
|
#ifdef TRACY_ENABLE
|
|
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
|
|
#endif
|
|
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:
|
|
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:
|
|
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
|