mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
3827 lines
196 KiB
C++
3827 lines
196 KiB
C++
// MobileGL - MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.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 "GL_Texture.h"
|
|
#include "Config.h"
|
|
#include "MG_State/GLState/TextureState/TextureObject2D.h"
|
|
#include "MG_Util/Types.h"
|
|
#include "Validators.h"
|
|
#include "ProxyTexture.h"
|
|
|
|
#include <MG_State/GLState/Core.h>
|
|
#include <MG_Backend/BackendObjects.h>
|
|
#include <MG_Util/Metrics/TextureMetrics.h>
|
|
#include <MG_State/GLState/ErrorState/Error.h>
|
|
#include <MG_Util/Texture/PixelStoreProcessor.h>
|
|
#include <MG_Util/Texture/TextureFormatProcessor.h>
|
|
#include <MG_Util/Classifiers/TextureEnumClassifier.h>
|
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
|
#include <MG_Util/Converters/MGToMG/TextureEnumConverter.h>
|
|
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
|
|
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
|
|
#include <MG_Util/Converters/MGToStr/TextureEnumConverter.h>
|
|
#include <MG_State/GLState/TextureState/TextureObjectBuffer.h>
|
|
|
|
namespace MobileGL::MG_Impl::GLImpl {
|
|
static SharedPtr<MG_State::GLState::ITextureObject> nullTextureObject;
|
|
static UnorderedMap<Uint, Bool> g_autoGenerateMipmapByTextureId;
|
|
|
|
Bool GetTexParameteriv_State(GLenum target, GLenum pname, GLint* params);
|
|
|
|
namespace {
|
|
void SetTextureBorderColorFromFloats(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const GLfloat* params) {
|
|
textureObject->SetBorderColor(FloatVec4(params[0], params[1], params[2], params[3]));
|
|
}
|
|
|
|
void SetTextureBorderColorFromInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const GLint* params) {
|
|
constexpr Float kSignedIntToFloat = 1.0f / 2147483647.0f;
|
|
textureObject->SetBorderColor(FloatVec4(static_cast<Float>(params[0]) * kSignedIntToFloat,
|
|
static_cast<Float>(params[1]) * kSignedIntToFloat,
|
|
static_cast<Float>(params[2]) * kSignedIntToFloat,
|
|
static_cast<Float>(params[3]) * kSignedIntToFloat));
|
|
}
|
|
|
|
void SetTextureBorderColorFromIntegerInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const GLint* params) {
|
|
textureObject->SetBorderColorI(IntVec4(params[0], params[1], params[2], params[3]));
|
|
}
|
|
|
|
void SetTextureBorderColorFromUnsignedInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const GLuint* params) {
|
|
textureObject->SetBorderColorUI(UintVec4(params[0], params[1], params[2], params[3]));
|
|
}
|
|
|
|
Bool SetTextureSwizzleParamsFromInts(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const GLint* params, const char* caller) {
|
|
Vec4<TextureSwizzleParam> swizzleParams;
|
|
for (int i = 0; i < 4; ++i) {
|
|
swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(params[i]);
|
|
if (TextureSwizzleParam::Unknown == swizzleParams[i]) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "`params` is not valid."));
|
|
return false;
|
|
}
|
|
}
|
|
textureObject->SetSwizzleParamRGBA(swizzleParams);
|
|
return true;
|
|
}
|
|
|
|
Bool ValidateMaxAnisotropy(Float maxAnisotropy, const char* caller) {
|
|
if (maxAnisotropy >= 1.0f) return true;
|
|
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"GL_TEXTURE_MAX_ANISOTROPY_EXT must be at least 1.0."));
|
|
return false;
|
|
}
|
|
|
|
template <typename Fn>
|
|
void WithTemporarilyBoundNamedTexture(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
Fn&& fn) {
|
|
if (!textureObject) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureObject->GetTarget());
|
|
const auto previousBinding = bindingSlot.GetBoundObject();
|
|
bindingSlot.Bind(textureObject);
|
|
fn(MG_Util::ConvertTextureTargetToGLEnum(textureObject->GetTarget()));
|
|
bindingSlot.Bind(previousBinding);
|
|
}
|
|
|
|
SizeT ComputeTextureStorageByteSize(TextureInternalFormat textureInternalFormat, GLsizei width, GLsizei height,
|
|
GLsizei depth) {
|
|
GLenum realInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat);
|
|
GLenum realFormat = GL_RGBA;
|
|
GLenum realType = GL_UNSIGNED_BYTE;
|
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
|
realInternalFormat, PixelFormatNormalizeOptionBit::None, &realInternalFormat, &realFormat, &realType);
|
|
return static_cast<SizeT>(width) * static_cast<SizeT>(height) * static_cast<SizeT>(depth) *
|
|
MG_Util::GetInternalBytesPerPixel(textureInternalFormat,
|
|
MG_Util::ConvertGLEnumToTexturePixelDataType(realType));
|
|
}
|
|
|
|
Bool IsSizedTextureStorageInternalFormat(TextureInternalFormat textureInternalFormat) {
|
|
switch (textureInternalFormat) {
|
|
case TextureInternalFormat::Red:
|
|
case TextureInternalFormat::RG:
|
|
case TextureInternalFormat::RGB:
|
|
case TextureInternalFormat::RGBA:
|
|
case TextureInternalFormat::DepthComponent:
|
|
case TextureInternalFormat::DepthStencil:
|
|
case TextureInternalFormat::Unknown:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Bool ValidateTextureStorageInternalFormat(TextureInternalFormat textureInternalFormat, const char* caller) {
|
|
// TODO: Replace this sized-format filter with the full ARB_texture_storage legal-format table.
|
|
if (!IsSizedTextureStorageInternalFormat(textureInternalFormat)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"TexStorage requires a sized internal format."));
|
|
return false;
|
|
}
|
|
return TextureImpl::ValidateTextureInternalFormat(textureInternalFormat);
|
|
}
|
|
|
|
Bool ValidateTextureMutable(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
const char* caller) {
|
|
if (!textureObject || !textureObject->IsImmutable()) return true;
|
|
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Immutable texture storage cannot be redefined."));
|
|
return false;
|
|
}
|
|
|
|
GLint GetTextureComponentType(TextureInternalFormat textureInternalFormat, GLint size, Bool depthComponent,
|
|
Bool stencilComponent) {
|
|
if (size <= 0) return GL_NONE;
|
|
if (stencilComponent) return GL_UNSIGNED_INT;
|
|
if (depthComponent) {
|
|
return (textureInternalFormat == TextureInternalFormat::DepthComponent32F ||
|
|
textureInternalFormat == TextureInternalFormat::Depth32FStencil8)
|
|
? GL_FLOAT
|
|
: GL_UNSIGNED_NORMALIZED;
|
|
}
|
|
switch (textureInternalFormat) {
|
|
case TextureInternalFormat::R8I:
|
|
case TextureInternalFormat::R16I:
|
|
case TextureInternalFormat::R32I:
|
|
case TextureInternalFormat::RG8I:
|
|
case TextureInternalFormat::RG16I:
|
|
case TextureInternalFormat::RG32I:
|
|
case TextureInternalFormat::RGB8I:
|
|
case TextureInternalFormat::RGB16I:
|
|
case TextureInternalFormat::RGB32I:
|
|
case TextureInternalFormat::RGBA8I:
|
|
case TextureInternalFormat::RGBA16I:
|
|
case TextureInternalFormat::RGBA32I:
|
|
return GL_INT;
|
|
case TextureInternalFormat::R8UI:
|
|
case TextureInternalFormat::R16UI:
|
|
case TextureInternalFormat::R32UI:
|
|
case TextureInternalFormat::RG8UI:
|
|
case TextureInternalFormat::RG16UI:
|
|
case TextureInternalFormat::RG32UI:
|
|
case TextureInternalFormat::RGB8UI:
|
|
case TextureInternalFormat::RGB16UI:
|
|
case TextureInternalFormat::RGB32UI:
|
|
case TextureInternalFormat::RGBA8UI:
|
|
case TextureInternalFormat::RGBA16UI:
|
|
case TextureInternalFormat::RGBA32UI:
|
|
case TextureInternalFormat::RGB10A2UI:
|
|
return GL_UNSIGNED_INT;
|
|
case TextureInternalFormat::R16F:
|
|
case TextureInternalFormat::RG16F:
|
|
case TextureInternalFormat::RGB16F:
|
|
case TextureInternalFormat::RGBA16F:
|
|
case TextureInternalFormat::R32F:
|
|
case TextureInternalFormat::RG32F:
|
|
case TextureInternalFormat::RGB32F:
|
|
case TextureInternalFormat::RGBA32F:
|
|
case TextureInternalFormat::R11FG11FB10F:
|
|
case TextureInternalFormat::RGB9E5:
|
|
return GL_FLOAT;
|
|
case TextureInternalFormat::R8Snorm:
|
|
case TextureInternalFormat::R16Snorm:
|
|
case TextureInternalFormat::RG8Snorm:
|
|
case TextureInternalFormat::RG16Snorm:
|
|
case TextureInternalFormat::RGB8Snorm:
|
|
case TextureInternalFormat::RGB16Snorm:
|
|
case TextureInternalFormat::RGBA8Snorm:
|
|
case TextureInternalFormat::RGBA16Snorm:
|
|
return GL_SIGNED_NORMALIZED;
|
|
default:
|
|
return GL_UNSIGNED_NORMALIZED;
|
|
}
|
|
}
|
|
|
|
GLint GetTextureLevelComponentParameter(TextureInternalFormat textureInternalFormat, GLenum pname) {
|
|
const ComponentSizes componentSizes = MG_Util::GetComponentSizesForInternalFormat(textureInternalFormat);
|
|
switch (pname) {
|
|
case GL_TEXTURE_RED_SIZE:
|
|
return componentSizes.Red;
|
|
case GL_TEXTURE_GREEN_SIZE:
|
|
return componentSizes.Green;
|
|
case GL_TEXTURE_BLUE_SIZE:
|
|
return componentSizes.Blue;
|
|
case GL_TEXTURE_ALPHA_SIZE:
|
|
return componentSizes.Alpha;
|
|
case GL_TEXTURE_DEPTH_SIZE:
|
|
return componentSizes.Depth;
|
|
case GL_TEXTURE_STENCIL_SIZE:
|
|
return componentSizes.Stencil;
|
|
case GL_TEXTURE_RED_TYPE:
|
|
return GetTextureComponentType(textureInternalFormat, componentSizes.Red, false, false);
|
|
case GL_TEXTURE_GREEN_TYPE:
|
|
return GetTextureComponentType(textureInternalFormat, componentSizes.Green, false, false);
|
|
case GL_TEXTURE_BLUE_TYPE:
|
|
return GetTextureComponentType(textureInternalFormat, componentSizes.Blue, false, false);
|
|
case GL_TEXTURE_ALPHA_TYPE:
|
|
return GetTextureComponentType(textureInternalFormat, componentSizes.Alpha, false, false);
|
|
case GL_TEXTURE_DEPTH_TYPE:
|
|
return GetTextureComponentType(textureInternalFormat, componentSizes.Depth, true, false);
|
|
default:
|
|
MOBILEGL_ASSERT(false, "Invalid texture level component pname: %d", pname);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Bool IsValidImageTextureFormat(GLenum format) {
|
|
switch (format) {
|
|
case GL_RGBA32F:
|
|
case GL_RGBA16F:
|
|
case GL_RG32F:
|
|
case GL_RG16F:
|
|
case GL_R11F_G11F_B10F:
|
|
case GL_R32F:
|
|
case GL_R16F:
|
|
case GL_RGBA32UI:
|
|
case GL_RGBA16UI:
|
|
case GL_RGB10_A2UI:
|
|
case GL_RGBA8UI:
|
|
case GL_RG32UI:
|
|
case GL_RG16UI:
|
|
case GL_RG8UI:
|
|
case GL_R32UI:
|
|
case GL_R16UI:
|
|
case GL_R8UI:
|
|
case GL_RGBA32I:
|
|
case GL_RGBA16I:
|
|
case GL_RGBA8I:
|
|
case GL_RG32I:
|
|
case GL_RG16I:
|
|
case GL_RG8I:
|
|
case GL_R32I:
|
|
case GL_R16I:
|
|
case GL_R8I:
|
|
case GL_RGBA16:
|
|
case GL_RGB10_A2:
|
|
case GL_RGBA8:
|
|
case GL_RG16:
|
|
case GL_RG8:
|
|
case GL_R16:
|
|
case GL_R8:
|
|
case GL_RGBA16_SNORM:
|
|
case GL_RGBA8_SNORM:
|
|
case GL_RG16_SNORM:
|
|
case GL_RG8_SNORM:
|
|
case GL_R16_SNORM:
|
|
case GL_R8_SNORM:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
GLuint GetAdvertisedImageUnitCount() {
|
|
return static_cast<GLuint>(std::min<GLint>(
|
|
MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxImageUnits,
|
|
MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS));
|
|
}
|
|
|
|
// Array targets store their layer count in z; layers never participate in mip
|
|
// reduction (GL 3.3 §3.8.14), only true 3D textures halve their depth per level.
|
|
Bool DepthParticipatesInMipmapping(TextureTarget target) {
|
|
return target == TextureTarget::Texture3D;
|
|
}
|
|
|
|
Uint ComputeFullMipmapLevelCount(const IntVec3& baseTexelSize, Bool depthMips) {
|
|
Int maxDimension = std::max<Int>(
|
|
baseTexelSize.x(),
|
|
std::max<Int>(baseTexelSize.y(), depthMips ? std::max<Int>(baseTexelSize.z(), 1) : 1));
|
|
Uint mipLevelCount = 1;
|
|
while (maxDimension > 1) {
|
|
maxDimension = std::max<Int>(maxDimension / 2, 1);
|
|
++mipLevelCount;
|
|
}
|
|
return mipLevelCount;
|
|
}
|
|
|
|
IntVec3 ComputeMipmapTexelSize(const IntVec3& baseTexelSize, Uint relativeLevel, Bool depthMips) {
|
|
return {
|
|
std::max<Int>(baseTexelSize.x() >> static_cast<Int>(relativeLevel), 1),
|
|
std::max<Int>(baseTexelSize.y() >> static_cast<Int>(relativeLevel), 1),
|
|
depthMips ? std::max<Int>(baseTexelSize.z() >> static_cast<Int>(relativeLevel), 1)
|
|
: std::max<Int>(baseTexelSize.z(), 1),
|
|
};
|
|
}
|
|
|
|
Bool EnsureGeneratedMipmapStorageAllocated(
|
|
MG_State::GLState::TextureObjectMipmap& texture,
|
|
TextureUploadTarget uploadTarget) {
|
|
const Uint existingLevelCount = texture.GetMipmapLevelCount();
|
|
if (existingLevelCount == 0) {
|
|
return false;
|
|
}
|
|
|
|
const IntVec3 baseTexelSize = texture.GetMipmapTexelSize(uploadTarget, 0);
|
|
const SizeT baseByteSize = texture.GetMipmapByteSize(uploadTarget, 0);
|
|
const SizeT baseTexelCount = static_cast<SizeT>(baseTexelSize.x()) *
|
|
static_cast<SizeT>(baseTexelSize.y()) *
|
|
static_cast<SizeT>(baseTexelSize.z());
|
|
if (baseTexelSize.x() <= 0 || baseTexelSize.y() <= 0 || baseTexelSize.z() <= 0 ||
|
|
baseByteSize == 0 || baseTexelCount == 0 || (baseByteSize % baseTexelCount) != 0) {
|
|
return false;
|
|
}
|
|
|
|
const SizeT bytesPerTexel = baseByteSize / baseTexelCount;
|
|
const Bool depthMips = DepthParticipatesInMipmapping(texture.GetTarget());
|
|
const Uint requiredLevelCount = ComputeFullMipmapLevelCount(baseTexelSize, depthMips);
|
|
for (Uint level = 1; level < requiredLevelCount; ++level) {
|
|
const IntVec3 levelTexelSize = ComputeMipmapTexelSize(baseTexelSize, level, depthMips);
|
|
const SizeT levelByteSize = bytesPerTexel * static_cast<SizeT>(levelTexelSize.x()) *
|
|
static_cast<SizeT>(levelTexelSize.y()) *
|
|
static_cast<SizeT>(levelTexelSize.z());
|
|
texture.AllocateStorage(uploadTarget, level, {levelTexelSize, levelByteSize});
|
|
texture.MarkStorageDirty(uploadTarget, level, false);
|
|
}
|
|
// Mip generation grows/regenerates the level set on the GPU without marking any CPU
|
|
// level dirty (MarkStorageDirty(...,false) above). Bump the content version so the
|
|
// backend re-syncs: a cached sampled VkImageView built for the pre-generate level
|
|
// range would otherwise stay stale and clamp LOD>0 sampling to mip 0.
|
|
texture.BumpContentVersion();
|
|
return true;
|
|
}
|
|
|
|
void EnsureGeneratedMipmapStorageAllocated(MG_State::GLState::TextureObjectMipmap& texture) {
|
|
for (const TextureUploadTarget uploadTarget : texture.GetUploadTargets()) {
|
|
EnsureGeneratedMipmapStorageAllocated(texture, uploadTarget);
|
|
}
|
|
}
|
|
|
|
Bool IsMultisampleTextureTarget(TextureTarget target) {
|
|
return target == TextureTarget::Texture2DMultisample ||
|
|
target == TextureTarget::Texture2DMultisampleArray;
|
|
}
|
|
|
|
Int GetMaxSupportedTextureSamples(TextureInternalFormat textureInternalFormat) {
|
|
if (MG_Backend::pActiveBackendObject == nullptr) {
|
|
return std::numeric_limits<Int>::max();
|
|
}
|
|
|
|
const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
|
if (MG_Util::IsDepthFormatInternalFormat(textureInternalFormat) ||
|
|
MG_Util::IsStencilFormatInternalFormat(textureInternalFormat)) {
|
|
return std::max(dynamicParameters.MaxDepthTextureSamples, 1);
|
|
}
|
|
|
|
GLenum normalizedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat);
|
|
GLenum normalizedFormat = GL_RGBA;
|
|
GLenum normalizedType = GL_UNSIGNED_BYTE;
|
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
|
normalizedInternalFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat,
|
|
&normalizedFormat, &normalizedType);
|
|
const Bool isIntegerFormat = normalizedFormat == GL_RED_INTEGER || normalizedFormat == GL_RG_INTEGER ||
|
|
normalizedFormat == GL_RGB_INTEGER || normalizedFormat == GL_RGBA_INTEGER;
|
|
return std::max(isIntegerFormat ? dynamicParameters.MaxIntegerSamples
|
|
: dynamicParameters.MaxColorTextureSamples,
|
|
1);
|
|
}
|
|
|
|
Bool ValidateTextureMultisampleStorage(TextureTarget textureTarget, GLsizei samples, GLsizei width,
|
|
GLsizei height, GLsizei depth, TextureInternalFormat textureInternalFormat,
|
|
const char* caller) {
|
|
if (!IsMultisampleTextureTarget(textureTarget)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Target is not a multisample texture target."));
|
|
return false;
|
|
}
|
|
if (samples <= 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Sample count must be positive."));
|
|
return false;
|
|
}
|
|
if (width < 0 || height < 0 || depth < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Texture size must be non-negative."));
|
|
return false;
|
|
}
|
|
if (textureTarget == TextureTarget::Texture2DMultisample && depth != 1) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"2D multisample textures must use depth 1."));
|
|
return false;
|
|
}
|
|
if (textureTarget == TextureTarget::Texture2DMultisampleArray && depth == 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"2D multisample array textures must have at least one layer."));
|
|
return false;
|
|
}
|
|
|
|
const Int maxSamples = GetMaxSupportedTextureSamples(textureInternalFormat);
|
|
if (samples > maxSamples) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", caller,
|
|
std::format("Sample count {} exceeds the supported maximum {} for this texture format.",
|
|
samples, maxSamples)));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AllocateMultisampleTextureStorage(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
TextureUploadTarget textureUploadTarget,
|
|
TextureInternalFormat textureInternalFormat, GLsizei samples,
|
|
GLsizei width, GLsizei height, GLsizei depth,
|
|
GLboolean fixedsamplelocations) {
|
|
MOBILEGL_ASSERT(textureObject != nullptr, "AllocateMultisampleTextureStorage requires a texture object");
|
|
MOBILEGL_ASSERT(textureObject->GetStorageType() == TextureStorageType::Mipmap,
|
|
"AllocateMultisampleTextureStorage requires mipmap-backed storage");
|
|
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
textureObject->SetSamples(samples);
|
|
textureObject->SetFixedSampleLocations(fixedsamplelocations == GL_TRUE);
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, 0, {{width, height, depth}, 0});
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, 0, false);
|
|
}
|
|
} // namespace
|
|
|
|
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByName(GLuint texture, const char* caller) {
|
|
auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
|
if (!textureObject) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
std::format("Texture object {} does not exist.", texture)));
|
|
return nullTextureObject;
|
|
}
|
|
return textureObject;
|
|
}
|
|
|
|
Bool ValidateTextureParameterForTarget(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
GLenum pname, GLint param, const char* caller) {
|
|
const auto target = textureObject->GetTarget();
|
|
if (pname == GL_TEXTURE_MAX_ANISOTROPY_EXT && !ValidateMaxAnisotropy(param, caller)) {
|
|
return false;
|
|
}
|
|
if ((pname == GL_TEXTURE_BASE_LEVEL || pname == GL_TEXTURE_MAX_LEVEL) && param < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Texture level parameter must be non-negative."));
|
|
return false;
|
|
}
|
|
|
|
if ((target == TextureTarget::Texture2DMultisample ||
|
|
target == TextureTarget::Texture2DMultisampleArray) &&
|
|
pname == GL_TEXTURE_BASE_LEVEL && param != 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Multisample texture base level must be zero."));
|
|
return false;
|
|
}
|
|
|
|
if (target == TextureTarget::TextureRectangle && pname == GL_TEXTURE_BASE_LEVEL && param != 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Rectangle texture base level must be zero."));
|
|
return false;
|
|
}
|
|
|
|
if ((target == TextureTarget::Texture2DMultisample ||
|
|
target == TextureTarget::Texture2DMultisampleArray) &&
|
|
(pname == GL_TEXTURE_WRAP_S || pname == GL_TEXTURE_WRAP_T || pname == GL_TEXTURE_WRAP_R ||
|
|
pname == GL_TEXTURE_MIN_FILTER || pname == GL_TEXTURE_MAG_FILTER || pname == GL_TEXTURE_MIN_LOD ||
|
|
pname == GL_TEXTURE_MAX_LOD || pname == GL_TEXTURE_LOD_BIAS || pname == GL_TEXTURE_COMPARE_MODE ||
|
|
pname == GL_TEXTURE_COMPARE_FUNC || pname == GL_TEXTURE_BORDER_COLOR ||
|
|
pname == GL_TEXTURE_MAX_ANISOTROPY_EXT)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Sampler state is invalid for multisample textures."));
|
|
return false;
|
|
}
|
|
|
|
if (target == TextureTarget::TextureRectangle) {
|
|
if ((pname == GL_TEXTURE_WRAP_S || pname == GL_TEXTURE_WRAP_T) &&
|
|
(param == GL_MIRROR_CLAMP_TO_EDGE || param == GL_MIRRORED_REPEAT || param == GL_REPEAT)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Invalid wrap mode for rectangle texture."));
|
|
return false;
|
|
}
|
|
if (pname == GL_TEXTURE_MIN_FILTER && param != GL_NEAREST && param != GL_LINEAR) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Invalid min filter for rectangle texture."));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
TextureUploadTarget GetPrimaryUploadTarget(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject) {
|
|
if (!textureObject) return TextureUploadTarget::Unknown;
|
|
const auto& uploadTargets = textureObject->GetUploadTargets();
|
|
return uploadTargets.empty() ? TextureUploadTarget::Unknown : uploadTargets[0];
|
|
}
|
|
|
|
void TextureParameterObject_State(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, GLenum pname,
|
|
GLint param, const char* caller) {
|
|
if (!textureObject) return;
|
|
if (!ValidateTextureParameterForTarget(textureObject, pname, param, caller)) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param));
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode(param));
|
|
textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode(param));
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD: {
|
|
Float maxLod = textureObject->GetSamplerObject()->GetMaxLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(param, maxLod);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_MAX_LOD: {
|
|
Float minLod = textureObject->GetSamplerObject()->GetMinLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(minLod, param);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
textureObject->SetBaseLevel(param);
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
textureObject->SetMaxLevel(param);
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_R:
|
|
case GL_TEXTURE_SWIZZLE_G:
|
|
case GL_TEXTURE_SWIZZLE_B:
|
|
case GL_TEXTURE_SWIZZLE_A: {
|
|
auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname);
|
|
auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam(param);
|
|
textureObject->SetSwizzleParam(swizzleParam, swizzleValue);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_WRAP_S:
|
|
textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode(param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode(param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode(param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
textureObject->GetSamplerObject()->SetCompareMode(MG_Util::ConvertGLEnumToSamplerCompareMode(param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
textureObject->GetSamplerObject()->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(param));
|
|
break;
|
|
case GL_TEXTURE_LOD_BIAS:
|
|
textureObject->GetSamplerObject()->SetLodBias((GLfloat)param);
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
textureObject->GetSamplerObject()->SetMaxAnisotropy(static_cast<GLfloat>(param));
|
|
break;
|
|
case GL_GENERATE_MIPMAP:
|
|
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != GL_FALSE);
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", caller,
|
|
std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname))));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TextureParameterObjectf_State(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject, GLenum pname,
|
|
GLfloat param, const char* caller) {
|
|
if (!textureObject) return;
|
|
if (pname == GL_TEXTURE_MAX_ANISOTROPY_EXT && !ValidateMaxAnisotropy(param, caller)) return;
|
|
const GLint validationParam =
|
|
pname == GL_TEXTURE_MAX_ANISOTROPY_EXT ? 1 : static_cast<GLint>(param);
|
|
if (!ValidateTextureParameterForTarget(textureObject, pname, validationParam, caller)) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param));
|
|
textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD: {
|
|
Float maxLod = textureObject->GetSamplerObject()->GetMaxLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(param, maxLod);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_MAX_LOD: {
|
|
Float minLod = textureObject->GetSamplerObject()->GetMinLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(minLod, param);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
textureObject->SetBaseLevel((Uint)param);
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
textureObject->SetMaxLevel((Uint)param);
|
|
break;
|
|
case GL_TEXTURE_WRAP_S:
|
|
textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
textureObject->GetSamplerObject()->SetCompareMode(
|
|
MG_Util::ConvertGLEnumToSamplerCompareMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
textureObject->GetSamplerObject()->SetSamplerCompareFunc(
|
|
MG_Util::ConvertGLEnumToSamplerCompareFunc((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_LOD_BIAS:
|
|
textureObject->GetSamplerObject()->SetLodBias(param);
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
textureObject->GetSamplerObject()->SetMaxAnisotropy(param);
|
|
break;
|
|
case GL_GENERATE_MIPMAP:
|
|
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f);
|
|
break;
|
|
case GL_DEPTH_STENCIL_TEXTURE_MODE:
|
|
if (param != GL_DEPTH_COMPONENT && param != GL_STENCIL_INDEX) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"Invalid GL_DEPTH_STENCIL_TEXTURE_MODE value."));
|
|
}
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", caller,
|
|
std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname))));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GetTextureParameterObjectiv_State(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
GLenum pname, GLint* params, const char* caller) {
|
|
if (!textureObject || !params) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
*params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(textureObject->GetSamplerObject()->GetMagFilter(),
|
|
SamplerMipmapMode::None);
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
*params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetMinFilter(), textureObject->GetSamplerObject()->GetMipmapMode());
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD:
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMinLod());
|
|
break;
|
|
case GL_TEXTURE_MAX_LOD:
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMaxLod());
|
|
break;
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
*params = static_cast<GLint>(textureObject->GetLevelRange().x());
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
*params = static_cast<GLint>(textureObject->GetLevelRange().y());
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_FORMAT:
|
|
*params = textureObject->IsImmutable() ? GL_TRUE : GL_FALSE;
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_LEVELS:
|
|
*params = static_cast<GLint>(textureObject->GetImmutableLevels());
|
|
break;
|
|
case GL_TEXTURE_WRAP_S:
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS());
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapT());
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapR());
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
*params =
|
|
(GLint)MG_Util::ConvertSamplerCompareModeToGLEnum(textureObject->GetSamplerObject()->GetCompareMode());
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
*params = (GLint)MG_Util::ConvertSamplerCompareFuncToGLEnum(
|
|
textureObject->GetSamplerObject()->GetSamplerCompareFunc());
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMaxAnisotropy());
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
|
"pname is not a valid texture parameter."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const SharedPtr<MG_State::GLState::ITextureObject>& GetTextureObjectByTarget(
|
|
TextureUploadTarget textureUploadTarget, TextureTarget textureTarget) {
|
|
if (TextureImpl::IsProxyTextureTarget(textureUploadTarget)) {
|
|
auto& textureObject =
|
|
TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget);
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return nullTextureObject;
|
|
return textureObject;
|
|
} else {
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return nullTextureObject;
|
|
return textureObject;
|
|
}
|
|
}
|
|
|
|
void GenerateMipmap_Backend(GLenum target) {
|
|
MG_Backend::gBackendFunctionsTable.GL.GenerateMipmap(target);
|
|
}
|
|
|
|
void MaybeAutoGenerateMipmap(GLenum target, const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
Bool isProxy, GLint level) {
|
|
if (isProxy || level != 0 || !textureObject) {
|
|
return;
|
|
}
|
|
const auto it = g_autoGenerateMipmapByTextureId.find(textureObject->GetExternalIndex());
|
|
if (it == g_autoGenerateMipmapByTextureId.end() || !it->second) {
|
|
return;
|
|
}
|
|
GenerateMipmap_Backend(target);
|
|
}
|
|
|
|
void TexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height, zoffset,
|
|
depth))
|
|
return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(),
|
|
texturePixelDataType))
|
|
return;
|
|
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
|
|
return;
|
|
}
|
|
|
|
const void* originalPixels = pixels;
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
if (!originalPixels) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"No data supplied from pixels parameter and no PBO bound."));
|
|
return;
|
|
}
|
|
|
|
SizeT inputSize = 0;
|
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(),
|
|
textureInputFormat, texturePixelDataType, {width, height, depth}, false, inputSize);
|
|
if (!processedPixels || inputSize == 0) {
|
|
if (processedPixels) free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level);
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType);
|
|
const SizeT srcRowSize = static_cast<SizeT>(width) * internalBpp;
|
|
const SizeT srcSliceSize = static_cast<SizeT>(height) * srcRowSize;
|
|
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
|
const SizeT destSliceSize = static_cast<SizeT>(texelSize.y()) * destRowSize;
|
|
|
|
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
|
yoffset + height > static_cast<GLsizei>(texelSize.y()) ||
|
|
zoffset + depth > static_cast<GLsizei>(texelSize.z())) {
|
|
MGLOG_E("TexSubImage3D_State: Specified region exceeds texture level dimensions");
|
|
free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const auto* srcData = static_cast<const Uint8*>(processedPixels);
|
|
Uint8* destData = static_cast<Uint8*>(textureMipmapObject->MapMipmapData(textureUploadTarget, level));
|
|
if (destData) {
|
|
for (GLsizei z = 0; z < depth; ++z) {
|
|
for (GLsizei y = 0; y < height; ++y) {
|
|
const SizeT destRowOffset =
|
|
static_cast<SizeT>(zoffset + z) * destSliceSize +
|
|
static_cast<SizeT>(yoffset + y) * destRowSize +
|
|
static_cast<SizeT>(xoffset) * internalBpp;
|
|
const SizeT srcRowOffset =
|
|
static_cast<SizeT>(z) * srcSliceSize + static_cast<SizeT>(y) * srcRowSize;
|
|
Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
free(processedPixels);
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
MaybeAutoGenerateMipmap(target, textureObject, false, level);
|
|
}
|
|
|
|
void TexSubImage2D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, const void* pixels) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
// TextureInternalFormat textureInternalFormat =
|
|
// MG_Util::ConvertGLEnumToTextureInternalFormat(format);
|
|
MGLOG_D("TexSubImage2D_State: target = %s, level = %d, (%d, %d), format = %s, pixels = %p",
|
|
MG_Util::ConvertGLEnumToString(target).c_str(), level, width, height,
|
|
MG_Util::ConvertTextureInputFormatToString(textureInputFormat).c_str(), pixels);
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
// TODO: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the
|
|
// GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped.
|
|
// GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the
|
|
// GL_PIXEL_UNPACK_BUFFER target and the data would be unpacked from the buffer object such that the
|
|
// memory reads required would exceed the data store size. GL_INVALID_OPERATION is generated if a
|
|
// non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER target and data is not evenly
|
|
// divisible into the number of bytes needed to store in memory a datum indicated by type.
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
TextureInternalFormat textureInternalFormat = textureObject->GetFormat();
|
|
MGLOG_D("%s: working on texture %d", __func__, textureObject->GetExternalIndex());
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureInternalFormat,
|
|
texturePixelDataType))
|
|
return;
|
|
|
|
// ======================= Processing ================================
|
|
// Texture object here should always be an object with mipmap
|
|
// Assert this for extra safety.
|
|
// This should automatically compiled out in release,
|
|
// so that we don't take the perf hit of dyn-cast.
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
|
|
return;
|
|
}
|
|
auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level);
|
|
|
|
SizeT inputSize = 0;
|
|
|
|
const void* originalPixels = pixels;
|
|
|
|
// PBO
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
MGLOG_D("TexSubImage2D_State: Using Pixel Unpack Buffer Object ID: %u",
|
|
pixelUnpackBufferObject->GetExternalIndex());
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
|
|
if (!originalPixels) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"No data supplied from pixels parameter and no PBO bound."));
|
|
return;
|
|
}
|
|
const auto& unpackParams = MG_State::pGLContext->GetPixelStoreParameters(true);
|
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, unpackParams, textureInternalFormat, textureInputFormat, texturePixelDataType,
|
|
{width, height, 1}, false, inputSize);
|
|
|
|
if (!processedPixels || inputSize == 0) {
|
|
MGLOG_E("TexSubImage2D_State: Failed to process pixel data for TexSubImage2D, width: %d, height: %d", width,
|
|
height);
|
|
if (processedPixels) free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
|
|
|
const SizeT srcRowSize = static_cast<SizeT>(width) * internalBpp;
|
|
const SizeT srcStride = srcRowSize;
|
|
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
|
|
|
if (xoffset + width > static_cast<GLsizei>(texelSize.x()) ||
|
|
yoffset + height > static_cast<GLsizei>(texelSize.y())) {
|
|
MGLOG_E("TexSubImage2D_State: Specified region exceeds texture dimensions");
|
|
free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const auto* srcData = static_cast<const Uint8*>(processedPixels);
|
|
Uint8* destData = static_cast<Uint8*>(textureMipmapObject->MapMipmapData(textureUploadTarget, level));
|
|
|
|
if (destData) {
|
|
for (GLsizei y = 0; y < height; y++) {
|
|
const SizeT destRowOffset = (yoffset + y) * destRowSize + xoffset * internalBpp;
|
|
const SizeT srcRowOffset = y * srcStride;
|
|
Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize);
|
|
}
|
|
}
|
|
|
|
free(processedPixels);
|
|
|
|
MGLOG_D("%s: mark mip %d as dirty", __func__, level);
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
MaybeAutoGenerateMipmap(target, textureObject, false, level);
|
|
}
|
|
|
|
void TexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type,
|
|
const GLvoid* pixels) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, 1)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(),
|
|
texturePixelDataType))
|
|
return;
|
|
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
const void* originalPixels = pixels;
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
if (!originalPixels) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"No data supplied from pixels parameter and no PBO bound."));
|
|
return;
|
|
}
|
|
|
|
SizeT inputSize = 0;
|
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(),
|
|
textureInputFormat, texturePixelDataType, {width, 1, 1}, false, inputSize);
|
|
if (!processedPixels || inputSize == 0) {
|
|
if (processedPixels) free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType);
|
|
const SizeT copySize = static_cast<SizeT>(width) * internalBpp;
|
|
Uint8* destData = static_cast<Uint8*>(textureMipmapObject->MapMipmapData(textureUploadTarget, level));
|
|
if (destData) {
|
|
Memcpy(destData + static_cast<SizeT>(xoffset) * internalBpp, processedPixels, copySize);
|
|
}
|
|
|
|
free(processedPixels);
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
MaybeAutoGenerateMipmap(target, textureObject, false, level);
|
|
}
|
|
|
|
// TexParameteriv/TexParameterfv are introduced in OpenGL 4.0, so do not support them for now.
|
|
void TexParameterf_State(GLenum target, GLenum pname, GLfloat param) {
|
|
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
textureObject->GetSamplerObject()->SetMagFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
textureObject->GetSamplerObject()->SetMinFilter(MG_Util::ConvertGLEnumToSamplerFilterMode((GLenum)param));
|
|
textureObject->GetSamplerObject()->SetMipmapMode(MG_Util::ConvertGLEnumToSamplerMipmapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD: {
|
|
Float maxLod = textureObject->GetSamplerObject()->GetMaxLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(param, maxLod);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_MAX_LOD: {
|
|
Float minLod = textureObject->GetSamplerObject()->GetMinLod();
|
|
textureObject->GetSamplerObject()->SetLodRange(minLod, param);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
textureObject->SetBaseLevel((Uint)param);
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
textureObject->SetMaxLevel((Uint)param);
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_R:
|
|
case GL_TEXTURE_SWIZZLE_G:
|
|
case GL_TEXTURE_SWIZZLE_B:
|
|
case GL_TEXTURE_SWIZZLE_A: {
|
|
auto swizzleParam = MG_Util::ConvertGLEnumPnameToTextureSwizzleParam(pname);
|
|
auto swizzleValue = MG_Util::ConvertGLEnumToTextureSwizzleParam((GLenum)param);
|
|
textureObject->SetSwizzleParam(swizzleParam, swizzleValue);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_WRAP_S:
|
|
textureObject->GetSamplerObject()->SetWrapS(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
textureObject->GetSamplerObject()->SetWrapT(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
textureObject->GetSamplerObject()->SetWrapR(MG_Util::ConvertGLEnumToSamplerWrapMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
textureObject->GetSamplerObject()->SetCompareMode(
|
|
MG_Util::ConvertGLEnumToSamplerCompareMode((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
textureObject->GetSamplerObject()->SetSamplerCompareFunc(
|
|
MG_Util::ConvertGLEnumToSamplerCompareFunc((GLenum)param));
|
|
break;
|
|
case GL_TEXTURE_LOD_BIAS:
|
|
textureObject->GetSamplerObject()->SetLodBias(param);
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
if (!ValidateMaxAnisotropy(param, __func__)) return;
|
|
textureObject->GetSamplerObject()->SetMaxAnisotropy(param);
|
|
break;
|
|
case GL_GENERATE_MIPMAP:
|
|
g_autoGenerateMipmapByTextureId[textureObject->GetExternalIndex()] = (param != 0.0f);
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_RGBA:
|
|
// Not supported in this function
|
|
case GL_TEXTURE_BORDER_COLOR:
|
|
// Not supported in this function
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", __func__,
|
|
std::format("pname {} is not a valid texture parameter.", MG_Util::ConvertGLEnumToString(pname))));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TexParameteri_State(GLenum target, GLenum pname, GLint param) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
|
|
TextureParameterObject_State(textureObject, pname, param, __func__);
|
|
}
|
|
|
|
// Quick and dirty TexParameter*v implementation to make NeoForge happy.
|
|
// TODO: implement the missing part
|
|
void TexParameterfv_State(GLenum target, GLenum pname, const GLfloat* params) {
|
|
switch (pname) {
|
|
case GL_TEXTURE_BORDER_COLOR: {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
SetTextureBorderColorFromFloats(textureObject, params);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_SWIZZLE_RGBA: {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
GLint signedParams[4] = {static_cast<GLint>(params[0]), static_cast<GLint>(params[1]),
|
|
static_cast<GLint>(params[2]), static_cast<GLint>(params[3])};
|
|
if (!SetTextureSwizzleParamsFromInts(textureObject, signedParams, __func__)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
TexParameterf_State(target, pname, *params);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TexParameteriv_State(GLenum target, GLenum pname, const GLint* params) {
|
|
switch (pname) {
|
|
case GL_TEXTURE_BORDER_COLOR: {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
SetTextureBorderColorFromInts(textureObject, params);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_SWIZZLE_RGBA: {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
if (!SetTextureSwizzleParamsFromInts(textureObject, params, __func__)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
TexParameteri_State(target, pname, *params);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TexParameterIiv_State(GLenum target, GLenum pname, const GLint* params) {
|
|
switch (pname) {
|
|
case GL_TEXTURE_BORDER_COLOR: {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
SetTextureBorderColorFromIntegerInts(textureObject, params);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_SWIZZLE_RGBA: {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
GLint signedParams[4] = {static_cast<GLint>(params[0]), static_cast<GLint>(params[1]),
|
|
static_cast<GLint>(params[2]), static_cast<GLint>(params[3])};
|
|
if (!SetTextureSwizzleParamsFromInts(textureObject, signedParams, __func__)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
TexParameteri_State(target, pname, *params);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TexParameterIuiv_State(GLenum target, GLenum pname, const GLuint* params) {
|
|
switch (pname) {
|
|
case GL_TEXTURE_BORDER_COLOR: {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
SetTextureBorderColorFromUnsignedInts(textureObject, params);
|
|
break;
|
|
}
|
|
case GL_TEXTURE_SWIZZLE_RGBA: {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!textureObject) return;
|
|
|
|
Vec4<TextureSwizzleParam> swizzleParams;
|
|
for (int i = 0; i < 4; i++) {
|
|
swizzleParams[i] = MG_Util::ConvertGLEnumToTextureSwizzleParam(static_cast<GLint>(params[i]));
|
|
if (TextureSwizzleParam::Unknown == swizzleParams[i]) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "`params` is not valid."));
|
|
return;
|
|
}
|
|
}
|
|
textureObject->SetSwizzleParamRGBA(swizzleParams);
|
|
break;
|
|
}
|
|
default:
|
|
TexParameteri_State(target, pname, static_cast<GLint>(*params));
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TexImage3DMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (textureTarget != TextureTarget::Texture2DMultisampleArray) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Target must be GL_TEXTURE_2D_MULTISAMPLE_ARRAY or its proxy."));
|
|
return;
|
|
}
|
|
|
|
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
|
|
TexturePixelDataType::UnsignedByte);
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
if (!ValidateTextureMultisampleStorage(textureTarget, samples, width, height, depth, textureInternalFormat,
|
|
__func__))
|
|
return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
const Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
|
|
AllocateMultisampleTextureStorage(textureObject, textureUploadTarget, textureInternalFormat, samples, width,
|
|
height, depth, fixedsamplelocations);
|
|
}
|
|
|
|
void TexImage2DMultisample_State(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLboolean fixedsamplelocations) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (textureTarget != TextureTarget::Texture2DMultisample) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Target must be GL_TEXTURE_2D_MULTISAMPLE or its proxy."));
|
|
return;
|
|
}
|
|
|
|
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
|
|
TexturePixelDataType::UnsignedByte);
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
if (!ValidateTextureMultisampleStorage(textureTarget, samples, width, height, 1, textureInternalFormat,
|
|
__func__))
|
|
return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
const Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
|
|
AllocateMultisampleTextureStorage(textureObject, textureUploadTarget, textureInternalFormat, samples, width,
|
|
height, 1, fixedsamplelocations);
|
|
}
|
|
|
|
void TexImage3D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels) {
|
|
MGLOG_D(
|
|
"%s called with target: %s, level: %d, internalformat: %s, width: %d, height: %d, depth: %d, "
|
|
"border: %d, format: %s, type: %s (%u), pixels: %p",
|
|
__func__,
|
|
MG_Util::ConvertTextureUploadTargetToString(MG_Util::ConvertGLEnumToTextureUploadTarget(target)).c_str(),
|
|
level,
|
|
MG_Util::ConvertTextureInternalFormatToString(MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat))
|
|
.c_str(),
|
|
width, height, depth, border,
|
|
MG_Util::ConvertTextureInputFormatToString(MG_Util::ConvertGLEnumToTextureInputFormat(format)).c_str(),
|
|
MG_Util::ConvertTexturePixelDataTypeToString(MG_Util::ConvertGLEnumToTexturePixelDataType(type)).c_str(),
|
|
type, pixels);
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
if (!TextureImpl::ValidateTextureBorderNumber(border)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureInternalFormat,
|
|
texturePixelDataType))
|
|
return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
// Depth and depth-stencil formats are not three-dimensional in core GL (2D-array targets are fine).
|
|
if ((textureUploadTarget == TextureUploadTarget::Texture3D ||
|
|
textureUploadTarget == TextureUploadTarget::ProxyTexture3D) &&
|
|
(textureInputFormat == TextureInputFormat::DepthComponent ||
|
|
textureInputFormat == TextureInputFormat::DepthStencil)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Depth formats are invalid for 3D texture targets"));
|
|
return;
|
|
}
|
|
|
|
// TODO: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the
|
|
// GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped.
|
|
// GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
|
|
// target and the data would be unpacked from the buffer object such that the memory reads required would
|
|
// exceed the data store size.
|
|
// GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
|
|
// target and data is not evenly divisible into the number of bytes needed to store in memory a datum
|
|
// indicated by type.
|
|
// ======================= Processing ================================
|
|
textureInternalFormat =
|
|
MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
// ======================= Processing ================================
|
|
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
|
|
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
|
|
TextureSwizzleParam::Zero, TextureSwizzleParam::Red});
|
|
}
|
|
|
|
SizeT imageSize = 0;
|
|
const SizeT inputBpp = MG_Util::GetInputBytesPerPixel(textureInputFormat, texturePixelDataType);
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
|
const SizeT internalBytes = width * height * depth * internalBpp;
|
|
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
|
|
const void* originalPixels = pixels;
|
|
|
|
// PBO
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
MGLOG_D("%s: Using Pixel Unpack Buffer Object ID: %u", __func__,
|
|
pixelUnpackBufferObject->GetExternalIndex());
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
// Allocate in TextureObject
|
|
if (isProxy) {
|
|
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
|
} else {
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, height, depth}, internalBytes});
|
|
}
|
|
|
|
if (!originalPixels) {
|
|
MGLOG_D("%s: No input pixel and no PBO bound, no pixel transfer", __func__);
|
|
return;
|
|
}
|
|
|
|
void* processedPixels = nullptr;
|
|
processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureInternalFormat,
|
|
textureInputFormat, texturePixelDataType, {width, height, depth}, false, imageSize);
|
|
|
|
if (processedPixels && imageSize > 0) {
|
|
if (imageSize != internalBytes) {
|
|
MGLOG_W("%s: Processed pixel data size (%zu) does not match expected size (%zu). "
|
|
"This may indicate an alignment or processing issue.",
|
|
__func__, imageSize, internalBytes);
|
|
}
|
|
|
|
const SizeT copySize = std::min(imageSize, internalBytes);
|
|
DataPtr texelInput{processedPixels, copySize};
|
|
textureMipmapObject->UpdateMipmapSubData(textureUploadTarget, level, texelInput);
|
|
}
|
|
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
|
|
free(processedPixels);
|
|
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
|
|
}
|
|
|
|
void TexImage2D_State(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border,
|
|
GLenum format, GLenum type, const void* pixels) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
MGLOG_D("%s called with target: %s (%s), level: %d, internalformat: %s (%s), width: %d, height: %d, "
|
|
"border: %d, format: %s (%s), type: %s (%s), pixels: %p",
|
|
__func__, MG_Util::ConvertTextureUploadTargetToString(textureUploadTarget).c_str(),
|
|
MG_Util::ConvertGLEnumToString(target).c_str(), level,
|
|
MG_Util::ConvertTextureInternalFormatToString(textureInternalFormat).c_str(),
|
|
MG_Util::ConvertGLEnumToString(internalformat).c_str(), width, height, border,
|
|
MG_Util::ConvertTextureInputFormatToString(textureInputFormat).c_str(),
|
|
MG_Util::ConvertGLEnumToString(format).c_str(),
|
|
MG_Util::ConvertTexturePixelDataTypeToString(texturePixelDataType).c_str(),
|
|
MG_Util::ConvertGLEnumToString(type).c_str(), pixels);
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, height)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
if (!TextureImpl::ValidateTextureBorderNumber(border)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureInternalFormat,
|
|
texturePixelDataType))
|
|
return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
// TODO: GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the
|
|
// GL_PIXEL_UNPACK_BUFFER target and the buffer object's data store is currently mapped.
|
|
// GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
|
|
// target and the data would be unpacked from the buffer object such that the memory reads required would
|
|
// exceed the data store size.
|
|
// GL_INVALID_OPERATION is generated if a non-zero buffer object name is bound to the GL_PIXEL_UNPACK_BUFFER
|
|
// target and data is not evenly divisible into the number of bytes needed to store in memory a datum
|
|
// indicated by type.
|
|
|
|
// ======================= Processing ================================
|
|
textureInternalFormat =
|
|
MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
// ======================= Processing ================================
|
|
if (internalformat == GL_ALPHA || format == GL_ALPHA) {
|
|
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
|
|
TextureSwizzleParam::Zero, TextureSwizzleParam::Red});
|
|
}
|
|
|
|
SizeT imageSize = 0;
|
|
const SizeT inputBpp = MG_Util::GetInputBytesPerPixel(textureInputFormat, texturePixelDataType);
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
|
const SizeT internalBytes = width * height * internalBpp;
|
|
|
|
MGLOG_D("%s: working on texture %d", __func__, textureObject->GetExternalIndex());
|
|
|
|
MGLOG_D("%s: texture object had internal format %s, new format %s", __func__,
|
|
MG_Util::ConvertTextureInternalFormatToString(textureObject->GetFormat()).c_str(),
|
|
MG_Util::ConvertTextureInternalFormatToString(textureInternalFormat).c_str());
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
|
|
const void* originalPixels = pixels;
|
|
|
|
// PBO
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
MGLOG_D("%s: Using Pixel Unpack Buffer Object ID: %u", __func__,
|
|
pixelUnpackBufferObject->GetExternalIndex());
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
// Allocate in TextureObject
|
|
if (isProxy) {
|
|
MGLOG_D("%s: isProxy = true, not allocating", __func__);
|
|
} else {
|
|
MGLOG_D("%s: Allocating %d bytes at mip %d", __func__, internalBytes, level);
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level,
|
|
{{width, height, 1}, internalBytes});
|
|
}
|
|
|
|
if (!originalPixels) {
|
|
MGLOG_D("%s: No input pixel and no PBO bound, no pixel transfer", __func__);
|
|
return;
|
|
}
|
|
|
|
void* processedPixels = nullptr;
|
|
processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureInternalFormat,
|
|
textureInputFormat, texturePixelDataType, {width, height, 1}, false, imageSize);
|
|
|
|
if (processedPixels && imageSize > 0) {
|
|
if (imageSize != internalBytes) {
|
|
MGLOG_W("TexImage2D_State: Processed pixel data size (%zu) does not match expected size (%zu). "
|
|
"This may indicate an alignment or processing issue.",
|
|
imageSize, internalBytes);
|
|
}
|
|
|
|
const SizeT copySize = std::min(imageSize, internalBytes);
|
|
DataPtr texelInput{processedPixels, copySize};
|
|
textureMipmapObject->UpdateMipmapSubData(textureUploadTarget, level, texelInput);
|
|
}
|
|
|
|
free(processedPixels);
|
|
|
|
MGLOG_D("%s: mark mip %d as dirty", __func__, level);
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
|
|
}
|
|
|
|
void TexImage1D_State(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format,
|
|
GLenum type, const GLvoid* pixels) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalFormat);
|
|
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeWithTextureUploadTarget(textureUploadTarget, width, 1)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
if (!TextureImpl::ValidateTextureBorderNumber(border)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureInternalFormat,
|
|
texturePixelDataType))
|
|
return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
textureInternalFormat =
|
|
MG_Util::ConvertInternalFormatToSized(textureInternalFormat, textureInputFormat, texturePixelDataType);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->CreateOrReplaceProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
if (internalFormat == GL_ALPHA || format == GL_ALPHA) {
|
|
textureObject->SetSwizzleParamRGBA({TextureSwizzleParam::Zero, TextureSwizzleParam::Zero,
|
|
TextureSwizzleParam::Zero, TextureSwizzleParam::Red});
|
|
}
|
|
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureInternalFormat, texturePixelDataType);
|
|
const SizeT internalBytes = static_cast<SizeT>(width) * internalBpp;
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
|
|
const void* originalPixels = pixels;
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
|
|
MOBILEGL_ASSERT(nullptr != static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get()),
|
|
"Texture object here should always be an object with mipmap");
|
|
auto textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (!isProxy) {
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{width, 1, 1}, internalBytes});
|
|
}
|
|
|
|
if (!originalPixels) {
|
|
return;
|
|
}
|
|
|
|
SizeT imageSize = 0;
|
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureInternalFormat,
|
|
textureInputFormat, texturePixelDataType, {width, 1, 1}, false, imageSize);
|
|
if (processedPixels && imageSize > 0) {
|
|
DataPtr texelInput{processedPixels, std::min(imageSize, internalBytes)};
|
|
textureMipmapObject->UpdateMipmapSubData(textureUploadTarget, level, texelInput);
|
|
}
|
|
|
|
free(processedPixels);
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
MaybeAutoGenerateMipmap(target, textureObject, isProxy, level);
|
|
}
|
|
|
|
void TexBuffer_State(GLenum target, GLenum internalformat, GLuint buffer) {
|
|
// ======================= Converting ================================
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
// TODO: make sure `internalformat` is in one of supported format for TexBuffer
|
|
auto& bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
|
if (!bufferObject) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"`buffer` is not zero and is not the name of an existing buffer object."));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Buffer) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"The effective target of `texture` is not `GL_TEXTURE_BUFFER`."));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
// Now we can rest assured, and down-cast texture object to texture buffer
|
|
auto* texBufferObject = static_cast<MG_State::GLState::TextureObjectBuffer*>(textureObject.get());
|
|
auto& bufferSlot = texBufferObject->GetBufferBindingSlot();
|
|
bufferSlot.Bind(bufferObject);
|
|
|
|
texBufferObject->SetInternalFormat(textureInternalFormat);
|
|
}
|
|
|
|
GLboolean IsTexture_State(GLuint texture) {
|
|
// ======================= Processing ================================
|
|
// GL 3.3 core 6.1.4: IsTexture generates no error - an unknown, deleted or merely reserved
|
|
// name is just GL_FALSE. Probing with the recording validator (as every other Is* entry
|
|
// point already avoids doing) would leave a spurious INVALID_VALUE behind.
|
|
return MG_State::pGLContext->ValidateTextureObject(texture) ? GL_TRUE : GL_FALSE;
|
|
}
|
|
|
|
void GetTexParameterIuiv_State(GLenum target, GLenum pname, GLuint* params) {
|
|
if (params == nullptr) return;
|
|
|
|
if (pname == GL_TEXTURE_BORDER_COLOR) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
const auto& borderColor = textureObject->GetBorderColorUI();
|
|
params[0] = borderColor.x();
|
|
params[1] = borderColor.y();
|
|
params[2] = borderColor.z();
|
|
params[3] = borderColor.w();
|
|
return;
|
|
}
|
|
|
|
GLint signedParams[4] = {0, 0, 0, 0};
|
|
if (!GetTexParameteriv_State(target, pname, signedParams)) return;
|
|
const int componentCount = pname == GL_TEXTURE_BORDER_COLOR || pname == GL_TEXTURE_SWIZZLE_RGBA ? 4 : 1;
|
|
for (int i = 0; i < componentCount; ++i) {
|
|
params[i] = static_cast<GLuint>(signedParams[i]);
|
|
}
|
|
}
|
|
|
|
void GetTexParameterIiv_State(GLenum target, GLenum pname, GLint* params) {
|
|
if (params == nullptr) return;
|
|
|
|
if (pname == GL_TEXTURE_BORDER_COLOR) {
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
const auto& borderColor = textureObject->GetBorderColorI();
|
|
params[0] = borderColor.x();
|
|
params[1] = borderColor.y();
|
|
params[2] = borderColor.z();
|
|
params[3] = borderColor.w();
|
|
return;
|
|
}
|
|
|
|
GetTexParameteriv_State(target, pname, params);
|
|
}
|
|
|
|
Bool GetTexParameteriv_State(GLenum target, GLenum pname, GLint* params) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return false;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetMagFilter(), SamplerMipmapMode::None);
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerFilterModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetMinFilter(),
|
|
textureObject->GetSamplerObject()->GetMipmapMode());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMinLod());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_LOD:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMaxLod());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetLevelRange().x());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetLevelRange().y());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_FORMAT:
|
|
if (params) {
|
|
*params = textureObject->IsImmutable() ? GL_TRUE : GL_FALSE;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_LEVELS:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetImmutableLevels());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_BORDER_COLOR:
|
|
if (params) {
|
|
const auto& borderColor = textureObject->GetBorderColor();
|
|
params[0] = static_cast<GLint>(borderColor.x());
|
|
params[1] = static_cast<GLint>(borderColor.y());
|
|
params[2] = static_cast<GLint>(borderColor.z());
|
|
params[3] = static_cast<GLint>(borderColor.w());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_RGBA:
|
|
if (params) {
|
|
const auto& swizzleParams = textureObject->GetAllSwizzleParams();
|
|
params[0] = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[0]));
|
|
params[1] = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[1]));
|
|
params[2] = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[2]));
|
|
params[3] = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[3]));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_R:
|
|
if (params) {
|
|
*params = static_cast<GLint>(
|
|
MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Red)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_G:
|
|
if (params) {
|
|
*params = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(
|
|
textureObject->GetSwizzleParam(TextureSwizzleParam::Green)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_B:
|
|
if (params) {
|
|
*params = static_cast<GLint>(
|
|
MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Blue)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_A:
|
|
if (params) {
|
|
*params = static_cast<GLint>(MG_Util::ConvertTextureSwizzleParamToGLEnum(
|
|
textureObject->GetSwizzleParam(TextureSwizzleParam::Alpha)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_S:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapT());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapR());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerCompareModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetCompareMode());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertSamplerCompareFuncToGLEnum(
|
|
textureObject->GetSamplerObject()->GetSamplerCompareFunc());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
if (params) {
|
|
*params = static_cast<GLint>(textureObject->GetSamplerObject()->GetMaxAnisotropy());
|
|
}
|
|
break;
|
|
case GL_IMAGE_FORMAT_COMPATIBILITY_TYPE:
|
|
if (params) {
|
|
*params = GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE;
|
|
}
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexParameteriv_State",
|
|
"pname is not a valid texture parameter."));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GetTexParameterfv_State(GLenum target, GLenum pname, GLfloat* params) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_MAG_FILTER:
|
|
if (params) {
|
|
*params = (GLfloat)MG_Util::ConvertSamplerFilterModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetMagFilter(), SamplerMipmapMode::None);
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MIN_FILTER:
|
|
if (params) {
|
|
*params = (GLfloat)MG_Util::ConvertSamplerFilterModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetMinFilter(),
|
|
textureObject->GetSamplerObject()->GetMipmapMode());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MIN_LOD:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetSamplerObject()->GetMinLod());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_LOD:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetSamplerObject()->GetMaxLod());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_BASE_LEVEL:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetLevelRange().x());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_LEVEL:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetLevelRange().y());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_FORMAT:
|
|
if (params) {
|
|
*params = textureObject->IsImmutable() ? 1.0f : 0.0f;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_IMMUTABLE_LEVELS:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetImmutableLevels());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_BORDER_COLOR:
|
|
if (params) {
|
|
const auto& borderColor = textureObject->GetBorderColor();
|
|
params[0] = borderColor.x();
|
|
params[1] = borderColor.y();
|
|
params[2] = borderColor.z();
|
|
params[3] = borderColor.w();
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_R:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(
|
|
MG_Util::ConvertTextureSwizzleParamToGLEnum(textureObject->GetSwizzleParam(TextureSwizzleParam::Red)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_G:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(
|
|
textureObject->GetSwizzleParam(TextureSwizzleParam::Green)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_B:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(
|
|
textureObject->GetSwizzleParam(TextureSwizzleParam::Blue)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_A:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(
|
|
textureObject->GetSwizzleParam(TextureSwizzleParam::Alpha)));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SWIZZLE_RGBA:
|
|
if (params) {
|
|
const auto& swizzleParams = textureObject->GetAllSwizzleParams();
|
|
params[0] = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[0]));
|
|
params[1] = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[1]));
|
|
params[2] = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[2]));
|
|
params[3] = static_cast<GLfloat>(MG_Util::ConvertTextureSwizzleParamToGLEnum(swizzleParams[3]));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_S:
|
|
if (params) {
|
|
*params =
|
|
(GLfloat)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapS());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_T:
|
|
if (params) {
|
|
*params =
|
|
(GLfloat)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapT());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_WRAP_R:
|
|
if (params) {
|
|
*params =
|
|
(GLfloat)MG_Util::ConvertSamplerWrapModeToGLEnum(textureObject->GetSamplerObject()->GetWrapR());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPARE_MODE:
|
|
if (params) {
|
|
*params = (GLfloat)MG_Util::ConvertSamplerCompareModeToGLEnum(
|
|
textureObject->GetSamplerObject()->GetCompareMode());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPARE_FUNC:
|
|
if (params) {
|
|
*params = (GLfloat)MG_Util::ConvertSamplerCompareFuncToGLEnum(
|
|
textureObject->GetSamplerObject()->GetSamplerCompareFunc());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
|
if (params) {
|
|
*params = textureObject->GetSamplerObject()->GetMaxAnisotropy();
|
|
}
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexParameterfv_State",
|
|
"pname is not a valid texture parameter."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GetTexLevelParameteriv_State(GLenum target, GLint level, GLenum pname, GLint* params) {
|
|
MGLOG_D("GetTexLevelParameteriv_State called");
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_WIDTH:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_HEIGHT:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_DEPTH:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_INTERNAL_FORMAT:
|
|
if (params) {
|
|
*params = (GLint)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SAMPLES:
|
|
if (params) {
|
|
*params = textureObject->GetSamples();
|
|
}
|
|
break;
|
|
case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS:
|
|
if (params) {
|
|
*params = textureObject->HasFixedSampleLocations() ? GL_TRUE : GL_FALSE;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_RED_TYPE:
|
|
case GL_TEXTURE_GREEN_TYPE:
|
|
case GL_TEXTURE_BLUE_TYPE:
|
|
case GL_TEXTURE_ALPHA_TYPE:
|
|
case GL_TEXTURE_DEPTH_TYPE:
|
|
case GL_TEXTURE_RED_SIZE:
|
|
case GL_TEXTURE_GREEN_SIZE:
|
|
case GL_TEXTURE_BLUE_SIZE:
|
|
case GL_TEXTURE_ALPHA_SIZE:
|
|
case GL_TEXTURE_DEPTH_SIZE:
|
|
case GL_TEXTURE_STENCIL_SIZE:
|
|
if (params) {
|
|
*params = GetTextureLevelComponentParameter(textureObject->GetFormat(), pname);
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED:
|
|
if (params) {
|
|
*params = GL_FALSE;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED_IMAGE_SIZE:
|
|
if (params) {
|
|
*params = 0;
|
|
}
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameteriv_State",
|
|
"pname is not a valid texture level parameter."));
|
|
return;
|
|
}
|
|
if (params) MGLOG_D("returned %u", *params);
|
|
}
|
|
|
|
void GetTexLevelParameterfv_State(GLenum target, GLint level, GLenum pname, GLfloat* params) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureLevelWithUploadTarget(textureUploadTarget, level)) return;
|
|
|
|
// ======================= Processing ================================
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
switch (pname) {
|
|
case GL_TEXTURE_WIDTH:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).x();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_HEIGHT:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).y();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_DEPTH:
|
|
if (params) {
|
|
switch (textureObject->GetStorageType()) {
|
|
case TextureStorageType::Mipmap: {
|
|
const auto textureMipmapObject =
|
|
static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
*params = (GLfloat)textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level).z();
|
|
break;
|
|
}
|
|
default:
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
}
|
|
break;
|
|
case GL_TEXTURE_INTERNAL_FORMAT:
|
|
if (params) {
|
|
*params = (GLfloat)MG_Util::ConvertTextureInternalFormatToGLEnum(textureObject->GetFormat());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_SAMPLES:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(textureObject->GetSamples());
|
|
}
|
|
break;
|
|
case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS:
|
|
if (params) {
|
|
*params = textureObject->HasFixedSampleLocations() ? 1.0f : 0.0f;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_RED_TYPE:
|
|
case GL_TEXTURE_GREEN_TYPE:
|
|
case GL_TEXTURE_BLUE_TYPE:
|
|
case GL_TEXTURE_ALPHA_TYPE:
|
|
case GL_TEXTURE_DEPTH_TYPE:
|
|
case GL_TEXTURE_RED_SIZE:
|
|
case GL_TEXTURE_GREEN_SIZE:
|
|
case GL_TEXTURE_BLUE_SIZE:
|
|
case GL_TEXTURE_ALPHA_SIZE:
|
|
case GL_TEXTURE_DEPTH_SIZE:
|
|
case GL_TEXTURE_STENCIL_SIZE:
|
|
if (params) {
|
|
*params = static_cast<GLfloat>(GetTextureLevelComponentParameter(textureObject->GetFormat(), pname));
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED:
|
|
if (params) {
|
|
*params = 0.0f;
|
|
}
|
|
break;
|
|
case GL_TEXTURE_COMPRESSED_IMAGE_SIZE:
|
|
if (params) {
|
|
*params = 0.0f;
|
|
}
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexLevelParameterfv_State",
|
|
"pname is not a valid texture level parameter."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GetCompressedTexImage_State(GLenum target, GLint level, void* img) {
|
|
// TODO: implement
|
|
}
|
|
|
|
void GenTextures_State(GLsizei n, GLuint* textures) {
|
|
// ===================== Error Checking ==============================
|
|
if (n < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GenTextures_State", "n must be non-negative"));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
Vector<Uint> textureNames;
|
|
MG_State::pGLContext->GenTextureNames(n, textureNames);
|
|
Memcpy(textures, textureNames.data(), n * sizeof(GLuint));
|
|
}
|
|
|
|
void DeleteTextures_State(GLsizei n, const GLuint* textures) {
|
|
// ===================== Error Checking ==============================
|
|
if (n < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteTextures_State", "n must be non-negative."));
|
|
return;
|
|
}
|
|
|
|
if (!textures) {
|
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "DeleteTextures_State",
|
|
"Texture names array cannot be null."));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
for (SizeT i = 0; i < static_cast<SizeT>(n); ++i) {
|
|
Uint textureName = textures[i];
|
|
if (textureName == 0) continue;
|
|
if (!MG_State::pGLContext->ValidateTextureName(textureName)) continue;
|
|
MG_State::pGLContext->MarkTextureObjectForDeletion(textureName);
|
|
}
|
|
}
|
|
|
|
void CopyTexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x,
|
|
GLint y, GLsizei width, GLsizei height) {
|
|
// TODO: implement
|
|
}
|
|
|
|
void CopyTexSubImage2D_Backend(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
|
|
GLsizei width, GLsizei height) {
|
|
MG_Backend::gBackendFunctionsTable.GL.CopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height);
|
|
}
|
|
|
|
void CopyImageSubData_Backend(const SharedPtr<MG_State::GLState::ITextureObject>& srcTexture,
|
|
GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ,
|
|
const SharedPtr<MG_State::GLState::ITextureObject>& dstTexture,
|
|
GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ,
|
|
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
|
|
auto copyImageSubData = MG_Backend::gBackendFunctionsTable.GL.CopyImageSubData;
|
|
if (!copyImageSubData) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Backend does not support image-to-image copies."));
|
|
return;
|
|
}
|
|
copyImageSubData(srcTexture, srcTarget, srcLevel, srcX, srcY, srcZ, dstTexture, dstTarget, dstLevel, dstX,
|
|
dstY, dstZ, srcWidth, srcHeight, srcDepth);
|
|
}
|
|
|
|
Bool ValidateCopyImageSubData_State(const SharedPtr<MG_State::GLState::ITextureObject>& srcTexture,
|
|
GLenum srcTarget, GLint srcLevel,
|
|
const SharedPtr<MG_State::GLState::ITextureObject>& dstTexture,
|
|
GLenum dstTarget, GLint dstLevel,
|
|
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
|
|
if (!TextureImpl::ValidateTextureObject(srcTexture) || !TextureImpl::ValidateTextureObject(dstTexture)) {
|
|
return false;
|
|
}
|
|
const auto srcTextureTarget = MG_Util::ConvertGLEnumToTextureTarget(srcTarget);
|
|
const auto dstTextureTarget = MG_Util::ConvertGLEnumToTextureTarget(dstTarget);
|
|
if (!TextureImpl::ValidateTextureTarget(srcTextureTarget) ||
|
|
!TextureImpl::ValidateTextureTarget(dstTextureTarget)) {
|
|
return false;
|
|
}
|
|
if (!TextureImpl::ValidateTextureTargetUniformity(srcTexture, srcTextureTarget) ||
|
|
!TextureImpl::ValidateTextureTargetUniformity(dstTexture, dstTextureTarget)) {
|
|
return false;
|
|
}
|
|
if (!TextureImpl::ValidateTextureLevelNumber(srcLevel) ||
|
|
!TextureImpl::ValidateTextureLevelNumber(dstLevel)) {
|
|
return false;
|
|
}
|
|
if (srcWidth < 0 || srcHeight < 0 || srcDepth < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Copy dimensions must be non-negative."));
|
|
return false;
|
|
}
|
|
if (srcWidth == 0 || srcHeight == 0 || srcDepth == 0) {
|
|
return false;
|
|
}
|
|
if (!TextureImpl::ValidateBaseInternalFormatMatch(srcTexture->GetFormat(), dstTexture->GetFormat())) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CopyTexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) {
|
|
// TODO: implement
|
|
}
|
|
|
|
Bool CopyTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
|
GLsizei height, GLint border) {
|
|
auto internalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return false;
|
|
|
|
const auto& currentReadFBO =
|
|
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
|
if (!currentReadFBO) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CopyTexImage2D_State",
|
|
"No framebuffer is currently bound to the GL_READ_FRAMEBUFFER target."));
|
|
return false;
|
|
}
|
|
|
|
Bool isDepth = MG_Util::IsDepthFormatInternalFormat(internalFormat);
|
|
Bool isStencil = MG_Util::IsStencilFormatInternalFormat(internalFormat);
|
|
TextureInternalFormat srcInternalFormat = TextureInternalFormat::Unknown;
|
|
#define GET_SRC_INTERNAL_FORMAT(AttachmentType) \
|
|
const auto& srcAttachment = currentReadFBO->GetAttachment(AttachmentType); \
|
|
if (srcAttachment.IsTexture()) { \
|
|
const auto& texObj = srcAttachment.GetTexture(); \
|
|
srcInternalFormat = texObj->GetFormat(); \
|
|
} else if (srcAttachment.IsRenderbuffer()) { \
|
|
const auto& rboObj = srcAttachment.GetRenderbuffer(); \
|
|
srcInternalFormat = rboObj->GetInternalFormat(); \
|
|
} else { \
|
|
MG_State::pGLContext->RecordError( \
|
|
ErrorCode::InvalidOperation, \
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "CopyTexImage2D_State", \
|
|
"The attachment specified by the read buffer is incomplete.")); \
|
|
return false; \
|
|
}
|
|
if (isDepth) {
|
|
GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Depth);
|
|
} else if (isStencil) {
|
|
GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Stencil);
|
|
} else {
|
|
const auto& readBufferType = currentReadFBO->GetReadBuffer();
|
|
GET_SRC_INTERNAL_FORMAT(readBufferType);
|
|
}
|
|
|
|
if (!TextureImpl::ValidateBaseInternalFormatMatch(internalFormat, srcInternalFormat)) THROW_UNIMPL_EXCEPTION;
|
|
|
|
GLenum outInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(srcInternalFormat);
|
|
GLenum realInternalFormat = GL_RGBA8;
|
|
GLenum format = GL_DEPTH_COMPONENT;
|
|
GLenum type = GL_UNSIGNED_INT;
|
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(outInternalFormat, PixelFormatNormalizeOptionBit::None,
|
|
&realInternalFormat, &format, &type);
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
TexImage2D_State(target, level, (GLint)realInternalFormat, width, height, border, format, type, nullptr);
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).Bind(pixelUnpackBufferObject);
|
|
return true;
|
|
}
|
|
|
|
void CopyTexImage2D_Backend(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
|
GLsizei height, GLint border) {
|
|
MG_Backend::gBackendFunctionsTable.GL.CopyTexImage2D(target, level, internalformat, x, y, width, height,
|
|
border);
|
|
}
|
|
|
|
void CopyTexImage1D_State(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
|
GLint border) {
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexSubImage3D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
|
|
GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize,
|
|
const void* data) {
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexSubImage2D_State(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
|
GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexSubImage1D_State(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
|
GLsizei imageSize, const void* data) {
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexImage3D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexImage2D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLint border, GLsizei imageSize, const void* data) {
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void CompressedTexImage1D_State(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
|
|
GLsizei imageSize, const void* data) {
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& textureObject = GetTextureObjectByTarget(textureUploadTarget, textureTarget);
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
// TODO: implement
|
|
THROW_UNIMPL_EXCEPTION;
|
|
}
|
|
|
|
void BindTexture_State(GLenum target, GLuint texture) {
|
|
const Int activeUnit = MG_State::pGLContext->GetActiveTextureUnit();
|
|
MGLOG_D("BindTexture_State called with target: 0x%X, texture: %u, unit: %d", target, texture, activeUnit);
|
|
// ======================= Converting ================================
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
|
|
// Name 0 unbinds the current target from the active texture unit.
|
|
if (texture == 0) {
|
|
auto& currentUnit = MG_State::pGLContext->GetTextureUnitObject(activeUnit);
|
|
auto& bindingSlot = currentUnit.GetBindingSlot(textureTarget);
|
|
bindingSlot.Bind(nullptr);
|
|
MG_State::pGLContext->NoteTextureUnitTouched(activeUnit);
|
|
return;
|
|
}
|
|
|
|
// Some desktop-side helper code saves GL_ACTIVE_TEXTURE and later feeds it back into glBindTexture
|
|
// as if it were a texture name. Treating that as a no-op preserves the previous "invalid bind does not
|
|
// change texture state" behavior, but avoids poisoning the error state every frame.
|
|
if (!MG_State::pGLContext->ValidateTextureName(texture) && texture >= GL_TEXTURE0 && texture <= GL_TEXTURE31) {
|
|
return;
|
|
}
|
|
|
|
// GL 3.3 core 3.8.1: a name that GenTextures never returned - or that has since been deleted -
|
|
// is not a legal bind target in the core profile (no application-generated names), and the error
|
|
// is INVALID_OPERATION, not INVALID_VALUE.
|
|
if (!MG_State::pGLContext->ValidateTextureName(texture)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "BindTexture_State", "Invalid texture name"));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
Bool doesTextureExist = MG_State::pGLContext->ValidateTextureObject(texture);
|
|
if (!doesTextureExist) {
|
|
MG_State::pGLContext->CreateTextureObject(texture, textureTarget);
|
|
}
|
|
auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
|
|
|
// ===================== Error Checking ==============================
|
|
if (doesTextureExist && !TextureImpl::ValidateTextureTargetUniformity(textureObject, textureTarget)) return;
|
|
|
|
// ======================= Processing ================================
|
|
auto& currentUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = currentUnit.GetBindingSlot(textureTarget);
|
|
bindingSlot.Bind(textureObject);
|
|
MG_State::pGLContext->NoteTextureUnitTouched(MG_State::pGLContext->GetActiveTextureUnit());
|
|
}
|
|
|
|
void ActiveTexture_State(GLenum texture) {
|
|
// ===================== Error Checking ==============================
|
|
if (texture < GL_TEXTURE0 || texture > GL_TEXTURE31) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", "ActiveTexture_State",
|
|
std::format("Texture must be one of GL_TEXTUREi, where i is in the range 0 to 31, but got "
|
|
"invalid enum: 0x{:X}, which may stand for unit {}.",
|
|
texture, texture - GL_TEXTURE0)));
|
|
return;
|
|
}
|
|
|
|
// ======================= Processing ================================
|
|
const Int unit = (Int)texture - GL_TEXTURE0;
|
|
MGLOG_D("ActiveTexture_State: unit = %d", unit);
|
|
MG_State::pGLContext->SetActiveTextureUnit(unit);
|
|
}
|
|
|
|
void GetTexImage_Backend(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
|
MG_Backend::gBackendFunctionsTable.GL.GetTexImage(target, level, format, type, pixels);
|
|
}
|
|
|
|
// Add to GL_Texture.cpp
|
|
Bool GetTexImage_State(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
|
// ======================= Converting ================================
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
|
|
// ===================== Error Checking ==============================
|
|
// Validate target
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State", "Invalid texture target"));
|
|
return false;
|
|
}
|
|
|
|
// Validate level
|
|
if (level < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State", "Level must be non-negative"));
|
|
return false;
|
|
}
|
|
|
|
// Validate format
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State", "Invalid format"));
|
|
return false;
|
|
}
|
|
|
|
// Validate type
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State", "Invalid pixel data type"));
|
|
return false;
|
|
}
|
|
|
|
// Get texture object
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
Bool isProxy = TextureImpl::IsProxyTextureTarget(textureUploadTarget);
|
|
auto& textureObject =
|
|
isProxy ? TextureImpl::pProxyTextureManager->GetProxyTextureObject(textureUploadTarget)
|
|
: bindingSlot.GetBoundObject();
|
|
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) {
|
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State",
|
|
"No valid texture bound to target"));
|
|
return false;
|
|
}
|
|
|
|
// Check texture completeness
|
|
if (!textureObject->IsComplete()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State", "Texture is incomplete"));
|
|
return false;
|
|
}
|
|
|
|
// Check PBO state
|
|
const auto& pixelPackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
|
|
|
|
if (pixelPackBufferObject) {
|
|
// Check if PBO is mapped
|
|
if (pixelPackBufferObject->IsMapped()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State",
|
|
"Pixel pack buffer is currently mapped"));
|
|
return false;
|
|
}
|
|
|
|
// Check alignment
|
|
const SizeT typeSize = MG_Util::GetTexturePixelDataTypeSize(texturePixelDataType);
|
|
if (reinterpret_cast<uintptr_t>(pixels) % typeSize != 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State",
|
|
"Pixel data not aligned for pixel pack buffer"));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Shared format/type/internal-format matrix (packed-type pairing, depth-vs-color mismatch,
|
|
// integer-ness). Also rejects STENCIL_INDEX readback, which needs GL_ARB_texture_stencil8
|
|
// (not advertised by MobileGL).
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(
|
|
textureInputFormat, textureObject->GetFormat(), texturePixelDataType)) {
|
|
return false;
|
|
}
|
|
|
|
// GetTexImage-specific: DEPTH_STENCIL readback needs a depth-stencil texture (a depth-only
|
|
// texture has no stencil data to return).
|
|
if (textureInputFormat == TextureInputFormat::DepthStencil &&
|
|
textureObject->GetFormat() != TextureInternalFormat::DepthStencil &&
|
|
textureObject->GetFormat() != TextureInternalFormat::Depth24Stencil8 &&
|
|
textureObject->GetFormat() != TextureInternalFormat::Depth32FStencil8) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetTexImage_State",
|
|
"DEPTH_STENCIL readback requires a depth-stencil texture"));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void CopyTextureImageToClientOrPBO_State(const SharedPtr<MG_State::GLState::ITextureObject>& textureObject,
|
|
TextureUploadTarget textureUploadTarget, GLint level, GLenum format,
|
|
GLenum type, GLsizei bufSize, void* pixels, const char* caller) {
|
|
if (!textureObject) return;
|
|
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Texture level is out of range."));
|
|
return;
|
|
}
|
|
|
|
const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level);
|
|
const void* src = textureMipmapObject->MapMipmapData(textureUploadTarget, level);
|
|
if (!src) return;
|
|
|
|
SizeT packedSize = 0;
|
|
void* packedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataPack(
|
|
src, MG_State::pGLContext->GetPixelStoreParameters(false), textureObject->GetFormat(), texturePixelDataType,
|
|
textureInputFormat, texturePixelDataType, texelSize, false, packedSize);
|
|
if (!packedPixels || packedSize == 0) {
|
|
if (packedPixels) free(packedPixels);
|
|
return;
|
|
}
|
|
|
|
if (bufSize >= 0 && static_cast<SizeT>(bufSize) < packedSize) {
|
|
free(packedPixels);
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Destination buffer is too small."));
|
|
return;
|
|
}
|
|
|
|
const auto& pixelPackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelPack).GetBoundObject();
|
|
if (pixelPackBufferObject) {
|
|
const SizeT offset = reinterpret_cast<SizeT>(pixels);
|
|
if (offset + packedSize > pixelPackBufferObject->GetSize()) {
|
|
free(packedPixels);
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "Pixel pack buffer is too small."));
|
|
return;
|
|
}
|
|
pixelPackBufferObject->UploadSubData({packedPixels, packedSize}, offset);
|
|
} else if (pixels) {
|
|
Memcpy(pixels, packedPixels, packedSize);
|
|
}
|
|
|
|
free(packedPixels);
|
|
}
|
|
|
|
/* @INSERTION_POINT:FUNCTION_IMPLEMENTATION@ */
|
|
void CreateTextures(GLenum target, GLsizei n, GLuint* textures) {
|
|
if (n < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "n must be non-negative."));
|
|
return;
|
|
}
|
|
if (n > 0 && !textures) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture output pointer cannot be null."));
|
|
return;
|
|
}
|
|
|
|
switch (target) {
|
|
case GL_TEXTURE_1D:
|
|
case GL_TEXTURE_2D:
|
|
case GL_TEXTURE_3D:
|
|
case GL_TEXTURE_1D_ARRAY:
|
|
case GL_TEXTURE_2D_ARRAY:
|
|
case GL_TEXTURE_RECTANGLE:
|
|
case GL_TEXTURE_CUBE_MAP:
|
|
case GL_TEXTURE_CUBE_MAP_ARRAY:
|
|
case GL_TEXTURE_BUFFER:
|
|
case GL_TEXTURE_2D_MULTISAMPLE:
|
|
case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
|
|
break;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Invalid texture target."));
|
|
return;
|
|
}
|
|
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
|
|
Vector<Uint> textureNames;
|
|
MG_State::pGLContext->GenTextureNames(n, textureNames);
|
|
for (GLsizei i = 0; i < n; ++i) {
|
|
textures[i] = textureNames[i];
|
|
MG_State::pGLContext->CreateTextureObject(textureNames[i], textureTarget);
|
|
}
|
|
}
|
|
|
|
void TextureStorage1D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
if (levels < 1) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "levels must be positive."));
|
|
return;
|
|
}
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, 1, 1)) return;
|
|
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
|
|
const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
for (GLsizei level = 0; level < levels; ++level) {
|
|
const GLsizei levelWidth = std::max<GLsizei>(1, width >> level);
|
|
const SizeT byteSize = ComputeTextureStorageByteSize(textureInternalFormat, levelWidth, 1, 1);
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, 1, 1}, byteSize});
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
|
}
|
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
|
}
|
|
|
|
void TextureStorage2D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
if (levels < 1) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "levels must be positive."));
|
|
return;
|
|
}
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return;
|
|
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
if (textureObject->GetTarget() == TextureTarget::TextureCubeMap && width != height) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Cube map immutable storage must be square."));
|
|
return;
|
|
}
|
|
|
|
auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
GLenum realInternalFormat = internalformat;
|
|
GLenum realFormat = GL_RGBA;
|
|
GLenum realType = GL_UNSIGNED_BYTE;
|
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
|
MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat), PixelFormatNormalizeOptionBit::None,
|
|
&realInternalFormat, &realFormat, &realType);
|
|
const SizeT bytesPerPixel = MG_Util::GetInternalBytesPerPixel(
|
|
textureInternalFormat, MG_Util::ConvertGLEnumToTexturePixelDataType(realType));
|
|
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
for (GLsizei level = 0; level < levels; ++level) {
|
|
const GLsizei levelWidth = std::max<GLsizei>(1, width >> level);
|
|
const GLsizei levelHeight = std::max<GLsizei>(1, height >> level);
|
|
const SizeT byteSize = static_cast<SizeT>(levelWidth) * static_cast<SizeT>(levelHeight) * bytesPerPixel;
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level, {{levelWidth, levelHeight, 1}, byteSize});
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
|
}
|
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
|
}
|
|
|
|
void TextureStorage3D(GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
if (levels < 1) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "levels must be positive."));
|
|
return;
|
|
}
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, depth)) return;
|
|
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
if (!ValidateTextureStorageInternalFormat(textureInternalFormat, __func__)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
if (!ValidateTextureMutable(textureObject, __func__)) return;
|
|
if (textureObject->GetTarget() == TextureTarget::TextureCubeMapArray &&
|
|
(width != height || depth % 6 != 0)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>(
|
|
"MG_Impl/GLImpl", __func__,
|
|
"Cube map array immutable storage must be square with depth multiple of 6."));
|
|
return;
|
|
}
|
|
|
|
const auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
|
|
textureObject->SetInternalFormat(textureInternalFormat);
|
|
// Array targets keep their layer count constant across levels; only true 3D
|
|
// textures halve depth per level (GL 3.3 §3.9 glTexStorage3D).
|
|
const Bool depthMips = DepthParticipatesInMipmapping(textureObject->GetTarget());
|
|
for (GLsizei level = 0; level < levels; ++level) {
|
|
const GLsizei levelWidth = std::max<GLsizei>(1, width >> level);
|
|
const GLsizei levelHeight = std::max<GLsizei>(1, height >> level);
|
|
const GLsizei levelDepth = depthMips ? std::max<GLsizei>(1, depth >> level) : depth;
|
|
const SizeT byteSize = ComputeTextureStorageByteSize(textureInternalFormat, levelWidth, levelHeight,
|
|
levelDepth);
|
|
textureMipmapObject->AllocateStorage(textureUploadTarget, level,
|
|
{{levelWidth, levelHeight, levelDepth}, byteSize});
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, false);
|
|
}
|
|
textureObject->SetImmutableLevels(static_cast<Uint>(levels));
|
|
}
|
|
|
|
void TextureStorage2DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLboolean fixedsamplelocations) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexStorage2DMultisample(target, samples, internalformat, width, height, fixedsamplelocations);
|
|
});
|
|
}
|
|
|
|
void TextureStorage3DMultisample(GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexStorage3DMultisample(target, samples, internalformat, width, height, depth, fixedsamplelocations);
|
|
});
|
|
}
|
|
|
|
void TexStorage1D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width) {
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
TextureStorage1D(textureObject->GetExternalIndex(), levels, internalformat, width);
|
|
}
|
|
|
|
void TexStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) {
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
TextureStorage2D(textureObject->GetExternalIndex(), levels, internalformat, width, height);
|
|
}
|
|
|
|
void TexStorage3D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth) {
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
const auto textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& bindingSlot = activeUnit.GetBindingSlot(textureTarget);
|
|
auto& textureObject = bindingSlot.GetBoundObject();
|
|
if (!TextureImpl::ValidateTextureObject(textureObject)) return;
|
|
|
|
TextureStorage3D(textureObject->GetExternalIndex(), levels, internalformat, width, height, depth);
|
|
}
|
|
|
|
void TexStorage2DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLboolean fixedsamplelocations) {
|
|
TexImage2DMultisample_State(target, samples, internalformat, width, height, fixedsamplelocations);
|
|
}
|
|
|
|
void TexStorage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) {
|
|
TexImage3DMultisample_State(target, samples, internalformat, width, height, depth, fixedsamplelocations);
|
|
}
|
|
|
|
void TextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type,
|
|
const void* pixels) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexSubImage1D_State(target, level, xoffset, width, format, type, pixels);
|
|
});
|
|
}
|
|
|
|
void TextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, const void* pixels) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
|
|
TextureInputFormat textureInputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
|
TexturePixelDataType texturePixelDataType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
|
if (!TextureImpl::ValidateTexturePixelDataType(texturePixelDataType)) return;
|
|
if (!TextureImpl::ValidateTextureInputFormat(textureInputFormat)) return;
|
|
if (!TextureImpl::ValidateTextureLevelNumber(level)) return;
|
|
if (!TextureImpl::ValidateTextureSizeRange(width, height, 1)) return;
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
|
|
auto textureUploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
if (!TextureImpl::ValidateTextureUploadTarget(textureUploadTarget)) return;
|
|
if (!TextureImpl::ValidateTextureInternalFormatCompatibleWithInput(textureInputFormat, textureObject->GetFormat(),
|
|
texturePixelDataType))
|
|
return;
|
|
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
|
|
return;
|
|
}
|
|
if (!TextureImpl::ValidateTextureSubImageOffsets(textureObject, xoffset, width, yoffset, height)) return;
|
|
|
|
const void* originalPixels = pixels;
|
|
const auto& pixelUnpackBufferObject =
|
|
MG_State::pGLContext->GetBufferBindingSlot(BufferTarget::PixelUnpack).GetBoundObject();
|
|
if (pixelUnpackBufferObject) {
|
|
originalPixels = reinterpret_cast<const char*>(pixelUnpackBufferObject->MappedData()) +
|
|
reinterpret_cast<SizeT>(pixels);
|
|
}
|
|
if (!originalPixels) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"No data supplied from pixels parameter and no PBO bound."));
|
|
return;
|
|
}
|
|
|
|
SizeT inputSize = 0;
|
|
void* processedPixels = MG_Util::PixelStoreProcessor::ProcessTexturePixelsDataUnpack(
|
|
originalPixels, MG_State::pGLContext->GetPixelStoreParameters(true), textureObject->GetFormat(),
|
|
textureInputFormat, texturePixelDataType, {width, height, 1}, false, inputSize);
|
|
if (!processedPixels || inputSize == 0) {
|
|
if (processedPixels) free(processedPixels);
|
|
return;
|
|
}
|
|
|
|
const auto texelSize = textureMipmapObject->GetMipmapTexelSize(textureUploadTarget, level);
|
|
const SizeT internalBpp = MG_Util::GetInternalBytesPerPixel(textureObject->GetFormat(), texturePixelDataType);
|
|
const SizeT srcRowSize = static_cast<SizeT>(width) * internalBpp;
|
|
const SizeT destRowSize = static_cast<SizeT>(texelSize.x()) * internalBpp;
|
|
|
|
const auto* srcData = static_cast<const Uint8*>(processedPixels);
|
|
Uint8* destData = static_cast<Uint8*>(textureMipmapObject->MapMipmapData(textureUploadTarget, level));
|
|
if (destData) {
|
|
for (GLsizei y = 0; y < height; ++y) {
|
|
const SizeT destRowOffset = static_cast<SizeT>(yoffset + y) * destRowSize +
|
|
static_cast<SizeT>(xoffset) * internalBpp;
|
|
const SizeT srcRowOffset = static_cast<SizeT>(y) * srcRowSize;
|
|
Memcpy(destData + destRowOffset, srcData + srcRowOffset, srcRowSize);
|
|
}
|
|
textureMipmapObject->MarkStorageDirty(textureUploadTarget, level, true);
|
|
}
|
|
free(processedPixels);
|
|
}
|
|
|
|
void TextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
|
});
|
|
}
|
|
|
|
void TextureParameteri(GLuint texture, GLenum pname, GLint param) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
TextureParameterObject_State(textureObject, pname, param, __func__);
|
|
}
|
|
|
|
void TextureParameterf(GLuint texture, GLenum pname, GLfloat param) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
TextureParameterObjectf_State(textureObject, pname, param, __func__);
|
|
}
|
|
|
|
void TextureParameterfv(GLuint texture, GLenum pname, const GLfloat* params) {
|
|
if (!params) return;
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { TexParameterfv_State(target, pname, params); });
|
|
}
|
|
|
|
void TextureParameteriv(GLuint texture, GLenum pname, const GLint* params) {
|
|
if (!params) return;
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { TexParameteriv_State(target, pname, params); });
|
|
}
|
|
|
|
void TextureParameterIiv(GLuint texture, GLenum pname, const GLint* params) {
|
|
if (!params) return;
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexParameterIiv_State(target, pname, params);
|
|
});
|
|
}
|
|
|
|
void TextureParameterIuiv(GLuint texture, GLenum pname, const GLuint* params) {
|
|
if (!params) return;
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
TexParameterIuiv_State(target, pname, params);
|
|
});
|
|
}
|
|
|
|
void BindTextureUnit(GLuint unit, GLuint texture) {
|
|
if (unit >= MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture unit is out of range."));
|
|
return;
|
|
}
|
|
|
|
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(static_cast<Int>(unit));
|
|
MG_State::pGLContext->NoteTextureUnitTouched(static_cast<Int>(unit));
|
|
if (texture == 0) {
|
|
for (auto& slot : textureUnit.GetAllBindingSlots()) {
|
|
slot.Bind(nullptr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
auto& textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
|
if (!textureObject) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture object does not exist."));
|
|
return;
|
|
}
|
|
textureUnit.GetBindingSlot(textureObject->GetTarget()).Bind(textureObject);
|
|
}
|
|
|
|
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
const auto uploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
if (MG_Backend::pActiveBackendObject != nullptr &&
|
|
MG_Backend::pActiveBackendObject->GetBackendType() == BackendType::DirectVulkan &&
|
|
MG_Backend::gBackendFunctionsTable.GL.GetTextureImage != nullptr) {
|
|
MG_Backend::gBackendFunctionsTable.GL.GetTextureImage(textureObject, uploadTarget, level, format, type,
|
|
bufSize, pixels);
|
|
return;
|
|
}
|
|
CopyTextureImageToClientOrPBO_State(textureObject, uploadTarget, level, format, type, bufSize, pixels,
|
|
__func__);
|
|
}
|
|
|
|
void GetTextureSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (!textureObject) return;
|
|
if (level < 0 || xoffset < 0 || yoffset < 0 || zoffset < 0 || width < 0 || height < 0 || depth < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture sub-image range is invalid."));
|
|
return;
|
|
}
|
|
if (textureObject->GetStorageType() != TextureStorageType::Mipmap) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture storage is not mipmap-backed."));
|
|
return;
|
|
}
|
|
|
|
const auto uploadTarget = GetPrimaryUploadTarget(textureObject);
|
|
auto* textureMipmapObject = static_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
if (static_cast<Uint>(level) >= textureMipmapObject->GetMipmapLevelCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level is out of range."));
|
|
return;
|
|
}
|
|
|
|
const auto texelSize = textureMipmapObject->GetMipmapTexelSize(uploadTarget, static_cast<Uint>(level));
|
|
const Bool isFullLevelRead = xoffset == 0 && yoffset == 0 && zoffset == 0 &&
|
|
width == texelSize.x() && height == texelSize.y() &&
|
|
depth == texelSize.z();
|
|
if (!isFullLevelRead) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Partial texture sub-image readback is not implemented yet."));
|
|
return;
|
|
}
|
|
|
|
GetTextureImage(texture, level, format, type, bufSize, pixels);
|
|
}
|
|
|
|
void GetTextureParameteriv(GLuint texture, GLenum pname, GLint* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameteriv_State(target, pname, params); });
|
|
}
|
|
|
|
void GetTextureParameterfv(GLuint texture, GLenum pname, GLfloat* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterfv_State(target, pname, params); });
|
|
}
|
|
|
|
void GetTextureParameterIiv(GLuint texture, GLenum pname, GLint* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterIiv_State(target, pname, params); });
|
|
}
|
|
|
|
void GetTextureParameterIuiv(GLuint texture, GLenum pname, GLuint* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) { GetTexParameterIuiv_State(target, pname, params); });
|
|
}
|
|
|
|
void GetTextureLevelParameteriv(GLuint texture, GLint level, GLenum pname, GLint* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
GetTexLevelParameteriv_State(target, level, pname, params);
|
|
});
|
|
}
|
|
|
|
void GetTextureLevelParameterfv(GLuint texture, GLint level, GLenum pname, GLfloat* params) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
GetTexLevelParameterfv_State(target, level, pname, params);
|
|
});
|
|
}
|
|
|
|
void BindImageTexture(GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access,
|
|
GLenum format) {
|
|
if (unit >= GetAdvertisedImageUnitCount()) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Image texture unit is out of range."));
|
|
return;
|
|
}
|
|
if (level < 0) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture level must be non-negative."));
|
|
return;
|
|
}
|
|
if (layer < 0 && layered == GL_FALSE) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture layer must be non-negative."));
|
|
return;
|
|
}
|
|
if (access != GL_READ_ONLY && access != GL_WRITE_ONLY && access != GL_READ_WRITE) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Invalid image texture access."));
|
|
return;
|
|
}
|
|
if (!IsValidImageTextureFormat(format)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Invalid image texture format."));
|
|
return;
|
|
}
|
|
|
|
SharedPtr<MG_State::GLState::ITextureObject> textureObject;
|
|
if (texture != 0) {
|
|
if (!MG_State::pGLContext->ValidateTextureObject(texture)) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Texture name is not a texture object."));
|
|
return;
|
|
}
|
|
textureObject = MG_State::pGLContext->GetTextureObject(texture);
|
|
}
|
|
|
|
auto bindImageTexture = MG_Backend::gBackendFunctionsTable.GL.BindImageTexture;
|
|
if (!bindImageTexture) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"Backend does not support image texture binding."));
|
|
return;
|
|
}
|
|
|
|
MG_State::pGLContext->GetImageTextureBinding(static_cast<Int>(unit))
|
|
.Bind(textureObject, level, layered, layer, access, format);
|
|
MG_State::pGLContext->NoteTextureUnitTouched(static_cast<Int>(unit));
|
|
bindImageTexture(unit, texture, level, layered, layer, access, format);
|
|
}
|
|
|
|
void GenerateMipmap(GLenum target) {
|
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
if (!TextureImpl::ValidateTextureTarget(textureTarget)) {
|
|
return;
|
|
}
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
auto& textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
|
if (!textureObject) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidOperation,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "GenerateMipmap requires a bound texture."));
|
|
return;
|
|
}
|
|
|
|
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage.");
|
|
EnsureGeneratedMipmapStorageAllocated(*mipmapTexture);
|
|
GenerateMipmap_Backend(target);
|
|
}
|
|
|
|
void GenerateTextureMipmap(GLuint texture) {
|
|
auto textureObject = GetTextureObjectByName(texture, __func__);
|
|
if (textureObject) {
|
|
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateTextureMipmap requires mipmap texture storage.");
|
|
}
|
|
WithTemporarilyBoundNamedTexture(textureObject, [&](GLenum target) {
|
|
if (textureObject) {
|
|
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(textureObject.get());
|
|
EnsureGeneratedMipmapStorageAllocated(*mipmapTexture);
|
|
}
|
|
GenerateMipmap_Backend(target);
|
|
});
|
|
}
|
|
|
|
void GetTexImage(GLenum target, GLint level, GLenum format, GLenum type, GLvoid* pixels) {
|
|
if (!GetTexImage_State(target, level, format, type, pixels)) return;
|
|
if (MG_Backend::gBackendFunctionsTable.GL.GetTexImage != nullptr) {
|
|
GetTexImage_Backend(target, level, format, type, pixels);
|
|
return;
|
|
}
|
|
TextureUploadTarget textureUploadTarget = MG_Util::ConvertGLEnumToTextureUploadTarget(target);
|
|
TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
auto& activeUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
|
const auto& textureObject = activeUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
|
CopyTextureImageToClientOrPBO_State(textureObject, textureUploadTarget, level, format, type, -1, pixels,
|
|
__func__);
|
|
}
|
|
|
|
void GetInternalformativ(GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) {
|
|
if (!params || bufSize <= 0) return;
|
|
|
|
const TextureTarget textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
|
const Bool isRenderbufferTarget = target == GL_RENDERBUFFER;
|
|
if (!isRenderbufferTarget && !TextureImpl::ValidateTextureTarget(textureTarget)) return;
|
|
|
|
TextureInternalFormat textureInternalFormat = MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
|
textureInternalFormat = MG_Util::ConvertInternalFormatToSized(textureInternalFormat, TextureInputFormat::RGBA,
|
|
TexturePixelDataType::UnsignedByte);
|
|
if (!TextureImpl::ValidateTextureInternalFormat(textureInternalFormat)) return;
|
|
|
|
GLenum preferredInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(textureInternalFormat);
|
|
GLenum imageFormat = GL_RGBA;
|
|
GLenum imageType = GL_UNSIGNED_BYTE;
|
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(preferredInternalFormat, PixelFormatNormalizeOptionBit::None,
|
|
&preferredInternalFormat, &imageFormat, &imageType);
|
|
|
|
auto writeValues = [&](std::initializer_list<GLint> values) {
|
|
GLsizei index = 0;
|
|
for (GLint value : values) {
|
|
if (index >= bufSize) break;
|
|
params[index++] = value;
|
|
}
|
|
while (index < bufSize) {
|
|
params[index++] = 0;
|
|
}
|
|
};
|
|
|
|
const Bool isDepthFormat = MG_Util::IsDepthFormatInternalFormat(textureInternalFormat);
|
|
const Bool isStencilFormat = MG_Util::IsStencilFormatInternalFormat(textureInternalFormat);
|
|
const ComponentSizes componentSizes = MG_Util::GetComponentSizesForInternalFormat(textureInternalFormat);
|
|
const Bool isIntegerFormat = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
|
|
imageFormat == GL_RGB_INTEGER || imageFormat == GL_RGBA_INTEGER;
|
|
|
|
SizeT targetIndex = isRenderbufferTarget ? MG_Backend::GetRenderbufferFormatCapabilityTargetIndex()
|
|
: MG_Backend::GetFormatCapabilityTargetIndex(textureTarget);
|
|
if (targetIndex >= MG_Backend::kFormatCapabilityTargetCount) return;
|
|
const SizeT formatIndex = static_cast<SizeT>(textureInternalFormat);
|
|
|
|
MG_Backend::FormatCapabilityFlags fullCaps{};
|
|
MG_Backend::FormatCapabilityFlags caveatCaps{};
|
|
const Vector<Int>* sampleCounts = nullptr;
|
|
if (MG_Backend::pActiveBackendObject) {
|
|
const auto& cache = MG_Backend::pActiveBackendObject->GetFormatCapabilities();
|
|
fullCaps = cache.FullCaps[targetIndex][formatIndex];
|
|
caveatCaps = cache.CaveatCaps[targetIndex][formatIndex];
|
|
sampleCounts = &cache.SampleCounts[targetIndex][formatIndex];
|
|
}
|
|
|
|
auto hasFull = [&](MG_Backend::FormatCapability capability) {
|
|
return MG_Backend::HasFormatCapability(fullCaps, capability);
|
|
};
|
|
auto hasCaveat = [&](MG_Backend::FormatCapability capability) {
|
|
return MG_Backend::HasFormatCapability(caveatCaps, capability);
|
|
};
|
|
auto supportFor = [&](MG_Backend::FormatCapability capability) -> GLint {
|
|
if (hasFull(capability)) return GL_FULL_SUPPORT;
|
|
if (hasCaveat(capability)) return GL_CAVEAT_SUPPORT;
|
|
return GL_NONE;
|
|
};
|
|
auto supportForWithFallback = [&](MG_Backend::FormatCapability primary,
|
|
MG_Backend::FormatCapability fallback) -> GLint {
|
|
if (hasFull(primary)) return GL_FULL_SUPPORT;
|
|
if (hasCaveat(primary) || hasFull(fallback) || hasCaveat(fallback)) return GL_CAVEAT_SUPPORT;
|
|
return GL_NONE;
|
|
};
|
|
switch (pname) {
|
|
case GL_INTERNALFORMAT_SUPPORTED:
|
|
writeValues({(hasFull(MG_Backend::FormatCapability::Creatable) ||
|
|
hasCaveat(MG_Backend::FormatCapability::Creatable))
|
|
? GL_TRUE
|
|
: GL_FALSE});
|
|
return;
|
|
case GL_INTERNALFORMAT_PREFERRED:
|
|
writeValues({static_cast<GLint>(preferredInternalFormat)});
|
|
return;
|
|
case GL_INTERNALFORMAT_RED_SIZE:
|
|
writeValues({componentSizes.Red});
|
|
return;
|
|
case GL_INTERNALFORMAT_GREEN_SIZE:
|
|
writeValues({componentSizes.Green});
|
|
return;
|
|
case GL_INTERNALFORMAT_BLUE_SIZE:
|
|
writeValues({componentSizes.Blue});
|
|
return;
|
|
case GL_INTERNALFORMAT_ALPHA_SIZE:
|
|
writeValues({componentSizes.Alpha});
|
|
return;
|
|
case GL_INTERNALFORMAT_DEPTH_SIZE:
|
|
writeValues({componentSizes.Depth});
|
|
return;
|
|
case GL_INTERNALFORMAT_STENCIL_SIZE:
|
|
writeValues({componentSizes.Stencil});
|
|
return;
|
|
case GL_INTERNALFORMAT_SHARED_SIZE:
|
|
writeValues({textureInternalFormat == TextureInternalFormat::RGB9E5 ? 5 : 0});
|
|
return;
|
|
case GL_INTERNALFORMAT_RED_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Red, false, false)});
|
|
return;
|
|
case GL_INTERNALFORMAT_GREEN_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Green, false, false)});
|
|
return;
|
|
case GL_INTERNALFORMAT_BLUE_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Blue, false, false)});
|
|
return;
|
|
case GL_INTERNALFORMAT_ALPHA_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Alpha, false, false)});
|
|
return;
|
|
case GL_INTERNALFORMAT_DEPTH_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Depth, true, false)});
|
|
return;
|
|
case GL_INTERNALFORMAT_STENCIL_TYPE:
|
|
writeValues({GetTextureComponentType(textureInternalFormat, componentSizes.Stencil, false, true)});
|
|
return;
|
|
case GL_TEXTURE_IMAGE_FORMAT:
|
|
writeValues({static_cast<GLint>(imageFormat)});
|
|
return;
|
|
case GL_TEXTURE_IMAGE_TYPE:
|
|
writeValues({static_cast<GLint>(imageType)});
|
|
return;
|
|
case GL_TEXTURE_COMPRESSED:
|
|
case GL_TEXTURE_COMPRESSED_BLOCK_WIDTH:
|
|
case GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT:
|
|
case GL_TEXTURE_COMPRESSED_BLOCK_SIZE:
|
|
writeValues({0});
|
|
return;
|
|
case GL_COLOR_COMPONENTS:
|
|
writeValues({(!isDepthFormat && !isStencilFormat) ? GL_TRUE : GL_FALSE});
|
|
return;
|
|
case GL_DEPTH_COMPONENTS:
|
|
writeValues({isDepthFormat ? GL_TRUE : GL_FALSE});
|
|
return;
|
|
case GL_STENCIL_COMPONENTS:
|
|
writeValues({isStencilFormat ? GL_TRUE : GL_FALSE});
|
|
return;
|
|
case GL_FRAMEBUFFER_RENDERABLE:
|
|
writeValues({supportFor(MG_Backend::FormatCapability::FramebufferRenderable)});
|
|
return;
|
|
case GL_FRAMEBUFFER_RENDERABLE_LAYERED:
|
|
writeValues({supportFor(MG_Backend::FormatCapability::FramebufferLayered)});
|
|
return;
|
|
case GL_FILTER:
|
|
writeValues({supportForWithFallback(MG_Backend::FormatCapability::LinearFilter,
|
|
MG_Backend::FormatCapability::Sampled)});
|
|
return;
|
|
case GL_MIPMAP:
|
|
writeValues({supportForWithFallback(MG_Backend::FormatCapability::GenerateMipmap,
|
|
MG_Backend::FormatCapability::Sampled)});
|
|
return;
|
|
case GL_TEXTURE_GATHER:
|
|
case GL_TEXTURE_GATHER_SHADOW:
|
|
writeValues({supportFor(MG_Backend::FormatCapability::TextureGather)});
|
|
return;
|
|
case GL_TEXTURE_SHADOW:
|
|
writeValues({supportFor(MG_Backend::FormatCapability::TextureShadow)});
|
|
return;
|
|
case GL_NUM_SAMPLE_COUNTS:
|
|
writeValues({sampleCounts != nullptr ? static_cast<GLint>(sampleCounts->size()) : 0});
|
|
return;
|
|
case GL_SAMPLES:
|
|
if (sampleCounts != nullptr) {
|
|
GLsizei index = 0;
|
|
for (Int sampleCount : *sampleCounts) {
|
|
if (index >= bufSize) break;
|
|
params[index++] = sampleCount;
|
|
}
|
|
while (index < bufSize) {
|
|
params[index++] = 0;
|
|
}
|
|
return;
|
|
}
|
|
writeValues({});
|
|
return;
|
|
default:
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
|
"pname is not supported by GetInternalformativ."));
|
|
return;
|
|
}
|
|
}
|
|
|
|
void GetMultisamplefv(GLenum pname, GLuint index, GLfloat* val) {
|
|
if (val == nullptr) return;
|
|
if (pname != GL_SAMPLE_POSITION) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidEnum,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Only GL_SAMPLE_POSITION is supported."));
|
|
return;
|
|
}
|
|
|
|
const Int maxSamples = MG_Backend::pActiveBackendObject != nullptr
|
|
? std::max(MG_Backend::pActiveBackendObject->GetDynamicParameters().MaxSamples, 1)
|
|
: 1;
|
|
if (static_cast<Int>(index) >= maxSamples) {
|
|
MG_State::pGLContext->RecordError(
|
|
ErrorCode::InvalidValue,
|
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "Sample index is out of range."));
|
|
return;
|
|
}
|
|
|
|
// Keep sample positions deterministic even before the backend exposes vendor-specific patterns.
|
|
val[0] = 0.5f;
|
|
val[1] = 0.5f;
|
|
}
|
|
|
|
void TexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels) {
|
|
TexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
|
|
}
|
|
|
|
void TexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
|
|
GLenum format, GLenum type, const void* pixels) {
|
|
TexSubImage2D_State(target, level, xoffset, yoffset, width, height, format, type, pixels);
|
|
}
|
|
|
|
void TexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type,
|
|
const GLvoid* pixels) {
|
|
TexSubImage1D_State(target, level, xoffset, width, format, type, pixels);
|
|
}
|
|
|
|
void TexParameterf(GLenum target, GLenum pname, GLfloat param) {
|
|
TexParameterf_State(target, pname, param);
|
|
}
|
|
|
|
void TexParameteri(GLenum target, GLenum pname, GLint param) {
|
|
TexParameteri_State(target, pname, param);
|
|
}
|
|
|
|
void TexParameterfv(GLenum target, GLenum pname, const GLfloat* params) {
|
|
TexParameterfv_State(target, pname, params);
|
|
}
|
|
|
|
void TexParameteriv(GLenum target, GLenum pname, const GLint* params) {
|
|
TexParameteriv_State(target, pname, params);
|
|
}
|
|
|
|
void TexParameterIiv(GLenum target, GLenum pname, const GLint* params) {
|
|
TexParameterIiv_State(target, pname, params);
|
|
}
|
|
|
|
void TexParameterIuiv(GLenum target, GLenum pname, const GLuint* params) {
|
|
TexParameterIuiv_State(target, pname, params);
|
|
}
|
|
|
|
void TexImage3DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLboolean fixedsamplelocations) {
|
|
TexImage3DMultisample_State(target, samples, internalformat, width, height, depth, fixedsamplelocations);
|
|
}
|
|
|
|
void TexImage2DMultisample(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLboolean fixedsamplelocations) {
|
|
TexImage2DMultisample_State(target, samples, internalformat, width, height, fixedsamplelocations);
|
|
}
|
|
|
|
void TexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth,
|
|
GLint border, GLenum format, GLenum type, const void* pixels) {
|
|
TexImage3D_State(target, level, internalformat, width, height, depth, border, format, type, pixels);
|
|
}
|
|
|
|
void TexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border,
|
|
GLenum format, GLenum type, const void* pixels) {
|
|
TexImage2D_State(target, level, internalformat, width, height, border, format, type, pixels);
|
|
}
|
|
|
|
void TexImage1D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format,
|
|
GLenum type, const GLvoid* pixels) {
|
|
TexImage1D_State(target, level, internalFormat, width, border, format, type, pixels);
|
|
}
|
|
|
|
void TexBuffer(GLenum target, GLenum internalformat, GLuint buffer) {
|
|
TexBuffer_State(target, internalformat, buffer);
|
|
}
|
|
|
|
GLboolean IsTexture(GLuint texture) {
|
|
return IsTexture_State(texture);
|
|
}
|
|
|
|
void GetTexParameterIuiv(GLenum target, GLenum pname, GLuint* params) {
|
|
GetTexParameterIuiv_State(target, pname, params);
|
|
}
|
|
|
|
void GetTexParameterIiv(GLenum target, GLenum pname, GLint* params) {
|
|
GetTexParameterIiv_State(target, pname, params);
|
|
}
|
|
|
|
void GetTexParameteriv(GLenum target, GLenum pname, GLint* params) {
|
|
GetTexParameteriv_State(target, pname, params);
|
|
}
|
|
|
|
void GetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) {
|
|
GetTexParameterfv_State(target, pname, params);
|
|
}
|
|
|
|
void GetTexLevelParameteriv(GLenum target, GLint level, GLenum pname, GLint* params) {
|
|
GetTexLevelParameteriv_State(target, level, pname, params);
|
|
}
|
|
|
|
void GetTexLevelParameterfv(GLenum target, GLint level, GLenum pname, GLfloat* params) {
|
|
GetTexLevelParameterfv_State(target, level, pname, params);
|
|
}
|
|
|
|
void GetCompressedTexImage(GLenum target, GLint level, void* img) {
|
|
GetCompressedTexImage_State(target, level, img);
|
|
}
|
|
|
|
void GenTextures(GLsizei n, GLuint* textures) {
|
|
GenTextures_State(n, textures);
|
|
}
|
|
|
|
void DeleteTextures(GLsizei n, const GLuint* textures) {
|
|
DeleteTextures_State(n, textures);
|
|
}
|
|
|
|
void CopyTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y,
|
|
GLsizei width, GLsizei height) {
|
|
CopyTexSubImage3D_State(target, level, xoffset, yoffset, zoffset, x, y, width, height);
|
|
}
|
|
|
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
|
GLsizei height) {
|
|
CopyTexSubImage2D_Backend(target, level, xoffset, yoffset, x, y, width, height);
|
|
}
|
|
|
|
void CopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) {
|
|
CopyTexSubImage1D_State(target, level, xoffset, x, y, width);
|
|
}
|
|
|
|
void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
|
GLsizei height, GLint border) {
|
|
if (!CopyTexImage2D_State(target, level, internalformat, x, y, width, height, border)) return;
|
|
CopyTexImage2D_Backend(target, level, internalformat, x, y, width, height, border);
|
|
}
|
|
|
|
void CopyTexImage1D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width,
|
|
GLint border) {
|
|
CopyTexImage1D_State(target, level, internalformat, x, y, width, border);
|
|
}
|
|
|
|
void CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ,
|
|
GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ,
|
|
GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth) {
|
|
auto srcTexture = GetTextureObjectByName(srcName, __func__);
|
|
auto dstTexture = GetTextureObjectByName(dstName, __func__);
|
|
if (!ValidateCopyImageSubData_State(srcTexture, srcTarget, srcLevel, dstTexture, dstTarget, dstLevel,
|
|
srcWidth, srcHeight, srcDepth)) {
|
|
return;
|
|
}
|
|
CopyImageSubData_Backend(srcTexture, srcTarget, srcLevel, srcX, srcY, srcZ, dstTexture, dstTarget, dstLevel,
|
|
dstX, dstY, dstZ, srcWidth, srcHeight, srcDepth);
|
|
}
|
|
|
|
void CompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
|
GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) {
|
|
CompressedTexSubImage3D_State(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize,
|
|
data);
|
|
}
|
|
|
|
void CompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
|
|
GLsizei height, GLenum format, GLsizei imageSize, const void* data) {
|
|
CompressedTexSubImage2D_State(target, level, xoffset, yoffset, width, height, format, imageSize, data);
|
|
}
|
|
|
|
void CompressedTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format,
|
|
GLsizei imageSize, const void* data) {
|
|
CompressedTexSubImage1D_State(target, level, xoffset, width, format, imageSize, data);
|
|
}
|
|
|
|
void CompressedTexImage3D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLsizei depth, GLint border, GLsizei imageSize, const void* data) {
|
|
CompressedTexImage3D_State(target, level, internalformat, width, height, depth, border, imageSize, data);
|
|
}
|
|
|
|
void CompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height,
|
|
GLint border, GLsizei imageSize, const void* data) {
|
|
CompressedTexImage2D_State(target, level, internalformat, width, height, border, imageSize, data);
|
|
}
|
|
|
|
void CompressedTexImage1D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border,
|
|
GLsizei imageSize, const void* data) {
|
|
CompressedTexImage1D_State(target, level, internalformat, width, border, imageSize, data);
|
|
}
|
|
|
|
void BindTexture(GLenum target, GLuint texture) {
|
|
BindTexture_State(target, texture);
|
|
}
|
|
|
|
void ActiveTexture(GLenum texture) {
|
|
ActiveTexture_State(texture);
|
|
}
|
|
|
|
} // namespace MobileGL::MG_Impl::GLImpl
|