mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 04:38:30 +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);
|
||||
}
|
||||
|
||||
// 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,
|
||||
GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask,
|
||||
GLenum filter) {
|
||||
if (!ValidateBlitMaskAndFilter(__func__, mask, filter)) return;
|
||||
BlitNamedFramebuffer_State(readFramebuffer, drawFramebuffer, srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
|
||||
dstY1, mask, filter);
|
||||
}
|
||||
|
||||
void BlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1,
|
||||
GLint dstY1, GLbitfield mask, GLenum filter) {
|
||||
if (!ValidateBlitMaskAndFilter(__func__, mask, filter)) return;
|
||||
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-GL31.api.coverage - a draw's mode INVALID_ENUM has to outrank MobileGL's own
|
||||
// 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 /
|
||||
// glGetFloati_v / glGetDoublei_v must answer every pname glGetIntegeri_v answers.
|
||||
//
|
||||
@@ -36,6 +38,7 @@
|
||||
#include "Init.h"
|
||||
#include <MG_Impl/GLImpl/Buffer/GL_Buffer.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/Program/GL_Program.h>
|
||||
#include <MG_Impl/GLImpl/RenderState/GL_RenderState.h>
|
||||
@@ -539,4 +542,49 @@ void main() { g_color = vec4(1); }
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user