Home | History | Annotate | Download | only in custom
      1 /*
      2  * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
      3  * Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved.
      4  * Copyright (C) 2012 Company 100, 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/CustomFilterRenderer.h"
     35 
     36 #include "core/platform/graphics/GraphicsContext3D.h"
     37 #include "core/platform/graphics/filters/custom/CustomFilterArrayParameter.h"
     38 #include "core/platform/graphics/filters/custom/CustomFilterCompiledProgram.h"
     39 #include "core/platform/graphics/filters/custom/CustomFilterConstants.h"
     40 #include "core/platform/graphics/filters/custom/CustomFilterMesh.h"
     41 #include "core/platform/graphics/filters/custom/CustomFilterNumberParameter.h"
     42 #include "core/platform/graphics/filters/custom/CustomFilterParameter.h"
     43 #include "core/platform/graphics/filters/custom/CustomFilterTransformParameter.h"
     44 #include "core/platform/graphics/transforms/TransformationMatrix.h"
     45 
     46 namespace WebCore {
     47 
     48 static void orthogonalProjectionMatrix(TransformationMatrix& matrix, float left, float right, float bottom, float top)
     49 {
     50     ASSERT(matrix.isIdentity());
     51 
     52     float deltaX = right - left;
     53     float deltaY = top - bottom;
     54     if (!deltaX || !deltaY)
     55         return;
     56     matrix.setM11(2.0f / deltaX);
     57     matrix.setM41(-(right + left) / deltaX);
     58     matrix.setM22(2.0f / deltaY);
     59     matrix.setM42(-(top + bottom) / deltaY);
     60 
     61     // Use big enough near/far values, so that simple rotations of rather large objects will not
     62     // get clipped. 10000 should cover most of the screen resolutions.
     63     const float farValue = 10000;
     64     const float nearValue = -10000;
     65     matrix.setM33(-2.0f / (farValue - nearValue));
     66     matrix.setM43(- (farValue + nearValue) / (farValue - nearValue));
     67     matrix.setM44(1.0f);
     68 }
     69 
     70 PassRefPtr<CustomFilterRenderer> CustomFilterRenderer::create(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters,
     71     unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
     72 {
     73     return adoptRef(new CustomFilterRenderer(context, programType, parameters, meshRows, meshColumns, meshType));
     74 }
     75 
     76 CustomFilterRenderer::CustomFilterRenderer(PassRefPtr<GraphicsContext3D> context, CustomFilterProgramType programType, const CustomFilterParameterList& parameters,
     77     unsigned meshRows, unsigned meshColumns, CustomFilterMeshType meshType)
     78     : m_context(context)
     79     , m_programType(programType)
     80     , m_parameters(parameters)
     81     , m_meshRows(meshRows)
     82     , m_meshColumns(meshColumns)
     83     , m_meshType(meshType)
     84 {
     85 }
     86 
     87 CustomFilterRenderer::~CustomFilterRenderer()
     88 {
     89 }
     90 
     91 bool CustomFilterRenderer::premultipliedAlpha() const
     92 {
     93     return m_programType == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE;
     94 }
     95 
     96 bool CustomFilterRenderer::programNeedsInputTexture() const
     97 {
     98     ASSERT(m_compiledProgram.get());
     99     return m_compiledProgram->samplerLocation() != -1;
    100 }
    101 
    102 void CustomFilterRenderer::draw(Platform3DObject inputTexture, const IntSize& size)
    103 {
    104     // FIXME: We would need something like CustomFilterRendererState that will contain the size and other parameters in the future. We should pass that to bindProgramBuffers instead of storing it.
    105     // https://bugs.webkit.org/show_bug.cgi?id=100107
    106     m_contextSize = size;
    107 
    108     bindProgramAndBuffers(inputTexture);
    109     m_context->drawElements(GraphicsContext3D::TRIANGLES, m_mesh->indicesCount(), GraphicsContext3D::UNSIGNED_SHORT, 0);
    110     unbindVertexAttributes();
    111 }
    112 
    113 void CustomFilterRenderer::setCompiledProgram(PassRefPtr<CustomFilterCompiledProgram> compiledProgram)
    114 {
    115     m_compiledProgram = compiledProgram;
    116 }
    117 
    118 bool CustomFilterRenderer::prepareForDrawing()
    119 {
    120     m_context->makeContextCurrent();
    121     if (!m_compiledProgram || !m_compiledProgram->isInitialized())
    122         return false;
    123     initializeMeshIfNeeded();
    124     return true;
    125 }
    126 
    127 void CustomFilterRenderer::initializeMeshIfNeeded()
    128 {
    129     if (m_mesh.get())
    130         return;
    131 
    132     // FIXME: Sharing the mesh would just save the time needed to upload it to the GPU, so I assume we could
    133     // benchmark that for performance.
    134     // https://bugs.webkit.org/show_bug.cgi?id=88429
    135     m_mesh = CustomFilterMesh::create(m_context.get(), m_meshColumns, m_meshRows, FloatRect(0, 0, 1, 1), m_meshType);
    136 }
    137 
    138 void CustomFilterRenderer::bindVertexAttribute(int attributeLocation, unsigned size, unsigned offset)
    139 {
    140     if (attributeLocation != -1) {
    141         m_context->vertexAttribPointer(attributeLocation, size, GraphicsContext3D::FLOAT, false, m_mesh->bytesPerVertex(), offset);
    142         m_context->enableVertexAttribArray(attributeLocation);
    143     }
    144 }
    145 
    146 void CustomFilterRenderer::unbindVertexAttribute(int attributeLocation)
    147 {
    148     if (attributeLocation != -1)
    149         m_context->disableVertexAttribArray(attributeLocation);
    150 }
    151 
    152 void CustomFilterRenderer::bindProgramArrayParameters(int uniformLocation, CustomFilterArrayParameter* arrayParameter)
    153 {
    154     unsigned parameterSize = arrayParameter->size();
    155     Vector<GC3Dfloat> floatVector;
    156 
    157     for (unsigned i = 0; i < parameterSize; ++i)
    158         floatVector.append(arrayParameter->valueAt(i));
    159 
    160     m_context->uniform1fv(uniformLocation, parameterSize, floatVector.data());
    161 }
    162 
    163 void CustomFilterRenderer::bindProgramNumberParameters(int uniformLocation, CustomFilterNumberParameter* numberParameter)
    164 {
    165     switch (numberParameter->size()) {
    166     case 1:
    167         m_context->uniform1f(uniformLocation, numberParameter->valueAt(0));
    168         break;
    169     case 2:
    170         m_context->uniform2f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1));
    171         break;
    172     case 3:
    173         m_context->uniform3f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2));
    174         break;
    175     case 4:
    176         m_context->uniform4f(uniformLocation, numberParameter->valueAt(0), numberParameter->valueAt(1), numberParameter->valueAt(2), numberParameter->valueAt(3));
    177         break;
    178     default:
    179         ASSERT_NOT_REACHED();
    180     }
    181 }
    182 
    183 void CustomFilterRenderer::bindProgramTransformParameter(int uniformLocation, CustomFilterTransformParameter* transformParameter)
    184 {
    185     TransformationMatrix matrix;
    186     if (m_contextSize.width() && m_contextSize.height()) {
    187         // The viewport is a box with the size of 1 unit, so we are scaling up here to make sure that translations happen using real pixel
    188         // units. At the end we scale back down in order to map it back to the original box. Note that transforms come in reverse order, because it is
    189         // supposed to multiply to the left of the coordinates of the vertices.
    190         // Note that the origin (0, 0) of the viewport is in the middle of the context, so there's no need to change the origin of the transform
    191         // in order to rotate around the middle of mesh.
    192         matrix.scale3d(1.0 / m_contextSize.width(), 1.0 / m_contextSize.height(), 1);
    193         transformParameter->applyTransform(matrix, m_contextSize);
    194         matrix.scale3d(m_contextSize.width(), m_contextSize.height(), 1);
    195     }
    196     float glMatrix[16];
    197     matrix.toColumnMajorFloatArray(glMatrix);
    198     m_context->uniformMatrix4fv(uniformLocation, 1, false, &glMatrix[0]);
    199 }
    200 
    201 void CustomFilterRenderer::bindProgramParameters()
    202 {
    203     // FIXME: Find a way to reset uniforms that are not specified in CSS. This is needed to avoid using values
    204     // set by other previous rendered filters.
    205     // https://bugs.webkit.org/show_bug.cgi?id=76440
    206 
    207     size_t parametersSize = m_parameters.size();
    208     for (size_t i = 0; i < parametersSize; ++i) {
    209         CustomFilterParameter* parameter = m_parameters.at(i).get();
    210         int uniformLocation = m_compiledProgram->uniformLocationByName(parameter->name());
    211         if (uniformLocation == -1)
    212             continue;
    213         switch (parameter->parameterType()) {
    214         case CustomFilterParameter::ARRAY:
    215             bindProgramArrayParameters(uniformLocation, static_cast<CustomFilterArrayParameter*>(parameter));
    216             break;
    217         case CustomFilterParameter::NUMBER:
    218             bindProgramNumberParameters(uniformLocation, static_cast<CustomFilterNumberParameter*>(parameter));
    219             break;
    220         case CustomFilterParameter::TRANSFORM:
    221             bindProgramTransformParameter(uniformLocation, static_cast<CustomFilterTransformParameter*>(parameter));
    222             break;
    223         }
    224     }
    225 }
    226 
    227 void CustomFilterRenderer::bindProgramAndBuffers(Platform3DObject inputTexture)
    228 {
    229     ASSERT(m_compiledProgram->isInitialized());
    230 
    231     m_context->useProgram(m_compiledProgram->program());
    232 
    233     if (programNeedsInputTexture()) {
    234         // We should be binding the DOM element texture sampler only if the author is using the CSS mix function.
    235         ASSERT(m_programType == PROGRAM_TYPE_BLENDS_ELEMENT_TEXTURE);
    236         ASSERT(m_compiledProgram->samplerLocation() != -1);
    237 
    238         m_context->activeTexture(GraphicsContext3D::TEXTURE0);
    239         m_context->uniform1i(m_compiledProgram->samplerLocation(), 0);
    240         m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, inputTexture);
    241         m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR);
    242         m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR);
    243         m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE);
    244         m_context->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE);
    245     }
    246 
    247     if (m_compiledProgram->projectionMatrixLocation() != -1) {
    248         TransformationMatrix projectionMatrix;
    249         orthogonalProjectionMatrix(projectionMatrix, -0.5, 0.5, -0.5, 0.5);
    250         float glProjectionMatrix[16];
    251         projectionMatrix.toColumnMajorFloatArray(glProjectionMatrix);
    252         m_context->uniformMatrix4fv(m_compiledProgram->projectionMatrixLocation(), 1, false, &glProjectionMatrix[0]);
    253     }
    254 
    255     ASSERT(m_meshColumns);
    256     ASSERT(m_meshRows);
    257 
    258     if (m_compiledProgram->meshSizeLocation() != -1)
    259         m_context->uniform2f(m_compiledProgram->meshSizeLocation(), m_meshColumns, m_meshRows);
    260 
    261     if (m_compiledProgram->tileSizeLocation() != -1)
    262         m_context->uniform2f(m_compiledProgram->tileSizeLocation(), 1.0 / m_meshColumns, 1.0 / m_meshRows);
    263 
    264     if (m_compiledProgram->meshBoxLocation() != -1) {
    265         // FIXME: This will change when filter margins will be implemented,
    266         // see https://bugs.webkit.org/show_bug.cgi?id=71400
    267         m_context->uniform4f(m_compiledProgram->meshBoxLocation(), -0.5, -0.5, 1.0, 1.0);
    268     }
    269 
    270     if (m_compiledProgram->samplerSizeLocation() != -1)
    271         m_context->uniform2f(m_compiledProgram->samplerSizeLocation(), m_contextSize.width(), m_contextSize.height());
    272 
    273     m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_mesh->verticesBufferObject());
    274     m_context->bindBuffer(GraphicsContext3D::ELEMENT_ARRAY_BUFFER, m_mesh->elementsBufferObject());
    275 
    276     bindVertexAttribute(m_compiledProgram->positionAttribLocation(), PositionAttribSize, PositionAttribOffset);
    277     bindVertexAttribute(m_compiledProgram->texAttribLocation(), TexAttribSize, TexAttribOffset);
    278     bindVertexAttribute(m_compiledProgram->meshAttribLocation(), MeshAttribSize, MeshAttribOffset);
    279     if (m_meshType == MeshTypeDetached)
    280         bindVertexAttribute(m_compiledProgram->triangleAttribLocation(), TriangleAttribSize, TriangleAttribOffset);
    281 
    282     bindProgramParameters();
    283 }
    284 
    285 void CustomFilterRenderer::unbindVertexAttributes()
    286 {
    287     unbindVertexAttribute(m_compiledProgram->positionAttribLocation());
    288     unbindVertexAttribute(m_compiledProgram->texAttribLocation());
    289     unbindVertexAttribute(m_compiledProgram->meshAttribLocation());
    290     if (m_meshType == MeshTypeDetached)
    291         unbindVertexAttribute(m_compiledProgram->triangleAttribLocation());
    292 }
    293 
    294 } // namespace WebCore
    295