[Fix] (MG_Impl/GLImpl): report backend format capabilities

This commit is contained in:
2026-06-25 23:25:31 +08:00
parent 9137396eae
commit 496fa50a23
6 changed files with 994 additions and 21 deletions
+38
View File
@@ -19,6 +19,36 @@ namespace MobileGL::MG_Backend {
}
} // namespace
void FormatCapabilityCache::Clear() {
for (auto& row : FullCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : CaveatCaps) {
row.fill(FormatCapabilityFlags{});
}
for (auto& row : SampleCounts) {
for (auto& counts : row) {
counts.clear();
}
}
}
Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability) {
return static_cast<Bool>(caps & capability);
}
SizeT GetFormatCapabilityTargetIndex(TextureTarget target) {
if (target == TextureTarget::Unknown || static_cast<Int>(target) < 0 ||
static_cast<SizeT>(target) >= kFormatCapabilityTextureTargetCount) {
return kFormatCapabilityTargetCount;
}
return static_cast<SizeT>(target);
}
SizeT GetRenderbufferFormatCapabilityTargetIndex() {
return kFormatCapabilityRenderbufferTargetIndex;
}
Bool BackendObject::InitializeEGLDisplay(EGLDisplay dpy, EGLint* major, EGLint* minor) {
const std::lock_guard<std::recursive_mutex> lock(m_eglStateMutex);
if (dpy == EGL_NO_DISPLAY) {
@@ -177,6 +207,14 @@ namespace MobileGL::MG_Backend {
m_windowHandle = handle;
}
const FormatCapabilityCache& BackendObject::GetFormatCapabilities() const {
return m_formatCapabilities;
}
FormatCapabilityCache& BackendObject::MutableFormatCapabilities() {
return m_formatCapabilities;
}
Bool BackendObject::InitPbufferSurface(EGLint width, EGLint height) {
(void)width;
(void)height;
+50
View File
@@ -24,6 +24,53 @@ namespace MobileGL {
};
namespace MG_Backend {
enum class FormatCapability : Uint64 {
Creatable = 1ull << 0,
Sampled = 1ull << 1,
LinearFilter = 1ull << 2,
GenerateMipmap = 1ull << 3,
TextureGather = 1ull << 4,
TextureShadow = 1ull << 5,
FramebufferRenderable = 1ull << 6,
FramebufferLayered = 1ull << 7,
MultisampleTexture = 1ull << 8,
MultisampleRenderbuffer = 1ull << 9,
ColorAttachment = 1ull << 10,
DepthAttachment = 1ull << 11,
StencilAttachment = 1ull << 12,
TextureBuffer = 1ull << 13
};
using FormatCapabilityFlags = Flags<FormatCapability>;
inline constexpr SizeT kFormatCapabilityTextureTargetCount =
static_cast<SizeT>(TextureTarget::TextureTargetCount);
inline constexpr SizeT kFormatCapabilityRenderbufferTargetIndex = kFormatCapabilityTextureTargetCount;
inline constexpr SizeT kFormatCapabilityTargetCount = kFormatCapabilityTextureTargetCount + 1;
inline constexpr SizeT kFormatCapabilityFormatCount =
static_cast<SizeT>(TextureInternalFormat::TextureInternalFormatCount);
using FormatCapabilityTable =
Array<Array<FormatCapabilityFlags, kFormatCapabilityFormatCount>, kFormatCapabilityTargetCount>;
using FormatSampleCountTable =
Array<Array<Vector<Int>, kFormatCapabilityFormatCount>, kFormatCapabilityTargetCount>;
struct FormatCapabilityCache {
FormatCapabilityTable FullCaps{};
FormatCapabilityTable CaveatCaps{};
FormatSampleCountTable SampleCounts{};
void Clear();
};
Bool HasFormatCapability(FormatCapabilityFlags caps, FormatCapability capability);
SizeT GetFormatCapabilityTargetIndex(TextureTarget target);
SizeT GetRenderbufferFormatCapabilityTargetIndex();
struct GLFunctionsTable {
void (*DrawArrays)(GLenum mode, GLint first, GLsizei count);
void (*DrawElements)(GLenum mode, GLsizei count, GLenum type, const void* indices);
@@ -206,6 +253,7 @@ namespace MobileGL {
virtual String GetBackendAPIVersionString() const = 0;
virtual const GlobalBackendFunctionsTable& GetBackendFunctions() const = 0;
virtual const DynamicBackendParameters& GetDynamicParameters() const = 0;
const FormatCapabilityCache& GetFormatCapabilities() const;
virtual BackendType GetBackendType() const = 0;
protected:
@@ -217,8 +265,10 @@ namespace MobileGL {
void ResetEGLRuntimeState();
virtual Bool InitPbufferSurface(EGLint width, EGLint height);
FormatCapabilityCache& MutableFormatCapabilities();
mutable std::recursive_mutex m_eglStateMutex;
FormatCapabilityCache m_formatCapabilities;
WindowHandle m_windowHandle;
EGLDisplay m_eglDisplay = EGL_NO_DISPLAY;
Bool m_eglDisplayInitialized = false;
@@ -9,7 +9,11 @@
#include "BackendObject_DirectGLES.h"
#include "MG_Backend/BackendObject.h"
#include <MG_Backend/DirectGLES/DirectGLES.h>
#include <MG_Backend/DirectGLES/Utils.h>
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
#include <MG_Util/Classifiers/TextureEnumClassifier.h>
#include <MG_Util/Converters/MGToGL/TextureEnumConverter.h>
#include <MG_Util/Texture/TextureFormatProcessor.h>
#include <format>
namespace MobileGL::MG_Backend::DirectGLES {
@@ -17,6 +21,379 @@ namespace MobileGL::MG_Backend::DirectGLES {
Bool IsReleaseCurrentRequest(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
return dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT;
}
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
if (!gl.glGetError) return;
while (gl.glGetError() != GL_NO_ERROR) {
}
}
Bool CheckNoGLError(const MG_External::GLESFunctionsTable& gl) {
return !gl.glGetError || gl.glGetError() == GL_NO_ERROR;
}
GLenum GetTextureBindingQuery(TextureTarget target) {
switch (target) {
case TextureTarget::Texture2D:
return GL_TEXTURE_BINDING_2D;
case TextureTarget::Texture3D:
return GL_TEXTURE_BINDING_3D;
case TextureTarget::TextureCubeMap:
return GL_TEXTURE_BINDING_CUBE_MAP;
case TextureTarget::Texture2DArray:
return GL_TEXTURE_BINDING_2D_ARRAY;
case TextureTarget::TextureCubeMapArray:
return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
case TextureTarget::Texture2DMultisample:
return GL_TEXTURE_BINDING_2D_MULTISAMPLE;
case TextureTarget::Texture2DMultisampleArray:
return GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY;
default:
return GL_UNKNOWN_MGL;
}
}
Bool IsGLESProbeTextureTarget(TextureTarget target) {
switch (target) {
case TextureTarget::Texture2D:
case TextureTarget::Texture3D:
case TextureTarget::TextureCubeMap:
case TextureTarget::Texture2DArray:
case TextureTarget::TextureCubeMapArray:
case TextureTarget::Texture2DMultisample:
case TextureTarget::Texture2DMultisampleArray:
return true;
default:
return false;
}
}
Bool IsGLESProbeMultisampleTarget(TextureTarget target) {
return target == TextureTarget::Texture2DMultisample ||
target == TextureTarget::Texture2DMultisampleArray;
}
GLenum GetFramebufferAttachment(TextureInternalFormat format) {
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(format);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(format);
if (isDepth && isStencil) return GL_DEPTH_STENCIL_ATTACHMENT;
if (isDepth) return GL_DEPTH_ATTACHMENT;
if (isStencil) return GL_STENCIL_ATTACHMENT;
return GL_COLOR_ATTACHMENT0;
}
FormatCapabilityFlags GetAttachmentCaps(TextureInternalFormat format) {
FormatCapabilityFlags caps = FormatCapability::FramebufferRenderable;
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(format);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(format);
if (!isDepth && !isStencil) {
caps |= FormatCapability::ColorAttachment;
}
if (isDepth) {
caps |= FormatCapability::DepthAttachment;
}
if (isStencil) {
caps |= FormatCapability::StencilAttachment;
}
return caps;
}
Bool IsDepthOnlyFormat(TextureInternalFormat format) {
return MG_Util::IsDepthFormatInternalFormat(format) && !MG_Util::IsStencilFormatInternalFormat(format);
}
Bool IsFilterableFormat(TextureInternalFormat format) {
const GLenum glFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(format);
GLenum normalizedInternalFormat = glFormat;
GLenum imageFormat = GL_RGBA;
GLenum imageType = GL_UNSIGNED_BYTE;
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
return imageFormat != GL_RED_INTEGER && imageFormat != GL_RG_INTEGER && imageFormat != GL_RGB_INTEGER &&
imageFormat != GL_RGBA_INTEGER && !MG_Util::IsDepthFormatInternalFormat(format) &&
!MG_Util::IsStencilFormatInternalFormat(format);
}
FormatCapabilityFlags GetTextureFeatureCaps(TextureInternalFormat format, TextureTarget target) {
FormatCapabilityFlags caps = FormatCapability::Creatable | FormatCapability::Sampled;
if (IsFilterableFormat(format)) {
caps |= FormatCapability::LinearFilter;
}
if (!IsGLESProbeMultisampleTarget(target) && !MG_Util::IsStencilFormatInternalFormat(format)) {
caps |= FormatCapability::GenerateMipmap;
}
if (IsDepthOnlyFormat(format)) {
caps |= FormatCapability::TextureShadow;
}
if (IsGLESProbeMultisampleTarget(target)) {
caps |= FormatCapability::MultisampleTexture;
}
return caps;
}
FormatCapabilityFlags GetRenderbufferFeatureCaps(TextureInternalFormat format) {
return FormatCapabilityFlags(FormatCapability::Creatable) | GetAttachmentCaps(format) |
FormatCapability::MultisampleRenderbuffer;
}
Bool ProbeFramebufferCompletenessForTexture(const MG_External::GLESFunctionsTable& gl,
TextureTarget target,
GLuint texture,
TextureInternalFormat format) {
GLuint framebuffer = 0;
GLint prevFramebuffer = 0;
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glCheckFramebufferStatus ||
!gl.glDeleteFramebuffers) {
return false;
}
gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
gl.glGenFramebuffers(1, &framebuffer);
gl.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
const GLenum attachment = GetFramebufferAttachment(format);
switch (target) {
case TextureTarget::Texture2D:
gl.glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, texture, 0);
break;
case TextureTarget::TextureCubeMap:
gl.glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_CUBE_MAP_POSITIVE_X, texture, 0);
break;
case TextureTarget::Texture3D:
case TextureTarget::Texture2DArray:
case TextureTarget::TextureCubeMapArray:
case TextureTarget::Texture2DMultisampleArray:
if (!gl.glFramebufferTextureLayer) {
gl.glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prevFramebuffer));
gl.glDeleteFramebuffers(1, &framebuffer);
return false;
}
gl.glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texture, 0, 0);
break;
case TextureTarget::Texture2DMultisample:
gl.glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D_MULTISAMPLE, texture, 0);
break;
default:
gl.glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prevFramebuffer));
gl.glDeleteFramebuffers(1, &framebuffer);
return false;
}
const Bool complete = gl.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
gl.glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prevFramebuffer));
gl.glDeleteFramebuffers(1, &framebuffer);
return complete;
}
Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl,
GLuint renderbuffer,
TextureInternalFormat format) {
GLuint framebuffer = 0;
GLint prevFramebuffer = 0;
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glFramebufferRenderbuffer ||
!gl.glCheckFramebufferStatus || !gl.glDeleteFramebuffers) {
return false;
}
gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer);
gl.glGenFramebuffers(1, &framebuffer);
gl.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GetFramebufferAttachment(format), GL_RENDERBUFFER,
renderbuffer);
const Bool complete = gl.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
gl.glBindFramebuffer(GL_FRAMEBUFFER, static_cast<GLuint>(prevFramebuffer));
gl.glDeleteFramebuffers(1, &framebuffer);
return complete;
}
Bool ProbeTexture(const MG_External::GLESFunctionsTable& gl, TextureTarget target, GLenum internalFormat,
GLenum imageFormat, GLenum imageType, TextureInternalFormat logicalFormat,
Bool* outRenderable) {
if (!IsGLESProbeTextureTarget(target) || !gl.glGenTextures || !gl.glBindTexture || !gl.glDeleteTextures) {
return false;
}
const GLenum glTarget = MG_Util::ConvertTextureTargetToGLEnum(target);
const GLenum bindingQuery = GetTextureBindingQuery(target);
if (glTarget == GL_UNKNOWN_MGL || bindingQuery == GL_UNKNOWN_MGL) {
return false;
}
GLint previousBinding = 0;
gl.glGetIntegerv(bindingQuery, &previousBinding);
GLuint texture = 0;
gl.glGenTextures(1, &texture);
gl.glBindTexture(glTarget, texture);
ClearGLErrors(gl);
const Bool isMultisample = IsGLESProbeMultisampleTarget(target);
if (isMultisample) {
if (target == TextureTarget::Texture2DMultisample && gl.glTexStorage2DMultisample) {
gl.glTexStorage2DMultisample(glTarget, 1, internalFormat, 1, 1, GL_TRUE);
} else if (target == TextureTarget::Texture2DMultisampleArray && gl.glTexStorage3DMultisample) {
gl.glTexStorage3DMultisample(glTarget, 1, internalFormat, 1, 1, 1, GL_TRUE);
} else {
gl.glBindTexture(glTarget, static_cast<GLuint>(previousBinding));
gl.glDeleteTextures(1, &texture);
return false;
}
} else {
if (gl.glTexParameteri) {
gl.glTexParameteri(glTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl.glTexParameteri(glTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
switch (target) {
case TextureTarget::Texture2D:
gl.glTexImage2D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 0, imageFormat, imageType,
nullptr);
break;
case TextureTarget::TextureCubeMap:
for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; ++face) {
gl.glTexImage2D(face, 0, static_cast<GLint>(internalFormat), 2, 2, 0, imageFormat, imageType,
nullptr);
}
break;
case TextureTarget::Texture3D:
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 2, 0, imageFormat,
imageType, nullptr);
break;
case TextureTarget::Texture2DArray:
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 1, 0, imageFormat,
imageType, nullptr);
break;
case TextureTarget::TextureCubeMapArray:
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 6, 0, imageFormat,
imageType, nullptr);
break;
default:
break;
}
}
const Bool created = CheckNoGLError(gl);
Bool renderable = false;
if (created) {
renderable = ProbeFramebufferCompletenessForTexture(gl, target, texture, logicalFormat);
}
if (outRenderable) {
*outRenderable = renderable;
}
gl.glBindTexture(glTarget, static_cast<GLuint>(previousBinding));
gl.glDeleteTextures(1, &texture);
ClearGLErrors(gl);
return created;
}
Bool ProbeRenderbuffer(const MG_External::GLESFunctionsTable& gl,
GLenum internalFormat,
TextureInternalFormat logicalFormat,
Bool multisample,
Int samples) {
if (!gl.glGenRenderbuffers || !gl.glBindRenderbuffer || !gl.glDeleteRenderbuffers) {
return false;
}
GLint prevRenderbuffer = 0;
gl.glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
GLuint renderbuffer = 0;
gl.glGenRenderbuffers(1, &renderbuffer);
gl.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
ClearGLErrors(gl);
if (multisample) {
if (!gl.glRenderbufferStorageMultisample) {
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
gl.glDeleteRenderbuffers(1, &renderbuffer);
return false;
}
gl.glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat, 1, 1);
} else {
gl.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
}
const Bool created = CheckNoGLError(gl);
const Bool complete = created && ProbeFramebufferCompletenessForRenderbuffer(gl, renderbuffer, logicalFormat);
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
gl.glDeleteRenderbuffers(1, &renderbuffer);
ClearGLErrors(gl);
return complete;
}
Vector<Int> ProbeRenderbufferSampleCounts(const MG_External::GLESFunctionsTable& gl,
GLenum internalFormat,
TextureInternalFormat logicalFormat,
Int maxSamples) {
Vector<Int> sampleCounts;
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
if (ProbeRenderbuffer(gl, internalFormat, logicalFormat, true, samples)) {
sampleCounts.push_back(samples);
}
}
sampleCounts.push_back(1);
return sampleCounts;
}
void ProbeGLESFormatCapabilities(const MG_External::GLESFunctionsTable& gl,
FormatCapabilityCache& cache,
const DynamicBackendParameters& dynamicParameters) {
cache.Clear();
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const auto logicalFormat = static_cast<TextureInternalFormat>(formatIndex);
GLenum requestedInternalFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(logicalFormat);
if (requestedInternalFormat == GL_UNKNOWN_MGL) {
continue;
}
GLenum backendInternalFormat = requestedInternalFormat;
GLenum imageFormat = GL_RGBA;
GLenum imageType = GL_UNSIGNED_BYTE;
TextureImpl::GenerateTextureFormatInfo(logicalFormat, &backendInternalFormat, &imageFormat,
&imageType);
const Bool isCaveat = backendInternalFormat != requestedInternalFormat;
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
const auto target = static_cast<TextureTarget>(targetIndex);
Bool renderable = false;
if (!ProbeTexture(gl, target, backendInternalFormat, imageFormat, imageType, logicalFormat,
&renderable)) {
continue;
}
FormatCapabilityFlags caps = GetTextureFeatureCaps(logicalFormat, target);
if (renderable) {
caps |= GetAttachmentCaps(logicalFormat);
if (target == TextureTarget::Texture3D || target == TextureTarget::Texture2DArray ||
target == TextureTarget::TextureCubeMapArray ||
target == TextureTarget::Texture2DMultisampleArray) {
caps |= FormatCapability::FramebufferLayered;
}
}
auto& table = isCaveat ? cache.CaveatCaps : cache.FullCaps;
table[targetIndex][formatIndex] |= caps;
if (IsGLESProbeMultisampleTarget(target)) {
cache.SampleCounts[targetIndex][formatIndex] = {1};
}
}
const SizeT renderbufferTargetIndex = GetRenderbufferFormatCapabilityTargetIndex();
if (ProbeRenderbuffer(gl, backendInternalFormat, logicalFormat, false, 1)) {
auto& table = isCaveat ? cache.CaveatCaps : cache.FullCaps;
table[renderbufferTargetIndex][formatIndex] |= GetRenderbufferFeatureCaps(logicalFormat);
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
const Bool isInteger = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
imageFormat == GL_RGB_INTEGER || imageFormat == GL_RGBA_INTEGER;
Int maxSamples = dynamicParameters.MaxColorTextureSamples;
if (isDepth || isStencil) {
maxSamples = dynamicParameters.MaxDepthTextureSamples;
} else if (isInteger) {
maxSamples = dynamicParameters.MaxIntegerSamples;
}
cache.SampleCounts[renderbufferTargetIndex][formatIndex] =
ProbeRenderbufferSampleCounts(gl, backendInternalFormat, logicalFormat, maxSamples);
}
}
}
} // namespace
BackendObject_DirectGLES::~BackendObject_DirectGLES() {
@@ -53,6 +430,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
DirectGLES::SetGLESCapabilities(m_GLESCapabilities);
UpdateDynamicBackendParameters();
ProbeGLESFormatCapabilities(m_GLESFunctions, MutableFormatCapabilities(), m_dynamicParameters);
return true;
}
@@ -11,12 +11,253 @@
#include "DirectVulkan.h"
#include "MG_State/GLState/FramebufferState/FramebufferObject.h"
#include "MG_State/GLState/TextureState/TextureState.h"
#include "MG_Util/Classifiers/TextureEnumClassifier.h"
#include "MG_Util/Converters/MGToGL/TextureEnumConverter.h"
#include "MG_Util/Converters/MGToVk/TextureEnumConverter.h"
#include "MG_Util/Texture/TextureFormatProcessor.h"
namespace MobileGL::MG_Backend::DirectVulkan {
namespace {
Bool IsReleaseCurrentRequest(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
return dpy == EGL_NO_DISPLAY && draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && ctx == EGL_NO_CONTEXT;
}
Bool IsFormatIndexValid(TextureInternalFormat format) {
return format != TextureInternalFormat::Unknown && static_cast<Int>(format) >= 0 &&
static_cast<SizeT>(format) < kFormatCapabilityFormatCount;
}
Bool IsLayeredTarget(TextureTarget target) {
return target == TextureTarget::Texture3D || target == TextureTarget::Texture1DArray ||
target == TextureTarget::Texture2DArray || target == TextureTarget::TextureCubeMap ||
target == TextureTarget::TextureCubeMapArray ||
target == TextureTarget::Texture2DMultisampleArray;
}
Bool IsMultisampleTarget(TextureTarget target) {
return target == TextureTarget::Texture2DMultisample ||
target == TextureTarget::Texture2DMultisampleArray;
}
Bool IsTextureBufferTarget(TextureTarget target) {
return target == TextureTarget::TextureBuffer;
}
Bool IsIntegerInternalFormat(TextureInternalFormat format) {
const GLenum glFormat = MG_Util::ConvertTextureInternalFormatToGLEnum(format);
GLenum normalizedInternalFormat = glFormat;
GLenum imageFormat = GL_RGBA;
GLenum imageType = GL_UNSIGNED_BYTE;
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
return imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || imageFormat == GL_RGB_INTEGER ||
imageFormat == GL_RGBA_INTEGER;
}
FormatCapabilityFlags GetAttachmentCaps(TextureInternalFormat format) {
FormatCapabilityFlags caps = FormatCapability::FramebufferRenderable;
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(format);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(format);
if (!isDepth && !isStencil) {
caps |= FormatCapability::ColorAttachment;
}
if (isDepth) {
caps |= FormatCapability::DepthAttachment;
}
if (isStencil) {
caps |= FormatCapability::StencilAttachment;
}
return caps;
}
FormatCapabilityFlags BuildVulkanCaps(TextureInternalFormat logicalFormat,
TextureTarget target,
VkFormatFeatureFlags features) {
FormatCapabilityFlags caps;
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
const Bool isInteger = IsIntegerInternalFormat(logicalFormat);
if (IsTextureBufferTarget(target)) {
if ((features & VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT) != 0) {
caps |= FormatCapability::Creatable;
caps |= FormatCapability::Sampled;
caps |= FormatCapability::TextureBuffer;
}
return caps;
}
const Bool sampled = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
const Bool linearFilter = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0;
const Bool colorRenderable = (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
const Bool depthStencilRenderable =
(features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
const Bool renderable = (isDepth || isStencil) ? depthStencilRenderable : colorRenderable;
if (sampled || renderable) {
caps |= FormatCapability::Creatable;
}
if (sampled) {
caps |= FormatCapability::Sampled;
if (linearFilter && !isInteger && !isStencil) {
caps |= FormatCapability::LinearFilter;
}
if (!isStencil && (features & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0 &&
(features & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0) {
caps |= FormatCapability::GenerateMipmap;
}
if (!isInteger && !isDepth && !isStencil) {
caps |= FormatCapability::TextureGather;
}
if (isDepth && !isStencil) {
caps |= FormatCapability::TextureShadow;
}
}
if (renderable) {
caps |= GetAttachmentCaps(logicalFormat);
if (IsLayeredTarget(target)) {
caps |= FormatCapability::FramebufferLayered;
}
}
if (IsMultisampleTarget(target)) {
caps |= FormatCapability::MultisampleTexture;
}
return caps;
}
Optional<VkFormat> ResolveVulkanFallbackFormat(TextureInternalFormat format) {
switch (format) {
case TextureInternalFormat::RGB:
case TextureInternalFormat::RGB8:
return VK_FORMAT_R8G8B8A8_UNORM;
case TextureInternalFormat::SRGB8:
return VK_FORMAT_R8G8B8A8_SRGB;
case TextureInternalFormat::RGB8Snorm:
return VK_FORMAT_R8G8B8A8_SNORM;
case TextureInternalFormat::RGB16:
return VK_FORMAT_R16G16B16A16_UNORM;
case TextureInternalFormat::RGB16Snorm:
return VK_FORMAT_R16G16B16A16_SNORM;
case TextureInternalFormat::RGB16F:
return VK_FORMAT_R16G16B16A16_SFLOAT;
case TextureInternalFormat::RGB32F:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case TextureInternalFormat::RGB8I:
return VK_FORMAT_R8G8B8A8_SINT;
case TextureInternalFormat::RGB8UI:
return VK_FORMAT_R8G8B8A8_UINT;
case TextureInternalFormat::RGB16I:
return VK_FORMAT_R16G16B16A16_SINT;
case TextureInternalFormat::RGB16UI:
return VK_FORMAT_R16G16B16A16_UINT;
case TextureInternalFormat::RGB32I:
return VK_FORMAT_R32G32B32A32_SINT;
case TextureInternalFormat::RGB32UI:
return VK_FORMAT_R32G32B32A32_UINT;
default:
return Nullopt;
}
}
Vector<Int> BuildSampleCounts(Int maxSamples) {
Vector<Int> counts;
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
counts.push_back(samples);
}
counts.push_back(1);
return counts;
}
void FillVulkanFormatCapabilities(VkPhysicalDevice physicalDevice,
const DynamicBackendParameters& dynamicParameters,
FormatCapabilityCache& cache) {
cache.Clear();
if (physicalDevice == VK_NULL_HANDLE) {
return;
}
for (SizeT formatIndex = 0; formatIndex < kFormatCapabilityFormatCount; ++formatIndex) {
const auto logicalFormat = static_cast<TextureInternalFormat>(formatIndex);
if (!IsFormatIndexValid(logicalFormat)) {
continue;
}
VkFormat nativeFormat = MG_Util::ConvertTextureInternalFormatToVkEnum(logicalFormat);
VkFormat fallbackFormat = ResolveVulkanFallbackFormat(logicalFormat).value_or(VK_FORMAT_UNDEFINED);
VkFormatProperties nativeProperties{};
if (nativeFormat != VK_FORMAT_UNDEFINED) {
vkGetPhysicalDeviceFormatProperties(physicalDevice, nativeFormat, &nativeProperties);
}
VkFormatProperties fallbackProperties{};
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
vkGetPhysicalDeviceFormatProperties(physicalDevice, fallbackFormat, &fallbackProperties);
}
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
const auto target = static_cast<TextureTarget>(targetIndex);
const VkFormatFeatureFlags nativeFeatures =
IsTextureBufferTarget(target) ? nativeProperties.bufferFeatures
: nativeProperties.optimalTilingFeatures;
FormatCapabilityFlags nativeCaps = BuildVulkanCaps(logicalFormat, target, nativeFeatures);
cache.FullCaps[targetIndex][formatIndex] |= nativeCaps;
const VkFormatFeatureFlags fallbackFeatures =
IsTextureBufferTarget(target) ? fallbackProperties.bufferFeatures
: fallbackProperties.optimalTilingFeatures;
FormatCapabilityFlags fallbackCaps = BuildVulkanCaps(logicalFormat, target, fallbackFeatures);
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
cache.CaveatCaps[targetIndex][formatIndex] |= fallbackCaps;
}
if (HasFormatCapability(nativeCaps | fallbackCaps, FormatCapability::MultisampleTexture)) {
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
const Bool isInteger = IsIntegerInternalFormat(logicalFormat);
Int maxSamples = dynamicParameters.MaxColorTextureSamples;
if (isDepth || isStencil) {
maxSamples = dynamicParameters.MaxDepthTextureSamples;
} else if (isInteger) {
maxSamples = dynamicParameters.MaxIntegerSamples;
}
cache.SampleCounts[targetIndex][formatIndex] = BuildSampleCounts(maxSamples);
}
}
const SizeT renderbufferTargetIndex = GetRenderbufferFormatCapabilityTargetIndex();
FormatCapabilityFlags renderbufferCaps =
BuildVulkanCaps(logicalFormat, TextureTarget::Texture2D, nativeProperties.optimalTilingFeatures);
renderbufferCaps &= FormatCapability::Creatable;
if ((nativeProperties.optimalTilingFeatures &
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) != 0) {
renderbufferCaps |= GetAttachmentCaps(logicalFormat);
renderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
}
cache.FullCaps[renderbufferTargetIndex][formatIndex] |= renderbufferCaps;
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
FormatCapabilityFlags fallbackRenderbufferCaps =
BuildVulkanCaps(logicalFormat, TextureTarget::Texture2D,
fallbackProperties.optimalTilingFeatures);
fallbackRenderbufferCaps &= FormatCapability::Creatable;
if ((fallbackProperties.optimalTilingFeatures &
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) !=
0) {
fallbackRenderbufferCaps |= GetAttachmentCaps(logicalFormat);
fallbackRenderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
}
cache.CaveatCaps[renderbufferTargetIndex][formatIndex] |= fallbackRenderbufferCaps;
}
const FormatCapabilityFlags rbCaps = cache.FullCaps[renderbufferTargetIndex][formatIndex] |
cache.CaveatCaps[renderbufferTargetIndex][formatIndex];
if (HasFormatCapability(rbCaps, FormatCapability::MultisampleRenderbuffer)) {
cache.SampleCounts[renderbufferTargetIndex][formatIndex] =
BuildSampleCounts(dynamicParameters.MaxFramebufferSamples);
}
}
}
} // namespace
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
@@ -88,6 +329,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
}
UpdateDynamicBackendParameters();
UpdateAdvertisedExtensions();
FillVulkanFormatCapabilities(physicalDevice.handle, m_dynamicParameters, MutableFormatCapabilities());
return true;
}
@@ -255,6 +497,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_vulkanCaps = capabilities;
UpdateDynamicBackendParameters();
UpdateAdvertisedExtensions();
MutableFormatCapabilities().Clear();
}
void BackendObject_DirectVulkan::UpdateAdvertisedExtensions() {
+170 -21
View File
@@ -2895,38 +2895,165 @@ namespace MobileGL::MG_Impl::GLImpl {
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;
const Bool isLayeredTarget = target == GL_TEXTURE_3D || target == GL_TEXTURE_1D_ARRAY ||
target == GL_TEXTURE_2D_ARRAY || target == GL_TEXTURE_CUBE_MAP ||
target == GL_TEXTURE_CUBE_MAP_ARRAY || target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
GLint maxSamples = 1;
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& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
if (isDepthFormat || isStencilFormat) {
maxSamples = dynamicParameters.MaxDepthTextureSamples;
} else if (isIntegerFormat) {
maxSamples = dynamicParameters.MaxIntegerSamples;
} else {
maxSamples = dynamicParameters.MaxColorTextureSamples;
}
maxSamples = std::max(maxSamples, 1);
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;
};
auto componentType = [&](GLint size, Bool depthComponent, Bool stencilComponent) -> GLint {
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;
}
};
switch (pname) {
case GL_INTERNALFORMAT_SUPPORTED:
writeValues({GL_TRUE});
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({componentType(componentSizes.Red, false, false)});
return;
case GL_INTERNALFORMAT_GREEN_TYPE:
writeValues({componentType(componentSizes.Green, false, false)});
return;
case GL_INTERNALFORMAT_BLUE_TYPE:
writeValues({componentType(componentSizes.Blue, false, false)});
return;
case GL_INTERNALFORMAT_ALPHA_TYPE:
writeValues({componentType(componentSizes.Alpha, false, false)});
return;
case GL_INTERNALFORMAT_DEPTH_TYPE:
writeValues({componentType(componentSizes.Depth, true, false)});
return;
case GL_INTERNALFORMAT_STENCIL_TYPE:
writeValues({componentType(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;
@@ -2937,20 +3064,42 @@ namespace MobileGL::MG_Impl::GLImpl {
writeValues({isStencilFormat ? GL_TRUE : GL_FALSE});
return;
case GL_FRAMEBUFFER_RENDERABLE:
writeValues({GL_FULL_SUPPORT});
writeValues({supportFor(MG_Backend::FormatCapability::FramebufferRenderable)});
return;
case GL_FRAMEBUFFER_RENDERABLE_LAYERED:
writeValues({isLayeredTarget ? GL_FULL_SUPPORT : GL_NONE});
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({maxSamples > 1 ? 2 : 1});
writeValues({sampleCounts != nullptr ? static_cast<GLint>(sampleCounts->size()) : 0});
return;
case GL_SAMPLES:
if (maxSamples > 1) {
writeValues({maxSamples, 1});
} else {
writeValues({1});
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(
+115
View File
@@ -10,6 +10,7 @@
#include "Includes.h"
#include "Init.h"
#include <MG_Backend/BackendObjects.h>
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
#include <MG_Impl/GLImpl/Texture/GL_Texture.h>
@@ -24,6 +25,93 @@ protected:
void SetUp() override { MobileGL::Initialize(); }
};
namespace {
class FormatCapabilityBackend final : public MobileGL::MG_Backend::BackendObject {
public:
FormatCapabilityBackend() {
auto& cache = MutableFormatCapabilities();
const auto texture2DIndex =
MobileGL::MG_Backend::GetFormatCapabilityTargetIndex(TextureTarget::Texture2D);
const auto texture3DIndex =
MobileGL::MG_Backend::GetFormatCapabilityTargetIndex(TextureTarget::Texture3D);
const auto renderbufferIndex =
MobileGL::MG_Backend::GetRenderbufferFormatCapabilityTargetIndex();
const auto rgba8Index = static_cast<SizeT>(TextureInternalFormat::RGBA8);
const auto rg8Index = static_cast<SizeT>(TextureInternalFormat::RG8);
const auto depthStencilIndex = static_cast<SizeT>(TextureInternalFormat::Depth24Stencil8);
cache.FullCaps[texture2DIndex][rgba8Index] |= MobileGL::MG_Backend::FormatCapability::Creatable;
cache.FullCaps[texture2DIndex][rgba8Index] |= MobileGL::MG_Backend::FormatCapability::Sampled;
cache.FullCaps[texture2DIndex][rgba8Index] |= MobileGL::MG_Backend::FormatCapability::LinearFilter;
cache.FullCaps[texture2DIndex][rgba8Index] |= MobileGL::MG_Backend::FormatCapability::GenerateMipmap;
cache.FullCaps[texture2DIndex][rgba8Index] |=
MobileGL::MG_Backend::FormatCapability::FramebufferRenderable;
cache.FullCaps[texture2DIndex][rgba8Index] |= MobileGL::MG_Backend::FormatCapability::ColorAttachment;
cache.SampleCounts[texture2DIndex][rgba8Index] = {1};
cache.CaveatCaps[texture2DIndex][rg8Index] |= MobileGL::MG_Backend::FormatCapability::Creatable;
cache.CaveatCaps[texture2DIndex][rg8Index] |= MobileGL::MG_Backend::FormatCapability::Sampled;
cache.CaveatCaps[texture2DIndex][rg8Index] |= MobileGL::MG_Backend::FormatCapability::LinearFilter;
cache.FullCaps[texture3DIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::Creatable;
cache.FullCaps[texture3DIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::FramebufferRenderable;
cache.FullCaps[texture3DIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::FramebufferLayered;
cache.FullCaps[texture3DIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::DepthAttachment;
cache.FullCaps[texture3DIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::StencilAttachment;
cache.FullCaps[renderbufferIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::Creatable;
cache.FullCaps[renderbufferIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::FramebufferRenderable;
cache.FullCaps[renderbufferIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::DepthAttachment;
cache.FullCaps[renderbufferIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::StencilAttachment;
cache.FullCaps[renderbufferIndex][depthStencilIndex] |=
MobileGL::MG_Backend::FormatCapability::MultisampleRenderbuffer;
cache.SampleCounts[renderbufferIndex][depthStencilIndex] = {4, 2, 1};
}
void Initialize() override {}
Bool InitCapabilities() override { return true; }
Bool InitWindowSurface() override { return true; }
const RendererInfo& GetRendererInfo() const override {
static RendererInfo info = {};
return info;
}
String GetBackendAPIVersionString() const override { return {}; }
const MobileGL::MG_Backend::GlobalBackendFunctionsTable& GetBackendFunctions() const override {
static MobileGL::MG_Backend::GlobalBackendFunctionsTable table = {};
return table;
}
const MobileGL::MG_Backend::DynamicBackendParameters& GetDynamicParameters() const override {
static MobileGL::MG_Backend::DynamicBackendParameters params = {};
return params;
}
BackendType GetBackendType() const override { return BackendType::Unknown; }
};
class ScopedBackendOverride {
public:
explicit ScopedBackendOverride(UniquePtr<MobileGL::MG_Backend::BackendObject> backend):
m_previous(Move(MobileGL::MG_Backend::pActiveBackendObject)) {
MobileGL::MG_Backend::pActiveBackendObject = Move(backend);
}
~ScopedBackendOverride() {
MobileGL::MG_Backend::pActiveBackendObject = Move(m_previous);
}
private:
UniquePtr<MobileGL::MG_Backend::BackendObject> m_previous;
};
} // namespace
TEST_F(TextureTest, CreateTexturesCreatesObjectsWithoutBinding) {
GLuint texture = 0;
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &texture);
@@ -346,20 +434,47 @@ TEST_F(TextureTest, NamedTextureVectorParametersAndGettersWorkWithoutBinding) {
}
TEST_F(TextureTest, GetInternalformativReportsBasicTextureMetadata) {
ScopedBackendOverride backend(MakeUnique<FormatCapabilityBackend>());
GLint params[4] = {};
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_INTERNALFORMAT_SUPPORTED, 1, params);
EXPECT_EQ(params[0], GL_TRUE);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_FRAMEBUFFER_RENDERABLE, 1, params);
EXPECT_EQ(params[0], GL_FULL_SUPPORT);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_FILTER, 1, params);
EXPECT_EQ(params[0], GL_FULL_SUPPORT);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_INTERNALFORMAT_RED_SIZE, 1, params);
EXPECT_EQ(params[0], 8);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_INTERNALFORMAT_RED_TYPE, 1, params);
EXPECT_EQ(params[0], GL_UNSIGNED_NORMALIZED);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_TEXTURE_IMAGE_FORMAT, 1, params);
EXPECT_EQ(params[0], GL_RGBA);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RGBA8, GL_TEXTURE_IMAGE_TYPE, 1, params);
EXPECT_EQ(params[0], GL_UNSIGNED_BYTE);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RG8, GL_INTERNALFORMAT_SUPPORTED, 1, params);
EXPECT_EQ(params[0], GL_TRUE);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_2D, GL_RG8, GL_FILTER, 1, params);
EXPECT_EQ(params[0], GL_CAVEAT_SUPPORT);
MG_Impl::GLImpl::GetInternalformativ(GL_TEXTURE_3D, GL_DEPTH24_STENCIL8, GL_FRAMEBUFFER_RENDERABLE_LAYERED, 1,
params);
EXPECT_EQ(params[0], GL_FULL_SUPPORT);
MG_Impl::GLImpl::GetInternalformativ(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, GL_NUM_SAMPLE_COUNTS, 1, params);
EXPECT_EQ(params[0], 3);
MG_Impl::GLImpl::GetInternalformativ(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, GL_SAMPLES, 4, params);
EXPECT_EQ(params[0], 4);
EXPECT_EQ(params[1], 2);
EXPECT_EQ(params[2], 1);
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
}