-
-
Notifications
You must be signed in to change notification settings - Fork 36.1k
Closed
Description
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!
three.js/src/renderers/webgpu/utils/WebGPUTextureUtils.js
Lines 522 to 569 in 26fb516
| 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)
- Initialize a scene with a basic mesh and a
WebGPURendererset to the WebGPU backend - Create a
RenderTargetwith the optioncolorSpace: SRGBColorSpace, then render to it - 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