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