tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_encoder_cycle_collection.html (7359B)


      1 <!doctype html>
      2 <html>
      3  <head>
      4    <meta charset="utf-8" />
      5    <script src="/tests/SimpleTest/SimpleTest.js"></script>
      6    <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
      7  </head>
      8  <body>
      9    <script>
     10      ok(
     11        SpecialPowers.getBoolPref("dom.webgpu.enabled"),
     12        "Pref should be enabled."
     13      );
     14 
     15      SimpleTest.waitForExplicitFinish();
     16 
     17      // This test has 3 phases:
     18      // 1) Repeatedly call a function that creates some WebGPU objects with
     19      //    some variations. One of the objects is always an encoder. Act on
     20      //    those objects in ways that might confuse the cycle detector. All
     21      //    of the objects *should* be garbage collectable, including the
     22      //    encoders. Store a weak link to each of the encoders.
     23      // 2) Invoke garbage collection.
     24      // 3) Confirm all the encoders were garbage collected.
     25 
     26      // Define some stuff we'll use in the various phases.
     27      const gc_promise = () =>
     28        new Promise(resolve => SpecialPowers.exactGC(resolve));
     29 
     30      // Define an array of structs containing a label and a weak reference
     31      // to an encoder, then fill it by executing a bunch of WebGPU commands.
     32      let results = [];
     33 
     34      // Here's our WebGPU test function, which we'll call with permuted
     35      // parameters:
     36      // label: string label to use in error messages
     37      // encoderType: string in ["render", "compute", "bundle"].
     38      // resourceExtraParam: boolean should one of the resources get an
     39      //   added property with a scalar value. This can change the order that
     40      //   things are processed by the cycle collector.
     41      // resourceCycle: boolean should one of the resources get an added
     42      //   property that is set to the encoder.
     43      // endOrFinish: boolean should the encoder be ended. If not, it's just
     44      //   dropped.
     45      let test_func = async (
     46        label,
     47        encoderType,
     48        resourceExtraParam,
     49        resourceCycle,
     50        endOrFinish
     51      ) => {
     52        const adapter = await navigator.gpu.requestAdapter();
     53        const device = await adapter.requestDevice();
     54 
     55        let encoder;
     56        let pass;
     57        let resource;
     58        if (encoderType == "render") {
     59          // Create some resources, and setup the pass.
     60          encoder = device.createCommandEncoder();
     61          const texture = device.createTexture({
     62            size: { width: 1, height: 1, depthOrArrayLayers: 1 },
     63            format: "rgba8unorm",
     64            usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
     65          });
     66          const view = texture.createView();
     67          pass = encoder.beginRenderPass({
     68            colorAttachments: [
     69              {
     70                view,
     71                loadOp: "load",
     72                storeOp: "store",
     73              },
     74            ],
     75          });
     76          resource = view;
     77        } else if (encoderType == "compute") {
     78          // Create some resources, and setup the pass.
     79          encoder = device.createCommandEncoder();
     80          const pipeline = device.createComputePipeline({
     81            layout: "auto",
     82            compute: {
     83              module: device.createShaderModule({
     84                code: `
     85                  struct Buffer { data: array<u32>, };
     86                  @group(0) @binding(0) var<storage, read_write> buffer: Buffer;
     87                  @compute @workgroup_size(1) fn main(
     88                      @builtin(global_invocation_id) id: vec3<u32>) {
     89                    buffer.data[id.x] = buffer.data[id.x] + 1u;
     90                  }
     91                `,
     92              }),
     93              entryPoint: "main",
     94            },
     95          });
     96          pass = encoder.beginComputePass();
     97          pass.setPipeline(pipeline);
     98          resource = pipeline;
     99        } else if (encoderType == "bundle") {
    100          // Create some resources and setup the encoder.
    101          const pipeline = device.createRenderPipeline({
    102            layout: "auto",
    103            vertex: {
    104              module: device.createShaderModule({
    105                code: `
    106                  @vertex fn vert_main() -> @builtin(position) vec4<f32> {
    107                    return vec4<f32>(0.5, 0.5, 0.0, 1.0);
    108                  }
    109              `,
    110              }),
    111              entryPoint: "vert_main",
    112            },
    113            fragment: {
    114              module: device.createShaderModule({
    115                code: `
    116                  struct Data {
    117                    a : u32
    118                  };
    119 
    120                  @group(0) @binding(0) var<storage, read_write> data : Data;
    121                  @fragment fn frag_main() -> @location(0) vec4<f32> {
    122                    data.a = 0u;
    123                    return vec4<f32>();
    124                  }
    125              `,
    126              }),
    127              entryPoint: "frag_main",
    128              targets: [{ format: "rgba8unorm" }],
    129            },
    130            primitive: { topology: "point-list" },
    131          });
    132          encoder = device.createRenderBundleEncoder({
    133            colorFormats: ["rgba8unorm"],
    134          });
    135          encoder.setPipeline(pipeline);
    136          resource = pipeline;
    137        }
    138 
    139        if (resourceExtraParam) {
    140          resource.extra = true;
    141        }
    142 
    143        if (resourceCycle) {
    144          resource.encoder = encoder;
    145        }
    146 
    147        if (endOrFinish) {
    148          if (encoderType == "render" || encoderType == "compute") {
    149            pass.end();
    150          } else if (encoderType == "bundle") {
    151            encoder.finish();
    152          }
    153        }
    154 
    155        // Get a weak ref to the encoder, which we'll check after GC to ensure
    156        // that it got collected.
    157        encoderWeakRef = SpecialPowers.Cu.getWeakReference(encoder);
    158        ok(encoderWeakRef.get(), `${label} got encoder weak ref.`);
    159 
    160        results.push({
    161          label,
    162          encoderWeakRef,
    163        });
    164      };
    165 
    166      // The rest of the test will run in a promise chain. Define an async
    167      // function to fill our results.
    168      let call_test_func = async () => {
    169        for (const encoderType of ["render", "compute", "bundle"]) {
    170          for (const resourceExtraParam of [true, false]) {
    171            for (const resourceCycle of [true, false]) {
    172              for (const endOrFinish of [true, false]) {
    173                const label = `[${encoderType}, ${resourceExtraParam}, ${resourceCycle}, ${endOrFinish}]`;
    174                await test_func(
    175                  label,
    176                  encoderType,
    177                  resourceExtraParam,
    178                  resourceCycle,
    179                  endOrFinish
    180                );
    181              }
    182            }
    183          }
    184        }
    185      };
    186 
    187      // Phase 1: Start the promise chain and call test_func repeated to fill
    188      // our results struct.
    189      call_test_func()
    190        // Phase 2: Do our garbage collection.
    191        .then(gc_promise)
    192        .then(gc_promise)
    193        .then(gc_promise)
    194        // Phase 3: Iterate over results and check that all of the encoders
    195        // were garbage collected.
    196        .then(() => {
    197          for (result of results) {
    198            ok(
    199              !result.encoderWeakRef.get(),
    200              `${result.label} cycle collected encoder.`
    201            );
    202          }
    203        })
    204        .catch(e => {
    205          ok(false, `unhandled exception ${e}`);
    206        })
    207        .finally(() => {
    208          SimpleTest.finish();
    209        });
    210    </script>
    211  </body>
    212 </html>