1/*
2 * Copyright (C) 2013 Intel Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "EGLXSurface.h"
28
29#if PLATFORM(X11) && USE(EGL) && USE(GRAPHICS_SURFACE)
30
31#include "EGLConfigSelector.h"
32#include "EGLHelper.h"
33#include "GLPlatformContext.h"
34
35namespace WebCore {
36
37EGLWindowTransportSurface::EGLWindowTransportSurface(const IntSize& size, GLPlatformSurface::SurfaceAttributes attributes)
38    : EGLTransportSurface(size, attributes)
39{
40    if (!m_configSelector)
41        return;
42
43    if (!m_configSelector->surfaceContextConfig()) {
44        destroy();
45        return;
46    }
47
48    EGLint visualId = m_configSelector->nativeVisualId(m_configSelector->surfaceContextConfig());
49
50    if (visualId == -1) {
51        destroy();
52        return;
53    }
54
55    NativeWrapper::createOffScreenWindow(&m_bufferHandle, visualId, m_configSelector->attributes() & GLPlatformSurface::SupportAlpha, size);
56
57    if (!m_bufferHandle) {
58        destroy();
59        return;
60    }
61
62    m_drawable = eglCreateWindowSurface(m_sharedDisplay, m_configSelector->surfaceContextConfig(), static_cast<EGLNativeWindowType>(m_bufferHandle), 0);
63
64    if (m_drawable == EGL_NO_SURFACE) {
65        LOG_ERROR("Failed to create EGL surface(%d).", eglGetError());
66        destroy();
67    }
68}
69
70EGLWindowTransportSurface::~EGLWindowTransportSurface()
71{
72}
73
74void EGLWindowTransportSurface::swapBuffers()
75{
76    if (!eglSwapBuffers(m_sharedDisplay, m_drawable))
77        LOG_ERROR("Failed to SwapBuffers(%d).", eglGetError());
78}
79
80void EGLWindowTransportSurface::destroy()
81{
82    EGLTransportSurface::destroy();
83
84    if (m_bufferHandle) {
85        NativeWrapper::destroyWindow(m_bufferHandle);
86        m_bufferHandle = 0;
87    }
88}
89
90EGLPixmapSurface::EGLPixmapSurface(GLPlatformSurface::SurfaceAttributes surfaceAttributes)
91    : EGLOffScreenSurface(surfaceAttributes)
92{
93    if (!m_configSelector)
94        return;
95
96    EGLConfig config = m_configSelector->pixmapContextConfig();
97
98    if (!config) {
99        destroy();
100        return;
101    }
102
103    EGLint visualId = m_configSelector->nativeVisualId(config);
104
105    if (visualId == -1) {
106        destroy();
107        return;
108    }
109
110    NativePixmap pixmap;
111    NativeWrapper::createPixmap(&pixmap, visualId, m_configSelector->attributes() & GLPlatformSurface::SupportAlpha);
112    m_bufferHandle = pixmap;
113
114    if (!m_bufferHandle) {
115        destroy();
116        return;
117    }
118
119    m_drawable = eglCreatePixmapSurface(m_sharedDisplay, config, static_cast<EGLNativePixmapType>(m_bufferHandle), 0);
120
121    if (m_drawable == EGL_NO_SURFACE) {
122        LOG_ERROR("Failed to create EGL surface(%d).", eglGetError());
123        destroy();
124    }
125}
126
127EGLPixmapSurface::~EGLPixmapSurface()
128{
129}
130
131void EGLPixmapSurface::destroy()
132{
133    EGLOffScreenSurface::destroy();
134
135    if (m_bufferHandle) {
136        NativeWrapper::destroyPixmap(m_bufferHandle);
137        m_bufferHandle = 0;
138    }
139}
140
141EGLXTransportSurfaceClient::EGLXTransportSurfaceClient(const PlatformBufferHandle handle, const IntSize& size, bool hasAlpha)
142    : GLTransportSurfaceClient()
143    , m_image(0)
144    , m_size(size)
145    , m_totalBytes(0)
146{
147    if (!handle)
148        return;
149
150    m_handle = handle;
151    XWindowAttributes attr;
152
153    if (!XGetWindowAttributes(NativeWrapper::nativeDisplay(), m_handle, &attr))
154        return;
155
156    createTexture();
157    GLPlatformSurface::SurfaceAttributes sharedSurfaceAttributes = GLPlatformSurface::Default;
158
159    if (hasAlpha)
160        sharedSurfaceAttributes = GLPlatformSurface::SupportAlpha;
161
162    EGLConfigSelector configSelector(sharedSurfaceAttributes);
163    EGLConfig config = configSelector.surfaceClientConfig(XVisualIDFromVisual(attr.visual));
164    m_eglImage = adoptPtr(new EGLTextureFromPixmap(m_handle, hasAlpha, config));
165
166    if (!m_eglImage->isValid() || eglGetError() != EGL_SUCCESS)
167        destroy();
168
169    if (m_eglImage)
170        return;
171
172    m_totalBytes = m_size.width() * m_size.height() * 4;
173
174#if USE(OPENGL_ES_2)
175    m_format = GraphicsContext3D::RGBA;
176    static bool bgraSupported = GLPlatformContext::supportsGLExtension("GL_EXT_texture_format_BGRA8888");
177    if (bgraSupported)
178        m_format = GraphicsContext3D::BGRA;
179#endif
180
181    createTexture();
182    prepareTexture();
183}
184
185EGLXTransportSurfaceClient::~EGLXTransportSurfaceClient()
186{
187}
188
189void EGLXTransportSurfaceClient::destroy()
190{
191    GLTransportSurfaceClient::destroy();
192
193    if (m_eglImage) {
194        m_eglImage->destroy();
195        m_eglImage = nullptr;
196    }
197
198    eglWaitGL();
199
200    if (m_image) {
201        XDestroyImage(m_image);
202        m_image = 0;
203    }
204}
205
206void EGLXTransportSurfaceClient::prepareTexture()
207{
208    ::glBindTexture(GL_TEXTURE_2D, m_texture);
209
210    if (m_eglImage) {
211        m_eglImage->reBindTexImage();
212        return;
213    }
214
215    // Fallback to use XImage in case EGLImage and TextureToPixmap are not supported.
216    m_image = XGetImage(NativeWrapper::nativeDisplay(), m_handle, 0, 0, m_size.width(), m_size.height(), AllPlanes, ZPixmap);
217
218#if USE(OPENGL_ES_2)
219    if (m_format != GraphicsContext3D::BGRA) {
220        for (unsigned i = 0; i < m_totalBytes; i += 4)
221            std::swap(m_image->data[i], m_image->data[i + 2]);
222    }
223#endif
224
225    glTexImage2D(GL_TEXTURE_2D, 0, m_format, m_size.width(), m_size.height(), 0, m_format, GL_UNSIGNED_BYTE, m_image->data);
226
227    if (m_image) {
228        XDestroyImage(m_image);
229        m_image = 0;
230    }
231}
232
233EGLTextureFromPixmap::EGLTextureFromPixmap(const NativePixmap handle, bool hasAlpha, EGLConfig config)
234    : m_eglImage(0)
235    , m_surface(EGL_NO_SURFACE)
236{
237    if (!handle)
238        return;
239
240    static bool textureFromPixmapSupported = GLPlatformContext::supportsEGLExtension(EGLHelper::eglDisplay(), "EGL_NOK_texture_from_pixmap");
241
242    if (textureFromPixmapSupported) {
243        const EGLint pixmapAttribs[] = { EGL_TEXTURE_FORMAT, hasAlpha ? EGL_TEXTURE_RGBA : EGL_TEXTURE_RGB, EGL_TEXTURE_TARGET, EGL_TEXTURE_2D, EGL_NONE };
244        m_surface = eglCreatePixmapSurface(EGLHelper::eglDisplay(), config, handle, pixmapAttribs);
245
246        if (m_surface != EGL_NO_SURFACE && !eglBindTexImage(EGLHelper::eglDisplay(), m_surface, EGL_BACK_BUFFER))
247            destroy();
248    }
249
250    if (m_surface != EGL_NO_SURFACE)
251        return;
252
253    static const EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
254    EGLHelper::createEGLImage(&m_eglImage, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)(handle), imageAttrs);
255
256    if (m_eglImage) {
257        EGLHelper::imageTargetTexture2DOES(m_eglImage);
258        EGLint error = eglGetError();
259
260        if (error != EGL_SUCCESS)
261            destroy();
262    }
263}
264
265EGLTextureFromPixmap::~EGLTextureFromPixmap()
266{
267}
268
269void EGLTextureFromPixmap::destroy()
270{
271    eglWaitNative(EGL_CORE_NATIVE_ENGINE);
272
273    if (m_surface != EGL_NO_SURFACE)
274        eglReleaseTexImage(EGLHelper::eglDisplay(), m_surface, EGL_BACK_BUFFER);
275
276    if (m_eglImage) {
277        EGLHelper::destroyEGLImage(m_eglImage);
278        m_eglImage = 0;
279    }
280
281    if (m_surface != EGL_NO_SURFACE) {
282        eglDestroySurface(EGLHelper::eglDisplay(), m_surface);
283        m_surface = EGL_NO_SURFACE;
284    }
285
286    eglWaitGL();
287}
288
289bool EGLTextureFromPixmap::isValid() const
290{
291    if (m_surface || m_eglImage)
292        return true;
293
294    return false;
295}
296
297bool EGLTextureFromPixmap::bindTexImage()
298{
299    if (m_surface != EGL_NO_SURFACE) {
300        bool success = eglBindTexImage(EGLHelper::eglDisplay(), m_surface, EGL_BACK_BUFFER);
301        return success;
302    }
303
304    if (m_eglImage) {
305        EGLHelper::imageTargetTexture2DOES(m_eglImage);
306        return true;
307    }
308
309    return false;
310}
311
312bool EGLTextureFromPixmap::reBindTexImage()
313{
314    if (m_surface != EGL_NO_SURFACE) {
315        bool success = eglReleaseTexImage(EGLHelper::eglDisplay(), m_surface, EGL_BACK_BUFFER);
316
317        if (success)
318            success = eglBindTexImage(EGLHelper::eglDisplay(), m_surface, EGL_BACK_BUFFER);
319
320        return success;
321    }
322
323    if (m_eglImage) {
324        EGLHelper::imageTargetTexture2DOES(m_eglImage);
325        return true;
326    }
327
328    return false;
329}
330
331}
332
333#endif
334