Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Apps/UnitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ if(GRAPHICS_API STREQUAL "D3D12")
endif()

if(GRAPHICS_API STREQUAL "Vulkan")
target_link_libraries(UnitTests PRIVATE ShaderCompilerInternal spirv-cross-core glslang)
# Vulkan packaging paths (e.g. the >16 sampler rejection) are gated on this define.
# ExternalTexture / native-texture helpers are not implemented for Vulkan yet, so skip
# those tests rather than failing at link or runtime.
Expand Down
101 changes: 101 additions & 0 deletions Apps/UnitTests/JavaScript/dist/tests.javaScript.all.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,105 @@
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({

/***/ "./src/tests.nativeEngine.attributeLessInstancing.ts"
/*!***********************************************************!*\
!*** ./src/tests.nativeEngine.attributeLessInstancing.ts ***!
\***********************************************************/
(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ registerAttributeLessInstancingTests: () => (/* binding */ registerAttributeLessInstancingTests)
/* harmony export */ });
/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ "../../node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js");
/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ "../../node_modules/@babel/runtime/regenerator/index.js");
/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var chai__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! chai */ "../../node_modules/chai/index.js");
/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @babylonjs/core */ "@babylonjs/core");
/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_3__);




function registerAttributeLessInstancingTests(
describe,
it,
enabled)
{
describe("NativeEngine attribute-less instancing", function () {
(enabled ? it : it.skip)("draws every instance without an instance buffer", /*#__PURE__*/(0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__["default"])(/*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {var engine, scene, camera, material, target, _i, _arr, unIndexed, mesh, _loop, _i2, _arr2;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context2) {while (1) switch (_context2.prev = _context2.next) {case 0:
this.timeout(15000);
engine = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.NativeEngine();
scene = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Scene(engine);_context2.prev = 1;

scene.clearColor = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Color4(0, 0, 0, 1);
camera = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.FreeCamera("camera", new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Vector3(0, 0, -5), scene);
camera.setTarget(_babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.Vector3.Zero());
material = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.ShaderMaterial("instances", scene, {
vertexSource: "\n precision highp float;\n attribute vec3 position;\n varying vec3 instanceColor;\n void main() {\n float offset = (float(gl_InstanceID) - 1.0) * 0.6;\n gl_Position = vec4(position.x * 0.2 + offset, position.y * 0.2, 0.5, 1.0);\n instanceColor = vec3(float(gl_InstanceID == 0), float(gl_InstanceID == 1), float(gl_InstanceID == 2));\n }",








fragmentSource: "\n precision highp float;\n varying vec3 instanceColor;\n void main() { gl_FragColor = vec4(instanceColor, 1.0); }"



}, { attributes: ["position"], uniforms: [] });
material.backFaceCulling = false;
target = new _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.RenderTargetTexture("instances", 64, scene, false);_i = 0, _arr =
[false, true];case 2:if (!(_i < _arr.length)) {_context2.next = 7;break;}unIndexed = _arr[_i];
mesh = _babylonjs_core__WEBPACK_IMPORTED_MODULE_3__.MeshBuilder.CreatePlane("plane", { size: 2 }, scene);
if (unIndexed) {
mesh.convertToUnIndexedMesh();
}
mesh.material = material;
target.renderList = [mesh];_loop = /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _loop() {var count, readinessDeadline, pixels, expected, centers, actual;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function (_context) {while (1) switch (_context.prev = _context.next) {case 0:
count = _arr2[_i2];
mesh.forcedInstanceCount = count;_context.next = 1;return (
material.forceCompilationAsync(mesh, { useInstances: count > 0 }));case 1:_context.next = 2;return (
scene.whenReadyAsync());case 2:
readinessDeadline = Date.now() + 10000;case 3:if (
target.isReadyForRendering()) {_context.next = 6;break;}if (!(
Date.now() >= readinessDeadline)) {_context.next = 4;break;}throw (
new Error("Render target did not become ready: unIndexed=".concat(unIndexed, ", count=").concat(count)));case 4:_context.next = 5;return (

new Promise(function (resolve) {return setTimeout(resolve, 10);}));case 5:_context.next = 3;break;case 6:

target.render();_context.next = 7;return (
target.readPixels());case 7:pixels = _context.sent;if (
pixels instanceof Uint8Array) {_context.next = 8;break;}throw (
new Error("Expected RGBA8 instance rendering readback"));case 8:

expected = [
[255, 0, 0, 255],
count === 3 ? [0, 255, 0, 255] : [0, 0, 0, 255],
count === 3 ? [0, 0, 255, 255] : [0, 0, 0, 255]];

centers = [13, 32, 51];
actual = centers.map(function (center) {
var offset = (32 * 64 + center) * 4;
return Array.from(pixels.subarray(offset, offset + 4));
});
(0,chai__WEBPACK_IMPORTED_MODULE_2__.expect)(actual, "unIndexed=".concat(unIndexed, ", count=").concat(count)).to.deep.equal(expected);case 9:case "end":return _context.stop();}}, _loop);});_i2 = 0, _arr2 = [0, 1, 3];case 3:if (!(_i2 < _arr2.length)) {_context2.next = 5;break;}return _context2.delegateYield(_loop(), "t0", 4);case 4:_i2++;_context2.next = 3;break;case 5:

mesh.dispose();case 6:_i++;_context2.next = 2;break;case 7:_context2.prev = 7;


scene.dispose();
engine.dispose();return _context2.finish(7);case 8:case "end":return _context2.stop();}}, _callee, this, [[1,, 7, 8]]);}))

);
});
}

/***/ },

/***/ "./src/tests.nativeEngine.png.ts"
/*!***************************************!*\
!*** ./src/tests.nativeEngine.png.ts ***!
Expand Down Expand Up @@ -28521,6 +28620,7 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @babylonjs/materials */ "@babylonjs/core");
/* harmony import */ var _babylonjs_core__WEBPACK_IMPORTED_MODULE_6___default = /*#__PURE__*/__webpack_require__.n(_babylonjs_core__WEBPACK_IMPORTED_MODULE_6__);
/* harmony import */ var _tests_nativeEngine_png__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./tests.nativeEngine.png */ "./src/tests.nativeEngine.png.ts");
/* harmony import */ var _tests_nativeEngine_attributeLessInstancing__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./tests.nativeEngine.attributeLessInstancing */ "./src/tests.nativeEngine.attributeLessInstancing.ts");
function _createForOfIteratorHelper(r, e) {var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];if (!t) {if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) {t && (r = t);var _n = 0,F = function F() {};return { s: F, n: function n() {return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] };}, e: function e(r) {throw r;}, f: F };}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");}var o,a = !0,u = !1;return { s: function s() {t = t.call(r);}, n: function n() {var r = t.next();return a = r.done, r;}, e: function e(r) {u = !0, o = r;}, f: function f() {try {a || null == t.return || t.return();} finally {if (u) throw o;}} };}function _unsupportedIterableToArray(r, a) {if (r) {if ("string" == typeof r) return _arrayLikeToArray(r, a);var t = {}.toString.call(r).slice(8, -1);return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;}}function _arrayLikeToArray(r, a) {(null == a || a > r.length) && (a = r.length);for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];return n;}


Expand Down Expand Up @@ -28703,6 +28803,7 @@ describe("Native texture readback", function () {

);
});
(0,_tests_nativeEngine_attributeLessInstancing__WEBPACK_IMPORTED_MODULE_8__.registerAttributeLessInstancingTests)(describe, it, hasAttributeLessInstancing);

describe("RequestFile", function () {
this.timeout(0);
Expand Down
3 changes: 3 additions & 0 deletions Apps/UnitTests/JavaScript/src/tests.javaScript.all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from "@babylonjs/core";
import { GradientMaterial } from "@babylonjs/materials";
import { registerPngTests } from "./tests.nativeEngine.png";
import { registerAttributeLessInstancingTests } from "./tests.nativeEngine.attributeLessInstancing";

declare var describe: typeof Mocha.describe;
declare var it: typeof Mocha.it;
Expand All @@ -35,12 +36,14 @@ Mocha.reporter("spec");
declare const hostPlatform: string;
declare const hasGpuRendering: boolean;
declare const hasNativeImageLoading: boolean;
declare const hasAttributeLessInstancing: boolean;
declare const setExitCode: (code: number) => void;
declare const setImageReloadTestResponse: (bytes: Uint8Array) => void;
declare const skipCanvasGpuTests: boolean;
declare const _native: any;

registerPngTests(describe, it, hasGpuRendering && hasNativeImageLoading);
registerAttributeLessInstancingTests(describe, it, hasAttributeLessInstancing);

describe("Native texture readback", function () {
this.timeout(10000);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as Mocha from "mocha";
import { expect } from "chai";
import {
Color4, FreeCamera, MeshBuilder, NativeEngine, RenderTargetTexture,
Scene, ShaderMaterial, Vector3
} from "@babylonjs/core";

export function registerAttributeLessInstancingTests(
describe: typeof Mocha.describe,
it: typeof Mocha.it,
enabled: boolean
): void {
describe("NativeEngine attribute-less instancing", function () {
(enabled ? it : it.skip)("draws every instance without an instance buffer", async function () {
this.timeout(15000);
const engine = new NativeEngine();
const scene = new Scene(engine);
try {
scene.clearColor = new Color4(0, 0, 0, 1);
const camera = new FreeCamera("camera", new Vector3(0, 0, -5), scene);
camera.setTarget(Vector3.Zero());
const material = new ShaderMaterial("instances", scene, {
vertexSource: `
precision highp float;
attribute vec3 position;
varying vec3 instanceColor;
void main() {
float offset = (float(gl_InstanceID) - 1.0) * 0.6;
gl_Position = vec4(position.x * 0.2 + offset, position.y * 0.2, 0.5, 1.0);
instanceColor = vec3(float(gl_InstanceID == 0), float(gl_InstanceID == 1), float(gl_InstanceID == 2));
}`,
fragmentSource: `
precision highp float;
varying vec3 instanceColor;
void main() { gl_FragColor = vec4(instanceColor, 1.0); }`
}, { attributes: ["position"], uniforms: [] });
material.backFaceCulling = false;
const target = new RenderTargetTexture("instances", 64, scene, false);
for (const unIndexed of [false, true]) {
const mesh = MeshBuilder.CreatePlane("plane", { size: 2 }, scene);
if (unIndexed) {
mesh.convertToUnIndexedMesh();
}
mesh.material = material;
target.renderList = [mesh];
for (const count of [0, 1, 3]) {
mesh.forcedInstanceCount = count;
await material.forceCompilationAsync(mesh, { useInstances: count > 0 });
await scene.whenReadyAsync();
const readinessDeadline = Date.now() + 10000;
while (!target.isReadyForRendering()) {
if (Date.now() >= readinessDeadline) {
throw new Error(`Render target did not become ready: unIndexed=${unIndexed}, count=${count}`);
}
await new Promise<void>(resolve => setTimeout(resolve, 10));
}
target.render();
const pixels = await target.readPixels();
if (!(pixels instanceof Uint8Array)) {
throw new Error("Expected RGBA8 instance rendering readback");
}
const expected = [
[255, 0, 0, 255],
count === 3 ? [0, 255, 0, 255] : [0, 0, 0, 255],
count === 3 ? [0, 0, 255, 255] : [0, 0, 0, 255]
];
const centers = [13, 32, 51];
const actual = centers.map(center => {
const offset = (32 * 64 + center) * 4;
return Array.from(pixels.subarray(offset, offset + 4));
});
expect(actual, `unIndexed=${unIndexed}, count=${count}`).to.deep.equal(expected);
}
mesh.dispose();
}
} finally {
scene.dispose();
engine.dispose();
}
});
});
}
5 changes: 5 additions & 0 deletions Apps/UnitTests/Source/Tests.JavaScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ TEST(JavaScript, All)
#else
env.Global().Set("hasNativeImageLoading", false);
#endif
#if defined(USE_NOOP_METAL_DEVICE) || defined(SKIP_RENDER_TESTS) || defined(TEST_INSTALLED_SDK)
env.Global().Set("hasAttributeLessInstancing", false);
#else
env.Global().Set("hasAttributeLessInstancing", true);
#endif
#ifdef USE_NOOP_METAL_DEVICE
env.Global().Set("hasGpuRendering", false);
#else
Expand Down
Loading