Project

General

Profile

Download (16.9 KB) Statistics
| Branch: | Revision:

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ ae71f169

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski  leszek@koltunski.pl                                          //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// This library is free software; you can redistribute it and/or                                 //
7
// modify it under the terms of the GNU Lesser General Public                                    //
8
// License as published by the Free Software Foundation; either                                  //
9
// version 2.1 of the License, or (at your option) any later version.                            //
10
//                                                                                               //
11
// This library is distributed in the hope that it will be useful,                               //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                             //
14
// Lesser General Public License for more details.                                               //
15
//                                                                                               //
16
// You should have received a copy of the GNU Lesser General Public                              //
17
// License along with this library; if not, write to the Free Software                           //
18
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA                //
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
package org.distorted.library.program;
22

    
23
import android.opengl.GLES30;
24

    
25
import java.io.BufferedReader;
26
import java.io.IOException;
27
import java.io.InputStream;
28
import java.io.InputStreamReader;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31
/**
32
 * An object which encapsulates a vertex/fragment shader combo, aka Shader Program.
33
 */
34
public class DistortedProgram
35
  {
36
  private String mAttributeStr, mUniformStr, mUniList;
37
  private int mAttributeLen, mUniformLen;
38
  private int mNumAttributes;
39
  private int mNumUniforms;
40
  private String[] mAttributeName;
41
  private final int mProgramHandle;
42

    
43
/**
44
 * List of Attributes (OpenGL ES 3.0: 'in' variables), in the same order as declared in the shader source.
45
 */
46
  public int[] mAttribute;
47
/**
48
 * List of Uniforms, in the same order as declared in the shader source.
49
 */
50
  public int[] mUniform;
51

    
52
///////////////////////////////////////////////////////////////////////////////////////////////////
53

    
54
  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes, final String[] feedbackVaryings)
55
  throws LinkingException
56
    {
57
    int programHandle = GLES30.glCreateProgram();
58

    
59
    if (programHandle != 0)
60
      {
61
      GLES30.glAttachShader(programHandle, vertexShaderHandle);
62
      GLES30.glAttachShader(programHandle, fragmentShaderHandle);
63

    
64
      if( feedbackVaryings!=null )
65
        {
66
        GLES30.glTransformFeedbackVaryings(programHandle, feedbackVaryings, GLES30.GL_INTERLEAVED_ATTRIBS);
67
        }
68

    
69
      if (attributes != null)
70
        {
71
        final int size = attributes.length;
72

    
73
        for(int i=0; i<size; i++)
74
          {
75
          GLES30.glBindAttribLocation(programHandle, i, attributes[i]);
76
          }
77
        }
78

    
79
      GLES30.glLinkProgram(programHandle);
80

    
81
      final int[] linkStatus = new int[1];
82
      GLES30.glGetProgramiv(programHandle, GLES30.GL_LINK_STATUS, linkStatus, 0);
83

    
84
      if (linkStatus[0] != GLES30.GL_TRUE )
85
        {
86
        String error = GLES30.glGetProgramInfoLog(programHandle);
87
        GLES30.glDeleteProgram(programHandle);
88
        throw new LinkingException(error);
89
        }
90

    
91
      final int[] numberOfUniforms = new int[1];
92
      GLES30.glGetProgramiv(programHandle, GLES30.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
93

    
94
      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
95
      }
96

    
97
    return programHandle;
98
    }
99

    
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101

    
102
  private void init(int glslVersion)
103
    {
104
    mAttributeStr  = (glslVersion == 100 ? "attribute " : "in ");
105
    mAttributeLen  = mAttributeStr.length();
106
    mNumAttributes = 0;
107
    mUniformStr    = "uniform ";
108
    mUniformLen    = mUniformStr.length();
109
    mNumUniforms   = 0;
110
    mUniList       = "";
111
    }
112

    
113
///////////////////////////////////////////////////////////////////////////////////////////////////
114

    
115
  private String parseOutUniform(final String line)
116
    {
117
    int len = line.length();
118
    int whiteSpace, semicolon, nameBegin;
119
    char currChar;
120

    
121
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
122
      {
123
      currChar = line.charAt(whiteSpace);
124
      if( currChar!=' ' && currChar!='\t') break;
125
      }
126

    
127
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
128
      {
129
      currChar = line.charAt(semicolon);
130
      if( currChar==';') break;
131
      }
132

    
133
    if( semicolon<len && semicolon-whiteSpace>=mUniformLen+1 )
134
      {
135
      String subline = line.substring(whiteSpace,semicolon);
136
      int subLen = semicolon-whiteSpace;
137

    
138
      if( subline.startsWith(mUniformStr))
139
        {
140
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
141

    
142
        for(nameBegin=subLen-1; nameBegin>mUniformLen-2; nameBegin--)
143
          {
144
          currChar=subline.charAt(nameBegin);
145

    
146
          if( currChar==' ' || currChar=='\t' )
147
            {
148
            mNumUniforms++;
149
            String uniform = subline.substring(nameBegin+1,subLen);
150
            int brace = uniform.indexOf("[");
151

    
152
            return brace>=0 ? uniform.substring(0,brace) : uniform;
153
            }
154
          }
155
        }
156
      }
157

    
158
    return null;
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  private String parseOutAttribute(final String line)
164
    {
165
    int len = line.length();
166
    int whiteSpace, semicolon, nameBegin;
167
    char currChar;
168

    
169
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
170
      {
171
      currChar = line.charAt(whiteSpace);
172
      if( currChar!=' ' && currChar!='\t') break;
173
      }
174

    
175
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
176
      {
177
      currChar = line.charAt(semicolon);
178
      if( currChar==';') break;
179
      }
180

    
181
    if( semicolon<len && semicolon-whiteSpace>=mAttributeLen+1 )
182
      {
183
      String subline = line.substring(whiteSpace,semicolon);
184
      int subLen = semicolon-whiteSpace;
185

    
186
      if( subline.startsWith(mAttributeStr))
187
        {
188
        for(nameBegin=subLen-1; nameBegin>mAttributeLen-2; nameBegin--)
189
          {
190
          currChar=subline.charAt(nameBegin);
191

    
192
          if( currChar==' ' || currChar=='\t' )
193
            {
194
            return subline.substring(nameBegin+1,subLen);
195
            }
196
          }
197
        }
198
      }
199

    
200
    return null;
201
    }
202

    
203
///////////////////////////////////////////////////////////////////////////////////////////////////
204

    
205
  private void doAttributes(final String shader, boolean doAttributes)
206
    {
207
    String attribute, attrList="", uniform;
208
    String[] lines = shader.split("\n");
209

    
210
    for (String line : lines)
211
      {
212
      if (doAttributes)
213
        {
214
        attribute = parseOutAttribute(line);
215

    
216
        if (attribute != null)
217
          {
218
          if (attrList.length() > 0) attrList += " ";
219
          attrList += attribute;
220
          }
221
        }
222

    
223
      uniform = parseOutUniform(line);
224

    
225
      if (uniform != null)
226
        {
227
        if (mUniList.length() > 0) mUniList += " ";
228
        mUniList += uniform;
229
        }
230
      }
231

    
232
    if( doAttributes )
233
      {
234
      mAttributeName = attrList.split(" ");
235
      mNumAttributes = mAttributeName.length;
236
      }
237
    }
238

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

    
241
  private String readTextFileFromRawResource(final InputStream inputStream, boolean doAttributes)
242
    {
243
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
244
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
245

    
246
    String nextLine, attribute, attrList="";
247
    final StringBuilder body = new StringBuilder();
248

    
249
    try
250
      {
251
      while ((nextLine = bufferedReader.readLine()) != null)
252
        {
253
        body.append(nextLine);
254
        body.append('\n');
255

    
256
        if( doAttributes )
257
          {
258
          attribute = parseOutAttribute(nextLine);
259

    
260
          if( attribute!=null )
261
            {
262
            //android.util.Log.d("program", "new attribute: "+attribute);
263
            if( attrList.length()>0 ) attrList+=" ";
264
            attrList += attribute;
265
            }
266
          }
267
        }
268
      }
269
    catch (IOException e)
270
      {
271
      return null;
272
      }
273

    
274
    if( doAttributes )
275
      {
276
      mAttributeName = attrList.split(" ");
277
      mNumAttributes = mAttributeName.length;
278
      }
279

    
280
    return body.toString();
281
    }
282

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

    
285
  private static int compileShader(final int shaderType, final String shaderSource)
286
  throws FragmentCompilationException,VertexCompilationException
287
    {
288
    int shaderHandle = GLES30.glCreateShader(shaderType);
289

    
290
    if (shaderHandle != 0)
291
      {
292
      GLES30.glShaderSource(shaderHandle, shaderSource);
293
      GLES30.glCompileShader(shaderHandle);
294
      final int[] compileStatus = new int[1];
295
      GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
296

    
297
      if (compileStatus[0] != GLES30.GL_TRUE)
298
        {
299
        String error = GLES30.glGetShaderInfoLog(shaderHandle);
300

    
301
        GLES30.glDeleteShader(shaderHandle);
302

    
303
        switch (shaderType)
304
          {
305
          case GLES30.GL_VERTEX_SHADER:   throw new VertexCompilationException(error);
306
          case GLES30.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
307
          default:                        throw new RuntimeException(error);
308
          }
309
        }
310
      }
311

    
312
    return shaderHandle;
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316

    
317
  private static String insertEnabledEffects(String code, final String effects)
318
    {
319
    final String marker = "// ENABLED EFFECTS WILL BE INSERTED HERE";
320
    int length = marker.length();
321

    
322
    int place = code.indexOf(marker);
323

    
324
    if( place>=0 )
325
      {
326
      String begin = code.substring(0,place-1);
327
      String end   = code.substring(place+length);
328

    
329
      return begin + effects + end;
330
      }
331
    else
332
      {
333
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
334
      }
335

    
336
    return null;
337
    }
338

    
339
///////////////////////////////////////////////////////////////////////////////////////////////////
340

    
341
  private void setUpAttributes()
342
    {
343
    int[] att = new int[mNumAttributes];
344

    
345
    for(int i=0; i<mNumAttributes; i++)
346
      {
347
      att[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
348
      }
349

    
350
    int emptyAttrs = 0;
351

    
352
    for(int i=0; i<mNumAttributes; i++)
353
      {
354
      if( att[i] < 0 )
355
        {
356
        emptyAttrs++;
357

    
358
        for(int j=i; j<mNumAttributes-1; j++)
359
          {
360
          att[j] = att[j+1];
361
          mAttributeName[j] = mAttributeName[j+1];
362
          }
363
        }
364
      }
365

    
366
    if( emptyAttrs>0 )
367
      {
368
      mNumAttributes -= emptyAttrs;
369
      mAttribute = new int[mNumAttributes];
370
      System.arraycopy(att, 0, mAttribute, 0, mNumAttributes);
371
      }
372
    else
373
      {
374
      mAttribute = att;
375
      }
376
    }
377

    
378
///////////////////////////////////////////////////////////////////////////////////////////////////
379

    
380
  private void setUpUniforms()
381
    {
382
    if( mNumUniforms>0 )
383
      {
384
      mUniform = new int[mNumUniforms];
385
      String[] uniformName = mUniList.split(" ");
386

    
387
      for(int i=0; i<mNumUniforms; i++)
388
        {
389
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, uniformName[i]);
390
        }
391
      }
392
    else mUniform = null;
393
    }
394

    
395
///////////////////////////////////////////////////////////////////////////////////////////////////
396
/**
397
 * Only for use by the library itself.
398
 *
399
 * @y.exclude
400
 */
401
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
402
                          int glslVersion, final String[] feedback )
403
  throws FragmentCompilationException,VertexCompilationException,LinkingException
404
    {
405
    init(glslVersion);
406

    
407
    final String vertShader = readTextFileFromRawResource(vert, true );
408
    final String fragShader = readTextFileFromRawResource(frag, false);
409

    
410
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
411
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
412

    
413
    mProgramHandle = createAndLinkProgram(vertShaderHandle, fragShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
414

    
415
    setUpAttributes();
416
    setUpUniforms();
417
    }
418

    
419
///////////////////////////////////////////////////////////////////////////////////////////////////
420
/**
421
 * Only for use by the library itself.
422
 *
423
 * @y.exclude
424
 */
425
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
426
                          final String enabledVert, final String enabledFrag, int glslVersion, final String[] feedback )
427
  throws FragmentCompilationException,VertexCompilationException,LinkingException
428
    {
429
    init(glslVersion);
430

    
431
    String vertShader = readTextFileFromRawResource( vert, true );
432
    String fragShader = readTextFileFromRawResource( frag, false);
433

    
434
    if( enabledVert!=null ) vertShader = insertEnabledEffects(vertShader,enabledVert);
435
    if( enabledFrag!=null ) fragShader = insertEnabledEffects(fragShader,enabledFrag);
436

    
437
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
438
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
439

    
440
    mProgramHandle = createAndLinkProgram(vertShaderHandle, fragShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
441

    
442
    setUpAttributes();
443
    setUpUniforms();
444
    }
445

    
446
///////////////////////////////////////////////////////////////////////////////////////////////////
447
/**
448
 * Only for use by the library itself.
449
 *
450
 * @y.exclude
451
 */
452
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion )
453
  throws FragmentCompilationException,VertexCompilationException,LinkingException
454
    {
455
    this(vertex,fragment,vertexHeader,fragmentHeader,glslVersion,null);
456
    }
457

    
458
///////////////////////////////////////////////////////////////////////////////////////////////////
459
// PUBLIC API
460
///////////////////////////////////////////////////////////////////////////////////////////////////
461
/**
462
 * Create a new Shader Program from two source strings.
463
 * <p>
464
 * Needs to be called from a thread holding the OpenGL context.
465
 *
466
 * @param vertex   Vertex shader code.
467
 * @param fragment Fragment shader code.
468
 * @throws FragmentCompilationException fragment shader failed to compile
469
 * @throws VertexCompilationException vertex shader failed to compile
470
 * @throws LinkingException shaders failed to link
471
 */
472
  public DistortedProgram(final String vertex, final String fragment)
473
  throws FragmentCompilationException,VertexCompilationException,LinkingException
474
    {
475
    init(300);
476

    
477
    doAttributes(vertex  , true );
478
    doAttributes(fragment, false);
479

    
480
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertex  );
481
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragment);
482

    
483
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
484

    
485
    setUpAttributes();
486
    setUpUniforms();
487
    }
488

    
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490
/**
491
 * Return the handle of the created program so that we can later, say, call glUseProgram.
492
 */
493
  public int getProgramHandle()
494
    {
495
    return mProgramHandle;
496
    }
497

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499
/**
500
 * Use the program and enable all vertex attribute arrays.
501
 *
502
 * Needs to be called from a thread holding the OpenGL context.
503
 */
504
  public void useProgram()
505
    {
506
    GLES30.glUseProgram(mProgramHandle);
507

    
508
    for(int i=0; i<mNumAttributes; i++)
509
      {
510
      GLES30.glEnableVertexAttribArray(mAttribute[i]);
511
      }
512
    }
513

    
514
///////////////////////////////////////////////////////////////////////////////////////////////////
515
/**
516
 * Disable all vertex attribute arrays.
517
 *
518
 * Needs to be called from a thread holding the OpenGL context.
519
 */
520
  public void stopUsingProgram()
521
    {
522
    GLES30.glUseProgram(0);
523

    
524
    for(int i=0; i<mNumAttributes; i++)
525
      {
526
      GLES30.glDisableVertexAttribArray(mAttribute[i]);
527
      }
528
    }
529
  }
530

    
531

    
(1-1/6)