eglCreatePbufferFromClientBuffer implementation
Added support for eglCreatePbufferFromClientBuffer(), using an
IOSurface on MacOS, or just a straight buffer pointer on other
platforms.
Added new unit tests (IOSurfaceClientBufferTest class), which
pass on both Windows and MacOS.
Change-Id: I79a6b420d85fb1f3ae505e0c0067bad2e27510d4
Reviewed-on: https://swiftshader-review.googlesource.com/17168
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/extensions/EGL_ANGLE_iosurface_client_buffer.txt b/extensions/EGL_ANGLE_iosurface_client_buffer.txt
new file mode 100644
index 0000000..692f721
--- /dev/null
+++ b/extensions/EGL_ANGLE_iosurface_client_buffer.txt
@@ -0,0 +1,122 @@
+Name
+
+ ANGLE_iosurface_client_buffer
+
+Name Strings
+
+ EGL_ANGLE_iosurface_client_buffer
+
+Contributors
+
+ Corentin Wallez
+ Geoff Lang
+
+Contacts
+
+ Corentin Wallez, Google Inc. (cwallez 'at' google.com)
+
+Status
+
+ Draft
+
+Version
+
+ Version 1, Dec 6, 2017
+
+Number
+
+ EGL Extension #??
+
+Dependencies
+
+ This extension is written against the wording of the EGL 1.4
+ Specification.
+
+Overview
+
+ This extension allows creating EGL surfaces from IOSurface objects.
+
+New Types
+
+ None
+
+New Procedures and Functions
+
+ None
+
+New Tokens
+
+ Accepted in the <buftype> parameter of eglCreatePbufferFromClientBuffer:
+
+ EGL_IOSURFACE_ANGLE 0x3454
+ EGL_IOSURFACE_PLANE_ANGLE 0x345A
+ EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
+ EGL_TEXTURE_TYPE_ANGLE 0x345C
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+
+Additions to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors)
+
+ Replace the last sentence of paragraph 1 of Section 3.5.3 with the
+ following text.
+ "Currently, the only client API resources which may be bound in this
+ fashion are OpenVG VGImage objects and IOSurface objects."
+
+ Replace the third paragraph of Section 3.5.3 with the following text.
+ "<buftype> specifies the type of buffer to be bound. The only allowed values
+ of <buftype> are EGL_OPENVG_IMAGE and EGL_IOSURFACE_ANGLE".
+
+ Append the following text to the fourth paragraph of Section 3.5.3.
+ "When <buftype> is EGL_IOSURFACE_ANGLE, <buffer> must be a valid IOSurface
+ object case into the type EGLClientBuffer."
+
+ Append to the end of Section 3.5.3.
+ "When <buftype> is EGL_IOSURFACE_ANGLE, <attrib_list> must contain all the
+ following attributes otherwise EGL_BAD_PARAMETER is generated. The
+ attributes must satisfy the following constraints otherwise
+ EGL_BAD_ATTRIBUTE is generated:
+ - EGL_TEXTURE_TYPE_ANGLE, and EGL_TEXTURE_INTERNAL_FORMAT_ANGLE followed
+ by OpenGL enums for texture types, and texture internal format
+ respectively.
+ - EGL_TEXTURE_FORMAT with a value of EGL_TEXTURE_RGBA
+ - EGL_WIDTH with a value between 1 and the width of <buffer>.
+ - EGL_HEIGHT with a value between 1 and the height of <buffer>.
+ - EGL_TEXTURE_TARGET with a value of EGL_TEXTURE_RECTANGLE_ANGLE
+ - EGL_IOSURFACE_PLANE_ANGLE with a value between 0 and the number of
+ planes of <buffer> (exclusive).
+
+ In addition the EGL_TEXTURE_TYPE_ANGLE and
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE attributes must be one of the
+ combinations listed in table egl.iosurface.formats or an
+ EGL_BAD_PARAMETER is generated. The combination must also be a valid
+ combinations for glTexImage2D or EGL_BAD_PARAMETER is generated."
+
+ ---------------------------------------------------------------------------
+ Texture Type Texture Internal Format
+ ---------------------------------------------------------------------------
+ GL_UNSIGNED_BYTE GL_RED
+ GL_UNSIGNED_SHORT GL_R16UI
+ GL_UNSIGNED_BYTE GL_RG
+ GL_UNSIGNED_BYTE GL_BGRA_EXT
+ GL_HALF_FLOAT GL_RGBA
+ ---------------------------------------------------------------------------
+ Table egl.iosurface.formats - Valid combinations of format, type and
+ internal format for IOSurface-backed pbuffers.
+ ---------------------------------------------------------------------------
+
+ Append to the end of Section 3.5.3.
+ "When a pbuffer is created with type EGL_IOSURFACE_ANGLE, the contents
+ of the associcated IOSurface object are undefined while the pbuffer is
+ bound to a client texture."
+
+ Append to the list of errors generated by eglMakeCurrent in Section 3.7.3:
+ " - If either draw or read are pbuffers created with
+ eglCreatePbufferFromClientBuffer with <buftype> set to EGL_IOSURFACE_ANGLE,
+ an EGL_BAD_SURFACE is generated."
+
+Issues
+
+ There are no issues, please move on.
+
+Revision History
+
+ Version 1, 2017/12/06 - first draft.
diff --git a/src/OpenGL/common/Image.cpp b/src/OpenGL/common/Image.cpp
index 8da7cd3..9c2645d 100644
--- a/src/OpenGL/common/Image.cpp
+++ b/src/OpenGL/common/Image.cpp
@@ -24,6 +24,11 @@
#include <string.h>
#include <algorithm>
+#if defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOSurface/IOSurface.h>
+#endif
+
namespace gl
{
sw::Format ConvertReadFormatType(GLenum format, GLenum type)
@@ -1205,6 +1210,195 @@
return new ImageImplementation(width, height, internalformat, multiSampleDepth, lockable);
}
+ int ClientBuffer::getWidth() const
+ {
+ return width;
+ }
+
+ int ClientBuffer::getHeight() const
+ {
+ return height;
+ }
+
+ sw::Format ClientBuffer::getFormat() const
+ {
+ return format;
+ }
+
+ int ClientBuffer::pitchP() const
+ {
+#if defined(__APPLE__)
+ if(buffer)
+ {
+ IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
+ int pitchB = static_cast<int>(IOSurfaceGetBytesPerRowOfPlane(ioSurface, plane));
+ int bytesPerPixel = sw::Surface::bytes(format);
+ ASSERT((pitchB % bytesPerPixel) == 0);
+ return pitchB / bytesPerPixel;
+ }
+
+ return 0;
+#else
+ return sw::Surface::pitchP(width, 0, format, false);
+#endif
+ }
+
+ void ClientBuffer::retain()
+ {
+#if defined(__APPLE__)
+ if(buffer)
+ {
+ CFRetain(reinterpret_cast<IOSurfaceRef>(buffer));
+ }
+#endif
+ }
+
+ void ClientBuffer::release()
+ {
+#if defined(__APPLE__)
+ if(buffer)
+ {
+ CFRelease(reinterpret_cast<IOSurfaceRef>(buffer));
+ buffer = nullptr;
+ }
+#endif
+ }
+
+ void* ClientBuffer::lock(int x, int y, int z)
+ {
+#if defined(__APPLE__)
+ if(buffer)
+ {
+ IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
+ IOSurfaceLock(ioSurface, 0, nullptr);
+ void* pixels = IOSurfaceGetBaseAddressOfPlane(ioSurface, plane);
+ int bytes = sw::Surface::bytes(format);
+ int pitchB = static_cast<int>(IOSurfaceGetBytesPerRowOfPlane(ioSurface, plane));
+ int sliceB = static_cast<int>(IOSurfaceGetHeightOfPlane(ioSurface, plane)) * pitchB;
+ return (unsigned char*)pixels + x * bytes + y * pitchB + z * sliceB;
+ }
+
+ return nullptr;
+#else
+ int bytes = sw::Surface::bytes(format);
+ int pitchB = sw::Surface::pitchB(width, 0, format, false);
+ int sliceB = height * pitchB;
+ return (unsigned char*)buffer + x * bytes + y * pitchB + z * sliceB;
+#endif
+ }
+
+ void ClientBuffer::unlock()
+ {
+#if defined(__APPLE__)
+ if(buffer)
+ {
+ IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(buffer);
+ IOSurfaceUnlock(ioSurface, 0, nullptr);
+ }
+#endif
+ }
+
+ class ClientBufferImage : public egl::Image
+ {
+ public:
+ explicit ClientBufferImage(const ClientBuffer& clientBuffer) :
+ egl::Image(clientBuffer.getWidth(),
+ clientBuffer.getHeight(),
+ getClientBufferInternalFormat(clientBuffer.getFormat()),
+ clientBuffer.pitchP()),
+ clientBuffer(clientBuffer)
+ {
+ shared = false;
+ this->clientBuffer.retain();
+ }
+
+ private:
+ ClientBuffer clientBuffer;
+
+ ~ClientBufferImage() override
+ {
+ sync(); // Wait for any threads that use this image to finish.
+
+ clientBuffer.release();
+ }
+
+ static GLint getClientBufferInternalFormat(sw::Format format)
+ {
+ switch(format)
+ {
+ case sw::FORMAT_R8: return GL_R8;
+ case sw::FORMAT_G8R8: return GL_RG8;
+ case sw::FORMAT_A8R8G8B8: return GL_BGRA8_EXT;
+ case sw::FORMAT_R16UI: return GL_R16UI;
+ case sw::FORMAT_A16B16G16R16F: return GL_RGBA16F;
+ default: return GL_NONE;
+ }
+ }
+
+ void *lockInternal(int x, int y, int z, sw::Lock lock, sw::Accessor client) override
+ {
+ LOGLOCK("image=%p op=%s.swsurface lock=%d", this, __FUNCTION__, lock);
+
+ // Always do this for reference counting.
+ void *data = sw::Surface::lockInternal(x, y, z, lock, client);
+
+ if(x != 0 || y != 0 || z != 0)
+ {
+ LOGLOCK("badness: %s called with unsupported parms: image=%p x=%d y=%d z=%d", __FUNCTION__, this, x, y, z);
+ }
+
+ LOGLOCK("image=%p op=%s.ani lock=%d", this, __FUNCTION__, lock);
+
+ // Lock the ClientBuffer and use its address.
+ data = clientBuffer.lock(x, y, z);
+
+ if(lock == sw::LOCK_UNLOCKED)
+ {
+ // We're never going to get a corresponding unlock, so unlock
+ // immediately. This keeps the reference counts sane.
+ clientBuffer.unlock();
+ }
+
+ return data;
+ }
+
+ void unlockInternal() override
+ {
+ LOGLOCK("image=%p op=%s.ani", this, __FUNCTION__);
+ clientBuffer.unlock();
+
+ LOGLOCK("image=%p op=%s.swsurface", this, __FUNCTION__);
+ sw::Surface::unlockInternal();
+ }
+
+ void *lock(int x, int y, int z, sw::Lock lock) override
+ {
+ LOGLOCK("image=%p op=%s lock=%d", this, __FUNCTION__, lock);
+ (void)sw::Surface::lockExternal(x, y, z, lock, sw::PUBLIC);
+
+ return clientBuffer.lock(x, y, z);
+ }
+
+ void unlock() override
+ {
+ LOGLOCK("image=%p op=%s.ani", this, __FUNCTION__);
+ clientBuffer.unlock();
+
+ LOGLOCK("image=%p op=%s.swsurface", this, __FUNCTION__);
+ sw::Surface::unlockExternal();
+ }
+
+ void release() override
+ {
+ Image::release();
+ }
+ };
+
+ Image *Image::create(const egl::ClientBuffer& clientBuffer)
+ {
+ return new ClientBufferImage(clientBuffer);
+ }
+
Image::~Image()
{
// sync() must be called in the destructor of the most derived class to ensure their vtable isn't destroyed
diff --git a/src/OpenGL/common/Image.hpp b/src/OpenGL/common/Image.hpp
index 3b984b9..5e924c0 100644
--- a/src/OpenGL/common/Image.hpp
+++ b/src/OpenGL/common/Image.hpp
@@ -63,6 +63,30 @@
namespace egl
{
+class ClientBuffer
+{
+public:
+ ClientBuffer(int width, int height, sw::Format format, void* buffer, size_t plane)
+ : width(width), height(height), format(format), buffer(buffer), plane(plane)
+ {}
+
+ int getWidth() const;
+ int getHeight() const;
+ sw::Format getFormat() const;
+ int pitchP() const;
+ void retain();
+ void release();
+ void* lock(int x, int y, int z);
+ void unlock();
+
+private:
+ int width;
+ int height;
+ sw::Format format;
+ void* buffer;
+ size_t plane;
+};
+
class [[clang::lto_visibility_public]] Image : public sw::Surface, public gl::Object
{
protected:
@@ -117,6 +141,9 @@
// Render target
static Image *create(GLsizei width, GLsizei height, GLint internalformat, int multiSampleDepth, bool lockable);
+ // Back buffer from client buffer
+ static Image *create(const egl::ClientBuffer& clientBuffer);
+
GLsizei getWidth() const
{
return width;
diff --git a/src/OpenGL/common/Surface.hpp b/src/OpenGL/common/Surface.hpp
index 493d676..c9e1973 100644
--- a/src/OpenGL/common/Surface.hpp
+++ b/src/OpenGL/common/Surface.hpp
@@ -42,6 +42,7 @@
virtual EGLint getWidth() const = 0;
virtual EGLint getHeight() const = 0;
+ virtual EGLenum getTextureTarget() const = 0;
virtual void setBoundTexture(egl::Texture *texture) = 0;
};
diff --git a/src/OpenGL/libEGL/BUILD.gn b/src/OpenGL/libEGL/BUILD.gn
index 24727cf..c9ea43b 100644
--- a/src/OpenGL/libEGL/BUILD.gn
+++ b/src/OpenGL/libEGL/BUILD.gn
@@ -67,6 +67,8 @@
libs = [
"Quartz.framework",
"Cocoa.framework",
+ "CoreFoundation.framework",
+ "IOSurface.framework",
]
ldflags = [ "-Wl,-install_name,@rpath/libswiftshader_libEGL.dylib" ]
} else if (is_win) {
diff --git a/src/OpenGL/libEGL/Display.cpp b/src/OpenGL/libEGL/Display.cpp
index 0e58125..a5c87b9 100644
--- a/src/OpenGL/libEGL/Display.cpp
+++ b/src/OpenGL/libEGL/Display.cpp
@@ -34,6 +34,8 @@
#include "Main/libX11.hpp"
#elif defined(__APPLE__)
#include "OSXUtils.hpp"
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOSurface/IOSurface.h>
#endif
#include <algorithm>
@@ -338,11 +340,13 @@
return success(surface);
}
-EGLSurface Display::createPBufferSurface(EGLConfig config, const EGLint *attribList)
+EGLSurface Display::createPBufferSurface(EGLConfig config, const EGLint *attribList, EGLClientBuffer clientBuffer)
{
- EGLint width = 0, height = 0;
+ EGLint width = -1, height = -1, ioSurfacePlane = -1;
EGLenum textureFormat = EGL_NO_TEXTURE;
EGLenum textureTarget = EGL_NO_TEXTURE;
+ EGLenum clientBufferFormat = EGL_NO_TEXTURE;
+ EGLenum clientBufferType = EGL_NO_TEXTURE;
EGLBoolean largestPBuffer = EGL_FALSE;
const Config *configuration = mConfigSet.get(config);
@@ -373,11 +377,45 @@
return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
}
break;
+ case EGL_TEXTURE_INTERNAL_FORMAT_ANGLE:
+ switch(attribList[1])
+ {
+ case GL_RED:
+ case GL_R16UI:
+ case GL_RG:
+ case GL_BGRA_EXT:
+ case GL_RGBA:
+ clientBufferFormat = attribList[1];
+ break;
+ default:
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+ break;
+ case EGL_TEXTURE_TYPE_ANGLE:
+ switch(attribList[1])
+ {
+ case GL_UNSIGNED_BYTE:
+ case GL_UNSIGNED_SHORT:
+ case GL_HALF_FLOAT:
+ clientBufferType = attribList[1];
+ break;
+ default:
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+ break;
+ case EGL_IOSURFACE_PLANE_ANGLE:
+ if(attribList[1] < 0)
+ {
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+ ioSurfacePlane = attribList[1];
+ break;
case EGL_TEXTURE_TARGET:
switch(attribList[1])
{
case EGL_NO_TEXTURE:
case EGL_TEXTURE_2D:
+ case EGL_TEXTURE_RECTANGLE_ANGLE:
textureTarget = attribList[1];
break;
default:
@@ -423,13 +461,92 @@
return error(EGL_BAD_MATCH, EGL_NO_SURFACE);
}
- if((textureFormat == EGL_TEXTURE_RGB && configuration->mBindToTextureRGB != EGL_TRUE) ||
- (textureFormat == EGL_TEXTURE_RGBA && configuration->mBindToTextureRGBA != EGL_TRUE))
+ if(clientBuffer)
{
- return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ switch(clientBufferType)
+ {
+ case GL_UNSIGNED_BYTE:
+ switch(clientBufferFormat)
+ {
+ case GL_RED:
+ case GL_RG:
+ case GL_BGRA_EXT:
+ break;
+ case GL_R16UI:
+ case GL_RGBA:
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ default:
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch(clientBufferFormat)
+ {
+ case GL_R16UI:
+ break;
+ case GL_RED:
+ case GL_RG:
+ case GL_BGRA_EXT:
+ case GL_RGBA:
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ default:
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ break;
+ case GL_HALF_FLOAT:
+ switch(clientBufferFormat)
+ {
+ case GL_RGBA:
+ break;
+ case GL_RED:
+ case GL_R16UI:
+ case GL_RG:
+ case GL_BGRA_EXT:
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ default:
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+ break;
+ default:
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+
+ if(ioSurfacePlane < 0)
+ {
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ }
+
+ if(textureFormat != EGL_TEXTURE_RGBA)
+ {
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+
+ if(textureTarget != EGL_TEXTURE_RECTANGLE_ANGLE)
+ {
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+
+#if defined(__APPLE__)
+ IOSurfaceRef ioSurface = reinterpret_cast<IOSurfaceRef>(clientBuffer);
+ size_t planeCount = IOSurfaceGetPlaneCount(ioSurface);
+ if((static_cast<size_t>(width) > IOSurfaceGetWidthOfPlane(ioSurface, ioSurfacePlane)) ||
+ (static_cast<size_t>(height) > IOSurfaceGetHeightOfPlane(ioSurface, ioSurfacePlane)) ||
+ ((planeCount != 0) && static_cast<size_t>(ioSurfacePlane) >= planeCount))
+ {
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
+#endif
+ }
+ else
+ {
+ if((textureFormat == EGL_TEXTURE_RGB && configuration->mBindToTextureRGB != EGL_TRUE) ||
+ ((textureFormat == EGL_TEXTURE_RGBA && configuration->mBindToTextureRGBA != EGL_TRUE)))
+ {
+ return error(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
+ }
}
- Surface *surface = new PBufferSurface(this, configuration, width, height, textureFormat, textureTarget, largestPBuffer);
+ Surface *surface = new PBufferSurface(this, configuration, width, height, textureFormat, textureTarget, clientBufferFormat, clientBufferType, largestPBuffer, clientBuffer, ioSurfacePlane);
if(!surface->initialize())
{
diff --git a/src/OpenGL/libEGL/Display.h b/src/OpenGL/libEGL/Display.h
index 5944d69..30f01b9 100644
--- a/src/OpenGL/libEGL/Display.h
+++ b/src/OpenGL/libEGL/Display.h
@@ -26,6 +26,15 @@
#include <set>
+#ifndef EGL_ANGLE_iosurface_client_buffer
+#define EGL_ANGLE_iosurface_client_buffer 1
+#define EGL_IOSURFACE_ANGLE 0x3454
+#define EGL_IOSURFACE_PLANE_ANGLE 0x345A
+#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
+#define EGL_TEXTURE_TYPE_ANGLE 0x345C
+#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+#endif // EGL_ANGLE_iosurface_client_buffer
+
namespace egl
{
class Surface;
@@ -51,7 +60,7 @@
bool getConfigAttrib(EGLConfig config, EGLint attribute, EGLint *value);
EGLSurface createWindowSurface(EGLNativeWindowType window, EGLConfig config, const EGLint *attribList);
- EGLSurface createPBufferSurface(EGLConfig config, const EGLint *attribList);
+ EGLSurface createPBufferSurface(EGLConfig config, const EGLint *attribList, EGLClientBuffer clientBuffer = nullptr);
EGLContext createContext(EGLConfig configHandle, const Context *shareContext, EGLint clientVersion);
EGLSyncKHR createSync(Context *context);
diff --git a/src/OpenGL/libEGL/Surface.cpp b/src/OpenGL/libEGL/Surface.cpp
index d373990..ead75a6 100644
--- a/src/OpenGL/libEGL/Surface.cpp
+++ b/src/OpenGL/libEGL/Surface.cpp
@@ -63,6 +63,10 @@
swapBehavior = EGL_BUFFER_PRESERVED;
textureFormat = EGL_NO_TEXTURE;
textureTarget = EGL_NO_TEXTURE;
+ clientBufferFormat = EGL_NO_TEXTURE;
+ clientBufferType = EGL_NO_TEXTURE;
+ clientBuffer = nullptr;
+ clientBufferPlane = -1;
swapInterval = -1;
setSwapInterval(1);
}
@@ -78,7 +82,15 @@
if(libGLESv2)
{
- backBuffer = libGLESv2->createBackBuffer(width, height, config->mRenderTargetFormat, config->mSamples);
+ if(clientBuffer)
+ {
+ backBuffer = libGLESv2->createBackBufferFromClientBuffer(
+ egl::ClientBuffer(width, height, getClientBufferFormat(), clientBuffer, clientBufferPlane));
+ }
+ else
+ {
+ backBuffer = libGLESv2->createBackBuffer(width, height, config->mRenderTargetFormat, config->mSamples);
+ }
}
else if(libGLES_CM)
{
@@ -222,6 +234,51 @@
return largestPBuffer;
}
+sw::Format Surface::getClientBufferFormat() const
+{
+ switch(clientBufferType)
+ {
+ case GL_UNSIGNED_BYTE:
+ switch(clientBufferFormat)
+ {
+ case GL_RED:
+ return sw::FORMAT_R8;
+ case GL_RG:
+ return sw::FORMAT_G8R8;
+ case GL_BGRA_EXT:
+ return sw::FORMAT_A8R8G8B8;
+ default:
+ UNREACHABLE(clientBufferFormat);
+ break;
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ switch(clientBufferFormat)
+ {
+ case GL_R16UI:
+ return sw::FORMAT_R16UI;
+ default:
+ UNREACHABLE(clientBufferFormat);
+ break;
+ }
+ break;
+ case GL_HALF_FLOAT:
+ switch(clientBufferFormat)
+ {
+ case GL_RGBA:
+ return sw::FORMAT_A16B16G16R16F;
+ default:
+ UNREACHABLE(clientBufferFormat);
+ break;
+ }
+ default:
+ UNREACHABLE(clientBufferType);
+ break;
+ }
+
+ return sw::FORMAT_NULL;
+}
+
void Surface::setBoundTexture(egl::Texture *texture)
{
this->texture = texture;
@@ -356,12 +413,21 @@
return Surface::initialize();
}
-PBufferSurface::PBufferSurface(Display *display, const Config *config, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureType, EGLBoolean largestPBuffer)
+PBufferSurface::PBufferSurface(Display *display, const Config *config, EGLint width, EGLint height,
+ EGLenum textureFormat, EGLenum textureTarget, EGLenum clientBufferFormat,
+ EGLenum clientBufferType, EGLBoolean largestPBuffer, EGLClientBuffer clientBuffer,
+ EGLint clientBufferPlane)
: Surface(display, config)
{
this->width = width;
this->height = height;
this->largestPBuffer = largestPBuffer;
+ this->textureFormat = textureFormat;
+ this->textureTarget = textureTarget;
+ this->clientBufferFormat = clientBufferFormat;
+ this->clientBufferType = clientBufferType;
+ this->clientBuffer = clientBuffer;
+ this->clientBufferPlane = clientBufferPlane;
}
PBufferSurface::~PBufferSurface()
diff --git a/src/OpenGL/libEGL/Surface.hpp b/src/OpenGL/libEGL/Surface.hpp
index 2e9e26b..be43edb 100644
--- a/src/OpenGL/libEGL/Surface.hpp
+++ b/src/OpenGL/libEGL/Surface.hpp
@@ -48,11 +48,11 @@
EGLint getWidth() const override;
EGLint getHeight() const override;
+ EGLenum getTextureTarget() const override;
virtual EGLint getPixelAspectRatio() const;
virtual EGLenum getRenderBuffer() const;
virtual EGLenum getSwapBehavior() const;
virtual EGLenum getTextureFormat() const;
- virtual EGLenum getTextureTarget() const;
virtual EGLBoolean getLargestPBuffer() const;
virtual EGLNativeWindowType getWindowHandle() const = 0;
@@ -61,6 +61,7 @@
virtual bool isWindowSurface() const { return false; }
virtual bool isPBufferSurface() const { return false; }
+ bool hasClientBuffer() const { return clientBuffer != nullptr; }
protected:
Surface(const Display *display, const Config *config);
@@ -69,6 +70,8 @@
virtual void deleteResources();
+ sw::Format getClientBufferFormat() const;
+
const Display *const display;
Image *depthStencil;
Image *backBuffer;
@@ -77,8 +80,8 @@
bool reset(int backbufferWidth, int backbufferHeight);
const Config *const config; // EGL config surface was created with
- EGLint height; // Height of surface
EGLint width; // Width of surface
+ EGLint height; // Height of surface
// EGLint horizontalResolution; // Horizontal dot pitch
// EGLint verticalResolution; // Vertical dot pitch
EGLBoolean largestPBuffer; // If true, create largest pbuffer possible
@@ -90,9 +93,13 @@
EGLenum swapBehavior; // Buffer swap behavior
EGLenum textureFormat; // Format of texture: RGB, RGBA, or no texture
EGLenum textureTarget; // Type of texture: 2D or no texture
+ EGLenum clientBufferFormat; // Format of the client buffer
+ EGLenum clientBufferType; // Type of the client buffer
// EGLenum vgAlphaFormat; // Alpha format for OpenVG
// EGLenum vgColorSpace; // Color space for OpenVG
EGLint swapInterval;
+ EGLClientBuffer clientBuffer;
+ EGLint clientBufferPlane;
};
class WindowSurface : public Surface
@@ -120,7 +127,10 @@
class PBufferSurface : public Surface
{
public:
- PBufferSurface(Display *display, const egl::Config *config, EGLint width, EGLint height, EGLenum textureFormat, EGLenum textureTarget, EGLBoolean largestPBuffer);
+ PBufferSurface(Display *display, const egl::Config *config, EGLint width, EGLint height,
+ EGLenum textureFormat, EGLenum textureTarget, EGLenum internalFormat,
+ EGLenum textureType, EGLBoolean largestPBuffer, EGLClientBuffer clientBuffer,
+ EGLint clientBufferPlane);
~PBufferSurface() override;
bool isPBufferSurface() const override { return true; }
diff --git a/src/OpenGL/libEGL/libEGL.cpp b/src/OpenGL/libEGL/libEGL.cpp
index 55ee224..74510df 100644
--- a/src/OpenGL/libEGL/libEGL.cpp
+++ b/src/OpenGL/libEGL/libEGL.cpp
@@ -204,6 +204,7 @@
"EGL_KHR_fence_sync "
"EGL_KHR_image_base "
"EGL_KHR_surfaceless_context "
+ "EGL_ANGLE_iosurface_client_buffer "
"EGL_ANDROID_framebuffer_target "
"EGL_ANDROID_recordable");
case EGL_VENDOR:
@@ -507,9 +508,25 @@
"EGLConfig config = %p, const EGLint *attrib_list = %p)",
dpy, buftype, buffer, config, attrib_list);
- UNIMPLEMENTED();
+ switch(buftype)
+ {
+ case EGL_IOSURFACE_ANGLE:
+ {
+ egl::Display *display = egl::Display::get(dpy);
- return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ if(!validateConfig(display, config))
+ {
+ return EGL_NO_SURFACE;
+ }
+
+ return display->createPBufferSurface(config, attrib_list, buffer);
+ }
+ case EGL_OPENVG_IMAGE:
+ UNIMPLEMENTED();
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ default:
+ return error(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
+ };
}
EGLBoolean SurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
@@ -810,6 +827,13 @@
return EGL_FALSE;
}
+ if((draw != EGL_NO_SURFACE && drawSurface->hasClientBuffer()) ||
+ (read != EGL_NO_SURFACE && readSurface->hasClientBuffer()))
+ {
+ // Make current is not supported on IOSurface pbuffers.
+ return error(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
if((draw != EGL_NO_SURFACE) ^ (read != EGL_NO_SURFACE))
{
return error(EGL_BAD_MATCH, EGL_FALSE);
diff --git a/src/OpenGL/libGLESv2/BUILD.gn b/src/OpenGL/libGLESv2/BUILD.gn
index b4383c9..82d3191 100644
--- a/src/OpenGL/libGLESv2/BUILD.gn
+++ b/src/OpenGL/libGLESv2/BUILD.gn
@@ -109,6 +109,10 @@
if (is_win) {
ldflags = [ "/DEF:" + rebase_path("libGLESv2.def", root_build_dir) ]
} else if (is_mac) {
+ libs = [
+ "CoreFoundation.framework",
+ "IOSurface.framework",
+ ]
ldflags = [ "-Wl,-install_name,@rpath/libswiftshader_libGLESv2.dylib" ]
} else if (is_linux) {
ldflags =
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp
index 6233e8f..5148e6b 100644
--- a/src/OpenGL/libGLESv2/Context.cpp
+++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -4093,8 +4093,8 @@
{
GLenum readColorbufferType = readFramebuffer->getReadBufferType();
GLenum drawColorbufferType = drawFramebuffer->getColorbufferType(0);
- const bool validReadType = readColorbufferType == GL_TEXTURE_2D || Framebuffer::IsRenderbuffer(readColorbufferType);
- const bool validDrawType = drawColorbufferType == GL_TEXTURE_2D || Framebuffer::IsRenderbuffer(drawColorbufferType);
+ const bool validReadType = readColorbufferType == GL_TEXTURE_2D || readColorbufferType == GL_TEXTURE_RECTANGLE_ARB || Framebuffer::IsRenderbuffer(readColorbufferType);
+ const bool validDrawType = drawColorbufferType == GL_TEXTURE_2D || drawColorbufferType == GL_TEXTURE_RECTANGLE_ARB || Framebuffer::IsRenderbuffer(drawColorbufferType);
if(!validReadType || !validDrawType)
{
return error(GL_INVALID_OPERATION);
@@ -4145,8 +4145,10 @@
return error(GL_INVALID_OPERATION);
}
- if((readRenderbuffer->getSamples() > 0) &&
- (readRenderbuffer->getFormat() != drawRenderbuffer->getFormat()))
+ // From the ANGLE_framebuffer_blit extension:
+ // "Calling BlitFramebufferANGLE will result in an INVALID_OPERATION error if <mask>
+ // includes COLOR_BUFFER_BIT and the source and destination color formats to not match."
+ if((clientVersion < 3) && (readRenderbuffer->getSamples() > 0) && (readFormat != drawFormat))
{
return error(GL_INVALID_OPERATION);
}
@@ -4291,7 +4293,8 @@
void Context::bindTexImage(gl::Surface *surface)
{
- es2::Texture2D *textureObject = getTexture2D();
+ bool isRect = (surface->getTextureTarget() == EGL_TEXTURE_RECTANGLE_ANGLE);
+ es2::Texture2D *textureObject = isRect ? getTexture2DRect() : getTexture2D();
if(textureObject)
{
diff --git a/src/OpenGL/libGLESv2/Texture.cpp b/src/OpenGL/libGLESv2/Texture.cpp
index dc0e807..ec96373 100644
--- a/src/OpenGL/libGLESv2/Texture.cpp
+++ b/src/OpenGL/libGLESv2/Texture.cpp
@@ -1900,6 +1900,18 @@
return egl::Image::create(width, height, internalformat, multiSampleDepth, false);
}
+NO_SANITIZE_FUNCTION egl::Image *createBackBufferFromClientBuffer(const egl::ClientBuffer& clientBuffer)
+{
+ if(clientBuffer.getWidth() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE ||
+ clientBuffer.getHeight() > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
+ {
+ ERR("Invalid parameters: %dx%d", clientBuffer.getWidth(), clientBuffer.getHeight());
+ return nullptr;
+ }
+
+ return egl::Image::create(clientBuffer);
+}
+
NO_SANITIZE_FUNCTION egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth)
{
if(width > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE || height > es2::IMPLEMENTATION_MAX_RENDERBUFFER_SIZE)
diff --git a/src/OpenGL/libGLESv2/entry_points.cpp b/src/OpenGL/libGLESv2/entry_points.cpp
index e4b35aa..b142b85 100644
--- a/src/OpenGL/libGLESv2/entry_points.cpp
+++ b/src/OpenGL/libGLESv2/entry_points.cpp
@@ -1211,6 +1211,7 @@
egl::Context *es2CreateContext(egl::Display *display, const egl::Context *shareContext, int clientVersion, const egl::Config *config);
extern "C" __eglMustCastToProperFunctionPointerType es2GetProcAddress(const char *procname);
egl::Image *createBackBuffer(int width, int height, sw::Format format, int multiSampleDepth);
+egl::Image *createBackBufferFromClientBuffer(const egl::ClientBuffer& clientBuffer);
egl::Image *createDepthStencil(int width, int height, sw::Format format, int multiSampleDepth);
sw::FrameBuffer *createFrameBuffer(void *nativeDisplay, EGLNativeWindowType window, int width, int height);
@@ -1414,6 +1415,7 @@
this->es2CreateContext = ::es2CreateContext;
this->es2GetProcAddress = ::es2GetProcAddress;
this->createBackBuffer = ::createBackBuffer;
+ this->createBackBufferFromClientBuffer = ::createBackBufferFromClientBuffer;
this->createDepthStencil = ::createDepthStencil;
this->createFrameBuffer = ::createFrameBuffer;
}
diff --git a/src/OpenGL/libGLESv2/libGLESv2.hpp b/src/OpenGL/libGLESv2/libGLESv2.hpp
index 0897d3b..8946f0b 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.hpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.hpp
@@ -33,6 +33,7 @@
class Context;
class Image;
class Config;
+class ClientBuffer;
}
class LibGLESv2exports
@@ -245,6 +246,7 @@
egl::Context *(*es2CreateContext)(egl::Display *display, const egl::Context *shareContext, int clientVersion, const egl::Config *config);
__eglMustCastToProperFunctionPointerType (*es2GetProcAddress)(const char *procname);
egl::Image *(*createBackBuffer)(int width, int height, sw::Format format, int multiSampleDepth);
+ egl::Image *(*createBackBufferFromClientBuffer)(const egl::ClientBuffer& clientBuffer);
egl::Image *(*createDepthStencil)(int width, int height, sw::Format format, int multiSampleDepth);
sw::FrameBuffer *(*createFrameBuffer)(void *nativeDisplay, EGLNativeWindowType window, int width, int height);
};
diff --git a/tests/unittests/BUILD.gn b/tests/unittests/BUILD.gn
index aa7dfef..10f5fa5 100644
--- a/tests/unittests/BUILD.gn
+++ b/tests/unittests/BUILD.gn
@@ -46,6 +46,10 @@
"-rpath",
"@executable_path/",
]
+ libs = [
+ "CoreFoundation.framework",
+ "IOSurface.framework",
+ ]
} else {
ldflags = [ "-Wl,-rpath=\$ORIGIN/swiftshader" ]
}
diff --git a/tests/unittests/unittests.cpp b/tests/unittests/unittests.cpp
index 7ae84da..99cbb61 100644
--- a/tests/unittests/unittests.cpp
+++ b/tests/unittests/unittests.cpp
@@ -97,6 +97,7 @@
EGL_ALPHA_SIZE, 8,
EGL_NONE
};
+
EGLint num_config = -1;
EGLBoolean success = eglChooseConfig(display, configAttributes, &config, 1, &num_config);
EXPECT_EQ(EGL_SUCCESS, eglGetError());
@@ -203,29 +204,43 @@
EXPECT_EQ((EGLBoolean)EGL_TRUE, success);
}
- GLuint createProgram(const std::string& vs, const std::string& fs)
+ struct ProgramHandles {
+ GLuint program;
+ GLuint vsShader;
+ GLuint fsShader;
+ };
+
+ ProgramHandles createProgram(const std::string& vs, const std::string& fs)
{
- GLuint program = glCreateProgram();
+ ProgramHandles ph;
+ ph.program = glCreateProgram();
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- GLuint vsShader = glCreateShader(GL_VERTEX_SHADER);
+ ph.vsShader = glCreateShader(GL_VERTEX_SHADER);
const char* vsSource[1] = { vs.c_str() };
- glShaderSource(vsShader, 1, vsSource, nullptr);
- glCompileShader(vsShader);
+ glShaderSource(ph.vsShader, 1, vsSource, nullptr);
+ glCompileShader(ph.vsShader);
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- GLuint fsShader = glCreateShader(GL_FRAGMENT_SHADER);
+ ph.fsShader = glCreateShader(GL_FRAGMENT_SHADER);
const char* fsSource[1] = { fs.c_str() };
- glShaderSource(fsShader, 1, fsSource, nullptr);
- glCompileShader(fsShader);
+ glShaderSource(ph.fsShader, 1, fsSource, nullptr);
+ glCompileShader(ph.fsShader);
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- glAttachShader(program, vsShader);
- glAttachShader(program, fsShader);
- glLinkProgram(program);
+ glAttachShader(ph.program, ph.vsShader);
+ glAttachShader(ph.program, ph.fsShader);
+ glLinkProgram(ph.program);
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- return program;
+ return ph;
+ }
+
+ void deleteProgram(const ProgramHandles& ph)
+ {
+ glDeleteShader(ph.fsShader);
+ glDeleteShader(ph.vsShader);
+ glDeleteProgram(ph.program);
}
void drawQuad(GLuint program)
@@ -262,7 +277,7 @@
}
EGLDisplay getDisplay() const { return display; }
- EGLDisplay getConfig() const { return config; }
+ EGLConfig getConfig() const { return config; }
EGLSurface getSurface() const { return surface; }
EGLContext getContext() const { return context; }
private:
@@ -532,10 +547,10 @@
" gl_FragColor = texture2DRect(tex, vec2(0, 0));\n"
"}\n";
- GLuint program = createProgram(vs, fs);
+ const ProgramHandles ph = createProgram(vs, fs);
- glUseProgram(program);
- GLint location = glGetUniformLocation(program, "tex");
+ glUseProgram(ph.program);
+ GLint location = glGetUniformLocation(ph.program, "tex");
ASSERT_NE(-1, location);
glUniform1i(location, 0);
@@ -543,7 +558,9 @@
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- drawQuad(program);
+ drawQuad(ph.program);
+
+ deleteProgram(ph);
compareColor(green);
@@ -584,9 +601,10 @@
" fragColor = texture(tex, vec2(0, 0));\n"
"}\n";
- GLuint program = createProgram(vs, fs);
- glUseProgram(program);
- GLint location = glGetUniformLocation(program, "tex");
+ const ProgramHandles ph = createProgram(vs, fs);
+
+ glUseProgram(ph.program);
+ GLint location = glGetUniformLocation(ph.program, "tex");
ASSERT_NE(-1, location);
glUniform1i(location, 0);
@@ -594,7 +612,9 @@
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_GLENUM_EQ(GL_NONE, glGetError());
- drawQuad(program);
+ drawQuad(ph.program);
+
+ deleteProgram(ph);
compareColor(green);
@@ -717,3 +737,743 @@
Uninitialize();
}
+
+#ifndef EGL_ANGLE_iosurface_client_buffer
+#define EGL_ANGLE_iosurface_client_buffer 1
+#define EGL_IOSURFACE_ANGLE 0x3454
+#define EGL_IOSURFACE_PLANE_ANGLE 0x345A
+#define EGL_TEXTURE_RECTANGLE_ANGLE 0x345B
+#define EGL_TEXTURE_TYPE_ANGLE 0x345C
+#define EGL_TEXTURE_INTERNAL_FORMAT_ANGLE 0x345D
+#endif /* EGL_ANGLE_iosurface_client_buffer */
+
+#if defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOSurface/IOSurface.h>
+
+namespace
+{
+ void AddIntegerValue(CFMutableDictionaryRef dictionary, const CFStringRef key, int32_t value)
+ {
+ CFNumberRef number = CFNumberCreate(nullptr, kCFNumberSInt32Type, &value);
+ CFDictionaryAddValue(dictionary, key, number);
+ CFRelease(number);
+ }
+} // anonymous namespace
+
+class EGLClientBufferWrapper
+{
+public:
+ EGLClientBufferWrapper(int width = 1, int height = 1)
+ {
+ // Create a 1 by 1 BGRA8888 IOSurface
+ ioSurface = nullptr;
+
+ CFMutableDictionaryRef dict = CFDictionaryCreateMutable(
+ kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ AddIntegerValue(dict, kIOSurfaceWidth, width);
+ AddIntegerValue(dict, kIOSurfaceHeight, height);
+ AddIntegerValue(dict, kIOSurfacePixelFormat, 'BGRA');
+ AddIntegerValue(dict, kIOSurfaceBytesPerElement, 4);
+
+ ioSurface = IOSurfaceCreate(dict);
+ CFRelease(dict);
+
+ EXPECT_NE(nullptr, ioSurface);
+ }
+
+ ~EGLClientBufferWrapper()
+ {
+ IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nullptr);
+
+ CFRelease(ioSurface);
+ }
+
+ EGLClientBuffer getClientBuffer() const
+ {
+ return ioSurface;
+ }
+
+ const unsigned char* lockColor()
+ {
+ IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nullptr);
+ return reinterpret_cast<const unsigned char*>(IOSurfaceGetBaseAddress(ioSurface));
+ }
+
+ void unlockColor()
+ {
+ IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nullptr);
+ }
+
+ void writeColor(void* data, size_t dataSize)
+ {
+ // Write the data to the IOSurface
+ IOSurfaceLock(ioSurface, 0, nullptr);
+ memcpy(IOSurfaceGetBaseAddress(ioSurface), data, dataSize);
+ IOSurfaceUnlock(ioSurface, 0, nullptr);
+ }
+private:
+ IOSurfaceRef ioSurface;
+};
+
+#else // __APPLE__
+
+class EGLClientBufferWrapper
+{
+public:
+ EGLClientBufferWrapper(int width = 1, int height = 1)
+ {
+ clientBuffer = new unsigned char[4 * width * height];
+ }
+
+ ~EGLClientBufferWrapper()
+ {
+ delete[] clientBuffer;
+ }
+
+ EGLClientBuffer getClientBuffer() const
+ {
+ return clientBuffer;
+ }
+
+ const unsigned char* lockColor()
+ {
+ return clientBuffer;
+ }
+
+ void unlockColor()
+ {
+ }
+
+ void writeColor(void* data, size_t dataSize)
+ {
+ memcpy(clientBuffer, data, dataSize);
+ }
+private:
+ unsigned char* clientBuffer;
+};
+
+#endif
+
+class IOSurfaceClientBufferTest : public SwiftShaderTest
+{
+protected:
+ EGLSurface createIOSurfacePbuffer(EGLClientBuffer buffer, EGLint width, EGLint height, EGLint plane, GLenum internalFormat, GLenum type) const
+ {
+ // Make a PBuffer from it using the EGL_ANGLE_iosurface_client_buffer extension
+ const EGLint attribs[] = {
+ EGL_WIDTH, width,
+ EGL_HEIGHT, height,
+ EGL_IOSURFACE_PLANE_ANGLE, plane,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, (EGLint)internalFormat,
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TYPE_ANGLE, (EGLint)type,
+ EGL_NONE, EGL_NONE,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, buffer, getConfig(), attribs);
+ EXPECT_NE(EGL_NO_SURFACE, pbuffer);
+ return pbuffer;
+ }
+
+ void bindIOSurfaceToTexture(EGLClientBuffer buffer, EGLint width, EGLint height, EGLint plane, GLenum internalFormat, GLenum type, EGLSurface *pbuffer, GLuint *texture) const
+ {
+ *pbuffer = createIOSurfacePbuffer(buffer, width, height, plane, internalFormat, type);
+
+ // Bind the pbuffer
+ glBindTexture(GL_TEXTURE_RECTANGLE_ARB, *texture);
+ EGLBoolean result = eglBindTexImage(getDisplay(), *pbuffer, EGL_BACK_BUFFER);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ void doClear(GLenum internalFormat, bool clearToZero)
+ {
+ if(internalFormat == GL_R16UI)
+ {
+ GLuint color = clearToZero ? 0 : 257;
+ glClearBufferuiv(GL_COLOR, 0, &color);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+ }
+ else
+ {
+ glClearColor(clearToZero ? 0.0f : 1.0f / 255.0f,
+ clearToZero ? 0.0f : 2.0f / 255.0f,
+ clearToZero ? 0.0f : 3.0f / 255.0f,
+ clearToZero ? 0.0f : 4.0f / 255.0f);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+ }
+ }
+
+ void doClearTest(EGLClientBufferWrapper& clientBufferWrapper, GLenum internalFormat, GLenum type, void *data, size_t dataSize)
+ {
+ ASSERT_TRUE(dataSize <= 4);
+
+ // Bind the IOSurface to a texture and clear it.
+ GLuint texture = 1;
+ EGLSurface pbuffer;
+ bindIOSurfaceToTexture(clientBufferWrapper.getClientBuffer(), 1, 1, 0, internalFormat, type, &pbuffer, &texture);
+
+ // glClear the pbuffer
+ GLuint fbo = 2;
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_RECTANGLE_ARB, texture, 0);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+ EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+
+ doClear(internalFormat, false);
+
+ // Unbind pbuffer and check content.
+ EGLBoolean result = eglReleaseTexImage(getDisplay(), pbuffer, EGL_BACK_BUFFER);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+ const unsigned char* color = clientBufferWrapper.lockColor();
+ for(size_t i = 0; i < dataSize; ++i)
+ {
+ EXPECT_EQ(color[i], reinterpret_cast<unsigned char*>(data)[i]);
+ }
+
+ result = eglDestroySurface(getDisplay(), pbuffer);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ void doSampleTest(EGLClientBufferWrapper& clientBufferWrapper, GLenum internalFormat, GLenum type, void *data, size_t dataSize)
+ {
+ ASSERT_TRUE(dataSize <= 4);
+
+ clientBufferWrapper.writeColor(data, dataSize);
+
+ // Bind the IOSurface to a texture and clear it.
+ GLuint texture = 1;
+ EGLSurface pbuffer;
+ bindIOSurfaceToTexture(clientBufferWrapper.getClientBuffer(), 1, 1, 0, internalFormat, type, &pbuffer, &texture);
+
+ doClear(internalFormat, true);
+
+ // Create program and draw quad using it
+ const std::string vs =
+ "attribute vec4 position;\n"
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(position.xy, 0.0, 1.0);\n"
+ "}\n";
+
+ const std::string fs =
+ "#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";
+
+ const ProgramHandles ph = createProgram(vs, fs);
+
+ drawQuad(ph.program);
+
+ deleteProgram(ph);
+
+ EXPECT_GLENUM_EQ(GL_NO_ERROR, glGetError());
+
+ // Unbind pbuffer and check content.
+ EGLBoolean result = eglReleaseTexImage(getDisplay(), pbuffer, EGL_BACK_BUFFER);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+
+ const unsigned char* color = clientBufferWrapper.lockColor();
+ for(size_t i = 0; i < dataSize; ++i)
+ {
+ EXPECT_EQ(color[i], reinterpret_cast<unsigned char*>(data)[i]);
+ }
+ clientBufferWrapper.unlockColor();
+ }
+};
+
+// Tests for the EGL_ANGLE_iosurface_client_buffer extension
+TEST_F(IOSurfaceClientBufferTest, RenderToBGRA8888IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[4] = { 3, 2, 1, 4 };
+ doClearTest(clientBufferWrapper, GL_BGRA_EXT, GL_UNSIGNED_BYTE, data, 4);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test reading from BGRA8888 IOSurfaces
+TEST_F(IOSurfaceClientBufferTest, ReadFromBGRA8888IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[4] = { 3, 2, 1, 4 };
+ doSampleTest(clientBufferWrapper, GL_BGRA_EXT, GL_UNSIGNED_BYTE, data, 4);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test using RG88 IOSurfaces for rendering
+TEST_F(IOSurfaceClientBufferTest, RenderToRG88IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[2] = { 1, 2 };
+ doClearTest(clientBufferWrapper, GL_RG, GL_UNSIGNED_BYTE, data, 2);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test reading from RG88 IOSurfaces
+TEST_F(IOSurfaceClientBufferTest, ReadFromRG88IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[2] = { 1, 2 };
+ doSampleTest(clientBufferWrapper, GL_RG, GL_UNSIGNED_BYTE, data, 2);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test using R8 IOSurfaces for rendering
+TEST_F(IOSurfaceClientBufferTest, RenderToR8IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[1] = { 1 };
+ doClearTest(clientBufferWrapper, GL_RED, GL_UNSIGNED_BYTE, data, 1);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test reading from R8 IOSurfaces
+TEST_F(IOSurfaceClientBufferTest, ReadFromR8IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ unsigned char data[1] = { 1 };
+ doSampleTest(clientBufferWrapper, GL_RED, GL_UNSIGNED_BYTE, data, 1);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test using R16 IOSurfaces for rendering
+TEST_F(IOSurfaceClientBufferTest, RenderToR16IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ uint16_t data[1] = { 257 };
+ doClearTest(clientBufferWrapper, GL_R16UI, GL_UNSIGNED_SHORT, data, 2);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test reading from R8 IOSurfaces
+TEST_F(IOSurfaceClientBufferTest, ReadFromR16IOSurface)
+{
+ Initialize(3, false);
+
+ { // EGLClientBufferWrapper scope
+ EGLClientBufferWrapper clientBufferWrapper;
+ uint16_t data[1] = { 257 };
+ doSampleTest(clientBufferWrapper, GL_R16UI, GL_UNSIGNED_SHORT, data, 1);
+ } // end of EGLClientBufferWrapper scope
+
+ Uninitialize();
+}
+
+// Test the validation errors for missing attributes for eglCreatePbufferFromClientBuffer with
+// IOSurface
+TEST_F(IOSurfaceClientBufferTest, NegativeValidationMissingAttributes)
+{
+ Initialize(3, false);
+
+ {
+ EGLClientBufferWrapper clientBufferWrapper(10, 10);
+
+ // Success case
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_NE(EGL_NO_SURFACE, pbuffer);
+
+ EGLBoolean result = eglDestroySurface(getDisplay(), pbuffer);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ // Missing EGL_WIDTH
+ {
+ const EGLint attribs[] = {
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError());
+ }
+
+ // Missing EGL_HEIGHT
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError());
+ }
+
+ // Missing EGL_IOSURFACE_PLANE_ANGLE
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError());
+ }
+
+ // Missing EGL_TEXTURE_TARGET - EGL_BAD_MATCH from the base spec of
+ // eglCreatePbufferFromClientBuffer
+ {
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_MATCH, eglGetError());
+ }
+
+ // Missing EGL_TEXTURE_INTERNAL_FORMAT_ANGLE
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
+ EGL_NONE, EGL_NONE,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError());
+ }
+
+ // Missing EGL_TEXTURE_FORMAT - EGL_BAD_MATCH from the base spec of
+ // eglCreatePbufferFromClientBuffer
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
+ EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
+ EGL_NONE, EGL_NONE,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_MATCH, eglGetError());
+ }
+
+ // Missing EGL_TEXTURE_TYPE_ANGLE
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_BGRA_EXT,
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_NONE, EGL_NONE,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_PARAMETER, eglGetError());
+ }
+ }
+
+ Uninitialize();
+}
+
+// Test the validation errors for bad parameters for eglCreatePbufferFromClientBuffer with IOSurface
+TEST_F(IOSurfaceClientBufferTest, NegativeValidationBadAttributes)
+{
+ Initialize(3, false);
+
+ {
+ EGLClientBufferWrapper clientBufferWrapper(10, 10);
+
+ // Success case
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_NE(EGL_NO_SURFACE, pbuffer);
+
+ EGLBoolean result = eglDestroySurface(getDisplay(), pbuffer);
+ EXPECT_EQ((EGLBoolean)EGL_TRUE, result);
+ EXPECT_EQ(EGL_SUCCESS, eglGetError());
+ }
+
+ // EGL_TEXTURE_FORMAT must be EGL_TEXTURE_RGBA
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // EGL_WIDTH must be at least 1
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 0,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // EGL_HEIGHT must be at least 1
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 0,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+#if defined(__APPLE__)
+ // EGL_WIDTH must be at most the width of the IOSurface
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 11,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // EGL_HEIGHT must be at most the height of the IOSurface
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 11,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // EGL_IOSURFACE_PLANE_ANGLE must less than the number of planes of the IOSurface
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 1,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+#endif
+
+ // EGL_TEXTURE_FORMAT must be at EGL_TEXTURE_RECTANGLE_ANGLE
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_2D,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // EGL_IOSURFACE_PLANE_ANGLE must be at least 0
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, -1,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ 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,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+
+ // The internal format / type most be listed in the table
+ {
+ const EGLint attribs[] = {
+ EGL_WIDTH, 10,
+ EGL_HEIGHT, 10,
+ EGL_IOSURFACE_PLANE_ANGLE, 0,
+ EGL_TEXTURE_TARGET, EGL_TEXTURE_RECTANGLE_ANGLE,
+ EGL_TEXTURE_INTERNAL_FORMAT_ANGLE, GL_RGBA,
+ EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA,
+ EGL_TEXTURE_TYPE_ANGLE, GL_UNSIGNED_BYTE,
+ EGL_NONE, EGL_NONE,
+ };
+
+ EGLSurface pbuffer = eglCreatePbufferFromClientBuffer(getDisplay(), EGL_IOSURFACE_ANGLE, clientBufferWrapper.getClientBuffer(), getConfig(), attribs);
+ EXPECT_EQ(EGL_NO_SURFACE, pbuffer);
+ EXPECT_EQ(EGL_BAD_ATTRIBUTE, eglGetError());
+ }
+ }
+
+ Uninitialize();
+}
+
+// Test IOSurface pbuffers cannot be made current
+TEST_F(IOSurfaceClientBufferTest, MakeCurrentDisallowed)
+{
+ Initialize(3, false);
+
+ {
+ EGLClientBufferWrapper clientBufferWrapper(10, 10);
+
+ EGLSurface pbuffer = createIOSurfacePbuffer(clientBufferWrapper.getClientBuffer(), 10, 10, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE);
+
+ EGLBoolean result = eglMakeCurrent(getDisplay(), pbuffer, pbuffer, getContext());
+ EXPECT_EQ((EGLBoolean)EGL_FALSE, result);
+ EXPECT_EQ(EGL_BAD_SURFACE, eglGetError());
+ }
+
+ Uninitialize();
+}