mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 14:18:31 +09:00
Compare commits
49
Commits
| 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 |
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -1096,12 +1097,12 @@ DECLARE_GL_FUNCTION_HEAD(void, GetVertexArrayiv, GLuint vaobj, GLenum pname, GLi
|
|||||||
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,
|
||||||
|
|||||||
@@ -118,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
|
||||||
|
|||||||
@@ -20,4 +20,15 @@ namespace MobileGL::MG_Impl::GLImpl::FramebufferImpl {
|
|||||||
Bool ValidateColorAttachmentInRange(FramebufferAttachmentType attachment, const char* caller);
|
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
|
||||||
|
|||||||
@@ -1432,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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -137,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;
|
||||||
@@ -390,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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
+10
-8
@@ -77,15 +77,17 @@ mustpass list, lavapipe / Mesa 25.2.8, `--deqp-surface-type=fbo`.
|
|||||||
|
|
||||||
| backend | conformance | strict Pass | Fail | InternalError | Crash |
|
| backend | conformance | strict Pass | Fail | InternalError | Crash |
|
||||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
| DirectGLES | **74.93%** | 67.39% | 85 | 8 | 0 |
|
| DirectGLES | **82.48%** | 74.93% | 57 | 8 | 0 |
|
||||||
| DirectVulkan | **73.32%** | 73.05% | 88 | 8 | 3 |
|
| DirectVulkan | **73.58%** | 73.32% | 87 | 8 | 3 |
|
||||||
|
|
||||||
`textures_storage_multisample_*` is 54 of what remains on either backend and
|
`textures_storage_multisample_*` is most of what remains, and the two backends
|
||||||
needs a feature rather than a fix: a multisample texture has to be attachable to
|
are there for different reasons. Espryt passes the whole `2d` half and fails only
|
||||||
a framebuffer and then sampleable through `sampler2DMS`, and the array half of
|
the `3d` one, which attaches a multisample **array** texture with
|
||||||
the group additionally needs layered attachments, which the framebuffer
|
`glFramebufferTextureLayer` - layered attachments are not something the
|
||||||
attachment model does not represent yet. `program_pipelines_*` needs
|
framebuffer attachment model represents yet. Magma fails both halves: it cannot
|
||||||
ARB_separate_shader_objects, which is stubbed throughout.
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ mustpass list, Mesa 25.2.8, `--deqp-surface-type=fbo`.
|
|||||||
|
|
||||||
| backend | renderer | conformance | strict Pass | Fail | InternalError | Crash |
|
| backend | renderer | conformance | strict Pass | Fail | InternalError | Crash |
|
||||||
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
| --- | --- | ---: | ---: | ---: | ---: | ---: |
|
||||||
| DirectGLES | Espryt | **74.93%** | 67.39% | 85 | 8 | 0 |
|
| DirectGLES | Espryt | **82.48%** | 74.93% | 57 | 8 | 0 |
|
||||||
| DirectVulkan | Magma | **73.32%** | 73.05% | 88 | 8 | 3 |
|
| DirectVulkan | Magma | **73.58%** | 73.32% | 87 | 8 | 3 |
|
||||||
|
|
||||||
## Contents
|
## Contents
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user