Skip to content

RenderTarget + WebGPU backend: Setting color space to SRGBColorSpace in constructor breaks "readRenderTargetPixelsAsync" #31654

@EepyBerry

Description

@EepyBerry

Description

I'm currently working on rewriting portions of my project to use WebGPU+TSL, and stumbled upon this bug during testing.
Creating a RenderTarget and explicitly setting its color space to SRGBColorSpace breaks at the last line of the snippet below when executing readRenderTargetPixelsAsync (probably related to _getTypedArrayType? not sure).

From my testing, only the WebGPU backend exhibits this behaviour.
Also, tested on Windows only, not sure about other OSes!

async copyTextureToBuffer( texture, x, y, width, height, faceIndex ) {
const device = this.backend.device;
const textureData = this.backend.get( texture );
const textureGPU = textureData.texture;
const format = textureData.textureDescriptorGPU.format;
const bytesPerTexel = this._getBytesPerTexel( format );
let bytesPerRow = width * bytesPerTexel;
bytesPerRow = Math.ceil( bytesPerRow / 256 ) * 256; // Align to 256 bytes
const readBuffer = device.createBuffer(
{
size: width * height * bytesPerTexel,
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ
}
);
const encoder = device.createCommandEncoder();
encoder.copyTextureToBuffer(
{
texture: textureGPU,
origin: { x, y, z: faceIndex },
},
{
buffer: readBuffer,
bytesPerRow: bytesPerRow
},
{
width: width,
height: height
}
);
const typedArrayType = this._getTypedArrayType( format );
device.queue.submit( [ encoder.finish() ] );
await readBuffer.mapAsync( GPUMapMode.READ );
const buffer = readBuffer.getMappedRange();
return new typedArrayType( buffer );
}

Reproduction steps

(note: check code or live example below for a pre-made setup)

  1. Initialize a scene with a basic mesh and a WebGPURenderer set to the WebGPU backend
  2. Create a RenderTarget with the option colorSpace: SRGBColorSpace, then render to it
  3. Try to extract pixel data using readRenderTargetPixelsAsync

Code

Important code is between the dashed test code comments

import * as THREE from "three";
import { SRGBColorSpace, WebGPURenderer } from "three/build/three.webgpu";

// IIFE
(async () => {
  const camera = new THREE.PerspectiveCamera(
    70,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  );
  camera.position.z = 4;

  const scene = new THREE.Scene();

  const geometry = new THREE.SphereGeometry();
  const material = new THREE.MeshBasicMaterial();

  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  const renderer = new WebGPURenderer({
    antialias: true,
    alpha: true,
    forceWebGL: false,
  });
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setAnimationLoop(() => renderer.render(scene, camera));
  document.body.appendChild(renderer.domElement);
  window.addEventListener("resize", () => onWindowResize(camera, renderer));

  // ----------------------- START test code -----------------------
  const canvas = renderer.domElement;
  const screenshotRTT = new THREE.RenderTarget(canvas.width, canvas.height, {
    // ISSUE: comment/uncomment line below
    colorSpace: SRGBColorSpace,
  });
  renderer.setRenderTarget(screenshotRTT);
  await renderer.renderAsync(scene, camera);
  const data: Uint8Array = (await renderer.readRenderTargetPixelsAsync(
    screenshotRTT,
    0,
    0,
    canvas.width,
    canvas.height
  )) as Uint8Array;
  console.log(data);
  renderer.setRenderTarget(null);
  // ------------------------ END test code ------------------------
})();

function onWindowResize(
  camera: THREE.PerspectiveCamera,
  renderer: WebGPURenderer
) {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
}

Live example

Screenshots

No response

Version

r179

Device

Desktop

Browser

Chrome

OS

Windows

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions