Project

General

Profile

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

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

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2016 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Distorted.                                                               //
5
//                                                                                               //
6
// Distorted is free software: you can redistribute it and/or modify                             //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Distorted 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                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.library.program;
21

    
22
import android.opengl.GLES31;
23

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

    
29
///////////////////////////////////////////////////////////////////////////////////////////////////
30
/**
31
 * An object which encapsulates a vertex/fragment shader combo, aka Shader Program.
32
 */
33
public class DistortedProgram
34
  {
35
  private String mAttributeStr, mUniformStr, mUniList;
36
  private int mAttributeLen, mUniformLen;
37
  private int mProgramHandle;
38
  private int mNumAttributes;
39
  private int mNumUniforms;
40
  private String[] mAttributeName;
41
  private String[] mUniformName;
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 final int[] mAttribute;
47
/**
48
 * List of Uniforms, in the same order as declared in the shader source.
49
 */
50
  public final 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 = GLES31.glCreateProgram();
58

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

    
64
      if( feedbackVaryings!=null )
65
        {
66
        GLES31.glTransformFeedbackVaryings(programHandle, feedbackVaryings, GLES31.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
          GLES31.glBindAttribLocation(programHandle, i, attributes[i]);
76
          }
77
        }
78

    
79
      GLES31.glLinkProgram(programHandle);
80

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

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

    
91
      final int[] numberOfUniforms = new int[1];
92
      GLES31.glGetProgramiv(programHandle, GLES31.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
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
189

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

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

    
203
    return null;
204
    }
205

    
206
///////////////////////////////////////////////////////////////////////////////////////////////////
207

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

    
214
    for(int i=0; i<length; i++)
215
      {
216
      if( doAttributes )
217
        {
218
        attribute = parseOutAttribute(lines[i]);
219

    
220
        if( attribute!=null )
221
          {
222
          //android.util.Log.d("program", "new attribute: "+attribute);
223
          if( attrList.length()>0 ) attrList+=" ";
224
          attrList += attribute;
225
          }
226
        }
227

    
228
      uniform = parseOutUniform(lines[i]);
229

    
230
      if( uniform!=null )
231
        {
232
        //android.util.Log.d("program", "new uniform: "+uniform);
233
        if( mUniList.length()>0 ) mUniList+=" ";
234
        mUniList += uniform;
235
        }
236
      }
237

    
238
    if( doAttributes ) mAttributeName = attrList.split(" ");
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
            //android.util.Log.d("program", "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 ) mAttributeName = attrList.split(" ");
277

    
278
    return body.toString();
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

    
283
  private static int compileShader(final int shaderType, final String shaderSource)
284
  throws FragmentCompilationException,VertexCompilationException
285
    {
286
    int shaderHandle = GLES31.glCreateShader(shaderType);
287

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

    
295
      if (compileStatus[0] != GLES31.GL_TRUE )
296
        {
297
        GLES31.glDeleteShader(shaderHandle);
298
        shaderHandle = 0;
299
        }
300
      }
301

    
302
    if (shaderHandle == 0)
303
      {
304
      String error = GLES31.glGetShaderInfoLog(shaderHandle);
305

    
306
      //android.util.Log.e("Program", "error compiling :"+error);
307

    
308
      switch(shaderType)
309
        {
310
        case GLES31.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
311
        case GLES31.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
312
        default                       : throw new RuntimeException(error);
313
        }
314
      }
315

    
316
    return shaderHandle;
317
    }
318

    
319
///////////////////////////////////////////////////////////////////////////////////////////////////
320

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

    
326
    int place = code.indexOf(marker);
327

    
328
    if( place>=0 )
329
      {
330
      String begin = code.substring(0,place-1);
331
      String end   = code.substring(place+length);
332
/*
333
int len = begin.length();
334

    
335
android.util.Log.d("Program", begin.substring(len-40));
336
android.util.Log.d("Program", effects);
337
android.util.Log.d("Program", end.substring(0,40));
338
*/
339
      return begin + effects + end;
340
      }
341
    else
342
      {
343
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
344
      }
345

    
346
    return null;
347
    }
348

    
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350
/**
351
 * Only for use by the library itself.
352
 *
353
 * @y.exclude
354
 */
355
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion, final String[] feedback )
356
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
357
    {
358
    init(glslVersion);
359

    
360
    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
361
    final String fragmentShader = readTextFileFromRawResource(fragment, false);
362

    
363
    final int vertexShaderHandle   = compileShader(GLES31.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
364
    final int fragmentShaderHandle = compileShader(GLES31.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
365

    
366
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
367

    
368
    mAttribute = new int[mNumAttributes];
369

    
370
    for(int i=0; i<mNumAttributes; i++)
371
      {
372
      mAttribute[i] = GLES31.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
373
      }
374

    
375
    if( mNumUniforms>0 )
376
      {
377
      mUniform = new int[mNumUniforms];
378
      mUniformName = mUniList.split(" ");
379

    
380
      for(int i=0; i<mNumUniforms; i++)
381
        {
382
        mUniform[i] = GLES31.glGetUniformLocation( mProgramHandle, mUniformName[i]);
383
        }
384
      }
385
    else mUniform = null;
386
    }
387

    
388
///////////////////////////////////////////////////////////////////////////////////////////////////
389
/**
390
 * Only for use by the library itself.
391
 *
392
 * @y.exclude
393
 */
394
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader,
395
                          final String enabledVertex, final String enabledFragment, int glslVersion, final String[] feedback )
396
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
397
    {
398
    init(glslVersion);
399

    
400
    String vertexShader   = readTextFileFromRawResource(vertex  , true );
401
    String fragmentShader = readTextFileFromRawResource(fragment, false);
402

    
403
    vertexShader   = insertEnabledEffects(vertexShader  ,enabledVertex  );
404
    fragmentShader = insertEnabledEffects(fragmentShader,enabledFragment);
405

    
406
    final int vertexShaderHandle   = compileShader(GLES31.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
407
    final int fragmentShaderHandle = compileShader(GLES31.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
408

    
409
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
410

    
411
    mAttribute = new int[mNumAttributes];
412

    
413
    for(int i=0; i<mNumAttributes; i++)
414
      {
415
      mAttribute[i] = GLES31.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
416
      }
417

    
418
    if( mNumUniforms>0 )
419
      {
420
      mUniform = new int[mNumUniforms];
421
      mUniformName = mUniList.split(" ");
422

    
423
      for(int i=0; i<mNumUniforms; i++)
424
        {
425
        mUniform[i] = GLES31.glGetUniformLocation( mProgramHandle, mUniformName[i]);
426
        }
427
      }
428
    else mUniform = null;
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,VertexUniformsException,FragmentUniformsException,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
 * Assumed to hold GLSL 'version 300 es' source.
451
 *
452
 * @param vertex   Vertex shader code.
453
 * @param fragment Fragment shader code.
454
 * @throws FragmentCompilationException
455
 * @throws VertexCompilationException
456
 * @throws VertexUniformsException
457
 * @throws FragmentUniformsException
458
 * @throws LinkingException
459
 */
460
  public DistortedProgram(final String vertex, final String fragment)
461
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
462
    {
463
    init(300);
464

    
465
    doAttributes(vertex  , true );
466
    doAttributes(fragment, false);
467

    
468
    final int vertexShaderHandle   = compileShader(GLES31.GL_VERTEX_SHADER  , vertex  );
469
    final int fragmentShaderHandle = compileShader(GLES31.GL_FRAGMENT_SHADER, fragment);
470

    
471
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
472

    
473
    mAttribute = new int[mNumAttributes];
474

    
475
    for(int i=0; i<mNumAttributes; i++)
476
      {
477
      mAttribute[i] = GLES31.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
478
      }
479

    
480
    if( mNumUniforms>0 )
481
      {
482
      mUniform = new int[mNumUniforms];
483
      mUniformName = mUniList.split(" ");
484

    
485
      for(int i=0; i<mNumUniforms; i++)
486
        {
487
        mUniform[i] = GLES31.glGetUniformLocation( mProgramHandle, mUniformName[i]);
488
        }
489
      }
490
    else mUniform = null;
491
    }
492

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

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

    
512
    for(int i=0; i<mNumAttributes; i++)
513
      GLES31.glEnableVertexAttribArray(mAttribute[i]);
514
    }
515
  }
516

    
517

    
(1-1/6)