1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/* 2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc. 3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * 4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be 5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file. 6f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com */ 7f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 8ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com 9f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com#ifndef GrGLProgram_DEFINED 10f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com#define GrGLProgram_DEFINED 11f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 12d8f856c32b679d9f5a9926feac005e2c0186f83ftomhudson@google.com#include "GrDrawState.h" 136177e6999d23a4268ffd98dedfb1da00e272a89brobertphillips@google.com#include "GrGLContext.h" 1431ec7985f2b52a0cab4aa714a613b918cf663c08bsalomon@google.com#include "GrGLProgramDesc.h" 155a02cb48fdff04dc274d4cb1af8c4dc65a503438commit-bot@chromium.org#include "GrGLShaderBuilder.h" 16086e5354fe7ae60e69c42bdfbc3d03bd8559b44ftomhudson@google.com#include "GrGLSL.h" 17890e3b58e78c0825820f75f1f0c5a5d71e855aa6bsalomon@google.com#include "GrGLTexture.h" 18dbbc4e2da93cef5c0cfb0b3c92ff6c2c80f6e67absalomon@google.com#include "GrGLUniformManager.h" 19f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 20f0a104e6f16dc095286d32f1e104894ae0b2b19fbsalomon@google.com#include "SkString.h" 2197c88c255cff3dbb8343c5d090526fdbedad6dd6Scroggo#include "SkXfermode.h" 2297c88c255cff3dbb8343c5d090526fdbedad6dd6Scroggo 23f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.comclass GrBinHashKeyBuilder; 24d698f77c13d97c61109b861eac4d25b14a5de935bsalomon@google.comclass GrGLEffect; 253390b9ac9ad69a6e772c2b957d75d19611239025commit-bot@chromium.orgclass GrGLProgramEffects; 26f9ad8867f2bcd8563862b0a5a90b473ad020d465tomhudson@google.comclass GrGLShaderBuilder; 27f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 28f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com/** 29f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * This class manages a GPU program and records per-program information. 30f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * We can specify the attribute locations so that they are constant 31f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * across our shaders. But the driver determines the uniform locations 32f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * at link time. We don't need to remember the sampler uniform location 33f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * because we will bind a texture slot to it and never change it 34f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * Uniforms are program-local so we can't rely on fHWState to hold the 35f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com * previous uniform state after a program change. 36f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com */ 37a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.orgclass GrGLProgram : public SkRefCnt { 38f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.compublic: 399ba4fa6f0fb8ef496d81ccac36e780aa806fea83bsalomon@google.com SK_DECLARE_INST_COUNT(GrGLProgram) 404fa6694c587b3830932429766c99d08c8dd9b723bsalomon@google.com 419188a15f846ae79892c332aed2a72ee38116bdc6commit-bot@chromium.org static GrGLProgram* Create(GrGpuGL* gpu, 4231ec7985f2b52a0cab4aa714a613b918cf663c08bsalomon@google.com const GrGLProgramDesc& desc, 432c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com const GrEffectStage* colorStages[], 442c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com const GrEffectStage* coverageStages[]); 45ecb60aad5c6fe5b1dbcfc86ac00bfc9326103c8dbsalomon@google.com 469ba4fa6f0fb8ef496d81ccac36e780aa806fea83bsalomon@google.com virtual ~GrGLProgram(); 47f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 4834cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com /** 4934cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com * Call to abandon GL objects owned by this program. 5034cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com */ 51ecb60aad5c6fe5b1dbcfc86ac00bfc9326103c8dbsalomon@google.com void abandon(); 52ecb60aad5c6fe5b1dbcfc86ac00bfc9326103c8dbsalomon@google.com 53f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com /** 542db3ded335fdb6697623bece61cabc307a414770bsalomon@google.com * The shader may modify the blend coefficients. Params are in/out. 55f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com */ 56ecb60aad5c6fe5b1dbcfc86ac00bfc9326103c8dbsalomon@google.com void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const; 57f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 5831ec7985f2b52a0cab4aa714a613b918cf663c08bsalomon@google.com const GrGLProgramDesc& getDesc() { return fDesc; } 599ba4fa6f0fb8ef496d81ccac36e780aa806fea83bsalomon@google.com 60271cffc77bd2fcb3458559e509634442517ca1e9bsalomon@google.com /** 616a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com * Gets the GL program ID for this program. 626a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com */ 63a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org GrGLuint programID() const { return fBuilderOutput.fProgramID; } 646a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com 65a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org bool hasVertexShader() const { return fBuilderOutput.fHasVertexShader; } 666b30e457409f37c91c301cd82040e733e2930286commit-bot@chromium.org 676a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com /** 68054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com * Some GL state that is relevant to programs is not stored per-program. In particular color 6905a2ee052c9ef4c781b7b590b00b3d2da3b3449askia.committer@gmail.com * and coverage attributes can be global state. This struct is read and updated by 7005a2ee052c9ef4c781b7b590b00b3d2da3b3449askia.committer@gmail.com * GrGLProgram::setColor and GrGLProgram::setCoverage to allow us to avoid setting this state 71054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com * redundantly. 7291207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com */ 7391207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com struct SharedGLState { 7491207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com GrColor fConstAttribColor; 75054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com int fConstAttribColorIndex; 7691207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com GrColor fConstAttribCoverage; 77054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com int fConstAttribCoverageIndex; 7891207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com 7991207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com SharedGLState() { this->invalidate(); } 8091207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com void invalidate() { 8191207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com fConstAttribColor = GrColor_ILLEGAL; 82054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com fConstAttribColorIndex = -1; 8391207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com fConstAttribCoverage = GrColor_ILLEGAL; 84054ae99d93711c26e40682a0e3a03a47ea605c53jvanverth@google.com fConstAttribCoverageIndex = -1; 8591207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com } 8691207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com }; 8791207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com 8891207482c9398944fc997aeb99ed5f8674be58cbbsalomon@google.com /** 896a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com * The GrDrawState's view matrix along with the aspects of the render target determine the 906a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com * matrix sent to GL. The size of the render target affects the GL matrix because we must 916a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com * convert from Skia device coords to GL's normalized coords. Also the origin of the render 926a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com * target may require us to perform a mirror-flip. 936a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com */ 946a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com struct MatrixState { 956a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com SkMatrix fViewMatrix; 966a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com SkISize fRenderTargetSize; 976a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com GrSurfaceOrigin fRenderTargetOrigin; 986a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com 996a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com MatrixState() { this->invalidate(); } 1006a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com void invalidate() { 1016a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com fViewMatrix = SkMatrix::InvalidMatrix(); 10245a412ee365694c61b50f6177382b509d1e2462bbsalomon@google.com fRenderTargetSize.fWidth = -1; 10345a412ee365694c61b50f6177382b509d1e2462bbsalomon@google.com fRenderTargetSize.fHeight = -1; 1046a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com fRenderTargetOrigin = (GrSurfaceOrigin) -1; 1056a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com } 10647c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org 10747c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org /** 10847c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * Gets a matrix that goes from local coords to Skia's device coordinates. 10947c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org */ 110215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org template<int Size> void getGLMatrix(GrGLfloat* destMatrix) { 11147c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org GrGLGetMatrix<Size>(destMatrix, fViewMatrix); 11247c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org } 11347c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org 11447c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org /** 11547c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * Gets a matrix that goes from local coordinates to GL normalized device coords. 11647c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org */ 11747c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org template<int Size> void getRTAdjustedGLMatrix(GrGLfloat* destMatrix) { 118215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org SkMatrix combined; 119215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org if (kBottomLeft_GrSurfaceOrigin == fRenderTargetOrigin) { 120215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org combined.setAll(SkIntToScalar(2) / fRenderTargetSize.fWidth, 0, -SK_Scalar1, 121215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org 0, -SkIntToScalar(2) / fRenderTargetSize.fHeight, SK_Scalar1, 12247c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org 0, 0, 1); 123215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org } else { 124215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org combined.setAll(SkIntToScalar(2) / fRenderTargetSize.fWidth, 0, -SK_Scalar1, 125215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org 0, SkIntToScalar(2) / fRenderTargetSize.fHeight, -SK_Scalar1, 12647c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org 0, 0, 1); 127215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org } 128b930cc3dcb0645a8ce0689c5feb9c0defda57f28commit-bot@chromium.org combined.preConcat(fViewMatrix); 129215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org GrGLGetMatrix<Size>(destMatrix, combined); 130215a682d2d561be69b7a28eb76a98849ad03cbc0commit-bot@chromium.org } 13147c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org 13247c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org /** 13347c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * Gets a vec4 that adjusts the position from Skia device coords to GL's normalized device 13447c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * coords. Assuming the transformed position, pos, is a homogeneous vec3, the vec, v, is 13547c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * applied as such: 13647c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * pos.x = dot(v.xy, pos.xz) 13747c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org * pos.y = dot(v.zq, pos.yz) 13847c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org */ 13947c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org void getRTAdjustmentVec(GrGLfloat* destVec) { 14047c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[0] = 2.f / fRenderTargetSize.fWidth; 14147c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[1] = -1.f; 14247c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org if (kBottomLeft_GrSurfaceOrigin == fRenderTargetOrigin) { 14347c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[2] = -2.f / fRenderTargetSize.fHeight; 14447c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[3] = 1.f; 14547c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org } else { 14647c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[2] = 2.f / fRenderTargetSize.fHeight; 14747c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org destVec[3] = -1.f; 14847c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org } 14947c66ddaeb48faf963a2ef3f508a7d816e4168cccommit-bot@chromium.org } 1506a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com }; 1516a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com 1526a51dcbf81cff6d92996ab3f4c7457478e441896bsalomon@google.com /** 15334cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com * This function uploads uniforms and calls each GrGLEffect's setData. It is called before a 15434cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com * draw occurs using the program after the program has already been bound. It also uses the 1552c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com * GrGpuGL object to bind the textures required by the GrGLEffects. The color and coverage 1562c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com * stages come from GrGLProgramDesc::Build(). 1574285accf5af574e6c826d5d09f0359c6149fd717bsalomon@google.com */ 1589188a15f846ae79892c332aed2a72ee38116bdc6commit-bot@chromium.org void setData(GrDrawState::BlendOptFlags, 1592c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com const GrEffectStage* colorStages[], 1602c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com const GrEffectStage* coverageStages[], 16126e18b593ab65e4d92dfbce92579d8bc180d4c2cbsalomon@google.com const GrDeviceCoordTexture* dstCopy, // can be NULL 16226e18b593ab65e4d92dfbce92579d8bc180d4c2cbsalomon@google.com SharedGLState*); 1639196130af83782fcac4334117142475a837dd74dbsalomon@google.com 1642a2e3ef5b84de3347aedaf1f7dd93cfcdb53d7f1tomhudson@google.comprivate: 165dbbc4e2da93cef5c0cfb0b3c92ff6c2c80f6e67absalomon@google.com typedef GrGLUniformManager::UniformHandle UniformHandle; 166dbbc4e2da93cef5c0cfb0b3c92ff6c2c80f6e67absalomon@google.com 167a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org GrGLProgram(GrGpuGL*, 168a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org const GrGLProgramDesc&, 169a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org GrGLUniformManager*, 170a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org const GrGLShaderBuilder::GenProgramOutput&); 1712c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com 172a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org // Sets the texture units for samplers. 1732c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com void initSamplerUniforms(); 1742c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com 1752c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com // Helper for setData(). Makes GL calls to specify the initial color when there is not 1762c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com // per-vertex colors. 1772c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com void setColor(const GrDrawState&, GrColor color, SharedGLState*); 1782c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com 1792c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com // Helper for setData(). Makes GL calls to specify the initial coverage when there is not 1802c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com // per-vertex coverages. 1812c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com void setCoverage(const GrDrawState&, GrColor coverage, SharedGLState*); 1822c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com 1832c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com // Helper for setData() that sets the view matrix and loads the render target height uniform 1842c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com void setMatrixAndRenderTargetHeight(const GrDrawState&); 1852c84aa35988c661b3e5513c8ba9b3959832ff288bsalomon@google.com 18634cccde630fc618649b9737bee464203d042bfbbbsalomon@google.com // these reflect the current values of uniforms (GL uniform values travel with program) 1876eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org MatrixState fMatrixState; 1886eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org GrColor fColor; 1896eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org GrColor fCoverage; 1906eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org int fDstCopyTexUnit; 1916eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org 192a05fa0669bac20e521ac3c1905fe8391fd659e60commit-bot@chromium.org GrGLShaderBuilder::GenProgramOutput fBuilderOutput; 1939681eebb0e441cee25b6faac82c3728512acda27skia.committer@gmail.com 1946eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org GrGLProgramDesc fDesc; 1956eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org GrGpuGL* fGpu; 1966eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org 1976eac42e3ab68b085117d7f91621276a722b5a3a7commit-bot@chromium.org SkAutoTUnref<GrGLUniformManager> fUniformManager; 1986b30e457409f37c91c301cd82040e733e2930286commit-bot@chromium.org 199a4de8c257ea0be8ff7081f645249b6afe5c48e7ecommit-bot@chromium.org typedef SkRefCnt INHERITED; 200f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com}; 201f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com 202f93e717c7f7ca679a80acbfda6a34013ae1e2b8djunov@google.com#endif 203