1 /*
   2  * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.prism.impl.ps;
  27 
  28 import com.sun.glass.ui.Screen;
  29 import com.sun.javafx.geom.Rectangle;
  30 import com.sun.javafx.geom.transform.Affine3D;
  31 import com.sun.javafx.geom.transform.BaseTransform;
  32 import com.sun.javafx.sg.prism.NGCamera;
  33 import com.sun.prism.CompositeMode;
  34 import com.sun.prism.PixelFormat;
  35 import com.sun.prism.RTTexture;
  36 import com.sun.prism.RenderTarget;
  37 import com.sun.prism.ResourceFactory;
  38 import com.sun.prism.Texture;
  39 import com.sun.prism.impl.BaseContext;
  40 import com.sun.prism.impl.BaseGraphics;
  41 import com.sun.prism.impl.VertexBuffer;
  42 import com.sun.prism.paint.Color;
  43 import com.sun.prism.paint.Gradient;
  44 import com.sun.prism.paint.ImagePattern;
  45 import com.sun.prism.paint.LinearGradient;
  46 import com.sun.prism.paint.Paint;
  47 import com.sun.prism.paint.RadialGradient;
  48 import com.sun.prism.ps.Shader;
  49 import com.sun.prism.ps.ShaderFactory;
  50 
  51 /**
  52  * Maintains resources such as Shaders and GlyphCaches that are intended to
  53  * be cached on a per-Screen basis, and provides methods that are called by
  54  * BaseShaderGraphics to validate current state.  The inner State class is
  55  * used to encapsulate the current and previously validated state (such as
  56  * texture bindings) so that the validation routines can avoid redundant
  57  * state changes.  There should be only one BaseShaderContext per Screen,
  58  * however there may be one or more State instances per BaseShaderContext.
  59  * <p>
  60  * A note about State objects... The JOGL architecture creates a GLContext
  61  * for each GLDrawable (one GLContext per GLDrawable, and one GLDrawable
  62  * per onscreen window).  Resources such as textures and shaders can be
  63  * shared between those GLContext instances, but other state (texture bindings,
  64  * scissor rect, etc) cannot be shared.  Therefore we need to maintain
  65  * one State instance per GLContext instance, which means there may be more
  66  * than one State instance per BaseShaderContext.  The currentState variable
  67  * holds the current State instance corresponding to the current RenderTarget,
  68  * and is revalidated as part of the updateRenderTarget() method.  The ES2
  69  * backend will create a new State instance for each window, but the D3D
  70  * backend is free to create a single State instance that can be shared for
  71  * the entire Screen.
  72  */
  73 public abstract class BaseShaderContext extends BaseContext {
  74     private static final int CHECK_SHADER    = (0x01     );
  75     private static final int CHECK_TRANSFORM = (0x01 << 1);
  76     private static final int CHECK_CLIP      = (0x01 << 2);
  77     private static final int CHECK_COMPOSITE = (0x01 << 3);
  78     private static final int CHECK_PAINT_OP_MASK =
  79         (CHECK_SHADER | CHECK_TRANSFORM | CHECK_CLIP | CHECK_COMPOSITE);
  80     private static final int CHECK_TEXTURE_OP_MASK =
  81         (CHECK_SHADER | CHECK_TRANSFORM | CHECK_CLIP | CHECK_COMPOSITE);
  82     private static final int CHECK_CLEAR_OP_MASK =
  83         (CHECK_CLIP);
  84 
  85     public enum MaskType {
  86         SOLID          ("Solid"),
  87         TEXTURE        ("Texture"),
  88         ALPHA_ONE           ("AlphaOne", true),
  89         ALPHA_TEXTURE       ("AlphaTexture", true),
  90         ALPHA_TEXTURE_DIFF  ("AlphaTextureDifference", true),
  91         FILL_PGRAM     ("FillPgram"),
  92         DRAW_PGRAM     ("DrawPgram", FILL_PGRAM),
  93         FILL_CIRCLE    ("FillCircle"),
  94         DRAW_CIRCLE    ("DrawCircle", FILL_CIRCLE),
  95         FILL_ELLIPSE   ("FillEllipse"),
  96         DRAW_ELLIPSE   ("DrawEllipse", FILL_ELLIPSE),
  97         FILL_ROUNDRECT ("FillRoundRect"),
  98         DRAW_ROUNDRECT ("DrawRoundRect", FILL_ROUNDRECT),
  99         DRAW_SEMIROUNDRECT("DrawSemiRoundRect"),
 100         FILL_CUBICCURVE("FillCubicCurve");
 101 
 102         private String name;
 103         private MaskType filltype;
 104         private boolean newPaintStyle;
 105         private MaskType(String name) {
 106             this.name = name;
 107         }
 108         private MaskType(String name, boolean newstyle) {
 109             this.name = name;
 110             this.newPaintStyle = newstyle;
 111         }
 112         private MaskType(String name, MaskType filltype) {
 113             this.name = name;
 114             this.filltype = filltype;
 115         }
 116         public String getName() {
 117             return name;
 118         }
 119         public MaskType getFillType() {
 120             return filltype;
 121         }
 122         public boolean isNewPaintStyle() {
 123             return newPaintStyle;
 124         }
 125     }
 126 
 127     // mask type     4 bits (12 types)
 128     // paint type    2 bits
 129     // paint opts    2 bits
 130     private static final int NUM_STOCK_SHADER_SLOTS =
 131         MaskType.values().length << 4;
 132     // TODO: need to dispose these when the context is disposed... (RT-27379)
 133     private final Shader[] stockShaders = new Shader[NUM_STOCK_SHADER_SLOTS];
 134     // stockShaders with alpha test
 135     private final Shader[] stockATShaders = new Shader[NUM_STOCK_SHADER_SLOTS];
 136 
 137     public enum SpecialShaderType {
 138         TEXTURE_RGB          ("Solid_TextureRGB"),
 139         TEXTURE_MASK_RGB     ("Mask_TextureRGB"),
 140         TEXTURE_YV12         ("Solid_TextureYV12"),
 141         TEXTURE_First_LCD    ("Solid_TextureFirstPassLCD"),
 142         TEXTURE_SECOND_LCD   ("Solid_TextureSecondPassLCD"),
 143         SUPER                ("Mask_TextureSuper");
 144          
 145         private String name;
 146         private SpecialShaderType(String name) {
 147             this.name = name;
 148         }
 149         public String getName() {
 150             return name;
 151         }
 152     }
 153     private final Shader[] specialShaders = new Shader[SpecialShaderType.values().length];
 154     // specialShaders with alpha test
 155     private final Shader[] specialATShaders = new Shader[SpecialShaderType.values().length];
 156     
 157     private Shader externalShader;
 158 
 159     private RTTexture lcdBuffer;
 160     private final ShaderFactory factory;
 161 
 162     private State state;
 163 
 164     protected BaseShaderContext(Screen screen, ShaderFactory factory, VertexBuffer vb) {
 165         super(screen, factory, vb);
 166         this.factory = factory;
 167         init();
 168     }
 169 
 170     protected void init() {
 171         state = null;
 172         if (externalShader != null && !externalShader.isValid()) {
 173             externalShader.dispose();
 174             externalShader = null;
 175         }
 176         // the rest of the shaders will be re-validated as they are used
 177    }
 178 
 179     public static class State {
 180         private Shader lastShader;
 181         private RenderTarget lastRenderTarget;
 182         private NGCamera lastCamera;
 183         private boolean lastDepthTest;
 184         private BaseTransform lastTransform = new Affine3D();
 185         private Rectangle lastClip;
 186         private CompositeMode lastComp;
 187         private Texture[] lastTextures = new Texture[4];
 188         private boolean isXformValid;
 189         private float lastConst1 = Float.NaN;
 190         private float lastConst2 = Float.NaN;
 191         private float lastConst3 = Float.NaN;
 192         private float lastConst4 = Float.NaN;
 193         private float lastConst5 = Float.NaN;
 194         private float lastConst6 = Float.NaN;
 195         private boolean lastState3D = false;
 196     }
 197 
 198     protected void resetLastClip(State state) {
 199         state.lastClip = null;
 200     }
 201 
 202     protected abstract State updateRenderTarget(RenderTarget target, NGCamera camera,
 203                                                 boolean depthTest);
 204 
 205     protected abstract void updateTexture(int texUnit, Texture tex);
 206 
 207     protected abstract void updateShaderTransform(Shader shader,
 208                                                   BaseTransform xform);
 209 
 210     protected abstract void updateWorldTransform(BaseTransform xform);
 211 
 212     protected abstract void updateClipRect(Rectangle clipRect);
 213 
 214     protected abstract void updateCompositeMode(CompositeMode mode);
 215 
 216     private static int getStockShaderIndex(MaskType maskType, Paint paint) {
 217         int paintType;
 218         int paintOption;
 219         if (paint == null) {
 220             paintType = 0;
 221             paintOption = 0;
 222         } else {
 223             paintType = paint.getType().ordinal();
 224             if (paint.getType().isGradient()) {
 225                 paintOption = ((Gradient)paint).getSpreadMethod();
 226             } else {
 227                 paintOption = 0;
 228             }
 229         }
 230         return (maskType.ordinal() << 4) | (paintType << 2) | (paintOption << 0);
 231     }
 232 
 233     private Shader getPaintShader(boolean alphaTest, MaskType maskType, Paint paint) {
 234         int index = getStockShaderIndex(maskType, paint);
 235         Shader shaders[] = alphaTest ? stockATShaders : stockShaders;
 236         Shader shader = shaders[index];
 237         if (shader != null && !shader.isValid()) {
 238             shader.dispose();
 239             shader = null;
 240         }
 241         if (shader == null) {
 242             String shaderName =
 243                 maskType.getName() + "_" + paint.getType().getName();
 244             if (paint.getType().isGradient() && !maskType.isNewPaintStyle()) {
 245                 Gradient grad = (Gradient) paint;
 246                 int spreadMethod = grad.getSpreadMethod();
 247                 if (spreadMethod == Gradient.PAD) {
 248                     shaderName += "_PAD";
 249                 } else if (spreadMethod == Gradient.REFLECT) {
 250                     shaderName += "_REFLECT";
 251                 } else if (spreadMethod == Gradient.REPEAT) {
 252                     shaderName += "_REPEAT";
 253                 }
 254             }
 255             if (alphaTest) {
 256                 shaderName += "_AlphaTest";
 257             }
 258             shader = shaders[index] = factory.createStockShader(shaderName);
 259         }
 260         return shader;
 261     }
 262 
 263     private void updatePaintShader(BaseShaderGraphics g, Shader shader,
 264                                    MaskType maskType, Paint paint,
 265                                    float bx, float by, float bw, float bh)
 266     {
 267         Paint.Type paintType = paint.getType();
 268         if (paintType == Paint.Type.COLOR || maskType.isNewPaintStyle()) {
 269             return;
 270         }
 271 
 272         float rx, ry, rw, rh;
 273         if (paint.isProportional()) {
 274             rx = bx; ry = by; rw = bw; rh = bh;
 275         } else {
 276             rx = 0f; ry = 0f; rw = 1f; rh = 1f;
 277         }
 278 
 279         switch (paintType) {
 280         case LINEAR_GRADIENT:
 281             PaintHelper.setLinearGradient(g, shader,
 282                                           (LinearGradient)paint,
 283                                           rx, ry, rw, rh);
 284             break;
 285         case RADIAL_GRADIENT:
 286             PaintHelper.setRadialGradient(g, shader,
 287                                           (RadialGradient)paint,
 288                                           rx, ry, rw, rh);
 289             break;
 290         case IMAGE_PATTERN:
 291             PaintHelper.setImagePattern(g, shader,
 292                                         (ImagePattern)paint,
 293                                         rx, ry, rw, rh);
 294         default:
 295             break;
 296         }
 297     }
 298 
 299     private Shader getSpecialShader(BaseGraphics g, SpecialShaderType sst) {
 300         // We do alpha test if depth test is enabled
 301         boolean alphaTest = g.isAlphaTestShader();
 302         Shader shaders[] = alphaTest ? specialATShaders : specialShaders;
 303         Shader shader = shaders[sst.ordinal()];
 304         if (shader != null && !shader.isValid()) {
 305             shader.dispose();
 306             shader = null;
 307         }
 308         if (shader == null) {
 309             String shaderName = sst.getName();
 310             if (alphaTest) {
 311                 shaderName += "_AlphaTest";
 312             }
 313             shaders[sst.ordinal()] = shader = factory.createStockShader(shaderName);
 314         }
 315         return shader;
 316     }
 317 
 318     @Override
 319     public boolean isSuperShaderEnabled() {
 320         return state.lastShader == specialATShaders[SpecialShaderType.SUPER.ordinal()]
 321                 || state.lastShader == specialShaders[SpecialShaderType.SUPER.ordinal()];
 322     }
 323 
 324     private void updatePerVertexColor(Paint paint, float extraAlpha) {
 325         if (paint != null && paint.getType() == Paint.Type.COLOR) {
 326             getVertexBuffer().setPerVertexColor((Color)paint, extraAlpha);
 327         } else {
 328             getVertexBuffer().setPerVertexColor(extraAlpha);
 329         }
 330     }
 331 
 332     @Override
 333     public void validateClearOp(BaseGraphics g) {
 334         checkState((BaseShaderGraphics) g, CHECK_CLEAR_OP_MASK, null, null);
 335     }
 336 
 337     @Override
 338     public void validatePaintOp(BaseGraphics g, BaseTransform xform,
 339                                 Texture maskTex,
 340                                 float bx, float by, float bw, float bh)
 341     {
 342         validatePaintOp((BaseShaderGraphics)g, xform,
 343                         maskTex, bx, by, bw, bh);
 344     }
 345 
 346     Shader validatePaintOp(BaseShaderGraphics g, BaseTransform xform,
 347                            MaskType maskType,
 348                            float bx, float by, float bw, float bh)
 349     {
 350         return validatePaintOp(g, xform, maskType, null, bx, by, bw, bh);
 351     }
 352 
 353     Shader validatePaintOp(BaseShaderGraphics g, BaseTransform xform,
 354                            MaskType maskType,
 355                            float bx, float by, float bw, float bh,
 356                            float k1, float k2, float k3, float k4, float k5, float k6)
 357     {
 358         // this is not ideal, but will have to do for now (tm).
 359         // various paint primitives use shader parameters, and we have to flush
 360         // the vertex buffer if those change.  Ideally we would do this in
 361         // checkState but there is no mechanism to pass this info through.
 362         if (state.lastConst1 != k1 || state.lastConst2 != k2 ||
 363             state.lastConst3 != k3 || state.lastConst4 != k4 ||
 364             state.lastConst5 != k5 || state.lastConst6 != k6)
 365         {
 366             flushVertexBuffer();
 367 
 368             state.lastConst1 = k1;
 369             state.lastConst2 = k2;
 370             state.lastConst3 = k3;
 371             state.lastConst4 = k4;
 372             state.lastConst5 = k5;
 373             state.lastConst6 = k6;
 374         }
 375 
 376         return validatePaintOp(g, xform, maskType, null, bx, by, bw, bh);
 377     }
 378 
 379     Shader validatePaintOp(BaseShaderGraphics g, BaseTransform xform,
 380                            MaskType maskType, Texture maskTex,
 381                            float bx, float by, float bw, float bh,
 382                            float k1, float k2, float k3, float k4, float k5, float k6)
 383     {
 384         // this is not ideal, but will have to do for now (tm).
 385         // various paint primitives use shader parameters, and we have to flush
 386         // the vertex buffer if those change.  Ideally we would do this in
 387         // checkState but there is no mechanism to pass this info through.
 388         if (state.lastConst1 != k1 || state.lastConst2 != k2 ||
 389             state.lastConst3 != k3 || state.lastConst4 != k4 ||
 390             state.lastConst5 != k5 || state.lastConst6 != k6)
 391         {
 392             flushVertexBuffer();
 393 
 394             state.lastConst1 = k1;
 395             state.lastConst2 = k2;
 396             state.lastConst3 = k3;
 397             state.lastConst4 = k4;
 398             state.lastConst5 = k5;
 399             state.lastConst6 = k6;
 400         }
 401 
 402         return validatePaintOp(g, xform, maskType, maskTex, bx, by, bw, bh);
 403     }
 404 
 405     Shader validatePaintOp(BaseShaderGraphics g, BaseTransform xform,
 406                            Texture maskTex,
 407                            float bx, float by, float bw, float bh)
 408     {
 409         return validatePaintOp(g, xform, MaskType.TEXTURE,
 410                                maskTex, bx, by, bw, bh);
 411     }
 412 
 413     Shader validatePaintOp(BaseShaderGraphics g, BaseTransform xform,
 414                            MaskType maskType, Texture maskTex,
 415                            float bx, float by, float bw, float bh)
 416     {
 417         if (maskType == null) {
 418             throw new InternalError("maskType must be non-null");
 419         }
 420 
 421         if (externalShader == null) {
 422             Paint paint = g.getPaint();
 423             Texture paintTex = null;
 424             Texture tex0;
 425             Texture tex1;
 426             if (paint.getType().isGradient()) {
 427                 // we need to flush here in case the paint shader is staying
 428                 // the same but the paint parameters are changing; we do this
 429                 // unconditionally for now (in theory we could keep track
 430                 // of the last validated paint, and the shape bounds in the
 431                 // case of proportional gradients, but the case where the
 432                 // same paint parameters are used multiple times in a row
 433                 // is so rare that it's not worth optimizing this any further)
 434                 flushVertexBuffer();
 435                 // we have to fetch the texture containing the gradient
 436                 // colors in advance since checkState() is responsible for
 437                 // binding the texture(s)
 438                 if (maskType.isNewPaintStyle()) {
 439                     paintTex = PaintHelper.getWrapGradientTexture(g);
 440                 } else {
 441                     paintTex = PaintHelper.getGradientTexture(g, (Gradient)paint);
 442                 }
 443             } else if (paint.getType() == Paint.Type.IMAGE_PATTERN) {
 444                 // We need to flush here. See comment above about paint parameters changing.
 445                 flushVertexBuffer();
 446                 ImagePattern texPaint = (ImagePattern)paint;
 447                 ResourceFactory rf = g.getResourceFactory();
 448                 paintTex = rf.getCachedTexture(texPaint.getImage(), Texture.WrapMode.REPEAT);
 449             }
 450             Shader shader;
 451             if (factory.isSuperShaderAllowed() &&
 452                 paintTex == null &&
 453                 maskTex == factory.getGlyphTexture())
 454             {
 455                 // Enabling the super shader to be used to render text.
 456                 // The texture pointed by tex0 is the region cache texture
 457                 // and it does not affect text rendering
 458                 shader = getSpecialShader(g, SpecialShaderType.SUPER);
 459                 tex0 = factory.getRegionTexture();
 460                 tex1 = maskTex;
 461             } else {
 462                 // NOTE: We are making assumptions here about which texture
 463                 // corresponds to which texture unit.  In a JSL file the
 464                 // first sampler mentioned will correspond to texture unit 0,
 465                 // the second sampler will correspond to texture unit 1,
 466                 // and so on, and there's currently no way to explicitly
 467                 // associate a sampler with a texture unit in the JSL file.
 468                 // So for now we assume that mask-related samplers are
 469                 // declared before any paint-related samplers in the
 470                 // composed JSL files.
 471                 if (maskTex != null) {
 472                     tex0 = maskTex;
 473                     tex1 = paintTex;
 474                 } else {
 475                     tex0 = paintTex;
 476                     tex1 = null;
 477                 }
 478                 // We do alpha test if depth test is enabled
 479                 shader = getPaintShader(g.isAlphaTestShader(), maskType, paint);
 480             }
 481             checkState(g, CHECK_PAINT_OP_MASK, xform, shader);
 482             setTexture(0, tex0);
 483             setTexture(1, tex1);
 484             updatePaintShader(g, shader, maskType, paint, bx, by, bw, bh);
 485             updatePerVertexColor(paint, g.getExtraAlpha());
 486             if (paintTex != null) paintTex.unlock();
 487             return shader;
 488         } else {
 489             // note that paint is assumed to be a simple Color in this case
 490             checkState(g, CHECK_PAINT_OP_MASK, xform, externalShader);
 491             setTexture(0, maskTex);
 492             setTexture(1, null);  // Needed?
 493             updatePerVertexColor(null, g.getExtraAlpha());
 494             return externalShader;
 495         }
 496     }
 497 
 498     @Override
 499     public void validateTextureOp(BaseGraphics g, BaseTransform xform,
 500                                   Texture tex0, PixelFormat format)
 501     {
 502         validateTextureOp((BaseShaderGraphics)g, xform, tex0, null, format);
 503     }
 504 
 505     //This function sets the first LCD sample shader.
 506     public Shader validateLCDOp(BaseShaderGraphics g, BaseTransform xform,
 507                                 Texture tex0, Texture tex1, boolean firstPass,
 508                                 Paint fillColor)
 509     {
 510         Shader shader = firstPass ? getSpecialShader(g, SpecialShaderType.TEXTURE_First_LCD) :
 511                                     getSpecialShader(g, SpecialShaderType.TEXTURE_SECOND_LCD);
 512 
 513         checkState(g, CHECK_TEXTURE_OP_MASK, xform, shader);
 514         setTexture(0, tex0);
 515         setTexture(1, tex1);
 516         updatePerVertexColor(fillColor, g.getExtraAlpha());
 517         return shader;
 518     }
 519 
 520     Shader validateTextureOp(BaseShaderGraphics g, BaseTransform xform,
 521                              Texture[] textures, PixelFormat format)
 522     {
 523         Shader shader;
 524 
 525         if (format == PixelFormat.MULTI_YCbCr_420) {
 526             // must have at least three textures, any more than four are ignored
 527             if (textures.length < 3) {
 528                 return null;
 529             }
 530 
 531             if (externalShader == null) {
 532                 shader = getSpecialShader(g, SpecialShaderType.TEXTURE_YV12);
 533             } else {
 534                 shader = externalShader;
 535             }
 536         } else { // add more multitexture shaders here
 537             return null;
 538         }
 539 
 540         if (null != shader) {
 541             checkState(g, CHECK_TEXTURE_OP_MASK, xform, shader);
 542             // clamp to 0..4 textures for now, expand on this later if we need to
 543             int texCount = Math.max(0, Math.min(textures.length, 4));
 544             for (int index = 0; index < texCount; index++) {
 545                 setTexture(index, textures[index]);
 546             }
 547             updatePerVertexColor(null, g.getExtraAlpha());
 548         }
 549         return shader;
 550     }
 551 
 552     Shader validateTextureOp(BaseShaderGraphics g, BaseTransform xform,
 553                              Texture tex0, Texture tex1, PixelFormat format)
 554     {
 555         Shader shader;
 556         if (externalShader == null) {
 557             switch (format) {
 558             case INT_ARGB_PRE:
 559             case BYTE_BGRA_PRE:
 560             case BYTE_RGB:
 561             case BYTE_GRAY:
 562             case BYTE_APPLE_422: // uses GL_RGBA as internal format
 563                 if (factory.isSuperShaderAllowed() &&
 564                     tex0 == factory.getRegionTexture() &&
 565                     tex1 == null)
 566                 {
 567                     // Enabling the super shader to be used for texture rendering.
 568                     // The shader was designed to render many Regions (from the Region
 569                     // texture cache) and text (from the glyph cache texture) without
 570                     // changing the state in the context.
 571                     shader = getSpecialShader(g, SpecialShaderType.SUPER);
 572                     tex1 = factory.getGlyphTexture();
 573                 } else {
 574                     shader = getSpecialShader(g, SpecialShaderType.TEXTURE_RGB);
 575                 }
 576                 break;
 577             case MULTI_YCbCr_420: // Must use multitexture method
 578             case BYTE_ALPHA:
 579             default:
 580                 throw new InternalError("Pixel format not supported: " + format);
 581             }
 582         } else {
 583             shader = externalShader;
 584         }
 585         checkState(g, CHECK_TEXTURE_OP_MASK, xform, shader);
 586         setTexture(0, tex0);
 587         setTexture(1, tex1);
 588         updatePerVertexColor(null, g.getExtraAlpha());
 589         return shader;
 590     }
 591 
 592     Shader validateMaskTextureOp(BaseShaderGraphics g, BaseTransform xform,
 593                                  Texture tex0, Texture tex1, PixelFormat format)
 594     {
 595         Shader shader;
 596         if (externalShader == null) {
 597             switch (format) {
 598             case INT_ARGB_PRE:
 599             case BYTE_BGRA_PRE:
 600             case BYTE_RGB:
 601             case BYTE_GRAY:
 602             case BYTE_APPLE_422: // uses GL_RGBA as internal format
 603                 shader = getSpecialShader(g, SpecialShaderType.TEXTURE_MASK_RGB);
 604                 break;
 605             case MULTI_YCbCr_420: // Must use multitexture method
 606             case BYTE_ALPHA:
 607             default:
 608                 throw new InternalError("Pixel format not supported: " + format);
 609             }
 610         } else {
 611             shader = externalShader;
 612         }
 613         checkState(g, CHECK_TEXTURE_OP_MASK, xform, shader);
 614         setTexture(0, tex0);
 615         setTexture(1, tex1);
 616         updatePerVertexColor(null, g.getExtraAlpha());
 617         return shader;
 618     }
 619 
 620     void setExternalShader(BaseShaderGraphics g, Shader shader) {
 621         // Note that this method is called when the user calls
 622         // ShaderGraphics.setExternalShader().  We flush any pending
 623         // operations and synchronously enable the given shader here
 624         // because the caller (i.e., decora-prism-ps peer) needs to be
 625         // able to call shader.setConstant() after calling setExternalShader().
 626         // (In the ES2 backend, setConstant() bottoms out in glUniform(),
 627         // which can only be called when the program is active, i.e., after
 628         // shader.enable() is called.  Kind of gross, but that's why the
 629         // external shader mechanism is setup the way it is currently.)
 630         // So here we enable the shader just so that the user can update
 631         // shader constants, and we set the externalShader instance variable.
 632         // Later in checkState(), we will set the externalShader and
 633         // update the current transform state "for real".
 634         flushVertexBuffer();
 635         if (shader != null) {
 636             shader.enable();
 637         }
 638         externalShader = shader;
 639     }
 640 
 641     private void checkState(BaseShaderGraphics g,
 642                             int checkFlags,
 643                             BaseTransform xform,
 644                             Shader shader)
 645     {
 646         setRenderTarget(g);
 647 
 648         if ((checkFlags & CHECK_SHADER) != 0) {
 649             if (shader != state.lastShader) {
 650                 flushVertexBuffer();
 651                 shader.enable();
 652                 state.lastShader = shader;
 653                 // the transform matrix is part of the state of each shader
 654                 // (in ES2 at least), so we need to make sure the transform
 655                 // is updated for the current shader by setting isXformValid=false
 656                 state.isXformValid = false;
 657                 checkFlags |= CHECK_TRANSFORM;
 658             }
 659         }
 660 
 661         if ((checkFlags & CHECK_TRANSFORM) != 0) {
 662             if (!state.isXformValid || !xform.equals(state.lastTransform)) {
 663                 flushVertexBuffer();
 664                 updateShaderTransform(shader, xform);
 665                 state.lastTransform.setTransform(xform);
 666                 state.isXformValid = true;
 667             }
 668         }
 669 
 670         if ((checkFlags & CHECK_CLIP) != 0) {
 671             Rectangle clip = g.getClipRectNoClone();
 672             if (clip != state.lastClip) {
 673                 flushVertexBuffer();
 674                 updateClipRect(clip);
 675                 state.lastClip = clip;
 676             }
 677         }
 678 
 679         if ((checkFlags & CHECK_COMPOSITE) != 0) {
 680             CompositeMode mode = g.getCompositeMode();
 681             if (mode != state.lastComp) {
 682                 flushVertexBuffer();
 683                 updateCompositeMode(mode);
 684                 state.lastComp = mode;
 685             }
 686         }
 687     }
 688 
 689     private void setTexture(int texUnit, Texture tex) {
 690         if (tex != null) tex.assertLocked();
 691         if (tex != state.lastTextures[texUnit]) {
 692             flushVertexBuffer();
 693             updateTexture(texUnit, tex);
 694             state.lastTextures[texUnit] = tex;
 695         }
 696     }
 697 
 698     //Current RenderTarget is the lcdBuffer after this method.
 699     public void initLCDBuffer(int width, int height) {
 700         lcdBuffer = factory.createRTTexture(width, height, Texture.WrapMode.CLAMP_NOT_NEEDED);
 701         // TODO: RT-29488 we need to track the uses of the LCD buffer,
 702         // but the flow of control through the text methods is
 703         // not straight-forward enough for a simple set of lock/unlock
 704         // fixes at this time.
 705         lcdBuffer.makePermanent();
 706     }
 707 
 708     public void disposeLCDBuffer() {
 709         if (lcdBuffer != null) {
 710             lcdBuffer.dispose();
 711             lcdBuffer = null;
 712         }
 713     }
 714 
 715     @Override
 716     public RTTexture getLCDBuffer() {
 717         return lcdBuffer;
 718     }
 719 
 720     //Current RenderTarget is undefined after this method.
 721     public void validateLCDBuffer(RenderTarget renderTarget) {
 722         if (lcdBuffer == null ||
 723                 lcdBuffer.getPhysicalWidth() < renderTarget.getPhysicalWidth() ||
 724                 lcdBuffer.getPhysicalHeight() < renderTarget.getPhysicalHeight())
 725         {
 726             disposeLCDBuffer();
 727             initLCDBuffer(renderTarget.getPhysicalWidth(), renderTarget.getPhysicalHeight());
 728         }
 729     }
 730 
 731     abstract public void blit(RTTexture srcRTT, RTTexture dstRTT,
 732                           int srcX0, int srcY0, int srcX1, int srcY1,
 733                           int dstX0, int dstY0, int dstX1, int dstY1);
 734 
 735     @Override
 736     protected void setRenderTarget(RenderTarget target, NGCamera camera,
 737             boolean depthTest, boolean state3D)
 738     {
 739         if (target instanceof Texture) {
 740             ((Texture) target).assertLocked();
 741         }
 742         if (state == null ||
 743             state3D != state.lastState3D ||
 744             target != state.lastRenderTarget ||
 745             camera != state.lastCamera ||
 746             depthTest != state.lastDepthTest)
 747         {
 748             flushVertexBuffer();
 749             state = updateRenderTarget(target, camera, depthTest);
 750             state.lastRenderTarget = target;
 751             state.lastCamera = camera;
 752             state.lastDepthTest = depthTest;
 753 
 754             // the projection matrix is set in updateShaderTransform()
 755             // because it depends on the dimensions of the destination surface,
 756             // so if the RenderTarget is changing we force a call to the
 757             // updateShaderTransform() method by setting isXformValid=false
 758             state.isXformValid = false;
 759 
 760             // True if we switch between 2D and 3D primitives
 761             if (state3D != state.lastState3D) {
 762                 state.lastState3D = state3D;
 763                 state.lastShader = null;
 764                 state.lastConst1 = Float.NaN;
 765                 state.lastConst2 = Float.NaN;
 766                 state.lastConst3 = Float.NaN;
 767                 state.lastConst4 = Float.NaN;
 768                 state.lastConst5 = Float.NaN;
 769                 state.lastConst6 = Float.NaN;
 770                 state.lastComp = null;
 771                 state.lastClip = null;
 772                 for (int i = 0; i != state.lastTextures.length; i++) {
 773                     state.lastTextures[i] = null;
 774                 }
 775                 if (state3D) {
 776                     // switch to 3D state
 777                     setDeviceParametersFor3D();
 778                 } else {
 779                     // switch to 2D state
 780                     setDeviceParametersFor2D();
 781                 }
 782             }
 783         }
 784     }
 785 
 786     @Override
 787     protected void releaseRenderTarget() {
 788         // Null out hard references that cause memory leak reported in RT-17304
 789         if (state != null) {
 790             state.lastRenderTarget = null;
 791             for (int i=0; i<state.lastTextures.length; i++) {
 792                 state.lastTextures[i] = null;
 793             }
 794         }
 795     }
 796 
 797 }