Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 3a54358a

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 String[] mUniformName;
42
  private final int mProgramHandle;
43

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

    
53
///////////////////////////////////////////////////////////////////////////////////////////////////
54

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

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

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

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

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

    
80
      GLES30.glLinkProgram(programHandle);
81

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

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

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

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

    
98
    return programHandle;
99
    }
100

    
101
///////////////////////////////////////////////////////////////////////////////////////////////////
102

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

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

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

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

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

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

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

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

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

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

    
159
    return null;
160
    }
161

    
162
///////////////////////////////////////////////////////////////////////////////////////////////////
163

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

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

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

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

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

    
193
          if( currChar==' ' || currChar=='\t' )
194
            {
195
            mNumAttributes++;
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 ) mAttributeName = attrList.split(" ");
235
    }
236

    
237
///////////////////////////////////////////////////////////////////////////////////////////////////
238

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

    
244
    String nextLine, attribute, attrList="";
245
    final StringBuilder body = new StringBuilder();
246

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

    
254
        if( doAttributes )
255
          {
256
          attribute = parseOutAttribute(nextLine);
257

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

    
272
    if( doAttributes ) mAttributeName = attrList.split(" ");
273

    
274
    return body.toString();
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278

    
279
  private static int compileShader(final int shaderType, final String shaderSource)
280
  throws FragmentCompilationException,VertexCompilationException
281
    {
282
    int shaderHandle = GLES30.glCreateShader(shaderType);
283

    
284
    if (shaderHandle != 0)
285
      {
286
      GLES30.glShaderSource(shaderHandle, shaderSource);
287
      GLES30.glCompileShader(shaderHandle);
288
      final int[] compileStatus = new int[1];
289
      GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
290

    
291
      if (compileStatus[0] != GLES30.GL_TRUE)
292
        {
293
        String error = GLES30.glGetShaderInfoLog(shaderHandle);
294

    
295
        GLES30.glDeleteShader(shaderHandle);
296

    
297
        switch (shaderType)
298
          {
299
          case GLES30.GL_VERTEX_SHADER:   throw new VertexCompilationException(error);
300
          case GLES30.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
301
          default:                        throw new RuntimeException(error);
302
          }
303
        }
304
      }
305

    
306
    return shaderHandle;
307
    }
308

    
309
///////////////////////////////////////////////////////////////////////////////////////////////////
310

    
311
  private static String insertEnabledEffects(String code, final String effects)
312
    {
313
    final String marker = "// ENABLED EFFECTS WILL BE INSERTED HERE";
314
    int length = marker.length();
315

    
316
    int place = code.indexOf(marker);
317

    
318
    if( place>=0 )
319
      {
320
      String begin = code.substring(0,place-1);
321
      String end   = code.substring(place+length);
322

    
323
      return begin + effects + end;
324
      }
325
    else
326
      {
327
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
328
      }
329

    
330
    return null;
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334

    
335
  private void setUpAttributes()
336
    {
337
    mAttribute = new int[mNumAttributes];
338

    
339
    for(int i=0; i<mNumAttributes; i++)
340
      {
341
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
342
      }
343

    
344
    int emptyAttrs = 0;
345

    
346
    for(int i=0; i<mNumAttributes; i++)
347
      {
348
      if( mAttribute[i] < 0 )
349
        {
350
        emptyAttrs++;
351

    
352
        for(int j=i; j<mNumAttributes-1; j++)
353
          {
354
          mAttribute[j] = mAttribute[j+1];
355
          mAttributeName[j] = mAttributeName[j+1];
356
          }
357
        }
358
      }
359

    
360
    mNumAttributes -= emptyAttrs;
361
    }
362

    
363
///////////////////////////////////////////////////////////////////////////////////////////////////
364

    
365
  private void setUpUniforms()
366
    {
367
    if( mNumUniforms>0 )
368
      {
369
      mUniform = new int[mNumUniforms];
370
      mUniformName = mUniList.split(" ");
371

    
372
      for(int i=0; i<mNumUniforms; i++)
373
        {
374
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
375
        }
376
      }
377
    else mUniform = null;
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381
/**
382
 * Only for use by the library itself.
383
 *
384
 * @y.exclude
385
 */
386
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
387
                          int glslVersion, final String[] feedback )
388
  throws FragmentCompilationException,VertexCompilationException,LinkingException
389
    {
390
    init(glslVersion);
391

    
392
    final String vertShader = readTextFileFromRawResource(vert, true );
393
    final String fragShader = readTextFileFromRawResource(frag, false);
394

    
395
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
396
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
397

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

    
400
    setUpAttributes();
401
    setUpUniforms();
402
    }
403

    
404
///////////////////////////////////////////////////////////////////////////////////////////////////
405
/**
406
 * Only for use by the library itself.
407
 *
408
 * @y.exclude
409
 */
410
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
411
                          final String enabledVert, final String enabledFrag, int glslVersion, final String[] feedback )
412
  throws FragmentCompilationException,VertexCompilationException,LinkingException
413
    {
414
    init(glslVersion);
415

    
416
    String vertShader = readTextFileFromRawResource( vert, true );
417
    String fragShader = readTextFileFromRawResource( frag, false);
418

    
419
    if( enabledVert!=null ) vertShader = insertEnabledEffects(vertShader,enabledVert);
420
    if( enabledFrag!=null ) fragShader = insertEnabledEffects(fragShader,enabledFrag);
421

    
422
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
423
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
424

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

    
427
    setUpAttributes();
428
    setUpUniforms();
429
    }
430

    
431
///////////////////////////////////////////////////////////////////////////////////////////////////
432
/**
433
 * Only for use by the library itself.
434
 *
435
 * @y.exclude
436
 */
437
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion )
438
  throws FragmentCompilationException,VertexCompilationException,LinkingException
439
    {
440
    this(vertex,fragment,vertexHeader,fragmentHeader,glslVersion,null);
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444
// PUBLIC API
445
///////////////////////////////////////////////////////////////////////////////////////////////////
446
/**
447
 * Create a new Shader Program from two source strings.
448
 * <p>
449
 * Needs to be called from a thread holding the OpenGL context.
450
 *
451
 * @param vertex   Vertex shader code.
452
 * @param fragment Fragment shader code.
453
 * @throws FragmentCompilationException fragment shader failed to compile
454
 * @throws VertexCompilationException vertex shader failed to compile
455
 * @throws LinkingException shaders failed to link
456
 */
457
  public DistortedProgram(final String vertex, final String fragment)
458
  throws FragmentCompilationException,VertexCompilationException,LinkingException
459
    {
460
    init(300);
461

    
462
    doAttributes(vertex  , true );
463
    doAttributes(fragment, false);
464

    
465
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertex  );
466
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragment);
467

    
468
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
469

    
470
    setUpAttributes();
471
    setUpUniforms();
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475
/**
476
 * Return the handle of the created program so that we can later, say, call glUseProgram.
477
 */
478
  public int getProgramHandle()
479
    {
480
    return mProgramHandle;
481
    }
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484
/**
485
 * Use the program and enable all vertex attribute arrays.
486
 *
487
 * Needs to be called from a thread holding the OpenGL context.
488
 */
489
  public void useProgram()
490
    {
491
    GLES30.glUseProgram(mProgramHandle);
492

    
493
    for(int i=0; i<mNumAttributes; i++)
494
      {
495
      GLES30.glEnableVertexAttribArray(mAttribute[i]);
496
      }
497
    }
498

    
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500
/**
501
 * Disable all vertex attribute arrays.
502
 *
503
 * Needs to be called from a thread holding the OpenGL context.
504
 */
505
  public void stopUsingProgram()
506
    {
507
    GLES30.glUseProgram(0);
508

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

    
516

    
(1-1/6)