@hology/core 0.0.266 → 0.0.267
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.
- package/dist/effects/sequence/sequence-player.js +1 -1
- package/dist/effects/vfx/gpu-sprite-runtime.d.ts +13 -0
- package/dist/effects/vfx/gpu-sprite-runtime.js +1 -1
- package/dist/effects/vfx/gpu-sprite-world-runtime.js +1 -1
- package/dist/gameplay/initiate.js +1 -1
- package/dist/rendering/node-material-webgpu-resolver.js +1 -1
- package/dist/rendering/webgpu-experimental-backend.js +1 -1
- package/dist/rendering/webgpu-runtime-bridge.d.ts +3 -0
- package/dist/rendering/webgpu-runtime-bridge.js +1 -1
- package/dist/scene/materializer.js +1 -1
- package/dist/test/gpu-sprite-pipeline-cache.test.js +1 -1
- package/dist/test/sequence-post-process.test.js +1 -1
- package/package.json +5 -3
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/test/renderer-loading.test.d.ts +0 -2
- package/dist/test/renderer-loading.test.js +0 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{afterEach as e,describe as t,expect as
|
|
1
|
+
import{afterEach as e,describe as t,expect as r,it as n,vi as a}from"vitest";import{Color as i,Texture as o,Vector3 as l}from"three";import{compileGpuSpriteEmitters as p}from"../effects/vfx/gpu-sprite-compiler.js";import{GpuSpriteRuntime as s}from"../effects/vfx/gpu-sprite-runtime.js";e(()=>a.unstubAllGlobals()),t("GPU sprite pipeline sharing",()=>{n("shares pipelines across classes and worlds while keeping their bindings independent",()=>{a.stubGlobal("GPUBufferUsage",{STORAGE:1,COPY_DST:2,UNIFORM:4,INDIRECT:8,COPY_SRC:16,MAP_READ:32}),a.stubGlobal("GPUShaderStage",{COMPUTE:1,VERTEX:2,FRAGMENT:4});const e=()=>({createBuffer:()=>({destroy:a.fn()}),createShaderModule:a.fn(()=>({})),createBindGroupLayout:a.fn(()=>({})),createPipelineLayout:()=>({}),createComputePipeline:()=>({}),createRenderPipeline:a.fn(e=>({...e})),createBindGroup:a.fn(e=>({...e})),queue:{writeBuffer:a.fn()}}),t=new o,n=new o,i=new Map([t,n].map(e=>[e,{sampler:{},view:{}}])),c=p({id:"sprite",name:"sprite",type:"vfx",vfx:{emitters:[{id:"emitter",name:"emitter",position:[0,0,0],rotation:[0,0,0],rate:{type:"once",count:1,delay:0},initializers:[],behaviours:[],output:{type:"sprite",texture:"dot",color:"#fff",intensity:1,blendingMode:"additive",space:"world"}}]}},[new l]).emitters,d=e(),u={device:d,colorFormat:"rgba16float",depthView:{},getExternalTexture:e=>i.get(e)},f=[],m=(e=u)=>{const r=new s(c,[{texture:t},{texture:n},{texture:t,localSpace:!0}]).createResource(e);return f.push(r),r.renderResources};try{const a=m();r(d.createRenderPipeline).toHaveBeenCalledTimes(2),r(a[0].pipeline).toBe(a[1].pipeline),r(a[2].pipeline).not.toBe(a[0].pipeline),r(a[0].renderClassBuffer).not.toBe(a[1].renderClassBuffer);const o=a[0].getBindGroup(u),l=a[1].getBindGroup(u);r(o).not.toBe(l),r(o.layout).toBe(l.layout),r(o.entries.some(e=>e.resource===i.get(t).view)).toBe(!0),r(l.entries.some(e=>e.resource===i.get(n).view)).toBe(!0);const p=m();r(d.createRenderPipeline).toHaveBeenCalledTimes(2),r(p[0].pipeline).toBe(a[0].pipeline),r(p[0].renderClassBuffer).not.toBe(a[0].renderClassBuffer);const s=m({...u,colorFormat:"rgba8unorm"});r(d.createRenderPipeline).toHaveBeenCalledTimes(4),r(s[0].pipeline).not.toBe(a[0].pipeline),r(s[0].pipeline.vertex.module).toBe(a[0].pipeline.vertex.module),m({...u,depthView:null}),r(d.createRenderPipeline).toHaveBeenCalledTimes(6);const c=e(),f=m({...u,device:c});r(c.createRenderPipeline).toHaveBeenCalledTimes(2),r(f[0].pipeline).not.toBe(a[0].pipeline)}finally{for(const e of f)e.dispose()}})}),t("GPU sprite async compute preparation",()=>{n("starts all compute pipelines together, waits for readiness, and reuses them on first render",async()=>{a.stubGlobal("GPUBufferUsage",{STORAGE:1,COPY_DST:2,UNIFORM:4,INDIRECT:8,COPY_SRC:16,MAP_READ:32}),a.stubGlobal("GPUShaderStage",{COMPUTE:1,VERTEX:2,FRAGMENT:4});const e=[],t={createBuffer:a.fn(()=>({destroy:a.fn()})),createShaderModule:a.fn(()=>({})),createBindGroupLayout:a.fn(()=>({})),createPipelineLayout:a.fn(()=>({})),createRenderPipeline:a.fn(()=>({})),createComputePipeline:a.fn(()=>({})),createComputePipelineAsync:a.fn(()=>new Promise(t=>e.push(t))),createBindGroup:a.fn(e=>({...e})),queue:{writeBuffer:a.fn()}},n=new s([],[{texture:new o}]),i=n.prepareComputePipelines(t);r(n.prepareComputePipelines(t)).toBe(i),await a.waitFor(()=>r(t.createComputePipelineAsync).toHaveBeenCalledTimes(3)),r(t.createComputePipeline).not.toHaveBeenCalled(),r(t.createBuffer).not.toHaveBeenCalled();let l=!1;i.then(()=>{l=!0});const p=[{},{},{}];e[0](p[0]),e[1](p[1]),await Promise.resolve(),r(l).toBe(!1),e[2](p[2]),await i;const c=n.createResource({device:t,colorFormat:"rgba16float",depthView:null,getExternalTexture:()=>({sampler:{},view:{}})});try{r(c.clearPipeline).toBe(p[0]),r(c.applyCommandsPipeline).toBe(p[1]),r(c.simulatePipeline).toBe(p[2]),r(t.createComputePipeline).not.toHaveBeenCalled(),await n.prepareComputePipelines(t),r(t.createComputePipelineAsync).toHaveBeenCalledTimes(3)}finally{c.dispose()}}),n("propagates compilation failure and allows a retry",async()=>{a.stubGlobal("GPUShaderStage",{COMPUTE:1,VERTEX:2,FRAGMENT:4});const e={createShaderModule:()=>({}),createBindGroupLayout:()=>({}),createPipelineLayout:()=>({}),createComputePipelineAsync:a.fn().mockRejectedValue(new Error("compile failed"))},t=new s([],[{texture:new o}]);await r(t.prepareComputePipelines(e)).rejects.toThrow("compile failed"),e.createComputePipelineAsync.mockResolvedValue({}),await t.prepareComputePipelines(e),r(e.createComputePipelineAsync).toHaveBeenCalledTimes(6)})}),t("GPU sprite async render preparation",()=>{n("prepares sprite, mesh and trail pipelines before resource creation and shares pending variants",async()=>{a.stubGlobal("GPUBufferUsage",{STORAGE:1,COPY_DST:2,UNIFORM:4,INDIRECT:8,COPY_SRC:16,MAP_READ:32}),a.stubGlobal("GPUShaderStage",{COMPUTE:1,VERTEX:2,FRAGMENT:4});const e=new o,t=[{texture:e},{texture:e,localSpace:!0},{texture:e,meshVertices:new Float32Array(24)},{texture:e,trail:{historyLength:4,width:1,taper:!1,colorStart:new i,colorEnd:new i,opacityStart:1,opacityEnd:0,scrollSpeed:0,uvMode:"stretch",unitsPerRepeat:1,opacityChannel:"alpha",blendingMode:"additive",billboard:!0}}],n=[],l={createBuffer:a.fn(()=>({destroy:a.fn()})),createShaderModule:a.fn(()=>({})),createBindGroupLayout:a.fn(()=>({})),createPipelineLayout:a.fn(()=>({})),createComputePipeline:a.fn(()=>({})),createRenderPipeline:a.fn(()=>({})),createRenderPipelineAsync:a.fn(e=>new Promise(t=>n.push(()=>t({...e})))),createBindGroup:a.fn(e=>({...e})),queue:{writeBuffer:a.fn()}},p=new s([],t),c=new s([],t);let d=!1;const u=Promise.all([p.prepareRenderPipelines(l,"rgba16float"),c.prepareRenderPipelines(l,"rgba16float")]).then(()=>{d=!0});await a.waitFor(()=>r(l.createRenderPipelineAsync).toHaveBeenCalledTimes(4)),r(d).toBe(!1),r(l.createBuffer).not.toHaveBeenCalled();for(const e of n)e();await u;const f={device:l,colorFormat:"rgba16float",depthView:{},getExternalTexture:()=>({sampler:{},view:{}})},m=[p,c].map(e=>e.createResource(f));try{r(l.createRenderPipeline).not.toHaveBeenCalled();for(let e=0;e<t.length;e++)r(m[0].renderResources[e].pipeline).toBe(m[1].renderResources[e].pipeline);r(m[0].renderResources[2].pipeline.vertex.buffers).toHaveLength(1),r(m[0].renderResources[0].pipeline.depthStencil.format).toBe("depth32float")}finally{m.forEach(e=>e.dispose())}}),n("removes failed pending variants so preparation can retry",async()=>{a.stubGlobal("GPUShaderStage",{COMPUTE:1,VERTEX:2,FRAGMENT:4});const e={createShaderModule:()=>({}),createBindGroupLayout:()=>({}),createPipelineLayout:()=>({}),createRenderPipelineAsync:a.fn().mockRejectedValue(new Error("render compile failed"))},t=new s([],[{texture:new o}]);await r(t.prepareRenderPipelines(e,"rgba16float")).rejects.toThrow("render compile failed"),e.createRenderPipelineAsync.mockResolvedValue({}),await t.prepareRenderPipelines(e,"rgba16float"),r(e.createRenderPipelineAsync).toHaveBeenCalledTimes(2)})});/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{describe as e,expect as s,it as a,vi as t}from"vitest";import*as
|
|
1
|
+
import{describe as e,expect as s,it as a,vi as t}from"vitest";import*as o from"three";import{createMaterialSubTrack as r,createPostProcessClipInstance as n,createPostProcessTrack as i,createPropertyKeyframe as l,createSequenceData as c}from"../effects/sequence/sequence-data.js";import{createDefaultPostProcessShaderGraphDocument as d,shaderGraphPostProcessWeightUniformName as p}from"../shader/graph/index.js";import{shaderParameterUniformName as f}from"../shader/parameter.js";import{UniformFloatNode as m}from"../shader-nodes/index.js";import{SerializedParamType as u}from"../scene/model.js";async function h(e,s){const{SequencePlayer:a}=await import("../effects/sequence/sequence-player.js"),t=new a;return t.setWorld({scene:new o.Scene}),t.setViewController(e),t.setAssetLoader(s),t}function y(e,s,a){const t=c();t.duration=Math.max(2,s+a+.25);const o=i(),r=n(s,a);return r.shaderGraphAssetId=e,o.clips.push(r),t.tracks.push(o),{sequence:t,track:o,clip:r}}function v(e,s={}){return{clearCacheById:t.fn(),getShaderGraphByAssetId:t.fn().mockResolvedValue(e),prepareShaderGraphParameters:t.fn().mockResolvedValue(s)}}function C(){const e=[],s=t.fn((s,a={})=>{const o={material:s,enabled:a.enabled??!0,priority:a.priority??0,stage:a.stage??"beforeOutput",dispose:t.fn()};return e.push(o),o});return{handles:e,addPostProcessEffect:s,clearCameraShakeContributionsForOwner:t.fn(),releaseCameraOverridesForOwner:t.fn(),pushCameraOverride:t.fn(),setCameraShakeContribution:t.fn()}}function w(e){return e.material.uniforms[p].value}async function g(e=6){for(let s=0;s<e;s++)await Promise.resolve()}t.mock("../gameplay/actors/builtin/index.js",()=>({builtInActors:{},default:{}})),t.mock("../gameplay/actors/index.js",()=>({CharacterAnimationComponent:class{},CharacterMovementComponent:class{}})),t.mock("../effects/vfx/vfx-actor.js",()=>({VfxActor:class{}})),t.mock("../effects/vfx/vfx-param.js",()=>({VisualEffect:class{}})),t.mock("../effects/vfx/vfx-service.js",()=>({VfxService:class{}})),t.hoisted(()=>{if("undefined"==typeof HTMLCanvasElement)return;const e=new Proxy({},{get:(e,s)=>(s in e||(e[s]=("string"!=typeof s||!s.startsWith("is"))&&t.fn()),e[s]),set:(e,s,a)=>(e[s]=a,!0)});Object.defineProperty(HTMLCanvasElement.prototype,"getContext",{configurable:!0,value:()=>e})}),e("sequence post-process tracks",()=>{a("registers active post-process clips with the view controller",async()=>{const e=d(),a=C(),t=await h(a,v(e)),{clip:o,sequence:r}=y("graph-asset",0,1);t.load(r),t.play(),await g(),s(a.addPostProcessEffect).toHaveBeenCalledTimes(1),s(a.addPostProcessEffect).toHaveBeenCalledWith(s.anything(),s.objectContaining({stage:"beforeOutput",priority:0,enabled:!0})),s(a.handles[0].material.uniforms[p]).toBeTruthy(),s(o.shaderGraphAssetId).toBe("graph-asset")}),a("updates post-process blend weight from clip fades",async()=>{const e=d(),a=C(),t=await h(a,v(e)),{clip:o,sequence:r}=y("graph-asset",0,2);o.fadeInDuration=.5,o.fadeOutDuration=.5,t.load(r),t.play(),await g(),t.seek(.25),s(w(a.handles[0])).toBeCloseTo(.5),t.seek(1),s(w(a.handles[0])).toBeCloseTo(1),t.seek(1.75),s(w(a.handles[0])).toBeCloseTo(.5)}),a("animates shader graph parameters on active post-process clips",async()=>{const e=(a="intensity",{...d(),parameters:[{id:a,name:a,type:"float",defaultValue:0}],nodes:[{id:a,kind:"input.parameter",params:{parameter:a}}],edges:[],outputs:{color:{input:{nodeId:a,port:"value"}}}});var a;const t=f("intensity"),o=C(),n=await h(o,v(e,{intensity:new m(t,0,void 0,!1)})),{sequence:i,track:c}=y("graph-asset",0,1),p=r({materialMatchMode:"parameterName",materialAssetId:null,parameterName:"intensity",uniformName:t,uniformType:u.FloatNode,valueType:u.Number});p.keyframes.push(l(0,{type:u.Number,value:0}),l(1,{type:u.Number,value:1})),c.subTracks.push(p),n.load(i),n.play(),await g(),n.seek(.5),s(o.handles[0].material.uniforms[t].value).toBeCloseTo(.5)}),a("reuses post-process handles across clip exits and stops, then disposes them with the player",async()=>{const e=d(),a=C(),o=await h(a,v(e)),{sequence:r}=y("graph-asset",0,1);o.load(r),o.play(),await g();const n=a.handles[0];o.seek(1.1),s(n.enabled).toBe(!1),s(n.dispose).not.toHaveBeenCalled(),o.seek(.1),await g(),s(a.addPostProcessEffect).toHaveBeenCalledTimes(1),s(n.enabled).toBe(!0),o.stop(),s(n.enabled).toBe(!1),s(n.dispose).not.toHaveBeenCalled(),o.restart(),o.play(),await g(),s(a.addPostProcessEffect).toHaveBeenCalledTimes(1),s(n.enabled).toBe(!0);const i=t.spyOn(n.material,"dispose");o.dispose(),s(n.dispose).toHaveBeenCalledTimes(1),s(i).toHaveBeenCalledTimes(1)}),a("releases retained effects when a sequence is replaced",async()=>{const e=C(),a=await h(e,v(d())),{sequence:t}=y("graph-asset",0,1);a.load(t),a.play(),await g();const o=e.handles[0];a.stop(),s(o.dispose).not.toHaveBeenCalled(),a.load(t),s(o.dispose).toHaveBeenCalledTimes(1),a.play(),await g(),s(e.addPostProcessEffect).toHaveBeenCalledTimes(2)}),a("does not register stale async post-process loads after stop",async()=>{const e=function(){let e;return{promise:new Promise(s=>{e=s}),resolve:e}}(),a=C(),t=await h(a,v(e.promise)),{sequence:o}=y("graph-asset",0,1);t.load(o),t.play(),t.stop(),e.resolve(d()),await g(),s(a.addPostProcessEffect).not.toHaveBeenCalled()})});/*
|
|
2
2
|
* Copyright (©) 2026 Hology Interactive AB. All rights reserved.
|
|
3
3
|
* See the LICENSE.md file for details.
|
|
4
4
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hology/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.267",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -69,7 +69,9 @@
|
|
|
69
69
|
"types": "./dist/index.d.ts",
|
|
70
70
|
"typesVersions": {
|
|
71
71
|
"*": {
|
|
72
|
-
"scene/animation-binary": [
|
|
72
|
+
"scene/animation-binary": [
|
|
73
|
+
"./dist/scene/animation-binary.d.ts"
|
|
74
|
+
],
|
|
73
75
|
"rendering": [
|
|
74
76
|
"./dist/rendering.d.ts"
|
|
75
77
|
],
|
|
@@ -246,4 +248,4 @@
|
|
|
246
248
|
"vitest": "^4.1.9",
|
|
247
249
|
"@hology/webgpu-renderer": "^0.1.12"
|
|
248
250
|
}
|
|
249
|
-
}
|
|
251
|
+
}
|