Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 041b6dee

1 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 194ab46f Leszek Koltunski
import android.opengl.GLES30;
23 432442f9 Leszek Koltunski
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 041b6dee Leszek Koltunski
  private String mAttributeStr, mUniformStr, mUniList;
36
  private int mAttributeLen, mUniformLen;
37 432442f9 Leszek Koltunski
38 94f6d472 Leszek Koltunski
  private int mProgramHandle;
39 432442f9 Leszek Koltunski
  private int mNumAttributes;
40 041b6dee Leszek Koltunski
  private int mNumUniforms;
41 432442f9 Leszek Koltunski
  private String[] mAttributeName;
42 041b6dee Leszek Koltunski
  private String[] mUniformName;
43 432442f9 Leszek Koltunski
44 02de77c9 Leszek Koltunski
  public final int[] mAttribute;
45 041b6dee Leszek Koltunski
  public final int[] mUniform;
46 02de77c9 Leszek Koltunski
47 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
48
49 4782b4e6 Leszek Koltunski
  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes, final String[] feedbackVaryings)
50 432442f9 Leszek Koltunski
  throws LinkingException
51
    {
52 194ab46f Leszek Koltunski
    int programHandle = GLES30.glCreateProgram();
53 432442f9 Leszek Koltunski
54
    if (programHandle != 0)
55
      {
56 194ab46f Leszek Koltunski
      GLES30.glAttachShader(programHandle, vertexShaderHandle);
57
      GLES30.glAttachShader(programHandle, fragmentShaderHandle);
58 432442f9 Leszek Koltunski
59 4782b4e6 Leszek Koltunski
      if( feedbackVaryings!=null )
60
        {
61 f80337b5 leszek
        GLES30.glTransformFeedbackVaryings(programHandle, feedbackVaryings, GLES30.GL_INTERLEAVED_ATTRIBS);
62 4782b4e6 Leszek Koltunski
        }
63
64 432442f9 Leszek Koltunski
      if (attributes != null)
65
        {
66
        final int size = attributes.length;
67
68
        for (int i = 0; i < size; i++)
69
          {
70 194ab46f Leszek Koltunski
          GLES30.glBindAttribLocation(programHandle, i, attributes[i]);
71 432442f9 Leszek Koltunski
          }
72
        }
73
74 194ab46f Leszek Koltunski
      GLES30.glLinkProgram(programHandle);
75 432442f9 Leszek Koltunski
76
      final int[] linkStatus = new int[1];
77 194ab46f Leszek Koltunski
      GLES30.glGetProgramiv(programHandle, GLES30.GL_LINK_STATUS, linkStatus, 0);
78 432442f9 Leszek Koltunski
79 194ab46f Leszek Koltunski
      if (linkStatus[0] != GLES30.GL_TRUE )
80 432442f9 Leszek Koltunski
        {
81 194ab46f Leszek Koltunski
        String error = GLES30.glGetProgramInfoLog(programHandle);
82
        GLES30.glDeleteProgram(programHandle);
83 432442f9 Leszek Koltunski
        throw new LinkingException(error);
84
        }
85
86
      final int[] numberOfUniforms = new int[1];
87 194ab46f Leszek Koltunski
      GLES30.glGetProgramiv(programHandle, GLES30.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
88 432442f9 Leszek Koltunski
89
      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
90
      }
91
92
    return programHandle;
93
    }
94
95 041b6dee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
96
97
  private void init(int glslVersion)
98
    {
99
    mAttributeStr  = (glslVersion == 100 ? "attribute " : "in ");
100
    mAttributeLen  = mAttributeStr.length();
101
    mNumAttributes = 0;
102
    mUniformStr    = "uniform ";
103
    mUniformLen    = mUniformStr.length();
104
    mNumUniforms   = 0;
105
    mUniList       = "";
106
    }
107
108
///////////////////////////////////////////////////////////////////////////////////////////////////
109
110
  private String parseOutUniform(final String line)
111
    {
112
    int len = line.length();
113
    int whiteSpace, semicolon, nameBegin;
114
    char currChar;
115
116
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
117
      {
118
      currChar = line.charAt(whiteSpace);
119
      if( currChar!=' ' && currChar!='\t') break;
120
      }
121
122
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
123
      {
124
      currChar = line.charAt(semicolon);
125
      if( currChar==';') break;
126
      }
127
128
    if( semicolon<len && semicolon-whiteSpace>=mUniformLen+1 )
129
      {
130
      String subline = line.substring(whiteSpace,semicolon);
131
      int subLen = semicolon-whiteSpace;
132
133
      if( subline.startsWith(mUniformStr))
134
        {
135
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
136
137
        for(nameBegin=subLen-1; nameBegin>mUniformLen-2; nameBegin--)
138
          {
139
          currChar=subline.charAt(nameBegin);
140
141
          if( currChar==' ' || currChar=='\t' )
142
            {
143
            mNumUniforms++;
144
            String uniform = subline.substring(nameBegin+1,subLen);
145
            int brace = uniform.indexOf("[");
146
147
            return brace>=0 ? uniform.substring(0,brace) : uniform;
148
            }
149
          }
150
        }
151
      }
152
153
    return null;
154
    }
155
156 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
157
158 3d804c91 Leszek Koltunski
  private String parseOutAttribute(final String line)
159 432442f9 Leszek Koltunski
    {
160
    int len = line.length();
161
    int whiteSpace, semicolon, nameBegin;
162
    char currChar;
163
164
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
165
      {
166
      currChar = line.charAt(whiteSpace);
167
      if( currChar!=' ' && currChar!='\t') break;
168
      }
169
170
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
171
      {
172
      currChar = line.charAt(semicolon);
173
      if( currChar==';') break;
174
      }
175
176 94f6d472 Leszek Koltunski
    if( semicolon<len && semicolon-whiteSpace>=mAttributeLen+1 )
177 432442f9 Leszek Koltunski
      {
178
      String subline = line.substring(whiteSpace,semicolon);
179
      int subLen = semicolon-whiteSpace;
180
181 94f6d472 Leszek Koltunski
      if( subline.startsWith(mAttributeStr))
182 432442f9 Leszek Koltunski
        {
183
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
184
185 94f6d472 Leszek Koltunski
        for(nameBegin=subLen-1; nameBegin>mAttributeLen-2; nameBegin--)
186 432442f9 Leszek Koltunski
          {
187
          currChar=subline.charAt(nameBegin);
188
189
          if( currChar==' ' || currChar=='\t' )
190
            {
191
            mNumAttributes++;
192 3d804c91 Leszek Koltunski
            return subline.substring(nameBegin+1,subLen);
193 432442f9 Leszek Koltunski
            }
194
          }
195
        }
196
      }
197 3d804c91 Leszek Koltunski
198
    return null;
199 432442f9 Leszek Koltunski
    }
200
201 041b6dee Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
202
203
  private void doAttributes(final String shader, boolean doAttributes)
204
    {
205
    String attribute, attrList="", uniform;
206
    String[] lines = shader.split("\n");
207
    int length = lines.length;
208
209
    for(int i=0; i<length; i++)
210
      {
211
      if( doAttributes )
212
        {
213
        attribute = parseOutAttribute(lines[i]);
214
215
        if( attribute!=null )
216
          {
217
          //android.util.Log.d("program", "new attribute: "+attribute);
218
          if( attrList.length()>0 ) attrList+=" ";
219
          attrList += attribute;
220
          }
221
        }
222
223
      uniform = parseOutUniform(lines[i]);
224
225
      if( uniform!=null )
226
        {
227
        android.util.Log.d("program", "new uniform: "+uniform);
228
        if( mUniList.length()>0 ) mUniList+=" ";
229
        mUniList += uniform;
230
        }
231
      }
232
233
    if( doAttributes ) mAttributeName = attrList.split(" ");
234
    }
235
236 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
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 3d804c91 Leszek Koltunski
    String nextLine, attribute, attrList="";
244 432442f9 Leszek Koltunski
    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 3d804c91 Leszek Koltunski
        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 432442f9 Leszek Koltunski
        }
265
      }
266
    catch (IOException e)
267
      {
268
      return null;
269
      }
270
271 041b6dee Leszek Koltunski
    if( doAttributes ) mAttributeName = attrList.split(" ");
272 432442f9 Leszek Koltunski
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 194ab46f Leszek Koltunski
    int shaderHandle = GLES30.glCreateShader(shaderType);
282 432442f9 Leszek Koltunski
283
    if (shaderHandle != 0)
284
      {
285 194ab46f Leszek Koltunski
      GLES30.glShaderSource(shaderHandle, shaderSource);
286
      GLES30.glCompileShader(shaderHandle);
287 432442f9 Leszek Koltunski
      final int[] compileStatus = new int[1];
288 194ab46f Leszek Koltunski
      GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
289 432442f9 Leszek Koltunski
290 194ab46f Leszek Koltunski
      if (compileStatus[0] != GLES30.GL_TRUE )
291 432442f9 Leszek Koltunski
        {
292 194ab46f Leszek Koltunski
        GLES30.glDeleteShader(shaderHandle);
293 432442f9 Leszek Koltunski
        shaderHandle = 0;
294
        }
295
      }
296
297
    if (shaderHandle == 0)
298
      {
299 194ab46f Leszek Koltunski
      String error = GLES30.glGetShaderInfoLog(shaderHandle);
300 432442f9 Leszek Koltunski
301 81a0b906 leszek
      //android.util.Log.e("Program", "error compiling :"+error);
302
303 432442f9 Leszek Koltunski
      switch(shaderType)
304
        {
305 194ab46f Leszek Koltunski
        case GLES30.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
306
        case GLES30.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
307 432442f9 Leszek Koltunski
        default                       : throw new RuntimeException(error);
308
        }
309
      }
310
311
    return shaderHandle;
312
    }
313
314 7cd24173 leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
315
316
  private static String insertEnabledEffects(String code, final String effects)
317
    {
318
    final String marker = "// ENABLED EFFECTS WILL BE INSERTED HERE";
319
    int length = marker.length();
320
321
    int place = code.indexOf(marker);
322
323
    if( place>=0 )
324
      {
325
      String begin = code.substring(0,place-1);
326
      String end   = code.substring(place+length);
327
/*
328
int len = begin.length();
329
330
android.util.Log.d("Program", begin.substring(len-40));
331
android.util.Log.d("Program", effects);
332
android.util.Log.d("Program", end.substring(0,40));
333
*/
334
      return begin + effects + end;
335
      }
336
    else
337
      {
338
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
339
      }
340
341
    return null;
342
    }
343
344 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
345 4782b4e6 Leszek Koltunski
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
346
// using Transform Feedback.
347 432442f9 Leszek Koltunski
348 4782b4e6 Leszek Koltunski
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion, final String[] feedback )
349 432442f9 Leszek Koltunski
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
350
    {
351 041b6dee Leszek Koltunski
    init(glslVersion);
352 432442f9 Leszek Koltunski
353
    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
354
    final String fragmentShader = readTextFileFromRawResource(fragment, false);
355
356 194ab46f Leszek Koltunski
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
357
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
358 432442f9 Leszek Koltunski
359 420836fc leszek
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
360 7cd24173 leszek
361
    mAttribute = new int[mNumAttributes];
362
363
    for(int i=0; i<mNumAttributes; i++)
364
      {
365
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
366
      }
367 041b6dee Leszek Koltunski
368
    if( mNumUniforms>0 )
369
      {
370
      mUniform = new int[mNumUniforms];
371
      mUniformName = mUniList.split(" ");
372
373
      for(int i=0; i<mNumUniforms; i++)
374
        {
375
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
376
        }
377
      }
378
    else mUniform = null;
379
    }
380
381
///////////////////////////////////////////////////////////////////////////////////////////////////
382
383
  public DistortedProgram(final String vertex, final String fragment)
384
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
385
    {
386
    init(300);
387
388
    doAttributes(vertex  , true );
389
    doAttributes(fragment, false);
390
391
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertex  );
392
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragment);
393
394
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, null );
395
396
    mAttribute = new int[mNumAttributes];
397
398
    for(int i=0; i<mNumAttributes; i++)
399
      {
400
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
401
      }
402
403
    if( mNumUniforms>0 )
404
      {
405
      mUniform = new int[mNumUniforms];
406
      mUniformName = mUniList.split(" ");
407
408
      for(int i=0; i<mNumUniforms; i++)
409
        {
410
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
411
        }
412
      }
413
    else mUniform = null;
414 7cd24173 leszek
    }
415
416
///////////////////////////////////////////////////////////////////////////////////////////////////
417
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
418
// using Transform Feedback.
419
420
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader,
421
                          final String enabledVertex, final String enabledFragment, int glslVersion, final String[] feedback )
422
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
423
    {
424 041b6dee Leszek Koltunski
    init(glslVersion);
425 7cd24173 leszek
426
    String vertexShader   = readTextFileFromRawResource(vertex  , true );
427
    String fragmentShader = readTextFileFromRawResource(fragment, false);
428
429
    vertexShader   = insertEnabledEffects(vertexShader  ,enabledVertex  );
430
    fragmentShader = insertEnabledEffects(fragmentShader,enabledFragment);
431
432
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
433
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
434
435
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName, glslVersion>= 300 ? feedback:null );
436 432442f9 Leszek Koltunski
437 02de77c9 Leszek Koltunski
    mAttribute = new int[mNumAttributes];
438 432442f9 Leszek Koltunski
439 02de77c9 Leszek Koltunski
    for(int i=0; i<mNumAttributes; i++)
440
      {
441 194ab46f Leszek Koltunski
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
442 02de77c9 Leszek Koltunski
      }
443 041b6dee Leszek Koltunski
444
    if( mNumUniforms>0 )
445
      {
446
      mUniform = new int[mNumUniforms];
447
      mUniformName = mUniList.split(" ");
448
449
      for(int i=0; i<mNumUniforms; i++)
450
        {
451
        mUniform[i] = GLES30.glGetUniformLocation( mProgramHandle, mUniformName[i]);
452
        }
453
      }
454
    else mUniform = null;
455 432442f9 Leszek Koltunski
    }
456
457 4782b4e6 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
458
// PUBLIC API
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460
  /**
461
   * Create a new Shader Program from source stored in resource files.
462
   * <p>
463
   * Needs to be called from a thread holding the OpenGL context.
464
   *
465
   * @param vertex   InputStream containing the opened Resource file from where to read vertex shader code.
466
   * @param fragment InputStream containing the opened Resource file from where to read fragment shader code.
467
   * @throws FragmentCompilationException
468
   * @throws VertexCompilationException
469
   * @throws VertexUniformsException
470
   * @throws FragmentUniformsException
471
   * @throws LinkingException
472
   */
473
474
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion )
475
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
476
    {
477
    this(vertex,fragment,vertexHeader,fragmentHeader,glslVersion,null);
478
    }
479
480 432442f9 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
481 3d804c91 Leszek Koltunski
/**
482
 * Return the handle of the created program so that we can later, say, call glUseProgram.
483
 */
484 432442f9 Leszek Koltunski
  public int getProgramHandle()
485
    {
486
    return mProgramHandle;
487
    }
488
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490 3d804c91 Leszek Koltunski
/**
491 02de77c9 Leszek Koltunski
 * Use the program and enable all vertex attribute arrays.
492
 *
493 3d804c91 Leszek Koltunski
 * Needs to be called from a thread holding the OpenGL context.
494
 */
495 02de77c9 Leszek Koltunski
  public void useProgram()
496 432442f9 Leszek Koltunski
    {
497 194ab46f Leszek Koltunski
    GLES30.glUseProgram(mProgramHandle);
498 02de77c9 Leszek Koltunski
499 432442f9 Leszek Koltunski
    for(int i=0; i<mNumAttributes; i++)
500 194ab46f Leszek Koltunski
      GLES30.glEnableVertexAttribArray(mAttribute[i]);
501 432442f9 Leszek Koltunski
    }
502
  }
503 02de77c9 Leszek Koltunski