mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
Compare commits
49
Commits
7463236b34
...
588ddba722
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
588ddba722 | ||
|
|
62301b1061 | ||
|
|
f3a846d336 | ||
|
|
9cdc82fbdd | ||
|
|
4a9d20c49f | ||
|
|
394d1ce748 | ||
|
|
300b458132 | ||
|
|
1f1a331a44 | ||
|
|
817091641c | ||
|
|
e64c7c7e65 | ||
|
|
dd60ff39ce | ||
|
|
96ad7ca0cc | ||
|
|
e80a23eae6 | ||
|
|
088f263495 | ||
|
|
3b3b6e5b8b | ||
|
|
9eda2147b1 | ||
|
|
a63699cde6 | ||
|
|
765aaec6dc | ||
|
|
bcd669bd25 | ||
|
|
f3405d1d53 | ||
|
|
81604d5596 | ||
|
|
31ea6aa5a3 | ||
|
|
7d6f6603c1 | ||
|
|
39c17c0b1b | ||
|
|
88138b48ec | ||
|
|
c114ce750b | ||
|
|
1ca2d3c0fe | ||
|
|
58c17f85a5 | ||
|
|
4873da6844 | ||
|
|
efeb24ff9b | ||
|
|
18c1a4d586 | ||
|
|
66ac3486e1 | ||
|
|
764a44f589 | ||
|
|
d96acb7972 | ||
|
|
bd710078fc | ||
|
|
95a7b17d45 | ||
|
|
19932f9e49 | ||
|
|
1011d9fea1 | ||
|
|
b5565ae503 | ||
|
|
b06ad3f877 | ||
|
|
3311e6034a | ||
|
|
027c1bd4ab | ||
|
|
da52cc3906 | ||
|
|
ebe4fe133f | ||
|
|
534ec65dda | ||
|
|
35ad1ae7fc | ||
|
|
6152ee933f | ||
|
|
dac02ca044 | ||
|
|
42fd02d82f |
@@ -201,6 +201,11 @@ fetch_file_from_mirror() {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Files no mirror could serve, even after retrying every mirror. Only these fall
|
||||||
|
# back to Git LFS, so a mirror that served the rest of the case still spares
|
||||||
|
# GitHub the bandwidth for those files.
|
||||||
|
mirror_failures=()
|
||||||
|
|
||||||
fetch_from_mirror() {
|
fetch_from_mirror() {
|
||||||
mkdir -p "${fixture_dir}"
|
mkdir -p "${fixture_dir}"
|
||||||
for file in "${files[@]}"; do
|
for file in "${files[@]}"; do
|
||||||
@@ -219,17 +224,19 @@ fetch_from_mirror() {
|
|||||||
echo "Mirror did not serve ${name}; trying the next mirror" >&2
|
echo "Mirror did not serve ${name}; trying the next mirror" >&2
|
||||||
done
|
done
|
||||||
if [ "${fetched}" -ne 1 ]; then
|
if [ "${fetched}" -ne 1 ]; then
|
||||||
return 1
|
mirror_failures+=("${file}")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
[ "${#mirror_failures[@]}" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
if fetch_from_mirror; then
|
if fetch_from_mirror; then
|
||||||
echo "Fetched trace fixture files for ${case_name} from mirror: ${include}"
|
echo "Fetched trace fixture files for ${case_name} from mirror: ${include}"
|
||||||
else
|
else
|
||||||
echo "All mirrors failed for ${case_name}; falling back to Git LFS: ${include}"
|
fallback_include="$(IFS=,; echo "${mirror_failures[*]}")"
|
||||||
|
echo "All mirrors failed for ${#mirror_failures[@]} of ${#files[@]} file(s) of ${case_name}; falling back to Git LFS: ${fallback_include}"
|
||||||
git lfs install --local
|
git lfs install --local
|
||||||
git lfs pull --include="${include}" --exclude=""
|
git lfs pull --include="${fallback_include}" --exclude=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for file in "${files[@]}"; do
|
for file in "${files[@]}"; do
|
||||||
|
|||||||
@@ -218,6 +218,7 @@ set(SOURCE_FILES
|
|||||||
MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp
|
MobileGL/MG_Impl/GLImpl/Framebuffer/Validators.cpp
|
||||||
MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp
|
MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp
|
||||||
MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp
|
MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp
|
||||||
|
MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.cpp
|
||||||
MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp
|
MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp
|
||||||
MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp
|
MobileGL/MG_Impl/GLImpl/Texture/Validators.cpp
|
||||||
MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp
|
MobileGL/MG_Impl/GLImpl/Texture/ProxyTexture.cpp
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ namespace MobileGL {
|
|||||||
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
void (*ClearNamedFramebufferfi)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
void (*ClearNamedFramebufferfi)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void (*ClearNamedFramebufferiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void (*ClearNamedFramebufferuiv)(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
|
void (*BlitFramebuffer)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
|
||||||
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||||
void (*BlitNamedFramebuffer)(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
void (*BlitNamedFramebuffer)(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
||||||
@@ -349,6 +353,12 @@ namespace MobileGL {
|
|||||||
// real ES drivers behind DirectGLES both do. Defaults to true so a backend
|
// real ES drivers behind DirectGLES both do. Defaults to true so a backend
|
||||||
// that never sets it keeps the permissive behaviour.
|
// that never sets it keeps the permissive behaviour.
|
||||||
Bool SupportsDistinctDepthStencilAttachments = true;
|
Bool SupportsDistinctDepthStencilAttachments = true;
|
||||||
|
// Whether attaching a single layer of a 3D or array texture to a framebuffer actually
|
||||||
|
// renders to that layer. DirectGLES hands the layer straight to
|
||||||
|
// glFramebufferTextureLayer, so it does; DirectVulkan maps a GL layer onto a Vulkan
|
||||||
|
// array layer with no notion of a 3D depth slice, so it does not yet. Defaults to false
|
||||||
|
// so a backend that never sets it gets the conservative answer.
|
||||||
|
Bool SupportsPerLayerFramebufferAttachment = false;
|
||||||
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024;
|
||||||
Uint32 SubgroupSize = 0;
|
Uint32 SubgroupSize = 0;
|
||||||
Uint32 SubgroupSupportedStages = 0;
|
Uint32 SubgroupSupportedStages = 0;
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
|
|
||||||
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
|
void ClearGLErrors(const MG_External::GLESFunctionsTable& gl) {
|
||||||
if (!gl.glGetError) return;
|
if (!gl.glGetError) return;
|
||||||
while (gl.glGetError() != GL_NO_ERROR) {
|
while (gl.glGetError() != GL_NO_ERROR) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool CheckNoGLError(const MG_External::GLESFunctionsTable& gl) {
|
Bool CheckNoGLError(const MG_External::GLESFunctionsTable& gl) {
|
||||||
@@ -77,8 +76,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bool IsGLESProbeMultisampleTarget(TextureTarget target) {
|
Bool IsGLESProbeMultisampleTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture2DMultisample ||
|
return target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GLenum GetFramebufferAttachment(TextureInternalFormat format) {
|
GLenum GetFramebufferAttachment(TextureInternalFormat format) {
|
||||||
@@ -115,8 +113,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
GLenum normalizedInternalFormat = glFormat;
|
GLenum normalizedInternalFormat = glFormat;
|
||||||
GLenum imageFormat = GL_RGBA;
|
GLenum imageFormat = GL_RGBA;
|
||||||
GLenum imageType = GL_UNSIGNED_BYTE;
|
GLenum imageType = GL_UNSIGNED_BYTE;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(glFormat, PixelFormatNormalizeOptionBit::None,
|
||||||
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
|
&normalizedInternalFormat, &imageFormat, &imageType);
|
||||||
return imageFormat != GL_RED_INTEGER && imageFormat != GL_RG_INTEGER && imageFormat != GL_RGB_INTEGER &&
|
return imageFormat != GL_RED_INTEGER && imageFormat != GL_RG_INTEGER && imageFormat != GL_RGB_INTEGER &&
|
||||||
imageFormat != GL_RGBA_INTEGER && !MG_Util::IsDepthFormatInternalFormat(format) &&
|
imageFormat != GL_RGBA_INTEGER && !MG_Util::IsDepthFormatInternalFormat(format) &&
|
||||||
!MG_Util::IsStencilFormatInternalFormat(format);
|
!MG_Util::IsStencilFormatInternalFormat(format);
|
||||||
@@ -154,9 +152,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
GLESProbeFormatInfo BuildNativeProbeFormatInfo(GLenum requestedInternalFormat) {
|
GLESProbeFormatInfo BuildNativeProbeFormatInfo(GLenum requestedInternalFormat) {
|
||||||
GLESProbeFormatInfo info;
|
GLESProbeFormatInfo info;
|
||||||
info.InternalFormat = requestedInternalFormat;
|
info.InternalFormat = requestedInternalFormat;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(requestedInternalFormat,
|
||||||
requestedInternalFormat, PixelFormatNormalizeOptionBit::None, nullptr, &info.ImageFormat,
|
PixelFormatNormalizeOptionBit::None, nullptr,
|
||||||
&info.ImageType);
|
&info.ImageFormat, &info.ImageType);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,20 +231,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return MG_Util::ConvertGLEnumToString(internalFormat);
|
return MG_Util::ConvertGLEnumToString(internalFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogGLESFormatCaveat(TextureInternalFormat logicalFormat,
|
void LogGLESFormatCaveat(TextureInternalFormat logicalFormat, SizeT targetIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
const GLESProbeFormatInfo& fallbackInfo) {
|
const GLESProbeFormatInfo& fallbackInfo) {
|
||||||
MGLOG_D("Caveat: %s %s not fully supported. Reason: %s. Fallback: %s",
|
MGLOG_D("Caveat: %s %s not fully supported. Reason: %s. Fallback: %s",
|
||||||
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
||||||
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(), fallbackInfo.Reason.c_str(),
|
||||||
fallbackInfo.Reason.c_str(),
|
|
||||||
ConvertFallbackInternalFormatToString(fallbackInfo.InternalFormat).c_str());
|
ConvertFallbackInternalFormatToString(fallbackInfo.InternalFormat).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool BuildFallbackProbeFormatInfo(GLenum requestedInternalFormat,
|
Bool BuildFallbackProbeFormatInfo(GLenum requestedInternalFormat, Flags<PixelFormatNormalizeOptionBit> options,
|
||||||
Flags<PixelFormatNormalizeOptionBit> options,
|
Bool forced, GLESProbeFormatInfo& outInfo) {
|
||||||
Bool forced,
|
|
||||||
GLESProbeFormatInfo& outInfo) {
|
|
||||||
const Flags<PixelFormatNormalizeOptionBit> applicableOptions =
|
const Flags<PixelFormatNormalizeOptionBit> applicableOptions =
|
||||||
MG_Util::TextureFormatProcessor::GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat,
|
MG_Util::TextureFormatProcessor::GetApplicablePixelFormatNormalizeOptions(requestedInternalFormat,
|
||||||
options);
|
options);
|
||||||
@@ -261,8 +255,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return outInfo.InternalFormat != GL_UNKNOWN_MGL;
|
return outInfo.InternalFormat != GL_UNKNOWN_MGL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatCapabilityFlags BuildTextureCapsFromProbe(TextureInternalFormat logicalFormat,
|
FormatCapabilityFlags BuildTextureCapsFromProbe(TextureInternalFormat logicalFormat, TextureTarget target,
|
||||||
TextureTarget target,
|
|
||||||
Bool renderable) {
|
Bool renderable) {
|
||||||
FormatCapabilityFlags caps = GetTextureFeatureCaps(logicalFormat, target);
|
FormatCapabilityFlags caps = GetTextureFeatureCaps(logicalFormat, target);
|
||||||
if (renderable) {
|
if (renderable) {
|
||||||
@@ -276,16 +269,12 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddFullFormatCaps(FormatCapabilityCache& cache,
|
void AddFullFormatCaps(FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
SizeT formatIndex,
|
|
||||||
FormatCapabilityFlags caps) {
|
FormatCapabilityFlags caps) {
|
||||||
cache.FullCaps[targetIndex][formatIndex] |= caps;
|
cache.FullCaps[targetIndex][formatIndex] |= caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool AddCaveatFormatCaps(FormatCapabilityCache& cache,
|
Bool AddCaveatFormatCaps(FormatCapabilityCache& cache, SizeT targetIndex, SizeT formatIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
SizeT formatIndex,
|
|
||||||
FormatCapabilityFlags caps) {
|
FormatCapabilityFlags caps) {
|
||||||
Bool added = false;
|
Bool added = false;
|
||||||
for (FormatCapability capability : kReportedFormatCapabilities) {
|
for (FormatCapability capability : kReportedFormatCapabilities) {
|
||||||
@@ -299,8 +288,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Int GetGLESFormatMaxSamples(const MG_External::GLESCapabilities& capabilities,
|
Int GetGLESFormatMaxSamples(const MG_External::GLESCapabilities& capabilities,
|
||||||
TextureInternalFormat logicalFormat,
|
TextureInternalFormat logicalFormat, GLenum imageFormat) {
|
||||||
GLenum imageFormat) {
|
|
||||||
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
||||||
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
|
const Bool isStencil = MG_Util::IsStencilFormatInternalFormat(logicalFormat);
|
||||||
const Bool isInteger = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
|
const Bool isInteger = imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER ||
|
||||||
@@ -314,10 +302,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return capabilities.MaxColorTextureSamples;
|
return capabilities.MaxColorTextureSamples;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeFramebufferCompletenessForTexture(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeFramebufferCompletenessForTexture(const MG_External::GLESFunctionsTable& gl, TextureTarget target,
|
||||||
TextureTarget target,
|
GLuint texture, TextureInternalFormat format) {
|
||||||
GLuint texture,
|
|
||||||
TextureInternalFormat format) {
|
|
||||||
GLuint framebuffer = 0;
|
GLuint framebuffer = 0;
|
||||||
GLint prevFramebuffer = 0;
|
GLint prevFramebuffer = 0;
|
||||||
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glCheckFramebufferStatus ||
|
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glCheckFramebufferStatus ||
|
||||||
@@ -399,9 +385,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return supported;
|
return supported;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl, GLuint renderbuffer,
|
||||||
GLuint renderbuffer,
|
TextureInternalFormat format) {
|
||||||
TextureInternalFormat format) {
|
|
||||||
GLuint framebuffer = 0;
|
GLuint framebuffer = 0;
|
||||||
GLint prevFramebuffer = 0;
|
GLint prevFramebuffer = 0;
|
||||||
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glFramebufferRenderbuffer ||
|
if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glFramebufferRenderbuffer ||
|
||||||
@@ -468,16 +453,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TextureTarget::Texture3D:
|
case TextureTarget::Texture3D:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 2, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 2, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
case TextureTarget::Texture2DArray:
|
case TextureTarget::Texture2DArray:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 1, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 1, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
case TextureTarget::TextureCubeMapArray:
|
case TextureTarget::TextureCubeMapArray:
|
||||||
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 6, 0, imageFormat,
|
gl.glTexImage3D(glTarget, 0, static_cast<GLint>(internalFormat), 2, 2, 6, 0, imageFormat, imageType,
|
||||||
imageType, nullptr);
|
nullptr);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -498,11 +483,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return created;
|
return created;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ProbeRenderbuffer(const MG_External::GLESFunctionsTable& gl,
|
Bool ProbeRenderbuffer(const MG_External::GLESFunctionsTable& gl, GLenum internalFormat,
|
||||||
GLenum internalFormat,
|
TextureInternalFormat logicalFormat, Bool multisample, Int samples) {
|
||||||
TextureInternalFormat logicalFormat,
|
|
||||||
Bool multisample,
|
|
||||||
Int samples) {
|
|
||||||
if (!gl.glGenRenderbuffers || !gl.glBindRenderbuffer || !gl.glDeleteRenderbuffers) {
|
if (!gl.glGenRenderbuffers || !gl.glBindRenderbuffer || !gl.glDeleteRenderbuffers) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -524,17 +506,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
gl.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
|
gl.glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, 1, 1);
|
||||||
}
|
}
|
||||||
const Bool created = CheckNoGLError(gl);
|
const Bool created = CheckNoGLError(gl);
|
||||||
const Bool complete = created && ProbeFramebufferCompletenessForRenderbuffer(gl, renderbuffer, logicalFormat);
|
const Bool complete =
|
||||||
|
created && ProbeFramebufferCompletenessForRenderbuffer(gl, renderbuffer, logicalFormat);
|
||||||
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
|
gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast<GLuint>(prevRenderbuffer));
|
||||||
gl.glDeleteRenderbuffers(1, &renderbuffer);
|
gl.glDeleteRenderbuffers(1, &renderbuffer);
|
||||||
ClearGLErrors(gl);
|
ClearGLErrors(gl);
|
||||||
return complete;
|
return complete;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Int> ProbeRenderbufferSampleCounts(const MG_External::GLESFunctionsTable& gl,
|
Vector<Int> ProbeRenderbufferSampleCounts(const MG_External::GLESFunctionsTable& gl, GLenum internalFormat,
|
||||||
GLenum internalFormat,
|
TextureInternalFormat logicalFormat, Int maxSamples) {
|
||||||
TextureInternalFormat logicalFormat,
|
|
||||||
Int maxSamples) {
|
|
||||||
Vector<Int> sampleCounts;
|
Vector<Int> sampleCounts;
|
||||||
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
|
for (Int samples = std::max(maxSamples, 1); samples > 1; samples >>= 1) {
|
||||||
if (ProbeRenderbuffer(gl, internalFormat, logicalFormat, true, samples)) {
|
if (ProbeRenderbuffer(gl, internalFormat, logicalFormat, true, samples)) {
|
||||||
@@ -589,8 +570,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
hasForcedFallback = BuildFallbackProbeFormatInfo(
|
hasForcedFallback = BuildFallbackProbeFormatInfo(
|
||||||
requestedInternalFormat, forcedOptions | targetOptions, true, fallbackInfo);
|
requestedInternalFormat, forcedOptions | targetOptions, true, fallbackInfo);
|
||||||
if (!hasForcedFallback) {
|
if (!hasForcedFallback) {
|
||||||
BuildFallbackProbeFormatInfo(requestedInternalFormat, driverOptions | targetOptions,
|
BuildFallbackProbeFormatInfo(requestedInternalFormat, driverOptions | targetOptions, false,
|
||||||
false, fallbackInfo);
|
fallbackInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -623,9 +604,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
ProbeTexture(gl, probeTarget, fallbackInfo.InternalFormat, fallbackInfo.ImageFormat,
|
ProbeTexture(gl, probeTarget, fallbackInfo.InternalFormat, fallbackInfo.ImageFormat,
|
||||||
fallbackInfo.ImageType, logicalFormat, &fallbackRenderable);
|
fallbackInfo.ImageType, logicalFormat, &fallbackRenderable);
|
||||||
if (fallbackCreated) {
|
if (fallbackCreated) {
|
||||||
if (AddCaveatFormatCaps(cache, targetIndex, formatIndex,
|
if (AddCaveatFormatCaps(
|
||||||
BuildTextureCapsFromProbe(logicalFormat, target,
|
cache, targetIndex, formatIndex,
|
||||||
fallbackRenderable))) {
|
BuildTextureCapsFromProbe(logicalFormat, target, fallbackRenderable))) {
|
||||||
LogGLESFormatCaveat(logicalFormat, targetIndex, fallbackInfo);
|
LogGLESFormatCaveat(logicalFormat, targetIndex, fallbackInfo);
|
||||||
}
|
}
|
||||||
if (IsGLESProbeMultisampleTarget(target)) {
|
if (IsGLESProbeMultisampleTarget(target)) {
|
||||||
@@ -676,7 +657,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
.ExtraVendor = Nullopt, // Extra vendor
|
.ExtraVendor = Nullopt, // Extra vendor
|
||||||
.RendererGLInfo =
|
.RendererGLInfo =
|
||||||
{
|
{
|
||||||
.TargetGLVersion = {3, 3, 0}, // GL target version
|
.TargetGLVersion = {4, 0, 0}, // GL target version
|
||||||
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
.TargetGLSLVersion = {4, 6, 0}, // Target Shading Language Version
|
||||||
// Baseline advertisement (no timer queries / anisotropy yet); reconciled
|
// Baseline advertisement (no timer queries / anisotropy yet); reconciled
|
||||||
// once the ES capabilities exist, see UpdateAdvertisedCapabilityExtensions.
|
// once the ES capabilities exist, see UpdateAdvertisedCapabilityExtensions.
|
||||||
@@ -707,8 +688,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void PopulateFormatCapabilities(const MG_External::GLESFunctionsTable& gl,
|
void PopulateFormatCapabilities(const MG_External::GLESFunctionsTable& gl,
|
||||||
const MG_External::GLESCapabilities& capabilities,
|
const MG_External::GLESCapabilities& capabilities, FormatCapabilityCache& cache) {
|
||||||
FormatCapabilityCache& cache) {
|
|
||||||
PopulateFormatCapabilitiesImpl(gl, capabilities, cache);
|
PopulateFormatCapabilitiesImpl(gl, capabilities, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,10 +752,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((handle.Backend != WindowBackend::Android &&
|
if ((handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::MetalLayer && handle.Backend != WindowBackend::Win32) ||
|
||||||
handle.Backend != WindowBackend::MetalLayer &&
|
|
||||||
handle.Backend != WindowBackend::Win32) ||
|
|
||||||
!handle.Handle) {
|
!handle.Handle) {
|
||||||
MGLOG_E("DirectGLES backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
MGLOG_E("DirectGLES backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
@@ -894,27 +872,25 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Vector<GLExtension> BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported) {
|
Vector<GLExtension> BuildAdvertisedExtensions(Bool timerQueriesSupported, Bool anisotropicFilteringSupported) {
|
||||||
Vector<GLExtension> extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
||||||
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_EXT_framebuffer_object,
|
||||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage, E_GL_ARB_texture_storage,
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample,
|
E_GL_ARB_texture_storage_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
||||||
E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters, E_GL_ARB_shader_draw_parameters,
|
||||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind, E_GL_ARB_shading_language_420pack,
|
||||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind,
|
E_GL_ARB_vertex_attrib_binding,
|
||||||
E_GL_ARB_shading_language_420pack, E_GL_ARB_vertex_attrib_binding,
|
// Both are core from GL 3.2/3.3 on and implemented here for
|
||||||
// Both are core from GL 3.2/3.3 on and implemented here for
|
// every advertised version, but an app targeting 3.0/3.1
|
||||||
// every advertised version, but an app targeting 3.0/3.1
|
// only reaches them through the extension string - the CTS
|
||||||
// only reaches them through the extension string - the CTS
|
// picks a whole different shader for draw_buffers without
|
||||||
// picks a whole different shader for draw_buffers without
|
// explicit_attrib_location. DirectVulkan advertises both.
|
||||||
// explicit_attrib_location. DirectVulkan advertises both.
|
E_GL_ARB_explicit_attrib_location, E_GL_ARB_texture_multisample, E_GL_ARB_shader_image_size,
|
||||||
E_GL_ARB_explicit_attrib_location, E_GL_ARB_texture_multisample,
|
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
||||||
E_GL_ARB_shader_image_size,
|
// extension explicitly permits. It is also the only thing that
|
||||||
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
// exposes glProgramParameteri before GL 4.1.
|
||||||
// extension explicitly permits. It is also the only thing that
|
E_GL_ARB_get_program_binary};
|
||||||
// exposes glProgramParameteri before GL 4.1.
|
|
||||||
E_GL_ARB_get_program_binary};
|
|
||||||
// Only advertised when the device driver actually has usable timer queries
|
// Only advertised when the device driver actually has usable timer queries
|
||||||
// (GL_EXT_disjoint_timer_query plus its entry points) and the
|
// (GL_EXT_disjoint_timer_query plus its entry points) and the
|
||||||
// MOBILEGL_DISABLE_TIMERQUERY escape hatch is off.
|
// MOBILEGL_DISABLE_TIMERQUERY escape hatch is off.
|
||||||
@@ -988,6 +964,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
|
funcsTable.GL.ClearBufferuiv = ClearBufferuiv;
|
||||||
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
||||||
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
|
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
|
||||||
|
funcsTable.GL.ClearNamedFramebufferiv = ClearNamedFramebufferiv;
|
||||||
|
funcsTable.GL.ClearNamedFramebufferuiv = ClearNamedFramebufferuiv;
|
||||||
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
|
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
|
||||||
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
||||||
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
|
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
|
||||||
@@ -1050,8 +1028,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return m_dynamicParameters;
|
return m_dynamicParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendObject_DirectGLES::ApplyGLESCapabilitiesForTesting(
|
void BackendObject_DirectGLES::ApplyGLESCapabilitiesForTesting(const MG_External::GLESCapabilities& capabilities) {
|
||||||
const MG_External::GLESCapabilities& capabilities) {
|
|
||||||
m_GLESCapabilities = capabilities;
|
m_GLESCapabilities = capabilities;
|
||||||
UpdateDynamicBackendParameters();
|
UpdateDynamicBackendParameters();
|
||||||
}
|
}
|
||||||
@@ -1115,8 +1092,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
m_dynamicParameters.TextureBufferOffsetAlignment = m_GLESCapabilities.TextureBufferOffsetAlignment;
|
m_dynamicParameters.TextureBufferOffsetAlignment = m_GLESCapabilities.TextureBufferOffsetAlignment;
|
||||||
m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings;
|
m_dynamicParameters.MaxUniformBufferBindings = m_GLESCapabilities.MaxUniformBufferBindings;
|
||||||
m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize;
|
m_dynamicParameters.MaxUniformBlockSize = m_GLESCapabilities.MaxUniformBlockSize;
|
||||||
const Int maxSupportedTextureUnits =
|
const Int maxSupportedTextureUnits = static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
|
||||||
m_dynamicParameters.MaxImageUnits =
|
m_dynamicParameters.MaxImageUnits =
|
||||||
std::max(std::min(m_GLESCapabilities.MaxImageUnits, maxSupportedTextureUnits), 0);
|
std::max(std::min(m_GLESCapabilities.MaxImageUnits, maxSupportedTextureUnits), 0);
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_GLESCapabilities.MaxCombinedImageUniforms, 0);
|
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_GLESCapabilities.MaxCombinedImageUniforms, 0);
|
||||||
@@ -1124,8 +1100,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
return std::min({std::max(stageLimit, 0), m_dynamicParameters.MaxImageUnits,
|
return std::min({std::max(stageLimit, 0), m_dynamicParameters.MaxImageUnits,
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms});
|
m_dynamicParameters.MaxCombinedImageUniforms});
|
||||||
};
|
};
|
||||||
m_dynamicParameters.MaxVertexImageUniforms =
|
m_dynamicParameters.MaxVertexImageUniforms = clampStageImageUniforms(m_GLESCapabilities.MaxVertexImageUniforms);
|
||||||
clampStageImageUniforms(m_GLESCapabilities.MaxVertexImageUniforms);
|
|
||||||
m_dynamicParameters.MaxGeometryImageUniforms =
|
m_dynamicParameters.MaxGeometryImageUniforms =
|
||||||
clampStageImageUniforms(m_GLESCapabilities.MaxGeometryImageUniforms);
|
clampStageImageUniforms(m_GLESCapabilities.MaxGeometryImageUniforms);
|
||||||
m_dynamicParameters.MaxFragmentImageUniforms =
|
m_dynamicParameters.MaxFragmentImageUniforms =
|
||||||
@@ -1134,6 +1109,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
clampStageImageUniforms(m_GLESCapabilities.MaxComputeImageUniforms);
|
clampStageImageUniforms(m_GLESCapabilities.MaxComputeImageUniforms);
|
||||||
m_dynamicParameters.SupportsDistinctDepthStencilAttachments =
|
m_dynamicParameters.SupportsDistinctDepthStencilAttachments =
|
||||||
ProbeDistinctDepthStencilAttachments(DirectGLES::g_GLESFuncs);
|
ProbeDistinctDepthStencilAttachments(DirectGLES::g_GLESFuncs);
|
||||||
|
// SyncAttachmentObject routes a layered upload target to glFramebufferTextureLayer with the
|
||||||
|
// attachment's layer passed through, so this backend really does render to the layer it was
|
||||||
|
// given - provided the driver resolved the entry point at all.
|
||||||
|
m_dynamicParameters.SupportsPerLayerFramebufferAttachment =
|
||||||
|
DirectGLES::g_GLESFuncs.glFramebufferTextureLayer != nullptr;
|
||||||
m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers;
|
m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers;
|
||||||
m_dynamicParameters.MaxColorAttachments = m_GLESCapabilities.MaxColorAttachments;
|
m_dynamicParameters.MaxColorAttachments = m_GLESCapabilities.MaxColorAttachments;
|
||||||
m_dynamicParameters.MaxClipDistances = m_GLESCapabilities.MaxClipDistances;
|
m_dynamicParameters.MaxClipDistances = m_GLESCapabilities.MaxClipDistances;
|
||||||
@@ -1155,8 +1135,7 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const Float requiredMaxOffset =
|
const Float requiredMaxOffset =
|
||||||
0.5f - std::ldexp(1.0f, -m_GLESCapabilities.FragmentInterpolationOffsetBits);
|
0.5f - std::ldexp(1.0f, -m_GLESCapabilities.FragmentInterpolationOffsetBits);
|
||||||
if (m_GLESCapabilities.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
if (m_GLESCapabilities.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
m_dynamicParameters.MaxFragmentInterpolationOffset =
|
m_dynamicParameters.MaxFragmentInterpolationOffset = m_GLESCapabilities.MaxFragmentInterpolationOffset;
|
||||||
m_GLESCapabilities.MaxFragmentInterpolationOffset;
|
|
||||||
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
||||||
m_GLESCapabilities.FragmentInterpolationOffsetBits;
|
m_GLESCapabilities.FragmentInterpolationOffsetBits;
|
||||||
}
|
}
|
||||||
@@ -1165,9 +1144,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
m_GLESCapabilities.AliasedLineWidthRangeMax > 1.0f || m_GLESCapabilities.SmoothLineWidthRangeMax > 1.0f;
|
||||||
|
|
||||||
const auto containsAny = [](const String& haystack, std::initializer_list<const char*> needles) {
|
const auto containsAny = [](const String& haystack, std::initializer_list<const char*> needles) {
|
||||||
return std::any_of(needles.begin(), needles.end(), [&](const char* needle) {
|
return std::any_of(needles.begin(), needles.end(),
|
||||||
return haystack.find(needle) != String::npos;
|
[&](const char* needle) { return haystack.find(needle) != String::npos; });
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const String vendorAndRenderer =
|
const String vendorAndRenderer =
|
||||||
m_GLESCapabilities.GLESVendorString + " " + m_GLESCapabilities.GLESRendererString;
|
m_GLESCapabilities.GLESVendorString + " " + m_GLESCapabilities.GLESRendererString;
|
||||||
|
|||||||
@@ -1663,8 +1663,11 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
if (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end()) {
|
if (backendSamplerIt != SamplerImpl::g_backendSamplerObjects.end()) {
|
||||||
backendSamplerIt->second->Bind(unit);
|
backendSamplerIt->second->Bind(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Symmetric with the bind above: a sampler object left on the unit by an earlier
|
||||||
|
// draw keeps being applied, and on a multisample texture - which takes no sampler
|
||||||
|
// object at all - the draw is rejected outright.
|
||||||
|
SamplerImpl::UnbindSampler(unit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1844,6 +1847,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
backendObj = MakeShared<SamplerImpl::BackendSamplerObject>();
|
backendObj = MakeShared<SamplerImpl::BackendSamplerObject>();
|
||||||
}
|
}
|
||||||
backendObj->SyncToBackend(samplerObject);
|
backendObj->SyncToBackend(samplerObject);
|
||||||
|
// Syncing the object's parameters is not the same as putting it on the
|
||||||
|
// unit: without this the driver kept sampling with the texture's own
|
||||||
|
// parameters and every sampler object was inert.
|
||||||
|
backendObj->Bind(unit);
|
||||||
} else {
|
} else {
|
||||||
SamplerImpl::UnbindSampler(unit);
|
SamplerImpl::UnbindSampler(unit);
|
||||||
}
|
}
|
||||||
@@ -4431,6 +4438,40 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearNamedFramebufferiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLint* value) {
|
||||||
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||||
|
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||||
|
#endif
|
||||||
|
TextureImpl::SyncNeccessaryTextures();
|
||||||
|
RenderStateImpl::SyncRenderState();
|
||||||
|
|
||||||
|
SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true);
|
||||||
|
g_GLESFuncs.glClearBufferiv(buffer, drawbuffer, value);
|
||||||
|
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
|
||||||
|
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||||
|
});
|
||||||
|
|
||||||
|
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearNamedFramebufferuiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLuint* value) {
|
||||||
|
#if MOBILEGL_LOG_ACTIVE_LEVEL <= MOBILEGL_LOG_LEVEL_DEBUG && MOBILEGL_ENABLE_SCOPE_MARKER
|
||||||
|
DebugImpl::OpenGLScopeMarker marker(__func__);
|
||||||
|
#endif
|
||||||
|
TextureImpl::SyncNeccessaryTextures();
|
||||||
|
RenderStateImpl::SyncRenderState();
|
||||||
|
|
||||||
|
SyncAndBindFramebufferObject(framebuffer, FramebufferTarget::Draw, true);
|
||||||
|
g_GLESFuncs.glClearBufferuiv(buffer, drawbuffer, value);
|
||||||
|
DebugImpl::ErrorLopper::Loop([file = __FILE__, line = __LINE__](auto err) {
|
||||||
|
MGLOG_D("ES error (%s:%d): %s", file, line, MG_Util::ConvertGLEnumToString(err).c_str());
|
||||||
|
});
|
||||||
|
|
||||||
|
ForceBindCurrentFBO(FramebufferTarget::Draw);
|
||||||
|
}
|
||||||
|
|
||||||
static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) {
|
static SizeT AlignPixelRow(SizeT rowBytes, Int alignment) {
|
||||||
const SizeT resolvedAlignment = static_cast<SizeT>(std::max(alignment, 1));
|
const SizeT resolvedAlignment = static_cast<SizeT>(std::max(alignment, 1));
|
||||||
return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
return (rowBytes + resolvedAlignment - 1) & ~(resolvedAlignment - 1);
|
||||||
@@ -4532,14 +4573,27 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
Vector<Uint32> packed(outStencil.size(), 0);
|
Vector<Uint32> packed(outStencil.size(), 0);
|
||||||
ClearGLErrors();
|
ClearGLErrors();
|
||||||
g_GLESFuncs.glReadPixels(x, y, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, packed.data());
|
g_GLESFuncs.glReadPixels(x, y, width, height, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, packed.data());
|
||||||
|
if (g_GLESFuncs.glGetError() == GL_NO_ERROR) {
|
||||||
|
for (SizeT i = 0; i < outStencil.size(); ++i) {
|
||||||
|
outStencil[i] = static_cast<Uint8>(packed[i] & 0xFFu);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A DEPTH32F_STENCIL8 attachment rejects the 24_8 type: its packed layout is a float depth
|
||||||
|
// followed by a padded stencil byte, eight bytes per pixel with the index at offset 4.
|
||||||
|
Vector<Uint8> packed32f(outStencil.size() * 8u, 0);
|
||||||
|
ClearGLErrors();
|
||||||
|
g_GLESFuncs.glReadPixels(x, y, width, height, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV,
|
||||||
|
packed32f.data());
|
||||||
const GLenum packedError = g_GLESFuncs.glGetError();
|
const GLenum packedError = g_GLESFuncs.glGetError();
|
||||||
if (packedError != GL_NO_ERROR) {
|
if (packedError != GL_NO_ERROR) {
|
||||||
MGLOG_E("ReadPixels: neither GL_STENCIL_INDEX nor GL_DEPTH_STENCIL readback is available: %s",
|
MGLOG_E("ReadPixels: no stencil readback path is available: %s",
|
||||||
MG_Util::ConvertGLEnumToString(packedError).c_str());
|
MG_Util::ConvertGLEnumToString(packedError).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (SizeT i = 0; i < outStencil.size(); ++i) {
|
for (SizeT i = 0; i < outStencil.size(); ++i) {
|
||||||
outStencil[i] = static_cast<Uint8>(packed[i] & 0xFFu);
|
outStencil[i] = packed32f[i * 8u + 4u];
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4549,11 +4603,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// values themselves are always 8 bits.
|
// values themselves are always 8 bits.
|
||||||
static Bool ReadPixelsStencilViaNative(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type,
|
static Bool ReadPixelsStencilViaNative(GLint x, GLint y, GLsizei width, GLsizei height, GLenum type,
|
||||||
void* pixels) {
|
void* pixels) {
|
||||||
|
// GL 4.6 core 18.2.8: a stencil index is written unconverted into whichever integer width
|
||||||
|
// the client asked for, and converted to a float value for GL_FLOAT. The signed widths are
|
||||||
|
// as legal as the unsigned ones - the CTS reads stencil with GL_INT - and rejecting them
|
||||||
|
// here used to let the call fall through to a native ES read the driver refuses, after
|
||||||
|
// which nothing was written at all and the caller kept its zeros.
|
||||||
SizeT dstPixelBytes = 0;
|
SizeT dstPixelBytes = 0;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GL_UNSIGNED_BYTE: dstPixelBytes = sizeof(Uint8); break;
|
case GL_UNSIGNED_BYTE:
|
||||||
case GL_UNSIGNED_SHORT: dstPixelBytes = sizeof(Uint16); break;
|
case GL_BYTE: dstPixelBytes = sizeof(Uint8); break;
|
||||||
case GL_UNSIGNED_INT: dstPixelBytes = sizeof(Uint32); break;
|
case GL_UNSIGNED_SHORT:
|
||||||
|
case GL_SHORT: dstPixelBytes = sizeof(Uint16); break;
|
||||||
|
case GL_UNSIGNED_INT:
|
||||||
|
case GL_INT: dstPixelBytes = sizeof(Uint32); break;
|
||||||
|
case GL_FLOAT: dstPixelBytes = sizeof(GLfloat); break;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
@@ -4587,11 +4650,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
for (GLsizei col = 0; col < width; ++col) {
|
for (GLsizei col = 0; col < width; ++col) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GL_UNSIGNED_BYTE:
|
case GL_UNSIGNED_BYTE:
|
||||||
|
case GL_BYTE:
|
||||||
rowBuf[static_cast<SizeT>(col)] = srcRow[col];
|
rowBuf[static_cast<SizeT>(col)] = srcRow[col];
|
||||||
break;
|
break;
|
||||||
case GL_UNSIGNED_SHORT:
|
case GL_UNSIGNED_SHORT:
|
||||||
|
case GL_SHORT:
|
||||||
reinterpret_cast<Uint16*>(rowBuf.data())[col] = srcRow[col];
|
reinterpret_cast<Uint16*>(rowBuf.data())[col] = srcRow[col];
|
||||||
break;
|
break;
|
||||||
|
case GL_FLOAT:
|
||||||
|
reinterpret_cast<GLfloat*>(rowBuf.data())[col] = static_cast<GLfloat>(srcRow[col]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reinterpret_cast<Uint32*>(rowBuf.data())[col] = srcRow[col];
|
reinterpret_cast<Uint32*>(rowBuf.data())[col] = srcRow[col];
|
||||||
break;
|
break;
|
||||||
@@ -5284,10 +5352,58 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
const Bool applyPackImageParams = backendAttachTarget == GL_TEXTURE_3D ||
|
const Bool applyPackImageParams = backendAttachTarget == GL_TEXTURE_3D ||
|
||||||
backendAttachTarget == GL_TEXTURE_2D_ARRAY ||
|
backendAttachTarget == GL_TEXTURE_2D_ARRAY ||
|
||||||
backendAttachTarget == GL_TEXTURE_CUBE_MAP_ARRAY;
|
backendAttachTarget == GL_TEXTURE_CUBE_MAP_ARRAY;
|
||||||
// 3D/array images read back every slice, but the FBO path can only read one layer:
|
|
||||||
// multi-slice reads are served from the CPU shadow (slice-major, tight layout).
|
|
||||||
const GLsizei sliceCount = std::max(size.z(), 1);
|
const GLsizei sliceCount = std::max(size.z(), 1);
|
||||||
const Bool multiSlice = size.z() > 1;
|
const Bool multiSlice = size.z() > 1;
|
||||||
|
// A multi-slice read used to go to the CPU shadow outright, on the grounds that the
|
||||||
|
// scratch FBO can only expose one layer at a time. But the shadow only holds what was
|
||||||
|
// uploaded, so every slice that was rendered to came back stale - which is exactly what
|
||||||
|
// a layered framebuffer produces, and what textures_storage_multisample_3d_* checks.
|
||||||
|
// Attach the layers one at a time instead and read each off the GPU, keeping the shadow
|
||||||
|
// for the formats the FBO cannot represent at all.
|
||||||
|
if (multiSlice && tempFBOComplete &&
|
||||||
|
(backendAttachTarget == GL_TEXTURE_3D || backendAttachTarget == GL_TEXTURE_2D_ARRAY)) {
|
||||||
|
// Each slice is packed as its own 2D image, so the per-slice call must not apply
|
||||||
|
// GL_PACK_SKIP_IMAGES / GL_PACK_IMAGE_HEIGHT itself - this walks the destination
|
||||||
|
// over them, using the same layout StoreWideRowsToClient computes.
|
||||||
|
const auto packParams = MG_State::pGLContext->GetPixelStoreParameters(false);
|
||||||
|
const SizeT dstPixelBytes = GetReadbackDstPixelSize(conversionMapping, type);
|
||||||
|
const SizeT rowPixels =
|
||||||
|
static_cast<SizeT>(packParams.RowLength > 0 ? packParams.RowLength : size.x());
|
||||||
|
const SizeT alignment =
|
||||||
|
packParams.Alignment > 0 ? static_cast<SizeT>(packParams.Alignment) : SizeT{1};
|
||||||
|
const SizeT dstRowStride = (rowPixels * dstPixelBytes + alignment - 1) / alignment * alignment;
|
||||||
|
const SizeT imageRows = applyPackImageParams && packParams.ImageHeight > 0
|
||||||
|
? static_cast<SizeT>(packParams.ImageHeight)
|
||||||
|
: static_cast<SizeT>(size.y());
|
||||||
|
const SizeT dstImageStride = imageRows * dstRowStride;
|
||||||
|
const SizeT skipImages =
|
||||||
|
applyPackImageParams ? static_cast<SizeT>(std::max(packParams.SkipImages, 0)) : SizeT{0};
|
||||||
|
|
||||||
|
Bool allSlicesRead = true;
|
||||||
|
for (GLsizei slice = 0; slice < sliceCount; ++slice) {
|
||||||
|
ScratchFBOImpl::EnsureColorAttachmentLayer(tempFB, GL_READ_FRAMEBUFFER, backendTexId, level,
|
||||||
|
slice);
|
||||||
|
if (g_GLESFuncs.glCheckFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
allSlicesRead = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const SizeT sliceOffset = (skipImages + static_cast<SizeT>(slice)) * dstImageStride;
|
||||||
|
void* sliceDst = static_cast<Uint8*>(pixels) + sliceOffset;
|
||||||
|
if (!ReadPixelsViaFormatConversion(0, 0, size.x(), size.y(), format, type, sliceDst,
|
||||||
|
/*honorPackImageParams=*/false,
|
||||||
|
/*applyFixedPointReadClamp=*/false)) {
|
||||||
|
allSlicesRead = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Leave the scratch FBO on layer 0 so the single-slice paths below see what they
|
||||||
|
// set up.
|
||||||
|
ScratchFBOImpl::EnsureColorAttachmentLayer(tempFB, GL_READ_FRAMEBUFFER, backendTexId, level, 0);
|
||||||
|
if (allSlicesRead) {
|
||||||
|
MGLOG_D("GetTexImage: finished %d slices via per-layer readback", sliceCount);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (multiSlice &&
|
if (multiSlice &&
|
||||||
GetTexImageViaShadowConversion(textureMipmapObject,
|
GetTexImageViaShadowConversion(textureMipmapObject,
|
||||||
MG_Util::ConvertGLEnumToTextureUploadTarget(target), level, size.x(),
|
MG_Util::ConvertGLEnumToTextureUploadTarget(target), level, size.x(),
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void ClearNamedFramebufferiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void ClearNamedFramebufferuiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||||
GLint dstY1, GLbitfield mask, GLenum filter);
|
GLint dstY1, GLbitfield mask, GLenum filter);
|
||||||
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
void BlitNamedFramebuffer(const SharedPtr<MG_State::GLState::FramebufferObject>& readFramebuffer,
|
||||||
|
|||||||
@@ -2479,7 +2479,10 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMultisampleTarget && m_cacheBorderColor != stateTextureObject->GetBorderColor()) {
|
// GL_TEXTURE_BORDER_COLOR needs ES 3.2 or EXT/OES_texture_border_clamp; on a driver
|
||||||
|
// without it every such call is INVALID_ENUM, so the parameter is simply not synced.
|
||||||
|
if (!isMultisampleTarget && g_GLESCapabilities.SupportsTextureBorderClamp &&
|
||||||
|
m_cacheBorderColor != stateTextureObject->GetBorderColor()) {
|
||||||
const auto& borderColor = stateTextureObject->GetBorderColor();
|
const auto& borderColor = stateTextureObject->GetBorderColor();
|
||||||
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
|
GLfloat borderColorArray[4] = {borderColor.x(), borderColor.y(), borderColor.z(), borderColor.w()};
|
||||||
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
|
g_GLESFuncs.glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, borderColorArray);
|
||||||
@@ -3755,6 +3758,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
m_cacheSamplerParameters.maxAnisotropy = samplerParams.maxAnisotropy;
|
m_cacheSamplerParameters.maxAnisotropy = samplerParams.maxAnisotropy;
|
||||||
}
|
}
|
||||||
|
if (m_cacheSamplerParameters.borderColor != samplerParams.borderColor) {
|
||||||
|
// Same gate as the texture-side border colour above.
|
||||||
|
if (g_GLESCapabilities.SupportsTextureBorderClamp && g_GLESFuncs.glSamplerParameterfv) {
|
||||||
|
const GLfloat borderColorArray[4] = {
|
||||||
|
samplerParams.borderColor.x(), samplerParams.borderColor.y(),
|
||||||
|
samplerParams.borderColor.z(), samplerParams.borderColor.w()};
|
||||||
|
g_GLESFuncs.glSamplerParameterfv(m_backendSamplerId, GL_TEXTURE_BORDER_COLOR, borderColorArray);
|
||||||
|
}
|
||||||
|
m_cacheSamplerParameters.borderColor = samplerParams.borderColor;
|
||||||
|
}
|
||||||
#undef SYNC_SAMPLER_PARAM_IF_CHANGED
|
#undef SYNC_SAMPLER_PARAM_IF_CHANGED
|
||||||
m_isInitialized = true;
|
m_isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,13 +41,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Bool IsLayeredTarget(TextureTarget target) {
|
Bool IsLayeredTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture3D || target == TextureTarget::Texture1DArray ||
|
return target == TextureTarget::Texture3D || target == TextureTarget::Texture1DArray ||
|
||||||
target == TextureTarget::Texture2DArray || target == TextureTarget::TextureCubeMap ||
|
target == TextureTarget::Texture2DArray || target == TextureTarget::TextureCubeMap ||
|
||||||
target == TextureTarget::TextureCubeMapArray ||
|
target == TextureTarget::TextureCubeMapArray || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsMultisampleTarget(TextureTarget target) {
|
Bool IsMultisampleTarget(TextureTarget target) {
|
||||||
return target == TextureTarget::Texture2DMultisample ||
|
return target == TextureTarget::Texture2DMultisample || target == TextureTarget::Texture2DMultisampleArray;
|
||||||
target == TextureTarget::Texture2DMultisampleArray;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool IsTextureBufferTarget(TextureTarget target) {
|
Bool IsTextureBufferTarget(TextureTarget target) {
|
||||||
@@ -59,8 +57,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
GLenum normalizedInternalFormat = glFormat;
|
GLenum normalizedInternalFormat = glFormat;
|
||||||
GLenum imageFormat = GL_RGBA;
|
GLenum imageFormat = GL_RGBA;
|
||||||
GLenum imageType = GL_UNSIGNED_BYTE;
|
GLenum imageType = GL_UNSIGNED_BYTE;
|
||||||
MG_Util::TextureFormatProcessor::NormalizePixelFormat(
|
MG_Util::TextureFormatProcessor::NormalizePixelFormat(glFormat, PixelFormatNormalizeOptionBit::None,
|
||||||
glFormat, PixelFormatNormalizeOptionBit::None, &normalizedInternalFormat, &imageFormat, &imageType);
|
&normalizedInternalFormat, &imageFormat, &imageType);
|
||||||
return imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || imageFormat == GL_RGB_INTEGER ||
|
return imageFormat == GL_RED_INTEGER || imageFormat == GL_RG_INTEGER || imageFormat == GL_RGB_INTEGER ||
|
||||||
imageFormat == GL_RGBA_INTEGER;
|
imageFormat == GL_RGBA_INTEGER;
|
||||||
}
|
}
|
||||||
@@ -81,8 +79,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
return caps;
|
return caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatCapabilityFlags BuildVulkanCaps(TextureInternalFormat logicalFormat,
|
FormatCapabilityFlags BuildVulkanCaps(TextureInternalFormat logicalFormat, TextureTarget target,
|
||||||
TextureTarget target,
|
|
||||||
VkFormatFeatureFlags features) {
|
VkFormatFeatureFlags features) {
|
||||||
FormatCapabilityFlags caps;
|
FormatCapabilityFlags caps;
|
||||||
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
const Bool isDepth = MG_Util::IsDepthFormatInternalFormat(logicalFormat);
|
||||||
@@ -101,8 +98,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
const Bool sampled = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
|
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 linearFilter = (features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0;
|
||||||
const Bool colorRenderable = (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
const Bool colorRenderable = (features & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) != 0;
|
||||||
const Bool depthStencilRenderable =
|
const Bool depthStencilRenderable = (features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
|
||||||
(features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0;
|
|
||||||
const Bool renderable = (isDepth || isStencil) ? depthStencilRenderable : colorRenderable;
|
const Bool renderable = (isDepth || isStencil) ? depthStencilRenderable : colorRenderable;
|
||||||
|
|
||||||
if (sampled || renderable) {
|
if (sampled || renderable) {
|
||||||
@@ -199,21 +195,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
Bool HasNewCaveatFormatCaps(FormatCapabilityFlags nativeCaps, FormatCapabilityFlags fallbackCaps) {
|
Bool HasNewCaveatFormatCaps(FormatCapabilityFlags nativeCaps, FormatCapabilityFlags fallbackCaps) {
|
||||||
for (FormatCapability capability : kReportedFormatCapabilities) {
|
for (FormatCapability capability : kReportedFormatCapabilities) {
|
||||||
if (HasFormatCapability(fallbackCaps, capability) &&
|
if (HasFormatCapability(fallbackCaps, capability) && !HasFormatCapability(nativeCaps, capability)) {
|
||||||
!HasFormatCapability(nativeCaps, capability)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogVulkanFormatCaveat(TextureInternalFormat logicalFormat,
|
void LogVulkanFormatCaveat(TextureInternalFormat logicalFormat, SizeT targetIndex,
|
||||||
SizeT targetIndex,
|
|
||||||
TextureInternalFormat fallbackFormat) {
|
TextureInternalFormat fallbackFormat) {
|
||||||
MGLOG_D("Caveat: %s %s not fully supported. Reason: native Vulkan format is not fully supported. Fallback: %s",
|
MGLOG_D(
|
||||||
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
"Caveat: %s %s not fully supported. Reason: native Vulkan format is not fully supported. Fallback: %s",
|
||||||
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
GetFormatCapabilityTargetName(targetIndex).c_str(),
|
||||||
MG_Util::ConvertTextureInternalFormatToString(fallbackFormat).c_str());
|
MG_Util::ConvertTextureInternalFormatToString(logicalFormat).c_str(),
|
||||||
|
MG_Util::ConvertTextureInternalFormatToString(fallbackFormat).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Int> BuildSampleCounts(Int maxSamples) {
|
Vector<Int> BuildSampleCounts(Int maxSamples) {
|
||||||
@@ -257,15 +252,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
|
for (SizeT targetIndex = 0; targetIndex < kFormatCapabilityTextureTargetCount; ++targetIndex) {
|
||||||
const auto target = static_cast<TextureTarget>(targetIndex);
|
const auto target = static_cast<TextureTarget>(targetIndex);
|
||||||
const VkFormatFeatureFlags nativeFeatures =
|
const VkFormatFeatureFlags nativeFeatures = IsTextureBufferTarget(target)
|
||||||
IsTextureBufferTarget(target) ? nativeProperties.bufferFeatures
|
? nativeProperties.bufferFeatures
|
||||||
: nativeProperties.optimalTilingFeatures;
|
: nativeProperties.optimalTilingFeatures;
|
||||||
FormatCapabilityFlags nativeCaps = BuildVulkanCaps(logicalFormat, target, nativeFeatures);
|
FormatCapabilityFlags nativeCaps = BuildVulkanCaps(logicalFormat, target, nativeFeatures);
|
||||||
cache.FullCaps[targetIndex][formatIndex] |= nativeCaps;
|
cache.FullCaps[targetIndex][formatIndex] |= nativeCaps;
|
||||||
|
|
||||||
const VkFormatFeatureFlags fallbackFeatures =
|
const VkFormatFeatureFlags fallbackFeatures = IsTextureBufferTarget(target)
|
||||||
IsTextureBufferTarget(target) ? fallbackProperties.bufferFeatures
|
? fallbackProperties.bufferFeatures
|
||||||
: fallbackProperties.optimalTilingFeatures;
|
: fallbackProperties.optimalTilingFeatures;
|
||||||
FormatCapabilityFlags fallbackCaps = BuildVulkanCaps(logicalFormat, target, fallbackFeatures);
|
FormatCapabilityFlags fallbackCaps = BuildVulkanCaps(logicalFormat, target, fallbackFeatures);
|
||||||
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
||||||
cache.CaveatCaps[targetIndex][formatIndex] |= fallbackCaps;
|
cache.CaveatCaps[targetIndex][formatIndex] |= fallbackCaps;
|
||||||
@@ -300,9 +295,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
cache.FullCaps[renderbufferTargetIndex][formatIndex] |= renderbufferCaps;
|
cache.FullCaps[renderbufferTargetIndex][formatIndex] |= renderbufferCaps;
|
||||||
|
|
||||||
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
if (fallbackFormat != VK_FORMAT_UNDEFINED && fallbackFormat != nativeFormat) {
|
||||||
FormatCapabilityFlags fallbackRenderbufferCaps =
|
FormatCapabilityFlags fallbackRenderbufferCaps = BuildVulkanCaps(
|
||||||
BuildVulkanCaps(logicalFormat, TextureTarget::Texture2D,
|
logicalFormat, TextureTarget::Texture2D, fallbackProperties.optimalTilingFeatures);
|
||||||
fallbackProperties.optimalTilingFeatures);
|
|
||||||
fallbackRenderbufferCaps &= FormatCapability::Creatable;
|
fallbackRenderbufferCaps &= FormatCapability::Creatable;
|
||||||
if ((fallbackProperties.optimalTilingFeatures &
|
if ((fallbackProperties.optimalTilingFeatures &
|
||||||
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) !=
|
(VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) !=
|
||||||
@@ -311,8 +305,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
fallbackRenderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
|
fallbackRenderbufferCaps |= FormatCapability::MultisampleRenderbuffer;
|
||||||
}
|
}
|
||||||
cache.CaveatCaps[renderbufferTargetIndex][formatIndex] |= fallbackRenderbufferCaps;
|
cache.CaveatCaps[renderbufferTargetIndex][formatIndex] |= fallbackRenderbufferCaps;
|
||||||
if (fallbackLogicalFormat &&
|
if (fallbackLogicalFormat && HasNewCaveatFormatCaps(renderbufferCaps, fallbackRenderbufferCaps)) {
|
||||||
HasNewCaveatFormatCaps(renderbufferCaps, fallbackRenderbufferCaps)) {
|
|
||||||
LogVulkanFormatCaveat(logicalFormat, renderbufferTargetIndex, *fallbackLogicalFormat);
|
LogVulkanFormatCaveat(logicalFormat, renderbufferTargetIndex, *fallbackLogicalFormat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -329,14 +322,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
|
|
||||||
void PopulateFormatCapabilities(VkPhysicalDevice physicalDevice,
|
void PopulateFormatCapabilities(VkPhysicalDevice physicalDevice,
|
||||||
PFN_vkGetPhysicalDeviceFormatProperties getFormatProperties,
|
PFN_vkGetPhysicalDeviceFormatProperties getFormatProperties,
|
||||||
const MG_External::VulkanCapabilities& capabilities,
|
const MG_External::VulkanCapabilities& capabilities, FormatCapabilityCache& cache) {
|
||||||
FormatCapabilityCache& cache) {
|
|
||||||
PopulateFormatCapabilitiesImpl(physicalDevice, getFormatProperties, capabilities, cache);
|
PopulateFormatCapabilitiesImpl(physicalDevice, getFormatProperties, capabilities, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
|
BackendObject_DirectVulkan::~BackendObject_DirectVulkan() = default;
|
||||||
|
|
||||||
BackendObject_DirectVulkan::BackendObject_DirectVulkan(): m_rendererInfo{GetRendererIdentity()} {}
|
BackendObject_DirectVulkan::BackendObject_DirectVulkan() : m_rendererInfo{GetRendererIdentity()} {}
|
||||||
|
|
||||||
Bool BackendObject_DirectVulkan::InitWindowSurface() {
|
Bool BackendObject_DirectVulkan::InitWindowSurface() {
|
||||||
if (!m_windowHandle.Handle) {
|
if (!m_windowHandle.Handle) {
|
||||||
@@ -410,10 +402,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MGLOG_E("DirectVulkan backend not initialized");
|
MGLOG_E("DirectVulkan backend not initialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!handle.Handle || (handle.Backend != WindowBackend::Android &&
|
if (!handle.Handle || (handle.Backend != WindowBackend::Android && handle.Backend != WindowBackend::X11 &&
|
||||||
handle.Backend != WindowBackend::X11 &&
|
handle.Backend != WindowBackend::MetalLayer && handle.Backend != WindowBackend::Win32)) {
|
||||||
handle.Backend != WindowBackend::MetalLayer &&
|
|
||||||
handle.Backend != WindowBackend::Win32)) {
|
|
||||||
MGLOG_E("DirectVulkan backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
MGLOG_E("DirectVulkan backend only supports Android, X11, CAMetalLayer, and Win32 native windows");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -504,37 +494,32 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
.RendererName = "Magma",
|
.RendererName = "Magma",
|
||||||
.BackendName = "Direct (Vulkan)",
|
.BackendName = "Direct (Vulkan)",
|
||||||
.ExtraVendor = Nullopt,
|
.ExtraVendor = Nullopt,
|
||||||
.RendererGLInfo =
|
.RendererGLInfo = {.TargetGLVersion = {4, 0, 0},
|
||||||
{
|
.TargetGLSLVersion = {4, 6, 0},
|
||||||
.TargetGLVersion = {3, 3, 0},
|
// Baseline advertisement (no shader subgroup, no timer queries); a
|
||||||
.TargetGLSLVersion = {4, 6, 0},
|
// live backend reconciles its copy in UpdateAdvertisedExtensions.
|
||||||
// Baseline advertisement (no shader subgroup, no timer queries); a
|
.Extensions = BuildAdvertisedExtensions(false, false, false),
|
||||||
// live backend reconciles its copy in UpdateAdvertisedExtensions.
|
.IsCompatibilityProfile = false},
|
||||||
.Extensions = BuildAdvertisedExtensions(false, false, false),
|
|
||||||
.IsCompatibilityProfile = false
|
|
||||||
},
|
|
||||||
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}};
|
.StaticBackendCapability = {.AllowVSOnlyPrograms = false}};
|
||||||
return rendererInfo;
|
return rendererInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<GLExtension> BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported,
|
Vector<GLExtension> BuildAdvertisedExtensions(Bool shaderSubgroupSupported, Bool timerQueriesSupported,
|
||||||
Bool anisotropicFilteringSupported) {
|
Bool anisotropicFilteringSupported) {
|
||||||
Vector<GLExtension> extensions = {V_OpenGL30, V_OpenGL31, V_OpenGL32,
|
Vector<GLExtension> extensions = {
|
||||||
V_OpenGL33, E_GL_ARB_draw_buffers_blend, E_GL_ARB_compute_shader,
|
V_OpenGL30, V_OpenGL31, V_OpenGL32, V_OpenGL33, V_OpenGL40, E_GL_ARB_draw_buffers_blend,
|
||||||
E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
E_GL_ARB_compute_shader, E_GL_ARB_shader_storage_buffer_object, E_GL_ARB_shader_image_load_store,
|
||||||
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object,
|
E_GL_ARB_program_interface_query, E_GL_ARB_framebuffer_object, E_GL_ARB_multi_draw_indirect,
|
||||||
E_GL_ARB_multi_draw_indirect, E_GL_ARB_indirect_parameters,
|
E_GL_ARB_indirect_parameters, E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
||||||
E_GL_EXT_framebuffer_object, E_GL_ARB_depth_texture, E_GL_ARB_buffer_storage,
|
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample, E_GL_ARB_texture_multisample,
|
||||||
E_GL_ARB_texture_storage, E_GL_ARB_texture_storage_multisample,
|
E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access, E_GL_ARB_shader_draw_parameters,
|
||||||
E_GL_ARB_texture_multisample, E_GL_ARB_clear_texture, E_GL_ARB_direct_state_access,
|
E_GL_ARB_gpu_shader_int64, E_GL_KHR_debug, E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind,
|
||||||
E_GL_ARB_shader_draw_parameters, E_GL_ARB_gpu_shader_int64, E_GL_KHR_debug,
|
E_GL_ARB_shading_language_420pack, E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size,
|
||||||
E_GL_ARB_gpu_shader5, E_GL_ARB_multi_bind, E_GL_ARB_shading_language_420pack,
|
E_GL_ARB_explicit_attrib_location,
|
||||||
E_GL_ARB_vertex_attrib_binding, E_GL_ARB_shader_image_size,
|
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
||||||
E_GL_ARB_explicit_attrib_location,
|
// extension explicitly permits. It is also the only thing that
|
||||||
// Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the
|
// exposes glProgramParameteri before GL 4.1.
|
||||||
// extension explicitly permits. It is also the only thing that
|
E_GL_ARB_get_program_binary};
|
||||||
// exposes glProgramParameteri before GL 4.1.
|
|
||||||
E_GL_ARB_get_program_binary};
|
|
||||||
if (shaderSubgroupSupported && !MG_Config::Features.DisableSubgroup) {
|
if (shaderSubgroupSupported && !MG_Config::Features.DisableSubgroup) {
|
||||||
extensions.push_back(E_GL_KHR_shader_subgroup);
|
extensions.push_back(E_GL_KHR_shader_subgroup);
|
||||||
}
|
}
|
||||||
@@ -597,6 +582,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
funcsTable.GL.ClearBufferiv = ClearBufferiv;
|
||||||
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
|
funcsTable.GL.ClearNamedFramebufferfv = ClearNamedFramebufferfv;
|
||||||
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
|
funcsTable.GL.ClearNamedFramebufferfi = ClearNamedFramebufferfi;
|
||||||
|
funcsTable.GL.ClearNamedFramebufferiv = ClearNamedFramebufferiv;
|
||||||
|
funcsTable.GL.ClearNamedFramebufferuiv = ClearNamedFramebufferuiv;
|
||||||
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
funcsTable.GL.BlitFramebuffer = BlitFramebuffer;
|
||||||
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
|
funcsTable.GL.BlitNamedFramebuffer = BlitNamedFramebuffer;
|
||||||
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
|
funcsTable.GL.CopyTexImage2D = CopyTexImage2D;
|
||||||
@@ -729,7 +716,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// rather than a maximum the sampler manager will never apply.
|
// rather than a maximum the sampler manager will never apply.
|
||||||
m_dynamicParameters.MaxTextureMaxAnisotropy =
|
m_dynamicParameters.MaxTextureMaxAnisotropy =
|
||||||
(pVulkanRenderer && pVulkanRenderer->IsSamplerAnisotropySupported()) ? m_vulkanCaps.MaxSamplerAnisotropy
|
(pVulkanRenderer && pVulkanRenderer->IsSamplerAnisotropySupported()) ? m_vulkanCaps.MaxSamplerAnisotropy
|
||||||
: 1.0f;
|
: 1.0f;
|
||||||
m_dynamicParameters.SmoothLineWidthRangeMin = m_vulkanCaps.SmoothLineWidthRangeMin;
|
m_dynamicParameters.SmoothLineWidthRangeMin = m_vulkanCaps.SmoothLineWidthRangeMin;
|
||||||
m_dynamicParameters.SmoothLineWidthRangeMax = m_vulkanCaps.SmoothLineWidthRangeMax;
|
m_dynamicParameters.SmoothLineWidthRangeMax = m_vulkanCaps.SmoothLineWidthRangeMax;
|
||||||
m_dynamicParameters.SmoothLineWidthGranularity = m_vulkanCaps.SmoothLineWidthGranularity;
|
m_dynamicParameters.SmoothLineWidthGranularity = m_vulkanCaps.SmoothLineWidthGranularity;
|
||||||
@@ -750,8 +737,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.MaxIntegerSamples = m_vulkanCaps.MaxIntegerSamples;
|
m_dynamicParameters.MaxIntegerSamples = m_vulkanCaps.MaxIntegerSamples;
|
||||||
m_dynamicParameters.MaxSamples = m_vulkanCaps.MaxSamples;
|
m_dynamicParameters.MaxSamples = m_vulkanCaps.MaxSamples;
|
||||||
m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords;
|
m_dynamicParameters.MaxSampleMaskWords = m_vulkanCaps.MaxSampleMaskWords;
|
||||||
const Int maxSupportedTextureUnits =
|
const Int maxSupportedTextureUnits = static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_TEXTURE_IMAGE_UNITS);
|
|
||||||
// GL_MAX_TEXTURE_IMAGE_UNITS is a *per-stage* sampler limit. Adreno/Qualcomm report a huge
|
// GL_MAX_TEXTURE_IMAGE_UNITS is a *per-stage* sampler limit. Adreno/Qualcomm report a huge
|
||||||
// maxPerStageDescriptorSampledImages (descriptor-indexing scale), so clamping it only to our
|
// maxPerStageDescriptorSampledImages (descriptor-indexing scale), so clamping it only to our
|
||||||
// combined array capacity (192) still advertises 192 per stage. Host code treats this value as
|
// combined array capacity (192) still advertises 192 per stage. Host code treats this value as
|
||||||
@@ -761,8 +747,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
// limits while keeping the combined limit at our texture-unit array capacity.
|
// limits while keeping the combined limit at our texture-unit array capacity.
|
||||||
constexpr Int maxPerStageTextureUnits =
|
constexpr Int maxPerStageTextureUnits =
|
||||||
static_cast<Int>(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS);
|
static_cast<Int>(MG_State::GLState::TextureState::MAX_PER_STAGE_TEXTURE_IMAGE_UNITS);
|
||||||
m_dynamicParameters.MaxTextureImageUnits =
|
m_dynamicParameters.MaxTextureImageUnits = std::min(m_vulkanCaps.MaxTextureImageUnits, maxPerStageTextureUnits);
|
||||||
std::min(m_vulkanCaps.MaxTextureImageUnits, maxPerStageTextureUnits);
|
|
||||||
m_dynamicParameters.MaxVertexTextureImageUnits =
|
m_dynamicParameters.MaxVertexTextureImageUnits =
|
||||||
std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxPerStageTextureUnits);
|
std::min(m_vulkanCaps.MaxVertexTextureImageUnits, maxPerStageTextureUnits);
|
||||||
m_dynamicParameters.MaxComputeTextureImageUnits =
|
m_dynamicParameters.MaxComputeTextureImageUnits =
|
||||||
@@ -771,9 +756,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
std::min(m_vulkanCaps.MaxCombinedTextureImageUnits, maxSupportedTextureUnits);
|
std::min(m_vulkanCaps.MaxCombinedTextureImageUnits, maxSupportedTextureUnits);
|
||||||
// Never advertise more attributes than the state layer can store: the current-value array and
|
// Never advertise more attributes than the state layer can store: the current-value array and
|
||||||
// the Uint32 attribute masks the draw path passes around are both bounded by MAX_VERTEX_ATTRIBS.
|
// the Uint32 attribute masks the draw path passes around are both bounded by MAX_VERTEX_ATTRIBS.
|
||||||
m_dynamicParameters.MaxVertexAttribs =
|
m_dynamicParameters.MaxVertexAttribs = std::min(
|
||||||
std::min(m_vulkanCaps.MaxVertexAttribs,
|
m_vulkanCaps.MaxVertexAttribs, static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
||||||
static_cast<Int>(MG_State::GLState::VertexArrayObject::MAX_VERTEX_ATTRIBS));
|
|
||||||
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_vulkanCaps.MaxComputeShaderStorageBlocks;
|
m_dynamicParameters.MaxComputeShaderStorageBlocks = m_vulkanCaps.MaxComputeShaderStorageBlocks;
|
||||||
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_vulkanCaps.MaxCombinedShaderStorageBlocks;
|
m_dynamicParameters.MaxCombinedShaderStorageBlocks = m_vulkanCaps.MaxCombinedShaderStorageBlocks;
|
||||||
m_dynamicParameters.MaxComputeUniformBlocks = m_vulkanCaps.MaxComputeUniformBlocks;
|
m_dynamicParameters.MaxComputeUniformBlocks = m_vulkanCaps.MaxComputeUniformBlocks;
|
||||||
@@ -783,8 +767,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
m_dynamicParameters.TextureBufferOffsetAlignment = m_vulkanCaps.TextureBufferOffsetAlignment;
|
||||||
m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings;
|
m_dynamicParameters.MaxUniformBufferBindings = m_vulkanCaps.MaxUniformBufferBindings;
|
||||||
m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize;
|
m_dynamicParameters.MaxUniformBlockSize = m_vulkanCaps.MaxUniformBlockSize;
|
||||||
m_dynamicParameters.MaxImageUnits =
|
m_dynamicParameters.MaxImageUnits = std::max(std::min(m_vulkanCaps.MaxImageUnits, maxSupportedTextureUnits), 0);
|
||||||
std::max(std::min(m_vulkanCaps.MaxImageUnits, maxSupportedTextureUnits), 0);
|
|
||||||
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_vulkanCaps.MaxCombinedImageUniforms, 0);
|
m_dynamicParameters.MaxCombinedImageUniforms = std::max(m_vulkanCaps.MaxCombinedImageUniforms, 0);
|
||||||
const Int maxPerStageImageUniforms =
|
const Int maxPerStageImageUniforms =
|
||||||
std::min(m_dynamicParameters.MaxImageUnits, m_dynamicParameters.MaxCombinedImageUniforms);
|
std::min(m_dynamicParameters.MaxImageUnits, m_dynamicParameters.MaxCombinedImageUniforms);
|
||||||
@@ -801,8 +784,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_vulkanCaps.SupportsFragmentStoresAndAtomics ? maxPerStageImageUniforms : 0;
|
m_vulkanCaps.SupportsFragmentStoresAndAtomics ? maxPerStageImageUniforms : 0;
|
||||||
m_dynamicParameters.MaxComputeImageUniforms =
|
m_dynamicParameters.MaxComputeImageUniforms =
|
||||||
std::min(std::max(m_vulkanCaps.MaxComputeImageUniforms, 0), maxPerStageImageUniforms);
|
std::min(std::max(m_vulkanCaps.MaxComputeImageUniforms, 0), maxPerStageImageUniforms);
|
||||||
const Int maxSupportedDrawBuffers =
|
const Int maxSupportedDrawBuffers = static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);
|
||||||
static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS);
|
|
||||||
m_dynamicParameters.MaxDrawBuffers = std::min(m_vulkanCaps.MaxDrawBuffers, maxSupportedDrawBuffers);
|
m_dynamicParameters.MaxDrawBuffers = std::min(m_vulkanCaps.MaxDrawBuffers, maxSupportedDrawBuffers);
|
||||||
m_dynamicParameters.MaxColorAttachments = std::min(m_vulkanCaps.MaxColorAttachments, maxSupportedDrawBuffers);
|
m_dynamicParameters.MaxColorAttachments = std::min(m_vulkanCaps.MaxColorAttachments, maxSupportedDrawBuffers);
|
||||||
m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances;
|
m_dynamicParameters.MaxClipDistances = m_vulkanCaps.MaxClipDistances;
|
||||||
@@ -821,12 +803,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
m_dynamicParameters.FragmentInterpolationOffsetBits = 4;
|
||||||
if (m_vulkanCaps.FragmentInterpolationOffsetBits >= 4 &&
|
if (m_vulkanCaps.FragmentInterpolationOffsetBits >= 4 &&
|
||||||
std::isfinite(m_vulkanCaps.MaxFragmentInterpolationOffset)) {
|
std::isfinite(m_vulkanCaps.MaxFragmentInterpolationOffset)) {
|
||||||
const Float requiredMaxOffset =
|
const Float requiredMaxOffset = 0.5f - std::ldexp(1.0f, -m_vulkanCaps.FragmentInterpolationOffsetBits);
|
||||||
0.5f - std::ldexp(1.0f, -m_vulkanCaps.FragmentInterpolationOffsetBits);
|
|
||||||
if (m_vulkanCaps.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
if (m_vulkanCaps.MaxFragmentInterpolationOffset >= requiredMaxOffset) {
|
||||||
m_dynamicParameters.MaxFragmentInterpolationOffset = m_vulkanCaps.MaxFragmentInterpolationOffset;
|
m_dynamicParameters.MaxFragmentInterpolationOffset = m_vulkanCaps.MaxFragmentInterpolationOffset;
|
||||||
m_dynamicParameters.FragmentInterpolationOffsetBits =
|
m_dynamicParameters.FragmentInterpolationOffsetBits = m_vulkanCaps.FragmentInterpolationOffsetBits;
|
||||||
m_vulkanCaps.FragmentInterpolationOffsetBits;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
m_dynamicParameters.SupportsWideLines = m_vulkanCaps.SupportsWideLines;
|
||||||
@@ -835,7 +815,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
if (m_vulkanCaps.SupportsShaderSubgroup) {
|
||||||
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
|
m_dynamicParameters.SubgroupSize = m_vulkanCaps.SubgroupSize;
|
||||||
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
|
m_dynamicParameters.SubgroupSupportedStages = mapShaderStages(m_vulkanCaps.SubgroupSupportedStages);
|
||||||
m_dynamicParameters.SubgroupSupportedFeatures = mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
|
m_dynamicParameters.SubgroupSupportedFeatures =
|
||||||
|
mapSubgroupFeatures(m_vulkanCaps.SubgroupSupportedOperations);
|
||||||
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
|
m_dynamicParameters.SubgroupQuadOperationsInAllStages = m_vulkanCaps.SubgroupQuadOperationsInAllStages;
|
||||||
} else {
|
} else {
|
||||||
m_dynamicParameters.SubgroupSize = 0;
|
m_dynamicParameters.SubgroupSize = 0;
|
||||||
@@ -845,8 +826,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) {
|
if (m_dynamicParameters.MaxShaderStorageBlockSize != m_vulkanCaps.MaxShaderStorageBlockSize) {
|
||||||
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
|
MGLOG_I("DirectVulkan: clamped GL_MAX_SHADER_STORAGE_BLOCK_SIZE from %zu to %zu",
|
||||||
m_vulkanCaps.MaxShaderStorageBlockSize,
|
m_vulkanCaps.MaxShaderStorageBlockSize, m_dynamicParameters.MaxShaderStorageBlockSize);
|
||||||
m_dynamicParameters.MaxShaderStorageBlockSize);
|
|
||||||
}
|
}
|
||||||
switch (m_vulkanCaps.VendorId) {
|
switch (m_vulkanCaps.VendorId) {
|
||||||
case 0x5143u: // VK_VENDOR_ID: Qualcomm
|
case 0x5143u: // VK_VENDOR_ID: Qualcomm
|
||||||
|
|||||||
@@ -440,6 +440,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
pVulkanRenderer->ClearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value);
|
pVulkanRenderer->ClearNamedFramebufferfv(framebuffer, buffer, drawbuffer, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearNamedFramebufferiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
|
GLint drawbuffer, const GLint* value) {
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferiv called with null VulkanRenderer");
|
||||||
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearNamedFramebufferiv called with null GL context");
|
||||||
|
pVulkanRenderer->ClearNamedFramebufferiv(framebuffer, buffer, drawbuffer, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearNamedFramebufferuiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
|
GLint drawbuffer, const GLuint* value) {
|
||||||
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferuiv called with null VulkanRenderer");
|
||||||
|
MOBILEGL_ASSERT(MG_State::pGLContext, "DirectVulkan::ClearNamedFramebufferuiv called with null GL context");
|
||||||
|
pVulkanRenderer->ClearNamedFramebufferuiv(framebuffer, buffer, drawbuffer, value);
|
||||||
|
}
|
||||||
|
|
||||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
GLint drawbuffer, GLfloat depth, GLint stencil) {
|
GLint drawbuffer, GLfloat depth, GLint stencil) {
|
||||||
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferfi called with null VulkanRenderer");
|
MOBILEGL_ASSERT(pVulkanRenderer, "DirectVulkan::ClearNamedFramebufferfi called with null VulkanRenderer");
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
GLint drawbuffer, const GLfloat* value);
|
GLint drawbuffer, const GLfloat* value);
|
||||||
|
void ClearNamedFramebufferiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
|
GLint drawbuffer, const GLint* value);
|
||||||
|
void ClearNamedFramebufferuiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
|
GLint drawbuffer, const GLuint* value);
|
||||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer,
|
||||||
GLint drawbuffer, GLfloat depth, GLint stencil);
|
GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
void Clear(GLbitfield mask);
|
void Clear(GLbitfield mask);
|
||||||
|
|||||||
@@ -8,15 +8,75 @@
|
|||||||
|
|
||||||
#include "VkClearManager.h"
|
#include "VkClearManager.h"
|
||||||
|
|
||||||
|
#include "MG_State/GLState/Core.h"
|
||||||
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
#include "MG_Util/Converters/MGToStr/FramebufferEnumConverter.h"
|
||||||
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
|
#include "MG_Util/Converters/MGToStr/TextureEnumConverter.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
namespace MobileGL::MG_Backend::DirectVulkan {
|
namespace MobileGL::MG_Backend::DirectVulkan {
|
||||||
static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) {
|
static Bool IsCubeMapFaceUploadTarget(TextureUploadTarget target) {
|
||||||
return target >= TextureUploadTarget::CubeMapPositiveX &&
|
return target >= TextureUploadTarget::CubeMapPositiveX &&
|
||||||
target <= TextureUploadTarget::CubeMapNegativeZ;
|
target <= TextureUploadTarget::CubeMapNegativeZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VkClearColorValue MakeVkClearColorValue(const ClearAttachmentPayload& payload, Bool formatLacksAlpha) {
|
||||||
|
VkClearColorValue clearValue{};
|
||||||
|
switch (payload.colorEncoding) {
|
||||||
|
case ClearColorEncoding::Int:
|
||||||
|
clearValue.int32[0] = payload.colorInt.x();
|
||||||
|
clearValue.int32[1] = payload.colorInt.y();
|
||||||
|
clearValue.int32[2] = payload.colorInt.z();
|
||||||
|
clearValue.int32[3] = formatLacksAlpha ? 1 : payload.colorInt.w();
|
||||||
|
break;
|
||||||
|
case ClearColorEncoding::Uint:
|
||||||
|
clearValue.uint32[0] = payload.colorUint.x();
|
||||||
|
clearValue.uint32[1] = payload.colorUint.y();
|
||||||
|
clearValue.uint32[2] = payload.colorUint.z();
|
||||||
|
clearValue.uint32[3] = formatLacksAlpha ? 1u : payload.colorUint.w();
|
||||||
|
break;
|
||||||
|
case ClearColorEncoding::Float:
|
||||||
|
clearValue.float32[0] = payload.color.x();
|
||||||
|
clearValue.float32[1] = payload.color.y();
|
||||||
|
clearValue.float32[2] = payload.color.z();
|
||||||
|
clearValue.float32[3] = formatLacksAlpha ? 1.0f : payload.color.w();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return clearValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PreCompensateSrgbClearColor(ClearAttachmentPayload& payload, VkFormat destinationFormat) {
|
||||||
|
if (payload.colorEncoding != ClearColorEncoding::Float) return;
|
||||||
|
// With GL_FRAMEBUFFER_SRGB enabled GL performs the encoding itself, so the driver doing it
|
||||||
|
// is exactly right and there is nothing to undo.
|
||||||
|
if (MG_State::pGLContext->IsCapabilityEnabled(MobileGL::CapabilityInput::FramebufferSrgb)) return;
|
||||||
|
if (ResolveSrgbAttachmentWriteFormat(destinationFormat, false) == destinationFormat) return;
|
||||||
|
|
||||||
|
// sRGB -> linear (GL 4.6 core 8.24), applied to the colour channels only: alpha is stored
|
||||||
|
// linearly in an sRGB format and must pass through untouched.
|
||||||
|
const auto toLinear = [](Float encoded) {
|
||||||
|
const Float value = std::clamp(encoded, 0.0f, 1.0f);
|
||||||
|
return value <= 0.04045f ? value / 12.92f : std::pow((value + 0.055f) / 1.055f, 2.4f);
|
||||||
|
};
|
||||||
|
payload.color = FloatVec4(toLinear(payload.color.x()), toLinear(payload.color.y()),
|
||||||
|
toLinear(payload.color.z()), payload.color.w());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForceOpaqueClearAlpha(ClearAttachmentPayload& payload) {
|
||||||
|
switch (payload.colorEncoding) {
|
||||||
|
case ClearColorEncoding::Int:
|
||||||
|
payload.colorInt = IntVec4(payload.colorInt.x(), payload.colorInt.y(), payload.colorInt.z(), 1);
|
||||||
|
break;
|
||||||
|
case ClearColorEncoding::Uint:
|
||||||
|
payload.colorUint = UintVec4(payload.colorUint.x(), payload.colorUint.y(), payload.colorUint.z(), 1u);
|
||||||
|
break;
|
||||||
|
case ClearColorEncoding::Float:
|
||||||
|
payload.color = FloatVec4(payload.color.x(), payload.color.y(), payload.color.z(), 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Bool PendingClearMatchesTextureIdentity(const PendingClearKey& key, const TextureIdentity& identity) {
|
static Bool PendingClearMatchesTextureIdentity(const PendingClearKey& key, const TextureIdentity& identity) {
|
||||||
return key.texture == identity.texture && key.textureLifetimeId == identity.lifetimeId;
|
return key.texture == identity.texture && key.textureLifetimeId == identity.lifetimeId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,13 +24,41 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Uint32 stencil{};
|
Uint32 stencil{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// A colour clear reaches us from one of glClear/ClearBufferfv, ClearBufferiv or
|
||||||
|
// ClearBufferuiv, and Vulkan reads VkClearColorValue's union according to the destination
|
||||||
|
// image's format rather than converting between the members - a float written where an
|
||||||
|
// integer format is expected is reinterpreted bit for bit, not rounded. Remember which entry
|
||||||
|
// point supplied the value so the member written when the clear is materialized matches.
|
||||||
|
enum class ClearColorEncoding : Uint8 { Float, Int, Uint };
|
||||||
|
|
||||||
struct ClearAttachmentPayload {
|
struct ClearAttachmentPayload {
|
||||||
GLbitfield mask = 0;
|
GLbitfield mask = 0;
|
||||||
FloatVec4 color = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f);
|
FloatVec4 color = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||||
|
ClearColorEncoding colorEncoding = ClearColorEncoding::Float;
|
||||||
|
IntVec4 colorInt = IntVec4(0, 0, 0, 0);
|
||||||
|
UintVec4 colorUint = UintVec4(0u, 0u, 0u, 0u);
|
||||||
Float depth = 1.0f;
|
Float depth = 1.0f;
|
||||||
Uint32 stencil = 0;
|
Uint32 stencil = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Builds the clear value for `payload` in the union member its encoding calls for.
|
||||||
|
// `formatLacksAlpha` applies GL's rule that a format without an alpha channel reads as one,
|
||||||
|
// expressed in whichever type matches (GL 4.6 core 15.2.3).
|
||||||
|
VkClearColorValue MakeVkClearColorValue(const ClearAttachmentPayload& payload, Bool formatLacksAlpha);
|
||||||
|
|
||||||
|
// Applies that same rule in place, for the paths that have to bake it into the payload before
|
||||||
|
// the destination is known.
|
||||||
|
void ForceOpaqueClearAlpha(ClearAttachmentPayload& payload);
|
||||||
|
|
||||||
|
// vkCmdClearColorImage names the image, so the driver applies the destination format's transfer
|
||||||
|
// function to whatever value it is handed. Every other write path in this backend goes through
|
||||||
|
// the UNORM twin view while GL_FRAMEBUFFER_SRGB is off (ResolveSrgbAttachmentWriteFormat) and
|
||||||
|
// therefore stores the raw value GL asked for. Rewrites `payload` to the linear colour whose
|
||||||
|
// encoding is that raw value, so a direct image clear of an sRGB destination agrees with them.
|
||||||
|
// A no-op for every other format, for integer clear encodings, and when GL is doing the
|
||||||
|
// encoding itself.
|
||||||
|
void PreCompensateSrgbClearColor(ClearAttachmentPayload& payload, VkFormat destinationFormat);
|
||||||
|
|
||||||
struct PendingClearKey {
|
struct PendingClearKey {
|
||||||
MG_State::GLState::ITextureObject* texture = nullptr;
|
MG_State::GLState::ITextureObject* texture = nullptr;
|
||||||
Uint64 textureLifetimeId = 0;
|
Uint64 textureLifetimeId = 0;
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
|
static Bool ColorFormatLacksAlpha(const MG_State::GLState::ITextureObject* texture) {
|
||||||
|
return texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
|
||||||
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
|
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
@@ -526,7 +530,13 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
pending.renderbuffer = renderbuffer;
|
pending.renderbuffer = renderbuffer;
|
||||||
pending.payload.mask |= clearPayload.mask;
|
pending.payload.mask |= clearPayload.mask;
|
||||||
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||||
|
// The whole colour description, not just the float vector: an integer clear keeps its
|
||||||
|
// value in colorInt/colorUint, and dropping the encoding here would leave the pending
|
||||||
|
// clear reading as an all-zero float one.
|
||||||
pending.payload.color = clearPayload.color;
|
pending.payload.color = clearPayload.color;
|
||||||
|
pending.payload.colorEncoding = clearPayload.colorEncoding;
|
||||||
|
pending.payload.colorInt = clearPayload.colorInt;
|
||||||
|
pending.payload.colorUint = clearPayload.colorUint;
|
||||||
}
|
}
|
||||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
pending.payload.depth = clearPayload.depth;
|
pending.payload.depth = clearPayload.depth;
|
||||||
@@ -924,9 +934,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
if (rbHasClear &&
|
if (rbHasClear &&
|
||||||
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3) {
|
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3) {
|
||||||
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
||||||
rbClearPayload.color =
|
ForceOpaqueClearAlpha(rbClearPayload);
|
||||||
FloatVec4(rbClearPayload.color.x(), rbClearPayload.color.y(),
|
|
||||||
rbClearPayload.color.z(), 1.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkImageLayout trackedRbLayout = rbResource->layout;
|
const VkImageLayout trackedRbLayout = rbResource->layout;
|
||||||
@@ -1496,12 +1504,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_COLOR_BUFFER_BIT) != 0) {
|
||||||
clearValues[pending.attachmentIndex].color = {
|
clearValues[pending.attachmentIndex].color =
|
||||||
clearPayload.color.x(),
|
MakeVkClearColorValue(clearPayload, ColorFormatLacksAlpha(liveTexture.get()));
|
||||||
clearPayload.color.y(),
|
|
||||||
clearPayload.color.z(),
|
|
||||||
ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w())
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth;
|
clearValues[pending.attachmentIndex].depthStencil.depth = clearPayload.depth;
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &maxAnisotropy, sizeof(maxAnisotropy)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &maxAnisotropy, sizeof(maxAnisotropy)));
|
||||||
const auto compareMode = sampler.GetCompareMode();
|
const auto compareMode = sampler.GetCompareMode();
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &compareMode, sizeof(compareMode)));
|
||||||
const auto compareFunc = ResolveCompareFunc(sampler, texture);
|
const auto compareFunc = sampler.GetSamplerCompareFunc();
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &compareFunc, sizeof(compareFunc)));
|
||||||
const auto borderColor = ResolveVkBorderColor(sampler, texture);
|
const auto borderColor = ResolveVkBorderColor(sampler, texture);
|
||||||
XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor)));
|
XXHASH_VERIFY(XXH64_update(m_hashState, &borderColor, sizeof(borderColor)));
|
||||||
@@ -207,7 +207,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
samplerInfo.anisotropyEnable = maxAnisotropy > 1.0f ? VK_TRUE : VK_FALSE;
|
samplerInfo.anisotropyEnable = maxAnisotropy > 1.0f ? VK_TRUE : VK_FALSE;
|
||||||
samplerInfo.maxAnisotropy = maxAnisotropy;
|
samplerInfo.maxAnisotropy = maxAnisotropy;
|
||||||
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
|
samplerInfo.compareEnable = sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture ? VK_TRUE : VK_FALSE;
|
||||||
samplerInfo.compareOp = ToVkCompareOp(ResolveCompareFunc(sampler, texture));
|
samplerInfo.compareOp = ToVkCompareOp(sampler.GetSamplerCompareFunc());
|
||||||
// Must match BuildSamplerKey's resolution exactly.
|
// Must match BuildSamplerKey's resolution exactly.
|
||||||
samplerInfo.maxLod = ResolveSingleLevelMaxLod(sampler, singleLevelView);
|
samplerInfo.maxLod = ResolveSingleLevelMaxLod(sampler, singleLevelView);
|
||||||
samplerInfo.minLod = ResolveEffectiveMinLod(sampler, samplerInfo.maxLod);
|
samplerInfo.minLod = ResolveEffectiveMinLod(sampler, samplerInfo.maxLod);
|
||||||
@@ -281,24 +281,15 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SamplerCompareFunc VkSamplerManager::ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
|
|
||||||
const MG_State::GLState::ITextureObject& texture) {
|
|
||||||
const auto compareFunc = sampler.GetSamplerCompareFunc();
|
|
||||||
if (sampler.GetCompareMode() == SamplerCompareMode::CompareToTexture &&
|
|
||||||
IsDepthTextureFormat(texture.GetFormat()) && compareFunc == SamplerCompareFunc::Always) {
|
|
||||||
return SamplerCompareFunc::LessEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
return compareFunc;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
VkBorderColor VkSamplerManager::ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
||||||
const MG_State::GLState::ITextureObject& texture) {
|
const MG_State::GLState::ITextureObject& texture) {
|
||||||
if (!UsesBorderColor(sampler)) {
|
if (!UsesBorderColor(sampler)) {
|
||||||
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& borderColor = texture.GetBorderColor();
|
// Border colour is sampler state: a bound sampler object supplies its own, and a texture
|
||||||
|
// with none reaches the very same value through the sampler object it owns.
|
||||||
|
const auto& borderColor = sampler.GetBorderColor();
|
||||||
const Bool isDepthTexture = IsDepthTextureFormat(texture.GetFormat());
|
const Bool isDepthTexture = IsDepthTextureFormat(texture.GetFormat());
|
||||||
|
|
||||||
if (isDepthTexture) {
|
if (isDepthTexture) {
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ private:
|
|||||||
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
static VkSamplerMipmapMode ToVkMipmapMode(SamplerMipmapMode mode);
|
||||||
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
static VkSamplerAddressMode ToVkAddressMode(SamplerWrapMode mode);
|
||||||
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
static VkCompareOp ToVkCompareOp(SamplerCompareFunc func);
|
||||||
static SamplerCompareFunc ResolveCompareFunc(const MG_State::GLState::SamplerObject& sampler,
|
|
||||||
const MG_State::GLState::ITextureObject& texture);
|
|
||||||
static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
static VkBorderColor ResolveVkBorderColor(const MG_State::GLState::SamplerObject& sampler,
|
||||||
const MG_State::GLState::ITextureObject& texture);
|
const MG_State::GLState::ITextureObject& texture);
|
||||||
// The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and
|
// The anisotropy Vulkan will actually apply: 1.0 (i.e. disabled) unless the feature is on and
|
||||||
|
|||||||
@@ -1497,15 +1497,17 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
: (mipLevels > 1 ? std::max(mipLevels, ComputeFullMipLevelCount(texelSize)) : 1u);
|
: (mipLevels > 1 ? std::max(mipLevels, ComputeFullMipLevelCount(texelSize)) : 1u);
|
||||||
TextureShapeInfo shapeInfo{};
|
TextureShapeInfo shapeInfo{};
|
||||||
const Bool supportedShape = TryResolveTextureShapeInfo(texture, uploadTarget, texelSize, shapeInfo);
|
const Bool supportedShape = TryResolveTextureShapeInfo(texture, uploadTarget, texelSize, shapeInfo);
|
||||||
MOBILEGL_ASSERT(supportedShape,
|
|
||||||
"SyncTextureResource: unsupported uploadTarget=%s textureTarget=%s textureId=%d size=(%d,%d,%d) "
|
|
||||||
"mipLevels=%u vkViewType=%d",
|
|
||||||
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
|
|
||||||
MG_Util::ConvertTextureTargetToString(texture.GetTarget()).c_str(),
|
|
||||||
texture.GetExternalIndex(), texelSize.x(), texelSize.y(), texelSize.z(), mipLevels,
|
|
||||||
static_cast<Int>(MG_Util::ConvertTextureUploadTargetToVkEnum(uploadTarget)));
|
|
||||||
if (!supportedShape) {
|
if (!supportedShape) {
|
||||||
MGLOG_D("%s: not Texture2D, unsupported", __func__);
|
// A gap in this backend's coverage, not a broken invariant: the GL front end accepts
|
||||||
|
// targets this manager has no Vulkan image shape for yet (cube map arrays above all).
|
||||||
|
// Declining the sync leaves the texture unbacked - wrong, but recoverable - where an
|
||||||
|
// assertion would take the whole process down instead.
|
||||||
|
MGLOG_W("SyncTextureResource: unsupported uploadTarget=%s textureTarget=%s textureId=%d size=(%d,%d,%d) "
|
||||||
|
"mipLevels=%u vkViewType=%d",
|
||||||
|
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str(),
|
||||||
|
MG_Util::ConvertTextureTargetToString(texture.GetTarget()).c_str(), texture.GetExternalIndex(),
|
||||||
|
texelSize.x(), texelSize.y(), texelSize.z(), mipLevels,
|
||||||
|
static_cast<Int>(MG_Util::ConvertTextureUploadTargetToVkEnum(uploadTarget)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
VkSampleCountFlagBits resolvedSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
VkSampleCountFlagBits resolvedSampleCount = VK_SAMPLE_COUNT_1_BIT;
|
||||||
@@ -1516,6 +1518,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str());
|
MG_Util::ConvertTextureUploadTargetToString(uploadTarget).c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// glTexStorage*Multisample(samples = 1) is legal GL, but a one-sample image cannot back a
|
||||||
|
// sampler2DMS: VUID-RuntimeSpirv-samples-08726 forbids an OpTypeImage with MS = 1 from
|
||||||
|
// reading an image created with VK_SAMPLE_COUNT_1_BIT, and the fetch returns undefined data
|
||||||
|
// rather than an error. GL only promises "at least the requested number of samples", so
|
||||||
|
// giving a multisample texture two is both legal and the only way to keep the shader's view
|
||||||
|
// of it honest. GL_TEXTURE_SAMPLES still reports what the application asked for - that is
|
||||||
|
// read off the texture object, not off the image.
|
||||||
|
if (isMultisampleTexture && resolvedSampleCount == VK_SAMPLE_COUNT_1_BIT) {
|
||||||
|
resolvedSampleCount = VK_SAMPLE_COUNT_2_BIT;
|
||||||
|
}
|
||||||
|
|
||||||
const VkImageAspectFlags aspect = GetAspectMaskForFormat(format);
|
const VkImageAspectFlags aspect = GetAspectMaskForFormat(format);
|
||||||
VkFormatProperties formatProperties{};
|
VkFormatProperties formatProperties{};
|
||||||
@@ -1591,7 +1603,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (rounded == 0) {
|
if (rounded == 0) {
|
||||||
for (Uint32 bit = static_cast<Uint32>(resolvedSampleCount) >> 1; bit != 0; bit >>= 1) {
|
// Never land on one sample: that is the VUID-RuntimeSpirv-samples-08726
|
||||||
|
// violation the floor above exists to avoid, and it would come back silently
|
||||||
|
// for any format whose only supported count is 1.
|
||||||
|
for (Uint32 bit = static_cast<Uint32>(resolvedSampleCount) >> 1;
|
||||||
|
bit > static_cast<Uint32>(VK_SAMPLE_COUNT_1_BIT); bit >>= 1) {
|
||||||
if ((supported & bit) != 0) {
|
if ((supported & bit) != 0) {
|
||||||
rounded = bit;
|
rounded = bit;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -190,11 +190,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Float ResolveColorClearAlpha(const MG_State::GLState::ITextureObject* texture, Float requestedAlpha) {
|
// GL 4.6 core 15.2.3: a colour format with no alpha channel reads as if alpha were one.
|
||||||
if (texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3) {
|
// The substitution has to happen in the clear value's own type, so this reports the condition
|
||||||
return 1.0f;
|
// and MakeVkClearColorValue applies it to whichever union member the encoding selects.
|
||||||
}
|
static Bool ColorFormatLacksAlpha(const MG_State::GLState::ITextureObject* texture) {
|
||||||
return requestedAlpha;
|
return texture != nullptr && MG_Util::GetBaseInternalFormatComponentCount(texture->GetFormat()) == 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Bool IsQuarterTurnPreTransform(VkSurfaceTransformFlagBitsKHR preTransform) {
|
static Bool IsQuarterTurnPreTransform(VkSurfaceTransformFlagBitsKHR preTransform) {
|
||||||
@@ -1235,10 +1235,20 @@ void main() {
|
|||||||
|
|
||||||
static Uint32 ResolveAttachmentBaseArrayLayer(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
static Uint32 ResolveAttachmentBaseArrayLayer(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||||
const TextureUploadTarget uploadTarget = attachment.GetTextureUploadTarget();
|
const TextureUploadTarget uploadTarget = attachment.GetTextureUploadTarget();
|
||||||
if (!IsCubeMapFaceUploadTarget(uploadTarget)) {
|
if (IsCubeMapFaceUploadTarget(uploadTarget)) {
|
||||||
return 0;
|
return static_cast<Uint32>(uploadTarget) - static_cast<Uint32>(TextureUploadTarget::CubeMapPositiveX);
|
||||||
}
|
}
|
||||||
return static_cast<Uint32>(uploadTarget) - static_cast<Uint32>(TextureUploadTarget::CubeMapPositiveX);
|
// Every other layered attachment names its layer directly. Returning 0 regardless made
|
||||||
|
// every blit, copy and ReadPixels against such an attachment read layer zero.
|
||||||
|
return static_cast<Uint32>(std::max(attachment.GetTextureLayer(), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A 3D image has arrayLayers == 1: its "layer" is a z slice, which has to travel as an
|
||||||
|
// image offset rather than a base array layer (VkBufferImageCopy requires baseArrayLayer 0
|
||||||
|
// for VK_IMAGE_TYPE_3D).
|
||||||
|
static Bool AttachmentIsDepthSlice(const MG_State::GLState::FramebufferAttachmentObject& attachment) {
|
||||||
|
return attachment.IsTexture() && attachment.GetTexture() &&
|
||||||
|
attachment.GetTexture()->GetTarget() == TextureTarget::Texture3D;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class BlitSurfaceTransform : Uint32 {
|
enum class BlitSurfaceTransform : Uint32 {
|
||||||
@@ -1259,6 +1269,8 @@ void main() {
|
|||||||
Uint32 mipLevelCount = 1;
|
Uint32 mipLevelCount = 1;
|
||||||
Uint32 baseArrayLayer = 0;
|
Uint32 baseArrayLayer = 0;
|
||||||
Uint32 layerCount = 1;
|
Uint32 layerCount = 1;
|
||||||
|
// z slice for a VK_IMAGE_TYPE_3D source; array attachments use baseArrayLayer instead.
|
||||||
|
Uint32 depthOffset = 0;
|
||||||
const char* label = nullptr;
|
const char* label = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1490,7 +1502,12 @@ void main() {
|
|||||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||||
outBinding.mipLevelCount = resource->mipLevels;
|
outBinding.mipLevelCount = resource->mipLevels;
|
||||||
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
if (AttachmentIsDepthSlice(attachment)) {
|
||||||
|
outBinding.depthOffset = static_cast<Uint32>(std::max(attachment.GetTextureLayer(), 0));
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
} else {
|
||||||
|
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
||||||
|
}
|
||||||
outBinding.layerCount = 1;
|
outBinding.layerCount = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1601,7 +1618,12 @@ void main() {
|
|||||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||||
outBinding.mipLevelCount = resource->mipLevels;
|
outBinding.mipLevelCount = resource->mipLevels;
|
||||||
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
if (AttachmentIsDepthSlice(attachment)) {
|
||||||
|
outBinding.depthOffset = static_cast<Uint32>(std::max(attachment.GetTextureLayer(), 0));
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
} else {
|
||||||
|
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
||||||
|
}
|
||||||
outBinding.layerCount = 1;
|
outBinding.layerCount = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1745,7 +1767,12 @@ void main() {
|
|||||||
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
outBinding.extent = {attachmentExtent.x(), attachmentExtent.y()};
|
||||||
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
outBinding.mipLevel = static_cast<Uint32>(std::max(attachment.GetTextureLevel(), 0));
|
||||||
outBinding.mipLevelCount = 1;
|
outBinding.mipLevelCount = 1;
|
||||||
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
if (AttachmentIsDepthSlice(attachment)) {
|
||||||
|
outBinding.depthOffset = static_cast<Uint32>(std::max(attachment.GetTextureLayer(), 0));
|
||||||
|
outBinding.baseArrayLayer = 0;
|
||||||
|
} else {
|
||||||
|
outBinding.baseArrayLayer = ResolveAttachmentBaseArrayLayer(attachment);
|
||||||
|
}
|
||||||
outBinding.layerCount = 1;
|
outBinding.layerCount = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -5401,9 +5428,11 @@ void main() {
|
|||||||
VkClearAttachment clearAttachment{};
|
VkClearAttachment clearAttachment{};
|
||||||
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
clearAttachment.colorAttachment = drawBufferIndex;
|
clearAttachment.colorAttachment = drawBufferIndex;
|
||||||
|
// glClear only ever supplies float values (ClearFramebufferPayload has no
|
||||||
|
// other form), so the float member is always the right one here.
|
||||||
clearAttachment.clearValue.color = {
|
clearAttachment.clearValue.color = {
|
||||||
payload.color.x(), payload.color.y(), payload.color.z(),
|
payload.color.x(), payload.color.y(), payload.color.z(),
|
||||||
ResolveColorClearAlpha(colorTexture, payload.color.w())
|
ColorFormatLacksAlpha(colorTexture) ? 1.0f : payload.color.w()
|
||||||
};
|
};
|
||||||
clearAttachments[clearAttachmentCount++] = clearAttachment;
|
clearAttachments[clearAttachmentCount++] = clearAttachment;
|
||||||
}
|
}
|
||||||
@@ -5680,10 +5709,8 @@ void main() {
|
|||||||
}
|
}
|
||||||
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
clearAttachment.colorAttachment = static_cast<Uint32>(drawbuffer);
|
clearAttachment.colorAttachment = static_cast<Uint32>(drawbuffer);
|
||||||
clearAttachment.clearValue.color = {
|
clearAttachment.clearValue.color =
|
||||||
clearPayload.color.x(), clearPayload.color.y(), clearPayload.color.z(),
|
MakeVkClearColorValue(clearPayload, ColorFormatLacksAlpha(colorTexture));
|
||||||
ResolveColorClearAlpha(colorTexture, clearPayload.color.w())
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
VkImageAspectFlags aspects = 0;
|
VkImageAspectFlags aspects = 0;
|
||||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0 && MG_State::pGLContext->GetDepthMask() &&
|
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0 && MG_State::pGLContext->GetDepthMask() &&
|
||||||
@@ -5771,6 +5798,46 @@ void main() {
|
|||||||
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The integer clears carry the same payload as their target-based siblings; only the
|
||||||
|
// destination differs, so they queue against the named framebuffer rather than the bound one.
|
||||||
|
void VulkanRenderer::ClearNamedFramebufferiv(
|
||||||
|
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||||
|
const GLint* value) {
|
||||||
|
if (!framebuffer || value == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClearAttachmentPayload payload{};
|
||||||
|
switch (buffer) {
|
||||||
|
case GL_COLOR:
|
||||||
|
payload.mask = GL_COLOR_BUFFER_BIT;
|
||||||
|
payload.colorEncoding = ClearColorEncoding::Int;
|
||||||
|
payload.colorInt = IntVec4(value[0], value[1], value[2], value[3]);
|
||||||
|
break;
|
||||||
|
case GL_STENCIL:
|
||||||
|
payload.mask = GL_STENCIL_BUFFER_BIT;
|
||||||
|
payload.stencil = static_cast<Uint32>(std::max(value[0], 0));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanRenderer::ClearNamedFramebufferuiv(
|
||||||
|
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||||
|
const GLuint* value) {
|
||||||
|
if (!framebuffer || value == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ClearAttachmentPayload payload{};
|
||||||
|
if (buffer == GL_COLOR) {
|
||||||
|
payload.mask = GL_COLOR_BUFFER_BIT;
|
||||||
|
payload.colorEncoding = ClearColorEncoding::Uint;
|
||||||
|
payload.colorUint = UintVec4(value[0], value[1], value[2], value[3]);
|
||||||
|
}
|
||||||
|
QueueClearBufferPayloadForFramebuffer(*framebuffer, buffer, drawbuffer, payload);
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanRenderer::ClearNamedFramebufferfi(
|
void VulkanRenderer::ClearNamedFramebufferfi(
|
||||||
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer, GLenum buffer, GLint drawbuffer,
|
||||||
GLfloat depth, GLint stencil) {
|
GLfloat depth, GLint stencil) {
|
||||||
@@ -5793,8 +5860,8 @@ void main() {
|
|||||||
switch (buffer) {
|
switch (buffer) {
|
||||||
case GL_COLOR:
|
case GL_COLOR:
|
||||||
payload.mask = GL_COLOR_BUFFER_BIT;
|
payload.mask = GL_COLOR_BUFFER_BIT;
|
||||||
payload.color = FloatVec4(static_cast<Float>(value[0]), static_cast<Float>(value[1]),
|
payload.colorEncoding = ClearColorEncoding::Uint;
|
||||||
static_cast<Float>(value[2]), static_cast<Float>(value[3]));
|
payload.colorUint = UintVec4(value[0], value[1], value[2], value[3]);
|
||||||
break;
|
break;
|
||||||
case GL_STENCIL:
|
case GL_STENCIL:
|
||||||
payload.mask = GL_STENCIL_BUFFER_BIT;
|
payload.mask = GL_STENCIL_BUFFER_BIT;
|
||||||
@@ -5814,8 +5881,8 @@ void main() {
|
|||||||
switch (buffer) {
|
switch (buffer) {
|
||||||
case GL_COLOR:
|
case GL_COLOR:
|
||||||
payload.mask = GL_COLOR_BUFFER_BIT;
|
payload.mask = GL_COLOR_BUFFER_BIT;
|
||||||
payload.color = FloatVec4(static_cast<Float>(value[0]), static_cast<Float>(value[1]),
|
payload.colorEncoding = ClearColorEncoding::Int;
|
||||||
static_cast<Float>(value[2]), static_cast<Float>(value[3]));
|
payload.colorInt = IntVec4(value[0], value[1], value[2], value[3]);
|
||||||
break;
|
break;
|
||||||
case GL_STENCIL:
|
case GL_STENCIL:
|
||||||
payload.mask = GL_STENCIL_BUFFER_BIT;
|
payload.mask = GL_STENCIL_BUFFER_BIT;
|
||||||
@@ -5874,14 +5941,12 @@ void main() {
|
|||||||
subresourceRange.baseArrayLayer = pendingClear.key.baseArrayLayer;
|
subresourceRange.baseArrayLayer = pendingClear.key.baseArrayLayer;
|
||||||
subresourceRange.layerCount = pendingClear.key.layerCount;
|
subresourceRange.layerCount = pendingClear.key.layerCount;
|
||||||
|
|
||||||
const auto& clearPayload = pendingClear.payload;
|
auto clearPayload = pendingClear.payload;
|
||||||
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||||
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
VkClearColorValue clearValue{};
|
PreCompensateSrgbClearColor(clearPayload, resource->format);
|
||||||
clearValue.float32[0] = clearPayload.color.x();
|
const VkClearColorValue clearValue =
|
||||||
clearValue.float32[1] = clearPayload.color.y();
|
MakeVkClearColorValue(clearPayload, ColorFormatLacksAlpha(&texture));
|
||||||
clearValue.float32[2] = clearPayload.color.z();
|
|
||||||
clearValue.float32[3] = ResolveColorClearAlpha(&texture, clearPayload.color.w());
|
|
||||||
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
&clearValue, 1, &subresourceRange);
|
&clearValue, 1, &subresourceRange);
|
||||||
} else {
|
} else {
|
||||||
@@ -5963,14 +6028,11 @@ void main() {
|
|||||||
VkImageLayout steadyLayout;
|
VkImageLayout steadyLayout;
|
||||||
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
if ((resource->aspect & VK_IMAGE_ASPECT_COLOR_BIT) != 0) {
|
||||||
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
VkClearColorValue clearValue{};
|
PreCompensateSrgbClearColor(clearPayload, resource->format);
|
||||||
clearValue.float32[0] = clearPayload.color.x();
|
|
||||||
clearValue.float32[1] = clearPayload.color.y();
|
|
||||||
clearValue.float32[2] = clearPayload.color.z();
|
|
||||||
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
// RGB renderbuffers are backed by an RGBA image; the missing alpha reads as 1.
|
||||||
clearValue.float32[3] =
|
const VkClearColorValue clearValue = MakeVkClearColorValue(
|
||||||
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3 ?
|
clearPayload,
|
||||||
1.0f : clearPayload.color.w();
|
MG_Util::GetBaseInternalFormatComponentCount(renderbuffer->GetInternalFormat()) == 3);
|
||||||
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
vkCmdClearColorImage(commandBuffer, resource->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
&clearValue, 1, &subresourceRange);
|
&clearValue, 1, &subresourceRange);
|
||||||
steadyLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
steadyLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||||
@@ -6267,17 +6329,20 @@ void main() {
|
|||||||
|
|
||||||
if (srcX1 < srcX0 || srcY1 < srcY0 || dstX1 < dstX0 || dstY1 < dstY0) {
|
if (srcX1 < srcX0 || srcY1 < srcY0 || dstX1 < dstX0 || dstY1 < dstY0) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth blits with flipped rectangles are not supported yet");
|
MGLOG_E("BlitFramebuffer skipped: depth blits with flipped rectangles are not supported yet");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Int srcWidth = srcX1 - srcX0;
|
const Int srcWidth = srcX1 - srcX0;
|
||||||
const Int srcHeight = srcY1 - srcY0;
|
const Int srcHeight = srcY1 - srcY0;
|
||||||
const Int dstWidth = dstX1 - dstX0;
|
const Int dstWidth = dstX1 - dstX0;
|
||||||
const Int dstHeight = dstY1 - dstY0;
|
const Int dstHeight = dstY1 - dstY0;
|
||||||
if (srcWidth <= 0 || srcHeight <= 0 || srcWidth != dstWidth || srcHeight != dstHeight) {
|
if (srcWidth <= 0 || srcHeight <= 0 || dstWidth <= 0 || dstHeight <= 0) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth blits currently require matching source and destination extents");
|
MGLOG_E("BlitFramebuffer skipped: degenerate depth blit rectangle");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
// A scaling depth blit is legal GL and vkCmdBlitImage scales natively; only a same-size
|
||||||
|
// pair can take the cheaper vkCmdCopyImage.
|
||||||
|
const Bool depthBlitScales = srcWidth != dstWidth || srcHeight != dstHeight;
|
||||||
|
|
||||||
if (!readIsDefaultFbo) {
|
if (!readIsDefaultFbo) {
|
||||||
const auto sourceAttachmentType = ResolveFramebufferCopyAttachmentType(*readFbo, true, srcBinding.aspectMask);
|
const auto sourceAttachmentType = ResolveFramebufferCopyAttachmentType(*readFbo, true, srcBinding.aspectMask);
|
||||||
@@ -6322,7 +6387,7 @@ void main() {
|
|||||||
: *srcBinding.trackedLayout;
|
: *srcBinding.trackedLayout;
|
||||||
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
if (srcOriginalLayout == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: depth source image layout is undefined");
|
MGLOG_E("BlitFramebuffer skipped: depth source image layout is undefined");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const VkImageLayout dstOriginalLayout = drawIsDefaultFbo
|
const VkImageLayout dstOriginalLayout = drawIsDefaultFbo
|
||||||
@@ -6339,7 +6404,7 @@ void main() {
|
|||||||
if (srcBinding.format != dstBinding.format) {
|
if (srcBinding.format != dstBinding.format) {
|
||||||
if (readIsDefaultFbo || drawIsDefaultFbo) {
|
if (readIsDefaultFbo || drawIsDefaultFbo) {
|
||||||
MGLOG_E("BlitFramebuffer skipped: cross-format depth/stencil blit with the default framebuffer");
|
MGLOG_E("BlitFramebuffer skipped: cross-format depth/stencil blit with the default framebuffer");
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
if (!BlitDepthAcrossFormats(frame, srcBinding.image, srcBinding.format, srcBinding.trackedLayout,
|
if (!BlitDepthAcrossFormats(frame, srcBinding.image, srcBinding.format, srcBinding.trackedLayout,
|
||||||
srcBinding.mipLevel, srcBinding.baseArrayLayer, dstBinding.image,
|
srcBinding.mipLevel, srcBinding.baseArrayLayer, dstBinding.image,
|
||||||
@@ -6347,7 +6412,7 @@ void main() {
|
|||||||
dstBinding.baseArrayLayer, srcX0, srcY0, dstX0, dstY0, srcX1 - srcX0,
|
dstBinding.baseArrayLayer, srcX0, srcY0, dstX0, dstY0, srcX1 - srcX0,
|
||||||
srcY1 - srcY0, srcOriginalLayout, dstRestoreLayout,
|
srcY1 - srcY0, srcOriginalLayout, dstRestoreLayout,
|
||||||
depthStencilAspect == VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
depthStencilAspect == VK_IMAGE_ASPECT_STENCIL_BIT)) {
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -6394,23 +6459,45 @@ void main() {
|
|||||||
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
MOBILEGL_ASSERT(ok, "%s: failed to transition depth destination image", __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageCopy copyRegion{};
|
if (depthBlitScales) {
|
||||||
copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
// vkCmdCopyImage cannot resize; NEAREST is the only filter Vulkan allows for a
|
||||||
copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
// depth/stencil blit anyway, and the GL front end already rejects the others.
|
||||||
copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
VkImageBlit blitRegion{};
|
||||||
copyRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
blitRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
||||||
copyRegion.srcOffset = {srcX0, srcY0, 0};
|
blitRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
||||||
copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
blitRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
||||||
copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
blitRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
||||||
copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
blitRegion.srcOffsets[0] = {srcX0, srcY0, 0};
|
||||||
copyRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
blitRegion.srcOffsets[1] = {srcX1, srcY1, 1};
|
||||||
copyRegion.dstOffset = {dstX0, dstY0, 0};
|
blitRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
||||||
copyRegion.extent = {static_cast<Uint32>(srcWidth), static_cast<Uint32>(srcHeight), 1};
|
blitRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
||||||
|
blitRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
||||||
|
blitRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
||||||
|
blitRegion.dstOffsets[0] = {dstX0, dstY0, 0};
|
||||||
|
blitRegion.dstOffsets[1] = {dstX1, dstY1, 1};
|
||||||
|
vkCmdBlitImage(frame.commandBuffer,
|
||||||
|
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
|
1, &blitRegion, VK_FILTER_NEAREST);
|
||||||
|
} else {
|
||||||
|
VkImageCopy copyRegion{};
|
||||||
|
copyRegion.srcSubresource.aspectMask = srcBinding.aspectMask;
|
||||||
|
copyRegion.srcSubresource.mipLevel = srcBinding.mipLevel;
|
||||||
|
copyRegion.srcSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
||||||
|
copyRegion.srcSubresource.layerCount = srcBinding.layerCount;
|
||||||
|
copyRegion.srcOffset = {srcX0, srcY0, 0};
|
||||||
|
copyRegion.dstSubresource.aspectMask = dstBinding.aspectMask;
|
||||||
|
copyRegion.dstSubresource.mipLevel = dstBinding.mipLevel;
|
||||||
|
copyRegion.dstSubresource.baseArrayLayer = dstBinding.baseArrayLayer;
|
||||||
|
copyRegion.dstSubresource.layerCount = dstBinding.layerCount;
|
||||||
|
copyRegion.dstOffset = {dstX0, dstY0, 0};
|
||||||
|
copyRegion.extent = {static_cast<Uint32>(srcWidth), static_cast<Uint32>(srcHeight), 1};
|
||||||
|
|
||||||
vkCmdCopyImage(frame.commandBuffer,
|
vkCmdCopyImage(frame.commandBuffer,
|
||||||
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
dstBinding.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||||
1, ©Region);
|
1, ©Region);
|
||||||
|
}
|
||||||
|
|
||||||
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
VkPipelineStageFlags srcRestoreStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||||
VkAccessFlags srcRestoreAccessMask = 0;
|
VkAccessFlags srcRestoreAccessMask = 0;
|
||||||
@@ -7141,7 +7228,7 @@ void main() {
|
|||||||
copyRegion.imageSubresource.mipLevel = srcBinding.mipLevel;
|
copyRegion.imageSubresource.mipLevel = srcBinding.mipLevel;
|
||||||
copyRegion.imageSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
copyRegion.imageSubresource.baseArrayLayer = srcBinding.baseArrayLayer;
|
||||||
copyRegion.imageSubresource.layerCount = 1;
|
copyRegion.imageSubresource.layerCount = 1;
|
||||||
copyRegion.imageOffset = {x, y, 0};
|
copyRegion.imageOffset = {x, y, static_cast<Int32>(srcBinding.depthOffset)};
|
||||||
copyRegion.imageExtent = {static_cast<Uint32>(width), static_cast<Uint32>(height), 1};
|
copyRegion.imageExtent = {static_cast<Uint32>(width), static_cast<Uint32>(height), 1};
|
||||||
vkCmdCopyImageToBuffer(frame.commandBuffer, srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
vkCmdCopyImageToBuffer(frame.commandBuffer, srcBinding.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
readback.GetHandle(), 1, ©Region);
|
readback.GetHandle(), 1, ©Region);
|
||||||
@@ -7599,13 +7686,16 @@ void main() {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case GL_FLOAT:
|
case GL_FLOAT:
|
||||||
case GL_UNSIGNED_INT:
|
case GL_UNSIGNED_INT:
|
||||||
|
case GL_INT:
|
||||||
case GL_UNSIGNED_INT_24_8:
|
case GL_UNSIGNED_INT_24_8:
|
||||||
dstPixelBytes = 4;
|
dstPixelBytes = 4;
|
||||||
break;
|
break;
|
||||||
case GL_UNSIGNED_SHORT:
|
case GL_UNSIGNED_SHORT:
|
||||||
|
case GL_SHORT:
|
||||||
dstPixelBytes = 2;
|
dstPixelBytes = 2;
|
||||||
break;
|
break;
|
||||||
case GL_UNSIGNED_BYTE:
|
case GL_UNSIGNED_BYTE:
|
||||||
|
case GL_BYTE:
|
||||||
dstPixelBytes = 1;
|
dstPixelBytes = 1;
|
||||||
break;
|
break;
|
||||||
case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
|
case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
|
||||||
@@ -7616,29 +7706,40 @@ void main() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL 4.6 core 18.2.8: a GL_STENCIL_INDEX read reports the index itself, unconverted, in
|
||||||
|
// whatever width the client asked for. Only the packed types mix depth in. Deciding this
|
||||||
|
// once - rather than per type, where GL_FLOAT and GL_UNSIGNED_SHORT used to emit a depth
|
||||||
|
// value that is meaningless for a stencil-only image - is what makes the CTS's
|
||||||
|
// (GL_STENCIL_INDEX, GL_INT) read return 7 instead of nothing.
|
||||||
|
const Bool stencilOnly = format == GL_STENCIL_INDEX;
|
||||||
|
|
||||||
Vector<Uint8> packed(pixelCount * dstPixelBytes);
|
Vector<Uint8> packed(pixelCount * dstPixelBytes);
|
||||||
for (SizeT i = 0; i < pixelCount; ++i) {
|
for (SizeT i = 0; i < pixelCount; ++i) {
|
||||||
Uint8* dst = packed.data() + i * dstPixelBytes;
|
Uint8* dst = packed.data() + i * dstPixelBytes;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case GL_FLOAT: {
|
case GL_FLOAT: {
|
||||||
const Float value = depthValueAt(i);
|
const Float value = stencilOnly ? static_cast<Float>(stencilSrc[i]) : depthValueAt(i);
|
||||||
Memcpy(dst, &value, sizeof(value));
|
Memcpy(dst, &value, sizeof(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GL_UNSIGNED_SHORT: {
|
case GL_UNSIGNED_SHORT:
|
||||||
const Uint16 value =
|
case GL_SHORT: {
|
||||||
static_cast<Uint16>(std::lround(static_cast<double>(depthValueAt(i)) * 65535.0));
|
const Uint16 value = stencilOnly
|
||||||
|
? static_cast<Uint16>(stencilSrc[i])
|
||||||
|
: static_cast<Uint16>(std::lround(static_cast<double>(depthValueAt(i)) * 65535.0));
|
||||||
Memcpy(dst, &value, sizeof(value));
|
Memcpy(dst, &value, sizeof(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GL_UNSIGNED_INT: {
|
case GL_UNSIGNED_INT:
|
||||||
const Uint32 value = format == GL_STENCIL_INDEX
|
case GL_INT: {
|
||||||
|
const Uint32 value = stencilOnly
|
||||||
? stencilSrc[i]
|
? stencilSrc[i]
|
||||||
: static_cast<Uint32>(static_cast<double>(depthValueAt(i)) * 4294967295.0);
|
: static_cast<Uint32>(static_cast<double>(depthValueAt(i)) * 4294967295.0);
|
||||||
Memcpy(dst, &value, sizeof(value));
|
Memcpy(dst, &value, sizeof(value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GL_UNSIGNED_BYTE: {
|
case GL_UNSIGNED_BYTE:
|
||||||
|
case GL_BYTE: {
|
||||||
dst[0] = stencilSrc[i];
|
dst[0] = stencilSrc[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -7850,9 +7951,18 @@ void main() {
|
|||||||
|
|
||||||
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
void VulkanRenderer::GenerateMipmap(GLenum target) {
|
||||||
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
const auto textureTarget = MG_Util::ConvertGLEnumToTextureTarget(target);
|
||||||
MOBILEGL_ASSERT(textureTarget == TextureTarget::Texture2D || textureTarget == TextureTarget::Texture2DArray ||
|
// The other mipmappable targets - 1D, 1D array, cube map array - are legal GL and the front
|
||||||
textureTarget == TextureTarget::Texture3D || textureTarget == TextureTarget::TextureCubeMap,
|
// end lets them through, so reaching one here is a coverage gap in this backend, not a
|
||||||
"GenerateMipmap currently only supports GL_TEXTURE_2D, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_3D, and GL_TEXTURE_CUBE_MAP.");
|
// broken invariant. Declining leaves the mip chain unwritten; asserting took the process
|
||||||
|
// down with it.
|
||||||
|
if (textureTarget != TextureTarget::Texture2D && textureTarget != TextureTarget::Texture2DArray &&
|
||||||
|
textureTarget != TextureTarget::Texture3D && textureTarget != TextureTarget::TextureCubeMap &&
|
||||||
|
// A 1D texture needs nothing special: its storage extent is {width, 1, 1}, so the blit
|
||||||
|
// loop below already emits the y and z offsets of 0 and 1 that a 1D image requires.
|
||||||
|
textureTarget != TextureTarget::Texture1D) {
|
||||||
|
MGLOG_W("GenerateMipmap: unsupported target %s", MG_Util::ConvertTextureTargetToString(textureTarget).c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
auto& textureUnit = MG_State::pGLContext->GetTextureUnitObject(MG_State::pGLContext->GetActiveTextureUnit());
|
||||||
auto texture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
auto texture = textureUnit.GetBindingSlot(textureTarget).GetBoundObject();
|
||||||
@@ -9191,10 +9301,17 @@ void main() {
|
|||||||
if (record.harvested) {
|
if (record.harvested) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!m_timerQueryManager || !IsFrameSerialComplete(record.frameSerial)) {
|
if (!m_timerQueryManager) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return m_timerQueryManager->TryHarvest(record);
|
// Ask the pool first. It polls with VK_QUERY_RESULT_WITH_AVAILABILITY_BIT and is the
|
||||||
|
// authority on whether the timestamp has landed; the frame serial is not, because it only
|
||||||
|
// advances at Present and neither completion notifier will mark the CURRENT serial done - so
|
||||||
|
// a timestamp written and fence-waited inside one GL frame could never be read back in it.
|
||||||
|
if (m_timerQueryManager->TryHarvest(record)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return IsFrameSerialComplete(record.frameSerial) && m_timerQueryManager->TryHarvest(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool VulkanRenderer::WaitForTimerQueryResult(VkTimerQueryManager::TimestampRecord& record) {
|
Bool VulkanRenderer::WaitForTimerQueryResult(VkTimerQueryManager::TimestampRecord& record) {
|
||||||
@@ -10622,12 +10739,8 @@ void main() {
|
|||||||
// VkClearAttachment::colorAttachment indexes the subpass pColorAttachments (draw-buffer
|
// VkClearAttachment::colorAttachment indexes the subpass pColorAttachments (draw-buffer
|
||||||
// slot space, with UNUSED holes), not the compacted attachment descriptions.
|
// slot space, with UNUSED holes), not the compacted attachment descriptions.
|
||||||
clearAttachment.colorAttachment = pending.colorAttachmentSlot;
|
clearAttachment.colorAttachment = pending.colorAttachmentSlot;
|
||||||
clearAttachment.clearValue.color = {
|
clearAttachment.clearValue.color =
|
||||||
clearPayload.color.x(),
|
MakeVkClearColorValue(clearPayload, ColorFormatLacksAlpha(liveTexture.get()));
|
||||||
clearPayload.color.y(),
|
|
||||||
clearPayload.color.z(),
|
|
||||||
ResolveColorClearAlpha(liveTexture.get(), clearPayload.color.w())
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
if ((clearPayload.mask & GL_DEPTH_BUFFER_BIT) != 0) {
|
||||||
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
clearAttachment.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||||
|
|||||||
@@ -181,6 +181,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
void ClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
void ClearNamedFramebufferfv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
|
void ClearNamedFramebufferiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void ClearNamedFramebufferuiv(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
|
GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
void ClearNamedFramebufferfi(const SharedPtr<MG_State::GLState::FramebufferObject>& framebuffer,
|
||||||
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#include "GL_Buffer.h"
|
#include "GL_Buffer.h"
|
||||||
#include "Validators.h"
|
#include "Validators.h"
|
||||||
|
#include "../Texture/GL_Texture.h"
|
||||||
|
#include <MG_Util/Converters/GLToMG/TextureEnumConverter.h>
|
||||||
|
#include <MG_Util/Metrics/TextureMetrics.h>
|
||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_State/GLState/ErrorState/Error.h>
|
#include <MG_State/GLState/ErrorState/Error.h>
|
||||||
@@ -38,6 +41,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GetNamedBufferParameteriv,
|
GetNamedBufferParameteriv,
|
||||||
GetNamedBufferParameteri64v,
|
GetNamedBufferParameteri64v,
|
||||||
GetNamedBufferPointerv,
|
GetNamedBufferPointerv,
|
||||||
|
GetNamedBufferSubData,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* GetBufferOpName(BufferOp op) {
|
const char* GetBufferOpName(BufferOp op) {
|
||||||
@@ -76,6 +80,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return "UnmapNamedBuffer";
|
return "UnmapNamedBuffer";
|
||||||
case BufferOp::FlushMappedNamedBufferRange:
|
case BufferOp::FlushMappedNamedBufferRange:
|
||||||
return "FlushMappedNamedBufferRange";
|
return "FlushMappedNamedBufferRange";
|
||||||
|
case BufferOp::GetNamedBufferSubData:
|
||||||
|
return "GetNamedBufferSubData";
|
||||||
case BufferOp::GetNamedBufferParameteriv:
|
case BufferOp::GetNamedBufferParameteriv:
|
||||||
return "GetNamedBufferParameteriv";
|
return "GetNamedBufferParameteriv";
|
||||||
case BufferOp::GetNamedBufferParameteri64v:
|
case BufferOp::GetNamedBufferParameteri64v:
|
||||||
@@ -89,25 +95,64 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
SharedPtr<MG_State::GLState::BufferObject> GetNamedBufferObject(GLuint buffer, BufferOp op);
|
SharedPtr<MG_State::GLState::BufferObject> GetNamedBufferObject(GLuint buffer, BufferOp op);
|
||||||
|
|
||||||
|
// The size of one cleared element, which is what offset and size must be multiples of
|
||||||
|
// (GL 4.6 core 6.3). `internalformat` is restricted to the buffer-texture format table, and
|
||||||
|
// `format`/`type` describe the client-side pattern, so both are validated here and the
|
||||||
|
// caller only has to know how wide an element is.
|
||||||
SizeT GetClearPatternSize(GLenum internalformat, GLenum format, GLenum type, BufferOp op) {
|
SizeT GetClearPatternSize(GLenum internalformat, GLenum format, GLenum type, BufferOp op) {
|
||||||
if (format != GL_RED_INTEGER) {
|
if (!IsBufferTextureInternalFormat(internalformat)) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidEnum,
|
ErrorCode::InvalidEnum,
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
MakeUnique<GenericErrorInfo>(
|
||||||
"Only GL_RED_INTEGER buffer clears are currently supported."));
|
"MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
|
std::format("internalformat 0x{:X} is not one of the sized formats a buffer clear accepts.",
|
||||||
|
internalformat)));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (internalformat == GL_R8UI && type == GL_UNSIGNED_BYTE) return sizeof(GLubyte);
|
// Unlike internalformat, a bad format or type here is INVALID_VALUE rather than
|
||||||
if (internalformat == GL_R32UI && type == GL_UNSIGNED_INT) return sizeof(GLuint);
|
// INVALID_ENUM (GL 4.6 core 6.3) - the odd one out among the enum arguments.
|
||||||
|
const TextureInputFormat inputFormat = MG_Util::ConvertGLEnumToTextureInputFormat(format);
|
||||||
|
if (inputFormat == TextureInputFormat::Unknown) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
|
std::format("format 0x{:X} is not a pixel format.", format)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
MG_State::pGLContext->RecordError(
|
const TexturePixelDataType pixelType = MG_Util::ConvertGLEnumToTexturePixelDataType(type);
|
||||||
ErrorCode::InvalidEnum,
|
if (pixelType == TexturePixelDataType::Unknown) {
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
MG_State::pGLContext->RecordError(
|
||||||
std::format("Unsupported clear format tuple: internalformat=0x{:X}, "
|
ErrorCode::InvalidValue,
|
||||||
"format=0x{:X}, type=0x{:X}",
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
internalformat, format, type)));
|
std::format("type 0x{:X} is not a pixel type.", type)));
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextureInternalFormat internal =
|
||||||
|
MG_Util::ConvertGLEnumToTextureInternalFormat(internalformat);
|
||||||
|
const SizeT elementSize = MG_Util::GetSizedInternalFormatSizeInBytes(internal);
|
||||||
|
if (elementSize == 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", GetBufferOpName(op),
|
||||||
|
std::format("internalformat 0x{:X} has no known element size.",
|
||||||
|
internalformat)));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The pattern is replicated verbatim, which is only the whole story while the client
|
||||||
|
// layout already matches the internal format - the case every entry point in practice
|
||||||
|
// uses, and the only one the conversion machinery here can express. Say so rather than
|
||||||
|
// quietly writing a differently-sized pattern.
|
||||||
|
const SizeT sourceSize = MG_Util::GetInputBytesPerPixel(inputFormat, pixelType);
|
||||||
|
if (sourceSize != elementSize) {
|
||||||
|
MGLOG_W("%s: clear pattern is %zu bytes but internalformat 0x%X stores %zu; "
|
||||||
|
"converting between them is not implemented",
|
||||||
|
GetBufferOpName(op), sourceSize, internalformat, elementSize);
|
||||||
|
}
|
||||||
|
return elementSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ValidateBufferClearRange(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject, GLintptr offset,
|
Bool ValidateBufferClearRange(const SharedPtr<MG_State::GLState::BufferObject>& bufferObject, GLintptr offset,
|
||||||
@@ -891,6 +936,44 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
bufferObject->DownloadSubData(data, static_cast<SizeT>(offset), static_cast<SizeT>(size));
|
bufferObject->DownloadSubData(data, static_cast<SizeT>(offset), static_cast<SizeT>(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetNamedBufferSubData_State(GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) {
|
||||||
|
if (!data) {
|
||||||
|
// Match GetBufferSubData_State: a null pointer is a caller bug, not a GL-specified error.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size < 0 || offset < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetNamedBufferSubData_State",
|
||||||
|
"Offset and size must be non-negative."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bufferObject = GetNamedBufferObject(buffer, BufferOp::GetNamedBufferSubData);
|
||||||
|
if (!bufferObject) return;
|
||||||
|
|
||||||
|
if (static_cast<SizeT>(offset) + static_cast<SizeT>(size) > bufferObject->GetSize()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetNamedBufferSubData_State",
|
||||||
|
"Offset and size exceed buffer size."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bufferObject->IsMapped() &&
|
||||||
|
!(bufferObject->GetMappingAccess() & BufferMappingAccessBit::Persistent)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetNamedBufferSubData_State",
|
||||||
|
"Cannot read from a buffer object mapped without GL_MAP_PERSISTENT_BIT."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufferObject->SyncGpuWrites();
|
||||||
|
bufferObject->DownloadSubData(data, static_cast<SizeT>(offset), static_cast<SizeT>(size));
|
||||||
|
}
|
||||||
|
|
||||||
void BufferData_State(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
void BufferData_State(GLenum target, GLsizeiptr size, const void* data, GLenum usage) {
|
||||||
MGLOG_D("%s: %s, size = %d, data = %p, usage = %s", __func__, MG_Util::ConvertGLEnumToString(target).c_str(),
|
MGLOG_D("%s: %s, size = %d, data = %p, usage = %s", __func__, MG_Util::ConvertGLEnumToString(target).c_str(),
|
||||||
size, data, MG_Util::ConvertGLEnumToString(usage).c_str());
|
size, data, MG_Util::ConvertGLEnumToString(usage).c_str());
|
||||||
@@ -1553,6 +1636,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
BufferSubData_State(target, offset, size, data);
|
BufferSubData_State(target, offset, size, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetNamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) {
|
||||||
|
GetNamedBufferSubData_State(buffer, offset, size, data);
|
||||||
|
}
|
||||||
|
|
||||||
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data) {
|
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data) {
|
||||||
GetBufferSubData_State(target, offset, size, data);
|
GetBufferSubData_State(target, offset, size, data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GLsizeiptr size);
|
GLsizeiptr size);
|
||||||
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
|
void BufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
|
||||||
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data);
|
void GetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void* data);
|
||||||
|
void GetNamedBufferSubData(GLuint buffer, GLintptr offset, GLsizeiptr size, void* data);
|
||||||
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
void BufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
|
||||||
void BindBuffer(GLenum target, GLuint buffer);
|
void BindBuffer(GLenum target, GLuint buffer);
|
||||||
void GenBuffers(GLsizei n, GLuint* buffers);
|
void GenBuffers(GLsizei n, GLuint* buffers);
|
||||||
|
|||||||
@@ -112,14 +112,10 @@ namespace MobileGL::MG_Impl::GLImpl::BufferImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) {
|
Bool ValidateBufferMappingAccess(Flags<BufferMappingAccessBit> accessBits) {
|
||||||
if (accessBits == BufferMappingAccessBit::Null) {
|
// An empty mask is a legal value for a bitfield - it just fails the rule that a mapping
|
||||||
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
// must ask for read or write access, which is INVALID_OPERATION and belongs to the callers
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/BufferImpl",
|
// (both of them check it immediately after this). Rejecting it here as INVALID_ENUM
|
||||||
"ValidateBufferMappingAccess",
|
// reported the wrong error and hid theirs.
|
||||||
"Access bits cannot be null."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto validBits = BufferMappingAccessBit::Read | BufferMappingAccessBit::Write |
|
const auto validBits = BufferMappingAccessBit::Read | BufferMappingAccessBit::Write |
|
||||||
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer |
|
BufferMappingAccessBit::InvalidateRange | BufferMappingAccessBit::InvalidateBuffer |
|
||||||
BufferMappingAccessBit::FlushExplicit | BufferMappingAccessBit::Unsynchronized |
|
BufferMappingAccessBit::FlushExplicit | BufferMappingAccessBit::Unsynchronized |
|
||||||
|
|||||||
@@ -877,6 +877,172 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
Memcpy(ids, names.data(), static_cast<SizeT>(n) * sizeof(GLuint));
|
Memcpy(ids, names.data(), static_cast<SizeT>(n) * sizeof(GLuint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreateTransformFeedbacks(GLsizei n, GLuint* ids) {
|
||||||
|
if (n < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "n must be non-negative."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (n == 0 || ids == nullptr) return;
|
||||||
|
Vector<Uint> names;
|
||||||
|
MG_State::pGLContext->GenTransformFeedbackNames(static_cast<Uint>(n), names);
|
||||||
|
// Unlike glGenTransformFeedbacks, the names are objects immediately: there is no bind step
|
||||||
|
// to create them from (GL 4.6 core 13.2.1).
|
||||||
|
for (const Uint name : names) {
|
||||||
|
MG_State::pGLContext->CreateTransformFeedbackObject(name);
|
||||||
|
}
|
||||||
|
Memcpy(ids, names.data(), static_cast<SizeT>(n) * sizeof(GLuint));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Shared front half of the by-name transform feedback entry points: the object has to exist
|
||||||
|
// (INVALID_OPERATION otherwise) before anything else about the call is looked at.
|
||||||
|
Bool ValidateNamedTransformFeedback(GLuint xfb, const char* functionName) {
|
||||||
|
if (!MG_State::pGLContext->IsTransformFeedbackObject(xfb)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
std::to_string(xfb) + " is not a transform feedback object."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool ValidateTransformFeedbackBufferIndex(GLuint index, const char* functionName) {
|
||||||
|
if (index >= MG_State::GLState::GLContext::MAX_TRANSFORM_FEEDBACK_BUFFERS) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
"index exceeds GL_MAX_TRANSFORM_FEEDBACK_BUFFERS."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A capture binding may not be changed while the object is capturing (GL 4.6 core 13.2.2).
|
||||||
|
Bool ValidateNamedTransformFeedbackNotActive(GLuint xfb, const char* functionName) {
|
||||||
|
if (MG_State::pGLContext->IsNamedTransformFeedbackActive(xfb)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
"The transform feedback object is capturing."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPtr<MG_State::GLState::BufferObject> ResolveTransformFeedbackBuffer(GLuint buffer,
|
||||||
|
const char* functionName) {
|
||||||
|
if (buffer == 0) return nullptr;
|
||||||
|
if (!MG_State::pGLContext->ValidateBufferName(buffer)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
std::to_string(buffer) + " is not a buffer object."));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer) {
|
||||||
|
if (!ValidateNamedTransformFeedback(xfb, __func__)) return;
|
||||||
|
if (!ValidateTransformFeedbackBufferIndex(index, __func__)) return;
|
||||||
|
if (!ValidateNamedTransformFeedbackNotActive(xfb, __func__)) return;
|
||||||
|
if (buffer != 0 && !MG_State::pGLContext->ValidateBufferName(buffer)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(buffer) + " is not a buffer object."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MG_State::pGLContext->SetNamedTransformFeedbackBinding(xfb, index,
|
||||||
|
ResolveTransformFeedbackBuffer(buffer, __func__), {},
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) {
|
||||||
|
if (!ValidateNamedTransformFeedback(xfb, __func__)) return;
|
||||||
|
if (!ValidateTransformFeedbackBufferIndex(index, __func__)) return;
|
||||||
|
if (!ValidateNamedTransformFeedbackNotActive(xfb, __func__)) return;
|
||||||
|
if (offset < 0 || size <= 0 || (offset % 4) != 0 || (size % 4) != 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"offset and size must be non-negative multiples of 4."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (buffer != 0 && !MG_State::pGLContext->ValidateBufferName(buffer)) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
std::to_string(buffer) + " is not a buffer object."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto bufferObject = ResolveTransformFeedbackBuffer(buffer, __func__);
|
||||||
|
const Range1D range{static_cast<SizeT>(offset), static_cast<SizeT>(offset) + static_cast<SizeT>(size)};
|
||||||
|
MG_State::pGLContext->SetNamedTransformFeedbackBinding(xfb, index, bufferObject, range,
|
||||||
|
bufferObject != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint* param) {
|
||||||
|
if (!ValidateNamedTransformFeedback(xfb, __func__)) return;
|
||||||
|
if (!param) return;
|
||||||
|
switch (pname) {
|
||||||
|
case GL_TRANSFORM_FEEDBACK_ACTIVE:
|
||||||
|
*param = MG_State::pGLContext->IsNamedTransformFeedbackActive(xfb) ? GL_TRUE : GL_FALSE;
|
||||||
|
return;
|
||||||
|
case GL_TRANSFORM_FEEDBACK_PAUSED:
|
||||||
|
*param = MG_State::pGLContext->IsNamedTransformFeedbackPaused(xfb) ? GL_TRUE : GL_FALSE;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"pname must be GL_TRANSFORM_FEEDBACK_ACTIVE or _PAUSED."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index, GLint* param) {
|
||||||
|
if (!ValidateNamedTransformFeedback(xfb, __func__)) return;
|
||||||
|
if (pname != GL_TRANSFORM_FEEDBACK_BUFFER_BINDING) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"pname must be GL_TRANSFORM_FEEDBACK_BUFFER_BINDING."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ValidateTransformFeedbackBufferIndex(index, __func__)) return;
|
||||||
|
if (!param) return;
|
||||||
|
const auto binding = MG_State::pGLContext->GetNamedTransformFeedbackBinding(xfb, index);
|
||||||
|
*param = binding.Buffer ? static_cast<GLint>(binding.Buffer->GetExternalIndex()) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index, GLint64* param) {
|
||||||
|
if (!ValidateNamedTransformFeedback(xfb, __func__)) return;
|
||||||
|
if (pname != GL_TRANSFORM_FEEDBACK_BUFFER_START && pname != GL_TRANSFORM_FEEDBACK_BUFFER_SIZE) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"pname must be GL_TRANSFORM_FEEDBACK_BUFFER_START or _SIZE."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ValidateTransformFeedbackBufferIndex(index, __func__)) return;
|
||||||
|
if (!param) return;
|
||||||
|
const auto binding = MG_State::pGLContext->GetNamedTransformFeedbackBinding(xfb, index);
|
||||||
|
// glTransformFeedbackBufferBase leaves both at zero; only the range form sets them
|
||||||
|
// (GL 4.6 core table 23.48).
|
||||||
|
if (!binding.Buffer || !binding.HasExplicitRange) {
|
||||||
|
*param = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*param = (pname == GL_TRANSFORM_FEEDBACK_BUFFER_START)
|
||||||
|
? static_cast<GLint64>(binding.Range.start)
|
||||||
|
: static_cast<GLint64>(binding.Range.end - binding.Range.start);
|
||||||
|
}
|
||||||
|
|
||||||
void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) {
|
void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids) {
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
|
|||||||
@@ -16,7 +16,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void PauseTransformFeedback(void);
|
void PauseTransformFeedback(void);
|
||||||
void ResumeTransformFeedback(void);
|
void ResumeTransformFeedback(void);
|
||||||
void GenTransformFeedbacks(GLsizei n, GLuint* ids);
|
void GenTransformFeedbacks(GLsizei n, GLuint* ids);
|
||||||
|
void CreateTransformFeedbacks(GLsizei n, GLuint* ids);
|
||||||
void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids);
|
void DeleteTransformFeedbacks(GLsizei n, const GLuint* ids);
|
||||||
|
void TransformFeedbackBufferBase(GLuint xfb, GLuint index, GLuint buffer);
|
||||||
|
void TransformFeedbackBufferRange(GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||||
|
void GetTransformFeedbackiv(GLuint xfb, GLenum pname, GLint* param);
|
||||||
|
void GetTransformFeedbacki_v(GLuint xfb, GLenum pname, GLuint index, GLint* param);
|
||||||
|
void GetTransformFeedbacki64_v(GLuint xfb, GLenum pname, GLuint index, GLint64* param);
|
||||||
void BindTransformFeedback(GLenum target, GLuint id);
|
void BindTransformFeedback(GLenum target, GLuint id);
|
||||||
GLboolean IsTransformFeedback(GLuint id);
|
GLboolean IsTransformFeedback(GLuint id);
|
||||||
void DrawTransformFeedback(GLenum mode, GLuint id);
|
void DrawTransformFeedback(GLenum mode, GLuint id);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "../Texture/GL_Texture.h"
|
#include "../Texture/GL_Texture.h"
|
||||||
#include "../Drawing/GL_Drawing.h"
|
#include "../Drawing/GL_Drawing.h"
|
||||||
#include "../Program/GL_Program.h"
|
#include "../Program/GL_Program.h"
|
||||||
|
#include "../Program/GL_ProgramPipeline.h"
|
||||||
#include "../RenderState/GL_RenderState.h"
|
#include "../RenderState/GL_RenderState.h"
|
||||||
#include "../Framebuffer/GL_Framebuffer.h"
|
#include "../Framebuffer/GL_Framebuffer.h"
|
||||||
#include "../VertexArray/GL_VertexArray.h"
|
#include "../VertexArray/GL_VertexArray.h"
|
||||||
@@ -301,8 +302,8 @@ DECLARE_GL_FUNCTION_HEAD(void, ResumeTransformFeedback) DECLARE_GL_FUNCTION_END_
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramBinary, GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramBinary, program, binaryFormat, binary, length)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramBinary, GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramBinary, program, binaryFormat, binary, length)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramParameteri, program, pname, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramParameteri, program, pname, value)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateFramebuffer, target, numAttachments, attachments)
|
DECLARE_GL_FUNCTION_HEAD(void, InvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_END_NO_RETURN(void, InvalidateFramebuffer, target, numAttachments, attachments)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, InvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth)
|
DECLARE_GL_FUNCTION_HEAD(void, TexStorage3D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage3D, target, levels, internalformat, width, height, depth)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetInternalformativ, GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetInternalformativ, target, internalformat, pname, bufSize, params)
|
||||||
@@ -310,21 +311,21 @@ DECLARE_GL_FUNCTION_HEAD(void, DispatchCompute, GLuint num_groups_x, GLuint num_
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, DispatchComputeIndirect, GLintptr indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchComputeIndirect, indirect)
|
DECLARE_GL_FUNCTION_HEAD(void, DispatchComputeIndirect, GLintptr indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DispatchComputeIndirect, indirect)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, DrawArraysIndirect, GLenum mode, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysIndirect, mode, indirect)
|
DECLARE_GL_FUNCTION_HEAD(void, DrawArraysIndirect, GLenum mode, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawArraysIndirect, mode, indirect)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsIndirect, GLenum mode, GLenum type, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsIndirect, mode, type, indirect)
|
DECLARE_GL_FUNCTION_HEAD(void, DrawElementsIndirect, GLenum mode, GLenum type, const void* indirect) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DrawElementsIndirect, mode, type, indirect)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, FramebufferParameteri, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, FramebufferParameteri, target, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, FramebufferParameteri, GLenum target, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, FramebufferParameteri, target, pname, param)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetFramebufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetFramebufferParameteriv, target, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetFramebufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetFramebufferParameteriv, target, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramInterfaceiv, program, programInterface, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramInterfaceiv, GLuint program, GLenum programInterface, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramInterfaceiv, program, programInterface, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(GLuint, GetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLuint, GetProgramResourceIndex, program, programInterface, name)
|
DECLARE_GL_FUNCTION_HEAD(GLuint, GetProgramResourceIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLuint, GetProgramResourceIndex, program, programInterface, name)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceName, program, programInterface, index, bufSize, length, name)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceName, GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei* length, GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceName, program, programInterface, index, bufSize, length, name)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramResourceiv, GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum* props, GLsizei bufSize, GLsizei* length, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramResourceiv, program, programInterface, index, propCount, props, bufSize, length, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocation, program, programInterface, name)
|
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocation, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocation, program, programInterface, name)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, UseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, UseProgramStages, pipeline, stages, program)
|
DECLARE_GL_FUNCTION_HEAD(void, UseProgramStages, GLuint pipeline, GLbitfield stages, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, UseProgramStages, pipeline, stages, program)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ActiveShaderProgram, GLuint pipeline, GLuint program) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ActiveShaderProgram, pipeline, program)
|
DECLARE_GL_FUNCTION_HEAD(void, ActiveShaderProgram, GLuint pipeline, GLuint program) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ActiveShaderProgram, pipeline, program)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateShaderProgramv, GLenum type, GLsizei count, const GLchar* const* strings) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateShaderProgramv, type, count, strings)
|
DECLARE_GL_FUNCTION_STUB_HEAD(GLuint, CreateShaderProgramv, GLenum type, GLsizei count, const GLchar* const* strings) DECLARE_GL_FUNCTION_STUB_END(GLuint, CreateShaderProgramv, type, count, strings)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindProgramPipeline, pipeline)
|
DECLARE_GL_FUNCTION_HEAD(void, BindProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindProgramPipeline, pipeline)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, DeleteProgramPipelines, GLsizei n, const GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, DeleteProgramPipelines, n, pipelines)
|
DECLARE_GL_FUNCTION_HEAD(void, DeleteProgramPipelines, GLsizei n, const GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, DeleteProgramPipelines, n, pipelines)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GenProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GenProgramPipelines, n, pipelines)
|
DECLARE_GL_FUNCTION_HEAD(void, GenProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenProgramPipelines, n, pipelines)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(GLboolean, IsProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END(GLboolean, IsProgramPipeline, pipeline)
|
DECLARE_GL_FUNCTION_HEAD(GLboolean, IsProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END(GLboolean, IsProgramPipeline, pipeline)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineiv, pipeline, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramPipelineiv, GLuint pipeline, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramPipelineiv, pipeline, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1i, GLuint program, GLint location, GLint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1i, program, location, v0)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform1i, GLuint program, GLint location, GLint v0) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform1i, program, location, v0)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2i, program, location, v0, v1)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform2i, GLuint program, GLint location, GLint v0, GLint v1) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform2i, program, location, v0, v1)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3i, program, location, v0, v1, v2)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniform3i, GLuint program, GLint location, GLint v0, GLint v1, GLint v2) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniform3i, program, location, v0, v1, v2)
|
||||||
@@ -358,8 +359,8 @@ DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix2x4fv, GLuint program, GLint
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x2fv, program, location, count, transpose, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x2fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x2fv, program, location, count, transpose, value)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix3x4fv, program, location, count, transpose, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix3x4fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix3x4fv, program, location, count, transpose, value)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x3fv, program, location, count, transpose, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ProgramUniformMatrix4x3fv, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramUniformMatrix4x3fv, program, location, count, transpose, value)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ValidateProgramPipeline, pipeline)
|
DECLARE_GL_FUNCTION_HEAD(void, ValidateProgramPipeline, GLuint pipeline) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ValidateProgramPipeline, pipeline)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog)
|
DECLARE_GL_FUNCTION_HEAD(void, GetProgramPipelineInfoLog, GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramPipelineInfoLog, pipeline, bufSize, length, infoLog)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format)
|
DECLARE_GL_FUNCTION_HEAD(void, BindImageTexture, GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindImageTexture, unit, texture, level, layered, layer, access, format)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBooleani_v, target, index, data)
|
DECLARE_GL_FUNCTION_HEAD(void, GetBooleani_v, GLenum target, GLuint index, GLboolean* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetBooleani_v, target, index, data)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrier, barriers)
|
DECLARE_GL_FUNCTION_HEAD(void, MemoryBarrier, GLbitfield barriers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, MemoryBarrier, barriers)
|
||||||
@@ -996,7 +997,7 @@ DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsIndirect, GLenum mode, GLenum ty
|
|||||||
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocationIndex, program, programInterface, name)
|
DECLARE_GL_FUNCTION_HEAD(GLint, GetProgramResourceLocationIndex, GLuint program, GLenum programInterface, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetProgramResourceLocationIndex, program, programInterface, name)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding)
|
DECLARE_GL_FUNCTION_HEAD(void, ShaderStorageBlockBinding, GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ShaderStorageBlockBinding, program, storageBlockIndex, storageBlockBinding)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureView, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureView, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, TextureView, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TextureView, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexAttribLFormat, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexAttribLFormat, attribindex, size, type, relativeoffset)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
|
DECLARE_GL_FUNCTION_HEAD(void, BufferStorage, GLenum target, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BufferStorage, target, size, data, flags)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ClearTexImage, GLuint texture, GLint level, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexImage, texture, level, format, type, data)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearTexImage, GLuint texture, GLint level, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexImage, texture, level, format, type, data)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ClearTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearTexSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearTexSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, data)
|
||||||
@@ -1007,12 +1008,12 @@ DECLARE_GL_FUNCTION_HEAD(void, BindSamplers, GLuint first, GLsizei count, const
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindImageTextures, GLuint first, GLsizei count, const GLuint* textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindImageTextures, first, count, textures)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, BindImageTextures, GLuint first, GLsizei count, const GLuint* textures) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindImageTextures, first, count, textures)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, BindVertexBuffers, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets, const GLsizei* strides) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindVertexBuffers, first, count, buffers, offsets, strides)
|
DECLARE_GL_FUNCTION_HEAD(void, BindVertexBuffers, GLuint first, GLsizei count, const GLuint* buffers, const GLintptr* offsets, const GLsizei* strides) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindVertexBuffers, first, count, buffers, offsets, strides)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClipControl, GLenum origin, GLenum depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClipControl, origin, depth)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClipControl, GLenum origin, GLenum depth) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClipControl, origin, depth)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateTransformFeedbacks, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateTransformFeedbacks, n, ids)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateTransformFeedbacks, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateTransformFeedbacks, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackBufferBase, GLuint xfb, GLuint index, GLuint buffer) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TransformFeedbackBufferBase, xfb, index, buffer)
|
DECLARE_GL_FUNCTION_HEAD(void, TransformFeedbackBufferBase, GLuint xfb, GLuint index, GLuint buffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TransformFeedbackBufferBase, xfb, index, buffer)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, TransformFeedbackBufferRange, GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, TransformFeedbackBufferRange, xfb, index, buffer, offset, size)
|
DECLARE_GL_FUNCTION_HEAD(void, TransformFeedbackBufferRange, GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TransformFeedbackBufferRange, xfb, index, buffer, offset, size)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbackiv, GLuint xfb, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbackiv, xfb, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTransformFeedbackiv, GLuint xfb, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTransformFeedbackiv, xfb, pname, param)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki_v, GLuint xfb, GLenum pname, GLuint index, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki_v, xfb, pname, index, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTransformFeedbacki_v, GLuint xfb, GLenum pname, GLuint index, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTransformFeedbacki_v, xfb, pname, index, param)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetTransformFeedbacki64_v, GLuint xfb, GLenum pname, GLuint index, GLint64* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetTransformFeedbacki64_v, xfb, pname, index, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTransformFeedbacki64_v, GLuint xfb, GLenum pname, GLuint index, GLint64* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTransformFeedbacki64_v, xfb, pname, index, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateBuffers, n, buffers)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateBuffers, GLsizei n, GLuint* buffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateBuffers, n, buffers)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferStorage, GLuint buffer, GLsizeiptr size, const void* data, GLbitfield flags) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferStorage, buffer, size, data, flags)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedBufferData, GLuint buffer, GLsizeiptr size, const void* data, GLenum usage) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedBufferData, buffer, size, data, usage)
|
||||||
@@ -1025,24 +1026,24 @@ DECLARE_GL_FUNCTION_HEAD(void, FlushMappedNamedBufferRange, GLuint buffer, GLint
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteriv, GLuint buffer, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteriv, buffer, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferParameteri64v, GLuint buffer, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferParameteri64v, buffer, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferPointerv, GLuint buffer, GLenum pname, void** params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferPointerv, buffer, pname, params)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedBufferSubData, buffer, offset, size, data)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedBufferSubData, GLuint buffer, GLintptr offset, GLsizeiptr size, void* data) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedBufferSubData, buffer, offset, size, data)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateFramebuffers, GLsizei n, GLuint* framebuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateFramebuffers, n, framebuffers)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferRenderbuffer, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferRenderbuffer, framebuffer, attachment, renderbuffertarget, renderbuffer)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, NamedFramebufferParameteri, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, NamedFramebufferParameteri, framebuffer, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferParameteri, GLuint framebuffer, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferParameteri, framebuffer, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTexture, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTexture, framebuffer, attachment, texture, level)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTextureLayer, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTextureLayer, framebuffer, attachment, texture, level, layer)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferTextureLayer, GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferTextureLayer, framebuffer, attachment, texture, level, layer)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffer, GLuint framebuffer, GLenum buf) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffer, framebuffer, buf)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferDrawBuffers, GLuint framebuffer, GLsizei n, const GLenum* bufs) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferDrawBuffers, framebuffer, n, bufs)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedFramebufferReadBuffer, GLuint framebuffer, GLenum src) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedFramebufferReadBuffer, framebuffer, src)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferData, framebuffer, numAttachments, attachments)
|
DECLARE_GL_FUNCTION_HEAD(void, InvalidateNamedFramebufferData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_END_NO_RETURN(void, InvalidateNamedFramebufferData, framebuffer, numAttachments, attachments)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateNamedFramebufferSubData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateNamedFramebufferSubData, framebuffer, numAttachments, attachments, x, y, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, InvalidateNamedFramebufferSubData, GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, InvalidateNamedFramebufferSubData, framebuffer, numAttachments, attachments, x, y, width, height)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferiv, framebuffer, buffer, drawbuffer, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferiv, framebuffer, buffer, drawbuffer, value)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, ClearNamedFramebufferuiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ClearNamedFramebufferuiv, framebuffer, buffer, drawbuffer, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferuiv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferuiv, framebuffer, buffer, drawbuffer, value)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfv, GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfv, framebuffer, buffer, drawbuffer, value)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil)
|
DECLARE_GL_FUNCTION_HEAD(void, ClearNamedFramebufferfi, GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ClearNamedFramebufferfi, framebuffer, buffer, drawbuffer, depth, stencil)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter)
|
DECLARE_GL_FUNCTION_HEAD(void, BlitNamedFramebuffer, GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BlitNamedFramebuffer, readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter)
|
||||||
DECLARE_GL_FUNCTION_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target)
|
DECLARE_GL_FUNCTION_HEAD(GLenum, CheckNamedFramebufferStatus, GLuint framebuffer, GLenum target) DECLARE_GL_FUNCTION_END(GLenum, CheckNamedFramebufferStatus, framebuffer, target)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetNamedFramebufferParameteriv, GLuint framebuffer, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetNamedFramebufferParameteriv, framebuffer, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferParameteriv, GLuint framebuffer, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferParameteriv, framebuffer, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetNamedFramebufferAttachmentParameteriv, GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetNamedFramebufferAttachmentParameteriv, framebuffer, attachment, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateRenderbuffers, GLsizei n, GLuint* renderbuffers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateRenderbuffers, n, renderbuffers)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, NamedRenderbufferStorage, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, NamedRenderbufferStorage, renderbuffer, internalformat, width, height)
|
||||||
@@ -1062,9 +1063,9 @@ DECLARE_GL_FUNCTION_HEAD(void, TextureSubImage3D, GLuint texture, GLint level, G
|
|||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage1D, texture, level, xoffset, width, format, imageSize, data)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage2D, texture, level, xoffset, yoffset, width, height, format, imageSize, data)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, CompressedTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CompressedTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage1D, texture, level, xoffset, x, y, width)
|
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage1D, GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage1D, texture, level, xoffset, x, y, width)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage2D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage2D, texture, level, xoffset, yoffset, x, y, width, height)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height)
|
DECLARE_GL_FUNCTION_HEAD(void, CopyTextureSubImage3D, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CopyTextureSubImage3D, texture, level, xoffset, yoffset, zoffset, x, y, width, height)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, TextureParameterf, GLuint texture, GLenum pname, GLfloat param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterf, texture, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterfv, texture, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, TextureParameterfv, GLuint texture, GLenum pname, const GLfloat* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameterfv, texture, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, TextureParameteri, GLuint texture, GLenum pname, GLint param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TextureParameteri, texture, pname, param)
|
||||||
@@ -1074,7 +1075,7 @@ DECLARE_GL_FUNCTION_HEAD(void, TextureParameteriv, GLuint texture, GLenum pname,
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenerateTextureMipmap, texture)
|
DECLARE_GL_FUNCTION_HEAD(void, GenerateTextureMipmap, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GenerateTextureMipmap, texture)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTextureUnit, unit, texture)
|
DECLARE_GL_FUNCTION_HEAD(void, BindTextureUnit, GLuint unit, GLuint texture) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindTextureUnit, unit, texture)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTextureImage, GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureImage, texture, level, format, type, bufSize, pixels)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureImage, GLuint texture, GLint level, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureImage, texture, level, bufSize, pixels)
|
DECLARE_GL_FUNCTION_HEAD(void, GetCompressedTextureImage, GLuint texture, GLint level, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetCompressedTextureImage, texture, level, bufSize, pixels)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameterfv, GLuint texture, GLint level, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameterfv, texture, level, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTextureLevelParameteriv, GLuint texture, GLint level, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureLevelParameteriv, texture, level, pname, params)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTextureParameterfv, GLuint texture, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureParameterfv, texture, pname, params)
|
||||||
@@ -1090,18 +1091,18 @@ DECLARE_GL_FUNCTION_HEAD(void, VertexArrayVertexBuffers, GLuint vaobj, GLuint fi
|
|||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribBinding, GLuint vaobj, GLuint attribindex, GLuint bindingindex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribBinding, vaobj, attribindex, bindingindex)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribBinding, GLuint vaobj, GLuint attribindex, GLuint bindingindex) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribBinding, vaobj, attribindex, bindingindex)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribFormat, vaobj, attribindex, size, type, normalized, relativeoffset)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribIFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribIFormat, vaobj, attribindex, size, type, relativeoffset)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, VertexArrayAttribLFormat, vaobj, attribindex, size, type, relativeoffset)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayAttribLFormat, GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayAttribLFormat, vaobj, attribindex, size, type, relativeoffset)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayBindingDivisor, GLuint vaobj, GLuint bindingindex, GLuint divisor) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayBindingDivisor, vaobj, bindingindex, divisor)
|
DECLARE_GL_FUNCTION_HEAD(void, VertexArrayBindingDivisor, GLuint vaobj, GLuint bindingindex, GLuint divisor) DECLARE_GL_FUNCTION_END_NO_RETURN(void, VertexArrayBindingDivisor, vaobj, bindingindex, divisor)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayiv, vaobj, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexediv, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexediv, vaobj, index, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexediv, GLuint vaobj, GLuint index, GLenum pname, GLint* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexediv, vaobj, index, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexed64iv, GLuint vaobj, GLuint index, GLenum pname, GLint64* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexed64iv, vaobj, index, pname, param)
|
DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayIndexed64iv, GLuint vaobj, GLuint index, GLenum pname, GLint64* param) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetVertexArrayIndexed64iv, vaobj, index, pname, param)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CreateSamplers, GLsizei n, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateSamplers, n, samplers)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateSamplers, GLsizei n, GLuint* samplers) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateSamplers, n, samplers)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, CreateProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, CreateProgramPipelines, n, pipelines)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateProgramPipelines, GLsizei n, GLuint* pipelines) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateProgramPipelines, n, pipelines)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, CreateQueries, GLenum target, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateQueries, target, n, ids)
|
DECLARE_GL_FUNCTION_HEAD(void, CreateQueries, GLenum target, GLsizei n, GLuint* ids) DECLARE_GL_FUNCTION_END_NO_RETURN(void, CreateQueries, target, n, ids)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjecti64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjecti64v, id, buffer, pname, offset)
|
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjecti64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjecti64v, id, buffer, pname, offset)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectiv, id, buffer, pname, offset)
|
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjectiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjectiv, id, buffer, pname, offset)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectui64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectui64v, id, buffer, pname, offset)
|
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjectui64v, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjectui64v, id, buffer, pname, offset)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetQueryBufferObjectuiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetQueryBufferObjectuiv, id, buffer, pname, offset)
|
DECLARE_GL_FUNCTION_HEAD(void, GetQueryBufferObjectuiv, GLuint id, GLuint buffer, GLenum pname, GLintptr offset) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryBufferObjectuiv, id, buffer, pname, offset)
|
||||||
DECLARE_GL_FUNCTION_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels)
|
DECLARE_GL_FUNCTION_HEAD(void, GetTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, format, type, bufSize, pixels)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, bufSize, pixels)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetCompressedTextureSubImage, GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetCompressedTextureSubImage, texture, level, xoffset, yoffset, zoffset, width, height, depth, bufSize, pixels)
|
||||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnCompressedTexImage, GLenum target, GLint lod, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnCompressedTexImage, target, lod, bufSize, pixels)
|
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetnCompressedTexImage, GLenum target, GLint lod, GLsizei bufSize, void* pixels) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetnCompressedTexImage, target, lod, bufSize, pixels)
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -58,7 +58,19 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void NamedFramebufferReadBuffer(GLuint framebuffer, GLenum src);
|
void NamedFramebufferReadBuffer(GLuint framebuffer, GLenum src);
|
||||||
void ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
void ClearNamedFramebufferfv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat* value);
|
||||||
void ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
void ClearNamedFramebufferfi(GLuint framebuffer, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil);
|
||||||
|
void InvalidateNamedFramebufferData(GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments);
|
||||||
|
void InvalidateNamedFramebufferSubData(GLuint framebuffer, GLsizei numAttachments, const GLenum* attachments,
|
||||||
|
GLint x, GLint y, GLsizei width, GLsizei height);
|
||||||
|
void InvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments);
|
||||||
|
void InvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y,
|
||||||
|
GLsizei width, GLsizei height);
|
||||||
|
void ClearNamedFramebufferiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint* value);
|
||||||
|
void ClearNamedFramebufferuiv(GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint* value);
|
||||||
GLenum CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target);
|
GLenum CheckNamedFramebufferStatus(GLuint framebuffer, GLenum target);
|
||||||
|
void GetFramebufferParameteriv(GLenum target, GLenum pname, GLint* params);
|
||||||
|
void FramebufferParameteri(GLenum target, GLenum pname, GLint param);
|
||||||
|
void GetNamedFramebufferParameteriv(GLuint framebuffer, GLenum pname, GLint* params);
|
||||||
|
void NamedFramebufferParameteri(GLuint framebuffer, GLenum pname, GLint param);
|
||||||
void GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params);
|
void GetNamedFramebufferAttachmentParameteriv(GLuint framebuffer, GLenum attachment, GLenum pname, GLint* params);
|
||||||
void BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1,
|
void BlitNamedFramebuffer(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1,
|
||||||
GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
|
GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
// End of Source File Header
|
// End of Source File Header
|
||||||
|
|
||||||
#include "Validators.h"
|
#include "Validators.h"
|
||||||
|
#include <MG_Backend/BackendObjects.h>
|
||||||
#include <MG_State/GLState/Core.h>
|
#include <MG_State/GLState/Core.h>
|
||||||
#include <MG_State/GLState/ErrorState/Error.h>
|
#include <MG_State/GLState/ErrorState/Error.h>
|
||||||
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||||
@@ -60,6 +61,26 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool ValidateColorAttachmentInRange(FramebufferAttachmentType attachment, const char* caller) {
|
||||||
|
const auto first = static_cast<SizeT>(FramebufferAttachmentType::Color0);
|
||||||
|
const auto index = static_cast<SizeT>(attachment);
|
||||||
|
if (index < first) return true;
|
||||||
|
const auto colorIndex = index - first;
|
||||||
|
const auto limit = static_cast<SizeT>(
|
||||||
|
MG_Backend::pActiveBackendObject ? MG_Backend::pActiveBackendObject->GetDynamicParameters()
|
||||||
|
.MaxColorAttachments
|
||||||
|
: static_cast<Int>(MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS));
|
||||||
|
if (colorIndex >= limit) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
std::format("Colour attachment {} is beyond GL_MAX_COLOR_ATTACHMENTS ({}).", colorIndex, limit)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Bool ValidateRenderbufferTarget(RenderbufferTarget target) {
|
Bool ValidateRenderbufferTarget(RenderbufferTarget target) {
|
||||||
if (target == RenderbufferTarget::Unknown) {
|
if (target == RenderbufferTarget::Unknown) {
|
||||||
using namespace MG_Util;
|
using namespace MG_Util;
|
||||||
@@ -97,4 +118,100 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
|
|||||||
std::format("Renderbuffer name {} is not valid.", index)));
|
std::format("Renderbuffer name {} is not valid.", index)));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool ValidateFramebufferParameterPname(GLenum pname, Bool isDefaultFramebuffer, Bool forSetter,
|
||||||
|
const char* caller) {
|
||||||
|
Bool isDefaultParameter = false;
|
||||||
|
switch (pname) {
|
||||||
|
case GL_FRAMEBUFFER_DEFAULT_WIDTH:
|
||||||
|
case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
|
||||||
|
case GL_FRAMEBUFFER_DEFAULT_LAYERS:
|
||||||
|
case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
|
||||||
|
case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
|
||||||
|
isDefaultParameter = true;
|
||||||
|
break;
|
||||||
|
case GL_DOUBLEBUFFER:
|
||||||
|
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
|
||||||
|
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
|
||||||
|
case GL_SAMPLES:
|
||||||
|
case GL_SAMPLE_BUFFERS:
|
||||||
|
case GL_STEREO:
|
||||||
|
// Queryable only; glFramebufferParameteri sets none of these.
|
||||||
|
if (forSetter) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
std::format("pname {} is not settable on a framebuffer.",
|
||||||
|
MG_Util::ConvertGLEnumToString(pname))));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
std::format("pname {} is not a framebuffer parameter.",
|
||||||
|
MG_Util::ConvertGLEnumToString(pname))));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default framebuffer has no DEFAULT_* state of its own - its shape comes from the
|
||||||
|
// surface - so those names are accepted enums it simply cannot answer or accept.
|
||||||
|
if (isDefaultFramebuffer && isDefaultParameter) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
std::format("pname {} does not apply to the default framebuffer.",
|
||||||
|
MG_Util::ConvertGLEnumToString(pname))));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool ValidateReadFramebufferForCopy(const char* caller) {
|
||||||
|
auto& framebufferObject =
|
||||||
|
MG_State::pGLContext->GetFramebufferBindingSlot(FramebufferTarget::Read).GetBoundObject();
|
||||||
|
if (!framebufferObject || !framebufferObject->CheckCompleteness()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidFramebufferOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
"Read framebuffer is not framebuffer complete."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FramebufferAttachmentType readBuffer = framebufferObject->GetReadBuffer();
|
||||||
|
if (readBuffer == FramebufferAttachmentType::None ||
|
||||||
|
!framebufferObject->GetAttachment(readBuffer).IsValid()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
"Read buffer names no attachment of the read framebuffer."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAMPLE_BUFFERS is one whenever the read buffer resolves to multisample storage. A
|
||||||
|
// multisample texture says so by its target - its sample count can legally be one - while a
|
||||||
|
// renderbuffer says so by having been given a non-zero sample count.
|
||||||
|
const auto& readAttachment = framebufferObject->GetAttachment(readBuffer);
|
||||||
|
Bool isMultisampled = false;
|
||||||
|
if (readAttachment.IsRenderbuffer() && readAttachment.GetRenderbuffer()) {
|
||||||
|
isMultisampled = readAttachment.GetRenderbuffer()->GetSamples() > 0;
|
||||||
|
} else if (readAttachment.IsTexture() && readAttachment.GetTexture()) {
|
||||||
|
const auto target = readAttachment.GetTexture()->GetTarget();
|
||||||
|
isMultisampled = target == TextureTarget::Texture2DMultisample ||
|
||||||
|
target == TextureTarget::Texture2DMultisampleArray;
|
||||||
|
}
|
||||||
|
if (isMultisampled) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl/FramebufferImpl", caller,
|
||||||
|
"Cannot copy from a multisampled read framebuffer."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
|
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
|
||||||
|
|||||||
@@ -14,6 +14,21 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
|
|||||||
Bool ValidateFramebufferTarget(FramebufferTarget target);
|
Bool ValidateFramebufferTarget(FramebufferTarget target);
|
||||||
Bool ValidateFramebufferName(Uint index, Bool allowZero = true);
|
Bool ValidateFramebufferName(Uint index, Bool allowZero = true);
|
||||||
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
|
Bool ValidateFramebufferAttachmentType(FramebufferAttachmentType attachment);
|
||||||
|
// GL_COLOR_ATTACHMENTn is a token per n up to 31, but only the first GL_MAX_COLOR_ATTACHMENTS of
|
||||||
|
// them name an attachment point of a framebuffer object; the rest are INVALID_OPERATION for the
|
||||||
|
// attaching entry points (GL 4.6 core 9.2.7). Non-colour attachments pass through unchanged.
|
||||||
|
Bool ValidateColorAttachmentInRange(FramebufferAttachmentType attachment, const char* caller);
|
||||||
Bool ValidateRenderbufferTarget(RenderbufferTarget target);
|
Bool ValidateRenderbufferTarget(RenderbufferTarget target);
|
||||||
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
|
Bool ValidateRenderbufferName(Uint index, Bool allowZero = true);
|
||||||
|
// The read-framebuffer preconditions the CopyTexSubImage family shares (GL 4.6 core 8.6): the
|
||||||
|
// read framebuffer must be complete, its read buffer must name a real attachment, and it must
|
||||||
|
// not be multisampled. Incompleteness is INVALID_FRAMEBUFFER_OPERATION, the other two are
|
||||||
|
// INVALID_OPERATION.
|
||||||
|
Bool ValidateReadFramebufferForCopy(const char* caller);
|
||||||
|
// The pname sets of glGet/FramebufferParameteri (GL 4.6 core 9.2.3). Order matters and is part
|
||||||
|
// of the contract: a name outside the table is INVALID_ENUM, and only then is a name that the
|
||||||
|
// DEFAULT framebuffer does not answer INVALID_OPERATION. Testing the framebuffer kind first
|
||||||
|
// would turn GL_FRAMEBUFFER_DEFAULT_WIDTH on framebuffer zero into the wrong error.
|
||||||
|
Bool ValidateFramebufferParameterPname(GLenum pname, Bool isDefaultFramebuffer, Bool forSetter,
|
||||||
|
const char* caller);
|
||||||
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
|
} // namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl
|
||||||
|
|||||||
@@ -671,6 +671,40 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (target) {
|
switch (target) {
|
||||||
|
// The vertex buffer binding points of the vertex array object that is bound. Indexed by
|
||||||
|
// binding point, not by attribute (GL 4.6 core 10.3.1).
|
||||||
|
case GL_VERTEX_BINDING_BUFFER:
|
||||||
|
case GL_VERTEX_BINDING_DIVISOR:
|
||||||
|
case GL_VERTEX_BINDING_OFFSET:
|
||||||
|
case GL_VERTEX_BINDING_STRIDE: {
|
||||||
|
if (index >= VertexArrayImpl::GetMaxVertexAttribBindings()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||||
|
"Vertex buffer binding index is out of range."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& vao = MG_State::pGLContext->GetBoundVertexArray();
|
||||||
|
if (!vao) {
|
||||||
|
*data = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& binding = vao->GetBindingPoint(index);
|
||||||
|
switch (target) {
|
||||||
|
case GL_VERTEX_BINDING_BUFFER:
|
||||||
|
*data = binding.Buffer ? static_cast<GLint>(binding.Buffer->GetExternalIndex()) : 0;
|
||||||
|
return;
|
||||||
|
case GL_VERTEX_BINDING_DIVISOR:
|
||||||
|
*data = static_cast<GLint>(binding.Divisor);
|
||||||
|
return;
|
||||||
|
case GL_VERTEX_BINDING_OFFSET:
|
||||||
|
*data = static_cast<GLint>(binding.Offset);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
*data = static_cast<GLint>(binding.Stride);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
case GL_IMAGE_BINDING_NAME:
|
case GL_IMAGE_BINDING_NAME:
|
||||||
case GL_IMAGE_BINDING_LEVEL:
|
case GL_IMAGE_BINDING_LEVEL:
|
||||||
case GL_IMAGE_BINDING_LAYERED:
|
case GL_IMAGE_BINDING_LAYERED:
|
||||||
@@ -1398,7 +1432,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = 0; // program-binary entrypoints are stubbed
|
*params = 0; // program-binary entrypoints are stubbed
|
||||||
return;
|
return;
|
||||||
case GL_PROGRAM_PIPELINE_BINDING:
|
case GL_PROGRAM_PIPELINE_BINDING:
|
||||||
*params = 0; // program-pipeline entrypoints are stubbed
|
*params = static_cast<GLint>(MG_State::pGLContext->GetBoundProgramPipelineName());
|
||||||
return;
|
return;
|
||||||
case GL_PROGRAM_POINT_SIZE:
|
case GL_PROGRAM_POINT_SIZE:
|
||||||
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE;
|
*params = MG_State::pGLContext->IsCapabilityEnabled(CapabilityInput::ProgramPointSize) ? GL_TRUE : GL_FALSE;
|
||||||
@@ -1738,20 +1772,22 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
*params = vao ? static_cast<GLint>(vao->GetExternalIndex()) : 0;
|
*params = vao ? static_cast<GLint>(vao->GetExternalIndex()) : 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// The vertex buffer binding points are per-binding-index state, so the non-indexed getter
|
||||||
|
// has nothing to answer with (GL 4.6 core table 23.4).
|
||||||
|
case GL_VERTEX_BINDING_BUFFER:
|
||||||
case GL_VERTEX_BINDING_DIVISOR:
|
case GL_VERTEX_BINDING_DIVISOR:
|
||||||
*params = 0; // vertex-binding entrypoints are stubbed
|
|
||||||
return;
|
|
||||||
case GL_VERTEX_BINDING_OFFSET:
|
case GL_VERTEX_BINDING_OFFSET:
|
||||||
*params = 0; // vertex-binding entrypoints are stubbed
|
|
||||||
return;
|
|
||||||
case GL_VERTEX_BINDING_STRIDE:
|
case GL_VERTEX_BINDING_STRIDE:
|
||||||
*params = 0; // vertex-binding entrypoints are stubbed
|
RecordIndexedOnlyGetterError(__func__, pname);
|
||||||
return;
|
return;
|
||||||
case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET:
|
case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET:
|
||||||
*params = 0; // vertex-binding entrypoints are stubbed
|
*params = static_cast<GLint>(VertexArrayImpl::GetMaxVertexAttribRelativeOffset());
|
||||||
return;
|
return;
|
||||||
case GL_MAX_VERTEX_ATTRIB_BINDINGS:
|
case GL_MAX_VERTEX_ATTRIB_BINDINGS:
|
||||||
*params = 0; // vertex-binding entrypoints are stubbed
|
*params = static_cast<GLint>(VertexArrayImpl::GetMaxVertexAttribBindings());
|
||||||
|
return;
|
||||||
|
case GL_MAX_VERTEX_ATTRIB_STRIDE:
|
||||||
|
*params = static_cast<GLint>(VertexArrayImpl::GetMaxVertexAttribStride());
|
||||||
return;
|
return;
|
||||||
case GL_VIEWPORT: {
|
case GL_VIEWPORT: {
|
||||||
const auto& vp = MG_State::pGLContext->GetViewport();
|
const auto& vp = MG_State::pGLContext->GetViewport();
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.cpp
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#include "GL_ProgramPipeline.h"
|
||||||
|
|
||||||
|
#include <MG_State/GLState/Core.h>
|
||||||
|
#include <MG_State/GLState/ErrorState/ErrorInfo.h>
|
||||||
|
#include <MG_Util/Converters/GLToStr/GLEnumConverter.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Impl::GLImpl {
|
||||||
|
namespace {
|
||||||
|
void RecordPipelineError(ErrorCode code, const char* function, String message) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
code, MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", function, Move(message)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A pipeline name only names an object once it has been bound or created; querying a
|
||||||
|
// reserved-but-unmaterialised name is INVALID_OPERATION (GL 4.6 core 7.4).
|
||||||
|
const SharedPtr<MG_State::GLState::ProgramPipelineObject>* TryGetPipeline(GLuint pipeline,
|
||||||
|
const char* function) {
|
||||||
|
if (!MG_State::pGLContext->IsProgramPipelineObject(pipeline)) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidOperation, function,
|
||||||
|
std::format("Program pipeline {} does not exist.", pipeline));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return &MG_State::pGLContext->GetProgramPipelineObject(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool ValidatePipelineCount(GLsizei n, const char* function) {
|
||||||
|
if (n < 0) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, function, "n must be non-negative.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GL 4.6 core table 7.1 maps each stage bit onto a shader stage.
|
||||||
|
Bool TryResolveStageBit(GLbitfield bit, ShaderStage& outStage) {
|
||||||
|
switch (bit) {
|
||||||
|
case GL_VERTEX_SHADER_BIT: outStage = ShaderStage::Vertex; return true;
|
||||||
|
case GL_TESS_CONTROL_SHADER_BIT: outStage = ShaderStage::TessControl; return true;
|
||||||
|
case GL_TESS_EVALUATION_SHADER_BIT: outStage = ShaderStage::TessEval; return true;
|
||||||
|
case GL_GEOMETRY_SHADER_BIT: outStage = ShaderStage::Geometry; return true;
|
||||||
|
case GL_FRAGMENT_SHADER_BIT: outStage = ShaderStage::Fragment; return true;
|
||||||
|
case GL_COMPUTE_SHADER_BIT: outStage = ShaderStage::Compute; return true;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr GLbitfield kAllStageBits = GL_VERTEX_SHADER_BIT | GL_TESS_CONTROL_SHADER_BIT |
|
||||||
|
GL_TESS_EVALUATION_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
|
||||||
|
GL_FRAGMENT_SHADER_BIT | GL_COMPUTE_SHADER_BIT;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void GenProgramPipelines(GLsizei n, GLuint* pipelines) {
|
||||||
|
if (!ValidatePipelineCount(n, __func__)) return;
|
||||||
|
if (n == 0 || !pipelines) return;
|
||||||
|
|
||||||
|
static thread_local Vector<GLuint> names;
|
||||||
|
MG_State::pGLContext->GenProgramPipelineNames(static_cast<Uint>(n), names);
|
||||||
|
Memcpy(pipelines, names.data(), static_cast<SizeT>(n) * sizeof(GLuint));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateProgramPipelines(GLsizei n, GLuint* pipelines) {
|
||||||
|
if (!ValidatePipelineCount(n, __func__)) return;
|
||||||
|
if (n == 0 || !pipelines) return;
|
||||||
|
|
||||||
|
static thread_local Vector<GLuint> names;
|
||||||
|
MG_State::pGLContext->GenProgramPipelineNames(static_cast<Uint>(n), names);
|
||||||
|
for (GLsizei i = 0; i < n; ++i) {
|
||||||
|
pipelines[i] = names[static_cast<SizeT>(i)];
|
||||||
|
MG_State::pGLContext->CreateProgramPipelineObject(names[static_cast<SizeT>(i)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeleteProgramPipelines(GLsizei n, const GLuint* pipelines) {
|
||||||
|
if (!ValidatePipelineCount(n, __func__)) return;
|
||||||
|
if (!pipelines) return;
|
||||||
|
|
||||||
|
for (GLsizei i = 0; i < n; ++i) {
|
||||||
|
// Deleting zero, an unknown name, or a name that was only reserved is silently ignored.
|
||||||
|
MG_State::pGLContext->MarkProgramPipelineForDeletion(pipelines[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BindProgramPipeline(GLuint pipeline) {
|
||||||
|
if (pipeline != 0 && !MG_State::pGLContext->ValidateProgramPipelineName(pipeline)) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||||
|
std::format("Program pipeline name {} is not valid.", pipeline));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MG_State::pGLContext->BindProgramPipelineObject(pipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLboolean IsProgramPipeline(GLuint pipeline) {
|
||||||
|
return MG_State::pGLContext->IsProgramPipelineObject(pipeline) ? GL_TRUE : GL_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint* params) {
|
||||||
|
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||||
|
if (!pipelineObject || !params) return;
|
||||||
|
|
||||||
|
const auto stageProgramName = [&](ShaderStage stage) -> GLint {
|
||||||
|
const auto& program = (*pipelineObject)->GetStageProgram(stage);
|
||||||
|
return program ? static_cast<GLint>(program->GetExternalIndex()) : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (pname) {
|
||||||
|
case GL_ACTIVE_PROGRAM: {
|
||||||
|
const auto& active = (*pipelineObject)->GetActiveProgram();
|
||||||
|
*params = active ? static_cast<GLint>(active->GetExternalIndex()) : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GL_VERTEX_SHADER: *params = stageProgramName(ShaderStage::Vertex); break;
|
||||||
|
case GL_TESS_CONTROL_SHADER: *params = stageProgramName(ShaderStage::TessControl); break;
|
||||||
|
case GL_TESS_EVALUATION_SHADER: *params = stageProgramName(ShaderStage::TessEval); break;
|
||||||
|
case GL_GEOMETRY_SHADER: *params = stageProgramName(ShaderStage::Geometry); break;
|
||||||
|
case GL_FRAGMENT_SHADER: *params = stageProgramName(ShaderStage::Fragment); break;
|
||||||
|
case GL_COMPUTE_SHADER: *params = stageProgramName(ShaderStage::Compute); break;
|
||||||
|
case GL_VALIDATE_STATUS: *params = (*pipelineObject)->GetValidateStatus() ? GL_TRUE : GL_FALSE; break;
|
||||||
|
case GL_INFO_LOG_LENGTH: {
|
||||||
|
// GL counts the null terminator, and reports 0 rather than 1 for an empty log.
|
||||||
|
const auto& log = (*pipelineObject)->GetInfoLog();
|
||||||
|
*params = log.empty() ? 0 : static_cast<GLint>(log.length()) + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
RecordPipelineError(ErrorCode::InvalidEnum, __func__,
|
||||||
|
std::format("pname {} is not a program pipeline parameter.",
|
||||||
|
MG_Util::ConvertGLEnumToString(pname)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog) {
|
||||||
|
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||||
|
if (!pipelineObject) return;
|
||||||
|
if (bufSize < 0) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__, "bufSize must be non-negative.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bufSize == 0 || !infoLog) {
|
||||||
|
if (length) *length = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& log = (*pipelineObject)->GetInfoLog();
|
||||||
|
const auto copied = std::min<GLsizei>(bufSize - 1, static_cast<GLsizei>(log.length()));
|
||||||
|
if (copied > 0) Memcpy(infoLog, log.data(), static_cast<SizeT>(copied));
|
||||||
|
infoLog[copied] = '\0';
|
||||||
|
if (length) *length = copied;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program) {
|
||||||
|
if (stages != GL_ALL_SHADER_BITS && (stages & ~kAllStageBits) != 0) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__, "stages names a bit that is not a shader stage.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||||
|
if (!pipelineObject) return;
|
||||||
|
|
||||||
|
SharedPtr<MG_State::GLState::ProgramObject> programObject;
|
||||||
|
if (program != 0) {
|
||||||
|
if (!MG_State::pGLContext->ValidateProgramName(program)) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||||
|
std::format("{} is not the name of a program object.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||||
|
if (!programObject) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||||
|
std::format("{} is not the name of a program object.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!programObject->GetLinkStatus()) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||||
|
std::format("Program {} has not been linked successfully.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const GLbitfield selected = stages == GL_ALL_SHADER_BITS ? kAllStageBits : stages;
|
||||||
|
for (GLbitfield bit = 1; bit != 0 && bit <= kAllStageBits; bit <<= 1) {
|
||||||
|
if ((selected & bit) == 0) continue;
|
||||||
|
ShaderStage stage = ShaderStage::Unknown;
|
||||||
|
if (!TryResolveStageBit(bit, stage)) continue;
|
||||||
|
// program == 0 clears the stage, which is what a null program reference means here.
|
||||||
|
(*pipelineObject)->SetStageProgram(stage, programObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActiveShaderProgram(GLuint pipeline, GLuint program) {
|
||||||
|
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||||
|
if (!pipelineObject) return;
|
||||||
|
|
||||||
|
if (program == 0) {
|
||||||
|
(*pipelineObject)->SetActiveProgram(nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!MG_State::pGLContext->ValidateProgramName(program)) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||||
|
std::format("{} is not the name of a program object.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto programObject = MG_State::pGLContext->GetProgramObject(program);
|
||||||
|
if (!programObject) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidValue, __func__,
|
||||||
|
std::format("{} is not the name of a program object.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!programObject->GetLinkStatus()) {
|
||||||
|
RecordPipelineError(ErrorCode::InvalidOperation, __func__,
|
||||||
|
std::format("Program {} has not been linked successfully.", program));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(*pipelineObject)->SetActiveProgram(programObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateProgramPipeline(GLuint pipeline) {
|
||||||
|
const auto* pipelineObject = TryGetPipeline(pipeline, __func__);
|
||||||
|
if (!pipelineObject) return;
|
||||||
|
// Nothing here can fail today: MobileGL links each stage program on its own, so there is no
|
||||||
|
// cross-stage interface to re-check at validation time. The log stays empty, which GL allows.
|
||||||
|
(*pipelineObject)->SetValidateStatus(true);
|
||||||
|
}
|
||||||
|
} // namespace MobileGL::MG_Impl::GLImpl
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// MobileGL - MobileGL/MG_Impl/GLImpl/Program/GL_ProgramPipeline.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
|
||||||
|
namespace MobileGL::MG_Impl::GLImpl {
|
||||||
|
void GenProgramPipelines(GLsizei n, GLuint* pipelines);
|
||||||
|
void CreateProgramPipelines(GLsizei n, GLuint* pipelines);
|
||||||
|
void DeleteProgramPipelines(GLsizei n, const GLuint* pipelines);
|
||||||
|
void BindProgramPipeline(GLuint pipeline);
|
||||||
|
GLboolean IsProgramPipeline(GLuint pipeline);
|
||||||
|
void GetProgramPipelineiv(GLuint pipeline, GLenum pname, GLint* params);
|
||||||
|
void GetProgramPipelineInfoLog(GLuint pipeline, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
|
||||||
|
void UseProgramStages(GLuint pipeline, GLbitfield stages, GLuint program);
|
||||||
|
void ActiveShaderProgram(GLuint pipeline, GLuint program);
|
||||||
|
void ValidateProgramPipeline(GLuint pipeline);
|
||||||
|
} // namespace MobileGL::MG_Impl::GLImpl
|
||||||
@@ -62,6 +62,34 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", function, message));
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", function, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The by-buffer query getters write the result into a buffer object instead of client
|
||||||
|
// memory. Everything about the query itself - the name, whether it is still active, the
|
||||||
|
// parameter - is checked by GetQueryObjectValue; what is left is the destination, so this
|
||||||
|
// resolves the buffer and confirms the write lands inside it (GL 4.6 core 4.2.1).
|
||||||
|
Bool ResolveQueryResultDestination(GLuint buffer, GLintptr offset, SizeT writeSize, const char* function,
|
||||||
|
SharedPtr<MG_State::GLState::BufferObject>& outBuffer) {
|
||||||
|
if (offset < 0) {
|
||||||
|
RecordQueryError(ErrorCode::InvalidValue, function, "Offset cannot be negative.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!MG_State::pGLContext->ValidateBufferObject(buffer)) {
|
||||||
|
RecordQueryError(ErrorCode::InvalidOperation, function, "Buffer object does not exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto bufferObject = MG_State::pGLContext->GetBufferObject(buffer);
|
||||||
|
if (!bufferObject) {
|
||||||
|
RecordQueryError(ErrorCode::InvalidOperation, function, "Buffer object does not exist.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (static_cast<SizeT>(offset) + writeSize > bufferObject->GetSize()) {
|
||||||
|
RecordQueryError(ErrorCode::InvalidOperation, function,
|
||||||
|
"The query result does not fit in the buffer object at this offset.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
outBuffer = bufferObject;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Callers must hold g_queryObjectsMutex.
|
// Callers must hold g_queryObjectsMutex.
|
||||||
QueryObject* FindQueryObjectLocked(GLuint id) {
|
QueryObject* FindQueryObjectLocked(GLuint id) {
|
||||||
const auto it = g_liveQueryObjects.find(id);
|
const auto it = g_liveQueryObjects.find(id);
|
||||||
@@ -95,8 +123,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shared GetQueryObject* implementation. Returns false when an error
|
// Shared GetQueryObject* implementation. Returns false when an error
|
||||||
// was recorded and no value should be written back.
|
// was recorded and no value should be written back. `outValueProduced`, when given,
|
||||||
Bool GetQueryObjectValue(GLuint id, GLenum pname, const char* function, Uint64& outValue) {
|
// additionally distinguishes "succeeded with a value" from "succeeded but the result is not
|
||||||
|
// ready" - the GL_QUERY_RESULT_NO_WAIT case, where GL_ARB_query_buffer_object says the
|
||||||
|
// destination is left alone rather than written with a placeholder.
|
||||||
|
Bool GetQueryObjectValue(GLuint id, GLenum pname, const char* function, Uint64& outValue,
|
||||||
|
Bool* outValueProduced = nullptr) {
|
||||||
|
if (outValueProduced) *outValueProduced = true;
|
||||||
const std::lock_guard<std::mutex> lock(g_queryObjectsMutex);
|
const std::lock_guard<std::mutex> lock(g_queryObjectsMutex);
|
||||||
auto* queryObject = FindQueryObjectLocked(id);
|
auto* queryObject = FindQueryObjectLocked(id);
|
||||||
if (!queryObject) {
|
if (!queryObject) {
|
||||||
@@ -109,6 +142,41 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (pname) {
|
switch (pname) {
|
||||||
|
case GL_QUERY_TARGET:
|
||||||
|
// The target a query was begun with (or created with, for glCreateQueries) - state
|
||||||
|
// the object has carried all along, GL 4.6 core table 23.35.
|
||||||
|
outValue = queryObject->target;
|
||||||
|
return true;
|
||||||
|
case GL_QUERY_RESULT_NO_WAIT: {
|
||||||
|
if (queryObject->resultCached) {
|
||||||
|
outValue = queryObject->cachedResult;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Uint64 result = 0;
|
||||||
|
const auto getQueryResult64 = MG_Backend::gBackendFunctionsTable.GL.GetQueryResult64;
|
||||||
|
if (queryObject->backendHandle && getQueryResult64 &&
|
||||||
|
!getQueryResult64(queryObject->backendHandle, /*wait=*/false, &result)) {
|
||||||
|
// Not ready. The whole point of the no-wait form is that the caller's
|
||||||
|
// destination keeps whatever it already held.
|
||||||
|
if (outValueProduced) *outValueProduced = false;
|
||||||
|
outValue = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (queryObject->target == GL_ANY_SAMPLES_PASSED ||
|
||||||
|
queryObject->target == GL_ANY_SAMPLES_PASSED_CONSERVATIVE) {
|
||||||
|
result = result != 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
if (queryObject->backendHandle) {
|
||||||
|
if (const auto deleteBackendQuery = MG_Backend::gBackendFunctionsTable.GL.DeleteBackendQuery) {
|
||||||
|
deleteBackendQuery(queryObject->backendHandle);
|
||||||
|
}
|
||||||
|
queryObject->backendHandle = nullptr;
|
||||||
|
}
|
||||||
|
queryObject->cachedResult = result;
|
||||||
|
queryObject->resultCached = true;
|
||||||
|
outValue = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
case GL_QUERY_RESULT_AVAILABLE: {
|
case GL_QUERY_RESULT_AVAILABLE: {
|
||||||
if (queryObject->resultCached || !queryObject->backendHandle) {
|
if (queryObject->resultCached || !queryObject->backendHandle) {
|
||||||
outValue = 1;
|
outValue = 1;
|
||||||
@@ -160,6 +228,21 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void GetQueryBufferObject(GLuint id, GLuint buffer, GLenum pname, GLintptr offset, const char* function) {
|
||||||
|
SharedPtr<MG_State::GLState::BufferObject> bufferObject;
|
||||||
|
if (!ResolveQueryResultDestination(buffer, offset, sizeof(T), function, bufferObject)) return;
|
||||||
|
|
||||||
|
Uint64 value = 0;
|
||||||
|
Bool valueProduced = false;
|
||||||
|
if (!GetQueryObjectValue(id, pname, function, value, &valueProduced)) return;
|
||||||
|
// GL_QUERY_RESULT_NO_WAIT on a result that has not landed writes nothing at all.
|
||||||
|
if (!valueProduced) return;
|
||||||
|
|
||||||
|
const T narrowed = static_cast<T>(value);
|
||||||
|
bufferObject->UploadSubData({const_cast<T*>(&narrowed), sizeof(T)}, static_cast<SizeT>(offset));
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void GenQueries(GLsizei n, GLuint* ids) {
|
void GenQueries(GLsizei n, GLuint* ids) {
|
||||||
@@ -475,9 +558,26 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname, GLintptr offset) {
|
||||||
|
GetQueryBufferObject<GLint>(id, buffer, pname, offset, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname, GLintptr offset) {
|
||||||
|
GetQueryBufferObject<GLuint>(id, buffer, pname, offset, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname, GLintptr offset) {
|
||||||
|
GetQueryBufferObject<GLint64>(id, buffer, pname, offset, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname, GLintptr offset) {
|
||||||
|
GetQueryBufferObject<GLuint64>(id, buffer, pname, offset, __FUNCTION__);
|
||||||
|
}
|
||||||
|
|
||||||
void GetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
|
void GetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
|
||||||
Uint64 value = 0;
|
Uint64 value = 0;
|
||||||
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
|
Bool valueProduced = false;
|
||||||
|
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
constexpr Uint64 kMaxInt = static_cast<Uint64>(INT_MAX);
|
constexpr Uint64 kMaxInt = static_cast<Uint64>(INT_MAX);
|
||||||
@@ -486,7 +586,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
|
void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
|
||||||
Uint64 value = 0;
|
Uint64 value = 0;
|
||||||
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
|
Bool valueProduced = false;
|
||||||
|
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*params = static_cast<GLuint>(value & 0xFFFFFFFFull);
|
*params = static_cast<GLuint>(value & 0xFFFFFFFFull);
|
||||||
@@ -494,7 +595,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
|
void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
|
||||||
Uint64 value = 0;
|
Uint64 value = 0;
|
||||||
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
|
Bool valueProduced = false;
|
||||||
|
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*params = static_cast<GLint64>(value);
|
*params = static_cast<GLint64>(value);
|
||||||
@@ -502,7 +604,8 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
|
void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
|
||||||
Uint64 value = 0;
|
Uint64 value = 0;
|
||||||
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value) || !params) {
|
Bool valueProduced = false;
|
||||||
|
if (!GetQueryObjectValue(id, pname, __FUNCTION__, value, &valueProduced) || !valueProduced || !params) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*params = static_cast<GLuint64>(value);
|
*params = static_cast<GLuint64>(value);
|
||||||
|
|||||||
@@ -24,5 +24,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params);
|
void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params);
|
||||||
void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params);
|
void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params);
|
||||||
void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params);
|
void GetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params);
|
||||||
|
void GetQueryBufferObjectiv(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||||
|
void GetQueryBufferObjectuiv(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||||
|
void GetQueryBufferObjecti64v(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||||
|
void GetQueryBufferObjectui64v(GLuint id, GLuint buffer, GLenum pname, GLintptr offset);
|
||||||
void QueryCounter(GLuint id, GLenum target);
|
void QueryCounter(GLuint id, GLenum target);
|
||||||
} // namespace MobileGL::MG_Impl::GLImpl
|
} // namespace MobileGL::MG_Impl::GLImpl
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_TEXTURE_MAX_LOD:
|
case GL_TEXTURE_MAX_LOD:
|
||||||
case GL_TEXTURE_LOD_BIAS:
|
case GL_TEXTURE_LOD_BIAS:
|
||||||
return true;
|
return true;
|
||||||
|
// Four components, and GL puts no range on them - a border colour outside [0,1] is
|
||||||
|
// clamped when a fixed-point format is sampled, not rejected here. The scalar readers
|
||||||
|
// below would look at one component and invent an error.
|
||||||
|
case GL_TEXTURE_BORDER_COLOR:
|
||||||
|
return true;
|
||||||
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
case GL_TEXTURE_MAX_ANISOTROPY_EXT:
|
||||||
if (ReadSamplerScalar(param, isFloat, isUnsignedInteger) >= 1.0f) return true;
|
if (ReadSamplerScalar(param, isFloat, isUnsignedInteger) >= 1.0f) return true;
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
@@ -99,6 +104,20 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_TEXTURE_COMPARE_FUNC:
|
case GL_TEXTURE_COMPARE_FUNC:
|
||||||
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(*(const GLint*)param));
|
samplerObj->SetSamplerCompareFunc(MG_Util::ConvertGLEnumToSamplerCompareFunc(*(const GLint*)param));
|
||||||
break;
|
break;
|
||||||
|
case GL_TEXTURE_BORDER_COLOR:
|
||||||
|
// The only four-component sampler parameter: the caller's form decides which
|
||||||
|
// representation is authoritative, and SamplerObject keeps the other two in step.
|
||||||
|
if (isFloat) {
|
||||||
|
const auto* values = (const GLfloat*)param;
|
||||||
|
samplerObj->SetBorderColor(FloatVec4(values[0], values[1], values[2], values[3]));
|
||||||
|
} else if (isUnsignedInteger) {
|
||||||
|
const auto* values = (const GLuint*)param;
|
||||||
|
samplerObj->SetBorderColorUI(UintVec4(values[0], values[1], values[2], values[3]));
|
||||||
|
} else {
|
||||||
|
const auto* values = (const GLint*)param;
|
||||||
|
samplerObj->SetBorderColorI(IntVec4(values[0], values[1], values[2], values[3]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "SetSamplerParam_State",
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "SetSamplerParam_State",
|
||||||
@@ -162,6 +181,31 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
case GL_TEXTURE_COMPARE_FUNC:
|
case GL_TEXTURE_COMPARE_FUNC:
|
||||||
*(GLuint*)params = MG_Util::ConvertSamplerCompareFuncToGLEnum(samplerObj->GetSamplerCompareFunc());
|
*(GLuint*)params = MG_Util::ConvertSamplerCompareFuncToGLEnum(samplerObj->GetSamplerCompareFunc());
|
||||||
break;
|
break;
|
||||||
|
case GL_TEXTURE_BORDER_COLOR: {
|
||||||
|
if (isFloat) {
|
||||||
|
const auto& color = samplerObj->GetBorderColor();
|
||||||
|
auto* out = (GLfloat*)params;
|
||||||
|
out[0] = color.x();
|
||||||
|
out[1] = color.y();
|
||||||
|
out[2] = color.z();
|
||||||
|
out[3] = color.w();
|
||||||
|
} else if (isUnsignedInteger) {
|
||||||
|
const auto& color = samplerObj->GetBorderColorUI();
|
||||||
|
auto* out = (GLuint*)params;
|
||||||
|
out[0] = color.x();
|
||||||
|
out[1] = color.y();
|
||||||
|
out[2] = color.z();
|
||||||
|
out[3] = color.w();
|
||||||
|
} else {
|
||||||
|
const auto& color = samplerObj->GetBorderColorI();
|
||||||
|
auto* out = (GLint*)params;
|
||||||
|
out[0] = color.x();
|
||||||
|
out[1] = color.y();
|
||||||
|
out[2] = color.z();
|
||||||
|
out[3] = color.w();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
MG_State::pGLContext->RecordError(ErrorCode::InvalidEnum,
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetSamplerParam_State",
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "GetSamplerParam_State",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,10 @@
|
|||||||
|
|
||||||
namespace MobileGL::MG_Impl::GLImpl {
|
namespace MobileGL::MG_Impl::GLImpl {
|
||||||
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
|
/* @INSERTION_POINT:FUNCTION_DECLARATION@ */
|
||||||
|
// The sized internal formats a buffer texture accepts (GL 4.6 core table 8.16). The buffer
|
||||||
|
// clears take the same list, so it is shared rather than written out twice.
|
||||||
|
Bool IsBufferTextureInternalFormat(GLenum internalformat);
|
||||||
|
|
||||||
void ClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, const void* data);
|
void ClearTexImage(GLuint texture, GLint level, GLenum format, GLenum type, const void* data);
|
||||||
void ClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
void ClearTexSubImage(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width,
|
||||||
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data);
|
GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* data);
|
||||||
@@ -42,6 +46,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void GenerateTextureMipmap(GLuint texture);
|
void GenerateTextureMipmap(GLuint texture);
|
||||||
void BindTextureUnit(GLuint unit, GLuint texture);
|
void BindTextureUnit(GLuint unit, GLuint texture);
|
||||||
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
|
void GetTextureImage(GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void* pixels);
|
||||||
|
void GetCompressedTextureImage(GLuint texture, GLint level, GLsizei bufSize, void* pixels);
|
||||||
void TexBufferRange(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
void TexBufferRange(GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||||
void TextureBuffer(GLuint texture, GLenum internalformat, GLuint buffer);
|
void TextureBuffer(GLuint texture, GLenum internalformat, GLuint buffer);
|
||||||
void TextureBufferRange(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
void TextureBufferRange(GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size);
|
||||||
@@ -101,6 +106,9 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GLsizei width, GLsizei height);
|
GLsizei width, GLsizei height);
|
||||||
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
void CopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width,
|
||||||
GLsizei height);
|
GLsizei height);
|
||||||
|
void CopyTextureSubImage1D(GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
|
||||||
|
void CopyTextureSubImage3D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x,
|
||||||
|
GLint y, GLsizei width, GLsizei height);
|
||||||
void CopyTextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
|
void CopyTextureSubImage2D(GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y,
|
||||||
GLsizei width, GLsizei height);
|
GLsizei width, GLsizei height);
|
||||||
void CopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
|
void CopyTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);
|
||||||
|
|||||||
@@ -226,6 +226,9 @@ namespace MobileGL::MG_Impl::GLImpl::TextureImpl {
|
|||||||
case TextureInternalFormat::Depth24Stencil8:
|
case TextureInternalFormat::Depth24Stencil8:
|
||||||
case TextureInternalFormat::Depth32FStencil8:
|
case TextureInternalFormat::Depth32FStencil8:
|
||||||
case TextureInternalFormat::DepthStencil:
|
case TextureInternalFormat::DepthStencil:
|
||||||
|
// Stencil-only is not a colour format either: a colour client format read against a
|
||||||
|
// STENCIL_INDEX8 texture has to be the same INVALID_OPERATION as against a depth one.
|
||||||
|
case TextureInternalFormat::StencilIndex8:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -105,12 +105,45 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
return pname == GL_CURRENT_VERTEX_ATTRIB;
|
return pname == GL_CURRENT_VERTEX_ATTRIB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The stride a pointer-style call gives its binding point: the argument when it is non-zero,
|
||||||
|
// otherwise the tightly packed element size (GL 4.6 core 10.3.2). A packed 2_10_10_10 or
|
||||||
|
// 10F_11F_11F attribute is one 32-bit word regardless of its component count.
|
||||||
|
static int EffectiveVertexStride(GLsizei stride, GLint size, GLenum type) {
|
||||||
|
if (stride != 0) return static_cast<int>(stride);
|
||||||
|
switch (type) {
|
||||||
|
case GL_INT_2_10_10_10_REV:
|
||||||
|
case GL_UNSIGNED_INT_2_10_10_10_REV:
|
||||||
|
case GL_UNSIGNED_INT_10F_11F_11F_REV:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return static_cast<int>(size * MG_Util::GetGLTypeSize(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// glBindVertexBuffers / glVertexArrayVertexBuffers take a range of binding points, and a
|
||||||
|
// range that runs past the last one is INVALID_OPERATION rather than the INVALID_VALUE a
|
||||||
|
// single out-of-range index gets (GL 4.6 core 10.3.1).
|
||||||
|
static bool ValidateVertexBindingRange(GLuint first, GLsizei count, const char* funcName) {
|
||||||
|
if (count < 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName, "count must be non-negative."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (static_cast<Uint64>(first) + static_cast<Uint64>(count) >
|
||||||
|
VertexArrayImpl::GetMaxVertexAttribBindings()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
|
||||||
|
"first + count exceeds GL_MAX_VERTEX_ATTRIB_BINDINGS."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool ValidateVertexBindingIndex(GLuint bindingindex, const char* funcName) {
|
static bool ValidateVertexBindingIndex(GLuint bindingindex, const char* funcName) {
|
||||||
// Bound by the same dynamic limit as attribute indices: the default attribute -> binding
|
if (bindingindex >= VertexArrayImpl::GetMaxVertexAttribBindings()) {
|
||||||
// mapping is the identity, so a binding point the backend cannot address as an attribute
|
|
||||||
// would resolve into an attribute the backend must then reject on every draw. Real drivers
|
|
||||||
// likewise report MAX_VERTEX_ATTRIB_BINDINGS == MAX_VERTEX_ATTRIBS.
|
|
||||||
if (bindingindex >= VertexArrayImpl::GetMaxVertexAttribs()) {
|
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidValue,
|
ErrorCode::InvalidValue,
|
||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", funcName,
|
||||||
@@ -155,6 +188,17 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
SharedPtr<MG_State::GLState::VertexArrayObject> GetNamedVertexArrayObject_State(GLuint vaobj,
|
SharedPtr<MG_State::GLState::VertexArrayObject> GetNamedVertexArrayObject_State(GLuint vaobj,
|
||||||
const char* caller) {
|
const char* caller) {
|
||||||
|
// Name zero is not a vertex array object in a core profile: it names the default vertex
|
||||||
|
// array, which the by-name (direct state access) entry points never accept. MobileGL keeps a
|
||||||
|
// real object at index 0 for the compatibility paths, so the generic name validation below
|
||||||
|
// would otherwise let it through (GL 4.6 core 10.3.1).
|
||||||
|
if (vaobj == 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
||||||
|
"Vertex array name 0 is not a vertex array object."));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr;
|
if (!VertexArrayImpl::ValidateVertexArrayName(vaobj)) return nullptr;
|
||||||
if (!VertexArrayImpl::ValidateVertexArrayObject(vaobj)) return nullptr;
|
if (!VertexArrayImpl::ValidateVertexArrayObject(vaobj)) return nullptr;
|
||||||
return MG_State::pGLContext->GetVertexArrayObject(vaobj);
|
return MG_State::pGLContext->GetVertexArrayObject(vaobj);
|
||||||
@@ -210,7 +254,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
||||||
// Integer path: never normalized, never BGRA/packed (the validator rejects those).
|
// Integer path: never normalized, never BGRA/packed (the validator rejects those).
|
||||||
if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, dataType, false, stride, true)) return;
|
if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, type, dataType, false, stride, true)) return;
|
||||||
|
|
||||||
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
|
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
|
||||||
if (!vao) {
|
if (!vao) {
|
||||||
@@ -227,6 +271,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
|
|
||||||
vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true, false);
|
vao->SetAttributeFormat(index, size, dataType, false, stride, offset, true, false);
|
||||||
vao->BindAttributeBuffer(index, vbo);
|
vao->BindAttributeBuffer(index, vbo);
|
||||||
|
vao->MirrorPointerIntoBinding(index, vbo, offset, EffectiveVertexStride(stride, size, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexAttribPointer_State(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
|
void VertexAttribPointer_State(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
|
||||||
@@ -234,7 +279,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
|
if (!VertexArrayImpl::ValidateVertexAttributeIndex(index)) return;
|
||||||
|
|
||||||
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
||||||
if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, dataType, normalized == GL_TRUE, stride, false))
|
if (!VertexArrayImpl::ValidateVertexAttribFormat(index, size, type, dataType, normalized == GL_TRUE, stride, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
|
auto& vao = MG_State::pGLContext->GetBoundVertexArray();
|
||||||
@@ -256,6 +301,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
const int effectiveSize = isBgra ? 4 : size;
|
const int effectiveSize = isBgra ? 4 : size;
|
||||||
vao->SetAttributeFormat(index, effectiveSize, dataType, normalized, stride, offset, false, isBgra);
|
vao->SetAttributeFormat(index, effectiveSize, dataType, normalized, stride, offset, false, isBgra);
|
||||||
vao->BindAttributeBuffer(index, vbo);
|
vao->BindAttributeBuffer(index, vbo);
|
||||||
|
vao->MirrorPointerIntoBinding(index, vbo, offset, EffectiveVertexStride(stride, effectiveSize, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindVertexArray_State(GLuint array) {
|
void BindVertexArray_State(GLuint array) {
|
||||||
@@ -359,6 +405,13 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "offset and stride must be non-negative."));
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller, "offset and stride must be non-negative."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (static_cast<Uint>(stride) > VertexArrayImpl::GetMaxVertexAttribStride()) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", caller,
|
||||||
|
"stride exceeds GL_MAX_VERTEX_ATTRIB_STRIDE."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto bufferObject = GetVertexArrayBufferObject_State(buffer, caller);
|
auto bufferObject = GetVertexArrayBufferObject_State(buffer, caller);
|
||||||
if (buffer != 0 && !bufferObject) return;
|
if (buffer != 0 && !bufferObject) return;
|
||||||
|
|
||||||
@@ -376,6 +429,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
const GLintptr* offsets, const GLsizei* strides) {
|
const GLintptr* offsets, const GLsizei* strides) {
|
||||||
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayVertexBuffers_State");
|
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayVertexBuffers_State");
|
||||||
if (!vao) return;
|
if (!vao) return;
|
||||||
|
if (!ValidateVertexBindingRange(first, count, "VertexArrayVertexBuffers_State")) return;
|
||||||
for (GLsizei i = 0; i < count; ++i) {
|
for (GLsizei i = 0; i < count; ++i) {
|
||||||
if (!buffers) {
|
if (!buffers) {
|
||||||
VertexBufferBinding_State(vao, first + i, 0, 0, 16, "VertexArrayVertexBuffers_State");
|
VertexBufferBinding_State(vao, first + i, 0, 0, 16, "VertexArrayVertexBuffers_State");
|
||||||
@@ -389,12 +443,36 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
static void VertexAttribFormatSeparate_State(const SharedPtr<MG_State::GLState::VertexArrayObject>& vao,
|
static void VertexAttribFormatSeparate_State(const SharedPtr<MG_State::GLState::VertexArrayObject>& vao,
|
||||||
GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
|
GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
|
||||||
GLuint relativeoffset, Bool isInteger, const char* caller) {
|
GLuint relativeoffset, Bool isInteger, const char* caller) {
|
||||||
|
static_cast<void>(caller);
|
||||||
if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return;
|
if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return;
|
||||||
|
|
||||||
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
DataType dataType = MG_Util::ConvertGLEnumToDataType(type);
|
||||||
if (!VertexArrayImpl::ValidateVertexAttribPointerParams(attribindex, size, dataType, 0)) return;
|
// The separate-format entry points take the same size/type rules as the pointer ones,
|
||||||
|
// GL_BGRA included, so they need the full format validation rather than the pointer-only
|
||||||
|
// subset - that one reports GL_BGRA as an out-of-range size.
|
||||||
|
if (!VertexArrayImpl::ValidateVertexAttribFormat(attribindex, size, type, dataType, normalized == GL_TRUE, 0,
|
||||||
|
isInteger))
|
||||||
|
return;
|
||||||
|
if (!VertexArrayImpl::ValidateVertexAttribRelativeOffset(relativeoffset)) return;
|
||||||
|
|
||||||
vao->SetAttributeFormatSeparate(attribindex, size, dataType, normalized, isInteger, relativeoffset);
|
const Bool isBgra = (size == static_cast<GLint>(GL_BGRA));
|
||||||
|
vao->SetAttributeFormatSeparate(attribindex, isBgra ? 4 : size, dataType, normalized, isInteger,
|
||||||
|
relativeoffset, isBgra);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The long (64-bit) attribute format. MobileGL has no 64-bit vertex attributes, so nothing is
|
||||||
|
// recorded; what the entry point owes the application is the parameter validation, which is
|
||||||
|
// observable through glGetError regardless of whether the format could be used in a draw.
|
||||||
|
static void VertexAttribLFormatSeparate_State(GLuint attribindex, GLint size, GLenum type,
|
||||||
|
GLuint relativeoffset) {
|
||||||
|
if (!VertexArrayImpl::ValidateVertexAttributeIndex(attribindex)) return;
|
||||||
|
if (!VertexArrayImpl::ValidateVertexAttribLFormat(attribindex, size, type)) return;
|
||||||
|
if (!VertexArrayImpl::ValidateVertexAttribRelativeOffset(relativeoffset)) return;
|
||||||
|
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", "VertexAttribLFormat",
|
||||||
|
"64-bit vertex attributes are not supported."));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexArrayAttribFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type,
|
void VertexArrayAttribFormat_State(GLuint vaobj, GLuint attribindex, GLint size, GLenum type,
|
||||||
@@ -1153,6 +1231,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
const GLsizei* strides) {
|
const GLsizei* strides) {
|
||||||
auto vao = GetBoundVertexArrayOrError("BindVertexBuffers");
|
auto vao = GetBoundVertexArrayOrError("BindVertexBuffers");
|
||||||
if (!vao) return;
|
if (!vao) return;
|
||||||
|
if (!ValidateVertexBindingRange(first, count, "BindVertexBuffers")) return;
|
||||||
for (GLsizei i = 0; i < count; ++i) {
|
for (GLsizei i = 0; i < count; ++i) {
|
||||||
if (!buffers) {
|
if (!buffers) {
|
||||||
VertexBufferBinding_State(vao, first + i, 0, 0, 16, "BindVertexBuffers");
|
VertexBufferBinding_State(vao, first + i, 0, 0, 16, "BindVertexBuffers");
|
||||||
@@ -1177,6 +1256,18 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
"VertexAttribIFormat");
|
"VertexAttribIFormat");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) {
|
||||||
|
auto vao = GetBoundVertexArrayOrError("VertexAttribLFormat");
|
||||||
|
if (!vao) return;
|
||||||
|
VertexAttribLFormatSeparate_State(attribindex, size, type, relativeoffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VertexArrayAttribLFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset) {
|
||||||
|
auto vao = GetNamedVertexArrayObject_State(vaobj, "VertexArrayAttribLFormat");
|
||||||
|
if (!vao) return;
|
||||||
|
VertexAttribLFormatSeparate_State(attribindex, size, type, relativeoffset);
|
||||||
|
}
|
||||||
|
|
||||||
void VertexAttribBinding(GLuint attribindex, GLuint bindingindex) {
|
void VertexAttribBinding(GLuint attribindex, GLuint bindingindex) {
|
||||||
auto vao = GetBoundVertexArrayOrError("VertexAttribBinding");
|
auto vao = GetBoundVertexArrayOrError("VertexAttribBinding");
|
||||||
if (!vao) return;
|
if (!vao) return;
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
|
void VertexArrayAttribFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized,
|
||||||
GLuint relativeoffset);
|
GLuint relativeoffset);
|
||||||
void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
void VertexArrayAttribIFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||||
|
void VertexArrayAttribLFormat(GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||||
void VertexArrayAttribBinding(GLuint vaobj, GLuint attribindex, GLuint bindingindex);
|
void VertexArrayAttribBinding(GLuint vaobj, GLuint attribindex, GLuint bindingindex);
|
||||||
void VertexArrayBindingDivisor(GLuint vaobj, GLuint bindingindex, GLuint divisor);
|
void VertexArrayBindingDivisor(GLuint vaobj, GLuint bindingindex, GLuint divisor);
|
||||||
void VertexArrayVertexBuffers(GLuint vaobj, GLuint first, GLsizei count, const GLuint* buffers,
|
void VertexArrayVertexBuffers(GLuint vaobj, GLuint first, GLsizei count, const GLuint* buffers,
|
||||||
@@ -107,6 +108,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
const GLsizei* strides);
|
const GLsizei* strides);
|
||||||
void VertexAttribFormat(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);
|
void VertexAttribFormat(GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset);
|
||||||
void VertexAttribIFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
void VertexAttribIFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||||
|
void VertexAttribLFormat(GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset);
|
||||||
void VertexAttribBinding(GLuint attribindex, GLuint bindingindex);
|
void VertexAttribBinding(GLuint attribindex, GLuint bindingindex);
|
||||||
void VertexBindingDivisor(GLuint bindingindex, GLuint divisor);
|
void VertexBindingDivisor(GLuint bindingindex, GLuint divisor);
|
||||||
void VertexAttribDivisor(GLuint index, GLuint divisor);
|
void VertexAttribDivisor(GLuint index, GLuint divisor);
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
|
|||||||
return std::min(static_cast<Uint>(backendLimit), capacity);
|
return std::min(static_cast<Uint>(backendLimit), capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uint GetMaxVertexAttribBindings() {
|
||||||
|
return GetMaxVertexAttribs();
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint GetMaxVertexAttribRelativeOffset() {
|
||||||
|
return 2047;
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint GetMaxVertexAttribStride() {
|
||||||
|
return 2048;
|
||||||
|
}
|
||||||
|
|
||||||
Bool ValidateVertexArrayName(Uint index) {
|
Bool ValidateVertexArrayName(Uint index) {
|
||||||
Bool isValid = MG_State::pGLContext->ValidateVertexArrayName(index);
|
Bool isValid = MG_State::pGLContext->ValidateVertexArrayName(index);
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
@@ -90,9 +102,31 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, DataType type, Bool normalized, Int stride,
|
Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, GLenum glType, DataType type, Bool normalized,
|
||||||
Bool integerPath) {
|
Int stride, Bool integerPath) {
|
||||||
constexpr const char* fn = "ValidateVertexAttribFormat";
|
constexpr const char* fn = "ValidateVertexAttribFormat";
|
||||||
|
// GL_UNSIGNED_INT_10F_11F_11F_REV is a three-component float-path-only packing that has no
|
||||||
|
// DataType of its own, so it has to be recognised by name before the conversion below turns
|
||||||
|
// it into Unknown and reports the wrong error (GL 4.6 core 10.3.2).
|
||||||
|
if (glType == GL_UNSIGNED_INT_10F_11F_11F_REV) {
|
||||||
|
if (integerPath) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", fn,
|
||||||
|
std::format("GL_UNSIGNED_INT_10F_11F_11F_REV is not an integer-path type (attribute {}).",
|
||||||
|
index)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sizeRaw != 3) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", fn,
|
||||||
|
std::format("GL_UNSIGNED_INT_10F_11F_11F_REV requires size 3 (attribute {}).", index)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (type == DataType::Unknown) {
|
if (type == DataType::Unknown) {
|
||||||
MG_State::pGLContext->RecordError(
|
MG_State::pGLContext->RecordError(
|
||||||
ErrorCode::InvalidEnum,
|
ErrorCode::InvalidEnum,
|
||||||
@@ -170,4 +204,40 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bool ValidateVertexAttribLFormat(Uint index, GLint size, GLenum type) {
|
||||||
|
constexpr const char* fn = "ValidateVertexAttribLFormat";
|
||||||
|
if (size < 1 || size > 4) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", fn,
|
||||||
|
std::format("Invalid size {} for attribute {}. Must be 1-4.", size, index)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// GL 4.6 core 10.3.2: the long form takes GL_DOUBLE and nothing else.
|
||||||
|
if (type != GL_DOUBLE) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", fn,
|
||||||
|
std::format("Type 0x{:X} is not GL_DOUBLE (attribute {}).", type, index)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool ValidateVertexAttribRelativeOffset(Uint relativeOffset) {
|
||||||
|
const Uint limit = GetMaxVertexAttribRelativeOffset();
|
||||||
|
if (relativeOffset > limit) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>(
|
||||||
|
"MG_Impl/GLImpl", "ValidateVertexAttribRelativeOffset",
|
||||||
|
std::format("relativeoffset {} exceeds GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET ({}).", relativeOffset,
|
||||||
|
limit)));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
|
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
|
||||||
|
|||||||
@@ -15,6 +15,20 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
|
|||||||
// capacity). Falls back to the capacity when no backend is active (unit tests).
|
// capacity). Falls back to the capacity when no backend is active (unit tests).
|
||||||
Uint GetMaxVertexAttribs();
|
Uint GetMaxVertexAttribs();
|
||||||
|
|
||||||
|
// GL_MAX_VERTEX_ATTRIB_BINDINGS. The default attribute -> binding mapping is the identity, so a
|
||||||
|
// binding point that cannot also be an attribute index would resolve into an attribute the
|
||||||
|
// backend has to reject on every draw; real drivers report the two limits equal as well.
|
||||||
|
Uint GetMaxVertexAttribBindings();
|
||||||
|
|
||||||
|
// GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET. The relative offset is folded into the resolved
|
||||||
|
// attribute offset in the frontend and never reaches a backend limit, so this is the value the
|
||||||
|
// spec requires an implementation to support at minimum (GL 4.6 core table 23.63).
|
||||||
|
Uint GetMaxVertexAttribRelativeOffset();
|
||||||
|
|
||||||
|
// GL_MAX_VERTEX_ATTRIB_STRIDE. Like the relative offset above, the stride never reaches a
|
||||||
|
// backend limit of its own, so this is the spec minimum (GL 4.6 core table 23.63).
|
||||||
|
Uint GetMaxVertexAttribStride();
|
||||||
|
|
||||||
Bool ValidateVertexArrayName(Uint index);
|
Bool ValidateVertexArrayName(Uint index);
|
||||||
Bool ValidateVertexArrayObject(Uint index);
|
Bool ValidateVertexArrayObject(Uint index);
|
||||||
Bool ValidateVertexAttributeIndex(Uint index);
|
Bool ValidateVertexAttributeIndex(Uint index);
|
||||||
@@ -22,6 +36,13 @@ namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl {
|
|||||||
// Full glVertexAttribPointer / glVertexAttribIPointer format validation, including the packed
|
// Full glVertexAttribPointer / glVertexAttribIPointer format validation, including the packed
|
||||||
// 2_10_10_10 types and GL_BGRA size. sizeRaw is the untranslated GL size (possibly GL_BGRA);
|
// 2_10_10_10 types and GL_BGRA size. sizeRaw is the untranslated GL size (possibly GL_BGRA);
|
||||||
// integerPath selects the glVertexAttribIPointer rules.
|
// integerPath selects the glVertexAttribIPointer rules.
|
||||||
Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, DataType type, Bool normalized, Int stride,
|
Bool ValidateVertexAttribFormat(Uint index, GLint sizeRaw, GLenum glType, DataType type, Bool normalized,
|
||||||
Bool integerPath);
|
Int stride, Bool integerPath);
|
||||||
|
// glVertexAttribLFormat / glVertexArrayAttribLFormat: the only accepted type is GL_DOUBLE and
|
||||||
|
// the size range is 1-4 (GL_BGRA is a float-path size). Separate from the function above
|
||||||
|
// because the long path shares none of its type or size rules.
|
||||||
|
Bool ValidateVertexAttribLFormat(Uint index, GLint size, GLenum type);
|
||||||
|
// Shared by every *Format entry point: INVALID_VALUE once relativeoffset leaves the range the
|
||||||
|
// implementation advertises.
|
||||||
|
Bool ValidateVertexAttribRelativeOffset(Uint relativeOffset);
|
||||||
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
|
} // namespace MobileGL::MG_Impl::GLImpl::VertexArrayImpl
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
BindingSlot<BufferObject>& GetBindingSlot(BufferTarget target);
|
BindingSlot<BufferObject>& GetBindingSlot(BufferTarget target);
|
||||||
// For glBindBufferBase / glBindBufferRange
|
// For glBindBufferBase / glBindBufferRange
|
||||||
BindingSlotRange1D<BufferObject>& GetBindingPoint(BufferTarget target, Uint index);
|
BindingSlotRange1D<BufferObject>& GetBindingPoint(BufferTarget target, Uint index);
|
||||||
|
const BindingSlotRange1D<BufferObject>& GetBindingPoint(BufferTarget target, Uint index) const {
|
||||||
|
return const_cast<BufferState*>(this)->GetBindingPoint(target, index);
|
||||||
|
}
|
||||||
constexpr SizeT GetBindingPointCount(const BufferTarget target) const {
|
constexpr SizeT GetBindingPointCount(const BufferTarget target) const {
|
||||||
auto it = std::find(BufferBindPointTargets.begin(), BufferBindPointTargets.end(), target);
|
auto it = std::find(BufferBindPointTargets.begin(), BufferBindPointTargets.end(), target);
|
||||||
auto index = std::distance(BufferBindPointTargets.begin(), it);
|
auto index = std::distance(BufferBindPointTargets.begin(), it);
|
||||||
|
|||||||
@@ -802,6 +802,53 @@ namespace MobileGL::MG_State {
|
|||||||
m_transformFeedbackObjects[id] = {};
|
m_transformFeedbackObjects[id] = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Program pipeline
|
||||||
|
void GLContext::GenProgramPipelineNames(Uint number, Vector<Uint>& pipelines) {
|
||||||
|
pipelines.resize(number);
|
||||||
|
// Names only: glIsProgramPipeline must answer GL_FALSE until one is bound or created.
|
||||||
|
m_programPipelineNames.Generate(number, pipelines.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::CreateProgramPipelineObject(Uint index) {
|
||||||
|
m_programPipelines[index] = MakeShared<ProgramPipelineObject>(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool GLContext::ValidateProgramPipelineName(Uint index) const {
|
||||||
|
return index == 0 || m_programPipelineNames.IsValid(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool GLContext::IsProgramPipelineObject(Uint index) const {
|
||||||
|
if (index == 0 || !m_programPipelineNames.IsValid(index)) return false;
|
||||||
|
return m_programPipelines.find(index) != m_programPipelines.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::BindProgramPipelineObject(Uint index) {
|
||||||
|
if (index != 0 && m_programPipelines.find(index) == m_programPipelines.end()) {
|
||||||
|
// First bind is what turns a reserved name into an object.
|
||||||
|
m_programPipelines[index] = MakeShared<ProgramPipelineObject>(index);
|
||||||
|
}
|
||||||
|
m_boundProgramPipeline = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::MarkProgramPipelineForDeletion(Uint index) {
|
||||||
|
if (index == 0 || !m_programPipelineNames.IsValid(index)) return;
|
||||||
|
if (index == m_boundProgramPipeline) {
|
||||||
|
m_boundProgramPipeline = 0;
|
||||||
|
}
|
||||||
|
m_programPipelines.erase(index);
|
||||||
|
m_programPipelineNames.Delete(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SharedPtr<ProgramPipelineObject>& GLContext::GetProgramPipelineObject(Uint index) const {
|
||||||
|
static const SharedPtr<ProgramPipelineObject> kNone;
|
||||||
|
const auto it = m_programPipelines.find(index);
|
||||||
|
return it == m_programPipelines.end() ? kNone : it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SharedPtr<ProgramPipelineObject>& GLContext::GetBoundProgramPipeline() const {
|
||||||
|
return GetProgramPipelineObject(m_boundProgramPipeline);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Bool GLContext::ValidateTransformFeedbackName(Uint index) const {
|
Bool GLContext::ValidateTransformFeedbackName(Uint index) const {
|
||||||
return index == 0 || m_transformFeedbackNames.IsValid(index);
|
return index == 0 || m_transformFeedbackNames.IsValid(index);
|
||||||
@@ -842,6 +889,64 @@ namespace MobileGL::MG_State {
|
|||||||
const auto it = m_transformFeedbackObjects.find(index);
|
const auto it = m_transformFeedbackObjects.find(index);
|
||||||
return it != m_transformFeedbackObjects.end() && it->second.hasCompletedSpan;
|
return it != m_transformFeedbackObjects.end() && it->second.hasCompletedSpan;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLContext::CreateTransformFeedbackObject(Uint index) {
|
||||||
|
// glCreateTransformFeedbacks has no bind step to infer existence from, so the name it
|
||||||
|
// hands out is already the name of an object (GL 4.6 core 13.2.1).
|
||||||
|
m_transformFeedbackObjects[index] = {};
|
||||||
|
m_transformFeedbackObjects[index].everBound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool GLContext::IsNamedTransformFeedbackActive(Uint index) const {
|
||||||
|
if (index == m_boundTransformFeedback) return m_transformFeedbackActive;
|
||||||
|
const auto it = m_transformFeedbackObjects.find(index);
|
||||||
|
return it != m_transformFeedbackObjects.end() && it->second.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bool GLContext::IsNamedTransformFeedbackPaused(Uint index) const {
|
||||||
|
if (index == m_boundTransformFeedback) return m_transformFeedbackPaused;
|
||||||
|
const auto it = m_transformFeedbackObjects.find(index);
|
||||||
|
return it != m_transformFeedbackObjects.end() && it->second.paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
NamedTransformFeedbackBinding GLContext::GetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex) const {
|
||||||
|
NamedTransformFeedbackBinding result;
|
||||||
|
if (bufferIndex >= MAX_TRANSFORM_FEEDBACK_BUFFERS) return result;
|
||||||
|
// The bound object's capture bindings live in the context's own binding points, not in
|
||||||
|
// the saved copy - that one is only written when the object is swapped out.
|
||||||
|
if (index == m_boundTransformFeedback) {
|
||||||
|
const auto& point = m_bufferState.GetBindingPoint(BufferTarget::TransformFeedback, bufferIndex);
|
||||||
|
result.Buffer = point.GetBoundObject();
|
||||||
|
result.Range = point.GetRange();
|
||||||
|
result.HasExplicitRange = point.HasExplicitRange();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const auto it = m_transformFeedbackObjects.find(index);
|
||||||
|
if (it == m_transformFeedbackObjects.end()) return result;
|
||||||
|
const auto& saved = it->second.bindings[bufferIndex];
|
||||||
|
result.Buffer = saved.buffer;
|
||||||
|
result.Range = saved.range;
|
||||||
|
result.HasExplicitRange = saved.hasExplicitRange;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLContext::SetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex,
|
||||||
|
const SharedPtr<BufferObject>& buffer, Range1D range,
|
||||||
|
Bool hasExplicitRange) {
|
||||||
|
if (bufferIndex >= MAX_TRANSFORM_FEEDBACK_BUFFERS) return;
|
||||||
|
if (index == m_boundTransformFeedback) {
|
||||||
|
auto& point = m_bufferState.GetBindingPoint(BufferTarget::TransformFeedback, bufferIndex);
|
||||||
|
point.Bind(buffer);
|
||||||
|
if (buffer && hasExplicitRange) {
|
||||||
|
point.SetRange(range, true);
|
||||||
|
} else {
|
||||||
|
point.ClearRange();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& object = m_transformFeedbackObjects[index];
|
||||||
|
object.bindings[bufferIndex] = {buffer, range, hasExplicitRange};
|
||||||
|
}
|
||||||
} // namespace GLState
|
} // namespace GLState
|
||||||
|
|
||||||
// Leak-at-exit storage; see GlobalObjects.cpp.
|
// Leak-at-exit storage; see GlobalObjects.cpp.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
#include "MG_State/GLState/RenderbufferState/RenderbufferState.h"
|
#include "MG_State/GLState/RenderbufferState/RenderbufferState.h"
|
||||||
#include "RenderState/RenderState.h"
|
#include "RenderState/RenderState.h"
|
||||||
#include "ProgramState/ProgramState.h"
|
#include "ProgramState/ProgramState.h"
|
||||||
|
#include "ProgramState/ProgramPipelineObject.h"
|
||||||
#include "SamplerState/SamplerState.h"
|
#include "SamplerState/SamplerState.h"
|
||||||
#include "TextureState/TextureState.h"
|
#include "TextureState/TextureState.h"
|
||||||
#include "FramebufferState/FramebufferState.h"
|
#include "FramebufferState/FramebufferState.h"
|
||||||
@@ -45,6 +46,14 @@ namespace MobileGL {
|
|||||||
// translates the result into its own API call.
|
// translates the result into its own API call.
|
||||||
VertexAttribTypeInfo ClassifyVertexAttribType(GLenum glType);
|
VertexAttribTypeInfo ClassifyVertexAttribType(GLenum glType);
|
||||||
|
|
||||||
|
// One indexed capture binding of a transform feedback object, as the by-name queries
|
||||||
|
// report it. An empty Buffer means the binding point is unbound.
|
||||||
|
struct NamedTransformFeedbackBinding {
|
||||||
|
SharedPtr<BufferObject> Buffer;
|
||||||
|
Range1D Range{};
|
||||||
|
Bool HasExplicitRange = false;
|
||||||
|
};
|
||||||
|
|
||||||
class GLContext {
|
class GLContext {
|
||||||
public:
|
public:
|
||||||
GLContext() = default;
|
GLContext() = default;
|
||||||
@@ -129,6 +138,19 @@ namespace MobileGL {
|
|||||||
void UseProgram(Uint program);
|
void UseProgram(Uint program);
|
||||||
const SharedPtr<ProgramObject>& GetCurrentProgram();
|
const SharedPtr<ProgramObject>& GetCurrentProgram();
|
||||||
|
|
||||||
|
// Program pipeline (GL_ARB_separate_shader_objects, GL 4.6 core 7.4). Like queries
|
||||||
|
// and transform feedbacks, glGenProgramPipelines only RESERVES a name - the object
|
||||||
|
// appears on first bind - while glCreateProgramPipelines makes it immediately.
|
||||||
|
void GenProgramPipelineNames(Uint number, Vector<Uint>& pipelines);
|
||||||
|
void CreateProgramPipelineObject(Uint index);
|
||||||
|
Bool ValidateProgramPipelineName(Uint index) const;
|
||||||
|
Bool IsProgramPipelineObject(Uint index) const;
|
||||||
|
void BindProgramPipelineObject(Uint index);
|
||||||
|
void MarkProgramPipelineForDeletion(Uint index);
|
||||||
|
const SharedPtr<ProgramPipelineObject>& GetProgramPipelineObject(Uint index) const;
|
||||||
|
Uint GetBoundProgramPipelineName() const { return m_boundProgramPipeline; }
|
||||||
|
const SharedPtr<ProgramPipelineObject>& GetBoundProgramPipeline() const;
|
||||||
|
|
||||||
// RenderState
|
// RenderState
|
||||||
Uint GetRenderStateParametersVersion() const;
|
Uint GetRenderStateParametersVersion() const;
|
||||||
const RenderStateParameters& GetRenderStateParameters() const;
|
const RenderStateParameters& GetRenderStateParameters() const;
|
||||||
@@ -299,6 +321,17 @@ namespace MobileGL {
|
|||||||
// cannot express: an empty completed span is legal and draws nothing.
|
// cannot express: an empty completed span is legal and draws nothing.
|
||||||
Bool HasTransformFeedbackCompletedSpan(Uint index) const;
|
Bool HasTransformFeedbackCompletedSpan(Uint index) const;
|
||||||
|
|
||||||
|
// The by-name (direct state access) view. A named object that happens to be the
|
||||||
|
// bound one is answered from the live copy, since that is where its state actually
|
||||||
|
// is until a bind swaps it out.
|
||||||
|
void CreateTransformFeedbackObject(Uint index);
|
||||||
|
Bool IsNamedTransformFeedbackActive(Uint index) const;
|
||||||
|
Bool IsNamedTransformFeedbackPaused(Uint index) const;
|
||||||
|
NamedTransformFeedbackBinding GetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex) const;
|
||||||
|
void SetNamedTransformFeedbackBinding(Uint index, Uint bufferIndex,
|
||||||
|
const SharedPtr<BufferObject>& buffer, Range1D range,
|
||||||
|
Bool hasExplicitRange);
|
||||||
|
|
||||||
// Framebuffer
|
// Framebuffer
|
||||||
void GenFramebufferNames(Uint number, Vector<Uint>& framebuffers);
|
void GenFramebufferNames(Uint number, Vector<Uint>& framebuffers);
|
||||||
const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
|
const SharedPtr<FramebufferObject>& GetFramebufferObject(Uint index);
|
||||||
@@ -371,6 +404,11 @@ namespace MobileGL {
|
|||||||
UnorderedMap<Uint, TransformFeedbackObjectState> m_transformFeedbackObjects;
|
UnorderedMap<Uint, TransformFeedbackObjectState> m_transformFeedbackObjects;
|
||||||
IndexGenerator<Uint> m_transformFeedbackNames;
|
IndexGenerator<Uint> m_transformFeedbackNames;
|
||||||
Uint m_boundTransformFeedback = 0;
|
Uint m_boundTransformFeedback = 0;
|
||||||
|
// Map membership IS object existence here: a pipeline has no stateful default
|
||||||
|
// object 0, so no everBound flag is needed.
|
||||||
|
UnorderedMap<Uint, SharedPtr<ProgramPipelineObject>> m_programPipelines;
|
||||||
|
IndexGenerator<Uint> m_programPipelineNames;
|
||||||
|
Uint m_boundProgramPipeline = 0;
|
||||||
TextureState m_textureState;
|
TextureState m_textureState;
|
||||||
ProgramState m_programState;
|
ProgramState m_programState;
|
||||||
RenderState m_renderState;
|
RenderState m_renderState;
|
||||||
|
|||||||
@@ -185,6 +185,20 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
return m_externalIndex;
|
return m_externalIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(name, member, type) \
|
||||||
|
void FramebufferObject::Set##name(type value) { \
|
||||||
|
if (member == value) return; \
|
||||||
|
member = value; \
|
||||||
|
++m_objectVersion; \
|
||||||
|
}
|
||||||
|
|
||||||
|
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultWidth, m_defaultWidth, Int)
|
||||||
|
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultHeight, m_defaultHeight, Int)
|
||||||
|
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultLayers, m_defaultLayers, Int)
|
||||||
|
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultSamples, m_defaultSamples, Int)
|
||||||
|
MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER(DefaultFixedSampleLocations, m_defaultFixedSampleLocations, Bool)
|
||||||
|
#undef MOBILEGL_DEFINE_FRAMEBUFFER_DEFAULT_SETTER
|
||||||
|
|
||||||
void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) {
|
void FramebufferObject::BumpAttachmentVersion(FramebufferAttachmentType type) {
|
||||||
++m_attachmentVersions[static_cast<SizeT>(type)];
|
++m_attachmentVersions[static_cast<SizeT>(type)];
|
||||||
++m_objectVersion;
|
++m_objectVersion;
|
||||||
|
|||||||
@@ -129,6 +129,19 @@ namespace MobileGL {
|
|||||||
void SetReadBuffer(FramebufferAttachmentType buf);
|
void SetReadBuffer(FramebufferAttachmentType buf);
|
||||||
FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; }
|
FramebufferAttachmentType GetReadBuffer() const { return m_readBuffer; }
|
||||||
|
|
||||||
|
// GL_ARB_framebuffer_no_attachments state (GL 4.6 core table 23.24). The shape a
|
||||||
|
// framebuffer with no attachments would rasterize at; all zero / FALSE until set.
|
||||||
|
Int GetDefaultWidth() const { return m_defaultWidth; }
|
||||||
|
Int GetDefaultHeight() const { return m_defaultHeight; }
|
||||||
|
Int GetDefaultLayers() const { return m_defaultLayers; }
|
||||||
|
Int GetDefaultSamples() const { return m_defaultSamples; }
|
||||||
|
Bool GetDefaultFixedSampleLocations() const { return m_defaultFixedSampleLocations; }
|
||||||
|
void SetDefaultWidth(Int value);
|
||||||
|
void SetDefaultHeight(Int value);
|
||||||
|
void SetDefaultLayers(Int value);
|
||||||
|
void SetDefaultSamples(Int value);
|
||||||
|
void SetDefaultFixedSampleLocations(Bool value);
|
||||||
|
|
||||||
FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const {
|
FramebufferAttachmentVersionArray GetAllFramebufferAttachmentVersions() const {
|
||||||
return m_attachmentVersions;
|
return m_attachmentVersions;
|
||||||
}
|
}
|
||||||
@@ -148,6 +161,12 @@ namespace MobileGL {
|
|||||||
FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality
|
FramebufferAttachmentArray m_drawBuffers; // Probably no versioning needed for this, just check equality
|
||||||
FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::None;
|
FramebufferAttachmentType m_readBuffer = FramebufferAttachmentType::None;
|
||||||
|
|
||||||
|
Int m_defaultWidth = 0;
|
||||||
|
Int m_defaultHeight = 0;
|
||||||
|
Int m_defaultLayers = 0;
|
||||||
|
Int m_defaultSamples = 0;
|
||||||
|
Bool m_defaultFixedSampleLocations = false;
|
||||||
|
|
||||||
// This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`)
|
// This version will bump when draw/read buffer changes (by `glDrawBuffer(s)`/`glReadBuffer`)
|
||||||
Uint16 m_objectVersion = 0;
|
Uint16 m_objectVersion = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -195,27 +195,42 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
static constexpr GLenum kIntTypes[5] = {0, GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4};
|
static constexpr GLenum kIntTypes[5] = {0, GL_INT, GL_INT_VEC2, GL_INT_VEC3, GL_INT_VEC4};
|
||||||
static constexpr GLenum kUintTypes[5] = {0, GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3,
|
static constexpr GLenum kUintTypes[5] = {0, GL_UNSIGNED_INT, GL_UNSIGNED_INT_VEC2, GL_UNSIGNED_INT_VEC3,
|
||||||
GL_UNSIGNED_INT_VEC4};
|
GL_UNSIGNED_INT_VEC4};
|
||||||
|
static constexpr GLenum kDoubleTypes[5] = {0, GL_DOUBLE, GL_DOUBLE_VEC2, GL_DOUBLE_VEC3,
|
||||||
|
GL_DOUBLE_VEC4};
|
||||||
if (type.isMatrix()) {
|
if (type.isMatrix()) {
|
||||||
if (basic != glslang::EbtFloat) return false;
|
if (basic != glslang::EbtFloat && basic != glslang::EbtDouble) return false;
|
||||||
static constexpr GLenum kMatTypes[5][5] = {
|
static constexpr GLenum kMatTypes[5][5] = {
|
||||||
{}, {},
|
{}, {},
|
||||||
{0, 0, GL_FLOAT_MAT2, GL_FLOAT_MAT2x3, GL_FLOAT_MAT2x4},
|
{0, 0, GL_FLOAT_MAT2, GL_FLOAT_MAT2x3, GL_FLOAT_MAT2x4},
|
||||||
{0, 0, GL_FLOAT_MAT3x2, GL_FLOAT_MAT3, GL_FLOAT_MAT3x4},
|
{0, 0, GL_FLOAT_MAT3x2, GL_FLOAT_MAT3, GL_FLOAT_MAT3x4},
|
||||||
{0, 0, GL_FLOAT_MAT4x2, GL_FLOAT_MAT4x3, GL_FLOAT_MAT4},
|
{0, 0, GL_FLOAT_MAT4x2, GL_FLOAT_MAT4x3, GL_FLOAT_MAT4},
|
||||||
};
|
};
|
||||||
|
static constexpr GLenum kDoubleMatTypes[5][5] = {
|
||||||
|
{}, {},
|
||||||
|
{0, 0, GL_DOUBLE_MAT2, GL_DOUBLE_MAT2x3, GL_DOUBLE_MAT2x4},
|
||||||
|
{0, 0, GL_DOUBLE_MAT3x2, GL_DOUBLE_MAT3, GL_DOUBLE_MAT3x4},
|
||||||
|
{0, 0, GL_DOUBLE_MAT4x2, GL_DOUBLE_MAT4x3, GL_DOUBLE_MAT4},
|
||||||
|
};
|
||||||
if (columns < 2 || columns > 4 || components < 2 || components > 4) return false;
|
if (columns < 2 || columns > 4 || components < 2 || components > 4) return false;
|
||||||
outType = kMatTypes[columns][components];
|
outType = basic == glslang::EbtDouble ? kDoubleMatTypes[columns][components]
|
||||||
|
: kMatTypes[columns][components];
|
||||||
} else if (components >= 1 && components <= 4) {
|
} else if (components >= 1 && components <= 4) {
|
||||||
switch (basic) {
|
switch (basic) {
|
||||||
case glslang::EbtFloat: outType = kFloatTypes[components]; break;
|
case glslang::EbtFloat: outType = kFloatTypes[components]; break;
|
||||||
case glslang::EbtInt: outType = kIntTypes[components]; break;
|
case glslang::EbtInt: outType = kIntTypes[components]; break;
|
||||||
case glslang::EbtUint: outType = kUintTypes[components]; break;
|
case glslang::EbtUint: outType = kUintTypes[components]; break;
|
||||||
|
// A double-typed varying is capturable like any other; rejecting it here reported
|
||||||
|
// the varying as "not an output of the vertex stage", which it plainly was.
|
||||||
|
case glslang::EbtDouble: outType = kDoubleTypes[components]; break;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
outBytesPerElement = static_cast<Uint32>(columns * components) * 4u;
|
// GL 4.6 core 11.1.2.1: a double component occupies eight basic machine units, and
|
||||||
|
// counts as two components against the transform feedback limits.
|
||||||
|
const Uint32 bytesPerComponent = basic == glslang::EbtDouble ? 8u : 4u;
|
||||||
|
outBytesPerElement = static_cast<Uint32>(columns * components) * bytesPerComponent;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// MobileGL - MobileGL/MG_State/GLState/ProgramState/ProgramPipelineObject.h
|
||||||
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
||||||
|
// Licensed under the GNU Lesser General Public License v3.0:
|
||||||
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
||||||
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||||||
|
// End of Source File Header
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <Includes.h>
|
||||||
|
#include "ProgramObject.h"
|
||||||
|
|
||||||
|
namespace MobileGL {
|
||||||
|
namespace MG_State {
|
||||||
|
namespace GLState {
|
||||||
|
// A GL_ARB_separate_shader_objects program pipeline (GL 4.6 core 7.4): a set of
|
||||||
|
// per-stage program references plus the program glProgramUniform* addresses when no
|
||||||
|
// program object is in use.
|
||||||
|
class ProgramPipelineObject {
|
||||||
|
public:
|
||||||
|
explicit ProgramPipelineObject(Uint externalIndex) : m_externalIndex(externalIndex) {}
|
||||||
|
|
||||||
|
const SharedPtr<ProgramObject>& GetStageProgram(ShaderStage stage) const {
|
||||||
|
return m_stagePrograms[static_cast<SizeT>(stage)];
|
||||||
|
}
|
||||||
|
void SetStageProgram(ShaderStage stage, const SharedPtr<ProgramObject>& program) {
|
||||||
|
m_stagePrograms[static_cast<SizeT>(stage)] = program;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SharedPtr<ProgramObject>& GetActiveProgram() const { return m_activeProgram; }
|
||||||
|
void SetActiveProgram(const SharedPtr<ProgramObject>& program) { m_activeProgram = program; }
|
||||||
|
|
||||||
|
// Distinct from ProgramObject's, which starts true: a pipeline that has never been
|
||||||
|
// validated must report GL_VALIDATE_STATUS as 0 (GL 4.6 core table 23.31).
|
||||||
|
Bool GetValidateStatus() const { return m_validateStatus; }
|
||||||
|
void SetValidateStatus(Bool value) { m_validateStatus = value; }
|
||||||
|
|
||||||
|
const String& GetInfoLog() const { return m_infoLog; }
|
||||||
|
void SetInfoLog(String log) { m_infoLog = Move(log); }
|
||||||
|
|
||||||
|
Uint GetExternalIndex() const { return m_externalIndex; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Array<SharedPtr<ProgramObject>, static_cast<SizeT>(ShaderStage::ShaderStageCount)> m_stagePrograms{};
|
||||||
|
SharedPtr<ProgramObject> m_activeProgram;
|
||||||
|
String m_infoLog;
|
||||||
|
const Uint m_externalIndex = 0;
|
||||||
|
Bool m_validateStatus = false;
|
||||||
|
};
|
||||||
|
} // namespace GLState
|
||||||
|
} // namespace MG_State
|
||||||
|
} // namespace MobileGL
|
||||||
@@ -139,6 +139,61 @@ namespace MobileGL {
|
|||||||
return m_samplerParameters.maxAnisotropy;
|
return m_samplerParameters.maxAnisotropy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The three border-colour representations are kept in step so a getter of any form has
|
||||||
|
// an answer whichever form was written. Integer <-> float uses the plain value, matching
|
||||||
|
// what glTexParameterIiv/Iuiv mean: those forms are for integer texture formats, whose
|
||||||
|
// border components are the raw integers rather than a normalized fraction.
|
||||||
|
void SamplerObject::SetBorderColor(const FloatVec4& color) {
|
||||||
|
if (color == m_samplerParameters.borderColor) return;
|
||||||
|
|
||||||
|
m_samplerParameters.borderColor = color;
|
||||||
|
m_samplerParameters.borderColorI =
|
||||||
|
IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
|
||||||
|
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
|
||||||
|
m_samplerParameters.borderColorUI =
|
||||||
|
UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
|
||||||
|
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
|
||||||
|
++m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SamplerObject::SetBorderColorI(const IntVec4& color) {
|
||||||
|
if (color == m_samplerParameters.borderColorI) return;
|
||||||
|
|
||||||
|
m_samplerParameters.borderColorI = color;
|
||||||
|
m_samplerParameters.borderColorUI =
|
||||||
|
UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
|
||||||
|
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
|
||||||
|
m_samplerParameters.borderColor =
|
||||||
|
FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
|
||||||
|
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
|
||||||
|
++m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SamplerObject::SetBorderColorUI(const UintVec4& color) {
|
||||||
|
if (color == m_samplerParameters.borderColorUI) return;
|
||||||
|
|
||||||
|
m_samplerParameters.borderColorUI = color;
|
||||||
|
m_samplerParameters.borderColorI =
|
||||||
|
IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
|
||||||
|
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
|
||||||
|
m_samplerParameters.borderColor =
|
||||||
|
FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
|
||||||
|
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
|
||||||
|
++m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FloatVec4& SamplerObject::GetBorderColor() const {
|
||||||
|
return m_samplerParameters.borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IntVec4& SamplerObject::GetBorderColorI() const {
|
||||||
|
return m_samplerParameters.borderColorI;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UintVec4& SamplerObject::GetBorderColorUI() const {
|
||||||
|
return m_samplerParameters.borderColorUI;
|
||||||
|
}
|
||||||
|
|
||||||
SamplerCompareMode SamplerObject::GetCompareMode() const {
|
SamplerCompareMode SamplerObject::GetCompareMode() const {
|
||||||
return m_samplerParameters.compareMode;
|
return m_samplerParameters.compareMode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <Includes.h>
|
#include <Includes.h>
|
||||||
|
#include <MG_Util/Math/VectorTypes.h>
|
||||||
|
|
||||||
namespace MobileGL {
|
namespace MobileGL {
|
||||||
enum class SamplerFilterMode {
|
enum class SamplerFilterMode {
|
||||||
@@ -66,8 +67,18 @@ namespace MobileGL {
|
|||||||
Float maxLod = 1000.0f;
|
Float maxLod = 1000.0f;
|
||||||
Float lodBias = 0.0f;
|
Float lodBias = 0.0f;
|
||||||
Float maxAnisotropy = 1.0f;
|
Float maxAnisotropy = 1.0f;
|
||||||
SamplerCompareFunc compareFunc = SamplerCompareFunc::Always;
|
// GL 4.6 core table 23.18 / GLES 3.2 table 21.16: TEXTURE_COMPARE_FUNC starts at LEQUAL,
|
||||||
|
// for both sampler objects and the sampler state a texture object carries.
|
||||||
|
SamplerCompareFunc compareFunc = SamplerCompareFunc::LessEqual;
|
||||||
SamplerCompareMode compareMode = SamplerCompareMode::None;
|
SamplerCompareMode compareMode = SamplerCompareMode::None;
|
||||||
|
// TEXTURE_BORDER_COLOR is sampler state (GL 4.6 core table 23.18), so it belongs here and
|
||||||
|
// not on the texture - a texture object reaches it through the sampler object it owns. The
|
||||||
|
// three representations are the float, integer and unsigned-integer forms glSamplerParameterfv,
|
||||||
|
// glSamplerParameterIiv and glSamplerParameterIuiv set; whichever is written last defines
|
||||||
|
// the colour and the other two follow it, so a getter always has an answer.
|
||||||
|
FloatVec4 borderColor = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||||
|
IntVec4 borderColorI = {0, 0, 0, 0};
|
||||||
|
UintVec4 borderColorUI = {0, 0, 0, 0};
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace MG_State {
|
namespace MG_State {
|
||||||
@@ -87,6 +98,9 @@ namespace MobileGL {
|
|||||||
void SetMaxAnisotropy(Float maxAnisotropy);
|
void SetMaxAnisotropy(Float maxAnisotropy);
|
||||||
void SetSamplerCompareFunc(SamplerCompareFunc func);
|
void SetSamplerCompareFunc(SamplerCompareFunc func);
|
||||||
void SetCompareMode(SamplerCompareMode mode);
|
void SetCompareMode(SamplerCompareMode mode);
|
||||||
|
void SetBorderColor(const FloatVec4& color);
|
||||||
|
void SetBorderColorI(const IntVec4& color);
|
||||||
|
void SetBorderColorUI(const UintVec4& color);
|
||||||
|
|
||||||
SamplerWrapMode GetWrapS() const;
|
SamplerWrapMode GetWrapS() const;
|
||||||
SamplerWrapMode GetWrapT() const;
|
SamplerWrapMode GetWrapT() const;
|
||||||
@@ -100,6 +114,9 @@ namespace MobileGL {
|
|||||||
Float GetMaxAnisotropy() const;
|
Float GetMaxAnisotropy() const;
|
||||||
SamplerCompareMode GetCompareMode() const;
|
SamplerCompareMode GetCompareMode() const;
|
||||||
SamplerCompareFunc GetSamplerCompareFunc() const;
|
SamplerCompareFunc GetSamplerCompareFunc() const;
|
||||||
|
const FloatVec4& GetBorderColor() const;
|
||||||
|
const IntVec4& GetBorderColorI() const;
|
||||||
|
const UintVec4& GetBorderColorUI() const;
|
||||||
Uint GetExternalIndex() const;
|
Uint GetExternalIndex() const;
|
||||||
Uint16 GetVersion() const;
|
Uint16 GetVersion() const;
|
||||||
// Globally-unique, never-reused id for this sampler object's lifetime. Lets a
|
// Globally-unique, never-reused id for this sampler object's lifetime. Lets a
|
||||||
|
|||||||
@@ -88,48 +88,40 @@ namespace MobileGL {
|
|||||||
return m_externalIndex;
|
return m_externalIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TEXTURE_BORDER_COLOR is sampler state, so it lives on the SamplerObject this texture
|
||||||
|
// owns rather than being duplicated here - a sampler object bound over the texture then
|
||||||
|
// supplies its own, exactly as GL says it should. The texture params version still moves
|
||||||
|
// on a write, because the DirectGLES texture sync memoises on it.
|
||||||
const FloatVec4& TextureObjectBase::GetBorderColor() const {
|
const FloatVec4& TextureObjectBase::GetBorderColor() const {
|
||||||
return m_borderColor;
|
return m_sampler->GetBorderColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureObjectBase::SetBorderColor(const FloatVec4& color) {
|
void TextureObjectBase::SetBorderColor(const FloatVec4& color) {
|
||||||
if (color == m_borderColor) return;
|
if (color == m_sampler->GetBorderColor()) return;
|
||||||
|
|
||||||
m_borderColor = color;
|
m_sampler->SetBorderColor(color);
|
||||||
m_borderColorI = IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
|
|
||||||
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
|
|
||||||
m_borderColorUI = UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
|
|
||||||
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
|
|
||||||
++m_textureParamsVersion;
|
++m_textureParamsVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
const IntVec4& TextureObjectBase::GetBorderColorI() const {
|
const IntVec4& TextureObjectBase::GetBorderColorI() const {
|
||||||
return m_borderColorI;
|
return m_sampler->GetBorderColorI();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureObjectBase::SetBorderColorI(const IntVec4& color) {
|
void TextureObjectBase::SetBorderColorI(const IntVec4& color) {
|
||||||
if (color == m_borderColorI) return;
|
if (color == m_sampler->GetBorderColorI()) return;
|
||||||
|
|
||||||
m_borderColorI = color;
|
m_sampler->SetBorderColorI(color);
|
||||||
m_borderColorUI = UintVec4(static_cast<Uint32>(color.x()), static_cast<Uint32>(color.y()),
|
|
||||||
static_cast<Uint32>(color.z()), static_cast<Uint32>(color.w()));
|
|
||||||
m_borderColor = FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
|
|
||||||
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
|
|
||||||
++m_textureParamsVersion;
|
++m_textureParamsVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UintVec4& TextureObjectBase::GetBorderColorUI() const {
|
const UintVec4& TextureObjectBase::GetBorderColorUI() const {
|
||||||
return m_borderColorUI;
|
return m_sampler->GetBorderColorUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureObjectBase::SetBorderColorUI(const UintVec4& color) {
|
void TextureObjectBase::SetBorderColorUI(const UintVec4& color) {
|
||||||
if (color == m_borderColorUI) return;
|
if (color == m_sampler->GetBorderColorUI()) return;
|
||||||
|
|
||||||
m_borderColorUI = color;
|
m_sampler->SetBorderColorUI(color);
|
||||||
m_borderColorI = IntVec4(static_cast<Int32>(color.x()), static_cast<Int32>(color.y()),
|
|
||||||
static_cast<Int32>(color.z()), static_cast<Int32>(color.w()));
|
|
||||||
m_borderColor = FloatVec4(static_cast<Float>(color.x()), static_cast<Float>(color.y()),
|
|
||||||
static_cast<Float>(color.z()), static_cast<Float>(color.w()));
|
|
||||||
++m_textureParamsVersion;
|
++m_textureParamsVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,10 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
virtual Bool HasFixedSampleLocations() const = 0;
|
virtual Bool HasFixedSampleLocations() const = 0;
|
||||||
virtual void SetFixedSampleLocations(Bool fixedSampleLocations) = 0;
|
virtual void SetFixedSampleLocations(Bool fixedSampleLocations) = 0;
|
||||||
virtual Uint64 GetLifetimeId() const = 0;
|
virtual Uint64 GetLifetimeId() const = 0;
|
||||||
|
// Which aspect of a packed depth/stencil texture a sampler reads (GL 4.6 core 8.10).
|
||||||
|
// DEPTH_COMPONENT until set, and meaningless for every other format.
|
||||||
|
virtual GLenum GetDepthStencilTextureMode() const = 0;
|
||||||
|
virtual void SetDepthStencilTextureMode(GLenum mode) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0;
|
virtual Uint GetIndexOfTextureUploadTarget(TextureUploadTarget target) const = 0;
|
||||||
@@ -104,6 +108,8 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
Bool HasFixedSampleLocations() const override;
|
Bool HasFixedSampleLocations() const override;
|
||||||
void SetFixedSampleLocations(Bool fixedSampleLocations) override;
|
void SetFixedSampleLocations(Bool fixedSampleLocations) override;
|
||||||
Uint64 GetLifetimeId() const override;
|
Uint64 GetLifetimeId() const override;
|
||||||
|
GLenum GetDepthStencilTextureMode() const override { return m_depthStencilTextureMode; }
|
||||||
|
void SetDepthStencilTextureMode(GLenum mode) override { m_depthStencilTextureMode = mode; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static Uint64 AllocateLifetimeId();
|
static Uint64 AllocateLifetimeId();
|
||||||
@@ -113,9 +119,6 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
const TextureTarget m_target = TextureTarget::Unknown;
|
const TextureTarget m_target = TextureTarget::Unknown;
|
||||||
TextureInternalFormat m_internalFormat = TextureInternalFormat::Unknown;
|
TextureInternalFormat m_internalFormat = TextureInternalFormat::Unknown;
|
||||||
SharedPtr<SamplerObject> m_sampler = nullptr;
|
SharedPtr<SamplerObject> m_sampler = nullptr;
|
||||||
FloatVec4 m_borderColor = {0.0f, 0.0f, 0.0f, 0.0f};
|
|
||||||
IntVec4 m_borderColorI = {0, 0, 0, 0};
|
|
||||||
UintVec4 m_borderColorUI = {0, 0, 0, 0};
|
|
||||||
Vec4<TextureSwizzleParam> m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green,
|
Vec4<TextureSwizzleParam> m_swizzleParams = {TextureSwizzleParam::Red, TextureSwizzleParam::Green,
|
||||||
TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha};
|
TextureSwizzleParam::Blue, TextureSwizzleParam::Alpha};
|
||||||
UintVec2 m_levelRange = {0, 1000};
|
UintVec2 m_levelRange = {0, 1000};
|
||||||
@@ -124,6 +127,7 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
// Starts at 1 so a freshly-created backend resource (snapshot 0) never spuriously
|
// Starts at 1 so a freshly-created backend resource (snapshot 0) never spuriously
|
||||||
// matches before its first sync. Bumped only on dirty=true in MarkStorageDirty.
|
// matches before its first sync. Bumped only on dirty=true in MarkStorageDirty.
|
||||||
Uint64 m_contentVersion = 1;
|
Uint64 m_contentVersion = 1;
|
||||||
|
GLenum m_depthStencilTextureMode = GL_DEPTH_COMPONENT;
|
||||||
Int m_samples = 0;
|
Int m_samples = 0;
|
||||||
Bool m_fixedSampleLocations = true;
|
Bool m_fixedSampleLocations = true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -77,6 +77,26 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
BumpAttributeFormatVersion(index);
|
BumpAttributeFormatVersion(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VertexArrayObject::MirrorPointerIntoBinding(Uint index, const SharedPtr<BufferObject>& buffer, SizeT offset,
|
||||||
|
int effectiveStride) {
|
||||||
|
if (index >= MAX_VERTEX_ATTRIBS || index >= MAX_VERTEX_ATTRIB_BINDINGS) return;
|
||||||
|
|
||||||
|
// glVertexAttribPointer is defined in terms of the binding model (GL 4.6 core 10.3.2): it
|
||||||
|
// also sets binding point `index` to the buffer, the pointer as the offset, and the
|
||||||
|
// *effective* stride, and points the attribute at that binding point with relative offset 0.
|
||||||
|
// The flat attribute view keeps the raw stride, because VERTEX_ATTRIB_ARRAY_STRIDE reports
|
||||||
|
// that argument verbatim, so the binding point is recorded alongside the resolved attribute
|
||||||
|
// rather than being resolved into it.
|
||||||
|
m_attributeBindingIndex[index] = index;
|
||||||
|
m_attributeRelativeOffset[index] = 0;
|
||||||
|
|
||||||
|
auto& binding = m_bindingPoints[index];
|
||||||
|
binding.Buffer = buffer;
|
||||||
|
binding.Offset = offset;
|
||||||
|
binding.Stride = effectiveStride;
|
||||||
|
binding.Divisor = m_attributes[index].Divisor;
|
||||||
|
}
|
||||||
|
|
||||||
void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer) {
|
void VertexArrayObject::BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer) {
|
||||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||||
|
|
||||||
@@ -111,6 +131,11 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
|
|
||||||
void VertexArrayObject::SetAttributeDivisor(Uint index, Uint divisor) {
|
void VertexArrayObject::SetAttributeDivisor(Uint index, Uint divisor) {
|
||||||
if (index >= MAX_VERTEX_ATTRIBS) return;
|
if (index >= MAX_VERTEX_ATTRIBS) return;
|
||||||
|
// glVertexAttribDivisor is VertexBindingDivisor on the attribute's own binding point
|
||||||
|
// (GL 4.6 core 10.3.2), so the binding-point view has to follow the resolved attribute.
|
||||||
|
if (index < MAX_VERTEX_ATTRIB_BINDINGS && m_attributeBindingIndex[index] == index) {
|
||||||
|
m_bindingPoints[index].Divisor = divisor;
|
||||||
|
}
|
||||||
if (m_attributes[index].Divisor == divisor) return;
|
if (m_attributes[index].Divisor == divisor) return;
|
||||||
m_attributes[index].Divisor = divisor;
|
m_attributes[index].Divisor = divisor;
|
||||||
BumpAttributeFormatVersion(index);
|
BumpAttributeFormatVersion(index);
|
||||||
@@ -187,18 +212,18 @@ namespace MobileGL::MG_State::GLState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VertexArrayObject::SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
|
void VertexArrayObject::SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
|
||||||
Bool isInteger, Uint relativeOffset) {
|
Bool isInteger, Uint relativeOffset, Bool isBgra) {
|
||||||
if (attribIndex >= MAX_VERTEX_ATTRIBS) return;
|
if (attribIndex >= MAX_VERTEX_ATTRIBS) return;
|
||||||
if (size < 1 || size > 4) return;
|
if (size < 1 || size > 4) return;
|
||||||
|
|
||||||
auto& attr = m_attributes[attribIndex];
|
auto& attr = m_attributes[attribIndex];
|
||||||
if (attr.Size != size || attr.Type != type || attr.Normalized != normalized || attr.IsInteger != isInteger ||
|
if (attr.Size != size || attr.Type != type || attr.Normalized != normalized || attr.IsInteger != isInteger ||
|
||||||
attr.IsBgra || m_attributeRelativeOffset[attribIndex] != relativeOffset) {
|
attr.IsBgra != isBgra || m_attributeRelativeOffset[attribIndex] != relativeOffset) {
|
||||||
attr.Size = size;
|
attr.Size = size;
|
||||||
attr.Type = type;
|
attr.Type = type;
|
||||||
attr.Normalized = normalized;
|
attr.Normalized = normalized;
|
||||||
attr.IsInteger = isInteger;
|
attr.IsInteger = isInteger;
|
||||||
attr.IsBgra = false; // the binding-format path (glVertexAttribFormat) does not carry BGRA
|
attr.IsBgra = isBgra;
|
||||||
m_attributeRelativeOffset[attribIndex] = relativeOffset;
|
m_attributeRelativeOffset[attribIndex] = relativeOffset;
|
||||||
BumpAttributeFormatVersion(attribIndex);
|
BumpAttributeFormatVersion(attribIndex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ namespace MobileGL {
|
|||||||
|
|
||||||
void BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer);
|
void BindAttributeBuffer(Uint index, const SharedPtr<BufferObject>& buffer);
|
||||||
|
|
||||||
|
// Record what the pointer-style API implies for the binding-point view: attribute
|
||||||
|
// `index` bound to binding point `index` with relative offset 0, and that binding
|
||||||
|
// point carrying the buffer, the pointer offset and the effective stride.
|
||||||
|
void MirrorPointerIntoBinding(Uint index, const SharedPtr<BufferObject>& buffer, SizeT offset,
|
||||||
|
int effectiveStride);
|
||||||
|
|
||||||
BindingSlot<BufferObject>& GetIndexBufferBindingSlot();
|
BindingSlot<BufferObject>& GetIndexBufferBindingSlot();
|
||||||
const BindingSlot<BufferObject>& GetIndexBufferBindingSlot() const;
|
const BindingSlot<BufferObject>& GetIndexBufferBindingSlot() const;
|
||||||
|
|
||||||
@@ -82,7 +88,7 @@ namespace MobileGL {
|
|||||||
void SetBindingDivisor(Uint bindingIndex, Uint divisor);
|
void SetBindingDivisor(Uint bindingIndex, Uint divisor);
|
||||||
void SetAttributeBinding(Uint attribIndex, Uint bindingIndex);
|
void SetAttributeBinding(Uint attribIndex, Uint bindingIndex);
|
||||||
void SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
|
void SetAttributeFormatSeparate(Uint attribIndex, int size, DataType type, Bool normalized,
|
||||||
Bool isInteger, Uint relativeOffset);
|
Bool isInteger, Uint relativeOffset, Bool isBgra = false);
|
||||||
|
|
||||||
// The binding-point view the attributes were resolved from. Kept queryable
|
// The binding-point view the attributes were resolved from. Kept queryable
|
||||||
// because glGetVertexArrayIndexed[64]iv reports it verbatim, and the resolved
|
// because glGetVertexArrayIndexed[64]iv reports it verbatim, and the resolved
|
||||||
|
|||||||
@@ -144,10 +144,19 @@ TEST_F(QueryTest, GenQueriesReturnsDistinctNonzeroIdsAndTracksLiveness) {
|
|||||||
EXPECT_NE(ids[0], ids[2]);
|
EXPECT_NE(ids[0], ids[2]);
|
||||||
EXPECT_NE(ids[1], ids[2]);
|
EXPECT_NE(ids[1], ids[2]);
|
||||||
|
|
||||||
|
// GenQueries only reserves names: "they acquire query state only when they are first used by
|
||||||
|
// calling BeginQuery" (GL 4.6 core 4.2.1), and IsQuery answers for objects, not for reserved
|
||||||
|
// names. So all three read as FALSE here even though the names are taken.
|
||||||
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(0), GL_FALSE);
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(0), GL_FALSE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_FALSE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[1]), GL_FALSE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[2]), GL_FALSE);
|
||||||
|
|
||||||
|
// First use is what creates the object.
|
||||||
|
MG_Impl::GLImpl::BeginQuery(GL_TIME_ELAPSED, ids[0]);
|
||||||
|
MG_Impl::GLImpl::EndQuery(GL_TIME_ELAPSED);
|
||||||
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_TRUE);
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_TRUE);
|
||||||
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[1]), GL_TRUE);
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[1]), GL_FALSE);
|
||||||
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[2]), GL_TRUE);
|
|
||||||
|
|
||||||
MG_Impl::GLImpl::DeleteQueries(3, ids);
|
MG_Impl::GLImpl::DeleteQueries(3, ids);
|
||||||
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_FALSE);
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_FALSE);
|
||||||
@@ -157,6 +166,26 @@ TEST_F(QueryTest, GenQueriesReturnsDistinctNonzeroIdsAndTracksLiveness) {
|
|||||||
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The direct state access counterpart: glCreateQueries creates the object outright, so unlike a
|
||||||
|
// glGenQueries name it is a query before anything is ever recorded into it.
|
||||||
|
TEST_F(QueryTest, CreateQueriesYieldsQueryObjectsImmediately) {
|
||||||
|
GLuint ids[2] = {0, 0};
|
||||||
|
MG_Impl::GLImpl::CreateQueries(GL_TIME_ELAPSED, 2, ids);
|
||||||
|
|
||||||
|
EXPECT_NE(ids[0], 0u);
|
||||||
|
EXPECT_NE(ids[1], 0u);
|
||||||
|
EXPECT_NE(ids[0], ids[1]);
|
||||||
|
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_TRUE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[1]), GL_TRUE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
|
||||||
|
MG_Impl::GLImpl::DeleteQueries(2, ids);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[0]), GL_FALSE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::IsQuery(ids[1]), GL_FALSE);
|
||||||
|
EXPECT_EQ(MG_Impl::GLImpl::GetError(), GL_NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(QueryTest, TimeElapsedSpanFallsBackToImmediateZeroResult) {
|
TEST_F(QueryTest, TimeElapsedSpanFallsBackToImmediateZeroResult) {
|
||||||
GLuint id = 0;
|
GLuint id = 0;
|
||||||
MG_Impl::GLImpl::GenQueries(1, &id);
|
MG_Impl::GLImpl::GenQueries(1, &id);
|
||||||
|
|||||||
@@ -197,15 +197,15 @@ TEST(DirectGLESSanity, AdvertisesDepthTextureForGlmarkShadowScenes) {
|
|||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_depth_texture), extensions.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The advertised target went back to 3.3 (see "restore target GL version to 3.3"); Voxy only ever
|
// Voxy only ever needed the extensions, which stay advertised whatever the version is; the version
|
||||||
// needed the extensions, which stay advertised, so assert what the backend really reports.
|
// assertion just pins what the backend really reports, now that V_OpenGL40 is in the list.
|
||||||
TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensions) {
|
TEST(DirectGLESSanity, AdvertisesVoxyRequiredRenderingExtensions) {
|
||||||
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
MobileGL::MG_Backend::DirectGLES::BackendObject_DirectGLES backend;
|
||||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||||
const auto& extensions = rendererInfo.Extensions;
|
const auto& extensions = rendererInfo.Extensions;
|
||||||
|
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 4);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 0);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
||||||
|
|
||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
||||||
@@ -244,6 +244,19 @@ TEST(DirectGLESSanity, BindingZeroClearsPreviousNativeTextureBinding) {
|
|||||||
ASSERT_NE(frontendTextureObject, nullptr);
|
ASSERT_NE(frontendTextureObject, nullptr);
|
||||||
ASSERT_EQ(frontendTextureObject->GetExternalIndex(), frontendTexture);
|
ASSERT_EQ(frontendTextureObject->GetExternalIndex(), frontendTexture);
|
||||||
|
|
||||||
|
// A texture with no image is incomplete, and an incomplete texture samples as (0, 0, 0, 1) -
|
||||||
|
// which DirectGLES expresses by leaving the native target unbound (see "sample a
|
||||||
|
// mipmap-incomplete texture as black"). This test is about the bind-0 clear, so the texture
|
||||||
|
// has to be complete enough to get bound in the first place: a format plus a level 0. At 1x1
|
||||||
|
// that single level is the whole mip chain, so it stays complete under any filter. The state
|
||||||
|
// is set directly rather than through glTexImage2D because the mock GLES table below wires
|
||||||
|
// only the binding entry points, not the upload path.
|
||||||
|
frontendTextureObject->SetInternalFormat(TextureInternalFormat::RGBA8);
|
||||||
|
MG_State::GLState::AsMipmapTexture(frontendTextureObject.get())
|
||||||
|
->AllocateStorage(TextureUploadTarget::Texture2D, 0, {{1, 1, 1}, 4});
|
||||||
|
ASSERT_FALSE(MG_State::GLState::SamplesAsIncompleteTexture(
|
||||||
|
frontendTextureObject.get(), frontendTextureObject->GetSamplerObject().get()));
|
||||||
|
|
||||||
auto& backendTexture = DirectGLES::TextureImpl::g_backendTextureObjects.GetOrCreate(frontendTextureObject);
|
auto& backendTexture = DirectGLES::TextureImpl::g_backendTextureObjects.GetOrCreate(frontendTextureObject);
|
||||||
backendTexture = MakeShared<DirectGLES::TextureImpl::BackendTextureObject>();
|
backendTexture = MakeShared<DirectGLES::TextureImpl::BackendTextureObject>();
|
||||||
const GLuint backendTextureId = backendTexture->GetBackendTextureId();
|
const GLuint backendTextureId = backendTexture->GetBackendTextureId();
|
||||||
@@ -399,14 +412,15 @@ TEST(DirectVulkanSanity, RenderPassExtentUsesSwapchainSizeOnlyForDefaultFramebuf
|
|||||||
MobileGL::IntVec2(512, 512));
|
MobileGL::IntVec2(512, 512));
|
||||||
}
|
}
|
||||||
|
|
||||||
// See the DirectGLES twin above: the target version is 3.3 again, the extensions are what matter.
|
// See the DirectGLES twin above: the target version follows the advertised list, the extensions are
|
||||||
|
// what matter.
|
||||||
TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensions) {
|
TEST(DirectVulkanSanity, AdvertisesVoxyRequiredRenderingExtensions) {
|
||||||
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
MobileGL::MG_Backend::DirectVulkan::BackendObject_DirectVulkan backend;
|
||||||
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
const auto& rendererInfo = backend.GetRendererInfo().RendererGLInfo;
|
||||||
const auto& extensions = rendererInfo.Extensions;
|
const auto& extensions = rendererInfo.Extensions;
|
||||||
|
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Major, 4);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 3);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Minor, 0);
|
||||||
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
EXPECT_EQ(rendererInfo.TargetGLVersion.Patch, 0);
|
||||||
|
|
||||||
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
EXPECT_NE(std::find(extensions.begin(), extensions.end(), MobileGL::E_GL_ARB_compute_shader),
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include <Config.h>
|
#include <Config.h>
|
||||||
#include <MG_Backend/BackendObjects.h>
|
#include <MG_Backend/BackendObjects.h>
|
||||||
#include <MG_Backend/DirectGLES/Managers.h>
|
#include <MG_Backend/DirectGLES/Managers.h>
|
||||||
|
#include <MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.h>
|
||||||
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
#include <MG_Impl/GLImpl/Getter/GL_Getter.h>
|
||||||
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
||||||
#include <MG_Impl/GLImpl/Sampler/GL_Sampler.h>
|
#include <MG_Impl/GLImpl/Sampler/GL_Sampler.h>
|
||||||
@@ -296,6 +297,18 @@ TEST_F(TextureTest, CopyTextureSubImage2DUsesNamedObjectAndRestoresBinding) {
|
|||||||
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &boundTexture);
|
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &boundTexture);
|
||||||
MG_Impl::GLImpl::BindTextureUnit(0, boundTexture);
|
MG_Impl::GLImpl::BindTextureUnit(0, boundTexture);
|
||||||
|
|
||||||
|
// The copy is only allowed to reach the backend when the destination region fits the level and
|
||||||
|
// the read framebuffer can supply pixels, so the call has to be set up as a legal one.
|
||||||
|
MG_Impl::GLImpl::TextureStorage2D(namedTexture, 3, GL_RGBA8, 16, 16);
|
||||||
|
|
||||||
|
GLuint readFramebuffer = 0;
|
||||||
|
GLuint readTexture = 0;
|
||||||
|
MG_Impl::GLImpl::CreateFramebuffers(1, &readFramebuffer);
|
||||||
|
MG_Impl::GLImpl::CreateTextures(GL_TEXTURE_2D, 1, &readTexture);
|
||||||
|
MG_Impl::GLImpl::TextureStorage2D(readTexture, 1, GL_RGBA8, 16, 16);
|
||||||
|
MG_Impl::GLImpl::NamedFramebufferTexture(readFramebuffer, GL_COLOR_ATTACHMENT0, readTexture, 0);
|
||||||
|
MG_Impl::GLImpl::BindFramebuffer(GL_READ_FRAMEBUFFER, readFramebuffer);
|
||||||
|
|
||||||
const auto boundBefore = MG_State::pGLContext->GetTextureUnitObject(0)
|
const auto boundBefore = MG_State::pGLContext->GetTextureUnitObject(0)
|
||||||
.GetBindingSlot(TextureTarget::Texture2D)
|
.GetBindingSlot(TextureTarget::Texture2D)
|
||||||
.GetBoundObject();
|
.GetBoundObject();
|
||||||
|
|||||||
@@ -833,6 +833,10 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
if (std::strcmp(extension, "GL_EXT_texture_filter_anisotropic") == 0) {
|
if (std::strcmp(extension, "GL_EXT_texture_filter_anisotropic") == 0) {
|
||||||
caps.SupportsTextureFilterAnisotropy = true;
|
caps.SupportsTextureFilterAnisotropy = true;
|
||||||
}
|
}
|
||||||
|
if (std::strcmp(extension, "GL_EXT_texture_border_clamp") == 0 ||
|
||||||
|
std::strcmp(extension, "GL_OES_texture_border_clamp") == 0) {
|
||||||
|
caps.SupportsTextureBorderClamp = true;
|
||||||
|
}
|
||||||
if (std::strcmp(extension, "GL_EXT_base_instance") == 0) {
|
if (std::strcmp(extension, "GL_EXT_base_instance") == 0) {
|
||||||
caps.SupportsBaseInstance = true;
|
caps.SupportsBaseInstance = true;
|
||||||
}
|
}
|
||||||
@@ -1014,6 +1018,10 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
}
|
}
|
||||||
// Only legal to query once the extension has been seen in the loop above, hence not batched
|
// Only legal to query once the extension has been seen in the loop above, hence not batched
|
||||||
// with the unconditional probes: on a driver without it this raises GL_INVALID_ENUM.
|
// with the unconditional probes: on a driver without it this raises GL_INVALID_ENUM.
|
||||||
|
// Core from ES 3.2 on, whatever the extension string says.
|
||||||
|
if (caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) {
|
||||||
|
caps.SupportsTextureBorderClamp = true;
|
||||||
|
}
|
||||||
if (caps.SupportsTextureFilterAnisotropy) {
|
if (caps.SupportsTextureFilterAnisotropy) {
|
||||||
GLfloat maxTextureMaxAnisotropy = 1.0f;
|
GLfloat maxTextureMaxAnisotropy = 1.0f;
|
||||||
glesFuncs.glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxTextureMaxAnisotropy);
|
glesFuncs.glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxTextureMaxAnisotropy);
|
||||||
@@ -1056,9 +1064,17 @@ namespace MobileGL::MG_Util::BackendLoader {
|
|||||||
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
|
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
|
||||||
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
|
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
|
||||||
caps.MaxTextureBufferSize = maxTextureBufferSize;
|
caps.MaxTextureBufferSize = maxTextureBufferSize;
|
||||||
|
// Through glesFuncs, like every other capability query here: a bare glGetIntegerv resolves
|
||||||
|
// to MobileGL's own exported entry point, which answers this pname from the very
|
||||||
|
// capability table being filled in - so the driver's real alignment never arrived and the
|
||||||
|
// backend reported an unconstrained offset it cannot honour.
|
||||||
GLint textureBufferOffsetAlignment = 1;
|
GLint textureBufferOffsetAlignment = 1;
|
||||||
glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &textureBufferOffsetAlignment);
|
glesFuncs.glGetIntegerv(GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT, &textureBufferOffsetAlignment);
|
||||||
while (glGetError() != GL_NO_ERROR) {}
|
// Core in ES 3.2 and in EXT_texture_buffer; an older context rejects the pname and leaves
|
||||||
|
// the default in place.
|
||||||
|
if (glesFuncs.glGetError) {
|
||||||
|
while (glesFuncs.glGetError() != GL_NO_ERROR) {}
|
||||||
|
}
|
||||||
caps.TextureBufferOffsetAlignment = std::max(1, textureBufferOffsetAlignment);
|
caps.TextureBufferOffsetAlignment = std::max(1, textureBufferOffsetAlignment);
|
||||||
caps.MaxUniformBufferBindings = maxUniformBufferBindings;
|
caps.MaxUniformBufferBindings = maxUniformBufferBindings;
|
||||||
caps.MaxUniformBlockSize = maxUniformBlockSize;
|
caps.MaxUniformBlockSize = maxUniformBlockSize;
|
||||||
|
|||||||
@@ -1041,6 +1041,10 @@ namespace MobileGL {
|
|||||||
// GL_EXT_texture_filter_anisotropic is present, so sampler/texture
|
// GL_EXT_texture_filter_anisotropic is present, so sampler/texture
|
||||||
// anisotropy may be forwarded without raising GL_INVALID_ENUM in GLES.
|
// anisotropy may be forwarded without raising GL_INVALID_ENUM in GLES.
|
||||||
Bool SupportsTextureFilterAnisotropy = false;
|
Bool SupportsTextureFilterAnisotropy = false;
|
||||||
|
// GL_TEXTURE_BORDER_COLOR and GL_CLAMP_TO_BORDER: ES 3.2 core, or
|
||||||
|
// EXT/OES_texture_border_clamp before that. Without it every border-colour parameter
|
||||||
|
// raises INVALID_ENUM on the driver, so the syncs have to be gated on it.
|
||||||
|
Bool SupportsTextureBorderClamp = false;
|
||||||
// GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT of the host driver; only queried when the
|
// GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT of the host driver; only queried when the
|
||||||
// extension above is present, and left at 1.0 (no anisotropy) otherwise.
|
// extension above is present, and left at 1.0 (no anisotropy) otherwise.
|
||||||
Float MaxTextureMaxAnisotropy = 1.0f;
|
Float MaxTextureMaxAnisotropy = 1.0f;
|
||||||
|
|||||||
@@ -288,6 +288,35 @@ namespace MobileGL {
|
|||||||
return TextureInternalFormat::SRGB8;
|
return TextureInternalFormat::SRGB8;
|
||||||
case GL_COMPRESSED_SRGB_ALPHA:
|
case GL_COMPRESSED_SRGB_ALPHA:
|
||||||
return TextureInternalFormat::SRGB8Alpha8;
|
return TextureInternalFormat::SRGB8Alpha8;
|
||||||
|
// BPTC and ETC2/EAC follow the same deviation as RGTC above, for the same reason: they
|
||||||
|
// are specific formats core GL requires (BPTC from 4.2, ETC2/EAC from 4.3) that this
|
||||||
|
// stack has no compressor for. INVALID_ENUM was never a legal answer for them, and
|
||||||
|
// uncompressed storage is the same trade the RGTC formats already take.
|
||||||
|
case GL_COMPRESSED_RGBA_BPTC_UNORM:
|
||||||
|
return TextureInternalFormat::RGBA8;
|
||||||
|
case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
|
||||||
|
return TextureInternalFormat::SRGB8Alpha8;
|
||||||
|
case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
|
||||||
|
case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
|
||||||
|
return TextureInternalFormat::RGB16F;
|
||||||
|
case GL_COMPRESSED_RGB8_ETC2:
|
||||||
|
return TextureInternalFormat::RGB8;
|
||||||
|
case GL_COMPRESSED_SRGB8_ETC2:
|
||||||
|
return TextureInternalFormat::SRGB8;
|
||||||
|
case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
||||||
|
case GL_COMPRESSED_RGBA8_ETC2_EAC:
|
||||||
|
return TextureInternalFormat::RGBA8;
|
||||||
|
case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
|
||||||
|
case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
|
||||||
|
return TextureInternalFormat::SRGB8Alpha8;
|
||||||
|
case GL_COMPRESSED_R11_EAC:
|
||||||
|
return TextureInternalFormat::R8;
|
||||||
|
case GL_COMPRESSED_SIGNED_R11_EAC:
|
||||||
|
return TextureInternalFormat::R8Snorm;
|
||||||
|
case GL_COMPRESSED_RG11_EAC:
|
||||||
|
return TextureInternalFormat::RG8;
|
||||||
|
case GL_COMPRESSED_SIGNED_RG11_EAC:
|
||||||
|
return TextureInternalFormat::RG8Snorm;
|
||||||
case GL_ALPHA:
|
case GL_ALPHA:
|
||||||
case GL_RED:
|
case GL_RED:
|
||||||
return TextureInternalFormat::Red;
|
return TextureInternalFormat::Red;
|
||||||
|
|||||||
@@ -298,6 +298,17 @@ namespace MobileGL::MG_Util::SelfTest {
|
|||||||
"not supported; no impact: the native indirect path deliberately does not "
|
"not supported; no impact: the native indirect path deliberately does not "
|
||||||
"rely on it (shader-side emulation handles baseInstance semantics)");
|
"rely on it (shader-side emulation handles baseInstance semantics)");
|
||||||
}
|
}
|
||||||
|
if (caps.SupportsTextureBorderClamp) {
|
||||||
|
builder.Pass("Texture border clamp",
|
||||||
|
"supported (GL_TEXTURE_BORDER_COLOR reaches the driver, so "
|
||||||
|
"GL_CLAMP_TO_BORDER samples the colour the application set)");
|
||||||
|
} else {
|
||||||
|
builder.Warn("Texture border clamp",
|
||||||
|
"not supported (pre-ES 3.2 without GL_EXT/OES_texture_border_clamp); "
|
||||||
|
"GL_TEXTURE_BORDER_COLOR is not synced to the driver at all, so anything "
|
||||||
|
"sampling outside a GL_CLAMP_TO_BORDER texture reads the driver's default "
|
||||||
|
"border instead of the requested colour");
|
||||||
|
}
|
||||||
if (glesFuncs.glPatchParameteri != nullptr) {
|
if (glesFuncs.glPatchParameteri != nullptr) {
|
||||||
builder.Pass("Tessellation patch parameters",
|
builder.Pass("Tessellation patch parameters",
|
||||||
"glPatchParameteri present (GL_PATCH_VERTICES reaches the driver)");
|
"glPatchParameteri present (GL_PATCH_VERTICES reaches the driver)");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ MobileGL is a *free* and *open-source* project that implements a desktop **OpenG
|
|||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
>
|
>
|
||||||
> **Status:** In development. Parts of the codebase are incomplete. Current short-term target: **OpenGL 3.3 (Core Profile)**.
|
> **Status:** In development. Parts of the codebase are incomplete. Current short-term target: **OpenGL 4.2 (Core Profile)**.
|
||||||
|
|
||||||
## Project positioning
|
## Project positioning
|
||||||
|
|
||||||
@@ -47,9 +47,10 @@ Refer to each component's repository for exact license texts. Any bundled third-
|
|||||||
|
|
||||||
## Compatibility & target
|
## Compatibility & target
|
||||||
|
|
||||||
* **Short-term target:** `OpenGL 3.3 (Core Profile)`.
|
* **Short-term target:** `OpenGL 4.2 (Core Profile)`.
|
||||||
* **Current development focus:**
|
* **Current development focus:**
|
||||||
* `MG_State` and `MG_Impl` for `OpenGL 3.3 (Core Profile)`
|
* Performance improvement
|
||||||
|
* `MG_State` and `MG_Impl` for `OpenGL 4.2 (Core Profile)`
|
||||||
* `Direct (Vulkan)` backend
|
* `Direct (Vulkan)` backend
|
||||||
* `Direct (OpenGL ES)` backend
|
* `Direct (OpenGL ES)` backend
|
||||||
|
|
||||||
@@ -163,11 +164,6 @@ MobileGL supports runtime configuration via environment variables.
|
|||||||
| `MOBILEGL_COHERENT_AS_FLUSH` | Treat persistent `GL_MAP_FLUSH_EXPLICIT_BIT` maps as coherent (app-compat for engines like Flywheel that never flush them). | `0`, `1` | `0` |
|
| `MOBILEGL_COHERENT_AS_FLUSH` | Treat persistent `GL_MAP_FLUSH_EXPLICIT_BIT` maps as coherent (app-compat for engines like Flywheel that never flush them). | `0`, `1` | `0` |
|
||||||
| `VK_ICD_FILENAMES` | Select the Vulkan ICD used by the Vulkan loader. | Path to an ICD JSON file | Loader default |
|
| `VK_ICD_FILENAMES` | Select the Vulkan ICD used by the Vulkan loader. | Path to an ICD JSON file | Loader default |
|
||||||
|
|
||||||
## Notice
|
|
||||||
|
|
||||||
* MobileGL is **not** production-ready currently.
|
|
||||||
* Some `OpenGL 3.3 (Core Profile)` features are still missing or under development.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is distributed under **GNU LGPL v3.0**. See the `LICENSE` file in the repository for detailed information.
|
This project is distributed under **GNU LGPL v3.0**. See the `LICENSE` file in the repository for detailed information.
|
||||||
|
|||||||
+65
-1
@@ -1,12 +1,16 @@
|
|||||||
# Running the OpenGL CTS against MobileGL
|
# Running the OpenGL CTS against MobileGL
|
||||||
|
|
||||||
This directory contains two supported paths:
|
This directory contains three supported paths:
|
||||||
|
|
||||||
- Android arm64 / MobileGL EGL: the KHR-GL33 workflow documented below and in
|
- Android arm64 / MobileGL EGL: the KHR-GL33 workflow documented below and in
|
||||||
`skills/gl-cts-on-mobilegl/SKILL.md`.
|
`skills/gl-cts-on-mobilegl/SKILL.md`.
|
||||||
- Windows x64 / MobileGL WGL: the GL30-GL46 pipeline in
|
- Windows x64 / MobileGL WGL: the GL30-GL46 pipeline in
|
||||||
`scripts/wgl_glcts_pipeline.py`, documented by
|
`scripts/wgl_glcts_pipeline.py`, documented by
|
||||||
`skills/wgl-gl-cts-on-mobilegl/SKILL.md`.
|
`skills/wgl-gl-cts-on-mobilegl/SKILL.md`.
|
||||||
|
- Desktop Linux x64 / MobileGL EGL: `scripts/run_cts_local.py` against the
|
||||||
|
`mobilegl-desktop` VK-GL-CTS target, documented in "Desktop Linux workflow"
|
||||||
|
below and in `skills/linux-gl-cts-on-mobilegl/SKILL.md`. This is the path that
|
||||||
|
needs no device and no GPU.
|
||||||
|
|
||||||
Windows prerequisites are Git, Python 3.9+, CMake, Visual Studio 2022's Desktop
|
Windows prerequisites are Git, Python 3.9+, CMake, Visual Studio 2022's Desktop
|
||||||
C++ workload, and a Vulkan SDK visible to CMake. DirectVulkan also needs a
|
C++ workload, and a Vulkan SDK visible to CMake. DirectVulkan also needs a
|
||||||
@@ -25,6 +29,66 @@ resumes individual suites after crashes/timeouts, and writes Markdown plus JSON
|
|||||||
reports below the printed `runs/<first-16-of-run-fingerprint>` directory. Its
|
reports below the printed `runs/<first-16-of-run-fingerprint>` directory. Its
|
||||||
manifest records provenance and the runner settings used to validate a resume.
|
manifest records provenance and the runner settings used to validate a resume.
|
||||||
|
|
||||||
|
## Desktop Linux workflow
|
||||||
|
|
||||||
|
The `mobilegl-desktop` target builds `glcts` as an ordinary host executable that
|
||||||
|
reaches OpenGL only through `libMobileGL.so`. Both backends run headless with no
|
||||||
|
GPU at all, which makes this the cheapest way to measure a single test group
|
||||||
|
while working on it.
|
||||||
|
|
||||||
|
Apt packages: `mesa-vulkan-drivers` (lavapipe, for DirectVulkan),
|
||||||
|
`libegl1-mesa-dev` and `libgles2-mesa-dev` (the system EGL/ES that DirectGLES
|
||||||
|
drives), `libvulkan-dev`, `ninja-build`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cmake -S . -B build-linux -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF
|
||||||
|
cmake --build build-linux --parallel "$(nproc)"
|
||||||
|
|
||||||
|
python tools/cts/scripts/sync_to_cts.py "$CTS"
|
||||||
|
git -C "$CTS" apply <path-to>/tools/cts/patches/0001-fbo-color-texture-attachment.patch
|
||||||
|
cmake -S "$CTS" -B "$CTS/build-cts" -G Ninja -DDEQP_TARGET=mobilegl-desktop \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release
|
||||||
|
ninja -C "$CTS/build-cts" glcts
|
||||||
|
|
||||||
|
python tools/cts/scripts/run_cts_local.py --backend DirectGLES \
|
||||||
|
--glcts "$CTS/build-cts/external/openglcts/modules/glcts" \
|
||||||
|
--lib build-linux/libMobileGL.so \
|
||||||
|
--caselist cases.txt --outdir runs/gles --env EGL_PLATFORM=surfaceless
|
||||||
|
python tools/cts/scripts/qpa_report.py runs/gles --label DirectGLES
|
||||||
|
```
|
||||||
|
|
||||||
|
`EGL_PLATFORM=surfaceless` is not optional for DirectGLES: without a `/dev/dri`
|
||||||
|
node Mesa's EGL fails `eglInitialize` on the default display, and MobileGL
|
||||||
|
reports that as `EGL_BAD_ALLOC` out of `eglCreatePbufferSurface`. DirectVulkan
|
||||||
|
needs nothing extra - lavapipe exposes `VK_EXT_headless_surface`, which is what
|
||||||
|
the desktop platform port's pbuffer path requires.
|
||||||
|
|
||||||
|
Two known differences from a real GPU, both MobileGL's rather than the harness's:
|
||||||
|
DirectVulkan reads back zeros from the **default** framebuffer (a user FBO,
|
||||||
|
renderbuffer- or texture-attached, is correct on both backends), and lavapipe
|
||||||
|
supports renderbuffer formats that Adreno reports as unsupported, so the Android
|
||||||
|
runs see `NotSupported` where these do not.
|
||||||
|
|
||||||
|
### Reference results: KHR-GL45.direct_state_access
|
||||||
|
|
||||||
|
`opengl-cts-4.6.8.1`, the 371 `direct_state_access` cases of the `gl45-main`
|
||||||
|
mustpass list, lavapipe / Mesa 25.2.8, `--deqp-surface-type=fbo`.
|
||||||
|
|
||||||
|
| backend | conformance | strict Pass | Fail | InternalError | Crash |
|
||||||
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
|
| DirectGLES | **82.48%** | 74.93% | 57 | 8 | 0 |
|
||||||
|
| DirectVulkan | **73.58%** | 73.32% | 87 | 8 | 3 |
|
||||||
|
|
||||||
|
`textures_storage_multisample_*` is most of what remains, and the two backends
|
||||||
|
are there for different reasons. Espryt passes the whole `2d` half and fails only
|
||||||
|
the `3d` one, which attaches a multisample **array** texture with
|
||||||
|
`glFramebufferTextureLayer` - layered attachments are not something the
|
||||||
|
framebuffer attachment model represents yet. Magma fails both halves: it cannot
|
||||||
|
sample a multisample texture at all, so the second pass reads back nothing.
|
||||||
|
`program_pipelines_*` needs ARB_separate_shader_objects, which is stubbed
|
||||||
|
throughout.
|
||||||
|
|
||||||
## Android KHR-GL33 workflow
|
## Android KHR-GL33 workflow
|
||||||
|
|
||||||
Goal: measure how much of the OpenGL 3.3 core-profile conformance suite MobileGL
|
Goal: measure how much of the OpenGL 3.3 core-profile conformance suite MobileGL
|
||||||
|
|||||||
@@ -17,3 +17,4 @@ Each skill is a self-contained package, matching the layout used by
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| [gl-cts-on-mobilegl](gl-cts-on-mobilegl/SKILL.md) | Build VK-GL-CTS `glcts` as a standalone Android arm64 binary against MobileGL's own EGL, run KHR-GL33, and report a per-backend OpenGL 3.3 core conformance rate. |
|
| [gl-cts-on-mobilegl](gl-cts-on-mobilegl/SKILL.md) | Build VK-GL-CTS `glcts` as a standalone Android arm64 binary against MobileGL's own EGL, run KHR-GL33, and report a per-backend OpenGL 3.3 core conformance rate. |
|
||||||
| [wgl-gl-cts-on-mobilegl](wgl-gl-cts-on-mobilegl/SKILL.md) | Build MobileGL's Windows x64 WGL drop-in, run GL30-GL46 core CTS against DirectGLES and DirectVulkan, resume safely, and emit validated reports. |
|
| [wgl-gl-cts-on-mobilegl](wgl-gl-cts-on-mobilegl/SKILL.md) | Build MobileGL's Windows x64 WGL drop-in, run GL30-GL46 core CTS against DirectGLES and DirectVulkan, resume safely, and emit validated reports. |
|
||||||
|
| [linux-gl-cts-on-mobilegl](linux-gl-cts-on-mobilegl/SKILL.md) | Build `glcts` as a desktop Linux host binary against MobileGL, run any CTS group headlessly with no GPU, and report Espryt and Magma separately. The path to reach for while iterating on a fix. |
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
---
|
||||||
|
name: linux-gl-cts-on-mobilegl
|
||||||
|
description: Run the Khronos OpenGL CTS (VK-GL-CTS glcts) against MobileGL on desktop Linux with no GPU and no device, and report a per-backend conformance rate for Espryt (DirectGLES) and Magma (DirectVulkan). Use when measuring or iterating on the conformance of one test group, when a fix needs a before/after number, or when neither an Android device nor a Windows GPU box is available.
|
||||||
|
---
|
||||||
|
|
||||||
|
# OpenGL CTS on MobileGL (desktop Linux)
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
`glcts` is built as an ordinary x86-64 host executable that reaches OpenGL only
|
||||||
|
through `libMobileGL.so`, using the `mobilegl-desktop` VK-GL-CTS target and the
|
||||||
|
`tcu::Platform` port in `tools/cts/platform/`. Nothing links `libGL` or `libEGL`,
|
||||||
|
so a result is unambiguously MobileGL's.
|
||||||
|
|
||||||
|
Both backends run headless on software rendering, so this needs no GPU at all:
|
||||||
|
|
||||||
|
- **Espryt** (`DirectGLES`) drives Mesa's OpenGL ES through the system EGL.
|
||||||
|
- **Magma** (`DirectVulkan`) runs on lavapipe, whose `VK_EXT_headless_surface`
|
||||||
|
is what the desktop platform port's pbuffer path requires.
|
||||||
|
|
||||||
|
Always report the two backends **separately**. They are different
|
||||||
|
implementations of the same front end, they fail different cases, and a single
|
||||||
|
combined number hides which one a change moved.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sudo apt-get install -y ninja-build cmake libvulkan-dev \
|
||||||
|
libegl1-mesa-dev libgles2-mesa-dev mesa-vulkan-drivers
|
||||||
|
```
|
||||||
|
|
||||||
|
`mesa-vulkan-drivers` is what installs lavapipe; without it DirectVulkan has no
|
||||||
|
ICD and `eglInitialize` fails inside the backend. A C++23 toolchain is required
|
||||||
|
(GCC 13+, or Clang 20+ — Clang 18 defines `__cpp_concepts` as 201907L, which
|
||||||
|
switches libstdc++'s `<expected>` off and the build fails in
|
||||||
|
`MG_Util/ShaderTranspiler/Types.h`).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export MG=<path-to-MobileGL-worktree>
|
||||||
|
export CTS=<path-to-VK-GL-CTS-checkout>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 1 — build libMobileGL.so
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git -C "$MG" submodule update --init --recursive
|
||||||
|
python3 "$MG/3rdparty/glslang/update_glslang_sources.py" # SPIRV-Tools; ENABLE_OPT is forced on
|
||||||
|
|
||||||
|
cmake -S "$MG" -B "$MG/build-linux" -G Ninja -DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DMOBILEGL_BUILD_TEST=OFF -DMOBILEGL_BUILD_BENCHMARK=OFF \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
|
cmake --build "$MG/build-linux" --parallel "$(nproc)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Add `-DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=FALSE` when iterating. The
|
||||||
|
Release configuration turns LTO on, which makes every relink cost minutes for no
|
||||||
|
behavioural difference; a conformance number is identical either way.
|
||||||
|
|
||||||
|
## Step 2 — get VK-GL-CTS and build glcts
|
||||||
|
|
||||||
|
Use a release tag so the mustpass list, and therefore the reported rate, is
|
||||||
|
citable.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git -C "$CTS" checkout opengl-cts-4.6.8.1
|
||||||
|
python3 "$CTS/external/fetch_sources.py"
|
||||||
|
|
||||||
|
python3 "$MG/tools/cts/scripts/sync_to_cts.py" "$CTS"
|
||||||
|
git -C "$CTS" apply "$MG/tools/cts/patches/0001-fbo-color-texture-attachment.patch"
|
||||||
|
|
||||||
|
cmake -S "$CTS" -B "$CTS/build-cts" -G Ninja -DDEQP_TARGET=mobilegl-desktop \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||||
|
ninja -C "$CTS/build-cts" glcts
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm the configure output says `*** Using MobileGL desktop target`. Budget a
|
||||||
|
couple of hours for the `glcts` link on a small machine; it is a one-time cost
|
||||||
|
that ccache makes cheap afterwards.
|
||||||
|
|
||||||
|
If `fetch_sources.py` dies with `HTTP Error 403` it is an egress policy blocking
|
||||||
|
the GitHub archive downloads (zlib, libpng), not a broken checkout: `git clone`
|
||||||
|
still works, so clone the package at the tag the script pins into
|
||||||
|
`external/<pkg>/src` by hand — for libpng also copy
|
||||||
|
`scripts/pnglibconf.h.prebuilt` to `src/pnglibconf.h`, which is what the
|
||||||
|
script's post-extract step does.
|
||||||
|
|
||||||
|
## Step 3 — run, once per backend
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd "$MG"
|
||||||
|
for BACKEND in DirectGLES DirectVulkan; do
|
||||||
|
python3 tools/cts/scripts/run_cts_local.py --backend "$BACKEND" \
|
||||||
|
--glcts "$CTS/build-cts/external/openglcts/modules/glcts" \
|
||||||
|
--lib build-linux/libMobileGL.so \
|
||||||
|
--caselist cases.txt --outdir "runs/${BACKEND}" \
|
||||||
|
--env EGL_PLATFORM=surfaceless
|
||||||
|
python3 tools/cts/scripts/qpa_report.py "runs/${BACKEND}" --label "$BACKEND"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
`cases.txt` is any subset of a mustpass list. For one group, filter the list
|
||||||
|
rather than running the whole suite:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
grep direct_state_access \
|
||||||
|
"$CTS"/external/openglcts/data/gl_cts/data/mustpass/gl/khronos_mustpass/main/gl45-main.txt \
|
||||||
|
> cases.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
`run_cts_local.py` re-invokes `glcts` with only the cases that have no result
|
||||||
|
yet, so a crash costs one case rather than the run, and it records the case that
|
||||||
|
was open when the process died as `Crash`. `qpa_report.py` scores `Pass`,
|
||||||
|
`NotSupported` and the warning statuses as non-failures, the way Khronos scores
|
||||||
|
a submission, and reports the strict `Pass`-only rate alongside.
|
||||||
|
|
||||||
|
## Required flags, and why
|
||||||
|
|
||||||
|
| Flag | Why it is not optional |
|
||||||
|
| --- | --- |
|
||||||
|
| `--env EGL_PLATFORM=surfaceless` | For DirectGLES. With no `/dev/dri` node Mesa's EGL fails `eglInitialize` on the default display, and MobileGL surfaces that as `EGL_BAD_ALLOC` out of `eglCreatePbufferSurface` — which points at the wrong call entirely. Harmless for DirectVulkan, so pass it to both. |
|
||||||
|
| `--deqp-surface-type=fbo` (the runner's default) | DirectVulkan reads back zeros from the **default** framebuffer. dEQP verifies nearly everything through `glReadPixels`, so rendering to the surface scores Magma near zero for a reason unrelated to conformance. Use it for both backends so the two numbers stay comparable. |
|
||||||
|
| `--deqp-terminate-on-device-lost=disable` (supplied by the runner) | Its default calls `glGetGraphicsResetStatus()` after every case. That is GL 4.5 / `KHR_robustness`, absent from what MobileGL exports, so the pointer is null and the process segfaults on the first case. |
|
||||||
|
|
||||||
|
## What this environment does and does not tell you
|
||||||
|
|
||||||
|
Reproducible here, and MobileGL's own rather than a driver quirk:
|
||||||
|
|
||||||
|
- DirectVulkan's default-framebuffer readback returns zeros; a user FBO,
|
||||||
|
renderbuffer- or texture-attached, is correct on both backends. This is the
|
||||||
|
same defect the Android runs work around, so it can be debugged without a
|
||||||
|
phone.
|
||||||
|
|
||||||
|
Different from a real GPU, so do not read conformance into it:
|
||||||
|
|
||||||
|
- lavapipe supports renderbuffer formats Adreno reports as unsupported, so the
|
||||||
|
Android runs see `NotSupported` where these do not, and vice versa.
|
||||||
|
- Backend limits differ. `GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT` is 16 on llvmpipe
|
||||||
|
and on lavapipe; a device that reports 1 will not exercise the same paths.
|
||||||
|
- Everything is a software rasterizer, so a case that fails only under real
|
||||||
|
timing or real tiling will not fail here.
|
||||||
|
|
||||||
|
## Reference results: KHR-GL45.direct_state_access
|
||||||
|
|
||||||
|
`opengl-cts-4.6.8.1`, the 371 `direct_state_access` cases of the `gl45-main`
|
||||||
|
mustpass list, Mesa 25.2.8, `--deqp-surface-type=fbo`.
|
||||||
|
|
||||||
|
| backend | renderer | conformance | strict Pass | Fail | InternalError | Crash |
|
||||||
|
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
|
| DirectGLES | Espryt | **82.48%** | 74.93% | 57 | 8 | 0 |
|
||||||
|
| DirectVulkan | Magma | **73.58%** | 73.32% | 87 | 8 | 3 |
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
platform/tcuMobileGLPlatform.{cpp,hpp} dEQP tcu::Platform for MobileGL
|
||||||
|
targets/mobilegl-desktop.cmake VK-GL-CTS target (-DDEQP_TARGET=mobilegl-desktop)
|
||||||
|
scripts/sync_to_cts.py inject the port into a CTS checkout
|
||||||
|
scripts/run_cts_local.py crash-resuming local-host runner
|
||||||
|
scripts/qpa_report.py .qpa -> conformance rate
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
interface:
|
||||||
|
display_name: "OpenGL CTS on MobileGL (desktop Linux)"
|
||||||
|
short_description: "Run VK-GL-CTS glcts against MobileGL headlessly and report Espryt and Magma separately"
|
||||||
|
default_prompt: "Use $linux-gl-cts-on-mobilegl to run the selected OpenGL CTS group against MobileGL on this Linux host and report the conformance rate for DirectGLES (Espryt) and DirectVulkan (Magma) separately."
|
||||||
Reference in New Issue
Block a user