CVE-2026-87464
Overview
Background
- ANGLE
- Chrome’s translation layer that implements the WebGL/OpenGL ES API on top of native graphics backends, including the GL backend where this bug lives.
- `TextureGL::setStorage()`
- the ANGLE GL-backend routine that allocates immutable texture storage in response to
glTexStorage3D-style calls. - `reattachTextureToFboAfterLayerIncrease`
- an ANGLE feature flag whose workaround forces framebuffers to re-bind to a texture after its layer count grows and its backing storage is reallocated.
- `TextureLayerCountIncreased`
- a
SubjectMessagebroadcast to observers (such as an attached FBO) signaling that a_2DArraytexture gained layers and must be re-attached to fresh storage.
Root Cause Analysis
When an FBO is attached to a layer of a gl::TextureType::_2DArray texture and the texture’s immutable storage is then (re)allocated through TextureGL::setStorage() with a larger size.depth (more array layers), the native GL driver allocates a new backing object, but the framebuffer attachment was left pointing at the old, now-freed storage. The pre-existing reattachTextureToFboAfterLayerIncrease workaround already emitted angle::SubjectMessage::TextureLayerCountIncreased on the mutable glTexImage3D path so observers would reattach, but setStorage() (the glTexStorage3D immutable path) never issued that notification, violating the invariant that any layer-count increase which reallocates storage must notify attached FBOs.
The fix mirrors the mutable-path logic inside setStorage(): it iterates the mip levels, compares the incoming size.depth against each gl::ImageDesc’s previous depth, and calls onStateChange(angle::SubjectMessage::TextureLayerCountIncreased) when depth grows, so the FBO reattaches to the new storage instead of dereferencing the stale one.
glTexStorage3D path, leaving attached framebuffers pointing at freed storage after a layer-count increase; the fix closes the gap by emitting the same TextureLayerCountIncreased notification from TextureGL::setStorage().Attack Path
- Create and attach
A page’s WebGL2 code creates a
2D_ARRAYtexture, gives it storage, and attaches layer 0 of it to a framebuffer color attachment viaglFramebufferTextureLayer. - Reallocate via TexStorage3D
The page calls
glTexStorage3D(or otherwise re-drivessetStorage()) with an increased depth, causing ANGLE to allocate new backing storage and free the old one without notifying the attached FBO. - Dangle the attachment
Because
setStorage()never emittedTextureLayerCountIncreased, the framebuffer still references the freed storage, producing a use-after-free. - Trigger the UAF The page renders to or samples from the framebuffer/texture, driving reads or writes through the dangling pointer to reclaimed GPU-process memory.
Impact Assessment
2D_ARRAY texture and framebuffer-layer calls; no special permissions are required beyond loading the page.Files Changed
src/libANGLE/renderer/gl/TextureGL.cppsrc/tests/gl_tests/TextureTest.cpp
Audit Directions
- Paired mutable/immutable pathsWhen a workaround or invariant is added to a mutable resource-allocation path (
glTexImage*), audit the immutable counterpart (glTexStorage*/setStorage) for the same notification or reattachment logic. - Reallocation without observer notificationFlag any code that frees and reallocates backing storage but does not broadcast a
SubjectMessage(e.g.ObjectReallocated,TextureLayerCountIncreased) to attachments such as FBOs. - Feature-flag-gated fixesVerify that security workarounds behind flags like
reattachTextureToFboAfterLayerIncreaseare consistently applied to every entry point that can trigger the vulnerable condition, not just the first one discovered.
Patch
From 87f5655006e4a66842c004a722440a643cc03c22 Mon Sep 17 00:00:00 2001
From: Zhenyao Mo <zmo@chromium.org>
Date: Tue, 18 Aug 2026 14:49:13 -0700
Subject: [PATCH] GL: Apply reattachTextureToFboAfterLayerIncrease to TexStorage3D
This adds the workaround logic inside TextureGL::setStorage() for cases
where an FBO is attached before the immutable storage is allocated. Also
updates the related tests to verify this with both TexImage3D and
TexStorage3D.
Bug: chromium:544163112
Change-Id: Ibb50ffeed6eaa369bed4ab25b625c16a1e63796c
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/8266365
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Zhenyao Mo <zmo@chromium.org>
---
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index be2ccee..45ea907 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -1300,6 +1300,20 @@
onStateChange(angle::SubjectMessage::ObjectReallocated);
}
+ if (features.reattachTextureToFboAfterLayerIncrease.enabled &&
+ getType() == gl::TextureType::_2DArray)
+ {
+ for (size_t level = 0; level < levels; level++)
+ {
+ const gl::ImageDesc &desc = mState.getImageDesc(gl::TextureTarget::_2DArray, level);
+ if (size.depth > desc.size.depth)
+ {
+ onStateChange(angle::SubjectMessage::TextureLayerCountIncreased);
+ break;
+ }
+ }
+ }
+
const gl::InternalFormat &originalInternalFormatInfo =
gl::GetSizedInternalFormatInfo(internalFormat);
nativegl::TexStorageFormat texStorageFormat =
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 3e6bc69..f391bdd 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -9363,54 +9363,70 @@
// layer count without releasing storage.
ANGLE_SKIP_TEST_IF(IsMetal());
- glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ for (bool useTexStorage : {false, true})
+ {
+ glDeleteTextures(1, &m2DArrayTexture);
+ glGenTextures(1, &m2DArrayTexture);
- std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsRed.data());
- ASSERT_GL_NO_ERROR();
+ glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GLFramebuffer fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
+ std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsRed.data());
+ ASSERT_GL_NO_ERROR();
- // Increase layer count to 2.
- std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsGreen.data());
- ASSERT_GL_NO_ERROR();
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- // Verify layer 0 points to the new memory (green).
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
- // Verify layer 1 is also green.
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ // Increase layer count to 2.
+ std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
+ if (useTexStorage)
+ {
+ glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 4, 4, 2);
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 4, 4, 2, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ else
+ {
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ ASSERT_GL_NO_ERROR();
- // Clear layer 1 to blue and verify.
- glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
+ // Verify layer 0 points to the new memory (green).
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
- // Now sample from layer 0 and layer 1 using a shader to ensure texture memory matches FBO
- // memory.
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
- glUseProgram(mProgram);
- glUniform1i(mTextureArrayLocation, 0);
+ // Verify layer 1 is also green.
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
- // Verify layer 0 is green.
- glUniform1i(mTextureArraySliceUniformLocation, 0);
- drawQuad(mProgram, "position", 0.5f);
- EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::green);
+ // Clear layer 1 to blue and verify.
+ glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
- // Verify layer 1 is blue.
- glUniform1i(mTextureArraySliceUniformLocation, 1);
- drawQuad(mProgram, "position", 0.5f);
- EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::blue);
+ // Now sample from layer 0 and layer 1 using a shader to ensure texture memory matches FBO
+ // memory.
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
+ glUseProgram(mProgram);
+ glUniform1i(mTextureArrayLocation, 0);
+
+ // Verify layer 0 is green.
+ glUniform1i(mTextureArraySliceUniformLocation, 0);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::green);
+
+ // Verify layer 1 is blue.
+ glUniform1i(mTextureArraySliceUniformLocation, 1);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::blue);
+ }
}
// Test increasing layer count of a 2D array texture when one of its layers is attached to a
@@ -9423,62 +9439,78 @@
// layer count without releasing storage.
ANGLE_SKIP_TEST_IF(IsMetal());
- glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ for (bool useTexStorage : {false, true})
+ {
+ glDeleteTextures(1, &m2DArrayTexture);
+ glGenTextures(1, &m2DArrayTexture);
- std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsRed.data());
- ASSERT_GL_NO_ERROR();
+ glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GLFramebuffer fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
+ std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsRed.data());
+ ASSERT_GL_NO_ERROR();
- // Set up and switch to a secondary context sharing resources with the current context.
- EGLWindow *window = getEGLWindow();
- EGLDisplay display = window->getDisplay();
- EGLConfig config = window->getConfig();
- EGLSurface surface = window->getSurface();
- EGLint contextAttributes[] = {
- EGL_CONTEXT_MAJOR_VERSION_KHR,
- GetParam().majorVersion,
- EGL_CONTEXT_MINOR_VERSION_KHR,
- GetParam().minorVersion,
- EGL_NONE,
- };
- EGLContext context1 = eglGetCurrentContext();
- EGLContext context2 = eglCreateContext(display, config, context1, contextAttributes);
Regression Test / PoC
diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp
index 3e6bc69..f391bdd 100644
--- a/src/tests/gl_tests/TextureTest.cpp
+++ b/src/tests/gl_tests/TextureTest.cpp
@@ -9363,54 +9363,70 @@
// layer count without releasing storage.
ANGLE_SKIP_TEST_IF(IsMetal());
- glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ for (bool useTexStorage : {false, true})
+ {
+ glDeleteTextures(1, &m2DArrayTexture);
+ glGenTextures(1, &m2DArrayTexture);
- std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsRed.data());
- ASSERT_GL_NO_ERROR();
+ glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GLFramebuffer fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
+ std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsRed.data());
+ ASSERT_GL_NO_ERROR();
- // Increase layer count to 2.
- std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsGreen.data());
- ASSERT_GL_NO_ERROR();
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- // Verify layer 0 points to the new memory (green).
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
- // Verify layer 1 is also green.
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ // Increase layer count to 2.
+ std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
+ if (useTexStorage)
+ {
+ glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 4, 4, 2);
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 4, 4, 2, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ else
+ {
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ ASSERT_GL_NO_ERROR();
- // Clear layer 1 to blue and verify.
- glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
+ // Verify layer 0 points to the new memory (green).
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
- // Now sample from layer 0 and layer 1 using a shader to ensure texture memory matches FBO
- // memory.
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
- glUseProgram(mProgram);
- glUniform1i(mTextureArrayLocation, 0);
+ // Verify layer 1 is also green.
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
- // Verify layer 0 is green.
- glUniform1i(mTextureArraySliceUniformLocation, 0);
- drawQuad(mProgram, "position", 0.5f);
- EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::green);
+ // Clear layer 1 to blue and verify.
+ glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
- // Verify layer 1 is blue.
- glUniform1i(mTextureArraySliceUniformLocation, 1);
- drawQuad(mProgram, "position", 0.5f);
- EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::blue);
+ // Now sample from layer 0 and layer 1 using a shader to ensure texture memory matches FBO
+ // memory.
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0);
+ glUseProgram(mProgram);
+ glUniform1i(mTextureArrayLocation, 0);
+
+ // Verify layer 0 is green.
+ glUniform1i(mTextureArraySliceUniformLocation, 0);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::green);
+
+ // Verify layer 1 is blue.
+ glUniform1i(mTextureArraySliceUniformLocation, 1);
+ drawQuad(mProgram, "position", 0.5f);
+ EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::blue);
+ }
}
// Test increasing layer count of a 2D array texture when one of its layers is attached to a
@@ -9423,62 +9439,78 @@
// layer count without releasing storage.
ANGLE_SKIP_TEST_IF(IsMetal());
- glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ for (bool useTexStorage : {false, true})
+ {
+ glDeleteTextures(1, &m2DArrayTexture);
+ glGenTextures(1, &m2DArrayTexture);
- std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsRed.data());
- ASSERT_GL_NO_ERROR();
+ glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GLFramebuffer fbo;
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
+ std::vector<GLColor> pixelsRed(4 * 4 * 1, GLColor::red);
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsRed.data());
+ ASSERT_GL_NO_ERROR();
- // Set up and switch to a secondary context sharing resources with the current context.
- EGLWindow *window = getEGLWindow();
- EGLDisplay display = window->getDisplay();
- EGLConfig config = window->getConfig();
- EGLSurface surface = window->getSurface();
- EGLint contextAttributes[] = {
- EGL_CONTEXT_MAJOR_VERSION_KHR,
- GetParam().majorVersion,
- EGL_CONTEXT_MINOR_VERSION_KHR,
- GetParam().minorVersion,
- EGL_NONE,
- };
- EGLContext context1 = eglGetCurrentContext();
- EGLContext context2 = eglCreateContext(display, config, context1, contextAttributes);
- ASSERT_NE(context2, EGL_NO_CONTEXT);
- eglMakeCurrent(display, surface, surface, context2);
+ GLFramebuffer fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 0);
- // In the secondary context, bind the texture and increase layer count to 2.
- glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
- std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
- glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
- pixelsGreen.data());
- ASSERT_GL_NO_ERROR();
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::red);
- // Switch back to the primary context.
- eglMakeCurrent(display, surface, surface, context1);
+ // Set up and switch to a secondary context sharing resources with the current context.
+ EGLWindow *window = getEGLWindow();
+ EGLDisplay display = window->getDisplay();
+ EGLConfig config = window->getConfig();
+ EGLSurface surface = window->getSurface();
+ EGLint contextAttributes[] = {
+ EGL_CONTEXT_MAJOR_VERSION_KHR,
+ GetParam().majorVersion,
+ EGL_CONTEXT_MINOR_VERSION_KHR,
+ GetParam().minorVersion,
+ EGL_NONE,
+ };
+ EGLContext context1 = eglGetCurrentContext();
+ EGLContext context2 = eglCreateContext(display, config, context1, contextAttributes);
+ ASSERT_NE(context2, EGL_NO_CONTEXT);
+ eglMakeCurrent(display, surface, surface, context2);
- // Verify layer 0 points to the new memory (green).
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ // In the secondary context, bind the texture and increase layer count to 2.
+ glBindTexture(GL_TEXTURE_2D_ARRAY, m2DArrayTexture);
+ std::vector<GLColor> pixelsGreen(4 * 4 * 2, GLColor::green);
+ if (useTexStorage)
+ {
+ glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, 4, 4, 2);
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 4, 4, 2, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ else
+ {
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 4, 4, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ pixelsGreen.data());
+ }
+ ASSERT_GL_NO_ERROR();
- // Attach layer 1 to the FBO in context1 and verify. Explicitly calling
- // glFramebufferTextureLayer attaches to the newly allocated texture storage.
- glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+ // Switch back to the primary context.
+ eglMakeCurrent(display, surface, surface, context1);
- // Clear layer 1 to blue and verify.
- glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT);
- EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
+ // Verify layer 0 points to the new memory (green).
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
- // Clean up secondary context.
- eglDestroyContext(display, context2);
+ // Attach layer 1 to the FBO in context1 and verify. Explicitly calling
+ // glFramebufferTextureLayer attaches to the newly allocated texture storage.
+ glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m2DArrayTexture, 0, 1);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::green);
+
+ // Clear layer 1 to blue and verify.
+ glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_PIXEL_RECT_EQ(0, 0, 4, 4, GLColor::blue);
+
+ // Clean up secondary context.
+ eglDestroyContext(display, context2);
+ }
}
// Create a 3D texture, use it, then redefine one level without changing dimensions.