mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 22:28:32 +09:00
[Fix, Test] (GLImpl, MG_Test): validate glBlitFramebuffer's mask bits, filter enum and LINEAR depth rule
This commit is contained in:
@@ -3153,15 +3153,55 @@ namespace MobileGL::MG_Impl::GLImpl {
|
|||||||
GetNamedFramebufferAttachmentParameteriv_State(framebuffer, attachment, pname, params);
|
GetNamedFramebufferAttachmentParameteriv_State(framebuffer, attachment, pname, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The three argument errors GL 4.6 core 18.3.1 asks a blit for. They have to be raised here,
|
||||||
|
// in the backend-independent frontend: DirectGLES drains the driver's error queue around the
|
||||||
|
// blit on purpose (that is how the resolve fallback probes the driver), so an ES-side
|
||||||
|
// rejection never reaches the application and glGetError() answered GL_NO_ERROR for a call
|
||||||
|
// the spec requires to fail (KHR-GL30.api.coverage's glBlitFramebuffer sub-check). DirectVulkan
|
||||||
|
// already dropped the bad-filter and LINEAR-with-depth/stencil calls on the floor with a log
|
||||||
|
// line (VulkanRenderer::BlitFramebuffer), so the only thing that changes for it is that the
|
||||||
|
// error is now visible where the spec says it should be.
|
||||||
|
static Bool ValidateBlitMaskAndFilter(const char* functionName, GLbitfield mask, GLenum filter) {
|
||||||
|
constexpr GLbitfield kBlitMaskBits = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||||
|
if ((mask & ~kBlitMaskBits) != 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidValue,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
"mask contains bits other than GL_COLOR_BUFFER_BIT, "
|
||||||
|
"GL_DEPTH_BUFFER_BIT and GL_STENCIL_BUFFER_BIT."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (filter != GL_NEAREST && filter != GL_LINEAR) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidEnum,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
"filter must be GL_NEAREST or GL_LINEAR."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Depth and stencil have no meaningful interpolation, so GL_LINEAR is rejected outright
|
||||||
|
// rather than downgraded - even when the mask also carries the colour bit.
|
||||||
|
if (filter == GL_LINEAR && (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0) {
|
||||||
|
MG_State::pGLContext->RecordError(
|
||||||
|
ErrorCode::InvalidOperation,
|
||||||
|
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", functionName,
|
||||||
|
"GL_LINEAR filtering is not allowed when mask includes "
|
||||||
|
"GL_DEPTH_BUFFER_BIT or GL_STENCIL_BUFFER_BIT."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
GLenum filter) {
|
GLenum filter) {
|
||||||
|
if (!ValidateBlitMaskAndFilter(__func__, mask, filter)) return;
|
||||||
BlitNamedFramebuffer_State(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
|
BlitNamedFramebuffer_State(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
|
||||||
dstY1, mask, filter);
|
dstY1, mask, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
if (!ValidateBlitMaskAndFilter(__func__, mask, filter)) return;
|
||||||
BlitFramebuffer_Backend(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
BlitFramebuffer_Backend(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
// * KHR-GL32.api.coverage - glFenceSync's condition/flags and glWaitSync's flags/timeout.
|
// * KHR-GL32.api.coverage - glFenceSync's condition/flags and glWaitSync's flags/timeout.
|
||||||
// * KHR-GL31.api.coverage - a draw's mode INVALID_ENUM has to outrank MobileGL's own
|
// * KHR-GL31.api.coverage - a draw's mode INVALID_ENUM has to outrank MobileGL's own
|
||||||
// no-current-program guard.
|
// no-current-program guard.
|
||||||
|
// * KHR-GL30.api.coverage - glBlitFramebuffer's mask bits, filter enum and the LINEAR-with-
|
||||||
|
// depth/stencil rule.
|
||||||
// Plus the indexed-getter parity RC-7b is about: glGetBooleani_v / glGetInteger64i_v /
|
// Plus the indexed-getter parity RC-7b is about: glGetBooleani_v / glGetInteger64i_v /
|
||||||
// glGetFloati_v / glGetDoublei_v must answer every pname glGetIntegeri_v answers.
|
// glGetFloati_v / glGetDoublei_v must answer every pname glGetIntegeri_v answers.
|
||||||
//
|
//
|
||||||
@@ -36,6 +38,7 @@
|
|||||||
#include "Init.h"
|
#include "Init.h"
|
||||||
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
|
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.h>
|
||||||
#include <MG_Impl/GLImpl/Drawing/GL_Drawing.h>
|
#include <MG_Impl/GLImpl/Drawing/GL_Drawing.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/Program/GL_Program.h>
|
#include <MG_Impl/GLImpl/Program/GL_Program.h>
|
||||||
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
||||||
@@ -539,4 +542,49 @@ void main() { g_color = vec4(1); }
|
|||||||
GL_INVALID_OPERATION},
|
GL_INVALID_OPERATION},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KHR-GL30.api.coverage's glBlitFramebuffer sub-check. The frontend passed mask and filter
|
||||||
|
// straight through, and DirectGLES drains the driver's error queue around the blit so the ES
|
||||||
|
// rejection never surfaced either - both illegal calls reported GL_NO_ERROR. Every row here
|
||||||
|
// is rejected before the backend function pointer is reached, which is what lets this
|
||||||
|
// GPU-free suite run them at all.
|
||||||
|
TEST_F(NegativeApiErrorsTest, BlitFramebufferRejectsBadMasksAndFilters) {
|
||||||
|
DrainErrors();
|
||||||
|
// The bit the coverage test smuggles in: a legal glMapBufferRange flag, not a blit one.
|
||||||
|
constexpr GLbitfield kForeignBit = GL_MAP_INVALIDATE_BUFFER_BIT;
|
||||||
|
|
||||||
|
RunRows({
|
||||||
|
{"glBlitFramebuffer with a mask bit outside COLOR|DEPTH|STENCIL",
|
||||||
|
[] {
|
||||||
|
BlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT | kForeignBit, GL_NEAREST);
|
||||||
|
},
|
||||||
|
GL_INVALID_VALUE},
|
||||||
|
{"glBlitFramebuffer with a filter that is neither GL_NEAREST nor GL_LINEAR",
|
||||||
|
[] { BlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NONE); }, GL_INVALID_ENUM},
|
||||||
|
{"glBlitFramebuffer of colour+stencil with GL_LINEAR",
|
||||||
|
[] {
|
||||||
|
BlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_LINEAR);
|
||||||
|
},
|
||||||
|
GL_INVALID_OPERATION},
|
||||||
|
{"glBlitFramebuffer of depth with GL_LINEAR",
|
||||||
|
[] { BlitFramebuffer(0, 0, 16, 16, 0, 0, 16, 16, GL_DEPTH_BUFFER_BIT, GL_LINEAR); },
|
||||||
|
GL_INVALID_OPERATION},
|
||||||
|
// The DSA form has to answer identically.
|
||||||
|
{"glBlitNamedFramebuffer with a mask bit outside COLOR|DEPTH|STENCIL",
|
||||||
|
[] {
|
||||||
|
BlitNamedFramebuffer(0, 0, 0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT | kForeignBit,
|
||||||
|
GL_NEAREST);
|
||||||
|
},
|
||||||
|
GL_INVALID_VALUE},
|
||||||
|
{"glBlitNamedFramebuffer with a bad filter",
|
||||||
|
[] { BlitNamedFramebuffer(0, 0, 0, 0, 16, 16, 0, 0, 16, 16, GL_COLOR_BUFFER_BIT, GL_NONE); },
|
||||||
|
GL_INVALID_ENUM},
|
||||||
|
{"glBlitNamedFramebuffer of depth+stencil with GL_LINEAR",
|
||||||
|
[] {
|
||||||
|
BlitNamedFramebuffer(0, 0, 0, 0, 16, 16, 0, 0, 16, 16,
|
||||||
|
GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_LINEAR);
|
||||||
|
},
|
||||||
|
GL_INVALID_OPERATION},
|
||||||
|
});
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
Reference in New Issue
Block a user