import Md5 from 'spark-md5';
import macro from 'vtk.js/Sources/macros'; import vtkShaderProgram from 'vtk.js/Sources/Rendering/OpenGL/ShaderProgram';
const SET_GET_FIELDS = [ 'lastShaderProgramBound', 'context', '_openGLRenderWindow', ];
function vtkShaderCache(publicAPI, model) { model.classHierarchy.push('vtkShaderCache');
publicAPI.replaceShaderValues = (VSSource, FSSource, GSSource) => {
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;
let shaderOutputs = ''; let outputCount = 0; while (nFSSource.includes(`gl_FragData[${outputCount}]`)) { nFSSource = vtkShaderProgram.substitute( nFSSource, `gl_FragData\\[${outputCount}\\]`, `fragOutput${outputCount}` ).result; shaderOutputs += `layout(location = ${outputCount}) out vec4 fragOutput${outputCount};\n`; outputCount++; } nFSSource = vtkShaderProgram.substitute( nFSSource, '//VTK::Output::Dec', shaderOutputs ).result; }
const nGSSource = vtkShaderProgram.substitute( GSSource, '//VTK::System::Dec', version ).result;
return { VSSource: nVSSource, FSSource: nFSSource, GSSource: nGSSource }; };
publicAPI.readyShaderProgramArray = ( vertexCode, fragmentCode, geometryCode ) => { const data = publicAPI.replaceShaderValues( vertexCode, fragmentCode, geometryCode );
const shaderProgram = publicAPI.getShaderProgram( data.VSSource, data.FSSource, data.GSSource );
return publicAPI.readyShaderProgram(shaderProgram); };
publicAPI.readyShaderProgram = (program) => { if (!program) { return null; }
if (!program.getCompiled() && !program.compileShader()) { return null; }
if (!publicAPI.bindShaderProgram(program)) { return null; }
return program; };
publicAPI.getShaderProgram = (vertexCode, fragmentCode, geometryCode) => { const hashInput = `${vertexCode}${fragmentCode}${geometryCode}`; const result = Md5.hash(hashInput);
if (!(result in model.shaderPrograms)) { 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) => {
publicAPI.releaseCurrentShaderProgram();
Object.keys(model.shaderPrograms) .map((key) => model.shaderPrograms[key]) .forEach((sp) => sp.cleanup()); model.shaderPrograms = {}; };
publicAPI.releaseCurrentShaderProgram = () => { if (model.lastShaderProgramBound) { model.lastShaderProgramBound.cleanup(); model.lastShaderProgramBound = null; } };
publicAPI.bindShaderProgram = (program) => { if (model.lastShaderProgramBound === program) { return 1; }
if (model.lastShaderProgramBound) { model.lastShaderProgramBound.release(); } program.bind(); model.lastShaderProgramBound = program; return 1; }; }
const DEFAULT_VALUES = { lastShaderProgramBound: null, shaderPrograms: null, context: null, };
export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues);
model.shaderPrograms = {};
macro.obj(publicAPI, model); macro.setGet(publicAPI, model, SET_GET_FIELDS); macro.moveToProtected(publicAPI, model, ['openGLRenderWindow']);
vtkShaderCache(publicAPI, model); }
export const newInstance = macro.newInstance(extend, 'vtkShaderCache');
export default { newInstance, extend };
|