buffer.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { WebGLContext } from './context';
  7. import { ValueCell } from '../../mol-util';
  8. import { RenderableSchema } from '../renderable/schema';
  9. import { idFactory } from '../../mol-util/id-factory';
  10. import { assertUnreachable, ValueOf } from '../../mol-util/type-helpers';
  11. import { GLRenderingContext, isWebGL2 } from './compat';
  12. import { WebGLExtensions } from './extensions';
  13. import { WebGLState } from './state';
  14. const getNextBufferId = idFactory();
  15. export type UsageHint = 'static' | 'dynamic' | 'stream'
  16. export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
  17. export type BufferType = 'attribute' | 'elements' | 'uniform'
  18. export type DataTypeArrayType = {
  19. 'uint8': Uint8Array
  20. 'int8': Int8Array
  21. 'uint16': Uint16Array
  22. 'int16': Int16Array
  23. 'uint32': Uint32Array
  24. 'int32': Int32Array
  25. 'float32': Float32Array
  26. }
  27. export type ArrayType = ValueOf<DataTypeArrayType>
  28. export type ArrayKind = keyof DataTypeArrayType
  29. export function getUsageHint(gl: GLRenderingContext, usageHint: UsageHint) {
  30. switch (usageHint) {
  31. case 'static': return gl.STATIC_DRAW;
  32. case 'dynamic': return gl.DYNAMIC_DRAW;
  33. case 'stream': return gl.STREAM_DRAW;
  34. }
  35. }
  36. export function getDataType(gl: GLRenderingContext, dataType: DataType) {
  37. switch (dataType) {
  38. case 'uint8': return gl.UNSIGNED_BYTE;
  39. case 'int8': return gl.BYTE;
  40. case 'uint16': return gl.UNSIGNED_SHORT;
  41. case 'int16': return gl.SHORT;
  42. case 'uint32': return gl.UNSIGNED_INT;
  43. case 'int32': return gl.INT;
  44. case 'float32': return gl.FLOAT;
  45. default: assertUnreachable(dataType);
  46. }
  47. }
  48. function dataTypeFromArray(gl: GLRenderingContext, array: ArrayType) {
  49. if (array instanceof Uint8Array) {
  50. return gl.UNSIGNED_BYTE;
  51. } else if (array instanceof Int8Array) {
  52. return gl.BYTE;
  53. } else if (array instanceof Uint16Array) {
  54. return gl.UNSIGNED_SHORT;
  55. } else if (array instanceof Int16Array) {
  56. return gl.SHORT;
  57. } else if (array instanceof Uint32Array) {
  58. return gl.UNSIGNED_INT;
  59. } else if (array instanceof Int32Array) {
  60. return gl.INT;
  61. } else if (array instanceof Float32Array) {
  62. return gl.FLOAT;
  63. }
  64. assertUnreachable(array);
  65. }
  66. export function getBufferType(gl: GLRenderingContext, bufferType: BufferType) {
  67. switch (bufferType) {
  68. case 'attribute': return gl.ARRAY_BUFFER;
  69. case 'elements': return gl.ELEMENT_ARRAY_BUFFER;
  70. case 'uniform':
  71. if (isWebGL2(gl)) {
  72. return gl.UNIFORM_BUFFER;
  73. } else {
  74. throw new Error('WebGL2 is required for uniform buffers');
  75. }
  76. }
  77. }
  78. export interface Buffer {
  79. readonly id: number
  80. readonly _usageHint: number
  81. readonly _bufferType: number
  82. readonly _dataType: number
  83. readonly _bpe: number
  84. readonly length: number
  85. getBuffer: () => WebGLBuffer
  86. updateData: (array: ArrayType) => void
  87. updateSubData: (array: ArrayType, offset: number, count: number) => void
  88. reset: () => void
  89. destroy: () => void
  90. }
  91. export function getBuffer(gl: GLRenderingContext) {
  92. const buffer = gl.createBuffer();
  93. if (buffer === null) {
  94. throw new Error('Could not create WebGL buffer');
  95. }
  96. return buffer;
  97. }
  98. function createBuffer(gl: GLRenderingContext, array: ArrayType, usageHint: UsageHint, bufferType: BufferType): Buffer {
  99. let _buffer = getBuffer(gl);
  100. const _usageHint = getUsageHint(gl, usageHint);
  101. const _bufferType = getBufferType(gl, bufferType);
  102. const _dataType = dataTypeFromArray(gl, array);
  103. const _bpe = array.BYTES_PER_ELEMENT;
  104. const _length = array.length;
  105. function updateData(array: ArrayType) {
  106. gl.bindBuffer(_bufferType, _buffer);
  107. gl.bufferData(_bufferType, array, _usageHint);
  108. }
  109. updateData(array);
  110. let destroyed = false;
  111. return {
  112. id: getNextBufferId(),
  113. _usageHint,
  114. _bufferType,
  115. _dataType,
  116. _bpe,
  117. length: _length,
  118. getBuffer: () => _buffer,
  119. updateData,
  120. updateSubData: (array: ArrayType, offset: number, count: number) => {
  121. gl.bindBuffer(_bufferType, _buffer);
  122. if (count - offset === array.length) {
  123. gl.bufferSubData(_bufferType, 0, array);
  124. } else {
  125. gl.bufferSubData(_bufferType, offset * _bpe, array.subarray(offset, offset + count));
  126. }
  127. },
  128. reset: () => {
  129. _buffer = getBuffer(gl);
  130. updateData(array);
  131. },
  132. destroy: () => {
  133. if (destroyed) return;
  134. gl.deleteBuffer(_buffer);
  135. destroyed = true;
  136. }
  137. };
  138. }
  139. //
  140. export type AttributeItemSize = 1 | 2 | 3 | 4 | 16
  141. export type AttributeKind = 'float32'
  142. export function getAttribType(gl: GLRenderingContext, kind: AttributeKind, itemSize: AttributeItemSize) {
  143. switch (kind) {
  144. case 'float32':
  145. switch (itemSize) {
  146. case 1: return gl.FLOAT;
  147. case 2: return gl.FLOAT_VEC2;
  148. case 3: return gl.FLOAT_VEC3;
  149. case 4: return gl.FLOAT_VEC4;
  150. case 16: return gl.FLOAT_MAT4;
  151. }
  152. default:
  153. assertUnreachable(kind);
  154. }
  155. }
  156. export type AttributeDefs = {
  157. [k: string]: { kind: AttributeKind, itemSize: AttributeItemSize, divisor: number }
  158. }
  159. export type AttributeValues = { [k: string]: ValueCell<ArrayType> }
  160. export type AttributeBuffers = [string, AttributeBuffer][]
  161. export interface AttributeBuffer extends Buffer {
  162. bind: (location: number) => void
  163. }
  164. export function createAttributeBuffer<T extends ArrayType, S extends AttributeItemSize>(gl: GLRenderingContext, state: WebGLState, extensions: WebGLExtensions, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
  165. const { instancedArrays } = extensions;
  166. const buffer = createBuffer(gl, array, usageHint, 'attribute');
  167. const { _bufferType, _dataType, _bpe } = buffer;
  168. return {
  169. ...buffer,
  170. bind: (location: number) => {
  171. gl.bindBuffer(_bufferType, buffer.getBuffer());
  172. if (itemSize === 16) {
  173. for (let i = 0; i < 4; ++i) {
  174. state.enableVertexAttrib(location + i);
  175. gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * 4 * _bpe, i * 4 * _bpe);
  176. instancedArrays.vertexAttribDivisor(location + i, divisor);
  177. }
  178. } else {
  179. state.enableVertexAttrib(location);
  180. gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0);
  181. instancedArrays.vertexAttribDivisor(location, divisor);
  182. }
  183. }
  184. };
  185. }
  186. export function createAttributeBuffers(ctx: WebGLContext, schema: RenderableSchema, values: AttributeValues) {
  187. const buffers: AttributeBuffers = [];
  188. Object.keys(schema).forEach(k => {
  189. const spec = schema[k];
  190. if (spec.type === 'attribute') {
  191. buffers[buffers.length] = [k, ctx.resources.attribute(values[k].ref.value, spec.itemSize, spec.divisor)];
  192. }
  193. });
  194. return buffers;
  195. }
  196. //
  197. export type ElementsType = Uint16Array | Uint32Array
  198. export type ElementsKind = 'uint16' | 'uint32'
  199. export interface ElementsBuffer extends Buffer {
  200. bind: () => void
  201. }
  202. export function createElementsBuffer(gl: GLRenderingContext, array: ElementsType, usageHint: UsageHint = 'static'): ElementsBuffer {
  203. const buffer = createBuffer(gl, array, usageHint, 'elements');
  204. return {
  205. ...buffer,
  206. bind: () => {
  207. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.getBuffer());
  208. }
  209. };
  210. }