resources.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /**
  2. * Copyright (c) 2020-2023 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, CubeFaces, createCubeTexture } 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. cubeTexture: (faces: CubeFaces, mipmaps: boolean, onload?: () => void) => Texture,
  54. vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => VertexArray,
  55. getByteCounts: () => ByteCounts
  56. reset: () => void
  57. destroy: () => void
  58. }
  59. export function createResources(gl: GLRenderingContext, state: WebGLState, stats: WebGLStats, extensions: WebGLExtensions): WebGLResources {
  60. const sets: { [k in ResourceName]: Set<Resource> } = {
  61. attribute: new Set<Resource>(),
  62. elements: new Set<Resource>(),
  63. framebuffer: new Set<Resource>(),
  64. program: new Set<Resource>(),
  65. renderbuffer: new Set<Resource>(),
  66. shader: new Set<Resource>(),
  67. texture: new Set<Resource>(),
  68. cubeTexture: new Set<Resource>(),
  69. vertexArray: new Set<Resource>(),
  70. };
  71. function wrap<T extends Resource>(name: ResourceName, resource: T) {
  72. sets[name].add(resource);
  73. stats.resourceCounts[name] += 1;
  74. return {
  75. ...resource,
  76. destroy: () => {
  77. resource.destroy();
  78. sets[name].delete(resource);
  79. stats.resourceCounts[name] -= 1;
  80. }
  81. };
  82. }
  83. const shaderCache = createReferenceCache(
  84. (props: ShaderProps) => JSON.stringify(props),
  85. (props: ShaderProps) => wrap('shader', createShader(gl, props)),
  86. (shader: Shader) => { shader.destroy(); }
  87. );
  88. function getShader(type: ShaderType, source: string) {
  89. return wrapCached(shaderCache.get({ type, source }));
  90. }
  91. const programCache = createReferenceCache(
  92. (props: ProgramProps) => {
  93. const array = [props.shaderCode.id];
  94. const variant = (props.defineValues.dRenderVariant?.ref.value || '') as string;
  95. Object.keys(props.defineValues).forEach(k => {
  96. if (!props.shaderCode.ignoreDefine?.(k, variant, props.defineValues)) {
  97. array.push(hashString(k), defineValueHash(props.defineValues[k].ref.value));
  98. }
  99. });
  100. return hashFnv32a(array).toString();
  101. },
  102. (props: ProgramProps) => wrap('program', createProgram(gl, state, extensions, getShader, props)),
  103. (program: Program) => { program.destroy(); }
  104. );
  105. return {
  106. attribute: (array: ArrayType, itemSize: AttributeItemSize, divisor: number, usageHint?: UsageHint) => {
  107. return wrap('attribute', createAttributeBuffer(gl, state, extensions, array, itemSize, divisor, usageHint));
  108. },
  109. elements: (array: ElementsType, usageHint?: UsageHint) => {
  110. return wrap('elements', createElementsBuffer(gl, array, usageHint));
  111. },
  112. framebuffer: () => {
  113. return wrap('framebuffer', createFramebuffer(gl));
  114. },
  115. program: (defineValues: DefineValues, shaderCode: ShaderCode, schema: RenderableSchema) => {
  116. return wrapCached(programCache.get({ defineValues, shaderCode, schema }));
  117. },
  118. renderbuffer: (format: RenderbufferFormat, attachment: RenderbufferAttachment, width: number, height: number) => {
  119. return wrap('renderbuffer', createRenderbuffer(gl, format, attachment, width, height));
  120. },
  121. shader: getShader,
  122. texture: (kind: TextureKind, format: TextureFormat, type: TextureType, filter: TextureFilter) => {
  123. return wrap('texture', createTexture(gl, extensions, kind, format, type, filter));
  124. },
  125. cubeTexture: (faces: CubeFaces, mipmaps: boolean, onload?: () => void) => {
  126. return wrap('cubeTexture', createCubeTexture(gl, faces, mipmaps, onload));
  127. },
  128. vertexArray: (program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) => {
  129. return wrap('vertexArray', createVertexArray(gl, extensions, program, attributeBuffers, elementsBuffer));
  130. },
  131. getByteCounts: () => {
  132. let texture = 0;
  133. sets.texture.forEach(r => {
  134. texture += (r as Texture).getByteCount();
  135. });
  136. sets.cubeTexture.forEach(r => {
  137. texture += (r as Texture).getByteCount();
  138. });
  139. let attribute = 0;
  140. sets.attribute.forEach(r => {
  141. attribute += (r as AttributeBuffer).length * 4;
  142. });
  143. let elements = 0;
  144. sets.elements.forEach(r => {
  145. elements += (r as ElementsBuffer).length * 4;
  146. });
  147. return { texture, attribute, elements };
  148. },
  149. reset: () => {
  150. sets.attribute.forEach(r => r.reset());
  151. sets.elements.forEach(r => r.reset());
  152. sets.framebuffer.forEach(r => r.reset());
  153. sets.renderbuffer.forEach(r => r.reset());
  154. sets.shader.forEach(r => r.reset());
  155. sets.program.forEach(r => r.reset());
  156. sets.vertexArray.forEach(r => r.reset());
  157. sets.texture.forEach(r => r.reset());
  158. },
  159. destroy: () => {
  160. sets.attribute.forEach(r => r.destroy());
  161. sets.elements.forEach(r => r.destroy());
  162. sets.framebuffer.forEach(r => r.destroy());
  163. sets.renderbuffer.forEach(r => r.destroy());
  164. sets.shader.forEach(r => r.destroy());
  165. sets.program.forEach(r => r.destroy());
  166. sets.vertexArray.forEach(r => r.destroy());
  167. sets.texture.forEach(r => r.destroy());
  168. shaderCache.clear();
  169. programCache.clear();
  170. }
  171. };
  172. }