1 /**
2  * Copyright 2010 JogAmp Community. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are
5  * permitted provided that the following conditions are met:
6  *
7  *    1. Redistributions of source code must retain the above copyright notice, this list of
8  *       conditions and the following disclaimer.
9  *
10  *    2. Redistributions in binary form must reproduce the above copyright notice, this list
11  *       of conditions and the following disclaimer in the documentation and/or other materials
12  *       provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
15  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
16  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * The views and conclusions contained in the software and documentation are those of the
25  * authors and should not be interpreted as representing official policies, either expressed
26  * or implied, of JogAmp Community.
27  */
28 package com.jogamp.graph.curve.opengl;
29 
30 import java.io.IOException;
31 import java.util.Iterator;
32 
33 import com.jogamp.opengl.GL;
34 import com.jogamp.opengl.GL2ES2;
35 import com.jogamp.opengl.GLES2;
36 import com.jogamp.opengl.GLException;
37 import com.jogamp.opengl.fixedfunc.GLMatrixFunc;
38 
39 import jogamp.graph.curve.opengl.shader.AttributeNames;
40 import jogamp.graph.curve.opengl.shader.UniformNames;
41 
42 import com.jogamp.opengl.GLExtensions;
43 import com.jogamp.opengl.util.glsl.ShaderCode;
44 import com.jogamp.opengl.util.glsl.ShaderProgram;
45 import com.jogamp.opengl.util.texture.TextureSequence;
46 import com.jogamp.opengl.util.PMVMatrix;
47 import com.jogamp.common.os.Platform;
48 import com.jogamp.common.util.IntObjectHashMap;
49 import com.jogamp.graph.curve.Region;
50 
51 /**
52  * OpenGL {@link Region} renderer
53  * <p>
54  * All OpenGL related operations regarding {@link Region}s
55  * are passed through an instance of this class.
56  * </p>
57  */
58 public class RegionRenderer {
59     protected static final boolean DEBUG = Region.DEBUG;
60     protected static final boolean DEBUG_INSTANCE = Region.DEBUG_INSTANCE;
61 
62     /**
63      * May be passed to
64      * {@link RegionRenderer#create(RenderState, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback, com.jogamp.graph.curve.opengl.RegionRenderer.GLCallback) RegionRenderer ctor},
65      * e.g.
66      * <ul>
67      *   <li>{@link RegionRenderer#defaultBlendEnable}</li>
68      *   <li>{@link RegionRenderer#defaultBlendDisable}</li>
69      * </ul>
70      */
71     public interface GLCallback {
72         /**
73          * @param gl a current GL object
74          * @param renderer {@link RegionRenderer} calling this method.
75          */
run(GL gl, RegionRenderer renderer)76         void run(GL gl, RegionRenderer renderer);
77     }
78 
79     /**
80      * Default {@link GL#GL_BLEND} <i>enable</i> {@link GLCallback},
81      * turning-off depth writing via {@link GL#glDepthMask(boolean)} if {@link RenderState#BITHINT_GLOBAL_DEPTH_TEST_ENABLED} is set
82      * and turning-on the {@link GL#GL_BLEND} state.
83      * <p>
84      * Implementation also sets {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint},
85      * which will cause {@link GLRegion#draw(GL2ES2, RegionRenderer, int[]) GLRegion's draw-method}
86      * to set the proper {@link GL#glBlendFuncSeparate(int, int, int, int) blend-function}
87      * and the clear-color to <i>transparent-black</i> in case of {@link Region#isTwoPass(int) multipass} FBO rendering.
88      * </p>
89      * @see #create(RenderState, GLCallback, GLCallback)
90      * @see #enable(GL2ES2, boolean)
91      */
92     public static final GLCallback defaultBlendEnable = new GLCallback() {
93         @Override
94         public void run(final GL gl, final RegionRenderer renderer) {
95             if( renderer.rs.isHintMaskSet(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED) ) {
96                 gl.glDepthMask(false);
97                 // gl.glDisable(GL.GL_DEPTH_TEST);
98                 // gl.glDepthFunc(GL.GL_ALWAYS);
99             }
100             gl.glEnable(GL.GL_BLEND);
101             gl.glBlendEquation(GL.GL_FUNC_ADD); // default
102             renderer.rs.setHintMask(RenderState.BITHINT_BLENDING_ENABLED);
103         }
104     };
105 
106     /**
107      * Default {@link GL#GL_BLEND} <i>disable</i> {@link GLCallback},
108      * simply turning-off the {@link GL#GL_BLEND} state
109      * and turning-on depth writing via {@link GL#glDepthMask(boolean)} if {@link RenderState#BITHINT_GLOBAL_DEPTH_TEST_ENABLED} is set.
110      * <p>
111      * Implementation also clears {@link RegionRenderer#getRenderState() RenderState}'s {@link RenderState#BITHINT_BLENDING_ENABLED blending bit-hint}.
112      * </p>
113      * @see #create(RenderState, GLCallback, GLCallback)
114      * @see #enable(GL2ES2, boolean)
115      */
116     public static final GLCallback defaultBlendDisable = new GLCallback() {
117         @Override
118         public void run(final GL gl, final RegionRenderer renderer) {
119             renderer.rs.clearHintMask(RenderState.BITHINT_BLENDING_ENABLED);
120             gl.glDisable(GL.GL_BLEND);
121             if( renderer.rs.isHintMaskSet(RenderState.BITHINT_GLOBAL_DEPTH_TEST_ENABLED) ) {
122                 // gl.glEnable(GL.GL_DEPTH_TEST);
123                 // gl.glDepthFunc(GL.GL_LESS);
124                 gl.glDepthMask(true);
125             }
126         }
127     };
128 
129     /**
130      * Create a Hardware accelerated Region Renderer.
131      * <p>
132      * The optional {@link GLCallback}s <code>enableCallback</code> and <code>disableCallback</code>
133      * maybe used to issue certain tasks at {@link #enable(GL2ES2, boolean)}.<br/>
134      * For example, instances {@link #defaultBlendEnable} and {@link #defaultBlendDisable}
135      * can be utilized to enable and disable {@link GL#GL_BLEND}.
136      * </p>
137      * @param rs the used {@link RenderState}
138      * @param enableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at
139      *                       {@link #init(GL2ES2, int) init(gl)} and {@link #enable(GL2ES2, boolean) enable(gl, true)}.
140      * @param disableCallback optional {@link GLCallback}, if not <code>null</code> will be issued at
141      *                        {@link #enable(GL2ES2, boolean) enable(gl, false)}.
142      * @return an instance of Region Renderer
143      * @see #enable(GL2ES2, boolean)
144      */
create(final RenderState rs, final GLCallback enableCallback, final GLCallback disableCallback)145     public static RegionRenderer create(final RenderState rs, final GLCallback enableCallback,
146                                         final GLCallback disableCallback) {
147         return new RegionRenderer(rs, enableCallback, disableCallback);
148     }
149 
150     private final RenderState rs;
151 
152     private final GLCallback enableCallback;
153     private final GLCallback disableCallback;
154 
155     private int vp_width;
156     private int vp_height;
157     private boolean initialized;
158     private boolean vboSupported = false;
159 
isInitialized()160     public final boolean isInitialized() { return initialized; }
161 
162     /** Return width of current viewport */
getWidth()163     public final int getWidth() { return vp_width; }
164     /** Return height of current viewport */
getHeight()165     public final int getHeight() { return vp_height; }
166 
getMatrix()167     public final PMVMatrix getMatrix() { return rs.getMatrix(); }
168 
169     //////////////////////////////////////
170 
171     /**
172      * @param rs the used {@link RenderState}
173      */
RegionRenderer(final RenderState rs, final GLCallback enableCallback, final GLCallback disableCallback)174     protected RegionRenderer(final RenderState rs, final GLCallback enableCallback, final GLCallback disableCallback) {
175         this.rs = rs;
176         this.enableCallback = enableCallback;
177         this.disableCallback = disableCallback;
178     }
179 
isVBOSupported()180     public final boolean isVBOSupported() { return vboSupported; }
181 
182     /**
183      * Initialize shader and bindings for GPU based rendering bound to the given GL object's GLContext
184      * if not initialized yet.
185      * <p>Leaves the renderer enabled, ie ShaderState.</p>
186      * <p>Shall be called by a {@code draw()} method, e.g. {@link RegionRenderer#draw(GL2ES2, Region, int)}</p>
187      *
188      * @param gl referencing the current GLContext to which the ShaderState is bound to
189      * @param renderModes
190      * @throws GLException if initialization failed
191      */
init(final GL2ES2 gl, final int renderModes)192     public final void init(final GL2ES2 gl, final int renderModes) throws GLException {
193         if(initialized){
194             return;
195         }
196         vboSupported =  gl.isFunctionAvailable("glGenBuffers") &&
197                         gl.isFunctionAvailable("glBindBuffer") &&
198                         gl.isFunctionAvailable("glBufferData") &&
199                         gl.isFunctionAvailable("glDrawElements") &&
200                         gl.isFunctionAvailable("glVertexAttribPointer") &&
201                         gl.isFunctionAvailable("glDeleteBuffers");
202 
203         if(DEBUG) {
204             System.err.println("TextRendererImpl01: VBO Supported = " + isVBOSupported());
205         }
206 
207         if(!vboSupported){
208             throw new GLException("VBO not supported");
209         }
210 
211         rs.attachTo(gl);
212 
213         if( null != enableCallback ) {
214             enableCallback.run(gl, this);
215         }
216         initialized = true;
217     }
218 
destroy(final GL2ES2 gl)219     public final void destroy(final GL2ES2 gl) {
220         if(!initialized){
221             if(DEBUG_INSTANCE) {
222                 System.err.println("TextRenderer: Not initialized!");
223             }
224             return;
225         }
226         for(final Iterator<IntObjectHashMap.Entry> i = shaderPrograms.iterator(); i.hasNext(); ) {
227             final ShaderProgram sp = (ShaderProgram) i.next().getValue();
228             sp.destroy(gl);
229         }
230         shaderPrograms.clear();
231         rs.destroy(gl);
232         initialized = false;
233     }
234 
getRenderState()235     public final RenderState getRenderState() { return rs; }
236 
237     /**
238      * Enabling or disabling the {@link #getRenderState() RenderState}'s
239      * {@link RenderState#getShaderProgram() shader program}.
240      * <p>
241      * In case enable and disable {@link GLCallback}s are setup via {@link #create(RenderState, GLCallback, GLCallback)},
242      * they will be called before toggling the shader program.
243      * </p>
244      * @see #create(RenderState, GLCallback, GLCallback)
245      */
enable(final GL2ES2 gl, final boolean enable)246     public final void enable(final GL2ES2 gl, final boolean enable) {
247         if( enable ) {
248             if( null != enableCallback ) {
249                 enableCallback.run(gl, this);
250             }
251         } else {
252             if( null != disableCallback ) {
253                 disableCallback.run(gl, this);
254             }
255         }
256         if( !enable ) {
257             final ShaderProgram sp = rs.getShaderProgram();
258             if( null != sp ) {
259                 sp.useProgram(gl, false);
260             }
261         }
262     }
263 
264     /** No PMVMatrix operation is performed here. PMVMatrix is marked dirty. */
reshapeNotify(final int width, final int height)265     public final void reshapeNotify(final int width, final int height) {
266         this.vp_width = width;
267         this.vp_height = height;
268     }
269 
reshapePerspective(final float angle, final int width, final int height, final float near, final float far)270     public final void reshapePerspective(final float angle, final int width, final int height, final float near, final float far) {
271         this.vp_width = width;
272         this.vp_height = height;
273         final float ratio = (float)width/(float)height;
274         final PMVMatrix p = rs.getMatrix();
275         p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
276         p.glLoadIdentity();
277         p.gluPerspective(angle, ratio, near, far);
278     }
279 
reshapeOrtho(final int width, final int height, final float near, final float far)280     public final void reshapeOrtho(final int width, final int height, final float near, final float far) {
281         this.vp_width = width;
282         this.vp_height = height;
283         final PMVMatrix p = rs.getMatrix();
284         p.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
285         p.glLoadIdentity();
286         p.glOrthof(0, width, 0, height, near, far);
287     }
288 
289     //
290     // Shader Management
291     //
292 
293     private static final String SHADER_SRC_SUB = "";
294     private static final String SHADER_BIN_SUB = "bin";
295 
296     private static String GLSL_USE_COLOR_CHANNEL = "#define USE_COLOR_CHANNEL 1\n";
297     private static String GLSL_USE_COLOR_TEXTURE = "#define USE_COLOR_TEXTURE 1\n";
298     private static String GLSL_DEF_SAMPLE_COUNT = "#define SAMPLE_COUNT ";
299     private static String GLSL_CONST_SAMPLE_COUNT = "const float sample_count = ";
300     private static String GLSL_MAIN_BEGIN = "void main (void)\n{\n";
301     private static final String gcuTexture2D = "gcuTexture2D";
302 
getVersionedShaderName()303     private String getVersionedShaderName() {
304         return "curverenderer01";
305     }
306 
307     // FIXME: Really required to have sampler2D def. precision ? If not, we can drop getFragmentShaderPrecision(..) and use default ShaderCode ..
308     private static final String es2_precision_fp = "\nprecision mediump float;\nprecision mediump int;\nprecision mediump sampler2D;\n";
309 
getFragmentShaderPrecision(final GL2ES2 gl)310     private final String getFragmentShaderPrecision(final GL2ES2 gl) {
311         if( gl.isGLES() ) {
312             return es2_precision_fp;
313         }
314         if( ShaderCode.requiresGL3DefaultPrecision(gl) ) {
315             return ShaderCode.gl3_default_precision_fp;
316         }
317         return null;
318     }
319 
320     private static enum ShaderModeSelector1 {
321         /** Pass-1: Curve Simple */
322         PASS1_SIMPLE("curve", "_simple", 0),
323         /** Pass-1: Curve Varying Weight */
324         PASS1_WEIGHT("curve", "_weight", 0),
325         /** Pass-2: MSAA */
326         PASS2_MSAA("msaa", "", 0),
327         /** Pass-2: VBAA Flipquad3, 1 sample */
328         PASS2_VBAA_QUAL0_SAMPLES1("vbaa", "_flipquad3", 1),
329         /** Pass-2: VBAA Flipquad3, 2 samples */
330         PASS2_VBAA_QUAL0_SAMPLES2("vbaa", "_flipquad3", 2),
331         /** Pass-2: VBAA Flipquad3, 4 samples */
332         PASS2_VBAA_QUAL0_SAMPLES4("vbaa", "_flipquad3", 4),
333         /** Pass-2: VBAA Flipquad3, 8 samples */
334         PASS2_VBAA_QUAL0_SAMPLES8("vbaa", "_flipquad3", 8),
335 
336         /** Pass-2: VBAA Brute-Force, Odd, 1 samples */
337         PASS2_VBAA_QUAL1_SAMPLES1("vbaa", "_bforce_odd",  1),
338         /** Pass-2: VBAA Brute-Force, Even, 2 samples */
339         PASS2_VBAA_QUAL1_SAMPLES2("vbaa", "_bforce_even", 2),
340         /** Pass-2: VBAA Brute-Force, Odd, 3 samples */
341         PASS2_VBAA_QUAL1_SAMPLES3("vbaa", "_bforce_odd",  3),
342         /** Pass-2: VBAA Brute-Force, Even, 4 samples */
343         PASS2_VBAA_QUAL1_SAMPLES4("vbaa", "_bforce_even", 4),
344         /** Pass-2: VBAA Brute-Force, Odd, 5 samples */
345         PASS2_VBAA_QUAL1_SAMPLES5("vbaa", "_bforce_odd",  5),
346         /** Pass-2: VBAA Brute-Force, Even, 6 samples */
347         PASS2_VBAA_QUAL1_SAMPLES6("vbaa", "_bforce_even", 6),
348         /** Pass-2: VBAA Brute-Force, Odd, 7 samples */
349         PASS2_VBAA_QUAL1_SAMPLES7("vbaa", "_bforce_odd",  7),
350         /** Pass-2: VBAA Brute-Force, Even, 8 samples */
351         PASS2_VBAA_QUAL1_SAMPLES8("vbaa", "_bforce_even", 8);
352 
353         public final String tech;
354         public final String sub;
355         public final int sampleCount;
356 
ShaderModeSelector1(final String tech, final String sub, final int sampleCount)357         ShaderModeSelector1(final String tech, final String sub, final int sampleCount) {
358             this.tech = tech;
359             this.sub= sub;
360             this.sampleCount = sampleCount;
361         }
362 
selectPass1(final int renderModes)363         public static ShaderModeSelector1 selectPass1(final int renderModes) {
364             return Region.hasVariableWeight(renderModes) ? PASS1_WEIGHT : PASS1_SIMPLE;
365         }
366 
selectPass2(final int renderModes, final int quality, final int sampleCount)367         public static ShaderModeSelector1 selectPass2(final int renderModes, final int quality, final int sampleCount) {
368             if( Region.isMSAA(renderModes) ) {
369                 return PASS2_MSAA;
370             } else if( Region.isVBAA(renderModes) ) {
371                 if( 0 == quality ) {
372                     if( sampleCount < 2 ) {
373                         return PASS2_VBAA_QUAL0_SAMPLES1;
374                     } else if( sampleCount < 4 ) {
375                         return PASS2_VBAA_QUAL0_SAMPLES2;
376                     } else if( sampleCount < 8 ) {
377                         return PASS2_VBAA_QUAL0_SAMPLES4;
378                     } else {
379                         return PASS2_VBAA_QUAL0_SAMPLES8;
380                     }
381                 } else {
382                     switch( sampleCount ) {
383                         case 0: // Fall through intended
384                         case 1:  return PASS2_VBAA_QUAL1_SAMPLES1;
385                         case 2:  return PASS2_VBAA_QUAL1_SAMPLES2;
386                         case 3:  return PASS2_VBAA_QUAL1_SAMPLES3;
387                         case 4:  return PASS2_VBAA_QUAL1_SAMPLES4;
388                         case 5:  return PASS2_VBAA_QUAL1_SAMPLES5;
389                         case 6:  return PASS2_VBAA_QUAL1_SAMPLES6;
390                         case 7:  return PASS2_VBAA_QUAL1_SAMPLES7;
391                         default: return PASS2_VBAA_QUAL1_SAMPLES8;
392                     }
393                 }
394             } else {
395                 return null;
396             }
397         }
398     }
399     private final IntObjectHashMap shaderPrograms = new IntObjectHashMap();
400 
401     private static final int HIGH_MASK = Region.COLORCHANNEL_RENDERING_BIT | Region.COLORTEXTURE_RENDERING_BIT;
402     private static final int TWO_PASS_BIT = 1 <<  31;
403 
404     /**
405      * @param gl
406      * @param renderModes
407      * @param pass1
408      * @param quality
409      * @param sampleCount
410      * @param colorTexSeq
411      * @return true if a new shader program is being used and hence external uniform-data and -location,
412      *         as well as the attribute-location must be updated, otherwise false.
413      */
useShaderProgram(final GL2ES2 gl, final int renderModes, final boolean pass1, final int quality, final int sampleCount, final TextureSequence colorTexSeq)414     public final boolean useShaderProgram(final GL2ES2 gl, final int renderModes,
415                                           final boolean pass1, final int quality, final int sampleCount, final TextureSequence colorTexSeq) {
416         final int colorTexSeqHash;
417         if( null != colorTexSeq ) {
418             colorTexSeqHash = colorTexSeq.getTextureFragmentShaderHashCode();
419         } else {
420             colorTexSeqHash = 0;
421         }
422         final ShaderModeSelector1 sel1 = pass1 ? ShaderModeSelector1.selectPass1(renderModes) :
423                                                  ShaderModeSelector1.selectPass2(renderModes, quality, sampleCount);
424         final boolean isTwoPass = Region.isTwoPass( renderModes );
425         final boolean isPass1ColorTexSeq = pass1 && null != colorTexSeq;
426         final int shaderKey = ( (colorTexSeqHash << 5) - colorTexSeqHash ) +
427                               ( sel1.ordinal() | ( HIGH_MASK & renderModes ) | ( isTwoPass ? TWO_PASS_BIT : 0 ) );
428 
429         /**
430         if(DEBUG) {
431             System.err.printf("RegionRendererImpl01.useShaderProgram.0: renderModes %s, sel1 %s, key 0x%X (pass1 %b, q %d, samples %d) - Thread %s%n",
432                     Region.getRenderModeString(renderModes), sel1, shaderKey, pass1, quality, sampleCount, Thread.currentThread());
433         } */
434 
435         ShaderProgram sp = (ShaderProgram) shaderPrograms.get( shaderKey );
436         if( null != sp ) {
437             final boolean spChanged = getRenderState().setShaderProgram(gl, sp);
438             if(DEBUG) {
439                 if( spChanged ) {
440                     System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
441                 } else {
442                     System.err.printf("RegionRendererImpl01.useShaderProgram.X1: GOT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (keep)%n", Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
443                 }
444             }
445             return spChanged;
446         }
447         final String versionedBaseName = getVersionedShaderName();
448         final String vertexShaderName;
449         if( isTwoPass ) {
450             vertexShaderName = versionedBaseName+"-pass"+(pass1?1:2);
451         } else {
452             vertexShaderName = versionedBaseName+"-single";
453         }
454         final ShaderCode rsVp = ShaderCode.create(gl, GL2ES2.GL_VERTEX_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, vertexShaderName, true);
455         final ShaderCode rsFp = ShaderCode.create(gl, GL2ES2.GL_FRAGMENT_SHADER, AttributeNames.class, SHADER_SRC_SUB, SHADER_BIN_SUB, versionedBaseName+"-segment-head", true);
456 
457         if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) {
458             if( !gl.isExtensionAvailable(GLExtensions.OES_EGL_image_external) ) {
459                 throw new GLException(GLExtensions.OES_EGL_image_external+" requested but not available");
460             }
461         }
462         boolean supressGLSLVersionES30 = false;
463         if( isPass1ColorTexSeq && GLES2.GL_TEXTURE_EXTERNAL_OES == colorTexSeq.getTextureTarget() ) {
464             if( Platform.OSType.ANDROID == Platform.getOSType() && gl.isGLES3() ) {
465                 // Bug on Nexus 10, ES3 - Android 4.3, where
466                 // GL_OES_EGL_image_external extension directive leads to a failure _with_ '#version 300 es' !
467                 //   P0003: Extension 'GL_OES_EGL_image_external' not supported
468                 supressGLSLVersionES30 = true;
469             }
470         }
471         //
472         // GLSL customization at top
473         //
474         int posVp = rsVp.defaultShaderCustomization(gl, !supressGLSLVersionES30, true);
475         // rsFp.defaultShaderCustomization(gl, true, true);
476         int posFp = supressGLSLVersionES30 ? 0 : rsFp.addGLSLVersion(gl);
477         if( isPass1ColorTexSeq ) {
478             posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getRequiredExtensionsShaderStub());
479         }
480         if( pass1 && supressGLSLVersionES30 || ( gl.isGLES2() && !gl.isGLES3() ) ) {
481             posFp = rsFp.insertShaderSource(0, posFp, ShaderCode.createExtensionDirective(GLExtensions.OES_standard_derivatives, ShaderCode.ENABLE));
482         }
483         if( false ) {
484             final String rsFpDefPrecision =  getFragmentShaderPrecision(gl);
485             if( null != rsFpDefPrecision ) {
486                 posFp = rsFp.insertShaderSource(0, posFp, rsFpDefPrecision);
487             }
488         } else {
489             posFp = rsFp.addDefaultShaderPrecision(gl, posFp);
490         }
491 
492         //
493         // GLSL append from here on
494         posFp = -1;
495 
496         if( Region.hasColorChannel( renderModes ) ) {
497             posVp = rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_CHANNEL);
498             posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_CHANNEL);
499         }
500         if( Region.hasColorTexture( renderModes ) ) {
501                     rsVp.insertShaderSource(0, posVp, GLSL_USE_COLOR_TEXTURE);
502             posFp = rsFp.insertShaderSource(0, posFp, GLSL_USE_COLOR_TEXTURE);
503         }
504         if( !pass1 ) {
505             posFp = rsFp.insertShaderSource(0, posFp, GLSL_DEF_SAMPLE_COUNT+sel1.sampleCount+"\n");
506             posFp = rsFp.insertShaderSource(0, posFp, GLSL_CONST_SAMPLE_COUNT+sel1.sampleCount+".0;\n");
507         }
508 
509         try {
510             posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, "uniforms.glsl");
511             posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, "varyings.glsl");
512         } catch (final IOException ioe) {
513             throw new RuntimeException("Failed to read: includes", ioe);
514         }
515         if( 0 > posFp ) {
516             throw new RuntimeException("Failed to read: includes");
517         }
518 
519         final String texLookupFuncName;
520         if( isPass1ColorTexSeq ) {
521             posFp = rsFp.insertShaderSource(0, posFp, "uniform "+colorTexSeq.getTextureSampler2DType()+" "+UniformNames.gcu_ColorTexUnit+";\n");
522             texLookupFuncName = colorTexSeq.getTextureLookupFunctionName(gcuTexture2D);
523             posFp = rsFp.insertShaderSource(0, posFp, colorTexSeq.getTextureLookupFragmentShaderImpl());
524         } else {
525             texLookupFuncName = null;
526         }
527 
528         posFp = rsFp.insertShaderSource(0, posFp, GLSL_MAIN_BEGIN);
529 
530         final String passS = pass1 ? "-pass1-" : "-pass2-";
531         final String shaderSegment = versionedBaseName+passS+sel1.tech+sel1.sub+".glsl";
532         if(DEBUG) {
533             System.err.printf("RegionRendererImpl01.useShaderProgram.1: segment %s%n", shaderSegment);
534         }
535         try {
536             posFp = rsFp.insertShaderSource(0, posFp, AttributeNames.class, shaderSegment);
537         } catch (final IOException ioe) {
538             throw new RuntimeException("Failed to read: "+shaderSegment, ioe);
539         }
540         if( 0 > posFp ) {
541             throw new RuntimeException("Failed to read: "+shaderSegment);
542         }
543         posFp = rsFp.insertShaderSource(0, posFp, "}\n");
544 
545         if( isPass1ColorTexSeq ) {
546             rsFp.replaceInShaderSource(gcuTexture2D, texLookupFuncName);
547         }
548 
549         sp = new ShaderProgram();
550         sp.add(rsVp);
551         sp.add(rsFp);
552 
553         if( !sp.init(gl) ) {
554             throw new GLException("RegionRenderer: Couldn't init program: "+sp);
555         }
556 
557         if( !sp.link(gl, System.err) ) {
558             throw new GLException("could not link program: "+sp);
559         }
560         getRenderState().setShaderProgram(gl, sp);
561 
562         shaderPrograms.put(shaderKey, sp);
563         if(DEBUG) {
564             System.err.printf("RegionRendererImpl01.useShaderProgram.X1: PUT renderModes %s, sel1 %s, key 0x%X -> sp %d / %d (changed)%n",
565                     Region.getRenderModeString(renderModes), sel1, shaderKey, sp.program(), sp.id());
566         }
567         return true;
568     }
569 }