commit d99fcc9c0cc46839892926bc78c2716a8864031f
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Jun 16 20:37:49 2020 +0100

    Only compile the Full, Normal & OIT programs when they are actually needed.

diff --git a/src/main/java/org/distorted/library/main/DistortedLibrary.java b/src/main/java/org/distorted/library/main/DistortedLibrary.java
index 6b64066..1eda4fe 100644
--- a/src/main/java/org/distorted/library/main/DistortedLibrary.java
+++ b/src/main/java/org/distorted/library/main/DistortedLibrary.java
@@ -58,7 +58,7 @@ public class DistortedLibrary
   {
   private static int mGLSL;
   private static String mGLSL_VERSION;
-  private static boolean mOITCompilationSuccessful;
+  private static boolean mOITCompilationAttempted;
   /**
    * When creating an instance of a DistortedTexture from another instance, clone the Bitmap that's
    * backing up our DistortedTexture.
@@ -202,6 +202,21 @@ public class DistortedLibrary
 
   /// END PROGRAMS //////
 
+  /**
+   * Every application using the library must implement this interface so that the library can send
+   * it exceptions that arise. The exceptions may come at any time, for example the library will
+   * compile its OIT problem only on the first attempt to use the OIT
+   * Those will mainly be hardware-related: shaders do not compile on particular hardware, the required
+   * OpenGL ES 3.0 is not supported, etc.
+   */
+  public interface ExceptionListener
+    {
+    void distortedException(Exception ex);
+    }
+
+  private static ExceptionListener mListener;
+  private static Resources mResources;
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // private: hide this from Javadoc
 
@@ -212,11 +227,11 @@ public class DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createPrograms(Resources resources)
+  private static void createMainProgram()
     {
     // MAIN PROGRAM ////////////////////////////////////
-    final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
-    final InputStream mainFragStream = resources.openRawResource(R.raw.main_fragment_shader);
+    final InputStream mainVertStream = mResources.openRawResource(R.raw.main_vertex_shader);
+    final InputStream mainFragStream = mResources.openRawResource(R.raw.main_fragment_shader);
 
     int numF = FragmentEffect.getNumEnabled();
     int numV = VertexEffect.getNumEnabled();
@@ -248,8 +263,8 @@ public class DistortedLibrary
     mMainTextureH= GLES30.glGetUniformLocation( mainProgramH, "u_Texture");
 
     // BLIT PROGRAM ////////////////////////////////////
-    final InputStream blitVertStream = resources.openRawResource(R.raw.blit_vertex_shader);
-    final InputStream blitFragStream = resources.openRawResource(R.raw.blit_fragment_shader);
+    final InputStream blitVertStream = mResources.openRawResource(R.raw.blit_vertex_shader);
+    final InputStream blitFragStream = mResources.openRawResource(R.raw.blit_fragment_shader);
 
     try
       {
@@ -266,8 +281,8 @@ public class DistortedLibrary
     mBlitDepthH    = GLES30.glGetUniformLocation( blitProgramH, "u_Depth");
 
     // BLIT DEPTH PROGRAM ////////////////////////////////////
-    final InputStream blitDepthVertStream = resources.openRawResource(R.raw.blit_depth_vertex_shader);
-    final InputStream blitDepthFragStream = resources.openRawResource(R.raw.blit_depth_fragment_shader);
+    final InputStream blitDepthVertStream = mResources.openRawResource(R.raw.blit_depth_vertex_shader);
+    final InputStream blitDepthFragStream = mResources.openRawResource(R.raw.blit_depth_fragment_shader);
 
     try
       {
@@ -284,7 +299,12 @@ public class DistortedLibrary
     mBlitDepthDepthTextureH = GLES30.glGetUniformLocation( blitDepthProgramH, "u_DepthTexture");
     mBlitDepthDepthH        = GLES30.glGetUniformLocation( blitDepthProgramH, "u_Depth");
     mBlitDepthTexCorrH      = GLES30.glGetUniformLocation( blitDepthProgramH, "u_TexCorr");
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
 
+  private static void createNormalProgram(Resources resources)
+    {
     // NORMAL PROGRAM //////////////////////////////////////
     final InputStream normalVertexStream   = resources.openRawResource(R.raw.normal_vertex_shader);
     final InputStream normalFragmentStream = resources.openRawResource(R.raw.normal_fragment_shader);
@@ -342,7 +362,7 @@ public class DistortedLibrary
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private static void createProgramsOIT(Resources resources)
+  private static void createOITProgram(Resources resources)
     {
     // MAIN OIT PROGRAM ////////////////////////////////
     final InputStream mainVertStream = resources.openRawResource(R.raw.main_vertex_shader);
@@ -462,6 +482,19 @@ public class DistortedLibrary
 
   private static void displayNormals(EffectQueue[] queues, MeshBase mesh)
     {
+    if( mNormalProgram==null )
+      {
+      try
+        {
+        createNormalProgram(mResources);
+        }
+      catch(Exception ex)
+        {
+        mListener.distortedException(ex);
+        return;
+        }
+      }
+
     int num = mesh.getNumVertices();
     int tfo = mesh.getTFO();
 
@@ -485,26 +518,36 @@ public class DistortedLibrary
 
   public static void adjustVertices(MeshBase mesh, EffectQueueVertex queue)
     {
-    if( mFullProgram!=null )
+    if( mFullProgram==null )
       {
-      int num = mesh.getNumVertices();
-      int tfo = mesh.getTFO();
-
-      mFullProgram.useProgram();
-      mesh.bindVertexAttribs(mFullProgram);
-      queue.compute(1);
-      queue.send(0.0f,3);
-      mesh.send(3);
-
-      GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo );
-      GLES30.glBeginTransformFeedback( GLES30.GL_POINTS);
-      InternalRenderState.switchOffDrawing();
-      GLES30.glDrawArrays( GLES30.GL_POINTS, 0, num );
-      InternalRenderState.restoreDrawing();
-      GLES30.glEndTransformFeedback();
-      mesh.copyTransformToVertex();
-      GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
+      try
+        {
+        createFullProgram(mResources);
+        }
+      catch(Exception ex)
+        {
+        mListener.distortedException(ex);
+        return;
+        }
       }
+
+    int num = mesh.getNumVertices();
+    int tfo = mesh.getTFO();
+
+    mFullProgram.useProgram();
+    mesh.bindVertexAttribs(mFullProgram);
+    queue.compute(1);
+    queue.send(0.0f,3);
+    mesh.send(3);
+
+    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfo );
+    GLES30.glBeginTransformFeedback( GLES30.GL_POINTS);
+    InternalRenderState.switchOffDrawing();
+    GLES30.glDrawArrays( GLES30.GL_POINTS, 0, num );
+    InternalRenderState.restoreDrawing();
+    GLES30.glEndTransformFeedback();
+    mesh.copyTransformToVertex();
+    GLES30.glBindBufferBase(GLES30.GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -516,11 +559,11 @@ public class DistortedLibrary
     EffectQueue.compute(queues, currTime);
     GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
 
-    DistortedLibrary.mMainOITProgram.useProgram();
-    GLES30.glUniform1i(DistortedLibrary.mMainOITTextureH, 0);
-    GLES30.glUniform2ui(DistortedLibrary.mMainOITSizeH, surface.mWidth, surface.mHeight);
-    GLES30.glUniform1ui(DistortedLibrary.mMainOITNumRecordsH, (int)(DistortedLibrary.mBufferSize*surface.mWidth*surface.mHeight) );
-    mesh.bindVertexAttribs(DistortedLibrary.mMainOITProgram);
+    mMainOITProgram.useProgram();
+    GLES30.glUniform1i(mMainOITTextureH, 0);
+    GLES30.glUniform2ui(mMainOITSizeH, surface.mWidth, surface.mHeight);
+    GLES30.glUniform1ui(mMainOITNumRecordsH, (int)(mBufferSize*surface.mWidth*surface.mHeight) );
+    mesh.bindVertexAttribs(mMainOITProgram);
     mesh.send(1);
 
     float inflate     = mesh.getInflate();
@@ -533,7 +576,7 @@ public class DistortedLibrary
 
     if( mesh.getShowNormals() )
       {
-      DistortedLibrary.mMainProgram.useProgram();
+      mMainProgram.useProgram();
       mesh.send(0);
       EffectQueue.send(queues, distance, mipmap, projection, inflate, mesh, 0 );
       displayNormals(queues,mesh);
@@ -667,7 +710,7 @@ public class DistortedLibrary
       }
 
     // reading the value of the buffer on every frame would slow down rendering by
-    // dialog_about 3%; doing it only once every 5 frames affects speed by less than 1%.
+    // about 3%; doing it only once every 5 frames affects speed by less than 1%.
     if( mCurrBuffer==0 )
       {
       GLES30.glBindBufferBase(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0, mAtomicCounter[mCurrBuffer]);
@@ -687,45 +730,65 @@ public class DistortedLibrary
 
   static void oitClear(InternalOutputSurface surface, int counter)
     {
-    if( mOITClearProgram!=null )
+    if( mOITClearProgram==null )
       {
-      if( mLinkedListSSBO[0]<0 )
+      android.util.Log.e("aa", "attempting OIT compilation");
+
+      if( mGLSL>=310 && !mOITCompilationAttempted )
         {
-        GLES30.glGenBuffers(1,mLinkedListSSBO,0);
+        mOITCompilationAttempted = true;
 
-        int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
-        GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
-        GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
-        GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
+        try
+          {
+          createOITProgram(mResources);
+          }
+        catch(Exception ex)
+          {
+          mListener.distortedException(ex);
+          return;
+          }
 
-        GLES30.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 1, mLinkedListSSBO[0]);
+        android.util.Log.e("aa", "OIT compilation successfull");
         }
-
-      // See if we have overflown the SSBO in one of the previous frames.
-      // If yes, assume we need to make the SSBO larger.
-      float overflow = counter/(mBufferSize*surface.mWidth*surface.mHeight);
-
-      if( overflow>1.0f )
+      else
         {
-        //android.util.Log.e("effects", "previous frame rendered "+counter+
-        //                   " fragments, but there was only "+(mBufferSize*surface.mWidth*surface.mHeight)+" space");
-
-        mBufferSize *= (int)(overflow+1.0f);
-        int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
-        GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
-        GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
-        GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
+        return;
         }
+      }
 
-      mOITClearProgram.useProgram();
+    if( mLinkedListSSBO[0]<0 )
+      {
+      GLES30.glGenBuffers(1,mLinkedListSSBO,0);
 
-      GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
-      GLES30.glUniform2f(mOITClearTexCorrH, 1.0f, 1.0f );   // corrections do not really matter here - only present because of common vertex shader.
-      GLES30.glUniform1f( mOITClearDepthH , 1.0f);          // likewise depth
-      GLES30.glUniform2ui(mOITClearSizeH, surface.mWidth, surface.mHeight);
-      GLES30.glVertexAttribPointer(mOITClearProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
-      GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
+      int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
+      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
+      GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
+      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
+
+      GLES30.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 1, mLinkedListSSBO[0]);
+      }
+
+    // See if we have overflown the SSBO in one of the previous frames.
+    // If yes, assume we need to make the SSBO larger.
+    float overflow = counter/(mBufferSize*surface.mWidth*surface.mHeight);
+
+    if( overflow>1.0f )
+      {
+      mBufferSize *= (int)(overflow+1.0f);
+      int size = (int)(surface.mWidth*surface.mHeight*(3*mBufferSize+1)*4);
+      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
+      GLES30.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, size, null, GLES30.GL_DYNAMIC_READ|GLES30.GL_DYNAMIC_DRAW);
+      GLES30.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, 0);
       }
+
+    mOITClearProgram.useProgram();
+
+    GLES30.glViewport(0, 0, surface.mWidth, surface.mHeight );
+    GLES30.glUniform2f(mOITClearTexCorrH, 1.0f, 1.0f );   // corrections do not really matter here - only present because of common vertex shader.
+    GLES30.glUniform1f( mOITClearDepthH , 1.0f);          // likewise depth
+    GLES30.glUniform2ui(mOITClearSizeH, surface.mWidth, surface.mHeight);
+    GLES30.glVertexAttribPointer(mOITClearProgram.mAttribute[0], 2, GLES30.GL_FLOAT, false, 0, mQuadPositions);
+    GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -864,20 +927,6 @@ public class DistortedLibrary
     return mGLSL;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Have we successfully managed to compile the OIT programs?
- * Some hardware ( Imagination GE8100 / 8300, driver build 1.8@4490469 present in my HTC phone and,
- * sadly, Amazon Fire Stick 4K ) fails to compile the shaders. See relevant thread in Imagination's
- * support forum:
- *
- * https://forums.imgtec.com/t/ge8100-in-htc-desire-12-android-7-1-1-fragment-shader-fails-to-compile/2708
- */
-  public static boolean OITCompilationSuccessful()
-    {
-    return mOITCompilationSuccessful;
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * Have we called onCreate yet, ie have we initialized the library?
@@ -896,10 +945,11 @@ public class DistortedLibrary
  * Needs to be called from a thread holding the OpenGL context.
  *
  * @param context Context of the App using the library - used to open up Resources and read Shader code.
+ * @param listener The library will send all (asynchronous!) exceptions there.
  */
-  public static void onCreate(final Context context) throws Exception
+  public static void onCreate(final Context context, final ExceptionListener listener)
     {
-    onCreate(context,4);
+    onCreate(context,listener,4);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -910,9 +960,10 @@ public class DistortedLibrary
  * Needs to be called from a thread holding the OpenGL context.
  *   
  * @param context Context of the App using the library - used to open up Resources and read Shader code.
+ * @param listener The library will send all (asynchronous!) exceptions there.
  * @param queueSize the size of the FBO queue, a workaround for the bug on Mali drivers.
  */
-  public static void onCreate(final Context context, int queueSize) throws Exception
+  public static void onCreate(final Context context, final ExceptionListener listener, int queueSize)
     {
     final ActivityManager activityManager     = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
     final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo();
@@ -921,10 +972,13 @@ public class DistortedLibrary
     int major = glESversion >> 16;
     int minor = glESversion & 0xff;
 
+    mListener = listener;
+
     if( major< 3 )
       {
       mGLSL = 100*major + 10*minor;
-      throw new VertexCompilationException("at least OpenGL ES 3.0 required, this device supports only "+major+"."+minor);
+      VertexCompilationException ex = new VertexCompilationException("at least OpenGL ES 3.0 required, this device supports only "+major+"."+minor);
+      mListener.distortedException(ex);
       }
     else
       {
@@ -935,54 +989,30 @@ public class DistortedLibrary
     mGLSL_VERSION = "#version "+mGLSL+" es\n";
 
     mInitialized = true;
-    mOITCompilationSuccessful = false;
+    mOITCompilationAttempted = false;
 
     detectBuggyDriversAndSetQueueSize(queueSize);
 
     EffectMessageSender.startSending();
 
-    final Resources resources = context.getResources();
-
-    Exception exception=null;
+    mResources = context.getResources();
 
     try
       {
-      createPrograms(resources);
+      createMainProgram();
       }
     catch(Exception ex)
       {
-      exception = ex;
+      mListener.distortedException(ex);
       }
 
     try
       {
-      createFullProgram(resources);
+      EffectQueuePostprocess.createPrograms(mResources, mGLSL);
       }
     catch(Exception ex)
       {
-      exception = ex;
-      }
-
-    if( mGLSL>=310)
-      {
-      try
-        {
-        createProgramsOIT(resources);
-        mOITCompilationSuccessful = true;
-        }
-      catch(Exception ex)
-        {
-        exception = ex;
-        }
-      }
-
-    try
-      {
-      EffectQueuePostprocess.createPrograms(resources, mGLSL);
-      }
-    catch(Exception ex)
-      {
-      exception = ex;
+      mListener.distortedException(ex);
       }
 
     try
@@ -991,12 +1021,7 @@ public class DistortedLibrary
       }
     catch(Exception ex)
       {
-      exception = ex;
-      }
-
-    if( exception!=null)
-      {
-      throw exception;
+      mListener.distortedException(ex);
       }
     }
 
@@ -1031,6 +1056,18 @@ public class DistortedLibrary
     EffectMessageSender.stopSending();
 
     mInitialized = false;
+    mOITCompilationAttempted = false;
+
+    mNormalProgram     = null;
+    mMainOITProgram    = null;
+    mMainProgram       = null;
+    mFullProgram       = null;
+    mOITClearProgram   = null;
+    mOITBuildProgram   = null;
+    mOITCollapseProgram= null;
+    mOITRenderProgram  = null;
+    mBlitDepthProgram  = null;
+    mBlitProgram       = null;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/InternalOutputSurface.java b/src/main/java/org/distorted/library/main/InternalOutputSurface.java
index 9f25922..9297ce2 100644
--- a/src/main/java/org/distorted/library/main/InternalOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/InternalOutputSurface.java
@@ -838,7 +838,7 @@ public abstract class InternalOutputSurface extends InternalSurface implements I
  */
   public void setOrderIndependentTransparency(boolean oit)
     {
-    if( DistortedLibrary.getGLSL()>=310 && DistortedLibrary.OITCompilationSuccessful() )
+    if( DistortedLibrary.getGLSL()>=310 )
       {
       mRenderWayOIT = oit;
       }
@@ -866,7 +866,7 @@ public abstract class InternalOutputSurface extends InternalSurface implements I
  */
   public void setOrderIndependentTransparency(boolean oit, float initialSize)
     {
-    if( DistortedLibrary.getGLSL()>=310 && DistortedLibrary.OITCompilationSuccessful() )
+    if( DistortedLibrary.getGLSL()>=310 )
       {
       mRenderWayOIT = oit;
 
