910e62b5创建于 1月15日历史提交
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

const parsedString = new URLSearchParams(window.location.search);
const canvas_string = parsedString.get('canvas');
const method_string = parsedString.get('method');

function is_onscreen() {
  return canvas_string == 'onscreen';
}

function is_offscreen() {
  return canvas_string == 'offscreen';
}

function is_transfer_to_offscreen() {
  return canvas_string == 'transferToOffscreen';
}

function is_drawImage_method() {
  return method_string == 'drawImage';
}

function is_blob_method() {
  return method_string == 'toBlob';
}

function is_toDataURL_method() {
  return method_string == 'toDataURL';
}

function is_transfter_to_image_bitmap_method() {
  return method_string == 'transferToImageBitmap';
}

function is_copy_external_image_to_texture_method() {
  return method_string == 'copyExternalImageToTexture';
}

function is_gl_texImage2d_method() {
  return method_string == 'glTexImage2D';
}

function is_capture_stream_method() {
  return method_string == 'captureStream';
}

function logOutput(s) {
  if (window.domAutomationController)
    window.domAutomationController.log(s);
  else
    console.log(s);
}

function sendResult(status, detail) {
  logOutput(status + ' ' + detail);
  if (window.domAutomationController) {
    window.domAutomationController.send(status);
  }
}

function waitForFinish() {
  let numFramesBeforeEnd = 15;

  function waitForFinishImpl() {
    if (--numFramesBeforeEnd == 0) {
      sendResult("SUCCESS", "Test complete");
    } else {
      window.requestAnimationFrame(waitForFinishImpl);
    }
  }

  window.requestAnimationFrame(waitForFinishImpl);
}

function createGpuCanvas(divId) {
  if (is_onscreen() || is_transfer_to_offscreen()) {
    const div = document.getElementById(divId);
    div.insertAdjacentHTML('afterbegin', `
      <canvas id="${divId}_gpu" width="100" height="100" class="nomargin"
        style="background-color: lightgray;"></canvas>`);
    const gpuElement = document.getElementById(divId + "_" + "gpu");
    if (is_transfer_to_offscreen()) {
      return gpuElement.transferControlToOffscreen();
    } else {
      return gpuElement;
    }
  } else if (is_offscreen()) {
    return new OffscreenCanvas(100, 100);
  }
}

function createTargetCanvas(divId) {
  const div = document.getElementById(divId);
  div.insertAdjacentHTML('beforeend', `
    <canvas id="${divId}_destroyed" width="100" height="100" class="nomargin"
        style="background-color: gray;"></canvas>
    <canvas id="${divId}_lost" width="100" height="100" class="nomargin"
        style="background-color: gray;"></canvas>`);
}

function createTargetImg(divId) {
  const div = document.getElementById(divId);
  div.insertAdjacentHTML('beforeend', `
    <img id="${divId}_destroyed" width="100" height="100" class="nomargin"
      style="background-color: gray;"></img>
    <img id="${divId}_lost" width="100" height="100" class="nomargin"
      style="background-color: gray;"></img>`);
}

function createTargetVideo(divId) {
  const div = document.getElementById(divId);
  div.insertAdjacentHTML('beforeend', `
    <video id="${divId}_destroyed" width="100" height="100" class="nomargin"
      playsinline autoplay muted style="background-color: gray;"></video>`);
}

function createTargetNodes(divId) {
  if (is_drawImage_method()) {
    createTargetCanvas(divId);
  } else if (is_blob_method()) {
    createTargetImg(divId);
  } else if (is_toDataURL_method()) {
    createTargetImg(divId);
  } else if (is_transfter_to_image_bitmap_method()) {
    createTargetCanvas(divId);
  } else if (is_copy_external_image_to_texture_method()) {
    createTargetCanvas(divId);
  } else if (is_gl_texImage2d_method()) {
    createTargetCanvas(divId);
  } else if (is_capture_stream_method()) {
    createTargetVideo(divId);
  } else {
    sendResult("FAILURE", "unknown method");
  }
}

function drawImage(canvas, divId, targetId) {
  const targetNode = document.getElementById(divId + "_" + targetId);
  const ctx = targetNode.getContext('2d');
  if (!ctx) {
    sendResult("FAILURE", "getContext(2d) failed");
    return;
  }

  ctx.drawImage(canvas, 0, 0);
}

function toBlob(canvas, divId, targetId) {
  if (is_onscreen()) {
    canvas.toBlob((blob) => {
      const url = URL.createObjectURL(blob);
      const image = document.getElementById(divId + "_" + targetId);
      image.src = url;
    });
  } else {
    canvas.convertToBlob().then((blob) => {
      const url = URL.createObjectURL(blob);
      const image = document.getElementById(divId + "_" + targetId);
      image.src = url;
    });
  }
}

function toDataURL(canvas, divId, targetId) {
  const image = document.getElementById(divId + "_" + targetId);
  image.src = canvas.toDataURL();
}

function transferToImageBitmap(canvas, divId, targetId) {
  const targetNode = document.getElementById(divId + "_" + targetId);
  const ctx = targetNode.getContext('2d');
  if (!ctx) {
    sendResult("FAILURE", "getContext(2d) failed");
    return;
  }

  const imageBitmap = canvas.transferToImageBitmap();
  ctx.drawImage(imageBitmap, 0, 0);
}

async function copyExternalImageToTexture(canvas, divId, targetId) {
  const gpuCanvas = document.getElementById(divId + "_" + targetId);
  const [gpuDevice, gpuContext] = await webGpuUtils.init(gpuCanvas);
  if (!gpuDevice || !gpuContext) {
    sendResult("FAILURE", "Failed to initialize WebGPU");
    return;
  }

  webGpuUtils.uploadToGPUTextureTest(gpuDevice, gpuContext, canvas);
}

function glTexImage2D(canvas, divId, targetId) {
  const glCanvas = document.getElementById(divId + "_" + targetId);
  const gl = glCanvas.getContext('webgl2', { antialias: false });
  if (!gl) {
    sendResult("FAILURE", "getContext(webgl) failed");
    return;
  }

  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas);

  const fbo = gl.createFramebuffer();
  gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
  gl.framebufferTexture2D(
    gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);

  gl.blitFramebuffer(0, 0, canvas.width, canvas.height, 0, 0,
    glCanvas.width, glCanvas.height, gl.COLOR_BUFFER_BIT,
    gl.LINEAR);

  gl.deleteFramebuffer(fbo);
  gl.deleteTexture(tex);
}

function captureStream(canvas, divId, targetId) {
  if (targetId == 'lost') {
    // When requests a new video frame, it will wait for the canvas to
    // be re-drawn. After re-draw, it will copy the image to the video.
    // But after the device is lost, the canvas will not be re-drawn.
    return;
  }

  const gpuCanvas = document.getElementById(divId + "_" + "gpu");
  const stream = gpuCanvas.captureStream(0);
  const video = document.getElementById(divId + "_" + "destroyed");
  video.srcObject = stream;

  stream.getVideoTracks()[0].requestFrame();
}

function copyCanvasImage(canvas, divId, targetId) {
  if (is_drawImage_method()) {
    drawImage(canvas, divId, targetId);
  } else if (is_blob_method()) {
    toBlob(canvas, divId, targetId);
  } else if (is_toDataURL_method()) {
    toDataURL(canvas, divId, targetId);
  } else if (is_transfter_to_image_bitmap_method()) {
    transferToImageBitmap(canvas, divId, targetId);
  } else if (is_copy_external_image_to_texture_method()) {
    copyExternalImageToTexture(canvas, divId, targetId);
  } else if (is_gl_texImage2d_method()) {
    glTexImage2D(canvas, divId, targetId);
  } else if (is_capture_stream_method()) {
    captureStream(canvas, divId, targetId);
  } else {
    sendResult("FAILURE", "unknown method");
  }
}

var deviceCount = 0;
function waitAllDevicesLost() {
  deviceCount += 1;
  if (deviceCount == 2) {
    waitForFinish();
  }
}

const opaque_id = "opaque";
const transparent_id = "transparent";

let gpuCanvas;
let gpuCanvasAlpha;

function setup() {

  gpuCanvas = createGpuCanvas(opaque_id);
  gpuCanvasAlpha = createGpuCanvas(transparent_id);

  // create target nodes.
  createTargetNodes(opaque_id);
  createTargetNodes(transparent_id);

  sendResult("READY");
}

async function initialize(canvas, divId, alpha) {
  const [gpuDevice, gpuContext] = await webGpuUtils.init(canvas, alpha);
  if (!gpuDevice || !gpuContext) {
    sendResult("FAILURE", "Failed to initialize WebGPU");
    return;
  }

  gpuDevice.lost.then(() => {
    console.log(`WebGPU Device for "${divId}" canvas lost`);

    copyCanvasImage(canvas, divId, "lost");
    waitAllDevicesLost();
  });

  return [gpuDevice, gpuContext];
}

async function render() {
  const [gpuDevice, gpuContext] =
    await initialize(gpuCanvas, opaque_id, false);

  const [gpuDeviceAlpha, gpuContextAlpha] =
    await initialize(gpuCanvasAlpha, transparent_id, true);

  const redColor = { r: 1, g: 0, b: 0, a: 1 };
  const greenColor = { r: 0, g: 1, b: 0, a: 1 };

  var g_renderCount = 1;
  const renderCallback = function () {
    const color = (g_renderCount % 2 == 0) ? redColor : greenColor;

    webGpuUtils.solidColorTest(gpuDevice, gpuContext, color);
    webGpuUtils.solidColorTest(gpuDeviceAlpha, gpuContextAlpha, color);

    if (g_renderCount == 0) {
      gpuDevice.destroy();
      gpuDeviceAlpha.destroy();

      copyCanvasImage(gpuCanvas, opaque_id, "destroyed");
      copyCanvasImage(gpuCanvasAlpha, transparent_id, "destroyed");
    } else {
      g_renderCount--;
      window.requestAnimationFrame(renderCallback);
    }
  };

  window.requestAnimationFrame(renderCallback);
}