buffer.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /**
  2. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Context } from './context'
  7. export type UsageHint = 'static' | 'dynamic' | 'stream'
  8. export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
  9. export type BufferType = 'attribute' | 'elements'
  10. export type DataTypeArrayType = {
  11. 'uint8': Uint8Array
  12. 'int8': Int8Array
  13. 'uint16': Uint16Array
  14. 'int16': Int16Array
  15. 'uint32': Uint32Array
  16. 'int32': Int32Array
  17. 'float32': Float32Array
  18. }
  19. export type ArrayType = Helpers.ValueOf<DataTypeArrayType>
  20. export type ArrayKind = keyof DataTypeArrayType
  21. export type BufferItemSize = 1 | 2 | 3 | 4 | 16
  22. export function getUsageHint(ctx: Context, usageHint: UsageHint) {
  23. const { gl } = ctx
  24. switch (usageHint) {
  25. case 'static': return gl.STATIC_DRAW
  26. case 'dynamic': return gl.DYNAMIC_DRAW
  27. case 'stream': return gl.STREAM_DRAW
  28. }
  29. }
  30. export function getDataType(ctx: Context, dataType: DataType) {
  31. const { gl } = ctx
  32. switch (dataType) {
  33. case 'uint8': return gl.UNSIGNED_BYTE
  34. case 'int8': return gl.BYTE
  35. case 'uint16': return gl.UNSIGNED_SHORT
  36. case 'int16': return gl.SHORT
  37. case 'uint32': return gl.UNSIGNED_INT
  38. case 'int32': return gl.INT
  39. case 'float32': return gl.FLOAT
  40. }
  41. }
  42. function dataTypeFromArray(ctx: Context, array: ArrayType) {
  43. const { gl } = ctx
  44. if (array instanceof Uint8Array) {
  45. return gl.UNSIGNED_BYTE
  46. } else if (array instanceof Int8Array) {
  47. return gl.BYTE
  48. } else if (array instanceof Uint16Array) {
  49. return gl.UNSIGNED_SHORT
  50. } else if (array instanceof Int16Array) {
  51. return gl.SHORT
  52. } else if (array instanceof Uint32Array) {
  53. return gl.UNSIGNED_INT
  54. } else if (array instanceof Int32Array) {
  55. return gl.INT
  56. } else if (array instanceof Float32Array) {
  57. return gl.FLOAT
  58. } else {
  59. throw new Error('Should nevver happen')
  60. }
  61. }
  62. export function getBufferType(ctx: Context, bufferType: BufferType) {
  63. const { gl } = ctx
  64. switch (bufferType) {
  65. case 'attribute': return gl.ARRAY_BUFFER
  66. case 'elements': return gl.ELEMENT_ARRAY_BUFFER
  67. }
  68. }
  69. export interface Buffer {
  70. readonly _buffer: WebGLBuffer
  71. readonly _usageHint: number
  72. readonly _bufferType: number
  73. readonly _dataType: number
  74. readonly _bpe: number
  75. updateData: (array: ArrayType) => void
  76. updateSubData: (array: ArrayType, offset: number, count: number) => void
  77. destroy: () => void
  78. }
  79. export function createBuffer(ctx: Context, array: ArrayType, itemSize: BufferItemSize, usageHint: UsageHint, bufferType: BufferType): Buffer {
  80. const { gl } = ctx
  81. const _buffer = gl.createBuffer()
  82. if (_buffer === null) {
  83. throw new Error('Could not create WebGL buffer')
  84. }
  85. const _usageHint = getUsageHint(ctx, usageHint)
  86. const _bufferType = getBufferType(ctx, bufferType)
  87. const _dataType = dataTypeFromArray(ctx, array)
  88. const _bpe = array.BYTES_PER_ELEMENT
  89. function updateData(array: ArrayType) {
  90. gl.bindBuffer(_bufferType, _buffer)
  91. gl.bufferData(_bufferType, array, _usageHint)
  92. }
  93. updateData(array)
  94. return {
  95. _buffer,
  96. _usageHint,
  97. _bufferType,
  98. _dataType,
  99. _bpe,
  100. updateData,
  101. updateSubData: (array: ArrayType, offset: number, count: number) => {
  102. gl.bindBuffer(_bufferType, _buffer)
  103. gl.bufferSubData(_bufferType, offset * _bpe, array.subarray(offset, offset + count))
  104. },
  105. destroy: () => {
  106. gl.bindBuffer(_bufferType, _buffer)
  107. // set size to 1 before deleting
  108. gl.bufferData(_bufferType, 1, _usageHint)
  109. gl.deleteBuffer(_buffer)
  110. }
  111. }
  112. }
  113. export type AttributeDefs = {
  114. [k: string]: { kind: ArrayKind, itemSize: BufferItemSize, divisor: number }
  115. }
  116. export type AttributeValues = { [k: string]: ArrayType }
  117. export type AttributeBuffers = { [k: string]: AttributeBuffer }
  118. export interface AttributeBuffer extends Buffer {
  119. bind: (location: number) => void
  120. }
  121. export function createAttributeBuffer<T extends ArrayType, S extends BufferItemSize>(ctx: Context, array: ArrayType, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
  122. const { gl } = ctx
  123. const { angleInstancedArrays } = ctx.extensions
  124. const buffer = createBuffer(ctx, array, itemSize, usageHint, 'attribute')
  125. const { _buffer, _bufferType, _dataType, _bpe } = buffer
  126. return {
  127. ...buffer,
  128. bind: (location: number) => {
  129. gl.bindBuffer(_bufferType, _buffer)
  130. if (itemSize === 16) {
  131. for (let i = 0; i < 4; ++i) {
  132. gl.enableVertexAttribArray(location + i)
  133. gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * 4 * _bpe, i * 4 * _bpe)
  134. angleInstancedArrays.vertexAttribDivisorANGLE(location + i, divisor)
  135. }
  136. } else {
  137. gl.enableVertexAttribArray(location)
  138. gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0)
  139. angleInstancedArrays.vertexAttribDivisorANGLE(location, divisor)
  140. }
  141. }
  142. }
  143. }
  144. export function createAttributeBuffers<T extends AttributeDefs>(ctx: Context, props: T, state: AttributeValues) {
  145. const buffers: AttributeBuffers = {}
  146. Object.keys(props).forEach(k => {
  147. buffers[k] = createAttributeBuffer(ctx, state[k], props[k].itemSize, props[k].divisor)
  148. })
  149. return buffers as AttributeBuffers
  150. }
  151. export type ElementsType = Uint16Array | Uint32Array
  152. export type ElementsKind = 'uint16' | 'uint32'
  153. export interface ElementsBuffer extends Buffer {
  154. bind: () => void
  155. }
  156. export function createElementsBuffer(ctx: Context, array: ElementsType, usageHint: UsageHint = 'static'): ElementsBuffer {
  157. const { gl } = ctx
  158. const buffer = createBuffer(ctx, array, 1, usageHint, 'elements')
  159. const { _buffer } = buffer
  160. return {
  161. ...buffer,
  162. bind: () => {
  163. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _buffer);
  164. }
  165. }
  166. }