@@ -21,6 +21,7 @@ import {
21
21
kTextureUsageCopyInfo ,
22
22
kShaderStageKeys ,
23
23
} from '../../../../capability_info.js' ;
24
+ import { GPUConst } from '../../../../constants.js' ;
24
25
import {
25
26
getBlockInfoForColorTextureFormat ,
26
27
kCompressedTextureFormats ,
@@ -52,16 +53,15 @@ class DeviceDestroyTests extends AllFeaturesMaxLimitsValidationTest {
52
53
* `fn` after the device is destroyed without any specific expectation. If `awaitLost` is true, we
53
54
* also wait for device.lost to resolve before executing `fn` in the destroy case.
54
55
*/
55
- async executeAfterDestroy ( fn : ( ) => void , awaitLost : boolean ) : Promise < void > {
56
+ async executeAfterDestroy ( fn : ( ) => void | Promise < void > , awaitLost : boolean ) : Promise < void > {
56
57
this . expectDeviceLost ( 'destroyed' ) ;
57
58
58
- this . expectValidationError ( fn , false ) ;
59
59
this . device . destroy ( ) ;
60
60
if ( awaitLost ) {
61
61
const lostInfo = await this . device . lost ;
62
62
this . expect ( lostInfo . reason === 'destroyed' ) ;
63
63
}
64
- fn ( ) ;
64
+ await fn ( ) ;
65
65
}
66
66
67
67
/**
@@ -146,6 +146,109 @@ Tests creating buffers on destroyed device. Tests valid combinations of:
146
146
} , awaitLost ) ;
147
147
} ) ;
148
148
149
+ g . test ( 'mapping,mappedAtCreation' )
150
+ . desc (
151
+ `
152
+ Tests behavior of mappedAtCreation buffers when destroying the device (multiple times).
153
+ - Various usages
154
+ - Wait for .lost or not
155
+ `
156
+ )
157
+ . params ( u =>
158
+ u
159
+ . combine ( 'usage' , [
160
+ GPUConst . BufferUsage . MAP_READ ,
161
+ GPUConst . BufferUsage . MAP_WRITE ,
162
+ GPUConst . BufferUsage . COPY_SRC ,
163
+ ] )
164
+ . combine ( 'awaitLost' , [ true , false ] )
165
+ )
166
+ . fn ( async t => {
167
+ const { awaitLost, usage } = t . params ;
168
+
169
+ const b1 = t . createBufferTracked ( { size : 16 , usage, mappedAtCreation : true } ) ;
170
+ t . expect ( b1 . mapState === 'mapped' , 'b1 before destroy 1' ) ;
171
+
172
+ await t . executeAfterDestroy ( ( ) => {
173
+ // Destroy should have unmapped everything.
174
+ t . expect ( b1 . mapState === 'unmapped' , 'b1 after destroy 1' ) ;
175
+ // But unmap just in case, to reset state before continuing.
176
+ b1 . unmap ( ) ;
177
+
178
+ // mappedAtCreation should still work.
179
+ const b2 = t . createBufferTracked ( { size : 16 , usage, mappedAtCreation : true } ) ;
180
+ t . expect ( b2 . mapState === 'mapped' , 'b2 at creation after destroy 1' ) ;
181
+
182
+ // Destroying again should unmap.
183
+ t . device . destroy ( ) ;
184
+ t . expect ( b2 . mapState === 'unmapped' , 'b2 after destroy 2' ) ;
185
+ } , awaitLost ) ;
186
+ } ) ;
187
+
188
+ g . test ( 'mapping,mapAsync' )
189
+ . desc (
190
+ `
191
+ Tests behavior of mapAsync'd buffers when destroying the device.
192
+ - Various usages
193
+ - Wait for .lost or not
194
+
195
+ TODO(https://github.com/gpuweb/gpuweb/issues/5101): Test which error we got at [1].
196
+ `
197
+ )
198
+ . params ( u =>
199
+ u
200
+ . combine ( 'usage' , [
201
+ GPUConst . BufferUsage . MAP_READ ,
202
+ GPUConst . BufferUsage . MAP_WRITE ,
203
+ GPUConst . BufferUsage . COPY_SRC ,
204
+ ] )
205
+ . combine ( 'awaitLost' , [ true , false ] )
206
+ )
207
+ . fn ( async t => {
208
+ const { awaitLost, usage } = t . params ;
209
+
210
+ const b = t . createBufferTracked ( { size : 16 , usage } ) ;
211
+ const mode =
212
+ usage === GPUBufferUsage . MAP_READ
213
+ ? GPUMapMode . READ
214
+ : usage === GPUBufferUsage . MAP_WRITE
215
+ ? GPUMapMode . WRITE
216
+ : 0 ;
217
+ if ( mode ) {
218
+ await b . mapAsync ( mode ) ;
219
+ t . expect ( b . mapState === 'mapped' , 'bAsync before destroy 1' ) ;
220
+ } else {
221
+ t . expect ( b . mapState === 'unmapped' , 'bAsync before destroy 1' ) ;
222
+ }
223
+
224
+ await t . executeAfterDestroy ( async ( ) => {
225
+ // Destroy should have unmapped everything.
226
+ t . expect ( b . mapState === 'unmapped' , 'bAsync after destroy 1' ) ;
227
+ // But unmap just in case, to reset state before continuing.
228
+ b . unmap ( ) ;
229
+
230
+ // Check that mapping after destroy fails.
231
+ // Also check that if mapAsync fails validation, it still produces an OperationError.
232
+ try {
233
+ await b . mapAsync ( mode ) ;
234
+ t . expect ( false , 'bAsync mapAsync after destroy 1 should reject' ) ;
235
+ } catch ( ex ) {
236
+ if ( mode ) {
237
+ // The mapAsync call is valid except for the fact that the device is lost.
238
+ t . expect ( ex instanceof DOMException && ex . name === 'AbortError' ) ;
239
+ } else {
240
+ // The mapAsync call is also invalid for other reasons.
241
+ // [1] Test which error type we got. (And maybe switch to shouldReject().)
242
+ t . expect ( ex instanceof DOMException ) ;
243
+ }
244
+ }
245
+ const mapPromise = b . mapAsync ( mode ) ;
246
+ t . shouldReject ( 'AbortError' , mapPromise ) ;
247
+ await mapPromise . catch ( ( ) => { } ) ;
248
+ t . expect ( b . mapState === 'unmapped' , 'bAsync after mapAsync after destroy 1' ) ;
249
+ } , awaitLost ) ;
250
+ } ) ;
251
+
149
252
g . test ( 'createTexture,2d,uncompressed_format' )
150
253
. desc (
151
254
`
0 commit comments