@carbonenginejs/runtime 0.1.0-alpha.1 → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,5 @@
1
+ import { CjsSchema } from './CjsSchema.js';
2
+
1
3
  /**
2
4
  * Hydration adapter seam - the population contract for readers that build
3
5
  * runtime objects incrementally (black/red). The ordering guarantee:
@@ -24,8 +26,16 @@
24
26
  * SetValues is called directly rather than through CjsSchema.setValues so a
25
27
  * reader works with only the schema layer loaded; the two are the same path
26
28
  * for every class that has the method.
29
+ *
30
+ * A REGISTERED class without SetValues - one that no longer extends CjsModel -
31
+ * is still a resolved class, so it populates through CjsSchema.setValues, which
32
+ * answers without the model layer. Only an unregistered carrier (the reader's
33
+ * `{ _sourceClassName }` fallback) takes raw assignment. Without this middle
34
+ * arm, taking a class off the base would silently drop it to Object.assign:
35
+ * no coercion, no writability gate, no settle.
27
36
  */
28
37
 
38
+
29
39
  /**
30
40
  * Resolves a normalized adapter from hydration options. The returned object
31
41
  * always exposes construct/applyValues/finalize with the documented defaults.
@@ -47,6 +57,10 @@ function resolveHydrationAdapter(options = {}) {
47
57
  instance.SetValues(values, ctx?.options);
48
58
  return instance;
49
59
  }
60
+ if (instance && CjsSchema.getClassName(instance.constructor)) {
61
+ CjsSchema.setValues(instance, values, ctx?.options);
62
+ return instance;
63
+ }
50
64
  return Object.assign(instance, values);
51
65
  },
52
66
  finalize(instance, ctx) {
@@ -1 +1 @@
1
- {"version":3,"file":"hydration.js","sources":["../../../../src/global/schema/hydration.js"],"sourcesContent":["/**\n * Hydration adapter seam - the population contract for readers that build\n * runtime objects incrementally (black/red). The ordering guarantee:\n *\n * 1. construct - build every instance (register refs before values, so\n * cycles/back-references resolve)\n * 2. applyValues - apply each node's field values\n * 3. finalize - run once per instance AFTER the whole graph is built,\n * so references are already resolved\n *\n * DEFAULTS (operator ruling, 2026-09-05; resource-population.md owns the\n * decision): a resolved class is populated through its own validated setter -\n * \"if someone is using Object.assign, they probably should be using\n * SetValues\" - and a class-owned Initialize runs once post-graph, which is\n * the incremental-reader counterpart of the reader+IInitialize handshake\n * Blue performs and CjsModel.from performs for whole-bag JSON. Plain\n * fallback carriers (unresolved kinds) keep raw assignment: that IS their\n * contract, not a bypass.\n *\n * The single escape hatch is `options.adapter`, per-hook optional. The\n * per-kind adapters map and the hydrationAdapter alias are gone - they had\n * zero consumers. Callers wanting raw assignment pass their own adapter.\n *\n * SetValues is called directly rather than through CjsSchema.setValues so a\n * reader works with only the schema layer loaded; the two are the same path\n * for every class that has the method.\n */\n\n/**\n * Resolves a normalized adapter from hydration options. The returned object\n * always exposes construct/applyValues/finalize with the documented defaults.\n *\n * @param {Object} [options]\n * @param {Object} [options.adapter] Per-hook overrides: construct/applyValues/finalize.\n * @returns {{ construct: Function, applyValues: Function, finalize: Function }}\n */\nexport function resolveHydrationAdapter(options = {})\n{\n const custom = options.adapter || null;\n\n return {\n construct(kind, ctx)\n {\n if (custom && typeof custom.construct === \"function\") return custom.construct(kind, ctx);\n return undefined;\n },\n applyValues(instance, values, ctx)\n {\n if (custom && typeof custom.applyValues === \"function\") return custom.applyValues(instance, values, ctx);\n if (instance && typeof instance.SetValues === \"function\")\n {\n instance.SetValues(values, ctx?.options);\n return instance;\n }\n return Object.assign(instance, values);\n },\n finalize(instance, ctx)\n {\n if (custom && typeof custom.finalize === \"function\")\n {\n custom.finalize(instance, ctx);\n return;\n }\n if (instance && typeof instance.Initialize === \"function\") instance.Initialize();\n }\n };\n}\n"],"names":["resolveHydrationAdapter","options","custom","adapter","construct","kind","ctx","undefined","applyValues","instance","values","SetValues","Object","assign","finalize","Initialize"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,uBAAuBA,CAACC,OAAO,GAAG,EAAE,EACpD;AACI,EAAA,MAAMC,MAAM,GAAGD,OAAO,CAACE,OAAO,IAAI,IAAI;EAEtC,OAAO;AACHC,IAAAA,SAASA,CAACC,IAAI,EAAEC,GAAG,EACnB;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACE,SAAS,KAAK,UAAU,EAAE,OAAOF,MAAM,CAACE,SAAS,CAACC,IAAI,EAAEC,GAAG,CAAC;AACxF,MAAA,OAAOC,SAAS;IACpB,CAAC;AACDC,IAAAA,WAAWA,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,EACjC;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACM,WAAW,KAAK,UAAU,EAAE,OAAON,MAAM,CAACM,WAAW,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,CAAC;MACxG,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACE,SAAS,KAAK,UAAU,EACxD;QACIF,QAAQ,CAACE,SAAS,CAACD,MAAM,EAAEJ,GAAG,EAAEL,OAAO,CAAC;AACxC,QAAA,OAAOQ,QAAQ;AACnB,MAAA;AACA,MAAA,OAAOG,MAAM,CAACC,MAAM,CAACJ,QAAQ,EAAEC,MAAM,CAAC;IAC1C,CAAC;AACDI,IAAAA,QAAQA,CAACL,QAAQ,EAAEH,GAAG,EACtB;MACI,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACY,QAAQ,KAAK,UAAU,EACnD;AACIZ,QAAAA,MAAM,CAACY,QAAQ,CAACL,QAAQ,EAAEH,GAAG,CAAC;AAC9B,QAAA;AACJ,MAAA;AACA,MAAA,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACM,UAAU,KAAK,UAAU,EAAEN,QAAQ,CAACM,UAAU,EAAE;AACpF,IAAA;GACH;AACL;;;;"}
1
+ {"version":3,"file":"hydration.js","sources":["../../../../src/global/schema/hydration.js"],"sourcesContent":["/**\n * Hydration adapter seam - the population contract for readers that build\n * runtime objects incrementally (black/red). The ordering guarantee:\n *\n * 1. construct - build every instance (register refs before values, so\n * cycles/back-references resolve)\n * 2. applyValues - apply each node's field values\n * 3. finalize - run once per instance AFTER the whole graph is built,\n * so references are already resolved\n *\n * DEFAULTS (operator ruling, 2026-09-05; resource-population.md owns the\n * decision): a resolved class is populated through its own validated setter -\n * \"if someone is using Object.assign, they probably should be using\n * SetValues\" - and a class-owned Initialize runs once post-graph, which is\n * the incremental-reader counterpart of the reader+IInitialize handshake\n * Blue performs and CjsModel.from performs for whole-bag JSON. Plain\n * fallback carriers (unresolved kinds) keep raw assignment: that IS their\n * contract, not a bypass.\n *\n * The single escape hatch is `options.adapter`, per-hook optional. The\n * per-kind adapters map and the hydrationAdapter alias are gone - they had\n * zero consumers. Callers wanting raw assignment pass their own adapter.\n *\n * SetValues is called directly rather than through CjsSchema.setValues so a\n * reader works with only the schema layer loaded; the two are the same path\n * for every class that has the method.\n *\n * A REGISTERED class without SetValues - one that no longer extends CjsModel -\n * is still a resolved class, so it populates through CjsSchema.setValues, which\n * answers without the model layer. Only an unregistered carrier (the reader's\n * `{ _sourceClassName }` fallback) takes raw assignment. Without this middle\n * arm, taking a class off the base would silently drop it to Object.assign:\n * no coercion, no writability gate, no settle.\n */\n\nimport { CjsSchema } from \"./CjsSchema.js\";\n\n/**\n * Resolves a normalized adapter from hydration options. The returned object\n * always exposes construct/applyValues/finalize with the documented defaults.\n *\n * @param {Object} [options]\n * @param {Object} [options.adapter] Per-hook overrides: construct/applyValues/finalize.\n * @returns {{ construct: Function, applyValues: Function, finalize: Function }}\n */\nexport function resolveHydrationAdapter(options = {})\n{\n const custom = options.adapter || null;\n\n return {\n construct(kind, ctx)\n {\n if (custom && typeof custom.construct === \"function\") return custom.construct(kind, ctx);\n return undefined;\n },\n applyValues(instance, values, ctx)\n {\n if (custom && typeof custom.applyValues === \"function\") return custom.applyValues(instance, values, ctx);\n if (instance && typeof instance.SetValues === \"function\")\n {\n instance.SetValues(values, ctx?.options);\n return instance;\n }\n if (instance && CjsSchema.getClassName(instance.constructor))\n {\n CjsSchema.setValues(instance, values, ctx?.options);\n return instance;\n }\n return Object.assign(instance, values);\n },\n finalize(instance, ctx)\n {\n if (custom && typeof custom.finalize === \"function\")\n {\n custom.finalize(instance, ctx);\n return;\n }\n if (instance && typeof instance.Initialize === \"function\") instance.Initialize();\n }\n };\n}\n"],"names":["resolveHydrationAdapter","options","custom","adapter","construct","kind","ctx","undefined","applyValues","instance","values","SetValues","CjsSchema","getClassName","constructor","setValues","Object","assign","finalize","Initialize"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;AAIA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASA,uBAAuBA,CAACC,OAAO,GAAG,EAAE,EACpD;AACI,EAAA,MAAMC,MAAM,GAAGD,OAAO,CAACE,OAAO,IAAI,IAAI;EAEtC,OAAO;AACHC,IAAAA,SAASA,CAACC,IAAI,EAAEC,GAAG,EACnB;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACE,SAAS,KAAK,UAAU,EAAE,OAAOF,MAAM,CAACE,SAAS,CAACC,IAAI,EAAEC,GAAG,CAAC;AACxF,MAAA,OAAOC,SAAS;IACpB,CAAC;AACDC,IAAAA,WAAWA,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,EACjC;AACI,MAAA,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACM,WAAW,KAAK,UAAU,EAAE,OAAON,MAAM,CAACM,WAAW,CAACC,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,CAAC;MACxG,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACE,SAAS,KAAK,UAAU,EACxD;QACIF,QAAQ,CAACE,SAAS,CAACD,MAAM,EAAEJ,GAAG,EAAEL,OAAO,CAAC;AACxC,QAAA,OAAOQ,QAAQ;AACnB,MAAA;MACA,IAAIA,QAAQ,IAAIG,SAAS,CAACC,YAAY,CAACJ,QAAQ,CAACK,WAAW,CAAC,EAC5D;QACIF,SAAS,CAACG,SAAS,CAACN,QAAQ,EAAEC,MAAM,EAAEJ,GAAG,EAAEL,OAAO,CAAC;AACnD,QAAA,OAAOQ,QAAQ;AACnB,MAAA;AACA,MAAA,OAAOO,MAAM,CAACC,MAAM,CAACR,QAAQ,EAAEC,MAAM,CAAC;IAC1C,CAAC;AACDQ,IAAAA,QAAQA,CAACT,QAAQ,EAAEH,GAAG,EACtB;MACI,IAAIJ,MAAM,IAAI,OAAOA,MAAM,CAACgB,QAAQ,KAAK,UAAU,EACnD;AACIhB,QAAAA,MAAM,CAACgB,QAAQ,CAACT,QAAQ,EAAEH,GAAG,CAAC;AAC9B,QAAA;AACJ,MAAA;AACA,MAAA,IAAIG,QAAQ,IAAI,OAAOA,QAAQ,CAACU,UAAU,KAAK,UAAU,EAAEV,QAAQ,CAACU,UAAU,EAAE;AACpF,IAAA;GACH;AACL;;;;"}
@@ -395,13 +395,23 @@ class DxbcGlslEmitter {
395
395
  this._analyzeUavStores(state);
396
396
  }
397
397
  state.integerVertexInputs = new Map();
398
+ // Register -> GLSL integer built-in (`gl_VertexID`/`gl_InstanceID`) for
399
+ // system-value inputs; filled by _declareSystemInput.
400
+ state.systemIntegerInputs = new Map();
401
+ // Integer companions for temporaries. Small integers held as float bit
402
+ // patterns are denormals, and ANGLE/D3D11 hardware does not preserve
403
+ // them in float storage: a vertex-id corner index `r0.x = uintBitsToFloat(id & 3u)`
404
+ // reads back 0 for ids 1..3, collapsing every quad (plane sets, the sprite
405
+ // pool). Packed-light shaders need the same companions for their words.
406
+ state.integerTemps = !!state.lightPackedTexture || state.decoder.instructions.some(instruction => (instruction.opcodeName === "dcl_input_sgv" || instruction.opcodeName === "dcl_input_ps_sgv") && (instruction.declaration?.systemValueName === "vertex_id" || instruction.declaration?.systemValueName === "instance_id"));
398
407
  state.formatter = new DxbcGlslOperandFormatter({
399
408
  // Populated during vertex-input declaration (the declaration loop
400
409
  // runs before any body emission, so the map is complete before the
401
410
  // first operand is formatted). Integer reads of these registers
402
411
  // value-convert instead of bitcast - see _declareVertexInput.
403
412
  integerInputs: state.integerVertexInputs,
404
- integerTemps: !!state.lightPackedTexture,
413
+ integerTemps: state.integerTemps,
414
+ systemIntegerInputs: state.systemIntegerInputs,
405
415
  componentMap: operand => {
406
416
  if (operand.type === 1) return state.inputMasks.get(operand.registerIndex) || null;
407
417
  if (operand.type === 2) return state.outputMasks.get(operand.registerIndex) || null;
@@ -1109,7 +1119,7 @@ class DxbcGlslEmitter {
1109
1119
  case "dcl_temps":
1110
1120
  for (let index = 0; index < declaration.tempCount; index += 1) {
1111
1121
  state.declarationLines.push(`vec4 r${index};`);
1112
- if (state.lightPackedTexture) state.declarationLines.push(`uvec4 cjsBitsR${index};`);
1122
+ if (state.integerTemps) state.declarationLines.push(`uvec4 cjsBitsR${index};`);
1113
1123
  }
1114
1124
  break;
1115
1125
  case "dcl_indexable_temp":
@@ -1515,9 +1525,11 @@ class DxbcGlslEmitter {
1515
1525
  break;
1516
1526
  case "vertex_id":
1517
1527
  state.inputNames.set(register, "vec4(intBitsToFloat(gl_VertexID))");
1528
+ state.systemIntegerInputs.set(register, "gl_VertexID");
1518
1529
  break;
1519
1530
  case "instance_id":
1520
1531
  state.inputNames.set(register, "vec4(intBitsToFloat(gl_InstanceID))");
1532
+ state.systemIntegerInputs.set(register, "gl_InstanceID");
1521
1533
  break;
1522
1534
  default:
1523
1535
  throw new WebglReadError("System-generated input is not supported by the WebGL2 emitter", {
@@ -1759,7 +1771,7 @@ class DxbcGlslEmitter {
1759
1771
  // Centralize writes here because several lowerings emit lane writes
1760
1772
  // directly rather than going through _assign. This is per assignment,
1761
1773
  // not a shader/register-number pattern or a control-flow dataflow guess.
1762
- const assignment = state.lightPackedTexture && /^(r(\d+)(?:\.[xyzw]+)?) = ([\s\S]+);$/.exec(text);
1774
+ const assignment = state.integerTemps && /^(r(\d+)(?:\.[xyzw]+)?) = ([\s\S]+);$/.exec(text);
1763
1775
  if (assignment) {
1764
1776
  const [, target, index, value] = assignment;
1765
1777
  const suffix = target.slice(`r${index}`.length);
@@ -2128,7 +2140,7 @@ class DxbcGlslEmitter {
2128
2140
  } = this._destMask(state, instruction);
2129
2141
  if (!target) return;
2130
2142
  const aliases = instruction.operands.slice(1).some(operand => operand.type === destOperand.type && operand.registerIndex === destOperand.registerIndex);
2131
- const rawMove = !!state.lightPackedTexture && destOperand.type === 0;
2143
+ const rawMove = state.integerTemps && destOperand.type === 0;
2132
2144
  if (aliases) {
2133
2145
  this._line(state, "{");
2134
2146
  state.indent += 1;
@@ -2503,7 +2515,7 @@ DxbcGlslEmitter.LOWERINGS = {
2503
2515
  const {
2504
2516
  mask
2505
2517
  } = this._destMask(state, instruction);
2506
- if (state.lightPackedTexture && instruction.operands[0].type === 0 && !instruction.saturate && !["neg", "abs", "absneg"].includes(instruction.operands[1].modifierName)) {
2518
+ if (state.integerTemps && instruction.operands[0].type === 0 && !instruction.saturate && !["neg", "abs", "absneg"].includes(instruction.operands[1].modifierName)) {
2507
2519
  const raw = this._vecArg(state, instruction.operands[1], mask, "uint");
2508
2520
  this._assign(state, instruction, `uintBitsToFloat(${raw})`);
2509
2521
  return;