resources.ts 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. /**
  2. * Copyright (c) 2020-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { ProgramProps, createProgram, Program } from './program';
  7. import { ShaderType, createShader, Shader, ShaderProps } from './shader';
  8. import { GLRenderingContext } from './compat';
  9. import { Framebuffer, createFramebuffer } from './framebuffer';
  10. import { WebGLExtensions } from './extensions';
  11. import { WebGLState } from './state';
  12. import { AttributeBuffer, UsageHint, ArrayType, AttributeItemSize, createAttributeBuffer, ElementsBuffer, createElementsBuffer, ElementsType, AttributeBuffers } from './buffer';
  13. import { createReferenceCache, ReferenceItem } from '../../mol-util/reference-cache';
  14. import { WebGLStats } from './context';
  15. import { hashString, hashFnv32a } from '../../mol-data/util';
  16. import { DefineValues, ShaderCode } from '../shader-code';
  17. import { RenderableSchema } from '../renderable/schema';
  18. import { createRenderbuffer, Renderbuffer, RenderbufferAttachment, RenderbufferFormat } from './renderbuffer';
  19. import { Texture, TextureKind, TextureFormat, TextureType, TextureFilter, createTexture } from './texture';
  20. import { VertexArray, createVertexArray } from './vertex-array';
  21. function defineValueHash(v: boolean | number | string): number {
  22. return typeof v === 'boolean' ? (v ? 1 : 0) :
  23. typeof v === 'number' ? (v * 10000) : hashString(v);
  24. }
  25. function wrapCached<T extends Resource>(resourceItem: ReferenceItem<T>) {
  26. const wrapped = {
  27. ...resourceItem.value,
  28. destroy: () => {
  29. resourceItem.free();
  30. }
  31. };
  32. return wrapped;
  33. }
  34. //
  35. interface Resource {
  36. reset: () => void
  37. destroy: () => void
  38. }
  39. type ResourceName = keyof WebGLStats['resourceCounts']
  40. type ByteCounts = {
  41. texture: number
  42. attribute: number
  43. elements: number
  44. }
  45. export interface WebGLResources {
  46. attribute: (array: ArrayType, itemSize: AttributeItemSize, divisor: number, usageHint?: UsageHint) => AttributeBuffer
  47. elements: (array: ElementsType, usageHint?: UsageHint) => ElementsBuffer
  48. framebuffer: () => Framebuffer
  49. program: (defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) => Program
  50. renderbuffer: (format: RenderbufferFormat, attachment: RenderbufferAttachment, width: number, height: number) => Renderbuffer
  51. shader: (type: ShaderType, source: string) => Shader
  52. texture: (kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter) => Texture,
  53. vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => VertexArray,
  54. getByteCounts: () => ByteCounts
  55. reset: () => void
  56. destroy: () => void
  57. }
  58. export function createResources(gl: GLRenderingContext, state: WebGLState, stats: WebGLStats, extensions: WebGLExtensions): WebGLResources {
  59. const sets: { [k in ResourceName]: Set<Resource> } = {
  60. attribute: new Set<Resource>(),
  61. elements: new Set<Resource>(),
  62. framebuffer: new Set<Resource>(),
  63. program: new Set<Resource>(),
  64. renderbuffer: new Set<Resource>(),
  65. shader: new Set<Resource>(),
  66. texture: new Set<Resource>(),
  67. vertexArray: new Set<Resource>(),
  68. };
  69. function wrap<T extends Resource>(name: ResourceName, resource: T) {
  70. sets[name].add(resource);
  71. stats.resourceCounts[name] += 1;
  72. return {
  73. ...resource,
  74. destroy: () => {
  75. resource.destroy();
  76. sets[name].delete(resource);
  77. stats.resourceCounts[name] -= 1;
  78. }
  79. };
  80. }
  81. const shaderCache = createReferenceCache(
  82. (props: ShaderProps) => JSON.stringify(props),
  83. (props: ShaderProps) => wrap('shader', createShader(gl, props)),
  84. (shader: Shader) => { shader.destroy(); }
  85. );
  86. function getShader(type: ShaderType, source: string) {
  87. return wrapCached(shaderCache.get({ type, source }));
  88. }
  89. const programCache = createReferenceCache(
  90. (props: ProgramProps) => {
  91. const array = [props.shaderCode.id];
  92. Object.keys(props.defineValues).forEach(k => array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value)));
  93. return hashFnv32a(array).toString();
  94. },
  95. (props: ProgramProps) => wrap('program', createProgram(gl, state, extensions, getShader, props)),
  96. (program: Program) => { program.destroy(); }
  97. );
  98. return {
  99. attribute: (array: ArrayType, itemSize: AttributeItemSize, divisor: number, usageHint?: UsageHint) => {
  100. return wrap('attribute', createAttributeBuffer(gl, extensions, array, itemSize, divisor, usageHint));
  101. },
  102. elements: (array: ElementsType, usageHint?: UsageHint) => {
  103. return wrap('elements', createElementsBuffer(gl, array, usageHint));
  104. },
  105. framebuffer: () => {
  106. return wrap('framebuffer', createFramebuffer(gl));
  107. },
  108. program: (defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) => {
  109. return wrapCached(programCache.get({ defineValues, shaderCode, schema }));
  110. },
  111. renderbuffer: (format: RenderbufferFormat, attachment: RenderbufferAttachment, width: number, height: number) => {
  112. return wrap('renderbuffer', createRenderbuffer(gl, format, attachment, width, height));
  113. },
  114. shader: getShader,
  115. texture: (kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter) => {
  116. return wrap('texture', createTexture(gl, extensions, kind, format, type, filter));
  117. },
  118. vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
  119. return wrap('vertexArray', createVertexArray(extensions, program, attributeBuffers, elementsBuffer));
  120. },
  121. getByteCounts: () => {
  122. let texture = 0;
  123. sets.texture.forEach(r => {
  124. texture += (r as Texture).getByteCount();
  125. });
  126. let attribute = 0;
  127. sets.attribute.forEach(r => {
  128. attribute += (r as AttributeBuffer).length * 4;
  129. });
  130. let elements = 0;
  131. sets.elements.forEach(r => {
  132. elements += (r as ElementsBuffer).length * 4;
  133. });
  134. return { texture, attribute, elements };
  135. },
  136. reset: () => {
  137. sets.attribute.forEach(r => r.reset());
  138. sets.elements.forEach(r => r.reset());
  139. sets.framebuffer.forEach(r => r.reset());
  140. sets.renderbuffer.forEach(r => r.reset());
  141. sets.shader.forEach(r => r.reset());
  142. sets.program.forEach(r => r.reset());
  143. sets.vertexArray.forEach(r => r.reset());
  144. sets.texture.forEach(r => r.reset());
  145. },
  146. destroy: () => {
  147. sets.attribute.forEach(r => r.destroy());
  148. sets.elements.forEach(r => r.destroy());
  149. sets.framebuffer.forEach(r => r.destroy());
  150. sets.renderbuffer.forEach(r => r.destroy());
  151. sets.shader.forEach(r => r.destroy());
  152. sets.program.forEach(r => r.destroy());
  153. sets.vertexArray.forEach(r => r.destroy());
  154. sets.texture.forEach(r => r.destroy());
  155. shaderCache.clear();
  156. programCache.clear();
  157. }
  158. };
  159. }