buffer.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /**
  2. * Copyright (c) 2018-2019 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 { ValueOf } from 'mol-util/type-helpers';
  11. import { GLRenderingContext } from './compat';
  12. const getNextBufferId = idFactory()
  13. export type UsageHint = 'static' | 'dynamic' | 'stream'
  14. export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
  15. export type BufferType = 'attribute' | 'elements' | 'uniform'
  16. export type DataTypeArrayType = {
  17. 'uint8': Uint8Array
  18. 'int8': Int8Array
  19. 'uint16': Uint16Array
  20. 'int16': Int16Array
  21. 'uint32': Uint32Array
  22. 'int32': Int32Array
  23. 'float32': Float32Array
  24. }
  25. export type ArrayType = ValueOf<DataTypeArrayType>
  26. export type ArrayKind = keyof DataTypeArrayType
  27. export function getUsageHint(ctx: WebGLContext, usageHint: UsageHint) {
  28. const { gl } = ctx
  29. switch (usageHint) {
  30. case 'static': return gl.STATIC_DRAW
  31. case 'dynamic': return gl.DYNAMIC_DRAW
  32. case 'stream': return gl.STREAM_DRAW
  33. }
  34. }
  35. export function getDataType(ctx: WebGLContext, dataType: DataType) {
  36. const { gl } = ctx
  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. }
  46. }
  47. function dataTypeFromArray(ctx: WebGLContext, array: ArrayType) {
  48. const { gl } = ctx
  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. } else {
  64. throw new Error('Should nevver happen')
  65. }
  66. }
  67. export function getBufferType(ctx: WebGLContext, bufferType: BufferType) {
  68. const { gl } = ctx
  69. switch (bufferType) {
  70. case 'attribute': return gl.ARRAY_BUFFER
  71. case 'elements': return gl.ELEMENT_ARRAY_BUFFER
  72. case 'uniform': return (gl as WebGL2RenderingContext).UNIFORM_BUFFER
  73. }
  74. }
  75. export interface Buffer {
  76. readonly id: number
  77. readonly _buffer: WebGLBuffer
  78. readonly _usageHint: number
  79. readonly _bufferType: number
  80. readonly _dataType: number
  81. readonly _bpe: number
  82. readonly length: number
  83. updateData: (array: ArrayType) => void
  84. updateSubData: (array: ArrayType, offset: number, count: number) => void
  85. destroy: () => void
  86. }
  87. export function createBuffer(ctx: WebGLContext, array: ArrayType, usageHint: UsageHint, bufferType: BufferType): Buffer {
  88. const { gl, stats } = ctx
  89. const _buffer = gl.createBuffer()
  90. if (_buffer === null) {
  91. throw new Error('Could not create WebGL buffer')
  92. }
  93. const _usageHint = getUsageHint(ctx, usageHint)
  94. const _bufferType = getBufferType(ctx, bufferType)
  95. const _dataType = dataTypeFromArray(ctx, array)
  96. const _bpe = array.BYTES_PER_ELEMENT
  97. const _length = array.length
  98. function updateData(array: ArrayType) {
  99. gl.bindBuffer(_bufferType, _buffer);
  100. gl.bufferData(_bufferType, array, _usageHint)
  101. }
  102. updateData(array)
  103. let destroyed = false
  104. stats.bufferCount += 1
  105. return {
  106. id: getNextBufferId(),
  107. _buffer,
  108. _usageHint,
  109. _bufferType,
  110. _dataType,
  111. _bpe,
  112. length: _length,
  113. updateData,
  114. updateSubData: (array: ArrayType, offset: number, count: number) => {
  115. gl.bindBuffer(_bufferType, _buffer);
  116. gl.bufferSubData(_bufferType, offset * _bpe, array.subarray(offset, offset + count))
  117. },
  118. destroy: () => {
  119. if (destroyed) return
  120. gl.deleteBuffer(_buffer)
  121. destroyed = true
  122. stats.bufferCount -= 1
  123. }
  124. }
  125. }
  126. //
  127. export type AttributeItemSize = 1 | 2 | 3 | 4 | 16
  128. export type AttributeKind = 'float32' | 'int32'
  129. export function getAttribType(gl: GLRenderingContext, kind: AttributeKind, itemSize: AttributeItemSize) {
  130. switch (kind) {
  131. case 'int32':
  132. switch (itemSize) {
  133. case 1: return gl.INT
  134. case 2: return gl.INT_VEC2
  135. case 3: return gl.INT_VEC3
  136. case 4: return gl.INT_VEC4
  137. }
  138. break
  139. case 'float32':
  140. switch (itemSize) {
  141. case 1: return gl.FLOAT
  142. case 2: return gl.FLOAT_VEC2
  143. case 3: return gl.FLOAT_VEC3
  144. case 4: return gl.FLOAT_VEC4
  145. case 16: return gl.FLOAT_MAT4
  146. }
  147. break
  148. }
  149. throw new Error(`unknown attribute type for kind '${kind}' and itemSize '${itemSize}'`)
  150. }
  151. export type AttributeDefs = {
  152. [k: string]: { kind: AttributeKind, itemSize: AttributeItemSize, divisor: number }
  153. }
  154. export type AttributeValues = { [k: string]: ValueCell<ArrayType> }
  155. export type AttributeBuffers = [string, AttributeBuffer][]
  156. export interface AttributeBuffer extends Buffer {
  157. bind: (location: number) => void
  158. }
  159. export function createAttributeBuffer<T extends ArrayType, S extends AttributeItemSize>(ctx: WebGLContext, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
  160. const { gl } = ctx
  161. const { instancedArrays } = ctx.extensions
  162. const buffer = createBuffer(ctx, array, usageHint, 'attribute')
  163. const { _buffer, _bufferType, _dataType, _bpe } = buffer
  164. return {
  165. ...buffer,
  166. bind: (location: number) => {
  167. gl.bindBuffer(_bufferType, _buffer)
  168. if (itemSize === 16) {
  169. for (let i = 0; i < 4; ++i) {
  170. gl.enableVertexAttribArray(location + i)
  171. gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * 4 * _bpe, i * 4 * _bpe)
  172. instancedArrays.vertexAttribDivisor(location + i, divisor)
  173. }
  174. } else {
  175. gl.enableVertexAttribArray(location)
  176. gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0)
  177. instancedArrays.vertexAttribDivisor(location, divisor)
  178. }
  179. }
  180. }
  181. }
  182. export function createAttributeBuffers(ctx: WebGLContext, schema: RenderableSchema, values: AttributeValues) {
  183. const buffers: AttributeBuffers = []
  184. Object.keys(schema).forEach(k => {
  185. const spec = schema[k]
  186. if (spec.type === 'attribute') {
  187. buffers[buffers.length] = [k, createAttributeBuffer(ctx, values[k].ref.value, spec.itemSize, spec.divisor)]
  188. }
  189. })
  190. return buffers
  191. }
  192. //
  193. export type ElementsType = Uint16Array | Uint32Array
  194. export type ElementsKind = 'uint16' | 'uint32'
  195. export interface ElementsBuffer extends Buffer {
  196. bind: () => void
  197. }
  198. export function createElementsBuffer(ctx: WebGLContext, array: ElementsType, usageHint: UsageHint = 'static'): ElementsBuffer {
  199. const { gl } = ctx
  200. const buffer = createBuffer(ctx, array, usageHint, 'elements')
  201. const { _buffer } = buffer
  202. return {
  203. ...buffer,
  204. bind: () => {
  205. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _buffer);
  206. }
  207. }
  208. }