blob: 0b65bd1a7bb76f42a8efd50a02e8a3648eb81e22 [file] [log] [blame] [edit]
//
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLIOSurfaceClientBufferTest.cpp: tests for the EGL_ANGLE_iosurface_client_buffer extension.
//
#include "test_utils/ANGLETest.h"
#include "common/mathutil.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
#include <CoreFoundation/CoreFoundation.h>
#if TARGET_OS_OSX
# include <IOSurface/IOSurface.h>
#else
# include <IOSurface/IOSurfaceRef.h>
#endif
using namespace angle;
namespace
{
constexpr char kIOSurfaceExt[] = "EGL_ANGLE_iosurface_client_buffer";
void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value)
{
CFNumberRef number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
CFDictionaryAddValue(dictionary, key, number);
CFRelease(number);
}
class [[nodiscard]] ScopedIOSurfaceRef : angle::NonCopyable
{
public:
explicit ScopedIOSurfaceRef(IOSurfaceRef surface) : mSurface(surface) {}
~ScopedIOSurfaceRef()
{
if (mSurface != nullptr)
{
CFRelease(mSurface);
mSurface = nullptr;
}
}
IOSurfaceRef get() const { return mSurface; }
ScopedIOSurfaceRef(ScopedIOSurfaceRef &&other)
{
if (mSurface != nullptr)
{
CFRelease(mSurface);
}
mSurface = other.mSurface;
other.mSurface = nullptr;
}
ScopedIOSurfaceRef &operator=(ScopedIOSurfaceRef &&other)
{
if (mSurface != nullptr)
{
CFRelease(mSurface);
}
mSurface = other.mSurface;
other.mSurface = nullptr;
return *this;
}
private:
IOSurfaceRef mSurface = nullptr;
};
struct IOSurfacePlaneInfo
{
int width;
int height;
int bytesPerElement;
};
ScopedIOSurfaceRef CreateIOSurface(int32_t format, const std::vector<IOSurfacePlaneInfo> &planes)
{
EXPECT_GT(planes.size(), 0u);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
AddIntegerValue(dict, kIOSurfaceWidth, planes[0].width);
AddIntegerValue(dict, kIOSurfaceHeight, planes[0].height);
AddIntegerValue(dict, kIOSurfacePixelFormat, format);
if (planes.size() > 1)
{
CFMutableArrayRef planesInfo =
CFArrayCreateMutable(kCFAllocatorDefault, planes.size(), &kCFTypeArrayCallBacks);
for (const IOSurfacePlaneInfo &plane : planes)
{
CFMutableDictionaryRef planeInfo =
CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
AddIntegerValue(planeInfo, kIOSurfacePlaneWidth, plane.width);
AddIntegerValue(planeInfo, kIOSurfacePlaneHeight, plane.height);
AddIntegerValue(planeInfo, kIOSurfacePlaneBytesPerElement, plane.bytesPerElement);
CFArrayAppendValue(planesInfo, planeInfo);
CFRelease(planeInfo);
}
CFDictionaryAddValue(dict, kIOSurfacePlaneInfo, planesInfo);
CFRelease(planesInfo);
}
else
{
AddIntegerValue(dict, kIOSurfaceBytesPerElement, planes[0].bytesPerElement);
}
IOSurfaceRef ioSurface = IOSurfaceCreate(dict);
EXPECT_NE(nullptr, ioSurface);
CFRelease(dict);
return ScopedIOSurfaceRef(ioSurface);
}
ScopedIOSurfaceRef CreateSinglePlaneIOSurface(int width,
int height,
int32_t format,
int bytesPerElement)
{
std::vector<IOSurfacePlaneInfo> planes{{width, height, bytesPerElement}};
return CreateIOSurface(format, planes);
}
} // anonymous namespace
class IOSurfaceClientBufferTest : public ANGLETest<>
{
protected:
EGLint getTextureTarget() const
{
EGLint target = 0;
eglGetConfigAttrib(mDisplay, mConfig, EGL_BIND_TO_TEXTURE_TARGET_ANGLE, &target);
return target;
}
GLint getGLTextureTarget() const
{
EGLint targetEGL = getTextureTarget();
GLenum targetGL = 0;
switch (targetEGL)
{
case EGL_TEXTURE_2D:
targetGL = GL_TEXTURE_2D;
break;
case EGL_TEXTURE_RECTANGLE_ANGLE:
targetGL = GL_TEXTURE_RECTANGLE_ANGLE;
break;
default:
break;
}
return targetGL;
}
IOSurfaceClientBufferTest() : mConfig(0), mDisplay(nullptr) {}
void testSetUp() override
{
mConfig = getEGLWindow()->getConfig();
mDisplay = getEGLWindow()->getDisplay();
}
void createIOSurfacePbuffer(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
EGLSurface *pbuffer) const
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_IOSURFACE_PLANE_ANGLE, plane,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE,
static_cast<EGLint>(internalFormat),
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, static_cast<EGLint>(type),
EGL_NONE, EGL_NONE,
};
// clang-format on
*pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(),
mConfig, attribs);
EXPECT_NE(EGL_NO_SURFACE, *pbuffer);
}
void bindIOSurfaceToTexture(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
EGLSurface *pbuffer,
GLTexture *texture) const
{
createIOSurfacePbuffer(ioSurface, width, height, plane, internalFormat, type, pbuffer);
// Bind the pbuffer
glBindTexture(getGLTextureTarget(), *texture);
EGLBoolean result = eglBindTexImage(mDisplay, *pbuffer, EGL_BACK_BUFFER);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
void doClearTest(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
const GLColor &data)
{
std::array<uint8_t, 4> dataArray{data.R, data.G, data.B, data.A};
doClearTest(ioSurface, width, height, plane, internalFormat, type, dataArray);
}
template <typename T, size_t dataSize>
void doClearTest(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
const std::array<T, dataSize> &data)
{
// Bind the IOSurface to a texture and clear it.
EGLSurface pbuffer;
GLTexture texture;
bindIOSurfaceToTexture(ioSurface, width, height, plane, internalFormat, type, &pbuffer,
&texture);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getGLTextureTarget(), texture,
0);
EXPECT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glClearColor(1.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 4.0f / 255.0f);
EXPECT_GL_NO_ERROR();
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
// Unbind pbuffer and check content.
EGLBoolean result = eglReleaseTexImage(mDisplay, pbuffer, EGL_BACK_BUFFER);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
// IOSurface client buffer's rendering doesn't automatically finish after
// eglReleaseTexImage(). Need to explicitly call glFinish().
glFinish();
IOSurfaceLock(ioSurface.get(), kIOSurfaceLockReadOnly, nullptr);
std::array<T, dataSize> iosurfaceData;
memcpy(iosurfaceData.data(), IOSurfaceGetBaseAddressOfPlane(ioSurface.get(), plane),
sizeof(T) * data.size());
IOSurfaceUnlock(ioSurface.get(), kIOSurfaceLockReadOnly, nullptr);
if (internalFormat == GL_RGB && IsOSX() && IsOpenGL())
{
// Ignore alpha component for BGRX, the alpha value is undefined
for (int i = 0; i < 3; i++)
{
ASSERT_EQ(data[i], iosurfaceData[i]);
}
}
else
{
ASSERT_EQ(data, iosurfaceData);
}
result = eglDestroySurface(mDisplay, pbuffer);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
enum ColorMask
{
R = 1,
G = 2,
B = 4,
A = 8,
};
void doSampleTestWithExtraSteps(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
void *data,
size_t dataSize,
int mask,
const std::function<void()> &extraStepsBeforeSample)
{
// Write the data to the IOSurface
IOSurfaceLock(ioSurface.get(), 0, nullptr);
memcpy(IOSurfaceGetBaseAddressOfPlane(ioSurface.get(), plane), data, dataSize);
IOSurfaceUnlock(ioSurface.get(), 0, nullptr);
GLTexture texture;
glBindTexture(getGLTextureTarget(), texture);
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Bind the IOSurface to a texture and clear it.
EGLSurface pbuffer;
bindIOSurfaceToTexture(ioSurface, width, height, plane, internalFormat, type, &pbuffer,
&texture);
if (extraStepsBeforeSample)
{
extraStepsBeforeSample();
}
doSampleTestWithTexture(texture, mask);
EGLBoolean result = eglDestroySurface(mDisplay, pbuffer);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
void doSampleTest(const ScopedIOSurfaceRef &ioSurface,
EGLint width,
EGLint height,
EGLint plane,
GLenum internalFormat,
GLenum type,
void *data,
size_t dataSize,
int mask)
{
doSampleTestWithExtraSteps(ioSurface, width, height, plane, internalFormat, type, data,
dataSize, mask, nullptr);
}
void doSampleTestWithTexture(const GLTexture &texture, int mask)
{
constexpr char kVS[] =
"attribute vec4 position;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(position.xy, 0.0, 1.0);\n"
"}\n";
constexpr char kFS_rect[] =
"#extension GL_ARB_texture_rectangle : require\n"
"precision mediump float;\n"
"uniform sampler2DRect tex;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2DRect(tex, vec2(0, 0));\n"
"}\n";
constexpr char kFS_2D[] =
"precision mediump float;\n"
"uniform sampler2D tex;\n"
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, vec2(0, 0));\n"
"}\n";
ANGLE_GL_PROGRAM(program, kVS,
(getTextureTarget() == EGL_TEXTURE_RECTANGLE_ANGLE ? kFS_rect : kFS_2D));
glUseProgram(program);
GLint location = glGetUniformLocation(program, "tex");
ASSERT_NE(-1, location);
glUniform1i(location, 0);
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0.5f, 1.0f, false);
GLColor expectedColor((mask & R) ? 1 : 0, (mask & G) ? 2 : 0, (mask & B) ? 3 : 0,
(mask & A) ? 4 : 255);
EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor);
ASSERT_GL_NO_ERROR();
}
void doBlitTest(bool ioSurfaceIsSource, int width, int height)
{
if (!hasBlitExt())
{
return;
}
// Create IOSurface and bind it to a texture.
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(width, height, 'BGRA', 4);
EGLSurface pbuffer;
GLTexture texture;
bindIOSurfaceToTexture(ioSurface, width, height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &pbuffer,
&texture);
GLFramebuffer iosurfaceFbo;
glBindFramebuffer(GL_FRAMEBUFFER, iosurfaceFbo);
EXPECT_GL_NO_ERROR();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, getGLTextureTarget(), texture,
0);
EXPECT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
// Create another framebuffer with a regular renderbuffer.
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer rbo;
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
EXPECT_GL_NO_ERROR();
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
EXPECT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
EXPECT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBindRenderbuffer(GL_RENDERBUFFER, 0);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
EXPECT_GL_NO_ERROR();
// Choose which is going to be the source and destination.
GLFramebuffer &src = ioSurfaceIsSource ? iosurfaceFbo : fbo;
GLFramebuffer &dst = ioSurfaceIsSource ? fbo : iosurfaceFbo;
// Clear source to known color.
glBindFramebuffer(GL_FRAMEBUFFER, src);
glClearColor(1.0f / 255.0f, 2.0f / 255.0f, 3.0f / 255.0f, 4.0f / 255.0f);
EXPECT_GL_NO_ERROR();
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GL_NO_ERROR();
// Blit to destination.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, dst);
glBlitFramebufferANGLE(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// Read back from destination.
glBindFramebuffer(GL_FRAMEBUFFER, dst);
GLColor expectedColor(1, 2, 3, 4);
EXPECT_PIXEL_COLOR_EQ(0, 0, expectedColor);
// Unbind pbuffer and check content.
EGLBoolean result = eglReleaseTexImage(mDisplay, pbuffer, EGL_BACK_BUFFER);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
result = eglDestroySurface(mDisplay, pbuffer);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
bool hasIOSurfaceExt() const { return IsEGLDisplayExtensionEnabled(mDisplay, kIOSurfaceExt); }
bool hasBlitExt() const
{
return IsEGLDisplayExtensionEnabled(mDisplay, "ANGLE_framebuffer_blit");
}
EGLConfig mConfig;
EGLDisplay mDisplay;
};
// Test using BGRA8888 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToBGRA8888IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLColor color(3, 2, 1, 4);
doClearTest(ioSurface, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, color);
}
// Test reading from BGRA8888 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromBGRA8888IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLColor color(3, 2, 1, 4);
doSampleTest(ioSurface, 1, 1, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &color, sizeof(color),
R | G | B | A);
}
// Test using BGRX8888 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToBGRX8888IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLColor color(3, 2, 1, 255);
doClearTest(ioSurface, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, color);
}
// Test reading from BGRX8888 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromBGRX8888IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLColor color(3, 2, 1, 4);
doSampleTest(ioSurface, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, &color, sizeof(color), R | G | B);
}
// Test using RG88 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToRG88IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2);
std::array<uint8_t, 2> color{1, 2};
doClearTest(ioSurface, 1, 1, 0, GL_RG, GL_UNSIGNED_BYTE, color);
}
// Test reading from RG88 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromRG88IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2);
uint8_t color[2] = {1, 2};
doSampleTest(ioSurface, 1, 1, 0, GL_RG, GL_UNSIGNED_BYTE, &color, sizeof(color), R | G);
}
// Test using R8 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToR8IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1);
std::array<uint8_t, 1> color{1};
doClearTest(ioSurface, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, color);
}
// Test reading from R8 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromR8IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1);
uint8_t color = 1;
doSampleTest(ioSurface, 1, 1, 0, GL_RED, GL_UNSIGNED_BYTE, &color, sizeof(color), R);
}
// Test using R16 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToR16IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
// This test only works on ES3 since it requires an integer texture.
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
// TODO(http://anglebug.com/7445): Fails with Metal backend.
ANGLE_SKIP_TEST_IF(IsMetal());
// HACK(cwallez@chromium.org) 'L016' doesn't seem to be an official pixel format but it works
// sooooooo let's test using it
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L016', 2);
std::array<uint16_t, 1> color{257};
doClearTest(ioSurface, 1, 1, 0, GL_R16UI, GL_UNSIGNED_SHORT, color);
}
// TODO(cwallez@chromium.org): test reading from R16? It returns 0 maybe because samplerRect is
// only for floating textures?
// Test using BGRA_1010102 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToBGRA1010102IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'l10r', 4);
std::array<uint32_t, 1> color{(0 << 30) | (1 << 22) | (2 << 12) | (3 << 2)};
doClearTest(ioSurface, 1, 1, 0, GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, color);
}
// Test reading from BGRA_1010102 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromBGRA1010102IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'l10r', 4);
uint32_t color = (3 << 30) | (1 << 22) | (2 << 12) | (3 << 2);
doSampleTest(ioSurface, 1, 1, 0, GL_RGB10_A2, GL_UNSIGNED_INT_2_10_10_10_REV, &color,
sizeof(color),
R | G | B); // Don't test alpha, unorm '4' can't be represented with 2 bits.
}
// Test using RGBA_16F IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToRGBA16FIOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'RGhA', 8);
std::array<GLushort, 4> color{
gl::float32ToFloat16(1.0f / 255.0f), gl::float32ToFloat16(2.0f / 255.0f),
gl::float32ToFloat16(3.0f / 255.0f), gl::float32ToFloat16(4.0f / 255.0f)};
doClearTest(ioSurface, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT, color);
}
// Test reading from RGBA_16F IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromToRGBA16FIOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'RGhA', 8);
std::array<GLushort, 4> color{
gl::float32ToFloat16(1.0f / 255.0f), gl::float32ToFloat16(2.0f / 255.0f),
gl::float32ToFloat16(3.0f / 255.0f), gl::float32ToFloat16(4.0f / 255.0f)};
doSampleTest(ioSurface, 1, 1, 0, GL_RGBA, GL_HALF_FLOAT, color.data(), sizeof(GLushort) * 4,
R | G | B | A);
}
// Test using YUV420 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToYUV420IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
std::vector<IOSurfacePlaneInfo> planes{{2, 2, 1}, {1, 1, 2}};
ScopedIOSurfaceRef ioSurface = CreateIOSurface('420v', planes);
{
std::array<GLubyte, 1> colors{1};
doClearTest(ioSurface, planes[0].width, planes[0].height, 0, GL_RED, GL_UNSIGNED_BYTE,
colors);
}
{
std::array<GLubyte, 2> colors{1, 2};
doClearTest(ioSurface, planes[1].width, planes[1].height, 1, GL_RG, GL_UNSIGNED_BYTE,
colors);
}
}
// Test reading from YUV420 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromToYUV420IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
std::vector<IOSurfacePlaneInfo> planes{{2, 2, 1}, {1, 1, 2}};
ScopedIOSurfaceRef ioSurface = CreateIOSurface('420v', planes);
{
std::array<GLubyte, 1> colors{1};
doSampleTest(ioSurface, planes[0].width, planes[0].height, 0, GL_RED, GL_UNSIGNED_BYTE,
colors.data(), sizeof(GLubyte) * colors.size(), R);
}
{
std::array<GLubyte, 2> colors{1, 2};
doSampleTest(ioSurface, planes[1].width, planes[1].height, 1, GL_RG, GL_UNSIGNED_BYTE,
colors.data(), sizeof(GLubyte) * colors.size(), R | G);
}
}
// Test using P010 IOSurfaces for rendering
TEST_P(IOSurfaceClientBufferTest, RenderToP010IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
std::vector<IOSurfacePlaneInfo> planes{{2, 2, 2}, {1, 1, 4}};
ScopedIOSurfaceRef ioSurface = CreateIOSurface('x420', planes);
{
std::array<GLushort, 1> colors{257};
doClearTest(ioSurface, planes[0].width, planes[0].height, 0, GL_RED, GL_UNSIGNED_SHORT,
colors);
}
{
std::array<GLushort, 2> colors{257, 514};
doClearTest(ioSurface, planes[1].width, planes[1].height, 1, GL_RG, GL_UNSIGNED_SHORT,
colors);
}
}
// Test reading from P010 IOSurfaces
TEST_P(IOSurfaceClientBufferTest, ReadFromToP010IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
std::vector<IOSurfacePlaneInfo> planes{{2, 2, 2}, {1, 1, 4}};
ScopedIOSurfaceRef ioSurface = CreateIOSurface('x420', planes);
{
std::array<GLushort, 1> colors{257};
doSampleTest(ioSurface, planes[0].width, planes[0].height, 0, GL_RED, GL_UNSIGNED_SHORT,
colors.data(), sizeof(GLushort) * colors.size(), R);
}
{
std::array<GLushort, 2> colors{257, 514};
doSampleTest(ioSurface, planes[1].width, planes[1].height, 1, GL_RG, GL_UNSIGNED_SHORT,
colors.data(), sizeof(GLushort) * colors.size(), R | G);
}
}
// Test blitting from IOSurface
TEST_P(IOSurfaceClientBufferTest, BlitFromIOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
doBlitTest(true, 2, 2);
}
// Test blitting to IOSurface
TEST_P(IOSurfaceClientBufferTest, BlitToIOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
doBlitTest(false, 2, 2);
}
// Test using glCopyTexSubImage to copy to BGRX8888 IOSurfaces works.
TEST_P(IOSurfaceClientBufferTest, CopySubImageToBGRX8888IOSurface)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLTexture texture;
glBindTexture(getGLTextureTarget(), texture);
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Bind the IOSurface to a texture and clear it.
EGLSurface pbuffer;
bindIOSurfaceToTexture(ioSurface, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, &pbuffer, &texture);
// 1. Clear default framebuffer with desired color.
GLColor color(1, 2, 3, 4);
glClearColor(color.R / 255.f, color.G / 255.f, color.B / 255.f, color.A / 255.f);
glClear(GL_COLOR_BUFFER_BIT);
// 2. Copy color from default framebuffer to iosurface's bound texture.
glCopyTexSubImage2D(getGLTextureTarget(), 0, 0, 0, 0, 0, 1, 1);
EXPECT_GL_NO_ERROR();
// 3. Do texture sampling verification.
doSampleTestWithTexture(texture, R | G | B);
}
// Test the validation errors for missing attributes for eglCreatePbufferFromClientBuffer with
// IOSurface
TEST_P(IOSurfaceClientBufferTest, NegativeValidationMissingAttributes)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
// Success case
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format off
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(), mConfig, attribs);
EXPECT_NE(EGL_NO_SURFACE, pbuffer);
EGLBoolean result = eglDestroySurface(mDisplay, pbuffer);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
// Missing EGL_WIDTH
{
// clang-format off
const EGLint attribs[] = {
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
// Missing EGL_HEIGHT
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
// Missing EGL_IOSURFACE_PLANE_ANGLE
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
// Missing EGL_TEXTURE_TARGET - EGL_BAD_MATCH from the base spec of
// eglCreatePbufferFromClientBuffer
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_MATCH);
}
// Missing EGL_TEXTURE_INTERNAL_FORMAT_ANGLE
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
// Missing EGL_TEXTURE_FORMAT - EGL_BAD_MATCH from the base spec of
// eglCreatePbufferFromClientBuffer
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_MATCH);
}
// Missing EGL_TEXTURE_TYPE_ANGLE
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}
}
// Test the validation errors for bad parameters for eglCreatePbufferFromClientBuffer with IOSurface
TEST_P(IOSurfaceClientBufferTest, NegativeValidationBadAttributes)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
// Success case
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format off
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE, ioSurface.get(), mConfig, attribs);
EXPECT_NE(EGL_NO_SURFACE, pbuffer);
EGLBoolean result = eglDestroySurface(mDisplay, pbuffer);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
// EGL_TEXTURE_FORMAT must be EGL_TEXTURE_RGBA
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_WIDTH must be at least 1
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 0,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_WIDTH must be at most the width of the IOSurface
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 11,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_HEIGHT must be at least 1
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 0,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_HEIGHT must be at most the height of the IOSurface
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 11,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_TEXTURE_FORMAT must be equal to the config's texture target
{
EGLint target = getTextureTarget();
EGLint wrongTarget = 0;
switch (target)
{
case EGL_TEXTURE_RECTANGLE_ANGLE:
wrongTarget = EGL_TEXTURE_2D;
break;
case EGL_TEXTURE_2D:
wrongTarget = EGL_TEXTURE_RECTANGLE_ANGLE;
break;
default:
break;
}
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, wrongTarget,
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_IOSURFACE_PLANE_ANGLE must be at least 0
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, -1,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// EGL_IOSURFACE_PLANE_ANGLE must less than the number of planes of the IOSurface
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 1,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
// The internal format / type most be listed in the table
{
// clang-format off
const EGLint attribs[] = {
EGL_WIDTH, 10,
EGL_HEIGHT, 10,
EGL_IOSURFACE_PLANE_ANGLE, 0,
EGL_TEXTURE_TARGET, getTextureTarget(),
EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGBA,
EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
EGL_NONE, EGL_NONE,
};
// clang-format on
EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(mDisplay, EGL_IOSURFACE_ANGLE,
ioSurface.get(), mConfig, attribs);
EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
EXPECT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
}
}
// Test IOSurface pbuffers can be made current
TEST_P(IOSurfaceClientBufferTest, MakeCurrent)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4);
EGLSurface pbuffer;
createIOSurfacePbuffer(ioSurface, 10, 10, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &pbuffer);
EGLContext context = getEGLWindow()->getContext();
EGLBoolean result = eglMakeCurrent(mDisplay, pbuffer, pbuffer, context);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
// The test harness expects the EGL state to be restored before the test exits.
result = eglMakeCurrent(mDisplay, getEGLWindow()->getSurface(), getEGLWindow()->getSurface(),
context);
EXPECT_EGL_TRUE(result);
EXPECT_EGL_SUCCESS();
}
// Test reading from BGRX8888 IOSurfaces with bound texture's base/max level set to zero.
// This to verify that changing base/level shouldn't delete the binding.
// bug: https://bugs.chromium.org/p/chromium/issues/detail?id=1337324
TEST_P(IOSurfaceClientBufferTest, ReadFromBGRX8888IOSurfaceWithTexBaseMaxLevelSetToZero)
{
ANGLE_SKIP_TEST_IF(!hasIOSurfaceExt());
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4);
GLColor color(3, 2, 1, 4);
doSampleTestWithExtraSteps(ioSurface, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, &color, sizeof(color),
R | G | B, /* extra steps */ [this] {
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MAX_LEVEL, 0);
});
}
// TODO(cwallez@chromium.org): Test setting width and height to less than the IOSurface's work as
// expected.
ANGLE_INSTANTIATE_TEST(IOSurfaceClientBufferTest,
ES2_OPENGL(),
ES3_OPENGL(),
ES2_VULKAN_SWIFTSHADER(),
ES3_VULKAN_SWIFTSHADER(),
ES2_METAL(),
ES3_METAL());