Project

General

Profile

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

library / src / main / java / org / distorted / library / program / DistortedProgram.java @ 3d804c91

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.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

    
46
///////////////////////////////////////////////////////////////////////////////////////////////////
47

    
48
  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes)
49
  throws LinkingException
50
    {
51
    int programHandle = GLES20.glCreateProgram();
52

    
53
    if (programHandle != 0)
54
      {
55
      GLES20.glAttachShader(programHandle, vertexShaderHandle);
56
      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
57

    
58
      if (attributes != null)
59
        {
60
        final int size = attributes.length;
61

    
62
        for (int i = 0; i < size; i++)
63
          {
64
          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
65
          }
66
        }
67

    
68
      GLES20.glLinkProgram(programHandle);
69

    
70
      final int[] linkStatus = new int[1];
71
      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
72

    
73
      if (linkStatus[0] != GLES20.GL_TRUE )
74
        {
75
        String error = GLES20.glGetProgramInfoLog(programHandle);
76
        GLES20.glDeleteProgram(programHandle);
77
        throw new LinkingException(error);
78
        }
79

    
80
      final int[] numberOfUniforms = new int[1];
81
      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
82

    
83
      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
84
      }
85

    
86
    return programHandle;
87
    }
88

    
89
///////////////////////////////////////////////////////////////////////////////////////////////////
90

    
91
  private String parseOutAttribute(final String line)
92
    {
93
    int len = line.length();
94
    int whiteSpace, semicolon, nameBegin;
95
    char currChar;
96

    
97
    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
98
      {
99
      currChar = line.charAt(whiteSpace);
100
      if( currChar!=' ' && currChar!='\t') break;
101
      }
102

    
103
    for(semicolon=whiteSpace; semicolon<len; semicolon++)
104
      {
105
      currChar = line.charAt(semicolon);
106
      if( currChar==';') break;
107
      }
108

    
109
    if( semicolon<len && semicolon-whiteSpace>=11 )   // "attribute a;" --> 11
110
      {
111
      String subline = line.substring(whiteSpace,semicolon);
112
      int subLen = semicolon-whiteSpace;
113

    
114
      if( subline.startsWith("attribute"))
115
        {
116
        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
117

    
118
        for(nameBegin=subLen-1; nameBegin>8; nameBegin--)
119
          {
120
          currChar=subline.charAt(nameBegin);
121

    
122
          if( currChar==' ' || currChar=='\t' )
123
            {
124
            mNumAttributes++;
125
            return subline.substring(nameBegin+1,subLen);
126
            }
127
          }
128
        }
129
      }
130

    
131
    return null;
132
    }
133

    
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

    
136
  private String readTextFileFromRawResource(final InputStream inputStream, boolean doAttributes)
137
    {
138
    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
139
    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
140

    
141
    String nextLine, attribute, attrList="";
142
    final StringBuilder body = new StringBuilder();
143

    
144
    try
145
      {
146
      while ((nextLine = bufferedReader.readLine()) != null)
147
        {
148
        body.append(nextLine);
149
        body.append('\n');
150

    
151
        if( doAttributes )
152
          {
153
          attribute = parseOutAttribute(nextLine);
154

    
155
          if( attribute!=null )
156
            {
157
            //android.util.Log.d("program", "new attribute: "+attribute);
158
            if( attrList.length()>0 ) attrList+=" ";
159
            attrList += attribute;
160
            }
161
          }
162
        }
163
      }
164
    catch (IOException e)
165
      {
166
      return null;
167
      }
168

    
169
    if( doAttributes )
170
      {
171
      mAttribute = new int[mNumAttributes];
172
      mAttributeName = attrList.split(" ");
173
      }
174

    
175
    return body.toString();
176
    }
177

    
178
///////////////////////////////////////////////////////////////////////////////////////////////////
179

    
180
  private static void sanitizeMaxValues()
181
  throws VertexUniformsException,FragmentUniformsException
182
    {
183
    int maxV,maxF;
184
    int[] param = new int[1];
185

    
186
    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
187
    maxV = param[0];
188
    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
189
    maxF = param[0];
190

    
191
    //Log.d("program", "Max vectors in vertex shader: "+maxV);
192
    //Log.d("program", "Max vectors in fragment shader: "+maxF);
193

    
194
    if( !Build.FINGERPRINT.contains("generic") )
195
      {
196
      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
197
      int realMaxF = (maxF- 2)/4;   //
198

    
199
      if( DistortedEffects.getMaxVertex()   > realMaxV )
200
        {
201
        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
202
        }
203
      if( DistortedEffects.getMaxFragment() > realMaxF )
204
        {
205
        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
206
        }
207
      }
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  private static int compileShader(final int shaderType, final String shaderSource)
213
  throws FragmentCompilationException,VertexCompilationException
214
    {
215
    int shaderHandle = GLES20.glCreateShader(shaderType);
216

    
217
    if (shaderHandle != 0)
218
      {
219
      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
220
      GLES20.glCompileShader(shaderHandle);
221
      final int[] compileStatus = new int[1];
222
      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
223

    
224
      if (compileStatus[0] != GLES20.GL_TRUE )
225
        {
226
        GLES20.glDeleteShader(shaderHandle);
227
        shaderHandle = 0;
228
        }
229
      }
230

    
231
    if (shaderHandle == 0)
232
      {
233
      String error = GLES20.glGetShaderInfoLog(shaderHandle);
234

    
235
      switch(shaderType)
236
        {
237
        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
238
        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
239
        default                       : throw new RuntimeException(error);
240
        }
241
      }
242

    
243
    return shaderHandle;
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
  private static String generateShaderHeader(final int type)
249
    {
250
    String header="";
251

    
252
    switch(type)
253
      {
254
      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + DistortedEffects.getMaxVertex()+"\n");
255

    
256
                                      for(EffectNames name: EffectNames.values() )
257
                                        {
258
                                        if( name.getType()== EffectTypes.VERTEX)
259
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
260
                                        }
261
                                      break;
262
      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ DistortedEffects.getMaxFragment()+"\n");
263

    
264
                                      for(EffectNames name: EffectNames.values() )
265
                                        {
266
                                        if( name.getType()==EffectTypes.FRAGMENT)
267
                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
268
                                        }
269
                                      break;
270
     }
271

    
272
    //android.util.Log.d("program",""+header);
273

    
274
    return header;
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278
// PUBLIC API
279
///////////////////////////////////////////////////////////////////////////////////////////////////
280
  /**
281
   * Create a new Shader Program from source stored in resource files.
282
   * <p>
283
   * Needs to be called from a thread holding the OpenGL context.
284
   *
285
   * @param vertex   InputStream containing the opened Resource file from where to read vertex shader code.
286
   * @param fragment InputStream containing the opened Resource file from where to read fragment shader code.
287
   * @throws FragmentCompilationException
288
   * @throws VertexCompilationException
289
   * @throws VertexUniformsException
290
   * @throws FragmentUniformsException
291
   * @throws LinkingException
292
   */
293

    
294
  public DistortedProgram(final InputStream vertex, final InputStream fragment)
295
  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
296
    {
297
    mNumAttributes = 0;
298

    
299
    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
300
    final String fragmentShader = readTextFileFromRawResource(fragment, false);
301

    
302
    sanitizeMaxValues();
303

    
304
    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );
305
    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
306

    
307
    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName);
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311
/**
312
 * Return the indexes off all attributes.
313
 */
314
  public int[] getAttributes()
315
    {
316
    return mAttribute;
317
    }
318

    
319
///////////////////////////////////////////////////////////////////////////////////////////////////
320
/**
321
 * Return the names of all vertex attributes.
322
 */
323
  public String[] getAttributeNames()
324
    {
325
    return mAttributeName;
326
    }
327

    
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329
/**
330
 * Return the handle of the created program so that we can later, say, call glUseProgram.
331
 */
332
  public int getProgramHandle()
333
    {
334
    return mProgramHandle;
335
    }
336

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338
/**
339
 * Bind all vertex attributes and enable them.
340
 * <p>
341
 * This assumes Program is in use. Call glUseProgram first.
342
 * Needs to be called from a thread holding the OpenGL context.
343
 */
344

    
345
  public void bindAndEnableAttributes()
346
    {
347
    for(int i=0; i<mNumAttributes; i++)
348
      {
349
      mAttribute[i] = GLES20.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
350
      GLES20.glEnableVertexAttribArray(mAttribute[i]);
351
      }
352
    }
353
  }
(1-1/6)