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