Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 8c57d77b

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 org.distorted.library.main.DistortedLibrary;
26

    
27
import java.io.BufferedReader;
28
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.InputStreamReader;
31

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

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

    
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55

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

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

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

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

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

    
81
      GLES30.glLinkProgram(programHandle);
82

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

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

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

    
96
      //DistortedLibrary.logMessage("DistortedProgram: number of active uniforms="+numberOfUniforms[0]);
97
      }
98

    
99
    return programHandle;
100
    }
101

    
102
///////////////////////////////////////////////////////////////////////////////////////////////////
103

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

    
115
///////////////////////////////////////////////////////////////////////////////////////////////////
116

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

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

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

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

    
140
      if( subline.startsWith(mUniformStr))
141
        {
142
        //DistortedLibrary.logMessage("DistortedProgram: GOOD LINE: " +subline+" subLen="+subLen);
143

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

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

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

    
160
    return null;
161
    }
162

    
163
///////////////////////////////////////////////////////////////////////////////////////////////////
164

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

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

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

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

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

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

    
202
    return null;
203
    }
204

    
205
///////////////////////////////////////////////////////////////////////////////////////////////////
206

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

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

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

    
225
      uniform = parseOutUniform(line);
226

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

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

    
241
///////////////////////////////////////////////////////////////////////////////////////////////////
242

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

    
248
    String nextLine, attribute, attrList="";
249
    final StringBuilder body = new StringBuilder();
250

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

    
258
        if( doAttributes )
259
          {
260
          attribute = parseOutAttribute(nextLine);
261

    
262
          if( attribute!=null )
263
            {
264
            //DistortedLibrary.logMessage("DistortedProgram: new attribute: "+attribute);
265
            if( attrList.length()>0 ) attrList+=" ";
266
            attrList += attribute;
267
            }
268
          }
269
        }
270
      }
271
    catch (IOException e)
272
      {
273
      return null;
274
      }
275

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

    
282
    return body.toString();
283
    }
284

    
285
///////////////////////////////////////////////////////////////////////////////////////////////////
286

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

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

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

    
303
        GLES30.glDeleteShader(shaderHandle);
304

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

    
314
    return shaderHandle;
315
    }
316

    
317
///////////////////////////////////////////////////////////////////////////////////////////////////
318

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

    
324
    int place = code.indexOf(marker);
325

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

    
331
      return begin + effects + end;
332
      }
333
    else
334
      {
335
      DistortedLibrary.logMessage("DistortedProgram: Error: marker string not found in SHADER!");
336
      }
337

    
338
    return null;
339
    }
340

    
341
///////////////////////////////////////////////////////////////////////////////////////////////////
342

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

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

    
352
    int emptyAttrs = 0;
353

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

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

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

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

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

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

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

    
409
    final String vertShader = readTextFileFromRawResource(vert, true );
410
    final String fragShader = readTextFileFromRawResource(frag, false);
411

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

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

    
417
    setUpAttributes();
418
    setUpUniforms();
419
    }
420

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

    
433
    String vertShader = readTextFileFromRawResource( vert, true );
434
    String fragShader = readTextFileFromRawResource( frag, false);
435

    
436
    if( enabledVert!=null ) vertShader = insertEnabledEffects(vertShader,enabledVert);
437
    if( enabledFrag!=null ) fragShader = insertEnabledEffects(fragShader,enabledFrag);
438

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

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

    
444
    setUpAttributes();
445
    setUpUniforms();
446
    }
447

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

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

    
479
    doAttributes(vertex  , true );
480
    doAttributes(fragment, false);
481

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

    
485
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
486

    
487
    setUpAttributes();
488
    setUpUniforms();
489
    }
490

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

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

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

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

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

    
533

    
(1-1/6)