diff --git a/Apps/Playground/ReferenceImages/dynamicTextureClip.png b/Apps/Playground/ReferenceImages/dynamicTextureClip.png index b8d198171..ffd588bad 100644 Binary files a/Apps/Playground/ReferenceImages/dynamicTextureClip.png and b/Apps/Playground/ReferenceImages/dynamicTextureClip.png differ diff --git a/Apps/Playground/ReferenceImages/scissorTestWith0.9HardwareScaling.png b/Apps/Playground/ReferenceImages/scissor-test-2.png similarity index 99% rename from Apps/Playground/ReferenceImages/scissorTestWith0.9HardwareScaling.png rename to Apps/Playground/ReferenceImages/scissor-test-2.png index bc9bdac19..807700317 100644 Binary files a/Apps/Playground/ReferenceImages/scissorTestWith0.9HardwareScaling.png and b/Apps/Playground/ReferenceImages/scissor-test-2.png differ diff --git a/Apps/Playground/ReferenceImages/scissorTestWith1.5HardwareScaling.png b/Apps/Playground/ReferenceImages/scissor-test-3.png similarity index 99% rename from Apps/Playground/ReferenceImages/scissorTestWith1.5HardwareScaling.png rename to Apps/Playground/ReferenceImages/scissor-test-3.png index 083884186..3c9e2b077 100644 Binary files a/Apps/Playground/ReferenceImages/scissorTestWith1.5HardwareScaling.png and b/Apps/Playground/ReferenceImages/scissor-test-3.png differ diff --git a/Apps/Playground/ReferenceImages/scissor-test.png b/Apps/Playground/ReferenceImages/scissor-test.png index 9d0d2ad93..04bf5c11b 100644 Binary files a/Apps/Playground/ReferenceImages/scissor-test.png and b/Apps/Playground/ReferenceImages/scissor-test.png differ diff --git a/Apps/Playground/Scripts/config.json b/Apps/Playground/Scripts/config.json index 559c8a6f4..c9cf4a8b3 100644 --- a/Apps/Playground/Scripts/config.json +++ b/Apps/Playground/Scripts/config.json @@ -27,10 +27,8 @@ }, { "title": "GLTF ClearCoat", - "playgroundId": "#YG3BBF#4", - "referenceImage": "glTFClearCoat.png", - "renderCount": 50, - "excludeFromAutomaticTesting": false + "playgroundId": "#YG3BBF#33", + "referenceImage": "glTFClearCoat.png" }, { "title": "Iridescence GLTF", @@ -98,7 +96,7 @@ }, { "title": "Dynamic Texture context clip", - "playgroundId": "#FU0ES5#37", + "playgroundId": "#FU0ES5#47", "referenceImage": "dynamicTextureClip.png" }, { @@ -255,27 +253,26 @@ }, { "title": "Scissor test", - "renderCount": 10, - "playgroundId": "#W7E7CF#12", + "playgroundId": "#W7E7CF#34", "referenceImage": "scissor-test.png", "excludedGraphicsApis": [ "D3D12" ], - "comment": "TODO: reenable D3D12 when automatica mip-maps issue is fixed in bgfx" + "comment": "TODO: reenable D3D12 when automatic mip-maps issue is fixed in bgfx" }, { "title": "Scissor test with 0.9 hardware scaling", - "renderCount": 10, - "playgroundId": "#W7E7CF#27", - "referenceImage": "scissorTestWith0.9HardwareScaling.png", + "playgroundId": "#W7E7CF#34", + "replace": "//options//, hardwareScalingLevel = 0.9;", + "referenceImage": "scissor-test-2.png", "excludedGraphicsApis": [ "D3D12" ], - "comment": "TODO: reenable D3D12 when automatica mip-maps issue is fixed in bgfx" + "comment": "TODO: reenable D3D12 when automatic mip-maps issue is fixed in bgfx" }, { "title": "Scissor test with 1.5 hardware scaling", - "renderCount": 10, - "playgroundId": "#W7E7CF#28", - "referenceImage": "scissorTestWith1.5HardwareScaling.png", + "playgroundId": "#W7E7CF#34", + "replace": "//options//, hardwareScalingLevel = 1.5;", + "referenceImage": "scissor-test-3.png", "excludedGraphicsApis": [ "D3D12" ], - "comment": "TODO: reenable D3D12 when automatica mip-maps issue is fixed in bgfx" + "comment": "TODO: reenable D3D12 when automatic mip-maps issue is fixed in bgfx" }, { "title": "Scissor test with negative x and y", @@ -411,16 +408,14 @@ "playgroundId": "#DS8AA7#27", "replace": "__folder__, Mesh_PrimitiveMode, __page__, 0, __disableSRGBBuffers__, 0", "referenceImage": "gltfMeshPrimitiveMode0.png", - "errorRatio": 8.0, - "excludedEngines": [ "webgpu" ] + "errorRatio": 8.0 }, { "title": "GLTF Mesh Primitive Mode (1)", "playgroundId": "#DS8AA7#27", "replace": "__folder__, Mesh_PrimitiveMode, __page__, 1, __disableSRGBBuffers__, 0", "referenceImage": "gltfMeshPrimitiveMode1.png", - "errorRatio": 8.0, - "excludedEngines": [ "webgpu" ] + "errorRatio": 8.0 }, { "title": "GLTF Mesh Primitives", diff --git a/Apps/Playground/Scripts/playground_runner.js b/Apps/Playground/Scripts/playground_runner.js index 7b07f2f3a..516109a3e 100644 --- a/Apps/Playground/Scripts/playground_runner.js +++ b/Apps/Playground/Scripts/playground_runner.js @@ -1,5 +1,5 @@ if (typeof createScene === "function") { - var engine = new BABYLON.NativeEngine({adaptToDeviceRatio: true}); + var engine = new BABYLON.NativeEngine({ adaptToDeviceRatio: true }); var scene = createScene(); if (scene.then) { scene.then(function (scene) { diff --git a/Apps/Playground/Scripts/validation_native.js b/Apps/Playground/Scripts/validation_native.js index 45a43371a..169d804f3 100644 --- a/Apps/Playground/Scripts/validation_native.js +++ b/Apps/Playground/Scripts/validation_native.js @@ -1,386 +1,385 @@ -var engine; -var canvas; -var currentScene; -var config; -var justOnce; -var saveResult = true; -var testWidth = 600; -var testHeight = 400; -var generateReferences = false; - -// Random replacement -var seed = 1; -Math.random = function () { - var x = Math.sin(seed++) * 10000; - return x - Math.floor(x); -} - -function compare(test, renderData, referenceImage, threshold, errorRatio) { - var size = renderData.length; - var referenceData = TestUtils.getImageData(referenceImage); - var differencesCount = 0; - - for (var index = 0; index < size; index += 4) { - if (Math.abs(renderData[index] - referenceData[index]) < threshold && - Math.abs(renderData[index + 1] - referenceData[index + 1]) < threshold && - Math.abs(renderData[index + 2] - referenceData[index + 2]) < threshold) { - continue; - } - - if (differencesCount === 0) { - console.log(`First pixel off at ${index}: Value: (${renderData[index]}, ${renderData[index + 1]}, ${renderData[index] + 2}) - Expected: (${referenceData[index]}, ${referenceData[index + 1]}, ${referenceData[index + 2]}) `); - } +(function () { + let currentScene; + let config; + const justOnce = false; + const saveResult = true; + const testWidth = 600; + const testHeight = 400; + const generateReferences = false; + + const engine = new BABYLON.NativeEngine(); + engine.getCaps().parallelShaderCompile = undefined; + + engine.getRenderingCanvas = function () { + return window; + } - referenceData[index] = 255; - referenceData[index + 1] *= 0.5; - referenceData[index + 2] *= 0.5; - differencesCount++; + engine.getInputElement = function () { + return 0; } - if (differencesCount) { - console.log("Pixel difference: " + differencesCount + " pixels."); - } else { - console.log("No pixel difference!"); + const canvas = window; + + // Random replacement + let seed = 1; + Math.random = function () { + const x = Math.sin(seed++) * 10000; + return x - Math.floor(x); } - let error = (differencesCount * 100) / (size / 4) > errorRatio; + function compare(test, renderData, referenceImage, threshold, errorRatio) { + const referenceData = TestUtils.getImageData(referenceImage); + if (referenceData.length != renderData.length) { + throw new Error(`Reference data length (${referenceData.length}) must match render data length (${renderData.length})`); + } - const width = testWidth / engine.getHardwareScalingLevel(); - const height = testHeight / engine.getHardwareScalingLevel(); + const size = renderData.length; + let differencesCount = 0; - if (error) { - TestUtils.writePNG(referenceData, width, height, TestUtils.getOutputDirectory() + "/Errors/" + test.referenceImage); - } - if (saveResult || error) { - TestUtils.writePNG(renderData, width, height, TestUtils.getOutputDirectory() + "/Results/" + test.referenceImage); - } - return error; -} - -function saveRenderedResult(test, renderData) { - const width = testWidth / engine.getHardwareScalingLevel(); - const height = testHeight / engine.getHardwareScalingLevel(); - TestUtils.writePNG(renderData, width, height, TestUtils.getOutputDirectory() + "/Results/" + test.referenceImage); - return false; // no error -} - -function evaluate(test, resultCanvas, result, referenceImage, index, waitRing, done, compareFunction) { - engine._engine.getFrameBufferData(function (screenshot) { - var testRes = true; - - if (!test.onlyVisual) { - - var defaultErrorRatio = 2.5; - - if (compareFunction(test, screenshot, referenceImage, test.threshold || 25, test.errorRatio || defaultErrorRatio)) { - testRes = false; - console.log('failed'); - } else { - testRes = true; - console.log('validated'); + for (let index = 0; index < size; index += 4) { + if (Math.abs(renderData[index] - referenceData[index]) < threshold && + Math.abs(renderData[index + 1] - referenceData[index + 1]) < threshold && + Math.abs(renderData[index + 2] - referenceData[index + 2]) < threshold) { + continue; + } + + if (differencesCount === 0) { + console.log(`First pixel off at ${index}: Value: (${renderData[index]}, ${renderData[index + 1]}, ${renderData[index] + 2}) - Expected: (${referenceData[index]}, ${referenceData[index + 1]}, ${referenceData[index + 2]}) `); } + + referenceData[index] = 255; + referenceData[index + 1] *= 0.5; + referenceData[index + 2] *= 0.5; + differencesCount++; } - currentScene.dispose(); - currentScene = null; - engine.setHardwareScalingLevel(1); + if (differencesCount) { + console.log("Pixel difference: " + differencesCount + " pixels."); + } else { + console.log("No pixel difference!"); + } - done(testRes); - }); -} + const error = (differencesCount * 100) / (size / 4) > errorRatio; -function processCurrentScene(test, resultCanvas, result, renderImage, index, waitRing, done, compareFunction) { - currentScene.useConstantAnimationDeltaTime = true; - var renderCount = test.renderCount || 1; + const width = testWidth / engine.getHardwareScalingLevel(); + const height = testHeight / engine.getHardwareScalingLevel(); - currentScene.executeWhenReady(function () { - if (currentScene.activeCamera && currentScene.activeCamera.useAutoRotationBehavior) { - currentScene.activeCamera.useAutoRotationBehavior = false; + if (error) { + TestUtils.writePNG(referenceData, width, height, TestUtils.getOutputDirectory() + "/Errors/" + test.referenceImage); + } + if (saveResult || error) { + TestUtils.writePNG(renderData, width, height, TestUtils.getOutputDirectory() + "/Results/" + test.referenceImage); } - engine.runRenderLoop(function () { - try { - currentScene.render(); - renderCount--; - - if (renderCount === 0) { - engine.stopRenderLoop(); - evaluate(test, resultCanvas, result, renderImage, index, waitRing, done, compareFunction); + return error; + } + + function saveRenderedResult(test, renderData) { + const width = testWidth / engine.getHardwareScalingLevel(); + const height = testHeight / engine.getHardwareScalingLevel(); + TestUtils.writePNG(renderData, width, height, TestUtils.getOutputDirectory() + "/Results/" + test.referenceImage); + return false; // no error + } + + function evaluate(test, referenceImage, done, compareFunction) { + engine._engine.getFrameBufferData(function (screenshot) { + let testRes = true; + + if (!test.onlyVisual) { + + const defaultErrorRatio = 2.5; + + if (compareFunction(test, screenshot, referenceImage, test.threshold || 25, test.errorRatio || defaultErrorRatio)) { + testRes = false; + console.log('failed'); + } else { + testRes = true; + console.log('validated'); } } - catch (e) { - console.error(e); - done(false); - } + + currentScene.dispose(); + currentScene = null; + engine.setHardwareScalingLevel(1); + + done(testRes); }); - }); -} - -function loadPlayground(test, done, index, referenceImage, compareFunction) { - if (test.sceneFolder) { - BABYLON.SceneLoader.Load(config.root + test.sceneFolder, test.sceneFilename, engine, function (newScene) { - currentScene = newScene; - processCurrentScene(test, resultCanvas, result, referenceImage, index, waitRing, done, compareFunction); - }, - null, - function (loadedScene, msg) { - console.error(msg); - done(false); + } + + function processCurrentScene(test, renderImage, done, compareFunction) { + currentScene.useConstantAnimationDeltaTime = true; + let renderCount = test.renderCount || 1; + + currentScene.executeWhenReady(function () { + if (currentScene.activeCamera && currentScene.activeCamera.useAutoRotationBehavior) { + currentScene.activeCamera.useAutoRotationBehavior = false; + } + engine.runRenderLoop(function () { + try { + currentScene.render(); + renderCount--; + + if (renderCount === 0) { + engine.stopRenderLoop(); + evaluate(test, renderImage, done, compareFunction); + } + } + catch (e) { + console.error(e); + done(false); + } }); + }); } - else if (test.playgroundId) { - if (test.playgroundId[0] !== "#" || test.playgroundId.indexOf("#", 1) === -1) { - test.playgroundId += "#0"; + + function loadPlayground(test, done, referenceImage, compareFunction) { + if (test.sceneFolder) { + BABYLON.SceneLoader.Load(config.root + test.sceneFolder, test.sceneFilename, engine, function (newScene) { + currentScene = newScene; + processCurrentScene(test, referenceImage, done, compareFunction); + }, + null, + function (loadedScene, msg) { + console.error(msg); + done(false); + }); } + else if (test.playgroundId) { + if (test.playgroundId[0] !== "#" || test.playgroundId.indexOf("#", 1) === -1) { + test.playgroundId += "#0"; + } - var snippetUrl = "https://snippet.babylonjs.com"; - var pgRoot = "https://playground.babylonjs.com"; + const snippetUrl = "https://snippet.babylonjs.com"; + const pgRoot = "https://playground.babylonjs.com"; - var retryTime = 500; - var maxRetry = 5; - var retry = 0; + const retryTime = 500; + const maxRetry = 5; + let retry = 0; - var onError = function () { - retry++; - if (retry < maxRetry) { - setTimeout(function () { - loadPG(); - }, retryTime); + const onError = function () { + retry++; + if (retry < maxRetry) { + setTimeout(function () { + loadPG(); + }, retryTime); + } + else { + // Fail the test, something wrong happen + console.log("Running the playground failed."); + done(false); + } } - else { - // Fail the test, something wrong happen - console.log("Running the playground failed."); - done(false); + + const loadPG = function () { + const xmlHttp = new XMLHttpRequest(); + xmlHttp.addEventListener("readystatechange", function () { + if (xmlHttp.readyState === 4) { + try { + xmlHttp.onreadystatechange = null; + const snippet = JSON.parse(xmlHttp.responseText); + let code = JSON.parse(snippet.jsonPayload).code.toString() + .replace(/"\/textures\//g, '"' + pgRoot + "/textures/") + .replace(/"textures\//g, '"' + pgRoot + "/textures/") + .replace(/\/scenes\//g, pgRoot + "/scenes/") + .replace(/"scenes\//g, '"' + pgRoot + "/scenes/") + .replace(/"\.\.\/\.\.https/g, '"' + "https") + .replace("http://", "https://"); + + if (test.replace) { + const split = test.replace.split(","); + for (let i = 0; i < split.length; i += 2) { + const source = split[i].trim(); + const destination = split[i + 1].trim(); + code = code.replace(source, destination); + } + } + + currentScene = eval(code + "\r\ncreateScene(engine)"); + + if (currentScene.then) { + // Handle if createScene returns a promise + currentScene.then(function (scene) { + currentScene = scene; + processCurrentScene(test, referenceImage, done, compareFunction); + }).catch(function (e) { + console.error(e); + onError(); + }) + } else { + // Handle if createScene returns a scene + processCurrentScene(test, referenceImage, done, compareFunction); + } + + } + catch (e) { + console.error(e); + onError(); + } + } + }, false); + xmlHttp.onerror = function () { + console.error("Network error during test load."); + onError(); + } + xmlHttp.open("GET", snippetUrl + test.playgroundId.replace(/#/g, "/")); + xmlHttp.send(); } - } + loadPG(); + } else { + // Fix references + if (test.specificRoot) { + BABYLON.Tools.BaseUrl = config.root + test.specificRoot; + } + + const request = new XMLHttpRequest(); + request.open('GET', config.root + test.scriptToRun, true); - var loadPG = function () { - var xmlHttp = new XMLHttpRequest(); - xmlHttp.addEventListener("readystatechange", function () { - if (xmlHttp.readyState === 4) { + request.onreadystatechange = function () { + if (request.readyState === 4) { try { - xmlHttp.onreadystatechange = null; - var snippet = JSON.parse(xmlHttp.responseText); - var code = JSON.parse(snippet.jsonPayload).code.toString(); - code = code - .replace(/"\/textures\//g, '"' + pgRoot + "/textures/") - .replace(/"textures\//g, '"' + pgRoot + "/textures/") - .replace(/\/scenes\//g, pgRoot + "/scenes/") - .replace(/"scenes\//g, '"' + pgRoot + "/scenes/") - .replace(/"\.\.\/\.\.https/g, '"' + "https") - .replace("http://", "https://"); + request.onreadystatechange = null; + + const scriptToRun = request.responseText.replace(/..\/..\/assets\//g, config.root + "/Assets/"); + scriptToRun = scriptToRun.replace(/..\/..\/Assets\//g, config.root + "/Assets/"); + scriptToRun = scriptToRun.replace(/\/assets\//g, config.root + "/Assets/"); if (test.replace) { const split = test.replace.split(","); for (let i = 0; i < split.length; i += 2) { const source = split[i].trim(); const destination = split[i + 1].trim(); - code = code.replace(source, destination); + scriptToRun = scriptToRun.replace(source, destination); } } - currentScene = eval(code + "\r\ncreateScene(engine)"); - var resultCanvas = 0; - var result; - var waitRing; - - if (currentScene.then) { - // Handle if createScene returns a promise - currentScene.then(function (scene) { - currentScene = scene; - processCurrentScene(test, resultCanvas, result, referenceImage, index, waitRing, done, compareFunction); - }).catch(function (e) { - console.error(e); - onError(); - }) - } else { - // Handle if createScene returns a scene - processCurrentScene(test, resultCanvas, result, referenceImage, index, waitRing, done, compareFunction); + if (test.replaceUrl) { + const split = test.replaceUrl.split(","); + for (let i = 0; i < split.length; i++) { + const source = split[i].trim(); + const regex = new RegExp(source, "g"); + scriptToRun = scriptToRun.replace(regex, config.root + test.rootPath + source); + } } + currentScene = eval(scriptToRun + test.functionToCall + "(engine)"); + processCurrentScene(test, renderImage, done, compareFunction); } catch (e) { console.error(e); - onError(); + done(false); } } - }, false); - xmlHttp.onerror = function () { + }; + request.onerror = function () { console.error("Network error during test load."); - onError(); + done(false); } - xmlHttp.open("GET", snippetUrl + test.playgroundId.replace(/#/g, "/")); - xmlHttp.send(); + + request.send(null); } - loadPG(); - } else { - // Fix references - if (test.specificRoot) { - BABYLON.Tools.BaseUrl = config.root + test.specificRoot; + } + function runTest(index, done) { + if (index >= config.tests.length) { + done(false); } - var request = new XMLHttpRequest(); - request.open('GET', config.root + test.scriptToRun, true); + const test = config.tests[index]; + if (test.onlyVisual || test.excludeFromAutomaticTesting) { + done(true); + return; + } + if (test.excludedGraphicsApis && test.excludedGraphicsApis.includes(TestUtils.getGraphicsApiName())) { + done(true); + return; + } + const testInfo = "Running " + test.title; + console.log(testInfo); + TestUtils.setTitle(testInfo); - request.onreadystatechange = function () { - if (request.readyState === 4) { - try { - request.onreadystatechange = null; - - var scriptToRun = request.responseText.replace(/..\/..\/assets\//g, config.root + "/Assets/"); - scriptToRun = scriptToRun.replace(/..\/..\/Assets\//g, config.root + "/Assets/"); - scriptToRun = scriptToRun.replace(/\/assets\//g, config.root + "/Assets/"); - - if (test.replace) { - var split = test.replace.split(","); - for (var i = 0; i < split.length; i += 2) { - var source = split[i].trim(); - var destination = split[i + 1].trim(); - scriptToRun = scriptToRun.replace(source, destination); - } - } + seed = 1; - if (test.replaceUrl) { - var split = test.replaceUrl.split(","); - for (var i = 0; i < split.length; i++) { - var source = split[i].trim(); - var regex = new RegExp(source, "g"); - scriptToRun = scriptToRun.replace(regex, config.root + test.rootPath + source); - } - } + if (generateReferences) { + loadPlayground(test, done, undefined, saveRenderedResult); + } else { + const onLoadFileError = function (request, exception) { + console.error("Failed to retrieve " + url + ".", exception); + done(false); + }; - currentScene = eval(scriptToRun + test.functionToCall + "(engine)"); - processCurrentScene(test, resultCanvas, result, renderImage, index, waitRing, done, compareFunction); - } - catch (e) { - console.error(e); - done(false); + const onload = function (data, responseURL) { + if (typeof (data) === "string") { + throw new Error("Decode Image from string data not yet implemented."); } - } - }; - request.onerror = function () { - console.error("Network error during test load."); - done(false); - } - request.send(null); - } -} -function runTest(index, done) { - if (index >= config.tests.length) { - done(false); + const referenceImage = TestUtils.decodeImage(data); + loadPlayground(test, done, referenceImage, compare); + }; + + // run test and image comparison + const url = "app:///ReferenceImages/" + test.referenceImage; + BABYLON.Tools.LoadFile(url, onload, undefined, undefined, /*useArrayBuffer*/true, onLoadFileError); + } } - var test = config.tests[index]; - if (test.onlyVisual || test.excludeFromAutomaticTesting) { - done(true); - return; + OffscreenCanvas = function (width, height) { + return { + width: width + , height: height + , getContext: function (type) { + return { + fillRect: function (x, y, w, h) { } + , measureText: function (text) { return 8; } + , fillText: function (text, x, y) { } + }; + } + }; } - if (test.excludedGraphicsApis && test.excludedGraphicsApis.includes(TestUtils.getGraphicsApiName())) { - done(true); - return; + + document = { + createElement: function (type) { + if (type === "canvas") { + return new OffscreenCanvas(64, 64); + } + return {}; + }, + removeEventListener: function () { } } - let testInfo = "Running " + test.title; - console.log(testInfo); - TestUtils.setTitle(testInfo); - - seed = 1; - - let onLoadFileError = function (request, exception) { - console.error("Failed to retrieve " + url + ".", exception); - done(false); - }; - var onload = function (data, responseURL) { - if (typeof (data) === "string") { - throw new Error("Decode Image from string data not yet implemented."); - } - var referenceImage = TestUtils.decodeImage(data); - loadPlayground(test, done, index, referenceImage, compare); + const xhr = new XMLHttpRequest(); + xhr.open("GET", "app:///Scripts/config.json", true); - }; + xhr.addEventListener("readystatechange", function () { + if (xhr.status === 200) { + config = JSON.parse(xhr.responseText); - if (generateReferences) { - loadPlayground(test, done, index, undefined, saveRenderedResult); - } else { - // run test and image comparison - const url = "app:///ReferenceImages/" + test.referenceImage; - BABYLON.Tools.LoadFile(url, onload, undefined, undefined, /*useArrayBuffer*/true, onLoadFileError); - } -} - -engine = new BABYLON.NativeEngine(); -engine.getCaps().parallelShaderCompile = undefined; - -engine.getRenderingCanvas = function () { - return window; -} - -engine.getInputElement = function () { - return 0; -} - -canvas = window; - -OffscreenCanvas = function (width, height) { - return { - width: width - , height: height - , getContext: function (type) { - return { - fillRect: function (x, y, w, h) { } - , measureText: function (text) { return 8; } - , fillText: function (text, x, y) { } - }; - } - }; -} + // Run tests + const recursiveRunTest = function (i) { + runTest(i, function (status) { + if (!status) { + TestUtils.exit(-1); + return; + } + i++; + if (justOnce || i >= config.tests.length) { + engine.dispose(); + TestUtils.exit(0); + return; + } + recursiveRunTest(i); + }); + } -document = { - createElement: function (type) { - if (type === "canvas") { - return new OffscreenCanvas(64, 64); - } - return {}; - }, - removeEventListener: function () { } -} - -var xhr = new XMLHttpRequest(); -xhr.open("GET", "app:///Scripts/config.json", true); - -xhr.addEventListener("readystatechange", function () { - if (xhr.status === 200) { - config = JSON.parse(xhr.responseText); - - // Run tests - var index = 0; - var recursiveRunTest = function (i) { - runTest(i, function (status) { - if (!status) { - TestUtils.exit(-1); - return; - } - i++; - if (justOnce || i >= config.tests.length) { - engine.dispose(); - TestUtils.exit(0); - return; - } - recursiveRunTest(i); - }); + recursiveRunTest(0); } + }, false); - recursiveRunTest(index); - } -}, false); - - -BABYLON.Tools.LoadFile("https://raw.githubusercontent.com/CedricGuillemet/dump/master/droidsans.ttf", (data) => { - _native.Canvas.loadTTFAsync("droidsans", data).then(function () { - _native.RootUrl = "https://playground.babylonjs.com"; - console.log("Starting"); - TestUtils.setTitle("Starting Native Validation Tests"); - TestUtils.updateSize(testWidth, testHeight); - xhr.send(); - }); -}, undefined, undefined, true); + + BABYLON.Tools.LoadFile("https://raw.githubusercontent.com/CedricGuillemet/dump/master/droidsans.ttf", (data) => { + _native.Canvas.loadTTFAsync("droidsans", data).then(function () { + _native.RootUrl = "https://playground.babylonjs.com"; + console.log("Starting"); + TestUtils.setTitle("Starting Native Validation Tests"); + TestUtils.updateSize(testWidth, testHeight); + xhr.send(); + }); + }, undefined, undefined, true); +})(); \ No newline at end of file diff --git a/Apps/Playground/Win32/App.cpp b/Apps/Playground/Win32/App.cpp index 029a30936..d9920d856 100644 --- a/Apps/Playground/Win32/App.cpp +++ b/Apps/Playground/Win32/App.cpp @@ -137,7 +137,23 @@ namespace device->StartRenderingCurrentFrame(); update->Start(); - runtime.emplace(); + Babylon::AppRuntime::Options options{}; + + options.EnableDebugger = true; + + options.UnhandledExceptionHandler = [hWnd](const Napi::Error& error) { + std::ostringstream ss{}; + ss << "[Uncaught Error] " << Napi::GetErrorString(error) << std::endl; + OutputDebugStringA(ss.str().data()); + + std::cerr << ss.str(); + std::cerr.flush(); + + Babylon::Plugins::TestUtils::errorCode = -1; + PostMessage(hWnd, WM_CLOSE, 0, 0); + }; + + runtime.emplace(options); runtime->Dispatch([hWnd](Napi::Env env) { device->AddToJavaScript(env); diff --git a/Apps/Playground/macOS/ViewController.mm b/Apps/Playground/macOS/ViewController.mm index 18f20bd2f..81190ccdf 100644 --- a/Apps/Playground/macOS/ViewController.mm +++ b/Apps/Playground/macOS/ViewController.mm @@ -103,8 +103,8 @@ - (void)refreshBabylon { Babylon::Graphics::Configuration graphicsConfig{}; graphicsConfig.Window = engineView; - graphicsConfig.Width = static_cast(600); - graphicsConfig.Height = static_cast(400); + graphicsConfig.Width = static_cast(engineView.drawableSize.width); + graphicsConfig.Height = static_cast(engineView.drawableSize.height); graphicsConfig.MSAASamples = 4; device.emplace(graphicsConfig); diff --git a/Apps/package-lock.json b/Apps/package-lock.json index 0251a5094..485399448 100644 --- a/Apps/package-lock.json +++ b/Apps/package-lock.json @@ -8,11 +8,11 @@ "name": "BabylonNative", "version": "0.0.1", "dependencies": { - "babylonjs": "^6.46.1", - "babylonjs-gltf2interface": "^6.46.1", - "babylonjs-gui": "^6.46.1", - "babylonjs-loaders": "^6.46.1", - "babylonjs-materials": "^6.46.1", + "babylonjs": "^7.6.2", + "babylonjs-gltf2interface": "^7.6.2", + "babylonjs-gui": "^7.6.2", + "babylonjs-loaders": "^7.6.2", + "babylonjs-materials": "^7.6.2", "chai": "^4.3.4", "jsc-android": "^241213.1.0", "mocha": "^9.2.2", @@ -80,39 +80,39 @@ } }, "node_modules/babylonjs": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-6.46.1.tgz", - "integrity": "sha512-UiXeoIP+xGdq0oBgQorcY2eOIGlCru8peTKJ3Z8+tLR6dp+icDxg7W+XEJqvT9eSd0f+IW1fTVJwvHQIr2IUQA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-7.6.2.tgz", + "integrity": "sha512-uolOo3wqRRZdVUtY4X8qocBXFP/VHNeSK7jWyFIpinxMP0UGKvk28Hvjncgh9mF0Bi52uVUtzTCifQr98nz4Mg==", "hasInstallScript": true }, "node_modules/babylonjs-gltf2interface": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.46.1.tgz", - "integrity": "sha512-BOeuBMmbfZh33xvrrDmogvxOIg7VybFuLPGfSm4GQP0p1cNQ85WKaKbAk6zaN628LboqWYFORuMnp5qa5ObjpQ==" + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.6.2.tgz", + "integrity": "sha512-Nr84TFwfrCOuoEhnEvGRgyvePX+HIcr1I2p+OQjSPEjiwwKCCusY29EC0q61LsVW2hU3Ahl5OKxrNSuAfTr/LQ==" }, "node_modules/babylonjs-gui": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-gui/-/babylonjs-gui-6.46.1.tgz", - "integrity": "sha512-v0sBI/xGcr6FK0lmxj6eOq+1jqDjwJQ3MsEvr5VTufP5RSXn6b5hPTbmFMkpT8WtqLlM9FzpW1GPha543QyJpA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-gui/-/babylonjs-gui-7.6.2.tgz", + "integrity": "sha512-BFq7n3Sf3kezWif3r0NcREnTPq8WoBOAukTpdIWUUIkNU8kNywZaVfGVXBHL/mnJ11CN7vWs8aWTp5cINpHXow==", "dependencies": { - "babylonjs": "^6.46.1" + "babylonjs": "^7.6.2" } }, "node_modules/babylonjs-loaders": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-loaders/-/babylonjs-loaders-6.46.1.tgz", - "integrity": "sha512-nUTGCZjSS2yT0IX3STDVebS6M5nOc/vnhy4bRh/d4FMt/9cB8+GcXPf2kKPZfsy5AdPKT/hyPvo1+ULWCd5APg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-loaders/-/babylonjs-loaders-7.6.2.tgz", + "integrity": "sha512-/c3JNz1SOasK4Rh/fGVjpIoi/umn3pvVaHcAumUXhsxz61+J/nFyeTJ5y51qyiZ9Bo7bWF8+FVZGtnRDq8RRiw==", "dependencies": { - "babylonjs": "^6.46.1", - "babylonjs-gltf2interface": "^6.46.1" + "babylonjs": "^7.6.2", + "babylonjs-gltf2interface": "^7.6.2" } }, "node_modules/babylonjs-materials": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-materials/-/babylonjs-materials-6.46.1.tgz", - "integrity": "sha512-uuIqnvjZYTkBxD5eWO6m8bPqWeq+H/UIHYqv5+p/eEu1XyJvferyF30sEsjeCziCJBmthV9Q48/N/zYTIC6ErQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-materials/-/babylonjs-materials-7.6.2.tgz", + "integrity": "sha512-kF1PMSEOyTfhCheGJ8uOsActRJ9rybtzJTisVsoZ4K830rkUkCgbnmSYhIXUnBAp+S0eTJx/pVLP2XeO4g6iUA==", "dependencies": { - "babylonjs": "^6.46.1" + "babylonjs": "^7.6.2" } }, "node_modules/balanced-match": { @@ -1038,38 +1038,38 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" }, "babylonjs": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-6.46.1.tgz", - "integrity": "sha512-UiXeoIP+xGdq0oBgQorcY2eOIGlCru8peTKJ3Z8+tLR6dp+icDxg7W+XEJqvT9eSd0f+IW1fTVJwvHQIr2IUQA==" + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs/-/babylonjs-7.6.2.tgz", + "integrity": "sha512-uolOo3wqRRZdVUtY4X8qocBXFP/VHNeSK7jWyFIpinxMP0UGKvk28Hvjncgh9mF0Bi52uVUtzTCifQr98nz4Mg==" }, "babylonjs-gltf2interface": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-6.46.1.tgz", - "integrity": "sha512-BOeuBMmbfZh33xvrrDmogvxOIg7VybFuLPGfSm4GQP0p1cNQ85WKaKbAk6zaN628LboqWYFORuMnp5qa5ObjpQ==" + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-gltf2interface/-/babylonjs-gltf2interface-7.6.2.tgz", + "integrity": "sha512-Nr84TFwfrCOuoEhnEvGRgyvePX+HIcr1I2p+OQjSPEjiwwKCCusY29EC0q61LsVW2hU3Ahl5OKxrNSuAfTr/LQ==" }, "babylonjs-gui": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-gui/-/babylonjs-gui-6.46.1.tgz", - "integrity": "sha512-v0sBI/xGcr6FK0lmxj6eOq+1jqDjwJQ3MsEvr5VTufP5RSXn6b5hPTbmFMkpT8WtqLlM9FzpW1GPha543QyJpA==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-gui/-/babylonjs-gui-7.6.2.tgz", + "integrity": "sha512-BFq7n3Sf3kezWif3r0NcREnTPq8WoBOAukTpdIWUUIkNU8kNywZaVfGVXBHL/mnJ11CN7vWs8aWTp5cINpHXow==", "requires": { - "babylonjs": "^6.46.1" + "babylonjs": "^7.6.2" } }, "babylonjs-loaders": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-loaders/-/babylonjs-loaders-6.46.1.tgz", - "integrity": "sha512-nUTGCZjSS2yT0IX3STDVebS6M5nOc/vnhy4bRh/d4FMt/9cB8+GcXPf2kKPZfsy5AdPKT/hyPvo1+ULWCd5APg==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-loaders/-/babylonjs-loaders-7.6.2.tgz", + "integrity": "sha512-/c3JNz1SOasK4Rh/fGVjpIoi/umn3pvVaHcAumUXhsxz61+J/nFyeTJ5y51qyiZ9Bo7bWF8+FVZGtnRDq8RRiw==", "requires": { - "babylonjs": "^6.46.1", - "babylonjs-gltf2interface": "^6.46.1" + "babylonjs": "^7.6.2", + "babylonjs-gltf2interface": "^7.6.2" } }, "babylonjs-materials": { - "version": "6.46.1", - "resolved": "https://registry.npmjs.org/babylonjs-materials/-/babylonjs-materials-6.46.1.tgz", - "integrity": "sha512-uuIqnvjZYTkBxD5eWO6m8bPqWeq+H/UIHYqv5+p/eEu1XyJvferyF30sEsjeCziCJBmthV9Q48/N/zYTIC6ErQ==", + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/babylonjs-materials/-/babylonjs-materials-7.6.2.tgz", + "integrity": "sha512-kF1PMSEOyTfhCheGJ8uOsActRJ9rybtzJTisVsoZ4K830rkUkCgbnmSYhIXUnBAp+S0eTJx/pVLP2XeO4g6iUA==", "requires": { - "babylonjs": "^6.46.1" + "babylonjs": "^7.6.2" } }, "balanced-match": { diff --git a/Apps/package.json b/Apps/package.json index 31a5a4e26..0114d71f0 100644 --- a/Apps/package.json +++ b/Apps/package.json @@ -6,11 +6,11 @@ "getNightly": "node scripts/getNightly.js" }, "dependencies": { - "babylonjs": "^6.46.1", - "babylonjs-gltf2interface": "^6.46.1", - "babylonjs-gui": "^6.46.1", - "babylonjs-loaders": "^6.46.1", - "babylonjs-materials": "^6.46.1", + "babylonjs": "^7.6.2", + "babylonjs-gltf2interface": "^7.6.2", + "babylonjs-gui": "^7.6.2", + "babylonjs-loaders": "^7.6.2", + "babylonjs-materials": "^7.6.2", "chai": "^4.3.4", "jsc-android": "^241213.1.0", "mocha": "^9.2.2", diff --git a/CMakeLists.txt b/CMakeLists.txt index 3780df8c7..f79c6d7c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ FetchContent_Declare(ios-cmake GIT_TAG 4.4.1) FetchContent_Declare(JsRuntimeHost GIT_REPOSITORY https://github.com/BabylonJS/JsRuntimeHost.git - GIT_TAG 66c863be8cedf2869a4e59ce99144417c78af73c) + GIT_TAG 5c06ce7c096fbce0523594979e0083410e1b4c15) FetchContent_Declare(OpenXR-MixedReality GIT_REPOSITORY https://github.com/microsoft/OpenXR-MixedReality.git GIT_TAG 67424511525b96a36847f2a96d689d99e5879503) diff --git a/Core/Graphics/CMakeLists.txt b/Core/Graphics/CMakeLists.txt index c17bd896f..44458c63e 100644 --- a/Core/Graphics/CMakeLists.txt +++ b/Core/Graphics/CMakeLists.txt @@ -41,7 +41,6 @@ elseif(ANDROID) endif() target_link_libraries(Graphics - PRIVATE napi_extensions PRIVATE JsRuntimeInternal PRIVATE bgfx PRIVATE bimg diff --git a/Core/Graphics/Source/DeviceContext.cpp b/Core/Graphics/Source/DeviceContext.cpp index 54b516c02..93512e0fd 100644 --- a/Core/Graphics/Source/DeviceContext.cpp +++ b/Core/Graphics/Source/DeviceContext.cpp @@ -2,7 +2,7 @@ #include "DeviceImpl.h" -#include +#include namespace Babylon::Graphics { diff --git a/Core/Graphics/Source/DeviceImpl.cpp b/Core/Graphics/Source/DeviceImpl.cpp index ee33877cb..1a21f3903 100644 --- a/Core/Graphics/Source/DeviceImpl.cpp +++ b/Core/Graphics/Source/DeviceImpl.cpp @@ -2,9 +2,9 @@ #include #include - #include #include +#include #if defined(__APPLE__) #include @@ -19,6 +19,11 @@ namespace { constexpr auto JS_GRAPHICS_NAME = "_Graphics"; + + bool FuzzyEqual(float a, float b, float epsilon = std::numeric_limits::epsilon()) + { + return std::abs(a - b) < epsilon; + } } namespace Babylon::Graphics @@ -52,22 +57,23 @@ namespace Babylon::Graphics uintptr_t DeviceImpl::GetId() const { - return m_bgfxId; + return m_bgfxId; } void DeviceImpl::UpdateWindow(WindowT window) { std::scoped_lock lock{m_state.Mutex}; - m_state.Bgfx.Dirty = true; ConfigureBgfxPlatformData(m_state.Bgfx.InitState.platformData, window); ConfigureBgfxRenderType(m_state.Bgfx.InitState.platformData, m_state.Bgfx.InitState.type); m_state.Resolution.DevicePixelRatio = GetDevicePixelRatio(window); + m_state.Bgfx.Dirty = true; } void DeviceImpl::UpdateDevice(DeviceT device) { - std::scoped_lock lock{ m_state.Mutex }; - m_state.Bgfx.InitState.platformData.context = device; + std::scoped_lock lock{m_state.Mutex}; + m_state.Bgfx.InitState.platformData.context = device; + m_state.Bgfx.Dirty = true; } void DeviceImpl::UpdateSize(size_t width, size_t height) @@ -81,7 +87,6 @@ namespace Babylon::Graphics void DeviceImpl::UpdateMSAA(uint8_t value) { std::scoped_lock lock{m_state.Mutex}; - m_state.Bgfx.Dirty = true; auto& init = m_state.Bgfx.InitState; init.resolution.reset &= ~BGFX_RESET_MSAA_MASK; switch (value) @@ -103,25 +108,26 @@ namespace Babylon::Graphics init.resolution.reset |= BGFX_RESET_MSAA_X16; break; default: - m_bgfxCallback.trace(__FILE__, __LINE__, "WARNING: Setting an incorrect value for SetMSAA (%d). Correct values are 0, 1 (disable MSAA) or 2, 4, 8, 16.", int(value)); + m_bgfxCallback.trace(__FILE__, __LINE__, "WARNING: Setting an incorrect value for SetMSAA (%d). Correct values are 0, 1 (disable MSAA) or 2, 4, 8, 16.", static_cast(value)); break; } + m_state.Bgfx.Dirty = true; } void DeviceImpl::UpdateAlphaPremultiplied(bool enabled) { std::scoped_lock lock{m_state.Mutex}; - m_state.Bgfx.Dirty = true; auto& init = m_state.Bgfx.InitState; init.resolution.reset &= ~BGFX_RESET_TRANSPARENT_BACKBUFFER; init.resolution.reset |= enabled ? BGFX_RESET_TRANSPARENT_BACKBUFFER : 0; + m_state.Bgfx.Dirty = true; } void DeviceImpl::UpdateDevicePixelRatio(float value) { std::scoped_lock lock{m_state.Mutex}; - m_state.Bgfx.Dirty = true; m_state.Resolution.DevicePixelRatio = value; + m_state.Bgfx.Dirty = true; } void DeviceImpl::SetRenderResetCallback(std::function callback) @@ -245,9 +251,6 @@ namespace Babylon::Graphics // Ensure rendering is enabled. EnableRendering(); - // Update bgfx state if necessary. - UpdateBgfxState(); - // Unlock the update safe timespans. { std::scoped_lock lock{m_updateSafeTimespansMutex}; @@ -300,12 +303,12 @@ namespace Babylon::Graphics throw std::runtime_error{"HardwareScalingValue cannot be less than or equal to 0."}; } + std::scoped_lock lock{m_state.Mutex}; + if (!FuzzyEqual(m_state.Resolution.HardwareScalingLevel, level)) { - std::scoped_lock lock{m_state.Mutex}; m_state.Resolution.HardwareScalingLevel = level; + UpdateBgfxResolution(); } - - UpdateBgfxResolution(); } float DeviceImpl::GetDevicePixelRatio() const @@ -343,8 +346,8 @@ namespace Babylon::Graphics std::scoped_lock lock{m_state.Mutex}; if ((m_state.Bgfx.InitState.resolution.reset & BGFX_RESET_CAPTURE) == 0) { - m_state.Bgfx.Dirty = true; m_state.Bgfx.InitState.resolution.reset |= BGFX_RESET_CAPTURE; + m_state.Bgfx.Dirty = true; } } @@ -369,12 +372,11 @@ namespace Babylon::Graphics bgfx::setPlatformData(m_state.Bgfx.InitState.platformData); // Ensure bgfx rebinds all texture information. - bgfx::discard(BGFX_DISCARD_ALL); + bgfx::discard(); auto& res = m_state.Bgfx.InitState.resolution; bgfx::reset(res.width, res.height, res.reset); bgfx::setViewRect(0, 0, 0, static_cast(res.width), static_cast(res.height)); - bgfx::frame(); m_state.Bgfx.Dirty = false; } @@ -383,20 +385,11 @@ namespace Babylon::Graphics void DeviceImpl::UpdateBgfxResolution() { std::scoped_lock lock{m_state.Mutex}; - m_state.Bgfx.Dirty = true; auto& res = m_state.Bgfx.InitState.resolution; auto level = m_state.Resolution.HardwareScalingLevel; res.width = static_cast(m_state.Resolution.Width / level); res.height = static_cast(m_state.Resolution.Height / level); - } - - void DeviceImpl::DiscardIfDirty() - { - std::scoped_lock lock{m_state.Mutex}; - if (m_state.Bgfx.Dirty) - { - bgfx::discard(); - } + m_state.Bgfx.Dirty = true; } void DeviceImpl::RequestScreenShots() @@ -421,8 +414,8 @@ namespace Babylon::Graphics // Automatically end bgfx encoders. EndEncoders(); - // Discard everything if the bgfx state is dirty. - DiscardIfDirty(); + // Update bgfx state if necessary. + UpdateBgfxState(); // Request screen shots before bgfx::frame. RequestScreenShots(); diff --git a/Dependencies/CMakeLists.txt b/Dependencies/CMakeLists.txt index 1657fb184..47cee4242 100644 --- a/Dependencies/CMakeLists.txt +++ b/Dependencies/CMakeLists.txt @@ -199,8 +199,3 @@ endif() if(WINDOWS_STORE) add_subdirectory(WindowsAppSDK) endif() - -# -------------------------------------------------- -# NAPI Extensions -# -------------------------------------------------- -add_subdirectory(napi-extensions) diff --git a/Dependencies/napi-extensions/CMakeLists.txt b/Dependencies/napi-extensions/CMakeLists.txt deleted file mode 100644 index a0d7204b5..000000000 --- a/Dependencies/napi-extensions/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -add_library(napi_extensions INTERFACE) -target_include_directories(napi_extensions INTERFACE "include") diff --git a/Dependencies/napi-extensions/include/napi/napi_pointer.h b/Dependencies/napi-extensions/include/napi/napi_pointer.h deleted file mode 100644 index bc16614a1..000000000 --- a/Dependencies/napi-extensions/include/napi/napi_pointer.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include - -#include -#include - -namespace Napi -{ - template - struct PointerTraits - { - using RepresentationT = uint32_t; - static_assert(sizeof(PointerT) % sizeof(RepresentationT) == 0); - static inline constexpr size_t ByteSize{sizeof(PointerT)}; - static inline constexpr size_t ArraySize{sizeof(PointerT) / sizeof(RepresentationT)}; - }; - - template - auto NapiPointerDeleter(PointerT pointer) - { - return [pointer]() - { - delete pointer; - }; - } - - class FunctionPointer - { - public: - template - static Napi::Value Create(Napi::Env env, ReturnT (ClassT::*functionPtr)(ArgsT...)) - { - using PointerT = decltype(functionPtr); - auto arrayBuffer = Napi::ArrayBuffer::New(env, PointerTraits::ByteSize); - std::memcpy(arrayBuffer.Data(), &functionPtr, sizeof(PointerT)); - return Napi::Uint32Array::New(env, PointerTraits::ArraySize, arrayBuffer, 0).template As(); - } - }; - - template - class Pointer - { - public: - static Napi::Value Create(Napi::Env env, const T* pointer) - { - using PointerT = T*; - auto arrayBuffer = Napi::ArrayBuffer::New(env, PointerTraits::ByteSize); - std::memcpy(arrayBuffer.Data(), &pointer, sizeof(PointerT)); - return Napi::Uint32Array::New(env, PointerTraits::ArraySize, arrayBuffer, 0); - } - - template - static Napi::Value Create(Napi::Env env, const T* pointer, CallableT&& finalizationCallback) - { - using PointerT = T*; - using DataT = std::array::ArraySize>; - auto finalizer = [callback = std::forward(finalizationCallback)](Napi::Env, void* ptr) - { - callback(); - delete reinterpret_cast(ptr); - }; - auto arrayBuffer = Napi::ArrayBuffer::New(env, new DataT, PointerTraits::ByteSize, std::move(finalizer)); - std::memcpy(arrayBuffer.Data(), &pointer, sizeof(PointerT)); - return Napi::Uint32Array::New(env, PointerTraits::ArraySize, arrayBuffer, 0); - } - - template - Pointer(EnvT&& env, ValueT&& value) - : m_pointer{*reinterpret_cast(Napi::Value(std::forward(env), std::forward(value)).As().Data())} - { - } - - T* Get() const - { - return m_pointer; - } - - T& operator*() const - { - return *m_pointer; - } - - private: - T* m_pointer; - }; -} diff --git a/Install/Install.cmake b/Install/Install.cmake index 6239096ac..ef2ebee8c 100644 --- a/Install/Install.cmake +++ b/Install/Install.cmake @@ -101,6 +101,9 @@ if(NAPI_JAVASCRIPT_ENGINE STREQUAL "JSI") install(DIRECTORY ${V8JSI_PACKAGE_PATH}/build/native/jsi/jsi TYPE INCLUDE) endif() +install_targets(napi-extensions) +install_include_for_targets(napi-extensions) + # ---------------- # Plugins # ---------------- diff --git a/Plugins/ExternalTexture/CMakeLists.txt b/Plugins/ExternalTexture/CMakeLists.txt index f2f8253f1..737bfab72 100644 --- a/Plugins/ExternalTexture/CMakeLists.txt +++ b/Plugins/ExternalTexture/CMakeLists.txt @@ -18,7 +18,6 @@ target_include_directories(ExternalTexture target_link_libraries(ExternalTexture PUBLIC napi PUBLIC GraphicsDevice - PRIVATE napi_extensions PRIVATE JsRuntimeInternal PRIVATE GraphicsDeviceContext) diff --git a/Plugins/ExternalTexture/Source/ExternalTexture_D3D11.cpp b/Plugins/ExternalTexture/Source/ExternalTexture_D3D11.cpp index c5bcacf36..37b08a76e 100644 --- a/Plugins/ExternalTexture/Source/ExternalTexture_D3D11.cpp +++ b/Plugins/ExternalTexture/Source/ExternalTexture_D3D11.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/Plugins/ExternalTexture/Source/ExternalTexture_D3D12.cpp b/Plugins/ExternalTexture/Source/ExternalTexture_D3D12.cpp index 9a693ca3c..6d49c017c 100644 --- a/Plugins/ExternalTexture/Source/ExternalTexture_D3D12.cpp +++ b/Plugins/ExternalTexture/Source/ExternalTexture_D3D12.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/Plugins/ExternalTexture/Source/ExternalTexture_Metal.mm b/Plugins/ExternalTexture/Source/ExternalTexture_Metal.mm index 3cf175c6a..38ff679f5 100644 --- a/Plugins/ExternalTexture/Source/ExternalTexture_Metal.mm +++ b/Plugins/ExternalTexture/Source/ExternalTexture_Metal.mm @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include "ExternalTexture_Base.h" diff --git a/Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp b/Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp index a3d7e00c1..37b7d4e1a 100644 --- a/Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp +++ b/Plugins/ExternalTexture/Source/ExternalTexture_OpenGL.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include diff --git a/Plugins/NativeCamera/CMakeLists.txt b/Plugins/NativeCamera/CMakeLists.txt index 29f590ec8..a3b73b4fc 100644 --- a/Plugins/NativeCamera/CMakeLists.txt +++ b/Plugins/NativeCamera/CMakeLists.txt @@ -43,7 +43,6 @@ target_link_libraries(NativeCamera PUBLIC napi PRIVATE JsRuntimeInternal PRIVATE GraphicsDeviceContext - PRIVATE napi_extensions ${ADDITIONAL_LIBRARIES}) set_property(TARGET NativeCamera PROPERTY FOLDER Plugins) diff --git a/Plugins/NativeCamera/Source/NativeCamera.cpp b/Plugins/NativeCamera/Source/NativeCamera.cpp index da9da32ba..906da78a8 100644 --- a/Plugins/NativeCamera/Source/NativeCamera.cpp +++ b/Plugins/NativeCamera/Source/NativeCamera.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include "NativeVideo.h" #include "MediaDevices.h" #include "ImageCapture.h" diff --git a/Plugins/NativeCapture/CMakeLists.txt b/Plugins/NativeCapture/CMakeLists.txt index 0ed5d3895..ec57cf6e4 100644 --- a/Plugins/NativeCapture/CMakeLists.txt +++ b/Plugins/NativeCapture/CMakeLists.txt @@ -10,8 +10,7 @@ target_include_directories(NativeCapture PUBLIC target_link_libraries(NativeCapture PUBLIC JsRuntimeInternal - PRIVATE GraphicsDeviceContext - PRIVATE napi_extensions) + PRIVATE GraphicsDeviceContext) set_property(TARGET NativeCapture PROPERTY FOLDER Plugins) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) diff --git a/Plugins/NativeCapture/Source/NativeCapture.cpp b/Plugins/NativeCapture/Source/NativeCapture.cpp index 0fee30fff..06888c384 100644 --- a/Plugins/NativeCapture/Source/NativeCapture.cpp +++ b/Plugins/NativeCapture/Source/NativeCapture.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include #include diff --git a/Plugins/NativeEngine/CMakeLists.txt b/Plugins/NativeEngine/CMakeLists.txt index 454e8b8a8..454687ddc 100644 --- a/Plugins/NativeEngine/CMakeLists.txt +++ b/Plugins/NativeEngine/CMakeLists.txt @@ -41,8 +41,7 @@ target_link_libraries(NativeEngine PRIVATE glslang PRIVATE glslang-default-resource-limits PRIVATE SPIRV - PRIVATE GraphicsDeviceContext - PRIVATE napi_extensions) + PRIVATE GraphicsDeviceContext) warnings_as_errors(NativeEngine) if(TARGET spirv-cross-hlsl) diff --git a/Plugins/NativeEngine/Source/IndexBuffer.h b/Plugins/NativeEngine/Source/IndexBuffer.h index eae42aa68..d680a5217 100644 --- a/Plugins/NativeEngine/Source/IndexBuffer.h +++ b/Plugins/NativeEngine/Source/IndexBuffer.h @@ -16,7 +16,7 @@ namespace Babylon class IndexBuffer final { public: - IndexBuffer(Graphics::DeviceContext& deviceContext, gsl::span bytes, uint16_t flags, bool dynamic); + IndexBuffer(Graphics::DeviceContext& deviceContext, const gsl::span bytes, uint16_t flags, bool dynamic); ~IndexBuffer(); // No copy or move semantics diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 754bca5a5..4f30f4ede 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include @@ -624,6 +624,8 @@ namespace Babylon StaticValue("COMMAND_SETTEXTUREWRAPMODE", Napi::FunctionPointer::Create(env, &NativeEngine::SetTextureWrapMode)), StaticValue("COMMAND_SETTEXTUREANISOTROPICLEVEL", Napi::FunctionPointer::Create(env, &NativeEngine::SetTextureAnisotropicLevel)), StaticValue("COMMAND_SETTEXTURE", Napi::FunctionPointer::Create(env, &NativeEngine::SetTexture)), + StaticValue("COMMAND_UNSETTEXTURE", Napi::FunctionPointer::Create(env, &NativeEngine::UnsetTexture)), + StaticValue("COMMAND_DISCARDALLTEXTURES", Napi::FunctionPointer::Create(env, &NativeEngine::DiscardAllTextures)), StaticValue("COMMAND_BINDVERTEXARRAY", Napi::FunctionPointer::Create(env, &NativeEngine::BindVertexArray)), StaticValue("COMMAND_SETSTATE", Napi::FunctionPointer::Create(env, &NativeEngine::SetState)), StaticValue("COMMAND_SETZOFFSET", Napi::FunctionPointer::Create(env, &NativeEngine::SetZOffset)), @@ -726,6 +728,8 @@ namespace Babylon void NativeEngine::Dispose() { + m_deviceContext.SetRenderResetCallback(nullptr); + m_cancellationSource->cancel(); } @@ -1619,6 +1623,21 @@ namespace Babylon encoder->setTexture(uniformInfo->Stage, uniformInfo->Handle, texture->Handle(), texture->SamplerFlags()); } + void NativeEngine::UnsetTexture(NativeDataStream::Reader& data) + { + bgfx::Encoder* encoder = GetUpdateToken().GetEncoder(); + + const UniformInfo* uniformInfo = data.ReadPointer(); + + encoder->setTexture(uniformInfo->Stage, uniformInfo->Handle, BGFX_INVALID_HANDLE); + } + + void NativeEngine::DiscardAllTextures(NativeDataStream::Reader&) + { + bgfx::Encoder* encoder = GetUpdateToken().GetEncoder(); + encoder->discard(BGFX_DISCARD_BINDINGS); + } + void NativeEngine::DeleteTexture(const Napi::CallbackInfo& info) { Graphics::Texture* texture = info[0].As>().Get(); @@ -2247,10 +2266,9 @@ namespace Babylon encoder->setState((m_engineState & ~BGFX_STATE_WRITE_Z) | fillModeState); } - // stencil boundFrameBuffer.SetStencil(*encoder, m_stencilState); - // Discard everything except bindings since we keep the state of everything else. + // Discard everything except textures since we keep the state of everything else. boundFrameBuffer.Submit(*encoder, m_currentProgram->Handle, BGFX_DISCARD_ALL & ~BGFX_DISCARD_BINDINGS); } diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index d9c5b01c0..f29245de6 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -195,6 +195,8 @@ namespace Babylon void SetTextureWrapMode(NativeDataStream::Reader& data); void SetTextureAnisotropicLevel(NativeDataStream::Reader& data); void SetTexture(NativeDataStream::Reader& data); + void UnsetTexture(NativeDataStream::Reader& data); + void DiscardAllTextures(NativeDataStream::Reader& data); void DeleteTexture(const Napi::CallbackInfo& info); Napi::Value ReadTexture(const Napi::CallbackInfo& info); Napi::Value CreateFrameBuffer(const Napi::CallbackInfo& info); diff --git a/Plugins/NativeEngine/Source/VertexBuffer.h b/Plugins/NativeEngine/Source/VertexBuffer.h index 33df36ba3..185379e7e 100644 --- a/Plugins/NativeEngine/Source/VertexBuffer.h +++ b/Plugins/NativeEngine/Source/VertexBuffer.h @@ -59,7 +59,7 @@ namespace Babylon Handle(Handle&&) noexcept; Handle& operator=(Handle&&) noexcept; - void Update(gsl::span bytes, uint32_t startVertex); + void Update(const gsl::span bytes, uint32_t startVertex); void Set(bgfx::Encoder* encoder, uint8_t stream, uint32_t startVertex, uint32_t numVertices, bgfx::VertexLayoutHandle layoutHandle); diff --git a/Plugins/NativeTracing/CMakeLists.txt b/Plugins/NativeTracing/CMakeLists.txt index cfa8bb047..8cbd591e0 100644 --- a/Plugins/NativeTracing/CMakeLists.txt +++ b/Plugins/NativeTracing/CMakeLists.txt @@ -15,8 +15,7 @@ target_include_directories(NativeTracing PUBLIC "Include") target_link_libraries(NativeTracing PUBLIC napi PRIVATE JsRuntimeInternal - PRIVATE arcana - PRIVATE napi_extensions) + PRIVATE arcana) set_property(TARGET NativeTracing PROPERTY FOLDER Plugins) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) diff --git a/Plugins/NativeTracing/Source/NativeTracing.cpp b/Plugins/NativeTracing/Source/NativeTracing.cpp index c10668b06..e9b0703c0 100644 --- a/Plugins/NativeTracing/Source/NativeTracing.cpp +++ b/Plugins/NativeTracing/Source/NativeTracing.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/Plugins/NativeXr/CMakeLists.txt b/Plugins/NativeXr/CMakeLists.txt index b15a7792c..97fa938a3 100644 --- a/Plugins/NativeXr/CMakeLists.txt +++ b/Plugins/NativeXr/CMakeLists.txt @@ -14,7 +14,6 @@ target_link_libraries(NativeXr PRIVATE bgfx PRIVATE GraphicsDeviceContext PRIVATE JsRuntimeInternal - PRIVATE napi_extensions PRIVATE xr) set_property(TARGET NativeXr PROPERTY FOLDER Plugins) diff --git a/Plugins/NativeXr/Source/NativeXr.cpp b/Plugins/NativeXr/Source/NativeXr.cpp index 5e6592fc0..1d2001fb9 100644 --- a/Plugins/NativeXr/Source/NativeXr.cpp +++ b/Plugins/NativeXr/Source/NativeXr.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include diff --git a/Plugins/TestUtils/Source/TestUtils.cpp b/Plugins/TestUtils/Source/TestUtils.cpp index 0dd6e4150..7ec3ad500 100644 --- a/Plugins/TestUtils/Source/TestUtils.cpp +++ b/Plugins/TestUtils/Source/TestUtils.cpp @@ -27,9 +27,9 @@ namespace Babylon::Plugins::Internal const auto height = info[2].As().Uint32Value(); const auto filename = info[3].As().Utf8Value(); - if (buffer.ByteLength() < (width * height * 4)) + if (buffer.ByteLength() < width * height * 4) { - return; + throw Napi::Error::New(info.Env(), "Buffer byte length is invalid for width and height"); } bx::MemoryBlock mb(&Graphics::DeviceContext::GetDefaultAllocator()); diff --git a/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp b/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp index 70ed42a76..dc208350d 100644 --- a/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp +++ b/Plugins/TestUtils/Source/Win32/TestUtilsImpl.cpp @@ -1,6 +1,8 @@ #include "../TestUtilsImplData.h" #include #include +#include +#include namespace { @@ -31,16 +33,21 @@ namespace Babylon::Plugins::Internal const int32_t width = info[0].As().Int32Value(); const int32_t height = info[1].As().Int32Value(); - auto hwnd = m_implData->m_window; + auto hWnd = m_implData->m_window; RECT rc{0, 0, width, height}; - AdjustWindowRectEx(&rc, GetWindowStyle(hwnd), GetMenu(hwnd) != NULL, GetWindowExStyle(hwnd)); - SetWindowPos(hwnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER); + AdjustWindowRectEx(&rc, GetWindowStyle(hWnd), GetMenu(hWnd) != NULL, GetWindowExStyle(hWnd)); + SetWindowPos(hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER); } void TestUtils::SetTitle(const Napi::CallbackInfo& info) { - const auto title = info[0].As().Utf8Value(); - SetWindowTextA(m_implData->m_window, title.c_str()); + // SetWindowText sends a window message synchronously and cannot be called the JS thread with the way the code + // is currently set up. If the main thread is calling FinishRenderingCurrentFrame, this will hang forever as + // the main message loop is not pumping. Once we fix the rendering code to never block the main thread, this + // threadpool scheduling will no longer be necessary. + arcana::threadpool_scheduler([hWnd = m_implData->m_window, title = info[0].As().Utf8Value()] { + SetWindowTextA(hWnd, title.c_str()); + }); } Napi::Value TestUtils::GetOutputDirectory(const Napi::CallbackInfo& info) diff --git a/Polyfills/Canvas/CMakeLists.txt b/Polyfills/Canvas/CMakeLists.txt index ab9704cbd..9403588ba 100644 --- a/Polyfills/Canvas/CMakeLists.txt +++ b/Polyfills/Canvas/CMakeLists.txt @@ -35,8 +35,7 @@ target_link_libraries(Canvas PRIVATE JsRuntimeInternal PRIVATE GraphicsDeviceContext PRIVATE UrlLib - PRIVATE base-n - PRIVATE napi_extensions) + PRIVATE base-n) set_property(TARGET Canvas PROPERTY FOLDER Polyfills) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCES}) diff --git a/Polyfills/Canvas/Source/Canvas.cpp b/Polyfills/Canvas/Source/Canvas.cpp index b84d8bf27..03d9eb02f 100644 --- a/Polyfills/Canvas/Source/Canvas.cpp +++ b/Polyfills/Canvas/Source/Canvas.cpp @@ -2,7 +2,7 @@ #include "Image.h" #include "Context.h" #include -#include +#include #include #include "Colors.h" diff --git a/Polyfills/Canvas/Source/Image.cpp b/Polyfills/Canvas/Source/Image.cpp index 9ec91a8c3..89dd3da86 100644 --- a/Polyfills/Canvas/Source/Image.cpp +++ b/Polyfills/Canvas/Source/Image.cpp @@ -10,7 +10,7 @@ #include #include "nanovg.h" #include -#include +#include #include namespace Babylon::Polyfills::Internal