1/*
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
3 * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above
10 *    copyright notice, this list of conditions and the following
11 *    disclaimer.
12 * 2. Redistributions in binary form must reproduce the above
13 *    copyright notice, this list of conditions and the following
14 *    disclaimer in the documentation and/or other materials
15 *    provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
26 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#if ENABLE(CSS_SHADERS) && USE(3D_GRAPHICS)
34#include "FECustomFilter.h"
35
36#include "CustomFilterCompiledProgram.h"
37#include "CustomFilterRenderer.h"
38#include "CustomFilterValidatedProgram.h"
39#include "Extensions3D.h"
40#include "GraphicsContext3D.h"
41#include "RenderTreeAsText.h"
42#include "TextStream.h"
43
44#include <wtf/Uint8ClampedArray.h>
45
46namespace WebCore {
47
48FECustomFilter::FECustomFilter(Filter* filter, PassRefPtr<GraphicsContext3D> context, PassRefPtr<CustomFilterValidatedProgram> validatedProgram, const CustomFilterParameterList& parameters,
49    unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
50    : FilterEffect(filter)
51    , m_context(context)
52    , m_validatedProgram(validatedProgram)
53    , m_inputTexture(0)
54    , m_frameBuffer(0)
55    , m_depthBuffer(0)
56    , m_destTexture(0)
57    , m_triedMultisampleBuffer(false)
58    , m_multisampleFrameBuffer(0)
59    , m_multisampleRenderBuffer(0)
60    , m_multisampleDepthBuffer(0)
61{
62    // We will not pass a CustomFilterCompiledProgram here, as we only want to compile it when we actually need it in the first paint.
63    m_customFilterRenderer = CustomFilterRenderer::create(m_context, m_validatedProgram->programInfo().programType(), parameters, meshRows, meshColumns, meshType);
64}
65
66PassRefPtr<FECustomFilter> FECustomFilter::create(Filter* filter, PassRefPtr<GraphicsContext3D> context, PassRefPtr<CustomFilterValidatedProgram> validatedProgram, const CustomFilterParameterList& parameters,
67    unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
68{
69    return adoptRef(new FECustomFilter(filter, context, validatedProgram, parameters, meshRows, meshColumns, meshType));
70}
71
72FECustomFilter::~FECustomFilter()
73{
74    deleteRenderBuffers();
75}
76
77void FECustomFilter::deleteRenderBuffers()
78{
79    ASSERT(m_context);
80    m_context->makeContextCurrent();
81    if (m_inputTexture) {
82        m_context->deleteTexture(m_inputTexture);
83        m_inputTexture = 0;
84    }
85    if (m_frameBuffer) {
86        // Make sure to unbind any framebuffer from the context first, otherwise
87        // some platforms might refuse to bind the same buffer id again.
88        m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0);
89        m_context->deleteFramebuffer(m_frameBuffer);
90        m_frameBuffer = 0;
91    }
92    if (m_depthBuffer) {
93        m_context->deleteRenderbuffer(m_depthBuffer);
94        m_depthBuffer = 0;
95    }
96    if (m_destTexture) {
97        m_context->deleteTexture(m_destTexture);
98        m_destTexture = 0;
99    }
100    deleteMultisampleRenderBuffers();
101}
102
103void FECustomFilter::deleteMultisampleRenderBuffers()
104{
105    if (m_multisampleFrameBuffer) {
106        // Make sure to unbind any framebuffer from the context first, otherwise
107        // some platforms might refuse to bind the same buffer id again.
108        m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0);
109        m_context->deleteFramebuffer(m_multisampleFrameBuffer);
110        m_multisampleFrameBuffer = 0;
111    }
112    if (m_multisampleRenderBuffer) {
113        m_context->deleteRenderbuffer(m_multisampleRenderBuffer);
114        m_multisampleRenderBuffer = 0;
115    }
116    if (m_multisampleDepthBuffer) {
117        m_context->deleteRenderbuffer(m_multisampleDepthBuffer);
118        m_multisampleDepthBuffer = 0;
119    }
120}
121
122void FECustomFilter::platformApplySoftware()
123{
124    if (!applyShader())
125        clearShaderResult();
126}
127
128void FECustomFilter::clearShaderResult()
129{
130    clearResult();
131    Uint8ClampedArray* dstPixelArray = createUnmultipliedImageResult();
132    if (!dstPixelArray)
133        return;
134
135    FilterEffect* in = inputEffect(0);
136    setIsAlphaImage(in->isAlphaImage());
137    IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
138    in->copyUnmultipliedImage(dstPixelArray, effectDrawingRect);
139}
140
141void FECustomFilter::drawFilterMesh(Platform3DObject inputTexture)
142{
143    bool multisample = canUseMultisampleBuffers();
144    m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, multisample ? m_multisampleFrameBuffer : m_frameBuffer);
145    m_context->viewport(0, 0, m_contextSize.width(), m_contextSize.height());
146
147    m_context->clearColor(0, 0, 0, 0);
148    m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT | GraphicsContext3D::DEPTH_BUFFER_BIT);
149
150    m_customFilterRenderer->draw(inputTexture, m_contextSize);
151
152    if (multisample)
153        resolveMultisampleBuffer();
154}
155
156bool FECustomFilter::prepareForDrawing()
157{
158    m_context->makeContextCurrent();
159
160    if (!m_customFilterRenderer->compiledProgram()) {
161        RefPtr<CustomFilterCompiledProgram> compiledProgram = m_validatedProgram->compiledProgram();
162        if (!compiledProgram) {
163            // Lazily create a compiled program and let CustomFilterValidatedProgram hold on to it.
164            compiledProgram = CustomFilterCompiledProgram::create(m_context, m_validatedProgram->validatedVertexShader(), m_validatedProgram->validatedFragmentShader(), m_validatedProgram->programInfo().programType());
165            m_validatedProgram->setCompiledProgram(compiledProgram);
166        }
167        // Lazily inject the compiled program into the CustomFilterRenderer.
168        m_customFilterRenderer->setCompiledProgram(compiledProgram.release());
169    }
170
171    if (!m_customFilterRenderer->prepareForDrawing())
172        return false;
173
174    // Only allocate a texture if the program needs one and the caller doesn't allocate one by itself.
175    if ((m_customFilterRenderer->programNeedsInputTexture() && !ensureInputTexture()) || !ensureFrameBuffer())
176        return false;
177
178    return true;
179}
180
181bool FECustomFilter::applyShader()
182{
183    Uint8ClampedArray* dstPixelArray = m_customFilterRenderer->premultipliedAlpha() ? createPremultipliedImageResult() : createUnmultipliedImageResult();
184    if (!dstPixelArray)
185        return false;
186
187    if (!prepareForDrawing())
188        return false;
189
190    FilterEffect* in = inputEffect(0);
191    IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
192    IntSize newContextSize(effectDrawingRect.size());
193    if (!resizeContextIfNeeded(newContextSize))
194        return false;
195
196    bool needsInputTexture = m_customFilterRenderer->programNeedsInputTexture();
197    if (needsInputTexture) {
198        RefPtr<Uint8ClampedArray> srcPixelArray = in->asUnmultipliedImage(effectDrawingRect);
199        uploadInputTexture(srcPixelArray.get());
200    }
201    drawFilterMesh(needsInputTexture ? m_inputTexture : 0);
202
203    ASSERT(static_cast<size_t>(newContextSize.width() * newContextSize.height() * 4) == dstPixelArray->length());
204    m_context->readPixels(0, 0, newContextSize.width(), newContextSize.height(), GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, dstPixelArray->data());
205
206    return true;
207}
208
209bool FECustomFilter::ensureInputTexture()
210{
211    if (!m_inputTexture)
212        m_inputTexture = m_context->createTexture();
213    return m_inputTexture;
214}
215
216void FECustomFilter::uploadInputTexture(Uint8ClampedArray* srcPixelArray)
217{
218    m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_inputTexture);
219    m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, m_contextSize.width(), m_contextSize.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, srcPixelArray->data());
220}
221
222bool FECustomFilter::ensureFrameBuffer()
223{
224    if (!m_frameBuffer)
225        m_frameBuffer = m_context->createFramebuffer();
226    if (!m_depthBuffer)
227        m_depthBuffer = m_context->createRenderbuffer();
228    if (!m_destTexture)
229        m_destTexture = m_context->createTexture();
230    return m_frameBuffer && m_depthBuffer && m_destTexture;
231}
232
233bool FECustomFilter::createMultisampleBuffer()
234{
235    ASSERT(!m_triedMultisampleBuffer);
236    m_triedMultisampleBuffer = true;
237
238    Extensions3D* extensions = m_context->getExtensions();
239    if (!extensions
240        || !extensions->maySupportMultisampling()
241        || !extensions->supports("GL_ANGLE_framebuffer_multisample")
242        || !extensions->supports("GL_ANGLE_framebuffer_blit")
243        || !extensions->supports("GL_OES_rgb8_rgba8"))
244        return false;
245
246    extensions->ensureEnabled("GL_ANGLE_framebuffer_blit");
247    extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample");
248    extensions->ensureEnabled("GL_OES_rgb8_rgba8");
249
250    if (!m_multisampleFrameBuffer)
251        m_multisampleFrameBuffer = m_context->createFramebuffer();
252    if (!m_multisampleRenderBuffer)
253        m_multisampleRenderBuffer = m_context->createRenderbuffer();
254    if (!m_multisampleDepthBuffer)
255        m_multisampleDepthBuffer = m_context->createRenderbuffer();
256
257    return true;
258}
259
260void FECustomFilter::resolveMultisampleBuffer()
261{
262    ASSERT(m_triedMultisampleBuffer && m_multisampleFrameBuffer && m_multisampleRenderBuffer && m_multisampleDepthBuffer);
263    m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, m_multisampleFrameBuffer);
264    m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, m_frameBuffer);
265
266    ASSERT(m_context->getExtensions());
267    m_context->getExtensions()->blitFramebuffer(0, 0, m_contextSize.width(), m_contextSize.height(), 0, 0, m_contextSize.width(), m_contextSize.height(), GraphicsContext3D::COLOR_BUFFER_BIT, GraphicsContext3D::NEAREST);
268
269    m_context->bindFramebuffer(Extensions3D::READ_FRAMEBUFFER, 0);
270    m_context->bindFramebuffer(Extensions3D::DRAW_FRAMEBUFFER, 0);
271
272    m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_frameBuffer);
273}
274
275bool FECustomFilter::canUseMultisampleBuffers() const
276{
277    return m_triedMultisampleBuffer && m_multisampleFrameBuffer && m_multisampleRenderBuffer && m_multisampleDepthBuffer;
278}
279
280bool FECustomFilter::resizeMultisampleBuffers(const IntSize& newContextSize)
281{
282    if (!m_triedMultisampleBuffer && !createMultisampleBuffer())
283        return false;
284
285    if (!canUseMultisampleBuffers())
286        return false;
287
288    static const int kMaxSampleCount = 4;
289    int maxSupportedSampleCount = 0;
290    m_context->getIntegerv(Extensions3D::MAX_SAMPLES, &maxSupportedSampleCount);
291    int sampleCount = std::min(kMaxSampleCount, maxSupportedSampleCount);
292    if (!sampleCount) {
293        deleteMultisampleRenderBuffers();
294        return false;
295    }
296
297    Extensions3D* extensions = m_context->getExtensions();
298    ASSERT(extensions);
299
300    m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFrameBuffer);
301
302    m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleRenderBuffer);
303    extensions->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, Extensions3D::RGBA8_OES, newContextSize.width(), newContextSize.height());
304    m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleRenderBuffer);
305
306    m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleDepthBuffer);
307    extensions->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, GraphicsContext3D::DEPTH_COMPONENT16, newContextSize.width(), newContextSize.height());
308    m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_multisampleDepthBuffer);
309
310    m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
311
312    if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) {
313        deleteMultisampleRenderBuffers();
314        return false;
315    }
316
317    return true;
318}
319
320bool FECustomFilter::resizeContextIfNeeded(const IntSize& newContextSize)
321{
322    if (newContextSize.isEmpty())
323        return false;
324    if (m_contextSize == newContextSize)
325        return true;
326
327    int maxTextureSize = 0;
328    m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize);
329    if (newContextSize.height() > maxTextureSize || newContextSize.width() > maxTextureSize)
330        return false;
331
332    return resizeContext(newContextSize);
333}
334
335bool FECustomFilter::resizeContext(const IntSize& newContextSize)
336{
337    bool multisample = resizeMultisampleBuffers(newContextSize);
338
339    m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_frameBuffer);
340    m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_destTexture);
341    // We are going to clear the output buffer anyway, so we can safely initialize the destination texture with garbage data.
342    m_context->texImage2DDirect(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, newContextSize.width(), newContextSize.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, 0);
343    m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_destTexture, 0);
344
345    // We don't need the depth buffer for the texture framebuffer, if we already
346    // have a multisample buffer.
347    if (!multisample) {
348        m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
349        m_context->renderbufferStorage(GraphicsContext3D::RENDERBUFFER, GraphicsContext3D::DEPTH_COMPONENT16, newContextSize.width(), newContextSize.height());
350        m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::DEPTH_ATTACHMENT, GraphicsContext3D::RENDERBUFFER, m_depthBuffer);
351    }
352
353    if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE)
354        return false;
355
356    if (multisample) {
357        // Clear the framebuffer first, otherwise the first blit will fail.
358        m_context->clearColor(0, 0, 0, 0);
359        m_context->clear(GraphicsContext3D::COLOR_BUFFER_BIT);
360    }
361
362    m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, 0);
363
364    m_contextSize = newContextSize;
365    return true;
366}
367
368void FECustomFilter::dump()
369{
370}
371
372TextStream& FECustomFilter::externalRepresentation(TextStream& ts, int indent) const
373{
374    writeIndent(ts, indent);
375    ts << "[feCustomFilter";
376    FilterEffect::externalRepresentation(ts);
377    ts << "]\n";
378    inputEffect(0)->externalRepresentation(ts, indent + 1);
379    return ts;
380}
381
382} // namespace WebCore
383
384#endif // ENABLE(CSS_SHADERS) && USE(3D_GRAPHICS)
385