webgpuComputeContext.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import { Logger } from "../../Misc/logger.js";
  2. import { ComputeBindingType } from "../Extensions/engine.computeShader.js";
  3. import * as WebGPUConstants from "./webgpuConstants.js";
  4. /** @internal */
  5. export class WebGPUComputeContext {
  6. getBindGroups(bindings, computePipeline, bindingsMapping) {
  7. if (!bindingsMapping) {
  8. throw new Error("WebGPUComputeContext.getBindGroups: bindingsMapping is required until browsers support reflection for wgsl shaders!");
  9. }
  10. if (this._bindGroups.length === 0) {
  11. const bindGroupEntriesExist = this._bindGroupEntries.length > 0;
  12. for (const key in bindings) {
  13. const binding = bindings[key], location = bindingsMapping[key], group = location.group, index = location.binding, type = binding.type, object = binding.object;
  14. let indexInGroupEntries = binding.indexInGroupEntries;
  15. let entries = this._bindGroupEntries[group];
  16. if (!entries) {
  17. entries = this._bindGroupEntries[group] = [];
  18. }
  19. switch (type) {
  20. case ComputeBindingType.Sampler: {
  21. const sampler = object;
  22. if (indexInGroupEntries !== undefined && bindGroupEntriesExist) {
  23. entries[indexInGroupEntries].resource = this._cacheSampler.getSampler(sampler);
  24. }
  25. else {
  26. binding.indexInGroupEntries = entries.length;
  27. entries.push({
  28. binding: index,
  29. resource: this._cacheSampler.getSampler(sampler),
  30. });
  31. }
  32. break;
  33. }
  34. case ComputeBindingType.Texture:
  35. case ComputeBindingType.TextureWithoutSampler: {
  36. const texture = object;
  37. const hardwareTexture = texture._texture._hardwareTexture;
  38. if (indexInGroupEntries !== undefined && bindGroupEntriesExist) {
  39. if (type === ComputeBindingType.Texture) {
  40. entries[indexInGroupEntries++].resource = this._cacheSampler.getSampler(texture._texture);
  41. }
  42. entries[indexInGroupEntries].resource = hardwareTexture.view;
  43. }
  44. else {
  45. binding.indexInGroupEntries = entries.length;
  46. if (type === ComputeBindingType.Texture) {
  47. entries.push({
  48. binding: index - 1,
  49. resource: this._cacheSampler.getSampler(texture._texture),
  50. });
  51. }
  52. entries.push({
  53. binding: index,
  54. resource: hardwareTexture.view,
  55. });
  56. }
  57. break;
  58. }
  59. case ComputeBindingType.StorageTexture: {
  60. const texture = object;
  61. const hardwareTexture = texture._texture._hardwareTexture;
  62. if ((hardwareTexture.textureAdditionalUsages & WebGPUConstants.TextureUsage.StorageBinding) === 0) {
  63. Logger.Error(`computeDispatch: The texture (name=${texture.name}, uniqueId=${texture.uniqueId}) is not a storage texture!`, 50);
  64. }
  65. if (indexInGroupEntries !== undefined && bindGroupEntriesExist) {
  66. entries[indexInGroupEntries].resource = hardwareTexture.viewForWriting;
  67. }
  68. else {
  69. binding.indexInGroupEntries = entries.length;
  70. entries.push({
  71. binding: index,
  72. resource: hardwareTexture.viewForWriting,
  73. });
  74. }
  75. break;
  76. }
  77. case ComputeBindingType.ExternalTexture: {
  78. const texture = object;
  79. const externalTexture = texture.underlyingResource;
  80. if (indexInGroupEntries !== undefined && bindGroupEntriesExist) {
  81. entries[indexInGroupEntries].resource = this._device.importExternalTexture({ source: externalTexture });
  82. }
  83. else {
  84. binding.indexInGroupEntries = entries.length;
  85. entries.push({
  86. binding: index,
  87. resource: this._device.importExternalTexture({ source: externalTexture }),
  88. });
  89. }
  90. break;
  91. }
  92. case ComputeBindingType.UniformBuffer:
  93. case ComputeBindingType.StorageBuffer:
  94. case ComputeBindingType.DataBuffer: {
  95. const dataBuffer = type === ComputeBindingType.DataBuffer
  96. ? object
  97. : type === ComputeBindingType.UniformBuffer
  98. ? object.getBuffer()
  99. : object.getBuffer();
  100. const webgpuBuffer = dataBuffer.underlyingResource;
  101. if (indexInGroupEntries !== undefined && bindGroupEntriesExist) {
  102. entries[indexInGroupEntries].resource.buffer = webgpuBuffer;
  103. entries[indexInGroupEntries].resource.size = dataBuffer.capacity;
  104. }
  105. else {
  106. binding.indexInGroupEntries = entries.length;
  107. entries.push({
  108. binding: index,
  109. resource: {
  110. buffer: webgpuBuffer,
  111. offset: 0,
  112. size: dataBuffer.capacity,
  113. },
  114. });
  115. }
  116. break;
  117. }
  118. }
  119. }
  120. for (let i = 0; i < this._bindGroupEntries.length; ++i) {
  121. const entries = this._bindGroupEntries[i];
  122. if (!entries) {
  123. this._bindGroups[i] = undefined;
  124. continue;
  125. }
  126. this._bindGroups[i] = this._device.createBindGroup({
  127. layout: computePipeline.getBindGroupLayout(i),
  128. entries,
  129. });
  130. }
  131. this._bindGroups.length = this._bindGroupEntries.length;
  132. }
  133. return this._bindGroups;
  134. }
  135. constructor(device, cacheSampler) {
  136. this._device = device;
  137. this._cacheSampler = cacheSampler;
  138. this.uniqueId = WebGPUComputeContext._Counter++;
  139. this._bindGroupEntries = [];
  140. this.clear();
  141. }
  142. clear() {
  143. this._bindGroups = [];
  144. // Don't reset _bindGroupEntries if they have already been created, they are still ok even if we have to clear _bindGroups (the layout of the compute shader can't change once created)
  145. }
  146. }
  147. WebGPUComputeContext._Counter = 0;
  148. //# sourceMappingURL=webgpuComputeContext.js.map