diff --git a/MobileGL/MG_Backend/BackendObject.h b/MobileGL/MG_Backend/BackendObject.h index 88190dd0..14014b3c 100644 --- a/MobileGL/MG_Backend/BackendObject.h +++ b/MobileGL/MG_Backend/BackendObject.h @@ -324,6 +324,13 @@ namespace MobileGL { Float MaxFragmentInterpolationOffset = 0.4375f; Int FragmentInterpolationOffsetBits = 4; Bool SupportsWideLines = false; + // Whether a framebuffer whose depth and stencil attachments are distinct + // images can be rendered to. GL only requires support when both refer to the + // same image and lets an implementation answer GL_FRAMEBUFFER_UNSUPPORTED + // otherwise, which is what DirectVulkan (one combined attachment) and the + // real ES drivers behind DirectGLES both do. Defaults to true so a backend + // that never sets it keeps the permissive behaviour. + Bool SupportsDistinctDepthStencilAttachments = true; SizeT MaxShaderStorageBlockSize = 128 * 1024 * 1024; Uint32 SubgroupSize = 0; Uint32 SubgroupSupportedStages = 0; diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 984cbbe8..366b7a67 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -357,6 +357,42 @@ namespace MobileGL::MG_Backend::DirectGLES { return complete; } + // Whether the driver renders to a framebuffer whose depth and stencil come from + // two different renderbuffers. GL only requires support when both attachments are + // the same image, and ES drivers commonly answer GL_FRAMEBUFFER_UNSUPPORTED here; + // reporting COMPLETE from the frontend and then rendering into a framebuffer the + // driver refuses leaves the results silently empty. + Bool ProbeDistinctDepthStencilAttachments(const MG_External::GLESFunctionsTable& gl) { + if (!gl.glGenFramebuffers || !gl.glBindFramebuffer || !gl.glFramebufferRenderbuffer || + !gl.glCheckFramebufferStatus || !gl.glDeleteFramebuffers || !gl.glGenRenderbuffers || + !gl.glBindRenderbuffer || !gl.glRenderbufferStorage || !gl.glDeleteRenderbuffers) { + return true; + } + + GLint prevFramebuffer = 0, prevRenderbuffer = 0; + gl.glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFramebuffer); + gl.glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer); + + GLuint framebuffer = 0; + GLuint renderbuffers[2] = {0, 0}; + gl.glGenFramebuffers(1, &framebuffer); + gl.glGenRenderbuffers(2, renderbuffers); + gl.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[0]); + gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 4, 4); + gl.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffers[1]); + gl.glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, 4, 4); + gl.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffers[0]); + gl.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffers[1]); + const Bool supported = gl.glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + + gl.glBindFramebuffer(GL_FRAMEBUFFER, static_cast(prevFramebuffer)); + gl.glBindRenderbuffer(GL_RENDERBUFFER, static_cast(prevRenderbuffer)); + gl.glDeleteFramebuffers(1, &framebuffer); + gl.glDeleteRenderbuffers(2, renderbuffers); + return supported; + } + Bool ProbeFramebufferCompletenessForRenderbuffer(const MG_External::GLESFunctionsTable& gl, GLuint renderbuffer, TextureInternalFormat format) { @@ -1046,6 +1082,8 @@ namespace MobileGL::MG_Backend::DirectGLES { clampStageImageUniforms(m_GLESCapabilities.MaxFragmentImageUniforms); m_dynamicParameters.MaxComputeImageUniforms = clampStageImageUniforms(m_GLESCapabilities.MaxComputeImageUniforms); + m_dynamicParameters.SupportsDistinctDepthStencilAttachments = + ProbeDistinctDepthStencilAttachments(DirectGLES::g_GLESFuncs); m_dynamicParameters.MaxDrawBuffers = m_GLESCapabilities.MaxDrawBuffers; m_dynamicParameters.MaxColorAttachments = m_GLESCapabilities.MaxColorAttachments; m_dynamicParameters.MaxClipDistances = m_GLESCapabilities.MaxClipDistances; diff --git a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp index 3155751a..40eb3166 100644 --- a/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp +++ b/MobileGL/MG_Impl/GLImpl/Framebuffer/GL_Framebuffer.cpp @@ -22,9 +22,21 @@ namespace MobileGL::MG_Impl::GLImpl { namespace { - Bool IsActiveBackendDirectVulkan() { + // GL only requires support for framebuffers whose depth and stencil attachments + // are the same image; anything else may be reported GL_FRAMEBUFFER_UNSUPPORTED. + // DirectVulkan cannot form two separate attachments at all, and the real ES + // drivers behind DirectGLES answer UNSUPPORTED for it too - so saying COMPLETE + // and then rendering into a framebuffer the driver refuses produced silently + // empty results (KHR-GL3x.packed_depth_stencil.verify_mixed_attachments). + Bool ActiveBackendRejectsDistinctDepthStencil() { auto* activeBackend = MG_Backend::pActiveBackendObject.get(); - return activeBackend != nullptr && activeBackend->GetBackendType() == BackendType::DirectVulkan; + if (activeBackend == nullptr) { + return false; + } + if (activeBackend->GetBackendType() == BackendType::DirectVulkan) { + return true; + } + return !activeBackend->GetDynamicParameters().SupportsDistinctDepthStencilAttachments; } Bool HasDistinctCompleteDepthStencilTextureAttachments( @@ -66,7 +78,7 @@ namespace MobileGL::MG_Impl::GLImpl { (depthAttachment.IsTexture() || stencilAttachment.IsTexture()); } - Bool IsUnsupportedFramebufferForDirectVulkan( + Bool HasUnsupportedDistinctDepthStencilAttachments( const MG_State::GLState::FramebufferObject& framebufferObject) { // TODO: Keep this in sync with DirectVulkan renderbuffer support as color renderbuffer rendering lands. return HasDistinctCompleteDepthStencilTextureAttachments(framebufferObject) || @@ -1631,8 +1643,8 @@ namespace MobileGL::MG_Impl::GLImpl { if (HasNonRenderableColorAttachment(*framebufferObject)) { return GL_FRAMEBUFFER_UNSUPPORTED; } - if (IsActiveBackendDirectVulkan() && - IsUnsupportedFramebufferForDirectVulkan(*framebufferObject)) { + if (ActiveBackendRejectsDistinctDepthStencil() && + HasUnsupportedDistinctDepthStencilAttachments(*framebufferObject)) { return GL_FRAMEBUFFER_UNSUPPORTED; } return GL_FRAMEBUFFER_COMPLETE; @@ -1659,8 +1671,8 @@ namespace MobileGL::MG_Impl::GLImpl { if (HasNonRenderableColorAttachment(*framebufferObject)) { return GL_FRAMEBUFFER_UNSUPPORTED; } - if (IsActiveBackendDirectVulkan() && - IsUnsupportedFramebufferForDirectVulkan(*framebufferObject)) { + if (ActiveBackendRejectsDistinctDepthStencil() && + HasUnsupportedDistinctDepthStencilAttachments(*framebufferObject)) { return GL_FRAMEBUFFER_UNSUPPORTED; } return GL_FRAMEBUFFER_COMPLETE;