1/*
2    Copyright (C) 2012 Samsung Electronics
3    Copyright (C) 2012 Intel Corporation. All rights reserved.
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public License
16    along with this library; see the file COPYING.LIB.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18    Boston, MA 02110-1301, USA.
19*/
20
21#include "config.h"
22#include "GraphicsContext3DPrivate.h"
23
24#include "HostWindow.h"
25#include "NotImplemented.h"
26
27namespace WebCore {
28
29std::unique_ptr<GraphicsContext3DPrivate> GraphicsContext3DPrivate::create(GraphicsContext3D* context, HostWindow* hostWindow)
30{
31    std::unique_ptr<GraphicsContext3DPrivate> platformLayer = std::make_unique<GraphicsContext3DPrivate>(context, hostWindow);
32
33    if (platformLayer && platformLayer->initialize())
34        return platformLayer;
35
36    return nullptr;
37}
38
39GraphicsContext3DPrivate::GraphicsContext3DPrivate(GraphicsContext3D* context, HostWindow* hostWindow)
40    : m_context(context)
41    , m_hostWindow(hostWindow)
42{
43}
44
45bool GraphicsContext3DPrivate::initialize()
46{
47    if (m_context->m_renderStyle == GraphicsContext3D::RenderDirectlyToHostWindow)
48        return false;
49
50    if (m_hostWindow && m_hostWindow->platformPageClient()) {
51        // FIXME: Implement this code path for WebKit1.
52        // Get Evas object from platformPageClient and set EvasGL related members.
53        return false;
54    }
55
56    m_offScreenContext = GLPlatformContext::createContext(m_context->m_renderStyle);
57    if (!m_offScreenContext)
58        return false;
59
60    if (m_context->m_renderStyle == GraphicsContext3D::RenderOffscreen) {
61        m_offScreenSurface = GLPlatformSurface::createOffScreenSurface();
62
63        if (!m_offScreenSurface)
64            return false;
65
66        if (!m_offScreenContext->initialize(m_offScreenSurface.get()))
67            return false;
68
69        if (!makeContextCurrent())
70            return false;
71#if USE(GRAPHICS_SURFACE)
72        m_surfaceOperation = CreateSurface;
73#endif
74    }
75
76    return true;
77}
78
79GraphicsContext3DPrivate::~GraphicsContext3DPrivate()
80{
81    releaseResources();
82}
83
84void GraphicsContext3DPrivate::releaseResources()
85{
86    if (m_context->m_renderStyle == GraphicsContext3D::RenderToCurrentGLContext)
87        return;
88
89    // Release the current context and drawable only after destroying any associated gl resources.
90#if USE(GRAPHICS_SURFACE)
91    if (m_previousGraphicsSurface)
92        m_previousGraphicsSurface = nullptr;
93
94    if (m_graphicsSurface)
95        m_graphicsSurface = nullptr;
96
97    m_surfaceHandle = GraphicsSurfaceToken();
98#endif
99    if (m_offScreenSurface)
100        m_offScreenSurface->destroy();
101
102    if (m_offScreenContext) {
103        m_offScreenContext->destroy();
104        m_offScreenContext->releaseCurrent();
105    }
106}
107
108void GraphicsContext3DPrivate::setContextLostCallback(std::unique_ptr<GraphicsContext3D::ContextLostCallback> callBack)
109{
110    m_contextLostCallback = WTF::move(callBack);
111}
112
113PlatformGraphicsContext3D GraphicsContext3DPrivate::platformGraphicsContext3D() const
114{
115    return m_offScreenContext->handle();
116}
117
118bool GraphicsContext3DPrivate::makeContextCurrent() const
119{
120    bool success = m_offScreenContext->makeCurrent(m_offScreenSurface.get());
121
122    if (!m_offScreenContext->isValid()) {
123        // FIXME: Restore context
124        if (m_contextLostCallback)
125            m_contextLostCallback->onContextLost();
126
127        return false;
128    }
129
130    return success;
131}
132
133bool GraphicsContext3DPrivate::prepareBuffer() const
134{
135    if (!makeContextCurrent())
136        return false;
137
138    m_context->markLayerComposited();
139
140    if (m_context->m_attrs.antialias) {
141        bool enableScissorTest = false;
142        int width = m_context->m_currentWidth;
143        int height = m_context->m_currentHeight;
144        // We should copy the full buffer, and not respect the current scissor bounds.
145        // FIXME: It would be more efficient to track the state of the scissor test.
146        if (m_context->isEnabled(GraphicsContext3D::SCISSOR_TEST)) {
147            enableScissorTest = true;
148            m_context->disable(GraphicsContext3D::SCISSOR_TEST);
149        }
150
151        glBindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_context->m_multisampleFBO);
152        glBindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_context->m_fbo);
153
154        // Use NEAREST as no scale is performed during the blit.
155        m_context->getExtensions()->blitFramebuffer(0, 0, width, height, 0, 0, width, height, GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
156
157        if (enableScissorTest)
158            m_context->enable(GraphicsContext3D::SCISSOR_TEST);
159
160        glBindFramebuffer(GL_FRAMEBUFFER, m_context->m_state.boundFBO);
161    }
162
163    return true;
164}
165
166#if USE(TEXTURE_MAPPER_GL)
167void GraphicsContext3DPrivate::paintToTextureMapper(TextureMapper*, const FloatRect& /* target */, const TransformationMatrix&, float /* opacity */)
168{
169    notImplemented();
170}
171#endif
172
173#if USE(GRAPHICS_SURFACE)
174void GraphicsContext3DPrivate::createGraphicsSurface()
175{
176    static PendingSurfaceOperation pendingOperation = DeletePreviousSurface | Resize | CreateSurface;
177    if (!(m_surfaceOperation & pendingOperation))
178        return;
179
180    if (m_surfaceOperation & DeletePreviousSurface) {
181        m_previousGraphicsSurface = nullptr;
182        m_surfaceOperation &= ~DeletePreviousSurface;
183    }
184
185    if (!(m_surfaceOperation & pendingOperation))
186        return;
187
188    // Don't release current graphics surface until we have prepared surface
189    // with requested size. This is to avoid flashing during resize.
190    if (m_surfaceOperation & Resize) {
191        m_previousGraphicsSurface = m_graphicsSurface;
192        m_surfaceOperation &= ~Resize;
193        m_surfaceOperation |= DeletePreviousSurface;
194        m_size = IntSize(m_context->m_currentWidth, m_context->m_currentHeight);
195    } else
196        m_surfaceOperation &= ~CreateSurface;
197
198    m_targetRect = IntRect(IntPoint(), m_size);
199
200    if (m_size.isEmpty()) {
201        if (m_graphicsSurface) {
202            m_graphicsSurface = nullptr;
203            m_surfaceHandle = GraphicsSurfaceToken();
204            makeContextCurrent();
205        }
206
207        return;
208    }
209
210    m_offScreenContext->releaseCurrent();
211    GraphicsSurface::Flags flags = GraphicsSurface::SupportsTextureTarget | GraphicsSurface::SupportsSharing;
212
213    if (m_context->m_attrs.alpha)
214        flags |= GraphicsSurface::SupportsAlpha;
215
216    m_graphicsSurface = GraphicsSurface::create(m_size, flags, m_offScreenContext->handle());
217
218    if (!m_graphicsSurface)
219        m_surfaceHandle = GraphicsSurfaceToken();
220    else
221        m_surfaceHandle = GraphicsSurfaceToken(m_graphicsSurface->exportToken());
222
223    makeContextCurrent();
224}
225
226void GraphicsContext3DPrivate::didResizeCanvas(const IntSize& size)
227{
228    if (m_surfaceOperation & CreateSurface) {
229        m_size = size;
230        createGraphicsSurface();
231        return;
232    }
233
234    m_surfaceOperation |= Resize;
235}
236
237uint32_t GraphicsContext3DPrivate::copyToGraphicsSurface()
238{
239    createGraphicsSurface();
240
241    if (!m_graphicsSurface || m_context->m_layerComposited || !prepareBuffer())
242        return 0;
243
244    m_graphicsSurface->copyFromTexture(m_context->m_texture, m_targetRect);
245    makeContextCurrent();
246
247    return m_graphicsSurface->frontBuffer();
248}
249
250GraphicsSurfaceToken GraphicsContext3DPrivate::graphicsSurfaceToken() const
251{
252    return m_surfaceHandle;
253}
254
255IntSize GraphicsContext3DPrivate::platformLayerSize() const
256{
257    return m_size;
258}
259
260GraphicsSurface::Flags GraphicsContext3DPrivate::graphicsSurfaceFlags() const
261{
262    if (m_graphicsSurface)
263        return m_graphicsSurface->flags();
264
265    return TextureMapperPlatformLayer::graphicsSurfaceFlags();
266}
267#endif
268
269} // namespace WebCore
270