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