diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 39638faa..60292eea 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -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("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("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("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); } diff --git a/MobileGL/MG_Test/State/NegativeApiErrorsTest.cpp b/MobileGL/MG_Test/State/NegativeApiErrorsTest.cpp index 50ee18b2..6459a0e7 100644 --- a/MobileGL/MG_Test/State/NegativeApiErrorsTest.cpp +++ b/MobileGL/MG_Test/State/NegativeApiErrorsTest.cpp @@ -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 #include +#include #include #include #include @@ -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