From dfdb8d4deff4ce858cac15acd6d6e6f4cdc44841 Mon Sep 17 00:00:00 2001 From: gokuthug1 Date: Wed, 19 Aug 2026 05:37:01 -0500 Subject: [PATCH 1/2] fix: migrate deprecated PCFSoftShadowMap to PCFShadowMap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCFSoftShadowMap (2) was deprecated in favor of PCFShadowMap (1), which now applies the same soft filtering. Migrate stale editor configs and project JSONs, update the examples, and add a regression test so old scenes stop emitting the WebGLShadowMap deprecation warning. Generated with Codebuff 🤖 Co-Authored-By: Codebuff --- editor/js/Config.js | 10 ++++++ editor/js/Menubar.Render.js | 4 +-- editor/js/libs/app.js | 12 +++++-- examples/webgpu_custom_fog.html | 2 +- examples/webgpu_generator_building.html | 2 +- examples/webgpu_generator_city.html | 2 +- examples/webgpu_lights_projector.html | 2 +- examples/webgpu_lights_spotlight.html | 2 +- examples/webgpu_postprocessing_sss.html | 2 +- examples/webgpu_reflection.html | 2 +- examples/webgpu_shadowmap_csm.html | 2 +- ...webgpu_skinning_instancing_individual.html | 2 +- ...shadows-spot-light-with-shadow-radius.html | 2 +- package.json | 2 ++ test/unit/UnitTestsEditor.html | 36 +++++++++++++++++++ test/unit/editor/app.tests.js | 29 +++++++++++++++ test/unit/four.editor.unit.js | 2 ++ 17 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 test/unit/UnitTestsEditor.html create mode 100644 test/unit/editor/app.tests.js create mode 100644 test/unit/four.editor.unit.js diff --git a/editor/js/Config.js b/editor/js/Config.js index 620596d1..e913fcfd 100644 --- a/editor/js/Config.js +++ b/editor/js/Config.js @@ -51,6 +51,16 @@ function Config() { } + // PCFSoftShadowMap ( 2 ) was deprecated in favor of PCFShadowMap ( 1 ), which + // now performs the same soft shadow filtering. Migrate stale saved values so + // old configs stop triggering the deprecation warning. + if ( storage[ 'project/renderer/shadowType' ] === 2 ) { + + storage[ 'project/renderer/shadowType' ] = 1; + window.localStorage[ name ] = JSON.stringify( storage ); + + } + return { getKey: function ( key ) { diff --git a/editor/js/Menubar.Render.js b/editor/js/Menubar.Render.js index 86ad7fcd..9c868778 100644 --- a/editor/js/Menubar.Render.js +++ b/editor/js/Menubar.Render.js @@ -3,7 +3,7 @@ import * as FOUR from '@tnb1j/4js'; import { UIPanel, UIRow, UIButton, UIInteger, UISelect, UIText } from './libs/ui.js'; import { ViewportPathtracer } from './Viewport.Pathtracer.js'; -import { APP } from './libs/app.js'; +import { APP, getShadowMapType } from './libs/app.js'; function MenubarRender( editor ) { @@ -228,7 +228,7 @@ class RenderImageDialog { renderer.setClearColor( editor.viewportColor ); if ( project.shadows !== undefined ) renderer.shadowMap.enabled = project.shadows; - if ( project.shadowType !== undefined ) renderer.shadowMap.type = project.shadowType; + if ( project.shadowType !== undefined ) renderer.shadowMap.type = getShadowMapType( project.shadowType ); if ( project.toneMapping !== undefined ) renderer.toneMapping = project.toneMapping; if ( project.toneMappingExposure !== undefined ) renderer.toneMappingExposure = project.toneMappingExposure; diff --git a/editor/js/libs/app.js b/editor/js/libs/app.js index fa8ca467..38338f3a 100644 --- a/editor/js/libs/app.js +++ b/editor/js/libs/app.js @@ -1,5 +1,13 @@ /* global FOUR */ +function getShadowMapType( shadowType ) { + + // PCFSoftShadowMap ( 2 ) was deprecated; PCFShadowMap ( 1 ) now applies the + // same soft filtering, so migrate the old value before using it. + return shadowType === FOUR.PCFSoftShadowMap ? FOUR.PCFShadowMap : shadowType; + +} + const APP = { Player: function () { @@ -48,7 +56,7 @@ const APP = { renderer.setPixelRatio( window.devicePixelRatio ); if ( project.shadows !== undefined ) renderer.shadowMap.enabled = project.shadows; - if ( project.shadowType !== undefined ) renderer.shadowMap.type = project.shadowType; + if ( project.shadowType !== undefined ) renderer.shadowMap.type = getShadowMapType( project.shadowType ); if ( project.toneMapping !== undefined ) renderer.toneMapping = project.toneMapping; if ( project.toneMappingExposure !== undefined ) renderer.toneMappingExposure = project.toneMappingExposure; @@ -306,4 +314,4 @@ const APP = { }; -export { APP }; +export { APP, getShadowMapType }; diff --git a/examples/webgpu_custom_fog.html b/examples/webgpu_custom_fog.html index d3b1d039..0752c87a 100644 --- a/examples/webgpu_custom_fog.html +++ b/examples/webgpu_custom_fog.html @@ -67,7 +67,7 @@ renderer.toneMapping = FOUR.ACESFilmicToneMapping; renderer.toneMappingExposure = 0.62; renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.inspector = new Inspector(); document.body.appendChild( renderer.domElement ); diff --git a/examples/webgpu_generator_building.html b/examples/webgpu_generator_building.html index 7e6070aa..1e4070c3 100644 --- a/examples/webgpu_generator_building.html +++ b/examples/webgpu_generator_building.html @@ -78,7 +78,7 @@ renderer.toneMapping = FOUR.ACESFilmicToneMapping; renderer.toneMappingExposure = 0.25; renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.inspector = new Inspector(); document.body.appendChild( renderer.domElement ); diff --git a/examples/webgpu_generator_city.html b/examples/webgpu_generator_city.html index 36c029d3..3e5a2235 100644 --- a/examples/webgpu_generator_city.html +++ b/examples/webgpu_generator_city.html @@ -64,7 +64,7 @@ renderer.toneMapping = FOUR.ACESFilmicToneMapping; renderer.toneMappingExposure = 0.45; renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.inspector = new Inspector(); document.body.appendChild( renderer.domElement ); diff --git a/examples/webgpu_lights_projector.html b/examples/webgpu_lights_projector.html index baa793bd..97daa3f5 100644 --- a/examples/webgpu_lights_projector.html +++ b/examples/webgpu_lights_projector.html @@ -66,7 +66,7 @@ document.body.appendChild( renderer.domElement ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.toneMapping = FOUR.ACESFilmicToneMapping; renderer.toneMappingExposure = 1; diff --git a/examples/webgpu_lights_spotlight.html b/examples/webgpu_lights_spotlight.html index 137a6051..6602cd52 100644 --- a/examples/webgpu_lights_spotlight.html +++ b/examples/webgpu_lights_spotlight.html @@ -62,7 +62,7 @@ renderer.toneMappingExposure = 1; renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; scene = new FOUR.Scene(); diff --git a/examples/webgpu_postprocessing_sss.html b/examples/webgpu_postprocessing_sss.html index 474039f9..8d652657 100644 --- a/examples/webgpu_postprocessing_sss.html +++ b/examples/webgpu_postprocessing_sss.html @@ -126,7 +126,7 @@ renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.inspector = new Inspector(); document.body.appendChild( renderer.domElement ); diff --git a/examples/webgpu_reflection.html b/examples/webgpu_reflection.html index b01aa660..481d003b 100644 --- a/examples/webgpu_reflection.html +++ b/examples/webgpu_reflection.html @@ -129,7 +129,7 @@ renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.toneMapping = FOUR.ACESFilmicToneMapping; renderer.inspector = new Inspector(); document.body.appendChild( renderer.domElement ); diff --git a/examples/webgpu_shadowmap_csm.html b/examples/webgpu_shadowmap_csm.html index 1356247d..c87ee4ca 100644 --- a/examples/webgpu_shadowmap_csm.html +++ b/examples/webgpu_shadowmap_csm.html @@ -98,7 +98,7 @@ renderer.setAnimationLoop( animate ); renderer.shadowMap.enabled = params.shadows; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.inspector = new Inspector(); diff --git a/examples/webgpu_skinning_instancing_individual.html b/examples/webgpu_skinning_instancing_individual.html index c9020745..8ba13c4c 100644 --- a/examples/webgpu_skinning_instancing_individual.html +++ b/examples/webgpu_skinning_instancing_individual.html @@ -319,7 +319,7 @@ renderer.setSize( window.innerWidth, window.innerHeight ); renderer.setAnimationLoop( animate ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; + renderer.shadowMap.type = FOUR.PCFShadowMap; renderer.toneMapping = FOUR.NeutralToneMapping; renderer.toneMappingExposure = 0.96; renderer.inspector = new Inspector(); diff --git a/manual/examples/shadows-spot-light-with-shadow-radius.html b/manual/examples/shadows-spot-light-with-shadow-radius.html index 5b6573f9..79977132 100644 --- a/manual/examples/shadows-spot-light-with-shadow-radius.html +++ b/manual/examples/shadows-spot-light-with-shadow-radius.html @@ -39,7 +39,7 @@ const canvas = document.querySelector( '#c' ); const renderer = new FOUR.WebGLRenderer( { antialias: true, canvas } ); renderer.shadowMap.enabled = true; - renderer.shadowMap.type = FOUR.PCFSoftShadowMap; // default FOUR.PCFShadowMap + renderer.shadowMap.type = FOUR.PCFShadowMap; const fov = 45; const aspect = 2; // the canvas default diff --git a/package.json b/package.json index 2b372fe3..4b9e5ccc 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,8 @@ "test-unit-headful": "node test/unit/puppeteer.unit.js --testPage=UnitTests.html --mode=headful", "test-unit-addons": "node test/unit/puppeteer.unit.js --testPage=UnitTestsAddons.html --mode=headless", "test-unit-addons-headful": "node test/unit/puppeteer.unit.js --testPage=UnitTestsAddons.html --mode=headful", + "test-unit-editor": "node test/unit/puppeteer.unit.js --testPage=UnitTestsEditor.html --mode=headless", + "test-unit-editor-headful": "node test/unit/puppeteer.unit.js --testPage=UnitTestsEditor.html --mode=headful", "test-e2e": "node test/e2e/puppeteer.js", "test-e2e-cov": "node test/e2e/check-coverage.js", "test-e2e-webgpu": "node test/e2e/puppeteer.js --webgpu", diff --git a/test/unit/UnitTestsEditor.html b/test/unit/UnitTestsEditor.html new file mode 100644 index 00000000..bb5e486c --- /dev/null +++ b/test/unit/UnitTestsEditor.html @@ -0,0 +1,36 @@ + + + + + 4.js Unit Tests - Using Files in /editor + + + + + + +
+
+ + + + + + + + + + diff --git a/test/unit/editor/app.tests.js b/test/unit/editor/app.tests.js new file mode 100644 index 00000000..5e0bb36b --- /dev/null +++ b/test/unit/editor/app.tests.js @@ -0,0 +1,29 @@ +import * as FOUR from '@tnb1j/4js'; +import { getShadowMapType } from '../../../editor/js/libs/app.js'; + +// app.js expects the FOUR namespace to be available on the global scope. +window.FOUR = FOUR; + +export default QUnit.module( 'Editor', () => { + + QUnit.module( 'APP.Player', () => { + + QUnit.test( 'getShadowMapType migrates deprecated PCFSoftShadowMap', ( assert ) => { + + const project = { shadowType: FOUR.PCFSoftShadowMap }; + + assert.equal( getShadowMapType( project.shadowType ), FOUR.PCFShadowMap, 'shadowType 2 is applied to the renderer as PCFShadowMap ( 1 )' ); + + } ); + + QUnit.test( 'getShadowMapType passes through supported types', ( assert ) => { + + assert.equal( getShadowMapType( FOUR.BasicShadowMap ), FOUR.BasicShadowMap, 'BasicShadowMap is unchanged' ); + assert.equal( getShadowMapType( FOUR.PCFShadowMap ), FOUR.PCFShadowMap, 'PCFShadowMap is unchanged' ); + assert.equal( getShadowMapType( FOUR.VSMShadowMap ), FOUR.VSMShadowMap, 'VSMShadowMap is unchanged' ); + + } ); + + } ); + +} ); diff --git a/test/unit/four.editor.unit.js b/test/unit/four.editor.unit.js new file mode 100644 index 00000000..2b33329f --- /dev/null +++ b/test/unit/four.editor.unit.js @@ -0,0 +1,2 @@ +//editor +import './editor/app.tests.js'; From 87757075317099a0ae457e99b795a13a6f5f3194 Mon Sep 17 00:00:00 2001 From: gokuthug1 Date: Wed, 19 Aug 2026 06:37:58 -0500 Subject: [PATCH 2/2] fix: tolerate slow SwiftShader rendering in offscreencanvas tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Software WebGL can take longer than a fixed 1s to draw the first frame for an OffscreenCanvas worker, which made the manual-security suite intermittently fail with a blank-canvas assertion on CI. Poll the screenshot until the scene is visibly rendered before asserting. 🤖 Generated with Codebuff Co-Authored-By: Codebuff --- test/integration/manual-security.tests.js | 32 ++++++++++++++++------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/test/integration/manual-security.tests.js b/test/integration/manual-security.tests.js index 7b42525a..5f38a679 100644 --- a/test/integration/manual-security.tests.js +++ b/test/integration/manual-security.tests.js @@ -331,24 +331,36 @@ async function testOffscreenExample( browser, baseURL, filename, interact = fals } - await new Promise( resolve => setTimeout( resolve, 1000 ) ); - - const screenshot = Buffer.from( await page.screenshot( { encoding: 'binary' } ) ); - const image = PNG.sync.read( screenshot ); - const background = Array.from( image.data.subarray( 0, 4 ) ); + // Software WebGL ( SwiftShader ) can be slow to draw the first frame, + // especially for an OffscreenCanvas rendered inside a worker. Poll the + // screenshot until the scene is visibly drawn instead of asserting after + // a single fixed delay. let changedPixels = 0; - for ( let i = 0; i < image.data.length; i += 4 ) { + for ( let attempt = 0; attempt < 20; attempt ++ ) { + + await new Promise( resolve => setTimeout( resolve, 500 ) ); + + const screenshot = Buffer.from( await page.screenshot( { encoding: 'binary' } ) ); + const image = PNG.sync.read( screenshot ); + const background = Array.from( image.data.subarray( 0, 4 ) ); + changedPixels = 0; + + for ( let i = 0; i < image.data.length; i += 4 ) { + + let difference = 0; + + for ( let channel = 0; channel < 4; channel ++ ) { - let difference = 0; + difference += Math.abs( image.data[ i + channel ] - background[ channel ] ); - for ( let channel = 0; channel < 4; channel ++ ) { + } - difference += Math.abs( image.data[ i + channel ] - background[ channel ] ); + if ( difference > 24 ) changedPixels ++; } - if ( difference > 24 ) changedPixels ++; + if ( changedPixels > 500 ) break; }