compute.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. /**
  2. * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author David Sehnal <david.sehnal@gmail.com>
  5. */
  6. import { QuadSchema, QuadValues } from '../../../mol-gl/compute/util';
  7. import { ComputeRenderable, createComputeRenderable } from '../../../mol-gl/renderable';
  8. import { DefineSpec, TextureSpec, UniformSpec, Values } from '../../../mol-gl/renderable/schema';
  9. import { ShaderCode } from '../../../mol-gl/shader-code';
  10. import quad_vert from '../../../mol-gl/shader/quad.vert';
  11. import { WebGLContext } from '../../../mol-gl/webgl/context';
  12. import { createComputeRenderItem } from '../../../mol-gl/webgl/render-item';
  13. import { ValueCell } from '../../../mol-util';
  14. import { arrayMin } from '../../../mol-util/array';
  15. import { isLittleEndian } from '../../../mol-util/is-little-endian';
  16. import { CollocationParams } from '../collocation';
  17. import { normalizeBasicOrder } from '../orbitals';
  18. import shader_frag from './shader.frag';
  19. const AlphaOrbitalsSchema = {
  20. ...QuadSchema,
  21. uDimensions: UniformSpec('v3'),
  22. uMin: UniformSpec('v3'),
  23. uDelta: UniformSpec('v3'),
  24. tCenters: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
  25. tInfo: TextureSpec('image-float32', 'rgba', 'float', 'nearest'),
  26. tCoeff: TextureSpec('image-float32', 'rgb', 'float', 'nearest'),
  27. tAlpha: TextureSpec('image-float32', 'alpha', 'float', 'nearest'),
  28. uNCenters: UniformSpec('i'),
  29. uNAlpha: UniformSpec('i'),
  30. uNCoeff: UniformSpec('i'),
  31. uMaxCoeffs: UniformSpec('i'),
  32. uLittleEndian: UniformSpec('i') // TODO: boolean uniforms
  33. };
  34. const AlphaOrbitalsShaderCode = ShaderCode('postprocessing', quad_vert, shader_frag);
  35. type AlphaOrbitalsRenderable = ComputeRenderable<Values<typeof AlphaOrbitalsSchema>>
  36. function createTextureData({
  37. basis,
  38. sphericalOrder,
  39. alphaOrbitals,
  40. cutoffThreshold
  41. }: CollocationParams) {
  42. let centerCount = 0;
  43. let baseCount = 0;
  44. let coeffCount = 0;
  45. for (const atom of basis.atoms) {
  46. for (const shell of atom.shells) {
  47. for (const L of shell.angularMomentum) {
  48. if (L > 4) {
  49. // TODO: will L > 4 be required? Would need to precompute more functions in that case.
  50. throw new Error('Angular momentum L > 4 not supported.');
  51. }
  52. centerCount++;
  53. baseCount += 2 * L + 1;
  54. coeffCount += shell.exponents.length;
  55. }
  56. }
  57. }
  58. const centers = new Float32Array(4 * centerCount);
  59. // L, alpha_offset, coeff_offset_start, coeff_offset_end
  60. const info = new Float32Array(4 * centerCount);
  61. const alpha = new Float32Array(baseCount);
  62. const coeff = new Float32Array(3 * coeffCount);
  63. let maxCoeffs = 0;
  64. let cO = 0, aO = 0, coeffO = 0;
  65. for (const atom of basis.atoms) {
  66. for (const shell of atom.shells) {
  67. let amIndex = 0;
  68. for (const L of shell.angularMomentum) {
  69. const a0 = normalizeBasicOrder(L, alphaOrbitals.slice(aO, aO + 2 * L + 1), sphericalOrder);
  70. const cutoffRadius = cutoffThreshold > 0
  71. ? Math.sqrt(-Math.log(cutoffThreshold) / arrayMin(shell.exponents))
  72. : 10000;
  73. centers[4 * cO + 0] = atom.center[0];
  74. centers[4 * cO + 1] = atom.center[1];
  75. centers[4 * cO + 2] = atom.center[2];
  76. centers[4 * cO + 3] = cutoffRadius * cutoffRadius;
  77. info[4 * cO + 0] = L;
  78. info[4 * cO + 1] = aO;
  79. info[4 * cO + 2] = coeffO;
  80. info[4 * cO + 3] = coeffO + shell.exponents.length;
  81. for (let i = 0; i < a0.length; i++) alpha[aO + i] = a0[i];
  82. const c0 = shell.coefficients[amIndex++];
  83. for (let i = 0; i < shell.exponents.length; i++) {
  84. coeff[3 * (coeffO + i) + 0] = c0[i];
  85. coeff[3 * (coeffO + i) + 1] = shell.exponents[i];
  86. }
  87. if (c0.length > maxCoeffs) {
  88. maxCoeffs = c0.length;
  89. }
  90. cO++;
  91. aO += 2 * L + 1;
  92. coeffO += shell.exponents.length;
  93. }
  94. }
  95. }
  96. return { nCenters: centerCount, nAlpha: baseCount, nCoeff: coeffCount, maxCoeffs, centers, info, alpha, coeff };
  97. }
  98. function getPostprocessingRenderable(ctx: WebGLContext, params: CollocationParams): AlphaOrbitalsRenderable {
  99. const data = createTextureData(params);
  100. const values: Values<typeof AlphaOrbitalsSchema> = {
  101. ...QuadValues,
  102. uDimensions: ValueCell.create(params.grid.dimensions),
  103. uMin: ValueCell.create(params.grid.box.min),
  104. uDelta: ValueCell.create(params.grid.delta),
  105. uNCenters: ValueCell.create(data.nCenters),
  106. uNAlpha: ValueCell.create(data.nAlpha),
  107. uNCoeff: ValueCell.create(data.nCoeff),
  108. uMaxCoeffs: ValueCell.create(data.maxCoeffs),
  109. tCenters: ValueCell.create({ width: data.nCenters, height: 1, array: data.centers }),
  110. tInfo: ValueCell.create({ width: data.nCenters, height: 1, array: data.info }),
  111. tCoeff: ValueCell.create({ width: data.nCoeff, height: 1, array: data.coeff }),
  112. tAlpha: ValueCell.create({ width: data.nAlpha, height: 1, array: data.alpha }),
  113. uLittleEndian: ValueCell.create(isLittleEndian()),
  114. };
  115. const schema = { ...AlphaOrbitalsSchema };
  116. const renderItem = createComputeRenderItem(ctx, 'triangles', AlphaOrbitalsShaderCode, schema, values);
  117. return createComputeRenderable(renderItem, values);
  118. }
  119. function normalizeParams(webgl: WebGLContext) {
  120. if (!webgl.isWebGL2) {
  121. // workaround for webgl1 limitation that loop counters need to be `const`
  122. (AlphaOrbitalsSchema.uNCenters as any) = DefineSpec('number');
  123. (AlphaOrbitalsSchema.uMaxCoeffs as any) = DefineSpec('number');
  124. }
  125. }
  126. export function gpuComputeAlphaOrbitalsGridValues(webgl: WebGLContext, params: CollocationParams) {
  127. const [nx, ny, nz] = params.grid.dimensions;
  128. normalizeParams(webgl);
  129. if (!webgl.computeTargets['alpha-oribtals']) {
  130. webgl.computeTargets['alpha-oribtals'] = webgl.createRenderTarget(nx, ny * nz, false, 'uint8', 'nearest');
  131. } else {
  132. webgl.computeTargets['alpha-oribtals'].setSize(nx, ny * nz);
  133. }
  134. const target = webgl.computeTargets['alpha-oribtals'];
  135. const renderable = getPostprocessingRenderable(webgl, params);
  136. const width = nx;
  137. const height = ny * nz;
  138. const { gl, state } = webgl;
  139. target.bind();
  140. gl.viewport(0, 0, width, height);
  141. gl.scissor(0, 0, width, height);
  142. state.disable(gl.SCISSOR_TEST);
  143. state.disable(gl.BLEND);
  144. state.disable(gl.DEPTH_TEST);
  145. state.depthMask(false);
  146. renderable.render();
  147. const array = new Uint8Array(width * height * 4);
  148. webgl.readPixels(0, 0, width, height, array);
  149. const floats = new Float32Array(array.buffer, array.byteOffset, width * height);
  150. renderable.dispose();
  151. return floats;
  152. }
  153. export function canComputeAlphaOrbitalsOnGPU(webgl?: WebGLContext) {
  154. return !!webgl?.extensions.textureFloat;
  155. }