resources.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. const variant = (props.defineValues.dRenderVariant?.ref.value || '') as string;
  93. Object.keys(props.defineValues).forEach(k => {
  94. if (!props.shaderCode.ignoreDefine?.(k, variant, props.defineValues)) {
  95. array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value));
  96. }
  97. });
  98. return hashFnv32a(array).toString();
  99. },
  100. (props: ProgramProps) => wrap('program', createProgram(gl, state, extensions, getShader, props)),
  101. (program: Program) => { program.destroy(); }
  102. );
  103. return {
  104. attribute: (array: ArrayType, itemSize: AttributeItemSize, divisor: number, usageHint?: UsageHint) => {
  105. return wrap('attribute', createAttributeBuffer(gl, state, extensions, array, itemSize, divisor, usageHint));
  106. },
  107. elements: (array: ElementsType, usageHint?: UsageHint) => {
  108. return wrap('elements', createElementsBuffer(gl, array, usageHint));
  109. },
  110. framebuffer: () => {
  111. return wrap('framebuffer', createFramebuffer(gl));
  112. },
  113. program: (defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) => {
  114. return wrapCached(programCache.get({ defineValues, shaderCode, schema }));
  115. },
  116. renderbuffer: (format: RenderbufferFormat, attachment: RenderbufferAttachment, width: number, height: number) => {
  117. return wrap('renderbuffer', createRenderbuffer(gl, format, attachment, width, height));
  118. },
  119. shader: getShader,
  120. texture: (kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter) => {
  121. return wrap('texture', createTexture(gl, extensions, kind, format, type, filter));
  122. },
  123. vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
  124. return wrap('vertexArray', createVertexArray(gl, extensions, program, attributeBuffers, elementsBuffer));
  125. },
  126. getByteCounts: () => {
  127. let texture = 0;
  128. sets.texture.forEach(r => {
  129. texture += (r as Texture).getByteCount();
  130. });
  131. let attribute = 0;
  132. sets.attribute.forEach(r => {
  133. attribute += (r as AttributeBuffer).length * 4;
  134. });
  135. let elements = 0;
  136. sets.elements.forEach(r => {
  137. elements += (r as ElementsBuffer).length * 4;
  138. });
  139. return { texture, attribute, elements };
  140. },
  141. reset: () => {
  142. sets.attribute.forEach(r => r.reset());
  143. sets.elements.forEach(r => r.reset());
  144. sets.framebuffer.forEach(r => r.reset());
  145. sets.renderbuffer.forEach(r => r.reset());
  146. sets.shader.forEach(r => r.reset());
  147. sets.program.forEach(r => r.reset());
  148. sets.vertexArray.forEach(r => r.reset());
  149. sets.texture.forEach(r => r.reset());
  150. },
  151. destroy: () => {
  152. sets.attribute.forEach(r => r.destroy());
  153. sets.elements.forEach(r => r.destroy());
  154. sets.framebuffer.forEach(r => r.destroy());
  155. sets.renderbuffer.forEach(r => r.destroy());
  156. sets.shader.forEach(r => r.destroy());
  157. sets.program.forEach(r => r.destroy());
  158. sets.vertexArray.forEach(r => r.destroy());
  159. sets.texture.forEach(r => r.destroy());
  160. shaderCache.clear();
  161. programCache.clear();
  162. }
  163. };
  164. }