1
1
import macro from 'vtk.js/Sources/macros' ;
2
2
import HalfFloat from 'vtk.js/Sources/Common/Core/HalfFloat' ;
3
- import vtkWebGPUBufferManager from 'vtk.js/Sources/Rendering/WebGPU/BufferManager' ;
4
3
import vtkWebGPUTextureView from 'vtk.js/Sources/Rendering/WebGPU/TextureView' ;
5
4
import vtkWebGPUTypes from 'vtk.js/Sources/Rendering/WebGPU/Types' ;
6
5
import vtkTexture from 'vtk.js/Sources/Rendering/Core/Texture' ;
7
6
8
- const { BufferUsage } = vtkWebGPUBufferManager ;
9
-
10
7
// ----------------------------------------------------------------------------
11
8
// Global methods
12
9
// ----------------------------------------------------------------------------
@@ -62,14 +59,19 @@ function vtkWebGPUTexture(publicAPI, model) {
62
59
63
60
publicAPI . writeImageData = ( req ) => {
64
61
let nativeArray = [ ] ;
65
- if ( req . canvas ) {
62
+ const _copyImageToTexture = ( source ) => {
66
63
model . device . getHandle ( ) . queue . copyExternalImageToTexture (
67
64
{
68
- source : req . canvas ,
65
+ source,
69
66
flipY : req . flip ,
70
67
} ,
71
- { texture : model . handle , premultipliedAlpha : true } ,
72
- [ model . width , model . height , model . depth ]
68
+ {
69
+ texture : model . handle ,
70
+ premultipliedAlpha : true ,
71
+ mipLevel : 0 ,
72
+ origin : { x : 0 , y : 0 , z : 0 } ,
73
+ } ,
74
+ [ source . width , source . height , model . depth ]
73
75
) ;
74
76
75
77
// Generate mipmaps on GPU if needed
@@ -82,6 +84,20 @@ function vtkWebGPUTexture(publicAPI, model) {
82
84
}
83
85
84
86
model . ready = true ;
87
+ } ;
88
+
89
+ if ( req . canvas ) {
90
+ _copyImageToTexture ( req . canvas ) ;
91
+ return ;
92
+ }
93
+
94
+ if ( req . imageBitmap ) {
95
+ req . width = req . imageBitmap . width ;
96
+ req . height = req . imageBitmap . height ;
97
+ req . depth = 1 ;
98
+ req . format = 'rgba8unorm' ;
99
+ req . flip = false ;
100
+ _copyImageToTexture ( req . imageBitmap ) ;
85
101
return ;
86
102
}
87
103
@@ -97,6 +113,15 @@ function vtkWebGPUTexture(publicAPI, model) {
97
113
const tDetails = vtkWebGPUTypes . getDetailsFromTextureFormat ( model . format ) ;
98
114
let bufferBytesPerRow = model . width * tDetails . stride ;
99
115
116
+ /**
117
+ * Align texture data to ensure bytesPerRow is a multiple of 256.
118
+ * This is necessary for WebGPU texture uploads, especially for half-float formats.
119
+ * It also handles half-float conversion if the texture format requires it.
120
+ * @param {* } arr - The input array containing texture data.
121
+ * @param {* } height - The height of the texture.
122
+ * @param {* } depth - The depth of the texture (1 for 2D textures).
123
+ * @returns
124
+ */
100
125
const alignTextureData = ( arr , height , depth ) => {
101
126
// bytesPerRow must be a multiple of 256 so we might need to rebuild
102
127
// the data here before passing to the buffer. e.g. if it is unorm8x4 then
@@ -156,9 +181,7 @@ function vtkWebGPUTexture(publicAPI, model) {
156
181
}
157
182
158
183
if ( req . image ) {
159
- const canvas = document . createElement ( 'canvas' ) ;
160
- canvas . width = req . image . width ;
161
- canvas . height = req . image . height ;
184
+ const canvas = new OffscreenCanvas ( req . image . width , req . image . height ) ;
162
185
const ctx = canvas . getContext ( '2d' ) ;
163
186
ctx . translate ( 0 , canvas . height ) ;
164
187
ctx . scale ( 1 , - 1 ) ;
@@ -183,68 +206,42 @@ function vtkWebGPUTexture(publicAPI, model) {
183
206
nativeArray = imageData . data ;
184
207
}
185
208
186
- const cmdEnc = model . device . createCommandEncoder ( ) ;
187
-
188
- if ( publicAPI . getDimensionality ( ) !== 3 ) {
189
- // Non-3D
190
- // First, upload the base mip level (level 0)
191
- const ret = alignTextureData ( nativeArray , model . height , 1 ) ;
192
- bufferBytesPerRow = ret [ 1 ] ;
193
- const buffRequest = {
194
- dataArray : req . dataArray ? req . dataArray : null ,
195
- nativeArray : ret [ 0 ] ,
196
- usage : BufferUsage . Texture ,
197
- } ;
198
- const buff = model . device . getBufferManager ( ) . getBuffer ( buffRequest ) ;
199
- cmdEnc . copyBufferToTexture (
200
- {
201
- buffer : buff . getHandle ( ) ,
202
- offset : 0 ,
203
- bytesPerRow : bufferBytesPerRow ,
204
- rowsPerImage : model . height ,
205
- } ,
206
- {
207
- texture : model . handle ,
208
- mipLevel : 0 ,
209
- } ,
210
- [ model . width , model . height , 1 ]
211
- ) ;
212
-
213
- // Submit the base level upload
214
- model . device . submitCommandEncoder ( cmdEnc ) ;
215
-
216
- // Generate remaining mip levels on GPU
217
- if ( model . mipLevel > 0 ) {
218
- vtkTexture . generateMipmaps (
219
- model . device . getHandle ( ) ,
220
- model . handle ,
221
- model . mipLevel + 1
222
- ) ;
209
+ const is3D = publicAPI . getDimensionality ( ) === 3 ;
210
+ const alignedTextureData = alignTextureData (
211
+ nativeArray ,
212
+ model . height ,
213
+ is3D ? model . depth : 1
214
+ ) ;
215
+ bufferBytesPerRow = alignedTextureData [ 1 ] ;
216
+ const data = alignedTextureData [ 0 ] ;
217
+
218
+ model . device . getHandle ( ) . queue . writeTexture (
219
+ {
220
+ texture : model . handle ,
221
+ mipLevel : 0 ,
222
+ origin : { x : 0 , y : 0 , z : 0 } ,
223
+ } ,
224
+ data ,
225
+ {
226
+ offset : 0 ,
227
+ bytesPerRow : bufferBytesPerRow ,
228
+ rowsPerImage : model . height ,
229
+ } ,
230
+ {
231
+ width : model . width ,
232
+ height : model . height ,
233
+ depthOrArrayLayers : is3D ? model . depth : 1 ,
223
234
}
224
- model . ready = true ;
225
- } else {
226
- // 3D, no mipmaps
227
- const ret = alignTextureData ( nativeArray , model . height , model . depth ) ;
228
- bufferBytesPerRow = ret [ 1 ] ;
229
- const buffRequest = {
230
- dataArray : req . dataArray ? req . dataArray : null ,
231
- usage : BufferUsage . Texture ,
232
- } ;
233
- buffRequest . nativeArray = ret [ 0 ] ;
234
- const buff = model . device . getBufferManager ( ) . getBuffer ( buffRequest ) ;
235
- cmdEnc . copyBufferToTexture (
236
- {
237
- buffer : buff . getHandle ( ) ,
238
- offset : 0 ,
239
- bytesPerRow : bufferBytesPerRow ,
240
- rowsPerImage : model . height ,
241
- } ,
242
- { texture : model . handle } ,
243
- [ model . width , model . height , model . depth ]
235
+ ) ;
236
+
237
+ if ( ! is3D && model . mipLevel > 0 ) {
238
+ vtkTexture . generateMipmaps (
239
+ model . device . getHandle ( ) ,
240
+ model . handle ,
241
+ model . mipLevel + 1
244
242
) ;
245
- model . device . submitCommandEncoder ( cmdEnc ) ;
246
- model . ready = true ;
247
243
}
244
+ model . ready = true ;
248
245
} ;
249
246
250
247
// when data is pulled out of this texture what scale must be applied to
0 commit comments