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