@driftengine/core 4.1.2 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -456,6 +456,22 @@ export declare class GpuDrivenPass implements PassDefinition {
|
|
|
456
456
|
private oitAccumView;
|
|
457
457
|
private oitRevealView;
|
|
458
458
|
private blendPipeline;
|
|
459
|
+
/**
|
|
460
|
+
* Whether the device has actually accepted the blend pipeline.
|
|
461
|
+
*
|
|
462
|
+
* **False until it says so, and 4.1.2 had this the wrong way round.** That version built the
|
|
463
|
+
* pipeline, bound a group against its layout, encoded the pass, and disowned the handle when the
|
|
464
|
+
* error scope came back — but a scope comes back on a microtask, after all three. The reporter's
|
|
465
|
+
* next run carried the new warning *and* every error it was meant to prevent: `GetBindGroupLayout`
|
|
466
|
+
* on an invalid pipeline, a bind group against an invalid layout, a `SetPipeline` with it, and a
|
|
467
|
+
* refused submit.
|
|
468
|
+
*
|
|
469
|
+
* Nothing touches the handle until this is true. The cost is no transparency for the frames
|
|
470
|
+
* before the device answers, which nobody sees; the alternative is a frame that draws nothing.
|
|
471
|
+
*/
|
|
472
|
+
private blendUsable;
|
|
473
|
+
/** Builds the transparent raster's bindings, held until there is a pipeline worth binding to. */
|
|
474
|
+
private makeBlendGroup;
|
|
459
475
|
private blendResolvePipeline;
|
|
460
476
|
private blendLodGroup;
|
|
461
477
|
private blendCompactGroup;
|
|
@@ -431,6 +431,22 @@ export class GpuDrivenPass {
|
|
|
431
431
|
oitAccumView = null;
|
|
432
432
|
oitRevealView = null;
|
|
433
433
|
blendPipeline = null;
|
|
434
|
+
/**
|
|
435
|
+
* Whether the device has actually accepted the blend pipeline.
|
|
436
|
+
*
|
|
437
|
+
* **False until it says so, and 4.1.2 had this the wrong way round.** That version built the
|
|
438
|
+
* pipeline, bound a group against its layout, encoded the pass, and disowned the handle when the
|
|
439
|
+
* error scope came back — but a scope comes back on a microtask, after all three. The reporter's
|
|
440
|
+
* next run carried the new warning *and* every error it was meant to prevent: `GetBindGroupLayout`
|
|
441
|
+
* on an invalid pipeline, a bind group against an invalid layout, a `SetPipeline` with it, and a
|
|
442
|
+
* refused submit.
|
|
443
|
+
*
|
|
444
|
+
* Nothing touches the handle until this is true. The cost is no transparency for the frames
|
|
445
|
+
* before the device answers, which nobody sees; the alternative is a frame that draws nothing.
|
|
446
|
+
*/
|
|
447
|
+
blendUsable = false;
|
|
448
|
+
/** Builds the transparent raster's bindings, held until there is a pipeline worth binding to. */
|
|
449
|
+
makeBlendGroup = null;
|
|
434
450
|
blendResolvePipeline = null;
|
|
435
451
|
blendLodGroup = null;
|
|
436
452
|
blendCompactGroup = null;
|
|
@@ -953,16 +969,36 @@ export class GpuDrivenPass {
|
|
|
953
969
|
* this came from was five errors that were all the same one.
|
|
954
970
|
*/
|
|
955
971
|
watchBlendPipeline(device) {
|
|
956
|
-
|
|
957
|
-
|
|
972
|
+
/*
|
|
973
|
+
* **A device with no error scopes is taken at its word.** Nothing can be asked of it, and
|
|
974
|
+
* refusing transparency forever on a device that never said no would be a worse failure than
|
|
975
|
+
* the one this guards: a frame that quietly loses its glass on hardware that could draw it.
|
|
976
|
+
*/
|
|
977
|
+
if (typeof device.popErrorScope !== 'function') {
|
|
978
|
+
this.blendUsable = true;
|
|
979
|
+
if (this.makeBlendGroup !== null)
|
|
980
|
+
this.blendRasterGroup = this.makeBlendGroup();
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
let outstanding = 2;
|
|
984
|
+
const settle = (error, scope) => {
|
|
985
|
+
outstanding -= 1;
|
|
986
|
+
if (error !== null && this.blendPipeline !== null) {
|
|
987
|
+
this.blendPipeline = null;
|
|
988
|
+
this.blendRasterGroup = null;
|
|
989
|
+
this.makeBlendGroup = null;
|
|
990
|
+
console.warn(`[driftengine] this device refused the gpu-driven blend pipeline (${scope}), so the ` +
|
|
991
|
+
`transparent pass is off and everything opaque still draws: ${error.message}`);
|
|
992
|
+
}
|
|
993
|
+
/* Usable only once both scopes have come back clean, and only then is anything bound. */
|
|
994
|
+
if (outstanding > 0 || this.blendPipeline === null)
|
|
958
995
|
return;
|
|
959
|
-
this.
|
|
960
|
-
this.
|
|
961
|
-
|
|
962
|
-
`transparent pass is off and everything opaque still draws: ${error.message}`);
|
|
996
|
+
this.blendUsable = true;
|
|
997
|
+
if (this.makeBlendGroup !== null)
|
|
998
|
+
this.blendRasterGroup = this.makeBlendGroup();
|
|
963
999
|
};
|
|
964
|
-
void device.popErrorScope().then((error) =>
|
|
965
|
-
void device.popErrorScope().then((error) =>
|
|
1000
|
+
void device.popErrorScope().then((error) => settle(error, 'internal'));
|
|
1001
|
+
void device.popErrorScope().then((error) => settle(error, 'validation'));
|
|
966
1002
|
}
|
|
967
1003
|
buildPipelines(device, format, samples) {
|
|
968
1004
|
const compute = (label, code) => device.createComputePipeline({
|
|
@@ -1083,8 +1119,11 @@ export class GpuDrivenPass {
|
|
|
1083
1119
|
* backends and as validation on others, and this is exactly the case where guessing which
|
|
1084
1120
|
* costs the whole frame.
|
|
1085
1121
|
*/
|
|
1086
|
-
device.
|
|
1087
|
-
device.pushErrorScope
|
|
1122
|
+
/* Only where the device offers them: see `watchBlendPipeline` for the case where it does not. */
|
|
1123
|
+
if (typeof device.pushErrorScope === 'function') {
|
|
1124
|
+
device.pushErrorScope('validation');
|
|
1125
|
+
device.pushErrorScope('internal');
|
|
1126
|
+
}
|
|
1088
1127
|
this.blendPipeline = device.createRenderPipeline({
|
|
1089
1128
|
label: 'gpu-driven blend',
|
|
1090
1129
|
layout: 'auto',
|
|
@@ -1627,41 +1666,44 @@ export class GpuDrivenPass {
|
|
|
1627
1666
|
});
|
|
1628
1667
|
/* Everything the transparent raster reads: the same scene, the same frame, its own list. */
|
|
1629
1668
|
/*
|
|
1630
|
-
*
|
|
1631
|
-
*
|
|
1632
|
-
*
|
|
1633
|
-
* cascade this guard exists to stop.
|
|
1669
|
+
* Held rather than built, because whether the pipeline is usable is not known yet: the error
|
|
1670
|
+
* scopes settle on a microtask and this runs synchronously. `watchBlendPipeline` runs it once
|
|
1671
|
+
* the device has answered, and never if the answer was no.
|
|
1634
1672
|
*/
|
|
1635
|
-
this.
|
|
1636
|
-
this.blendPipeline
|
|
1637
|
-
|
|
1638
|
-
:
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1673
|
+
this.makeBlendGroup = () => device.createBindGroup({
|
|
1674
|
+
layout: this.blendPipeline.getBindGroupLayout(0),
|
|
1675
|
+
entries: [
|
|
1676
|
+
{ binding: 0, resource: { buffer: this.viewProjBuffer } },
|
|
1677
|
+
{ binding: 1, resource: { buffer: this.blendList } },
|
|
1678
|
+
{ binding: 2, resource: { buffer: this.clusterMeta } },
|
|
1679
|
+
{ binding: 3, resource: { buffer: this.indices } },
|
|
1680
|
+
{ binding: 4, resource: { buffer: this.vertices } },
|
|
1681
|
+
{ binding: 5, resource: { buffer: this.transformBuffer } },
|
|
1682
|
+
{ binding: 6, resource: { buffer: this.materialOf } },
|
|
1683
|
+
{ binding: 7, resource: { buffer: this.materialTable } },
|
|
1684
|
+
{ binding: 8, resource: { buffer: this.frameBuffer } },
|
|
1685
|
+
{
|
|
1686
|
+
binding: 9,
|
|
1687
|
+
resource: this.environmentView ?? this.emptyEnvironmentView,
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
binding: 10,
|
|
1691
|
+
resource: this.environmentSampler ?? this.emptySampler,
|
|
1692
|
+
},
|
|
1693
|
+
{ binding: 11, resource: this.latentsView },
|
|
1694
|
+
{ binding: 12, resource: this.latentClamp },
|
|
1695
|
+
{ binding: 13, resource: this.latentRepeat },
|
|
1696
|
+
{ binding: 14, resource: { buffer: this.decodeNodes } },
|
|
1697
|
+
{ binding: 15, resource: { buffer: this.decodeWeights } },
|
|
1698
|
+
],
|
|
1699
|
+
});
|
|
1700
|
+
/*
|
|
1701
|
+
* And build it now where the device has already answered, which is the ordinary case: the
|
|
1702
|
+
* verdict lands before this runs on a device with no error scopes, and after it on one that
|
|
1703
|
+
* has them. Whichever order, exactly one of the two places builds the group.
|
|
1704
|
+
*/
|
|
1705
|
+
if (this.blendUsable)
|
|
1706
|
+
this.blendRasterGroup = this.makeBlendGroup();
|
|
1665
1707
|
this.blendResolveGroup = device.createBindGroup({
|
|
1666
1708
|
layout: this.blendResolvePipeline.getBindGroupLayout(0),
|
|
1667
1709
|
entries: [
|
|
@@ -2086,7 +2128,7 @@ export class GpuDrivenPass {
|
|
|
2086
2128
|
* Everything opaque has already been drawn by the time this runs, so what is lost is the
|
|
2087
2129
|
* glass and not the city. See `watchBlendPipeline`.
|
|
2088
2130
|
*/
|
|
2089
|
-
if (this.blendPipeline === null || this.blendRasterGroup === null)
|
|
2131
|
+
if (!this.blendUsable || this.blendPipeline === null || this.blendRasterGroup === null)
|
|
2090
2132
|
return;
|
|
2091
2133
|
const pass = encoder.beginRenderPass({
|
|
2092
2134
|
label: 'gpu-driven blend draw',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@driftengine/core",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.3",
|
|
4
4
|
"description": "DriftEngine reusable browser 3D runtime",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@driftengine/drft": "4.1.
|
|
33
|
-
"@driftengine/physics": "4.1.
|
|
32
|
+
"@driftengine/drft": "4.1.3",
|
|
33
|
+
"@driftengine/physics": "4.1.3",
|
|
34
34
|
"gl-matrix": "^3.4.3"
|
|
35
35
|
},
|
|
36
36
|
"author": "Drift Technologies",
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
"prepack": "node ../../scripts/build.mjs @driftengine/core"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@driftengine/nav": "4.1.
|
|
67
|
-
"@driftengine/network": "4.1.
|
|
68
|
-
"@driftengine/texture": "4.1.
|
|
66
|
+
"@driftengine/nav": "4.1.3",
|
|
67
|
+
"@driftengine/network": "4.1.3",
|
|
68
|
+
"@driftengine/texture": "4.1.3"
|
|
69
69
|
}
|
|
70
70
|
}
|
|
@@ -723,6 +723,22 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
723
723
|
private oitAccumView: GPUTextureView | null = null;
|
|
724
724
|
private oitRevealView: GPUTextureView | null = null;
|
|
725
725
|
private blendPipeline: GPURenderPipeline | null = null;
|
|
726
|
+
/**
|
|
727
|
+
* Whether the device has actually accepted the blend pipeline.
|
|
728
|
+
*
|
|
729
|
+
* **False until it says so, and 4.1.2 had this the wrong way round.** That version built the
|
|
730
|
+
* pipeline, bound a group against its layout, encoded the pass, and disowned the handle when the
|
|
731
|
+
* error scope came back — but a scope comes back on a microtask, after all three. The reporter's
|
|
732
|
+
* next run carried the new warning *and* every error it was meant to prevent: `GetBindGroupLayout`
|
|
733
|
+
* on an invalid pipeline, a bind group against an invalid layout, a `SetPipeline` with it, and a
|
|
734
|
+
* refused submit.
|
|
735
|
+
*
|
|
736
|
+
* Nothing touches the handle until this is true. The cost is no transparency for the frames
|
|
737
|
+
* before the device answers, which nobody sees; the alternative is a frame that draws nothing.
|
|
738
|
+
*/
|
|
739
|
+
private blendUsable = false;
|
|
740
|
+
/** Builds the transparent raster's bindings, held until there is a pipeline worth binding to. */
|
|
741
|
+
private makeBlendGroup: (() => GPUBindGroup) | null = null;
|
|
726
742
|
private blendResolvePipeline: GPURenderPipeline | null = null;
|
|
727
743
|
private blendLodGroup: GPUBindGroup | null = null;
|
|
728
744
|
private blendCompactGroup: GPUBindGroup | null = null;
|
|
@@ -1311,17 +1327,36 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
1311
1327
|
* this came from was five errors that were all the same one.
|
|
1312
1328
|
*/
|
|
1313
1329
|
private watchBlendPipeline(device: GPUDevice): void {
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
);
|
|
1330
|
+
/*
|
|
1331
|
+
* **A device with no error scopes is taken at its word.** Nothing can be asked of it, and
|
|
1332
|
+
* refusing transparency forever on a device that never said no would be a worse failure than
|
|
1333
|
+
* the one this guards: a frame that quietly loses its glass on hardware that could draw it.
|
|
1334
|
+
*/
|
|
1335
|
+
if (typeof device.popErrorScope !== 'function') {
|
|
1336
|
+
this.blendUsable = true;
|
|
1337
|
+
if (this.makeBlendGroup !== null) this.blendRasterGroup = this.makeBlendGroup();
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
let outstanding = 2;
|
|
1342
|
+
const settle = (error: GPUError | null, scope: string): void => {
|
|
1343
|
+
outstanding -= 1;
|
|
1344
|
+
if (error !== null && this.blendPipeline !== null) {
|
|
1345
|
+
this.blendPipeline = null;
|
|
1346
|
+
this.blendRasterGroup = null;
|
|
1347
|
+
this.makeBlendGroup = null;
|
|
1348
|
+
console.warn(
|
|
1349
|
+
`[driftengine] this device refused the gpu-driven blend pipeline (${scope}), so the ` +
|
|
1350
|
+
`transparent pass is off and everything opaque still draws: ${error.message}`,
|
|
1351
|
+
);
|
|
1352
|
+
}
|
|
1353
|
+
/* Usable only once both scopes have come back clean, and only then is anything bound. */
|
|
1354
|
+
if (outstanding > 0 || this.blendPipeline === null) return;
|
|
1355
|
+
this.blendUsable = true;
|
|
1356
|
+
if (this.makeBlendGroup !== null) this.blendRasterGroup = this.makeBlendGroup();
|
|
1322
1357
|
};
|
|
1323
|
-
void device.popErrorScope().then((error) =>
|
|
1324
|
-
void device.popErrorScope().then((error) =>
|
|
1358
|
+
void device.popErrorScope().then((error) => settle(error, 'internal'));
|
|
1359
|
+
void device.popErrorScope().then((error) => settle(error, 'validation'));
|
|
1325
1360
|
}
|
|
1326
1361
|
|
|
1327
1362
|
private buildPipelines(device: GPUDevice, format: GPUTextureFormat, samples: number): void {
|
|
@@ -1449,8 +1484,11 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
1449
1484
|
* backends and as validation on others, and this is exactly the case where guessing which
|
|
1450
1485
|
* costs the whole frame.
|
|
1451
1486
|
*/
|
|
1452
|
-
device.
|
|
1453
|
-
device.pushErrorScope
|
|
1487
|
+
/* Only where the device offers them: see `watchBlendPipeline` for the case where it does not. */
|
|
1488
|
+
if (typeof device.pushErrorScope === 'function') {
|
|
1489
|
+
device.pushErrorScope('validation');
|
|
1490
|
+
device.pushErrorScope('internal');
|
|
1491
|
+
}
|
|
1454
1492
|
this.blendPipeline = device.createRenderPipeline({
|
|
1455
1493
|
label: 'gpu-driven blend',
|
|
1456
1494
|
layout: 'auto',
|
|
@@ -2059,41 +2097,45 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
2059
2097
|
|
|
2060
2098
|
/* Everything the transparent raster reads: the same scene, the same frame, its own list. */
|
|
2061
2099
|
/*
|
|
2062
|
-
*
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
2065
|
-
* cascade this guard exists to stop.
|
|
2100
|
+
* Held rather than built, because whether the pipeline is usable is not known yet: the error
|
|
2101
|
+
* scopes settle on a microtask and this runs synchronously. `watchBlendPipeline` runs it once
|
|
2102
|
+
* the device has answered, and never if the answer was no.
|
|
2066
2103
|
*/
|
|
2067
|
-
this.
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
:
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2104
|
+
this.makeBlendGroup = (): GPUBindGroup =>
|
|
2105
|
+
device.createBindGroup({
|
|
2106
|
+
layout: (this.blendPipeline as GPURenderPipeline).getBindGroupLayout(0),
|
|
2107
|
+
entries: [
|
|
2108
|
+
{ binding: 0, resource: { buffer: this.viewProjBuffer as GPUBuffer } },
|
|
2109
|
+
{ binding: 1, resource: { buffer: this.blendList as GPUBuffer } },
|
|
2110
|
+
{ binding: 2, resource: { buffer: this.clusterMeta as GPUBuffer } },
|
|
2111
|
+
{ binding: 3, resource: { buffer: this.indices as GPUBuffer } },
|
|
2112
|
+
{ binding: 4, resource: { buffer: this.vertices as GPUBuffer } },
|
|
2113
|
+
{ binding: 5, resource: { buffer: this.transformBuffer as GPUBuffer } },
|
|
2114
|
+
{ binding: 6, resource: { buffer: this.materialOf as GPUBuffer } },
|
|
2115
|
+
{ binding: 7, resource: { buffer: this.materialTable as GPUBuffer } },
|
|
2116
|
+
{ binding: 8, resource: { buffer: this.frameBuffer as GPUBuffer } },
|
|
2117
|
+
{
|
|
2118
|
+
binding: 9,
|
|
2119
|
+
resource: this.environmentView ?? (this.emptyEnvironmentView as GPUTextureView),
|
|
2120
|
+
},
|
|
2121
|
+
{
|
|
2122
|
+
binding: 10,
|
|
2123
|
+
resource: this.environmentSampler ?? (this.emptySampler as GPUSampler),
|
|
2124
|
+
},
|
|
2125
|
+
{ binding: 11, resource: this.latentsView as GPUTextureView },
|
|
2126
|
+
{ binding: 12, resource: this.latentClamp as GPUSampler },
|
|
2127
|
+
{ binding: 13, resource: this.latentRepeat as GPUSampler },
|
|
2128
|
+
{ binding: 14, resource: { buffer: this.decodeNodes as GPUBuffer } },
|
|
2129
|
+
{ binding: 15, resource: { buffer: this.decodeWeights as GPUBuffer } },
|
|
2130
|
+
],
|
|
2131
|
+
});
|
|
2132
|
+
|
|
2133
|
+
/*
|
|
2134
|
+
* And build it now where the device has already answered, which is the ordinary case: the
|
|
2135
|
+
* verdict lands before this runs on a device with no error scopes, and after it on one that
|
|
2136
|
+
* has them. Whichever order, exactly one of the two places builds the group.
|
|
2137
|
+
*/
|
|
2138
|
+
if (this.blendUsable) this.blendRasterGroup = this.makeBlendGroup();
|
|
2097
2139
|
this.blendResolveGroup = device.createBindGroup({
|
|
2098
2140
|
layout: (this.blendResolvePipeline as GPURenderPipeline).getBindGroupLayout(0),
|
|
2099
2141
|
entries: [
|
|
@@ -2579,7 +2621,8 @@ export class GpuDrivenPass implements PassDefinition {
|
|
|
2579
2621
|
* Everything opaque has already been drawn by the time this runs, so what is lost is the
|
|
2580
2622
|
* glass and not the city. See `watchBlendPipeline`.
|
|
2581
2623
|
*/
|
|
2582
|
-
if (this.blendPipeline === null || this.blendRasterGroup === null)
|
|
2624
|
+
if (!this.blendUsable || this.blendPipeline === null || this.blendRasterGroup === null)
|
|
2625
|
+
return;
|
|
2583
2626
|
const pass = encoder.beginRenderPass({
|
|
2584
2627
|
label: 'gpu-driven blend draw',
|
|
2585
2628
|
colorAttachments: [
|