Browse Source

Merge pull request #320 from molstar/shader-compilation

Shader compilation
Alexander Rose 3 years ago
parent
commit
7fc1866dac
49 changed files with 301 additions and 237 deletions
  1. 9 0
      CHANGELOG.md
  2. 2 2
      src/extensions/model-archive/quality-assessment/behavior.ts
  3. 2 1
      src/mol-canvas3d/canvas3d.ts
  4. 2 1
      src/mol-canvas3d/helper/bounding-sphere-helper.ts
  5. 3 2
      src/mol-canvas3d/helper/camera-helper.ts
  6. 2 1
      src/mol-canvas3d/helper/handle-helper.ts
  7. 10 10
      src/mol-canvas3d/passes/pick.ts
  8. 3 3
      src/mol-canvas3d/passes/postprocessing.ts
  9. 1 1
      src/mol-canvas3d/passes/smaa.ts
  10. 2 2
      src/mol-geo/geometry/cylinders/cylinders.ts
  11. 2 2
      src/mol-geo/geometry/direct-volume/direct-volume.ts
  12. 1 1
      src/mol-geo/geometry/lines/lines.ts
  13. 0 5
      src/mol-geo/geometry/marker-data.ts
  14. 2 2
      src/mol-geo/geometry/mesh/mesh.ts
  15. 2 2
      src/mol-geo/geometry/spheres/spheres.ts
  16. 2 2
      src/mol-geo/geometry/texture-mesh/texture-mesh.ts
  17. 5 5
      src/mol-gl/_spec/renderer.spec.ts
  18. 11 10
      src/mol-gl/render-object.ts
  19. 4 4
      src/mol-gl/renderable/cylinders.ts
  20. 4 4
      src/mol-gl/renderable/direct-volume.ts
  21. 3 3
      src/mol-gl/renderable/image.ts
  22. 5 5
      src/mol-gl/renderable/lines.ts
  23. 4 4
      src/mol-gl/renderable/mesh.ts
  24. 3 3
      src/mol-gl/renderable/points.ts
  25. 2 1
      src/mol-gl/renderable/schema.ts
  26. 4 4
      src/mol-gl/renderable/spheres.ts
  27. 3 3
      src/mol-gl/renderable/text.ts
  28. 4 4
      src/mol-gl/renderable/texture-mesh.ts
  29. 26 8
      src/mol-gl/renderer.ts
  30. 6 5
      src/mol-gl/scene.ts
  31. 41 13
      src/mol-gl/shader-code.ts
  32. 3 1
      src/mol-gl/shader/chunks/apply-light-color.glsl.ts
  33. 4 4
      src/mol-gl/shader/chunks/assign-color-varying.glsl.ts
  34. 1 1
      src/mol-gl/shader/chunks/assign-marker-varying.glsl.ts
  35. 27 25
      src/mol-gl/shader/chunks/assign-material-color.glsl.ts
  36. 5 5
      src/mol-gl/shader/chunks/color-frag-params.glsl.ts
  37. 4 4
      src/mol-gl/shader/chunks/color-vert-params.glsl.ts
  38. 5 2
      src/mol-gl/shader/chunks/common-frag-params.glsl.ts
  39. 4 2
      src/mol-gl/shader/chunks/common-vert-params.glsl.ts
  40. 2 10
      src/mol-gl/shader/chunks/common.glsl.ts
  41. 2 2
      src/mol-gl/shader/cylinders.frag.ts
  42. 28 29
      src/mol-gl/shader/direct-volume.frag.ts
  43. 15 17
      src/mol-gl/shader/image.frag.ts
  44. 1 3
      src/mol-gl/shader/mesh.frag.ts
  45. 4 2
      src/mol-gl/shader/mesh.vert.ts
  46. 3 4
      src/mol-gl/shader/spheres.frag.ts
  47. 8 6
      src/mol-gl/webgl/render-item.ts
  48. 6 1
      src/mol-gl/webgl/resources.ts
  49. 9 6
      src/mol-repr/visual.ts

+ 9 - 0
CHANGELOG.md

@@ -6,6 +6,15 @@ Note that since we don't clearly distinguish between a public and private interf
 
 ## [Unreleased]
 
+- Reduce number of created programs/shaders
+    - Support specifying variants when creating graphics render-items
+    - Change double-side shader param from define to uniform
+    - Remove dMarkerType shader define (use uMarker as needed)
+    - Support to ignore defines depending on the shader variant
+    - Combine pickObject/pickInstance/pickGroup shader variants into one
+    - Combine markingDepth/markingMask shader variants into one
+    - Correctly set shader define flags for overpaint, transparency, substance, clipping
+
 ## [v3.0.0-dev.6] - 2021-12-19
 
 - Enable temporal multi-sampling by default

+ 2 - 2
src/extensions/model-archive/quality-assessment/behavior.ts

@@ -26,7 +26,7 @@ export const MAQualityAssessment = PluginBehavior.create<{ autoAttach: boolean,
         description: 'Data included in Model Archive files.'
     },
     ctor: class extends PluginBehavior.Handler<{ autoAttach: boolean, showTooltip: boolean }> {
-        private provider = QualityAssessmentProvider
+        private provider = QualityAssessmentProvider;
 
         private labelProvider = {
             label: (loci: Loci): string | undefined => {
@@ -36,7 +36,7 @@ export const MAQualityAssessment = PluginBehavior.create<{ autoAttach: boolean,
                     qmeanLabel(loci),
                 ].filter(l => !!l).join('</br>');
             }
-        }
+        };
 
         register(): void {
             DefaultQueryRuntimeTable.addCustomProp(this.provider.descriptor);

+ 2 - 1
src/mol-canvas3d/canvas3d.ts

@@ -39,6 +39,7 @@ import { Helper } from './helper/helper';
 import { Passes } from './passes/passes';
 import { shallowEqual } from '../mol-util';
 import { MarkingParams } from './passes/marking';
+import { GraphicsRenderVariantsBlended, GraphicsRenderVariantsWboit } from '../mol-gl/webgl/render-item';
 
 export const Canvas3DParams = {
     camera: PD.Group({
@@ -296,7 +297,7 @@ namespace Canvas3D {
         let height = 128;
         updateViewport();
 
-        const scene = Scene.create(webgl);
+        const scene = Scene.create(webgl, passes.draw.wboitEnabled ? GraphicsRenderVariantsWboit : GraphicsRenderVariantsBlended);
 
         const camera = new Camera({
             position: Vec3.create(0, 0, 100),

+ 2 - 1
src/mol-canvas3d/helper/bounding-sphere-helper.ts

@@ -18,6 +18,7 @@ import { TransformData } from '../../mol-geo/geometry/transform-data';
 import { sphereVertexCount } from '../../mol-geo/primitive/sphere';
 import { ValueCell } from '../../mol-util';
 import { Geometry } from '../../mol-geo/geometry/geometry';
+import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
 
 export const DebugHelperParams = {
     sceneBoundingSpheres: PD.Boolean(false, { description: 'Show full scene bounding spheres.' }),
@@ -41,7 +42,7 @@ export class BoundingSphereHelper {
     private visibleSceneData: BoundingSphereData | undefined;
 
     constructor(ctx: WebGLContext, parent: Scene, props: Partial<DebugHelperProps>) {
-        this.scene = Scene.create(ctx);
+        this.scene = Scene.create(ctx, GraphicsRenderVariantsBlended);
         this.parent = parent;
         this._props = { ...PD.getDefaultValues(DebugHelperParams), ...props };
     }

+ 3 - 2
src/mol-canvas3d/helper/camera-helper.ts

@@ -14,6 +14,7 @@ import { PickingId } from '../../mol-geo/geometry/picking';
 import { GraphicsRenderObject } from '../../mol-gl/render-object';
 import { Scene } from '../../mol-gl/scene';
 import { WebGLContext } from '../../mol-gl/webgl/context';
+import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
 import { Sphere3D } from '../../mol-math/geometry';
 import { Mat4, Vec3 } from '../../mol-math/linear-algebra';
 import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
@@ -58,7 +59,7 @@ export class CameraHelper {
     private renderObject: GraphicsRenderObject | undefined;
 
     constructor(private webgl: WebGLContext, props: Partial<CameraHelperProps> = {}) {
-        this.scene = Scene.create(webgl);
+        this.scene = Scene.create(webgl, GraphicsRenderVariantsBlended);
 
         this.camera = new Camera();
         Vec3.set(this.camera.up, 0, 1, 0);
@@ -201,7 +202,7 @@ function createAxesMesh(scale: number, mesh?: Mesh) {
     const x = Vec3.scale(Vec3(), Vec3.unitX, scale);
     const y = Vec3.scale(Vec3(), Vec3.unitY, scale);
     const z = Vec3.scale(Vec3(), Vec3.unitZ, scale);
-    const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
+    const cylinderProps = { radiusTop: radius, radiusBottom: radius, radialSegments: 32 };
 
     state.currentGroup = CameraHelperAxis.None;
     addSphere(state, Vec3.origin, radius, 2);

+ 2 - 1
src/mol-canvas3d/helper/handle-helper.ts

@@ -24,6 +24,7 @@ import { DataLoci, EmptyLoci, Loci } from '../../mol-model/loci';
 import { MarkerAction, MarkerActions } from '../../mol-util/marker-action';
 import { Visual } from '../../mol-repr/visual';
 import { Interval } from '../../mol-data/int';
+import { GraphicsRenderVariantsBlended } from '../../mol-gl/webgl/render-item';
 
 const HandleParams = {
     ...Mesh.Params,
@@ -127,7 +128,7 @@ export class HandleHelper {
     }
 
     constructor(private webgl: WebGLContext, props: Partial<HandleHelperProps> = {}) {
-        this.scene = Scene.create(webgl);
+        this.scene = Scene.create(webgl, GraphicsRenderVariantsBlended);
         this.setProps(props);
     }
 }

+ 10 - 10
src/mol-canvas3d/passes/pick.ts

@@ -5,7 +5,7 @@
  */
 
 import { PickingId } from '../../mol-geo/geometry/picking';
-import { Renderer } from '../../mol-gl/renderer';
+import { PickType, Renderer } from '../../mol-gl/renderer';
 import { Scene } from '../../mol-gl/scene';
 import { WebGLContext } from '../../mol-gl/webgl/context';
 import { GraphicsRenderVariant } from '../../mol-gl/webgl/render-item';
@@ -64,35 +64,35 @@ export class PickPass {
         }
     }
 
-    private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant) {
+    private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: GraphicsRenderVariant, pickType: number) {
         const depth = this.drawPass.depthTexturePrimitives;
         renderer.clear(false);
 
         renderer.update(camera);
-        renderer.renderPick(scene.primitives, camera, variant, null);
-        renderer.renderPick(scene.volumes, camera, variant, depth);
-        renderer.renderPick(helper.handle.scene, camera, variant, null);
+        renderer.renderPick(scene.primitives, camera, variant, null, pickType);
+        renderer.renderPick(scene.volumes, camera, variant, depth, pickType);
+        renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
 
         if (helper.camera.isEnabled) {
             helper.camera.update(camera);
             renderer.update(helper.camera.camera);
-            renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null);
+            renderer.renderPick(helper.camera.scene, helper.camera.camera, variant, null, pickType);
         }
     }
 
     render(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper) {
         this.objectPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pickObject');
+        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Object);
 
         this.instancePickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pickInstance');
+        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Instance);
 
         this.groupPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'pickGroup');
+        this.renderVariant(renderer, camera, scene, helper, 'pick', PickType.Group);
         // printTexture(this.webgl, this.groupPickTarget.texture, { id: 'group' })
 
         this.depthPickTarget.bind();
-        this.renderVariant(renderer, camera, scene, helper, 'depth');
+        this.renderVariant(renderer, camera, scene, helper, 'depth', PickType.None);
     }
 }
 

+ 3 - 3
src/mol-canvas3d/passes/postprocessing.ts

@@ -86,7 +86,7 @@ function getSsaoRenderable(ctx: WebGLContext, depthTexture: Texture): SsaoRender
         tDepth: ValueCell.create(depthTexture),
 
         uSamples: ValueCell.create([0.0, 0.0, 1.0]),
-        dNSamples: ValueCell.create(1),
+        dNSamples: ValueCell.create(32),
 
         uProjection: ValueCell.create(Mat4.identity()),
         uInvProjection: ValueCell.create(Mat4.identity()),
@@ -133,7 +133,7 @@ function getSsaoBlurRenderable(ctx: WebGLContext, ssaoDepthTexture: Texture, dir
         uTexSize: ValueCell.create(Vec2.create(ssaoDepthTexture.getWidth(), ssaoDepthTexture.getHeight())),
 
         uKernel: ValueCell.create([0.0]),
-        dOcclusionKernelSize: ValueCell.create(1),
+        dOcclusionKernelSize: ValueCell.create(15),
 
         uBlurDirectionX: ValueCell.create(direction === 'horizontal' ? 1 : 0),
         uBlurDirectionY: ValueCell.create(direction === 'vertical' ? 1 : 0),
@@ -226,7 +226,7 @@ function getPostprocessingRenderable(ctx: WebGLContext, colorTexture: Texture, d
 
         uMaxPossibleViewZDiff: ValueCell.create(0.5),
 
-        dOcclusionEnable: ValueCell.create(false),
+        dOcclusionEnable: ValueCell.create(true),
 
         dOutlineEnable: ValueCell.create(false),
         dOutlineScale: ValueCell.create(1),

+ 1 - 1
src/mol-canvas3d/passes/smaa.ts

@@ -201,7 +201,7 @@ function getWeightsRenderable(ctx: WebGLContext, edgesTexture: Texture): Weights
         uTexSizeInv: ValueCell.create(Vec2.create(1 / width, 1 / height)),
         uViewport: ValueCell.create(Vec4()),
 
-        dMaxSearchSteps: ValueCell.create(8),
+        dMaxSearchSteps: ValueCell.create(16),
     };
 
     // Note: loading image textures requires `HTMLImageElement` to be available

+ 2 - 2
src/mol-geo/geometry/cylinders/cylinders.ts

@@ -236,7 +236,7 @@ export namespace Cylinders {
 
             ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor * props.sizeAspectRatio),
-            dDoubleSided: ValueCell.create(props.doubleSided),
+            uDoubleSided: ValueCell.create(props.doubleSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
             dXrayShaded: ValueCell.create(props.xrayShaded),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
@@ -253,7 +253,7 @@ export namespace Cylinders {
     function updateValues(values: CylindersValues, props: PD.Values<Params>) {
         BaseGeometry.updateValues(values, props);
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor * props.sizeAspectRatio);
-        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);

+ 2 - 2
src/mol-geo/geometry/direct-volume/direct-volume.ts

@@ -307,7 +307,7 @@ export namespace DirectVolume {
             dPackedGroup: directVolume.packedGroup,
             dSingleLayer: ValueCell.create(singleLayer),
 
-            dDoubleSided: ValueCell.create(props.doubleSided),
+            uDoubleSided: ValueCell.create(props.doubleSided),
             dFlatShaded: ValueCell.create(props.flatShaded),
             dFlipSided: ValueCell.create(props.flipSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
@@ -323,7 +323,7 @@ export namespace DirectVolume {
 
     function updateValues(values: DirectVolumeValues, props: PD.Values<Params>) {
         BaseGeometry.updateValues(values, props);
-        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
         ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);

+ 1 - 1
src/mol-geo/geometry/lines/lines.ts

@@ -240,7 +240,7 @@ export namespace Lines {
             ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
             dLineSizeAttenuation: ValueCell.create(props.lineSizeAttenuation),
-            dDoubleSided: ValueCell.create(true),
+            uDoubleSided: ValueCell.create(true),
             dFlipSided: ValueCell.create(false),
         };
     }

+ 0 - 5
src/mol-geo/geometry/marker-data.ts

@@ -12,7 +12,6 @@ export type MarkerData = {
     uMarker: ValueCell<number>,
     tMarker: ValueCell<TextureImage<Uint8Array>>
     uMarkerTexDim: ValueCell<Vec2>
-    dMarkerType: ValueCell<string>,
     markerAverage: ValueCell<number>
     markerStatus: ValueCell<number>
 }
@@ -66,7 +65,6 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
         ValueCell.updateIfChanged(markerData.uMarker, 0);
         ValueCell.update(markerData.tMarker, markers);
         ValueCell.update(markerData.uMarkerTexDim, Vec2.create(markers.width, markers.height));
-        ValueCell.updateIfChanged(markerData.dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
         ValueCell.updateIfChanged(markerData.markerAverage, average);
         ValueCell.updateIfChanged(markerData.markerStatus, status);
         return markerData;
@@ -77,7 +75,6 @@ export function createMarkers(count: number, markerData?: MarkerData): MarkerDat
             uMarkerTexDim: ValueCell.create(Vec2.create(markers.width, markers.height)),
             markerAverage: ValueCell.create(average),
             markerStatus: ValueCell.create(status),
-            dMarkerType: ValueCell.create('uniform'),
         };
     }
 }
@@ -88,7 +85,6 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
         ValueCell.updateIfChanged(markerData.uMarker, 0);
         ValueCell.update(markerData.tMarker, emptyMarkerTexture);
         ValueCell.update(markerData.uMarkerTexDim, Vec2.create(1, 1));
-        ValueCell.updateIfChanged(markerData.dMarkerType, 'uniform');
         ValueCell.updateIfChanged(markerData.markerAverage, 0);
         ValueCell.updateIfChanged(markerData.markerStatus, 0);
         return markerData;
@@ -99,7 +95,6 @@ export function createEmptyMarkers(markerData?: MarkerData): MarkerData {
             uMarkerTexDim: ValueCell.create(Vec2.create(1, 1)),
             markerAverage: ValueCell.create(0),
             markerStatus: ValueCell.create(0),
-            dMarkerType: ValueCell.create('uniform'),
         };
     }
 }

+ 2 - 2
src/mol-geo/geometry/mesh/mesh.ts

@@ -693,7 +693,7 @@ export namespace Mesh {
             ...transform,
 
             ...BaseGeometry.createValues(props, counts),
-            dDoubleSided: ValueCell.create(props.doubleSided),
+            uDoubleSided: ValueCell.create(props.doubleSided),
             dFlatShaded: ValueCell.create(props.flatShaded),
             dFlipSided: ValueCell.create(props.flipSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
@@ -713,7 +713,7 @@ export namespace Mesh {
 
     function updateValues(values: MeshValues, props: PD.Values<Params>) {
         BaseGeometry.updateValues(values, props);
-        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
         ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);

+ 2 - 2
src/mol-geo/geometry/spheres/spheres.ts

@@ -203,7 +203,7 @@ export namespace Spheres {
 
             ...BaseGeometry.createValues(props, counts),
             uSizeFactor: ValueCell.create(props.sizeFactor),
-            dDoubleSided: ValueCell.create(props.doubleSided),
+            uDoubleSided: ValueCell.create(props.doubleSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
             dXrayShaded: ValueCell.create(props.xrayShaded),
             uBumpFrequency: ValueCell.create(props.bumpFrequency),
@@ -220,7 +220,7 @@ export namespace Spheres {
     function updateValues(values: SpheresValues, props: PD.Values<Params>) {
         BaseGeometry.updateValues(values, props);
         ValueCell.updateIfChanged(values.uSizeFactor, props.sizeFactor);
-        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);
         ValueCell.updateIfChanged(values.dXrayShaded, props.xrayShaded);
         ValueCell.updateIfChanged(values.uBumpFrequency, props.bumpFrequency);

+ 2 - 2
src/mol-geo/geometry/texture-mesh/texture-mesh.ts

@@ -165,7 +165,7 @@ export namespace TextureMesh {
             ...transform,
 
             ...BaseGeometry.createValues(props, counts),
-            dDoubleSided: ValueCell.create(props.doubleSided),
+            uDoubleSided: ValueCell.create(props.doubleSided),
             dFlatShaded: ValueCell.create(props.flatShaded),
             dFlipSided: ValueCell.create(props.flipSided),
             dIgnoreLight: ValueCell.create(props.ignoreLight),
@@ -186,7 +186,7 @@ export namespace TextureMesh {
 
     function updateValues(values: TextureMeshValues, props: PD.Values<Params>) {
         BaseGeometry.updateValues(values, props);
-        ValueCell.updateIfChanged(values.dDoubleSided, props.doubleSided);
+        ValueCell.updateIfChanged(values.uDoubleSided, props.doubleSided);
         ValueCell.updateIfChanged(values.dFlatShaded, props.flatShaded);
         ValueCell.updateIfChanged(values.dFlipSided, props.flipSided);
         ValueCell.updateIfChanged(values.dIgnoreLight, props.ignoreLight);

+ 5 - 5
src/mol-gl/_spec/renderer.spec.ts

@@ -53,17 +53,17 @@ describe('renderer', () => {
         scene.commit();
         expect(ctx.stats.resourceCounts.attribute).toBe(ctx.isWebGL2 ? 4 : 5);
         expect(ctx.stats.resourceCounts.texture).toBe(8);
-        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 8 : 0);
-        expect(ctx.stats.resourceCounts.program).toBe(8);
-        expect(ctx.stats.resourceCounts.shader).toBe(16);
+        expect(ctx.stats.resourceCounts.vertexArray).toBe(ctx.extensions.vertexArrayObject ? 5 : 0);
+        expect(ctx.stats.resourceCounts.program).toBe(5);
+        expect(ctx.stats.resourceCounts.shader).toBe(10);
 
         scene.remove(points);
         scene.commit();
         expect(ctx.stats.resourceCounts.attribute).toBe(0);
         expect(ctx.stats.resourceCounts.texture).toBe(0);
         expect(ctx.stats.resourceCounts.vertexArray).toBe(0);
-        expect(ctx.stats.resourceCounts.program).toBe(8);
-        expect(ctx.stats.resourceCounts.shader).toBe(16);
+        expect(ctx.stats.resourceCounts.program).toBe(5);
+        expect(ctx.stats.resourceCounts.shader).toBe(10);
 
         ctx.resources.destroy();
         expect(ctx.stats.resourceCounts.program).toBe(0);

+ 11 - 10
src/mol-gl/render-object.ts

@@ -16,6 +16,7 @@ import { TextValues, TextRenderable } from './renderable/text';
 import { TextureMeshValues, TextureMeshRenderable } from './renderable/texture-mesh';
 import { ImageValues, ImageRenderable } from './renderable/image';
 import { CylindersRenderable, CylindersValues } from './renderable/cylinders';
+import { GraphicsRenderVariant } from './webgl/render-item';
 
 const getNextId = idFactory(0, 0x7FFFFFFF);
 
@@ -48,17 +49,17 @@ export function createRenderObject<T extends RenderObjectType>(type: T, values:
     return { id: getNextId(), type, values, state, materialId } as GraphicsRenderObject<T>;
 }
 
-export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>): Renderable<any> {
+export function createRenderable<T extends RenderObjectType>(ctx: WebGLContext, o: GraphicsRenderObject<T>, variants: GraphicsRenderVariant[]): Renderable<any> {
     switch (o.type) {
-        case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId);
-        case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId);
-        case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId);
-        case 'cylinders': return CylindersRenderable(ctx, o.id, o.values as CylindersValues, o.state, o.materialId);
-        case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId);
-        case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId);
-        case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId);
-        case 'image': return ImageRenderable(ctx, o.id, o.values as ImageValues, o.state, o.materialId);
-        case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId);
+        case 'mesh': return MeshRenderable(ctx, o.id, o.values as MeshValues, o.state, o.materialId, variants);
+        case 'points': return PointsRenderable(ctx, o.id, o.values as PointsValues, o.state, o.materialId, variants);
+        case 'spheres': return SpheresRenderable(ctx, o.id, o.values as SpheresValues, o.state, o.materialId, variants);
+        case 'cylinders': return CylindersRenderable(ctx, o.id, o.values as CylindersValues, o.state, o.materialId, variants);
+        case 'text': return TextRenderable(ctx, o.id, o.values as TextValues, o.state, o.materialId, variants);
+        case 'lines': return LinesRenderable(ctx, o.id, o.values as LinesValues, o.state, o.materialId, variants);
+        case 'direct-volume': return DirectVolumeRenderable(ctx, o.id, o.values as DirectVolumeValues, o.state, o.materialId, variants);
+        case 'image': return ImageRenderable(ctx, o.id, o.values as ImageValues, o.state, o.materialId, variants);
+        case 'texture-mesh': return TextureMeshRenderable(ctx, o.id, o.values as TextureMeshValues, o.state, o.materialId, variants);
     }
     throw new Error('unsupported type');
 }

+ 4 - 4
src/mol-gl/renderable/cylinders.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
 import { CylindersShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -23,7 +23,7 @@ export const CylindersSchema = {
     elements: ElementsSpec('uint32'),
 
     padding: ValueSpec('number'),
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     uBumpFrequency: UniformSpec('f'),
@@ -32,12 +32,12 @@ export const CylindersSchema = {
 export type CylindersSchema = typeof CylindersSchema
 export type CylindersValues = Values<CylindersSchema>
 
-export function CylindersRenderable(ctx: WebGLContext, id: number, values: CylindersValues, state: RenderableState, materialId: number): Renderable<CylindersValues> {
+export function CylindersRenderable(ctx: WebGLContext, id: number, values: CylindersValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<CylindersValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...CylindersSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = CylindersShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 4 - 4
src/mol-gl/renderable/direct-volume.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { AttributeSpec, Values, UniformSpec, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, GlobalTextureSchema, BaseSchema } from './schema';
 import { DirectVolumeShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -46,7 +46,7 @@ export const DirectVolumeSchema = {
     uUnitToCartn: UniformSpec('m4'),
     dPackedGroup: DefineSpec('boolean'),
 
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dFlipSided: DefineSpec('boolean'),
     dFlatShaded: DefineSpec('boolean'),
     dIgnoreLight: DefineSpec('boolean'),
@@ -55,7 +55,7 @@ export const DirectVolumeSchema = {
 export type DirectVolumeSchema = typeof DirectVolumeSchema
 export type DirectVolumeValues = Values<DirectVolumeSchema>
 
-export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: DirectVolumeValues, state: RenderableState, materialId: number): Renderable<DirectVolumeValues> {
+export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: DirectVolumeValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<DirectVolumeValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...DirectVolumeSchema };
     if (!ctx.isWebGL2) {
         // workaround for webgl1 limitation that loop counters need to be `const`
@@ -65,6 +65,6 @@ export function DirectVolumeRenderable(ctx: WebGLContext, id: number, values: Di
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = DirectVolumeShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 3 - 3
src/mol-gl/renderable/image.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { AttributeSpec, Values, GlobalUniformSchema, InternalSchema, TextureSpec, ElementsSpec, DefineSpec, InternalValues, BaseSchema, UniformSpec, GlobalTextureSchema } from './schema';
 import { ImageShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -29,12 +29,12 @@ export const ImageSchema = {
 export type ImageSchema = typeof ImageSchema
 export type ImageValues = Values<ImageSchema>
 
-export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValues, state: RenderableState, materialId: number): Renderable<ImageValues> {
+export function ImageRenderable(ctx: WebGLContext, id: number, values: ImageValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<ImageValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...ImageSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = ImageShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 5 - 5
src/mol-gl/renderable/lines.ts

@@ -6,8 +6,8 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
-import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec, InternalValues, GlobalTextureSchema } from './schema';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
+import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, ElementsSpec, InternalValues, GlobalTextureSchema, UniformSpec } from './schema';
 import { ValueCell } from '../../mol-util';
 import { LinesShaderCode } from '../shader-code';
 
@@ -20,19 +20,19 @@ export const LinesSchema = {
     aEnd: AttributeSpec('float32', 3, 0),
     elements: ElementsSpec('uint32'),
     dLineSizeAttenuation: DefineSpec('boolean'),
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dFlipSided: DefineSpec('boolean'),
 };
 export type LinesSchema = typeof LinesSchema
 export type LinesValues = Values<LinesSchema>
 
-export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValues, state: RenderableState, materialId: number): Renderable<LinesValues> {
+export function LinesRenderable(ctx: WebGLContext, id: number, values: LinesValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<LinesValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...LinesSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = LinesShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
 
     return createRenderable(renderItem, values, state);
 }

+ 4 - 4
src/mol-gl/renderable/mesh.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, AttributeSpec, ElementsSpec, DefineSpec, Values, InternalSchema, InternalValues, GlobalTextureSchema, ValueSpec, UniformSpec } from './schema';
 import { MeshShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -18,7 +18,7 @@ export const MeshSchema = {
     aNormal: AttributeSpec('float32', 3, 0),
     elements: ElementsSpec('uint32'),
     dFlatShaded: DefineSpec('boolean'),
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dFlipSided: DefineSpec('boolean'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
@@ -29,13 +29,13 @@ export const MeshSchema = {
 export type MeshSchema = typeof MeshSchema
 export type MeshValues = Values<MeshSchema>
 
-export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues, state: RenderableState, materialId: number): Renderable<MeshValues> {
+export function MeshRenderable(ctx: WebGLContext, id: number, values: MeshValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<MeshValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...MeshSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = MeshShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
 
     return createRenderable(renderItem, values, state);
 }

+ 3 - 3
src/mol-gl/renderable/points.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, AttributeSpec, DefineSpec, Values, InternalSchema, SizeSchema, InternalValues, GlobalTextureSchema } from './schema';
 import { PointsShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -22,12 +22,12 @@ export const PointsSchema = {
 export type PointsSchema = typeof PointsSchema
 export type PointsValues = Values<PointsSchema>
 
-export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsValues, state: RenderableState, materialId: number): Renderable<PointsValues> {
+export function PointsRenderable(ctx: WebGLContext, id: number, values: PointsValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<PointsValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...PointsSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = PointsShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'points', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 2 - 1
src/mol-gl/renderable/schema.ts

@@ -161,6 +161,8 @@ export const GlobalUniformSchema = {
 
     uRenderWboit: UniformSpec('b'),
     uMarkingDepthTest: UniformSpec('b'),
+    uMarkingType: UniformSpec('i'),
+    uPickType: UniformSpec('i'),
 } as const;
 export type GlobalUniformSchema = typeof GlobalUniformSchema
 export type GlobalUniformValues = Values<GlobalUniformSchema>
@@ -207,7 +209,6 @@ export const MarkerSchema = {
     uMarker: UniformSpec('f'),
     uMarkerTexDim: UniformSpec('v2'),
     tMarker: TextureSpec('image-uint8', 'alpha', 'ubyte', 'nearest'),
-    dMarkerType: DefineSpec('string', ['uniform', 'groupInstance']),
     markerAverage: ValueSpec('number'),
     markerStatus: ValueSpec('number'),
 } as const;

+ 4 - 4
src/mol-gl/renderable/spheres.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, AttributeSpec, Values, InternalSchema, SizeSchema, InternalValues, ElementsSpec, ValueSpec, DefineSpec, GlobalTextureSchema, UniformSpec } from './schema';
 import { SpheresShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -20,7 +20,7 @@ export const SpheresSchema = {
     elements: ElementsSpec('uint32'),
 
     padding: ValueSpec('number'),
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
     uBumpFrequency: UniformSpec('f'),
@@ -29,12 +29,12 @@ export const SpheresSchema = {
 export type SpheresSchema = typeof SpheresSchema
 export type SpheresValues = Values<SpheresSchema>
 
-export function SpheresRenderable(ctx: WebGLContext, id: number, values: SpheresValues, state: RenderableState, materialId: number): Renderable<SpheresValues> {
+export function SpheresRenderable(ctx: WebGLContext, id: number, values: SpheresValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<SpheresValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...SpheresSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = SpheresShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 3 - 3
src/mol-gl/renderable/text.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, AttributeSpec, UniformSpec, Values, InternalSchema, SizeSchema, InternalValues, TextureSpec, ElementsSpec, ValueSpec, GlobalTextureSchema } from './schema';
 import { TextShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -35,12 +35,12 @@ export const TextSchema = {
 export type TextSchema = typeof TextSchema
 export type TextValues = Values<TextSchema>
 
-export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues, state: RenderableState, materialId: number): Renderable<TextValues> {
+export function TextRenderable(ctx: WebGLContext, id: number, values: TextValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<TextValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...TextSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = TextShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
     return createRenderable(renderItem, values, state);
 }

+ 4 - 4
src/mol-gl/renderable/texture-mesh.ts

@@ -6,7 +6,7 @@
 
 import { Renderable, RenderableState, createRenderable } from '../renderable';
 import { WebGLContext } from '../webgl/context';
-import { createGraphicsRenderItem } from '../webgl/render-item';
+import { createGraphicsRenderItem, GraphicsRenderVariant } from '../webgl/render-item';
 import { GlobalUniformSchema, BaseSchema, DefineSpec, Values, InternalSchema, InternalValues, UniformSpec, TextureSpec, GlobalTextureSchema, ValueSpec } from './schema';
 import { MeshShaderCode } from '../shader-code';
 import { ValueCell } from '../../mol-util';
@@ -19,7 +19,7 @@ export const TextureMeshSchema = {
     tNormal: TextureSpec('texture', 'rgb', 'float', 'nearest'),
 
     dFlatShaded: DefineSpec('boolean'),
-    dDoubleSided: DefineSpec('boolean'),
+    uDoubleSided: UniformSpec('b'),
     dFlipSided: DefineSpec('boolean'),
     dIgnoreLight: DefineSpec('boolean'),
     dXrayShaded: DefineSpec('boolean'),
@@ -31,13 +31,13 @@ export const TextureMeshSchema = {
 export type TextureMeshSchema = typeof TextureMeshSchema
 export type TextureMeshValues = Values<TextureMeshSchema>
 
-export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: TextureMeshValues, state: RenderableState, materialId: number): Renderable<TextureMeshValues> {
+export function TextureMeshRenderable(ctx: WebGLContext, id: number, values: TextureMeshValues, state: RenderableState, materialId: number, variants: GraphicsRenderVariant[]): Renderable<TextureMeshValues> {
     const schema = { ...GlobalUniformSchema, ...GlobalTextureSchema, ...InternalSchema, ...TextureMeshSchema };
     const internalValues: InternalValues = {
         uObjectId: ValueCell.create(id),
     };
     const shaderCode = MeshShaderCode;
-    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId);
+    const renderItem = createGraphicsRenderItem(ctx, 'triangles', shaderCode, schema, { ...values, ...internalValues }, materialId, variants);
 
     return createRenderable(renderItem, values, state);
 }

+ 26 - 8
src/mol-gl/renderer.ts

@@ -38,6 +38,19 @@ export interface RendererStats {
     instancedDrawCount: number
 }
 
+export const enum PickType {
+    None = 0,
+    Object = 1,
+    Instance = 2,
+    Group = 3,
+}
+
+export const enum MarkingType {
+    None = 0,
+    Depth = 1,
+    Mask = 2,
+}
+
 interface Renderer {
     readonly stats: RendererStats
     readonly props: Readonly<RendererProps>
@@ -46,7 +59,7 @@ interface Renderer {
     clearDepth: () => void
     update: (camera: ICamera) => void
 
-    renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => void
+    renderPick: (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => void
     renderDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderMarkingDepth: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
     renderMarkingMask: (group: Scene.Group, camera: ICamera, depthTexture: Texture | null) => void
@@ -232,6 +245,8 @@ namespace Renderer {
 
             uRenderWboit: ValueCell.create(false),
             uMarkingDepthTest: ValueCell.create(false),
+            uPickType: ValueCell.create(PickType.None),
+            uMarkingType: ValueCell.create(MarkingType.None),
 
             uTransparentBackground: ValueCell.create(false),
 
@@ -264,7 +279,7 @@ namespace Renderer {
         let globalUniformsNeedUpdate = true;
 
         const renderObject = (r: GraphicsRenderable, variant: GraphicsRenderVariant) => {
-            if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant[0] === 'p')) {
+            if (r.state.disposed || !r.state.visible || (!r.state.pickable && variant === 'pick')) {
                 return;
             }
 
@@ -304,7 +319,7 @@ namespace Renderer {
             }
 
             if (r.values.dRenderMode) { // indicates direct-volume
-                if ((variant[0] === 'p' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
+                if ((variant === 'pick' || variant === 'depth') && r.values.dRenderMode.ref.value === 'volume') {
                     return; // no picking/depth in volume mode
                 }
 
@@ -324,8 +339,8 @@ namespace Renderer {
                     }
                 }
             } else {
-                if (r.values.dDoubleSided) {
-                    if (r.values.dDoubleSided.ref.value || r.values.hasReflection.ref.value) {
+                if (r.values.uDoubleSided) {
+                    if (r.values.uDoubleSided.ref.value || r.values.hasReflection.ref.value) {
                         state.disable(gl.CULL_FACE);
                     } else {
                         state.enable(gl.CULL_FACE);
@@ -395,12 +410,13 @@ namespace Renderer {
             state.currentRenderItemId = -1;
         };
 
-        const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null) => {
+        const renderPick = (group: Scene.Group, camera: ICamera, variant: GraphicsRenderVariant, depthTexture: Texture | null, pickType: PickType) => {
             state.disable(gl.BLEND);
             state.enable(gl.DEPTH_TEST);
             state.depthMask(true);
 
             updateInternal(group, camera, depthTexture, false, false);
+            ValueCell.updateIfChanged(globalUniforms.uPickType, pickType);
 
             const { renderables } = group;
             for (let i = 0, il = renderables.length; i < il; ++i) {
@@ -429,13 +445,14 @@ namespace Renderer {
             state.depthMask(true);
 
             updateInternal(group, camera, depthTexture, false, false);
+            ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Depth);
 
             const { renderables } = group;
             for (let i = 0, il = renderables.length; i < il; ++i) {
                 const r = renderables[i];
 
                 if (r.values.markerAverage.ref.value !== 1) {
-                    renderObject(renderables[i], 'markingDepth');
+                    renderObject(renderables[i], 'marking');
                 }
             }
         };
@@ -446,13 +463,14 @@ namespace Renderer {
             state.depthMask(true);
 
             updateInternal(group, camera, depthTexture, false, !!depthTexture);
+            ValueCell.updateIfChanged(globalUniforms.uMarkingType, MarkingType.Mask);
 
             const { renderables } = group;
             for (let i = 0, il = renderables.length; i < il; ++i) {
                 const r = renderables[i];
 
                 if (r.values.markerAverage.ref.value > 0) {
-                    renderObject(renderables[i], 'markingMask');
+                    renderObject(renderables[i], 'marking');
                 }
             }
         };

+ 6 - 5
src/mol-gl/scene.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2018-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  * @author David Sehnal <david.sehnal@gmail.com>
@@ -15,6 +15,7 @@ import { arraySetRemove } from '../mol-util/array';
 import { BoundaryHelper } from '../mol-math/geometry/boundary-helper';
 import { hash1 } from '../mol-data/util';
 import { GraphicsRenderable } from './renderable';
+import { GraphicsRenderVariants } from './webgl/render-item';
 
 const boundaryHelper = new BoundaryHelper('98');
 
@@ -43,8 +44,8 @@ function calculateBoundingSphere(renderables: GraphicsRenderable[], boundingSphe
 }
 
 function renderableSort(a: GraphicsRenderable, b: GraphicsRenderable) {
-    const drawProgramIdA = a.getProgram('colorBlended').id;
-    const drawProgramIdB = b.getProgram('colorBlended').id;
+    const drawProgramIdA = (a.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
+    const drawProgramIdB = (b.getProgram('colorBlended') || a.getProgram('colorWboit')).id;
     const materialIdA = a.materialId;
     const materialIdB = b.materialId;
 
@@ -85,7 +86,7 @@ namespace Scene {
         readonly renderables: ReadonlyArray<GraphicsRenderable>
     }
 
-    export function create(ctx: WebGLContext): Scene {
+    export function create(ctx: WebGLContext, variants = GraphicsRenderVariants): Scene {
         const renderableMap = new Map<GraphicsRenderObject, GraphicsRenderable>();
         const renderables: GraphicsRenderable[] = [];
         const boundingSphere = Sphere3D();
@@ -102,7 +103,7 @@ namespace Scene {
 
         function add(o: GraphicsRenderObject) {
             if (!renderableMap.has(o)) {
-                const renderable = createRenderable(ctx, o);
+                const renderable = createRenderable(ctx, o, variants);
                 renderables.push(renderable);
                 if (o.type === 'direct-volume') {
                     volumes.push(renderable);

+ 41 - 13
src/mol-gl/shader-code.ts

@@ -23,6 +23,7 @@ export interface ShaderExtensions {
 }
 
 type FragOutTypes = { [k in number]: 'vec4' | 'ivec4' }
+type IgnoreDefine = (name: string, variant: string, defines: ShaderDefines) => boolean
 
 export interface ShaderCode {
     readonly id: number
@@ -32,6 +33,7 @@ export interface ShaderCode {
     readonly extensions: ShaderExtensions
     /** Fragment shader output type only applicable for webgl2 */
     readonly outTypes: FragOutTypes
+    readonly ignoreDefine?: IgnoreDefine
 }
 
 import { apply_fog } from './shader/chunks/apply-fog.glsl';
@@ -143,35 +145,56 @@ 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 };
+export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}, outTypes: FragOutTypes = {}, ignoreDefine?: IgnoreDefine): ShaderCode {
+    return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions, outTypes, ignoreDefine };
 }
 
 // Note: `drawBuffers` need to be 'optional' for wboit
 
+function ignoreDefine(name: string, variant: string, defines: ShaderDefines): boolean {
+    if (variant.startsWith('color')) {
+        if (name === 'dLightCount') {
+            return !!defines.dIgnoreLight?.ref.value;
+        }
+    } else {
+        return [
+            'dColorType', 'dUsePalette',
+            'dLightCount',
+            'dOverpaintType', 'dOverpaint',
+            'dSubstanceType', 'dSubstance',
+        ].includes(name);
+    }
+    return false;
+};
+
+function ignoreDefineUnlit(name: string, variant: string, defines: ShaderDefines): boolean {
+    if (name === 'dLightCount') return true;
+    return ignoreDefine(name, variant, defines);
+};
+
 import { points_vert } from './shader/points.vert';
 import { points_frag } from './shader/points.frag';
-export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' });
+export const PointsShaderCode = ShaderCode('points', points_vert, points_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
 
 import { spheres_vert } from './shader/spheres.vert';
 import { spheres_frag } from './shader/spheres.frag';
-export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' });
+export const SpheresShaderCode = ShaderCode('spheres', spheres_vert, spheres_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
 
 import { cylinders_vert } from './shader/cylinders.vert';
 import { cylinders_frag } from './shader/cylinders.frag';
-export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' });
+export const CylindersShaderCode = ShaderCode('cylinders', cylinders_vert, cylinders_frag, { fragDepth: 'required', drawBuffers: 'optional' }, {}, ignoreDefine);
 
 import { text_vert } from './shader/text.vert';
 import { text_frag } from './shader/text.frag';
-export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' });
+export const TextShaderCode = ShaderCode('text', text_vert, text_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
 
 import { lines_vert } from './shader/lines.vert';
 import { lines_frag } from './shader/lines.frag';
-export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' });
+export const LinesShaderCode = ShaderCode('lines', lines_vert, lines_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
 
 import { mesh_vert } from './shader/mesh.vert';
 import { mesh_frag } from './shader/mesh.frag';
-export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' });
+export const MeshShaderCode = ShaderCode('mesh', mesh_vert, mesh_frag, { drawBuffers: 'optional' }, {}, ignoreDefine);
 
 import { directVolume_vert } from './shader/direct-volume.vert';
 import { directVolume_frag } from './shader/direct-volume.frag';
@@ -179,7 +202,7 @@ export const DirectVolumeShaderCode = ShaderCode('direct-volume', directVolume_v
 
 import { image_vert } from './shader/image.vert';
 import { image_frag } from './shader/image.frag';
-export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' });
+export const ImageShaderCode = ShaderCode('image', image_vert, image_frag, { drawBuffers: 'optional' }, {}, ignoreDefineUnlit);
 
 //
 
@@ -187,10 +210,14 @@ export type ShaderDefines = {
     [k: string]: ValueCell<DefineType>
 }
 
-function getDefinesCode(defines: ShaderDefines) {
+function getDefinesCode(defines: ShaderDefines, ignore?: IgnoreDefine) {
     if (defines === undefined) return '';
+    const variant = (defines.dRenderVariant?.ref.value || '') as string;
+
     const lines = [];
     for (const name in defines) {
+        if (ignore?.(name, variant, defines)) continue;
+
         const define = defines[name];
         const v = define.ref.value;
         if (v !== undefined) {
@@ -288,7 +315,8 @@ function transformGlsl300Frag(frag: string) {
 }
 
 export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtensions, defines: ShaderDefines, shaders: ShaderCode): ShaderCode {
-    const header = getDefinesCode(defines);
+    const vertHeader = getDefinesCode(defines, shaders.ignoreDefine);
+    const fragHeader = getDefinesCode(defines, shaders.ignoreDefine);
     const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
     const fragPrefix = isWebGL2(gl)
         ? getGlsl300FragPrefix(gl, extensions, shaders.extensions, shaders.outTypes)
@@ -297,8 +325,8 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
     return {
         id: shaderCodeId(),
         name: shaders.name,
-        vert: `${vertPrefix}${header}${preprocess(shaders.vert, defines)}`,
-        frag: `${fragPrefix}${header}${preprocess(frag, defines)}`,
+        vert: `${vertPrefix}${vertHeader}${preprocess(shaders.vert, defines)}`,
+        frag: `${fragPrefix}${fragHeader}${preprocess(frag, defines)}`,
         extensions: shaders.extensions,
         outTypes: shaders.outTypes
     };

+ 3 - 1
src/mol-gl/shader/chunks/apply-light-color.glsl.ts

@@ -12,7 +12,9 @@ export const apply_light_color = `
     if (uBumpFrequency > 0.0 && uBumpAmplitude > 0.0) {
         vec3 bumpNormal = perturbNormal(-vViewPosition, normal, fbm(vModelPosition * uBumpFrequency), (uBumpAmplitude * bumpiness) / uBumpFrequency);
         #ifdef enabledFragDepth
-            if (!any(isNaN(bumpNormal))) normal = bumpNormal;
+            if (!isNaN(bumpNormal.x) && !isNaN(bumpNormal.y) && !isNaN(bumpNormal.z)) {
+                normal = bumpNormal;
+            }
         #else
             normal = bumpNormal;
         #endif

+ 4 - 4
src/mol-gl/shader/chunks/assign-color-varying.glsl.ts

@@ -56,13 +56,13 @@ export const assign_color_varying = `
         vSubstance.rgb = mix(vec3(uMetalness, uRoughness, uBumpiness), vSubstance.rgb, vSubstance.a);
     #endif
 #elif defined(dRenderVariant_pick)
-    #if defined(dRenderVariant_pickObject)
+    if (uPickType == 1) {
         vColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
-    #elif defined(dRenderVariant_pickInstance)
+    } else if (uPickType == 2) {
         vColor = vec4(encodeFloatRGB(aInstance), 1.0);
-    #elif defined(dRenderVariant_pickGroup)
+    } else {
         vColor = vec4(encodeFloatRGB(group), 1.0);
-    #endif
+    }
 #endif
 
 #ifdef dTransparency

+ 1 - 1
src/mol-gl/shader/chunks/assign-marker-varying.glsl.ts

@@ -1,5 +1,5 @@
 export const assign_marker_varying = `
-#if defined(dMarkerType_groupInstance)
+#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
     vMarker = readFromTexture(tMarker, aInstance * float(uGroupCount) + group, uMarkerTexDim).a;
 #endif
 `;

+ 27 - 25
src/mol-gl/shader/chunks/assign-material-color.glsl.ts

@@ -1,10 +1,9 @@
 export const assign_material_color = `
 #if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
-    #if defined(dMarkerType_uniform)
-        float marker = uMarker;
-    #elif defined(dMarkerType_groupInstance)
-        float marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
-    #endif
+    float marker = uMarker;
+    if (uMarker == -1.0) {
+        marker = floor(vMarker * 255.0 + 0.5); // rounding required to work on some cards on win
+    }
 #endif
 
 #if defined(dRenderVariant_color)
@@ -37,27 +36,30 @@ export const assign_material_color = `
     #else
         vec4 material = packDepthToRGBA(gl_FragCoord.z);
     #endif
-#elif defined(dRenderVariant_markingDepth)
-    if (marker > 0.0)
-        discard;
-    #ifdef enabledFragDepth
-        vec4 material = packDepthToRGBA(gl_FragDepthEXT);
-    #else
-        vec4 material = packDepthToRGBA(gl_FragCoord.z);
-    #endif
-#elif defined(dRenderVariant_markingMask)
-    if (marker == 0.0)
-        discard;
-    float depthTest = 1.0;
-    if (uMarkingDepthTest) {
-        depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
+#elif defined(dRenderVariant_marking)
+    vec4 material;
+    if(uMarkingType == 1) {
+        if (marker > 0.0)
+            discard;
+        #ifdef enabledFragDepth
+            material = packDepthToRGBA(gl_FragDepthEXT);
+        #else
+            material = packDepthToRGBA(gl_FragCoord.z);
+        #endif
+    } else {
+        if (marker == 0.0)
+            discard;
+        float depthTest = 1.0;
+        if (uMarkingDepthTest) {
+            depthTest = (fragmentDepth >= getDepth(gl_FragCoord.xy / uDrawingBufferSize)) ? 1.0 : 0.0;
+        }
+        bool isHighlight = intMod(marker, 2.0) > 0.1;
+        float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
+        float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
+        if (fogFactor == 1.0)
+            discard;
+        material = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0 - fogFactor);
     }
-    bool isHighlight = intMod(marker, 2.0) > 0.1;
-    float viewZ = depthToViewZ(uIsOrtho, fragmentDepth, uNear, uFar);
-    float fogFactor = smoothstep(uFogNear, uFogFar, abs(viewZ));
-    if (fogFactor == 1.0)
-        discard;
-    vec4 material = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0 - fogFactor);
 #endif
 
 // apply screendoor transparency

+ 5 - 5
src/mol-gl/shader/chunks/color-frag-params.glsl.ts

@@ -14,6 +14,11 @@ uniform float uBumpiness;
         varying vec4 vColor;
     #endif
 
+    #ifdef dUsePalette
+        uniform sampler2D tPalette;
+        varying float vPaletteV;
+    #endif
+
     #ifdef dOverpaint
         varying vec4 vOverpaint;
     #endif
@@ -33,9 +38,4 @@ uniform float uBumpiness;
     varying float vGroup;
     varying float vTransparency;
 #endif
-
-#ifdef dUsePalette
-    uniform sampler2D tPalette;
-    varying float vPaletteV;
-#endif
 `;

+ 4 - 4
src/mol-gl/shader/chunks/color-vert-params.glsl.ts

@@ -21,6 +21,10 @@ uniform float uBumpiness;
         uniform sampler2D tColorGrid;
     #endif
 
+    #ifdef dUsePalette
+        varying float vPaletteV;
+    #endif
+
     #ifdef dOverpaint
         #if defined(dOverpaintType_groupInstance) || defined(dOverpaintType_vertexInstance)
             varying vec4 vOverpaint;
@@ -70,8 +74,4 @@ uniform float uBumpiness;
         uniform sampler2D tTransparencyGrid;
     #endif
 #endif
-
-#ifdef dUsePalette
-    varying float vPaletteV;
-#endif
 `;

+ 5 - 2
src/mol-gl/shader/chunks/common-frag-params.glsl.ts

@@ -3,6 +3,9 @@ uniform int uObjectId;
 uniform int uInstanceCount;
 uniform int uGroupCount;
 
+uniform int uPickType;
+uniform int uMarkingType;
+
 #if dClipObjectCount != 0
     uniform int uClipObjectType[dClipObjectCount];
     uniform bool uClipObjectInvert[dClipObjectCount];
@@ -25,9 +28,8 @@ uniform float uHighlightStrength;
 uniform float uSelectStrength;
 uniform int uMarkerPriority;
 
-#if defined(dMarkerType_uniform)
+#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
     uniform float uMarker;
-#elif defined(dMarkerType_groupInstance)
     #if __VERSION__ == 100
         varying float vMarker;
     #else
@@ -52,6 +54,7 @@ uniform float uAlpha;
 uniform float uPickingAlphaThreshold;
 uniform bool uTransparentBackground;
 
+uniform bool uDoubleSided;
 uniform float uInteriorDarkening;
 uniform bool uInteriorColorFlag;
 uniform vec3 uInteriorColor;

+ 4 - 2
src/mol-gl/shader/chunks/common-vert-params.glsl.ts

@@ -8,6 +8,9 @@ uniform int uInstanceCount;
 uniform int uGroupCount;
 uniform vec4 uInvariantBoundingSphere;
 
+uniform bool uDoubleSided;
+uniform int uPickType;
+
 #if dClipObjectCount != 0
     uniform int uClipObjectType[dClipObjectCount];
     uniform bool uClipObjectInvert[dClipObjectCount];
@@ -26,9 +29,8 @@ uniform vec4 uInvariantBoundingSphere;
     #endif
 #endif
 
-#if defined(dMarkerType_uniform)
+#if defined(dRenderVariant_color) || defined(dRenderVariant_marking)
     uniform float uMarker;
-#elif defined(dMarkerType_groupInstance)
     uniform vec2 uMarkerTexDim;
     uniform sampler2D tMarker;
     #if __VERSION__ == 100

+ 2 - 10
src/mol-gl/shader/chunks/common.glsl.ts

@@ -5,14 +5,6 @@ export const common = `
     #define dRenderVariant_color
 #endif
 
-#if defined(dRenderVariant_pickObject) || defined(dRenderVariant_pickInstance) || defined(dRenderVariant_pickGroup)
-    #define dRenderVariant_pick
-#endif
-
-#if defined(dRenderVariant_markingDepth) || defined(dRenderVariant_markingMask)
-    #define dRenderVariant_marking
-#endif
-
 #if defined(dColorType_instance) || defined(dColorType_group) || defined(dColorType_groupInstance) || defined(dColorType_vertex) || defined(dColorType_vertexInstance)
     #define dColorType_texture
 #endif
@@ -215,8 +207,8 @@ float depthToViewZ(const in float isOrtho, const in float linearClipZ, const in
             a20 * b03 - a21 * b01 + a22 * b00) / det;
     }
 
-    #define isNaN(x) ( (x) != (x)    )
-    #define isInf(x) ( (x) == (x)+1. )
+    #define isNaN(x) ((x) != (x))
+    #define isInf(x) ((x) == (x) + 1.0)
 #else
     #define transpose2(m) transpose(m)
     #define transpose3(m) transpose(m)

+ 2 - 2
src/mol-gl/shader/cylinders.frag.ts

@@ -80,7 +80,7 @@ bool CylinderImpostor(
         }
     }
 
-    #ifdef dDoubleSided
+    if (uDoubleSided) {
         // body inside
         h = -h;
         t = (-k1 - h) / k2;
@@ -92,7 +92,7 @@ bool CylinderImpostor(
         }
 
         // TODO: handle inside caps???
-    #endif
+    }
 
     return false;
 }

+ 28 - 29
src/mol-gl/shader/direct-volume.frag.ts

@@ -57,12 +57,9 @@ uniform float uHighlightStrength;
 uniform float uSelectStrength;
 uniform int uMarkerPriority;
 
-#if defined(dMarkerType_uniform)
-    uniform float uMarker;
-#elif defined(dMarkerType_groupInstance)
-    uniform vec2 uMarkerTexDim;
-    uniform sampler2D tMarker;
-#endif
+uniform float uMarker;
+uniform vec2 uMarkerTexDim;
+uniform sampler2D tMarker;
 
 uniform float uMetalness;
 uniform float uRoughness;
@@ -82,6 +79,8 @@ uniform vec3 uInteriorColor;
 bool interior;
 
 uniform bool uRenderWboit;
+uniform bool uDoubleSided;
+uniform int uPickType;
 
 uniform float uNear;
 uniform float uFar;
@@ -275,17 +274,19 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
                     }
                 #endif
 
-                #if defined(dRenderVariant_pickObject)
-                    return vec4(encodeFloatRGB(float(uObjectId)), 1.0);
-                #elif defined(dRenderVariant_pickInstance)
-                    return vec4(encodeFloatRGB(vInstance), 1.0);
-                #elif defined(dRenderVariant_pickGroup)
-                    #ifdef dPackedGroup
-                        return vec4(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb, 1.0);
-                    #else
-                        vec3 g = floor(isoPos * uGridDim + 0.5);
-                        return vec4(encodeFloatRGB(g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y), 1.0);
-                    #endif
+                #if defined(dRenderVariant_pick)
+                    if (uPickType == 1) {
+                        return vec4(encodeFloatRGB(float(uObjectId)), 1.0);
+                    } else if (uPickType == 2) {
+                        return vec4(encodeFloatRGB(vInstance), 1.0);
+                    } else {
+                        #ifdef dPackedGroup
+                            return vec4(textureGroup(floor(isoPos * uGridDim + 0.5) / uGridDim).rgb, 1.0);
+                        #else
+                            vec3 g = floor(isoPos * uGridDim + 0.5);
+                            return vec4(encodeFloatRGB(g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y), 1.0);
+                        #endif
+                    }
                 #elif defined(dRenderVariant_depth)
                     #ifdef enabledFragDepth
                         return packDepthToRGBA(gl_FragDepthEXT);
@@ -331,13 +332,13 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
                         bool flipped = value > uIsoValue.y;
                     #endif
                     interior = value < uIsoValue.x && flipped;
-                    #ifndef dDoubleSided
+                    if (uDoubleSided) {
                         if (interior) {
                             prevValue = value;
                             pos += step;
                             continue;
                         }
-                    #endif
+                    }
                     vec3 vViewPosition = mvPosition.xyz;
                     vec4 material = vec4(color, uAlpha);
 
@@ -371,12 +372,11 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
                         #include apply_light_color
                     #endif
 
-                    #if defined(dMarkerType_uniform)
-                        float marker = uMarker;
-                    #elif defined(dMarkerType_groupInstance)
-                        float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
+                    float marker = uMarker;
+                    if (uMarker == -1.0) {
+                        marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
                         marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
-                    #endif
+                    }
                     #include apply_interior_color
                     #include apply_marker_color
 
@@ -439,18 +439,17 @@ vec4 raymarch(vec3 startLoc, vec3 step, vec3 rayDir) {
 
                 gl_FragColor.a = material.a * uAlpha * uTransferScale;
 
-                #if defined(dMarkerType_uniform)
-                    float marker = uMarker;
-                #elif defined(dMarkerType_groupInstance)
+                float marker = uMarker;
+                if (uMarker == -1.0) {
                     #ifdef dPackedGroup
                         float group = decodeFloatRGB(textureGroup(floor(unitPos * uGridDim + 0.5) / uGridDim).rgb);
                     #else
                         vec3 g = floor(unitPos * uGridDim + 0.5);
                         float group = g.z + g.y * uGridDim.z + g.x * uGridDim.z * uGridDim.y;
                     #endif
-                    float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
+                    marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
                     marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
-                #endif
+                }
                 #include apply_marker_color
 
                 preFogAlphaBlended = (1.0 - preFogAlphaBlended) * gl_FragColor.a + preFogAlphaBlended;

+ 15 - 17
src/mol-gl/shader/image.frag.ts

@@ -104,30 +104,29 @@ void main() {
     #if defined(dRenderVariant_pick)
         if (imageData.a < 0.3)
             discard;
-        #if defined(dRenderVariant_pickObject)
+        if (uPickType == 1) {
             gl_FragColor = vec4(encodeFloatRGB(float(uObjectId)), 1.0);
-        #elif defined(dRenderVariant_pickInstance)
+        } else if (uPickType == 2) {
             gl_FragColor = vec4(encodeFloatRGB(vInstance), 1.0);
-        #elif defined(dRenderVariant_pickGroup)
+        } else {
             gl_FragColor = vec4(texture2D(tGroupTex, vUv).rgb, 1.0);
-        #endif
+        }
     #elif defined(dRenderVariant_depth)
         if (imageData.a < 0.05)
             discard;
         gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
     #elif defined(dRenderVariant_marking)
-        #if defined(dMarkerType_uniform)
-            float marker = uMarker;
-        #elif defined(dMarkerType_groupInstance)
+        float marker = uMarker;
+        if (uMarker == -1.0) {
             float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
-            float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
+            marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
             marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
-        #endif
-        #if defined(dRenderVariant_markingDepth)
+        }
+        if (uMarkingType == 1) {
             if (marker > 0.0 || imageData.a < 0.05)
                 discard;
             gl_FragColor = packDepthToRGBA(gl_FragCoord.z);
-        #elif defined(dRenderVariant_markingMask)
+        } else {
             if (marker == 0.0 || imageData.a < 0.05)
                 discard;
             float depthTest = 1.0;
@@ -136,20 +135,19 @@ void main() {
             }
             bool isHighlight = intMod(marker, 2.0) > 0.1;
             gl_FragColor = vec4(0.0, depthTest, isHighlight ? 1.0 : 0.0, 1.0);
-        #endif
+        }
     #elif defined(dRenderVariant_color)
         if (imageData.a < 0.05)
             discard;
         gl_FragColor = imageData;
         gl_FragColor.a *= uAlpha;
 
-        #if defined(dMarkerType_uniform)
-            float marker = uMarker;
-        #elif defined(dMarkerType_groupInstance)
+        float marker = uMarker;
+        if (uMarker == -1.0) {
             float group = decodeFloatRGB(texture2D(tGroupTex, vUv).rgb);
-            float marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
+            marker = readFromTexture(tMarker, vInstance * float(uGroupCount) + group, uMarkerTexDim).a;
             marker = floor(marker * 255.0 + 0.5); // rounding required to work on some cards on win
-        #endif
+        }
 
         #include apply_marker_color
         #include apply_fog

+ 1 - 3
src/mol-gl/shader/mesh.frag.ts

@@ -50,9 +50,7 @@ void main() {
                 vec3 normal = -faceNormal;
             #else
                 vec3 normal = -normalize(vNormal);
-                #ifdef dDoubleSided
-                    normal = normal * (float(frontFacing) * 2.0 - 1.0);
-                #endif
+                if (uDoubleSided) normal *= float(frontFacing) * 2.0 - 1.0;
             #endif
             #include apply_light_color
         #endif

+ 4 - 2
src/mol-gl/shader/mesh.vert.ts

@@ -46,8 +46,10 @@ void main(){
     #endif
     mat3 normalMatrix = transpose3(inverse3(mat3(modelView)));
     vec3 transformedNormal = normalize(normalMatrix * normalize(normal));
-    #if defined(dFlipSided) && !defined(dDoubleSided) // TODO checking dDoubleSided should not be required, ASR
-        transformedNormal = -transformedNormal;
+    #if defined(dFlipSided)
+        if (!uDoubleSided) { // TODO checking uDoubleSided should not be required, ASR
+            transformedNormal = -transformedNormal;
+        }
     #endif
     vNormal = transformedNormal;
 }

+ 3 - 4
src/mol-gl/shader/spheres.frag.ts

@@ -66,10 +66,9 @@ void main(void){
     #include clip_pixel
 
     bool flag = Impostor(cameraPos, cameraNormal);
-    #ifndef dDoubleSided
-        if (interior)
-            discard;
-    #endif
+    if (!uDoubleSided) {
+        if (interior) discard;
+    }
 
     vec3 vViewPosition = cameraPos;
     gl_FragDepthEXT = calcDepth(vViewPosition);

+ 8 - 6
src/mol-gl/webgl/render-item.ts

@@ -49,13 +49,15 @@ export interface RenderItem<T extends string> {
 
 //
 
-const GraphicsRenderVariant = { 'colorBlended': '', 'colorWboit': '', 'pickObject': '', 'pickInstance': '', 'pickGroup': '', 'depth': '', 'markingDepth': '', 'markingMask': '' };
+const GraphicsRenderVariant = { colorBlended: '', colorWboit: '', pick: '', depth: '', marking: '' };
 export type GraphicsRenderVariant = keyof typeof GraphicsRenderVariant
-const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
+export const GraphicsRenderVariants = Object.keys(GraphicsRenderVariant) as GraphicsRenderVariant[];
+export const GraphicsRenderVariantsBlended = GraphicsRenderVariants.filter(v => v !== 'colorWboit');
+export const GraphicsRenderVariantsWboit = GraphicsRenderVariants.filter(v => v !== 'colorBlended');
 
-const ComputeRenderVariant = { 'compute': '' };
+const ComputeRenderVariant = { compute: '' };
 export type ComputeRenderVariant = keyof typeof ComputeRenderVariant
-const ComputeRenderVariants = Object.keys(ComputeRenderVariant) as ComputeRenderVariant[];
+export const ComputeRenderVariants = Object.keys(ComputeRenderVariant) as ComputeRenderVariant[];
 
 function createProgramVariant(ctx: WebGLContext, variant: string, defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) {
     defineValues = { ...defineValues, dRenderVariant: ValueCell.create(variant) };
@@ -90,8 +92,8 @@ function resetValueChanges(valueChanges: ValueChanges) {
 //
 
 export type GraphicsRenderItem = RenderItem<GraphicsRenderVariant>
-export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number) {
-    return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, GraphicsRenderVariants);
+export function createGraphicsRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues, materialId: number, variants: GraphicsRenderVariant[]) {
+    return createRenderItem(ctx, drawMode, shaderCode, schema, values, materialId, variants);
 }
 
 export type ComputeRenderItem = RenderItem<ComputeRenderVariant>

+ 6 - 1
src/mol-gl/webgl/resources.ts

@@ -105,7 +105,12 @@ export function createResources(gl: GLRenderingContext, state: WebGLState, stats
     const programCache = createReferenceCache(
         (props: ProgramProps) => {
             const array = [props.shaderCode.id];
-            Object.keys(props.defineValues).forEach(k => array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value)));
+            const variant = (props.defineValues.dRenderVariant?.ref.value || '') as string;
+            Object.keys(props.defineValues).forEach(k => {
+                if (!props.shaderCode.ignoreDefine?.(k, variant, props.defineValues)) {
+                    array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value));
+                }
+            });
             return hashFnv32a(array).toString();
         },
         (props: ProgramProps) => wrap('program', createProgram(gl, state, extensions, getShader, props)),

+ 9 - 6
src/mol-repr/visual.ts

@@ -82,7 +82,7 @@ namespace Visual {
     export function mark(renderObject: GraphicsRenderObject | undefined, loci: Loci, action: MarkerAction, lociApply: LociApply, previous?: PreviousMark) {
         if (!renderObject || isEmptyLoci(loci)) return false;
 
-        const { tMarker, dMarkerType, uMarker, markerAverage, markerStatus, uGroupCount, instanceCount } = renderObject.values;
+        const { tMarker, uMarker, markerAverage, markerStatus, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
         const { array } = tMarker.ref.value;
         const currentStatus = markerStatus.ref.value as MarkerInfo['status'];
@@ -135,7 +135,6 @@ namespace Visual {
             }
             ValueCell.updateIfChanged(uMarker, status);
             if (status === -1) ValueCell.update(tMarker, tMarker.ref.value);
-            ValueCell.updateIfChanged(dMarkerType, status === -1 ? 'groupInstance' : 'uniform');
             ValueCell.updateIfChanged(markerAverage, average);
             ValueCell.updateIfChanged(markerStatus, status);
         }
@@ -158,7 +157,7 @@ namespace Visual {
     export function setOverpaint(renderObject: GraphicsRenderObject | undefined, overpaint: Overpaint, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
         if (!renderObject) return;
 
-        const { tOverpaint, dOverpaintType, uGroupCount, instanceCount } = renderObject.values;
+        const { tOverpaint, dOverpaintType, dOverpaint, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
         // ensure texture has right size
@@ -181,6 +180,7 @@ namespace Visual {
         }
         ValueCell.update(tOverpaint, tOverpaint.ref.value);
         ValueCell.updateIfChanged(dOverpaintType, 'groupInstance');
+        ValueCell.updateIfChanged(dOverpaint, overpaint.layers.length > 0);
 
         if (overpaint.layers.length === 0) return;
 
@@ -207,7 +207,7 @@ namespace Visual {
     export function setTransparency(renderObject: GraphicsRenderObject | undefined, transparency: Transparency, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
         if (!renderObject) return;
 
-        const { tTransparency, dTransparencyType, transparencyAverage, uGroupCount, instanceCount } = renderObject.values;
+        const { tTransparency, dTransparencyType, transparencyAverage, dTransparency, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
         // ensure texture has right size and variant
@@ -229,6 +229,7 @@ namespace Visual {
         ValueCell.update(tTransparency, tTransparency.ref.value);
         ValueCell.updateIfChanged(transparencyAverage, getTransparencyAverage(array, count));
         ValueCell.updateIfChanged(dTransparencyType, 'groupInstance');
+        ValueCell.updateIfChanged(dTransparency, transparency.layers.length > 0);
 
         if (transparency.layers.length === 0) return;
 
@@ -255,7 +256,7 @@ namespace Visual {
     export function setSubstance(renderObject: GraphicsRenderObject | undefined, substance: Substance, lociApply: LociApply, clear: boolean, smoothing?: SmoothingContext) {
         if (!renderObject) return;
 
-        const { tSubstance, dSubstanceType, uGroupCount, instanceCount } = renderObject.values;
+        const { tSubstance, dSubstanceType, dSubstance, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
         // ensure texture has right size
@@ -278,6 +279,7 @@ namespace Visual {
         }
         ValueCell.update(tSubstance, tSubstance.ref.value);
         ValueCell.updateIfChanged(dSubstanceType, 'groupInstance');
+        ValueCell.updateIfChanged(dSubstance, substance.layers.length > 0);
 
         if (substance.layers.length === 0) return;
 
@@ -304,7 +306,7 @@ namespace Visual {
     export function setClipping(renderObject: GraphicsRenderObject | undefined, clipping: Clipping, lociApply: LociApply, clear: boolean) {
         if (!renderObject) return;
 
-        const { tClipping, uGroupCount, instanceCount } = renderObject.values;
+        const { tClipping, dClipping, uGroupCount, instanceCount } = renderObject.values;
         const count = uGroupCount.ref.value * instanceCount.ref.value;
 
         // ensure texture has right size
@@ -324,6 +326,7 @@ namespace Visual {
             lociApply(loci, apply, false);
         }
         ValueCell.update(tClipping, tClipping.ref.value);
+        ValueCell.updateIfChanged(dClipping, clipping.layers.length > 0);
     }
 
     export function setTransform(renderObject: GraphicsRenderObject | undefined, transform?: Mat4, instanceTransforms?: Float32Array | null) {