mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 12:18:30 +09:00
[Fix] (MG_Impl/GLImpl): report backend format capabilities
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user