UNPKG

@acransac/vtk.js

Version:

Visualization Toolkit for the Web

253 lines (207 loc) 7.3 kB
import md5 from 'blueimp-md5'; import macro from 'vtk.js/Sources/macro'; import vtkShaderProgram from 'vtk.js/Sources/Rendering/OpenGL/ShaderProgram'; // ---------------------------------------------------------------------------- const SET_GET_FIELDS = ['lastShaderBound', 'context', 'openGLRenderWindow']; // ---------------------------------------------------------------------------- // vtkShaderCache methods // ---------------------------------------------------------------------------- function vtkShaderCache(publicAPI, model) { // Set our className model.classHierarchy.push('vtkShaderCache'); publicAPI.replaceShaderValues = (VSSource, FSSource, GSSource) => { // first handle renaming any Fragment shader inputs // if we have a geometry shader. By default fragment shaders // assume their inputs come from a Vertex Shader. When we // have a Geometry shader we rename the frament shader inputs // to come from the geometry shader let nFSSource = FSSource; if (GSSource.length > 0) { nFSSource = vtkShaderProgram.substitute(nFSSource, 'VSOut', 'GSOut') .result; } const gl2 = model.openGLRenderWindow.getWebgl2(); let fragDepthString = '\n'; let version = '#version 100\n'; if (gl2) { version = '#version 300 es\n' + '#define attribute in\n' + '#define textureCube texture\n' + '#define texture2D texture\n' + '#define textureCubeLod textureLod\n' + '#define texture2DLod textureLod\n'; } else { model.context.getExtension('OES_standard_derivatives'); if (model.context.getExtension('EXT_frag_depth')) { fragDepthString = '#extension GL_EXT_frag_depth : enable\n'; } if (model.context.getExtension('EXT_shader_texture_lod')) { fragDepthString += '#extension GL_EXT_shader_texture_lod : enable\n' + '#define textureCubeLod textureCubeLodEXT\n' + '#define texture2DLod texture2DLodEXT'; } } nFSSource = vtkShaderProgram.substitute(nFSSource, '//VTK::System::Dec', [ `${version}\n`, gl2 ? '' : '#extension GL_OES_standard_derivatives : enable\n', fragDepthString, '#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp float;', 'precision highp int;', '#else', 'precision mediump float;', 'precision mediump int;', '#endif', ]).result; let nVSSource = vtkShaderProgram.substitute( VSSource, '//VTK::System::Dec', [ `${version}\n`, '#ifdef GL_FRAGMENT_PRECISION_HIGH', 'precision highp float;', 'precision highp int;', '#else', 'precision mediump float;', 'precision mediump int;', '#endif', ] ).result; if (gl2) { nVSSource = vtkShaderProgram.substitute(nVSSource, 'varying', 'out') .result; nFSSource = vtkShaderProgram.substitute(nFSSource, 'varying', 'in') .result; nFSSource = vtkShaderProgram.substitute( nFSSource, 'gl_FragData\\[0\\]', 'fragOutput0' ).result; nFSSource = vtkShaderProgram.substitute( nFSSource, '//VTK::Output::Dec', 'layout(location = 0) out vec4 fragOutput0;' ).result; } // nFSSource = ShaderProgram.substitute(nFSSource, 'gl_FragData\\[0\\]', // 'gl_FragColor').result; const nGSSource = vtkShaderProgram.substitute( GSSource, '//VTK::System::Dec', version ).result; return { VSSource: nVSSource, FSSource: nFSSource, GSSource: nGSSource }; }; // return NULL if there is an issue publicAPI.readyShaderProgramArray = ( vertexCode, fragmentCode, geometryCode ) => { const data = publicAPI.replaceShaderValues( vertexCode, fragmentCode, geometryCode ); const shader = publicAPI.getShaderProgram( data.VSSource, data.FSSource, data.GSSource ); return publicAPI.readyShaderProgram(shader); }; publicAPI.readyShaderProgram = (shader) => { if (!shader) { return null; } // compile if needed if (!shader.getCompiled() && !shader.compileShader()) { return null; } // bind if needed if (!publicAPI.bindShader(shader)) { return null; } return shader; }; publicAPI.getShaderProgram = (vertexCode, fragmentCode, geometryCode) => { // compute the MD5 and the check the map const hashInput = `${vertexCode}${fragmentCode}${geometryCode}`; const result = md5(hashInput); // does it already exist? const loc = Object.keys(model.shaderPrograms).indexOf(result); if (loc === -1) { // create one const sps = vtkShaderProgram.newInstance(); sps.setContext(model.context); sps.getVertexShader().setSource(vertexCode); sps.getFragmentShader().setSource(fragmentCode); if (geometryCode) { sps.getGeometryShader().setSource(geometryCode); } sps.setMd5Hash(result); model.shaderPrograms[result] = sps; return sps; } return model.shaderPrograms[result]; }; publicAPI.releaseGraphicsResources = (win) => { // NOTE: // In the current implementation as of October 26th, if a shader // program is created by ShaderCache then it should make sure // that it releases the graphics resources used by these programs. // It is not wisely for callers to do that since then they would // have to loop over all the programs were in use and invoke // release graphics resources individually. publicAPI.releaseCurrentShader(); Object.keys(model.shaderPrograms) .map((key) => model.shaderPrograms[key]) .forEach((sp) => sp.releaseGraphicsResources(win)); }; publicAPI.releaseGraphicsResources = () => { // release prior shader if (model.astShaderBound) { model.lastShaderBound.release(); model.lastShaderBound = null; } }; publicAPI.bindShader = (shader) => { if (model.lastShaderBound === shader) { return 1; } // release prior shader if (model.lastShaderBound) { model.lastShaderBound.release(); } shader.bind(); model.lastShaderBound = shader; return 1; }; } // ---------------------------------------------------------------------------- // Object factory // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { lastShaderBound: null, shaderPrograms: null, context: null, openGLRenderWindow: null, }; // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); // Internal objects model.shaderPrograms = {}; // Build VTK API macro.obj(publicAPI, model); macro.setGet(publicAPI, model, SET_GET_FIELDS); // Object methods vtkShaderCache(publicAPI, model); return Object.freeze(publicAPI); } // ---------------------------------------------------------------------------- export const newInstance = macro.newInstance(extend, 'vtkShaderCache'); // ---------------------------------------------------------------------------- export default { newInstance, extend };