Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 7cd24173

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
import android.os.Build;
24

    
25
import org.distorted.library.effect.EffectType;
26
import org.distorted.library.main.DistortedEffects;
27

    
28
import java.io.BufferedReader;
29
import java.io.IOException;
30
import java.io.InputStream;
31
import java.io.InputStreamReader;
32

    
33
///////////////////////////////////////////////////////////////////////////////////////////////////
34
/**
35
 * An object which encapsulates a vertex/fragment shader combo, aka Shader Program.
36
 */
37
public class DistortedProgram
38
  {
39
  private String mAttributeStr;
40
  private int mAttributeLen;
41

    
42
  private int mProgramHandle;
43
  private int mNumAttributes;
44
  private String[] mAttributeName;
45

    
46
  public final int[] mAttribute;
47

    
48
///////////////////////////////////////////////////////////////////////////////////////////////////
49

    
50
  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes, final String[] feedbackVaryings)
51
  throws LinkingException
52
    {
53
    int programHandle = GLES30.glCreateProgram();
54

    
55
    if (programHandle != 0)
56
      {
57
      GLES30.glAttachShader(programHandle, vertexShaderHandle);
58
      GLES30.glAttachShader(programHandle, fragmentShaderHandle);
59

    
60
      if( feedbackVaryings!=null )
61
        {
62
        GLES30.glTransformFeedbackVaryings(programHandle, feedbackVaryings, GLES30.GL_INTERLEAVED_ATTRIBS);
63
        }
64

    
65
      if (attributes != null)
66
        {
67
        final int size = attributes.length;
68

    
69
        for (int i = 0; i < size; i++)
70
          {
71
          GLES30.glBindAttribLocation(programHandle, i, attributes[i]);
72
          }
73
        }
74

    
75
      GLES30.glLinkProgram(programHandle);
76

    
77
      final int[] linkStatus = new int[1];
78
      GLES30.glGetProgramiv(programHandle, GLES30.GL_LINK_STATUS, linkStatus, 0);
79

    
80
      if (linkStatus[0] != GLES30.GL_TRUE )
81
        {
82
        String error = GLES30.glGetProgramInfoLog(programHandle);
83
        GLES30.glDeleteProgram(programHandle);
84
        throw new LinkingException(error);
85
        }
86

    
87
      final int[] numberOfUniforms = new int[1];
88
      GLES30.glGetProgramiv(programHandle, GLES30.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
89

    
90
      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
91
      }
92

    
93
    return programHandle;
94
    }
95

    
96
///////////////////////////////////////////////////////////////////////////////////////////////////
97

    
98
  private String parseOutAttribute(final String line)
99
    {
100
    int len = line.length();
101
    int whiteSpace, semicolon, nameBegin;
102
    char currChar;
103

    
104
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
105
      {
106
      currChar = line.charAt(whiteSpace);
107
      if( currChar!=' ' && currChar!='\t') break;
108
      }
109

    
110
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
111
      {
112
      currChar = line.charAt(semicolon);
113
      if( currChar==';') break;
114
      }
115

    
116
    if( semicolon<len && semicolon-whiteSpace>=mAttributeLen+1 )
117
      {
118
      String subline = line.substring(whiteSpace,semicolon);
119
      int subLen = semicolon-whiteSpace;
120

    
121
      if( subline.startsWith(mAttributeStr))
122
        {
123
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
124

    
125
        for(nameBegin=subLen-1; nameBegin>mAttributeLen-2; nameBegin--)
126
          {
127
          currChar=subline.charAt(nameBegin);
128

    
129
          if( currChar==' ' || currChar=='\t' )
130
            {
131
            mNumAttributes++;
132
            return subline.substring(nameBegin+1,subLen);
133
            }
134
          }
135
        }
136
      }
137

    
138
    return null;
139
    }
140

    
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

    
143
  private String readTextFileFromRawResource(final InputStream inputStream, boolean doAttributes)
144
    {
145
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
146
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
147

    
148
    String nextLine, attribute, attrList="";
149
    final StringBuilder body = new StringBuilder();
150

    
151
    try
152
      {
153
      while ((nextLine = bufferedReader.readLine()) != null)
154
        {
155
        body.append(nextLine);
156
        body.append('\n');
157

    
158
        if( doAttributes )
159
          {
160
          attribute = parseOutAttribute(nextLine);
161

    
162
          if( attribute!=null )
163
            {
164
            //android.util.Log.d("program", "new attribute: "+attribute);
165
            if( attrList.length()>0 ) attrList+=" ";
166
            attrList += attribute;
167
            }
168
          }
169
        }
170
      }
171
    catch (IOException e)
172
      {
173
      return null;
174
      }
175

    
176
    if( doAttributes )
177
      {
178
      mAttributeName = attrList.split(" ");
179
      }
180

    
181
    return body.toString();
182
    }
183

    
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

    
186
  private static void sanitizeMaxValues()
187
  throws VertexUniformsException,FragmentUniformsException
188
    {
189
    int maxV,maxF;
190
    int[] param = new int[1];
191

    
192
    GLES30.glGetIntegerv(GLES30.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
193
    maxV = param[0];
194
    GLES30.glGetIntegerv(GLES30.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
195
    maxF = param[0];
196

    
197
    //Log.d("program", "Max vectors in vertex shader: "+maxV);
198
    //Log.d("program", "Max vectors in fragment shader: "+maxF);
199

    
200
    if( !Build.FINGERPRINT.contains("generic") )
201
      {
202
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
203
      int realMaxF = (maxF- 2)/4;   //
204

    
205
      if( DistortedEffects.getMax(EffectType.VERTEX)   > realMaxV )
206
        {
207
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
208
        }
209
      if( DistortedEffects.getMax(EffectType.FRAGMENT) > realMaxF )
210
        {
211
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
212
        }
213
      }
214
    }
215

    
216
///////////////////////////////////////////////////////////////////////////////////////////////////
217

    
218
  private static int compileShader(final int shaderType, final String shaderSource)
219
  throws FragmentCompilationException,VertexCompilationException
220
    {
221
    int shaderHandle = GLES30.glCreateShader(shaderType);
222

    
223
    if (shaderHandle != 0)
224
      {
225
      GLES30.glShaderSource(shaderHandle, shaderSource);
226
      GLES30.glCompileShader(shaderHandle);
227
      final int[] compileStatus = new int[1];
228
      GLES30.glGetShaderiv(shaderHandle, GLES30.GL_COMPILE_STATUS, compileStatus, 0);
229

    
230
      if (compileStatus[0] != GLES30.GL_TRUE )
231
        {
232
        GLES30.glDeleteShader(shaderHandle);
233
        shaderHandle = 0;
234
        }
235
      }
236

    
237
    if (shaderHandle == 0)
238
      {
239
      String error = GLES30.glGetShaderInfoLog(shaderHandle);
240

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

    
243
      switch(shaderType)
244
        {
245
        case GLES30.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
246
        case GLES30.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
247
        default                       : throw new RuntimeException(error);
248
        }
249
      }
250

    
251
    return shaderHandle;
252
    }
253

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255

    
256
  private static String insertEnabledEffects(String code, final String effects)
257
    {
258
    final String marker = "// ENABLED EFFECTS WILL BE INSERTED HERE";
259
    int length = marker.length();
260

    
261
    int place = code.indexOf(marker);
262

    
263
    if( place>=0 )
264
      {
265
      String begin = code.substring(0,place-1);
266
      String end   = code.substring(place+length);
267
/*
268
int len = begin.length();
269

    
270
android.util.Log.d("Program", begin.substring(len-40));
271
android.util.Log.d("Program", effects);
272
android.util.Log.d("Program", end.substring(0,40));
273
*/
274
      return begin + effects + end;
275
      }
276
    else
277
      {
278
      android.util.Log.e("Program", "Error: marker string not found in SHADER!");
279
      }
280

    
281
    return null;
282
    }
283

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
286
// using Transform Feedback.
287

    
288
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion, final String[] feedback )
289
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
290
    {
291
    mAttributeStr = (glslVersion == 100 ? "attribute " : "in ");
292
    mAttributeLen = mAttributeStr.length();
293

    
294
    mNumAttributes = 0;
295

    
296
    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
297
    final String fragmentShader = readTextFileFromRawResource(fragment, false);
298

    
299
    sanitizeMaxValues();
300

    
301
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
302
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
303

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

    
306
    mAttribute = new int[mNumAttributes];
307

    
308
    for(int i=0; i<mNumAttributes; i++)
309
      {
310
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
311
      }
312
    }
313

    
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315
// feedback: List of 'out' variables (OpenGL ES >= 3.0 only!) that will be transferred back to CPU
316
// using Transform Feedback.
317

    
318
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader,
319
                          final String enabledVertex, final String enabledFragment, int glslVersion, final String[] feedback )
320
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
321
    {
322
    mAttributeStr = (glslVersion == 100 ? "attribute " : "in ");
323
    mAttributeLen = mAttributeStr.length();
324

    
325
    mNumAttributes = 0;
326

    
327
    String vertexShader   = readTextFileFromRawResource(vertex  , true );
328
    String fragmentShader = readTextFileFromRawResource(fragment, false);
329

    
330
    vertexShader   = insertEnabledEffects(vertexShader  ,enabledVertex  );
331
    fragmentShader = insertEnabledEffects(fragmentShader,enabledFragment);
332

    
333
    sanitizeMaxValues();
334

    
335
    final int vertexShaderHandle   = compileShader(GLES30.GL_VERTEX_SHADER  , vertexHeader   + vertexShader  );
336
    final int fragmentShaderHandle = compileShader(GLES30.GL_FRAGMENT_SHADER, fragmentHeader + fragmentShader);
337

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

    
340
    mAttribute = new int[mNumAttributes];
341

    
342
    for(int i=0; i<mNumAttributes; i++)
343
      {
344
      mAttribute[i] = GLES30.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
345
      }
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349
// PUBLIC API
350
///////////////////////////////////////////////////////////////////////////////////////////////////
351
  /**
352
   * Create a new Shader Program from source stored in resource files.
353
   * <p>
354
   * Needs to be called from a thread holding the OpenGL context.
355
   *
356
   * @param vertex   InputStream containing the opened Resource file from where to read vertex shader code.
357
   * @param fragment InputStream containing the opened Resource file from where to read fragment shader code.
358
   * @throws FragmentCompilationException
359
   * @throws VertexCompilationException
360
   * @throws VertexUniformsException
361
   * @throws FragmentUniformsException
362
   * @throws LinkingException
363
   */
364

    
365
  public DistortedProgram(final InputStream vertex, final InputStream fragment, final String vertexHeader, final String fragmentHeader, int glslVersion )
366
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
367
    {
368
    this(vertex,fragment,vertexHeader,fragmentHeader,glslVersion,null);
369
    }
370

    
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372
/**
373
 * Return the handle of the created program so that we can later, say, call glUseProgram.
374
 */
375
  public int getProgramHandle()
376
    {
377
    return mProgramHandle;
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381
/**
382
 * Use the program and enable all vertex attribute arrays.
383
 *
384
 * Needs to be called from a thread holding the OpenGL context.
385
 */
386
  public void useProgram()
387
    {
388
    GLES30.glUseProgram(mProgramHandle);
389

    
390
    for(int i=0; i<mNumAttributes; i++)
391
      GLES30.glEnableVertexAttribArray(mAttribute[i]);
392
    }
393
  }
394

    
395

    
(1-1/6)