Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 36d65d88

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.GLES30;
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 = 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
            mNumAttributes++;
195
            return subline.substring(nameBegin+1,subLen);
196
            }
197
          }
198
        }
199
      }
200

    
201
    return null;
202
    }
203

    
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205

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

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

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

    
224
      uniform = parseOutUniform(line);
225

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

    
233
    if( doAttributes ) mAttributeName = attrList.split(" ");
234
    }
235

    
236
///////////////////////////////////////////////////////////////////////////////////////////////////
237

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

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

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

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

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

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

    
273
    return body.toString();
274
    }
275

    
276
///////////////////////////////////////////////////////////////////////////////////////////////////
277

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

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

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

    
294
        GLES30.glDeleteShader(shaderHandle);
295

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

    
305
    return shaderHandle;
306
    }
307

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309

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

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

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

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

    
329
    return null;
330
    }
331

    
332
///////////////////////////////////////////////////////////////////////////////////////////////////
333
/**
334
 * Only for use by the library itself.
335
 *
336
 * @y.exclude
337
 */
338
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
339
                          int glslVersion, final String[] feedback )
340
  throws FragmentCompilationException,VertexCompilationException,LinkingException
341
    {
342
    init(glslVersion);
343

    
344
    final String vertShader = readTextFileFromRawResource(vert, true );
345
    final String fragShader = readTextFileFromRawResource(frag, false);
346

    
347
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
348
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
349

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

    
352
    mAttribute = new int[mNumAttributes];
353

    
354
    for(int i=0; i<mNumAttributes; i++)
355
      {
356
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
357
      }
358

    
359
    if( mNumUniforms>0 )
360
      {
361
      mUniform = new int[mNumUniforms];
362
      mUniformName = mUniList.split(" ");
363

    
364
      for(int i=0; i<mNumUniforms; i++)
365
        {
366
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
367
        }
368
      }
369
    else mUniform = null;
370
    }
371

    
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373
/**
374
 * Only for use by the library itself.
375
 *
376
 * @y.exclude
377
 */
378
  public DistortedProgram(final InputStream vert, final InputStream frag, final String vertHeader, final String fragHeader,
379
                          final String enabledVert, final String enabledFrag, int glslVersion, final String[] feedback )
380
  throws FragmentCompilationException,VertexCompilationException,LinkingException
381
    {
382
    init(glslVersion);
383

    
384
    String vertShader = readTextFileFromRawResource( vert, true );
385
    String fragShader = readTextFileFromRawResource( frag, false);
386

    
387
    if( enabledVert!=null ) vertShader = insertEnabledEffects(vertShader,enabledVert);
388
    if( enabledFrag!=null ) fragShader = insertEnabledEffects(fragShader,enabledFrag);
389

    
390
    final int vertShaderHandle = compileShader(GLES30.GL_VERTEX_SHADER  , vertHeader + vertShader);
391
    final int fragShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragHeader + fragShader);
392

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

    
395
    mAttribute = new int[mNumAttributes];
396

    
397
    for(int i=0; i<mNumAttributes; i++)
398
      {
399
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
400
      }
401

    
402
    if( mNumUniforms>0 )
403
      {
404
      mUniform = new int[mNumUniforms];
405
      mUniformName = mUniList.split(" ");
406

    
407
      for(int i=0; i<mNumUniforms; i++)
408
        {
409
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
410
        }
411
      }
412
    else mUniform = null;
413
    }
414

    
415
///////////////////////////////////////////////////////////////////////////////////////////////////
416
/**
417
 * Only for use by the library itself.
418
 *
419
 * @y.exclude
420
 */
421
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion )
422
  throws FragmentCompilationException,VertexCompilationException,LinkingException
423
    {
424
    this(vertex,fragment,vertexHeader,fragmentHeader,glslVersion,null);
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428
// PUBLIC API
429
///////////////////////////////////////////////////////////////////////////////////////////////////
430
/**
431
 * Create a new Shader Program from two source strings.
432
 * <p>
433
 * Needs to be called from a thread holding the OpenGL context.
434
 *
435
 * @param vertex   Vertex shader code.
436
 * @param fragment Fragment shader code.
437
 * @throws FragmentCompilationException
438
 * @throws VertexCompilationException
439
 * @throws LinkingException
440
 */
441
  public DistortedProgram(final String vertex, final String fragment)
442
  throws FragmentCompilationException,VertexCompilationException,LinkingException
443
    {
444
    init(300);
445

    
446
    doAttributes(vertex  , true );
447
    doAttributes(fragment, false);
448

    
449
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertex  );
450
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragment);
451

    
452
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
453

    
454
    mAttribute = new int[mNumAttributes];
455

    
456
    for(int i=0; i<mNumAttributes; i++)
457
      {
458
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
459
      }
460

    
461
    if( mNumUniforms>0 )
462
      {
463
      mUniform = new int[mNumUniforms];
464
      mUniformName = mUniList.split(" ");
465

    
466
      for(int i=0; i<mNumUniforms; i++)
467
        {
468
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
469
        }
470
      }
471
    else mUniform = null;
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
      GLES30.glEnableVertexAttribArray(mAttribute[i]);
495
    }
496
  }
497

    
498

    
(1-1/6)