context.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 { createProgramCache, ProgramCache } from './program'
  7. import { createShaderCache, ShaderCache } from './shader'
  8. import { GLRenderingContext, COMPAT_instanced_arrays, COMPAT_standard_derivatives, COMPAT_vertex_array_object, getInstancedArrays, getStandardDerivatives, getVertexArrayObject, isWebGL2, COMPAT_element_index_uint, getElementIndexUint } from './compat';
  9. export function getGLContext(canvas: HTMLCanvasElement, contextAttributes?: WebGLContextAttributes): GLRenderingContext | null {
  10. function getContext(contextId: 'webgl' | 'experimental-webgl' | 'webgl2') {
  11. try {
  12. return canvas.getContext(contextId, contextAttributes) as GLRenderingContext | null
  13. } catch (e) {
  14. return null
  15. }
  16. }
  17. return getContext('webgl2') || getContext('webgl') || getContext('experimental-webgl')
  18. }
  19. function getPixelRatio() {
  20. return (typeof window !== 'undefined') ? window.devicePixelRatio : 1
  21. }
  22. function unbindResources (gl: GLRenderingContext) {
  23. // bind null to all texture units
  24. const maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
  25. for (let i = 0; i < maxTextureImageUnits; ++i) {
  26. gl.activeTexture(gl.TEXTURE0 + i)
  27. gl.bindTexture(gl.TEXTURE_2D, null)
  28. gl.bindTexture(gl.TEXTURE_CUBE_MAP, null)
  29. }
  30. // assign the smallest possible buffer to all attributes
  31. const buf = gl.createBuffer();
  32. gl.bindBuffer(gl.ARRAY_BUFFER, buf);
  33. const maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
  34. for (let i = 0; i < maxVertexAttribs; ++i) {
  35. gl.vertexAttribPointer(i, 1, gl.FLOAT, false, 0, 0);
  36. }
  37. // bind null to all buffers
  38. gl.bindBuffer(gl.ARRAY_BUFFER, null)
  39. gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null)
  40. gl.bindRenderbuffer(gl.RENDERBUFFER, null)
  41. unbindFramebuffer(gl)
  42. }
  43. function unbindFramebuffer(gl: GLRenderingContext) {
  44. gl.bindFramebuffer(gl.FRAMEBUFFER, null)
  45. }
  46. export function createImageData(buffer: Uint8Array, width: number, height: number) {
  47. const w = width * 4
  48. const h = height
  49. const data = new Uint8ClampedArray(width * height * 4)
  50. for (let i = 0, maxI = h / 2; i < maxI; ++i) {
  51. for (let j = 0, maxJ = w; j < maxJ; ++j) {
  52. const index1 = i * w + j;
  53. const index2 = (h-i-1) * w + j;
  54. data[index1] = buffer[index2];
  55. data[index2] = buffer[index1];
  56. }
  57. }
  58. return new ImageData(data, width, height);
  59. }
  60. type Extensions = {
  61. instancedArrays: COMPAT_instanced_arrays
  62. standardDerivatives: COMPAT_standard_derivatives
  63. elementIndexUint: COMPAT_element_index_uint | null
  64. vertexArrayObject: COMPAT_vertex_array_object | null
  65. }
  66. /** A WebGL context object, including the rendering context, resource caches and counts */
  67. export interface Context {
  68. readonly gl: GLRenderingContext
  69. readonly isWebGL2: boolean
  70. readonly extensions: Extensions
  71. readonly pixelRatio: number
  72. readonly shaderCache: ShaderCache
  73. readonly programCache: ProgramCache
  74. bufferCount: number
  75. framebufferCount: number
  76. renderbufferCount: number
  77. textureCount: number
  78. vaoCount: number
  79. drawCount: number
  80. instanceCount: number
  81. instancedDrawCount: number
  82. readonly maxTextureSize: number
  83. unbindFramebuffer: () => void
  84. readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => void
  85. destroy: () => void
  86. }
  87. export function createContext(gl: GLRenderingContext): Context {
  88. const instancedArrays = getInstancedArrays(gl)
  89. if (instancedArrays === null) {
  90. throw new Error('Could not find support for "instanced_arrays"')
  91. }
  92. const standardDerivatives = getStandardDerivatives(gl)
  93. if (standardDerivatives === null) {
  94. throw new Error('Could not find support for "standard_derivatives"')
  95. }
  96. const elementIndexUint = getElementIndexUint(gl)
  97. if (elementIndexUint === null) {
  98. console.warn('Could not find support for "element_index_uint"')
  99. }
  100. const vertexArrayObject = getVertexArrayObject(gl)
  101. if (vertexArrayObject === null) {
  102. console.log('Could not find support for "vertex_array_object"')
  103. }
  104. const shaderCache = createShaderCache()
  105. const programCache = createProgramCache()
  106. const parameters = {
  107. maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE)
  108. }
  109. return {
  110. gl,
  111. isWebGL2: isWebGL2(gl),
  112. extensions: { instancedArrays, standardDerivatives, elementIndexUint, vertexArrayObject },
  113. pixelRatio: getPixelRatio(),
  114. shaderCache,
  115. programCache,
  116. bufferCount: 0,
  117. framebufferCount: 0,
  118. renderbufferCount: 0,
  119. textureCount: 0,
  120. vaoCount: 0,
  121. drawCount: 0,
  122. instanceCount: 0,
  123. instancedDrawCount: 0,
  124. get maxTextureSize () { return parameters.maxTextureSize },
  125. unbindFramebuffer: () => unbindFramebuffer(gl),
  126. readPixels: (x: number, y: number, width: number, height: number, buffer: Uint8Array) => {
  127. gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
  128. // TODO check is very expensive
  129. // if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) === gl.FRAMEBUFFER_COMPLETE) {
  130. // gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buffer)
  131. // } else {
  132. // console.error('Reading pixels failed. Framebuffer not complete.')
  133. // }
  134. },
  135. destroy: () => {
  136. unbindResources(gl)
  137. programCache.dispose()
  138. shaderCache.dispose()
  139. // TODO destroy buffers and textures
  140. }
  141. }
  142. }