Skip to content

Commit f184d55

Browse files
committed
Add support for texture async write (#7782)
1 parent 5bc5104 commit f184d55

File tree

3 files changed

+70
-22
lines changed

3 files changed

+70
-22
lines changed

src/platform/graphics/texture.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,6 +1173,22 @@ class Texture {
11731173
read(x, y, width, height, options = {}) {
11741174
return this.impl.read?.(x, y, width, height, options);
11751175
}
1176+
1177+
/**
1178+
* Upload texture data asynchronously to the GPU.
1179+
*
1180+
* @param {number} x - The left edge of the rectangle.
1181+
* @param {number} y - The top edge of the rectangle.
1182+
* @param {number} width - The width of the rectangle.
1183+
* @param {number} height - The height of the rectangle.
1184+
* @param {Uint8Array|Uint16Array|Uint32Array|Float32Array} data - The pixel data to upload. This should be a typed array.
1185+
*
1186+
* @returns {Promise<void>} A promise that resolves when the upload is complete.
1187+
* @ignore
1188+
*/
1189+
write(x, y, width, height, data) {
1190+
return this.impl.write?.(x, y, width, height, data);
1191+
}
11761192
}
11771193

11781194
export { Texture };

src/platform/graphics/webgl/webgl-graphics-device.js

Lines changed: 46 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,6 +1938,30 @@ class WebglGraphicsDevice extends GraphicsDevice {
19381938
gl.readPixels(x, y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
19391939
}
19401940

1941+
clientWaitAsync(flags, interval_ms) {
1942+
const gl = this.gl;
1943+
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
1944+
this.submit();
1945+
1946+
return new Promise((resolve, reject) => {
1947+
function test() {
1948+
const res = gl.clientWaitSync(sync, flags, 0);
1949+
if (res === gl.TIMEOUT_EXPIRED) {
1950+
// check again in a while
1951+
setTimeout(test, interval_ms);
1952+
} else {
1953+
gl.deleteSync(sync);
1954+
if (res === gl.WAIT_FAILED) {
1955+
reject(new Error('webgl clientWaitSync sync failed'));
1956+
} else {
1957+
resolve();
1958+
}
1959+
}
1960+
}
1961+
test();
1962+
});
1963+
}
1964+
19411965
/**
19421966
* Asynchronously reads a block of pixels from a specified rectangle of the current color framebuffer
19431967
* into an ArrayBufferView object.
@@ -1953,27 +1977,6 @@ class WebglGraphicsDevice extends GraphicsDevice {
19531977
async readPixelsAsync(x, y, w, h, pixels) {
19541978
const gl = this.gl;
19551979

1956-
const clientWaitAsync = (flags, interval_ms) => {
1957-
const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
1958-
this.submit();
1959-
1960-
return new Promise((resolve, reject) => {
1961-
function test() {
1962-
const res = gl.clientWaitSync(sync, flags, 0);
1963-
if (res === gl.WAIT_FAILED) {
1964-
gl.deleteSync(sync);
1965-
reject(new Error('webgl clientWaitSync sync failed'));
1966-
} else if (res === gl.TIMEOUT_EXPIRED) {
1967-
setTimeout(test, interval_ms);
1968-
} else {
1969-
gl.deleteSync(sync);
1970-
resolve();
1971-
}
1972-
}
1973-
test();
1974-
});
1975-
};
1976-
19771980
const impl = this.renderTarget.colorBuffer?.impl;
19781981
const format = impl?._glFormat ?? gl.RGBA;
19791982
const pixelType = impl?._glPixelType ?? gl.UNSIGNED_BYTE;
@@ -1986,7 +1989,7 @@ class WebglGraphicsDevice extends GraphicsDevice {
19861989
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
19871990

19881991
// async wait for previous read to finish
1989-
await clientWaitAsync(0, 20);
1992+
await this.clientWaitAsync(0, 16);
19901993

19911994
// copy the resulting data once it's arrived
19921995
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, buf);
@@ -2027,6 +2030,27 @@ class WebglGraphicsDevice extends GraphicsDevice {
20272030
});
20282031
}
20292032

2033+
async writeTextureAsync(texture, x, y, width, height, data) {
2034+
const gl = this.gl;
2035+
const impl = texture.impl;
2036+
const format = impl?._glFormat ?? gl.RGBA;
2037+
const pixelType = impl?._glPixelType ?? gl.UNSIGNED_BYTE;
2038+
2039+
// create temporary (gpu-side) buffer and copy data into it
2040+
const buf = gl.createBuffer();
2041+
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, buf);
2042+
gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STREAM_DRAW);
2043+
gl.bindTexture(gl.TEXTURE_2D, impl._glTexture);
2044+
gl.texSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, format, pixelType, 0);
2045+
gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null);
2046+
2047+
texture._needsUpload = false;
2048+
texture._mipmapsUploaded = false;
2049+
2050+
// async wait for previous read to finish
2051+
await this.clientWaitAsync(0, 16);
2052+
}
2053+
20302054
/**
20312055
* Enables or disables alpha to coverage.
20322056
*

src/platform/graphics/webgl/webgl-texture.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,14 @@ class WebglTexture {
801801
const device = texture.device;
802802
return device.readTextureAsync(texture, x, y, width, height, options);
803803
}
804+
805+
write(x, y, width, height, data) {
806+
const { texture } = this;
807+
const { device } = texture;
808+
// ensure texture is created and bound
809+
device.setTexture(texture, 0);
810+
return device.writeTextureAsync(texture, x, y, width, height, data);
811+
}
804812
}
805813

806814
export { WebglTexture };

0 commit comments

Comments
 (0)