Project

General

Profile

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

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

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

    
38
  private int mProgramHandle;
39
  private int mNumAttributes;
40
  private int mNumUniforms;
41
  private String[] mAttributeName;
42
  private String[] mUniformName;
43

    
44
/**
45
 * List of Attributes (on OpenGL ES 3.0: 'in' variables) in the same order as declared in the shader source.
46
 */
47
  public final int[] mAttribute;
48
/**
49
 * List of Uniforms in the same order as declared in the shader source.
50
 */
51
  public final 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
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
190

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

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

    
204
    return null;
205
    }
206

    
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

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

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

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

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

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

    
239
    if( doAttributes ) mAttributeName = attrList.split(" ");
240
    }
241

    
242
///////////////////////////////////////////////////////////////////////////////////////////////////
243

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

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

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

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

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

    
277
    if( doAttributes ) mAttributeName = attrList.split(" ");
278

    
279
    return body.toString();
280
    }
281

    
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283

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

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

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

    
303
    if (shaderHandle == 0)
304
      {
305
      String error = GLES30.glGetShaderInfoLog(shaderHandle);
306

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

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

    
317
    return shaderHandle;
318
    }
319

    
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

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

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

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

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

    
347
    return null;
348
    }
349

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

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

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

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

    
369
    mAttribute = new int[mNumAttributes];
370

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

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

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

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

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

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

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

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

    
412
    mAttribute = new int[mNumAttributes];
413

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

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

    
424
      for(int i=0; i<mNumUniforms; i++)
425
        {
426
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
427
        }
428
      }
429
    else mUniform = null;
430
    }
431

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

    
444
///////////////////////////////////////////////////////////////////////////////////////////////////
445
// PUBLIC API
446
///////////////////////////////////////////////////////////////////////////////////////////////////
447
/**
448
 * Create a new Shader Program from two source strings.
449
 * <p>
450
 * Needs to be called from a thread holding the OpenGL context.
451
 * Assumed to hold GLSL 'version 300 es' source.
452
 *
453
 * @param vertex   Vertex shader code.
454
 * @param fragment Fragment shader code.
455
 * @throws FragmentCompilationException
456
 * @throws VertexCompilationException
457
 * @throws VertexUniformsException
458
 * @throws FragmentUniformsException
459
 * @throws LinkingException
460
 */
461
  public DistortedProgram(final String vertex, final String fragment)
462
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
463
    {
464
    init(300);
465

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

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

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

    
474
    mAttribute = new int[mNumAttributes];
475

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

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

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

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

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

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

    
518

    
(1-1/6)