From 8eaf2d0069a4bb418e484458c2489af1f5068fbc Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Sat, 1 Aug 2026 00:27:05 -0400 Subject: [PATCH] [Fix] (MG_Impl): create sampler objects at Gen; guard combined-format CopyTexImage glGenSamplers creates the sampler objects themselves (unlike texture and buffer names), so glIsSampler must answer GL_TRUE before any bind - the names now get their state vectors at Gen time (KHR-GL33.api.coverage). glCopyTexImage2D with a combined DEPTH_STENCIL internalformat now requires both halves in the read framebuffer and reports GL_INVALID_OPERATION when only the depth or only the stencil attachment point is populated (packed_depth_stencil.validate_errors.*). --- MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp | 5 +++++ MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp | 17 ++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp index 2c78160c..e28c5963 100644 --- a/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp +++ b/MobileGL/MG_Impl/GLImpl/Sampler/GL_Sampler.cpp @@ -185,6 +185,11 @@ namespace MobileGL::MG_Impl::GLImpl { static thread_local Vector names; MG_State::pGLContext->GenSamplerNames(count, names); Memcpy(samplers, names.data(), count * sizeof(GLuint)); + // Unlike textures/buffers, glGenSamplers CREATES the sampler objects: each name + // is immediately a sampler (glIsSampler == GL_TRUE before any bind). + for (GLsizei i = 0; i < count; ++i) { + MG_State::pGLContext->CreateSamplerObject(names[i]); + } } void DeleteSamplers_State(GLsizei count, const GLuint* samplers) { diff --git a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp index 753005a3..a2f2a335 100644 --- a/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp +++ b/MobileGL/MG_Impl/GLImpl/Texture/GL_Texture.cpp @@ -2820,7 +2820,22 @@ namespace MobileGL::MG_Impl::GLImpl { "The attachment specified by the read buffer is incomplete.")); \ return false; \ } - if (isDepth) { + if (isDepth && isStencil) { + // A combined internalformat copies both halves, so the read framebuffer + // must populate both attachment points. + const auto& stencilAttachment = currentReadFBO->GetAttachment(FramebufferAttachmentType::Stencil); + const auto& depthAttachment = currentReadFBO->GetAttachment(FramebufferAttachmentType::Depth); + if (!depthAttachment.IsValid() || depthAttachment.IsEmpty() || !stencilAttachment.IsValid() || + stencilAttachment.IsEmpty()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique( + "MG_Impl/GLImpl", "CopyTexImage2D_State", + "DEPTH_STENCIL copy requires both depth and stencil attachments in the read framebuffer.")); + return false; + } + GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Depth); + } else if (isDepth) { GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Depth); } else if (isStencil) { GET_SRC_INTERNAL_FORMAT(FramebufferAttachmentType::Stencil);