commit 8777ce17bf6160614ec61b6f4ed01b0f2ec08846
Author: Leszek Koltunski <leszek@koltunski.pl>
Date:   Tue Apr 17 22:21:30 2018 +0100

    Order Independent Transparency. Does not work yet.

diff --git a/src/main/java/org/distorted/library/main/DistortedEffects.java b/src/main/java/org/distorted/library/main/DistortedEffects.java
index 5a52229..5f6744a 100644
--- a/src/main/java/org/distorted/library/main/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/main/DistortedEffects.java
@@ -41,6 +41,7 @@ import java.io.InputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -74,6 +75,27 @@ public class DistortedEffects
   private static int mBlitDepthDepthTextureH;
   private static int mBlitDepthDepthH;
   private static int mBlitDepthTexCorrH;
+  private static int mBlitDepthSizeH;
+  private static int mBlitDepthNumRecordsH;
+
+  private static int[] mLinkedListSSBO = new int[1];
+  private static int[] mAtomicCounter = new int[1];
+
+  static
+    {
+    mLinkedListSSBO[0]= -1;
+    mAtomicCounter[0] = -1;
+    }
+
+  private static int mBufferSize=(0x1<<23);  // 8 million entries
+  private static IntBuffer mIntBuffer;
+
+  /// BLIT DEPTH RENDER PROGRAM ///
+  private static DistortedProgram mBlitDepthRenderProgram;
+  private static int mBlitDepthRenderDepthTextureH;
+  private static int mBlitDepthRenderDepthH;
+  private static int mBlitDepthRenderTexCorrH;
+  private static int mBlitDepthRenderSizeH;
 
   /// NORMAL PROGRAM /////
   private static DistortedProgram mNormalProgram;
@@ -164,6 +186,47 @@ public class DistortedEffects
     mBlitDepthDepthTextureH = GLES31.glGetUniformLocation( blitDepthProgramH, "u_DepthTexture");
     mBlitDepthDepthH        = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Depth");
     mBlitDepthTexCorrH      = GLES31.glGetUniformLocation( blitDepthProgramH, "u_TexCorr");
+    mBlitDepthSizeH         = GLES31.glGetUniformLocation( blitDepthProgramH, "u_Size");
+    mBlitDepthNumRecordsH   = GLES31.glGetUniformLocation( blitDepthProgramH, "u_numRecords");
+
+    mIntBuffer = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
+    mIntBuffer.put(0,0);
+
+    if( mLinkedListSSBO[0]<0 )
+      {
+      GLES31.glGenBuffers(1,mLinkedListSSBO,0);
+      GLES31.glBindBuffer(GLES31.GL_SHADER_STORAGE_BUFFER, mLinkedListSSBO[0]);
+      GLES31.glBufferData(GLES31.GL_SHADER_STORAGE_BUFFER, mBufferSize*4 , null, GLES31.GL_DYNAMIC_READ);
+      GLES31.glBindBufferBase(GLES31.GL_SHADER_STORAGE_BUFFER, 1, mLinkedListSSBO[0]);
+      }
+
+    if( mAtomicCounter[0]<0 )
+      {
+      GLES31.glGenBuffers(1,mAtomicCounter,0);
+      GLES31.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, mAtomicCounter[0] );
+      GLES31.glBufferData(GLES31.GL_ATOMIC_COUNTER_BUFFER, 4, mIntBuffer, GLES31.GL_DYNAMIC_DRAW);
+      GLES31.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0);
+      }
+
+    // BLIT DEPTH RENDER PROGRAM ///////////////////////////
+    final InputStream blitDepthRenderVertStream = resources.openRawResource(R.raw.blit_depth_vertex_shader);
+    final InputStream blitDepthRenderFragStream = resources.openRawResource(R.raw.blit_depth_render_fragment_shader);
+
+    try
+      {
+      mBlitDepthRenderProgram = new DistortedProgram(blitDepthRenderVertStream,blitDepthRenderFragStream,blitVertHeader,blitFragHeader, Distorted.GLSL);
+      }
+    catch(Exception e)
+      {
+      Log.e("EFFECTS", "exception trying to compile BLIT DEPTH RENDER program: "+e.getMessage());
+      throw new RuntimeException(e.getMessage());
+      }
+
+    int blitDepthRenderProgramH   = mBlitDepthRenderProgram.getProgramHandle();
+    mBlitDepthRenderDepthTextureH = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_DepthTexture");
+    mBlitDepthRenderDepthH        = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_Depth");
+    mBlitDepthRenderTexCorrH      = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_TexCorr");
+    mBlitDepthRenderSizeH         = GLES31.glGetUniformLocation( blitDepthRenderProgramH, "u_Size");
 
     // NORMAL PROGRAM //////////////////////////////////////
     final InputStream normalVertexStream   = resources.openRawResource(R.raw.normal_vertex_shader);
@@ -287,23 +350,11 @@ public class DistortedEffects
     GLES31.glUniform1i(mMainTextureH, 0);
     GLES31.glUniform1i(mCountIndexH, surface.getNewCounter() );
 
-    if( Distorted.GLSL >= 300 )
-      {
-      GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
-      GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
-      }
-    else
-      {
-      mesh.mVertAttribs.position(0);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      mesh.mVertAttribs.position(MeshObject.POS_DATA_SIZE);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      mesh.mVertAttribs.position(MeshObject.POS_DATA_SIZE+MeshObject.NOR_DATA_SIZE);
-      GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, mesh.mVertAttribs);
-      }
+    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, mesh.mAttVBO[0]);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[0], MeshObject.POS_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET0);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[1], MeshObject.NOR_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET1);
+    GLES31.glVertexAttribPointer(mMainProgram.mAttribute[2], MeshObject.TEX_DATA_SIZE, GLES31.GL_FLOAT, false, MeshObject.VERTSIZE, MeshObject.OFFSET2);
+    GLES31.glBindBuffer(GLES31.GL_ARRAY_BUFFER, 0);
 
     mM.send(surface,halfW,halfH,halfZ,marginInPixels);
     mV.send();
@@ -341,9 +392,36 @@ public class DistortedEffects
     GLES31.glUniform1i(mBlitDepthTextureH, 0);
     GLES31.glUniform1i(mBlitDepthDepthTextureH, 1);
     GLES31.glUniform2f(mBlitDepthTexCorrH, corrW, corrH );
-    GLES31.glUniform1f( mBlitDepthDepthH , 1.0f-surface.mNear);
+    GLES31.glUniform2i(mBlitDepthSizeH, surface.mWidth, surface.mHeight);
+    GLES31.glUniform1ui(mBlitDepthNumRecordsH, (mBufferSize-surface.mWidth*surface.mHeight)/3 );  // see the fragment shader
+    GLES31.glUniform1f(mBlitDepthDepthH , 1.0f-surface.mNear);
     GLES31.glVertexAttribPointer(mBlitDepthProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
     GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
+
+
+    //android.util.Log.e("effects", "bufferSize: "+mBufferSize+" numRecords: "+((mBufferSize-surface.mWidth*surface.mHeight)/3) );
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// render all the transparent pixels from the per-pixel linked lists. This is in the 'merge
+// postprocessing buckets' stage.
+
+  static void blitDepthRenderPriv(DistortedOutputSurface surface, float corrW, float corrH)
+    {
+    mBlitDepthRenderProgram.useProgram();
+
+    GLES31.glViewport(0, 0, surface.mWidth, surface.mHeight );
+    GLES31.glUniform1i(mBlitDepthRenderDepthTextureH, 1);
+    GLES31.glUniform2f(mBlitDepthRenderTexCorrH, corrW, corrH );
+    GLES31.glUniform2i(mBlitDepthRenderSizeH, surface.mWidth, surface.mHeight);
+    GLES31.glUniform1f( mBlitDepthRenderDepthH , 1.0f-surface.mNear);
+    GLES31.glVertexAttribPointer(mBlitDepthRenderProgram.mAttribute[0], 2, GLES31.GL_FLOAT, false, 0, mQuadPositions);
+    GLES31.glDrawArrays(GLES31.GL_TRIANGLE_STRIP, 0, 4);
+
+    // reset atomic counter to 0
+    GLES31.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, mAtomicCounter[0] );
+    GLES31.glBufferData(GLES31.GL_ATOMIC_COUNTER_BUFFER, 4, mIntBuffer, GLES31.GL_DYNAMIC_DRAW);
+    GLES31.glBindBuffer(GLES31.GL_ATOMIC_COUNTER_BUFFER, 0);
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -365,7 +443,9 @@ public class DistortedEffects
 
   static void onDestroy()
     {
-    mNextID = 0;
+    mNextID           =  0;
+    mLinkedListSSBO[0]= -1;
+    mAtomicCounter[0] = -1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
index c188ed0..53459da 100644
--- a/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
+++ b/src/main/java/org/distorted/library/main/DistortedOutputSurface.java
@@ -390,7 +390,33 @@ public static final int DEBUG_FPS = 1;
     GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
     GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);
 
-    // clear buffers
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int blitWithDepthRender(long currTime, DistortedOutputSurface buffer)
+    {
+    GLES31.glViewport(0, 0, mWidth, mHeight);
+    setAsOutput(currTime);
+    GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
+    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, buffer.mDepthStencilH[0]);
+
+    DistortedRenderState.enableStencil();
+
+    DistortedEffects.blitDepthRenderPriv(this, buffer.getWidthCorrection(), buffer.getHeightCorrection() );
+    GLES31.glActiveTexture(GLES31.GL_TEXTURE1);
+    GLES31.glBindTexture(GLES31.GL_TEXTURE_2D, 0);
+
+    DistortedRenderState.restoreStencil();
+
+    return 1;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void clearBuffer(DistortedOutputSurface buffer)
+    {
     GLES31.glStencilMask(0xff);
     GLES31.glDepthMask(true);
     GLES31.glColorMask(true,true,true,true);
@@ -403,8 +429,6 @@ public static final int DEBUG_FPS = 1;
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT|GLES31.GL_DEPTH_BUFFER_BIT|GLES31.GL_STENCIL_BUFFER_BIT);
     GLES31.glFramebufferTexture2D(GLES31.GL_FRAMEBUFFER, GLES31.GL_COLOR_ATTACHMENT0, GLES31.GL_TEXTURE_2D, buffer.mColorH[0], 0);
     GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
-
-    return 1;
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -442,8 +466,7 @@ public static final int DEBUG_FPS = 1;
 
             numRenders += lastQueue.postprocess(mBuffer);
             numRenders += blitWithDepth(time, mBuffer[quality]);
-
-            mBuffer[quality].setAsOutputAndClear(time);
+            clearBuffer(mBuffer[quality]);
             }
 
           internalQuality = currQueue.getInternalQuality();
@@ -465,6 +488,8 @@ public static final int DEBUG_FPS = 1;
 
           numRenders += currQueue.postprocess(mBuffer);
           numRenders += blitWithDepth(time, mBuffer[quality]);
+          numRenders += blitWithDepthRender(time,mBuffer[quality]);  // merge the OIT linked list
+          clearBuffer(mBuffer[quality]);
           }
         }
 
@@ -504,25 +529,6 @@ public static final int DEBUG_FPS = 1;
     return (float)mHeight/mRealHeight;
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Bind this Surface as a Framebuffer we can render to; always clear it's color bit.
- *
- * Useful for drawing to the postprocessing buffer, which must sometimes be cleared multiple times
- * per frame.
- */
-
-  private void setAsOutputAndClear(long time)
-    {
-    GLES31.glBindFramebuffer(GLES31.GL_FRAMEBUFFER, mFBOH[0]);
-
-    mTime = time;    // have to do this otherwise on the next setAsOutput() we would clear
-    DistortedRenderState.colorDepthStencilOn();
-    GLES31.glClearColor(mClearR, mClearG, mClearB, mClearA);
-    GLES31.glClear(GLES31.GL_COLOR_BUFFER_BIT);
-    DistortedRenderState.colorDepthStencilRestore();
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // PUBLIC API
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/main/DistortedRenderState.java b/src/main/java/org/distorted/library/main/DistortedRenderState.java
index ae10754..86a224f 100644
--- a/src/main/java/org/distorted/library/main/DistortedRenderState.java
+++ b/src/main/java/org/distorted/library/main/DistortedRenderState.java
@@ -199,6 +199,70 @@ public class DistortedRenderState
     GLES31.glDisable(GLES31.GL_SCISSOR_TEST);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void enableStencil()
+    {
+    sState.stencilTest = cState.stencilTest;
+
+    if (cState.stencilTest != 1)
+      {
+      cState.stencilTest = 1;
+      GLES31.glEnable(GLES31.GL_STENCIL_TEST);
+      }
+
+    sState.stencilFuncFunc = cState.stencilFuncFunc;
+    sState.stencilFuncRef  = cState.stencilFuncRef;
+    sState.stencilFuncMask = cState.stencilFuncMask;
+
+    if( cState.stencilFuncFunc!=GLES31.GL_EQUAL || cState.stencilFuncRef!=1 || cState.stencilFuncMask!=STENCIL_MASK )
+      {
+      cState.stencilFuncFunc = GLES31.GL_EQUAL;
+      cState.stencilFuncRef  = 1;
+      cState.stencilFuncMask = STENCIL_MASK;
+      GLES31.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
+      }
+
+    sState.stencilMask = cState.stencilMask;
+
+    if( cState.stencilMask!= 0x00 )
+      {
+      cState.stencilMask = 0x00;
+      GLES31.glStencilMask(cState.stencilMask);
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  static void restoreStencil()
+    {
+    if (sState.stencilTest != cState.stencilTest)
+      {
+      cState.stencilTest = sState.stencilTest;
+
+      if (cState.stencilTest == 0)
+        {
+        GLES31.glDisable(GLES31.GL_STENCIL_TEST);
+        }
+      else
+        {
+        GLES31.glEnable(GLES31.GL_STENCIL_TEST);
+        }
+      }
+    if( sState.stencilFuncFunc!=cState.stencilFuncFunc || sState.stencilFuncRef!=cState.stencilFuncRef || sState.stencilFuncMask!=cState.stencilFuncMask )
+      {
+      cState.stencilFuncFunc = sState.stencilFuncFunc;
+      cState.stencilFuncRef  = sState.stencilFuncRef ;
+      cState.stencilFuncMask = sState.stencilFuncMask;
+      GLES31.glStencilFunc(cState.stencilFuncFunc,cState.stencilFuncRef,cState.stencilFuncMask);
+      }
+    if( sState.stencilMask!=cState.stencilMask )
+      {
+      cState.stencilMask = sState.stencilMask;
+      GLES31.glStencilMask(cState.stencilMask);
+      }
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static void setUpStencilMark()
diff --git a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
index d7e9da7..d3bb295 100644
--- a/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/main/EffectQueuePostprocess.java
@@ -32,7 +32,6 @@ class EffectQueuePostprocess extends EffectQueue
   private static final int NUM_UNIFORMS = PostprocessEffect.NUM_UNIFORMS;
   private static final int INDEX = EffectType.POSTPROCESS.ordinal();
 
-
   private int mHalo;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/res/raw/blit_depth_fragment_shader.glsl b/src/main/res/raw/blit_depth_fragment_shader.glsl
index 5e72e64..5395e45 100644
--- a/src/main/res/raw/blit_depth_fragment_shader.glsl
+++ b/src/main/res/raw/blit_depth_fragment_shader.glsl
@@ -1,5 +1,5 @@
 //////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                          //
+// Copyright 2018 Leszek Koltunski                                                          //
 //                                                                                          //
 // This file is part of Distorted.                                                          //
 //                                                                                          //
@@ -18,14 +18,17 @@
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 precision lowp float;
+precision highp uint;
 
 #if __VERSION__ != 100
 out vec4 fragColor;           // The output color
 in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
+in vec2 v_Pixel;              // location of the current fragment, in pixels
 #define TEXTURE texture
 #define FRAG_COLOR fragColor
 #else
 varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
+varying vec2 v_Pixel;         // location of the current fragment, in pixels
 #define TEXTURE texture2D
 #define FRAG_COLOR gl_FragColor
 #endif
@@ -33,10 +36,87 @@ varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
 uniform sampler2D u_Texture;
 uniform sampler2D u_DepthTexture;
 
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform ivec2 u_Size;
+uniform uint u_numRecords;
+
+layout (binding=0, offset=0) uniform atomic_uint u_Counter;  // initialize to 0
+
+layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
+  {                                          // one for each pixel in the Output rectangle.
+  uint u_Records[];                          //
+  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
+                                             // triplets of (pointer,depth,rgba).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Concurrent insert to a linked list. Tim Harris, 'pragmatic implementation of non-blocking
+// linked-lists', 2001.
+// This arranges fragments by decreasing 'depth', so one would think - from back to front, but
+// in main() below the depth is mapped with S*(1-depth)/2, so it is really front to back.
+
+void insert( vec2 ij, uint depth, uint rgba )
+  {
+  uint ptr = atomicCounterIncrement(u_Counter)-1u;
+
+  if( ptr<u_numRecords )
+    {
+    ptr = 3u*ptr + uint(u_Size.x*u_Size.y);
+
+    u_Records[ptr+1u] = depth;
+    u_Records[ptr+2u] = rgba;
+
+    memoryBarrier();
+
+    uint prev = uint(ij.x + ij.y * float(u_Size.x));
+    uint curr = u_Records[prev];
+/*
+    while (true)
+      {
+      if ( curr==0u || depth > u_Records[curr+1u] )  // need to insert here
+        {
+*/
+        u_Records[ptr] = curr; // next of new record is curr
+        memoryBarrier();
+        uint res = atomicCompSwap( u_Records[prev], curr, ptr );
+/*
+        if (res==curr) break;      // done!
+        else           curr = res; // could not insert! retry from same place in list
+        }
+      else // advance in list
+        {
+        prev = curr;
+        curr = u_Records[prev];
+        }
+      }
+*/
+    }
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+uint convert(vec4 c)
+  {
+  return ((uint(255.0*c.r))<<24u) + ((uint(255.0*c.g))<<16u) + ((uint(255.0*c.b))<<8u) + uint(255.0*c.a);
+  }
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()                    		
   {
-  gl_FragDepth = TEXTURE(u_DepthTexture,v_TexCoordinate).r;
-  FRAG_COLOR   = TEXTURE(u_Texture     ,v_TexCoordinate);
+  vec4 frag  = TEXTURE(u_Texture     , v_TexCoordinate);
+  float depth= TEXTURE(u_DepthTexture, v_TexCoordinate).r;
+
+  if( frag.a > 0.95 )
+    {
+    gl_FragDepth = depth;
+    FRAG_COLOR   = frag;
+    }
+  else if( frag.a > 0.0 )
+    {
+    const float S= 2147483647.0; // max signed int. Could probably be max unsigned int but this is enough.
+    insert(v_Pixel, uint(S*(1.0-depth)/2.0), convert(frag) );
+    discard;
+    }
   }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_depth_render_fragment_shader.glsl b/src/main/res/raw/blit_depth_render_fragment_shader.glsl
new file mode 100644
index 0000000..06273ae
--- /dev/null
+++ b/src/main/res/raw/blit_depth_render_fragment_shader.glsl
@@ -0,0 +1,91 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2018 Leszek Koltunski                                                          //
+//                                                                                          //
+// This file is part of Distorted.                                                          //
+//                                                                                          //
+// Distorted is free software: you can redistribute it and/or modify                        //
+// it under the terms of the GNU General Public License as published by                     //
+// the Free Software Foundation, either version 2 of the License, or                        //
+// (at your option) any later version.                                                      //
+//                                                                                          //
+// Distorted is distributed in the hope that it will be useful,                             //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of                           //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            //
+// GNU General Public License for more details.                                             //
+//                                                                                          //
+// You should have received a copy of the GNU General Public License                        //
+// along with Distorted.  If not, see <http://www.gnu.org/licenses/>.                       //
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+precision lowp float;
+precision highp uint;
+
+#if __VERSION__ != 100
+out vec4 fragColor;           // The output color
+in vec2 v_TexCoordinate;      // Interpolated texture coordinate per fragment.
+in vec2 v_Pixel;              // location of the current fragment, in pixels
+#define TEXTURE texture
+#define FRAG_COLOR fragColor
+#else
+varying vec2 v_TexCoordinate; // Interpolated texture coordinate per fragment.
+varying vec2 v_Pixel;         // location of the current fragment, in pixels
+#define TEXTURE texture2D
+#define FRAG_COLOR gl_FragColor
+#endif
+
+uniform sampler2D u_DepthTexture;
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// per-pixel linked list. Order Independent Transparency.
+
+uniform ivec2 u_Size;
+
+layout (std430,binding=1) buffer linkedlist  // first (u_Size.x*u_Size.y) uints - head pointers,
+  {                                          // one for each pixel in the Output rectangle.
+  uint u_Records[];                          //
+  };                                         // Next 3*u_numRecords uints - actual linked list, i.e.
+                                             // triplets of (pointer,depth,rgba).
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+vec4 convert(uint rgba)
+  {
+  return vec4( float((rgba>>24u)&255u),float((rgba>>16u)&255u),float((rgba>>8u)&255u),float(rgba&255u) ) / 255.0;
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// https://en.wikipedia.org/wiki/Alpha_compositing (premultiplied)
+
+vec4 blend(vec4 clr,vec4 srf)
+  {
+  return clr + (1.0 - clr.a) * vec4(srf.rgb * srf.a , srf.a);
+  }
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+void main()                    		
+  {
+  uint index = uint(v_Pixel.x + v_Pixel.y * float(u_Size.x));
+  uint curr = u_Records[index];
+
+  if (curr == 0u) discard;
+
+  vec4 color= vec4(0.0,0.0,0.0,0.0);
+  u_Records[index] = 0u;
+
+  while (curr > 0u)                                      // keep walking the linked list
+    {                                                    // and blending the colors in
+    curr = u_Records[curr];                              //
+    //color= blend( color, convert(u_Records[curr+2u]) );  //
+
+    color = convert(u_Records[curr+2u]);
+
+    if( color.a == 0.0 ) color = vec4(0.0,1.0,0.0,1.0);
+    }
+
+  gl_FragDepth = TEXTURE(u_DepthTexture, v_TexCoordinate).r;
+  FRAG_COLOR = color;
+
+//else
+//  FRAG_COLOR = vec4(1.0,0.0,0.0,1.0);
+  }
\ No newline at end of file
diff --git a/src/main/res/raw/blit_depth_vertex_shader.glsl b/src/main/res/raw/blit_depth_vertex_shader.glsl
index a9d3811..d8ecabe 100644
--- a/src/main/res/raw/blit_depth_vertex_shader.glsl
+++ b/src/main/res/raw/blit_depth_vertex_shader.glsl
@@ -18,13 +18,16 @@
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 precision lowp float;
+precision highp uint;
 
 #if __VERSION__ != 100
 in vec2 a_Position;           // Per-vertex position.
 out vec2 v_TexCoordinate;     //
+out vec2 v_Pixel;             //
 #else
 attribute vec2 a_Position;    // Per-vertex position.
 varying vec2 v_TexCoordinate; //
+varying vec2 v_Pixel;         //
 #endif
 
 uniform float u_Depth;        // distance from the near plane to render plane, in clip coords
@@ -33,10 +36,13 @@ uniform vec2  u_TexCorr;      // when we blit from postprocessing buffers, the b
                               // reused!) so we need to compensate here by adjusting the texture
                               // coords.
 
+uniform ivec2 u_Size;         // size of the output surface, in pixels.
+
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 void main()
   {
   v_TexCoordinate = (a_Position + 0.5) * u_TexCorr;
+  v_Pixel         = v_TexCoordinate * vec2(u_Size);
   gl_Position     = vec4(2.0*a_Position,u_Depth,1.0);
   }
