From ebf64404beda0916c3682c437fd866f37c37a09b Mon Sep 17 00:00:00 2001
From: Alexander Rose <alexander.rose@weirdbyte.de>
Date: Sat, 6 Nov 2021 14:55:23 -0700
Subject: [PATCH] add loop unrolling glsl support

---
 src/mol-gl/shader-code.ts                    | 30 ++++++++++++++++++--
 src/mol-gl/shader/chunks/common-clip.glsl.ts |  4 ++-
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/mol-gl/shader-code.ts b/src/mol-gl/shader-code.ts
index 63836245a..5e7c10475 100644
--- a/src/mol-gl/shader-code.ts
+++ b/src/mol-gl/shader-code.ts
@@ -101,7 +101,8 @@ const ShaderChunks: { [k: string]: string } = {
     wboit_write
 };
 
-const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gmi;
+const reInclude = /^(?!\/\/)\s*#include\s+(\S+)/gm;
+const reUnrollLoop = /#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*\+\+i\s*\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;
 const reSingleLineComment = /[ \t]*\/\/.*\n/g;
 const reMultiLineComment = /[ \t]*\/\*[\s\S]*?\*\//g;
 const reMultipleLinebreaks = /\n{2,}/g;
@@ -119,6 +120,29 @@ function addIncludes(text: string) {
         .replace(reMultipleLinebreaks, '\n');
 }
 
+function unrollLoops(str: string) {
+    return str.replace(reUnrollLoop, loopReplacer);
+}
+
+function loopReplacer(match: string, start: string, end: string, snippet: string) {
+    let out = '';
+    for (let i = parseInt(start); i < parseInt(end); ++i) {
+        out += snippet
+            .replace(/\[\s*i\s*\]/g, `[${i}]`)
+            .replace(/UNROLLED_LOOP_INDEX/g, `${i}`);
+    }
+    return out;
+}
+
+function replaceCounts(str: string, defines: ShaderDefines) {
+    if (defines.dClipObjectCount) str = str.replace(/dClipObjectCount/g, `${defines.dClipObjectCount.ref.value}`);
+    return str;
+}
+
+function preprocess(str: string, defines: ShaderDefines) {
+    return unrollLoops(replaceCounts(str, defines));
+}
+
 export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}): ShaderCode {
     return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes };
 }
@@ -278,8 +302,8 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
     return {
         id: shaderCodeId(),
         name: shaders.name,
-        vert: `${vertPrefix}${header}${shaders.vert}`,
-        frag: `${fragPrefix}${header}${frag}`,
+        vert: `${vertPrefix}${header}${preprocess(shaders.vert, defines)}`,
+        frag: `${fragPrefix}${header}${preprocess(frag, defines)}`,
         extensions: shaders.extensions,
         outTypes: shaders.outTypes
     };
diff --git a/src/mol-gl/shader/chunks/common-clip.glsl.ts b/src/mol-gl/shader/chunks/common-clip.glsl.ts
index f38c78a1e..5bca9dec1 100644
--- a/src/mol-gl/shader/chunks/common-clip.glsl.ts
+++ b/src/mol-gl/shader/chunks/common-clip.glsl.ts
@@ -89,8 +89,9 @@ export const common_clip = `
 
     // flag is a bit-flag for clip-objects to ignore (note, object ids start at 1 not 0)
     bool clipTest(vec4 sphere, int flag) {
+        #pragma unroll_loop_start
         for (int i = 0; i < dClipObjectCount; ++i) {
-            if (flag == 0 || hasBit(flag, i + 1)) {
+            if (flag == 0 || hasBit(flag, UNROLLED_LOOP_INDEX + 1)) {
                 // TODO take sphere radius into account?
                 bool test = getSignedDistance(sphere.xyz, uClipObjectType[i], uClipObjectPosition[i], uClipObjectRotation[i], uClipObjectScale[i]) <= 0.0;
                 if ((!uClipObjectInvert[i] && test) || (uClipObjectInvert[i] && !test)) {
@@ -98,6 +99,7 @@ export const common_clip = `
                 }
             }
         }
+        #pragma unroll_loop_end
         return false;
     }
 #endif
-- 
GitLab