From d7e1d1613f483d88a1a165c6dce9e2fb16c86de1 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 24 Feb 2023 11:21:45 -0800 Subject: [PATCH 01/24] Preliminary testing code. --- Apps/Playground/Scripts/experience.js | 424 ++++---------------------- 1 file changed, 66 insertions(+), 358 deletions(-) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index 852b6aa62..eb62b0e5d 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -3,382 +3,90 @@ /// /// -var wireframe = false; -var turntable = false; -var logfps = true; -var ibl = false; -var rtt = false; -var vr = false; -var ar = false; -var xrHitTest = false; -var xrFeaturePoints = false; -var meshDetection = false; -var text = false; -var hololens = false; -var cameraTexture = false; -var imageTracking = false; -const readPixels = false; +const url1 = "https://i.imgur.com/B93vzrL.jpeg"; -function CreateBoxAsync(scene) { - BABYLON.Mesh.CreateBox("box1", 0.2, scene); - return Promise.resolve(); +const vertexShaderCode = ` +uniform mat4 worldViewProjection; +uniform vec2 meshSize; + +attribute vec3 position; + +varying vec2 global_positionNormalized; + +void main() +{ + global_positionNormalized=(position.xy/meshSize.xy)+vec2(0.5,0.5); + gl_Position=worldViewProjection*vec4(position,1.0); } +`; -function CreateSpheresAsync(scene) { - var size = 12; - for (var i = 0; i < size; i++) { - for (var j = 0; j < size; j++) { - for (var k = 0; k < size; k++) { - var sphere = BABYLON.Mesh.CreateSphere("sphere" + i + j + k, 32, 0.9, scene); - sphere.position.x = i; - sphere.position.y = j; - sphere.position.z = k; - } - } - } +const fragmentShaderCode = ` +varying vec2 global_positionNormalized; + +uniform sampler2D mediaTexture0; +void main() +{ +gl_FragColor=texture(mediaTexture0,global_positionNormalized); +} +`; + +let populateScene = function (scene) { + engine.setHardwareScalingLevel(1 / window.devicePixelRatio); + let meshSize = new BABYLON.Vector2(0.8, 0.8); + let texture1 = new BABYLON.Texture(url1, scene); + let material = new BABYLON.ShaderMaterial( + "customMaterial", + scene, + { + vertexSource: vertexShaderCode, + fragmentSource: fragmentShaderCode, + }, + { + attributes: ["position"], + uniforms: ["worldViewProjection","meshSize"], + samplers: ["mediaTexture0"], + } + ); + material.setVector2("meshSize", meshSize); + material.setTexture("mediaTexture0", texture1); + let rectMesh = BABYLON.MeshBuilder.CreatePlane( + "rectMesh", + { width: meshSize.x, height: meshSize.y }, + scene + ); + rectMesh.setMaterialById(material.id); + return scene; +}; + +function StartAsync(scene) { return Promise.resolve(); } var engine = new BABYLON.NativeEngine(); var scene = new BABYLON.Scene(engine); -CreateBoxAsync(scene).then(function () { -//CreateSpheresAsync(scene).then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Box/glTF/Box.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxTextured/glTF/BoxTextured.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Suzanne/glTF/Suzanne.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Avocado/glTF/Avocado.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoomBox/glTF/BoomBox.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Sponza/glTF/Sponza.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BrainStem/glTF/BrainStem.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/FlightHelmet/glTF/FlightHelmet.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/EnvironmentTest/glTF/EnvironmentTest.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxAnimated/glTF/BoxAnimated.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/AnimatedMorphCube/glTF/AnimatedMorphCube.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/RiggedSimple/glTF/RiggedSimple.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/stevk/glTF-Asset-Generator/skins/Output/Animation_Skin/Animation_Skin_01.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/RiggedFigure/glTF/RiggedFigure.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/CesiumMan/glTF/CesiumMan.gltf").then(function () { -//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/ClearCoatTest/glTF/ClearCoatTest.gltf").then(function () { - BABYLON.Tools.Log("Loaded"); +StartAsync(scene).then(function () { + try { + populateScene(scene); + } catch (e) { + BABYLON.Tools.Log(e.message); + } + BABYLON.Tools.Log("Loaded"); // This creates and positions a free camera (non-mesh) scene.createDefaultCamera(true, true, true); - scene.activeCamera.alpha += Math.PI; - - if (ibl) { - scene.createDefaultEnvironment({ createGround: false, createSkybox: false }); - } - else { - scene.createDefaultLight(true); - } - - if (cameraTexture) { - scene.activeCamera.position.set(0, 1, -10); - scene.activeCamera.setTarget(new BABYLON.Vector3(0, 1, 0)); - - scene.meshes[0].setEnabled(false); - var plane = BABYLON.MeshBuilder.CreatePlane("plane", {size: 1, sideOrientation: BABYLON.Mesh.DOUBLESIDE}); - plane.rotation.y = Math.PI; - plane.rotation.z = Math.PI; - plane.position.y = 1; - - var mat = new BABYLON.StandardMaterial("mat", scene); - mat.diffuseColor = BABYLON.Color3.Black(); + scene.createDefaultLight(true); - var tex = BABYLON.VideoTexture.CreateFromWebCam(scene, function(videoTexture) { - const videoSize = videoTexture.getSize(); - mat.emissiveTexture = videoTexture; - plane.material = mat; - plane.scaling.x = 5; - plane.scaling.y = 5 * (videoSize.height / videoSize.width); - console.log("Video texture size: " + videoSize); - }, { maxWidth: 1280, maxHeight: 720, facingMode: 'environment'}); + engine.runRenderLoop(function () { + try { + scene.render(); + } catch (e) { + BABYLON.Tools.Log(e.message); } - - if (readPixels) { - const texture = new BABYLON.Texture("https://assets.babylonjs.com/textures/earth.jpg", scene); - texture.onLoadObservable.addOnce(() => { - const mip = 1; - const textureWidth = texture.getSize().width >> mip; - const textureHeight = texture.getSize().height >> mip; - const x = textureWidth / 4; - const y = textureHeight / 4; - const width = textureWidth / 2; - const height = textureHeight / 2; - // This read will create a new buffer. - texture.readPixels(undefined, mip, undefined, undefined, undefined, x, y, width, height).then((buffer) => { - console.log(`Read ${buffer.byteLength} pixel bytes.`); - return buffer; - }) - .then(buffer => { - // This read reuses the existing buffer. - texture.readPixels(undefined, mip, buffer, undefined, undefined, x, y, width, height).then((buffer) => { - console.log(`Read ${buffer.byteLength} pixel bytes.`); - }); - }); - }); - } - - if (wireframe) { - var material = new BABYLON.StandardMaterial("wireframe", scene); - material.wireframe = true; - material.pointsCloud = true; - - for (var index = 0; index < scene.meshes.length; index++) { - scene.meshes[0].material = material; - } - } - - if (rtt) { - var rttTexture = new BABYLON.RenderTargetTexture("rtt", 1024, scene); - scene.meshes.forEach(mesh => { - rttTexture.renderList.push(mesh); - }); - rttTexture.activeCamera = scene.activeCamera; - rttTexture.vScale = -1; - - scene.customRenderTargets.push(rttTexture); - - var rttMaterial = new BABYLON.StandardMaterial("rttMaterial", scene); - rttMaterial.diffuseTexture = rttTexture; - - var plane = BABYLON.MeshBuilder.CreatePlane("rttPlane", { width: 4, height: 4 }, scene); - plane.position.y = 1; - plane.position.z = -5; - plane.rotation.y = Math.PI; - plane.material = rttMaterial; - } - - if (turntable) { - scene.beforeRender = function () { - scene.meshes[0].rotate(BABYLON.Vector3.Up(), 0.005 * scene.getAnimationRatio()); - }; - } - - if (logfps) { - var logFpsLoop = function () { - BABYLON.Tools.Log("FPS: " + Math.round(engine.getFps())); - window.setTimeout(logFpsLoop, 1000); - }; - - window.setTimeout(logFpsLoop, 3000); - } - - engine.runRenderLoop(function () { - scene.render(); }); - if (vr || ar || hololens) { - setTimeout(function () { - scene.createDefaultXRExperienceAsync({ disableDefaultUI: true, disableTeleportation: true }).then((xr) => { - if (xrHitTest) { - // Create the hit test module. OffsetRay specifies the target direction, and entityTypes can be any combination of "mesh", "plane", and "point". - const xrHitTestModule = xr.baseExperience.featuresManager.enableFeature( - BABYLON.WebXRFeatureName.HIT_TEST, - "latest", - { offsetRay: { origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: 0, z: -1 } }, entityTypes: ["mesh"] }); - - // When we receive hit test results, if there were any valid hits move the position of the mesh to the hit test point. - xrHitTestModule.onHitTestResultObservable.add((results) => { - if (results.length) { - scene.meshes[0].position.x = results[0].position.x; - scene.meshes[0].position.y = results[0].position.y; - scene.meshes[0].position.z = results[0].position.z; - } - }); - } - else { - setTimeout(function () { - scene.meshes[0].position.z = 2; - scene.meshes[0].rotate(BABYLON.Vector3.Up(), 3.14159); - }, 5000); - } - - // Showing visualization for ARKit LiDAR mesh data - if (meshDetection) { - var mat = new BABYLON.StandardMaterial("mat", scene); - mat.wireframe = true; - mat.diffuseColor = BABYLON.Color3.Blue(); - const xrMeshes = xr.baseExperience.featuresManager.enableFeature( - BABYLON.WebXRFeatureName.MESH_DETECTION, - "latest", - {convertCoordinateSystems: true}); - console.log("Enabled mesh detection."); - const meshMap = new Map(); - - // adding meshes - xrMeshes.onMeshAddedObservable.add(mesh=> { - try { - console.log("Mesh added."); - // create new mesh object - var customMesh = new BABYLON.Mesh("custom", scene); - var vertexData = new BABYLON.VertexData(); - vertexData.positions = mesh.positions; - vertexData.indices = mesh.indices; - vertexData.normals = mesh.normals; - vertexData.applyToMesh(customMesh, true); - customMesh.material = mat; - // add mesh and mesh id to map - meshMap.set(mesh.id, customMesh); - } catch (ex) { - console.error(ex); - } - }); - - // updating meshes - xrMeshes.onMeshUpdatedObservable.add(mesh=> { - try { - console.log("Mesh updated."); - if (meshMap.has(mesh.id)) { - var vertexData = new BABYLON.VertexData(); - vertexData.positions = mesh.positions; - vertexData.indices = mesh.indices; - vertexData.normals = mesh.normals; - vertexData.applyToMesh(meshMap.get(mesh.id), true); - } - } catch (ex) { - console.error(ex); - } - }); - - // removing meshes - xrMeshes.onMeshRemovedObservable.add(mesh => { - try { - console.log("Mesh removed."); - if (meshMap.has(mesh.id)) { - meshMap.get(mesh.id).dispose(); - meshMap.delete(mesh.id); - } - } catch (ex) { - console.error(ex); - } - }); - } - - // Below is an example of how to process feature points. - if (xrFeaturePoints) { - // First we attach the feature point system feature the XR experience. - const xrFeaturePointsModule = xr.baseExperience.featuresManager.enableFeature( - BABYLON.WebXRFeatureName.FEATURE_POINTS, - "latest", - {}); - - // Next We create the point cloud system which we will use to display feature points. - var pcs = new BABYLON.PointsCloudSystem("pcs", 5, scene); - var featurePointInitFunc = function (particle, i, s) { - particle.position = new BABYLON.Vector3(0, -5, 0); - } - - // Add some starting points and build the mesh. - pcs.addPoints(3000, featurePointInitFunc); - pcs.buildMeshAsync().then((mesh) => { - mesh.alwaysSelectAsActiveMesh = true; - }); - - // Define the logic for how to display a particular particle in the particle system - // which represents a feature point. - pcs.updateParticle = function (particle) { - // Grab the feature point cloud from the xrFeaturePointsModule. - var featurePointCloud = xrFeaturePointsModule.featurePointCloud; - - // Find the index of this particle in the particle system. If there exists a - // mapping to a feature point then display its position, otherwise hide it somewhere far away. - var index = particle.idx; - if (index >= featurePointCloud.length) { - // Hide the particle not currently in use. - particle.position = new BABYLON.Vector3(-100, -100, -100); - } - else { - // To display a feature point set its position to the position of the feature point - // and set its color on the scale from white->red where white = least confident and - // red = most confident. - particle.position = featurePointCloud[index].position; - particle.color = new BABYLON.Color4(1, 1 - featurePointCloud[index].confidenceValue, 1 - featurePointCloud[index].confidenceValue, 1); - } - - return particle; - } - - // Listen for changes in feature points both being added and updated, and only update - // our display every 60 changes to the feature point cloud to avoid slowdowns. - var featurePointChangeCounter = 0; - xrFeaturePointsModule.onFeaturePointsAddedObservable.add((addedPointIds) => { - if (++featurePointChangeCounter % 60 == 0) { - pcs.setParticles(); - } - }); - - xrFeaturePointsModule.onFeaturePointsUpdatedObservable.add((updatedPointIds) => { - if (++featurePointChangeCounter % 60 == 0) { - pcs.setParticles(); - } - }); - } - - let sessionMode = vr ? "immersive-vr" : "immersive-ar" - if (hololens) { - // Because HoloLens 2 is a head mounted display, its Babylon.js immersive experience more closely aligns to vr - sessionMode = "immersive-vr"; - - // Below is an example for enabling hand tracking. The code is not unique to HoloLens 2, and may be reused for other WebXR hand tracking enabled devices. - xr.baseExperience.featuresManager.enableFeature( - BABYLON.WebXRFeatureName.HAND_TRACKING, - "latest", - { xrInput: xr.input }); - } - - // Test image tracking and detection. - // To test image tracking locally either bring up the images below on your machine by loading the URL or by printing them out. - // Then gain tracking on them during the AR Session by orienting your camera towards the image, tracking will be represented by a colored cube at the center of the image. - if (imageTracking) { - const webXRTrackingMeshes = []; - const webXRImageTrackingModule = xr.baseExperience.featuresManager.enableFeature( - BABYLON.WebXRFeatureName.IMAGE_TRACKING, - "latest", - { - images: [ - { src: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/IridescentDishWithOlives/screenshot/screenshot_Large.jpg", estimatedRealWorldWidth: .2 }, - { src: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/DragonAttenuation/screenshot/screenshot_large.png", estimatedRealWorldWidth: .2 }, - ]}); - - webXRImageTrackingModule.onTrackedImageUpdatedObservable.add((imageObject) => { - if (webXRTrackingMeshes[imageObject.id] === undefined) { - webXRTrackingMeshes[imageObject.id] = BABYLON.Mesh.CreateBox("box1", 0.05, scene); - const mat = new BABYLON.StandardMaterial("mat", scene); - mat.diffuseColor = BABYLON.Color3.Random(); - webXRTrackingMeshes[imageObject.id].material = mat; - } - webXRTrackingMeshes[imageObject.id].setEnabled(!imageObject.emulated); - imageObject.transformationMatrix.decomposeToTransformNode(webXRTrackingMeshes[imageObject.id]); - }); - } - - xr.baseExperience.enterXRAsync(sessionMode, "unbounded", xr.renderTarget).then((xrSessionManager) => { - if (hololens) { - // Pass through, head mounted displays (HoloLens 2) require autoClear and a black clear color - xrSessionManager.scene.autoClear = true; - xrSessionManager.scene.clearColor = new BABYLON.Color4(0, 0, 0, 0); - } - }); - }); - }, 5000); - } - - if (text) { - var Writer = BABYLON.MeshWriter(scene, { scale: 1.0, defaultFont: "Arial" }); - new Writer( - "Lorem ipsum dolor sit amet...", - { - "anchor": "center", - "letter-height": 5, - "color": "#FF0000" - } - ); - } - }, function (ex) { console.log(ex.message, ex.stack); }); From 143164a314410550146bc41b0ae16c9cd9948375 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 24 Feb 2023 11:48:22 -0800 Subject: [PATCH 02/24] Code for measuring render loop times. --- Apps/Playground/Scripts/experience.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index eb62b0e5d..e30361694 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -20,7 +20,8 @@ void main() } `; -const fragmentShaderCode = ` +function getFragmentShaderCode() { + return ` varying vec2 global_positionNormalized; uniform sampler2D mediaTexture0; @@ -30,9 +31,12 @@ void main() gl_FragColor=texture(mediaTexture0,global_positionNormalized); } `; +} let populateScene = function (scene) { - engine.setHardwareScalingLevel(1 / window.devicePixelRatio); + if (window) { + engine.setHardwareScalingLevel(1 / window.devicePixelRatio); + } let meshSize = new BABYLON.Vector2(0.8, 0.8); let texture1 = new BABYLON.Texture(url1, scene); let material = new BABYLON.ShaderMaterial( @@ -40,7 +44,7 @@ let populateScene = function (scene) { scene, { vertexSource: vertexShaderCode, - fragmentSource: fragmentShaderCode, + fragmentSource: getFragmentShaderCode(), }, { attributes: ["position"], @@ -65,6 +69,7 @@ function StartAsync(scene) { var engine = new BABYLON.NativeEngine(); var scene = new BABYLON.Scene(engine); +var lastRenderTime = -1; StartAsync(scene).then(function () { try { @@ -82,6 +87,12 @@ StartAsync(scene).then(function () { engine.runRenderLoop(function () { try { scene.render(); + var renderTime = new Date().valueOf(); + var span = renderTime - lastRenderTime; + if (lastRenderTime >= 0) { + console.log(`${span} ms since last render${span>33?" - very long!":""}.`); + } + lastRenderTime = renderTime; } catch (e) { BABYLON.Tools.Log(e.message); } From 6cad4f6c27e07ad9fe0c6f8185e1aa01eff535c3 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 24 Feb 2023 13:26:52 -0800 Subject: [PATCH 03/24] Fix test code. --- Apps/Playground/Scripts/experience.js | 111 +++++++++++++++++++------- 1 file changed, 83 insertions(+), 28 deletions(-) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index e30361694..fddf10c8e 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -3,8 +3,17 @@ /// /// + const url1 = "https://i.imgur.com/B93vzrL.jpeg"; +function glslFloat(float) { + return ` ${float.toString(10)}${float % 1 === 0 ? '.' : ''} `; +} + +function randomGlslFloat() { + return glslFloat(Math.random()); +} + const vertexShaderCode = ` uniform mat4 worldViewProjection; uniform vec2 meshSize; @@ -21,26 +30,54 @@ void main() `; function getFragmentShaderCode() { + var ifs = []; + for (var i = 0; i < 30; i++) { + ifs.push(`if(col.x${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()} && col.y${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()} && col.z${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()}) +{ +return vec4(${randomGlslFloat()},${randomGlslFloat()},${randomGlslFloat()},1.); +}`); + } return ` varying vec2 global_positionNormalized; uniform sampler2D mediaTexture0; +vec4 getColor() +{ +vec4 col=texture(mediaTexture0,global_positionNormalized); +${ifs.join("\nelse ")} +return vec4(${randomGlslFloat()},${randomGlslFloat()},${randomGlslFloat()},1.); +} + void main() { -gl_FragColor=texture(mediaTexture0,global_positionNormalized); +gl_FragColor=getColor(); } `; } -let populateScene = function (scene) { - if (window) { - engine.setHardwareScalingLevel(1 / window.devicePixelRatio); +var meshSize = new BABYLON.Vector2(0.8, 0.8); +var engine; +var scene; +var texture; +var material; +var waitingMaterial; +var rectMesh; +var lastRefreshTime = -1; + +let refreshShader = function () { + console.log(`Start refresh shader.`); + lastRefreshTime = new Date().valueOf(); + var oldMaterial = material; + material = waitingMaterial; + if (material) { + rectMesh.setMaterialById(material.id); + } + if (oldMaterial && oldMaterial!=material) { + oldMaterial.dispose(true); } - let meshSize = new BABYLON.Vector2(0.8, 0.8); - let texture1 = new BABYLON.Texture(url1, scene); - let material = new BABYLON.ShaderMaterial( - "customMaterial", + waitingMaterial = new BABYLON.ShaderMaterial( + `customMaterial${lastRefreshTime.toString(36)}${Math.random().toString(36)}`, scene, { vertexSource: vertexShaderCode, @@ -48,55 +85,73 @@ let populateScene = function (scene) { }, { attributes: ["position"], - uniforms: ["worldViewProjection","meshSize"], + uniforms: ["worldViewProjection", "meshSize"], samplers: ["mediaTexture0"], - } + }, + false ); - material.setVector2("meshSize", meshSize); - material.setTexture("mediaTexture0", texture1); - let rectMesh = BABYLON.MeshBuilder.CreatePlane( + waitingMaterial.setVector2("meshSize", meshSize); + waitingMaterial.setTexture("mediaTexture0", texture); + if (!material) { + material = waitingMaterial; + if (material) { + rectMesh.setMaterialById(material.id); + } + } +} + +function populateScene() { + if (window) { + engine.setHardwareScalingLevel(1 / window.devicePixelRatio); + } + texture = new BABYLON.Texture(url1, scene); + rectMesh = BABYLON.MeshBuilder.CreatePlane( "rectMesh", { width: meshSize.x, height: meshSize.y }, scene ); - rectMesh.setMaterialById(material.id); - return scene; + refreshShader(); }; -function StartAsync(scene) { +function StartAsync() { return Promise.resolve(); } -var engine = new BABYLON.NativeEngine(); -var scene = new BABYLON.Scene(engine); +engine = new BABYLON.NativeEngine(); +scene = new BABYLON.Scene(engine); var lastRenderTime = -1; StartAsync(scene).then(function () { try { - populateScene(scene); + populateScene(); } catch (e) { BABYLON.Tools.Log(e.message); } BABYLON.Tools.Log("Loaded"); // This creates and positions a free camera (non-mesh) - scene.createDefaultCamera(true, true, true); + scene.createDefaultCamera(true, true, true); - scene.createDefaultLight(true); + scene.createDefaultLight(true); engine.runRenderLoop(function () { try { - scene.render(); - var renderTime = new Date().valueOf(); - var span = renderTime - lastRenderTime; - if (lastRenderTime >= 0) { - console.log(`${span} ms since last render${span>33?" - very long!":""}.`); + if (material && material.isReady()) { + scene.render(); + var renderTime = new Date().valueOf(); + var span = renderTime - lastRenderTime; + if (lastRenderTime >= 0) { + console.log(`${span} ms since last render${span>33?" - too long!":""}.`); + } + lastRenderTime = renderTime; + if (lastRefreshTime>=0 && renderTime-lastRefreshTime > 1000) { + refreshShader(); + } } - lastRenderTime = renderTime; } catch (e) { BABYLON.Tools.Log(e.message); } - }); + }); }, function (ex) { console.log(ex.message, ex.stack); From 8460274052b8cee47472c3850daa636e9e04a797 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Tue, 28 Feb 2023 11:15:33 -0800 Subject: [PATCH 04/24] Refactor experience.js. --- Apps/Playground/Scripts/experience.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index fddf10c8e..0332f9eb1 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -3,9 +3,14 @@ /// /// - +const fragmentShaderComplexity = 30; const url1 = "https://i.imgur.com/B93vzrL.jpeg"; +function randomCharacter(str) { + if (!str) { return ""; } + return str[Math.floor(Math.random()*str.length)]; +} + function glslFloat(float) { return ` ${float.toString(10)}${float % 1 === 0 ? '.' : ''} `; } @@ -31,8 +36,8 @@ void main() function getFragmentShaderCode() { var ifs = []; - for (var i = 0; i < 30; i++) { - ifs.push(`if(col.x${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()} && col.y${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()} && col.z${"<>"[Math.floor(Math.random() * 2)]}${randomGlslFloat()}) + for (var i = 0; i < fragmentShaderComplexity; ++i) { + ifs.push(`if(col.x${randomCharacter("<>")}${randomGlslFloat()} && col.y${randomCharacter("<>")}${randomGlslFloat()} && col.z${randomCharacter("<>")}${randomGlslFloat()}) { return vec4(${randomGlslFloat()},${randomGlslFloat()},${randomGlslFloat()},1.); }`); From 38c273a8293329fd7877a7d886ec3ab41a72af60 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Tue, 28 Feb 2023 12:59:55 -0800 Subject: [PATCH 05/24] Preliminary code to separate out shader compilation from CreateProgram. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 37 ++++++++++++++------ Plugins/NativeEngine/Source/NativeEngine.h | 1 + 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index ae5dde25b..d27fc2220 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -685,21 +685,17 @@ namespace Babylon return vertexSource; } - Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) + void NativeEngine::CreateProgramSynchronous(const std::string vertexSource, const std::string fragmentSource, ProgramData& program, Napi::FunctionReference onSuccess, Napi::FunctionReference onError) { - const std::string vertexSource = info[0].As().Utf8Value(); - const std::string fragmentSource = info[1].As().Utf8Value(); - - ProgramData* program = new ProgramData{}; ShaderCompiler::BgfxShaderInfo shaderInfo{}; try { shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); } - catch (const std::exception& ex) + catch (const std::exception&) { - throw Napi::Error::New(info.Env(), ex.what()); + onError.Call({}); } static auto InitUniformInfos{ @@ -721,13 +717,32 @@ namespace Babylon }}; auto vertexShader = bgfx::createShader(bgfx::copy(shaderInfo.VertexBytes.data(), static_cast(shaderInfo.VertexBytes.size()))); - InitUniformInfos(vertexShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); - program->VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); + InitUniformInfos(vertexShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); + program.VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); - InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); + InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); - program->Handle = bgfx::createProgram(vertexShader, fragmentShader, true); + program.Handle = bgfx::createProgram(vertexShader, fragmentShader, true); + onSuccess.Call({}); + } + + Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) + { + const std::string vertexSource = info[0].As().Utf8Value(); + const std::string fragmentSource = info[1].As().Utf8Value(); + const bool isAsync = info[2].As().Value(); + const Napi::Function onSuccess = info[3].As(); + const Napi::Function onError = info[4].As(); + ProgramData* program = new ProgramData{}; + if (isAsync) + { + CreateProgramSynchronous(vertexSource, fragmentSource, *program, Napi::Persistent(onSuccess), Napi::Persistent(onError)); + } + else + { + CreateProgramSynchronous(vertexSource, fragmentSource, *program, Napi::Persistent(onSuccess), Napi::Persistent(onError)); + } return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); } diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 1b86b577d..3223db689 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -120,6 +120,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); + void CreateProgramSynchronous(const std::string vertexSource, const std::string fragmentSource, ProgramData& program, Napi::FunctionReference onSuccess, Napi::FunctionReference onError); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); Napi::Value GetAttributes(const Napi::CallbackInfo& info); From 9257652ca7b38945749169fb5db6d7d909b235e8 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 1 Mar 2023 14:44:10 -0800 Subject: [PATCH 06/24] Force shader compilation in the test code. --- Apps/Playground/Scripts/experience.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index 0332f9eb1..700b0dee5 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -95,6 +95,7 @@ let refreshShader = function () { }, false ); + waitingMaterial.forceCompilationAsync(rectMesh); waitingMaterial.setVector2("meshSize", meshSize); waitingMaterial.setTexture("mediaTexture0", texture); if (!material) { From 1f04b1ccb856b03318ccfbdea5636140b6ba6c67 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 1 Mar 2023 15:28:56 -0800 Subject: [PATCH 07/24] Make async code work + rename function. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 41 +++++++++++++------- Plugins/NativeEngine/Source/NativeEngine.h | 2 +- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index d27fc2220..4bfaf9c09 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -685,18 +685,9 @@ namespace Babylon return vertexSource; } - void NativeEngine::CreateProgramSynchronous(const std::string vertexSource, const std::string fragmentSource, ProgramData& program, Napi::FunctionReference onSuccess, Napi::FunctionReference onError) + void NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) { - ShaderCompiler::BgfxShaderInfo shaderInfo{}; - - try - { - shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); - } - catch (const std::exception&) - { - onError.Call({}); - } + ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); static auto InitUniformInfos{ [](bgfx::ShaderHandle shader, const std::unordered_map& uniformStages, std::unordered_map& uniformInfos, std::unordered_map& uniformNameToIndex) { @@ -724,7 +715,6 @@ namespace Babylon InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); program.Handle = bgfx::createProgram(vertexShader, fragmentShader, true); - onSuccess.Call({}); } Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) @@ -737,11 +727,34 @@ namespace Babylon ProgramData* program = new ProgramData{}; if (isAsync) { - CreateProgramSynchronous(vertexSource, fragmentSource, *program, Napi::Persistent(onSuccess), Napi::Persistent(onError)); + arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() + { + CreateProgramInternal(vertexSource, fragmentSource, *program); + }) + .then(m_runtimeScheduler, *m_cancellationSource, [onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) + { + if (result.has_error()) + { + onErrorRef.Call({}); + } + else + { + onSuccessRef.Call({}); + } + }); } else { - CreateProgramSynchronous(vertexSource, fragmentSource, *program, Napi::Persistent(onSuccess), Napi::Persistent(onError)); + try + { + CreateProgramInternal(vertexSource, fragmentSource, *program); + onSuccess.Call({}); + } + catch (const std::exception&) + { + onError.Call({}); + } } return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); } diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 3223db689..d0700c261 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -120,7 +120,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); - void CreateProgramSynchronous(const std::string vertexSource, const std::string fragmentSource, ProgramData& program, Napi::FunctionReference onSuccess, Napi::FunctionReference onError); + void CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); Napi::Value GetAttributes(const Napi::CallbackInfo& info); From ec25c681ba5f8c89526d390336de06718d1e5aea Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 1 Mar 2023 16:14:31 -0800 Subject: [PATCH 08/24] Separate synchronous and asynchronous versions of CreateProgram. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 58 ++++++++++---------- Plugins/NativeEngine/Source/NativeEngine.h | 1 + 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 4bfaf9c09..5fd6ca12f 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -456,6 +456,7 @@ namespace Babylon InstanceMethod("updateDynamicVertexBuffer", &NativeEngine::UpdateDynamicVertexBuffer), InstanceMethod("createProgram", &NativeEngine::CreateProgram), + InstanceMethod("createProgramAsync", &NativeEngine::CreateProgramAsync), InstanceMethod("getUniforms", &NativeEngine::GetUniforms), InstanceMethod("getAttributes", &NativeEngine::GetAttributes), @@ -721,41 +722,40 @@ namespace Babylon { const std::string vertexSource = info[0].As().Utf8Value(); const std::string fragmentSource = info[1].As().Utf8Value(); - const bool isAsync = info[2].As().Value(); - const Napi::Function onSuccess = info[3].As(); - const Napi::Function onError = info[4].As(); ProgramData* program = new ProgramData{}; - if (isAsync) + try { - arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() - { - CreateProgramInternal(vertexSource, fragmentSource, *program); - }) - .then(m_runtimeScheduler, *m_cancellationSource, [onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) - { - if (result.has_error()) - { - onErrorRef.Call({}); - } - else - { - onSuccessRef.Call({}); - } - }); + CreateProgramInternal(vertexSource, fragmentSource, *program); } - else + catch (const std::exception& ex) { - try + throw Napi::Error::New(info.Env(), ex.what()); + } + return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + } + + Napi::Value NativeEngine::CreateProgramAsync(const Napi::CallbackInfo& info) + { + const std::string vertexSource = info[0].As().Utf8Value(); + const std::string fragmentSource = info[1].As().Utf8Value(); + const Napi::Function onSuccess = info[2].As(); + const Napi::Function onError = info[3].As(); + ProgramData* program = new ProgramData{}; + arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() { CreateProgramInternal(vertexSource, fragmentSource, *program); - onSuccess.Call({}); - } - catch (const std::exception&) - { - onError.Call({}); - } - } + }) + .then(m_runtimeScheduler, *m_cancellationSource, [onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) + { + if (result.has_error()) + { + onErrorRef.Call({}); + } + else + { + onSuccessRef.Call({}); + } }); return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); } diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index d0700c261..78d7b1c02 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -122,6 +122,7 @@ namespace Babylon void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); void CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); Napi::Value CreateProgram(const Napi::CallbackInfo& info); + Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); Napi::Value GetAttributes(const Napi::CallbackInfo& info); void SetProgram(NativeDataStream::Reader& data); From f814deba0cc597d1fef332b81492ea68ead0308a Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 3 Mar 2023 14:01:31 -0800 Subject: [PATCH 09/24] Assign the program handle on the Javascript thread. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 13 +++++++------ Plugins/NativeEngine/Source/NativeEngine.h | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 5fd6ca12f..3713f235e 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -686,7 +686,7 @@ namespace Babylon return vertexSource; } - void NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) + bgfx::ProgramHandle NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) { ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); @@ -715,7 +715,7 @@ namespace Babylon auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); - program.Handle = bgfx::createProgram(vertexShader, fragmentShader, true); + return bgfx::createProgram(vertexShader, fragmentShader, true); } Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) @@ -725,7 +725,7 @@ namespace Babylon ProgramData* program = new ProgramData{}; try { - CreateProgramInternal(vertexSource, fragmentSource, *program); + program->Handle = CreateProgramInternal(vertexSource, fragmentSource, *program); } catch (const std::exception& ex) { @@ -742,11 +742,11 @@ namespace Babylon const Napi::Function onError = info[3].As(); ProgramData* program = new ProgramData{}; arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> bgfx::ProgramHandle { - CreateProgramInternal(vertexSource, fragmentSource, *program); + return CreateProgramInternal(vertexSource, fragmentSource, *program); }) - .then(m_runtimeScheduler, *m_cancellationSource, [onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) + .then(m_runtimeScheduler, *m_cancellationSource, [program, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) { if (result.has_error()) { @@ -754,6 +754,7 @@ namespace Babylon } else { + program->Handle = result.value(); onSuccessRef.Call({}); } }); return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 78d7b1c02..0d2067007 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -120,7 +120,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); - void CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); + bgfx::ProgramHandle CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); From 77f5c19dc367ddabe11040b34111850a70319a0f Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 3 Mar 2023 15:00:25 -0800 Subject: [PATCH 10/24] Restore original experience.js. --- Apps/Playground/Scripts/experience.js | 490 +++++++++++++++++++------- 1 file changed, 355 insertions(+), 135 deletions(-) diff --git a/Apps/Playground/Scripts/experience.js b/Apps/Playground/Scripts/experience.js index 700b0dee5..852b6aa62 100644 --- a/Apps/Playground/Scripts/experience.js +++ b/Apps/Playground/Scripts/experience.js @@ -3,161 +3,381 @@ /// /// -const fragmentShaderComplexity = 30; -const url1 = "https://i.imgur.com/B93vzrL.jpeg"; +var wireframe = false; +var turntable = false; +var logfps = true; +var ibl = false; +var rtt = false; +var vr = false; +var ar = false; +var xrHitTest = false; +var xrFeaturePoints = false; +var meshDetection = false; +var text = false; +var hololens = false; +var cameraTexture = false; +var imageTracking = false; +const readPixels = false; -function randomCharacter(str) { - if (!str) { return ""; } - return str[Math.floor(Math.random()*str.length)]; +function CreateBoxAsync(scene) { + BABYLON.Mesh.CreateBox("box1", 0.2, scene); + return Promise.resolve(); } -function glslFloat(float) { - return ` ${float.toString(10)}${float % 1 === 0 ? '.' : ''} `; -} +function CreateSpheresAsync(scene) { + var size = 12; + for (var i = 0; i < size; i++) { + for (var j = 0; j < size; j++) { + for (var k = 0; k < size; k++) { + var sphere = BABYLON.Mesh.CreateSphere("sphere" + i + j + k, 32, 0.9, scene); + sphere.position.x = i; + sphere.position.y = j; + sphere.position.z = k; + } + } + } -function randomGlslFloat() { - return glslFloat(Math.random()); + return Promise.resolve(); } -const vertexShaderCode = ` -uniform mat4 worldViewProjection; -uniform vec2 meshSize; +var engine = new BABYLON.NativeEngine(); +var scene = new BABYLON.Scene(engine); -attribute vec3 position; +CreateBoxAsync(scene).then(function () { +//CreateSpheresAsync(scene).then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Box/glTF/Box.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxTextured/glTF/BoxTextured.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Suzanne/glTF/Suzanne.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Avocado/glTF/Avocado.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoomBox/glTF/BoomBox.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Sponza/glTF/Sponza.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BrainStem/glTF/BrainStem.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/FlightHelmet/glTF/FlightHelmet.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/EnvironmentTest/glTF/EnvironmentTest.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/BoxAnimated/glTF/BoxAnimated.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/AnimatedMorphCube/glTF/AnimatedMorphCube.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/RiggedSimple/glTF/RiggedSimple.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/stevk/glTF-Asset-Generator/skins/Output/Animation_Skin/Animation_Skin_01.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/RiggedFigure/glTF/RiggedFigure.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/CesiumMan/glTF/CesiumMan.gltf").then(function () { +//BABYLON.SceneLoader.AppendAsync("https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/ClearCoatTest/glTF/ClearCoatTest.gltf").then(function () { + BABYLON.Tools.Log("Loaded"); -varying vec2 global_positionNormalized; + // This creates and positions a free camera (non-mesh) + scene.createDefaultCamera(true, true, true); + scene.activeCamera.alpha += Math.PI; -void main() -{ - global_positionNormalized=(position.xy/meshSize.xy)+vec2(0.5,0.5); - gl_Position=worldViewProjection*vec4(position,1.0); -} -`; - -function getFragmentShaderCode() { - var ifs = []; - for (var i = 0; i < fragmentShaderComplexity; ++i) { - ifs.push(`if(col.x${randomCharacter("<>")}${randomGlslFloat()} && col.y${randomCharacter("<>")}${randomGlslFloat()} && col.z${randomCharacter("<>")}${randomGlslFloat()}) -{ -return vec4(${randomGlslFloat()},${randomGlslFloat()},${randomGlslFloat()},1.); -}`); - } - return ` -varying vec2 global_positionNormalized; - -uniform sampler2D mediaTexture0; - -vec4 getColor() -{ -vec4 col=texture(mediaTexture0,global_positionNormalized); -${ifs.join("\nelse ")} -return vec4(${randomGlslFloat()},${randomGlslFloat()},${randomGlslFloat()},1.); -} + if (ibl) { + scene.createDefaultEnvironment({ createGround: false, createSkybox: false }); + } + else { + scene.createDefaultLight(true); + } -void main() -{ -gl_FragColor=getColor(); -} -`; -} + if (cameraTexture) { + scene.activeCamera.position.set(0, 1, -10); + scene.activeCamera.setTarget(new BABYLON.Vector3(0, 1, 0)); + + scene.meshes[0].setEnabled(false); + var plane = BABYLON.MeshBuilder.CreatePlane("plane", {size: 1, sideOrientation: BABYLON.Mesh.DOUBLESIDE}); + plane.rotation.y = Math.PI; + plane.rotation.z = Math.PI; -var meshSize = new BABYLON.Vector2(0.8, 0.8); -var engine; -var scene; -var texture; -var material; -var waitingMaterial; -var rectMesh; -var lastRefreshTime = -1; - -let refreshShader = function () { - console.log(`Start refresh shader.`); - lastRefreshTime = new Date().valueOf(); - var oldMaterial = material; - material = waitingMaterial; - if (material) { - rectMesh.setMaterialById(material.id); - } - if (oldMaterial && oldMaterial!=material) { - oldMaterial.dispose(true); - } - waitingMaterial = new BABYLON.ShaderMaterial( - `customMaterial${lastRefreshTime.toString(36)}${Math.random().toString(36)}`, - scene, - { - vertexSource: vertexShaderCode, - fragmentSource: getFragmentShaderCode(), - }, - { - attributes: ["position"], - uniforms: ["worldViewProjection", "meshSize"], - samplers: ["mediaTexture0"], - }, - false - ); - waitingMaterial.forceCompilationAsync(rectMesh); - waitingMaterial.setVector2("meshSize", meshSize); - waitingMaterial.setTexture("mediaTexture0", texture); - if (!material) { - material = waitingMaterial; - if (material) { - rectMesh.setMaterialById(material.id); + plane.position.y = 1; + + var mat = new BABYLON.StandardMaterial("mat", scene); + mat.diffuseColor = BABYLON.Color3.Black(); + + var tex = BABYLON.VideoTexture.CreateFromWebCam(scene, function(videoTexture) { + const videoSize = videoTexture.getSize(); + mat.emissiveTexture = videoTexture; + plane.material = mat; + plane.scaling.x = 5; + plane.scaling.y = 5 * (videoSize.height / videoSize.width); + console.log("Video texture size: " + videoSize); + }, { maxWidth: 1280, maxHeight: 720, facingMode: 'environment'}); } - } -} -function populateScene() { - if (window) { - engine.setHardwareScalingLevel(1 / window.devicePixelRatio); - } - texture = new BABYLON.Texture(url1, scene); - rectMesh = BABYLON.MeshBuilder.CreatePlane( - "rectMesh", - { width: meshSize.x, height: meshSize.y }, - scene - ); - refreshShader(); -}; - -function StartAsync() { - return Promise.resolve(); -} + if (readPixels) { + const texture = new BABYLON.Texture("https://assets.babylonjs.com/textures/earth.jpg", scene); + texture.onLoadObservable.addOnce(() => { + const mip = 1; + const textureWidth = texture.getSize().width >> mip; + const textureHeight = texture.getSize().height >> mip; + const x = textureWidth / 4; + const y = textureHeight / 4; + const width = textureWidth / 2; + const height = textureHeight / 2; + // This read will create a new buffer. + texture.readPixels(undefined, mip, undefined, undefined, undefined, x, y, width, height).then((buffer) => { + console.log(`Read ${buffer.byteLength} pixel bytes.`); + return buffer; + }) + .then(buffer => { + // This read reuses the existing buffer. + texture.readPixels(undefined, mip, buffer, undefined, undefined, x, y, width, height).then((buffer) => { + console.log(`Read ${buffer.byteLength} pixel bytes.`); + }); + }); + }); + } -engine = new BABYLON.NativeEngine(); -scene = new BABYLON.Scene(engine); -var lastRenderTime = -1; + if (wireframe) { + var material = new BABYLON.StandardMaterial("wireframe", scene); + material.wireframe = true; + material.pointsCloud = true; -StartAsync(scene).then(function () { - try { - populateScene(); - } catch (e) { - BABYLON.Tools.Log(e.message); - } - BABYLON.Tools.Log("Loaded"); + for (var index = 0; index < scene.meshes.length; index++) { + scene.meshes[0].material = material; + } + } - // This creates and positions a free camera (non-mesh) - scene.createDefaultCamera(true, true, true); + if (rtt) { + var rttTexture = new BABYLON.RenderTargetTexture("rtt", 1024, scene); + scene.meshes.forEach(mesh => { + rttTexture.renderList.push(mesh); + }); + rttTexture.activeCamera = scene.activeCamera; + rttTexture.vScale = -1; + + scene.customRenderTargets.push(rttTexture); + + var rttMaterial = new BABYLON.StandardMaterial("rttMaterial", scene); + rttMaterial.diffuseTexture = rttTexture; + + var plane = BABYLON.MeshBuilder.CreatePlane("rttPlane", { width: 4, height: 4 }, scene); + plane.position.y = 1; + plane.position.z = -5; + plane.rotation.y = Math.PI; + plane.material = rttMaterial; + } + + if (turntable) { + scene.beforeRender = function () { + scene.meshes[0].rotate(BABYLON.Vector3.Up(), 0.005 * scene.getAnimationRatio()); + }; + } + + if (logfps) { + var logFpsLoop = function () { + BABYLON.Tools.Log("FPS: " + Math.round(engine.getFps())); + window.setTimeout(logFpsLoop, 1000); + }; - scene.createDefaultLight(true); + window.setTimeout(logFpsLoop, 3000); + } - engine.runRenderLoop(function () { - try { - if (material && material.isReady()) { + engine.runRenderLoop(function () { scene.render(); - var renderTime = new Date().valueOf(); - var span = renderTime - lastRenderTime; - if (lastRenderTime >= 0) { - console.log(`${span} ms since last render${span>33?" - too long!":""}.`); - } - lastRenderTime = renderTime; - if (lastRefreshTime>=0 && renderTime-lastRefreshTime > 1000) { - refreshShader(); - } - } - } catch (e) { - BABYLON.Tools.Log(e.message); + }); + + if (vr || ar || hololens) { + setTimeout(function () { + scene.createDefaultXRExperienceAsync({ disableDefaultUI: true, disableTeleportation: true }).then((xr) => { + if (xrHitTest) { + // Create the hit test module. OffsetRay specifies the target direction, and entityTypes can be any combination of "mesh", "plane", and "point". + const xrHitTestModule = xr.baseExperience.featuresManager.enableFeature( + BABYLON.WebXRFeatureName.HIT_TEST, + "latest", + { offsetRay: { origin: { x: 0, y: 0, z: 0 }, direction: { x: 0, y: 0, z: -1 } }, entityTypes: ["mesh"] }); + + // When we receive hit test results, if there were any valid hits move the position of the mesh to the hit test point. + xrHitTestModule.onHitTestResultObservable.add((results) => { + if (results.length) { + scene.meshes[0].position.x = results[0].position.x; + scene.meshes[0].position.y = results[0].position.y; + scene.meshes[0].position.z = results[0].position.z; + } + }); + } + else { + setTimeout(function () { + scene.meshes[0].position.z = 2; + scene.meshes[0].rotate(BABYLON.Vector3.Up(), 3.14159); + }, 5000); + } + + // Showing visualization for ARKit LiDAR mesh data + if (meshDetection) { + var mat = new BABYLON.StandardMaterial("mat", scene); + mat.wireframe = true; + mat.diffuseColor = BABYLON.Color3.Blue(); + const xrMeshes = xr.baseExperience.featuresManager.enableFeature( + BABYLON.WebXRFeatureName.MESH_DETECTION, + "latest", + {convertCoordinateSystems: true}); + console.log("Enabled mesh detection."); + const meshMap = new Map(); + + // adding meshes + xrMeshes.onMeshAddedObservable.add(mesh=> { + try { + console.log("Mesh added."); + // create new mesh object + var customMesh = new BABYLON.Mesh("custom", scene); + var vertexData = new BABYLON.VertexData(); + vertexData.positions = mesh.positions; + vertexData.indices = mesh.indices; + vertexData.normals = mesh.normals; + vertexData.applyToMesh(customMesh, true); + customMesh.material = mat; + // add mesh and mesh id to map + meshMap.set(mesh.id, customMesh); + } catch (ex) { + console.error(ex); + } + }); + + // updating meshes + xrMeshes.onMeshUpdatedObservable.add(mesh=> { + try { + console.log("Mesh updated."); + if (meshMap.has(mesh.id)) { + var vertexData = new BABYLON.VertexData(); + vertexData.positions = mesh.positions; + vertexData.indices = mesh.indices; + vertexData.normals = mesh.normals; + vertexData.applyToMesh(meshMap.get(mesh.id), true); + } + } catch (ex) { + console.error(ex); + } + }); + + // removing meshes + xrMeshes.onMeshRemovedObservable.add(mesh => { + try { + console.log("Mesh removed."); + if (meshMap.has(mesh.id)) { + meshMap.get(mesh.id).dispose(); + meshMap.delete(mesh.id); + } + } catch (ex) { + console.error(ex); + } + }); + } + + // Below is an example of how to process feature points. + if (xrFeaturePoints) { + // First we attach the feature point system feature the XR experience. + const xrFeaturePointsModule = xr.baseExperience.featuresManager.enableFeature( + BABYLON.WebXRFeatureName.FEATURE_POINTS, + "latest", + {}); + + // Next We create the point cloud system which we will use to display feature points. + var pcs = new BABYLON.PointsCloudSystem("pcs", 5, scene); + var featurePointInitFunc = function (particle, i, s) { + particle.position = new BABYLON.Vector3(0, -5, 0); + } + + // Add some starting points and build the mesh. + pcs.addPoints(3000, featurePointInitFunc); + pcs.buildMeshAsync().then((mesh) => { + mesh.alwaysSelectAsActiveMesh = true; + }); + + // Define the logic for how to display a particular particle in the particle system + // which represents a feature point. + pcs.updateParticle = function (particle) { + // Grab the feature point cloud from the xrFeaturePointsModule. + var featurePointCloud = xrFeaturePointsModule.featurePointCloud; + + // Find the index of this particle in the particle system. If there exists a + // mapping to a feature point then display its position, otherwise hide it somewhere far away. + var index = particle.idx; + if (index >= featurePointCloud.length) { + // Hide the particle not currently in use. + particle.position = new BABYLON.Vector3(-100, -100, -100); + } + else { + // To display a feature point set its position to the position of the feature point + // and set its color on the scale from white->red where white = least confident and + // red = most confident. + particle.position = featurePointCloud[index].position; + particle.color = new BABYLON.Color4(1, 1 - featurePointCloud[index].confidenceValue, 1 - featurePointCloud[index].confidenceValue, 1); + } + + return particle; + } + + // Listen for changes in feature points both being added and updated, and only update + // our display every 60 changes to the feature point cloud to avoid slowdowns. + var featurePointChangeCounter = 0; + xrFeaturePointsModule.onFeaturePointsAddedObservable.add((addedPointIds) => { + if (++featurePointChangeCounter % 60 == 0) { + pcs.setParticles(); + } + }); + + xrFeaturePointsModule.onFeaturePointsUpdatedObservable.add((updatedPointIds) => { + if (++featurePointChangeCounter % 60 == 0) { + pcs.setParticles(); + } + }); + } + + let sessionMode = vr ? "immersive-vr" : "immersive-ar" + if (hololens) { + // Because HoloLens 2 is a head mounted display, its Babylon.js immersive experience more closely aligns to vr + sessionMode = "immersive-vr"; + + // Below is an example for enabling hand tracking. The code is not unique to HoloLens 2, and may be reused for other WebXR hand tracking enabled devices. + xr.baseExperience.featuresManager.enableFeature( + BABYLON.WebXRFeatureName.HAND_TRACKING, + "latest", + { xrInput: xr.input }); + } + + // Test image tracking and detection. + // To test image tracking locally either bring up the images below on your machine by loading the URL or by printing them out. + // Then gain tracking on them during the AR Session by orienting your camera towards the image, tracking will be represented by a colored cube at the center of the image. + if (imageTracking) { + const webXRTrackingMeshes = []; + const webXRImageTrackingModule = xr.baseExperience.featuresManager.enableFeature( + BABYLON.WebXRFeatureName.IMAGE_TRACKING, + "latest", + { + images: [ + { src: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/IridescentDishWithOlives/screenshot/screenshot_Large.jpg", estimatedRealWorldWidth: .2 }, + { src: "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/DragonAttenuation/screenshot/screenshot_large.png", estimatedRealWorldWidth: .2 }, + ]}); + + webXRImageTrackingModule.onTrackedImageUpdatedObservable.add((imageObject) => { + if (webXRTrackingMeshes[imageObject.id] === undefined) { + webXRTrackingMeshes[imageObject.id] = BABYLON.Mesh.CreateBox("box1", 0.05, scene); + const mat = new BABYLON.StandardMaterial("mat", scene); + mat.diffuseColor = BABYLON.Color3.Random(); + webXRTrackingMeshes[imageObject.id].material = mat; + } + webXRTrackingMeshes[imageObject.id].setEnabled(!imageObject.emulated); + imageObject.transformationMatrix.decomposeToTransformNode(webXRTrackingMeshes[imageObject.id]); + }); + } + + xr.baseExperience.enterXRAsync(sessionMode, "unbounded", xr.renderTarget).then((xrSessionManager) => { + if (hololens) { + // Pass through, head mounted displays (HoloLens 2) require autoClear and a black clear color + xrSessionManager.scene.autoClear = true; + xrSessionManager.scene.clearColor = new BABYLON.Color4(0, 0, 0, 0); + } + }); + }); + }, 5000); + } + + if (text) { + var Writer = BABYLON.MeshWriter(scene, { scale: 1.0, defaultFont: "Arial" }); + new Writer( + "Lorem ipsum dolor sit amet...", + { + "anchor": "center", + "letter-height": 5, + "color": "#FF0000" + } + ); } - }); }, function (ex) { console.log(ex.message, ex.stack); From 1df96a2a8ddccc95905b30e986d32bf6c44de240 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Fri, 3 Mar 2023 16:45:35 -0800 Subject: [PATCH 11/24] Return both the program handle and VertexAttributeLocations from CreateProgramInternal. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 18 +++++++++++------- Plugins/NativeEngine/Source/NativeEngine.h | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 3713f235e..9d4eba627 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -686,7 +686,7 @@ namespace Babylon return vertexSource; } - bgfx::ProgramHandle NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) + std::pair> NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) { ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); @@ -710,12 +710,11 @@ namespace Babylon auto vertexShader = bgfx::createShader(bgfx::copy(shaderInfo.VertexBytes.data(), static_cast(shaderInfo.VertexBytes.size()))); InitUniformInfos(vertexShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); - program.VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); - return bgfx::createProgram(vertexShader, fragmentShader, true); + return std::pair>{bgfx::createProgram(vertexShader, fragmentShader, true), std::move(shaderInfo.VertexAttributeLocations)}; } Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) @@ -725,10 +724,13 @@ namespace Babylon ProgramData* program = new ProgramData{}; try { - program->Handle = CreateProgramInternal(vertexSource, fragmentSource, *program); + std::pair> programInfo = CreateProgramInternal(vertexSource, fragmentSource, *program); + program->Handle = programInfo.first; + program->VertexAttributeLocations = std::move(programInfo.second); } catch (const std::exception& ex) { + delete program; throw Napi::Error::New(info.Env(), ex.what()); } return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); @@ -742,11 +744,11 @@ namespace Babylon const Napi::Function onError = info[3].As(); ProgramData* program = new ProgramData{}; arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> bgfx::ProgramHandle + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> std::pair> { return CreateProgramInternal(vertexSource, fragmentSource, *program); }) - .then(m_runtimeScheduler, *m_cancellationSource, [program, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected result) + .then(m_runtimeScheduler, *m_cancellationSource, [program, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected>, std::exception_ptr> result) { if (result.has_error()) { @@ -754,7 +756,9 @@ namespace Babylon } else { - program->Handle = result.value(); + std::pair> programInfo = result.value(); + program->Handle = programInfo.first; + program->VertexAttributeLocations = std::move(programInfo.second); onSuccessRef.Call({}); } }); return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 0d2067007..c4f77923b 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -120,7 +120,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); - bgfx::ProgramHandle CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); + std::pair> CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); From 209c34ace747a9bc4799dd1bc5e3e66d480b7ac8 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Mon, 6 Mar 2023 10:25:57 -0800 Subject: [PATCH 12/24] Throw a proper error after encountering an async shader compilation error. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 30 +++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 9d4eba627..fdad47ede 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -750,17 +750,25 @@ namespace Babylon }) .then(m_runtimeScheduler, *m_cancellationSource, [program, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected>, std::exception_ptr> result) { - if (result.has_error()) - { - onErrorRef.Call({}); - } - else - { - std::pair> programInfo = result.value(); - program->Handle = programInfo.first; - program->VertexAttributeLocations = std::move(programInfo.second); - onSuccessRef.Call({}); - } }); + if (result.has_error()) + { + try + { + std::rethrow_exception(result.error()); + } + catch (const std::exception& ex) + { + onErrorRef.Call({Napi::Error::New(onErrorRef.Env(), ex.what()).Value()}); + } + } + else + { + std::pair> programInfo = result.value(); + program->Handle = programInfo.first; + program->VertexAttributeLocations = std::move(programInfo.second); + onSuccessRef.Call({}); + } + }); return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); } From d4401b7a99781296da08b7d9630607a887422907 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Mon, 6 Mar 2023 14:47:45 -0800 Subject: [PATCH 13/24] Simplify error reporting. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index fdad47ede..264e3b7f8 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -752,14 +752,7 @@ namespace Babylon { if (result.has_error()) { - try - { - std::rethrow_exception(result.error()); - } - catch (const std::exception& ex) - { - onErrorRef.Call({Napi::Error::New(onErrorRef.Env(), ex.what()).Value()}); - } + onErrorRef.Call({Napi::Error::New(onErrorRef.Env(), result.error()).Value()}); } else { From 678fb499d54f22ac0715ea2ff3f02e878fc148b0 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Tue, 7 Mar 2023 14:39:57 -0800 Subject: [PATCH 14/24] Preliminary code for movable ProgramData (not working yet). --- Plugins/NativeEngine/Source/NativeEngine.cpp | 34 +++++++++++++------- Plugins/NativeEngine/Source/NativeEngine.h | 21 ++++++++++-- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 264e3b7f8..388192d78 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -686,10 +686,12 @@ namespace Babylon return vertexSource; } - std::pair> NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program) + ProgramData NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource) { ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); + ProgramData program; + static auto InitUniformInfos{ [](bgfx::ShaderHandle shader, const std::unordered_map& uniformStages, std::unordered_map& uniformInfos, std::unordered_map& uniformNameToIndex) { auto numUniforms = bgfx::getShaderUniforms(shader); @@ -714,7 +716,10 @@ namespace Babylon auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); - return std::pair>{bgfx::createProgram(vertexShader, fragmentShader, true), std::move(shaderInfo.VertexAttributeLocations)}; + program.Handle = bgfx::createProgram(vertexShader, fragmentShader, true); + program.VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); + + return program; } Napi::Value NativeEngine::CreateProgram(const Napi::CallbackInfo& info) @@ -724,9 +729,7 @@ namespace Babylon ProgramData* program = new ProgramData{}; try { - std::pair> programInfo = CreateProgramInternal(vertexSource, fragmentSource, *program); - program->Handle = programInfo.first; - program->VertexAttributeLocations = std::move(programInfo.second); + *program = CreateProgramInternal(vertexSource, fragmentSource); } catch (const std::exception& ex) { @@ -742,13 +745,21 @@ namespace Babylon const std::string fragmentSource = info[1].As().Utf8Value(); const Napi::Function onSuccess = info[2].As(); const Napi::Function onError = info[3].As(); + ProgramData* program = new ProgramData{}; + Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> std::pair> + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> ProgramData { - return CreateProgramInternal(vertexSource, fragmentSource, *program); + return CreateProgramInternal(vertexSource, fragmentSource); }) - .then(m_runtimeScheduler, *m_cancellationSource, [program, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, cancellationSource{m_cancellationSource}](arcana::expected>, std::exception_ptr> result) + .then(m_runtimeScheduler, *m_cancellationSource, + [program, + jsProgramRef{Napi::Persistent(jsProgram)}, + onSuccessRef{Napi::Persistent(onSuccess)}, + onErrorRef{Napi::Persistent(onError)}, + cancellationSource{m_cancellationSource}](arcana::expected result) { if (result.has_error()) { @@ -756,13 +767,12 @@ namespace Babylon } else { - std::pair> programInfo = result.value(); - program->Handle = programInfo.first; - program->VertexAttributeLocations = std::move(programInfo.second); + *program = std::move(result.value()); onSuccessRef.Call({}); } }); - return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + + return jsProgram; } Napi::Value NativeEngine::GetUniforms(const Napi::CallbackInfo& info) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index c4f77923b..77dcf1c87 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -43,11 +43,26 @@ namespace Babylon struct ProgramData final { ProgramData() = default; - ProgramData(ProgramData&& other) = delete; ProgramData(const ProgramData&) = delete; - ProgramData& operator=(ProgramData&& other) = delete; ProgramData& operator=(const ProgramData& other) = delete; + ProgramData(ProgramData&& other) noexcept + { + (*this) = std::move(other); + } + + ProgramData& operator=(ProgramData&& other) noexcept + { + Handle = std::move(other.Handle); + other.Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; + Disposed = std::move(other.Disposed); + Uniforms = std::move(other.Uniforms); + UniformNameToIndex = std::move(other.UniformNameToIndex); + UniformInfos = std::move(other.UniformInfos); + VertexAttributeLocations = std::move(other.VertexAttributeLocations); + return *this; + } + ~ProgramData() { Dispose(); @@ -120,7 +135,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); - std::pair> CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource, ProgramData& program); + ProgramData CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); From 5e66231ec3e0f0c859d1572c142ff5f596c2aa3f Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Tue, 7 Mar 2023 15:06:22 -0800 Subject: [PATCH 15/24] Use unique_ptr to make the Arcana return type work. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 20 ++++++++++---------- Plugins/NativeEngine/Source/NativeEngine.h | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 388192d78..c9976baa1 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -686,11 +686,11 @@ namespace Babylon return vertexSource; } - ProgramData NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource) + std::unique_ptr NativeEngine::CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource) { ShaderCompiler::BgfxShaderInfo shaderInfo = m_shaderCompiler.Compile(ProcessShaderCoordinates(vertexSource), ProcessSamplerFlip(fragmentSource)); - ProgramData program; + std::unique_ptr program = std::make_unique(); static auto InitUniformInfos{ [](bgfx::ShaderHandle shader, const std::unordered_map& uniformStages, std::unordered_map& uniformInfos, std::unordered_map& uniformNameToIndex) { @@ -711,13 +711,13 @@ namespace Babylon }}; auto vertexShader = bgfx::createShader(bgfx::copy(shaderInfo.VertexBytes.data(), static_cast(shaderInfo.VertexBytes.size()))); - InitUniformInfos(vertexShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); + InitUniformInfos(vertexShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); auto fragmentShader = bgfx::createShader(bgfx::copy(shaderInfo.FragmentBytes.data(), static_cast(shaderInfo.FragmentBytes.size()))); - InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program.UniformInfos, program.UniformNameToIndex); + InitUniformInfos(fragmentShader, shaderInfo.UniformStages, program->UniformInfos, program->UniformNameToIndex); - program.Handle = bgfx::createProgram(vertexShader, fragmentShader, true); - program.VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); + program->Handle = bgfx::createProgram(vertexShader, fragmentShader, true); + program->VertexAttributeLocations = std::move(shaderInfo.VertexAttributeLocations); return program; } @@ -729,7 +729,7 @@ namespace Babylon ProgramData* program = new ProgramData{}; try { - *program = CreateProgramInternal(vertexSource, fragmentSource); + *program = std::move(*CreateProgramInternal(vertexSource, fragmentSource).release()); } catch (const std::exception& ex) { @@ -750,7 +750,7 @@ namespace Babylon Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> ProgramData + [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> std::unique_ptr { return CreateProgramInternal(vertexSource, fragmentSource); }) @@ -759,7 +759,7 @@ namespace Babylon jsProgramRef{Napi::Persistent(jsProgram)}, onSuccessRef{Napi::Persistent(onSuccess)}, onErrorRef{Napi::Persistent(onError)}, - cancellationSource{m_cancellationSource}](arcana::expected result) + cancellationSource{m_cancellationSource}](const arcana::expected, std::exception_ptr>& result) { if (result.has_error()) { @@ -767,7 +767,7 @@ namespace Babylon } else { - *program = std::move(result.value()); + *program = std::move(*result.value()); onSuccessRef.Call({}); } }); diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 77dcf1c87..35bb17959 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -135,7 +135,7 @@ namespace Babylon void DeleteVertexBuffer(NativeDataStream::Reader& data); void RecordVertexBuffer(const Napi::CallbackInfo& info); void UpdateDynamicVertexBuffer(const Napi::CallbackInfo& info); - ProgramData CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource); + std::unique_ptr CreateProgramInternal(const std::string vertexSource, const std::string fragmentSource); Napi::Value CreateProgram(const Napi::CallbackInfo& info); Napi::Value CreateProgramAsync(const Napi::CallbackInfo& info); Napi::Value GetUniforms(const Napi::CallbackInfo& info); From eb5a1049b5e97a91f953c393f69582ffd2f97e51 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:13:01 -0800 Subject: [PATCH 16/24] Initialize member variables in constructor. --- Plugins/NativeEngine/Source/NativeEngine.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 35bb17959..90d32a0f9 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -46,16 +46,20 @@ namespace Babylon ProgramData(const ProgramData&) = delete; ProgramData& operator=(const ProgramData& other) = delete; - ProgramData(ProgramData&& other) noexcept + ProgramData(ProgramData&& other) noexcept: + Handle{other.Handle}, + Uniforms{std::move(other.Uniforms)}, + UniformNameToIndex{std::move(other.UniformNameToIndex)}, + UniformInfos{std::move(other.UniformInfos)}, + VertexAttributeLocations{std::move(other.VertexAttributeLocations)} { - (*this) = std::move(other); + other.Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; } ProgramData& operator=(ProgramData&& other) noexcept { Handle = std::move(other.Handle); other.Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; - Disposed = std::move(other.Disposed); Uniforms = std::move(other.Uniforms); UniformNameToIndex = std::move(other.UniformNameToIndex); UniformInfos = std::move(other.UniformInfos); From f8c062d6bff46fa22d9f206528574008c576b8ab Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:22:14 -0800 Subject: [PATCH 17/24] Remove unnecessary release of unique_ptr. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index c9976baa1..3591f3b8b 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -729,7 +729,7 @@ namespace Babylon ProgramData* program = new ProgramData{}; try { - *program = std::move(*CreateProgramInternal(vertexSource, fragmentSource).release()); + *program = std::move(*CreateProgramInternal(vertexSource, fragmentSource)); } catch (const std::exception& ex) { From 089a27833bdb34b650ac8f873802f12b31bda3c0 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:23:01 -0800 Subject: [PATCH 18/24] Leave parameter of nulled operator anonymous. --- Plugins/NativeEngine/Source/NativeEngine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 90d32a0f9..56739a403 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -44,7 +44,7 @@ namespace Babylon { ProgramData() = default; ProgramData(const ProgramData&) = delete; - ProgramData& operator=(const ProgramData& other) = delete; + ProgramData& operator=(const ProgramData&) = delete; ProgramData(ProgramData&& other) noexcept: Handle{other.Handle}, From e1872d5ea1367c655e942c9f874faa79f6fa2d84 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:27:39 -0800 Subject: [PATCH 19/24] Indent lambda captures. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 3591f3b8b..6414de6e5 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -756,10 +756,10 @@ namespace Babylon }) .then(m_runtimeScheduler, *m_cancellationSource, [program, - jsProgramRef{Napi::Persistent(jsProgram)}, - onSuccessRef{Napi::Persistent(onSuccess)}, - onErrorRef{Napi::Persistent(onError)}, - cancellationSource{m_cancellationSource}](const arcana::expected, std::exception_ptr>& result) + jsProgramRef{Napi::Persistent(jsProgram)}, + onSuccessRef{Napi::Persistent(onSuccess)}, + onErrorRef{Napi::Persistent(onError)}, + cancellationSource{m_cancellationSource}](const arcana::expected, std::exception_ptr>& result) { if (result.has_error()) { From 2319953305f13e3debee1f6f81d6290c7194f9af Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:31:06 -0800 Subject: [PATCH 20/24] Remove unused lambda capture. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 6414de6e5..3b8af2b78 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -750,7 +750,7 @@ namespace Babylon Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); arcana::make_task(arcana::threadpool_scheduler, *m_cancellationSource, - [this, vertexSource, fragmentSource, program, cancellationSource{m_cancellationSource}]() -> std::unique_ptr + [this, vertexSource, fragmentSource, cancellationSource{m_cancellationSource}]() -> std::unique_ptr { return CreateProgramInternal(vertexSource, fragmentSource); }) From 654886c53ee8b5fd68c04f12157f5f870da4a48d Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 09:59:07 -0800 Subject: [PATCH 21/24] Remove ProgramData.Disposed. --- Plugins/NativeEngine/Source/NativeEngine.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 56739a403..13ab92eff 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -74,15 +74,14 @@ namespace Babylon void Dispose() { - if (!Disposed && bgfx::isValid(Handle)) + if (bgfx::isValid(Handle)) { bgfx::destroy(Handle); + Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; } - Disposed = true; } bgfx::ProgramHandle Handle{bgfx::kInvalidHandle}; - bool Disposed{false}; struct UniformValue { From 8f17fb7ffd00e5f1b60f2ff6af69c895695281cc Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 10:30:49 -0800 Subject: [PATCH 22/24] Use pattern similar to CreateProgramAsync in CreateProgram. --- Plugins/NativeEngine/Source/NativeEngine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.cpp b/Plugins/NativeEngine/Source/NativeEngine.cpp index 3b8af2b78..410caaf80 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.cpp +++ b/Plugins/NativeEngine/Source/NativeEngine.cpp @@ -727,16 +727,16 @@ namespace Babylon const std::string vertexSource = info[0].As().Utf8Value(); const std::string fragmentSource = info[1].As().Utf8Value(); ProgramData* program = new ProgramData{}; + Napi::Value jsProgram = Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); try { *program = std::move(*CreateProgramInternal(vertexSource, fragmentSource)); } catch (const std::exception& ex) { - delete program; throw Napi::Error::New(info.Env(), ex.what()); } - return Napi::Pointer::Create(info.Env(), program, Napi::NapiPointerDeleter(program)); + return jsProgram; } Napi::Value NativeEngine::CreateProgramAsync(const Napi::CallbackInfo& info) From 4977f3ef2a83b5ed37a7be239020e1886c2b9733 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 10:58:20 -0800 Subject: [PATCH 23/24] Use BGFX_INVALID_HANDLE instead of bgfx::kInvalidHandle. --- Plugins/NativeEngine/Source/NativeEngine.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 13ab92eff..8480acb1e 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -36,7 +36,7 @@ namespace Babylon } uint8_t Stage{}; - bgfx::UniformHandle Handle{bgfx::kInvalidHandle}; + bgfx::UniformHandle Handle = BGFX_INVALID_HANDLE; size_t MaxElementLength{}; }; @@ -53,13 +53,13 @@ namespace Babylon UniformInfos{std::move(other.UniformInfos)}, VertexAttributeLocations{std::move(other.VertexAttributeLocations)} { - other.Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; + other.Handle = BGFX_INVALID_HANDLE; } ProgramData& operator=(ProgramData&& other) noexcept { Handle = std::move(other.Handle); - other.Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; + other.Handle = BGFX_INVALID_HANDLE; Uniforms = std::move(other.Uniforms); UniformNameToIndex = std::move(other.UniformNameToIndex); UniformInfos = std::move(other.UniformInfos); @@ -77,11 +77,11 @@ namespace Babylon if (bgfx::isValid(Handle)) { bgfx::destroy(Handle); - Handle = bgfx::ProgramHandle{bgfx::kInvalidHandle}; + Handle = BGFX_INVALID_HANDLE; } } - bgfx::ProgramHandle Handle{bgfx::kInvalidHandle}; + bgfx::ProgramHandle Handle = BGFX_INVALID_HANDLE; struct UniformValue { From 17666c0e9c6bd52c64714c30daf97c75bf854bc0 Mon Sep 17 00:00:00 2001 From: Graham Langston Date: Wed, 8 Mar 2023 11:05:37 -0800 Subject: [PATCH 24/24] Use brace initialization for BGFX handles. --- Plugins/NativeEngine/Source/NativeEngine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/NativeEngine/Source/NativeEngine.h b/Plugins/NativeEngine/Source/NativeEngine.h index 8480acb1e..385e64547 100644 --- a/Plugins/NativeEngine/Source/NativeEngine.h +++ b/Plugins/NativeEngine/Source/NativeEngine.h @@ -36,7 +36,7 @@ namespace Babylon } uint8_t Stage{}; - bgfx::UniformHandle Handle = BGFX_INVALID_HANDLE; + bgfx::UniformHandle Handle{bgfx::kInvalidHandle}; size_t MaxElementLength{}; }; @@ -81,7 +81,7 @@ namespace Babylon } } - bgfx::ProgramHandle Handle = BGFX_INVALID_HANDLE; + bgfx::ProgramHandle Handle{bgfx::kInvalidHandle}; struct UniformValue {