Browse Source

mol-gl improvements

- int textures (webgl2)
- read into Int32Array (webgl2)
- fix ctx.parameters.maxDrawingBuffers (webgl1)
- support setting frag out type (webgl2)
Alexander Rose 4 năm trước cách đây
mục cha
commit
b8aafa1d78

+ 1 - 1
src/mol-gl/renderable/util.ts

@@ -17,7 +17,7 @@ export function calculateTextureInfo (n: number, itemSize: number) {
     return { width, height, length: width * height * itemSize };
 }
 
-export interface TextureImage<T extends Uint8Array | Float32Array> {
+export interface TextureImage<T extends Uint8Array | Float32Array | Int32Array> {
     readonly array: T
     readonly width: number
     readonly height: number

+ 16 - 9
src/mol-gl/shader-code.ts

@@ -23,12 +23,16 @@ export interface ShaderExtensions {
     readonly shaderTextureLod?: ShaderExtensionsValue
 }
 
+type FragOutTypes = { [k in number]: 'vec4' | 'ivec4' }
+
 export interface ShaderCode {
     readonly id: number
     readonly name: string
     readonly vert: string
     readonly frag: string
     readonly extensions: ShaderExtensions
+    /** Fragment shader output type only applicable for webgl2 */
+    readonly outTypes: FragOutTypes
 }
 
 import apply_fog from './shader/chunks/apply-fog.glsl';
@@ -117,8 +121,8 @@ function addIncludes(text: string) {
         .replace(reMultipleLinebreaks, '\n');
 }
 
-export function ShaderCode(name: string, vert: string, frag: string, extensions: ShaderExtensions = {}): ShaderCode {
-    return { id: shaderCodeId(), name, vert: addIncludes(vert), frag: addIncludes(frag), extensions };
+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 };
 }
 
 // Note: `drawBuffers` need to be 'optional' for wboit
@@ -226,8 +230,6 @@ const glsl300VertPrefix = `#version 300 es
 `;
 
 const glsl300FragPrefixCommon = `
-layout(location = 0) out highp vec4 out_FragData0;
-
 #define varying in
 #define texture2D texture
 #define texture2DLodEXT textureLod
@@ -238,8 +240,12 @@ layout(location = 0) out highp vec4 out_FragData0;
 #define depthTextureSupport
 `;
 
-function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions) {
-    const prefix = [ '#version 300 es' ];
+function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExtensions, shaderExtensions: ShaderExtensions, outTypes: FragOutTypes) {
+    const prefix = [
+        '#version 300 es',
+        `layout(location = 0) out highp ${outTypes[0] || 'vec4'} out_FragData0;`
+    ];
+
     if (shaderExtensions.standardDerivatives) {
         prefix.push('#define enabledStandardDerivatives');
     }
@@ -250,7 +256,7 @@ function getGlsl300FragPrefix(gl: WebGL2RenderingContext, extensions: WebGLExten
         prefix.push('#define requiredDrawBuffers');
         const maxDrawBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS) as number;
         for (let i = 1, il = maxDrawBuffers; i < il; ++i) {
-            prefix.push(`layout(location = ${i}) out highp vec4 out_FragData${i};`);
+            prefix.push(`layout(location = ${i}) out highp ${outTypes[i] || 'vec4'} out_FragData${i};`);
         }
     }
     if (shaderExtensions.shaderTextureLod) {
@@ -268,7 +274,7 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
     const header = getDefinesCode(defines);
     const vertPrefix = isWebGL2(gl) ? glsl300VertPrefix : '';
     const fragPrefix = isWebGL2(gl)
-        ? getGlsl300FragPrefix(gl, extensions, shaders.extensions)
+        ? getGlsl300FragPrefix(gl, extensions, shaders.extensions, shaders.outTypes)
         : getGlsl100FragPrefix(extensions, shaders.extensions);
     const frag = isWebGL2(gl) ? transformGlsl300Frag(shaders.frag) : shaders.frag;
     return {
@@ -276,6 +282,7 @@ export function addShaderDefines(gl: GLRenderingContext, extensions: WebGLExtens
         name: shaders.name,
         vert: `${vertPrefix}${header}${shaders.vert}`,
         frag: `${fragPrefix}${header}${frag}`,
-        extensions: shaders.extensions
+        extensions: shaders.extensions,
+        outTypes: shaders.outTypes
     };
 }

+ 6 - 4
src/mol-gl/webgl/context.ts

@@ -127,12 +127,14 @@ function waitForGpuCommandsCompleteSync(gl: GLRenderingContext): void {
     gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, tmpPixel);
 }
 
-export function readPixels(gl: GLRenderingContext, x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) {
+export function readPixels(gl: GLRenderingContext, x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array | Int32Array) {
     if (isDebugMode) checkFramebufferStatus(gl);
     if (buffer instanceof Uint8Array) {
         gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer);
     } else if (buffer instanceof Float32Array) {
         gl.readPixels(x, y, width, height, gl.RGBA, gl.FLOAT, buffer);
+    } else if (buffer instanceof Int32Array && isWebGL2(gl)) {
+        gl.readPixels(x, y, width, height, gl.RGBA_INTEGER, gl.INT, buffer);
     } else {
         throw new Error('unsupported readPixels buffer type');
     }
@@ -204,7 +206,7 @@ export interface WebGLContext {
 
     createRenderTarget: (width: number, height: number, depth?: boolean, type?: 'uint8' | 'float32' | 'fp16', filter?: TextureFilter) => RenderTarget
     unbindFramebuffer: () => void
-    readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) => void
+    readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array | Int32Array) => void
     readPixelsAsync: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => Promise<void>
     waitForGpuCommandsComplete: () => Promise<void>
     waitForGpuCommandsCompleteSync: () => void
@@ -222,7 +224,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
     const parameters = {
         maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE) as number,
         maxRenderbufferSize: gl.getParameter(gl.MAX_RENDERBUFFER_SIZE) as number,
-        maxDrawBuffers: isWebGL2(gl) ? gl.getParameter(gl.MAX_DRAW_BUFFERS) as number : 0,
+        maxDrawBuffers: extensions.drawBuffers ? gl.getParameter(extensions.drawBuffers.MAX_DRAW_BUFFERS) as number : 0,
         maxTextureImageUnits: gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS) as number,
         maxVertexTextureImageUnits: gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) as number,
     };
@@ -330,7 +332,7 @@ export function createContext(gl: GLRenderingContext, props: Partial<{ pixelScal
             };
         },
         unbindFramebuffer: () => unbindFramebuffer(gl),
-        readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array) => {
+        readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array | Float32Array | Int32Array) => {
             readPixels(gl, x, y, width, height, buffer);
         },
         readPixelsAsync,

+ 18 - 4
src/mol-gl/webgl/texture.ts

@@ -20,6 +20,7 @@ export type TextureKindValue = {
     'image-uint8': TextureImage<Uint8Array>
     'image-float32': TextureImage<Float32Array>
     'image-float16': TextureImage<Float32Array>
+    'image-int32': TextureImage<Int32Array>
     'image-depth': TextureImage<Uint8Array> // TODO should be Uint32Array
     'volume-uint8': TextureVolume<Uint8Array>
     'volume-float32': TextureVolume<Float32Array>
@@ -28,7 +29,7 @@ export type TextureKindValue = {
 }
 export type TextureValueType = ValueOf<TextureKindValue>
 export type TextureKind = keyof TextureKindValue
-export type TextureType = 'ubyte' | 'ushort' | 'float' | 'fp16'
+export type TextureType = 'ubyte' | 'ushort' | 'float' | 'fp16' | 'int'
 export type TextureFormat = 'alpha' | 'rgb' | 'rgba' | 'depth'
 /** Numbers are shortcuts for color attachment */
 export type TextureAttachment = 'depth' | 'stencil' | 'color0' | 'color1' | 'color2' | 'color3' | 'color4' | 'color5' | 'color6' | 'color7' | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
@@ -43,6 +44,7 @@ export function getTarget(gl: GLRenderingContext, kind: TextureKind): number {
     }
     if (isWebGL2(gl)) {
         switch (kind) {
+            case 'image-int32': return gl.TEXTURE_2D;
             case 'volume-uint8': return gl.TEXTURE_3D;
             case 'volume-float32': return gl.TEXTURE_3D;
             case 'volume-float16': return gl.TEXTURE_3D;
@@ -56,8 +58,12 @@ export function getFormat(gl: GLRenderingContext, format: TextureFormat, type: T
         case 'alpha':
             if (isWebGL2(gl) && type === 'float') return gl.RED;
             else return gl.ALPHA;
-        case 'rgb': return gl.RGB;
-        case 'rgba': return gl.RGBA;
+        case 'rgb':
+            if (isWebGL2(gl) && type === 'int') return gl.RGB_INTEGER;
+            return gl.RGB;
+        case 'rgba':
+            if (isWebGL2(gl) && type === 'int') return gl.RGBA_INTEGER;
+            return gl.RGBA;
         case 'depth': return gl.DEPTH_COMPONENT;
     }
 }
@@ -70,18 +76,21 @@ export function getInternalFormat(gl: GLRenderingContext, format: TextureFormat,
                     case 'ubyte': return gl.ALPHA;
                     case 'float': return gl.R32F;
                     case 'fp16': return gl.R16F;
+                    case 'int': return gl.R32I;
                 }
             case 'rgb':
                 switch (type) {
                     case 'ubyte': return gl.RGB;
                     case 'float': return gl.RGB32F;
                     case 'fp16': return gl.RGB16F;
+                    case 'int': return gl.RGB32I;
                 }
             case 'rgba':
                 switch (type) {
                     case 'ubyte': return gl.RGBA;
                     case 'float': return gl.RGBA32F;
                     case 'fp16': return gl.RGBA16F;
+                    case 'int': return gl.RGBA32I;
                 }
             case 'depth':
                 return gl.DEPTH_COMPONENT16;
@@ -110,6 +119,7 @@ function getTypeSize(type: TextureType): number {
         case 'ushort': return 2;
         case 'float': return 4;
         case 'fp16': return 2;
+        case 'int': return 4;
     }
 }
 
@@ -121,6 +131,9 @@ export function getType(gl: GLRenderingContext, extensions: WebGLExtensions, typ
         case 'fp16':
             if (extensions.textureHalfFloat) return extensions.textureHalfFloat.HALF_FLOAT;
             else throw new Error('extension "texture_half_float" unavailable');
+        case 'int':
+            if (isWebGL2(gl)) return gl.INT;
+            else throw new Error('texture type "int" requires webgl2');
     }
 }
 
@@ -214,6 +227,7 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
         (kind.endsWith('float32') && _type !== 'float') ||
         (kind.endsWith('float16') && _type !== 'fp16') ||
         (kind.endsWith('uint8') && _type !== 'ubyte') ||
+        (kind.endsWith('int32') && _type !== 'int') ||
         (kind.endsWith('depth') && _type !== 'ushort')
     ) {
         throw new Error(`texture kind '${kind}' and type '${_type}' are incompatible`);
@@ -295,7 +309,7 @@ export function createTexture(gl: GLRenderingContext, extensions: WebGLExtension
             if (layer === undefined) throw new Error('need `layer` to attach 3D texture');
             gl.framebufferTextureLayer(gl.FRAMEBUFFER, getAttachment(gl, extensions, attachment), texture, 0, layer);
         } else {
-            throw new Error('unknown texture target');
+            throw new Error('unknown/unsupported texture target');
         }
     }