Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 432442f9

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
import android.opengl.GLES20;
23
import android.os.Build;
24
25
import org.distorted.library.DistortedEffects;
26
import org.distorted.library.EffectNames;
27
import org.distorted.library.EffectTypes;
28
29
import java.io.BufferedReader;
30
import java.io.IOException;
31
import java.io.InputStream;
32
import java.io.InputStreamReader;
33
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35
/**
36
 * An object which encapsulates a vertex/fragment shader combo, aka Shader Program.
37
 */
38
public class DistortedProgram
39
  {
40
  private int mProgramHandle;
41
42
  private int mNumAttributes;
43
  private int[] mAttribute;
44
  private String[] mAttributeName;
45
  private String mAttributeTmp;
46
47
///////////////////////////////////////////////////////////////////////////////////////////////////
48
49
  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes)
50
  throws LinkingException
51
    {
52
    int programHandle = GLES20.glCreateProgram();
53
54
    if (programHandle != 0)
55
      {
56
      GLES20.glAttachShader(programHandle, vertexShaderHandle);
57
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
58
59
      if (attributes != null)
60
        {
61
        final int size = attributes.length;
62
63
        for (int i = 0; i < size; i++)
64
          {
65
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
66
          }
67
        }
68
69
      GLES20.glLinkProgram(programHandle);
70
71
      final int[] linkStatus = new int[1];
72
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
73
74
      if (linkStatus[0] != GLES20.GL_TRUE )
75
        {
76
        String error = GLES20.glGetProgramInfoLog(programHandle);
77
        GLES20.glDeleteProgram(programHandle);
78
        throw new LinkingException(error);
79
        }
80
81
      final int[] numberOfUniforms = new int[1];
82
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
83
84
      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
85
      }
86
87
    return programHandle;
88
    }
89
90
///////////////////////////////////////////////////////////////////////////////////////////////////
91
92
  private void doAttributes(final String line)
93
    {
94
    int len = line.length();
95
    int whiteSpace, semicolon, nameBegin;
96
    char currChar;
97
98
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
99
      {
100
      currChar = line.charAt(whiteSpace);
101
      if( currChar!=' ' && currChar!='\t') break;
102
      }
103
104
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
105
      {
106
      currChar = line.charAt(semicolon);
107
      if( currChar==';') break;
108
      }
109
110
    if( semicolon<len && semicolon-whiteSpace>=11 )   // "attribute a;" --> 11
111
      {
112
      String subline = line.substring(whiteSpace,semicolon);
113
      int subLen = semicolon-whiteSpace;
114
115
      if( subline.startsWith("attribute"))
116
        {
117
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
118
119
        for(nameBegin=subLen-1; nameBegin>8; nameBegin--)
120
          {
121
          currChar=subline.charAt(nameBegin);
122
123
          if( currChar==' ' || currChar=='\t' )
124
            {
125
            subline = subline.substring(nameBegin+1,subLen);
126
            //android.util.Log.d("program", "new attribute: "+subline);
127
128
            if( mAttributeTmp.length()>0 ) mAttributeTmp+=" ";
129
            mAttributeTmp += subline;
130
            mNumAttributes++;
131
            break;
132
            }
133
          }
134
        }
135
      }
136
    }
137
138
///////////////////////////////////////////////////////////////////////////////////////////////////
139
140
  private String readTextFileFromRawResource(final InputStream inputStream, boolean doAttributes)
141
    {
142
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
143
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
144
145
    String nextLine;
146
    final StringBuilder body = new StringBuilder();
147
148
    try
149
      {
150
      while ((nextLine = bufferedReader.readLine()) != null)
151
        {
152
        body.append(nextLine);
153
        body.append('\n');
154
155
        if( doAttributes ) doAttributes(nextLine);
156
        }
157
      }
158
    catch (IOException e)
159
      {
160
      return null;
161
      }
162
163
    if( doAttributes )
164
      {
165
      mAttribute = new int[mNumAttributes];
166
      mAttributeName = mAttributeTmp.split(" ");
167
      mAttributeTmp = "";
168
/*
169
      int len = mAttributeName.length;
170
171
      for(int i=0; i<len; i++)
172
        {
173
        android.util.Log.e("program","ATTRIBUTE "+i+" :" + mAttributeName[i]);
174
        }
175
176
      android.util.Log.e("program","mNumAttributes: "+mNumAttributes);
177
*/
178
      }
179
180
    return body.toString();
181
    }
182
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184
185
  private static void sanitizeMaxValues()
186
  throws VertexUniformsException,FragmentUniformsException
187
    {
188
    int maxV,maxF;
189
    int[] param = new int[1];
190
191
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
192
    maxV = param[0];
193
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
194
    maxF = param[0];
195
196
    //Log.d("program", "Max vectors in vertex shader: "+maxV);
197
    //Log.d("program", "Max vectors in fragment shader: "+maxF);
198
199
    if( !Build.FINGERPRINT.contains("generic") )
200
      {
201
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
202
      int realMaxF = (maxF- 2)/4;   //
203
204
      if( DistortedEffects.getMaxVertex()   > realMaxV )
205
        {
206
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
207
        }
208
      if( DistortedEffects.getMaxFragment() > realMaxF )
209
        {
210
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
211
        }
212
      }
213
    }
214
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216
217
  private static int compileShader(final int shaderType, final String shaderSource)
218
  throws FragmentCompilationException,VertexCompilationException
219
    {
220
    int shaderHandle = GLES20.glCreateShader(shaderType);
221
222
    if (shaderHandle != 0)
223
      {
224
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
225
      GLES20.glCompileShader(shaderHandle);
226
      final int[] compileStatus = new int[1];
227
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
228
229
      if (compileStatus[0] != GLES20.GL_TRUE )
230
        {
231
        GLES20.glDeleteShader(shaderHandle);
232
        shaderHandle = 0;
233
        }
234
      }
235
236
    if (shaderHandle == 0)
237
      {
238
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
239
240
      switch(shaderType)
241
        {
242
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
243
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
244
        default                       : throw new RuntimeException(error);
245
        }
246
      }
247
248
    return shaderHandle;
249
    }
250
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252
253
  private static String generateShaderHeader(final int type)
254
    {
255
    String header="";
256
257
    switch(type)
258
      {
259
      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + DistortedEffects.getMaxVertex()+"\n");
260
261
                                      for(EffectNames name: EffectNames.values() )
262
                                        {
263
                                        if( name.getType()== EffectTypes.VERTEX)
264
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
265
                                        }
266
                                      break;
267
      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ DistortedEffects.getMaxFragment()+"\n");
268
269
                                      for(EffectNames name: EffectNames.values() )
270
                                        {
271
                                        if( name.getType()==EffectTypes.FRAGMENT)
272
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
273
                                        }
274
                                      break;
275
     }
276
277
    //Log.d(TAG,""+header);
278
279
    return header;
280
    }
281
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283
// PUBLIC API
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
  /**
286
   * Create a new Shader Program from source stored in resource files.
287
   * <p>
288
   * Needs to be called from a thread holding the OpenGL context.
289
   *
290
   * @param vertex   InputStream containing the opened Resource file from where to read vertex shader code.
291
   * @param fragment InputStream containing the opened Resource file from where to read fragment shader code.
292
   *
293
   */
294
295
  public DistortedProgram(final InputStream vertex, final InputStream fragment)
296
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
297
    {
298
    mNumAttributes = 0;
299
    mAttributeTmp  = "";
300
301
    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
302
    final String fragmentShader = readTextFileFromRawResource(fragment, false);
303
304
    sanitizeMaxValues();
305
306
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );
307
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
308
309
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName);
310
    }
311
312
///////////////////////////////////////////////////////////////////////////////////////////////////
313
314
  public int[] getAttributes()
315
    {
316
    return mAttribute;
317
    }
318
319
///////////////////////////////////////////////////////////////////////////////////////////////////
320
321
  public String[] getAttributeNames()
322
    {
323
    return mAttributeName;
324
    }
325
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327
328
  public int getProgramHandle()
329
    {
330
    return mProgramHandle;
331
    }
332
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334
// glUseProgram first; needs to be called from a thread holding the OpenGL context
335
336
  public void bindAndEnableAttributes()
337
    {
338
    for(int i=0; i<mNumAttributes; i++)
339
      {
340
      mAttribute[i] = GLES20.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
341
      GLES20.glEnableVertexAttribArray(mAttribute[i]);
342
      }
343
    }
344
  }